Backed out 10 changesets (bug 1732343) for causing coverage build bustages (Bug 1739590).
authorCristian Tuns <ctuns@mozilla.com>
Fri, 05 Nov 2021 07:21:04 -0400
changeset 598326 0d855795667b1c7d416397782512e709a9d4f8bc
parent 598325 7e8e3747c3f81e844e17ae8d645dd6091bafcae7
child 598327 eb905e7bd8d210d5c8fe29d595afb4b8f5b2312f
push id38951
push usercsabou@mozilla.com
push dateFri, 05 Nov 2021 12:22:16 +0000
treeherdermozilla-central@eb905e7bd8d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1732343, 1739590
milestone96.0a1
backs outbba94c79f3e1bbd7ad158d362cf0df252c9f3e39
d30fa1e1f60556ba98bb7fe17c4cdefeb75727b1
ed0b4f757c4bd31cf3e3b8b1c9105aa3f32e9b17
a272da134c34498ceeaaf4ebca3e47298b4df836
ccb259d7384336b83c511bb58a9ae379aebb6ed0
a292990b62de356c9c305f9c6caafe2dbde2a84a
7d1854782ca82edc9aeb44a5c3aa7e0ce1af2a7f
29eaabd9ffb32654be441db1c28f97900a293616
1aa26657a7a6aba290d9935012752a692d8d021c
7a6708dc620a342dd654e3c212b5b7cd3488a379
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
Backed out 10 changesets (bug 1732343) for causing coverage build bustages (Bug 1739590). Backed out changeset bba94c79f3e1 (bug 1732343) Backed out changeset d30fa1e1f605 (bug 1732343) Backed out changeset ed0b4f757c4b (bug 1732343) Backed out changeset a272da134c34 (bug 1732343) Backed out changeset ccb259d73843 (bug 1732343) Backed out changeset a292990b62de (bug 1732343) Backed out changeset 7d1854782ca8 (bug 1732343) Backed out changeset 29eaabd9ffb3 (bug 1732343) Backed out changeset 1aa26657a7a6 (bug 1732343) Backed out changeset 7a6708dc620a (bug 1732343)
dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/DOMTypes.ipdlh
dom/ipc/PContent.ipdl
dom/media/gmp/ChromiumCDMParent.cpp
gfx/layers/CanvasDrawEventRecorder.cpp
gfx/layers/CanvasDrawEventRecorder.h
gfx/layers/SourceSurfaceSharedData.cpp
gfx/layers/SourceSurfaceSharedData.h
gfx/layers/client/TextureClient.cpp
gfx/layers/client/TextureClient.h
gfx/layers/composite/TextureHost.cpp
gfx/layers/composite/TextureHost.h
gfx/layers/ipc/CanvasChild.cpp
gfx/layers/ipc/CanvasTranslator.cpp
gfx/layers/ipc/CanvasTranslator.h
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeChild.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CompositorManagerParent.cpp
gfx/layers/ipc/CompositorManagerParent.h
gfx/layers/ipc/ContentCompositorBridgeParent.cpp
gfx/layers/ipc/ContentCompositorBridgeParent.h
gfx/layers/ipc/ISurfaceAllocator.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/ImageBridgeParent.cpp
gfx/layers/ipc/ImageBridgeParent.h
gfx/layers/ipc/LayersMessages.ipdlh
gfx/layers/ipc/LayersSurfaces.ipdlh
gfx/layers/ipc/PCanvas.ipdl
gfx/layers/ipc/PCompositorManager.ipdl
gfx/layers/ipc/SharedSurfacesChild.cpp
gfx/layers/ipc/SharedSurfacesParent.cpp
gfx/layers/ipc/SharedSurfacesParent.h
gfx/layers/ipc/TextureForwarder.h
gfx/layers/ipc/VideoBridgeChild.cpp
gfx/layers/ipc/VideoBridgeChild.h
gfx/layers/ipc/VideoBridgeParent.cpp
gfx/layers/ipc/VideoBridgeParent.h
gfx/thebes/SharedFontList-impl.h
gfx/thebes/SharedFontList.cpp
gfx/thebes/gfxPlatformFontList.cpp
intl/hyphenation/glue/nsHyphenator.cpp
ipc/chromium/moz.build
ipc/chromium/src/base/file_descriptor_posix.h
ipc/chromium/src/base/shared_memory.h
ipc/chromium/src/base/shared_memory_posix.cc
ipc/chromium/src/base/shared_memory_win.cc
ipc/chromium/src/chrome/common/child_process_host.cc
ipc/chromium/src/chrome/common/child_thread.cc
ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc
ipc/chromium/src/chrome/common/file_descriptor_set_posix.h
ipc/chromium/src/chrome/common/ipc_channel.h
ipc/chromium/src/chrome/common/ipc_channel_posix.cc
ipc/chromium/src/chrome/common/ipc_channel_posix.h
ipc/chromium/src/chrome/common/ipc_channel_win.cc
ipc/chromium/src/chrome/common/ipc_channel_win.h
ipc/chromium/src/chrome/common/ipc_message.cc
ipc/chromium/src/chrome/common/ipc_message.h
ipc/chromium/src/chrome/common/ipc_message_utils.h
ipc/glue/AutoTransportDescriptor.cpp
ipc/glue/AutoTransportDescriptor.h
ipc/glue/CrossProcessMutex.h
ipc/glue/CrossProcessMutex_posix.cpp
ipc/glue/CrossProcessMutex_windows.cpp
ipc/glue/CrossProcessSemaphore.h
ipc/glue/CrossProcessSemaphore_posix.cpp
ipc/glue/CrossProcessSemaphore_windows.cpp
ipc/glue/FileDescriptor.cpp
ipc/glue/FileDescriptor.h
ipc/glue/IdleSchedulerChild.cpp
ipc/glue/IdleSchedulerParent.cpp
ipc/glue/MiniTransceiver.cpp
ipc/glue/NodeController.cpp
ipc/glue/NodeController.h
ipc/glue/PIdleScheduler.ipdl
ipc/glue/ProcessUtils.h
ipc/glue/ProcessUtils_common.cpp
ipc/glue/ProtocolUtils.cpp
ipc/glue/ProtocolUtils.h
ipc/glue/SharedMemory.h
ipc/glue/SharedMemoryBasic_android.cpp
ipc/glue/SharedMemoryBasic_android.h
ipc/glue/SharedMemoryBasic_chromium.h
ipc/glue/SharedMemoryBasic_mach.h
ipc/glue/SharedMemoryBasic_mach.mm
ipc/glue/Transport.h
ipc/glue/Transport_posix.cpp
ipc/glue/Transport_posix.h
ipc/glue/Transport_win.cpp
ipc/glue/Transport_win.h
ipc/gtest/TestSharedMemory.cpp
ipc/ipdl/ipdl/ast.py
ipc/ipdl/ipdl/lower.py
ipc/ipdl/ipdl/type.py
ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl
ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl
js/xpconnect/src/XPCSelfHostedShmem.cpp
layout/style/GlobalStyleSheetCache.cpp
layout/style/GlobalStyleSheetCache.h
tools/fuzzing/faulty/Faulty.cpp
widget/windows/PCompositorWidget.ipdl
widget/windows/RemoteBackbuffer.cpp
widget/windows/RemoteBackbuffer.h
--- a/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
+++ b/dom/indexedDB/PBackgroundIDBSharedTypes.ipdlh
@@ -30,17 +30,17 @@ using class mozilla::dom::indexedDB::Key
   from "mozilla/dom/indexedDB/Key.h";
 
 using class mozilla::dom::indexedDB::KeyPath
   from "mozilla/dom/indexedDB/KeyPath.h";
 
 using mozilla::dom::quota::PersistenceType
   from "mozilla/dom/quota/PersistenceType.h";
 
-[MoveOnly=data] using mozilla::SerializedStructuredCloneBuffer
+[MoveOnly] using mozilla::SerializedStructuredCloneBuffer
   from "mozilla/ipc/SerializedStructuredCloneBuffer.h";
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 struct SerializedKeyRange
 {
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -672,33 +672,33 @@ ContentChild::~ContentChild() {
 NS_INTERFACE_MAP_BEGIN(ContentChild)
   NS_INTERFACE_MAP_ENTRY(nsIDOMProcessChild)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessChild)
 NS_INTERFACE_MAP_END
 
 mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
     XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData,
     FullLookAndFeel&& aLookAndFeelData, dom::SystemFontList&& aFontList,
-    Maybe<SharedMemoryHandle>&& aSharedUASheetHandle,
+    const Maybe<SharedMemoryHandle>& aSharedUASheetHandle,
     const uintptr_t& aSharedUASheetAddress,
     nsTArray<SharedMemoryHandle>&& aSharedFontListBlocks) {
   if (!sShutdownCanary) {
     return IPC_OK();
   }
 
   mLookAndFeelData = std::move(aLookAndFeelData);
   mFontList = std::move(aFontList);
   mSharedFontListBlocks = std::move(aSharedFontListBlocks);
 #ifdef XP_WIN
   widget::WinContentSystemParameters::GetSingleton()->SetContentValues(
       aXPCOMInit.systemParameters());
 #endif
 
   gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
-  InitSharedUASheets(std::move(aSharedUASheetHandle), aSharedUASheetAddress);
+  InitSharedUASheets(aSharedUASheetHandle, aSharedUASheetAddress);
   InitXPCOM(std::move(aXPCOMInit), aInitialData);
   InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
 
   return IPC_OK();
 }
 
 class nsGtkNativeInitRunnable : public Runnable {
  public:
@@ -1308,28 +1308,28 @@ void ContentChild::AppendProcessId(nsACS
   unsigned pid = getpid();
   aName.Append(nsPrintfCString("(pid %u)", pid));
 }
 
 void ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData) {
   gfxPlatform::InitChild(aData);
 }
 
-void ContentChild::InitSharedUASheets(Maybe<SharedMemoryHandle>&& aHandle,
+void ContentChild::InitSharedUASheets(const Maybe<SharedMemoryHandle>& aHandle,
                                       uintptr_t aAddress) {
   MOZ_ASSERT_IF(!aHandle, !aAddress);
 
   if (!aAddress) {
     return;
   }
 
   // Map the shared memory storing the user agent style sheets.  Do this as
   // early as possible to maximize the chance of being able to map at the
   // address we want.
-  GlobalStyleSheetCache::SetSharedMemory(std::move(*aHandle), aAddress);
+  GlobalStyleSheetCache::SetSharedMemory(*aHandle, aAddress);
 }
 
 void ContentChild::InitXPCOM(
     XPCOMInitData&& aXPCOMInit,
     const mozilla::dom::ipc::StructuredCloneData& aInitialData) {
 #ifdef MOZ_WIDGET_GTK
   // LookAndFeel::NativeInit takes a long time to run on Linux, here we schedule
   // it as soon as possible after BackgroundChild::Startup to give
@@ -2515,20 +2515,20 @@ mozilla::ipc::IPCResult ContentChild::Re
   if (gfxPlatform::Initialized()) {
     gfxPlatform::GetPlatform()->UpdateFontList(aFullRebuild);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvFontListShmBlockAdded(
     const uint32_t& aGeneration, const uint32_t& aIndex,
-    base::SharedMemoryHandle&& aHandle) {
+    const base::SharedMemoryHandle& aHandle) {
   if (gfxPlatform::Initialized()) {
     gfxPlatformFontList::PlatformFontList()->ShmBlockAdded(aGeneration, aIndex,
-                                                           std::move(aHandle));
+                                                           aHandle);
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult ContentChild::RecvUpdateAppLocales(
     nsTArray<nsCString>&& aAppLocales) {
   LocaleService::GetInstance()->AssignAppLocales(aAppLocales);
   return IPC_OK();
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -121,17 +121,17 @@ class ContentChild final : public PConte
 
   void Init(base::ProcessId aParentPid, const char* aParentBuildID,
             mozilla::ipc::ScopedPort aPort, uint64_t aChildID,
             bool aIsForBrowser);
 
   void InitXPCOM(XPCOMInitData&& aXPCOMInit,
                  const mozilla::dom::ipc::StructuredCloneData& aInitialData);
 
-  void InitSharedUASheets(Maybe<base::SharedMemoryHandle>&& aHandle,
+  void InitSharedUASheets(const Maybe<base::SharedMemoryHandle>& aHandle,
                           uintptr_t aAddress);
 
   void InitGraphicsDeviceData(const ContentDeviceData& aData);
 
   static ContentChild* GetSingleton() { return sSingleton; }
 
   const AppInfo& GetAppInfo() { return mAppInfo; }
 
@@ -353,17 +353,17 @@ class ContentChild final : public PConte
 
   mozilla::ipc::IPCResult RecvUpdateDictionaryList(
       nsTArray<nsCString>&& aDictionaries);
 
   mozilla::ipc::IPCResult RecvUpdateFontList(SystemFontList&&);
   mozilla::ipc::IPCResult RecvRebuildFontList(const bool& aFullRebuild);
   mozilla::ipc::IPCResult RecvFontListShmBlockAdded(
       const uint32_t& aGeneration, const uint32_t& aIndex,
-      base::SharedMemoryHandle&& aHandle);
+      const base::SharedMemoryHandle& aHandle);
 
   mozilla::ipc::IPCResult RecvUpdateAppLocales(
       nsTArray<nsCString>&& aAppLocales);
   mozilla::ipc::IPCResult RecvUpdateRequestedLocales(
       nsTArray<nsCString>&& aRequestedLocales);
 
   mozilla::ipc::IPCResult RecvAddPermission(const IPC::Permission& permission);
 
@@ -537,17 +537,17 @@ class ContentChild final : public PConte
 #if defined(XP_WIN)
   mozilla::ipc::IPCResult RecvGetUntrustedModulesData(
       GetUntrustedModulesDataResolver&& aResolver);
 #endif  // defined(XP_WIN)
 
   mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes(
       XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData,
       FullLookAndFeel&& aLookAndFeelData, SystemFontList&& aFontList,
-      Maybe<base::SharedMemoryHandle>&& aSharedUASheetHandle,
+      const Maybe<base::SharedMemoryHandle>& aSharedUASheetHandle,
       const uintptr_t& aSharedUASheetAddress,
       nsTArray<base::SharedMemoryHandle>&& aSharedFontListBlocks);
 
   mozilla::ipc::IPCResult RecvProvideAnonymousTemporaryFile(
       const uint64_t& aID, const FileDescOrError& aFD);
 
   mozilla::ipc::IPCResult RecvSetPermissionsWithKey(
       const nsCString& aPermissionKey, nsTArray<IPC::Permission>&& aPerms);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1632,18 +1632,17 @@ void ContentParent::BroadcastShmBlockAdd
   for (auto* cp : AllProcesses(eLive)) {
     base::SharedMemoryHandle handle =
         pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
     if (handle == base::SharedMemory::NULLHandle()) {
       // If something went wrong here, we just skip it; the child will need to
       // request the block as needed, at some performance cost.
       continue;
     }
-    Unused << cp->SendFontListShmBlockAdded(aGeneration, aIndex,
-                                            std::move(handle));
+    Unused << cp->SendFontListShmBlockAdded(aGeneration, aIndex, handle);
   }
 }
 
 void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
   const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
   for (auto* cp : AllProcesses(eLive)) {
     Unused << cp->SendThemeChanged(lnf, aKind);
   }
@@ -2972,24 +2971,24 @@ bool ContentParent::InitInternal(Process
   screenManager.CopyScreensToRemote(this);
 
   // Send the UA sheet shared memory buffer and the address it is mapped at.
   Maybe<SharedMemoryHandle> sharedUASheetHandle;
   uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
 
   SharedMemoryHandle handle;
   if (sheetCache->ShareToProcess(OtherPid(), &handle)) {
-    sharedUASheetHandle.emplace(std::move(handle));
+    sharedUASheetHandle.emplace(handle);
   } else {
     sharedUASheetAddress = 0;
   }
 
   Unused << SendSetXPCOMProcessAttributes(
-      xpcomInit, initialData, lnf, fontList, std::move(sharedUASheetHandle),
-      sharedUASheetAddress, std::move(sharedFontListBlocks));
+      xpcomInit, initialData, lnf, fontList, sharedUASheetHandle,
+      sharedUASheetAddress, sharedFontListBlocks);
 
   ipc::WritableSharedMap* sharedData =
       nsFrameMessageManager::sParentProcessManager->SharedData();
   sharedData->Flush();
   sharedData->SendTo(this);
 
   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
   nsChromeRegistryChrome* chromeRegistry =
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -16,17 +16,17 @@ include "mozilla/layers/LayersMessageUti
 
 include IPCBlob;
 include IPCStream;
 include protocol PRemoteLazyInputStream;
 include ProtocolTypes;
 
 using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
 
-[MoveOnly=data] using struct mozilla::SerializedStructuredCloneBuffer
+[MoveOnly] using struct mozilla::SerializedStructuredCloneBuffer
   from "mozilla/ipc/SerializedStructuredCloneBuffer.h";
 
 using class mozilla::dom::LoadingSessionHistoryInfo
   from "mozilla/dom/SessionHistoryEntry.h";
 
 using LayoutDeviceIntRect from "Units.h";
 using DesktopIntRect from "Units.h";
 using DesktopToLayoutDeviceScale from "Units.h";
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -112,24 +112,24 @@ using mozilla::Telemetry::ChildEventData
 
 #if defined(XP_WIN)
 [MoveOnly] using mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
 [MoveOnly] using mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
 [MoveOnly] using mozilla::ModulesMapResult from "mozilla/UntrustedModulesData.h";
 #endif  // defined(XP_WIN)
 
 using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
-[MoveOnly] using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextTransaction from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext.h";
 using mozilla::dom::PermitUnloadResult from "nsIContentViewer.h";
 using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
 using mozilla::dom::WindowContextTransaction from "mozilla/dom/WindowContext.h";
-[MoveOnly] using base::SharedMemoryHandle from "base/shared_memory.h";
+using base::SharedMemoryHandle from "base/shared_memory.h";
 using mozilla::fontlist::Pointer from "SharedFontList.h";
 using gfxSparseBitSet from "gfxFontUtils.h";
 using FontVisibility from "gfxFontEntry.h";
 using mozilla::dom::MediaControlAction from "mozilla/dom/MediaControlKeySource.h";
 using mozilla::dom::MediaPlaybackState from "mozilla/dom/MediaPlaybackStatus.h";
 using mozilla::dom::MediaAudibleState from "mozilla/dom/MediaPlaybackStatus.h";
 using mozilla::dom::MediaMetadataBase from "mozilla/dom/MediaMetadata.h";
 using mozilla::dom::MediaSessionAction from "mozilla/dom/MediaSessionBinding.h";
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -374,17 +374,17 @@ bool ChromiumCDMParent::InitCDMInputBuff
       MOZ_ASSERT_UNREACHABLE("Should not have unrecognized encryption type");
       break;
   }
 
   const nsTArray<uint8_t>& iv = encryptionScheme != cdm::EncryptionScheme::kCbcs
                                     ? crypto.mIV
                                     : crypto.mConstantIV;
   aBuffer = gmp::CDMInputBuffer(
-      shmem, crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
+      std::move(shmem), crypto.mKeyId, iv, aSample->mTime.ToMicroseconds(),
       aSample->mDuration.ToMicroseconds(), crypto.mPlainSizes,
       crypto.mEncryptedSizes, crypto.mCryptByteBlock, crypto.mSkipByteBlock,
       encryptionScheme);
   return true;
 }
 
 bool ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes) {
   MOZ_ASSERT(mGMPThread->IsOnCurrentThread());
--- a/gfx/layers/CanvasDrawEventRecorder.cpp
+++ b/gfx/layers/CanvasDrawEventRecorder.cpp
@@ -79,35 +79,35 @@ bool CanvasEventRingBuffer::InitWriter(
 
   mWriterServices = std::move(aWriterServices);
 
   mGood = true;
   return true;
 }
 
 bool CanvasEventRingBuffer::InitReader(
-    ipc::SharedMemoryBasic::Handle aReadHandle,
-    CrossProcessSemaphoreHandle aReaderSem,
-    CrossProcessSemaphoreHandle aWriterSem,
+    const ipc::SharedMemoryBasic::Handle& aReadHandle,
+    const CrossProcessSemaphoreHandle& aReaderSem,
+    const CrossProcessSemaphoreHandle& aWriterSem,
     UniquePtr<ReaderServices> aReaderServices) {
   mSharedMemory = MakeAndAddRef<ipc::SharedMemoryBasic>();
   if (NS_WARN_IF(!mSharedMemory->SetHandle(
-          std::move(aReadHandle), ipc::SharedMemory::RightsReadWrite)) ||
+          aReadHandle, ipc::SharedMemory::RightsReadWrite)) ||
       NS_WARN_IF(!mSharedMemory->Map(kShmemSize))) {
     return false;
   }
 
   mSharedMemory->CloseHandle();
 
   mBuf = static_cast<char*>(mSharedMemory->memory());
   mRead = reinterpret_cast<ReadFooter*>(mBuf + kStreamSize);
   mWrite = reinterpret_cast<WriteFooter*>(mBuf + kStreamSize + kCacheLineSize);
-  mReaderSemaphore.reset(CrossProcessSemaphore::Create(std::move(aReaderSem)));
+  mReaderSemaphore.reset(CrossProcessSemaphore::Create(aReaderSem));
   mReaderSemaphore->CloseHandle();
-  mWriterSemaphore.reset(CrossProcessSemaphore::Create(std::move(aWriterSem)));
+  mWriterSemaphore.reset(CrossProcessSemaphore::Create(aWriterSem));
   mWriterSemaphore->CloseHandle();
 
   mReaderServices = std::move(aReaderServices);
 
   mGood = true;
   return true;
 }
 
--- a/gfx/layers/CanvasDrawEventRecorder.h
+++ b/gfx/layers/CanvasDrawEventRecorder.h
@@ -77,19 +77,19 @@ class CanvasEventRingBuffer final : publ
    * Initialize the read side of a CanvasEventRingBuffer.
    *
    * @param aReadHandle handle to the shared memory for the buffer
    * @param aReaderSem reading blocked semaphore
    * @param aWriterSem writing blocked semaphore
    * @param aReaderServices provides functions required by the reader
    * @returns true if initialization succeeds
    */
-  bool InitReader(ipc::SharedMemoryBasic::Handle aReadHandle,
-                  CrossProcessSemaphoreHandle aReaderSem,
-                  CrossProcessSemaphoreHandle aWriterSem,
+  bool InitReader(const ipc::SharedMemoryBasic::Handle& aReadHandle,
+                  const CrossProcessSemaphoreHandle& aReaderSem,
+                  const CrossProcessSemaphoreHandle& aWriterSem,
                   UniquePtr<ReaderServices> aReaderServices);
 
   bool good() const final { return mGood; }
 
   bool WriterFailed() const { return mWrite->state == State::Failed; }
 
   void SetIsBad() final {
     mGood = false;
--- a/gfx/layers/SourceSurfaceSharedData.cpp
+++ b/gfx/layers/SourceSurfaceSharedData.cpp
@@ -25,29 +25,28 @@
 #  define SHARED_SURFACE_PROTECT_FINALIZED
 #endif
 
 using namespace mozilla::layers;
 
 namespace mozilla {
 namespace gfx {
 
-void SourceSurfaceSharedDataWrapper::Init(const IntSize& aSize, int32_t aStride,
-                                          SurfaceFormat aFormat,
-                                          SharedMemoryBasic::Handle aHandle,
-                                          base::ProcessId aCreatorPid) {
+void SourceSurfaceSharedDataWrapper::Init(
+    const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
+    const SharedMemoryBasic::Handle& aHandle, base::ProcessId aCreatorPid) {
   MOZ_ASSERT(!mBuf);
   mSize = aSize;
   mStride = aStride;
   mFormat = aFormat;
   mCreatorPid = aCreatorPid;
 
   size_t len = GetAlignedDataLength();
   mBuf = MakeAndAddRef<SharedMemoryBasic>();
-  if (!mBuf->SetHandle(std::move(aHandle), ipc::SharedMemory::RightsReadOnly)) {
+  if (!mBuf->SetHandle(aHandle, ipc::SharedMemory::RightsReadOnly)) {
     MOZ_CRASH("Invalid shared memory handle!");
   }
 
   bool mapped = EnsureMapped(len);
   if ((sizeof(uintptr_t) <= 4 ||
        StaticPrefs::image_mem_shared_unmap_force_enabled_AtStartup()) &&
       len / 1024 >
           StaticPrefs::image_mem_shared_unmap_min_threshold_kb_AtStartup()) {
--- a/gfx/layers/SourceSurfaceSharedData.h
+++ b/gfx/layers/SourceSurfaceSharedData.h
@@ -42,17 +42,18 @@ class SourceSurfaceSharedDataWrapper fin
   SourceSurfaceSharedDataWrapper()
       : mStride(0),
         mConsumers(0),
         mFormat(SurfaceFormat::UNKNOWN),
         mCreatorPid(0),
         mCreatorRef(true) {}
 
   void Init(const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat,
-            SharedMemoryBasic::Handle aHandle, base::ProcessId aCreatorPid);
+            const SharedMemoryBasic::Handle& aHandle,
+            base::ProcessId aCreatorPid);
 
   void Init(SourceSurfaceSharedData* aSurface);
 
   base::ProcessId GetCreatorPid() const { return mCreatorPid; }
 
   int32_t Stride() override { return mStride; }
 
   SurfaceType GetType() const override {
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -1034,19 +1034,18 @@ bool TextureClient::InitIPDLActor(Compos
       aForwarder->GetTextureForwarder()->GetNextExternalImageId();
 
   ReadLockDescriptor readLockDescriptor = null_t();
   if (mReadLock) {
     mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid());
   }
 
   PTextureChild* actor = aForwarder->GetTextureForwarder()->CreateTexture(
-      desc, std::move(readLockDescriptor),
-      aForwarder->GetCompositorBackendType(), GetFlags(), mSerial,
-      mExternalImageId, nullptr);
+      desc, readLockDescriptor, aForwarder->GetCompositorBackendType(),
+      GetFlags(), mSerial, mExternalImageId, nullptr);
 
   if (!actor) {
     gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
                     << static_cast<int32_t>(
                            aForwarder->GetCompositorBackendType())
                     << ", " << static_cast<uint32_t>(GetFlags()) << ", "
                     << mSerial;
     return false;
@@ -1101,20 +1100,19 @@ bool TextureClient::InitIPDLActor(KnowsC
   mExternalImageId =
       aKnowsCompositor->GetTextureForwarder()->GetNextExternalImageId();
 
   ReadLockDescriptor readLockDescriptor = null_t();
   if (mReadLock) {
     mReadLock->Serialize(readLockDescriptor, GetAllocator()->GetParentPid());
   }
 
-  PTextureChild* actor =
-      fwd->CreateTexture(desc, std::move(readLockDescriptor),
-                         aKnowsCompositor->GetCompositorBackendType(),
-                         GetFlags(), mSerial, mExternalImageId);
+  PTextureChild* actor = fwd->CreateTexture(
+      desc, readLockDescriptor, aKnowsCompositor->GetCompositorBackendType(),
+      GetFlags(), mSerial, mExternalImageId);
   if (!actor) {
     gfxCriticalNote << static_cast<int32_t>(desc.type()) << ", "
                     << static_cast<int32_t>(
                            aKnowsCompositor->GetCompositorBackendType())
                     << ", " << static_cast<uint32_t>(GetFlags()) << ", "
                     << mSerial;
     return false;
   }
@@ -1501,18 +1499,17 @@ class ShmemTextureReadLock : public NonB
 };
 
 class CrossProcessSemaphoreReadLock : public TextureReadLock {
  public:
   CrossProcessSemaphoreReadLock()
       : mSemaphore(CrossProcessSemaphore::Create("TextureReadLock", 1)),
         mShared(false) {}
   explicit CrossProcessSemaphoreReadLock(CrossProcessSemaphoreHandle aHandle)
-      : mSemaphore(CrossProcessSemaphore::Create(std::move(aHandle))),
-        mShared(false) {}
+      : mSemaphore(CrossProcessSemaphore::Create(aHandle)), mShared(false) {}
 
   bool ReadLock() override {
     if (!IsValid()) {
       return false;
     }
     return mSemaphore->Wait();
   }
   bool TryReadLock(TimeDuration aTimeout) override {
@@ -1535,17 +1532,17 @@ class CrossProcessSemaphoreReadLock : pu
   LockType GetType() override { return TYPE_CROSS_PROCESS_SEMAPHORE; }
 
   UniquePtr<CrossProcessSemaphore> mSemaphore;
   bool mShared;
 };
 
 // static
 already_AddRefed<TextureReadLock> TextureReadLock::Deserialize(
-    ReadLockDescriptor&& aDescriptor, ISurfaceAllocator* aAllocator) {
+    const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator) {
   switch (aDescriptor.type()) {
     case ReadLockDescriptor::TShmemSection: {
       const ShmemSection& section = aDescriptor.get_ShmemSection();
       MOZ_RELEASE_ASSERT(section.shmem().IsReadable());
       return MakeAndAddRef<ShmemTextureReadLock>(section);
     }
     case ReadLockDescriptor::Tuintptr_t: {
       if (!aAllocator->IsSameProcess()) {
@@ -1564,17 +1561,17 @@ already_AddRefed<TextureReadLock> Textur
         // The corresponding AddRef is in MemoryTextureReadLock::Serialize
         lock.get()->Release();
       }
 
       return lock.forget();
     }
     case ReadLockDescriptor::TCrossProcessSemaphoreDescriptor: {
       return MakeAndAddRef<CrossProcessSemaphoreReadLock>(
-          std::move(aDescriptor.get_CrossProcessSemaphoreDescriptor().sem()));
+          aDescriptor.get_CrossProcessSemaphoreDescriptor().sem());
     }
     case ReadLockDescriptor::Tnull_t: {
       return nullptr;
     }
     default: {
       // Invalid descriptor.
       MOZ_DIAGNOSTIC_ASSERT(false);
     }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -190,17 +190,17 @@ class TextureReadLock {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureReadLock)
 
   virtual bool ReadLock() = 0;
   virtual bool TryReadLock(TimeDuration aTimeout) { return ReadLock(); }
   virtual int32_t ReadUnlock() = 0;
   virtual bool IsValid() const = 0;
 
   static already_AddRefed<TextureReadLock> Deserialize(
-      ReadLockDescriptor&& aDescriptor, ISurfaceAllocator* aAllocator);
+      const ReadLockDescriptor& aDescriptor, ISurfaceAllocator* aAllocator);
 
   virtual bool Serialize(ReadLockDescriptor& aOutput,
                          base::ProcessId aOther) = 0;
 
   enum LockType {
     TYPE_NONBLOCKING_MEMORY,
     TYPE_NONBLOCKING_SHMEM,
     TYPE_CROSS_PROCESS_SEMAPHORE
--- a/gfx/layers/composite/TextureHost.cpp
+++ b/gfx/layers/composite/TextureHost.cpp
@@ -74,18 +74,18 @@ namespace layers {
 class TextureParent : public ParentActor<PTextureParent> {
  public:
   TextureParent(HostIPCAllocator* aAllocator, uint64_t aSerial,
                 const wr::MaybeExternalImageId& aExternalImageId);
 
   virtual ~TextureParent();
 
   bool Init(const SurfaceDescriptor& aSharedData,
-            ReadLockDescriptor&& aReadLock, const LayersBackend& aLayersBackend,
-            const TextureFlags& aFlags);
+            const ReadLockDescriptor& aReadLock,
+            const LayersBackend& aLayersBackend, const TextureFlags& aFlags);
 
   void NotifyNotUsed(uint64_t aTransactionId);
 
   mozilla::ipc::IPCResult RecvRecycleTexture(
       const TextureFlags& aTextureFlags) final;
 
   TextureHost* GetTextureHost() { return mTextureHost; }
 
@@ -110,22 +110,22 @@ static bool WrapWithWebRenderTextureHost
     return false;
   }
   return true;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 PTextureParent* TextureHost::CreateIPDLActor(
     HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
-    ReadLockDescriptor&& aReadLock, LayersBackend aLayersBackend,
+    const ReadLockDescriptor& aReadLock, LayersBackend aLayersBackend,
     TextureFlags aFlags, uint64_t aSerial,
     const wr::MaybeExternalImageId& aExternalImageId) {
   TextureParent* actor =
       new TextureParent(aAllocator, aSerial, aExternalImageId);
-  if (!actor->Init(aSharedData, std::move(aReadLock), aLayersBackend, aFlags)) {
+  if (!actor->Init(aSharedData, aReadLock, aLayersBackend, aFlags)) {
     actor->ActorDestroy(ipc::IProtocol::ActorDestroyReason::FailedConstructor);
     delete actor;
     return nullptr;
   }
   return actor;
 }
 
 // static
@@ -177,17 +177,17 @@ already_AddRefed<TextureHost> CreateDumm
   const MemoryOrShmem& data = bufferDesc.data();
   RefPtr<TextureHost> host =
       new MemoryTextureHost(reinterpret_cast<uint8_t*>(data.get_uintptr_t()),
                             bufferDesc.desc(), aFlags);
   return host.forget();
 }
 
 already_AddRefed<TextureHost> TextureHost::Create(
-    const SurfaceDescriptor& aDesc, ReadLockDescriptor&& aReadLock,
+    const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
     ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
     TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId) {
   RefPtr<TextureHost> result;
 
   switch (aDesc.type()) {
     case SurfaceDescriptor::TSurfaceDescriptorBuffer:
     case SurfaceDescriptor::TSurfaceDescriptorGPUVideo:
       result = CreateBackendIndependentTextureHost(aDesc, aDeallocator,
@@ -220,19 +220,18 @@ already_AddRefed<TextureHost> TextureHos
               ->LookupSurfaceDescriptorForClientTexture(desc.textureId());
       if (!realDesc) {
         gfxCriticalNote << "Failed to get descriptor for recorded texture.";
         // Create a dummy to prevent any crashes due to missing IPDL actors.
         result = CreateDummyBufferTextureHost(aBackend, aFlags);
         break;
       }
 
-      result =
-          TextureHost::Create(*realDesc, std::move(aReadLock), aDeallocator,
-                              aBackend, aFlags, aExternalImageId);
+      result = TextureHost::Create(*realDesc, aReadLock, aDeallocator, aBackend,
+                                   aFlags, aExternalImageId);
       return result.forget();
     }
     default:
       MOZ_CRASH("GFX: Unsupported Surface type host");
   }
 
   if (!result) {
     gfxCriticalNote << "TextureHost creation failure type=" << aDesc.type();
@@ -240,17 +239,17 @@ already_AddRefed<TextureHost> TextureHos
 
   if (result && WrapWithWebRenderTextureHost(aDeallocator, aBackend, aFlags)) {
     MOZ_ASSERT(aExternalImageId.isSome());
     result =
         new WebRenderTextureHost(aDesc, aFlags, result, aExternalImageId.ref());
   }
 
   if (result) {
-    result->DeserializeReadLock(std::move(aReadLock), aDeallocator);
+    result->DeserializeReadLock(aReadLock, aDeallocator);
   }
 
   return result.forget();
 }
 
 already_AddRefed<TextureHost> CreateBackendIndependentTextureHost(
     const SurfaceDescriptor& aDesc, ISurfaceAllocator* aDeallocator,
     LayersBackend aBackend, TextureFlags aFlags) {
@@ -676,23 +675,23 @@ void BufferTextureHost::PushDisplayItems
         aBounds, aClip, true, aImageKeys[0], aImageKeys[1], aImageKeys[2],
         wr::ToWrColorDepth(desc.colorDepth()),
         wr::ToWrYuvColorSpace(desc.yUVColorSpace()),
         wr::ToWrColorRange(desc.colorRange()), aFilter, preferCompositorSurface,
         useExternalSurface);
   }
 }
 
-void TextureHost::DeserializeReadLock(ReadLockDescriptor&& aDesc,
+void TextureHost::DeserializeReadLock(const ReadLockDescriptor& aDesc,
                                       ISurfaceAllocator* aAllocator) {
   if (mReadLock) {
     return;
   }
 
-  mReadLock = TextureReadLock::Deserialize(std::move(aDesc), aAllocator);
+  mReadLock = TextureReadLock::Deserialize(aDesc, aAllocator);
 }
 
 void TextureHost::SetReadLocked() {
   if (!mReadLock) {
     return;
   }
   // If mReadLocked is true it means we haven't read unlocked yet and the
   // content side should not have been able to write into this texture and read
@@ -1220,22 +1219,21 @@ TextureParent::~TextureParent() { MOZ_CO
 void TextureParent::NotifyNotUsed(uint64_t aTransactionId) {
   if (!mTextureHost) {
     return;
   }
   mSurfaceAllocator->NotifyNotUsed(this, aTransactionId);
 }
 
 bool TextureParent::Init(const SurfaceDescriptor& aSharedData,
-                         ReadLockDescriptor&& aReadLock,
+                         const ReadLockDescriptor& aReadLock,
                          const LayersBackend& aBackend,
                          const TextureFlags& aFlags) {
-  mTextureHost =
-      TextureHost::Create(aSharedData, std::move(aReadLock), mSurfaceAllocator,
-                          aBackend, aFlags, mExternalImageId);
+  mTextureHost = TextureHost::Create(aSharedData, aReadLock, mSurfaceAllocator,
+                                     aBackend, aFlags, mExternalImageId);
   if (mTextureHost) {
     mTextureHost->mActor = this;
   }
 
   return !!mTextureHost;
 }
 
 void TextureParent::Destroy() {
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -404,17 +404,17 @@ class TextureHost : public AtomicRefCoun
  protected:
   virtual ~TextureHost();
 
  public:
   /**
    * Factory method.
    */
   static already_AddRefed<TextureHost> Create(
-      const SurfaceDescriptor& aDesc, ReadLockDescriptor&& aReadLock,
+      const SurfaceDescriptor& aDesc, const ReadLockDescriptor& aReadLock,
       ISurfaceAllocator* aDeallocator, LayersBackend aBackend,
       TextureFlags aFlags, wr::MaybeExternalImageId& aExternalImageId);
 
   /**
    * Lock the texture host for compositing.
    */
   virtual bool Lock() { return true; }
   /**
@@ -566,17 +566,17 @@ class TextureHost : public AtomicRefCoun
    *
    * TextureParent< is an implementation detail of TextureHost that is not
    * exposed to the rest of the code base. CreateIPDLActor and DestroyIPDLActor
    * are for use with the managing IPDL protocols only (so that they can
    * implement AllocPTextureParent and DeallocPTextureParent).
    */
   static PTextureParent* CreateIPDLActor(
       HostIPCAllocator* aAllocator, const SurfaceDescriptor& aSharedData,
-      ReadLockDescriptor&& aDescriptor, LayersBackend aLayersBackend,
+      const ReadLockDescriptor& aDescriptor, LayersBackend aLayersBackend,
       TextureFlags aFlags, uint64_t aSerial,
       const wr::MaybeExternalImageId& aExternalImageId);
   static bool DestroyIPDLActor(PTextureParent* actor);
 
   /**
    * Destroy the TextureChild/Parent pair.
    */
   static bool SendDeleteIPDLActor(PTextureParent* actor);
@@ -638,17 +638,17 @@ class TextureHost : public AtomicRefCoun
       NotifyNotUsed();
     }
   }
 
   int NumCompositableRefs() const { return mCompositableCount; }
 
   void SetLastFwdTransactionId(uint64_t aTransactionId);
 
-  void DeserializeReadLock(ReadLockDescriptor&& aDesc,
+  void DeserializeReadLock(const ReadLockDescriptor& aDesc,
                            ISurfaceAllocator* aAllocator);
   void SetReadLocked();
 
   TextureReadLock* GetReadLock() { return mReadLock; }
 
   virtual BufferTextureHost* AsBufferTextureHost() { return nullptr; }
   virtual MacIOSurfaceTextureHostOGL* AsMacIOSurfaceTextureHost() {
     return nullptr;
--- a/gfx/layers/ipc/CanvasChild.cpp
+++ b/gfx/layers/ipc/CanvasChild.cpp
@@ -144,18 +144,17 @@ void CanvasChild::EnsureRecorder(Texture
     CrossProcessSemaphoreHandle writerSem;
     if (!mRecorder->Init(OtherPid(), &handle, &readerSem, &writerSem,
                          MakeUnique<RingBufferWriterServices>(this))) {
       mRecorder = nullptr;
       return;
     }
 
     if (CanSend()) {
-      Unused << SendInitTranslator(mTextureType, std::move(handle),
-                                   std::move(readerSem), std::move(writerSem));
+      Unused << SendInitTranslator(mTextureType, handle, readerSem, writerSem);
     }
   }
 
   MOZ_RELEASE_ASSERT(mTextureType == aTextureType,
                      "We only support one remote TextureType currently.");
 }
 
 void CanvasChild::ActorDestroy(ActorDestroyReason aWhy) {
--- a/gfx/layers/ipc/CanvasTranslator.cpp
+++ b/gfx/layers/ipc/CanvasTranslator.cpp
@@ -122,26 +122,25 @@ void CanvasTranslator::Bind(Endpoint<PCa
     return;
   }
 
   CanvasTranslators().Insert(this);
 }
 
 mozilla::ipc::IPCResult CanvasTranslator::RecvInitTranslator(
     const TextureType& aTextureType,
-    ipc::SharedMemoryBasic::Handle&& aReadHandle,
-    CrossProcessSemaphoreHandle&& aReaderSem,
-    CrossProcessSemaphoreHandle&& aWriterSem) {
+    const ipc::SharedMemoryBasic::Handle& aReadHandle,
+    const CrossProcessSemaphoreHandle& aReaderSem,
+    const CrossProcessSemaphoreHandle& aWriterSem) {
   mTextureType = aTextureType;
 
   // We need to initialize the stream first, because it might be used to
   // communicate other failures back to the writer.
   mStream = MakeUnique<CanvasEventRingBuffer>();
-  if (!mStream->InitReader(std::move(aReadHandle), std::move(aReaderSem),
-                           std::move(aWriterSem),
+  if (!mStream->InitReader(aReadHandle, aReaderSem, aWriterSem,
                            MakeUnique<RingBufferReaderServices>(this))) {
     return IPC_FAIL(this, "Failed to initialize ring buffer reader.");
   }
 
 #if defined(XP_WIN)
   if (!CheckForFreshCanvasDevice(__LINE__)) {
     gfxCriticalNote << "GFX: CanvasTranslator failed to get device";
     return IPC_OK();
--- a/gfx/layers/ipc/CanvasTranslator.h
+++ b/gfx/layers/ipc/CanvasTranslator.h
@@ -53,19 +53,19 @@ class CanvasTranslator final : public gf
    * @param aTextureType the TextureType the translator will create
    * @param aReadHandle handle to the shared memory for the
    *        CanvasEventRingBuffer
    * @param aReaderSem reading blocked semaphore for the CanvasEventRingBuffer
    * @param aWriterSem writing blocked semaphore for the CanvasEventRingBuffer
    */
   ipc::IPCResult RecvInitTranslator(
       const TextureType& aTextureType,
-      ipc::SharedMemoryBasic::Handle&& aReadHandle,
-      CrossProcessSemaphoreHandle&& aReaderSem,
-      CrossProcessSemaphoreHandle&& aWriterSem);
+      const ipc::SharedMemoryBasic::Handle& aReadHandle,
+      const CrossProcessSemaphoreHandle& aReaderSem,
+      const CrossProcessSemaphoreHandle& aWriterSem);
 
   /**
    * Used to tell the CanvasTranslator to start translating again after it has
    * stopped due to a timeout waiting for events.
    */
   ipc::IPCResult RecvResumeTranslation();
 
   void ActorDestroy(ActorDestroyReason why) final;
--- a/gfx/layers/ipc/CompositorBridgeChild.cpp
+++ b/gfx/layers/ipc/CompositorBridgeChild.cpp
@@ -418,17 +418,17 @@ bool CompositorBridgeChild::SendStopFram
   if (!mCanSend) {
     return false;
   }
   return PCompositorBridgeChild::SendStopFrameTimeRecording(startIndex,
                                                             intervals);
 }
 
 PTextureChild* CompositorBridgeChild::AllocPTextureChild(
-    const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
+    const SurfaceDescriptor&, const ReadLockDescriptor&, const LayersBackend&,
     const TextureFlags&, const LayersId&, const uint64_t& aSerial,
     const wr::MaybeExternalImageId& aExternalImageId) {
   return TextureClient::CreateIPDLActor();
 }
 
 bool CompositorBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
   return TextureClient::DestroyIPDLActor(actor);
 }
@@ -550,30 +550,30 @@ CompositorBridgeChild::GetTileLockAlloca
 
   if (!mSectionAllocator) {
     mSectionAllocator = new FixedSizeSmallShmemSectionAllocator(this);
   }
   return mSectionAllocator;
 }
 
 PTextureChild* CompositorBridgeChild::CreateTexture(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
     wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
   PTextureChild* textureChild =
       AllocPTextureChild(aSharedData, aReadLock, aLayersBackend, aFlags,
                          LayersId{0} /* FIXME */, aSerial, aExternalImageId);
 
   // Do the DOM labeling.
   if (aTarget) {
     SetEventTargetForActor(textureChild, aTarget);
   }
 
   return SendPTextureConstructor(
-      textureChild, aSharedData, std::move(aReadLock), aLayersBackend, aFlags,
+      textureChild, aSharedData, aReadLock, aLayersBackend, aFlags,
       LayersId{0} /* FIXME? */, aSerial, aExternalImageId);
 }
 
 already_AddRefed<CanvasChild> CompositorBridgeChild::GetCanvasChild() {
   MOZ_ASSERT(gfx::gfxVars::RemoteCanvasEnabled());
 
   if (CanvasChild::Deactivated()) {
     return nullptr;
--- a/gfx/layers/ipc/CompositorBridgeChild.h
+++ b/gfx/layers/ipc/CompositorBridgeChild.h
@@ -88,27 +88,27 @@ class CompositorBridgeChild final : publ
       nsTArray<FrameStats>&& aFrameStats);
 
   mozilla::ipc::IPCResult RecvInvalidateLayers(const LayersId& aLayersId);
 
   mozilla::ipc::IPCResult RecvNotifyJankedAnimations(
       const LayersId& aLayersId, nsTArray<uint64_t>&& aJankedAnimations);
 
   PTextureChild* AllocPTextureChild(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
       const LayersId& aId, const uint64_t& aSerial,
       const wr::MaybeExternalImageId& aExternalImageId);
 
   bool DeallocPTextureChild(PTextureChild* actor);
 
   mozilla::ipc::IPCResult RecvParentAsyncMessages(
       nsTArray<AsyncParentMessageData>&& aMessages);
   PTextureChild* CreateTexture(const SurfaceDescriptor& aSharedData,
-                               ReadLockDescriptor&& aReadLock,
+                               const ReadLockDescriptor& aReadLock,
                                LayersBackend aLayersBackend,
                                TextureFlags aFlags, uint64_t aSerial,
                                wr::MaybeExternalImageId& aExternalImageId,
                                nsISerialEventTarget* aTarget) override;
 
   already_AddRefed<CanvasChild> GetCanvasChild() final;
 
   void EndCanvasTransaction();
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -1872,21 +1872,21 @@ CompositorBridgeParent::GetGeckoContentC
     LayersId aContentLayersId) {
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   CompositorBridgeParent::LayerTreeState* state =
       GetStateForRoot(aContentLayersId, lock);
   return state ? state->mController.get() : nullptr;
 }
 
 PTextureParent* CompositorBridgeParent::AllocPTextureParent(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
     const LayersId& aId, const uint64_t& aSerial,
     const wr::MaybeExternalImageId& aExternalImageId) {
-  return TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock),
+  return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
                                       aLayersBackend, aFlags, aSerial,
                                       aExternalImageId);
 }
 
 bool CompositorBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
   return TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -190,17 +190,17 @@ class CompositorBridgeParentBase : publi
   virtual bool DeallocPAPZParent(PAPZParent* aActor) = 0;
 
   virtual PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
       const LayersId& layersId) = 0;
   virtual bool DeallocPAPZCTreeManagerParent(
       PAPZCTreeManagerParent* aActor) = 0;
 
   virtual PTextureParent* AllocPTextureParent(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aBackend, const TextureFlags& aTextureFlags,
       const LayersId& id, const uint64_t& aSerial,
       const MaybeExternalImageId& aExternalImageId) = 0;
   virtual bool DeallocPTextureParent(PTextureParent* aActor) = 0;
 
   virtual PWebRenderBridgeParent* AllocPWebRenderBridgeParent(
       const PipelineId& pipelineId, const LayoutDeviceIntSize& aSize,
       const WindowKind& aWindowKind) = 0;
@@ -355,17 +355,17 @@ class CompositorBridgeParent final : pub
   void GetFrameUniformity(const LayersId& aLayersId,
                           FrameUniformityData* data) override;
   void SetConfirmedTargetAPZC(
       const LayersId& aLayersId, const uint64_t& aInputBlockId,
       nsTArray<ScrollableLayerGuid>&& aTargets) override;
   void SetFixedLayerMargins(ScreenIntCoord aTop, ScreenIntCoord aBottom);
 
   PTextureParent* AllocPTextureParent(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
       const LayersId& aId, const uint64_t& aSerial,
       const wr::MaybeExternalImageId& aExternalImageId) override;
   bool DeallocPTextureParent(PTextureParent* actor) override;
 
   mozilla::ipc::IPCResult RecvInitPCanvasParent(
       Endpoint<PCanvasParent>&& aEndpoint) final;
 
--- a/gfx/layers/ipc/CompositorManagerParent.cpp
+++ b/gfx/layers/ipc/CompositorManagerParent.cpp
@@ -257,18 +257,18 @@ CompositorManagerParent::AllocPComposito
     default:
       break;
   }
 
   return nullptr;
 }
 
 mozilla::ipc::IPCResult CompositorManagerParent::RecvAddSharedSurface(
-    const wr::ExternalImageId& aId, SurfaceDescriptorShared&& aDesc) {
-  SharedSurfacesParent::Add(aId, std::move(aDesc), OtherPid());
+    const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc) {
+  SharedSurfacesParent::Add(aId, aDesc, OtherPid());
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult CompositorManagerParent::RecvRemoveSharedSurface(
     const wr::ExternalImageId& aId) {
   SharedSurfacesParent::Remove(aId);
   return IPC_OK();
 }
--- a/gfx/layers/ipc/CompositorManagerParent.h
+++ b/gfx/layers/ipc/CompositorManagerParent.h
@@ -35,18 +35,18 @@ class CompositorManagerParent final : pu
   static void Shutdown();
 
   static already_AddRefed<CompositorBridgeParent>
   CreateSameProcessWidgetCompositorBridge(CSSToLayoutDeviceScale aScale,
                                           const CompositorOptions& aOptions,
                                           bool aUseExternalSurfaceSize,
                                           const gfx::IntSize& aSurfaceSize);
 
-  mozilla::ipc::IPCResult RecvAddSharedSurface(const wr::ExternalImageId& aId,
-                                               SurfaceDescriptorShared&& aDesc);
+  mozilla::ipc::IPCResult RecvAddSharedSurface(
+      const wr::ExternalImageId& aId, const SurfaceDescriptorShared& aDesc);
   mozilla::ipc::IPCResult RecvRemoveSharedSurface(
       const wr::ExternalImageId& aId);
   mozilla::ipc::IPCResult RecvReportSharedSurfacesMemory(
       ReportSharedSurfacesMemoryResolver&&);
 
   mozilla::ipc::IPCResult RecvNotifyMemoryPressure();
 
   mozilla::ipc::IPCResult RecvReportMemory(ReportMemoryResolver&&);
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -376,17 +376,17 @@ void ContentCompositorBridgeParent::SetC
 
 void ContentCompositorBridgeParent::DeferredDestroy() { mSelfRef = nullptr; }
 
 ContentCompositorBridgeParent::~ContentCompositorBridgeParent() {
   MOZ_ASSERT(XRE_GetIOMessageLoop());
 }
 
 PTextureParent* ContentCompositorBridgeParent::AllocPTextureParent(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
     const LayersId& aId, const uint64_t& aSerial,
     const wr::MaybeExternalImageId& aExternalImageId) {
   CompositorBridgeParent::LayerTreeState* state = nullptr;
 
   LayerTreeMap::iterator itr = sIndirectLayerTrees.find(aId);
   if (sIndirectLayerTrees.end() != itr) {
     state = &itr->second;
@@ -403,17 +403,17 @@ PTextureParent* ContentCompositorBridgeP
     // TextureHost should not attempt to access the compositor.
     flags |= TextureFlags::INVALID_COMPOSITOR;
   } else if (actualBackend != LayersBackend::LAYERS_NONE &&
              aLayersBackend != actualBackend) {
     gfxDevCrash(gfx::LogReason::PAllocTextureBackendMismatch)
         << "Texture backend is wrong";
   }
 
-  return TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock),
+  return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
                                       aLayersBackend, aFlags, aSerial,
                                       aExternalImageId);
 }
 
 bool ContentCompositorBridgeParent::DeallocPTextureParent(
     PTextureParent* actor) {
   return TextureHost::DestroyIPDLActor(actor);
 }
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.h
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h
@@ -125,17 +125,17 @@ class ContentCompositorBridgeParent fina
 
   // Use DidCompositeLocked if you already hold a lock on
   // sIndirectLayerTreesLock; Otherwise use DidComposite, which would request
   // the lock automatically.
   void DidCompositeLocked(LayersId aId, const VsyncId& aVsyncId,
                           TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
 
   PTextureParent* AllocPTextureParent(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
       const LayersId& aId, const uint64_t& aSerial,
       const wr::MaybeExternalImageId& aExternalImageId) override;
 
   bool DeallocPTextureParent(PTextureParent* actor) override;
 
   mozilla::ipc::IPCResult RecvInitPCanvasParent(
       Endpoint<PCanvasParent>&& aEndpoint) final;
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -15,36 +15,46 @@ namespace mozilla {
 namespace layers {
 
 NS_IMPL_ISUPPORTS(GfxMemoryImageReporter, nsIMemoryReporter)
 
 mozilla::Atomic<ptrdiff_t> GfxMemoryImageReporter::sAmount(0);
 
 /* static */
 uint32_t CompositableForwarder::GetMaxFileDescriptorsPerMessage() {
+#if defined(OS_POSIX)
   static const uint32_t kMaxFileDescriptors =
-      IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE;
+      FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+  // default number that works everywhere else
+  static const uint32_t kMaxFileDescriptors = 250;
+#endif
   return kMaxFileDescriptors;
 }
 
 mozilla::ipc::SharedMemory::SharedMemoryType OptimalShmemType() {
   return ipc::SharedMemory::SharedMemoryType::TYPE_BASIC;
 }
 
 void HostIPCAllocator::SendPendingAsyncMessages() {
   if (mPendingAsyncMessage.empty()) {
     return;
   }
 
   // Some type of AsyncParentMessageData message could have
   // one file descriptor (e.g. OpDeliverFence).
   // A number of file descriptors per gecko ipc message have a limitation
   // on OS_POSIX (MACOSX or LINUX).
+#if defined(OS_POSIX)
   static const uint32_t kMaxMessageNumber =
-      IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE;
+      FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE;
+#else
+  // default number that works everywhere else
+  static const uint32_t kMaxMessageNumber = 250;
+#endif
 
   nsTArray<AsyncParentMessageData> messages;
   messages.SetCapacity(mPendingAsyncMessage.size());
   for (size_t i = 0; i < mPendingAsyncMessage.size(); i++) {
     messages.AppendElement(mPendingAsyncMessage[i]);
     // Limit maximum number of messages.
     if (messages.Length() >= kMaxMessageNumber) {
       SendAsyncMessage(messages);
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -781,17 +781,17 @@ bool ImageBridgeChild::DeallocShmem(ipc:
       &task, &aShmem, &result);
   GetThread()->Dispatch(runnable.forget());
 
   task.Wait();
   return result;
 }
 
 PTextureChild* ImageBridgeChild::AllocPTextureChild(
-    const SurfaceDescriptor&, ReadLockDescriptor&, const LayersBackend&,
+    const SurfaceDescriptor&, const ReadLockDescriptor&, const LayersBackend&,
     const TextureFlags&, const uint64_t& aSerial,
     const wr::MaybeExternalImageId& aExternalImageId) {
   MOZ_ASSERT(CanSend());
   return TextureClient::CreateIPDLActor();
 }
 
 bool ImageBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
   return TextureClient::DestroyIPDLActor(actor);
@@ -872,23 +872,22 @@ mozilla::ipc::IPCResult ImageBridgeChild
   if (listener) {
     listener->NotifyDropped(aFrames);
   }
 
   return IPC_OK();
 }
 
 PTextureChild* ImageBridgeChild::CreateTexture(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
     wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
   MOZ_ASSERT(CanSend());
-  return SendPTextureConstructor(aSharedData, std::move(aReadLock),
-                                 aLayersBackend, aFlags, aSerial,
-                                 aExternalImageId);
+  return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags,
+                                 aSerial, aExternalImageId);
 }
 
 static bool IBCAddOpDestroy(CompositableTransaction* aTxn,
                             const OpDestroy& op) {
   if (aTxn->Finished()) {
     return false;
   }
 
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -159,17 +159,17 @@ class ImageBridgeChild final : public PI
    *
    * Can be called from any thread.
    */
   nsISerialEventTarget* GetThread() const override;
 
   base::ProcessId GetParentPid() const override { return OtherPid(); }
 
   PTextureChild* AllocPTextureChild(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
       const uint64_t& aSerial,
       const wr::MaybeExternalImageId& aExternalImageId);
 
   bool DeallocPTextureChild(PTextureChild* actor);
 
   PMediaSystemResourceManagerChild* AllocPMediaSystemResourceManagerChild();
   bool DeallocPMediaSystemResourceManagerChild(
@@ -294,17 +294,17 @@ class ImageBridgeChild final : public PI
    * See ISurfaceAllocator.h
    * Can be used from any thread.
    * If used outside the ImageBridgeChild thread, it will proxy a synchronous
    * call on the ImageBridgeChild thread.
    */
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   PTextureChild* CreateTexture(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
       wr::MaybeExternalImageId& aExternalImageId,
       nsISerialEventTarget* aTarget = nullptr) override;
 
   bool IsSameProcess() const override;
 
   void UpdateFwdTransactionId() override { ++mFwdTransactionId; }
   uint64_t GetFwdTransactionId() override { return mFwdTransactionId; }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -284,20 +284,20 @@ mozilla::ipc::IPCResult ImageBridgeParen
 
 mozilla::ipc::IPCResult ImageBridgeParent::RecvReleaseCompositable(
     const CompositableHandle& aHandle) {
   ReleaseCompositable(aHandle);
   return IPC_OK();
 }
 
 PTextureParent* ImageBridgeParent::AllocPTextureParent(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
     const uint64_t& aSerial, const wr::MaybeExternalImageId& aExternalImageId) {
-  return TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock),
+  return TextureHost::CreateIPDLActor(this, aSharedData, aReadLock,
                                       aLayersBackend, aFlags, aSerial,
                                       aExternalImageId);
 }
 
 bool ImageBridgeParent::DeallocPTextureParent(PTextureParent* actor) {
   return TextureHost::DestroyIPDLActor(actor);
 }
 
--- a/gfx/layers/ipc/ImageBridgeParent.h
+++ b/gfx/layers/ipc/ImageBridgeParent.h
@@ -75,17 +75,17 @@ class ImageBridgeParent final : public P
   base::ProcessId GetChildProcessId() override { return OtherPid(); }
 
   // PImageBridge
   mozilla::ipc::IPCResult RecvUpdate(EditArray&& aEdits,
                                      OpDestroyArray&& aToDestroy,
                                      const uint64_t& aFwdTransactionId);
 
   PTextureParent* AllocPTextureParent(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
       const uint64_t& aSerial,
       const wr::MaybeExternalImageId& aExternalImageId);
   bool DeallocPTextureParent(PTextureParent* actor);
 
   mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                               const TextureInfo& aInfo);
   mozilla::ipc::IPCResult RecvReleaseCompositable(
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -49,17 +49,17 @@ using mozilla::layers::ScaleMode from "m
 using mozilla::layers::EventRegions from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::EventRegionsOverride from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::DiagnosticTypes from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositionPayload from "mozilla/layers/LayersTypes.h";
-[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
 using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
 using mozilla::LengthPercentage from "mozilla/ServoStyleConsts.h";
 using mozilla::RayReferenceData from "mozilla/MotionPathUtils.h";
 using mozilla::StyleOffsetPath from "mozilla/ServoStyleConsts.h";
 using mozilla::StyleOffsetRotate from "mozilla/ServoStyleConsts.h";
 using mozilla::StylePositionOrAuto from "mozilla/ServoStyleConsts.h";
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -10,17 +10,17 @@ using mozilla::StereoMode from "ImageTyp
 using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
 using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
 using mozilla::gfx::YUVColorSpace from "mozilla/gfx/Types.h";
 using mozilla::gfx::ColorDepth from "mozilla/gfx/Types.h";
 using mozilla::gfx::ColorRange from "mozilla/gfx/Types.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
-[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using gfxImageFormat from "gfxTypes.h";
 using mozilla::layers::MaybeVideoBridgeSource from "mozilla/layers/VideoBridgeUtils.h";
 
 namespace mozilla {
 namespace layers {
 
 [Comparable] struct SurfaceDescriptorD3D10 {
   WindowsHandle handle;
--- a/gfx/layers/ipc/PCanvas.ipdl
+++ b/gfx/layers/ipc/PCanvas.ipdl
@@ -1,19 +1,19 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et : */
 /* 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 "mozilla/layers/LayersMessageUtils.h";
 
-[MoveOnly] using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
+using mozilla::CrossProcessSemaphoreHandle from "mozilla/ipc/CrossProcessSemaphore.h";
 using mozilla::layers::TextureType from "mozilla/layers/LayersTypes.h";
-[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 
 namespace mozilla {
 namespace layers {
 
 /**
  * PCanvas is the IPDL for recorded Canvas drawing.
  */
 [RefCounted] async protocol PCanvas {
--- a/gfx/layers/ipc/PCompositorManager.ipdl
+++ b/gfx/layers/ipc/PCompositorManager.ipdl
@@ -10,17 +10,17 @@ include protocol PCompositorBridge;
 include LayersSurfaces;
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/WebRenderMessageUtils.h";
 
 using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
 using mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using mozilla::CSSToLayoutDeviceScale from "Units.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
-[MoveOnly] using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
+using mozilla::ipc::SharedMemoryBasic::Handle from "mozilla/ipc/SharedMemoryBasic.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/LayersMessageUtils.h";
 using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::MemoryReport from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::WebRenderError from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::layers::SharedSurfacesMemoryReport from "mozilla/layers/SharedSurfacesMemoryReport.h";
 
 namespace mozilla {
 namespace layers {
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -239,19 +239,18 @@ nsresult SharedSurfacesChild::ShareInter
 
   SurfaceFormat format = aSurface->GetFormat();
   MOZ_RELEASE_ASSERT(
       format == SurfaceFormat::B8G8R8X8 || format == SurfaceFormat::B8G8R8A8,
       "bad format");
 
   data->MarkShared();
   manager->SendAddSharedSurface(
-      data->Id(),
-      SurfaceDescriptorShared(aSurface->GetSize(), aSurface->Stride(), format,
-                              std::move(handle)));
+      data->Id(), SurfaceDescriptorShared(aSurface->GetSize(),
+                                          aSurface->Stride(), format, handle));
   *aUserData = data;
   return NS_OK;
 }
 
 /* static */
 void SharedSurfacesChild::Share(SourceSurfaceSharedData* aSurface) {
   MOZ_ASSERT(aSurface);
 
--- a/gfx/layers/ipc/SharedSurfacesParent.cpp
+++ b/gfx/layers/ipc/SharedSurfacesParent.cpp
@@ -192,33 +192,33 @@ void SharedSurfacesParent::DestroyProces
       wr::RenderThread::Get()->UnregisterExternalImage(i.Key());
       i.Remove();
     }
   }
 }
 
 /* static */
 void SharedSurfacesParent::Add(const wr::ExternalImageId& aId,
-                               SurfaceDescriptorShared&& aDesc,
+                               const SurfaceDescriptorShared& aDesc,
                                base::ProcessId aPid) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(aPid != base::GetCurrentProcId());
 
   RefPtr<SourceSurfaceSharedDataWrapper> surface =
       new SourceSurfaceSharedDataWrapper();
 
   // We preferentially map in new surfaces when they are initially received
   // because we are likely to reference them in a display list soon. The unmap
   // will ensure we add the surface to the expiration tracker. We do it outside
   // the mutex to ensure we always lock the surface mutex first, and our mutex
   // second, to avoid deadlock.
   //
   // Note that the surface wrapper maps in the given handle as read only.
-  surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(),
-                std::move(aDesc.handle()), aPid);
+  surface->Init(aDesc.size(), aDesc.stride(), aDesc.format(), aDesc.handle(),
+                aPid);
 
   StaticMutexAutoLock lock(sMutex);
   if (!sInstance) {
     gfxCriticalNote << "SSP:Add " << wr::AsUint64(aId) << " shtd";
     return;
   }
 
   uint64_t id = wr::AsUint64(aId);
--- a/gfx/layers/ipc/SharedSurfacesParent.h
+++ b/gfx/layers/ipc/SharedSurfacesParent.h
@@ -43,17 +43,17 @@ class SharedSurfacesParent final {
 
   // Get but also increase the consumer count. Must call Release after finished.
   static already_AddRefed<gfx::DataSourceSurface> Acquire(
       const wr::ExternalImageId& aId);
 
   static bool Release(const wr::ExternalImageId& aId, bool aForCreator = false);
 
   static void Add(const wr::ExternalImageId& aId,
-                  SurfaceDescriptorShared&& aDesc, base::ProcessId aPid);
+                  const SurfaceDescriptorShared& aDesc, base::ProcessId aPid);
 
   static void Remove(const wr::ExternalImageId& aId);
 
   static void DestroyProcess(base::ProcessId aPid);
 
   static void AccumulateMemoryReport(base::ProcessId aPid,
                                      SharedSurfacesMemoryReport& aReport);
 
--- a/gfx/layers/ipc/TextureForwarder.h
+++ b/gfx/layers/ipc/TextureForwarder.h
@@ -69,17 +69,17 @@ class LayersIPCChannel : public LayersIP
  */
 class TextureForwarder : public LayersIPCChannel {
  public:
   /**
    * Create a TextureChild/Parent pair as as well as the TextureHost on the
    * parent side.
    */
   virtual PTextureChild* CreateTexture(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
       wr::MaybeExternalImageId& aExternalImageId,
       nsISerialEventTarget* aTarget = nullptr) = 0;
 
   /**
    * Returns the CanvasChild for this TextureForwarder.
    */
   virtual already_AddRefed<CanvasChild> GetCanvasChild() { return nullptr; };
--- a/gfx/layers/ipc/VideoBridgeChild.cpp
+++ b/gfx/layers/ipc/VideoBridgeChild.cpp
@@ -140,17 +140,17 @@ bool VideoBridgeChild::DeallocShmem(ipc:
       &task, &aShmem, &result);
   GetThread()->Dispatch(runnable.forget());
 
   task.Wait();
   return result;
 }
 
 PTextureChild* VideoBridgeChild::AllocPTextureChild(const SurfaceDescriptor&,
-                                                    ReadLockDescriptor&,
+                                                    const ReadLockDescriptor&,
                                                     const LayersBackend&,
                                                     const TextureFlags&,
                                                     const uint64_t& aSerial) {
   MOZ_ASSERT(CanSend());
   return TextureClient::CreateIPDLActor();
 }
 
 bool VideoBridgeChild::DeallocPTextureChild(PTextureChild* actor) {
@@ -159,22 +159,22 @@ bool VideoBridgeChild::DeallocPTextureCh
 
 void VideoBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
   mCanSend = false;
 }
 
 void VideoBridgeChild::ActorDealloc() { mIPDLSelfRef = nullptr; }
 
 PTextureChild* VideoBridgeChild::CreateTexture(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
     wr::MaybeExternalImageId& aExternalImageId, nsISerialEventTarget* aTarget) {
   MOZ_ASSERT(CanSend());
-  return SendPTextureConstructor(aSharedData, std::move(aReadLock),
-                                 aLayersBackend, aFlags, aSerial);
+  return SendPTextureConstructor(aSharedData, aReadLock, aLayersBackend, aFlags,
+                                 aSerial);
 }
 
 bool VideoBridgeChild::IsSameProcess() const {
   return OtherPid() == base::GetCurrentProcId();
 }
 
 void VideoBridgeChild::HandleFatalError(const char* aMsg) const {
   dom::ContentChild::FatalErrorIfNotUsingGPUProcess(aMsg, OtherPid());
--- a/gfx/layers/ipc/VideoBridgeChild.h
+++ b/gfx/layers/ipc/VideoBridgeChild.h
@@ -24,17 +24,17 @@ class VideoBridgeChild final : public PV
 
   static void StartupForGPUProcess();
   static void Shutdown();
 
   static VideoBridgeChild* GetSingleton();
 
   // PVideoBridgeChild
   PTextureChild* AllocPTextureChild(const SurfaceDescriptor& aSharedData,
-                                    ReadLockDescriptor& aReadLock,
+                                    const ReadLockDescriptor& aReadLock,
                                     const LayersBackend& aLayersBackend,
                                     const TextureFlags& aFlags,
                                     const uint64_t& aSerial);
   bool DeallocPTextureChild(PTextureChild* actor);
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   void ActorDealloc() override;
 
@@ -44,17 +44,17 @@ class VideoBridgeChild final : public PV
                         mozilla::ipc::Shmem* aShmem) override;
   bool AllocShmem(size_t aSize,
                   mozilla::ipc::SharedMemory::SharedMemoryType aShmType,
                   mozilla::ipc::Shmem* aShmem) override;
   bool DeallocShmem(mozilla::ipc::Shmem& aShmem) override;
 
   // TextureForwarder
   PTextureChild* CreateTexture(
-      const SurfaceDescriptor& aSharedData, ReadLockDescriptor&& aReadLock,
+      const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       LayersBackend aLayersBackend, TextureFlags aFlags, uint64_t aSerial,
       wr::MaybeExternalImageId& aExternalImageId,
       nsISerialEventTarget* aTarget = nullptr) override;
 
   // ClientIPCAllocator
   base::ProcessId GetParentPid() const override { return OtherPid(); }
   nsISerialEventTarget* GetThread() const override { return mThread; }
   void CancelWaitForNotifyNotUsed(uint64_t aTextureId) override {
--- a/gfx/layers/ipc/VideoBridgeParent.cpp
+++ b/gfx/layers/ipc/VideoBridgeParent.cpp
@@ -104,22 +104,21 @@ void VideoBridgeParent::ReleaseComposito
 
 void VideoBridgeParent::ActorDealloc() {
   mCompositorThreadHolder = nullptr;
   ReleaseCompositorThread();
   mSelfRef = nullptr;
 }
 
 PTextureParent* VideoBridgeParent::AllocPTextureParent(
-    const SurfaceDescriptor& aSharedData, ReadLockDescriptor& aReadLock,
+    const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
     const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
     const uint64_t& aSerial) {
-  PTextureParent* parent =
-      TextureHost::CreateIPDLActor(this, aSharedData, std::move(aReadLock),
-                                   aLayersBackend, aFlags, aSerial, Nothing());
+  PTextureParent* parent = TextureHost::CreateIPDLActor(
+      this, aSharedData, aReadLock, aLayersBackend, aFlags, aSerial, Nothing());
 
   if (!parent) {
     return nullptr;
   }
 
   mTextureMap[aSerial] = parent;
   return parent;
 }
--- a/gfx/layers/ipc/VideoBridgeParent.h
+++ b/gfx/layers/ipc/VideoBridgeParent.h
@@ -29,17 +29,17 @@ class VideoBridgeParent final : public P
                    VideoBridgeSource aSource);
   static void Shutdown();
 
   TextureHost* LookupTexture(uint64_t aSerial);
 
   // PVideoBridgeParent
   void ActorDestroy(ActorDestroyReason aWhy) override;
   PTextureParent* AllocPTextureParent(const SurfaceDescriptor& aSharedData,
-                                      ReadLockDescriptor& aReadLock,
+                                      const ReadLockDescriptor& aReadLock,
                                       const LayersBackend& aLayersBackend,
                                       const TextureFlags& aFlags,
                                       const uint64_t& aSerial);
   bool DeallocPTextureParent(PTextureParent* actor);
 
   // HostIPCAllocator
   base::ProcessId GetChildProcessId() override { return OtherPid(); }
   void NotifyNotUsed(PTextureParent* aTexture,
--- a/gfx/thebes/SharedFontList-impl.h
+++ b/gfx/thebes/SharedFontList-impl.h
@@ -247,20 +247,18 @@ class FontList {
    * list has changed/grown since the child was first initialized).
    */
   void ShareShmBlockToProcess(uint32_t aIndex, base::ProcessId aPid,
                               base::SharedMemoryHandle* aOut) {
     MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
     if (aIndex >= mReadOnlyShmems.Length()) {
       // Block index out of range
       *aOut = base::SharedMemory::NULLHandle();
-      return;
     }
-    *aOut = mReadOnlyShmems[aIndex]->CloneHandle();
-    if (!*aOut) {
+    if (!mReadOnlyShmems[aIndex]->ShareToProcess(aPid, aOut)) {
       MOZ_CRASH("failed to share block");
     }
   }
 
   /**
    * Collect an array of handles to all the shmem blocks, ready to be
    * shared to the given process. This is used at child process startup
    * to pass the complete list at once.
--- a/gfx/thebes/SharedFontList.cpp
+++ b/gfx/thebes/SharedFontList.cpp
@@ -644,23 +644,23 @@ FontList::FontList(uint32_t aGeneration)
       header.mLocalFaces = Pointer::Null();
     } else {
       MOZ_CRASH("parent: failed to initialize FontList");
     }
   } else {
     // Initialize using the list of shmem blocks passed by the parent via
     // SetXPCOMProcessAttributes.
     auto& blocks = dom::ContentChild::GetSingleton()->SharedFontListBlocks();
-    for (auto& handle : blocks) {
+    for (auto handle : blocks) {
       auto newShm = MakeUnique<base::SharedMemory>();
       if (!newShm->IsHandleValid(handle)) {
         // Bail out and let UpdateShmBlocks try to do its thing below.
         break;
       }
-      if (!newShm->SetHandle(std::move(handle), true)) {
+      if (!newShm->SetHandle(handle, true)) {
         MOZ_CRASH("failed to set shm handle");
       }
       if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) {
         MOZ_CRASH("failed to map shared memory");
       }
       uint32_t size = static_cast<BlockHeader*>(newShm->memory())->mBlockSize;
       MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
       if (size != SHM_BLOCK_SIZE) {
@@ -741,17 +741,17 @@ void FontList::ShmBlockAdded(uint32_t aG
                              base::SharedMemoryHandle aHandle) {
   MOZ_ASSERT(!XRE_IsParentProcess());
   MOZ_ASSERT(mBlocks.Length() > 0);
 
   auto newShm = MakeUnique<base::SharedMemory>();
   if (!newShm->IsHandleValid(aHandle)) {
     return;
   }
-  if (!newShm->SetHandle(std::move(aHandle), true)) {
+  if (!newShm->SetHandle(aHandle, true)) {
     MOZ_CRASH("failed to set shm handle");
   }
 
   if (aIndex != mBlocks.Length()) {
     return;
   }
   if (aGeneration != GetGeneration()) {
     return;
@@ -790,17 +790,17 @@ FontList::ShmBlock* FontList::GetBlockFr
   if (!dom::ContentChild::GetSingleton()->SendGetFontListShmBlock(
           generation, aIndex, &handle)) {
     return nullptr;
   }
   auto newShm = MakeUnique<base::SharedMemory>();
   if (!newShm->IsHandleValid(handle)) {
     return nullptr;
   }
-  if (!newShm->SetHandle(std::move(handle), true)) {
+  if (!newShm->SetHandle(handle, true)) {
     MOZ_CRASH("failed to set shm handle");
   }
   if (!newShm->Map(SHM_BLOCK_SIZE) || !newShm->memory()) {
     MOZ_CRASH("failed to map shared memory");
   }
   uint32_t size = static_cast<BlockHeader*>(newShm->memory())->mBlockSize;
   MOZ_ASSERT(size >= SHM_BLOCK_SIZE);
   if (size != SHM_BLOCK_SIZE) {
@@ -823,36 +823,41 @@ bool FontList::UpdateShmBlocks() {
   }
   return true;
 }
 
 void FontList::ShareBlocksToProcess(nsTArray<base::SharedMemoryHandle>* aBlocks,
                                     base::ProcessId aPid) {
   MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
   for (auto& shmem : mReadOnlyShmems) {
-    auto handle = shmem->CloneHandle();
-    if (!handle) {
+    base::SharedMemoryHandle* handle =
+        aBlocks->AppendElement(base::SharedMemory::NULLHandle());
+    if (!shmem->ShareToProcess(aPid, handle)) {
       // If something went wrong here, we just bail out; the child will need to
       // request the blocks as needed, at some performance cost. (Although in
       // practice this may mean resources are so constrained the child process
       // isn't really going to work at all. But that's not our problem here.)
       aBlocks->Clear();
       return;
     }
-    aBlocks->AppendElement(std::move(handle));
   }
 }
 
 base::SharedMemoryHandle FontList::ShareBlockToProcess(uint32_t aIndex,
                                                        base::ProcessId aPid) {
   MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
   MOZ_RELEASE_ASSERT(mReadOnlyShmems.Length() == mBlocks.Length());
   MOZ_RELEASE_ASSERT(aIndex < mReadOnlyShmems.Length());
 
-  return mReadOnlyShmems[aIndex]->CloneHandle();
+  base::SharedMemoryHandle handle = base::SharedMemory::NULLHandle();
+  if (mReadOnlyShmems[aIndex]->ShareToProcess(aPid, &handle)) {
+    return handle;
+  }
+
+  return base::SharedMemory::NULLHandle();
 }
 
 Pointer FontList::Alloc(uint32_t aSize) {
   // Only the parent process does allocation.
   MOZ_ASSERT(XRE_IsParentProcess());
 
   // 4-byte alignment is good enough for anything we allocate in the font list,
   // as our "Pointer" (block index/offset) is a 32-bit value even on x64.
--- a/gfx/thebes/gfxPlatformFontList.cpp
+++ b/gfx/thebes/gfxPlatformFontList.cpp
@@ -2772,17 +2772,17 @@ base::SharedMemoryHandle gfxPlatformFont
     uint32_t aIndex, base::ProcessId aPid) {
   MOZ_RELEASE_ASSERT(SharedFontList());
   return SharedFontList()->ShareBlockToProcess(aIndex, aPid);
 }
 
 void gfxPlatformFontList::ShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
                                         base::SharedMemoryHandle aHandle) {
   if (SharedFontList()) {
-    SharedFontList()->ShmBlockAdded(aGeneration, aIndex, std::move(aHandle));
+    SharedFontList()->ShmBlockAdded(aGeneration, aIndex, aHandle);
   }
 }
 
 void gfxPlatformFontList::InitializeFamily(uint32_t aGeneration,
                                            uint32_t aFamilyIndex,
                                            bool aLoadCmaps) {
   auto list = SharedFontList();
   MOZ_ASSERT(list);
--- a/intl/hyphenation/glue/nsHyphenator.cpp
+++ b/intl/hyphenation/glue/nsHyphenator.cpp
@@ -73,17 +73,17 @@ static UniquePtr<base::SharedMemory> Get
   if (!dom::ContentChild::GetSingleton()->SendGetHyphDict(aURI, &handle,
                                                           &size)) {
     return nullptr;
   }
   UniquePtr<base::SharedMemory> shm = MakeUnique<base::SharedMemory>();
   if (!shm->IsHandleValid(handle)) {
     return nullptr;
   }
-  if (!shm->SetHandle(std::move(handle), true)) {
+  if (!shm->SetHandle(handle, true)) {
     return nullptr;
   }
   if (!shm->Map(size)) {
     return nullptr;
   }
   char* addr = static_cast<char*>(shm->memory());
   if (!addr) {
     return nullptr;
@@ -491,11 +491,14 @@ void nsHyphenator::ShareToProcess(base::
                                   base::SharedMemoryHandle* aOutHandle,
                                   uint32_t* aOutSize) {
   // If the resource is invalid, or if we fail to share it to the child
   // process, we'll just bail out and continue without hyphenation; no need
   // for this to be a fatal error.
   if (!mDict.is<UniquePtr<base::SharedMemory>>()) {
     return;
   }
-  *aOutHandle = mDict.as<UniquePtr<base::SharedMemory>>()->CloneHandle();
+  if (!mDict.as<UniquePtr<base::SharedMemory>>()->ShareToProcess(aPid,
+                                                                 aOutHandle)) {
+    return;
+  }
   *aOutSize = mDictSize;
 }
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -66,16 +66,17 @@ if os_posix:
         "src/base/lock_impl_posix.cc",
         "src/base/message_pump_libevent.cc",
         "src/base/platform_thread_posix.cc",
         "src/base/process_util_posix.cc",
         "src/base/shared_memory_posix.cc",
         "src/base/string16.cc",
         "src/base/thread_local_posix.cc",
         "src/base/waitable_event_posix.cc",
+        "src/chrome/common/file_descriptor_set_posix.cc",
         "src/chrome/common/ipc_channel_posix.cc",
         "src/chrome/common/process_watcher_posix_sigchld.cc",
     ]
 
 if os_macosx:
     UNIFIED_SOURCES += [
         "src/base/chrome_application_mac.mm",
         "src/base/mac_util.mm",
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/base/file_descriptor_posix.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_
+#define BASE_FILE_DESCRIPTOR_POSIX_H_
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// We introduct a special structure for file descriptors in order that we are
+// able to use template specialisation to special-case their handling.
+//
+// WARNING: (Chromium only) There are subtleties to consider if serialising
+// these objects over IPC. See comments in chrome/common/ipc_message_utils.h
+// above the template specialisation for this structure.
+// -----------------------------------------------------------------------------
+struct FileDescriptor {
+  FileDescriptor() : fd(-1), auto_close(false) {}
+
+  FileDescriptor(int ifd, bool iauto_close)
+      : fd(ifd), auto_close(iauto_close) {}
+
+  bool operator==(const FileDescriptor& aOther) const {
+    return fd == aOther.fd && auto_close == aOther.auto_close;
+  }
+
+  int fd;
+  // If true, this file descriptor should be closed after it has been used. For
+  // example an IPC system might interpret this flag as indicating that the
+  // file descriptor it has been given should be closed after use.
+  bool auto_close;
+};
+
+}  // namespace base
+
+#endif  // BASE_FILE_DESCRIPTOR_POSIX_H_
--- a/ipc/chromium/src/base/shared_memory.h
+++ b/ipc/chromium/src/base/shared_memory.h
@@ -7,42 +7,47 @@
 #ifndef BASE_SHARED_MEMORY_H_
 #define BASE_SHARED_MEMORY_H_
 
 #include "build/build_config.h"
 
 #if defined(OS_POSIX)
 #  include <sys/types.h>
 #  include <semaphore.h>
+#  include "base/file_descriptor_posix.h"
 #endif
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/process.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/UniquePtrExtensions.h"
 
 namespace base {
 
 // SharedMemoryHandle is a platform specific type which represents
 // the underlying OS handle to a shared memory segment.
-typedef mozilla::UniqueFileHandle SharedMemoryHandle;
+#if defined(OS_WIN)
+typedef HANDLE SharedMemoryHandle;
+#elif defined(OS_POSIX)
+typedef FileDescriptor SharedMemoryHandle;
+#endif
 
 // Platform abstraction for shared memory.  Provides a C++ wrapper
 // around the OS primitive for a memory mapped file.
 class SharedMemory {
  public:
   // Create a new SharedMemory object.
   SharedMemory() = default;
 
   // Create a new SharedMemory object from an existing, open
   // shared memory file.
   SharedMemory(SharedMemoryHandle init_handle, bool read_only)
       : SharedMemory() {
-    SetHandle(std::move(init_handle), read_only);
+    SetHandle(init_handle, read_only);
   }
 
   // Move constructor; transfers ownership.
   SharedMemory(SharedMemory&& other) = default;
 
   // Destructor.  Will close any open files.
   ~SharedMemory();
 
@@ -88,30 +93,26 @@ class SharedMemory {
   // created externally.
   // Returns 0 if not opened or unknown.
   size_t max_size() const { return max_size_; }
 
   // Gets a pointer to the opened memory space if it has been
   // Mapped via Map().  Returns NULL if it is not mapped.
   void* memory() const { return memory_.get(); }
 
-  // Extracts the underlying file handle, returning a RAII type.
-  // This unmaps the memory as a side-effect (and cleans up any OS-specific
-  // resources).
+  // Extracts the underlying file handle; similar to
+  // GiveToProcess(GetCurrentProcId(), ...) but returns a RAII type.
+  // Like GiveToProcess, this unmaps the memory as a side-effect (and
+  // cleans up any OS-specific resources).
   mozilla::UniqueFileHandle TakeHandle() {
     mozilla::UniqueFileHandle handle = std::move(mapped_file_);
     Close();
     return handle;
   }
 
-  // Creates a copy of the underlying file handle, returning a RAII type.
-  // This operation may fail, in which case the returned file handle will be
-  // invalid.
-  mozilla::UniqueFileHandle CloneHandle();
-
   // Make the shared memory object read-only, such that it cannot be
   // written even if it's sent to an untrusted process.  If it was
   // mapped in this process, it will be unmapped.  The object must
   // have been created with CreateFreezeable(), and must not have
   // already been shared to another process.
   //
   // (See bug 1479960 comment #0 for OS-specific implementation
   // details.)
@@ -144,25 +145,49 @@ class SharedMemory {
   // probably be mapped.  Returns NULL on error or if there is insufficient
   // contiguous address space to map the required number of pages.
   //
   // Note that there is no guarantee that the given address space will actually
   // be free by the time this function returns, since another thread might map
   // something there in the meantime.
   static void* FindFreeAddressSpace(size_t size);
 
+  // Share the shared memory to another process.  Attempts
+  // to create a platform-specific new_handle which can be
+  // used in a remote process to access the shared memory
+  // file.  new_handle is an ouput parameter to receive
+  // the handle for use in the remote process.
+  // Returns true on success, false otherwise.
+  bool ShareToProcess(base::ProcessId target_pid,
+                      SharedMemoryHandle* new_handle) {
+    return ShareToProcessCommon(target_pid, new_handle, false);
+  }
+
+  // Logically equivalent to:
+  //   bool ok = ShareToProcess(process, new_handle);
+  //   Close();
+  //   return ok;
+  // Note that the memory is unmapped by calling this method, regardless of the
+  // return value.
+  bool GiveToProcess(ProcessId target_pid, SharedMemoryHandle* new_handle) {
+    return ShareToProcessCommon(target_pid, new_handle, true);
+  }
+
 #ifdef OS_POSIX
   // If named POSIX shm is being used, append the prefix (including
   // the leading '/') that would be used by a process with the given
   // pid to the given string and return true.  If not, return false.
   // (This is public so that the Linux sandboxing code can use it.)
   static bool AppendPosixShmPrefix(std::string* str, pid_t pid);
 #endif
 
  private:
+  bool ShareToProcessCommon(ProcessId target_pid,
+                            SharedMemoryHandle* new_handle, bool close_self);
+
   bool CreateInternal(size_t size, bool freezeable);
 
   // Unmapping shared memory requires the mapped size on Unix but not
   // Windows; this encapsulates that difference.
   struct MappingDeleter {
 #ifdef OS_POSIX
     // A default-constructed deleter must be used only with nullptr
     // (to allow default-constructing UniqueMapping).  A deleter with
--- a/ipc/chromium/src/base/shared_memory_posix.cc
+++ b/ipc/chromium/src/base/shared_memory_posix.cc
@@ -59,29 +59,29 @@ SharedMemory::~SharedMemory() {
 
 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
   DCHECK(!mapped_file_);
 #ifndef ANDROID
   DCHECK(!frozen_file_);
 #endif
 
   freezeable_ = false;
-  mapped_file_ = std::move(handle);
+  mapped_file_.reset(handle.fd);
   read_only_ = read_only;
   // is_memfd_ only matters for freezing, which isn't possible
   return true;
 }
 
 // static
 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
-  return handle != nullptr;
+  return handle.fd >= 0;
 }
 
 // static
-SharedMemoryHandle SharedMemory::NULLHandle() { return nullptr; }
+SharedMemoryHandle SharedMemory::NULLHandle() { return SharedMemoryHandle(); }
 
 #ifdef ANDROID
 
 // Android has its own shared memory API, ashmem.  It doesn't support
 // POSIX shm_open, and the memfd support (see below) also doesn't work
 // because its SELinux policy prevents the procfs operations we'd use
 // (see bug 1670277 for more details).
 
@@ -522,26 +522,34 @@ bool SharedMemory::Map(size_t bytes, voi
 
 void* SharedMemory::FindFreeAddressSpace(size_t size) {
   void* memory = mmap(nullptr, size, PROT_NONE,
                       MAP_ANONYMOUS | MAP_NORESERVE | MAP_PRIVATE, -1, 0);
   munmap(memory, size);
   return memory != MAP_FAILED ? memory : NULL;
 }
 
-SharedMemoryHandle SharedMemory::CloneHandle() {
+bool SharedMemory::ShareToProcessCommon(ProcessId processId,
+                                        SharedMemoryHandle* new_handle,
+                                        bool close_self) {
   freezeable_ = false;
   const int new_fd = dup(mapped_file_.get());
   if (new_fd < 0) {
     CHROMIUM_LOG(WARNING) << "failed to duplicate file descriptor: "
                           << strerror(errno);
-    return nullptr;
+    return false;
   }
-  return mozilla::UniqueFileHandle(new_fd);
+  new_handle->fd = new_fd;
+  new_handle->auto_close = true;
+
+  if (close_self) Close();
+
+  return true;
 }
+
 void SharedMemory::Close(bool unmap_view) {
   if (unmap_view) {
     Unmap();
   }
 
   mapped_file_ = nullptr;
 #ifndef ANDROID
   if (frozen_file_) {
--- a/ipc/chromium/src/base/shared_memory_win.cc
+++ b/ipc/chromium/src/base/shared_memory_win.cc
@@ -64,17 +64,17 @@ void SharedMemory::MappingDeleter::opera
 
 SharedMemory::~SharedMemory() = default;
 
 bool SharedMemory::SetHandle(SharedMemoryHandle handle, bool read_only) {
   DCHECK(!mapped_file_);
 
   external_section_ = true;
   freezeable_ = false;  // just in case
-  mapped_file_ = std::move(handle);
+  mapped_file_.reset(handle);
   read_only_ = read_only;
   return true;
 }
 
 // static
 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
   return handle != nullptr;
 }
@@ -186,26 +186,49 @@ bool SharedMemory::Map(size_t bytes, voi
 void* SharedMemory::FindFreeAddressSpace(size_t size) {
   void* memory = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
   if (memory) {
     VirtualFree(memory, 0, MEM_RELEASE);
   }
   return memory;
 }
 
-SharedMemoryHandle SharedMemory::CloneHandle() {
+bool SharedMemory::ShareToProcessCommon(ProcessId processId,
+                                        SharedMemoryHandle* new_handle,
+                                        bool close_self) {
   freezeable_ = false;
-  HANDLE handle = INVALID_HANDLE_VALUE;
-  if (DuplicateHandle(GetCurrentProcess(), mapped_file_.get(),
-                      GetCurrentProcess(), &handle, 0, false,
-                      DUPLICATE_SAME_ACCESS)) {
-    return SharedMemoryHandle(handle);
+  *new_handle = 0;
+  DWORD access = FILE_MAP_READ | SECTION_QUERY;
+  DWORD options = 0;
+  HANDLE mapped_file;
+  HANDLE result;
+  if (!read_only_) {
+    access |= FILE_MAP_WRITE;
   }
-  NS_WARNING("DuplicateHandle Failed!");
-  return nullptr;
+  if (close_self) {
+    // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
+    mapped_file = mapped_file_.release();
+    options = DUPLICATE_CLOSE_SOURCE;
+    Unmap();
+  } else {
+    mapped_file = mapped_file_.get();
+  }
+
+  if (processId == GetCurrentProcId() && close_self) {
+    *new_handle = mapped_file;
+    return true;
+  }
+
+  if (!mozilla::ipc::DuplicateHandle(mapped_file, processId, &result, access,
+                                     options)) {
+    return false;
+  }
+
+  *new_handle = result;
+  return true;
 }
 
 void SharedMemory::Close(bool unmap_view) {
   if (unmap_view) {
     Unmap();
   }
 
   mapped_file_ = nullptr;
--- a/ipc/chromium/src/chrome/common/child_process_host.cc
+++ b/ipc/chromium/src/chrome/common/child_process_host.cc
@@ -24,19 +24,16 @@ ChildProcessHost::ChildProcessHost()
       opening_channel_(false) {}
 
 ChildProcessHost::~ChildProcessHost() {}
 
 bool ChildProcessHost::CreateChannel() {
   channel_id_ = IPC::Channel::GenerateVerifiedChannelID();
   channel_.reset(
       new IPC::Channel(channel_id_, IPC::Channel::MODE_SERVER, &listener_));
-#if defined(OS_WIN)
-  channel_->StartAcceptingHandles(IPC::Channel::MODE_SERVER);
-#endif
   if (!channel_->Connect()) return false;
 
   opening_channel_ = true;
 
   return true;
 }
 
 ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host)
--- a/ipc/chromium/src/chrome/common/child_thread.cc
+++ b/ipc/chromium/src/chrome/common/child_thread.cc
@@ -26,17 +26,14 @@ bool ChildThread::Run() {
 
 ChildThread* ChildThread::current() {
   return ChildProcess::current()->child_thread();
 }
 
 void ChildThread::Init() {
   auto channel = mozilla::MakeUnique<IPC::Channel>(
       channel_name_, IPC::Channel::MODE_CLIENT, nullptr);
-#if defined(OS_WIN)
-  channel->StartAcceptingHandles(IPC::Channel::MODE_CLIENT);
-#endif
 
   initial_port_ =
       mozilla::ipc::NodeController::InitChildProcess(std::move(channel));
 }
 
 void ChildThread::CleanUp() { mozilla::ipc::NodeController::CleanUp(); }
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.cc
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/file_descriptor_set_posix.h"
+
+#include "base/eintr_wrapper.h"
+#include "base/logging.h"
+
+#include <unistd.h>
+
+FileDescriptorSet::FileDescriptorSet() : consumed_descriptor_highwater_(0) {}
+
+FileDescriptorSet::~FileDescriptorSet() {
+  if (consumed_descriptor_highwater_ == descriptors_.size()) return;
+
+  CHROMIUM_LOG(WARNING)
+      << "FileDescriptorSet destroyed with unconsumed descriptors";
+
+  // We close all the descriptors where the close flag is set. If this
+  // message should have been transmitted, then closing those with close
+  // flags set mirrors the expected behaviour.
+  //
+  // If this message was received with more descriptors than expected
+  // (which could a DOS against the browser by a rogue renderer) then all
+  // the descriptors have their close flag set and we free all the extra
+  // kernel resources.
+  for (unsigned i = consumed_descriptor_highwater_; i < descriptors_.size();
+       ++i) {
+    if (descriptors_[i].auto_close) IGNORE_EINTR(close(descriptors_[i].fd));
+  }
+}
+
+void FileDescriptorSet::CopyFrom(const FileDescriptorSet& other) {
+  for (std::vector<base::FileDescriptor>::const_iterator i =
+           other.descriptors_.begin();
+       i != other.descriptors_.end(); ++i) {
+    int fd = IGNORE_EINTR(dup(i->fd));
+    AddAndAutoClose(fd);
+  }
+}
+
+bool FileDescriptorSet::Add(int fd) {
+  if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) return false;
+
+  struct base::FileDescriptor sd;
+  sd.fd = fd;
+  sd.auto_close = false;
+  descriptors_.push_back(sd);
+  return true;
+}
+
+bool FileDescriptorSet::AddAndAutoClose(int fd) {
+  if (descriptors_.size() == MAX_DESCRIPTORS_PER_MESSAGE) return false;
+
+  struct base::FileDescriptor sd;
+  sd.fd = fd;
+  sd.auto_close = true;
+  descriptors_.push_back(sd);
+  DCHECK(descriptors_.size() <= MAX_DESCRIPTORS_PER_MESSAGE);
+  return true;
+}
+
+int FileDescriptorSet::GetDescriptorAt(unsigned index) const {
+  if (index >= descriptors_.size()) return -1;
+
+  // We should always walk the descriptors in order, so it's reasonable to
+  // enforce this. Consider the case where a compromised renderer sends us
+  // the following message:
+  //
+  //   ExampleMsg:
+  //     num_fds:2 msg:FD(index = 1) control:SCM_RIGHTS {n, m}
+  //
+  // Here the renderer sent us a message which should have a descriptor, but
+  // actually sent two in an attempt to fill our fd table and kill us. By
+  // setting the index of the descriptor in the message to 1 (it should be
+  // 0), we would record a highwater of 1 and then consider all the
+  // descriptors to have been used.
+  //
+  // So we can either track of the use of each descriptor in a bitset, or we
+  // can enforce that we walk the indexes strictly in order.
+  //
+  // There's one more wrinkle: When logging messages, we may reparse them. So
+  // we have an exception: When the consumed_descriptor_highwater_ is at the
+  // end of the array and index 0 is requested, we reset the highwater value.
+  if (index == 0 && consumed_descriptor_highwater_ == descriptors_.size())
+    consumed_descriptor_highwater_ = 0;
+
+  if (index != consumed_descriptor_highwater_) return -1;
+
+  consumed_descriptor_highwater_ = index + 1;
+  return descriptors_[index].fd;
+}
+
+void FileDescriptorSet::GetDescriptors(int* buffer) const {
+  for (std::vector<base::FileDescriptor>::const_iterator i =
+           descriptors_.begin();
+       i != descriptors_.end(); ++i) {
+    *(buffer++) = i->fd;
+  }
+}
+
+void FileDescriptorSet::CommitAll() {
+  for (std::vector<base::FileDescriptor>::iterator i = descriptors_.begin();
+       i != descriptors_.end(); ++i) {
+    if (i->auto_close) IGNORE_EINTR(close(i->fd));
+  }
+  descriptors_.clear();
+  consumed_descriptor_highwater_ = 0;
+}
+
+void FileDescriptorSet::SetDescriptors(const int* buffer, unsigned count) {
+  DCHECK_LE(count, MAX_DESCRIPTORS_PER_MESSAGE);
+  DCHECK_EQ(descriptors_.size(), 0u);
+  DCHECK_EQ(consumed_descriptor_highwater_, 0u);
+
+  descriptors_.reserve(count);
+  for (unsigned i = 0; i < count; ++i) {
+    struct base::FileDescriptor sd;
+    sd.fd = buffer[i];
+    sd.auto_close = true;
+    descriptors_.push_back(sd);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/chrome/common/file_descriptor_set_posix.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+#define CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/file_descriptor_posix.h"
+#include "nsISupportsImpl.h"
+
+// -----------------------------------------------------------------------------
+// A FileDescriptorSet is an ordered set of POSIX file descriptors. These are
+// associated with IPC messages so that descriptors can be transmitted over a
+// UNIX domain socket.
+// -----------------------------------------------------------------------------
+class FileDescriptorSet {
+ public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileDescriptorSet)
+  FileDescriptorSet();
+
+  // Mac and Linux both limit the number of file descriptors per message to
+  // slightly more than 250.
+  enum { MAX_DESCRIPTORS_PER_MESSAGE = 200 };
+
+  void CopyFrom(const FileDescriptorSet& other);
+
+  // ---------------------------------------------------------------------------
+  // Interfaces for building during message serialisation...
+
+  // Add a descriptor to the end of the set. Returns false iff the set is full.
+  bool Add(int fd);
+  // Add a descriptor to the end of the set and automatically close it after
+  // transmission. Returns false iff the set is full.
+  bool AddAndAutoClose(int fd);
+
+  // ---------------------------------------------------------------------------
+
+  // ---------------------------------------------------------------------------
+  // Interfaces for accessing during message deserialisation...
+
+  // Return the number of descriptors
+  unsigned size() const { return descriptors_.size(); }
+  // Return true if no unconsumed descriptors remain
+  bool empty() const { return descriptors_.empty(); }
+  // Fetch the nth descriptor from the beginning of the set. Code using this
+  // /must/ access the descriptors in order, except that it may wrap from the
+  // end to index 0 again.
+  //
+  // This interface is designed for the deserialising code as it doesn't
+  // support close flags.
+  //   returns: file descriptor, or -1 on error
+  int GetDescriptorAt(unsigned n) const;
+
+  // ---------------------------------------------------------------------------
+
+  // ---------------------------------------------------------------------------
+  // Interfaces for transmission...
+
+  // Fill an array with file descriptors without 'consuming' them. CommitAll
+  // must be called after these descriptors have been transmitted.
+  //   buffer: (output) a buffer of, at least, size() integers.
+  void GetDescriptors(int* buffer) const;
+  // This must be called after transmitting the descriptors returned by
+  // GetDescriptors. It marks all the descriptors as consumed and closes those
+  // which are auto-close.
+  void CommitAll();
+
+  // ---------------------------------------------------------------------------
+
+  // ---------------------------------------------------------------------------
+  // Interfaces for receiving...
+
+  // Set the contents of the set from the given buffer. This set must be empty
+  // before calling. The auto-close flag is set on all the descriptors so that
+  // unconsumed descriptors are closed on destruction.
+  void SetDescriptors(const int* buffer, unsigned count);
+
+  // ---------------------------------------------------------------------------
+
+ private:
+  ~FileDescriptorSet();
+
+  // A vector of descriptors and close flags. If this message is sent, then
+  // these descriptors are sent as control data. After sending, any descriptors
+  // with a true flag are closed. If this message has been received, then these
+  // are the descriptors which were received and all close flags are true.
+  std::vector<base::FileDescriptor> descriptors_;
+
+  // This contains the index of the next descriptor which should be consumed.
+  // It's used in a couple of ways. Firstly, at destruction we can check that
+  // all the descriptors have been read (with GetNthDescriptor). Secondly, we
+  // can check that they are read in order.
+  mutable unsigned consumed_descriptor_highwater_;
+
+  DISALLOW_COPY_AND_ASSIGN(FileDescriptorSet);
+};
+
+#endif  // CHROME_COMMON_FILE_DESCRIPTOR_SET_POSIX_H_
--- a/ipc/chromium/src/chrome/common/ipc_channel.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel.h
@@ -154,21 +154,16 @@ class Channel {
   void ResetFileDescriptor(int fd);
 
   // Close the client side of the socketpair.
   void CloseClientFileDescriptor();
 
 #elif defined(OS_WIN)
   // Return the server pipe handle.
   void* GetServerPipeHandle() const;
-
-  // Tell this pipe to accept handles. Exactly one side of the IPC connection
-  // must be set as `MODE_SERVER`, and that side will be responsible for calling
-  // `DuplicateHandle` to transfer the handle between processes.
-  void StartAcceptingHandles(Mode mode);
 #endif  // defined(OS_POSIX)
 
   // On Windows: Generates a channel ID that, if passed to the client
   // as a shared secret, will validate the client's authenticity.
   // Other platforms don't use channel IDs, so this returns the dummy
   // ChannelId value.
   static ChannelId GenerateVerifiedChannelID();
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -24,16 +24,17 @@
 #include <map>
 
 #include "base/command_line.h"
 #include "base/eintr_wrapper.h"
 #include "base/logging.h"
 #include "base/process_util.h"
 #include "base/string_util.h"
 #include "chrome/common/chrome_switches.h"
+#include "chrome/common/file_descriptor_set_posix.h"
 #include "chrome/common/ipc_channel_utils.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "mozilla/ipc/Endpoint.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
@@ -477,35 +478,35 @@ bool Channel::ChannelImpl::ProcessIncomi
       }
 
       if (partial) {
         break;
       }
 
       Message& m = incoming_message_.ref();
 
-      if (m.header()->num_handles) {
+      if (m.header()->num_fds) {
         // the message has file descriptors
         const char* error = NULL;
-        if (m.header()->num_handles > num_fds - fds_i) {
+        if (m.header()->num_fds > num_fds - fds_i) {
           // the message has been completely received, but we didn't get
           // enough file descriptors.
           error = "Message needs unreceived descriptors";
         }
 
-        if (m.header()->num_handles >
-            IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE) {
+        if (m.header()->num_fds >
+            FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
           // There are too many descriptors in this message
           error = "Message requires an excessive number of descriptors";
         }
 
         if (error) {
           CHROMIUM_LOG(WARNING)
               << error << " channel:" << this << " message-type:" << m.type()
-              << " header()->num_handles:" << m.header()->num_handles
+              << " header()->num_fds:" << m.header()->num_fds
               << " num_fds:" << num_fds << " fds_i:" << fds_i;
           // close the existing file descriptors so that we don't leak them
           for (unsigned i = fds_i; i < num_fds; ++i)
             IGNORE_EINTR(close(fds[i]));
           input_overflow_fds_.clear();
           // abort the connection
           return false;
         }
@@ -515,22 +516,19 @@ bool Channel::ChannelImpl::ProcessIncomi
         // responsible for closing the descriptor.
         auto fdAck = mozilla::MakeUnique<Message>(MSG_ROUTING_NONE,
                                                   RECEIVED_FDS_MESSAGE_TYPE);
         DCHECK(m.fd_cookie() != 0);
         fdAck->set_fd_cookie(m.fd_cookie());
         OutputQueuePush(std::move(fdAck));
 #endif
 
-        nsTArray<mozilla::UniqueFileHandle> handles(m.header()->num_handles);
-        for (unsigned end_i = fds_i + m.header()->num_handles; fds_i < end_i;
-             ++fds_i) {
-          handles.AppendElement(mozilla::UniqueFileHandle(fds[fds_i]));
-        }
-        m.SetAttachedFileHandles(std::move(handles));
+        m.file_descriptor_set()->SetDescriptors(&fds[fds_i],
+                                                m.header()->num_fds);
+        fds_i += m.header()->num_fds;
       }
 
       // Note: We set other_pid_ below when we receive a Hello message (which
       // has no routing ID), but we only emit a profiler marker for messages
       // with a routing ID, so there's no conflict here.
       AddIPCProfilerMarker(m, other_pid_, MessageDirection::eReceiving,
                            MessagePhase::TransferEnd);
 
@@ -588,17 +586,17 @@ bool Channel::ChannelImpl::ProcessOutgoi
 #ifdef FUZZING
     mozilla::ipc::Faulty::instance().MaybeCollectAndClosePipe(pipe_);
 #endif
     Message* msg = output_queue_.FirstElement().get();
 
     struct msghdr msgh = {0};
 
     static const int tmp =
-        CMSG_SPACE(sizeof(int[IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE]));
+        CMSG_SPACE(sizeof(int[FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE]));
     char buf[tmp];
 
     if (partial_write_iter_.isNothing()) {
       Pickle::BufferList::IterImpl iter(msg->Buffers());
       MOZ_DIAGNOSTIC_ASSERT(!iter.Done(), "empty message");
       partial_write_iter_.emplace(iter);
     }
 
@@ -607,41 +605,39 @@ bool Channel::ChannelImpl::ProcessOutgoi
       // report a send error to our caller, which will close the channel.
       return false;
     }
 
     if (partial_write_iter_.value().Data() == msg->Buffers().Start()) {
       AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
                            MessagePhase::TransferStart);
 
-      if (!msg->attached_handles_.IsEmpty()) {
+      if (!msg->file_descriptor_set()->empty()) {
         // This is the first chunk of a message which has descriptors to send
         struct cmsghdr* cmsg;
-        const unsigned num_fds = msg->attached_handles_.Length();
+        const unsigned num_fds = msg->file_descriptor_set()->size();
 
-        if (num_fds > IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE) {
+        if (num_fds > FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE) {
           MOZ_DIAGNOSTIC_ASSERT(false, "Too many file descriptors!");
           CHROMIUM_LOG(FATAL) << "Too many file descriptors!";
           // This should not be reached.
           return false;
         }
 
         msgh.msg_control = buf;
         msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
         cmsg = CMSG_FIRSTHDR(&msgh);
         cmsg->cmsg_level = SOL_SOCKET;
         cmsg->cmsg_type = SCM_RIGHTS;
         cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
-        for (unsigned i = 0; i < num_fds; ++i) {
-          reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] =
-              msg->attached_handles_[i].get();
-        }
+        msg->file_descriptor_set()->GetDescriptors(
+            reinterpret_cast<int*>(CMSG_DATA(cmsg)));
         msgh.msg_controllen = cmsg->cmsg_len;
 
-        msg->header()->num_handles = num_fds;
+        msg->header()->num_fds = num_fds;
 #if defined(OS_MACOSX)
         msg->set_fd_cookie(++last_pending_fd_id_);
 #endif
       }
     }
 
     struct iovec iov[kMaxIOVecSize];
     size_t iov_count = 0;
@@ -677,21 +673,19 @@ bool Channel::ChannelImpl::ProcessOutgoi
     const bool intentional_short_write = !iter.Done();
     msgh.msg_iov = iov;
     msgh.msg_iovlen = iov_count;
 
     ssize_t bytes_written =
         HANDLE_EINTR(corrected_sendmsg(pipe_, &msgh, MSG_DONTWAIT));
 
 #if !defined(OS_MACOSX)
-    // On OSX the attached_handles_ array gets cleared later, once we get the
+    // On OSX CommitAll gets called later, once we get the
     // RECEIVED_FDS_MESSAGE_TYPE message.
-    if (bytes_written > 0) {
-      msg->attached_handles_.Clear();
-    }
+    if (bytes_written > 0) msg->file_descriptor_set()->CommitAll();
 #endif
 
     if (bytes_written < 0) {
       switch (errno) {
         case EAGAIN:
           // Not an error; the sendmsg would have blocked, so return to the
           // event loop and try again later.
           break;
@@ -746,20 +740,19 @@ bool Channel::ChannelImpl::ProcessOutgoi
           pipe_,
           false,  // One shot
           MessageLoopForIO::WATCH_WRITE, &write_watcher_, this);
       return true;
     } else {
       partial_write_iter_.reset();
 
 #if defined(OS_MACOSX)
-      if (!msg->attached_handles_.IsEmpty()) {
-        pending_fds_.push_back(PendingDescriptors{
-            msg->fd_cookie(), std::move(msg->attached_handles_)});
-      }
+      if (!msg->file_descriptor_set()->empty())
+        pending_fds_.push_back(
+            PendingDescriptors(msg->fd_cookie(), msg->file_descriptor_set()));
 #endif
 
       // Message sent OK!
 
       AddIPCProfilerMarker(*msg, other_pid_, MessageDirection::eSending,
                            MessagePhase::TransferEnd);
 
 #ifdef IPC_MESSAGE_DEBUG_EXTRA
@@ -836,16 +829,17 @@ void Channel::ChannelImpl::OnFileCanRead
 }
 
 #if defined(OS_MACOSX)
 void Channel::ChannelImpl::CloseDescriptors(uint32_t pending_fd_id) {
   DCHECK(pending_fd_id != 0);
   for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
        i != pending_fds_.end(); i++) {
     if ((*i).id == pending_fd_id) {
+      (*i).fds->CommitAll();
       pending_fds_.erase(i);
       return;
     }
   }
   DCHECK(false) << "pending_fd_id not in our list!";
 }
 #endif
 
@@ -905,16 +899,20 @@ void Channel::ChannelImpl::Close() {
   // Close any outstanding, received file descriptors
   for (std::vector<int>::iterator i = input_overflow_fds_.begin();
        i != input_overflow_fds_.end(); ++i) {
     IGNORE_EINTR(close(*i));
   }
   input_overflow_fds_.clear();
 
 #if defined(OS_MACOSX)
+  for (std::list<PendingDescriptors>::iterator i = pending_fds_.begin();
+       i != pending_fds_.end(); i++) {
+    (*i).fds->CommitAll();
+  }
   pending_fds_.clear();
 #endif
 
   closed_ = true;
 }
 
 bool Channel::ChannelImpl::Unsound_IsClosed() const { return closed_; }
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.h
@@ -13,21 +13,21 @@
 
 #include <atomic>
 #include <string>
 #include <vector>
 #include <list>
 
 #include "base/message_loop.h"
 #include "base/task.h"
+#include "chrome/common/file_descriptor_set_posix.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Queue.h"
 #include "mozilla/UniquePtr.h"
-#include "mozilla/UniquePtrExtensions.h"
 
 namespace IPC {
 
 // An implementation of ChannelImpl for POSIX systems that works via
 // socketpairs.  See the .cc file for an overview of the implementation.
 class Channel::ChannelImpl : public MessageLoopForIO::Watcher {
  public:
   using ChannelId = Channel::ChannelId;
@@ -118,17 +118,17 @@ class Channel::ChannelImpl : public Mess
   //
   // This buffer also holds a control message header of size CMSG_SPACE(0)
   // bytes. However, CMSG_SPACE is not a constant on Macs, so we can't use it
   // here. Consequently, we pick a number here that is at least CMSG_SPACE(0) on
   // all platforms. We assert at runtime, in Channel::ChannelImpl::Init, that
   // it's big enough.
   static constexpr size_t kControlBufferHeaderSize = 32;
   static constexpr size_t kControlBufferSize =
-      IPC::Message::MAX_DESCRIPTORS_PER_MESSAGE * sizeof(int) +
+      FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE * sizeof(int) +
       kControlBufferHeaderSize;
 
   // Large incoming messages that span multiple pipe buffers get built-up in the
   // buffers of this message.
   mozilla::Maybe<Message> incoming_message_;
   std::vector<int> input_overflow_fds_;
 
   // In server-mode, we have to wait for the client to connect before we
@@ -146,17 +146,21 @@ class Channel::ChannelImpl : public Mess
 
   // We keep track of the PID of the other side of this channel so that we can
   // record this when generating logs of IPC messages.
   int32_t other_pid_ = -1;
 
 #if defined(OS_MACOSX)
   struct PendingDescriptors {
     uint32_t id;
-    nsTArray<mozilla::UniqueFileHandle> handles;
+    RefPtr<FileDescriptorSet> fds;
+
+    PendingDescriptors() : id(0) {}
+    PendingDescriptors(uint32_t id, FileDescriptorSet* fds)
+        : id(id), fds(fds) {}
   };
 
   std::list<PendingDescriptors> pending_fds_;
 
   // A generation ID for RECEIVED_FD messages.
   uint32_t last_pending_fd_id_;
 #endif
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -56,33 +56,37 @@ Channel::ChannelImpl::State::~State() {
 }
 
 //------------------------------------------------------------------------------
 
 Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id, Mode mode,
                                   Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+      shared_secret_(0),
+      waiting_for_shared_secret_(false) {
   Init(mode, listener);
 
   if (!CreatePipe(channel_id, mode)) {
     // The pipe may have been closed already.
     CHROMIUM_LOG(WARNING) << "Unable to create pipe named \"" << channel_id
                           << "\" in " << (mode == 0 ? "server" : "client")
                           << " mode.";
   }
 }
 
 Channel::ChannelImpl::ChannelImpl(const ChannelId& channel_id,
                                   HANDLE server_pipe, Mode mode,
                                   Listener* listener)
     : ALLOW_THIS_IN_INITIALIZER_LIST(input_state_(this)),
       ALLOW_THIS_IN_INITIALIZER_LIST(output_state_(this)),
-      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)) {
+      ALLOW_THIS_IN_INITIALIZER_LIST(factory_(this)),
+      shared_secret_(0),
+      waiting_for_shared_secret_(false) {
   Init(mode, listener);
 
   if (mode == MODE_SERVER) {
     // We don't need the pipe name because we've been passed a handle, but we do
     // need to get the shared secret from the channel_id.
     PipeName(channel_id, &shared_secret_);
     waiting_for_shared_secret_ = !!shared_secret_;
 
@@ -102,19 +106,16 @@ void Channel::ChannelImpl::Init(Mode mod
   pipe_ = INVALID_HANDLE_VALUE;
   listener_ = listener;
   waiting_connect_ = (mode == MODE_SERVER);
   processing_incoming_ = false;
   closed_ = false;
   output_queue_length_ = 0;
   input_buf_offset_ = 0;
   input_buf_ = mozilla::MakeUnique<char[]>(Channel::kReadBufferSize);
-  accept_handles_ = false;
-  privileged_ = false;
-  other_process_ = INVALID_HANDLE_VALUE;
 }
 
 void Channel::ChannelImpl::OutputQueuePush(mozilla::UniquePtr<Message> msg) {
   mozilla::LogIPCMessage::LogDispatchWithPid(msg.get(), other_pid_);
 
   output_queue_.Push(std::move(msg));
   output_queue_length_++;
 }
@@ -135,22 +136,16 @@ void Channel::ChannelImpl::Close() {
 
   // Closing the handle at this point prevents us from issuing more requests
   // form OnIOCompleted().
   if (pipe_ != INVALID_HANDLE_VALUE) {
     CloseHandle(pipe_);
     pipe_ = INVALID_HANDLE_VALUE;
   }
 
-  // If we have a connection to the other process, close the handle.
-  if (other_process_ != INVALID_HANDLE_VALUE) {
-    CloseHandle(other_process_);
-    other_process_ = INVALID_HANDLE_VALUE;
-  }
-
   while (input_state_.is_pending || output_state_.is_pending) {
     MessageLoopForIO::current()->WaitForIOCompletion(INFINITE, this);
   }
 
   while (!output_queue_.IsEmpty()) {
     OutputQueuePop();
   }
 
@@ -453,34 +448,19 @@ bool Channel::ChannelImpl::ProcessIncomi
         if (waiting_for_shared_secret_ && (it.NextInt() != shared_secret_)) {
           NOTREACHED();
           // Something went wrong. Abort connection.
           Close();
           listener_->OnChannelError();
           return false;
         }
         waiting_for_shared_secret_ = false;
-
-        // Now that we know the remote pid, open a privileged handle to the
-        // child process if needed to transfer handles to/from it.
-        if (privileged_ && other_process_ == INVALID_HANDLE_VALUE) {
-          other_process_ = OpenProcess(PROCESS_DUP_HANDLE, false, other_pid_);
-          if (!other_process_) {
-            other_process_ = INVALID_HANDLE_VALUE;
-            CHROMIUM_LOG(ERROR) << "Failed to acquire privileged handle to "
-                                << other_pid_ << ", cannot accept handles";
-          }
-        }
-
         listener_->OnChannelConnected(other_pid_);
       } else {
         mozilla::LogIPCMessage::Run run(&m);
-        if (!AcceptHandles(m)) {
-          return false;
-        }
         listener_->OnMessageReceived(std::move(m));
       }
 
       incoming_message_.reset();
     }
 
     bytes_read = 0;  // Get more data.
   }
@@ -527,19 +507,16 @@ bool Channel::ChannelImpl::ProcessOutgoi
   if (INVALID_HANDLE_VALUE == pipe_) return false;
 
   // Write to pipe...
   Message* m = output_queue_.FirstElement().get();
 
   if (partial_write_iter_.isNothing()) {
     AddIPCProfilerMarker(*m, other_pid_, MessageDirection::eSending,
                          MessagePhase::TransferStart);
-    if (!TransferHandles(*m)) {
-      return false;
-    }
     Pickle::BufferList::IterImpl iter(m->Buffers());
     partial_write_iter_.emplace(iter);
   }
 
   Pickle::BufferList::IterImpl& iter = partial_write_iter_.ref();
 
   // Don't count this write for the purposes of late write checking. If this
   // message results in a legitimate file write, that will show up when it
@@ -600,181 +577,16 @@ void Channel::ChannelImpl::OnIOCompleted
   }
   if (!ok && INVALID_HANDLE_VALUE != pipe_) {
     // We don't want to re-enter Close().
     Close();
     listener_->OnChannelError();
   }
 }
 
-void Channel::ChannelImpl::StartAcceptingHandles(Mode mode) {
-  if (accept_handles_) {
-    MOZ_ASSERT(privileged_ == (mode == MODE_SERVER));
-    return;
-  }
-  accept_handles_ = true;
-  privileged_ = mode == MODE_SERVER;
-
-  if (privileged_ && other_pid_ != -1 &&
-      other_process_ == INVALID_HANDLE_VALUE) {
-    other_process_ = OpenProcess(PROCESS_DUP_HANDLE, false, other_pid_);
-    if (!other_process_) {
-      other_process_ = INVALID_HANDLE_VALUE;
-      CHROMIUM_LOG(ERROR) << "Failed to acquire privileged handle to "
-                          << other_pid_ << ", cannot accept handles";
-    }
-  }
-}
-
-static uint32_t HandleToUint32(HANDLE h) {
-  // Cast through uintptr_t and then unsigned int to make the truncation to
-  // 32 bits explicit. Handles are size of-pointer but are always 32-bit values.
-  // https://docs.microsoft.com/en-ca/windows/win32/winprog64/interprocess-communication
-  // says: 64-bit versions of Windows use 32-bit handles for interoperability.
-  return static_cast<uint32_t>(reinterpret_cast<uintptr_t>(h));
-}
-
-static HANDLE Uint32ToHandle(uint32_t h) {
-  return reinterpret_cast<HANDLE>(
-      static_cast<uintptr_t>(static_cast<int32_t>(h)));
-}
-
-bool Channel::ChannelImpl::AcceptHandles(Message& msg) {
-  MOZ_ASSERT(msg.num_handles() == 0);
-
-  uint32_t num_handles = msg.header()->num_handles;
-  if (num_handles == 0) {
-    return true;
-  }
-
-  if (!accept_handles_) {
-    CHROMIUM_LOG(ERROR) << "invalid message: " << msg.name()
-                        << ". channel is not configured to accept handles";
-    return false;
-  }
-
-  // Seek to the start of our handle payload.
-  mozilla::CheckedInt<uint32_t> handles_payload_size(num_handles);
-  handles_payload_size *= sizeof(uint32_t);
-  if (!handles_payload_size.isValid() ||
-      handles_payload_size.value() > msg.header()->payload_size) {
-    CHROMIUM_LOG(ERROR) << "invalid handle count " << num_handles
-                        << " or payload size: " << msg.header()->payload_size;
-    return false;
-  }
-  uint32_t handles_offset =
-      msg.header()->payload_size - handles_payload_size.value();
-
-  PickleIterator handles_start{msg};
-  if (!msg.IgnoreBytes(&handles_start, handles_offset)) {
-    CHROMIUM_LOG(ERROR) << "IgnoreBytes failed";
-    return false;
-  }
-
-  // Read in the handles themselves, transferring ownership as required.
-  nsTArray<mozilla::UniqueFileHandle> handles;
-  {
-    PickleIterator iter{handles_start};
-    for (uint32_t i = 0; i < num_handles; ++i) {
-      uint32_t handleValue;
-      if (!msg.ReadUInt32(&iter, &handleValue)) {
-        CHROMIUM_LOG(ERROR) << "failed to read handle value";
-        return false;
-      }
-      HANDLE handle = Uint32ToHandle(handleValue);
-
-      // If we're the privileged process, the remote process will have leaked
-      // the sent handles in its local address space, and be relying on us to
-      // duplicate them, otherwise the remote privileged side will have
-      // transferred the handles to us already.
-      if (privileged_) {
-        if (other_process_ == INVALID_HANDLE_VALUE) {
-          CHROMIUM_LOG(ERROR) << "other_process_ is invalid in AcceptHandles";
-          return false;
-        }
-        if (!::DuplicateHandle(
-                other_process_, handle, GetCurrentProcess(), &handle, 0, FALSE,
-                DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
-          CHROMIUM_LOG(ERROR) << "DuplicateHandle failed for handle " << handle
-                              << " in AcceptHandles";
-          return false;
-        }
-      }
-
-      // The handle is directly owned by this process now, and can be added to
-      // our `handles` array.
-      handles.AppendElement(mozilla::UniqueFileHandle(handle));
-    }
-  }
-
-  // We're done with the handle footer, truncate the message at that point.
-  msg.Truncate(&handles_start);
-  msg.SetAttachedFileHandles(std::move(handles));
-  msg.header()->num_handles = 0;
-  MOZ_ASSERT(msg.header()->payload_size == handles_offset);
-  MOZ_ASSERT(msg.num_handles() == num_handles);
-  return true;
-}
-
-bool Channel::ChannelImpl::TransferHandles(Message& msg) {
-  MOZ_ASSERT(msg.header()->num_handles == 0);
-
-  uint32_t num_handles = msg.num_handles();
-  if (num_handles == 0) {
-    return true;
-  }
-
-  if (!accept_handles_) {
-    CHROMIUM_LOG(ERROR) << "cannot send message: " << msg.name()
-                        << ". channel is not configured to accept handles";
-    return false;
-  }
-
-#ifdef DEBUG
-  uint32_t handles_offset = msg.header()->payload_size;
-#endif
-
-  // Write handles from `attached_handles_` into the message payload.
-  for (uint32_t i = 0; i < num_handles; ++i) {
-    // Release ownership of the handle. It'll be cloned when the parent process
-    // transfers it with DuplicateHandle either in this process or the remote
-    // process.
-    HANDLE handle = msg.attached_handles_[i].release();
-
-    // If we're the privileged process, transfer the HANDLE to our remote before
-    // sending the message. Otherwise, the remote privileged process will
-    // transfer the handle for us, so leak it.
-    if (privileged_) {
-      if (other_process_ == INVALID_HANDLE_VALUE) {
-        CHROMIUM_LOG(ERROR) << "other_process_ is invalid in TransferHandles";
-        return false;
-      }
-      if (!::DuplicateHandle(GetCurrentProcess(), handle, other_process_,
-                             &handle, 0, FALSE,
-                             DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE)) {
-        CHROMIUM_LOG(ERROR) << "DuplicateHandle failed for handle " << handle
-                            << " in TransferHandles";
-        return false;
-      }
-    }
-
-    if (!msg.WriteUInt32(HandleToUint32(handle))) {
-      CHROMIUM_LOG(ERROR) << "failed to write handle value " << handle;
-      return false;
-    }
-  }
-  msg.attached_handles_.Clear();
-
-  msg.header()->num_handles = num_handles;
-  MOZ_ASSERT(msg.header()->payload_size ==
-                 handles_offset + (sizeof(uint32_t) * num_handles),
-             "Unexpected number of bytes written for handles footer?");
-  return true;
-}
-
 bool Channel::ChannelImpl::Unsound_IsClosed() const { return closed_; }
 
 uint32_t Channel::ChannelImpl::Unsound_NumQueuedMessages() const {
   return output_queue_length_;
 }
 
 //------------------------------------------------------------------------------
 // Channel's methods simply call through to ChannelImpl.
@@ -797,20 +609,16 @@ Channel::~Channel() {
 bool Channel::Connect() { return channel_impl_->Connect(); }
 
 void Channel::Close() { channel_impl_->Close(); }
 
 void* Channel::GetServerPipeHandle() const {
   return channel_impl_->GetServerPipeHandle();
 }
 
-void Channel::StartAcceptingHandles(Mode mode) {
-  channel_impl_->StartAcceptingHandles(mode);
-}
-
 Channel::Listener* Channel::set_listener(Listener* listener) {
   return channel_impl_->set_listener(listener);
 }
 
 bool Channel::Send(mozilla::UniquePtr<Message> message) {
   return channel_impl_->Send(std::move(message));
 }
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.h
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.h
@@ -27,25 +27,23 @@ class Channel::ChannelImpl : public Mess
  public:
   using ChannelId = Channel::ChannelId;
 
   // Mirror methods of Channel, see ipc_channel.h for description.
   ChannelImpl(const ChannelId& channel_id, Mode mode, Listener* listener);
   ChannelImpl(const ChannelId& channel_id, HANDLE server_pipe, Mode mode,
               Listener* listener);
   ~ChannelImpl() {
-    if (pipe_ != INVALID_HANDLE_VALUE ||
-        other_process_ != INVALID_HANDLE_VALUE) {
+    if (pipe_ != INVALID_HANDLE_VALUE) {
       Close();
     }
   }
   bool Connect();
   void Close();
   HANDLE GetServerPipeHandle() const;
-  void StartAcceptingHandles(Mode mode);
   Listener* set_listener(Listener* listener) {
     Listener* old = listener_;
     listener_ = listener;
     return old;
   }
   bool Send(mozilla::UniquePtr<Message> message);
 
   int32_t OtherPid() const { return other_pid_; }
@@ -66,100 +64,85 @@ class Channel::ChannelImpl : public Mess
   bool EnqueueHelloMessage();
 
   bool ProcessConnection();
   bool ProcessIncomingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_read);
   bool ProcessOutgoingMessages(MessageLoopForIO::IOContext* context,
                                DWORD bytes_written);
 
-  // Called on a Message immediately before it is sent/recieved to transfer
-  // handles to the remote process, or accept handles from the remote process.
-  bool AcceptHandles(Message& msg);
-  bool TransferHandles(Message& msg);
-
   // MessageLoop::IOHandler implementation.
   virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
                              DWORD bytes_transfered, DWORD error);
 
  private:
   struct State {
     explicit State(ChannelImpl* channel);
     ~State();
     MessageLoopForIO::IOContext context;
-    bool is_pending = false;
+    bool is_pending;
   };
 
   State input_state_;
   State output_state_;
 
-  HANDLE pipe_ = INVALID_HANDLE_VALUE;
+  HANDLE pipe_;
 
-  Listener* listener_ = nullptr;
+  Listener* listener_;
 
   // Messages to be sent are queued here.
   mozilla::Queue<mozilla::UniquePtr<Message>, 64> output_queue_;
 
   // If sending a message blocks then we use this iterator to keep track of
   // where in the message we are. It gets reset when the message is finished
   // sending.
   mozilla::Maybe<Pickle::BufferList::IterImpl> partial_write_iter_;
 
   // We read from the pipe into this buffer
   mozilla::UniquePtr<char[]> input_buf_;
-  size_t input_buf_offset_ = 0;
+  size_t input_buf_offset_;
 
   // Large incoming messages that span multiple pipe buffers get built-up in the
   // buffers of this message.
   mozilla::Maybe<Message> incoming_message_;
 
   // In server-mode, we have to wait for the client to connect before we
   // can begin reading.  We make use of the input_state_ when performing
   // the connect operation in overlapped mode.
-  bool waiting_connect_ = false;
+  bool waiting_connect_;
 
   // This flag is set when processing incoming messages.  It is used to
   // avoid recursing through ProcessIncomingMessages, which could cause
   // problems.  TODO(darin): make this unnecessary
-  bool processing_incoming_ = false;
+  bool processing_incoming_;
 
   // This flag is set after Close() is run on the channel.
-  std::atomic<bool> closed_ = false;
+  std::atomic<bool> closed_;
 
   // We keep track of the PID of the other side of this channel so that we can
   // record this when generating logs of IPC messages.
   int32_t other_pid_ = -1;
 
   // This variable is updated so it matches output_queue_.Count(), except we can
   // read output_queue_length_ from any thread (if we're OK getting an
   // occasional out-of-date or bogus value).  We use output_queue_length_ to
   // implement Unsound_NumQueuedMessages.
-  std::atomic<size_t> output_queue_length_ = 0;
+  std::atomic<size_t> output_queue_length_;
 
   ScopedRunnableMethodFactory<ChannelImpl> factory_;
 
   // This is a unique per-channel value used to authenticate the client end of
   // a connection. If the value is non-zero, the client passes it in the hello
   // and the host validates. (We don't send the zero value to preserve IPC
   // compatibility with existing clients that don't validate the channel.)
-  int32_t shared_secret_ = 0;
+  int32_t shared_secret_;
 
   // In server-mode, we wait for the channel at the other side of the pipe to
   // send us back our shared secret, if we are using one.
-  bool waiting_for_shared_secret_ = false;
-
-  // Whether or not to accept handles from a remote process, and whether this
-  // process is the privileged side of a IPC::Channel which can transfer
-  // handles.
-  bool accept_handles_ = false;
-  bool privileged_ = false;
-
-  // A privileged process handle used to transfer HANDLEs to and from the remote
-  // process. This will only be used if `privileged_` is set.
-  HANDLE other_process_ = INVALID_HANDLE_VALUE;
+  bool waiting_for_shared_secret_;
 
 #ifdef DEBUG
   mozilla::UniquePtr<nsAutoOwningThread> _mOwningThread;
 #endif
 
   DISALLOW_COPY_AND_ASSIGN(ChannelImpl);
 };
 
--- a/ipc/chromium/src/chrome/common/ipc_message.cc
+++ b/ipc/chromium/src/chrome/common/ipc_message.cc
@@ -5,44 +5,52 @@
 // found in the LICENSE file.
 
 #include "chrome/common/ipc_message.h"
 
 #include "base/logging.h"
 #include "build/build_config.h"
 #include "mojo/core/ports/event.h"
 
+#if defined(OS_POSIX)
+#  include "chrome/common/file_descriptor_set_posix.h"
+#endif
+
 #include <utility>
 
 #include "nsISupportsImpl.h"
 
 namespace IPC {
 
 //------------------------------------------------------------------------------
 
 const mojo::core::ports::UserMessage::TypeInfo Message::kUserMessageTypeInfo{};
 
 Message::~Message() { MOZ_COUNT_DTOR(IPC::Message); }
 
 Message::Message()
     : UserMessage(&kUserMessageTypeInfo), Pickle(sizeof(Header)) {
   MOZ_COUNT_CTOR(IPC::Message);
   header()->routing = header()->type = 0;
-  header()->num_handles = 0;
+#if defined(OS_POSIX)
+  header()->num_fds = 0;
+#endif
 }
 
 Message::Message(int32_t routing_id, msgid_t type, uint32_t segment_capacity,
                  HeaderFlags flags, bool recordWriteLatency)
     : UserMessage(&kUserMessageTypeInfo),
       Pickle(sizeof(Header), segment_capacity) {
   MOZ_COUNT_CTOR(IPC::Message);
   header()->routing = routing_id;
   header()->type = type;
   header()->flags = flags;
-  header()->num_handles = 0;
+#if defined(OS_POSIX)
+  header()->num_fds = 0;
+#endif
   header()->interrupt_remote_stack_depth_guess = static_cast<uint32_t>(-1);
   header()->interrupt_local_stack_depth = static_cast<uint32_t>(-1);
   header()->seqno = 0;
 #if defined(OS_MACOSX)
   header()->cookie = 0;
 #endif
   header()->footer_offset = -1;
   if (recordWriteLatency) {
@@ -54,19 +62,21 @@ Message::Message(const char* data, int d
     : UserMessage(&kUserMessageTypeInfo),
       Pickle(sizeof(Header), data, data_len) {
   MOZ_COUNT_CTOR(IPC::Message);
 }
 
 Message::Message(Message&& other)
     : UserMessage(&kUserMessageTypeInfo),
       Pickle(std::move(other)),
-      attached_handles_(std::move(other.attached_handles_)),
       attached_ports_(std::move(other.attached_ports_)) {
   MOZ_COUNT_CTOR(IPC::Message);
+#if defined(OS_POSIX)
+  file_descriptor_set_ = std::move(other.file_descriptor_set_);
+#endif
 }
 
 /*static*/ Message* Message::IPDLMessage(int32_t routing_id, msgid_t type,
                                          HeaderFlags flags) {
   return new Message(routing_id, type, 0, flags, true);
 }
 
 /*static*/ Message* Message::ForSyncDispatchError(NestedLevel level) {
@@ -84,18 +94,20 @@ Message::Message(Message&& other)
   flags.SetInterrupt();
   flags.SetReply();
   flags.SetReplyError();
   return m;
 }
 
 Message& Message::operator=(Message&& other) {
   *static_cast<Pickle*>(this) = std::move(other);
-  attached_handles_ = std::move(other.attached_handles_);
   attached_ports_ = std::move(other.attached_ports_);
+#if defined(OS_POSIX)
+  file_descriptor_set_.swap(other.file_descriptor_set_);
+#endif
   return *this;
 }
 
 void Message::WriteFooter(const void* data, uint32_t data_len) {
   MOZ_ASSERT(header()->footer_offset < 0, "Already wrote a footer!");
   if (data_len == 0) {
     return;
   }
@@ -138,48 +150,54 @@ bool Message::ReadFooter(void* buffer, u
 uint32_t Message::FooterSize() const {
   if (header()->footer_offset >= 0 &&
       uint32_t(header()->footer_offset) < header()->payload_size) {
     return header()->payload_size - header()->footer_offset;
   }
   return 0;
 }
 
-bool Message::WriteFileHandle(mozilla::UniqueFileHandle handle) {
-  uint32_t handle_index = attached_handles_.Length();
-  WriteUInt32(handle_index);
-  if (handle_index == MAX_DESCRIPTORS_PER_MESSAGE) {
-    return false;
+#if defined(OS_POSIX)
+bool Message::WriteFileDescriptor(const base::FileDescriptor& descriptor) {
+  // We write the index of the descriptor so that we don't have to
+  // keep the current descriptor as extra decoding state when deserialising.
+  // Also, we rely on each file descriptor being accompanied by sizeof(int)
+  // bytes of data in the message. See the comment for input_cmsg_buf_.
+  WriteInt(file_descriptor_set()->size());
+  if (descriptor.auto_close) {
+    return file_descriptor_set()->AddAndAutoClose(descriptor.fd);
+  } else {
+    return file_descriptor_set()->Add(descriptor.fd);
   }
-  attached_handles_.AppendElement(std::move(handle));
-  return true;
 }
 
-bool Message::ConsumeFileHandle(PickleIterator* iter,
-                                mozilla::UniqueFileHandle* handle) const {
-  uint32_t handle_index;
-  if (!ReadUInt32(iter, &handle_index)) {
-    return false;
-  }
-  if (handle_index >= attached_handles_.Length()) {
-    return false;
-  }
-  // NOTE: This mutates the underlying array, replacing the handle with an
-  // invalid handle.
-  *handle = std::exchange(attached_handles_[handle_index], nullptr);
-  return true;
+bool Message::ReadFileDescriptor(PickleIterator* iter,
+                                 base::FileDescriptor* descriptor) const {
+  int descriptor_index;
+  if (!ReadInt(iter, &descriptor_index)) return false;
+
+  FileDescriptorSet* file_descriptor_set = file_descriptor_set_.get();
+  if (!file_descriptor_set) return false;
+
+  descriptor->fd = file_descriptor_set->GetDescriptorAt(descriptor_index);
+  descriptor->auto_close = false;
+
+  return descriptor->fd >= 0;
 }
 
-void Message::SetAttachedFileHandles(
-    nsTArray<mozilla::UniqueFileHandle> handles) {
-  MOZ_DIAGNOSTIC_ASSERT(attached_handles_.IsEmpty());
-  attached_handles_ = std::move(handles);
+void Message::EnsureFileDescriptorSet() {
+  if (file_descriptor_set_.get() == NULL)
+    file_descriptor_set_ = new FileDescriptorSet;
 }
 
-uint32_t Message::num_handles() const { return attached_handles_.Length(); }
+uint32_t Message::num_fds() const {
+  return file_descriptor_set() ? file_descriptor_set()->size() : 0;
+}
+
+#endif
 
 void Message::WritePort(mozilla::ipc::ScopedPort port) {
   uint32_t port_index = attached_ports_.Length();
   WriteUInt32(port_index);
   attached_ports_.AppendElement(std::move(port));
 }
 
 bool Message::ConsumePort(PickleIterator* iter,
--- a/ipc/chromium/src/chrome/common/ipc_message.h
+++ b/ipc/chromium/src/chrome/common/ipc_message.h
@@ -10,30 +10,35 @@
 #include <string>
 
 #include "base/basictypes.h"
 #include "base/pickle.h"
 #include "mojo/core/ports/user_message.h"
 #include "mojo/core/ports/port_ref.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
-#include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/ipc/ScopedPort.h"
 #include "nsTArray.h"
 
 #ifdef FUZZING
 #  include "mozilla/ipc/Faulty.h"
 #endif
 
+namespace base {
+struct FileDescriptor;
+}
+
 namespace mozilla {
 namespace ipc {
 class MiniTransceiver;
 }
 }  // namespace mozilla
 
+class FileDescriptorSet;
+
 namespace IPC {
 
 //------------------------------------------------------------------------------
 
 // Generated by IPDL compiler
 const char* StringFromIPCMessageType(uint32_t aMessageType);
 
 class Channel;
@@ -84,34 +89,29 @@ class Message : public mojo::core::ports
     CONSTRUCTOR = 1,
   };
 
   enum Reply {
     NOT_REPLY = 0,
     REPLY = 1,
   };
 
-  // Mac and Linux both limit the number of file descriptors per message to
-  // slightly more than 250.
-  enum { MAX_DESCRIPTORS_PER_MESSAGE = 200 };
-
   class HeaderFlags {
     friend class Message;
 
     enum {
       NESTED_MASK = 0x0003,
       PRIO_MASK = 0x001C,
       SYNC_BIT = 0x0020,
       REPLY_BIT = 0x0040,
       REPLY_ERROR_BIT = 0x0080,
       INTERRUPT_BIT = 0x0100,
       COMPRESS_BIT = 0x0200,
       COMPRESSALL_BIT = 0x0400,
       CONSTRUCTOR_BIT = 0x0800,
-      RELAY_BIT = 0x1000,
     };
 
    public:
     constexpr HeaderFlags() : mFlags(NOT_NESTED) {}
 
     explicit constexpr HeaderFlags(NestedLevel level) : mFlags(level) {}
 
     constexpr HeaderFlags(NestedLevel level, PriorityValue priority,
@@ -142,30 +142,22 @@ class Message : public mojo::core::ports
     }
 
     bool IsConstructor() const { return (mFlags & CONSTRUCTOR_BIT) != 0; }
     bool IsSync() const { return (mFlags & SYNC_BIT) != 0; }
     bool IsInterrupt() const { return (mFlags & INTERRUPT_BIT) != 0; }
     bool IsReply() const { return (mFlags & REPLY_BIT) != 0; }
 
     bool IsReplyError() const { return (mFlags & REPLY_ERROR_BIT) != 0; }
-    bool IsRelay() const { return (mFlags & RELAY_BIT) != 0; }
 
    private:
     void SetSync() { mFlags |= SYNC_BIT; }
     void SetInterrupt() { mFlags |= INTERRUPT_BIT; }
     void SetReply() { mFlags |= REPLY_BIT; }
     void SetReplyError() { mFlags |= REPLY_ERROR_BIT; }
-    void SetRelay(bool relay) {
-      if (relay) {
-        mFlags |= RELAY_BIT;
-      } else {
-        mFlags &= ~RELAY_BIT;
-      }
-    }
 
     uint32_t mFlags;
   };
 
   virtual ~Message();
 
   Message();
 
@@ -249,20 +241,19 @@ class Message : public mojo::core::ports
   int32_t seqno() const { return header()->seqno; }
 
   void set_seqno(int32_t aSeqno) { header()->seqno = aSeqno; }
 
   const char* name() const { return StringFromIPCMessageType(type()); }
 
   const mozilla::TimeStamp& create_time() const { return create_time_; }
 
-  uint32_t num_handles() const;
-
-  bool is_relay() const { return header()->flags.IsRelay(); }
-  void set_relay(bool new_relay) { header()->flags.SetRelay(new_relay); }
+#if defined(OS_POSIX)
+  uint32_t num_fds() const;
+#endif
 
   template <class T>
   static bool Dispatch(const Message* msg, T* obj, void (T::*func)()) {
     (obj->*func)();
     return true;
   }
 
   template <class T>
@@ -304,31 +295,31 @@ class Message : public mojo::core::ports
 
   // Figure out how big the message starting at range_start is. Returns 0 if
   // there's no enough data to determine (i.e., if [range_start, range_end) does
   // not contain enough of the message header to know the size).
   static uint32_t MessageSize(const char* range_start, const char* range_end) {
     return Pickle::MessageSize(HeaderSize(), range_start, range_end);
   }
 
-  bool WriteFileHandle(mozilla::UniqueFileHandle handle);
-
-  // WARNING: This method is marked as `const` so it can be called when
-  // deserializing the message, but will mutate it, consuming the handle.
-  bool ConsumeFileHandle(PickleIterator* iter,
-                         mozilla::UniqueFileHandle* handle) const;
+#if defined(OS_POSIX)
+  // On POSIX, a message supports reading / writing FileDescriptor objects.
+  // This is used to pass a file descriptor to the peer of an IPC channel.
 
-  // Called when receiving an IPC message to attach file handles which were
-  // received from IPC. Must only be called when there are no handles on this
-  // IPC::Message.
-  void SetAttachedFileHandles(nsTArray<mozilla::UniqueFileHandle> handles);
+  // Add a descriptor to the end of the set. Returns false iff the set is full.
+  bool WriteFileDescriptor(const base::FileDescriptor& descriptor);
+  // Get a file descriptor from the message. Returns false on error.
+  //   iter: a Pickle iterator to the current location in the message.
+  bool ReadFileDescriptor(PickleIterator* iter,
+                          base::FileDescriptor* descriptor) const;
 
-#if defined(OS_MACOSX)
+#  if defined(OS_MACOSX)
   void set_fd_cookie(uint32_t cookie) { header()->cookie = cookie; }
   uint32_t fd_cookie() const { return header()->cookie; }
+#  endif
 #endif
 
   void WritePort(mozilla::ipc::ScopedPort port);
 
   // This method consumes the port from the message, preventing the message's
   // destructor from destroying the port and meaning that future attempts to
   // read this port will instead produce an invalid port.
   //
@@ -348,22 +339,24 @@ class Message : public mojo::core::ports
 #endif
   friend class mozilla::ipc::MiniTransceiver;
 
 #if !defined(OS_MACOSX)
  protected:
 #endif
 
   struct Header : Pickle::Header {
-    int32_t routing;       // ID of the view that this message is destined for
-    msgid_t type;          // specifies the user-defined message type
-    HeaderFlags flags;     // specifies control flags for the message
-    uint32_t num_handles;  // the number of handles included with this message
-#if defined(OS_MACOSX)
+    int32_t routing;    // ID of the view that this message is destined for
+    msgid_t type;       // specifies the user-defined message type
+    HeaderFlags flags;  // specifies control flags for the message
+#if defined(OS_POSIX)
+    uint32_t num_fds;  // the number of descriptors included with this message
+#  if defined(OS_MACOSX)
     uint32_t cookie;  // cookie to ACK that the descriptors have been read.
+#  endif
 #endif
     union {
       // For Interrupt messages, a guess at what the *other* side's stack depth
       // is.
       uint32_t interrupt_remote_stack_depth_guess;
 
       // For RPC and Urgent messages, a transaction ID for message ordering.
       int32_t txid;
@@ -374,21 +367,31 @@ class Message : public mojo::core::ports
     int32_t seqno;
     // Offset of the message's footer in the payload, or -1 if invalid.
     int32_t footer_offset;
   };
 
   Header* header() { return headerT<Header>(); }
   const Header* header() const { return headerT<Header>(); }
 
-  // The set of file handles which are attached to this message.
-  //
-  // Mutable, as this array can be mutated during `ReadHandle` when
-  // deserializing a message.
-  mutable nsTArray<mozilla::UniqueFileHandle> attached_handles_;
+#if defined(OS_POSIX)
+  // The set of file descriptors associated with this message.
+  RefPtr<FileDescriptorSet> file_descriptor_set_;
+
+  // Ensure that a FileDescriptorSet is allocated
+  void EnsureFileDescriptorSet();
+
+  FileDescriptorSet* file_descriptor_set() {
+    EnsureFileDescriptorSet();
+    return file_descriptor_set_.get();
+  }
+  const FileDescriptorSet* file_descriptor_set() const {
+    return file_descriptor_set_.get();
+  }
+#endif
 
   // The set of mojo ports which are attached to this message.
   //
   // Mutable, as this array can be mutated during `ConsumePort` when
   // deserializing a message.
   mutable nsTArray<mozilla::ipc::ScopedPort> attached_ports_;
 
   mozilla::TimeStamp create_time_;
--- a/ipc/chromium/src/chrome/common/ipc_message_utils.h
+++ b/ipc/chromium/src/chrome/common/ipc_message_utils.h
@@ -16,16 +16,19 @@
 #include "base/basictypes.h"
 #include "base/compiler_specific.h"
 #include "base/logging.h"
 #include "base/pickle.h"
 #include "base/string_util.h"
 #include "build/build_config.h"
 #include "chrome/common/ipc_message.h"
 
+#if defined(OS_POSIX)
+#  include "chrome/common/file_descriptor_set_posix.h"
+#endif
 #if defined(OS_WIN)
 #  include <windows.h>
 #endif
 
 template <typename T>
 class RefPtr;
 template <typename T>
 class nsCOMPtr;
@@ -378,52 +381,66 @@ struct ParamTraitsWindows<HWND> {
 };
 #endif  // defined(OS_WIN)
 
 // Various ipc/chromium types.
 
 template <class P>
 struct ParamTraitsIPC : ParamTraitsWindows<P> {};
 
-// `UniqueFileHandle` may be serialized over IPC channels. On the receiving
-// side, the UniqueFileHandle is a valid duplicate of the handle which was
-// transmitted.
+#if defined(OS_POSIX)
+// FileDescriptors may be serialised over IPC channels on POSIX. On the
+// receiving side, the FileDescriptor is a valid duplicate of the file
+// descriptor which was transmitted: *it is not just a copy of the integer like
+// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In
+// this case, the receiving end will see a value of -1. *Zero is a valid file
+// descriptor*.
 //
-// When sending a UniqueFileHandle, the handle must be valid at the time of
-// transmission. As transmission is asynchronous, this requires passing
-// ownership of the handle to IPC.
+// The received file descriptor will have the |auto_close| flag set to true. The
+// code which handles the message is responsible for taking ownership of it.
+// File descriptors are OS resources and must be closed when no longer needed.
 //
-// A UniqueFileHandle may only be read once. After it has been read once, it
-// will be consumed, and future reads will return an invalid handle.
+// When sending a file descriptor, the file descriptor must be valid at the time
+// of transmission. Since transmission is not synchronous, one should consider
+// dup()ing any file descriptors to be transmitted and setting the |auto_close|
+// flag, which causes the file descriptor to be closed after writing.
 template <>
-struct ParamTraitsIPC<mozilla::UniqueFileHandle> {
-  typedef mozilla::UniqueFileHandle param_type;
-  static void Write(Message* m, param_type&& p) {
-    const bool valid = p != nullptr;
+struct ParamTraitsIPC<base::FileDescriptor> {
+  typedef base::FileDescriptor param_type;
+  static void Write(Message* m, const param_type& p) {
+    const bool valid = p.fd >= 0;
     WriteParam(m, valid);
+
     if (valid) {
-      if (!m->WriteFileHandle(std::move(p))) {
-        NOTREACHED() << "Too many file handles for one message!";
+      if (!m->WriteFileDescriptor(p)) {
+        NOTREACHED() << "Too many file descriptors for one message!";
       }
     }
   }
   static bool Read(const Message* m, PickleIterator* iter, param_type* r) {
     bool valid;
-    if (!ReadParam(m, iter, &valid)) {
-      return false;
-    }
+    if (!ReadParam(m, iter, &valid)) return false;
 
     if (!valid) {
-      *r = nullptr;
+      r->fd = -1;
+      r->auto_close = false;
       return true;
     }
 
-    return m->ConsumeFileHandle(iter, r);
+    return m->ReadFileDescriptor(iter, r);
+  }
+  static void Log(const param_type& p, std::wstring* l) {
+    if (p.auto_close) {
+      l->append(StringPrintf(L"FD(%d auto-close)", p.fd));
+    } else {
+      l->append(StringPrintf(L"FD(%d)", p.fd));
+    }
   }
 };
+#endif  // defined(OS_POSIX)
 
 // Mozilla-specific types.
 
 template <class P>
 struct ParamTraitsMozilla : ParamTraitsIPC<P> {};
 
 template <>
 struct ParamTraitsMozilla<nsresult> {
--- a/ipc/glue/AutoTransportDescriptor.cpp
+++ b/ipc/glue/AutoTransportDescriptor.cpp
@@ -28,19 +28,19 @@ AutoTransportDescriptor& AutoTransportDe
 
 AutoTransportDescriptor::~AutoTransportDescriptor() {
   if (mValid) {
     CloseDescriptor(mTransport);
   }
 }
 
 Result<std::pair<AutoTransportDescriptor, AutoTransportDescriptor>, nsresult>
-AutoTransportDescriptor::Create() {
+AutoTransportDescriptor::Create(int32_t aProcIdOne) {
   TransportDescriptor one, two;
-  MOZ_TRY(CreateTransport(&one, &two));
+  MOZ_TRY(CreateTransport(aProcIdOne, &one, &two));
   return std::pair{AutoTransportDescriptor(one), AutoTransportDescriptor(two)};
 }
 
 AutoTransportDescriptor AutoTransportDescriptor::Duplicate() const {
   if (mValid) {
     return AutoTransportDescriptor{DuplicateDescriptor(mTransport)};
   }
   return {};
--- a/ipc/glue/AutoTransportDescriptor.h
+++ b/ipc/glue/AutoTransportDescriptor.h
@@ -21,17 +21,17 @@ class AutoTransportDescriptor final {
       : mTransport(aTransport), mValid(true) {}
   ~AutoTransportDescriptor();
 
   AutoTransportDescriptor(AutoTransportDescriptor&& aOther) noexcept;
   AutoTransportDescriptor& operator=(AutoTransportDescriptor&& aOther) noexcept;
 
   static Result<std::pair<AutoTransportDescriptor, AutoTransportDescriptor>,
                 nsresult>
-  Create();
+  Create(int32_t aProcIdOne);
 
   AutoTransportDescriptor Duplicate() const;
 
   // NOTE: This will consume this transport descriptor, making it invalid.
   UniquePtr<Transport> Open(Transport::Mode aMode);
 
   explicit operator bool() const { return mValid; }
 
--- a/ipc/glue/CrossProcessMutex.h
+++ b/ipc/glue/CrossProcessMutex.h
@@ -5,19 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_CrossProcessMutex_h
 #define mozilla_CrossProcessMutex_h
 
 #include "base/process.h"
 #include "mozilla/Mutex.h"
 
-#if defined(OS_WIN)
-#  include "mozilla/UniquePtrExtensions.h"
-#endif
 #if !defined(OS_WIN) && !defined(OS_NETBSD) && !defined(OS_OPENBSD)
 #  include <pthread.h>
 #  include "mozilla/ipc/SharedMemoryBasic.h"
 #  include "mozilla/Atomics.h"
 #endif
 
 namespace IPC {
 template <typename T>
@@ -32,17 +29,17 @@ struct ParamTraits;
 //  - CrossProcessMutexAutoLock, an RAII class for ensuring that Mutexes are
 //    properly locked and unlocked
 //
 // Using CrossProcessMutexAutoLock/CrossProcessMutexAutoUnlock is MUCH
 // preferred to making bare calls to CrossProcessMutex.Lock and Unlock.
 //
 namespace mozilla {
 #if defined(OS_WIN)
-typedef mozilla::UniqueFileHandle CrossProcessMutexHandle;
+typedef HANDLE CrossProcessMutexHandle;
 #elif !defined(OS_NETBSD) && !defined(OS_OPENBSD)
 typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessMutexHandle;
 #else
 // Stub for other platforms. We can't use uintptr_t here since different
 // processes could disagree on its size.
 typedef uintptr_t CrossProcessMutexHandle;
 #endif
 
--- a/ipc/glue/CrossProcessMutex_posix.cpp
+++ b/ipc/glue/CrossProcessMutex_posix.cpp
@@ -70,18 +70,17 @@ CrossProcessMutex::CrossProcessMutex(con
 CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle aHandle)
     : mMutex(nullptr), mCount(nullptr) {
   mSharedBuffer = new ipc::SharedMemoryBasic;
 
   if (!mSharedBuffer->IsHandleValid(aHandle)) {
     MOZ_CRASH();
   }
 
-  if (!mSharedBuffer->SetHandle(std::move(aHandle),
-                                ipc::SharedMemory::RightsReadWrite)) {
+  if (!mSharedBuffer->SetHandle(aHandle, ipc::SharedMemory::RightsReadWrite)) {
     MOZ_CRASH();
   }
 
   if (!mSharedBuffer->Map(sizeof(MutexData))) {
     MOZ_CRASH();
   }
 
   MutexData* data = static_cast<MutexData*>(mSharedBuffer->memory());
--- a/ipc/glue/CrossProcessMutex_windows.cpp
+++ b/ipc/glue/CrossProcessMutex_windows.cpp
@@ -25,20 +25,20 @@ CrossProcessMutex::CrossProcessMutex(con
   if (!mMutex) {
     MOZ_CRASH("This shouldn't happen - failed to create mutex!");
   }
   MOZ_COUNT_CTOR(CrossProcessMutex);
 }
 
 CrossProcessMutex::CrossProcessMutex(CrossProcessMutexHandle aHandle) {
   DWORD flags;
-  if (!::GetHandleInformation(aHandle.get(), &flags)) {
+  if (!::GetHandleInformation(aHandle, &flags)) {
     MOZ_CRASH("Attempt to construct a mutex from an invalid handle!");
   }
-  mMutex = aHandle.release();
+  mMutex = aHandle;
   MOZ_COUNT_CTOR(CrossProcessMutex);
 }
 
 CrossProcessMutex::~CrossProcessMutex() {
   NS_ASSERTION(mMutex, "Improper construction of mutex or double free.");
   ::CloseHandle(mMutex);
   MOZ_COUNT_DTOR(CrossProcessMutex);
 }
@@ -51,16 +51,19 @@ void CrossProcessMutex::Lock() {
 void CrossProcessMutex::Unlock() {
   NS_ASSERTION(mMutex, "Improper construction of mutex.");
   ::ReleaseMutex(mMutex);
 }
 
 CrossProcessMutexHandle CrossProcessMutex::ShareToProcess(
     base::ProcessId aTargetPid) {
   HANDLE newHandle;
-  if (!::DuplicateHandle(GetCurrentProcess(), mMutex, GetCurrentProcess(),
-                         &newHandle, 0, false, DUPLICATE_SAME_ACCESS)) {
+  bool succeeded = ipc::DuplicateHandle(mMutex, aTargetPid, &newHandle, 0,
+                                        DUPLICATE_SAME_ACCESS);
+
+  if (!succeeded) {
     return nullptr;
   }
-  return mozilla::UniqueFileHandle(newHandle);
+
+  return newHandle;
 }
 
 }  // namespace mozilla
--- a/ipc/glue/CrossProcessSemaphore.h
+++ b/ipc/glue/CrossProcessSemaphore.h
@@ -6,19 +6,16 @@
 
 #ifndef mozilla_CrossProcessSemaphore_h
 #define mozilla_CrossProcessSemaphore_h
 
 #include "base/process.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Maybe.h"
 
-#if defined(OS_WIN)
-#  include "mozilla/UniquePtrExtensions.h"
-#endif
 #if !defined(OS_WIN) && !defined(OS_MACOSX)
 #  include <pthread.h>
 #  include <semaphore.h>
 #  include "mozilla/ipc/SharedMemoryBasic.h"
 #  include "mozilla/Atomics.h"
 #endif
 
 namespace IPC {
@@ -33,17 +30,17 @@ struct ParamTraits;
 namespace mozilla {
 
 template <typename T>
 inline bool IsHandleValid(const T& handle) {
   return bool(handle);
 }
 
 #if defined(OS_WIN)
-typedef mozilla::UniqueFileHandle CrossProcessSemaphoreHandle;
+typedef HANDLE CrossProcessSemaphoreHandle;
 #elif !defined(OS_MACOSX)
 typedef mozilla::ipc::SharedMemoryBasic::Handle CrossProcessSemaphoreHandle;
 
 template <>
 inline bool IsHandleValid<CrossProcessSemaphoreHandle>(
     const CrossProcessSemaphoreHandle& handle) {
   return !(handle == mozilla::ipc::SharedMemoryBasic::NULLHandle());
 }
--- a/ipc/glue/CrossProcessSemaphore_posix.cpp
+++ b/ipc/glue/CrossProcessSemaphore_posix.cpp
@@ -62,18 +62,17 @@ CrossProcessSemaphore* CrossProcessSemap
 CrossProcessSemaphore* CrossProcessSemaphore::Create(
     CrossProcessSemaphoreHandle aHandle) {
   RefPtr<ipc::SharedMemoryBasic> sharedBuffer = new ipc::SharedMemoryBasic;
 
   if (!sharedBuffer->IsHandleValid(aHandle)) {
     return nullptr;
   }
 
-  if (!sharedBuffer->SetHandle(std::move(aHandle),
-                               ipc::SharedMemory::RightsReadWrite)) {
+  if (!sharedBuffer->SetHandle(aHandle, ipc::SharedMemory::RightsReadWrite)) {
     return nullptr;
   }
 
   if (!sharedBuffer->Map(sizeof(SemaphoreData))) {
     return nullptr;
   }
 
   sharedBuffer->CloseHandle();
--- a/ipc/glue/CrossProcessSemaphore_windows.cpp
+++ b/ipc/glue/CrossProcessSemaphore_windows.cpp
@@ -30,21 +30,21 @@ CrossProcessSemaphore* CrossProcessSemap
   }
   return new CrossProcessSemaphore(semaphore);
 }
 
 /* static */
 CrossProcessSemaphore* CrossProcessSemaphore::Create(
     CrossProcessSemaphoreHandle aHandle) {
   DWORD flags;
-  if (!::GetHandleInformation(aHandle.get(), &flags)) {
+  if (!::GetHandleInformation(aHandle, &flags)) {
     return nullptr;
   }
 
-  return new CrossProcessSemaphore(aHandle.release());
+  return new CrossProcessSemaphore(aHandle);
 }
 
 CrossProcessSemaphore::CrossProcessSemaphore(HANDLE aSemaphore)
     : mSemaphore(aSemaphore) {
   MOZ_COUNT_CTOR(CrossProcessSemaphore);
 }
 
 CrossProcessSemaphore::~CrossProcessSemaphore() {
@@ -63,22 +63,21 @@ bool CrossProcessSemaphore::Wait(const M
 void CrossProcessSemaphore::Signal() {
   MOZ_ASSERT(mSemaphore, "Improper construction of semaphore.");
   ::ReleaseSemaphore(mSemaphore, 1, nullptr);
 }
 
 CrossProcessSemaphoreHandle CrossProcessSemaphore::ShareToProcess(
     base::ProcessId aTargetPid) {
   HANDLE newHandle;
-  bool succeeded =
-      ::DuplicateHandle(GetCurrentProcess(), mSemaphore, GetCurrentProcess(),
-                        &newHandle, 0, false, DUPLICATE_SAME_ACCESS);
+  bool succeeded = ipc::DuplicateHandle(mSemaphore, aTargetPid, &newHandle, 0,
+                                        DUPLICATE_SAME_ACCESS);
 
   if (!succeeded) {
     return nullptr;
   }
 
-  return UniqueFileHandle(newHandle);
+  return newHandle;
 }
 
 void CrossProcessSemaphore::CloseHandle() {}
 
 }  // namespace mozilla
--- a/ipc/glue/FileDescriptor.cpp
+++ b/ipc/glue/FileDescriptor.cpp
@@ -30,32 +30,67 @@ FileDescriptor::FileDescriptor(FileDescr
     : mHandle(std::move(aOther.mHandle)) {}
 
 FileDescriptor::FileDescriptor(PlatformHandleType aHandle)
     : mHandle(Clone(aHandle)) {}
 
 FileDescriptor::FileDescriptor(UniquePlatformHandle&& aHandle)
     : mHandle(std::move(aHandle)) {}
 
+FileDescriptor::FileDescriptor(const IPDLPrivate&, const PickleType& aPickle) {
+#ifdef XP_WIN
+  mHandle.reset(aPickle);
+#else
+  mHandle.reset(aPickle.fd);
+#endif
+}
+
 FileDescriptor::~FileDescriptor() = default;
 
 FileDescriptor& FileDescriptor::operator=(const FileDescriptor& aOther) {
   if (this != &aOther) {
     mHandle = Clone(aOther.mHandle.get());
   }
   return *this;
 }
 
 FileDescriptor& FileDescriptor::operator=(FileDescriptor&& aOther) {
   if (this != &aOther) {
     mHandle = std::move(aOther.mHandle);
   }
   return *this;
 }
 
+FileDescriptor::PickleType FileDescriptor::ShareTo(
+    const FileDescriptor::IPDLPrivate&,
+    FileDescriptor::ProcessId aTargetPid) const {
+  PlatformHandleType newHandle;
+#ifdef XP_WIN
+  if (IsValid()) {
+    if (mozilla::ipc::DuplicateHandle(mHandle.get(), aTargetPid, &newHandle, 0,
+                                      DUPLICATE_SAME_ACCESS)) {
+      return newHandle;
+    }
+    NS_WARNING("Failed to duplicate file handle for other process!");
+  }
+  return INVALID_HANDLE_VALUE;
+#else  // XP_WIN
+  if (IsValid()) {
+    newHandle = dup(mHandle.get());
+    if (newHandle >= 0) {
+      return base::FileDescriptor(newHandle, /* auto_close */ true);
+    }
+    NS_WARNING("Failed to duplicate file handle for other process!");
+  }
+  return base::FileDescriptor();
+#endif
+
+  MOZ_CRASH("Must not get here!");
+}
+
 bool FileDescriptor::IsValid() const { return mHandle != nullptr; }
 
 FileDescriptor::UniquePlatformHandle FileDescriptor::ClonePlatformHandle()
     const {
   return Clone(mHandle.get());
 }
 
 FileDescriptor::UniquePlatformHandle FileDescriptor::TakePlatformHandle() {
@@ -90,29 +125,40 @@ FileDescriptor::UniquePlatformHandle Fil
 #endif
   NS_WARNING("Failed to duplicate file handle for current process!");
   return UniqueFileHandle();
 }
 
 void IPDLParamTraits<FileDescriptor>::Write(IPC::Message* aMsg,
                                             IProtocol* aActor,
                                             const FileDescriptor& aParam) {
-  WriteIPDLParam(aMsg, aActor, aParam.ClonePlatformHandle());
+#ifdef XP_WIN
+  FileDescriptor::PickleType pfd =
+      aParam.ShareTo(FileDescriptor::IPDLPrivate(), aActor->OtherPid());
+#else
+  // The pid returned by OtherPID() is only required for Windows to
+  // send file descriptors.  For the use case of the fork server,
+  // aActor is always null.  Since it is only for the special case of
+  // Windows, here we skip it for other platforms.
+  FileDescriptor::PickleType pfd =
+      aParam.ShareTo(FileDescriptor::IPDLPrivate(), 0);
+#endif
+  WriteIPDLParam(aMsg, aActor, pfd);
 }
 
 bool IPDLParamTraits<FileDescriptor>::Read(const IPC::Message* aMsg,
                                            PickleIterator* aIter,
                                            IProtocol* aActor,
                                            FileDescriptor* aResult) {
-  UniqueFileHandle handle;
-  if (!ReadIPDLParam(aMsg, aIter, aActor, &handle)) {
+  FileDescriptor::PickleType pfd;
+  if (!ReadIPDLParam(aMsg, aIter, aActor, &pfd)) {
     return false;
   }
 
-  *aResult = FileDescriptor(std::move(handle));
+  *aResult = FileDescriptor(FileDescriptor::IPDLPrivate(), pfd);
   if (!aResult->IsValid()) {
     printf_stderr("IPDL protocol Error: Received an invalid file descriptor\n");
   }
   return true;
 }
 
 }  // namespace ipc
 }  // namespace mozilla
--- a/ipc/glue/FileDescriptor.h
+++ b/ipc/glue/FileDescriptor.h
@@ -6,34 +6,48 @@
 
 #ifndef mozilla_ipc_FileDescriptor_h
 #define mozilla_ipc_FileDescriptor_h
 
 #include "base/basictypes.h"
 #include "base/process.h"
 #include "mozilla/UniquePtrExtensions.h"
 
+#ifdef XP_UNIX
+#  include "base/file_descriptor_posix.h"
+#endif
+
 namespace mozilla {
 namespace ipc {
 
 // This class is used by IPDL to share file descriptors across processes. When
-// sending a FileDescriptor, IPDL will transfer a duplicate of the handle into
-// the remote process.
+// sending a FileDescriptor IPDL will first duplicate a platform-specific file
+// handle type ('PlatformHandleType') into a handle that is valid in the other
+// process. Then IPDL will convert the duplicated handle into a type suitable
+// for pickling ('PickleType') and then send that through the IPC pipe. In the
+// receiving process the pickled data is converted into a platform-specific file
+// handle and then returned to the receiver.
 //
 // To use this class add 'FileDescriptor' as an argument in the IPDL protocol
-// and then pass a file descriptor from C++ to the Send method. The Recv method
-// will receive a FileDescriptor& on which PlatformHandle() can be called to
-// return the platform file handle.
+// and then pass a file descriptor from C++ to the Call/Send method. The
+// Answer/Recv method will receive a FileDescriptor& on which PlatformHandle()
+// can be called to return the platform file handle.
 class FileDescriptor {
  public:
   typedef base::ProcessId ProcessId;
 
   using UniquePlatformHandle = mozilla::UniqueFileHandle;
   using PlatformHandleType = UniquePlatformHandle::ElementType;
 
+#ifdef XP_WIN
+  typedef PlatformHandleType PickleType;
+#else
+  typedef base::FileDescriptor PickleType;
+#endif
+
   // This should only ever be created by IPDL.
   struct IPDLPrivate {};
 
   // Represents an invalid handle.
   FileDescriptor();
 
   // Copy constructor will duplicate a new handle.
   FileDescriptor(const FileDescriptor& aOther);
@@ -41,22 +55,31 @@ class FileDescriptor {
   FileDescriptor(FileDescriptor&& aOther);
 
   // This constructor will duplicate a new handle.
   // The caller still have to close aHandle.
   explicit FileDescriptor(PlatformHandleType aHandle);
 
   explicit FileDescriptor(UniquePlatformHandle&& aHandle);
 
+  // This constructor WILL NOT duplicate the handle.
+  // FileDescriptor takes the ownership from IPC message.
+  FileDescriptor(const IPDLPrivate&, const PickleType& aPickle);
+
   ~FileDescriptor();
 
   FileDescriptor& operator=(const FileDescriptor& aOther);
 
   FileDescriptor& operator=(FileDescriptor&& aOther);
 
+  // Performs platform-specific actions to duplicate mHandle in the other
+  // process (e.g. dup() on POSIX, DuplicateHandle() on Windows). Returns a
+  // pickled value that can be passed to the other process via IPC.
+  PickleType ShareTo(const IPDLPrivate&, ProcessId aTargetPid) const;
+
   // Tests mHandle against a well-known invalid platform-specific file handle
   // (e.g. -1 on POSIX, INVALID_HANDLE_VALUE on Windows).
   bool IsValid() const;
 
   // Returns a duplicated handle, it is caller's responsibility to close the
   // handle.
   UniquePlatformHandle ClonePlatformHandle() const;
 
--- a/ipc/glue/IdleSchedulerChild.cpp
+++ b/ipc/glue/IdleSchedulerChild.cpp
@@ -27,17 +27,17 @@ IdleSchedulerChild::~IdleSchedulerChild(
 
 void IdleSchedulerChild::Init(IdlePeriodState* aIdlePeriodState) {
   mIdlePeriodState = aIdlePeriodState;
 
   RefPtr<IdleSchedulerChild> scheduler = this;
   auto resolve =
       [&](Tuple<mozilla::Maybe<SharedMemoryHandle>, uint32_t>&& aResult) {
         if (Get<0>(aResult)) {
-          mActiveCounter.SetHandle(std::move(*Get<0>(aResult)), false);
+          mActiveCounter.SetHandle(*Get<0>(aResult), false);
           mActiveCounter.Map(sizeof(int32_t));
           mChildId = Get<1>(aResult);
           if (mChildId && mIdlePeriodState && mIdlePeriodState->IsActive()) {
             SetActive();
           }
         }
       };
 
--- a/ipc/glue/IdleSchedulerParent.cpp
+++ b/ipc/glue/IdleSchedulerParent.cpp
@@ -186,35 +186,36 @@ IPCResult IdleSchedulerParent::RecvInitF
               ->memory())[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER] =
           static_cast<int32_t>(sMaxConcurrentIdleTasksInChildProcesses);
     } else {
       delete sActiveChildCounter;
       sActiveChildCounter = nullptr;
     }
   }
   Maybe<SharedMemoryHandle> activeCounter;
-  if (SharedMemoryHandle handle =
-          sActiveChildCounter ? sActiveChildCounter->CloneHandle() : nullptr) {
-    activeCounter.emplace(std::move(handle));
+  SharedMemoryHandle handle;
+  if (sActiveChildCounter &&
+      sActiveChildCounter->ShareToProcess(OtherPid(), &handle)) {
+    activeCounter.emplace(handle);
   }
 
   uint32_t unusedId = 0;
   for (uint32_t i = 0; i < NS_IDLE_SCHEDULER_COUNTER_ARRAY_LENGHT; ++i) {
     if (!sInUseChildCounters[i]) {
       sInUseChildCounters[i] = true;
       unusedId = i;
       break;
     }
   }
 
   // If there wasn't an empty item, we'll fallback to 0.
   mChildId = unusedId;
 
-  aResolve(Tuple<mozilla::Maybe<SharedMemoryHandle>&&, const uint32_t&>(
-      std::move(activeCounter), mChildId));
+  aResolve(Tuple<const mozilla::Maybe<SharedMemoryHandle>&, const uint32_t&>(
+      activeCounter, mChildId));
   return IPC_OK();
 }
 
 IPCResult IdleSchedulerParent::RecvRequestIdleTime(uint64_t aId,
                                                    TimeDuration aBudget) {
   MOZ_ASSERT(aBudget);
   MOZ_ASSERT(IsNotDoingIdleTask());
 
--- a/ipc/glue/MiniTransceiver.cpp
+++ b/ipc/glue/MiniTransceiver.cpp
@@ -33,58 +33,56 @@ MiniTransceiver::MiniTransceiver(int aFd
 }
 
 namespace {
 
 /**
  * Initialize the IO vector for sending data and the control buffer for sending
  * FDs.
  */
-static void InitMsgHdr(msghdr* aHdr, int aIOVSize, size_t aMaxNumFds) {
+static void InitMsgHdr(msghdr* aHdr, int aIOVSize, int aMaxNumFds) {
   aHdr->msg_name = nullptr;
   aHdr->msg_namelen = 0;
   aHdr->msg_flags = 0;
 
   // Prepare the IO vector to receive the content of message.
-  auto* iov = new iovec[aIOVSize];
+  auto iov = new iovec[aIOVSize];
   aHdr->msg_iov = iov;
   aHdr->msg_iovlen = aIOVSize;
 
   // Prepare the control buffer to receive file descriptors.
-  auto* cbuf = new char[CMSG_SPACE(sizeof(int) * aMaxNumFds)];
+  auto cbuf = new char[CMSG_SPACE(sizeof(int) * aMaxNumFds)];
   aHdr->msg_control = cbuf;
   aHdr->msg_controllen = CMSG_SPACE(sizeof(int) * aMaxNumFds);
 }
 
 /**
  * Delete resources allocated by InitMsgHdr().
  */
 static void DeinitMsgHdr(msghdr* aHdr) {
   delete aHdr->msg_iov;
   delete static_cast<char*>(aHdr->msg_control);
 }
 
 }  // namespace
 
 void MiniTransceiver::PrepareFDs(msghdr* aHdr, IPC::Message& aMsg) {
   // Set control buffer to send file descriptors of the Message.
-  size_t num_fds = aMsg.attached_handles_.Length();
+  int num_fds = aMsg.file_descriptor_set()->size();
 
   cmsghdr* cmsg = CMSG_FIRSTHDR(aHdr);
   cmsg->cmsg_level = SOL_SOCKET;
   cmsg->cmsg_type = SCM_RIGHTS;
   cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
-  for (size_t i = 0; i < num_fds; ++i) {
-    reinterpret_cast<int*>(CMSG_DATA(cmsg))[i] =
-        aMsg.attached_handles_[i].get();
-  }
+  aMsg.file_descriptor_set()->GetDescriptors(
+      reinterpret_cast<int*>(CMSG_DATA(cmsg)));
 
   // This number will be sent in the header of the message. So, we
   // can check it at the other side.
-  aMsg.header()->num_handles = num_fds;
+  aMsg.header()->num_fds = num_fds;
 }
 
 size_t MiniTransceiver::PrepareBuffers(msghdr* aHdr, IPC::Message& aMsg) {
   // Set iovec to send for all buffers of the Message.
   iovec* iov = aHdr->msg_iov;
   size_t iovlen = 0;
   size_t bytes_to_send = 0;
   for (Pickle::BufferList::IterImpl iter(aMsg.Buffers()); !iter.Done();
@@ -108,20 +106,21 @@ bool MiniTransceiver::Send(IPC::Message&
   if (mState == STATE_SENDING) {
     MOZ_CRASH(
         "STATE_SENDING: It violates of request-response and no concurrent "
         "rules");
   }
   mState = STATE_SENDING;
 #endif
 
-  auto clean_fdset = MakeScopeExit([&] { aMsg.attached_handles_.Clear(); });
+  auto clean_fdset =
+      MakeScopeExit([&] { aMsg.file_descriptor_set()->CommitAll(); });
 
-  size_t num_fds = aMsg.attached_handles_.Length();
-  msghdr hdr{};
+  int num_fds = aMsg.file_descriptor_set()->size();
+  msghdr hdr;
   InitMsgHdr(&hdr, kMaxIOVecSize, num_fds);
 
   UniquePtr<msghdr, decltype(&DeinitMsgHdr)> uniq(&hdr, &DeinitMsgHdr);
 
   PrepareFDs(&hdr, aMsg);
   DebugOnly<size_t> bytes_to_send = PrepareBuffers(&hdr, aMsg);
 
   ssize_t bytes_written = HANDLE_EINTR(sendmsg(mFd, &hdr, 0));
@@ -223,29 +222,25 @@ bool MiniTransceiver::Recv(IPC::Message&
 
   if (!RecvData(databuf.get(), kMaxDataSize, &msgsz, all_fds, kMaxDataSize,
                 &num_all_fds)) {
     return false;
   }
 
   // Create Message from databuf & all_fds.
   UniquePtr<IPC::Message> msg = MakeUnique<IPC::Message>(databuf.get(), msgsz);
-  nsTArray<UniqueFileHandle> handles(num_all_fds);
-  for (unsigned i = 0; i < num_all_fds; ++i) {
-    handles.AppendElement(UniqueFileHandle(all_fds[i]));
-  }
-  msg->SetAttachedFileHandles(std::move(handles));
+  msg->file_descriptor_set()->SetDescriptors(all_fds, num_all_fds);
 
   if (mDataBufClear == DataBufferClear::AfterReceiving) {
     // Avoid content processes from reading the content of
     // messages.
     memset(databuf.get(), 0, msgsz);
   }
 
-  MOZ_ASSERT(msg->header()->num_handles == msg->attached_handles_.Length(),
+  MOZ_ASSERT(msg->header()->num_fds == msg->file_descriptor_set()->size(),
              "The number of file descriptors in the header is different from"
              " the number actually received");
 
   aMsg = std::move(*msg);
   return true;
 }
 
 }  // namespace mozilla::ipc
--- a/ipc/glue/NodeController.cpp
+++ b/ipc/glue/NodeController.cpp
@@ -158,100 +158,66 @@ bool NodeController::SendUserMessage(con
     return true;
   }
   NODECONTROLLER_WARNING("Failed to send message to port %s",
                          ToString(aPort.name()).c_str());
   return false;
 }
 
 auto NodeController::SerializeEventMessage(UniquePtr<Event> aEvent,
-                                           const NodeName* aRelayTarget,
                                            uint32_t aType)
     -> UniquePtr<IPC::Message> {
   UniquePtr<IPC::Message> message;
   if (aEvent->type() == Event::kUserMessage) {
     MOZ_DIAGNOSTIC_ASSERT(
         aType == EVENT_MESSAGE_TYPE,
         "Can only send a UserMessage in an EVENT_MESSAGE_TYPE");
     message = static_cast<UserMessageEvent*>(aEvent.get())
                   ->TakeMessage<IPC::Message>();
   } else {
     message = MakeUnique<IPC::Message>(MSG_ROUTING_CONTROL, aType);
   }
 
-  message->set_relay(aRelayTarget != nullptr);
-
-  size_t length = aEvent->GetSerializedSize();
-  if (aRelayTarget) {
-    length += sizeof(NodeName);
-  }
-
   // Use an intermediate buffer to serialize to avoid potential issues with the
   // segmented `IPC::Message` bufferlist. This should be fairly cheap, as the
   // majority of events are fairly small.
   Vector<char, 256, InfallibleAllocPolicy> buffer;
-  (void)buffer.initLengthUninitialized(length);
-  if (aRelayTarget) {
-    memcpy(buffer.begin(), aRelayTarget, sizeof(NodeName));
-    aEvent->Serialize(buffer.begin() + sizeof(NodeName));
-  } else {
-    aEvent->Serialize(buffer.begin());
-  }
+  (void)buffer.initLengthUninitialized(aEvent->GetSerializedSize());
+  aEvent->Serialize(buffer.begin());
 
   message->WriteFooter(buffer.begin(), buffer.length());
 
 #ifdef DEBUG
   // Debug-assert that we can read the same data back out of the buffer.
-  MOZ_ASSERT(message->FooterSize() == length);
+  MOZ_ASSERT(message->FooterSize() == aEvent->GetSerializedSize());
   Vector<char, 256, InfallibleAllocPolicy> buffer2;
   (void)buffer2.initLengthUninitialized(message->FooterSize());
   MOZ_ASSERT(message->ReadFooter(buffer2.begin(), buffer2.length(),
                                  /* truncate */ false));
   MOZ_ASSERT(!memcmp(buffer2.begin(), buffer.begin(), buffer.length()));
 #endif
 
   return message;
 }
 
-auto NodeController::DeserializeEventMessage(UniquePtr<IPC::Message> aMessage,
-                                             NodeName* aRelayTarget)
+auto NodeController::DeserializeEventMessage(UniquePtr<IPC::Message> aMessage)
     -> UniquePtr<Event> {
-  if (aMessage->is_relay() && !aRelayTarget) {
-    NODECONTROLLER_WARNING("Unexpected relay message '%s'", aMessage->name());
-    return nullptr;
-  }
-
   Vector<char, 256, InfallibleAllocPolicy> buffer;
   (void)buffer.initLengthUninitialized(aMessage->FooterSize());
   // Truncate the message when reading the footer, so that the extra footer data
   // is no longer present in the message. This allows future code to eventually
   // send the same `IPC::Message` to another process.
   if (!aMessage->ReadFooter(buffer.begin(), buffer.length(),
                             /* truncate */ true)) {
     NODECONTROLLER_WARNING("Call to ReadFooter for message '%s' Failed",
                            aMessage->name());
     return nullptr;
   }
 
-  UniquePtr<Event> event;
-  if (aRelayTarget) {
-    MOZ_ASSERT(aMessage->is_relay());
-    if (buffer.length() < sizeof(NodeName)) {
-      NODECONTROLLER_WARNING(
-          "Insufficient space in message footer for message '%s'",
-          aMessage->name());
-      return nullptr;
-    }
-    memcpy(aRelayTarget, buffer.begin(), sizeof(NodeName));
-    event = Event::Deserialize(buffer.begin() + sizeof(NodeName),
-                               buffer.length() - sizeof(NodeName));
-  } else {
-    event = Event::Deserialize(buffer.begin(), buffer.length());
-  }
-
+  UniquePtr<Event> event = Event::Deserialize(buffer.begin(), buffer.length());
   if (!event) {
     NODECONTROLLER_WARNING("Call to Event::Deserialize for message '%s' Failed",
                            aMessage->name());
     return nullptr;
   }
 
   if (event->type() == Event::kUserMessage) {
     static_cast<UserMessageEvent*>(event.get())
@@ -301,94 +267,65 @@ void NodeController::DropPeer(NodeName a
   mNode->LostConnectionToNode(aNodeName);
 }
 
 void NodeController::ForwardEvent(const NodeName& aNode,
                                   UniquePtr<Event> aEvent) {
   if (aNode == mName) {
     (void)mNode->AcceptEvent(std::move(aEvent));
   } else {
-    // On Windows, messages holding HANDLEs must be relayed via the broker
-    // process so it can transfer handle ownership.
-    bool needsRelay = false;
-#ifdef XP_WIN
-    if (!IsBroker() && aNode != kBrokerNodeName &&
-        aEvent->type() == Event::kUserMessage) {
-      auto* userEvent = static_cast<UserMessageEvent*>(aEvent.get());
-      needsRelay = userEvent->HasMessage() &&
-                   userEvent->GetMessage<IPC::Message>()->num_handles() > 0;
-    }
-#endif
-
-    UniquePtr<IPC::Message> message =
-        SerializeEventMessage(std::move(aEvent), needsRelay ? &aNode : nullptr);
-    MOZ_ASSERT(message->is_relay() == needsRelay,
-               "Message relay status set incorrectly");
+    UniquePtr<IPC::Message> message = SerializeEventMessage(std::move(aEvent));
 
     RefPtr<NodeChannel> peer;
     RefPtr<NodeChannel> broker;
     bool needsIntroduction = false;
     {
       auto state = mState.Lock();
 
       // Check if we know this peer. If we don't, we'll need to request an
       // introduction.
       peer = state->mPeers.Get(aNode);
-      if (!peer || needsRelay) {
+      if (!peer) {
         if (IsBroker()) {
           NODECONTROLLER_WARNING("Ignoring message '%s' to unknown peer %s",
                                  message->name(), ToString(aNode).c_str());
           return;
         }
 
         broker = state->mPeers.Get(kBrokerNodeName);
         if (!broker) {
           NODECONTROLLER_WARNING(
               "Ignoring message '%s' to peer %s due to a missing broker",
               message->name(), ToString(aNode).c_str());
           return;
         }
 
-        if (!needsRelay) {
-          auto& queue =
-              state->mPendingMessages.LookupOrInsertWith(aNode, [&]() {
-                needsIntroduction = true;
-                return Queue<UniquePtr<IPC::Message>, 64>{};
-              });
-          queue.Push(std::move(message));
-        }
+        auto& queue = state->mPendingMessages.LookupOrInsertWith(aNode, [&]() {
+          needsIntroduction = true;
+          return Queue<UniquePtr<IPC::Message>, 64>{};
+        });
+        queue.Push(std::move(message));
       }
     }
 
-    MOZ_ASSERT(!needsIntroduction || !needsRelay,
-               "Only one of the two should ever be set");
+    // TODO: On windows, we can relay the message through the broker process
+    // here to handle transferring handles without using sync IPC.
 
-    if (needsRelay) {
-#ifdef XP_WIN
-      NODECONTROLLER_LOG(LogLevel::Info,
-                         "Relaying message '%s' for peer %s due to %lu handles",
-                         message->name(), ToString(aNode).c_str(),
-                         message->num_handles());
-      MOZ_ASSERT(broker);
-      broker->SendEventMessage(std::move(message));
-#else
-      MOZ_ASSERT_UNREACHABLE("relaying messages is only supported on windows");
-#endif
-    } else if (needsIntroduction) {
+    if (needsIntroduction) {
       MOZ_ASSERT(broker);
       broker->RequestIntroduction(aNode);
     } else if (peer) {
       peer->SendEventMessage(std::move(message));
     }
   }
 }
 
 void NodeController::BroadcastEvent(UniquePtr<Event> aEvent) {
   UniquePtr<IPC::Message> message =
-      SerializeEventMessage(std::move(aEvent), nullptr, BROADCAST_MESSAGE_TYPE);
+      SerializeEventMessage(std::move(aEvent), BROADCAST_MESSAGE_TYPE);
 
   if (IsBroker()) {
     OnBroadcast(mName, std::move(message));
   } else if (RefPtr<NodeChannel> broker = GetNodeChannel(kBrokerNodeName)) {
     broker->Broadcast(std::move(message));
   } else {
     NODECONTROLLER_WARNING(
         "Trying to broadcast event, but no connection to broker");
@@ -410,103 +347,35 @@ void NodeController::PortStatusChanged(c
     static_cast<PortObserver*>(userData.get())->OnPortStatusChanged();
   }
 }
 
 void NodeController::OnEventMessage(const NodeName& aFromNode,
                                     UniquePtr<IPC::Message> aMessage) {
   AssertIOThread();
 
-  bool isRelay = aMessage->is_relay();
-  NodeName relayTarget;
-
-  UniquePtr<Event> event = DeserializeEventMessage(
-      std::move(aMessage), isRelay ? &relayTarget : nullptr);
+  UniquePtr<Event> event = DeserializeEventMessage(std::move(aMessage));
   if (!event) {
     NODECONTROLLER_WARNING("Invalid EventMessage from peer %s!",
                            ToString(aFromNode).c_str());
     DropPeer(aFromNode);
     return;
   }
 
-  NodeName fromNode = aFromNode;
-#ifdef XP_WIN
-  if (isRelay) {
-    if (event->type() != Event::kUserMessage) {
-      NODECONTROLLER_WARNING(
-          "Unexpected relay of non-UserMessage event from peer %s!",
-          ToString(aFromNode).c_str());
-      DropPeer(aFromNode);
-      return;
-    }
-
-    // If we're the broker, then we'll need to forward this message on to the
-    // true recipient. To do this, we re-serialize the message, passing along
-    // the original source node, and send it to the final node.
-    if (IsBroker()) {
-      UniquePtr<IPC::Message> message =
-          SerializeEventMessage(std::move(event), &aFromNode);
-      if (!message) {
-        NODECONTROLLER_WARNING(
-            "Relaying EventMessage from peer %s failed to re-serialize!",
-            ToString(aFromNode).c_str());
-        DropPeer(aFromNode);
-        return;
-      }
-      MOZ_ASSERT(message->is_relay(), "Message stopped being a relay message?");
-
-      NODECONTROLLER_LOG(
-          LogLevel::Info,
-          "Relaying message '%s' from peer %s to peer %s (%lu handles)",
-          message->name(), ToString(aFromNode).c_str(),
-          ToString(relayTarget).c_str(), message->num_handles());
-
-      RefPtr<NodeChannel> peer;
-      {
-        auto state = mState.Lock();
-        peer = state->mPeers.Get(relayTarget);
-      }
-      if (!peer) {
-        NODECONTROLLER_WARNING(
-            "Dropping relayed message from %s to unknown peer %s",
-            ToString(aFromNode).c_str(), ToString(relayTarget).c_str());
-        return;
-      }
-
-      peer->SendEventMessage(std::move(message));
-      return;
-    }
-
-    // Otherwise, we're the final recipient, so we can continue & process the
-    // message as usual.
-    if (aFromNode != kBrokerNodeName) {
-      NODECONTROLLER_WARNING(
-          "Unexpected relayed EventMessage from non-broker peer %s!",
-          ToString(aFromNode).c_str());
-      DropPeer(aFromNode);
-      return;
-    }
-    fromNode = relayTarget;
-
-    NODECONTROLLER_LOG(LogLevel::Info, "Got relayed message from peer %s",
-                       ToString(fromNode).c_str());
-  }
-#endif
-
   // If we're getting a requested port merge from another process, check to make
   // sure that we're expecting the request, and record that the merge has
   // arrived so we don't try to close the port on error.
   if (event->type() == Event::kMergePort) {
     // Check that the target port for the merge actually exists.
     auto targetPort = GetPort(event->port_name());
     if (!targetPort.is_valid()) {
       NODECONTROLLER_WARNING(
           "Unexpected MergePortEvent from peer %s for unknown port %s",
-          ToString(fromNode).c_str(), ToString(event->port_name()).c_str());
-      DropPeer(fromNode);
+          ToString(aFromNode).c_str(), ToString(event->port_name()).c_str());
+      DropPeer(aFromNode);
       return;
     }
 
     // Check if `targetPort` is in our pending merges entry for the given source
     // node. If this makes the `mPendingMerges` entry empty, remove it.
     bool expectingMerge = [&] {
       auto state = mState.Lock();
       auto pendingMerges = state->mPendingMerges.Lookup(aFromNode);
@@ -519,18 +388,18 @@ void NodeController::OnEventMessage(cons
         pendingMerges.Remove();
       }
       return removed != 0;
     }();
 
     if (!expectingMerge) {
       NODECONTROLLER_WARNING(
           "Unexpected MergePortEvent from peer %s for port %s",
-          ToString(fromNode).c_str(), ToString(event->port_name()).c_str());
-      DropPeer(fromNode);
+          ToString(aFromNode).c_str(), ToString(event->port_name()).c_str());
+      DropPeer(aFromNode);
       return;
     }
   }
 
   (void)mNode->AcceptEvent(std::move(event));
 }
 
 void NodeController::OnBroadcast(const NodeName& aFromNode,
@@ -646,17 +515,17 @@ void NodeController::OnRequestIntroducti
   if (!peerA || aName == mojo::core::ports::kInvalidNodeName) {
     NODECONTROLLER_WARNING("Invalid OnRequestIntroduction message from node %s",
                            ToString(aFromNode).c_str());
     DropPeer(aFromNode);
     return;
   }
 
   RefPtr<NodeChannel> peerB = GetNodeChannel(aName);
-  auto result = AutoTransportDescriptor::Create();
+  auto result = AutoTransportDescriptor::Create(peerA->OtherPid());
   if (!peerB || result.isErr()) {
     NODECONTROLLER_WARNING(
         "Rejecting introduction request from '%s' for unknown peer '%s'",
         ToString(aFromNode).c_str(), ToString(aName).c_str());
 
     // We don't know this peer, or ran into issues creating the descriptor! Send
     // an invalid introduction to content to clean up any pending outbound
     // messages.
--- a/ipc/glue/NodeController.h
+++ b/ipc/glue/NodeController.h
@@ -100,20 +100,18 @@ class NodeController final : public mojo
   // Called when the IO thread is torn down.
   static void CleanUp();
 
  private:
   explicit NodeController(const NodeName& aName);
   ~NodeController();
 
   UniquePtr<IPC::Message> SerializeEventMessage(
-      UniquePtr<Event> aEvent, const NodeName* aRelayTarget = nullptr,
-      uint32_t aType = EVENT_MESSAGE_TYPE);
-  UniquePtr<Event> DeserializeEventMessage(UniquePtr<IPC::Message> aMessage,
-                                           NodeName* aRelayTarget = nullptr);
+      UniquePtr<Event> aEvent, uint32_t aType = EVENT_MESSAGE_TYPE);
+  UniquePtr<Event> DeserializeEventMessage(UniquePtr<IPC::Message> aMessage);
 
   // Get the `NodeChannel` for the named node.
   already_AddRefed<NodeChannel> GetNodeChannel(const NodeName& aName);
 
   // Stop communicating with this peer. Must be called on the IO thread.
   void DropPeer(NodeName aNodeName);
 
   // Message Handlers
--- a/ipc/glue/PIdleScheduler.ipdl
+++ b/ipc/glue/PIdleScheduler.ipdl
@@ -1,16 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 protocol PBackground;
 using mozilla::TimeDuration from "mozilla/TimeStamp.h";
-[MoveOnly] using base::SharedMemoryHandle from "base/shared_memory.h";
+using base::SharedMemoryHandle from "base/shared_memory.h";
 namespace mozilla {
 namespace ipc {
 
 /**
  * PIdleScheduler is the protocol for cross-process idle scheduling.
  * Only child processes participate in the scheduling and parent process
  * can run its idle tasks whenever it needs to.
  *
--- a/ipc/glue/ProcessUtils.h
+++ b/ipc/glue/ProcessUtils.h
@@ -52,20 +52,22 @@ class SharedPreferenceDeserializer final
  public:
   SharedPreferenceDeserializer();
   ~SharedPreferenceDeserializer();
 
   bool DeserializeFromSharedMemory(uint64_t aPrefsHandle,
                                    uint64_t aPrefMapHandle, uint64_t aPrefsLen,
                                    uint64_t aPrefMapSize);
 
+  const base::SharedMemoryHandle& GetPrefsHandle() const;
   const FileDescriptor& GetPrefMapHandle() const;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SharedPreferenceDeserializer);
+  Maybe<base::SharedMemoryHandle> mPrefsHandle;
   Maybe<FileDescriptor> mPrefMapHandle;
   Maybe<size_t> mPrefsLen;
   Maybe<size_t> mPrefMapSize;
   base::SharedMemory mShmem;
 };
 
 #ifdef ANDROID
 // Android doesn't use -prefsHandle or -prefMapHandle. It gets those FDs
--- a/ipc/glue/ProcessUtils_common.cpp
+++ b/ipc/glue/ProcessUtils_common.cpp
@@ -104,20 +104,18 @@ SharedPreferenceDeserializer::SharedPref
 
 SharedPreferenceDeserializer::~SharedPreferenceDeserializer() {
   MOZ_COUNT_DTOR(SharedPreferenceDeserializer);
 }
 
 bool SharedPreferenceDeserializer::DeserializeFromSharedMemory(
     uint64_t aPrefsHandle, uint64_t aPrefMapHandle, uint64_t aPrefsLen,
     uint64_t aPrefMapSize) {
-  Maybe<base::SharedMemoryHandle> prefsHandle;
-
 #ifdef XP_WIN
-  prefsHandle = Some(UniqueFileHandle(HANDLE((uintptr_t)(aPrefsHandle))));
+  mPrefsHandle = Some(HANDLE((uintptr_t)(aPrefsHandle)));
   if (!aPrefsHandle) {
     return false;
   }
 
   FileDescriptor::UniquePlatformHandle handle(
       HANDLE((uintptr_t)(aPrefMapHandle)));
   if (!aPrefMapHandle) {
     return false;
@@ -134,49 +132,57 @@ bool SharedPreferenceDeserializer::Deser
   mPrefMapSize = Some((uintptr_t)(aPrefMapSize));
   if (!aPrefMapSize) {
     return false;
   }
 
 #ifdef ANDROID
   // Android is different; get the FD via gPrefsFd instead of a fixed fd.
   MOZ_RELEASE_ASSERT(gPrefsFd != -1);
-  prefsHandle = Some(UniqueFileHandle(gPrefsFd));
+  mPrefsHandle = Some(base::FileDescriptor(gPrefsFd, /* auto_close */ true));
 
   mPrefMapHandle.emplace(UniqueFileHandle(gPrefMapFd));
 #elif XP_UNIX
-  prefsHandle = Some(UniqueFileHandle(kPrefsFileDescriptor));
+  mPrefsHandle = Some(base::FileDescriptor(kPrefsFileDescriptor,
+                                           /* auto_close */ true));
 
   mPrefMapHandle.emplace(UniqueFileHandle(kPrefMapFileDescriptor));
 #endif
 
-  if (prefsHandle.isNothing() || mPrefsLen.isNothing() ||
+  if (mPrefsHandle.isNothing() || mPrefsLen.isNothing() ||
       mPrefMapHandle.isNothing() || mPrefMapSize.isNothing()) {
     return false;
   }
 
   // Init the shared-memory base preference mapping first, so that only changed
   // preferences wind up in heap memory.
   Preferences::InitSnapshot(mPrefMapHandle.ref(), *mPrefMapSize);
 
   // Set up early prefs from the shared memory.
-  if (!mShmem.SetHandle(std::move(*prefsHandle), /* read_only */ true)) {
+  if (!mShmem.SetHandle(*mPrefsHandle, /* read_only */ true)) {
     NS_ERROR("failed to open shared memory in the child");
     return false;
   }
   if (!mShmem.Map(*mPrefsLen)) {
     NS_ERROR("failed to map shared memory in the child");
     return false;
   }
   Preferences::DeserializePreferences(static_cast<char*>(mShmem.memory()),
                                       *mPrefsLen);
 
   return true;
 }
 
+const base::SharedMemoryHandle& SharedPreferenceDeserializer::GetPrefsHandle()
+    const {
+  MOZ_ASSERT(mPrefsHandle.isSome());
+
+  return mPrefsHandle.ref();
+}
+
 const FileDescriptor& SharedPreferenceDeserializer::GetPrefMapHandle() const {
   MOZ_ASSERT(mPrefMapHandle.isSome());
 
   return mPrefMapHandle.ref();
 }
 
 #ifdef XP_UNIX
 // On Unix, file descriptors are per-process. This value is used when mapping
@@ -243,23 +249,24 @@ bool ImportSharedJSInit(uint64_t aJsInit
 #endif
 
   size_t len = (uintptr_t)(aJsInitLen);
   if (!aJsInitLen) {
     return false;
   }
 
 #ifdef XP_UNIX
-  auto handle = UniqueFileHandle(kJSInitFileDescriptor);
+  auto handle = base::FileDescriptor(kJSInitFileDescriptor,
+                                     /* auto_close */ true);
 #endif
 
   // Initialize the shared memory with the file handle and size of the content
   // of the self-hosted Xdr.
   auto& shmem = xpc::SelfHostedShmem::GetSingleton();
-  if (!shmem.InitFromChild(std::move(handle), len)) {
+  if (!shmem.InitFromChild(handle, len)) {
     NS_ERROR("failed to open shared memory in the child");
     return false;
   }
 
   return true;
 }
 
 }  // namespace ipc
--- a/ipc/glue/ProtocolUtils.cpp
+++ b/ipc/glue/ProtocolUtils.cpp
@@ -60,16 +60,52 @@ IPCResult IPCResult::Fail(NotNull<IProto
                           const char* why) {
   // Calls top-level protocol to handle the error.
   nsPrintfCString errorMsg("%s %s\n", where, why);
   actor->GetIPCChannel()->Listener()->ProcessingError(
       HasResultCodes::MsgProcessingError, errorMsg.get());
   return IPCResult(false);
 }
 
+#if defined(XP_WIN)
+bool DuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
+                     HANDLE* aTargetHandle, DWORD aDesiredAccess,
+                     DWORD aOptions) {
+  // If our process is the target just duplicate the handle.
+  if (aTargetProcessId == base::GetCurrentProcId()) {
+    return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
+                               ::GetCurrentProcess(), aTargetHandle,
+                               aDesiredAccess, false, aOptions);
+  }
+
+#  if defined(MOZ_SANDBOX)
+  // Try the broker next (will fail if not sandboxed).
+  if (SandboxTarget::Instance()->BrokerDuplicateHandle(
+          aSourceHandle, aTargetProcessId, aTargetHandle, aDesiredAccess,
+          aOptions)) {
+    return true;
+  }
+#  endif
+
+  // Finally, see if we already have access to the process.
+  ScopedProcessHandle targetProcess(
+      OpenProcess(PROCESS_DUP_HANDLE, FALSE, aTargetProcessId));
+  if (!targetProcess) {
+    CrashReporter::AnnotateCrashReport(
+        CrashReporter::Annotation::IPCTransportFailureReason,
+        "Failed to open target process."_ns);
+    return false;
+  }
+
+  return !!::DuplicateHandle(::GetCurrentProcess(), aSourceHandle,
+                             targetProcess, aTargetHandle, aDesiredAccess,
+                             FALSE, aOptions);
+}
+#endif
+
 void AnnotateSystemError() {
   int64_t error = 0;
 #if defined(XP_WIN)
   error = ::GetLastError();
 #elif defined(OS_POSIX)
   error = errno;
 #endif
   if (error) {
--- a/ipc/glue/ProtocolUtils.h
+++ b/ipc/glue/ProtocolUtils.h
@@ -634,16 +634,26 @@ MOZ_NEVER_INLINE void ActorLookupError(c
 MOZ_NEVER_INLINE void MismatchedActorTypeError(const char* aActorDescription);
 
 MOZ_NEVER_INLINE void UnionTypeReadError(const char* aUnionName);
 
 MOZ_NEVER_INLINE void ArrayLengthReadError(const char* aElementName);
 
 MOZ_NEVER_INLINE void SentinelReadError(const char* aElementName);
 
+#if defined(XP_WIN)
+// This is a restricted version of Windows' DuplicateHandle() function
+// that works inside the sandbox and can send handles but not retrieve
+// them.  Unlike DuplicateHandle(), it takes a process ID rather than
+// a process handle.  It returns true on success, false otherwise.
+bool DuplicateHandle(HANDLE aSourceHandle, DWORD aTargetProcessId,
+                     HANDLE* aTargetHandle, DWORD aDesiredAccess,
+                     DWORD aOptions);
+#endif
+
 /**
  * Annotate the crash reporter with the error code from the most recent system
  * call. Returns the system error.
  */
 void AnnotateSystemError();
 
 // The ActorLifecycleProxy is a helper type used internally by IPC to maintain a
 // maybe-owning reference to an IProtocol object. For well-behaved actors
--- a/ipc/glue/SharedMemory.h
+++ b/ipc/glue/SharedMemory.h
@@ -116,32 +116,32 @@ class SharedMemory {
 
 template <typename HandleImpl>
 class SharedMemoryCommon : public SharedMemory {
  public:
   typedef HandleImpl Handle;
 
   virtual bool ShareToProcess(base::ProcessId aProcessId, Handle* aHandle) = 0;
   virtual bool IsHandleValid(const Handle& aHandle) const = 0;
-  virtual bool SetHandle(Handle aHandle, OpenRights aRights) = 0;
+  virtual bool SetHandle(const Handle& aHandle, OpenRights aRights) = 0;
 
   virtual bool ShareHandle(base::ProcessId aProcessId,
                            IPC::Message* aMessage) override {
     Handle handle;
     if (!ShareToProcess(aProcessId, &handle)) {
       return false;
     }
-    IPC::WriteParam(aMessage, std::move(handle));
+    IPC::WriteParam(aMessage, handle);
     return true;
   }
 
   virtual bool ReadHandle(const IPC::Message* aMessage,
                           PickleIterator* aIter) override {
     Handle handle;
     return IPC::ReadParam(aMessage, aIter, &handle) && IsHandleValid(handle) &&
-           SetHandle(std::move(handle), RightsReadWrite);
+           SetHandle(handle, RightsReadWrite);
   }
 };
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // ifndef mozilla_ipc_SharedMemory_h
--- a/ipc/glue/SharedMemoryBasic_android.cpp
+++ b/ipc/glue/SharedMemoryBasic_android.cpp
@@ -31,19 +31,19 @@ static void LogError(const char* what) {
 SharedMemoryBasic::SharedMemoryBasic()
     : mShmFd(-1), mMemory(nullptr), mOpenRights(RightsReadWrite) {}
 
 SharedMemoryBasic::~SharedMemoryBasic() {
   Unmap();
   CloseHandle();
 }
 
-bool SharedMemoryBasic::SetHandle(Handle aHandle, OpenRights aRights) {
+bool SharedMemoryBasic::SetHandle(const Handle& aHandle, OpenRights aRights) {
   MOZ_ASSERT(-1 == mShmFd, "Already Create()d");
-  mShmFd = aHandle.release();
+  mShmFd = aHandle.fd;
   mOpenRights = aRights;
   return true;
 }
 
 bool SharedMemoryBasic::Create(size_t aNbytes) {
   MOZ_ASSERT(-1 == mShmFd, "Already Create()d");
 
   // Carve a new instance off of /dev/ashmem
@@ -102,17 +102,18 @@ bool SharedMemoryBasic::ShareToProcess(b
   MOZ_ASSERT(mShmFd >= 0, "Should have been Create()d by now");
 
   int shmfdDup = dup(mShmFd);
   if (-1 == shmfdDup) {
     LogError("ShmemAndroid::ShareToProcess()");
     return false;
   }
 
-  *aNewHandle = mozilla::UniqueFileHandle(shmfdDup);
+  aNewHandle->fd = shmfdDup;
+  aNewHandle->auto_close = true;
   return true;
 }
 
 void SharedMemoryBasic::Unmap() {
   if (!mMemory) {
     return;
   }
 
--- a/ipc/glue/SharedMemoryBasic_android.h
+++ b/ipc/glue/SharedMemoryBasic_android.h
@@ -2,37 +2,38 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_SharedMemoryBasic_android_h
 #define mozilla_ipc_SharedMemoryBasic_android_h
 
+#include "base/file_descriptor_posix.h"
+
 #include "mozilla/ipc/SharedMemory.h"
-#include "mozilla/UniquePtrExtensions.h"
 
 #ifdef FUZZING
 #  include "mozilla/ipc/SharedMemoryFuzzer.h"
 #endif
 
 //
 // This is a low-level wrapper around platform shared memory.  Don't
 // use it directly; use Shmem allocated through IPDL interfaces.
 //
 
 namespace mozilla {
 namespace ipc {
 
 class SharedMemoryBasic final
-    : public SharedMemoryCommon<mozilla::UniqueFileHandle> {
+    : public SharedMemoryCommon<base::FileDescriptor> {
  public:
   SharedMemoryBasic();
 
-  virtual bool SetHandle(Handle aHandle, OpenRights aRights) override;
+  virtual bool SetHandle(const Handle& aHandle, OpenRights aRights) override;
 
   virtual bool Create(size_t aNbytes) override;
 
   virtual bool Map(size_t nBytes, void* fixed_address = nullptr) override;
 
   virtual void Unmap() override;
 
   virtual void CloseHandle() override;
@@ -47,17 +48,17 @@ class SharedMemoryBasic final
 
   virtual SharedMemoryType Type() const override { return TYPE_BASIC; }
 
   static Handle NULLHandle() { return Handle(); }
 
   static void* FindFreeAddressSpace(size_t aSize);
 
   virtual bool IsHandleValid(const Handle& aHandle) const override {
-    return aHandle != nullptr;
+    return aHandle.fd >= 0;
   }
 
   virtual bool ShareToProcess(base::ProcessId aProcessId,
                               Handle* aNewHandle) override;
 
  private:
   ~SharedMemoryBasic();
 
--- a/ipc/glue/SharedMemoryBasic_chromium.h
+++ b/ipc/glue/SharedMemoryBasic_chromium.h
@@ -24,19 +24,18 @@
 namespace mozilla {
 namespace ipc {
 
 class SharedMemoryBasic final
     : public SharedMemoryCommon<base::SharedMemoryHandle> {
  public:
   SharedMemoryBasic() = default;
 
-  virtual bool SetHandle(Handle aHandle, OpenRights aRights) override {
-    return mSharedMemory.SetHandle(std::move(aHandle),
-                                   aRights == RightsReadOnly);
+  virtual bool SetHandle(const Handle& aHandle, OpenRights aRights) override {
+    return mSharedMemory.SetHandle(aHandle, aRights == RightsReadOnly);
   }
 
   virtual bool Create(size_t aNbytes) override {
     bool ok = mSharedMemory.Create(aNbytes);
     if (ok) {
       Created(aNbytes);
     }
     return ok;
@@ -68,18 +67,20 @@ class SharedMemoryBasic final
   static Handle NULLHandle() { return base::SharedMemory::NULLHandle(); }
 
   virtual bool IsHandleValid(const Handle& aHandle) const override {
     return base::SharedMemory::IsHandleValid(aHandle);
   }
 
   virtual bool ShareToProcess(base::ProcessId aProcessId,
                               Handle* new_handle) override {
-    *new_handle = mSharedMemory.CloneHandle();
-    return base::SharedMemory::IsHandleValid(*new_handle);
+    base::SharedMemoryHandle handle;
+    bool ret = mSharedMemory.ShareToProcess(aProcessId, &handle);
+    if (ret) *new_handle = handle;
+    return ret;
   }
 
   static void* FindFreeAddressSpace(size_t size) {
     return base::SharedMemory::FindFreeAddressSpace(size);
   }
 
  private:
   ~SharedMemoryBasic() = default;
--- a/ipc/glue/SharedMemoryBasic_mach.h
+++ b/ipc/glue/SharedMemoryBasic_mach.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ipc_SharedMemoryBasic_mach_h
 #define mozilla_ipc_SharedMemoryBasic_mach_h
 
+#include "base/file_descriptor_posix.h"
 #include "base/process.h"
 
 #include "mozilla/ipc/SharedMemory.h"
 #include <mach/port.h>
 #include "chrome/common/mach_ipc_mac.h"
 
 #ifdef FUZZING
 #  include "mozilla/ipc/SharedMemoryFuzzer.h"
@@ -61,17 +62,17 @@ class SharedMemoryBasic final : public S
 
   static void Shutdown();
 
   static bool SendMachMessage(pid_t pid, MachSendMessage& message,
                               MachReceiveMessage* response);
 
   SharedMemoryBasic();
 
-  virtual bool SetHandle(Handle aHandle, OpenRights aRights) override;
+  virtual bool SetHandle(const Handle& aHandle, OpenRights aRights) override;
 
   virtual bool Create(size_t aNbytes) override;
 
   virtual bool Map(size_t nBytes, void* fixed_address = nullptr) override;
 
   virtual void Unmap() override;
 
   virtual void CloseHandle() override;
--- a/ipc/glue/SharedMemoryBasic_mach.mm
+++ b/ipc/glue/SharedMemoryBasic_mach.mm
@@ -504,17 +504,17 @@ bool SharedMemoryBasic::SendMachMessage(
 SharedMemoryBasic::SharedMemoryBasic()
     : mPort(MACH_PORT_NULL), mMemory(nullptr), mOpenRights(RightsReadWrite) {}
 
 SharedMemoryBasic::~SharedMemoryBasic() {
   Unmap();
   CloseHandle();
 }
 
-bool SharedMemoryBasic::SetHandle(Handle aHandle, OpenRights aRights) {
+bool SharedMemoryBasic::SetHandle(const Handle& aHandle, OpenRights aRights) {
   MOZ_ASSERT(mPort == MACH_PORT_NULL, "already initialized");
 
   mPort = aHandle;
   mOpenRights = aRights;
   return true;
 }
 
 static inline void* toPointer(mach_vm_address_t address) {
--- a/ipc/glue/Transport.h
+++ b/ipc/glue/Transport.h
@@ -15,23 +15,29 @@
 #elif OS_WIN
 #  include "mozilla/ipc/Transport_win.h"
 #endif
 #include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 namespace ipc {
 
+class FileDescriptor;
+
 typedef IPC::Channel Transport;
 
-nsresult CreateTransport(TransportDescriptor* aOne, TransportDescriptor* aTwo);
+nsresult CreateTransport(base::ProcessId aProcIdOne, TransportDescriptor* aOne,
+                         TransportDescriptor* aTwo);
 
 UniquePtr<Transport> OpenDescriptor(const TransportDescriptor& aTd,
                                     Transport::Mode aMode);
 
+UniquePtr<Transport> OpenDescriptor(const FileDescriptor& aFd,
+                                    Transport::Mode aMode);
+
 TransportDescriptor DuplicateDescriptor(const TransportDescriptor& aTd);
 
 void CloseDescriptor(const TransportDescriptor& aTd);
 
 }  // namespace ipc
 }  // namespace mozilla
 
 #endif  // mozilla_ipc_Transport_h
--- a/ipc/glue/Transport_posix.cpp
+++ b/ipc/glue/Transport_posix.cpp
@@ -15,17 +15,18 @@
 #include "mozilla/ipc/FileDescriptor.h"
 #include "mozilla/ipc/ProtocolUtils.h"
 
 using base::ProcessHandle;
 
 namespace mozilla {
 namespace ipc {
 
-nsresult CreateTransport(TransportDescriptor* aOne, TransportDescriptor* aTwo) {
+nsresult CreateTransport(base::ProcessId aProcIdOne, TransportDescriptor* aOne,
+                         TransportDescriptor* aTwo) {
   auto id = IPC::Channel::GenerateVerifiedChannelID();
   // Use MODE_SERVER to force creation of the socketpair
   Transport t(id, Transport::MODE_SERVER, nullptr);
   int fd1 = t.GetFileDescriptor();
   int fd2, dontcare;
   t.GetClientFileDescriptorMapping(&fd2, &dontcare);
   if (fd1 < 0 || fd2 < 0) {
     return NS_ERROR_TRANSPORT_INIT;
@@ -45,32 +46,38 @@ nsresult CreateTransport(TransportDescri
   }
 
   if (fd1 < 0 || fd2 < 0) {
     IGNORE_EINTR(close(fd1));
     IGNORE_EINTR(close(fd2));
     return NS_ERROR_DUPLICATE_HANDLE;
   }
 
-  aOne->mFd = fd1;
-  aTwo->mFd = fd2;
+  aOne->mFd = base::FileDescriptor(fd1, true /*close after sending*/);
+  aTwo->mFd = base::FileDescriptor(fd2, true /*close after sending*/);
   return NS_OK;
 }
 
 UniquePtr<Transport> OpenDescriptor(const TransportDescriptor& aTd,
                                     Transport::Mode aMode) {
-  return MakeUnique<Transport>(aTd.mFd, aMode, nullptr);
+  return MakeUnique<Transport>(aTd.mFd.fd, aMode, nullptr);
+}
+
+UniquePtr<Transport> OpenDescriptor(const FileDescriptor& aFd,
+                                    Transport::Mode aMode) {
+  auto rawFD = aFd.ClonePlatformHandle();
+  return MakeUnique<Transport>(rawFD.release(), aMode, nullptr);
 }
 
 TransportDescriptor DuplicateDescriptor(const TransportDescriptor& aTd) {
   TransportDescriptor result = aTd;
-  result.mFd = dup(aTd.mFd);
-  if (result.mFd == -1) {
+  result.mFd.fd = dup(aTd.mFd.fd);
+  if (result.mFd.fd == -1) {
     AnnotateSystemError();
   }
-  MOZ_RELEASE_ASSERT(result.mFd != -1, "DuplicateDescriptor failed");
+  MOZ_RELEASE_ASSERT(result.mFd.fd != -1, "DuplicateDescriptor failed");
   return result;
 }
 
-void CloseDescriptor(const TransportDescriptor& aTd) { close(aTd.mFd); }
+void CloseDescriptor(const TransportDescriptor& aTd) { close(aTd.mFd.fd); }
 
 }  // namespace ipc
 }  // namespace mozilla
--- a/ipc/glue/Transport_posix.h
+++ b/ipc/glue/Transport_posix.h
@@ -8,36 +8,31 @@
 #define mozilla_ipc_Transport_posix_h 1
 
 #include "ipc/IPCMessageUtils.h"
 
 namespace mozilla {
 namespace ipc {
 
 struct TransportDescriptor {
-  int mFd;
+  base::FileDescriptor mFd;
 };
 
 }  // namespace ipc
 }  // namespace mozilla
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::ipc::TransportDescriptor> {
   typedef mozilla::ipc::TransportDescriptor paramType;
   static void Write(Message* aMsg, const paramType& aParam) {
-    WriteParam(aMsg, mozilla::UniqueFileHandle{aParam.mFd});
+    WriteParam(aMsg, aParam.mFd);
   }
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
-    mozilla::UniqueFileHandle fd;
-    if (!ReadParam(aMsg, aIter, &fd)) {
-      return false;
-    }
-    aResult->mFd = fd.release();
-    return true;
+    return ReadParam(aMsg, aIter, &aResult->mFd);
   }
 };
 
 }  // namespace IPC
 
 #endif  // mozilla_ipc_Transport_posix_h
--- a/ipc/glue/Transport_win.cpp
+++ b/ipc/glue/Transport_win.cpp
@@ -3,69 +3,103 @@
 /* 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 "base/message_loop.h"
 
 #include "mozilla/ipc/Transport.h"
 #include "mozilla/ipc/ProtocolUtils.h"
-#include <windows.h>
 
 using base::ProcessHandle;
 
 namespace mozilla {
 namespace ipc {
 
-nsresult CreateTransport(TransportDescriptor* aOne, TransportDescriptor* aTwo) {
+nsresult CreateTransport(base::ProcessId aProcIdOne, TransportDescriptor* aOne,
+                         TransportDescriptor* aTwo) {
   auto id = IPC::Channel::GenerateVerifiedChannelID();
+  // Use MODE_SERVER to force creation of the pipe
+  Transport t(id, Transport::MODE_SERVER, nullptr);
+  HANDLE serverPipe = t.GetServerPipeHandle();
+  if (!serverPipe) {
+    return NS_ERROR_TRANSPORT_INIT;
+  }
 
-  // Use MODE_SERVER to force creation of the pipe
   // NB: we create the server pipe immediately, instead of just
   // grabbing an ID, on purpose.  In the current setup, the client
   // needs to connect to an existing server pipe, so to prevent race
   // conditions, we create the server side here. When we send the pipe
   // to the server, we DuplicateHandle it to the server process to give it
   // access.
-  Transport t(id, Transport::MODE_SERVER, nullptr);
-  HANDLE serverPipe = t.GetServerPipeHandle();
-  if (!serverPipe) {
-    return NS_ERROR_TRANSPORT_INIT;
-  }
-
-  // Make a copy of the handle owned by the `Transport` which will be
-  // transferred to the actual server process.
-  if (!::DuplicateHandle(GetCurrentProcess(), serverPipe, GetCurrentProcess(),
-                         &aOne->mServerPipeHandle, 0, false,
-                         DUPLICATE_SAME_ACCESS)) {
+  HANDLE serverDup;
+  DWORD access = 0;
+  DWORD options = DUPLICATE_SAME_ACCESS;
+  if (!DuplicateHandle(serverPipe, base::GetCurrentProcId(), &serverDup, access,
+                       options)) {
     return NS_ERROR_DUPLICATE_HANDLE;
   }
 
   aOne->mPipeName = aTwo->mPipeName = id;
+  aOne->mServerPipeHandle = serverDup;
+  aOne->mDestinationProcessId = aProcIdOne;
   aTwo->mServerPipeHandle = INVALID_HANDLE_VALUE;
+  aTwo->mDestinationProcessId = 0;
   return NS_OK;
 }
 
+HANDLE
+TransferHandleToProcess(HANDLE source, base::ProcessId pid) {
+  // At this point we're sending the handle to another process.
+
+  if (source == INVALID_HANDLE_VALUE) {
+    return source;
+  }
+  HANDLE handleDup;
+  DWORD access = 0;
+  DWORD options = DUPLICATE_SAME_ACCESS;
+  bool ok = DuplicateHandle(source, pid, &handleDup, access, options);
+  if (!ok) {
+    return nullptr;
+  }
+
+  // Now close our own copy of the handle (we're supposed to be transferring,
+  // not copying).
+  CloseHandle(source);
+
+  return handleDup;
+}
+
 UniquePtr<Transport> OpenDescriptor(const TransportDescriptor& aTd,
                                     Transport::Mode aMode) {
+  if (aTd.mServerPipeHandle != INVALID_HANDLE_VALUE) {
+    MOZ_RELEASE_ASSERT(aTd.mDestinationProcessId == base::GetCurrentProcId());
+  }
   return MakeUnique<Transport>(aTd.mPipeName, aTd.mServerPipeHandle, aMode,
                                nullptr);
 }
 
+UniquePtr<Transport> OpenDescriptor(const FileDescriptor& aFd,
+                                    Transport::Mode aMode) {
+  MOZ_ASSERT_UNREACHABLE("Not implemented!");
+  return nullptr;
+}
+
 TransportDescriptor DuplicateDescriptor(const TransportDescriptor& aTd) {
   // We're duplicating this handle in our own process for bookkeeping purposes.
 
   if (aTd.mServerPipeHandle == INVALID_HANDLE_VALUE) {
     return aTd;
   }
 
   HANDLE serverDup;
-  bool ok = ::DuplicateHandle(GetCurrentProcess(), aTd.mServerPipeHandle,
-                              GetCurrentProcess(), &serverDup, 0, false,
-                              DUPLICATE_SAME_ACCESS);
+  DWORD access = 0;
+  DWORD options = DUPLICATE_SAME_ACCESS;
+  bool ok = DuplicateHandle(aTd.mServerPipeHandle, base::GetCurrentProcId(),
+                            &serverDup, access, options);
   if (!ok) {
     AnnotateSystemError();
   }
   MOZ_RELEASE_ASSERT(ok);
 
   TransportDescriptor desc = aTd;
   desc.mServerPipeHandle = serverDup;
   return desc;
--- a/ipc/glue/Transport_win.h
+++ b/ipc/glue/Transport_win.h
@@ -8,54 +8,102 @@
 #define mozilla_ipc_Transport_win_h 1
 
 #include <string>
 
 #include "base/process.h"
 #include "ipc/IPCMessageUtils.h"
 #include "nsWindowsHelpers.h"
 #include "nsXULAppAPI.h"
-#include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 namespace ipc {
 
 struct TransportDescriptor {
   std::wstring mPipeName;
   HANDLE mServerPipeHandle;
   base::ProcessId mDestinationProcessId;
 };
 
+HANDLE
+TransferHandleToProcess(HANDLE source, base::ProcessId pid);
+
 }  // namespace ipc
 }  // namespace mozilla
 
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::ipc::TransportDescriptor> {
   typedef mozilla::ipc::TransportDescriptor paramType;
   static void Write(Message* aMsg, const paramType& aParam) {
+    HANDLE pipe = mozilla::ipc::TransferHandleToProcess(
+        aParam.mServerPipeHandle, aParam.mDestinationProcessId);
+    DWORD duplicateFromProcessId = 0;
+    if (!pipe) {
+      if (XRE_IsParentProcess()) {
+        // If we are the parent and failed to transfer then there is no hope,
+        // just close the handle.
+        ::CloseHandle(aParam.mServerPipeHandle);
+      } else {
+        // We are probably sending to parent so it should be able to duplicate.
+        pipe = aParam.mServerPipeHandle;
+        duplicateFromProcessId = ::GetCurrentProcessId();
+      }
+    }
+
     WriteParam(aMsg, aParam.mPipeName);
-    WriteParam(aMsg, mozilla::UniqueFileHandle(aParam.mServerPipeHandle));
+    WriteParam(aMsg, pipe);
+    WriteParam(aMsg, duplicateFromProcessId);
     WriteParam(aMsg, aParam.mDestinationProcessId);
   }
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
-    mozilla::UniqueFileHandle serverPipeHandle;
+    DWORD duplicateFromProcessId;
     bool r = (ReadParam(aMsg, aIter, &aResult->mPipeName) &&
-              ReadParam(aMsg, aIter, &serverPipeHandle) &&
+              ReadParam(aMsg, aIter, &aResult->mServerPipeHandle) &&
+              ReadParam(aMsg, aIter, &duplicateFromProcessId) &&
               ReadParam(aMsg, aIter, &aResult->mDestinationProcessId));
     if (!r) {
       return r;
     }
 
-    if (serverPipeHandle) {
-      aResult->mServerPipeHandle = serverPipeHandle.release();
-    } else {
+    MOZ_RELEASE_ASSERT(
+        aResult->mServerPipeHandle,
+        "Main process failed to duplicate pipe handle to child.");
+
+    // If this is a not the "server" side descriptor, we have finished.
+    if (aResult->mServerPipeHandle == INVALID_HANDLE_VALUE) {
+      return true;
+    }
+
+    MOZ_RELEASE_ASSERT(aResult->mDestinationProcessId ==
+                       base::GetCurrentProcId());
+
+    // If the pipe has already been duplicated to us, we have finished.
+    if (!duplicateFromProcessId) {
+      return true;
+    }
+
+    // Otherwise duplicate the handle to us.
+    nsAutoHandle sourceProcess(
+        ::OpenProcess(PROCESS_DUP_HANDLE, FALSE, duplicateFromProcessId));
+    if (!sourceProcess) {
+      return false;
+    }
+
+    HANDLE ourHandle;
+    BOOL duped = ::DuplicateHandle(
+        sourceProcess, aResult->mServerPipeHandle, ::GetCurrentProcess(),
+        &ourHandle, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+    if (!duped) {
       aResult->mServerPipeHandle = INVALID_HANDLE_VALUE;
+      return false;
     }
+
+    aResult->mServerPipeHandle = ourHandle;
     return true;
   }
 };
 
 }  // namespace IPC
 
 #endif  // mozilla_ipc_Transport_win_h
--- a/ipc/gtest/TestSharedMemory.cpp
+++ b/ipc/gtest/TestSharedMemory.cpp
@@ -41,20 +41,21 @@ TEST(IPCSharedMemory, FreezeAndMapRW)
   ASSERT_TRUE(mem);
   *mem = 'A';
 
   // Freeze
   ASSERT_TRUE(shm.Freeze());
   ASSERT_FALSE(shm.memory());
 
   // Re-create as writeable
-  auto handle = shm.TakeHandle();
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
   ASSERT_TRUE(shm.IsHandleValid(handle));
   ASSERT_FALSE(shm.IsValid());
-  ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ false));
+  ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ false));
   ASSERT_TRUE(shm.IsValid());
 
   // This should fail
   EXPECT_FALSE(shm.Map(1));
 }
 
 // Try to restore write permissions to a frozen mapping.  Threat
 // model: the process has mapped frozen shm normally and then is
@@ -97,20 +98,21 @@ TEST(IPCSharedMemory, Reprotect)
   // Create and initialize
   ASSERT_TRUE(shm.CreateFreezeable(1));
   ASSERT_TRUE(shm.Map(1));
   auto mem = reinterpret_cast<char*>(shm.memory());
   ASSERT_TRUE(mem);
   *mem = 'A';
 
   // Re-create as read-only
-  auto handle = shm.TakeHandle();
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
   ASSERT_TRUE(shm.IsHandleValid(handle));
   ASSERT_FALSE(shm.IsValid());
-  ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ true));
+  ASSERT_TRUE(shm.SetHandle(handle, /* read-only */ true));
   ASSERT_TRUE(shm.IsValid());
 
   // Re-map
   ASSERT_TRUE(shm.Map(1));
   mem = reinterpret_cast<char*>(shm.memory());
   ASSERT_EQ(*mem, 'A');
 
   // Try to alter protection; should succeed, because not frozen
@@ -134,24 +136,24 @@ TEST(IPCSharedMemory, WinUnfreeze)
   ASSERT_TRUE(mem);
   *mem = 'A';
 
   // Freeze
   ASSERT_TRUE(shm.Freeze());
   ASSERT_FALSE(shm.memory());
 
   // Extract handle.
-  auto handle = shm.TakeHandle();
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shm.GiveToProcess(base::GetCurrentProcId(), &handle));
   ASSERT_TRUE(shm.IsHandleValid(handle));
   ASSERT_FALSE(shm.IsValid());
 
   // Unfreeze.
-  HANDLE newHandle = INVALID_HANDLE_VALUE;
   bool unfroze = ::DuplicateHandle(
-      GetCurrentProcess(), handle.release(), GetCurrentProcess(), &newHandle,
+      GetCurrentProcess(), handle, GetCurrentProcess(), &handle,
       FILE_MAP_ALL_ACCESS, false, DUPLICATE_CLOSE_SOURCE);
   ASSERT_FALSE(unfroze);
 }
 #endif
 
 // Test that a read-only copy sees changes made to the writeable
 // mapping in the case that the page wasn't accessed before the copy.
 TEST(IPCSharedMemory, ROCopyAndWrite)
@@ -228,20 +230,21 @@ TEST(IPCSharedMemory, ROCopyAndMapRW)
   ASSERT_TRUE(memRW);
   *memRW = 'A';
 
   // Create read-only copy
   ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO));
   ASSERT_TRUE(shmRO.IsValid());
 
   // Re-create as writeable
-  auto handle = shmRO.TakeHandle();
+  auto handle = base::SharedMemory::NULLHandle();
+  ASSERT_TRUE(shmRO.GiveToProcess(base::GetCurrentProcId(), &handle));
   ASSERT_TRUE(shmRO.IsHandleValid(handle));
   ASSERT_FALSE(shmRO.IsValid());
-  ASSERT_TRUE(shmRO.SetHandle(std::move(handle), /* read-only */ false));
+  ASSERT_TRUE(shmRO.SetHandle(handle, /* read-only */ false));
   ASSERT_TRUE(shmRO.IsValid());
 
   // This should fail
   EXPECT_FALSE(shmRO.Map(1));
 }
 
 // See FreezeAndReprotect
 TEST(IPCSharedMemory, ROCopyAndReprotect)
--- a/ipc/ipdl/ipdl/ast.py
+++ b/ipc/ipdl/ipdl/ast.py
@@ -231,23 +231,18 @@ class UsingStmt(Node):
         return self.kind == "class"
 
     def isStruct(self):
         return self.kind == "struct"
 
     def isRefcounted(self):
         return "RefCounted" in self.attributes
 
-    def isSendMoveOnly(self):
-        moveonly = self.attributes.get("MoveOnly")
-        return moveonly and moveonly.value in (None, "send")
-
-    def isDataMoveOnly(self):
-        moveonly = self.attributes.get("MoveOnly")
-        return moveonly and moveonly.value in (None, "data")
+    def isMoveonly(self):
+        return "MoveOnly" in self.attributes
 
 
 # "singletons"
 
 
 class PrettyPrinted:
     @classmethod
     def __hash__(cls):
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -264,16 +264,20 @@ def _abortIfFalse(cond, msg):
         ExprCall(ExprVar("MOZ_RELEASE_ASSERT"), [cond, ExprLiteral.String(msg)])
     )
 
 
 def _refptr(T):
     return Type("RefPtr", T=T)
 
 
+def _uniqueptr(T):
+    return Type("UniquePtr", T=T)
+
+
 def _alreadyaddrefed(T):
     return Type("already_AddRefed", T=T)
 
 
 def _tuple(types, const=False, ref=False):
     return Type("Tuple", T=types, const=const, ref=ref)
 
 
@@ -291,18 +295,18 @@ def _makePromise(returns, side, resolver
     # MozPromise is purposefully made to be exclusive only. Really, we mean it.
     return _promise(
         resolvetype, _ResponseRejectReason.Type(), ExprLiteral.TRUE, resolver=resolver
     )
 
 
 def _resolveType(returns, side):
     if len(returns) > 1:
-        return _tuple([d.inType(side) for d in returns])
-    return returns[0].inType(side)
+        return _tuple([d.moveType(side) for d in returns])
+    return returns[0].moveType(side)
 
 
 def _makeResolver(returns, side):
     return TypeFunction([Decl(_resolveType(returns, side), "")])
 
 
 def _cxxArrayType(basetype, const=False, ref=False):
     return Type("nsTArray", T=basetype, const=const, ref=ref, hasimplicitcopyctor=False)
@@ -554,120 +558,114 @@ def _cxxRefType(ipdltype, side):
 
 def _cxxConstRefType(ipdltype, side):
     t = _cxxBareType(ipdltype, side)
     if ipdltype.isIPDL() and ipdltype.isActor():
         return t
     if ipdltype.isIPDL() and ipdltype.isShmem():
         t.ref = True
         return t
+    if ipdltype.isIPDL() and ipdltype.isByteBuf():
+        t.ref = True
+        return t
     if ipdltype.isIPDL() and ipdltype.hasBaseType():
         # Keep same constness as inner type.
         inner = _cxxConstRefType(ipdltype.basetype, side)
         t.const = inner.const or not inner.ref
         t.ref = True
         return t
-    if ipdltype.isCxx() and (ipdltype.isSendMoveOnly() or ipdltype.isDataMoveOnly()):
+    if ipdltype.isCxx() and ipdltype.isMoveonly():
         t.const = True
         t.ref = True
         return t
     if ipdltype.isCxx() and ipdltype.isRefcounted():
         # Use T* instead of const RefPtr<T>&
         t = t.T
         t.ptr = True
         return t
+    if ipdltype.isUniquePtr():
+        t.ref = True
+        return t
     t.const = True
     t.ref = True
     return t
 
 
-def _cxxTypeNeedsMoveForSend(ipdltype, context="root", visited=None):
-    """Returns `True` if serializing ipdltype requires a mutable reference, e.g.
-    because the underlying resource represented by the value is being
-    transferred to another process. This is occasionally distinct from whether
-    the C++ type exposes a copy constructor, such as for types which are not
-    cheaply copiable, but are not mutated when serialized."""
-
+def _cxxTypeCanMoveSend(ipdltype):
+    return ipdltype.isUniquePtr()
+
+
+def _cxxTypeNeedsMove(ipdltype):
+    if _cxxTypeNeedsMoveForSend(ipdltype):
+        return True
+
+    if ipdltype.isIPDL():
+        return ipdltype.isArray()
+
+    return False
+
+
+def _cxxTypeNeedsMoveForSend(ipdltype):
+    if ipdltype.isUniquePtr():
+        return True
+
+    if ipdltype.isCxx():
+        return ipdltype.isMoveonly()
+
+    if ipdltype.isIPDL():
+        if ipdltype.hasBaseType():
+            return _cxxTypeNeedsMove(ipdltype.basetype)
+        return (
+            ipdltype.isShmem()
+            or ipdltype.isByteBuf()
+            or ipdltype.isEndpoint()
+            or ipdltype.isManagedEndpoint()
+        )
+
+    return False
+
+
+# FIXME Bug 1547019 This should be the same as _cxxTypeNeedsMoveForSend, but
+#                   a lot of existing code needs to be updated and fixed before
+#                   we can do that.
+def _cxxTypeCanOnlyMove(ipdltype, visited=None):
     if visited is None:
         visited = set()
 
     visited.add(ipdltype)
 
     if ipdltype.isCxx():
-        return ipdltype.isSendMoveOnly()
+        return ipdltype.isMoveonly()
 
     if ipdltype.isIPDL():
-        if ipdltype.hasBaseType():
-            return _cxxTypeNeedsMoveForSend(ipdltype.basetype, "wrapper", visited)
+        if ipdltype.isMaybe() or ipdltype.isArray():
+            return _cxxTypeCanOnlyMove(ipdltype.basetype, visited)
         if ipdltype.isStruct() or ipdltype.isUnion():
             return any(
-                _cxxTypeNeedsMoveForSend(t, "compound", visited)
+                _cxxTypeCanOnlyMove(t, visited)
                 for t in ipdltype.itercomponents()
                 if t not in visited
             )
-
-        # For historical reasons, shmem is `const_cast` to a mutable reference
-        # when being stored in a struct or union (see
-        # `_StructField.constRefExpr` and `_UnionMember.getConstValue`), meaning
-        # that they do not cause the containing struct to require move for
-        # sending.
-        if ipdltype.isShmem():
-            return context != "compound"
-
-        return (
-            ipdltype.isByteBuf()
-            or ipdltype.isEndpoint()
-            or ipdltype.isManagedEndpoint()
-        )
-
-    return False
-
-
-def _cxxTypeNeedsMoveForData(ipdltype, context="root", visited=None):
-    """Returns `True` if the bare C++ type corresponding to ipdltype does not
-    satisfy std::is_copy_constructible_v<T>. All C++ types supported by IPDL
-    must support std::is_move_constructible_v<T>, so non-movable types must be
-    passed behind a `UniquePtr`."""
-
-    if visited is None:
-        visited = set()
-
-    visited.add(ipdltype)
-
-    if ipdltype.isUniquePtr():
-        return True
-
-    if ipdltype.isCxx():
-        return ipdltype.isDataMoveOnly()
-
-    if ipdltype.isIPDL():
-        # When nested within a maybe or array, arrays are no longer copyable.
-        if context == "wrapper" and ipdltype.isArray():
-            return True
-        if ipdltype.hasBaseType():
-            return _cxxTypeNeedsMoveForData(ipdltype.basetype, "wrapper", visited)
-        if ipdltype.isStruct() or ipdltype.isUnion():
-            return any(
-                _cxxTypeNeedsMoveForData(t, "compound", visited)
-                for t in ipdltype.itercomponents()
-                if t not in visited
-            )
-        return (
-            ipdltype.isByteBuf()
-            or ipdltype.isEndpoint()
-            or ipdltype.isManagedEndpoint()
-        )
+        return ipdltype.isManagedEndpoint()
 
     return False
 
 
 def _cxxTypeCanMove(ipdltype):
     return not (ipdltype.isIPDL() and ipdltype.isActor())
 
 
+def _cxxMoveRefType(ipdltype, side):
+    t = _cxxBareType(ipdltype, side)
+    if _cxxTypeNeedsMove(ipdltype):
+        t.rvalref = True
+        return t
+    return _cxxConstRefType(ipdltype, side)
+
+
 def _cxxForceMoveRefType(ipdltype, side):
     assert _cxxTypeCanMove(ipdltype)
     t = _cxxBareType(ipdltype, side)
     t.rvalref = True
     return t
 
 
 def _cxxPtrToType(ipdltype, side):
@@ -686,33 +684,16 @@ def _cxxConstPtrToType(ipdltype, side):
         t.ptr = False
         t.ptrconstptr = True
         return t
     t.const = True
     t.ptr = True
     return t
 
 
-def _cxxInType(ipdltype, side):
-    t = _cxxBareType(ipdltype, side)
-    if ipdltype.isIPDL() and ipdltype.isActor():
-        return t
-    if _cxxTypeNeedsMoveForSend(ipdltype):
-        t.rvalref = True
-        return t
-    if ipdltype.isCxx() and ipdltype.isRefcounted():
-        # Use T* instead of const RefPtr<T>&
-        t = t.T
-        t.ptr = True
-        return t
-    t.const = True
-    t.ref = True
-    return t
-
-
 def _allocMethod(ptype, side):
     return "Alloc" + ptype.name() + side.title()
 
 
 def _deallocMethod(ptype, side):
     return "Dealloc" + ptype.name() + side.title()
 
 
@@ -743,25 +724,39 @@ class _HybridDecl:
         """Return this decl's C++ type as a 'reference' type, which is not
         necessarily a C++ reference."""
         return _cxxRefType(self.ipdltype, side)
 
     def constRefType(self, side):
         """Return this decl's C++ type as a const, 'reference' type."""
         return _cxxConstRefType(self.ipdltype, side)
 
+    def rvalueRefType(self, side):
+        """Return this decl's C++ type as an r-value 'reference' type."""
+        return _cxxMoveRefType(self.ipdltype, side)
+
     def ptrToType(self, side):
         return _cxxPtrToType(self.ipdltype, side)
 
     def constPtrToType(self, side):
         return _cxxConstPtrToType(self.ipdltype, side)
 
     def inType(self, side):
-        """Return this decl's C++ Type with sending inparam semantics."""
-        return _cxxInType(self.ipdltype, side)
+        """Return this decl's C++ Type with inparam semantics."""
+        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+            return self.bareType(side)
+        elif _cxxTypeNeedsMoveForSend(self.ipdltype):
+            return self.rvalueRefType(side)
+        return self.constRefType(side)
+
+    def moveType(self, side):
+        """Return this decl's C++ Type with move semantics."""
+        if self.ipdltype.isIPDL() and self.ipdltype.isActor():
+            return self.bareType(side)
+        return self.rvalueRefType(side)
 
     def outType(self, side):
         """Return this decl's C++ Type with outparam semantics."""
         t = self.bareType(side)
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             t.ptr = False
             t.ptrptr = True
             return t
@@ -852,16 +847,20 @@ class _StructField(_CompoundTypeComponen
             ref = ExprSelect(thisexpr, ".", ref.name)
         return ref
 
     def constRefExpr(self, thisexpr=None):
         # sigh, gross hack
         refexpr = self.refExpr(thisexpr)
         if "Shmem" == self.ipdltype.name():
             refexpr = ExprCast(refexpr, Type("Shmem", ref=True), const=True)
+        if "ByteBuf" == self.ipdltype.name():
+            refexpr = ExprCast(refexpr, Type("ByteBuf", ref=True), const=True)
+        if "FileDescriptor" == self.ipdltype.name():
+            refexpr = ExprCast(refexpr, Type("FileDescriptor", ref=True), const=True)
         return refexpr
 
     def argVar(self):
         return ExprVar("_" + self.name)
 
     def memberVar(self):
         return ExprVar(self.name + "_")
 
@@ -1025,18 +1024,22 @@ class _UnionMember(_CompoundTypeComponen
         if self.ipdltype.isIPDL() and self.ipdltype.isActor():
             return ExprLiteral.NULL
         # XXX sneaky here, maybe need ExprCtor()?
         return ExprCall(self.bareType(fq=fq))
 
     def getConstValue(self):
         v = ExprDeref(self.callGetConstPtr())
         # sigh
+        if "ByteBuf" == self.ipdltype.name():
+            v = ExprCast(v, Type("ByteBuf", ref=True), const=True)
         if "Shmem" == self.ipdltype.name():
             v = ExprCast(v, Type("Shmem", ref=True), const=True)
+        if "FileDescriptor" == self.ipdltype.name():
+            v = ExprCast(v, Type("FileDescriptor", ref=True), const=True)
         return v
 
 
 # --------------------------------------------------
 
 
 class MessageDecl(ipdl.ast.MessageDecl):
     def baseName(self):
@@ -1119,33 +1122,19 @@ class MessageDecl(ipdl.ast.MessageDecl):
                 and "NoTaint" not in d.attributes
                 and direction == "recv"
             ):
                 # Tainted types are passed by-value, allowing the receiver to move them if desired.
                 assert sems != "out"
                 return Decl(Type("Tainted", T=d.bareType(side)), d.name)
 
             if sems == "in":
-                t = d.inType(side)
-                # If this is the `recv` side, and we're not using "move"
-                # semantics, that means we're an alloc method, and cannot accept
-                # values by rvalue reference. Downgrade to an lvalue reference.
-                if direction == "recv" and t.rvalref:
-                    t.rvalref = False
-                    t.ref = True
-                return Decl(t, d.name)
+                return Decl(d.inType(side), d.name)
             elif sems == "move":
-                assert direction == "recv"
-                # For legacy reasons, use an rvalue reference when generating
-                # parameters for recv methods which accept arrays.
-                if d.ipdltype.isIPDL() and d.ipdltype.isArray():
-                    t = d.bareType(side)
-                    t.rvalref = True
-                    return Decl(t, d.name)
-                return Decl(d.inType(side), d.name)
+                return Decl(d.moveType(side), d.name)
             elif sems == "out":
                 return Decl(d.outType(side), d.name)
             else:
                 assert 0
 
         def makeResolverDecl(returns):
             return Decl(Type(self.resolverName(), rvalref=True), "aResolve")
 
@@ -2016,16 +2005,18 @@ class _ParamTraits:
 
         return [
             Whitespace("// Sentinel = " + repr(sentinelKey) + "\n", indent=True),
             ifsentinel,
         ]
 
     @classmethod
     def write(cls, var, msgvar, actor, ipdltype=None):
+        # WARNING: This doesn't set AutoForActor for you, make sure this is
+        # only called when the actor is already correctly set.
         if ipdltype and _cxxTypeNeedsMoveForSend(ipdltype):
             var = ExprMove(var)
         return ExprCall(ExprVar("WriteIPDLParam"), args=[msgvar, actor, var])
 
     @classmethod
     def checkedWrite(cls, ipdltype, var, msgvar, sentinelKey, actor):
         assert sentinelKey
         block = Block()
@@ -2162,36 +2153,33 @@ class _ParamTraits:
             errfn=errfn,
             paramtype=what,
             sentinelKey=sentinelKey,
             errfnSentinel=errfnSentinel(),
             actor=cls.actor,
         )
 
     @classmethod
-    def generateDecl(cls, fortype, write, read, needsmove=False):
+    def generateDecl(cls, fortype, write, read, constin=True):
         # IPDLParamTraits impls are selected ignoring constness, and references.
         pt = Class(
             "IPDLParamTraits",
             specializes=Type(
                 fortype.name, T=fortype.T, inner=fortype.inner, ptr=fortype.ptr
             ),
             struct=True,
         )
 
         # typedef T paramType;
         pt.addstmt(Typedef(fortype, "paramType"))
 
         iprotocoltype = Type("mozilla::ipc::IProtocol", ptr=True)
 
         # static void Write(Message*, const T&);
-        if needsmove:
-            intype = Type("paramType", rvalref=True)
-        else:
-            intype = Type("paramType", ref=True, const=True)
+        intype = Type("paramType", ref=True, const=constin)
         writemthd = MethodDefn(
             MethodDecl(
                 "Write",
                 params=[
                     Decl(Type("IPC::Message", ptr=True), cls.msgvar.name),
                     Decl(iprotocoltype, cls.actor.name),
                     Decl(intype, cls.var.name),
                 ],
@@ -2338,19 +2326,17 @@ class _ParamTraits:
                 writefield = cls.checkedBulkWrite(size, fields)
                 readfield = cls.checkedBulkRead(size, fields)
 
                 write.append(writefield)
                 read.append(readfield)
 
         read.append(StmtReturn.TRUE)
 
-        return cls.generateDecl(
-            cxxtype, write, read, needsmove=_cxxTypeNeedsMoveForSend(structtype)
-        )
+        return cls.generateDecl(cxxtype, write, read)
 
     @classmethod
     def unionPickling(cls, uniontype):
         # NOTE: Not using _cxxBareType here as we don't have a side
         cxxtype = Type(uniontype.fullname())
         ud = uniontype._ast
 
         # Use typedef to set up an alias so it's easier to reference the struct type.
@@ -2440,19 +2426,17 @@ class _ParamTraits:
             DefaultLabel(),
             StmtBlock([cls.fatalError("unknown union type"), StmtReturn()]),
         )
         readswitch.addcase(
             DefaultLabel(),
             StmtBlock([cls.fatalError("unknown union type"), StmtReturn.FALSE]),
         )
 
-        return cls.generateDecl(
-            cxxtype, write, read, needsmove=_cxxTypeNeedsMoveForSend(uniontype)
-        )
+        return cls.generateDecl(cxxtype, write, read)
 
 
 # --------------------------------------------------
 
 
 class _ComputeTypeDeps(TypeVisitor):
     """Pass that gathers the C++ types that a particular IPDL type
     (recursively) depends on.  There are three kinds of dependencies: (i)
@@ -2616,21 +2600,22 @@ def _generateCxxStruct(sd):
     fulldecltypes = gettypedeps.fullDeclTypes
 
     struct = Class(sd.name, final=True)
     struct.addstmts([Label.PRIVATE] + usingTypedefs + [Whitespace.NL, Label.PUBLIC])
 
     constreftype = Type(sd.name, const=True, ref=True)
 
     def fieldsAsParamList():
+        # FIXME Bug 1547019 inType() should do the right thing once
+        #                   _cxxTypeCanOnlyMove is replaced with
+        #                   _cxxTypeNeedsMoveForSend
         return [
             Decl(
-                f.forceMoveType()
-                if _cxxTypeNeedsMoveForData(f.ipdltype)
-                else f.constRefType(),
+                f.forceMoveType() if _cxxTypeCanOnlyMove(f.ipdltype) else f.inType(),
                 f.argVar().name,
             )
             for f in sd.fields_ipdl_order()
         ]
 
     # If this is an empty struct (no fields), then the default ctor
     # and "create-with-fields" ctors are equivalent.  So don't bother
     # with the default ctor.
@@ -2652,17 +2637,17 @@ def _generateCxxStruct(sd):
 
     # Struct(const field1& _f1, ...)
     valctor = ConstructorDefn(
         ConstructorDecl(sd.name, params=fieldsAsParamList(), force_inline=True)
     )
     valctor.memberinits = []
     for f in sd.fields_member_order():
         arg = f.argVar()
-        if _cxxTypeNeedsMoveForData(f.ipdltype):
+        if _cxxTypeCanOnlyMove(f.ipdltype):
             arg = ExprMove(arg)
         valctor.memberinits.append(ExprMemberInit(f.memberVar(), args=[arg]))
 
     struct.addstmts([valctor, Whitespace.NL])
 
     # The default copy, move, and assignment constructors, and the default
     # destructor, will do the right thing.
 
@@ -2963,42 +2948,42 @@ def _generateCxxUnion(ud):
             ),
             Whitespace.NL,
         ]
     )
 
     # Union(const T&) copy & Union(T&&) move ctors
     othervar = ExprVar("aOther")
     for c in ud.components:
-        if not _cxxTypeNeedsMoveForData(c.ipdltype):
+        if not _cxxTypeCanOnlyMove(c.ipdltype):
             copyctor = ConstructorDefn(
-                ConstructorDecl(ud.name, params=[Decl(c.constRefType(), othervar.name)])
+                ConstructorDecl(ud.name, params=[Decl(c.inType(), othervar.name)])
             )
             copyctor.addstmts(
                 [
                     StmtExpr(c.callCtor(othervar)),
                     StmtExpr(ExprAssn(mtypevar, c.enumvar())),
                 ]
             )
             cls.addstmts([copyctor, Whitespace.NL])
 
-        if not _cxxTypeCanMove(c.ipdltype):
+        if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
             continue
         movector = ConstructorDefn(
             ConstructorDecl(ud.name, params=[Decl(c.forceMoveType(), othervar.name)])
         )
         movector.addstmts(
             [
                 StmtExpr(c.callCtor(ExprMove(othervar))),
                 StmtExpr(ExprAssn(mtypevar, c.enumvar())),
             ]
         )
         cls.addstmts([movector, Whitespace.NL])
 
-    unionNeedsMove = any(_cxxTypeNeedsMoveForData(c.ipdltype) for c in ud.components)
+    unionNeedsMove = any(_cxxTypeCanOnlyMove(c.ipdltype) for c in ud.components)
 
     # Union(const Union&) copy ctor
     if not unionNeedsMove:
         copyctor = ConstructorDefn(
             ConstructorDecl(ud.name, params=[Decl(inClsType, othervar.name)])
         )
         othertype = ud.callType(othervar)
         copyswitch = StmtSwitch(othertype)
@@ -3105,38 +3090,36 @@ def _generateCxxUnion(ud):
         MethodDecl("type", ret=typetype, const=True, force_inline=True)
     )
     typemeth.addstmt(StmtReturn(mtypevar))
     cls.addstmts([typemeth, Whitespace.NL])
 
     # Union& operator= methods
     rhsvar = ExprVar("aRhs")
     for c in ud.components:
-        if not _cxxTypeNeedsMoveForData(c.ipdltype):
+        if not _cxxTypeCanOnlyMove(c.ipdltype):
             # Union& operator=(const T&)
             opeq = MethodDefn(
                 MethodDecl(
-                    "operator=",
-                    params=[Decl(c.constRefType(), rhsvar.name)],
-                    ret=refClsType,
+                    "operator=", params=[Decl(c.inType(), rhsvar.name)], ret=refClsType
                 )
             )
             opeq.addstmts(
                 [
                     # might need to placement-delete old value first
                     maybeReconstruct(c, c.enumvar()),
                     StmtExpr(c.callOperatorEq(rhsvar)),
                     StmtExpr(ExprAssn(mtypevar, c.enumvar())),
                     StmtReturn(ExprDeref(ExprVar.THIS)),
                 ]
             )
             cls.addstmts([opeq, Whitespace.NL])
 
         # Union& operator=(T&&)
-        if not _cxxTypeCanMove(c.ipdltype):
+        if not _cxxTypeCanMove(c.ipdltype) or _cxxTypeNeedsMoveForSend(c.ipdltype):
             continue
 
         opeq = MethodDefn(
             MethodDecl(
                 "operator=",
                 params=[Decl(c.forceMoveType(), rhsvar.name)],
                 ret=refClsType,
             )
@@ -3267,17 +3250,17 @@ def _generateCxxUnion(ud):
     cls.addstmts([opeq, Whitespace.NL])
 
     if "Comparable" in ud.attributes:
         # bool operator==(const T&)
         for c in ud.components:
             opeqeq = MethodDefn(
                 MethodDecl(
                     "operator==",
-                    params=[Decl(c.constRefType(), rhsvar.name)],
+                    params=[Decl(c.inType(), rhsvar.name)],
                     ret=Type.BOOL,
                     const=True,
                 )
             )
             opeqeq.addstmt(
                 StmtReturn(ExprBinary(ExprCall(ExprVar(c.getTypeName())), "==", rhsvar))
             )
             cls.addstmts([opeqeq, Whitespace.NL])
@@ -4742,21 +4725,17 @@ class _GenerateProtocolActorCode(ipdl.as
     def genHelperCtor(self, md):
         helperdecl = self.makeSendMethodDecl(md)
         helperdecl.params = helperdecl.params[1:]
         helper = MethodDefn(helperdecl)
 
         helper.addstmts(
             [
                 self.callAllocActor(md, retsems="out", side=self.side),
-                StmtReturn(
-                    ExprCall(
-                        ExprVar(helperdecl.name), args=md.makeCxxArgs(paramsems="move")
-                    )
-                ),
+                StmtReturn(ExprCall(ExprVar(helperdecl.name), args=md.makeCxxArgs())),
             ]
         )
         return helper
 
     def genAsyncDtor(self, md):
         actor = md.actorDecl()
         actorvar = actor.var()
         method = MethodDefn(self.makeDtorMethodDecl(md))
--- a/ipc/ipdl/ipdl/type.py
+++ b/ipc/ipdl/ipdl/type.py
@@ -158,38 +158,34 @@ class VoidType(Type):
 
 
 VOID = VoidType()
 
 # --------------------
 
 
 class ImportedCxxType(Type):
-    def __init__(self, qname, refcounted, sendmoveonly, datamoveonly):
+    def __init__(self, qname, refcounted, moveonly):
         assert isinstance(qname, QualifiedId)
         self.loc = qname.loc
         self.qname = qname
         self.refcounted = refcounted
-        self.sendmoveonly = sendmoveonly
-        self.datamoveonly = datamoveonly
+        self.moveonly = moveonly
 
     def isCxx(self):
         return True
 
     def isAtom(self):
         return True
 
     def isRefcounted(self):
         return self.refcounted
 
-    def isSendMoveOnly(self):
-        return self.sendmoveonly
-
-    def isDataMoveOnly(self):
-        return self.datamoveonly
+    def isMoveonly(self):
+        return self.moveonly
 
     def name(self):
         return self.qname.baseid
 
     def fullname(self):
         return str(self.qname)
 
 
@@ -1106,46 +1102,40 @@ class GatherDecls(TcheckVisitor):
             # Prevent generation of typedefs.  If basename == fullname then
             # there is nothing to typedef.  With UniquePtrs, basenames
             # are generic so typedefs would be illegal.
             fullname = None
 
         self.checkAttributes(
             using.attributes,
             {
-                "MoveOnly": (None, "data", "send"),
+                "MoveOnly": None,
                 "RefCounted": None,
             },
         )
 
         if fullname == "mozilla::ipc::Shmem":
             ipdltype = ShmemType(using.type.spec)
         elif fullname == "mozilla::ipc::ByteBuf":
             ipdltype = ByteBufType(using.type.spec)
         elif fullname == "mozilla::ipc::FileDescriptor":
             ipdltype = FDType(using.type.spec)
         else:
             ipdltype = ImportedCxxType(
-                using.type.spec,
-                using.isRefcounted(),
-                using.isSendMoveOnly(),
-                using.isDataMoveOnly(),
+                using.type.spec, using.isRefcounted(), using.isMoveonly()
             )
             existingType = self.symtab.lookup(ipdltype.fullname())
             if existingType and existingType.fullname == ipdltype.fullname():
                 if ipdltype.isRefcounted() != existingType.type.isRefcounted():
                     self.error(
                         using.loc,
                         "inconsistent refcounted status of type `%s`",
                         str(using.type),
                     )
-                if (
-                    ipdltype.isSendMoveOnly() != existingType.type.isSendMoveOnly()
-                    or ipdltype.isDataMoveOnly() != existingType.type.isDataMoveOnly()
-                ):
+                if ipdltype.isMoveonly() != existingType.type.isMoveonly():
                     self.error(
                         using.loc,
                         "inconsistent moveonly status of type `%s`",
                         str(using.type),
                     )
                 using.decl = existingType
                 return
         using.decl = self.declare(
--- a/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl
+++ b/ipc/ipdl/test/ipdl/error/PInconsistentMoveOnly.ipdl
@@ -1,13 +1,9 @@
 //error: inconsistent moveonly status of type `mozilla::ipc::SomeMoveonlyType`
-//error: inconsistent moveonly status of type `mozilla::ipc::SomeMoveonlySendType`
 
 [MoveOnly] using class mozilla::ipc::SomeMoveonlyType from "SomeFile.h";
 using class mozilla::ipc::SomeMoveonlyType from "SomeFile.h";
 
-[MoveOnly=send] using class mozilla::ipc::SomeMoveonlySendType from "SomeFile.h";
-[MoveOnly=data] using class mozilla::ipc::SomeMoveonlySendType from "SomeFile.h";
-
 protocol PInconsistentMoveOnly {
 child:
   async SomeMessage();
 };
--- a/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl
+++ b/ipc/ipdl/test/ipdl/ok/PbasicUsing.ipdl
@@ -9,42 +9,28 @@ using struct SomeStruct from "SomeFile.h
 [MoveOnly] using SomeMoveonlyType from "SomeFile.h";
 [MoveOnly] using class SomeMoveonlyClass from "SomeFile.h";
 [MoveOnly] using struct SomeMoveonlyStruct from "SomeFile.h";
 
 [RefCounted, MoveOnly] using SomeRefcountedMoveonlyType from "SomeFile.h";
 [RefCounted, MoveOnly] using class SomeRefcountedMoveonlyClass from "SomeFile.h";
 [RefCounted, MoveOnly] using struct SomeRefcountedMoveonlyStruct from "SomeFile.h";
 
-[MoveOnly=data] using SomeMoveonlyDataType from "SomeFile.h";
-[MoveOnly=data] using class SomeMoveonlyDataClass from "SomeFile.h";
-[MoveOnly=data] using struct SomeMoveonlyDataStruct from "SomeFile.h";
-
-[MoveOnly=send] using SomeMoveonlySendType from "SomeFile.h";
-[MoveOnly=send] using class SomeMoveonlySendClass from "SomeFile.h";
-[MoveOnly=send] using struct SomeMoveonlySendStruct from "SomeFile.h";
-
 union SomeUnion
 {
   SomeType;
   SomeClass;
   SomeStruct;
   SomeRefcountedType;
   SomeRefcountedClass;
   SomeRefcountedStruct;
   SomeMoveonlyType;
   SomeMoveonlyClass;
   SomeMoveonlyStruct;
   SomeRefcountedMoveonlyType;
   SomeRefcountedMoveonlyClass;
   SomeRefcountedMoveonlyStruct;
-  SomeMoveonlyDataType;
-  SomeMoveonlyDataClass;
-  SomeMoveonlyDataStruct;
-  SomeMoveonlySendType;
-  SomeMoveonlySendClass;
-  SomeMoveonlySendStruct;
 };
 
 protocol PbasicUsing {
 child:
     async Msg(SomeUnion foo);
 };
--- a/js/xpconnect/src/XPCSelfHostedShmem.cpp
+++ b/js/xpconnect/src/XPCSelfHostedShmem.cpp
@@ -66,17 +66,17 @@ void xpc::SelfHostedShmem::InitFromParen
 
 bool xpc::SelfHostedShmem::InitFromChild(::base::SharedMemoryHandle aHandle,
                                          size_t aLen) {
   MOZ_ASSERT(!XRE_IsParentProcess());
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mLen, "Shouldn't call this more than once");
 
   auto shm = mozilla::MakeUnique<base::SharedMemory>();
-  if (NS_WARN_IF(!shm->SetHandle(std::move(aHandle), /* read_only */ true))) {
+  if (NS_WARN_IF(!shm->SetHandle(aHandle, /* read_only */ true))) {
     return false;
   }
 
   if (NS_WARN_IF(!shm->Map(aLen))) {
     return false;
   }
 
   // Note: mHandle remains empty, as content processes are not spawning more
--- a/layout/style/GlobalStyleSheetCache.cpp
+++ b/layout/style/GlobalStyleSheetCache.cpp
@@ -665,38 +665,35 @@ bool GlobalStyleSheetCache::AffectedByPr
       return true;
     }
   }
 
   return false;
 }
 
 /* static */ void GlobalStyleSheetCache::SetSharedMemory(
-    base::SharedMemoryHandle aHandle, uintptr_t aAddress) {
+    const base::SharedMemoryHandle& aHandle, uintptr_t aAddress) {
   MOZ_ASSERT(!XRE_IsParentProcess());
   MOZ_ASSERT(!gStyleCache, "Too late, GlobalStyleSheetCache already created!");
   MOZ_ASSERT(!sSharedMemory, "Shouldn't call this more than once");
 
   auto shm = MakeUnique<base::SharedMemory>();
-  if (!shm->SetHandle(std::move(aHandle), /* read_only */ true)) {
+  if (!shm->SetHandle(aHandle, /* read_only */ true)) {
     return;
   }
 
   if (shm->Map(kSharedMemorySize, reinterpret_cast<void*>(aAddress))) {
     sSharedMemory = shm.release();
   }
 }
 
 bool GlobalStyleSheetCache::ShareToProcess(base::ProcessId aProcessId,
                                            base::SharedMemoryHandle* aHandle) {
   MOZ_ASSERT(XRE_IsParentProcess());
-  if (sSharedMemory) {
-    *aHandle = sSharedMemory->CloneHandle();
-  }
-  return !!*aHandle;
+  return sSharedMemory && sSharedMemory->ShareToProcess(aProcessId, aHandle);
 }
 
 StaticRefPtr<GlobalStyleSheetCache> GlobalStyleSheetCache::gStyleCache;
 StaticRefPtr<css::Loader> GlobalStyleSheetCache::gCSSLoader;
 StaticRefPtr<nsIURI> GlobalStyleSheetCache::gUserContentSheetURL;
 
 StaticAutoPtr<base::SharedMemory> GlobalStyleSheetCache::sSharedMemory;
 size_t GlobalStyleSheetCache::sUsedSharedMemory;
--- a/layout/style/GlobalStyleSheetCache.h
+++ b/layout/style/GlobalStyleSheetCache.h
@@ -10,17 +10,16 @@
 #include "nsIMemoryReporter.h"
 #include "nsIObserver.h"
 #include "base/shared_memory.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/PreferenceSheet.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/StaticPtr.h"
-#include "mozilla/UserAgentStyleSheetID.h"
 #include "mozilla/css/Loader.h"
 
 class nsIFile;
 class nsIURI;
 
 namespace mozilla {
 class CSSStyleSheet;
 }  // namespace mozilla
@@ -60,17 +59,17 @@ class GlobalStyleSheetCache final : publ
   static void SetUserContentCSSURL(nsIURI* aURI);
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   // Set the shared memory segment to load the shared UA sheets from.
   // Called early on in a content process' life from
   // ContentChild::InitSharedUASheets, before the GlobalStyleSheetCache
   // singleton has been created.
-  static void SetSharedMemory(base::SharedMemoryHandle aHandle,
+  static void SetSharedMemory(const base::SharedMemoryHandle& aHandle,
                               uintptr_t aAddress);
 
   // Obtain a shared memory handle for the shared UA sheets to pass into a
   // content process.  Called by ContentParent::InitInternal shortly after
   // a content process has been created.
   bool ShareToProcess(base::ProcessId aProcessId,
                       base::SharedMemoryHandle* aHandle);
 
--- a/tools/fuzzing/faulty/Faulty.cpp
+++ b/tools/fuzzing/faulty/Faulty.cpp
@@ -17,16 +17,17 @@
 #else
 #  include <unistd.h>
 #endif
 #include "base/string_util.h"
 #include "FuzzingMutate.h"
 #include "FuzzingTraits.h"
 #include "chrome/common/ipc_channel.h"
 #include "chrome/common/ipc_message.h"
+#include "chrome/common/file_descriptor_set_posix.h"
 #include "mozilla/ipc/Faulty.h"
 #include "nsComponentManagerUtils.h"
 #include "nsNetCID.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsILineInputStream.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
@@ -653,17 +654,24 @@ std::vector<uint8_t> Faulty::GetDataFrom
     i.Advance(buffers, s);
   }
 
   return data;
 }
 
 // static
 void Faulty::CopyFDs(IPC::Message* aDstMsg, IPC::Message* aSrcMsg) {
-  std::swap(aDstMsg->attached_handles_, aSrcMsg->attached_handles_);
+#ifndef _WINDOWS
+  FileDescriptorSet* dstFdSet = aDstMsg->file_descriptor_set();
+  FileDescriptorSet* srcFdSet = aSrcMsg->file_descriptor_set();
+  for (size_t i = 0; i < srcFdSet->size(); i++) {
+    int fd = srcFdSet->GetDescriptorAt(i);
+    dstFdSet->Add(fd);
+  }
+#endif
 }
 
 UniquePtr<IPC::Message> Faulty::MutateIPCMessage(const char* aChannel,
                                                  UniquePtr<IPC::Message> aMsg,
                                                  unsigned int aProbability) {
   if (!mIsValidProcessType || !mFuzzMessages) {
     return aMsg;
   }
--- a/widget/windows/PCompositorWidget.ipdl
+++ b/widget/windows/PCompositorWidget.ipdl
@@ -11,19 +11,19 @@ include "mozilla/widget/WidgetMessageUti
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
 using nsTransparencyMode from "nsIWidget.h";
 
 namespace mozilla {
 namespace widget {
 
 struct RemoteBackbufferHandles {
-  FileDescriptor fileMapping;
-  FileDescriptor requestReadyEvent;
-  FileDescriptor responseReadyEvent;
+  WindowsHandle fileMapping;
+  WindowsHandle requestReadyEvent;
+  WindowsHandle responseReadyEvent;
 };
 
 sync protocol PCompositorWidget
 {
   manager PCompositorBridge;
 
 parent:
   sync Initialize(RemoteBackbufferHandles aRemoteHandles);
--- a/widget/windows/RemoteBackbuffer.cpp
+++ b/widget/windows/RemoteBackbuffer.cpp
@@ -144,23 +144,22 @@ class SharedImage {
     bitmapInfo.bmiHeader.biBitCount = 32;
     bitmapInfo.bmiHeader.biCompression = BI_RGB;
     void* dummy = nullptr;
     return ::CreateDIBSection(nullptr /*paletteDC*/, &bitmapInfo,
                               DIB_RGB_COLORS, &dummy, mFileMapping,
                               0 /*offset*/);
   }
 
-  HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
-    MOZ_ASSERT(aTargetProcess);
+  HANDLE CreateRemoteFileMapping(DWORD aTargetProcessId) {
+    MOZ_ASSERT(aTargetProcessId);
 
     HANDLE fileMapping = nullptr;
-    if (!::DuplicateHandle(GetCurrentProcess(), mFileMapping, aTargetProcess,
-                           &fileMapping, 0 /*desiredAccess*/,
-                           FALSE /*inheritHandle*/, DUPLICATE_SAME_ACCESS)) {
+    if (!ipc::DuplicateHandle(mFileMapping, aTargetProcessId, &fileMapping,
+                              0 /*desiredAccess*/, DUPLICATE_SAME_ACCESS)) {
       return nullptr;
     }
     return fileMapping;
   }
 
   already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
     return gfx::Factory::CreateDrawTargetForData(
         gfx::BackendType::CAIRO, mPixelData, IntSize(mWidth, mHeight),
@@ -310,18 +309,18 @@ class PresentableSharedImage {
       }
     }
 
     MOZ_ALWAYS_TRUE(::ReleaseDC(aWindowHandle, windowDC));
 
     return result;
   }
 
-  HANDLE CreateRemoteFileMapping(HANDLE aTargetProcess) {
-    return mSharedImage.CreateRemoteFileMapping(aTargetProcess);
+  HANDLE CreateRemoteFileMapping(DWORD aTargetProcessId) {
+    return mSharedImage.CreateRemoteFileMapping(aTargetProcessId);
   }
 
   already_AddRefed<gfx::DrawTarget> CreateDrawTarget() {
     return mSharedImage.CreateDrawTarget();
   }
 
   void CopyPixelsFrom(const PresentableSharedImage& other) {
     mSharedImage.CopyPixelsFrom(other.mSharedImage);
@@ -340,17 +339,17 @@ class PresentableSharedImage {
   SharedImage mSharedImage;
   HDC mDeviceContext;
   HBITMAP mDIBSection;
   HGDIOBJ mSavedObject;
 };
 
 Provider::Provider()
     : mWindowHandle(nullptr),
-      mTargetProcess(nullptr),
+      mTargetProcessId(0),
       mFileMapping(nullptr),
       mRequestReadyEvent(nullptr),
       mResponseReadyEvent(nullptr),
       mSharedDataPtr(nullptr),
       mStopServiceThread(false),
       mServiceThread(nullptr),
       mBackbuffer() {}
 
@@ -373,34 +372,25 @@ Provider::~Provider() {
 
   if (mRequestReadyEvent) {
     MOZ_ALWAYS_TRUE(::CloseHandle(mRequestReadyEvent));
   }
 
   if (mFileMapping) {
     MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
   }
-
-  if (mTargetProcess) {
-    MOZ_ALWAYS_TRUE(::CloseHandle(mTargetProcess));
-  }
 }
 
 bool Provider::Initialize(HWND aWindowHandle, DWORD aTargetProcessId,
                           nsTransparencyMode aTransparencyMode) {
   MOZ_ASSERT(aWindowHandle);
   MOZ_ASSERT(aTargetProcessId);
 
   mWindowHandle = aWindowHandle;
-
-  mTargetProcess = ::OpenProcess(PROCESS_DUP_HANDLE, FALSE /*inheritHandle*/,
-                                 aTargetProcessId);
-  if (!mTargetProcess) {
-    return false;
-  }
+  mTargetProcessId = aTargetProcessId;
 
   mFileMapping = ::CreateFileMappingW(
       INVALID_HANDLE_VALUE, nullptr /*secattr*/, PAGE_READWRITE, 0 /*sizeHigh*/,
       static_cast<DWORD>(sizeof(SharedData)), nullptr /*name*/);
   if (!mFileMapping) {
     return false;
   }
 
@@ -442,20 +432,40 @@ bool Provider::Initialize(HWND aWindowHa
   }
 
   mTransparencyMode = aTransparencyMode;
 
   return true;
 }
 
 Maybe<RemoteBackbufferHandles> Provider::CreateRemoteHandles() {
-  return Some(
-      RemoteBackbufferHandles(ipc::FileDescriptor(mFileMapping),
-                              ipc::FileDescriptor(mRequestReadyEvent),
-                              ipc::FileDescriptor(mResponseReadyEvent)));
+  HANDLE fileMapping = nullptr;
+  if (!ipc::DuplicateHandle(mFileMapping, mTargetProcessId, &fileMapping,
+                            0 /*desiredAccess*/, DUPLICATE_SAME_ACCESS)) {
+    return Nothing();
+  }
+
+  HANDLE requestReadyEvent = nullptr;
+  if (!ipc::DuplicateHandle(mRequestReadyEvent, mTargetProcessId,
+                            &requestReadyEvent, 0 /*desiredAccess*/,
+                            DUPLICATE_SAME_ACCESS)) {
+    return Nothing();
+  }
+
+  HANDLE responseReadyEvent = nullptr;
+  if (!ipc::DuplicateHandle(mResponseReadyEvent, mTargetProcessId,
+                            &responseReadyEvent, 0 /*desiredAccess*/,
+                            DUPLICATE_SAME_ACCESS)) {
+    return Nothing();
+  }
+
+  return Some(RemoteBackbufferHandles(
+      reinterpret_cast<WindowsHandle>(fileMapping),
+      reinterpret_cast<WindowsHandle>(requestReadyEvent),
+      reinterpret_cast<WindowsHandle>(responseReadyEvent)));
 }
 
 void Provider::UpdateTransparencyMode(nsTransparencyMode aTransparencyMode) {
   mTransparencyMode = aTransparencyMode;
 }
 
 void Provider::ThreadMain() {
   AUTO_PROFILER_REGISTER_THREAD("RemoteBackbuffer");
@@ -537,17 +547,17 @@ void Provider::HandleBorrowRequest(Borro
 
   // Preserve the contents of the old backbuffer (if it exists)
   if (mBackbuffer) {
     newBackbuffer->CopyPixelsFrom(*mBackbuffer);
     mBackbuffer.reset();
   }
 
   HANDLE remoteFileMapping =
-      newBackbuffer->CreateRemoteFileMapping(mTargetProcess);
+      newBackbuffer->CreateRemoteFileMapping(mTargetProcessId);
   if (!remoteFileMapping) {
     return;
   }
 
   aResponseData->result = ResponseResult::BorrowSuccess;
   aResponseData->width = width;
   aResponseData->height = height;
   aResponseData->fileMapping = remoteFileMapping;
@@ -599,32 +609,25 @@ Client::~Client() {
   }
 
   if (mFileMapping) {
     MOZ_ALWAYS_TRUE(::CloseHandle(mFileMapping));
   }
 }
 
 bool Client::Initialize(const RemoteBackbufferHandles& aRemoteHandles) {
-  MOZ_ASSERT(aRemoteHandles.fileMapping().IsValid());
-  MOZ_ASSERT(aRemoteHandles.requestReadyEvent().IsValid());
-  MOZ_ASSERT(aRemoteHandles.responseReadyEvent().IsValid());
+  MOZ_ASSERT(aRemoteHandles.fileMapping());
+  MOZ_ASSERT(aRemoteHandles.requestReadyEvent());
+  MOZ_ASSERT(aRemoteHandles.responseReadyEvent());
 
-  // FIXME: Due to PCompositorWidget using virtual Recv methods,
-  // RemoteBackbufferHandles is passed by const reference, and cannot have its
-  // signature customized, meaning that we need to clone the handles here.
-  //
-  // Once PCompositorWidget is migrated to use direct call semantics, the
-  // signature can be changed to accept RemoteBackbufferHandles by rvalue
-  // reference or value, and the DuplicateHandle calls here can be avoided.
-  mFileMapping = aRemoteHandles.fileMapping().ClonePlatformHandle().release();
+  mFileMapping = reinterpret_cast<HANDLE>(aRemoteHandles.fileMapping());
   mRequestReadyEvent =
-      aRemoteHandles.requestReadyEvent().ClonePlatformHandle().release();
+      reinterpret_cast<HANDLE>(aRemoteHandles.requestReadyEvent());
   mResponseReadyEvent =
-      aRemoteHandles.responseReadyEvent().ClonePlatformHandle().release();
+      reinterpret_cast<HANDLE>(aRemoteHandles.responseReadyEvent());
 
   void* mappedFilePtr =
       ::MapViewOfFile(mFileMapping, FILE_MAP_ALL_ACCESS, 0 /*offsetHigh*/,
                       0 /*offsetLow*/, 0 /*bytesToMap*/);
   if (!mappedFilePtr) {
     return false;
   }
 
--- a/widget/windows/RemoteBackbuffer.h
+++ b/widget/windows/RemoteBackbuffer.h
@@ -45,17 +45,17 @@ class Provider {
   void ThreadMain();
 
   void HandleBorrowRequest(BorrowResponseData* aResponseData,
                            bool aAllowSameBuffer);
   void HandlePresentRequest(const PresentRequestData& aRequestData,
                             PresentResponseData* aResponseData);
 
   HWND mWindowHandle;
-  HANDLE mTargetProcess;
+  DWORD mTargetProcessId;
   HANDLE mFileMapping;
   HANDLE mRequestReadyEvent;
   HANDLE mResponseReadyEvent;
   SharedData* mSharedDataPtr;
   bool mStopServiceThread;
   PRThread* mServiceThread;
   std::unique_ptr<PresentableSharedImage> mBackbuffer;
   mozilla::Atomic<nsTransparencyMode, MemoryOrdering::Relaxed>