Merge inbound to mozilla-central. a=merge
authorGurzau Raul <rgurzau@mozilla.com>
Wed, 04 Jul 2018 12:58:33 +0300
changeset 425019 cc3401e78e8bbae22e6dbc854e525ceae4923bcf
parent 424934 3d7f2fdc5bf7ca7521013b28e0d8be0785d2b58a (current diff)
parent 425018 ff25e66da36e4c95363e684880085f99d845da2a (diff)
child 425020 d090f23cc74045b3783d67bafb1710eb76044a62
child 425023 0c5ad222605bc6579f4e281a30fc55b0634c08e1
child 425046 796893f4d2f5555c8e29658bae05b98cffad0fe1
push id65909
push userrgurzau@mozilla.com
push dateWed, 04 Jul 2018 10:01:39 +0000
treeherderautoland@d090f23cc740 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone63.0a1
first release with
nightly linux32
cc3401e78e8b / 63.0a1 / 20180704100142 / files
nightly linux64
cc3401e78e8b / 63.0a1 / 20180704100142 / files
nightly mac
cc3401e78e8b / 63.0a1 / 20180704100142 / files
nightly win32
cc3401e78e8b / 63.0a1 / 20180704100142 / files
nightly win64
cc3401e78e8b / 63.0a1 / 20180704100142 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central. a=merge
--- a/accessible/windows/msaa/Compatibility.cpp
+++ b/accessible/windows/msaa/Compatibility.cpp
@@ -90,17 +90,18 @@ Compatibility::IsModuleVersionLessThan(H
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // Compatibility
 ////////////////////////////////////////////////////////////////////////////////
 
 static WindowsDllInterceptor sUser32Interceptor;
-static decltype(&InSendMessageEx) sInSendMessageExStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<decltype(&InSendMessageEx)>
+  sInSendMessageExStub;
 static bool sInSendMessageExHackEnabled = false;
 static PVOID sVectoredExceptionHandler = nullptr;
 
 #if defined(_MSC_VER)
 #include <intrin.h>
 #pragma intrinsic(_ReturnAddress)
 #define RETURN_ADDRESS() _ReturnAddress()
 #elif defined(__GNUC__) || defined(__clang__)
@@ -262,21 +263,19 @@ Compatibility::Init()
   }
 
   // If we have a consumer who is not NVDA, we enable detection for the
   // InSendMessageEx compatibility hack. NVDA does not require this.
   // We also skip UIA, as we see crashes there.
   if ((sConsumers & (~(UIAUTOMATION | NVDA))) &&
       BrowserTabsRemoteAutostart()) {
     sUser32Interceptor.Init("user32.dll");
-    if (!sInSendMessageExStub) {
-      sUser32Interceptor.AddHook("InSendMessageEx",
-                                 reinterpret_cast<intptr_t>(&InSendMessageExHook),
-                                 (void**)&sInSendMessageExStub);
-    }
+    sInSendMessageExStub.Set(sUser32Interceptor, "InSendMessageEx",
+                             &InSendMessageExHook);
+
     // The vectored exception handler allows us to catch exceptions ahead of any
     // SEH handlers.
     if (!sVectoredExceptionHandler) {
       // We need to let ASan's ShadowExceptionHandler remain in the firstHandler
       // position, otherwise we'll get infinite recursion when our handler
       // faults on shadow memory.
       const ULONG firstHandler = FALSE;
       sVectoredExceptionHandler =
--- a/browser/app/winlauncher/DllBlocklistWin.cpp
+++ b/browser/app/winlauncher/DllBlocklistWin.cpp
@@ -241,17 +241,18 @@ IsDllAllowed(const UNICODE_STRING& aLeaf
     gBlockSet.Add(info->name, version);
     return false;
   }
 
   return true;
 }
 
 typedef decltype(&NtMapViewOfSection) NtMapViewOfSection_func;
-static NtMapViewOfSection_func stub_NtMapViewOfSection;
+static mozilla::CrossProcessDllInterceptor::FuncHookType<NtMapViewOfSection_func>
+  stub_NtMapViewOfSection;
 
 static NTSTATUS NTAPI
 patched_NtMapViewOfSection(HANDLE aSection, HANDLE aProcess, PVOID* aBaseAddress,
                            ULONG_PTR aZeroBits, SIZE_T aCommitSize,
                            PLARGE_INTEGER aSectionOffset, PSIZE_T aViewSize,
                            SECTION_INHERIT aInheritDisposition,
                            ULONG aAllocationType, ULONG aProtectionFlags)
 {
@@ -351,19 +352,18 @@ private:
   DWORD   mPrevProt;
 };
 
 bool
 InitializeDllBlocklistOOP(HANDLE aChildProcess)
 {
   mozilla::CrossProcessDllInterceptor intcpt(aChildProcess);
   intcpt.Init(L"ntdll.dll");
-  bool ok = intcpt.AddDetour("NtMapViewOfSection",
-                             reinterpret_cast<intptr_t>(&patched_NtMapViewOfSection),
-                             (void**) &stub_NtMapViewOfSection);
+  bool ok = stub_NtMapViewOfSection.SetDetour(intcpt, "NtMapViewOfSection",
+                                              &patched_NtMapViewOfSection);
   if (!ok) {
     return false;
   }
 
   // Set the child process's copy of stub_NtMapViewOfSection
   SIZE_T bytesWritten;
   ok = !!::WriteProcessMemory(aChildProcess, &stub_NtMapViewOfSection,
                               &stub_NtMapViewOfSection,
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -990,22 +990,32 @@ nsContextMenu.prototype = {
     }
     if (!name)
       name = "snapshot.jpg";
 
     mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage", {}, {
       target: this.target,
     });
 
+    // Cache this because we fetch the data async
+    let {documentURIObject} = gContextMenuContentData;
+
     let onMessage = (message) => {
       mm.removeMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
+      // FIXME can we switch this to a blob URL?
       let dataURL = message.data.dataURL;
-      saveImageURL(dataURL, name, "SaveImageTitle", true, false,
-                   document.documentURIObject, null, null, null,
-                   isPrivate);
+      saveImageURL(dataURL, name, "SaveImageTitle",
+                   true, // bypass cache
+                   false, // don't skip prompt for where to save
+                   documentURIObject, // referrer
+                   null, // document
+                   null, // content type
+                   null, // content disposition
+                   isPrivate,
+                   this.principal);
     };
     mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
   },
 
   leaveDOMFullScreen() {
     document.exitFullscreen();
   },
 
@@ -1233,23 +1243,25 @@ nsContextMenu.prototype = {
     let isContentWindowPrivate = this.isRemote ? this.ownerDoc.isPrivate : undefined;
     let referrerURI = gContextMenuContentData.documentURIObject;
     let isPrivate = PrivateBrowsingUtils.isBrowserPrivate(this.browser);
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       this._canvasToBlobURL(this.target).then(function(blobURL) {
         saveImageURL(blobURL, "canvas.png", "SaveImageTitle",
                      true, false, referrerURI, null, null, null,
-                     isPrivate);
+                     isPrivate,
+                     document.nodePrincipal /* system, because blob: */);
       }, Cu.reportError);
     } else if (this.onImage) {
       urlSecurityCheck(this.mediaURL, this.principal);
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
                    false, referrerURI, null, gContextMenuContentData.contentType,
-                   gContextMenuContentData.contentDisposition, isPrivate);
+                   gContextMenuContentData.contentDisposition, isPrivate,
+                   this.principal);
     } else if (this.onVideo || this.onAudio) {
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
       this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, referrerURI,
                       this.frameOuterWindowID, "", isContentWindowPrivate);
     }
   },
 
   // Backwards-compatibility wrapper
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -684,17 +684,18 @@ function saveMedia() {
               null, gDocInfo.isContentWindowPrivate);
     }
   } else {
     selectSaveFolder(function(aDirectory) {
       if (aDirectory) {
         var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
           uniqueFile(aChosenData.file);
           internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
-                       aChosenData, aBaseURI, null, false, null, gDocInfo.isContentWindowPrivate);
+                       aChosenData, aBaseURI, null, false, null,
+                       gDocInfo.isContentWindowPrivate, gDocInfo.principal);
         };
 
         for (var i = 0; i < rowArray.length; i++) {
           let v = rowArray[i];
           let dir = aDirectory.clone();
           let item = gImageView.data[v][COL_IMAGE_NODE];
           let uriString = gImageView.data[v][COL_IMAGE_ADDRESS];
           let uri = makeURI(uriString);
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -300,17 +300,18 @@ function openLinkIn(url, where, params) 
       params.forceAboutBlankViewerInCurrent;
   var aResolveOnNewTabCreated = params.resolveOnNewTabCreated;
 
   if (where == "save") {
     // TODO(1073187): propagate referrerPolicy.
 
     // ContentClick.jsm passes isContentWindowPrivate for saveURL instead of passing a CPOW initiatingDoc
     if ("isContentWindowPrivate" in params) {
-      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, null, params.isContentWindowPrivate);
+      saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI,
+              null, params.isContentWindowPrivate, aPrincipal);
     } else {
       if (!aInitiatingDoc) {
         Cu.reportError("openUILink/openLinkIn was called with " +
           "where == 'save' but without initiatingDoc.  See bug 814264.");
         return;
       }
       saveURL(url, null, null, true, true, aNoReferrer ? null : aReferrerURI, aInitiatingDoc);
     }
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -148,17 +148,17 @@ nsMacShellService::SetDesktopBackground(
 
   nsCOMPtr<nsILoadContext> loadContext;
   nsCOMPtr<nsISupports> container = aElement->OwnerDoc()->GetContainer();
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
   if (docShell) {
     loadContext = do_QueryInterface(docShell);
   }
 
-  return wbp->SaveURI(imageURI, 0,
+  return wbp->SaveURI(imageURI, aElement->NodePrincipal(), 0,
                       docURI, aElement->OwnerDoc()->GetReferrerPolicy(),
                       nullptr, nullptr,
                       mBackgroundFile, loadContext);
 }
 
 NS_IMETHODIMP
 nsMacShellService::OnProgressChange(nsIWebProgress* aWebProgress,
                                     nsIRequest* aRequest,
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -549,17 +549,16 @@ var saveToFile = Task.async(function* (c
   // Create download and track its progress.
   // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
   // and modified to allow saving to arbitrary paths on disk.  Using these
   // objects as opposed to just writing with OS.File allows us to tie into the
   // download manager to record a download entry and to get visual feedback from
   // the downloads toolbar button when the save is done.
   const nsIWBP = Ci.nsIWebBrowserPersist;
   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES |
                 nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
                 nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
   const isPrivate =
     PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
   const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                   .createInstance(Ci.nsIWebBrowserPersist);
   persist.persistFlags = flags;
   const tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
@@ -568,17 +567,19 @@ var saveToFile = Task.async(function* (c
           "",
           null,
           null,
           null,
           persist,
           isPrivate);
   const listener = new DownloadListener(window, tr);
   persist.progressListener = listener;
+  const principal = Services.scriptSecurityManager.getSystemPrincipal();
   persist.savePrivacyAwareURI(sourceURI,
+                              principal,
                               0,
                               document.documentURIObject,
                               Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                               null,
                               null,
                               targetFileURI,
                               isPrivate);
 
--- a/devtools/shared/webconsole/screenshot-helper.js
+++ b/devtools/shared/webconsole/screenshot-helper.js
@@ -350,17 +350,16 @@ async function saveToFile(window, image)
   // Create download and track its progress.
   // This is adapted from saveURL in contentAreaUtils.js, but simplified greatly
   // and modified to allow saving to arbitrary paths on disk. Using these
   // objects as opposed to just writing with OS.File allows us to tie into the
   // download manager to record a download entry and to get visual feedback from
   // the downloads toolbar button when the save is done.
   const nsIWBP = Ci.nsIWebBrowserPersist;
   const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES |
                 nsIWBP.PERSIST_FLAGS_BYPASS_CACHE |
                 nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
   const isPrivate =
     PrivateBrowsingUtils.isContentWindowPrivate(document.defaultView);
   const persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
                   .createInstance(Ci.nsIWebBrowserPersist);
   persist.persistFlags = flags;
   const tr = Cc["@mozilla.org/transfer;1"].createInstance(Ci.nsITransfer);
@@ -369,17 +368,19 @@ async function saveToFile(window, image)
           "",
           null,
           null,
           null,
           persist,
           isPrivate);
   const listener = new DownloadListener(window, tr);
   persist.progressListener = listener;
+  const principal = Services.scriptSecurityManager.getSystemPrincipal();
   persist.savePrivacyAwareURI(sourceURI,
+                              principal,
                               0,
                               document.documentURIObject,
                               Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                               null,
                               null,
                               targetFileURI,
                               isPrivate);
 
--- a/devtools/startup/devtools-startup.js
+++ b/devtools/startup/devtools-startup.js
@@ -35,16 +35,18 @@ const { XPCOMUtils } = ChromeUtils.impor
 ChromeUtils.defineModuleGetter(this, "Services",
                                "resource://gre/modules/Services.jsm");
 ChromeUtils.defineModuleGetter(this, "AppConstants",
                                "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
                                "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableWidgets",
                                "resource:///modules/CustomizableWidgets.jsm");
+ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
+                               "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 // We don't want to spend time initializing the full loader here so we create
 // our own lazy require.
 XPCOMUtils.defineLazyGetter(this, "Telemetry", function() {
   const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
   // eslint-disable-next-line no-shadow
   const Telemetry = require("devtools/client/shared/telemetry");
 
@@ -943,26 +945,42 @@ const JsonView = {
    */
   onSave: function(message) {
     const chrome = Services.wm.getMostRecentWindow("navigator:browser");
     const browser = chrome.gBrowser.selectedBrowser;
     if (message.data === null) {
       // Save original contents
       chrome.saveBrowser(browser);
     } else {
+      if (!message.data.startsWith("blob:null") || !browser.contentPrincipal.isNullPrincipal) {
+        Cu.reportError("Got invalid request to save JSON data");
+        return;
+      }
       // The following code emulates saveBrowser, but:
       // - Uses the given blob URL containing the custom contents to save.
       // - Obtains the file name from the URL of the document, not the blob.
+      // - avoids passing the document and explicitly passes system principal.
+      //   We have a blob created by a null principal to save, and the null
+      //   principal is from the child. Null principals don't survive crossing
+      //   over IPC, so there's no other principal that'll work.
       const persistable = browser.frameLoader;
       persistable.startPersistence(0, {
         onDocumentReady(doc) {
           const uri = chrome.makeURI(doc.documentURI, doc.characterSet);
           const filename = chrome.getDefaultFileName(undefined, uri, doc, null);
-          chrome.internalSave(message.data, doc, filename, null, doc.contentType,
-            false, null, null, null, doc, false, null, undefined);
+          chrome.internalSave(message.data, null, filename, null, doc.contentType,
+            false /* bypass cache */,
+            null, /* filepicker title key */
+            null, /* file chosen */
+            null, /* referrer */
+            null, /* initiating document */
+            false, /* don't skip prompt for a location */
+            null, /* cache key */
+            PrivateBrowsingUtils.isBrowserPrivate(browser), /* private browsing ? */
+            Services.scriptSecurityManager.getSystemPrincipal());
         },
         onError(status) {
           throw new Error("JSON Viewer's onSave failed in startPersistence");
         }
       });
     }
   }
 };
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCounters.conf
@@ -110,8 +110,12 @@ method console.groupCollapsed
 method console.groupEnd
 method console.time
 method console.timeLog
 method console.timeEnd
 method console.exception
 method console.timeStamp
 method console.profile
 method console.profileEnd
+
+// document.open information
+custom DocumentOpen calls document.open in a way that creates a new Window object
+custom DocumentOpenReplace calls document.open in a way that creates a new Window object and replaces the old history entry.
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -136,16 +136,17 @@ nsContentAreaDragDrop::GetDragData(nsPID
 
 NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
 
 // SaveURIToFile
 // used on platforms where it's possible to drag items (e.g. images)
 // into the file system
 nsresult
 nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI,
+                                                 nsIPrincipal* inTriggeringPrincipal,
                                                  nsIFile* inDestFile,
                                                  bool isPrivate)
 {
   nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
   if (!sourceURL) {
     return NS_ERROR_NO_INTERFACE;
   }
 
@@ -157,17 +158,18 @@ nsContentAreaDragDropDataProvider::SaveU
   nsCOMPtr<nsIWebBrowserPersist> persist =
     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
                       &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
 
   // referrer policy can be anything since the referrer is nullptr
-  return persist->SavePrivacyAwareURI(inSourceURI, 0, nullptr,
+  return persist->SavePrivacyAwareURI(inSourceURI,
+                                      inTriggeringPrincipal, 0, nullptr,
                                       mozilla::net::RP_Unset,
                                       nullptr, nullptr,
                                       inDestFile, isPrivate);
 }
 
 /*
  * Check if the provided filename extension is valid for the MIME type and
  * return the MIME type's primary extension.
@@ -337,17 +339,19 @@ nsContentAreaDragDropDataProvider::GetFl
     rv = destDirectory->Clone(getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
 
     file->Append(targetFilename);
 
     bool isPrivate;
     aTransferable->GetIsPrivateData(&isPrivate);
 
-    rv = SaveURIToFile(sourceURI, file, isPrivate);
+    nsCOMPtr<nsIPrincipal> principal;
+    aTransferable->GetRequestingPrincipal(getter_AddRefs(principal));
+    rv = SaveURIToFile(sourceURI, principal, file, isPrivate);
     // send back an nsIFile
     if (NS_SUCCEEDED(rv)) {
       CallQueryInterface(file, aData);
       *aDataLen = sizeof(nsIFile*);
     }
   }
 
   return rv;
--- a/dom/base/nsContentAreaDragDrop.h
+++ b/dom/base/nsContentAreaDragDrop.h
@@ -72,14 +72,15 @@ class nsContentAreaDragDropDataProvider 
 {
   virtual ~nsContentAreaDragDropDataProvider() {}
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIFLAVORDATAPROVIDER
 
   nsresult SaveURIToFile(nsIURI* inSourceURI,
+                         nsIPrincipal* inTriggeringPrincipal,
                          nsIFile* inDestFile, bool isPrivate);
 };
 
 
 #endif /* nsContentAreaDragDrop_h__ */
 
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1334,16 +1334,24 @@ nsHTMLDocument::Open(JSContext* cx,
            callerDocURI ? callerDocURI->GetSpecOrDefault().get() : "",
            thisURI ? thisURI->GetSpecOrDefault().get() : "");
 #endif
 
     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
+  // At this point we know this is a valid-enough document.open() call
+  // and not a no-op.  Increment our use counters.
+  SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpen);
+  bool isReplace = aReplace.LowerCaseEqualsLiteral("replace");
+  if (isReplace) {
+    SetDocumentAndPageUseCounter(eUseCounter_custom_DocumentOpenReplace);
+  }
+
   // Stop current loads targeted at the window this document is in.
   if (mScriptGlobalObject) {
     nsCOMPtr<nsIContentViewer> cv;
     shell->GetContentViewer(getter_AddRefs(cv));
 
     if (cv) {
       bool okToUnload;
       if (NS_SUCCEEDED(cv->PermitUnload(&okToUnload)) && !okToUnload) {
@@ -1562,18 +1570,17 @@ nsHTMLDocument::Open(JSContext* cx,
   // Prepare the docshell and the document viewer for the impending
   // out of band document.write()
   shell->PrepareForNewContentModel();
 
   // Now check whether we were opened with a "replace" argument.  If
   // so, we need to tell the docshell to not create a new history
   // entry for this load. Otherwise, make sure that we're doing a normal load,
   // not whatever type of load was previously done on this docshell.
-  shell->SetLoadType(aReplace.LowerCaseEqualsLiteral("replace") ?
-                       LOAD_NORMAL_REPLACE : LOAD_NORMAL);
+  shell->SetLoadType(isReplace ? LOAD_NORMAL_REPLACE : LOAD_NORMAL);
 
   nsCOMPtr<nsIContentViewer> cv;
   shell->GetContentViewer(getter_AddRefs(cv));
   if (cv) {
     cv->LoadStart(this);
   }
 
   // Add a wyciwyg channel request into the document load group
--- a/dom/media/gmp/ChromiumCDMAdapter.cpp
+++ b/dom/media/gmp/ChromiumCDMAdapter.cpp
@@ -192,17 +192,18 @@ ChromiumCDMAdapter::Supports(int32_t aMo
 #ifdef XP_WIN
 
 static WindowsDllInterceptor sKernel32Intercept;
 
 typedef DWORD(WINAPI* QueryDosDeviceWFnPtr)(_In_opt_ LPCWSTR lpDeviceName,
                                             _Out_ LPWSTR lpTargetPath,
                                             _In_ DWORD ucchMax);
 
-static QueryDosDeviceWFnPtr sOriginalQueryDosDeviceWFnPtr = nullptr;
+static WindowsDllInterceptor::FuncHookType<QueryDosDeviceWFnPtr>
+  sOriginalQueryDosDeviceWFnPtr;
 
 static std::unordered_map<std::wstring, std::wstring>* sDeviceNames = nullptr;
 
 DWORD WINAPI
 QueryDosDeviceWHook(LPCWSTR lpDeviceName, LPWSTR lpTargetPath, DWORD ucchMax)
 {
   if (!sDeviceNames) {
     return 0;
@@ -271,19 +272,18 @@ InitializeHooks()
   }
   initialized = true;
   sDeviceNames = new std::unordered_map<std::wstring, std::wstring>();
   for (const std::wstring& name : GetDosDeviceNames()) {
     sDeviceNames->emplace(name, GetDeviceMapping(name));
   }
 
   sKernel32Intercept.Init("kernelbase.dll");
-  sKernel32Intercept.AddHook("QueryDosDeviceW",
-                             reinterpret_cast<intptr_t>(QueryDosDeviceWHook),
-                             (void**)(&sOriginalQueryDosDeviceWFnPtr));
+  sOriginalQueryDosDeviceWFnPtr.Set(sKernel32Intercept, "QueryDosDeviceW",
+                                    &QueryDosDeviceWHook);
 }
 #endif
 
 HostFile::HostFile(HostFile&& aOther)
   : mPath(aOther.mPath)
   , mFile(aOther.TakePlatformFile())
 {
 }
--- a/dom/plugins/base/nsPluginNativeWindowWin.cpp
+++ b/dom/plugins/base/nsPluginNativeWindowWin.cpp
@@ -352,29 +352,33 @@ static WindowsDllInterceptor sUser32Inte
 typedef LONG_PTR
   (WINAPI *User32SetWindowLongPtrA)(HWND hWnd,
                                     int nIndex,
                                     LONG_PTR dwNewLong);
 typedef LONG_PTR
   (WINAPI *User32SetWindowLongPtrW)(HWND hWnd,
                                     int nIndex,
                                     LONG_PTR dwNewLong);
-static User32SetWindowLongPtrA sUser32SetWindowLongAHookStub = nullptr;
-static User32SetWindowLongPtrW sUser32SetWindowLongWHookStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrA>
+  sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrW>
+  sUser32SetWindowLongWHookStub;
 #else
 typedef LONG
 (WINAPI *User32SetWindowLongA)(HWND hWnd,
                                int nIndex,
                                LONG dwNewLong);
 typedef LONG
 (WINAPI *User32SetWindowLongW)(HWND hWnd,
                                int nIndex,
                                LONG dwNewLong);
-static User32SetWindowLongA sUser32SetWindowLongAHookStub = nullptr;
-static User32SetWindowLongW sUser32SetWindowLongWHookStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongA>
+  sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongW>
+  sUser32SetWindowLongWHookStub;
 #endif
 static inline bool
 SetWindowLongHookCheck(HWND hWnd,
                        int nIndex,
                        LONG_PTR newLong)
 {
   nsPluginNativeWindowWin * win =
     (nsPluginNativeWindowWin *)GetProp(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION);
@@ -443,33 +447,25 @@ SetWindowLongWHook(HWND hWnd,
   return proc;
 }
 
 static void
 HookSetWindowLongPtr()
 {
   sUser32Intercept.Init("user32.dll");
 #ifdef _WIN64
-  if (!sUser32SetWindowLongAHookStub)
-    sUser32Intercept.AddHook("SetWindowLongPtrA",
-                             reinterpret_cast<intptr_t>(SetWindowLongPtrAHook),
-                             (void**) &sUser32SetWindowLongAHookStub);
-  if (!sUser32SetWindowLongWHookStub)
-    sUser32Intercept.AddHook("SetWindowLongPtrW",
-                             reinterpret_cast<intptr_t>(SetWindowLongPtrWHook),
-                             (void**) &sUser32SetWindowLongWHookStub);
+  sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongPtrA",
+                                    &SetWindowLongPtrAHook);
+  sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongPtrW",
+                                    &SetWindowLongPtrWHook);
 #else
-  if (!sUser32SetWindowLongAHookStub)
-    sUser32Intercept.AddHook("SetWindowLongA",
-                             reinterpret_cast<intptr_t>(SetWindowLongAHook),
-                             (void**) &sUser32SetWindowLongAHookStub);
-  if (!sUser32SetWindowLongWHookStub)
-    sUser32Intercept.AddHook("SetWindowLongW",
-                             reinterpret_cast<intptr_t>(SetWindowLongWHook),
-                             (void**) &sUser32SetWindowLongWHookStub);
+  sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongA",
+                                    &SetWindowLongAHook);
+  sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongW",
+                                    &SetWindowLongWHook);
 #endif
 }
 
 /**
  *   nsPluginNativeWindowWin implementation
  */
 nsPluginNativeWindowWin::nsPluginNativeWindowWin() : nsPluginNativeWindow()
 {
--- a/dom/plugins/ipc/FunctionHook.cpp
+++ b/dom/plugins/ipc/FunctionHook.cpp
@@ -163,23 +163,23 @@ BOOL WINAPI PrintDlgWHook(LPPRINTDLGW aD
 
 // Hooking CreateFileW for protected-mode magic
 static WindowsDllInterceptor sKernel32Intercept;
 typedef HANDLE (WINAPI *CreateFileWPtr)(LPCWSTR aFname, DWORD aAccess,
                                         DWORD aShare,
                                         LPSECURITY_ATTRIBUTES aSecurity,
                                         DWORD aCreation, DWORD aFlags,
                                         HANDLE aFTemplate);
-static CreateFileWPtr sCreateFileWStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<CreateFileWPtr> sCreateFileWStub;
 typedef HANDLE (WINAPI *CreateFileAPtr)(LPCSTR aFname, DWORD aAccess,
                                         DWORD aShare,
                                         LPSECURITY_ATTRIBUTES aSecurity,
                                         DWORD aCreation, DWORD aFlags,
                                         HANDLE aFTemplate);
-static CreateFileAPtr sCreateFileAStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<CreateFileAPtr> sCreateFileAStub;
 
 // Windows 8 RTM (kernelbase's version is 6.2.9200.16384) doesn't call
 // CreateFileW from CreateFileA.
 // So we hook CreateFileA too to use CreateFileW hook.
 static HANDLE WINAPI
 CreateFileAHookFn(LPCSTR aFname, DWORD aAccess, DWORD aShare,
                   LPSECURITY_ATTRIBUTES aSecurity, DWORD aCreation, DWORD aFlags,
                   HANDLE aFTemplate)
@@ -258,17 +258,17 @@ CreateFileWHookFn(LPCWSTR aFname, DWORD 
     if (GetTempFileNameW(tempPath, L"fx", 0, tempFile) == 0) {
       break;
     }
     HANDLE replacement =
       sCreateFileWStub(tempFile, GENERIC_READ | GENERIC_WRITE, aShare,
                        aSecurity, TRUNCATE_EXISTING,
                        FILE_ATTRIBUTE_TEMPORARY |
                          FILE_FLAG_DELETE_ON_CLOSE,
-                       NULL);
+                       nullptr);
     if (replacement == INVALID_HANDLE_VALUE) {
       break;
     }
 
     HANDLE original = sCreateFileWStub(aFname, aAccess, aShare, aSecurity,
                                        aCreation, aFlags, aFTemplate);
     if (original != INVALID_HANDLE_VALUE) {
       // copy original to replacement
@@ -295,33 +295,22 @@ CreateFileWHookFn(LPCWSTR aFname, DWORD 
     return replacement;
   }
   return sCreateFileWStub(aFname, aAccess, aShare, aSecurity, aCreation, aFlags,
                           aFTemplate);
 }
 
 void FunctionHook::HookProtectedMode()
 {
-  // Make sure we only do this once.
-  static bool sRunOnce = false;
-  if (sRunOnce) {
-    return;
-  }
-  sRunOnce = true;
-
   // Legacy code.  Uses the nsWindowsDLLInterceptor directly instead of
   // using the FunctionHook
   sKernel32Intercept.Init("kernel32.dll");
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Plugin);
-  sKernel32Intercept.AddHook("CreateFileW",
-                             reinterpret_cast<intptr_t>(CreateFileWHookFn),
-                             (void**) &sCreateFileWStub);
-  sKernel32Intercept.AddHook("CreateFileA",
-                             reinterpret_cast<intptr_t>(CreateFileAHookFn),
-                             (void**) &sCreateFileAStub);
+  sCreateFileWStub.Set(sKernel32Intercept, "CreateFileW", &CreateFileWHookFn);
+  sCreateFileAStub.Set(sKernel32Intercept, "CreateFileA", &CreateFileAHookFn);
 }
 
 #endif // defined(XP_WIN)
 
 #define FUN_HOOK(x) static_cast<FunctionHook*>(x)
 
 void
 FunctionHook::AddFunctionHooks(FunctionHookArray& aHooks)
--- a/dom/plugins/ipc/FunctionHook.h
+++ b/dom/plugins/ipc/FunctionHook.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef dom_plugins_ipc_functionhook_h
 #define dom_plugins_ipc_functionhook_h 1
 
 #include "IpdlTuple.h"
 #include "base/process.h"
+#include "mozilla/Atomics.h"
 
 #if defined(XP_WIN)
 #include "nsWindowsDllInterceptor.h"
 #endif
 
 namespace mozilla {
 namespace plugins {
 
@@ -91,22 +92,29 @@ public:
 };
 
 // Type of function that returns true if a function should be hooked according to quirks.
 typedef bool(ShouldHookFunc)(int aQuirks);
 
 template<FunctionHookId functionId, typename FunctionType>
 class BasicFunctionHook : public FunctionHook
 {
+#if defined(XP_WIN)
+  using FuncHookType = WindowsDllInterceptor::FuncHookType<FunctionType*>;
+#endif // defined(XP_WIN)
+
 public:
   BasicFunctionHook(const char* aModuleName,
                     const char* aFunctionName, FunctionType* aOldFunction,
-                    FunctionType* aNewFunction) :
-    mOldFunction(aOldFunction), mRegistration(UNREGISTERED), mModuleName(aModuleName),
-    mFunctionName(aFunctionName), mNewFunction(aNewFunction)
+                    FunctionType* aNewFunction)
+    : mOldFunction(aOldFunction)
+    , mRegistration(UNREGISTERED)
+    , mModuleName(aModuleName)
+    , mFunctionName(aFunctionName)
+    , mNewFunction(aNewFunction)
   {
     MOZ_ASSERT(mOldFunction);
     MOZ_ASSERT(mNewFunction);
   }
 
   /**
    * Hooks the function if we haven't already and if ShouldHook() says to.
    */
@@ -123,17 +131,20 @@ public:
   FunctionHookId FunctionId() const override { return functionId; }
 
   FunctionType* OriginalFunction() const { return mOldFunction; }
 
 protected:
   // Once the function is hooked, this field will take the value of a pointer to
   // a function that performs the old behavior.  Before that, it is a pointer to
   // the original function.
-  FunctionType* mOldFunction;
+  Atomic<FunctionType*> mOldFunction;
+#if defined(XP_WIN)
+  FuncHookType mStub;
+#endif // defined(XP_WIN)
 
   enum RegistrationStatus { UNREGISTERED, FAILED, SUCCEEDED };
   RegistrationStatus mRegistration;
 
   // The name of the module containing the function to hook.  E.g. "user32.dll".
   const nsCString mModuleName;
   // The name of the function in the module.
   const nsCString mFunctionName;
@@ -165,24 +176,26 @@ BasicFunctionHook<functionId, FunctionTy
 
 #if defined(XP_WIN)
   WindowsDllInterceptor* dllInterceptor =
     FunctionHook::GetDllInterceptorFor(mModuleName.Data());
   if (!dllInterceptor) {
     return false;
   }
 
-  isHooked =
-    dllInterceptor->AddHook(mFunctionName.Data(), reinterpret_cast<intptr_t>(mNewFunction),
-                            reinterpret_cast<void**>(&mOldFunction));
+  isHooked = mStub.Set(*dllInterceptor, mFunctionName.Data(), mNewFunction);
 #endif
 
   if (isHooked) {
+#if defined(XP_WIN)
+    mOldFunction = mStub.GetStub();
+#endif
     mRegistration = SUCCEEDED;
   }
+
   HOOK_LOG(LogLevel::Debug,
            ("Registering to intercept function '%s' : '%s'", mFunctionName.Data(),
             SuccessMsg(isHooked)));
 
   return isHooked;
 }
 
 }
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -68,31 +68,27 @@ typedef BOOL (WINAPI *User32TrackPopupMe
                                             UINT uFlags,
                                             int x,
                                             int y,
                                             int nReserved,
                                             HWND hWnd,
                                             CONST RECT *prcRect);
 static WindowsDllInterceptor sUser32Intercept;
 static HWND sWinlessPopupSurrogateHWND = nullptr;
-static User32TrackPopupMenu sUser32TrackPopupMenuStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<User32TrackPopupMenu> sUser32TrackPopupMenuStub;
 
 static WindowsDllInterceptor sImm32Intercept;
-static decltype(ImmGetContext)* sImm32ImmGetContextStub = nullptr;
-static decltype(ImmGetCompositionStringW)* sImm32ImmGetCompositionStringStub =
-                                             nullptr;
-static decltype(ImmSetCandidateWindow)* sImm32ImmSetCandidateWindowStub =
-                                          nullptr;
-static decltype(ImmNotifyIME)* sImm32ImmNotifyIME = nullptr;
-static decltype(ImmAssociateContextEx)* sImm32ImmAssociateContextExStub =
-                                          nullptr;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetContext)> sImm32ImmGetContextStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmGetCompositionStringW)> sImm32ImmGetCompositionStringStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmSetCandidateWindow)> sImm32ImmSetCandidateWindowStub;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmNotifyIME)> sImm32ImmNotifyIME;
+static WindowsDllInterceptor::FuncHookType<decltype(&ImmAssociateContextEx)> sImm32ImmAssociateContextExStub;
+
 static PluginInstanceChild* sCurrentPluginInstance = nullptr;
 static const HIMC sHookIMC = (const HIMC)0xefefefef;
-static bool sPopupMenuHookSet;
-static bool sSetWindowLongHookSet;
 
 using mozilla::gfx::SharedDIB;
 
 // Flash WM_USER message delay time for PostDelayedTask. Borrowed
 // from Chromium's web plugin delegate src. See 'flash msg throttling
 // helpers' section for details.
 const int kFlashWMUSERMessageThrottleDelayMs = 5;
 
@@ -1788,29 +1784,29 @@ PluginInstanceChild::MaybePostKeyMessage
 typedef LONG_PTR
   (WINAPI *User32SetWindowLongPtrA)(HWND hWnd,
                                     int nIndex,
                                     LONG_PTR dwNewLong);
 typedef LONG_PTR
   (WINAPI *User32SetWindowLongPtrW)(HWND hWnd,
                                     int nIndex,
                                     LONG_PTR dwNewLong);
-static User32SetWindowLongPtrA sUser32SetWindowLongAHookStub = nullptr;
-static User32SetWindowLongPtrW sUser32SetWindowLongWHookStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrA> sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongPtrW> sUser32SetWindowLongWHookStub;
 #else
 typedef LONG
 (WINAPI *User32SetWindowLongA)(HWND hWnd,
                                int nIndex,
                                LONG dwNewLong);
 typedef LONG
 (WINAPI *User32SetWindowLongW)(HWND hWnd,
                                int nIndex,
                                LONG dwNewLong);
-static User32SetWindowLongA sUser32SetWindowLongAHookStub = nullptr;
-static User32SetWindowLongW sUser32SetWindowLongWHookStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongA> sUser32SetWindowLongAHookStub;
+static WindowsDllInterceptor::FuncHookType<User32SetWindowLongW> sUser32SetWindowLongWHookStub;
 #endif
 
 extern LRESULT CALLBACK
 NeuteredWindowProc(HWND hwnd,
                    UINT uMsg,
                    WPARAM wParam,
                    LPARAM lParam);
 
@@ -1910,37 +1906,27 @@ PluginInstanceChild::SetWindowLongWHook(
 
 void
 PluginInstanceChild::HookSetWindowLongPtr()
 {
     if (!(GetQuirks() & QUIRK_FLASH_HOOK_SETLONGPTR)) {
         return;
     }
 
-    // Only pass through here once
-    if (sSetWindowLongHookSet) {
-        return;
-    }
-    sSetWindowLongHookSet = true;
-
     sUser32Intercept.Init("user32.dll");
 #ifdef _WIN64
-    if (!sUser32SetWindowLongAHookStub)
-        sUser32Intercept.AddHook("SetWindowLongPtrA", reinterpret_cast<intptr_t>(SetWindowLongPtrAHook),
-                                 (void**) &sUser32SetWindowLongAHookStub);
-    if (!sUser32SetWindowLongWHookStub)
-        sUser32Intercept.AddHook("SetWindowLongPtrW", reinterpret_cast<intptr_t>(SetWindowLongPtrWHook),
-                                 (void**) &sUser32SetWindowLongWHookStub);
+    sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongPtrA",
+                                      &SetWindowLongPtrAHook);
+    sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongPtrW",
+                                      &SetWindowLongPtrWHook);
 #else
-    if (!sUser32SetWindowLongAHookStub)
-        sUser32Intercept.AddHook("SetWindowLongA", reinterpret_cast<intptr_t>(SetWindowLongAHook),
-                                 (void**) &sUser32SetWindowLongAHookStub);
-    if (!sUser32SetWindowLongWHookStub)
-        sUser32Intercept.AddHook("SetWindowLongW", reinterpret_cast<intptr_t>(SetWindowLongWHook),
-                                 (void**) &sUser32SetWindowLongWHookStub);
+    sUser32SetWindowLongAHookStub.Set(sUser32Intercept, "SetWindowLongA",
+                                      &SetWindowLongAHook);
+    sUser32SetWindowLongWHookStub.Set(sUser32Intercept, "SetWindowLongW",
+                                      &SetWindowLongWHook);
 #endif
 }
 
 /* windowless track popup menu helpers */
 
 BOOL
 WINAPI
 PluginInstanceChild::TrackPopupHookProc(HMENU hMenu,
@@ -1998,29 +1984,23 @@ PluginInstanceChild::TrackPopupHookProc(
 
 void
 PluginInstanceChild::InitPopupMenuHook()
 {
     if (!(GetQuirks() & QUIRK_WINLESS_TRACKPOPUP_HOOK)) {
         return;
     }
 
-    // Only pass through here once
-    if (sPopupMenuHookSet) {
-        return;
-    }
-    sPopupMenuHookSet = true;
-
     // Note, once WindowsDllInterceptor is initialized for a module,
     // it remains initialized for that particular module for it's
     // lifetime. Additional instances are needed if other modules need
     // to be hooked.
     sUser32Intercept.Init("user32.dll");
-    sUser32Intercept.AddHook("TrackPopupMenu", reinterpret_cast<intptr_t>(TrackPopupHookProc),
-                             (void**) &sUser32TrackPopupMenuStub);
+    sUser32TrackPopupMenuStub.Set(sUser32Intercept, "TrackPopupMenu",
+                                  &TrackPopupHookProc);
 }
 
 void
 PluginInstanceChild::CreateWinlessPopupSurrogate()
 {
     // already initialized
     if (mWinlessPopupSurrogateHWND)
         return;
@@ -2152,46 +2132,33 @@ PluginInstanceChild::ImmAssociateContext
 
 void
 PluginInstanceChild::InitImm32Hook()
 {
     if (!(GetQuirks() & QUIRK_WINLESS_HOOK_IME)) {
         return;
     }
 
-    if (sImm32ImmGetContextStub) {
-        return;
-    }
-
     // When using windowless plugin, IMM API won't work due ot OOP.
     //
     // ImmReleaseContext on Windows 7+ just returns TRUE only, so we don't
     // need to hook this.
 
     sImm32Intercept.Init("imm32.dll");
-    sImm32Intercept.AddHook(
-        "ImmGetContext",
-        reinterpret_cast<intptr_t>(ImmGetContextProc),
-        (void**)&sImm32ImmGetContextStub);
-    sImm32Intercept.AddHook(
-        "ImmGetCompositionStringW",
-        reinterpret_cast<intptr_t>(ImmGetCompositionStringProc),
-        (void**)&sImm32ImmGetCompositionStringStub);
-    sImm32Intercept.AddHook(
-        "ImmSetCandidateWindow",
-        reinterpret_cast<intptr_t>(ImmSetCandidateWindowProc),
-        (void**)&sImm32ImmSetCandidateWindowStub);
-    sImm32Intercept.AddHook(
-        "ImmNotifyIME",
-        reinterpret_cast<intptr_t>(ImmNotifyIME),
-        (void**)&sImm32ImmNotifyIME);
-    sImm32Intercept.AddHook(
-        "ImmAssociateContextEx",
-        reinterpret_cast<intptr_t>(ImmAssociateContextExProc),
-        (void**)&sImm32ImmAssociateContextExStub);
+    sImm32ImmGetContextStub.Set(sImm32Intercept, "ImmGetContext",
+                                &ImmGetContextProc);
+    sImm32ImmGetCompositionStringStub.Set(sImm32Intercept,
+                                          "ImmGetCompositionStringW",
+                                          &ImmGetCompositionStringProc);
+    sImm32ImmSetCandidateWindowStub.Set(sImm32Intercept,
+                                        "ImmSetCandidateWindow",
+                                        &ImmSetCandidateWindowProc);
+    sImm32ImmNotifyIME.Set(sImm32Intercept, "ImmNotifyIME", &ImmNotifyIME);
+    sImm32ImmAssociateContextExStub.Set(sImm32Intercept, "ImmAssociateContextEx",
+                                        &ImmAssociateContextExProc);
 }
 
 void
 PluginInstanceChild::DestroyWinlessPopupSurrogate()
 {
     if (mWinlessPopupSurrogateHWND)
         DestroyWindow(mWinlessPopupSurrogateHWND);
     mWinlessPopupSurrogateHWND = nullptr;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -52,16 +52,20 @@ support-files =
 run-if = e10s
 [browser_largeAllocation_win32.js]
 skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
 [browser_largeAllocation_non_win32.js]
 skip-if = !e10s || (os == "win" && processor == "x86") || (verify && debug && (os == 'linux')) # Large-Allocation requires e10s
 [browser_localStorage_e10s.js]
 skip-if = !e10s || verify # This is a test of e10s functionality.
 [browser_localStorage_privatestorageevent.js]
+[browser_persist_cookies.js]
+support-files =
+  set-samesite-cookies-and-redirect.sjs
+  mimeme.sjs
 [browser_test_focus_after_modal_state.js]
 skip-if = verify
 support-files =
   focus_after_prompt.html
 [browser_test_new_window_from_content.js]
 tags = openwindow
 skip-if = toolkit == 'android'  || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_persist_cookies.js
@@ -0,0 +1,94 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.org");
+const TEST_PATH2 = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
+
+var MockFilePicker = SpecialPowers.MockFilePicker;
+MockFilePicker.init(window);
+
+registerCleanupFunction(async function() {
+  info("Running the cleanup code");
+  MockFilePicker.cleanup();
+  Services.obs.removeObserver(checkRequest, "http-on-modify-request");
+  if (gTestDir && gTestDir.exists()) {
+    // On Windows, sometimes nsIFile.remove() throws, probably because we're
+    // still writing to the directory we're trying to remove, despite
+    // waiting for the download to complete. Just retry a bit later...
+    let succeeded = false;
+    while (!succeeded) {
+      try {
+        gTestDir.remove(true);
+        succeeded = true;
+      } catch (ex) {
+        await new Promise(requestAnimationFrame);
+      }
+    }
+  }
+});
+
+let gTestDir = null;
+
+
+function checkRequest(subject) {
+  let httpChannel = subject.QueryInterface(Ci.nsIHttpChannel);
+  let spec = httpChannel.URI.spec;
+  // Ignore initial requests for page that sets cookies and its favicon, which may not have
+  // cookies.
+  if (httpChannel.URI.host == "example.org" && !spec.endsWith("favicon.ico") && !spec.includes("redirect.sjs")) {
+    let cookie = httpChannel.getRequestHeader("cookie");
+    is(cookie.trim(), "normalCookie=true", "Should have correct cookie in request for " + spec);
+  }
+}
+
+function createTemporarySaveDirectory() {
+  var saveDir = Services.dirsvc.get("TmpD", Ci.nsIFile);
+  saveDir.append("testsavedir");
+  if (!saveDir.exists()) {
+    info("create testsavedir!");
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
+  }
+  info("return from createTempSaveDir: " + saveDir.path);
+  return saveDir;
+}
+
+add_task(async function() {
+  await BrowserTestUtils.withNewTab("about:blank", async function(browser) {
+    Services.obs.addObserver(checkRequest, "http-on-modify-request");
+    BrowserTestUtils.loadURI(browser, TEST_PATH + "set-samesite-cookies-and-redirect.sjs");
+    // Test that the original document load doesn't send same-site cookies.
+    await BrowserTestUtils.browserLoaded(browser, true, TEST_PATH2 + "set-samesite-cookies-and-redirect.sjs");
+    // Now check the saved page.
+    // Create the folder the link will be saved into.
+    gTestDir = createTemporarySaveDirectory();
+    let destFile = gTestDir.clone();
+
+    MockFilePicker.displayDirectory = gTestDir;
+    let fileName;
+    MockFilePicker.showCallback = function(fp) {
+      info("showCallback");
+      fileName = fp.defaultString;
+      info("fileName: " + fileName);
+      destFile.append(fileName);
+      info("path: " + destFile.path);
+      MockFilePicker.setFiles([destFile]);
+      MockFilePicker.filterIndex = 0; // kSaveAsType_Complete
+      info("done showCallback");
+    };
+    saveBrowser(browser);
+    await new Promise(async (resolve) => {
+      let dls = await Downloads.getList(Downloads.PUBLIC);
+      dls.addView({
+        onDownloadChanged(download) {
+          if (download.succeeded) {
+            dls.removeView(this);
+            dls.removeFinished();
+            resolve();
+          }
+        }
+      });
+    });
+  });
+});
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/mimeme.sjs
@@ -0,0 +1,26 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function handleRequest(request, response) {
+  let mimeType = request.queryString.match(/type=([a-z]*)/)[1];
+  switch (mimeType) {
+    case "css":
+      response.setHeader("Content-Type", "text/css");
+      response.write("#hi {color: red}");
+      break;
+    case "js":
+      response.setHeader("Content-Type", "application/javascript");
+      response.write("var foo;");
+      break;
+    case "png":
+      response.setHeader("Content-Type", "image/png");
+      response.write("");
+      break;
+    case "html":
+      response.setHeader("Content-Type", "text/html");
+      response.write("<body>I am a subframe</body>");
+      break;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/set-samesite-cookies-and-redirect.sjs
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function handleRequest(request, response) {
+  // Set cookies and redirect for .org:
+  if (request.host.endsWith(".org")) {
+    response.setHeader("Set-Cookie", "normalCookie=true; path=/;", true);
+    response.setHeader("Set-Cookie", "laxHeader=true; path=/; SameSite=Lax", true);
+    response.setHeader("Set-Cookie", "strictHeader=true; path=/; SameSite=Strict", true);
+    response.write(`
+      <head>
+        <meta http-equiv='set-cookie' content='laxMeta=true; path=/; SameSite=Lax'>
+        <meta http-equiv='set-cookie' content='strictMeta=true; path=/; SameSite=Strict'>
+      </head>
+      <body>
+        <script>
+        document.cookie = 'laxScript=true; path=/; SameSite=Lax';
+        document.cookie = 'strictScript=true; path=/; SameSite=Strict';
+        location.href = location.href.replace(/\.org/, ".com");
+        </script>
+      </body>`);
+  } else {
+    let baseURI = "https://example.org/" + request.path.replace(/[a-z-]*\.sjs/, "mimeme.sjs?type=");
+    response.write(`
+      <link rel="stylesheet" type="text/css" href="${baseURI}css">
+      <iframe src="${baseURI}html"></iframe>
+      <script src="${baseURI}js"></script>
+      <img src="${baseURI}png">
+    `);
+  }
+}
--- a/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
+++ b/dom/webbrowserpersist/PWebBrowserPersistDocument.ipdl
@@ -5,16 +5,17 @@
 
 include protocol PContent;
 include protocol PWebBrowserPersistResources;
 include protocol PWebBrowserPersistSerialize;
 include protocol PFileDescriptorSet;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 
+include PBackgroundSharedTypes;
 include IPCStream;
 
 namespace mozilla {
 
 // nsIWebBrowserPersistDocument has attributes which can be read
 // synchronously.  To avoid using sync IPC for them, the actor sends
 // this structure from the child to the parent before the parent actor
 // is exposed to XPCOM.
@@ -24,16 +25,17 @@ struct WebBrowserPersistDocumentAttrs {
   nsCString baseURI;
   nsCString contentType;
   nsCString characterSet;
   nsString title;
   nsString referrer;
   nsString contentDisposition;
   uint32_t cacheKey;
   uint32_t persistFlags;
+  PrincipalInfo principal;
 };
 
 // IPDL doesn't have tuples, so this gives the pair of strings from
 // nsIWebBrowserPersistURIMap::getURIMapping a name.
 struct WebBrowserPersistURIMapEntry {
   nsCString mapFrom;
   nsCString mapTo;
 };
--- a/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistDocumentChild.cpp
@@ -36,16 +36,17 @@ void
 WebBrowserPersistDocumentChild::Start(nsIWebBrowserPersistDocument* aDocument)
 {
     MOZ_ASSERT(!mDocument);
     if (!aDocument) {
         SendInitFailure(NS_ERROR_FAILURE);
         return;
     }
 
+    nsCOMPtr<nsIPrincipal> principal;
     WebBrowserPersistDocumentAttrs attrs;
     nsCOMPtr<nsIInputStream> postDataStream;
 #define ENSURE(e) do {           \
         nsresult rv = (e);       \
         if (NS_FAILED(rv)) {     \
             SendInitFailure(rv); \
             return;              \
         }                        \
@@ -55,16 +56,20 @@ WebBrowserPersistDocumentChild::Start(ns
     ENSURE(aDocument->GetBaseURI(attrs.baseURI()));
     ENSURE(aDocument->GetContentType(attrs.contentType()));
     ENSURE(aDocument->GetCharacterSet(attrs.characterSet()));
     ENSURE(aDocument->GetTitle(attrs.title()));
     ENSURE(aDocument->GetReferrer(attrs.referrer()));
     ENSURE(aDocument->GetContentDisposition(attrs.contentDisposition()));
     ENSURE(aDocument->GetCacheKey(&(attrs.cacheKey())));
     ENSURE(aDocument->GetPersistFlags(&(attrs.persistFlags())));
+
+    ENSURE(aDocument->GetPrincipal(getter_AddRefs(principal)));
+    ENSURE(ipc::PrincipalToPrincipalInfo(principal, &(attrs.principal())));
+
     ENSURE(aDocument->GetPostData(getter_AddRefs(postDataStream)));
 #undef ENSURE
 
     mozilla::ipc::AutoIPCStream autoStream;
     autoStream.Serialize(postDataStream,
                          static_cast<mozilla::dom::ContentChild*>(Manager()));
 
     mDocument = aDocument;
--- a/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistLocalDocument.cpp
@@ -179,16 +179,24 @@ WebBrowserPersistLocalDocument::GetPostD
     nsCOMPtr<nsISHEntry> history = GetHistory();
     if (!history) {
         *aStream = nullptr;
         return NS_OK;
     }
     return history->GetPostData(aStream);
 }
 
+NS_IMETHODIMP
+WebBrowserPersistLocalDocument::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+  nsCOMPtr<nsIPrincipal> nodePrincipal = mDocument->NodePrincipal();
+  nodePrincipal.forget(aPrincipal);
+  return NS_OK;
+}
+
 already_AddRefed<nsISHEntry>
 WebBrowserPersistLocalDocument::GetHistory()
 {
     nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetDefaultView();
     if (NS_WARN_IF(!window)) {
         return nullptr;
     }
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(window);
--- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
+++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.cpp
@@ -4,30 +4,35 @@
  * 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 "WebBrowserPersistRemoteDocument.h"
 #include "WebBrowserPersistDocumentParent.h"
 #include "WebBrowserPersistResourcesParent.h"
 #include "WebBrowserPersistSerializeParent.h"
 #include "mozilla/Unused.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+#include "nsIPrincipal.h"
 
 namespace mozilla {
 
 NS_IMPL_ISUPPORTS(WebBrowserPersistRemoteDocument,
                   nsIWebBrowserPersistDocument)
 
 WebBrowserPersistRemoteDocument
 ::WebBrowserPersistRemoteDocument(WebBrowserPersistDocumentParent* aActor,
                                   const Attrs& aAttrs,
                                   nsIInputStream* aPostData)
 : mActor(aActor)
 , mAttrs(aAttrs)
 , mPostData(aPostData)
 {
+  nsresult rv;
+  mPrincipal = ipc::PrincipalInfoToPrincipal(mAttrs.principal(), &rv);
 }
 
 WebBrowserPersistRemoteDocument::~WebBrowserPersistRemoteDocument()
 {
     if (mActor) {
         Unused << WebBrowserPersistDocumentParent::Send__delete__(mActor);
         // That will call mActor->ActorDestroy, which calls this->ActorDestroy
         // (whether or not the IPC send succeeds).
@@ -128,16 +133,24 @@ NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::GetPostData(nsIInputStream** aStream)
 {
     nsCOMPtr<nsIInputStream> stream = mPostData;
     stream.forget(aStream);
     return NS_OK;
 }
 
 NS_IMETHODIMP
+WebBrowserPersistRemoteDocument::GetPrincipal(nsIPrincipal** aPrincipal)
+{
+    nsCOMPtr<nsIPrincipal> nodePrincipal = mPrincipal;
+    nodePrincipal.forget(aPrincipal);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 WebBrowserPersistRemoteDocument::ReadResources(nsIWebBrowserPersistResourceVisitor* aVisitor)
 {
     if (!mActor) {
         return NS_ERROR_FAILURE;
     }
     RefPtr<WebBrowserPersistResourcesParent> subActor =
         new WebBrowserPersistResourcesParent(this, aVisitor);
     return mActor->SendPWebBrowserPersistResourcesConstructor(
--- a/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h
+++ b/dom/webbrowserpersist/WebBrowserPersistRemoteDocument.h
@@ -8,16 +8,18 @@
 #define WebBrowserPersistRemoteDocument_h__
 
 #include "mozilla/Maybe.h"
 #include "mozilla/PWebBrowserPersistDocumentParent.h"
 #include "nsCOMPtr.h"
 #include "nsIWebBrowserPersistDocument.h"
 #include "nsIInputStream.h"
 
+class nsIPrincipal;
+
 // This class is the XPCOM half of the glue between the
 // nsIWebBrowserPersistDocument interface and a remote document; it is
 // created by WebBrowserPersistDocumentParent when (and if) it
 // receives the information needed to populate the interface's
 // properties.
 //
 // This object has a normal refcounted lifetime.  The corresponding
 // IPC actor holds a weak reference to this class; when the last
@@ -35,16 +37,17 @@ public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIWEBBROWSERPERSISTDOCUMENT
 
 private:
     using Attrs = WebBrowserPersistDocumentAttrs;
     WebBrowserPersistDocumentParent* mActor;
     Attrs mAttrs;
     nsCOMPtr<nsIInputStream> mPostData;
+    nsCOMPtr<nsIPrincipal> mPrincipal;
 
     friend class WebBrowserPersistDocumentParent;
     WebBrowserPersistRemoteDocument(WebBrowserPersistDocumentParent* aActor,
                                     const Attrs& aAttrs,
                                     nsIInputStream* aPostData);
     ~WebBrowserPersistRemoteDocument();
 
     void ActorDestroy(void);
--- a/dom/webbrowserpersist/nsIWebBrowserPersist.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersist.idl
@@ -7,16 +7,17 @@
 #include "nsICancelable.idl"
 
 interface nsIURI;
 interface nsIInputStream;
 interface nsIWebProgressListener;
 interface nsIFile;
 interface nsIChannel;
 interface nsILoadContext;
+interface nsIPrincipal;
 
 /**
  * Interface for persisting DOM documents and URIs to local or remote storage.
  */
 [scriptable, uuid(8cd752a4-60b1-42c3-a819-65c7a1138a28)]
 interface nsIWebBrowserPersist : nsICancelable
 {
   /** No special persistence behaviour. */
@@ -61,22 +62,16 @@ interface nsIWebBrowserPersist : nsICanc
   const unsigned long PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION = 16384;
   /**
    * Append the downloaded data to the target file.
    * This can only be used when persisting to a local file.
    */
   const unsigned long PERSIST_FLAGS_APPEND_TO_FILE = 32768;
 
   /**
-   * Force relevant cookies to be sent with this load even if normally they
-   * wouldn't be.
-   */
-  const unsigned long PERSIST_FLAGS_FORCE_ALLOW_COOKIES = 65536;
-
-  /**
    * Flags governing how data is fetched and saved from the network.
    * It is best to set this value explicitly unless you are prepared
    * to accept the default values.
    */
   attribute unsigned long persistFlags;
 
   /** Persister is ready to save data */
   const unsigned long PERSIST_STATE_READY = 1;
@@ -111,16 +106,18 @@ interface nsIWebBrowserPersist : nsICanc
   attribute nsIWebProgressListener progressListener;
 
   /**
    * Save the specified URI to file.
    *
    * @param aURI       URI to save to file. Some implementations of this interface
    *                   may also support <CODE>nullptr</CODE> to imply the currently
    *                   loaded URI.
+   * @param aTriggeringPrincipal
+   *                   The triggering principal for the URI we're saving.
    * @param aCacheKey  The necko cache key integer.
    * @param aReferrer  The referrer URI to pass with an HTTP request or
    *                   <CODE>nullptr</CODE>.
    * @param aReferrerPolicy  The referrer policy for when and what to send via
    *                   HTTP Referer header.  Ignored if aReferrer is
    *                   <CODE>nullptr</CODE>.  Taken from REFERRER_POLICY
    *                   constants in nsIHttpChannel.
    * @param aPostData  Post data to pass with an HTTP request or
@@ -137,29 +134,31 @@ interface nsIWebBrowserPersist : nsICanc
    *                   window or document)
    *
    * @see nsIFile
    * @see nsIURI
    * @see nsIInputStream
    *
    * @throws NS_ERROR_INVALID_ARG One or more arguments was invalid.
    */
-  void saveURI(in nsIURI aURI, in unsigned long aCacheKey,
+  void saveURI(in nsIURI aURI, in nsIPrincipal aTriggeringPrincipal,
+      in unsigned long aCacheKey,
       in nsIURI aReferrer, in unsigned long aReferrerPolicy,
       in nsIInputStream aPostData,
       in string aExtraHeaders, in nsISupports aFile,
       in nsILoadContext aPrivacyContext);
 
   /**
    * @param aIsPrivate Treat the save operation as private (ie. with
    *                   regards to networking operations and persistence
    *                   of intermediate data, etc.)
    * @see saveURI for all other parameter descriptions
    */
-  void savePrivacyAwareURI(in nsIURI aURI, in unsigned long aCacheKey,
+  void savePrivacyAwareURI(in nsIURI aURI,
+      in nsIPrincipal aTriggeringPrincipal, in unsigned long aCacheKey,
       in nsIURI aReferrer, in unsigned long aReferrerPolicy,
       in nsIInputStream aPostData,
       in string aExtraHeaders, in nsISupports aFile,
       in boolean aIsPrivate);
 
   /**
    * Save a channel to a file. It must not be opened yet.
    * @see saveURI
--- a/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
+++ b/dom/webbrowserpersist/nsIWebBrowserPersistDocument.idl
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIInputStream;
 interface nsIOutputStream;
+interface nsIPrincipal;
 interface nsITabParent;
 interface nsIWebBrowserPersistResourceVisitor;
 interface nsIWebBrowserPersistWriteCompletion;
 
 /**
  * Interface for the URI-mapping information that can be supplied when
  * serializing the DOM of an nsIWebBrowserPersistDocument.
  *
@@ -55,16 +56,17 @@ interface nsIWebBrowserPersistDocument :
   readonly attribute AUTF8String documentURI;
   readonly attribute AUTF8String baseURI;
   readonly attribute ACString contentType;
   readonly attribute ACString characterSet;
   readonly attribute AString title;
   readonly attribute AString referrer;
   readonly attribute AString contentDisposition;
   readonly attribute nsIInputStream postData;
+  readonly attribute nsIPrincipal principal;
 
   /**
    * The cache key.  Unlike in nsISHEntry, where it's wrapped in an
    * nsISupportsPRUint32, this is just the integer.
    */
   readonly attribute unsigned long cacheKey;
 
   /**
--- a/dom/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -78,16 +78,17 @@ struct nsWebBrowserPersist::WalkData
 };
 
 // Information about a DOM document
 struct nsWebBrowserPersist::DocData
 {
     nsCOMPtr<nsIURI> mBaseURI;
     nsCOMPtr<nsIWebBrowserPersistDocument> mDocument;
     nsCOMPtr<nsIURI> mFile;
+    nsCOMPtr<nsIPrincipal> mPrincipal;
     nsCString mCharset;
 };
 
 // Information about a URI
 struct nsWebBrowserPersist::URIData
 {
     bool mNeedsPersisting;
     bool mSaved;
@@ -408,44 +409,48 @@ NS_IMETHODIMP nsWebBrowserPersist::SetPr
 {
     mProgressListener = aProgressListener;
     mProgressListener2 = do_QueryInterface(aProgressListener);
     mEventSink = do_GetInterface(aProgressListener);
     return NS_OK;
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SaveURI(
-    nsIURI *aURI, uint32_t aCacheKey,
+    nsIURI *aURI, nsIPrincipal *aPrincipal, uint32_t aCacheKey,
     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     nsIInputStream *aPostData, const char *aExtraHeaders,
     nsISupports *aFile, nsILoadContext* aPrivacyContext)
 {
-    return SavePrivacyAwareURI(aURI, aCacheKey, aReferrer, aReferrerPolicy,
-                               aPostData, aExtraHeaders, aFile,
-                               aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
+    bool isPrivate =
+      aPrivacyContext && aPrivacyContext->UsePrivateBrowsing();
+    return SavePrivacyAwareURI(aURI, aPrincipal, aCacheKey,
+                               aReferrer, aReferrerPolicy,
+                               aPostData, aExtraHeaders, aFile, isPrivate);
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SavePrivacyAwareURI(
-    nsIURI *aURI, uint32_t aCacheKey,
+    nsIURI *aURI, nsIPrincipal *aPrincipal, uint32_t aCacheKey,
     nsIURI *aReferrer, uint32_t aReferrerPolicy,
     nsIInputStream *aPostData, const char *aExtraHeaders,
     nsISupports *aFile, bool aIsPrivate)
 {
     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     mFirstAndOnlyUse = false; // Stop people from reusing this object!
 
     nsCOMPtr<nsIURI> fileAsURI;
     nsresult rv;
     rv = GetValidURIFromObject(aFile, getter_AddRefs(fileAsURI));
     NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
 
     // SaveURI doesn't like broken uris.
     mPersistFlags |= PERSIST_FLAGS_FAIL_ON_BROKEN_LINKS;
-    rv = SaveURIInternal(aURI, aCacheKey, aReferrer, aReferrerPolicy,
-                         aPostData, aExtraHeaders, fileAsURI, false, aIsPrivate);
+    rv = SaveURIInternal(aURI, aPrincipal, aCacheKey,
+                         aReferrer, aReferrerPolicy,
+                         aPostData, aExtraHeaders, fileAsURI,
+                         false, aIsPrivate);
     return NS_FAILED(rv) ? rv : NS_OK;
 }
 
 NS_IMETHODIMP nsWebBrowserPersist::SaveChannel(
     nsIChannel *aChannel, nsISupports *aFile)
 {
     NS_ENSURE_TRUE(mFirstAndOnlyUse, NS_ERROR_FAILURE);
     mFirstAndOnlyUse = false; // Stop people from reusing this object!
@@ -594,27 +599,32 @@ nsWebBrowserPersist::SerializeNextFile()
             URIData *data = iter.UserData();
             if (data->mNeedsPersisting && !data->mSaved) {
                 urisToPersist++;
             }
         }
     }
 
     if (urisToPersist > 0) {
+        nsCOMPtr<nsIPrincipal> docPrincipal;
+        //XXXgijs I *think* this is already always true, but let's be sure.
+        MOZ_ASSERT(mDocList.Length() > 0,
+            "Should have the document for any walked URIs to persist!");
+        nsresult rv = mDocList.ElementAt(0)->mDocument->
+            GetPrincipal(getter_AddRefs(docPrincipal));
+        NS_ENSURE_SUCCESS_VOID(rv);
         // Persist each file in the uri map. The document(s)
         // will be saved after the last one of these is saved.
         for (auto iter = mURIMap.Iter(); !iter.Done(); iter.Next()) {
             URIData *data = iter.UserData();
 
             if (!data->mNeedsPersisting || data->mSaved) {
                 continue;
             }
 
-            nsresult rv;
-
             // Create a URI from the key.
             nsCOMPtr<nsIURI> uri;
             rv = NS_NewURI(getter_AddRefs(uri), iter.Key(),
                            data->mCharset.get());
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
@@ -622,17 +632,17 @@ nsWebBrowserPersist::SerializeNextFile()
             nsCOMPtr<nsIURI> fileAsURI = data->mDataPath;
             rv = AppendPathToURI(fileAsURI, data->mFilename, fileAsURI);
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
             // The Referrer Policy doesn't matter here since the referrer is
             // nullptr.
-            rv = SaveURIInternal(uri, 0, nullptr,
+            rv = SaveURIInternal(uri, docPrincipal, 0, nullptr,
                                  mozilla::net::RP_Unset, nullptr, nullptr,
                                  fileAsURI, true, mIsPrivate);
             // If SaveURIInternal fails, then it will have called EndDownload,
             // which means that |data| is no longer valid memory. We MUST bail.
             if (NS_WARN_IF(NS_FAILED(rv))) {
                 break;
             }
 
@@ -1319,17 +1329,18 @@ nsWebBrowserPersist::AppendPathToURI(nsI
     AppendUTF16toUTF8(aPath, newPath);
 
     return NS_MutateURI(aURI)
              .SetPathQueryRef(newPath)
              .Finalize(aOutURI);
 }
 
 nsresult nsWebBrowserPersist::SaveURIInternal(
-    nsIURI *aURI, uint32_t aCacheKey, nsIURI *aReferrer,
+    nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal,
+    uint32_t aCacheKey, nsIURI *aReferrer,
     uint32_t aReferrerPolicy, nsIInputStream *aPostData,
     const char *aExtraHeaders, nsIURI *aFile,
     bool aCalcFileExt, bool aIsPrivate)
 {
     NS_ENSURE_ARG_POINTER(aURI);
     NS_ENSURE_ARG_POINTER(aFile);
 
     nsresult rv = NS_OK;
@@ -1345,17 +1356,17 @@ nsresult nsWebBrowserPersist::SaveURIInt
     {
         loadFlags |= nsIRequest::LOAD_FROM_CACHE;
     }
 
     // Open a channel to the URI
     nsCOMPtr<nsIChannel> inputChannel;
     rv = NS_NewChannel(getter_AddRefs(inputChannel),
                        aURI,
-                       nsContentUtils::GetSystemPrincipal(),
+                       aTriggeringPrincipal,
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                        nsIContentPolicy::TYPE_OTHER,
                        nullptr,  // aPerformanceStorage
                        nullptr,  // aLoadGroup
                        static_cast<nsIInterfaceRequestor*>(this),
                        loadFlags);
 
     nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel = do_QueryInterface(inputChannel);
@@ -1375,26 +1386,16 @@ nsresult nsWebBrowserPersist::SaveURIInt
     {
         nsCOMPtr<nsIEncodedChannel> encodedChannel(do_QueryInterface(inputChannel));
         if (encodedChannel)
         {
             encodedChannel->SetApplyConversion(false);
         }
     }
 
-    if (mPersistFlags & PERSIST_FLAGS_FORCE_ALLOW_COOKIES)
-    {
-        nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
-                do_QueryInterface(inputChannel);
-        if (httpChannelInternal) {
-            rv = httpChannelInternal->SetThirdPartyFlags(nsIHttpChannelInternal::THIRD_PARTY_FORCE_ALLOW);
-            MOZ_ASSERT(NS_SUCCEEDED(rv));
-        }
-    }
-
     // Set the referrer, post data and headers if any
     nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(inputChannel));
     if (httpChannel)
     {
         // Referrer
         if (aReferrer)
         {
             rv = httpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
--- a/dom/webbrowserpersist/nsWebBrowserPersist.h
+++ b/dom/webbrowserpersist/nsWebBrowserPersist.h
@@ -52,17 +52,18 @@ public:
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSIPROGRESSEVENTSINK
 
 // Private members
 private:
     virtual ~nsWebBrowserPersist();
     nsresult SaveURIInternal(
-        nsIURI *aURI, uint32_t aCacheKey, nsIURI *aReferrer,
+        nsIURI *aURI, nsIPrincipal* aTriggeringPrincipal,
+        uint32_t aCacheKey, nsIURI *aReferrer,
         uint32_t aReferrerPolicy, nsIInputStream *aPostData,
         const char *aExtraHeaders, nsIURI *aFile,
         bool aCalcFileExt, bool aIsPrivate);
     nsresult SaveChannelInternal(
         nsIChannel *aChannel, nsIURI *aFile, bool aCalcFileExt);
     nsresult SaveDocumentInternal(
         nsIWebBrowserPersistDocument *aDocument,
         nsIURI *aFile,
--- a/ipc/mscom/COMPtrHolder.h
+++ b/ipc/mscom/COMPtrHolder.h
@@ -157,17 +157,17 @@ struct ParamTraits<mozilla::mscom::COMPt
   {
 #if defined(MOZ_CONTENT_SANDBOX)
     static const bool sIsStreamPreservationNeeded =
       XRE_IsParentProcess() && mozilla::GetEffectiveContentSandboxLevel() >= 3;
 #else
     const bool sIsStreamPreservationNeeded = false;
 #endif // defined(MOZ_CONTENT_SANDBOX)
 
-    paramType::EnvType env;
+    typename paramType::EnvType env;
 
     mozilla::mscom::ProxyStreamFlags flags = sIsStreamPreservationNeeded ?
          mozilla::mscom::ProxyStreamFlags::ePreservable :
          mozilla::mscom::ProxyStreamFlags::eDefault;
 
     mozilla::mscom::ProxyStream proxyStream(_IID, aParam.Get(), &env, flags);
     int bufLen;
     const BYTE* buf = proxyStream.GetBuffer(bufLen);
--- a/ipc/mscom/Interceptor.cpp
+++ b/ipc/mscom/Interceptor.cpp
@@ -239,17 +239,17 @@ Interceptor::Create(STAUniquePtr<IUnknow
 {
   MOZ_ASSERT(aOutInterface && aTarget && aSink);
   if (!aOutInterface) {
     return E_INVALIDARG;
   }
 
   detail::LiveSetAutoLock lock(GetLiveSet());
 
-  RefPtr<IWeakReference> existingWeak(std::move(GetLiveSet().Get(aTarget.get())));
+  RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget.get()));
   if (existingWeak) {
     RefPtr<IWeakReferenceSource> existingStrong;
     if (SUCCEEDED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
       // QI on existingStrong may touch other threads. Since we now hold a
       // strong ref on the interceptor, we may now release the lock.
       lock.Unlock();
       return existingStrong->QueryInterface(aInitialIid, aOutInterface);
     }
@@ -886,17 +886,17 @@ Interceptor::Release()
 Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget)
 {
   MOZ_ASSERT(aTarget);
 
   detail::LiveSetAutoLock lock(GetLiveSet());
 
   // It is not an error if the interceptor doesn't exist, so we return
   // S_FALSE instead of an error in that case.
-  RefPtr<IWeakReference> existingWeak(std::move(GetLiveSet().Get(aTarget)));
+  RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget));
   if (!existingWeak) {
     return S_FALSE;
   }
 
   RefPtr<IWeakReferenceSource> existingStrong;
   if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
     return S_FALSE;
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2947,24 +2947,26 @@ nsIFrame::BuildDisplayListForStackingCon
   if (usingSVGEffects) {
     dirtyRect =
       nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
     visibleRect =
       nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
     aBuilder->EnterSVGEffectsContents(&hoistedScrollInfoItemsStorage);
   }
 
+
+  bool needsActiveOpacityLayer = false;
   // We build an opacity item if it's not going to be drawn by SVG content, or
   // SVG effects. SVG effects won't handle the opacity if we want an active
   // layer (for async animations), see
   // nsSVGIntegrationsUtils::PaintMaskAndClipPath or
   // nsSVGIntegrationsUtils::PaintFilter.
   bool useOpacity = HasVisualOpacity(effectSet) &&
                     !nsSVGUtils::CanOptimizeOpacity(this) &&
-                    (!usingSVGEffects || nsDisplayOpacity::NeedsActiveLayer(aBuilder, this));
+                    ((needsActiveOpacityLayer = nsDisplayOpacity::NeedsActiveLayer(aBuilder, this)) || !usingSVGEffects);
   bool useBlendMode = effects->mMixBlendMode != NS_STYLE_BLEND_NORMAL;
   bool useStickyPosition = disp->mPosition == NS_STYLE_POSITION_STICKY &&
     IsScrollFrameActive(aBuilder,
                         nsLayoutUtils::GetNearestScrollableFrame(GetParent(),
                         nsLayoutUtils::SCROLLABLE_SAME_DOC |
                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
   bool useFixedPosition = disp->mPosition == NS_STYLE_POSITION_FIXED &&
     (nsLayoutUtils::IsFixedPosFrameInDisplayPort(this) || BuilderHasScrolledClip(aBuilder));
@@ -3254,19 +3256,20 @@ nsIFrame::BuildDisplayListForStackingCon
    * effects, wrap it up in an opacity item.
    */
   if (useOpacity) {
     // Don't clip nsDisplayOpacity items. We clip their descendants instead.
     // The clip we would set on an element with opacity would clip
     // all descendant content, but some should not be clipped.
     DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
     resultList.AppendToTop(
-        MakeDisplayItem<nsDisplayOpacity>(aBuilder, this, &resultList,
+      MakeDisplayItem<nsDisplayOpacity>(aBuilder, this, &resultList,
                                         containerItemASR,
-                                        opacityItemForEventsAndPluginsOnly));
+                                        opacityItemForEventsAndPluginsOnly,
+                                        needsActiveOpacityLayer));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
 
   /* If we're going to apply a transformation and don't have preserve-3d set, wrap
    * everything in an nsDisplayTransform. If there's nothing in the list, don't add
    * anything.
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -292,16 +292,17 @@ public:
         MOZ_DIAGNOSTIC_ASSERT(!mOldItems[oldIndex.val].IsUsed());
         if (aNewItem->GetChildren()) {
           Maybe<const ActiveScrolledRoot*> containerASRForChildren;
           if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
                                           oldItem->GetChildren(),
                                           aNewItem->GetChildren(),
                                           containerASRForChildren,
                                           aNewItem->GetPerFrameKey())) {
+            aNewItem->InvalidateCachedChildInfo();
             mResultIsModified = true;
 
           }
           UpdateASR(aNewItem, containerASRForChildren);
           aNewItem->UpdateBounds(mBuilder->Builder());
         }
 
         AutoTArray<MergedListIndex, 2> directPredecessors = ProcessPredecessorsOfOldNode(oldIndex);
@@ -396,16 +397,17 @@ public:
       mOldItems[aNode.val].Discard(mBuilder, std::move(aDirectPredecessors));
       mResultIsModified = true;
     } else {
       if (item->GetChildren()) {
         Maybe<const ActiveScrolledRoot*> containerASRForChildren;
         nsDisplayList empty;
         if (mBuilder->MergeDisplayLists(&empty, item->GetChildren(), item->GetChildren(),
                                         containerASRForChildren, item->GetPerFrameKey())) {
+          item->InvalidateCachedChildInfo();
           mResultIsModified = true;
         }
         UpdateASR(item, containerASRForChildren);
         item->UpdateBounds(mBuilder->Builder());
       }
       if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
         mBuilder->IncrementSubDocPresShellPaintCount(item);
       }
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6232,21 +6232,23 @@ nsresult nsDisplayWrapper::WrapListsInPl
   NS_ENSURE_SUCCESS(rv, rv);
   // The outlines may not be in-flow
   return WrapEachDisplayItem(aBuilder, aLists.Outlines(), this);
 }
 
 nsDisplayOpacity::nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                                    nsIFrame* aFrame, nsDisplayList* aList,
                                    const ActiveScrolledRoot* aActiveScrolledRoot,
-                                   bool aForEventsAndPluginsOnly)
+                                   bool aForEventsAndPluginsOnly,
+                                   bool aNeedsActiveLayer)
     : nsDisplayWrapList(aBuilder, aFrame, aList, aActiveScrolledRoot, true)
     , mOpacity(aFrame->StyleEffects()->mOpacity)
     , mForEventsAndPluginsOnly(aForEventsAndPluginsOnly)
-    , mOpacityAppliedToChildren(false)
+    , mNeedsActiveLayer(aNeedsActiveLayer)
+    , mChildOpacityState(ChildOpacityState::Unknown)
 {
   MOZ_COUNT_CTOR(nsDisplayOpacity);
   mState.mOpacity = mOpacity;
 }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
 nsDisplayOpacity::~nsDisplayOpacity() {
   MOZ_COUNT_DTOR(nsDisplayOpacity);
@@ -6373,24 +6375,29 @@ CollectItemsWithOpacity(nsDisplayList* a
   }
 
   return true;
 }
 
 bool
 nsDisplayOpacity::ApplyOpacityToChildren(nsDisplayListBuilder* aBuilder)
 {
+  if (mChildOpacityState == ChildOpacityState::Deferred) {
+    return false;
+  }
+
   // Only try folding our opacity down if we have at most kMaxChildCount
   // children that don't overlap and can all apply the opacity to themselves.
   static const size_t kMaxChildCount = 3;
 
   // Iterate through the child display list and copy at most kMaxChildCount
   // child display item pointers to a temporary list.
   AutoTArray<nsDisplayItem*, kMaxChildCount> items;
   if (!CollectItemsWithOpacity(&mList, items, kMaxChildCount)) {
+    mChildOpacityState = ChildOpacityState::Deferred;
     return false;
   }
 
   struct {
     nsDisplayItem* item;
     nsRect bounds;
   } children[kMaxChildCount];
 
@@ -6400,43 +6407,41 @@ nsDisplayOpacity::ApplyOpacityToChildren
     children[childCount].item = item;
     children[childCount].bounds = item->GetBounds(aBuilder, &snap);
     childCount++;
   }
 
   for (size_t i = 0; i < childCount; i++) {
     for (size_t j = i+1; j < childCount; j++) {
       if (children[i].bounds.Intersects(children[j].bounds)) {
+        mChildOpacityState = ChildOpacityState::Deferred;
         return false;
       }
     }
   }
 
   for (uint32_t i = 0; i < childCount; i++) {
     children[i].item->ApplyOpacity(aBuilder, mOpacity, mClipChain);
   }
 
-  mOpacityAppliedToChildren = true;
+  mChildOpacityState = ChildOpacityState::Applied;
   return true;
 }
 
 bool
 nsDisplayOpacity::ShouldFlattenAway(nsDisplayListBuilder* aBuilder)
 {
-  // ShouldFlattenAway() should be called only once during painting.
-  MOZ_ASSERT(!mOpacityAppliedToChildren);
-
   if (mFrame->GetPrevContinuation() || mFrame->GetNextContinuation() ||
       mFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
     // If we've been split, then we might need to merge, so
     // don't flatten us away.
     return false;
   }
 
-  if (NeedsActiveLayer(aBuilder, mFrame) || mOpacity == 0.0) {
+  if (mNeedsActiveLayer || mOpacity == 0.0) {
     // If our opacity is zero then we'll discard all descendant display items
     // except for layer event regions, so there's no point in doing this
     // optimization (and if we do do it, then invalidations of those descendants
     // might trigger repainting).
     return false;
   }
 
   if (mList.IsEmpty()) {
@@ -6456,17 +6461,17 @@ nsDisplayOpacity::GetLayerState(nsDispla
   // If we only created this item so that we'd get correct nsDisplayEventRegions for child
   // frames, then force us to inactive to avoid unnecessary layerization changes for content
   // that won't ever be painted.
   if (mForEventsAndPluginsOnly) {
     MOZ_ASSERT(mOpacity == 0);
     return LAYER_INACTIVE;
   }
 
-  if (NeedsActiveLayer(aBuilder, mFrame)) {
+  if (mNeedsActiveLayer) {
     // Returns LAYER_ACTIVE_FORCE to avoid flatterning the layer for async
     // animations.
     return LAYER_ACTIVE_FORCE;
   }
 
   return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, GetAnimatedGeometryRoot());
 }
 
@@ -6644,17 +6649,17 @@ nsDisplayBlendMode::ComputeVisibility(ns
   return nsDisplayWrapList::ComputeVisibility(aBuilder, &visibleUnderChildren);
 }
 
 bool
 nsDisplayBlendMode::CanMerge(const nsDisplayItem* aItem) const
 {
   // Items for the same content element should be merged into a single
   // compositing group.
-  if (!HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
+  if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
     return false;
   }
 
   const nsDisplayBlendMode* item =
     static_cast<const nsDisplayBlendMode*>(aItem);
 
   if (item->mIndex != 0 || mIndex != 0) {
     // Don't merge background-blend-mode items
@@ -9360,17 +9365,17 @@ CanMergeDisplayMaskFrame(nsIFrame* aFram
   return true;
 }
 
 bool
 nsDisplayMask::CanMerge(const nsDisplayItem* aItem) const
 {
   // Items for the same content element should be merged into a single
   // compositing group.
-  if (!HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
+  if (!HasDifferentFrame(aItem) || !HasSameTypeAndClip(aItem) || !HasSameContent(aItem)) {
     return false;
   }
 
   return CanMergeDisplayMaskFrame(mFrame) &&
          CanMergeDisplayMaskFrame(aItem->Frame());
 }
 
 already_AddRefed<Layer>
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -2368,16 +2368,22 @@ public:
         aInvalidRegion->Or(aGeometry->mBounds, bounds);
       } else {
         aInvalidRegion->Xor(aGeometry->mBounds, bounds);
       }
     }
   }
 
   /**
+   * This function is called when an item's list of children has been omdified
+   * by RetaineDisplayListBuilder.
+   */
+  virtual void InvalidateCachedChildInfo() {}
+
+  /**
    * @param aSnap set to true if the edges of the rectangles of the opaque
    * region would be snapped to device pixels when drawing
    * @return a region of the item that is opaque --- that is, every pixel
    * that is visible is painted with an opaque
    * color. This is useful for determining when one piece
    * of content completely obscures another so that we can do occlusion
    * culling.
    * This does not take clipping into account.
@@ -2780,16 +2786,21 @@ public:
 
   bool BackfaceIsHidden() const { return mFrame->BackfaceIsHidden(); }
 
   bool In3DContextAndBackfaceIsHidden()
   {
     return mBackfaceHidden;
   }
 
+  bool HasDifferentFrame(const nsDisplayItem* aOther) const
+  {
+    return mFrame != aOther->mFrame;
+  }
+
   bool HasSameTypeAndClip(const nsDisplayItem* aOther) const
   {
     return GetPerFrameKey() == aOther->GetPerFrameKey() &&
            GetClipChain() == aOther->GetClipChain();
   }
 
   bool HasSameContent(const nsDisplayItem* aOther) const
   {
@@ -4962,16 +4973,17 @@ public:
   virtual bool CanMerge(const nsDisplayItem* aItem) const override
   {
     return false;
   }
 
   virtual void Merge(const nsDisplayItem* aItem) override
   {
     MOZ_ASSERT(CanMerge(aItem));
+    MOZ_ASSERT(Frame() != aItem->Frame());
     MergeFromTrackingMergedFrames(static_cast<const nsDisplayWrapList*>(aItem));
   }
 
   virtual void GetMergedFrames(nsTArray<nsIFrame*>* aFrames) const override
   {
     aFrames->AppendElements(mMergedFrames);
   }
 
@@ -5110,63 +5122,66 @@ protected:
  * The standard display item to paint a stacking context with translucency
  * set by the stacking context root frame's 'opacity' style.
  */
 class nsDisplayOpacity : public nsDisplayWrapList {
 public:
   nsDisplayOpacity(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                    nsDisplayList* aList,
                    const ActiveScrolledRoot* aActiveScrolledRoot,
-                   bool aForEventsAndPluginsOnly);
+                   bool aForEventsAndPluginsOnly,
+                   bool aNeedsActiveLayer);
 
   nsDisplayOpacity(nsDisplayListBuilder* aBuilder,
                    const nsDisplayOpacity& aOther)
     : nsDisplayWrapList(aBuilder, aOther)
     , mOpacity(aOther.mOpacity)
     , mForEventsAndPluginsOnly(aOther.mForEventsAndPluginsOnly)
-    , mOpacityAppliedToChildren(false)
+    , mNeedsActiveLayer(aOther.mNeedsActiveLayer)
+    , mChildOpacityState(ChildOpacityState::Unknown)
   {
     // We should not try to merge flattened opacities.
-    MOZ_ASSERT(!aOther.mOpacityAppliedToChildren);
+    MOZ_ASSERT(aOther.mChildOpacityState != ChildOpacityState::Applied);
   }
 
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayOpacity();
 #endif
 
   virtual void RestoreState() override
   {
     nsDisplayItem::RestoreState();
     mOpacity = mState.mOpacity;
-    mOpacityAppliedToChildren = false;
   }
 
   virtual nsDisplayWrapList* Clone(nsDisplayListBuilder* aBuilder) const override
   {
     MOZ_COUNT_CTOR(nsDisplayOpacity);
     return MakeDisplayItem<nsDisplayOpacity>(aBuilder, *this);
   }
 
+  virtual void InvalidateCachedChildInfo() override { mChildOpacityState = ChildOpacityState::Unknown; }
+
   virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                    bool* aSnap) const override;
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) override;
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) override;
   virtual bool ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                  nsRegion* aVisibleRegion) override;
 
   virtual bool CanMerge(const nsDisplayItem* aItem) const override
   {
     // items for the same content element should be merged into a single
     // compositing group
     // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplayOpacity
-    return HasSameTypeAndClip(aItem) && HasSameContent(aItem);
+    return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) && HasSameContent(aItem);
   }
 
   virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) override
   {
     return new nsDisplayOpacityGeometry(this, aBuilder, mOpacity);
   }
 
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
@@ -5184,17 +5199,17 @@ public:
                             float aOpacity,
                             const DisplayItemClipChain* aClip) override;
   virtual bool CanApplyOpacity() const override;
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override;
 
   /**
    * Returns true if ShouldFlattenAway() applied opacity to children.
    */
-  bool OpacityAppliedToChildren() const { return mOpacityAppliedToChildren; }
+  bool OpacityAppliedToChildren() const { return mChildOpacityState == ChildOpacityState::Applied; }
 
   static bool NeedsActiveLayer(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
   NS_DISPLAY_DECL_NAME("Opacity", TYPE_OPACITY)
   virtual void WriteDebugInfo(std::stringstream& aStream) override;
 
   bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
 
   virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
@@ -5204,18 +5219,31 @@ public:
                                        nsDisplayListBuilder* aDisplayListBuilder) override;
 
   float GetOpacity() { return mOpacity; }
 
 private:
   bool ApplyOpacityToChildren(nsDisplayListBuilder* aBuilder);
 
   float mOpacity;
-  bool mForEventsAndPluginsOnly;
-  bool mOpacityAppliedToChildren;
+  bool mForEventsAndPluginsOnly : 1;
+  enum class ChildOpacityState : uint8_t {
+    // Our child list has changed since the last time ApplyOpacityToChildren was called.
+    Unknown,
+    // Our children defer opacity handling to us.
+    Deferred,
+    // Opacity is applied to our children.
+    Applied
+  };
+  bool mNeedsActiveLayer : 1;
+#ifndef __GNUC__
+  ChildOpacityState mChildOpacityState : 2;
+#else
+  ChildOpacityState mChildOpacityState;
+#endif
 
   struct {
     float mOpacity;
   } mState;
 };
 
 class nsDisplayBlendMode : public nsDisplayWrapList {
 public:
@@ -5376,17 +5404,17 @@ public:
                                  const StackingContextHelper& aSc,
                                  mozilla::layers::WebRenderLayerManager* aManager,
                                  nsDisplayListBuilder* aDisplayListBuilder) override;
 
     virtual bool CanMerge(const nsDisplayItem* aItem) const override
     {
       // Items for the same content element should be merged into a single
       // compositing group.
-      return HasSameTypeAndClip(aItem) && HasSameContent(aItem)
+      return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) && HasSameContent(aItem)
           && mIsForBackground == static_cast<const nsDisplayBlendContainer*>(aItem)->mIsForBackground;
     }
 
     virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override
     {
       return false;
     }
     virtual uint32_t GetPerFrameKey() const override {
@@ -6131,17 +6159,17 @@ public:
   }
 
   NS_DISPLAY_DECL_NAME("Filter", TYPE_FILTER)
 
   virtual bool CanMerge(const nsDisplayItem* aItem) const override
   {
     // Items for the same content element should be merged into a single
     // compositing group.
-    return HasSameTypeAndClip(aItem) && HasSameContent(aItem);
+    return HasDifferentFrame(aItem) && HasSameTypeAndClip(aItem) && HasSameContent(aItem);
   }
 
   virtual void Merge(const nsDisplayItem* aItem) override
   {
     nsDisplayWrapList::Merge(aItem);
 
     const nsDisplayFilter* other = static_cast<const nsDisplayFilter*>(aItem);
     mEffectsBounds.UnionRect(mEffectsBounds,
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -85,24 +85,24 @@ printf_stderr(const char *fmt, ...)
   vfprintf(fp, fmt, args);
   va_end(args);
 
   fclose(fp);
 }
 
 
 typedef MOZ_NORETURN_PTR void (__fastcall* BaseThreadInitThunk_func)(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
-static BaseThreadInitThunk_func stub_BaseThreadInitThunk = nullptr;
+static WindowsDllInterceptor::FuncHookType<BaseThreadInitThunk_func> stub_BaseThreadInitThunk;
 
 typedef NTSTATUS (NTAPI *LdrLoadDll_func) (PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
-static LdrLoadDll_func stub_LdrLoadDll;
+static WindowsDllInterceptor::FuncHookType<LdrLoadDll_func> stub_LdrLoadDll;
 
 #ifdef _M_AMD64
 typedef decltype(RtlInstallFunctionTableCallback)* RtlInstallFunctionTableCallback_func;
-static RtlInstallFunctionTableCallback_func stub_RtlInstallFunctionTableCallback;
+static WindowsDllInterceptor::FuncHookType<RtlInstallFunctionTableCallback_func> stub_RtlInstallFunctionTableCallback;
 
 extern uint8_t* sMsMpegJitCodeRegionStart;
 extern size_t sMsMpegJitCodeRegionSize;
 
 BOOLEAN WINAPI patched_RtlInstallFunctionTableCallback(DWORD64 TableIdentifier,
   DWORD64 BaseAddress, DWORD Length, PGET_RUNTIME_FUNCTION_CALLBACK Callback,
   PVOID Context, PCWSTR OutOfProcessCallbackDll)
 {
@@ -657,17 +657,18 @@ DllBlocklist_Initialize(uint32_t aInitFl
 
   NtDllIntercept.Init("ntdll.dll");
 
   ReentrancySentinel::InitializeStatics();
 
   // We specifically use a detour, because there are cases where external
   // code also tries to hook LdrLoadDll, and doesn't know how to relocate our
   // nop space patches. (Bug 951827)
-  bool ok = NtDllIntercept.AddDetour("LdrLoadDll", reinterpret_cast<intptr_t>(patched_LdrLoadDll), (void**) &stub_LdrLoadDll);
+  bool ok = stub_LdrLoadDll.SetDetour(NtDllIntercept, "LdrLoadDll",
+                                      &patched_LdrLoadDll);
 
   if (!ok) {
     sBlocklistInitFailed = true;
 #ifdef DEBUG
     printf_stderr("LdrLoadDll hook failed, no dll blocklisting active\n");
 #endif
   }
 
@@ -678,28 +679,28 @@ DllBlocklist_Initialize(uint32_t aInitFl
     ::LoadLibraryW(L"user32.dll");
   }
 
   Kernel32Intercept.Init("kernel32.dll");
 
 #ifdef _M_AMD64
   if (!IsWin8OrLater()) {
     // The crash that this hook works around is only seen on Win7.
-    Kernel32Intercept.AddHook("RtlInstallFunctionTableCallback",
-                              reinterpret_cast<intptr_t>(patched_RtlInstallFunctionTableCallback),
-                              (void**)&stub_RtlInstallFunctionTableCallback);
+    stub_RtlInstallFunctionTableCallback.Set(Kernel32Intercept,
+                                             "RtlInstallFunctionTableCallback",
+                                             &patched_RtlInstallFunctionTableCallback);
   }
 #endif
 
   // Bug 1361410: WRusr.dll will overwrite our hook and cause a crash.
   // Workaround: If we detect WRusr.dll, don't hook.
   if (!GetModuleHandleW(L"WRusr.dll")) {
-    if(!Kernel32Intercept.AddDetour("BaseThreadInitThunk",
-                                    reinterpret_cast<intptr_t>(patched_BaseThreadInitThunk),
-                                    (void**) &stub_BaseThreadInitThunk)) {
+    if (!stub_BaseThreadInitThunk.SetDetour(Kernel32Intercept,
+                                            "BaseThreadInitThunk",
+                                            &patched_BaseThreadInitThunk)) {
 #ifdef DEBUG
     printf_stderr("BaseThreadInitThunk hook failed\n");
 #endif
     }
   }
 
 #if defined(NIGHTLY_BUILD)
   // Populate a list of thread start addresses to block.
--- a/mozglue/misc/interceptor/PatcherDetour.h
+++ b/mozglue/misc/interceptor/PatcherDetour.h
@@ -37,29 +37,29 @@ public:
 
   WindowsDllDetourPatcher(const WindowsDllDetourPatcher&) = delete;
   WindowsDllDetourPatcher(WindowsDllDetourPatcher&&) = delete;
   WindowsDllDetourPatcher& operator=(const WindowsDllDetourPatcher&) = delete;
   WindowsDllDetourPatcher& operator=(WindowsDllDetourPatcher&&) = delete;
 
   void Clear()
   {
-    if (!mVMPolicy.ShouldUnhookUponDestruction()) {
+    if (!this->mVMPolicy.ShouldUnhookUponDestruction()) {
       return;
     }
 
 #if defined(_M_IX86)
     size_t nBytes = 1 + sizeof(intptr_t);
 #elif defined(_M_X64)
     size_t nBytes = 2 + sizeof(intptr_t);
 #else
 #error "Unknown processor type"
 #endif
 
-    const auto& tramps = mVMPolicy.Items();
+    const auto& tramps = this->mVMPolicy.Items();
     for (auto&& tramp : tramps) {
       // First we read the pointer to the interceptor instance.
       Maybe<uintptr_t> instance = tramp.ReadEncodedPointer();
       if (!instance) {
         continue;
       }
 
       if (instance.value() != reinterpret_cast<uintptr_t>(this)) {
@@ -75,17 +75,17 @@ public:
       });
 
       // Now we read the pointer to the intercepted function.
       Maybe<uintptr_t> interceptedFn = tramp.ReadEncodedPointer();
       if (!interceptedFn) {
         continue;
       }
 
-      WritableTargetFunction<MMPolicyT> origBytes(mVMPolicy,
+      WritableTargetFunction<MMPolicyT> origBytes(this->mVMPolicy,
                                                   interceptedFn.value(), nBytes);
       if (!origBytes) {
         continue;
       }
 
       Maybe<uint8_t> maybeOpcode1 = origBytes.ReadByte();
       if (!maybeOpcode1) {
         continue;
@@ -130,43 +130,43 @@ public:
       }
 #else
 #error "Unknown processor type"
 #endif
 
       origBytes.Commit();
     }
 
-    mVMPolicy.Clear();
+    this->mVMPolicy.Clear();
   }
 
   void Init(int aNumHooks = 0)
   {
     if (Initialized()) {
       return;
     }
 
     if (aNumHooks == 0) {
       // Win32 allocates VM addresses at a 64KiB granularity, so by default we
       // might as well utilize that entire 64KiB reservation instead of
       // artifically constraining ourselves to the page size.
-      aNumHooks = mVMPolicy.GetAllocGranularity() / kHookSize;
+      aNumHooks = this->mVMPolicy.GetAllocGranularity() / kHookSize;
     }
 
-    mVMPolicy.Reserve(aNumHooks);
+    this->mVMPolicy.Reserve(aNumHooks);
   }
 
   bool Initialized() const
   {
-    return !!mVMPolicy;
+    return !!this->mVMPolicy;
   }
 
   bool AddHook(FARPROC aTargetFn, intptr_t aHookDest, void** aOrigFunc)
   {
-    ReadOnlyTargetFunction<MMPolicyT> target(ResolveRedirectedAddress(aTargetFn));
+    ReadOnlyTargetFunction<MMPolicyT> target(this->ResolveRedirectedAddress(aTargetFn));
 
     CreateTrampoline(target, aHookDest, aOrigFunc);
     if (!*aOrigFunc) {
       return false;
     }
 
     return true;
   }
--- a/mozglue/misc/interceptor/VMSharingPolicies.h
+++ b/mozglue/misc/interceptor/VMSharingPolicies.h
@@ -29,33 +29,34 @@ public:
     MOZ_ASSERT(aCount);
     uint32_t bytesReserved = MMPolicy::Reserve(aCount * kChunkSize);
     return !!bytesReserved;
   }
 
   Trampoline<MMPolicy> GetNextTrampoline()
   {
     uint32_t offset = mNextChunkIndex * kChunkSize;
-    if (!MaybeCommitNextPage(offset, kChunkSize)) {
+    if (!this->MaybeCommitNextPage(offset, kChunkSize)) {
       return nullptr;
     }
 
 
-    Trampoline<MMPolicy> result(this, GetLocalView() + offset,
-                                GetRemoteView() + offset, kChunkSize);
+    Trampoline<MMPolicy> result(this, this->GetLocalView() + offset,
+                                this->GetRemoteView() + offset, kChunkSize);
     if (!!result) {
       ++mNextChunkIndex;
     }
 
     return std::move(result);
   }
 
   TrampolineCollection<MMPolicy> Items() const
   {
-    return TrampolineCollection<MMPolicy>(*this, GetLocalView(), GetRemoteView(),
+    return TrampolineCollection<MMPolicy>(*this, this->GetLocalView(),
+                                          this->GetRemoteView(),
                                           kChunkSize, mNextChunkIndex);
   }
 
   void Clear()
   {
     mNextChunkIndex = 0;
   }
 
--- a/mozglue/misc/nsWindowsDllInterceptor.h
+++ b/mozglue/misc/nsWindowsDllInterceptor.h
@@ -3,20 +3,22 @@
 /* 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 NS_WINDOWS_DLL_INTERCEPTOR_H_
 #define NS_WINDOWS_DLL_INTERCEPTOR_H_
 
 #include "mozilla/Assertions.h"
+#include "mozilla/Attributes.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
-#include "mozilla/NotNull.h"
+#include "mozilla/Move.h"
+#include "mozilla/Tuple.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Types.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Vector.h"
 #include "nsWindowsHelpers.h"
 
 #include <wchar.h>
 #include <windows.h>
@@ -77,26 +79,165 @@
  *
  * Note that this is not thread-safe.  Sad day.
  *
  */
 
 namespace mozilla {
 namespace interceptor {
 
+template <typename InterceptorT, typename FuncPtrT>
+class FuncHook final
+{
+  template <typename T>
+  struct OriginalFunctionPtrTraits;
+
+  template <typename R, typename... Args>
+  struct OriginalFunctionPtrTraits<R (*)(Args...)>
+  {
+    using ReturnType = R;
+  };
+
+#if defined(_M_IX86)
+  template <typename R, typename... Args>
+  struct OriginalFunctionPtrTraits<R (__stdcall*)(Args...)>
+  {
+    using ReturnType = R;
+  };
+
+  template <typename R, typename... Args>
+  struct OriginalFunctionPtrTraits<R (__fastcall*)(Args...)>
+  {
+    using ReturnType = R;
+  };
+#endif // defined(_M_IX86)
+
+public:
+  using ThisType = FuncHook<InterceptorT, FuncPtrT>;
+  using ReturnType = typename OriginalFunctionPtrTraits<FuncPtrT>::ReturnType;
+
+  constexpr FuncHook()
+    : mOrigFunc(nullptr)
+    , mInitOnce(INIT_ONCE_STATIC_INIT)
+  {
+  }
+
+  ~FuncHook() = default;
+
+  bool Set(InterceptorT& aInterceptor, const char* aName,
+           FuncPtrT aHookDest)
+  {
+    LPVOID addHookOk;
+    InitOnceContext ctx(this, &aInterceptor, aName, aHookDest, false);
+
+    return ::InitOnceExecuteOnce(&mInitOnce, &InitOnceCallback, &ctx,
+                                 &addHookOk) && addHookOk;
+  }
+
+  bool SetDetour(InterceptorT& aInterceptor, const char* aName,
+                 FuncPtrT aHookDest)
+  {
+    LPVOID addHookOk;
+    InitOnceContext ctx(this, &aInterceptor, aName, aHookDest, true);
+
+    return ::InitOnceExecuteOnce(&mInitOnce, &InitOnceCallback, &ctx,
+                                 &addHookOk) && addHookOk;
+  }
+
+  explicit operator bool() const
+  {
+    return !!mOrigFunc;
+  }
+
+  template <typename... ArgsType>
+  ReturnType operator()(ArgsType... aArgs) const
+  {
+    return mOrigFunc(std::forward<ArgsType>(aArgs)...);
+  }
+
+  FuncPtrT GetStub() const
+  {
+    return mOrigFunc;
+  }
+
+  // One-time init stuff cannot be moved or copied
+  FuncHook(const FuncHook&) = delete;
+  FuncHook(FuncHook&&) = delete;
+  FuncHook& operator=(const FuncHook&) = delete;
+  FuncHook& operator=(FuncHook&& aOther) = delete;
+
+private:
+  struct MOZ_RAII InitOnceContext final
+  {
+    InitOnceContext(ThisType* aHook, InterceptorT* aInterceptor,
+                    const char* aName, void* aHookDest, bool aForceDetour)
+      : mHook(aHook)
+      , mInterceptor(aInterceptor)
+      , mName(aName)
+      , mHookDest(aHookDest)
+      , mForceDetour(aForceDetour)
+    {
+    }
+
+    ThisType*     mHook;
+    InterceptorT* mInterceptor;
+    const char*   mName;
+    void*         mHookDest;
+    bool          mForceDetour;
+  };
+
+private:
+  bool Apply(InterceptorT* aInterceptor, const char* aName, void* aHookDest)
+  {
+    return aInterceptor->AddHook(aName, reinterpret_cast<intptr_t>(aHookDest),
+                                 reinterpret_cast<void**>(&mOrigFunc));
+  }
+
+  bool ApplyDetour(InterceptorT* aInterceptor, const char* aName,
+                   void* aHookDest)
+  {
+    return aInterceptor->AddDetour(aName, reinterpret_cast<intptr_t>(aHookDest),
+                                   reinterpret_cast<void**>(&mOrigFunc));
+  }
+
+  static BOOL CALLBACK
+  InitOnceCallback(PINIT_ONCE aInitOnce, PVOID aParam, PVOID* aOutContext)
+  {
+    MOZ_ASSERT(aOutContext);
+
+    bool result;
+    auto ctx = reinterpret_cast<InitOnceContext*>(aParam);
+    if (ctx->mForceDetour) {
+      result = ctx->mHook->ApplyDetour(ctx->mInterceptor, ctx->mName,
+                                       ctx->mHookDest);
+    } else {
+      result = ctx->mHook->Apply(ctx->mInterceptor, ctx->mName, ctx->mHookDest);
+    }
+
+    *aOutContext = result ? reinterpret_cast<PVOID>(1U << INIT_ONCE_CTX_RESERVED_BITS) : nullptr;
+    return TRUE;
+  }
+
+private:
+  FuncPtrT  mOrigFunc;
+  INIT_ONCE mInitOnce;
+};
+
 enum
 {
   kDefaultTrampolineSize = 128
 };
 
 template <typename VMPolicy =
             mozilla::interceptor::VMSharingPolicyShared<
               mozilla::interceptor::MMPolicyInProcess, kDefaultTrampolineSize>>
 class WindowsDllInterceptor final
 {
+  typedef WindowsDllInterceptor<VMPolicy> ThisType;
+
   interceptor::WindowsDllDetourPatcher<VMPolicy> mDetourPatcher;
 #if defined(_M_IX86)
   interceptor::WindowsDllNopSpacePatcher<typename VMPolicy::MMPolicyT> mNopSpacePatcher;
 #endif // defined(_M_IX86)
 
   HMODULE mModule;
   int mNHooks;
 
@@ -155,16 +296,17 @@ public:
 #if defined(_M_IX86)
     mNopSpacePatcher.Clear();
 #endif // defined(_M_IX86)
     mDetourPatcher.Clear();
 
     // NB: We intentionally leak mModule
   }
 
+private:
   /**
    * Hook/detour the method aName from the DLL we set in Init so that it calls
    * aHookDest instead.  Returns the original method pointer in aOrigFunc
    * and returns true if successful.
    *
    * IMPORTANT: If you use this method, please add your case to the
    * TestDllInterceptor in order to detect future failures.  Even if this
    * succeeds now, updates to the hooked DLL could cause it to fail in
@@ -214,27 +356,34 @@ public:
     FARPROC proc = ::GetProcAddress(mModule, aName);
     if (!proc) {
       return false;
     }
 
     return AddDetour(proc, aHookDest, aOrigFunc);
   }
 
-private:
   bool AddDetour(FARPROC aProc, intptr_t aHookDest, void** aOrigFunc)
   {
     MOZ_ASSERT(mModule && aProc);
 
     if (!mDetourPatcher.Initialized()) {
       mDetourPatcher.Init(mNHooks);
     }
 
     return mDetourPatcher.AddHook(aProc, aHookDest, aOrigFunc);
   }
+
+public:
+  template <typename FuncPtrT>
+  using FuncHookType = FuncHook<ThisType, FuncPtrT>;
+
+private:
+  template <typename InterceptorT, typename FuncPtrT>
+  friend class FuncHook;
 };
 
 } // namespace interceptor
 
 using WindowsDllInterceptor = interceptor::WindowsDllInterceptor<>;
 
 using CrossProcessDllInterceptor = interceptor::WindowsDllInterceptor<
   mozilla::interceptor::VMSharingPolicyUnique<
--- a/mozglue/tests/interceptor/TestDllInterceptor.cpp
+++ b/mozglue/tests/interceptor/TestDllInterceptor.cpp
@@ -5,20 +5,43 @@
 #include <shlobj.h>
 #include <stdio.h>
 #include <commdlg.h>
 #define SECURITY_WIN32
 #include <security.h>
 #include <wininet.h>
 #include <schnlsp.h>
 
+#include "mozilla/TypeTraits.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsDllInterceptor.h"
 #include "nsWindowsHelpers.h"
 
+NTSTATUS NTAPI NtFlushBuffersFile(HANDLE, PIO_STATUS_BLOCK);
+NTSTATUS NTAPI NtReadFile(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
+                          PIO_STATUS_BLOCK, PVOID, ULONG,
+                          PLARGE_INTEGER, PULONG);
+NTSTATUS NTAPI NtReadFileScatter(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
+                                 PIO_STATUS_BLOCK, PFILE_SEGMENT_ELEMENT, ULONG,
+                                 PLARGE_INTEGER, PULONG);
+NTSTATUS NTAPI NtWriteFile(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
+                           PIO_STATUS_BLOCK, PVOID, ULONG,
+                           PLARGE_INTEGER, PULONG);
+NTSTATUS NTAPI NtWriteFileGather(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
+                                 PIO_STATUS_BLOCK, PFILE_SEGMENT_ELEMENT, ULONG,
+                                 PLARGE_INTEGER, PULONG);
+NTSTATUS NTAPI NtQueryFullAttributesFile(POBJECT_ATTRIBUTES, PVOID);
+NTSTATUS NTAPI LdrLoadDll(PWCHAR filePath, PULONG flags, PUNICODE_STRING moduleFileName, PHANDLE handle);
+NTSTATUS NTAPI LdrUnloadDll(HMODULE);
+// These pointers are disguised as PVOID to avoid pulling in obscure headers
+PVOID NTAPI LdrResolveDelayLoadedAPI(PVOID, PVOID, PVOID, PVOID, PVOID, ULONG);
+void CALLBACK ProcessCaretEvents(HWINEVENTHOOK, DWORD, HWND, LONG, LONG, DWORD, DWORD);
+void __fastcall BaseThreadInitThunk(BOOL aIsInitialThread, void* aStartAddress, void* aThreadParam);
+
 using namespace mozilla;
 
 struct payload {
   UINT64 a;
   UINT64 b;
   UINT64 c;
 
   bool operator==(const payload &other) const {
@@ -33,90 +56,319 @@ extern "C" __declspec(dllexport) __decls
   p.a = p.b;
   p.b = p.c;
   p.c = tmp;
   return p;
 }
 
 static bool patched_func_called = false;
 
-static payload (*orig_rotatePayload)(payload);
+static WindowsDllInterceptor::FuncHookType<decltype(&rotatePayload)>
+  orig_rotatePayload;
 
 static payload
 patched_rotatePayload(payload p)
 {
   patched_func_called = true;
   return orig_rotatePayload(p);
 }
 
-typedef bool(*HookTestFunc)(void*);
-bool CheckHook(HookTestFunc aHookTestFunc, void* aOrigFunc,
-               const char* aDllName, const char* aFuncName)
+// Invoke aFunc by taking aArg's contents and using them as aFunc's arguments
+template <typename CallableT, typename... Args, typename ArgTuple = Tuple<Args...>, size_t... Indices>
+decltype(auto) Apply(CallableT&& aFunc, ArgTuple&& aArgs, std::index_sequence<Indices...>)
 {
-  if (aHookTestFunc(aOrigFunc)) {
+  return std::forward<CallableT>(aFunc)(Get<Indices>(std::forward<ArgTuple>(aArgs))...);
+}
+
+template <typename CallableT>
+bool TestFunction(CallableT aFunc);
+
+#define DEFINE_TEST_FUNCTION(calling_convention) \
+  template <typename R, typename... Args, typename... TestArgs> \
+  bool TestFunction(WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>&& aFunc, \
+                    bool (* aPred)(R), TestArgs... aArgs) \
+  { \
+    using FuncHookType = WindowsDllInterceptor::FuncHookType<R (calling_convention *)(Args...)>; \
+    using ArgTuple = Tuple<Args...>; \
+    using Indices = std::index_sequence_for<Args...>; \
+    ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
+    return aPred(Apply(std::forward<FuncHookType>(aFunc), std::forward<ArgTuple>(fakeArgs), Indices())); \
+  } \
+  \
+  /* Specialization for functions returning void */ \
+  template <typename... Args, typename PredicateT, typename... TestArgs> \
+  bool TestFunction(WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>&& aFunc, \
+                    PredicateT&& aPred, TestArgs... aArgs) \
+  { \
+    using FuncHookType = WindowsDllInterceptor::FuncHookType<void (calling_convention *)(Args...)>; \
+    using ArgTuple = Tuple<Args...>; \
+    using Indices = std::index_sequence_for<Args...>; \
+    ArgTuple fakeArgs{ std::forward<TestArgs>(aArgs)... }; \
+    Apply(std::forward<FuncHookType>(aFunc), std::forward<ArgTuple>(fakeArgs), Indices()); \
+    return true; \
+  }
+
+// C++11 allows empty arguments to macros. clang works just fine. MSVC does the
+// right thing, but it also throws up warning C4003.
+#if defined(_MSC_VER) && !defined(__clang__)
+DEFINE_TEST_FUNCTION(__cdecl)
+#else
+DEFINE_TEST_FUNCTION()
+#endif
+
+#ifdef _M_IX86
+DEFINE_TEST_FUNCTION(__stdcall)
+DEFINE_TEST_FUNCTION(__fastcall)
+#endif // _M_IX86
+
+// Test the hooked function against the supplied predicate
+template <typename OrigFuncT, typename PredicateT, typename... Args>
+bool CheckHook(WindowsDllInterceptor::FuncHookType<OrigFuncT> &aOrigFunc,
+               const char* aDllName, const char* aFuncName, PredicateT&& aPred,
+               Args... aArgs)
+{
+  if (TestFunction(std::forward<WindowsDllInterceptor::FuncHookType<OrigFuncT>>(aOrigFunc), std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...)) {
     printf("TEST-PASS | WindowsDllInterceptor | "
            "Executed hooked function %s from %s\n", aFuncName, aDllName);
     return true;
   }
   printf("TEST-FAILED | WindowsDllInterceptor | "
          "Failed to execute hooked function %s from %s\n", aFuncName, aDllName);
   return false;
 }
 
-template <size_t N>
-bool TestHook(HookTestFunc funcTester, const char (&dll)[N], const char *func)
+// Hook the function and optionally attempt calling it
+template <typename OrigFuncT, size_t N, typename PredicateT, typename... Args>
+bool TestHook(const char (&dll)[N], const char *func, PredicateT&& aPred, Args... aArgs)
 {
-  void *orig_func;
+  auto orig_func(mozilla::MakeUnique<WindowsDllInterceptor::FuncHookType<OrigFuncT>>());
+
   bool successful = false;
   {
     WindowsDllInterceptor TestIntercept;
     TestIntercept.Init(dll);
-    successful = TestIntercept.AddHook(func, 0, &orig_func);
+    successful = orig_func->Set(TestIntercept, func, nullptr);
   }
 
   if (successful) {
     printf("TEST-PASS | WindowsDllInterceptor | Could hook %s from %s\n", func, dll);
-    return CheckHook(funcTester, orig_func, dll, func);
+    if (!aPred) {
+      printf("TEST-SKIPPED | WindowsDllInterceptor | "
+             "Will not attempt to execute patched %s.\n", func);
+      return true;
+    }
+
+    return CheckHook(*orig_func, dll, func, std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...);
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to hook %s from %s\n", func, dll);
     return false;
   }
 }
 
-template <size_t N>
-bool TestDetour(const char (&dll)[N], const char *func)
+// Detour the function and optionally attempt calling it
+template <typename OrigFuncT, size_t N, typename PredicateT>
+bool TestDetour(const char (&dll)[N], const char *func, PredicateT&& aPred)
 {
-  void *orig_func;
+  auto orig_func(mozilla::MakeUnique<WindowsDllInterceptor::FuncHookType<OrigFuncT>>());
+
   bool successful = false;
   {
     WindowsDllInterceptor TestIntercept;
     TestIntercept.Init(dll);
-    successful = TestIntercept.AddDetour(func, 0, &orig_func);
+    successful = orig_func->SetDetour(TestIntercept, func, nullptr);
   }
 
   if (successful) {
     printf("TEST-PASS | WindowsDllInterceptor | Could detour %s from %s\n", func, dll);
-    return true;
+    if (!aPred) {
+      printf("TEST-SKIPPED | WindowsDllInterceptor | "
+             "Will not attempt to execute patched %s.\n", func);
+      return true;
+    }
+
+    return CheckHook(*orig_func, dll, func, std::forward<PredicateT>(aPred));
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to detour %s from %s\n", func, dll);
     return false;
   }
 }
 
-template <size_t N>
-bool MaybeTestHook(const bool cond, HookTestFunc funcTester, const char (&dll)[N], const char* func)
+// If a function pointer's type returns void*, this template converts that type
+// to return uintptr_t instead, for the purposes of predicates.
+template <typename FuncT>
+struct SubstituteForVoidPtr
+{
+  using Type = FuncT;
+};
+
+template <typename... Args>
+struct SubstituteForVoidPtr<void* (*)(Args...)>
+{
+  using Type = uintptr_t (*)(Args...);
+};
+
+#ifdef _M_IX86
+template <typename... Args>
+struct SubstituteForVoidPtr<void* (__stdcall*)(Args...)>
+{
+  using Type = uintptr_t (__stdcall*)(Args...);
+};
+
+template <typename... Args>
+struct SubstituteForVoidPtr<void* (__fastcall*)(Args...)>
+{
+  using Type = uintptr_t (__fastcall*)(Args...);
+};
+#endif // _M_IX86
+
+// Determines the function's return type
+template <typename FuncT>
+struct ReturnType;
+
+template <typename R, typename... Args>
+struct ReturnType<R (*)(Args...)>
+{
+  using Type = R;
+};
+
+#ifdef _M_IX86
+template <typename R, typename... Args>
+struct ReturnType<R (__stdcall*)(Args...)>
+{
+  using Type = R;
+};
+
+template <typename R, typename... Args>
+struct ReturnType<R (__fastcall*)(Args...)>
+{
+  using Type = R;
+};
+#endif // _M_IX86
+
+// Predicates that may be supplied during tests
+template <typename FuncT>
+struct Predicates
+{
+  using ArgType = typename ReturnType<FuncT>::Type;
+
+  template <ArgType CompVal>
+  static bool Equals(ArgType aValue)
+  {
+    return CompVal == aValue;
+  }
+
+  template <ArgType CompVal>
+  static bool NotEquals(ArgType aValue)
+  {
+    return CompVal != aValue;
+  }
+
+  template <ArgType CompVal>
+  static bool Ignore(ArgType aValue)
+  {
+    return true;
+  }
+};
+
+// Functions that return void should be ignored, so we specialize the
+// Ignore predicate for that case. Use nullptr as the value to compare against.
+template <typename... Args>
+struct Predicates<void (*)(Args...)>
+{
+  template <nullptr_t DummyVal>
+  static bool Ignore()
+  {
+    return true;
+  }
+};
+
+#ifdef _M_IX86
+template <typename... Args>
+struct Predicates<void (__stdcall*)(Args...)>
+{
+  template <nullptr_t DummyVal>
+  static bool Ignore()
+  {
+    return true;
+  }
+};
+
+template <typename... Args>
+struct Predicates<void (__fastcall*)(Args...)>
+{
+  template <nullptr_t DummyVal>
+  static bool Ignore()
+  {
+    return true;
+  }
+};
+#endif // _M_IX86
+
+// The standard test. Hook |func|, and then try executing it with all zero
+// arguments, using |pred| and |comp| to determine whether the call successfully
+// executed. In general, you want set pred and comp such that they return true
+// when the function is returning whatever value is expected with all-zero
+// arguments.
+//
+// Note: When |func| returns void, you must supply |Ignore| and |nullptr| as the
+// |pred| and |comp| arguments, respectively.
+#define TEST_HOOK(dll, func, pred, comp) \
+  TestHook<decltype(&func)>(#dll, #func, &Predicates<decltype(&func)>::pred<comp>)
+
+// We need to special-case functions that return INVALID_HANDLE_VALUE
+// (ie, CreateFile). Our template machinery for comparing values doesn't work
+// with integer constants passed as pointers (well, it works on MSVC, but not
+// clang, because that is not standard-compliant).
+#define TEST_HOOK_FOR_INVALID_HANDLE_VALUE(dll, func) \
+  TestHook<SubstituteForVoidPtr<decltype(&func)>::Type>(#dll, #func, &Predicates<SubstituteForVoidPtr<decltype(&func)>::Type>::Equals<uintptr_t(-1)>)
+
+// This variant allows you to explicitly supply arguments to the hooked function
+// during testing. You want to provide arguments that produce the conditions that
+// induce the function to return a value that is accepted by your predicate.
+#define TEST_HOOK_PARAMS(dll, func, pred, comp, ...) \
+  TestHook<decltype(&func)>(#dll, #func, &Predicates<decltype(&func)>::pred<comp>, __VA_ARGS__)
+
+// This is for cases when we want to hook |func|, but it is unsafe to attempt
+// to execute the function in the context of a test.
+#define TEST_HOOK_SKIP_EXEC(dll, func) \
+  TestHook<decltype(&func)>(#dll, #func, reinterpret_cast<bool (*)(typename ReturnType<decltype(&func)>::Type)>(NULL))
+
+// The following three variants are identical to the previous macros,
+// however the forcibly use a Detour on 32-bit Windows. On 64-bit Windows,
+// these macros are identical to their TEST_HOOK variants.
+#define TEST_DETOUR(dll, func, pred, comp) \
+  TestDetour<decltype(&func)>(#dll, #func, &Predicates<decltype(&func)>::pred<comp>)
+
+#define TEST_DETOUR_PARAMS(dll, func, pred, comp, ...) \
+  TestDetour<decltype(&func)>(#dll, #func, &Predicates<decltype(&func)>::pred<comp>, __VA_ARGS__)
+
+#define TEST_DETOUR_SKIP_EXEC(dll, func) \
+  TestDetour<decltype(&func)>(#dll, #func, reinterpret_cast<bool (*)(typename ReturnType<decltype(&func)>::Type)>(NULL))
+
+template <typename OrigFuncT, size_t N, typename PredicateT, typename... Args>
+bool MaybeTestHook(const bool cond, const char (&dll)[N], const char *func, PredicateT&& aPred, Args... aArgs)
 {
   if (!cond) {
     printf("TEST-SKIPPED | WindowsDllInterceptor | Skipped hook test for %s from %s\n", func, dll);
     return true;
   }
 
-  return TestHook(funcTester, dll, func);
+  return TestHook<OrigFuncT>(dll, func, std::forward<PredicateT>(aPred), std::forward<Args>(aArgs)...);
 }
 
+// Like TEST_HOOK, but the test is only executed when cond is true.
+#define MAYBE_TEST_HOOK(cond, dll, func, pred, comp) \
+  MaybeTestHook<decltype(&func)>(cond, #dll, #func, &Predicates<decltype(&func)>::pred<comp>)
+
+#define MAYBE_TEST_HOOK_PARAMS(cond, dll, func, pred, comp, ...) \
+  MaybeTestHook<decltype(&func)>(cond, #dll, #func, &Predicates<decltype(&func)>::pred<comp>, __VA_ARGS__)
+
+#define MAYBE_TEST_HOOK_SKIP_EXEC(cond, dll, func) \
+  MaybeTestHook<decltype(&func)>(cond, #dll, #func, reinterpret_cast<bool (*)(typename ReturnType<decltype(&func)>::Type)>(NULL))
+
 bool ShouldTestTipTsf()
 {
   if (!IsWin8OrLater()) {
     return false;
   }
 
   nsModuleHandle shell32(LoadLibraryW(L"shell32.dll"));
   if (!shell32) {
@@ -142,465 +394,16 @@ bool ShouldTestTipTsf()
   if (!LoadLibraryW(fullPath)) {
     return false;
   }
 
   // Leak the module so that it's loaded for the interceptor test
   return true;
 }
 
-// These test the patched function returned by the DLL
-// interceptor.  They check that the patched assembler preamble does
-// something sane.  The parameter is a pointer to the patched function.
-bool TestGetWindowInfo(void* aFunc)
-{
-  auto patchedGetWindowInfo =
-    reinterpret_cast<decltype(&GetWindowInfo)>(aFunc);
-  return patchedGetWindowInfo(0, 0) == FALSE;
-}
-
-bool TestSetWindowLongPtr(void* aFunc)
-{
-  auto patchedSetWindowLongPtr =
-    reinterpret_cast<decltype(&SetWindowLongPtr)>(aFunc);
-  return patchedSetWindowLongPtr(0, 0, 0) == 0;
-}
-
-bool TestSetWindowLong(void* aFunc)
-{
-  auto patchedSetWindowLong =
-    reinterpret_cast<decltype(&SetWindowLong)>(aFunc);
-  return patchedSetWindowLong(0, 0, 0) == 0;
-}
-
-bool TestTrackPopupMenu(void* aFunc)
-{
-  auto patchedTrackPopupMenu =
-    reinterpret_cast<decltype(&TrackPopupMenu)>(aFunc);
-  return patchedTrackPopupMenu(0, 0, 0, 0, 0, 0, 0) == 0;
-}
-
-bool TestNtFlushBuffersFile(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtFlushBuffersFileType)(HANDLE, PIO_STATUS_BLOCK);
-  auto patchedNtFlushBuffersFile =
-    reinterpret_cast<NtFlushBuffersFileType>(aFunc);
-  patchedNtFlushBuffersFile(0, 0);
-  return true;
-}
-
-bool TestNtCreateFile(void* aFunc)
-{
-  auto patchedNtCreateFile =
-    reinterpret_cast<decltype(&NtCreateFile)>(aFunc);
-  return patchedNtCreateFile(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) != 0;
-}
-
-bool TestNtReadFile(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtReadFileType)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
-                                           PIO_STATUS_BLOCK, PVOID, ULONG,
-                                           PLARGE_INTEGER, PULONG);
-  auto patchedNtReadFile =
-    reinterpret_cast<NtReadFileType>(aFunc);
-  return patchedNtReadFile(0, 0, 0, 0, 0, 0, 0, 0, 0) != 0;
-}
-
-bool TestNtReadFileScatter(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtReadFileScatterType)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
-                                                  PIO_STATUS_BLOCK, PFILE_SEGMENT_ELEMENT, ULONG,
-                                                  PLARGE_INTEGER, PULONG);
-  auto patchedNtReadFileScatter =
-    reinterpret_cast<NtReadFileScatterType>(aFunc);
-  return patchedNtReadFileScatter(0, 0, 0, 0, 0, 0, 0, 0, 0) != 0;
-}
-
-bool TestNtWriteFile(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtWriteFileType)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
-                                            PIO_STATUS_BLOCK, PVOID, ULONG,
-                                            PLARGE_INTEGER, PULONG);
-  auto patchedNtWriteFile =
-    reinterpret_cast<NtWriteFileType>(aFunc);
-  return patchedNtWriteFile(0, 0, 0, 0, 0, 0, 0, 0, 0) != 0;
-}
-
-bool TestNtWriteFileGather(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtWriteFileGatherType)(HANDLE, HANDLE, PIO_APC_ROUTINE, PVOID,
-                                                  PIO_STATUS_BLOCK, PFILE_SEGMENT_ELEMENT, ULONG,
-                                                  PLARGE_INTEGER, PULONG);
-  auto patchedNtWriteFileGather =
-    reinterpret_cast<NtWriteFileGatherType>(aFunc);
-  return patchedNtWriteFileGather(0, 0, 0, 0, 0, 0, 0, 0, 0) != 0;
-}
-
-bool TestNtQueryFullAttributesFile(void* aFunc)
-{
-  typedef NTSTATUS(WINAPI *NtQueryFullAttributesFileType)(POBJECT_ATTRIBUTES,
-                                                          PVOID);
-  auto patchedNtQueryFullAttributesFile =
-    reinterpret_cast<NtQueryFullAttributesFileType>(aFunc);
-  return patchedNtQueryFullAttributesFile(0, 0) != 0;
-}
-
-bool TestLdrUnloadDll(void* aFunc)
-{
-  typedef NTSTATUS (NTAPI *LdrUnloadDllType)(HMODULE);
-  auto patchedLdrUnloadDll = reinterpret_cast<LdrUnloadDllType>(aFunc);
-  return patchedLdrUnloadDll(0) != 0;
-}
-
-bool TestLdrResolveDelayLoadedAPI(void* aFunc)
-{
-  // These pointers are disguised as PVOID to avoid pulling in obscure headers
-  typedef PVOID (WINAPI *LdrResolveDelayLoadedAPIType)(PVOID, PVOID, PVOID,
-                                                       PVOID, PVOID, ULONG);
-  auto patchedLdrResolveDelayLoadedAPI =
-    reinterpret_cast<LdrResolveDelayLoadedAPIType>(aFunc);
-  // No idea how to call this API. Flags==99 is just an arbitrary number that
-  // doesn't crash when the other params are null.
-  return patchedLdrResolveDelayLoadedAPI(0, 0, 0, 0, 0, 99) == 0;
-}
-
-#ifdef _M_AMD64
-bool TestRtlInstallFunctionTableCallback(void* aFunc)
-{
-  auto patchedRtlInstallFunctionTableCallback =
-    reinterpret_cast<decltype(RtlInstallFunctionTableCallback)*>(aFunc);
-
-  return patchedRtlInstallFunctionTableCallback(0, 0, 0, 0, 0, 0) == FALSE;
-}
-#endif
-
-bool TestSetUnhandledExceptionFilter(void* aFunc)
-{
-  auto patchedSetUnhandledExceptionFilter =
-    reinterpret_cast<decltype(&SetUnhandledExceptionFilter)>(aFunc);
-  // Retrieve the current filter as we set the new filter to null, then restore the current filter.
-  LPTOP_LEVEL_EXCEPTION_FILTER current = patchedSetUnhandledExceptionFilter(0);
-  patchedSetUnhandledExceptionFilter(current);
-  return true;
-}
-
-bool TestVirtualAlloc(void* aFunc)
-{
-  auto patchedVirtualAlloc =
-    reinterpret_cast<decltype(&VirtualAlloc)>(aFunc);
-  return patchedVirtualAlloc(0, 0, 0, 0) == 0;
-}
-
-bool TestMapViewOfFile(void* aFunc)
-{
-  auto patchedMapViewOfFile =
-    reinterpret_cast<decltype(&MapViewOfFile)>(aFunc);
-  return patchedMapViewOfFile(0, 0, 0, 0, 0) == 0;
-}
-
-bool TestCreateDIBSection(void* aFunc)
-{
-  auto patchedCreateDIBSection =
-    reinterpret_cast<decltype(&CreateDIBSection)>(aFunc);
-  // MSDN is wrong here.  This does not return ERROR_INVALID_PARAMETER.  It
-  // sets the value of GetLastError to ERROR_INVALID_PARAMETER.
-  // CreateDIBSection returns 0 on error.
-  return patchedCreateDIBSection(0, 0, 0, 0, 0, 0) == 0;
-}
-
-bool TestCreateFileW(void* aFunc)
-{
-  auto patchedCreateFileW =
-    reinterpret_cast<decltype(&CreateFileW)>(aFunc);
-  return patchedCreateFileW(0, 0, 0, 0, 0, 0, 0) == INVALID_HANDLE_VALUE;
-}
-
-bool TestCreateFileA(void* aFunc)
-{
-  auto patchedCreateFileA =
-    reinterpret_cast<decltype(&CreateFileA)>(aFunc);
-//  return patchedCreateFileA(0, 0, 0, 0, 0, 0, 0) == INVALID_HANDLE_VALUE;
-  printf("TEST-SKIPPED | WindowsDllInterceptor | "
-          "Will not attempt to execute patched CreateFileA -- patched method is known to fail.\n");
-  return true;
-}
-
-bool TestQueryDosDeviceW(void* aFunc)
-{
-  auto patchedQueryDosDeviceW =
-    reinterpret_cast<decltype(&QueryDosDeviceW)>(aFunc);
-  return patchedQueryDosDeviceW(nullptr, nullptr, 0) == 0;
-}
-
-bool TestInSendMessageEx(void* aFunc)
-{
-  auto patchedInSendMessageEx =
-    reinterpret_cast<decltype(&InSendMessageEx)>(aFunc);
-  patchedInSendMessageEx(0);
-  return true;
-}
-
-bool TestImmGetContext(void* aFunc)
-{
-  auto patchedImmGetContext =
-    reinterpret_cast<decltype(&ImmGetContext)>(aFunc);
-  patchedImmGetContext(0);
-  return true;
-}
-
-bool TestImmGetCompositionStringW(void* aFunc)
-{
-  auto patchedImmGetCompositionStringW =
-    reinterpret_cast<decltype(&ImmGetCompositionStringW)>(aFunc);
-  patchedImmGetCompositionStringW(0, 0, 0, 0);
-  return true;
-}
-
-bool TestImmSetCandidateWindow(void* aFunc)
-{
-  auto patchedImmSetCandidateWindow =
-    reinterpret_cast<decltype(&ImmSetCandidateWindow)>(aFunc);
-//  return patchedImmSetCandidateWindow(0, 0) == 0;
-  // ImmSetCandidateWindow crashes if given bad parameters.
-  printf("TEST-SKIPPED | WindowsDllInterceptor | "
-          "Will not attempt to execute patched ImmSetCandidateWindow.\n");
-  return true;
-}
-
-bool TestImmNotifyIME(void* aFunc)
-{
-  auto patchedImmNotifyIME =
-    reinterpret_cast<decltype(&ImmNotifyIME)>(aFunc);
-  return patchedImmNotifyIME(0, 0, 0, 0) == 0;
-}
-
-bool TestGetSaveFileNameW(void* aFunc)
-{
-  auto patchedGetSaveFileNameWType =
-    reinterpret_cast<decltype(&GetSaveFileNameW)>(aFunc);
-  patchedGetSaveFileNameWType(0);
-  return true;
-}
-
-bool TestGetOpenFileNameW(void* aFunc)
-{
-  auto patchedGetOpenFileNameWType =
-    reinterpret_cast<decltype(&GetOpenFileNameW)>(aFunc);
-  patchedGetOpenFileNameWType(0);
-  return true;
-}
-
-bool TestGetKeyState(void* aFunc)
-{
-  auto patchedGetKeyState =
-    reinterpret_cast<decltype(&GetKeyState)>(aFunc);
-  patchedGetKeyState(0);
-  return true;
-}
-
-bool TestSendMessageTimeoutW(void* aFunc)
-{
-  auto patchedSendMessageTimeoutW =
-    reinterpret_cast<decltype(&SendMessageTimeoutW)>(aFunc);
-  return patchedSendMessageTimeoutW(0, 0, 0, 0, 0, 0, 0) == 0;
-}
-
-bool TestProcessCaretEvents(void* aFunc)
-{
-  auto patchedProcessCaretEvents =
-    reinterpret_cast<WINEVENTPROC>(aFunc);
-  patchedProcessCaretEvents(0, 0, 0, 0, 0, 0, 0);
-  return true;
-}
-
-bool TestSetCursorPos(void* aFunc)
-{
-  // SetCursorPos has some issues in automation -- see bug 1368033.
-  // For that reason, we don't check the return value -- we only
-  // check that the method runs without producing an exception.
-  auto patchedSetCursorPos =
-    reinterpret_cast<decltype(&SetCursorPos)>(aFunc);
-  patchedSetCursorPos(512, 512);
-  return true;
-}
-
-static DWORD sTlsIndex = 0;
-
-bool TestTlsAlloc(void* aFunc)
-{
-  auto patchedTlsAlloc =
-    reinterpret_cast<decltype(&TlsAlloc)>(aFunc);
-  sTlsIndex = patchedTlsAlloc();
-  return sTlsIndex != TLS_OUT_OF_INDEXES;
-}
-
-bool TestTlsFree(void* aFunc)
-{
-  auto patchedTlsFree =
-    reinterpret_cast<decltype(&TlsFree)>(aFunc);
-  return sTlsIndex != 0 && patchedTlsFree(sTlsIndex);
-}
-
-bool TestCloseHandle(void* aFunc)
-{
-  auto patchedCloseHandle =
-    reinterpret_cast<decltype(&CloseHandle)>(aFunc);
-  return patchedCloseHandle(0) == FALSE;
-}
-
-bool TestDuplicateHandle(void* aFunc)
-{
-  auto patchedDuplicateHandle =
-    reinterpret_cast<decltype(&DuplicateHandle)>(aFunc);
-  return patchedDuplicateHandle(0, 0, 0, 0, 0, 0, 0) == FALSE;
-}
-
-bool TestPrintDlgW(void* aFunc)
-{
-  auto patchedPrintDlgW =
-    reinterpret_cast<decltype(&PrintDlgW)>(aFunc);
-  patchedPrintDlgW(0);
-  return true;
-}
-
-bool TestInternetConnectA(void* aFunc)
-{
-  auto patchedInternetConnectA =
-    reinterpret_cast<decltype(&InternetConnectA)>(aFunc);
-  return patchedInternetConnectA(0, 0, 0, 0, 0, 0, 0, 0) == 0;
-}
-
-HINTERNET sInternet = 0;
-
-bool TestInternetOpenA(void* aFunc)
-{
-  auto patchedInternetOpenA =
-    reinterpret_cast<decltype(&InternetOpenA)>(aFunc);
-  sInternet = patchedInternetOpenA(0, 0, 0, 0, 0);
-  return sInternet != 0;
-}
-
-bool TestInternetCloseHandle(void* aFunc)
-{
-  auto patchedInternetCloseHandle =
-    reinterpret_cast<decltype(&InternetCloseHandle)>(aFunc);
-  return patchedInternetCloseHandle(sInternet);
-}
-
-bool TestInternetQueryDataAvailable(void* aFunc)
-{
-  auto patchedInternetQueryDataAvailable =
-    reinterpret_cast<decltype(&InternetQueryDataAvailable)>(aFunc);
-  return patchedInternetQueryDataAvailable(0, 0, 0, 0) == FALSE;
-}
-
-bool TestInternetReadFile(void* aFunc)
-{
-  auto patchedInternetReadFile =
-    reinterpret_cast<decltype(&InternetReadFile)>(aFunc);
-  return patchedInternetReadFile(0, 0, 0, 0) == FALSE;
-}
-
-bool TestInternetWriteFile(void* aFunc)
-{
-  auto patchedInternetWriteFile =
-    reinterpret_cast<decltype(&InternetWriteFile)>(aFunc);
-  return patchedInternetWriteFile(0, 0, 0, 0) == FALSE;
-}
-
-bool TestInternetSetOptionA(void* aFunc)
-{
-  auto patchedInternetSetOptionA =
-    reinterpret_cast<decltype(&InternetSetOptionA)>(aFunc);
-  return patchedInternetSetOptionA(0, 0, 0, 0) == FALSE;
-}
-
-bool TestHttpAddRequestHeadersA(void* aFunc)
-{
-  auto patchedHttpAddRequestHeadersA =
-    reinterpret_cast<decltype(&HttpAddRequestHeadersA)>(aFunc);
-  return patchedHttpAddRequestHeadersA(0, 0, 0, 0) == FALSE;
-}
-
-bool TestHttpOpenRequestA(void* aFunc)
-{
-  auto patchedHttpOpenRequestA =
-    reinterpret_cast<decltype(&HttpOpenRequestA)>(aFunc);
-  return patchedHttpOpenRequestA(0, 0, 0, 0, 0, 0, 0, 0) == 0;
-}
-
-bool TestHttpQueryInfoA(void* aFunc)
-{
-  auto patchedHttpQueryInfoA =
-    reinterpret_cast<decltype(&HttpQueryInfoA)>(aFunc);
-  return patchedHttpQueryInfoA(0, 0, 0, 0, 0) == FALSE;
-}
-
-bool TestHttpSendRequestA(void* aFunc)
-{
-  auto patchedHttpSendRequestA =
-    reinterpret_cast<decltype(&HttpSendRequestA)>(aFunc);
-  return patchedHttpSendRequestA(0, 0, 0, 0, 0) == FALSE;
-}
-
-bool TestHttpSendRequestExA(void* aFunc)
-{
-  auto patchedHttpSendRequestExA =
-    reinterpret_cast<decltype(&HttpSendRequestExA)>(aFunc);
-  return patchedHttpSendRequestExA(0, 0, 0, 0, 0) == FALSE;
-}
-
-bool TestHttpEndRequestA(void* aFunc)
-{
-  auto patchedHttpEndRequestA =
-    reinterpret_cast<decltype(&HttpEndRequestA)>(aFunc);
-  return patchedHttpEndRequestA(0, 0, 0, 0) == FALSE;
-}
-
-bool TestInternetQueryOptionA(void* aFunc)
-{
-  auto patchedInternetQueryOptionA =
-    reinterpret_cast<decltype(&InternetQueryOptionA)>(aFunc);
-  return patchedInternetQueryOptionA(0, 0, 0, 0) == FALSE;
-}
-
-bool TestInternetErrorDlg(void* aFunc)
-{
-  auto patchedInternetErrorDlg =
-    reinterpret_cast<decltype(&InternetErrorDlg)>(aFunc);
-  return patchedInternetErrorDlg(0, 0, 0, 0, 0) == ERROR_INVALID_HANDLE;
-}
-
-CredHandle sCredHandle;
-
-bool TestAcquireCredentialsHandleA(void* aFunc)
-{
-  auto patchedAcquireCredentialsHandleA =
-    reinterpret_cast<decltype(&AcquireCredentialsHandleA)>(aFunc);
-  SCHANNEL_CRED cred;
-  memset(&cred, 0, sizeof(cred));
-  cred.dwVersion = SCHANNEL_CRED_VERSION;
-  return patchedAcquireCredentialsHandleA(0, UNISP_NAME, SECPKG_CRED_OUTBOUND,
-                                          0, &cred, 0, 0, &sCredHandle, 0) == S_OK;
-}
-
-bool TestQueryCredentialsAttributesA(void* aFunc)
-{
-  auto patchedQueryCredentialsAttributesA =
-    reinterpret_cast<decltype(&QueryCredentialsAttributesA)>(aFunc);
-  return patchedQueryCredentialsAttributesA(&sCredHandle, 0, 0) == SEC_E_UNSUPPORTED_FUNCTION;
-}
-
-bool TestFreeCredentialsHandle(void* aFunc)
-{
-  auto patchedFreeCredentialsHandle =
-    reinterpret_cast<decltype(&FreeCredentialsHandle)>(aFunc);
-  return patchedFreeCredentialsHandle(&sCredHandle) == S_OK;
-}
-
 int main()
 {
   LARGE_INTEGER start;
   QueryPerformanceCounter(&start);
 
   // We disable this part of the test because the code coverage instrumentation
   // injects code in rotatePayload in a way that WindowsDllInterceptor doesn't
   // understand.
@@ -610,17 +413,17 @@ int main()
   ZeroMemory(&p0, sizeof(p0));
   ZeroMemory(&p1, sizeof(p1));
 
   p0 = rotatePayload(initial);
 
   {
     WindowsDllInterceptor ExeIntercept;
     ExeIntercept.Init("TestDllInterceptor.exe");
-    if (ExeIntercept.AddHook("rotatePayload", reinterpret_cast<intptr_t>(patched_rotatePayload), (void**) &orig_rotatePayload)) {
+    if (orig_rotatePayload.Set(ExeIntercept, "rotatePayload", &patched_rotatePayload)) {
       printf("TEST-PASS | WindowsDllInterceptor | Hook added\n");
     } else {
       printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Failed to add hook\n");
       return 1;
     }
 
     p1 = rotatePayload(initial);
 
@@ -654,95 +457,95 @@ int main()
   if (p0 == p1) {
     printf("TEST-PASS | WindowsDllInterceptor | Original function worked properly\n");
   } else {
     printf("TEST-UNEXPECTED-FAIL | WindowsDllInterceptor | Original function didn't return the right information\n");
     return 1;
   }
 #endif
 
-  if (TestHook(TestGetWindowInfo, "user32.dll", "GetWindowInfo") &&
+  if (TEST_HOOK(user32.dll, GetWindowInfo, Equals, FALSE) &&
 #ifdef _WIN64
-      TestHook(TestSetWindowLongPtr, "user32.dll", "SetWindowLongPtrA") &&
-      TestHook(TestSetWindowLongPtr, "user32.dll", "SetWindowLongPtrW") &&
+      TEST_HOOK(user32.dll, SetWindowLongPtrA, Equals, 0) &&
+      TEST_HOOK(user32.dll, SetWindowLongPtrW, Equals, 0) &&
 #else
-      TestHook(TestSetWindowLong, "user32.dll", "SetWindowLongA") &&
-      TestHook(TestSetWindowLong, "user32.dll", "SetWindowLongW") &&
+      TEST_HOOK(user32.dll, SetWindowLongA, Equals, 0) &&
+      TEST_HOOK(user32.dll, SetWindowLongW, Equals, 0) &&
 #endif
-      TestHook(TestTrackPopupMenu, "user32.dll", "TrackPopupMenu") &&
+      TEST_HOOK(user32.dll, TrackPopupMenu, Equals, FALSE) &&
 #ifdef _M_IX86
       // We keep this test to hook complex code on x86. (Bug 850957)
-      TestHook(TestNtFlushBuffersFile, "ntdll.dll", "NtFlushBuffersFile") &&
+      TEST_HOOK(ntdll.dll, NtFlushBuffersFile, NotEquals, 0) &&
 #endif
-      TestHook(TestNtCreateFile, "ntdll.dll", "NtCreateFile") &&
-      TestHook(TestNtReadFile, "ntdll.dll", "NtReadFile") &&
-      TestHook(TestNtReadFileScatter, "ntdll.dll", "NtReadFileScatter") &&
-      TestHook(TestNtWriteFile, "ntdll.dll", "NtWriteFile") &&
-      TestHook(TestNtWriteFileGather, "ntdll.dll", "NtWriteFileGather") &&
-      TestHook(TestNtQueryFullAttributesFile, "ntdll.dll", "NtQueryFullAttributesFile") &&
+      TEST_HOOK(ntdll.dll, NtCreateFile, NotEquals, 0) &&
+      TEST_HOOK(ntdll.dll, NtReadFile, NotEquals, 0) &&
+      TEST_HOOK(ntdll.dll, NtReadFileScatter, NotEquals, 0) &&
+      TEST_HOOK(ntdll.dll, NtWriteFile, NotEquals, 0) &&
+      TEST_HOOK(ntdll.dll, NtWriteFileGather, NotEquals, 0) &&
+      TEST_HOOK(ntdll.dll, NtQueryFullAttributesFile, NotEquals, 0) &&
 #ifndef MOZ_ASAN
       // Bug 733892: toolkit/crashreporter/nsExceptionHandler.cpp
       // This fails on ASan because the ASan runtime already hooked this function
-      TestHook(TestSetUnhandledExceptionFilter, "kernel32.dll", "SetUnhandledExceptionFilter") &&
+      TEST_HOOK(kernel32.dll, SetUnhandledExceptionFilter, Ignore, nullptr) &&
 #endif
 #ifdef _M_IX86
       // Bug 670967: xpcom/base/AvailableMemoryTracker.cpp
-      TestHook(TestVirtualAlloc, "kernel32.dll", "VirtualAlloc") &&
-      TestHook(TestMapViewOfFile, "kernel32.dll", "MapViewOfFile") &&
-      TestHook(TestCreateDIBSection, "gdi32.dll", "CreateDIBSection") &&
-      TestHook(TestCreateFileW, "kernel32.dll", "CreateFileW") &&    // see Bug 1316415
+      TEST_HOOK(kernel32.dll, VirtualAlloc, Equals, nullptr) &&
+      TEST_HOOK(kernel32.dll, MapViewOfFile, Equals, nullptr) &&
+      TEST_HOOK(gdi32.dll, CreateDIBSection, Equals, nullptr) &&
+      TEST_HOOK_FOR_INVALID_HANDLE_VALUE(kernel32.dll, CreateFileW) &&
 #endif
-      TestHook(TestCreateFileA, "kernel32.dll", "CreateFileA") &&
-      TestHook(TestQueryDosDeviceW, "kernelbase.dll", "QueryDosDeviceW") &&
-      TestDetour("user32.dll", "CreateWindowExW") &&
-      TestHook(TestInSendMessageEx, "user32.dll", "InSendMessageEx") &&
-      TestHook(TestImmGetContext, "imm32.dll", "ImmGetContext") &&
-      TestHook(TestImmGetCompositionStringW, "imm32.dll", "ImmGetCompositionStringW") &&
-      TestHook(TestImmSetCandidateWindow, "imm32.dll", "ImmSetCandidateWindow") &&
-      TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") &&
-      TestHook(TestGetSaveFileNameW, "comdlg32.dll", "GetSaveFileNameW") &&
-      TestHook(TestGetOpenFileNameW, "comdlg32.dll", "GetOpenFileNameW") &&
+      TEST_HOOK_FOR_INVALID_HANDLE_VALUE(kernel32.dll, CreateFileA) &&
+      TEST_HOOK(kernelbase.dll, QueryDosDeviceW, Equals, 0) &&
+      TEST_DETOUR(user32.dll, CreateWindowExW, Equals, nullptr) &&
+      TEST_HOOK(user32.dll, InSendMessageEx, Equals, ISMEX_NOSEND) &&
+      TEST_HOOK(imm32.dll, ImmGetContext, Equals, nullptr) &&
+      TEST_HOOK(imm32.dll, ImmGetCompositionStringW, Ignore, 0) &&
+      TEST_HOOK_SKIP_EXEC(imm32.dll, ImmSetCandidateWindow) &&
+      TEST_HOOK(imm32.dll, ImmNotifyIME, Equals, 0) &&
+      TEST_HOOK(comdlg32.dll, GetSaveFileNameW, Ignore, FALSE) &&
+      TEST_HOOK(comdlg32.dll, GetOpenFileNameW, Ignore, FALSE) &&
 #ifdef _M_X64
-      TestHook(TestGetKeyState, "user32.dll", "GetKeyState") &&    // see Bug 1316415
-      TestHook(TestLdrUnloadDll, "ntdll.dll", "LdrUnloadDll") &&
-      MaybeTestHook(IsWin8OrLater(), TestLdrResolveDelayLoadedAPI, "ntdll.dll", "LdrResolveDelayLoadedAPI") &&
-      MaybeTestHook(!IsWin8OrLater(), TestRtlInstallFunctionTableCallback, "kernel32.dll", "RtlInstallFunctionTableCallback") &&
-      TestHook(TestPrintDlgW, "comdlg32.dll", "PrintDlgW") &&
+      TEST_HOOK(user32.dll, GetKeyState, Ignore, 0) &&    // see Bug 1316415
+      TEST_HOOK(ntdll.dll, LdrUnloadDll, NotEquals, 0) &&
+      MAYBE_TEST_HOOK_SKIP_EXEC(IsWin8OrLater(), ntdll.dll, LdrResolveDelayLoadedAPI) &&
+      MAYBE_TEST_HOOK(!IsWin8OrLater(), kernel32.dll, RtlInstallFunctionTableCallback, Equals, FALSE) &&
+      TEST_HOOK(comdlg32.dll, PrintDlgW, Ignore, 0) &&
 #endif
-      MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") &&
+      MAYBE_TEST_HOOK(ShouldTestTipTsf(), tiptsf.dll, ProcessCaretEvents, Ignore, nullptr) &&
 #ifdef _M_IX86
-      TestHook(TestSendMessageTimeoutW, "user32.dll", "SendMessageTimeoutW") &&
+      TEST_HOOK(user32.dll, SendMessageTimeoutW, Equals, 0) &&
 #endif
-      TestHook(TestSetCursorPos, "user32.dll", "SetCursorPos") &&
-      TestHook(TestTlsAlloc, "kernel32.dll", "TlsAlloc") &&
-      TestHook(TestTlsFree, "kernel32.dll", "TlsFree") &&
-      TestHook(TestCloseHandle, "kernel32.dll", "CloseHandle") &&
-      TestHook(TestDuplicateHandle, "kernel32.dll", "DuplicateHandle") &&
+      TEST_HOOK(user32.dll, SetCursorPos, NotEquals, FALSE) &&
+      TEST_HOOK(kernel32.dll, TlsAlloc, NotEquals, TLS_OUT_OF_INDEXES) &&
+      TEST_HOOK_PARAMS(kernel32.dll, TlsFree, Equals, FALSE, TLS_OUT_OF_INDEXES) &&
+      TEST_HOOK(kernel32.dll, CloseHandle, Equals, FALSE) &&
+      TEST_HOOK(kernel32.dll, DuplicateHandle, Equals, FALSE) &&
 
-      TestHook(TestInternetOpenA, "wininet.dll", "InternetOpenA") &&
-      TestHook(TestInternetCloseHandle, "wininet.dll", "InternetCloseHandle") &&
-      TestHook(TestInternetConnectA, "wininet.dll", "InternetConnectA") &&
-      TestHook(TestInternetQueryDataAvailable, "wininet.dll", "InternetQueryDataAvailable") &&
-      TestHook(TestInternetReadFile, "wininet.dll", "InternetReadFile") &&
-      TestHook(TestInternetWriteFile, "wininet.dll", "InternetWriteFile") &&
-      TestHook(TestInternetSetOptionA, "wininet.dll", "InternetSetOptionA") &&
-      TestHook(TestHttpAddRequestHeadersA, "wininet.dll", "HttpAddRequestHeadersA") &&
-      TestHook(TestHttpOpenRequestA, "wininet.dll", "HttpOpenRequestA") &&
-      TestHook(TestHttpQueryInfoA, "wininet.dll", "HttpQueryInfoA") &&
-      TestHook(TestHttpSendRequestA, "wininet.dll", "HttpSendRequestA") &&
-      TestHook(TestHttpSendRequestExA, "wininet.dll", "HttpSendRequestExA") &&
-      TestHook(TestHttpEndRequestA, "wininet.dll", "HttpEndRequestA") &&
-      TestHook(TestInternetQueryOptionA, "wininet.dll", "InternetQueryOptionA") &&
+      TEST_HOOK(wininet.dll, InternetOpenA, NotEquals, nullptr) &&
+      TEST_HOOK(wininet.dll, InternetCloseHandle, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, InternetConnectA, Equals, nullptr) &&
+      TEST_HOOK(wininet.dll, InternetQueryDataAvailable, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, InternetReadFile, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, InternetWriteFile, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, InternetSetOptionA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, HttpAddRequestHeadersA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, HttpOpenRequestA, Equals, nullptr) &&
+      TEST_HOOK(wininet.dll, HttpQueryInfoA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, HttpSendRequestA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, HttpSendRequestExA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, HttpEndRequestA, Equals, FALSE) &&
+      TEST_HOOK(wininet.dll, InternetQueryOptionA, Equals, FALSE) &&
 
-      TestHook(TestAcquireCredentialsHandleA, "sspicli.dll", "AcquireCredentialsHandleA") &&
-      TestHook(TestQueryCredentialsAttributesA, "sspicli.dll", "QueryCredentialsAttributesA") &&
-      TestHook(TestFreeCredentialsHandle, "sspicli.dll", "FreeCredentialsHandle") &&
+      TEST_HOOK(sspicli.dll, AcquireCredentialsHandleA, NotEquals, SEC_E_OK) &&
+      TEST_HOOK(sspicli.dll, QueryCredentialsAttributesA, NotEquals, SEC_E_OK) &&
+      TEST_HOOK(sspicli.dll, FreeCredentialsHandle, NotEquals, SEC_E_OK) &&
 
-      TestDetour("kernel32.dll", "BaseThreadInitThunk") &&
-      TestDetour("ntdll.dll", "LdrLoadDll")) {
+      TEST_DETOUR_SKIP_EXEC(kernel32.dll, BaseThreadInitThunk) &&
+      TEST_DETOUR_SKIP_EXEC(ntdll.dll, LdrLoadDll)) {
     printf("TEST-PASS | WindowsDllInterceptor | all checks passed\n");
 
     LARGE_INTEGER end, freq;
     QueryPerformanceCounter(&end);
 
     QueryPerformanceFrequency(&freq);
 
     LARGE_INTEGER result;
--- a/mozglue/tests/interceptor/TestDllInterceptorCrossProcess.cpp
+++ b/mozglue/tests/interceptor/TestDllInterceptorCrossProcess.cpp
@@ -6,29 +6,29 @@
 
 #include "nsWindowsDllInterceptor.h"
 #include "nsWindowsHelpers.h"
 
 #include <string>
 
 using std::wstring;
 
-static void* gOrigReturnResult;
-
 extern "C" __declspec(dllexport) int
 ReturnResult()
 {
   return 2;
 }
 
+static mozilla::CrossProcessDllInterceptor::FuncHookType<decltype(&ReturnResult)>
+  gOrigReturnResult;
+
 static int
 ReturnResultHook()
 {
-  auto origFn = reinterpret_cast<decltype(&ReturnResult)>(gOrigReturnResult);
-  if (origFn() != 2) {
+  if (gOrigReturnResult() != 2) {
     return 3;
   }
 
   return 0;
 }
 
 int ParentMain()
 {
@@ -68,19 +68,17 @@ int ParentMain()
     printf("TEST-UNEXPECTED-FAIL | DllInterceptorCrossProcess | Failed to assign child process to job\n");
     ::TerminateProcess(childProcess.get(), 1);
     return 1;
   }
 
   mozilla::CrossProcessDllInterceptor intcpt(childProcess.get());
   intcpt.Init("TestDllInterceptorCrossProcess.exe");
 
-  if (!intcpt.AddHook("ReturnResult",
-                      reinterpret_cast<intptr_t>(&ReturnResultHook),
-                      &gOrigReturnResult)) {
+  if (!gOrigReturnResult.Set(intcpt, "ReturnResult", &ReturnResultHook)) {
     printf("TEST-UNEXPECTED-FAIL | DllInterceptorCrossProcess | Failed to add hook\n");
     return 1;
   }
 
   printf("TEST-PASS | DllInterceptorCrossProcess | Hook added\n");
 
   // Let's save the original hook
   SIZE_T bytesWritten;
--- a/python/mozboot/bin/bootstrap-msys2.vbs
+++ b/python/mozboot/bin/bootstrap-msys2.vbs
@@ -64,17 +64,17 @@ If Err.Number <> 0 Then
     WScript.Quit 1
 End If
 On Error GoTo 0
 
 ' Install ConEmu
 ' Download installer
 On Error Resume Next
 Download "https://conemu.github.io/install2.ps1", "install2.ps1"
-conemuSettingsURI = "https://api.pub.build.mozilla.org/tooltool/sha512/9aa384ecc8025a974999e913c83064b3b797e05d19806e62ef558c8300e4c3f72967e9464ace59759f76216fc2fc66f338a1e5cdea3b9aa264529487f091d929"
+conemuSettingsURI = "https://tooltool.mozilla-releng.net/sha512/9aa384ecc8025a974999e913c83064b3b797e05d19806e62ef558c8300e4c3f72967e9464ace59759f76216fc2fc66f338a1e5cdea3b9aa264529487f091d929"
 ' Run installer
 errorCode = shell.Run("powershell.exe -NoProfile -ExecutionPolicy Unrestricted set dst '" & conemuPath & "'; set ver 'stable'; set lnk 'Mozilla Development Shell'; set xml '" & conemuSettingsURI & "'; set run $FALSE; .\install2.ps1", 0, true)
 ' Delete ConEmu installer
 fso.DeleteFile("install2.ps1")
 If Err.Number <> 0 Then
     MsgBox("Error downloading and installing ConEmu. Make sure you have internet connection and Powershell installed. If you think this is a bug, please file one in Bugzilla https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Build%20Config")
     WScript.Quit 1
 End If
--- a/security/sandbox/win/SandboxInitialization.cpp
+++ b/security/sandbox/win/SandboxInitialization.cpp
@@ -10,26 +10,27 @@
 #include "nsWindowsDllInterceptor.h"
 #include "sandbox/win/src/sandbox_factory.h"
 #include "mozilla/sandboxing/permissionsService.h"
 
 namespace mozilla {
 namespace sandboxing {
 
 typedef BOOL(WINAPI* CloseHandle_func) (HANDLE hObject);
-static CloseHandle_func stub_CloseHandle = nullptr;
+static WindowsDllInterceptor::FuncHookType<CloseHandle_func> stub_CloseHandle;
 
 typedef BOOL(WINAPI* DuplicateHandle_func)(HANDLE hSourceProcessHandle,
                                            HANDLE hSourceHandle,
                                            HANDLE hTargetProcessHandle,
                                            LPHANDLE lpTargetHandle,
                                            DWORD dwDesiredAccess,
                                            BOOL bInheritHandle,
                                            DWORD dwOptions);
-static DuplicateHandle_func stub_DuplicateHandle = nullptr;
+static WindowsDllInterceptor::FuncHookType<DuplicateHandle_func>
+  stub_DuplicateHandle;
 
 static BOOL WINAPI
 patched_CloseHandle(HANDLE hObject)
 {
   // Check all handles being closed against the sandbox's tracked handles.
   base::win::OnHandleBeingClosed(hObject);
   return stub_CloseHandle(hObject);
 }
@@ -57,27 +58,24 @@ patched_DuplicateHandle(HANDLE hSourcePr
 
 static WindowsDllInterceptor Kernel32Intercept;
 
 static bool
 EnableHandleCloseMonitoring()
 {
   Kernel32Intercept.Init("kernel32.dll");
   bool hooked =
-    Kernel32Intercept.AddHook("CloseHandle",
-                              reinterpret_cast<intptr_t>(patched_CloseHandle),
-                              (void**)&stub_CloseHandle);
+    stub_CloseHandle.Set(Kernel32Intercept, "CloseHandle", &patched_CloseHandle);
   if (!hooked) {
     return false;
   }
 
   hooked =
-    Kernel32Intercept.AddHook("DuplicateHandle",
-                              reinterpret_cast<intptr_t>(patched_DuplicateHandle),
-                              (void**)&stub_DuplicateHandle);
+    stub_DuplicateHandle.Set(Kernel32Intercept, "DuplicateHandle",
+                             &patched_DuplicateHandle);
   if (!hooked) {
     return false;
   }
 
   return true;
 }
 
 static bool
--- a/testing/mozharness/configs/android/android_hw.py
+++ b/testing/mozharness/configs/android/android_hw.py
@@ -18,13 +18,13 @@ config = {
         'create-virtualenv',
         'verify-device',
         'install',
         'run-tests',
     ],
     # from android_common.py
     "download_tooltool": True,
     "download_minidump_stackwalk": True,
-    "tooltool_servers": ['https://api.pub.build.mozilla.org/tooltool/'],
+    "tooltool_servers": ['https://tooltool.mozilla-releng.net/'],
     # minidump_tooltool_manifest_path is relative to workspace/build/tests/
     "minidump_tooltool_manifest_path": "config/tooltool-manifests/linux64/releng.manifest",
     "xpcshell_extra": "--remoteTestRoot=/data/local/tests",
 }
--- a/testing/mozharness/configs/repackage/win32_partner.py
+++ b/testing/mozharness/configs/repackage/win32_partner.py
@@ -24,13 +24,13 @@ config = {
     "repack_id": os.environ.get("REPACK_ID"),
 
     "download_config": download_config,
 
     "repackage_config": repackage_config,
 
     # ToolTool
     "tooltool_manifest_src": 'browser\\config\\tooltool-manifests\\{}\\releng.manifest'.format(platform),
-    'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/',
+    'tooltool_url': 'https://tooltool.mozilla-releng.net/',
     'tooltool_cache': os.environ.get('TOOLTOOL_CACHE'),
 
     'run_configure': False,
 }
--- a/testing/mozharness/configs/repackage/win64_partner.py
+++ b/testing/mozharness/configs/repackage/win64_partner.py
@@ -24,13 +24,13 @@ config = {
     "repack_id": os.environ.get("REPACK_ID"),
 
     "download_config": download_config,
 
     "repackage_config": repackage_config,
 
     # ToolTool
     "tooltool_manifest_src": 'browser\\config\\tooltool-manifests\\{}\\releng.manifest'.format(platform),
-    'tooltool_url': 'https://api.pub.build.mozilla.org/tooltool/',
+    'tooltool_url': 'https://tooltool.mozilla-releng.net/',
     'tooltool_cache': os.environ.get('TOOLTOOL_CACHE'),
 
     'run_configure': False,
 }
--- a/toolkit/components/browser/nsWebBrowser.cpp
+++ b/toolkit/components/browser/nsWebBrowser.cpp
@@ -981,31 +981,34 @@ NS_IMETHODIMP
 nsWebBrowser::SetProgressListener(nsIWebProgressListener* aProgressListener)
 {
   mProgressListener = aProgressListener;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SaveURI(nsIURI* aURI,
+                      nsIPrincipal* aPrincipal,
                       uint32_t aCacheKey,
                       nsIURI* aReferrer,
                       uint32_t aReferrerPolicy,
                       nsIInputStream* aPostData,
                       const char* aExtraHeaders,
                       nsISupports* aFile,
                       nsILoadContext* aPrivacyContext)
 {
   return SavePrivacyAwareURI(
-    aURI, aCacheKey, aReferrer, aReferrerPolicy, aPostData, aExtraHeaders,
-    aFile, aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
+    aURI, aPrincipal, aCacheKey, aReferrer, aReferrerPolicy, aPostData,
+    aExtraHeaders, aFile,
+    aPrivacyContext && aPrivacyContext->UsePrivateBrowsing());
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SavePrivacyAwareURI(nsIURI* aURI,
+                                  nsIPrincipal* aPrincipal,
                                   uint32_t aCacheKey,
                                   nsIURI* aReferrer,
                                   uint32_t aReferrerPolicy,
                                   nsIInputStream* aPostData,
                                   const char* aExtraHeaders,
                                   nsISupports* aFile,
                                   bool aIsPrivate)
 {
@@ -1033,18 +1036,19 @@ nsWebBrowser::SavePrivacyAwareURI(nsIURI
   // Create a throwaway persistence object to do the work
   nsresult rv;
   mPersist = do_CreateInstance(NS_WEBBROWSERPERSIST_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   mPersist->SetProgressListener(this);
   mPersist->SetPersistFlags(mPersistFlags);
   mPersist->GetCurrentState(&mPersistCurrentState);
 
-  rv = mPersist->SavePrivacyAwareURI(uri, aCacheKey, aReferrer, aReferrerPolicy,
-                                     aPostData, aExtraHeaders, aFile, aIsPrivate);
+  rv = mPersist->SavePrivacyAwareURI(uri, aPrincipal, aCacheKey,
+                                     aReferrer, aReferrerPolicy, aPostData,
+                                     aExtraHeaders, aFile, aIsPrivate);
   if (NS_FAILED(rv)) {
     mPersist = nullptr;
   }
   return rv;
 }
 
 NS_IMETHODIMP
 nsWebBrowser::SaveChannel(nsIChannel* aChannel, nsISupports* aFile)
--- a/toolkit/components/downloads/test/unit/head.js
+++ b/toolkit/components/downloads/test/unit/head.js
@@ -278,17 +278,18 @@ function promiseStartLegacyDownload(aSou
       // Initialize the components so they reference each other.  This will cause
       // the Download object to be created and added to the public downloads.
       transfer.init(sourceURI, NetUtil.newURI(targetFile), null, mimeInfo, null,
                     null, persist, isPrivate);
       persist.progressListener = transfer;
 
       // Start the actual download process.
       persist.savePrivacyAwareURI(
-        sourceURI, 0, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
+        sourceURI, Services.scriptSecurityManager.getSystemPrincipal(),
+        0, referrer, Ci.nsIHttpChannel.REFERRER_POLICY_UNSAFE_URL,
         null, null, targetFile, isPrivate);
     }).catch(do_report_unexpected_exception);
 
   });
 }
 
 /**
  * Starts a new download using the nsIHelperAppService interface, and controls
--- a/toolkit/components/viewsource/content/viewSourceUtils.js
+++ b/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -221,17 +221,21 @@ var gViewSourceUtils = {
           this.viewSourceProgressListener.file = file;
 
           var webBrowserPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
             .createInstance(this.mnsIWebBrowserPersist);
           // the default setting is to not decode. we need to decode.
           webBrowserPersist.persistFlags = this.mnsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
           webBrowserPersist.progressListener = this.viewSourceProgressListener;
           let referrerPolicy = Ci.nsIHttpChannel.REFERRER_POLICY_NO_REFERRER;
-          webBrowserPersist.savePrivacyAwareURI(uri, null, null, referrerPolicy, null, null, file, data.isPrivate);
+          let ssm = Services.scriptSecurityManager;
+          let principal = ssm.createCodebasePrincipal(data.uri,
+            browser.contentPrincipal.originAttributes);
+          webBrowserPersist.savePrivacyAwareURI(uri, principal, null, null,
+            referrerPolicy, null, null, file, data.isPrivate);
 
           let helperService = Cc["@mozilla.org/uriloader/external-helper-app-service;1"]
             .getService(Ci.nsPIExternalAppLauncher);
           if (data.isPrivate) {
             // register the file to be deleted when possible
             helperService.deleteTemporaryPrivateFileWhenPossible(file);
           } else {
             // register the file to be deleted on app exit
--- a/toolkit/content/contentAreaUtils.js
+++ b/toolkit/content/contentAreaUtils.js
@@ -57,24 +57,25 @@ function forbidCPOW(arg, func, argname) 
 // - An image with an extension (e.g. .jpg) in its file name, using
 //   Context->Save Image As...
 // - An image without an extension (e.g. a banner ad on cnn.com) using
 //   the above method.
 // - A linked document using Save Link As...
 // - A linked document using Alt-click Save Link As...
 //
 function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
-                 aSkipPrompt, aReferrer, aSourceDocument, aIsContentWindowPrivate) {
+                 aSkipPrompt, aReferrer, aSourceDocument,
+                 aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "saveURL", "aURL");
   forbidCPOW(aReferrer, "saveURL", "aReferrer");
   // Allow aSourceDocument to be a CPOW.
 
   internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
                aFilePickerTitleKey, null, aReferrer, aSourceDocument,
-               aSkipPrompt, null, aIsContentWindowPrivate);
+               aSkipPrompt, null, aIsContentWindowPrivate, aPrincipal);
 }
 
 // Just like saveURL, but will get some info off the image before
 // calling internalSave
 // Clientele: (Make sure you don't break any of these)
 //  - Context ->  Save Image As...
 const imgICache = Ci.imgICache;
 const nsISupportsCString = Ci.nsISupportsCString;
@@ -107,17 +108,17 @@ const nsISupportsCString = Ci.nsISupport
  * @param aContentDisp (string, optional)
  *        The content disposition of the image.
  * @param aIsContentWindowPrivate (bool)
  *        Whether or not the containing window is in private browsing mode.
  *        Does not need to be provided is aDoc is passed.
  */
 function saveImageURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
                       aSkipPrompt, aReferrer, aDoc, aContentType, aContentDisp,
-                      aIsContentWindowPrivate) {
+                      aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "saveImageURL", "aURL");
   forbidCPOW(aReferrer, "saveImageURL", "aReferrer");
 
   if (aDoc && aIsContentWindowPrivate == undefined) {
     if (Cu.isCrossProcessWrapper(aDoc)) {
       Deprecated.warning("saveImageURL should not be passed document CPOWs. " +
                          "The caller should pass in the content type and " +
                          "disposition themselves",
@@ -151,17 +152,17 @@ function saveImageURL(aURL, aFileName, a
       }
     } catch (e) {
       // Failure to get type and content-disposition off the image is non-fatal
     }
   }
 
   internalSave(aURL, null, aFileName, aContentDisp, aContentType,
                aShouldBypassCache, aFilePickerTitleKey, null, aReferrer,
-               null, aSkipPrompt, null, aIsContentWindowPrivate);
+               aDoc, aSkipPrompt, null, aIsContentWindowPrivate, aPrincipal);
 }
 
 // This is like saveDocument, but takes any browser/frame-like element
 // and saves the current document inside it,
 // whether in-process or out-of-process.
 function saveBrowser(aBrowser, aSkipPrompt, aOuterWindowID = 0) {
   if (!aBrowser) {
     throw "Must have a browser when calling saveBrowser";
@@ -326,21 +327,25 @@ XPCOMUtils.defineConstant(this, "kSaveAs
  *        default downloads folder without prompting.
  * @param aCacheKey [optional]
  *        If set will be passed to saveURI.  See nsIWebBrowserPersist for
  *        allowed values.
  * @param aIsContentWindowPrivate [optional]
  *        This parameter is provided when the aInitiatingDocument is not a
  *        real document object. Stores whether aInitiatingDocument.defaultView
  *        was private or not.
+ * @param aPrincipal [optional]
+ *        This parameter is provided when neither aDocument nor
+ *        aInitiatingDocument is provided. Used to determine what level of
+ *        privilege to load the URI with.
  */
 function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
                       aContentType, aShouldBypassCache, aFilePickerTitleKey,
                       aChosenData, aReferrer, aInitiatingDocument, aSkipPrompt,
-                      aCacheKey, aIsContentWindowPrivate) {
+                      aCacheKey, aIsContentWindowPrivate, aPrincipal) {
   forbidCPOW(aURL, "internalSave", "aURL");
   forbidCPOW(aReferrer, "internalSave", "aReferrer");
   forbidCPOW(aCacheKey, "internalSave", "aCacheKey");
   // Allow aInitiatingDocument to be a CPOW.
 
   if (aSkipPrompt == undefined)
     aSkipPrompt = false;
 
@@ -406,18 +411,27 @@ function internalSave(aURL, aDocument, a
 
     let isPrivate = aIsContentWindowPrivate;
     if (isPrivate === undefined) {
       isPrivate = aInitiatingDocument.nodeType == 9 /* DOCUMENT_NODE */
         ? PrivateBrowsingUtils.isContentWindowPrivate(aInitiatingDocument.defaultView)
         : aInitiatingDocument.isPrivate;
     }
 
+    // We have to cover the cases here where we were either passed an explicit
+    // principal, or a 'real' document (with a nodePrincipal property), or an
+    // nsIWebBrowserPersistDocument which has a principal property.
+    let sourcePrincipal =
+      aPrincipal ||
+      (aDocument && (aDocument.nodePrincipal || aDocument.principal)) ||
+      (aInitiatingDocument && aInitiatingDocument.nodePrincipal);
+
     var persistArgs = {
       sourceURI,
+      sourcePrincipal,
       sourceReferrer: aReferrer,
       sourceDocument: useSaveDocument ? aDocument : null,
       targetContentType: (saveAsType == kSaveAsType_Text) ? "text/plain" : null,
       targetFile: file,
       sourceCacheKey: aCacheKey,
       sourcePostData: nonCPOWDocument ? getPostData(aDocument) : null,
       bypassCache: aShouldBypassCache,
       isPrivate,
@@ -458,18 +472,17 @@ function internalSave(aURL, aDocument, a
  * @param persistArgs.isPrivate
  *        Indicates whether this is taking place in a private browsing context.
  */
 function internalPersist(persistArgs) {
   var persist = makeWebBrowserPersist();
 
   // Calculate persist flags.
   const nsIWBP = Ci.nsIWebBrowserPersist;
-  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
-                nsIWBP.PERSIST_FLAGS_FORCE_ALLOW_COOKIES;
+  const flags = nsIWBP.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
   if (persistArgs.bypassCache)
     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_BYPASS_CACHE;
   else
     persist.persistFlags = flags | nsIWBP.PERSIST_FLAGS_FROM_CACHE;
 
   // Leave it to WebBrowserPersist to discover the encoding type (or lack thereof):
   persist.persistFlags |= nsIWBP.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
 
@@ -506,16 +519,17 @@ function internalPersist(persistArgs) {
       encodingFlags |= nsIWBP.ENCODE_FLAGS_ENCODE_BASIC_ENTITIES;
     }
 
     const kWrapColumn = 80;
     persist.saveDocument(persistArgs.sourceDocument, targetFileURL, filesFolder,
                          persistArgs.targetContentType, encodingFlags, kWrapColumn);
   } else {
     persist.savePrivacyAwareURI(persistArgs.sourceURI,
+                                persistArgs.sourcePrincipal,
                                 persistArgs.sourceCacheKey,
                                 persistArgs.sourceReferrer,
                                 Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                                 persistArgs.sourcePostData,
                                 null,
                                 targetFileURL,
                                 persistArgs.isPrivate);
   }
--- a/toolkit/content/tests/browser/browser_saveImageURL.js
+++ b/toolkit/content/tests/browser/browser_saveImageURL.js
@@ -31,17 +31,18 @@ add_task(async function preferred_API() 
     gBrowser,
     url: IMAGE_PAGE,
   }, async function(browser) {
     let url = await ContentTask.spawn(browser, null, async function() {
       let image = content.document.getElementById("image");
       return image.href;
     });
 
-    saveImageURL(url, "image.jpg", null, true, false, null, null, null, null, false);
+    saveImageURL(url, "image.jpg", null, true, false, null, null, null, null,
+      false, gBrowser.contentPrincipal);
     let channel = gBrowser.contentDocumentAsCPOW.docShell.currentDocumentChannel;
     if (channel) {
       ok(true, channel.QueryInterface(Ci.nsIHttpChannelInternal)
                       .channelIsForDownload);
 
       // Throttleable is the only class flag assigned to downloads.
       ok(channel.QueryInterface(Ci.nsIClassOfService).classFlags,
          Ci.nsIClassOfService.Throttleable);
--- a/toolkit/crashreporter/breakpad-windows-libxul/moz.build
+++ b/toolkit/crashreporter/breakpad-windows-libxul/moz.build
@@ -1,35 +1,29 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-SOURCES += [
-    '../google-breakpad/src/common/windows/http_upload.cc',
-]
-
 Library('google_breakpad_libxul_s')
 
 FINAL_LIBRARY = 'xul'
 
 for var in ('UNICODE', 'UNICODE_', 'BREAKPAD_NO_TERMINATE_THREAD'):
     DEFINES[var] = True
 
 LOCAL_INCLUDES += [
     '/toolkit/crashreporter/breakpad-client',
     '/toolkit/crashreporter/google-breakpad/src',
 ]
 
 include('/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mozbuild')
 include('/toolkit/crashreporter/breakpad-client/windows/handler/objs.mozbuild')
-include('/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild')
 include('/toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild')
 include('/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild')
 
 SOURCES += objs_common
 SOURCES += objs_crash_generation
 SOURCES += objs_handler
-SOURCES += objs_sender
 SOURCES += objs_client_common
 
 DisableStlWrapping()
--- a/toolkit/crashreporter/client/moz.build
+++ b/toolkit/crashreporter/client/moz.build
@@ -19,20 +19,30 @@ if CONFIG['OS_TARGET'] != 'Android':
     USE_LIBS += [
         'jsoncpp',
     ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     UNIFIED_SOURCES += [
         'crashreporter_win.cpp',
     ]
+    include('/toolkit/crashreporter/google-breakpad/src/common/windows/objs.mozbuild')
+    include('/toolkit/crashreporter/breakpad-client/windows/sender/objs.mozbuild')
+    include('/toolkit/crashreporter/breakpad-client/windows/crash_generation/objs.mozbuild')
+    include('/toolkit/crashreporter/breakpad-client/windows/common/objs.mozbuild')
+    SOURCES += objs_common
+    SOURCES += objs_sender
+    SOURCES += objs_crash_generation
+    SOURCES += objs_client_common
+    SOURCES += [
+        '../google-breakpad/src/common/windows/http_upload.cc',
+    ]
     DEFINES['UNICODE'] = True
     DEFINES['_UNICODE'] = True
     USE_LIBS += [
-        'google_breakpad_libxul_s',
         'nss',
     ]
     OS_LIBS += [
         'comctl32',
         'ole32',
         'shell32',
         'wininet',
         'shlwapi',
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -333,17 +333,18 @@ nsTArray<nsAutoPtr<DelayedNote> >* gDela
 
 #if defined(XP_WIN)
 // the following are used to prevent other DLLs reverting the last chance
 // exception handler to the windows default. Any attempt to change the
 // unhandled exception filter or to reset it is ignored and our crash
 // reporter is loaded instead (in case it became unloaded somehow)
 typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI *SetUnhandledExceptionFilter_func)
   (LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
-static SetUnhandledExceptionFilter_func stub_SetUnhandledExceptionFilter = 0;
+static WindowsDllInterceptor::FuncHookType<SetUnhandledExceptionFilter_func>
+  stub_SetUnhandledExceptionFilter;
 static LPTOP_LEVEL_EXCEPTION_FILTER previousUnhandledExceptionFilter = nullptr;
 static WindowsDllInterceptor gKernel32Intercept;
 static bool gBlockUnhandledExceptionFilter = true;
 
 static LPTOP_LEVEL_EXCEPTION_FILTER GetUnhandledExceptionFilter()
 {
   // Set a dummy value to get the current filter, then restore
   LPTOP_LEVEL_EXCEPTION_FILTER current = SetUnhandledExceptionFilter(nullptr);
@@ -1634,19 +1635,19 @@ nsresult SetExceptionHandler(nsIFile* aX
 #ifdef _WIN64
   // Tell JS about the new filter before we disable SetUnhandledExceptionFilter
   SetJitExceptionHandler();
 #endif
 
   // protect the crash reporter from being unloaded
   gBlockUnhandledExceptionFilter = true;
   gKernel32Intercept.Init("kernel32.dll");
-  bool ok = gKernel32Intercept.AddHook("SetUnhandledExceptionFilter",
-          reinterpret_cast<intptr_t>(patched_SetUnhandledExceptionFilter),
-          (void**) &stub_SetUnhandledExceptionFilter);
+  bool ok = stub_SetUnhandledExceptionFilter.Set(gKernel32Intercept,
+                                                 "SetUnhandledExceptionFilter",
+                                                 &patched_SetUnhandledExceptionFilter);
 
 #ifdef DEBUG
   if (!ok)
     printf_stderr ("SetUnhandledExceptionFilter hook failed; crash reporter is vulnerable.\n");
 #endif
 #endif
 
   // store application start time
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -966,17 +966,18 @@ function _persistImage(sourceURL, localF
     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
     (PERSIST_BYPASS_CACHE ?
        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE :
        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE);
 
   persist.progressListener = new _persistProgressListener(successCallback);
 
-  persist.saveURI(sourceURI, 0,
+  let sourcePrincipal = Services.scriptSecurityManager.createCodebasePrincipal(sourceURI, {});
+  persist.saveURI(sourceURI, sourcePrincipal, 0,
                   null, Ci.nsIHttpChannel.REFERRER_POLICY_UNSET,
                   null, null, targetURI, null);
 }
 
 function _persistProgressListener(successCallback) {
   this.onLocationChange = function() {};
   this.onProgressChange = function() {};
   this.onStatusChange   = function() {};
--- a/tools/lint/eslint/eslint-plugin-mozilla/update.sh
+++ b/tools/lint/eslint/eslint-plugin-mozilla/update.sh
@@ -1,25 +1,25 @@
 #!/bin/sh
 # Script to regenerate the npm packages used for eslint-plugin-mozilla by the builders.
 # Requires
 
 # Force the scripts working directory to be projdir/tools/lint/eslint/eslint-plugin-mozilla.
 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 cd $DIR
 
-echo "To complete this script you will need the following tokens from https://api.pub.build.mozilla.org/tokenauth/"
+echo "To complete this script you will need the following tokens from https://mozilla-releng.net/tokens"
 echo " - tooltool.upload.public"
 echo " - tooltool.download.public"
 echo ""
 read -p "Are these tokens visible at the above URL (y/n)?" choice
 case "$choice" in
   y|Y )
     echo ""
-    echo "1. Go to https://api.pub.build.mozilla.org/"
+    echo "1. Go to https://mozilla-releng.net"
     echo "2. Log in using your Mozilla LDAP account."
     echo "3. Click on \"Tokens.\""
     echo "4. Issue a user token with the permissions tooltool.upload.public and tooltool.download.public."
     echo ""
     echo "When you click issue you will be presented with a long string. Paste the string into a temporary file called ~/.tooltool-token."
     echo ""
     read -rsp $'Press any key to continue...\n' -n 1
     ;;
@@ -44,18 +44,18 @@ rm package-lock.json
 echo "Installing modules for eslint-plugin-mozilla..."
 npm install
 
 echo "Creating eslint-plugin-mozilla.tar.gz..."
 tar cvz -f eslint-plugin-mozilla.tar.gz node_modules
 
 echo "Adding eslint-plugin-mozilla.tar.gz to tooltool..."
 rm -f manifest.tt
-../../../../python/mozbuild/mozbuild/action/tooltool.py add --visibility public --unpack eslint-plugin-mozilla.tar.gz --url="https://api.pub.build.mozilla.org/tooltool/"
+../../../../python/mozbuild/mozbuild/action/tooltool.py add --visibility public --unpack eslint-plugin-mozilla.tar.gz --url="https://tooltool.mozilla-releng.net/"
 
 echo "Uploading eslint-plugin-mozilla.tar.gz to tooltool..."
-../../../../python/mozbuild/mozbuild/action/tooltool.py upload --authentication-file=~/.tooltool-token --message "node_modules folder update for tools/lint/eslint/eslint-plugin-mozilla" --url="https://api.pub.build.mozilla.org/tooltool/"
+../../../../python/mozbuild/mozbuild/action/tooltool.py upload --authentication-file=~/.tooltool-token --message "node_modules folder update for tools/lint/eslint/eslint-plugin-mozilla" --url="https://tooltool.mozilla-releng.net/"
 
 echo "Cleaning up..."
 rm eslint-plugin-mozilla.tar.gz
 
 echo ""
 echo "Update complete, please commit and check in your changes."
--- a/tools/lint/eslint/update.sh
+++ b/tools/lint/eslint/update.sh
@@ -1,25 +1,25 @@
 #!/bin/sh
 # Script to regenerate the npm packages used for ESLint by the builders.
 # Requires
 
 # Force the scripts working directory to be projdir/tools/lint/eslint.
 DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 cd $DIR
 
-echo "To complete this script you will need the following tokens from https://api.pub.build.mozilla.org/tokenauth/"
+echo "To complete this script you will need the following tokens from https://mozilla-releng.net/tokens"
 echo " - tooltool.upload.public"
 echo " - tooltool.download.public"
 echo ""
 read -p "Are these tokens visible at the above URL (y/n)?" choice
 case "$choice" in
   y|Y )
     echo ""
-    echo "1. Go to https://api.pub.build.mozilla.org/"
+    echo "1. Go to https://mozilla-releng.net"
     echo "2. Log in using your Mozilla LDAP account."
     echo "3. Click on \"Tokens.\""
     echo "4. Issue a user token with the permissions tooltool.upload.public and tooltool.download.public."
     echo ""
     echo "When you click issue you will be presented with a long string. Paste the string into a temporary file called ~/.tooltool-token."
     echo ""
     read -rsp $'Press any key to continue...\n' -n 1
     ;;
@@ -51,20 +51,20 @@ echo "Installing eslint and external plu
 # access to make changes.
 npm install
 
 echo "Creating eslint.tar.gz..."
 tar cvz --exclude=eslint-plugin-mozilla --exclude=eslint-plugin-spidermonkey-js -f eslint.tar.gz node_modules
 
 echo "Adding eslint.tar.gz to tooltool..."
 rm tools/lint/eslint/manifest.tt
-./python/mozbuild/mozbuild/action/tooltool.py add --visibility public --unpack eslint.tar.gz --url="https://api.pub.build.mozilla.org/tooltool/"
+./python/mozbuild/mozbuild/action/tooltool.py add --visibility public --unpack eslint.tar.gz --url="https://tooltool.mozilla-releng.net/"
 
 echo "Uploading eslint.tar.gz to tooltool..."
-./python/mozbuild/mozbuild/action/tooltool.py upload --authentication-file=~/.tooltool-token --message "node_modules folder update for tools/lint/eslint" --url="https://api.pub.build.mozilla.org/tooltool/"
+./python/mozbuild/mozbuild/action/tooltool.py upload --authentication-file=~/.tooltool-token --message "node_modules folder update for tools/lint/eslint" --url="https://tooltool.mozilla-releng.net/"
 
 echo "Cleaning up..."
 mv manifest.tt tools/lint/eslint/manifest.tt
 rm eslint.tar.gz
 
 cd $DIR
 
 echo ""
--- a/tools/profiler/core/platform-win32.cpp
+++ b/tools/profiler/core/platform-win32.cpp
@@ -298,32 +298,34 @@ Registers::SyncPopulate()
   PopulateRegsFromContext(*this, &context);
 }
 #endif
 
 #if defined(GP_PLAT_amd64_windows)
 static WindowsDllInterceptor NtDllIntercept;
 
 typedef NTSTATUS (NTAPI *LdrUnloadDll_func)(HMODULE module);
-static LdrUnloadDll_func stub_LdrUnloadDll;
+static WindowsDllInterceptor::FuncHookType<LdrUnloadDll_func>
+  stub_LdrUnloadDll;
 
 static NTSTATUS NTAPI
 patched_LdrUnloadDll(HMODULE module)
 {
   // Prevent the stack walker from suspending this thread when LdrUnloadDll
   // holds the RtlLookupFunctionEntry lock.
   AutoSuppressStackWalking suppress;
   return stub_LdrUnloadDll(module);
 }
 
 // These pointers are disguised as PVOID to avoid pulling in obscure headers
 typedef PVOID (WINAPI *LdrResolveDelayLoadedAPI_func)(PVOID ParentModuleBase,
   PVOID DelayloadDescriptor, PVOID FailureDllHook, PVOID FailureSystemHook,
   PVOID ThunkAddress, ULONG Flags);
-static LdrResolveDelayLoadedAPI_func stub_LdrResolveDelayLoadedAPI;
+static WindowsDllInterceptor::FuncHookType<LdrResolveDelayLoadedAPI_func>
+  stub_LdrResolveDelayLoadedAPI;
 
 static PVOID WINAPI
 patched_LdrResolveDelayLoadedAPI(PVOID ParentModuleBase,
   PVOID DelayloadDescriptor, PVOID FailureDllHook, PVOID FailureSystemHook,
   PVOID ThunkAddress, ULONG Flags)
 {
   // Prevent the stack walker from suspending this thread when
   // LdrResolveDelayLoadAPI holds the RtlLookupFunctionEntry lock.
@@ -331,26 +333,18 @@ patched_LdrResolveDelayLoadedAPI(PVOID P
   return stub_LdrResolveDelayLoadedAPI(ParentModuleBase, DelayloadDescriptor,
                                        FailureDllHook, FailureSystemHook,
                                        ThunkAddress, Flags);
 }
 
 void
 InitializeWin64ProfilerHooks()
 {
-  static bool initialized = false;
-  if (initialized) {
-    return;
-  }
-  initialized = true;
-
   NtDllIntercept.Init("ntdll.dll");
-  NtDllIntercept.AddHook("LdrUnloadDll",
-                         reinterpret_cast<intptr_t>(patched_LdrUnloadDll),
-                         (void**)&stub_LdrUnloadDll);
+  stub_LdrUnloadDll.Set(NtDllIntercept, "LdrUnloadDll", &patched_LdrUnloadDll);
   if (IsWin8OrLater()) { // LdrResolveDelayLoadedAPI was introduced in Win8
-    NtDllIntercept.AddHook("LdrResolveDelayLoadedAPI",
-                           reinterpret_cast<intptr_t>(patched_LdrResolveDelayLoadedAPI),
-                           (void**)&stub_LdrResolveDelayLoadedAPI);
+    stub_LdrResolveDelayLoadedAPI.Set(NtDllIntercept,
+                                      "LdrResolveDelayLoadedAPI",
+                                      &patched_LdrResolveDelayLoadedAPI);
   }
 }
 #endif // defined(GP_PLAT_amd64_windows)
 
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -347,19 +347,16 @@ static bool     gWindowsVisible         
 // True if we have sent a notification that we are suspending/sleeping.
 static bool     gIsSleepMode                      = false;
 
 static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 
 // General purpose user32.dll hook object
 static WindowsDllInterceptor sUser32Intercept;
 
-// AddHook success checks
-static mozilla::Maybe<bool> sHookedGetWindowInfo;
-
 // 2 pixel offset for eTransparencyBorderlessGlass which equals the size of
 // the default window border Windows paints. Glass will be extended inward
 // this distance to remove the border.
 static const int32_t kGlassMarginAdjustment = 2;
 
 // When the client area is extended out into the default window frame area,
 // this is the minimum amount of space along the edge of resizable windows
 // we will always display a resize cursor in, regardless of the underlying
@@ -456,27 +453,27 @@ private:
                                ::GetCurrentThreadId());
     MOZ_ASSERT(mHook);
 
     // On touchscreen devices, tiptsf.dll will have been loaded when STA COM was
     // first initialized.
     if (!IsWin10OrLater() && GetModuleHandle(L"tiptsf.dll") &&
         !sProcessCaretEventsStub) {
       sTipTsfInterceptor.Init("tiptsf.dll");
-      DebugOnly<bool> ok = sTipTsfInterceptor.AddHook("ProcessCaretEvents",
-          reinterpret_cast<intptr_t>(&ProcessCaretEventsHook),
-          (void**) &sProcessCaretEventsStub);
+      DebugOnly<bool> ok = sProcessCaretEventsStub.Set(sTipTsfInterceptor,
+                                                       "ProcessCaretEvents",
+                                                       &ProcessCaretEventsHook);
       MOZ_ASSERT(ok);
     }
 
     if (!sSendMessageTimeoutWStub) {
       sUser32Intercept.Init("user32.dll");
-      DebugOnly<bool> hooked = sUser32Intercept.AddHook("SendMessageTimeoutW",
-          reinterpret_cast<intptr_t>(&SendMessageTimeoutWHook),
-          (void**) &sSendMessageTimeoutWStub);
+      DebugOnly<bool> hooked = sSendMessageTimeoutWStub.Set(sUser32Intercept,
+                                                            "SendMessageTimeoutW",
+                                                            &SendMessageTimeoutWHook);
       MOZ_ASSERT(hooked);
     }
   }
 
   class MOZ_RAII A11yInstantiationBlocker
   {
   public:
     A11yInstantiationBlocker()
@@ -551,28 +548,32 @@ private:
     // off to DefWindowProc to accomplish this.
     *aMsgResult = static_cast<DWORD_PTR>(::DefWindowProcW(aHwnd, aMsgCode,
                                                           aWParam, aLParam));
 
     return static_cast<LRESULT>(TRUE);
   }
 
   static WindowsDllInterceptor sTipTsfInterceptor;
-  static WINEVENTPROC sProcessCaretEventsStub;
-  static decltype(&SendMessageTimeoutW) sSendMessageTimeoutWStub;
+  static WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
+    sProcessCaretEventsStub;
+  static WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
+    sSendMessageTimeoutWStub;
   static StaticAutoPtr<TIPMessageHandler> sInstance;
 
   HHOOK                 mHook;
   UINT                  mMessages[7];
   uint32_t              mA11yBlockCount;
 };
 
 WindowsDllInterceptor TIPMessageHandler::sTipTsfInterceptor;
-WINEVENTPROC TIPMessageHandler::sProcessCaretEventsStub;
-decltype(&SendMessageTimeoutW) TIPMessageHandler::sSendMessageTimeoutWStub;
+WindowsDllInterceptor::FuncHookType<WINEVENTPROC>
+  TIPMessageHandler::sProcessCaretEventsStub;
+WindowsDllInterceptor::FuncHookType<decltype(&SendMessageTimeoutW)>
+  TIPMessageHandler::sSendMessageTimeoutWStub;
 StaticAutoPtr<TIPMessageHandler> TIPMessageHandler::sInstance;
 
 } // namespace mozilla
 
 #endif // defined(ACCESSIBILITY)
 
 /**************************************************************
  **************************************************************
@@ -2467,17 +2468,17 @@ nsWindow::ResetLayout()
   Invalidate();
 }
 
 // Internally track the caption status via a window property. Required
 // due to our internal handling of WM_NCACTIVATE when custom client
 // margins are set.
 static const wchar_t kManageWindowInfoProperty[] = L"ManageWindowInfoProperty";
 typedef BOOL (WINAPI *GetWindowInfoPtr)(HWND hwnd, PWINDOWINFO pwi);
-static GetWindowInfoPtr sGetWindowInfoPtrStub = nullptr;
+static WindowsDllInterceptor::FuncHookType<GetWindowInfoPtr> sGetWindowInfoPtrStub;
 
 BOOL WINAPI
 GetWindowInfoHook(HWND hWnd, PWINDOWINFO pwi)
 {
   if (!sGetWindowInfoPtrStub) {
     NS_ASSERTION(FALSE, "Something is horribly wrong in GetWindowInfoHook!");
     return FALSE;
   }
@@ -2495,28 +2496,25 @@ GetWindowInfoHook(HWND hWnd, PWINDOWINFO
 }
 
 void
 nsWindow::UpdateGetWindowInfoCaptionStatus(bool aActiveCaption)
 {
   if (!mWnd)
     return;
 
-  if (sHookedGetWindowInfo.isNothing()) {
-    sUser32Intercept.Init("user32.dll");
-    sHookedGetWindowInfo =
-      Some(sUser32Intercept.AddHook("GetWindowInfo",
-                                    reinterpret_cast<intptr_t>(GetWindowInfoHook),
-                                    (void**) &sGetWindowInfoPtrStub));
-    if (!sHookedGetWindowInfo.value()) {
-      return;
-    }
-  }
+  sUser32Intercept.Init("user32.dll");
+  sGetWindowInfoPtrStub.Set(sUser32Intercept, "GetWindowInfo",
+                            &GetWindowInfoHook);
+  if (!sGetWindowInfoPtrStub) {
+    return;
+  }
+
   // Update our internally tracked caption status
-  SetPropW(mWnd, kManageWindowInfoProperty, 
+  SetPropW(mWnd, kManageWindowInfoProperty,
     reinterpret_cast<HANDLE>(static_cast<INT_PTR>(aActiveCaption) + 1));
 }
 
 /**
  * Called when the window layout changes: full screen mode transitions,
  * theme changes, and composition changes. Calculates the new non-client
  * margins and fires off a frame changed event, which triggers an nc calc
  * size windows event, kicking the changes in.
--- a/xpcom/base/AvailableMemoryTracker.cpp
+++ b/xpcom/base/AvailableMemoryTracker.cpp
@@ -79,26 +79,24 @@ bool sHooksActive = false;
 
 // Alas, we'd like to use mozilla::TimeStamp, but we can't, because it acquires
 // a lock!
 volatile bool sUnderMemoryPressure = false;
 volatile PRIntervalTime sLastLowMemoryNotificationTime;
 
 // These are function pointers to the functions we wrap in Init().
 
-void* (WINAPI* sVirtualAllocOrig)(LPVOID aAddress, SIZE_T aSize,
-                                  DWORD aAllocationType, DWORD aProtect);
+static WindowsDllInterceptor::FuncHookType<decltype(&VirtualAlloc)>
+  sVirtualAllocOrig;
 
-void* (WINAPI* sMapViewOfFileOrig)(HANDLE aFileMappingObject,
-                                   DWORD aDesiredAccess, DWORD aFileOffsetHigh,
-                                   DWORD aFileOffsetLow, SIZE_T aNumBytesToMap);
+static WindowsDllInterceptor::FuncHookType<decltype(&MapViewOfFile)>
+  sMapViewOfFileOrig;
 
-HBITMAP(WINAPI* sCreateDIBSectionOrig)(HDC aDC, const BITMAPINFO* aBitmapInfo,
-                                       UINT aUsage, VOID** aBits,
-                                       HANDLE aSection, DWORD aOffset);
+static WindowsDllInterceptor::FuncHookType<decltype(&CreateDIBSection)>
+  sCreateDIBSectionOrig;
 
 /**
  * Fire a memory pressure event if we were not under memory pressure yet, or
  * fire an ongoing one if it's been long enough since the last one we
  * fired.
  */
 bool
 MaybeScheduleMemoryPressureEvent()
@@ -640,27 +638,22 @@ Init()
 
 #if defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
   // Don't register the hooks if we're a build instrumented for PGO: If we're
   // an instrumented build, the compiler adds function calls all over the place
   // which may call VirtualAlloc; this makes it hard to prevent
   // VirtualAllocHook from reentering itself.
   if (!PR_GetEnv("MOZ_PGO_INSTRUMENTED")) {
     sKernel32Intercept.Init("Kernel32.dll");
-    sKernel32Intercept.AddHook("VirtualAlloc",
-                               reinterpret_cast<intptr_t>(VirtualAllocHook),
-                               reinterpret_cast<void**>(&sVirtualAllocOrig));
-    sKernel32Intercept.AddHook("MapViewOfFile",
-                               reinterpret_cast<intptr_t>(MapViewOfFileHook),
-                               reinterpret_cast<void**>(&sMapViewOfFileOrig));
+    sVirtualAllocOrig.Set(sKernel32Intercept, "VirtualAlloc", &VirtualAllocHook);
+    sMapViewOfFileOrig.Set(sKernel32Intercept, "MapViewOfFile", &MapViewOfFileHook);
 
     sGdi32Intercept.Init("Gdi32.dll");
-    sGdi32Intercept.AddHook("CreateDIBSection",
-                            reinterpret_cast<intptr_t>(CreateDIBSectionHook),
-                            reinterpret_cast<void**>(&sCreateDIBSectionOrig));
+    sCreateDIBSectionOrig.Set(sGdi32Intercept, "CreateDIBSection",
+                              &CreateDIBSectionHook);
   }
 
   sInitialized = true;
 #endif // defined(XP_WIN) && !defined(HAVE_64BIT_BUILD)
 }
 
 } // namespace AvailableMemoryTracker
 } // namespace mozilla
--- a/xpcom/build/PoisonIOInterposerWin.cpp
+++ b/xpcom/build/PoisonIOInterposerWin.cpp
@@ -204,23 +204,30 @@ WinIOAutoObservation::Filename(nsAString
   mHasQueriedFilename = true;
 
   aFilename = mFilename;
 }
 
 /*************************** IO Interposing Methods ***************************/
 
 // Function pointers to original functions
-static NtCreateFileFn         gOriginalNtCreateFile;
-static NtReadFileFn           gOriginalNtReadFile;
-static NtReadFileScatterFn    gOriginalNtReadFileScatter;
-static NtWriteFileFn          gOriginalNtWriteFile;
-static NtWriteFileGatherFn    gOriginalNtWriteFileGather;
-static NtFlushBuffersFileFn   gOriginalNtFlushBuffersFile;
-static NtQueryFullAttributesFileFn gOriginalNtQueryFullAttributesFile;
+static WindowsDllInterceptor::FuncHookType<NtCreateFileFn>
+  gOriginalNtCreateFile;
+static WindowsDllInterceptor::FuncHookType<NtReadFileFn>
+  gOriginalNtReadFile;
+static WindowsDllInterceptor::FuncHookType<NtReadFileScatterFn>
+  gOriginalNtReadFileScatter;
+static WindowsDllInterceptor::FuncHookType<NtWriteFileFn>
+  gOriginalNtWriteFile;
+static WindowsDllInterceptor::FuncHookType<NtWriteFileGatherFn>
+  gOriginalNtWriteFileGather;
+static WindowsDllInterceptor::FuncHookType<NtFlushBuffersFileFn>
+  gOriginalNtFlushBuffersFile;
+static WindowsDllInterceptor::FuncHookType<NtQueryFullAttributesFileFn>
+  gOriginalNtQueryFullAttributesFile;
 
 static NTSTATUS NTAPI
 InterposedNtCreateFile(PHANDLE aFileHandle,
                        ACCESS_MASK aDesiredAccess,
                        POBJECT_ATTRIBUTES aObjectAttributes,
                        PIO_STATUS_BLOCK aIoStatusBlock,
                        PLARGE_INTEGER aAllocationSize,
                        ULONG aFileAttributes,
@@ -443,44 +450,31 @@ InitPoisonIOInterposer()
   // at any moment, so the instance needs to persist longer than the scope
   // of this functions.
   static DebugFdRegistry registry;
   ReplaceMalloc::InitDebugFd(registry);
 #endif
 
   // Initialize dll interceptor and add hooks
   sNtDllInterceptor.Init("ntdll.dll");
-  sNtDllInterceptor.AddHook(
-    "NtCreateFile",
-    reinterpret_cast<intptr_t>(InterposedNtCreateFile),
-    reinterpret_cast<void**>(&gOriginalNtCreateFile));
-  sNtDllInterceptor.AddHook(
-    "NtReadFile",
-    reinterpret_cast<intptr_t>(InterposedNtReadFile),
-    reinterpret_cast<void**>(&gOriginalNtReadFile));
-  sNtDllInterceptor.AddHook(
-    "NtReadFileScatter",
-    reinterpret_cast<intptr_t>(InterposedNtReadFileScatter),
-    reinterpret_cast<void**>(&gOriginalNtReadFileScatter));
-  sNtDllInterceptor.AddHook(
-    "NtWriteFile",
-    reinterpret_cast<intptr_t>(InterposedNtWriteFile),
-    reinterpret_cast<void**>(&gOriginalNtWriteFile));
-  sNtDllInterceptor.AddHook(
-    "NtWriteFileGather",
-    reinterpret_cast<intptr_t>(InterposedNtWriteFileGather),
-    reinterpret_cast<void**>(&gOriginalNtWriteFileGather));
-  sNtDllInterceptor.AddHook(
-    "NtFlushBuffersFile",
-    reinterpret_cast<intptr_t>(InterposedNtFlushBuffersFile),
-    reinterpret_cast<void**>(&gOriginalNtFlushBuffersFile));
-  sNtDllInterceptor.AddHook(
-    "NtQueryFullAttributesFile",
-    reinterpret_cast<intptr_t>(InterposedNtQueryFullAttributesFile),
-    reinterpret_cast<void**>(&gOriginalNtQueryFullAttributesFile));
+  gOriginalNtCreateFile.Set(sNtDllInterceptor, "NtCreateFile",
+                            &InterposedNtCreateFile);
+  gOriginalNtReadFile.Set(sNtDllInterceptor, "NtReadFile",
+                          &InterposedNtReadFile);
+  gOriginalNtReadFileScatter.Set(sNtDllInterceptor, "NtReadFileScatter",
+                                 &InterposedNtReadFileScatter);
+  gOriginalNtWriteFile.Set(sNtDllInterceptor, "NtWriteFile",
+                           &InterposedNtWriteFile);
+  gOriginalNtWriteFileGather.Set(sNtDllInterceptor, "NtWriteFileGather",
+                                 &InterposedNtWriteFileGather);
+  gOriginalNtFlushBuffersFile.Set(sNtDllInterceptor, "NtFlushBuffersFile",
+                                  &InterposedNtFlushBuffersFile);
+  gOriginalNtQueryFullAttributesFile.Set(sNtDllInterceptor,
+                                         "NtQueryFullAttributesFile",
+                                         &InterposedNtQueryFullAttributesFile);
 }
 
 void
 ClearPoisonIOInterposer()
 {
   MOZ_ASSERT(false);
   if (sIOPoisoned) {
     // Destroy the DLL interceptor