Merge inbound to mozilla-central a=merge
authorarthur.iakab <aiakab@mozilla.com>
Fri, 08 Jun 2018 12:55:49 +0300
changeset 478655 e0595117ff5bda3a63a72ad7b3b8754fec4fb4f0
parent 478611 5eb98e41ebd9776cdbe2fd7616676d87f78c10d3 (current diff)
parent 478654 f4c9e858aff07034f8d3d3161ccac29ad497c2df (diff)
child 478659 664495b616f82904292da79c74cea1c2a668a3e2
child 478680 79db03d7394385d2337ecfe967250de2db5d5bdc
push id1757
push userffxbld-merge
push dateFri, 24 Aug 2018 17:02:43 +0000
treeherdermozilla-release@736023aebdb1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone62.0a1
first release with
nightly linux32
e0595117ff5b / 62.0a1 / 20180608100105 / files
nightly linux64
e0595117ff5b / 62.0a1 / 20180608100105 / files
nightly mac
e0595117ff5b / 62.0a1 / 20180608100105 / files
nightly win32
e0595117ff5b / 62.0a1 / 20180608100105 / files
nightly win64
e0595117ff5b / 62.0a1 / 20180608100105 / 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
browser/app/LaunchUnelevated.cpp
browser/app/LaunchUnelevated.h
browser/app/LauncherProcessWin.cpp
browser/app/LauncherProcessWin.h
browser/app/ProcThreadAttributes.h
build/build-clang/clang-6-pre-linux64.json
build/build-clang/clang-6-pre-macosx64.json
build/build-clang/r321543.patch
taskcluster/scripts/misc/build-clang-6-pre-linux-macosx-cross.sh
taskcluster/scripts/misc/build-clang-6-pre-linux.sh
testing/config/tooltool-manifests/androidarm_6_0/mach-emulator.manifest
testing/config/tooltool-manifests/androidx86_6_0/mach-emulator.manifest
toolkit/components/places/UnifiedComplete.js
toolkit/components/search/nsSearchService.js
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -57,28 +57,29 @@ if CONFIG['ENABLE_GECKODRIVER']:
 if CONFIG['CC_TYPE'] in ('msvc', 'clang-cl'):
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     RCINCLUDE = 'splash.rc'
     DEFINES['MOZ_PHOENIX'] = True
-    SOURCES += [
-        'LauncherProcessWin.cpp',
-        'LaunchUnelevated.cpp',
+    DIRS += [
+        'winlauncher',
+    ]
+    USE_LIBS += [
+        'winlauncher',
+    ]
+    LOCAL_INCLUDES += [
+        '/browser/app/winlauncher',
     ]
     DELAYLOAD_DLLS += [
         'oleaut32.dll',
         'ole32.dll',
     ]
-    OS_LIBS += [
-        'oleaut32',
-        'ole32',
-    ]
 
 if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_ARCH'] == 'WINNT':
     # For sandbox includes and the include dependencies those have
     LOCAL_INCLUDES += [
         '/security/sandbox/chromium',
         '/security/sandbox/chromium-shim',
     ]
 
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -249,16 +249,21 @@ InitXPCOMGlue()
   }
 
   // This will set this thread as the main thread.
   gBootstrap->NS_LogInit();
 
   return NS_OK;
 }
 
+#ifdef HAS_DLL_BLOCKLIST
+// NB: This must be extern, as this value is checked elsewhere
+uint32_t gBlocklistInitFlags = eDllBlocklistInitFlagDefault;
+#endif
+
 int main(int argc, char* argv[], char* envp[])
 {
   mozilla::TimeStamp start = mozilla::TimeStamp::Now();
 
 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
   // We are launching as a content process, delegate to the appropriate
   // main
   if (argc > 1 && IsArg(argv[1], "contentproc")) {
@@ -284,17 +289,17 @@ int main(int argc, char* argv[], char* e
     // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
     gBootstrap->NS_LogTerm();
 
     return result;
   }
 #endif
 
 #ifdef HAS_DLL_BLOCKLIST
-  DllBlocklist_Initialize();
+  DllBlocklist_Initialize(gBlocklistInitFlags);
 #endif
 
   nsresult rv = InitXPCOMGlue();
   if (NS_FAILED(rv)) {
     return 255;
   }
 
   gBootstrap->XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/DllBlocklistWin.cpp
@@ -0,0 +1,431 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "NativeNt.h"
+#include "nsWindowsDllInterceptor.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Types.h"
+#include "mozilla/WindowsDllBlocklist.h"
+
+#define DLL_BLOCKLIST_ENTRY(name, ...) \
+  { L##name, __VA_ARGS__ },
+#define DLL_BLOCKLIST_CHAR_TYPE wchar_t
+
+// Restrict the blocklist definitions to Nightly-only for now
+#if defined(NIGHTLY_BUILD)
+#include "mozilla/WindowsDllBlocklistDefs.h"
+#else
+#include "mozilla/WindowsDllBlocklistCommon.h"
+DLL_BLOCKLIST_DEFINITIONS_BEGIN
+DLL_BLOCKLIST_DEFINITIONS_END
+#endif
+
+extern uint32_t gBlocklistInitFlags;
+
+static const HANDLE kCurrentProcess = reinterpret_cast<HANDLE>(-1);
+
+class MOZ_STATIC_CLASS MOZ_TRIVIAL_CTOR_DTOR NativeNtBlockSet final
+{
+  struct NativeNtBlockSetEntry
+  {
+    NativeNtBlockSetEntry() = default;
+    ~NativeNtBlockSetEntry() = default;
+    NativeNtBlockSetEntry(const wchar_t* aName, uint64_t aVersion,
+                          NativeNtBlockSetEntry* aNext)
+      : mName(aName)
+      , mVersion(aVersion)
+      , mNext(aNext)
+    {}
+    const wchar_t*          mName;
+    uint64_t                mVersion;
+    NativeNtBlockSetEntry*  mNext;
+  };
+
+public:
+  // Constructor and destructor MUST be trivial
+  NativeNtBlockSet() = default;
+  ~NativeNtBlockSet() = default;
+
+  void Add(const wchar_t* aName, uint64_t aVersion);
+  void Write(HANDLE aFile);
+
+private:
+  static NativeNtBlockSetEntry* NewEntry(const wchar_t* aName, uint64_t aVersion,
+                                         NativeNtBlockSetEntry* aNextEntry);
+
+private:
+  NativeNtBlockSetEntry* mFirstEntry;
+  // SRWLOCK_INIT == 0, so this is okay to use without any additional work as
+  // long as NativeNtBlockSet is instantiated statically
+  SRWLOCK                mLock;
+};
+
+NativeNtBlockSet::NativeNtBlockSetEntry*
+NativeNtBlockSet::NewEntry(const wchar_t* aName, uint64_t aVersion,
+                           NativeNtBlockSet::NativeNtBlockSetEntry* aNextEntry)
+{
+  HANDLE processHeap = mozilla::nt::RtlGetProcessHeap();
+  if (!processHeap) {
+    return nullptr;
+  }
+
+  PVOID memory = ::RtlAllocateHeap(processHeap, 0, sizeof(NativeNtBlockSetEntry));
+  if (!memory) {
+    return nullptr;
+  }
+
+  return new (memory) NativeNtBlockSetEntry(aName, aVersion, aNextEntry);
+}
+
+void
+NativeNtBlockSet::Add(const wchar_t* aName, uint64_t aVersion)
+{
+  ::RtlAcquireSRWLockExclusive(&mLock);
+
+  for (NativeNtBlockSetEntry* entry = mFirstEntry; entry; entry = entry->mNext) {
+    // We just need to compare the string pointers, not the strings themselves,
+    // as we always pass in the strings statically defined in the blocklist.
+    if (aName == entry->mName && aVersion == entry->mVersion) {
+      ::RtlReleaseSRWLockExclusive(&mLock);
+      return;
+    }
+  }
+
+  // Not present, add it
+  NativeNtBlockSetEntry* newEntry = NewEntry(aName, aVersion, mFirstEntry);
+  mFirstEntry = newEntry;
+
+  ::RtlReleaseSRWLockExclusive(&mLock);
+}
+
+void
+NativeNtBlockSet::Write(HANDLE aFile)
+{
+  // NB: If this function is called, it is long after kernel32 is initialized,
+  // so it is safe to use Win32 calls here.
+  DWORD nBytes;
+  char buf[MAX_PATH];
+
+  // It would be nicer to use RAII here. However, its destructor
+  // might not run if an exception occurs, in which case we would never release
+  // the lock (MSVC warns about this possibility). So we acquire and release
+  // manually.
+  ::AcquireSRWLockExclusive(&mLock);
+
+  MOZ_SEH_TRY {
+    for (auto entry = mFirstEntry; entry; entry = entry->mNext) {
+      int convOk = ::WideCharToMultiByte(CP_UTF8, 0, entry->mName, -1, buf,
+                                         sizeof(buf), nullptr, nullptr);
+      if (!convOk) {
+        continue;
+      }
+
+      // write name[,v.v.v.v];
+      if (!WriteFile(aFile, buf, convOk, &nBytes, nullptr)) {
+        continue;
+      }
+
+      if (entry->mVersion != ALL_VERSIONS) {
+        WriteFile(aFile, ",", 1, &nBytes, nullptr);
+        uint16_t parts[4];
+        parts[0] = entry->mVersion >> 48;
+        parts[1] = (entry->mVersion >> 32) & 0xFFFF;
+        parts[2] = (entry->mVersion >> 16) & 0xFFFF;
+        parts[3] = entry->mVersion & 0xFFFF;
+        for (size_t p = 0; p < mozilla::ArrayLength(parts); ++p) {
+          ltoa(parts[p], buf, 10);
+          WriteFile(aFile, buf, strlen(buf), &nBytes, nullptr);
+          if (p != mozilla::ArrayLength(parts) - 1) {
+            WriteFile(aFile, ".", 1, &nBytes, nullptr);
+          }
+        }
+      }
+      WriteFile(aFile, ";", 1, &nBytes, nullptr);
+    }
+  }
+  MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) {
+  }
+
+  ::ReleaseSRWLockExclusive(&mLock);
+}
+
+static NativeNtBlockSet gBlockSet;
+
+extern "C" void MOZ_EXPORT
+NativeNtBlockSet_Write(HANDLE aHandle)
+{
+  gBlockSet.Write(aHandle);
+}
+
+static bool
+CheckBlockInfo(const DllBlockInfo* aInfo, void* aBaseAddress, uint64_t& aVersion)
+{
+  aVersion = ALL_VERSIONS;
+
+  if (aInfo->flags & (DllBlockInfo::BLOCK_WIN8PLUS_ONLY | DllBlockInfo::BLOCK_WIN8_ONLY)) {
+    RTL_OSVERSIONINFOW osv;
+    NTSTATUS ntStatus = ::RtlGetVersion(&osv);
+    if (!NT_SUCCESS(ntStatus)) {
+      // huh?
+      return false;
+    }
+
+    if (osv.dwMajorVersion < 8) {
+      return true;
+    }
+
+    if ((aInfo->flags & DllBlockInfo::BLOCK_WIN8_ONLY) && (osv.dwMajorVersion > 8 ||
+          (osv.dwMajorVersion == 8 && osv.dwMinorVersion > 0))) {
+      return true;
+    }
+  }
+
+  // We're not bootstrapping child processes at this time, so this case is
+  // always true.
+  if (aInfo->flags & DllBlockInfo::CHILD_PROCESSES_ONLY) {
+    return true;
+  }
+
+  if (aInfo->maxVersion == ALL_VERSIONS) {
+    return false;
+  }
+
+  mozilla::nt::PEHeaders headers(aBaseAddress);
+  if (!headers) {
+    return false;
+  }
+
+  if (aInfo->flags & DllBlockInfo::USE_TIMESTAMP) {
+    DWORD timestamp;
+    if (!headers.GetTimeStamp(timestamp)) {
+      return false;
+    }
+
+    return timestamp > aInfo->maxVersion;
+  }
+
+  // Else we try to get the file version information. Note that we don't have
+  // access to GetFileVersionInfo* APIs.
+  if (!headers.GetVersionInfo(aVersion)) {
+    return false;
+  }
+
+  return aVersion > aInfo->maxVersion;
+}
+
+static bool
+IsDllAllowed(const UNICODE_STRING& aLeafName, void* aBaseAddress)
+{
+  if (mozilla::nt::Contains12DigitHexString(aLeafName) ||
+      mozilla::nt::IsFileNameAtLeast16HexDigits(aLeafName)) {
+    return false;
+  }
+
+  UNICODE_STRING testStr;
+  DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
+  while (info->name) {
+    ::RtlInitUnicodeString(&testStr, info->name);
+    if (::RtlEqualUnicodeString(&aLeafName, &testStr, TRUE)) {
+      break;
+    }
+
+    ++info;
+  }
+
+  uint64_t version;
+  if (info->name && !CheckBlockInfo(info, aBaseAddress, version)) {
+    gBlockSet.Add(info->name, version);
+    return false;
+  }
+
+  return true;
+}
+
+typedef decltype(&NtMapViewOfSection) NtMapViewOfSection_func;
+static 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)
+{
+  // We always map first, then we check for additional info after.
+  NTSTATUS stubStatus = stub_NtMapViewOfSection(aSection, aProcess, aBaseAddress,
+                                                aZeroBits, aCommitSize,
+                                                aSectionOffset, aViewSize,
+                                                aInheritDisposition,
+                                                aAllocationType, aProtectionFlags);
+  if (!NT_SUCCESS(stubStatus)) {
+    return stubStatus;
+  }
+
+  if (aProcess != kCurrentProcess) {
+    // We're only interested in mapping for the current process.
+    return stubStatus;
+  }
+
+  // Do a query to see if the memory is MEM_IMAGE. If not, continue
+  MEMORY_BASIC_INFORMATION mbi;
+  NTSTATUS ntStatus = ::NtQueryVirtualMemory(aProcess, *aBaseAddress,
+                                             MemoryBasicInformation, &mbi,
+                                             sizeof(mbi), nullptr);
+  if (!NT_SUCCESS(ntStatus)) {
+    ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+    return STATUS_ACCESS_DENIED;
+  }
+
+  // We don't care about mappings that aren't MEM_IMAGE
+  if (!(mbi.Type & MEM_IMAGE)) {
+    return stubStatus;
+  }
+
+  // Get the section name
+  mozilla::nt::MemorySectionNameBuf buf;
+
+  ntStatus = ::NtQueryVirtualMemory(aProcess, *aBaseAddress, MemorySectionName,
+                                    &buf, sizeof(buf), nullptr);
+  if (!NT_SUCCESS(ntStatus)) {
+    ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+    return STATUS_ACCESS_DENIED;
+  }
+
+  // Find the leaf name
+  UNICODE_STRING leaf;
+  mozilla::nt::GetLeafName(&leaf, &buf.mSectionFileName);
+
+  // Check blocklist
+  if (IsDllAllowed(leaf, *aBaseAddress)) {
+    return stubStatus;
+  }
+
+  ::NtUnmapViewOfSection(aProcess, *aBaseAddress);
+  return STATUS_ACCESS_DENIED;
+}
+
+namespace mozilla {
+
+class MOZ_RAII AutoVirtualProtect final
+{
+public:
+  AutoVirtualProtect(void* aAddress, size_t aLength, DWORD aProtFlags,
+                     HANDLE aTargetProcess = nullptr)
+    : mAddress(aAddress)
+    , mLength(aLength)
+    , mTargetProcess(aTargetProcess)
+    , mPrevProt(0)
+  {
+    ::VirtualProtectEx(aTargetProcess, aAddress, aLength, aProtFlags,
+                       &mPrevProt);
+  }
+
+  ~AutoVirtualProtect()
+  {
+    if (!mPrevProt) {
+      return;
+    }
+
+    ::VirtualProtectEx(mTargetProcess, mAddress, mLength, mPrevProt,
+                       &mPrevProt);
+  }
+
+  explicit operator bool() const
+  {
+    return !!mPrevProt;
+  }
+
+  AutoVirtualProtect(const AutoVirtualProtect&) = delete;
+  AutoVirtualProtect(AutoVirtualProtect&&) = delete;
+  AutoVirtualProtect& operator=(const AutoVirtualProtect&) = delete;
+  AutoVirtualProtect& operator=(AutoVirtualProtect&&) = delete;
+
+private:
+  void*   mAddress;
+  size_t  mLength;
+  HANDLE  mTargetProcess;
+  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);
+  if (!ok) {
+    return false;
+  }
+
+  // Set the child process's copy of stub_NtMapViewOfSection
+  SIZE_T bytesWritten;
+  ok = !!::WriteProcessMemory(aChildProcess, &stub_NtMapViewOfSection,
+                              &stub_NtMapViewOfSection,
+                              sizeof(stub_NtMapViewOfSection), &bytesWritten);
+  if (!ok) {
+    return false;
+  }
+
+  // Because aChildProcess has just been created in a suspended state, its
+  // dynamic linker has not yet been initialized, thus its executable has
+  // not yet been linked with ntdll.dll. If the blocklist hook intercepts a
+  // library load prior to the link, the hook will be unable to invoke any
+  // ntdll.dll functions.
+  //
+  // We know that the executable for our *current* process's binary is already
+  // linked into ntdll, so we obtain the IAT from our own executable and graft
+  // it onto the child process's IAT, thus enabling the child process's hook to
+  // safely make its ntdll calls.
+  mozilla::nt::PEHeaders ourExeImage(::GetModuleHandleW(nullptr));
+  if (!ourExeImage) {
+    return false;
+  }
+
+  PIMAGE_IMPORT_DESCRIPTOR impDesc = ourExeImage.GetIATForModule("ntdll.dll");
+  if (!impDesc) {
+    return false;
+  }
+
+  // This is the pointer we need to write
+  auto firstIatThunk = ourExeImage.template
+    RVAToPtr<PIMAGE_THUNK_DATA>(impDesc->FirstThunk);
+  if (!firstIatThunk) {
+    return false;
+  }
+
+  // Find the length by iterating through the table until we find a null entry
+  PIMAGE_THUNK_DATA curIatThunk = firstIatThunk;
+  while (mozilla::nt::PEHeaders::IsValid(curIatThunk)) {
+    ++curIatThunk;
+  }
+
+  ptrdiff_t iatLength = (curIatThunk - firstIatThunk) * sizeof(IMAGE_THUNK_DATA);
+
+  { // Scope for prot
+    AutoVirtualProtect prot(firstIatThunk, iatLength, PAGE_READWRITE,
+                            aChildProcess);
+    if (!prot) {
+      return false;
+    }
+
+    ok = !!::WriteProcessMemory(aChildProcess, firstIatThunk, firstIatThunk,
+                                iatLength, &bytesWritten);
+    if (!ok) {
+      return false;
+    }
+  }
+
+  // Tell the mozglue blocklist that we have bootstrapped
+  uint32_t newFlags = eDllBlocklistInitFlagWasBootstrapped;
+  ok = !!::WriteProcessMemory(aChildProcess, &gBlocklistInitFlags, &newFlags,
+                              sizeof(newFlags), &bytesWritten);
+  return ok;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/DllBlocklistWin.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DllBlocklistWin_h
+#define mozilla_DllBlocklistWin_h
+
+#include <windows.h>
+
+namespace mozilla {
+
+bool InitializeDllBlocklistOOP(HANDLE aChildProcess);
+
+} // namespace mozilla
+
+#endif // mozilla_DllBlocklistWin_h
rename from browser/app/LaunchUnelevated.cpp
rename to browser/app/winlauncher/LaunchUnelevated.cpp
rename from browser/app/LaunchUnelevated.h
rename to browser/app/winlauncher/LaunchUnelevated.h
rename from browser/app/LauncherProcessWin.cpp
rename to browser/app/winlauncher/LauncherProcessWin.cpp
--- a/browser/app/LauncherProcessWin.cpp
+++ b/browser/app/winlauncher/LauncherProcessWin.cpp
@@ -16,30 +16,31 @@
 #include "mozilla/SafeMode.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 
 #include <windows.h>
 #include <processthreadsapi.h>
 
+#include "DllBlocklistWin.h"
 #include "LaunchUnelevated.h"
 #include "ProcThreadAttributes.h"
 
 /**
  * At this point the child process has been created in a suspended state. Any
  * additional startup work (eg, blocklist setup) should go here.
  *
  * @return true if browser startup should proceed, otherwise false.
  */
 static bool
 PostCreationSetup(HANDLE aChildProcess, HANDLE aChildMainThread,
                   const bool aIsSafeMode)
 {
-  return true;
+  return mozilla::InitializeDllBlocklistOOP(aChildProcess);
 }
 
 #if !defined(PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON)
 # define PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON (0x00000001ui64 << 60)
 #endif // !defined(PROCESS_CREATION_MITIGATION_POLICY_IMAGE_LOAD_PREFER_SYSTEM32_ALWAYS_ON)
 
 #if (_WIN32_WINNT < 0x0602)
 BOOL WINAPI
rename from browser/app/LauncherProcessWin.h
rename to browser/app/winlauncher/LauncherProcessWin.h
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/NativeNt.h
@@ -0,0 +1,546 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_NativeNt_h
+#define mozilla_NativeNt_h
+
+#if defined(MOZILLA_INTERNAL_API)
+#error "This header is for initial process initialization. You don't want to be including this here."
+#endif // defined(MOZILLA_INTERNAL_API)
+
+#include <stdint.h>
+#include <windows.h>
+#include <winnt.h>
+#include <winternl.h>
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Attributes.h"
+
+extern "C" {
+
+#if !defined(STATUS_ACCESS_DENIED)
+#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)
+#endif // !defined(STATUS_ACCESS_DENIED)
+
+#if !defined(STATUS_DLL_NOT_FOUND)
+#define STATUS_DLL_NOT_FOUND ((NTSTATUS)0xC0000135L)
+#endif // !defined(STATUS_DLL_NOT_FOUND)
+
+enum SECTION_INHERIT
+{
+  ViewShare = 1,
+  ViewUnmap = 2
+};
+
+NTSTATUS NTAPI
+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);
+
+NTSTATUS NTAPI
+NtUnmapViewOfSection(HANDLE aProcess, PVOID aBaseAddress);
+
+enum MEMORY_INFORMATION_CLASS
+{
+  MemoryBasicInformation = 0,
+  MemorySectionName = 2
+};
+
+// NB: When allocating, space for the buffer must also be included
+typedef struct _MEMORY_SECTION_NAME
+{
+  UNICODE_STRING mSectionFileName;
+} MEMORY_SECTION_NAME, *PMEMORY_SECTION_NAME;
+
+NTSTATUS NTAPI
+NtQueryVirtualMemory(HANDLE aProcess, PVOID aBaseAddress,
+                     MEMORY_INFORMATION_CLASS aMemInfoClass, PVOID aMemInfo,
+                     SIZE_T aMemInfoLen, PSIZE_T aReturnLen);
+
+LONG NTAPI
+RtlCompareUnicodeString(PCUNICODE_STRING aStr1, PCUNICODE_STRING aStr2,
+                        BOOLEAN aCaseInsensitive);
+
+BOOLEAN NTAPI
+RtlEqualUnicodeString(PCUNICODE_STRING aStr1, PCUNICODE_STRING aStr2,
+                      BOOLEAN aCaseInsensitive);
+
+NTSTATUS NTAPI
+RtlGetVersion(PRTL_OSVERSIONINFOW aOutVersionInformation);
+
+PVOID NTAPI
+RtlAllocateHeap(PVOID aHeapHandle, ULONG aFlags, SIZE_T aSize);
+
+PVOID NTAPI
+RtlReAllocateHeap(PVOID aHeapHandle, ULONG aFlags, LPVOID aMem, SIZE_T aNewSize);
+
+BOOLEAN NTAPI
+RtlFreeHeap(PVOID aHeapHandle, ULONG aFlags, PVOID aHeapBase);
+
+VOID NTAPI
+RtlAcquireSRWLockExclusive(PSRWLOCK aLock);
+
+VOID NTAPI
+RtlReleaseSRWLockExclusive(PSRWLOCK aLock);
+
+} // extern "C"
+
+namespace mozilla {
+namespace nt {
+
+struct MemorySectionNameBuf : public _MEMORY_SECTION_NAME
+{
+  MemorySectionNameBuf()
+  {
+    mSectionFileName.Length = 0;
+    mSectionFileName.MaximumLength = sizeof(mBuf);
+    mSectionFileName.Buffer = mBuf;
+  }
+
+  WCHAR mBuf[MAX_PATH];
+};
+
+inline bool
+FindCharInUnicodeString(const UNICODE_STRING& aStr, WCHAR aChar, uint16_t& aPos,
+                        uint16_t aStartIndex = 0)
+{
+  const uint16_t aMaxIndex = aStr.Length / sizeof(WCHAR);
+
+  for (uint16_t curIndex = aStartIndex; curIndex < aMaxIndex; ++curIndex) {
+    if (aStr.Buffer[curIndex] == aChar) {
+      aPos = curIndex;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+inline bool
+IsHexDigit(WCHAR aChar)
+{
+  return aChar >= L'0' && aChar <= L'9' ||
+         aChar >= L'A' && aChar <= L'F' ||
+         aChar >= L'a' && aChar <= L'f';
+}
+
+inline bool
+MatchUnicodeString(const UNICODE_STRING& aStr, bool (*aPredicate)(WCHAR))
+{
+  WCHAR* cur = aStr.Buffer;
+  WCHAR* end = &aStr.Buffer[aStr.Length / sizeof(WCHAR)];
+  while (cur < end) {
+    if (!aPredicate(*cur)) {
+      return false;
+    }
+
+    ++cur;
+  }
+
+  return true;
+}
+
+inline bool
+Contains12DigitHexString(const UNICODE_STRING& aLeafName)
+{
+  uint16_t start, end;
+  if (!FindCharInUnicodeString(aLeafName, L'.', start)) {
+    return false;
+  }
+
+  ++start;
+  if (!FindCharInUnicodeString(aLeafName, L'.', end, start)) {
+    return false;
+  }
+
+  if (end - start != 12) {
+    return false;
+  }
+
+  UNICODE_STRING test;
+  test.Buffer = &aLeafName.Buffer[start];
+  test.Length = (end - start) * sizeof(WCHAR);
+  test.MaximumLength = test.Length;
+
+  return MatchUnicodeString(test, &IsHexDigit);
+}
+
+inline bool
+IsFileNameAtLeast16HexDigits(const UNICODE_STRING& aLeafName)
+{
+  uint16_t dotIndex;
+  if (!FindCharInUnicodeString(aLeafName, L'.', dotIndex)) {
+    return false;
+  }
+
+  if (dotIndex < 16) {
+    return false;
+  }
+
+  UNICODE_STRING test;
+  test.Buffer = aLeafName.Buffer;
+  test.Length = dotIndex * sizeof(WCHAR);
+  test.MaximumLength = aLeafName.MaximumLength;
+
+  return MatchUnicodeString(test, &IsHexDigit);
+}
+
+inline void
+GetLeafName(PUNICODE_STRING aDestString, PCUNICODE_STRING aSrcString)
+{
+  WCHAR* buf = aSrcString->Buffer;
+  WCHAR* end = &aSrcString->Buffer[(aSrcString->Length / sizeof(WCHAR)) - 1];
+  WCHAR* cur = end;
+  while (cur >= buf) {
+    if (*cur == L'\\') {
+      break;
+    }
+
+    --cur;
+  }
+
+  // At this point, either cur points to the final backslash, or it points to
+  // buf - 1. Either way, we're interested in cur + 1 as the desired buffer.
+  aDestString->Buffer = cur + 1;
+  aDestString->Length = (end - aDestString->Buffer + 1) * sizeof(WCHAR);
+  aDestString->MaximumLength = aDestString->Length;
+}
+
+inline char
+EnsureLowerCaseASCII(char aChar)
+{
+  if (aChar >= 'A' && aChar <= 'Z') {
+    aChar -= 'A' - 'a';
+  }
+
+  return aChar;
+}
+
+inline int
+StricmpASCII(const char* aLeft, const char* aRight)
+{
+  char curLeft, curRight;
+
+  do {
+    curLeft = EnsureLowerCaseASCII(*(aLeft++));
+    curRight = EnsureLowerCaseASCII(*(aRight++));
+  } while(curLeft && curLeft == curRight);
+
+  return curLeft - curRight;
+}
+
+class MOZ_RAII PEHeaders final
+{
+  /**
+   * This structure is documented on MSDN as VS_VERSIONINFO, but is not present
+   * in SDK headers because it cannot be specified as a C struct. The following
+   * structure contains the fixed-length fields at the beginning of
+   * VS_VERSIONINFO.
+   */
+  struct VS_VERSIONINFO_HEADER
+  {
+    WORD             wLength;
+    WORD             wValueLength;
+    WORD             wType;
+    WCHAR            szKey[16]; // ArrayLength(L"VS_VERSION_INFO")
+    // Additional data goes here, aligned on a 4-byte boundary
+  };
+
+public:
+  explicit PEHeaders(void* aBaseAddress)
+    : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(aBaseAddress))
+  {
+  }
+
+  // The lowest two bits of an HMODULE are used as flags. Stripping those bits
+  // from the HMODULE yields the base address of the binary's memory mapping.
+  // (See LoadLibraryEx docs on MSDN)
+  explicit PEHeaders(HMODULE aModule)
+    : PEHeaders(reinterpret_cast<PIMAGE_DOS_HEADER>(
+                  reinterpret_cast<uintptr_t>(aModule) & ~uintptr_t(3)))
+  {
+  }
+
+  explicit operator bool() const
+  {
+    return !!mImageLimit;
+  }
+
+  /**
+   * This overload computes absolute virtual addresses relative to the base
+   * address of the binary.
+   */
+  template <typename T, typename R>
+  T RVAToPtr(R aRva)
+  {
+    return RVAToPtr<T>(mMzHeader, aRva);
+  }
+
+  /**
+   * This overload computes a result by adding aRva to aBase, but also ensures
+   * that the resulting pointer falls within the bounds of this binary's memory
+   * mapping.
+   */
+  template <typename T, typename R>
+  T RVAToPtr(void* aBase, R aRva)
+  {
+    if (!mImageLimit) {
+      return nullptr;
+    }
+
+    char* absAddress = reinterpret_cast<char*>(aBase) + aRva;
+    if (absAddress < reinterpret_cast<char*>(mMzHeader) ||
+        absAddress > reinterpret_cast<char*>(mImageLimit)) {
+      return nullptr;
+    }
+
+    return reinterpret_cast<T>(absAddress);
+  }
+
+  PIMAGE_IMPORT_DESCRIPTOR GetImportDirectory()
+  {
+    return GetImageDirectoryEntry<PIMAGE_IMPORT_DESCRIPTOR>(
+      IMAGE_DIRECTORY_ENTRY_IMPORT);
+  }
+
+  PIMAGE_RESOURCE_DIRECTORY GetResourceTable()
+  {
+    return GetImageDirectoryEntry<PIMAGE_RESOURCE_DIRECTORY>(
+      IMAGE_DIRECTORY_ENTRY_RESOURCE);
+  }
+
+  bool
+  GetVersionInfo(uint64_t& aOutVersion)
+  {
+    // RT_VERSION == 16
+    // Version resources require an id of 1
+    auto root = FindResourceLeaf<VS_VERSIONINFO_HEADER*>(16, 1);
+    if (!root) {
+      return false;
+    }
+
+    VS_FIXEDFILEINFO* fixedInfo = GetFixedFileInfo(root);
+    if (!fixedInfo) {
+      return false;
+    }
+
+    aOutVersion = ((static_cast<uint64_t>(fixedInfo->dwFileVersionMS) << 32) |
+                   static_cast<uint64_t>(fixedInfo->dwFileVersionLS));
+    return true;
+  }
+
+  bool
+  GetTimeStamp(DWORD& aResult)
+  {
+    if (!(*this)) {
+      return false;
+    }
+
+    aResult = mPeHeader->FileHeader.TimeDateStamp;
+    return true;
+  }
+
+  PIMAGE_IMPORT_DESCRIPTOR
+  GetIATForModule(const char* aModuleNameASCII)
+  {
+    for (PIMAGE_IMPORT_DESCRIPTOR curImpDesc = GetImportDirectory();
+         IsValid(curImpDesc); ++curImpDesc) {
+      auto curName = RVAToPtr<const char*>(curImpDesc->Name);
+      if (!curName) {
+        return nullptr;
+      }
+
+      if (StricmpASCII(aModuleNameASCII, curName)) {
+        continue;
+      }
+
+      // curImpDesc now points to the IAT for the module we're interested in
+      return curImpDesc;
+    }
+
+    return nullptr;
+  }
+
+  /**
+   * Resources are stored in a three-level tree. To locate a particular entry,
+   * you must supply a resource type, the resource id, and then the language id.
+   * If aLangId == 0, we just resolve the first entry regardless of language.
+   */
+  template <typename T> T
+  FindResourceLeaf(WORD aType, WORD aResId, WORD aLangId = 0)
+  {
+    PIMAGE_RESOURCE_DIRECTORY topLevel = GetResourceTable();
+    if (!topLevel) {
+      return nullptr;
+    }
+
+    PIMAGE_RESOURCE_DIRECTORY_ENTRY typeEntry = FindResourceEntry(topLevel,
+                                                                  aType);
+    if (!typeEntry || !typeEntry->DataIsDirectory) {
+      return nullptr;
+    }
+
+    auto idDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(topLevel,
+                                                     typeEntry->OffsetToDirectory);
+    PIMAGE_RESOURCE_DIRECTORY_ENTRY idEntry = FindResourceEntry(idDir, aResId);
+    if (!idEntry || !idEntry->DataIsDirectory) {
+      return nullptr;
+    }
+
+    auto langDir = RVAToPtr<PIMAGE_RESOURCE_DIRECTORY>(topLevel,
+                                                       idEntry->OffsetToDirectory);
+    PIMAGE_RESOURCE_DIRECTORY_ENTRY langEntry;
+    if (aLangId) {
+      langEntry = FindResourceEntry(langDir, aLangId);
+    } else {
+      langEntry = FindFirstResourceEntry(langDir);
+    }
+
+    if (!langEntry || langEntry->DataIsDirectory) {
+      return nullptr;
+    }
+
+    auto dataEntry = RVAToPtr<PIMAGE_RESOURCE_DATA_ENTRY>(topLevel,
+                                                          langEntry->OffsetToData);
+    return RVAToPtr<T>(dataEntry->OffsetToData);
+  }
+
+  static bool IsValid(PIMAGE_IMPORT_DESCRIPTOR aImpDesc)
+  {
+    return aImpDesc && aImpDesc->OriginalFirstThunk != 0;
+  }
+
+  static bool IsValid(PIMAGE_THUNK_DATA aImgThunk)
+  {
+    return aImgThunk && aImgThunk->u1.Ordinal != 0;
+  }
+
+private:
+  explicit PEHeaders(PIMAGE_DOS_HEADER aMzHeader)
+    : mMzHeader(aMzHeader)
+    , mPeHeader(nullptr)
+    , mImageLimit(nullptr)
+  {
+    if (!mMzHeader || mMzHeader->e_magic != IMAGE_DOS_SIGNATURE) {
+      return;
+    }
+
+    mPeHeader = RVAToPtrUnchecked<PIMAGE_NT_HEADERS>(mMzHeader->e_lfanew);
+    if (!mPeHeader || mPeHeader->Signature != IMAGE_NT_SIGNATURE) {
+      return;
+    }
+
+    mImageLimit =
+      RVAToPtrUnchecked<void*>(mPeHeader->OptionalHeader.SizeOfImage - 1UL);
+  }
+
+  template <typename T>
+  T GetImageDirectoryEntry(unsigned int aDirectoryIndex)
+  {
+    if (aDirectoryIndex >= IMAGE_NUMBEROF_DIRECTORY_ENTRIES) {
+      return nullptr;
+    }
+
+    IMAGE_DATA_DIRECTORY& dirEntry =
+      mPeHeader->OptionalHeader.DataDirectory[aDirectoryIndex];
+    return RVAToPtr<T>(dirEntry.VirtualAddress);
+  }
+
+  // This private overload does not have bounds checks, because we need to be
+  // able to resolve the bounds themselves.
+  template <typename T, typename R>
+  T RVAToPtrUnchecked(R aRva)
+  {
+    return reinterpret_cast<T>(reinterpret_cast<char*>(mMzHeader) + aRva);
+  }
+
+  PIMAGE_RESOURCE_DIRECTORY_ENTRY
+  FindResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel, WORD aId)
+  {
+    // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
+    // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. Since this function
+    // searches by ID, we need to skip past any named entries before iterating.
+    auto dirEnt =
+      reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1) +
+      aCurLevel->NumberOfNamedEntries;
+    for (WORD i = 0; i < aCurLevel->NumberOfIdEntries; ++i) {
+      if (dirEnt[i].Id == aId) {
+        return &dirEnt[i];
+      }
+    }
+
+    return nullptr;
+  }
+
+  PIMAGE_RESOURCE_DIRECTORY_ENTRY
+  FindFirstResourceEntry(PIMAGE_RESOURCE_DIRECTORY aCurLevel)
+  {
+    // Immediately after the IMAGE_RESOURCE_DIRECTORY structure is an array
+    // of IMAGE_RESOURCE_DIRECTORY_ENTRY structures. We just return the first
+    // entry, regardless of whether it is indexed by name or by id.
+    auto dirEnt = reinterpret_cast<PIMAGE_RESOURCE_DIRECTORY_ENTRY>(aCurLevel + 1);
+    WORD numEntries = aCurLevel->NumberOfNamedEntries +
+                      aCurLevel->NumberOfIdEntries;
+    if (!numEntries) {
+      return nullptr;
+    }
+
+    return dirEnt;
+  }
+
+  VS_FIXEDFILEINFO*
+  GetFixedFileInfo(VS_VERSIONINFO_HEADER* aVerInfo)
+  {
+    WORD length = aVerInfo->wLength;
+    WORD offset = sizeof(VS_VERSIONINFO_HEADER);
+    if (!offset) {
+      return nullptr;
+    }
+
+    const wchar_t kVersionInfoKey[] = L"VS_VERSION_INFO";
+    if (::RtlCompareMemory(aVerInfo->szKey, kVersionInfoKey,
+                           ArrayLength(kVersionInfoKey)) !=
+        ArrayLength(kVersionInfoKey)) {
+      return nullptr;
+    }
+
+    uintptr_t base = reinterpret_cast<uintptr_t>(aVerInfo);
+    // Align up to 4-byte boundary
+#pragma warning(suppress: 4146)
+    offset += (-(base + offset) & 3);
+
+    if (offset > length) {
+      return nullptr;
+    }
+
+    auto result = reinterpret_cast<VS_FIXEDFILEINFO*>(base + offset);
+    if (result->dwSignature != 0xFEEF04BD) {
+      return nullptr;
+    }
+
+    return result;
+  }
+
+private:
+  PIMAGE_DOS_HEADER mMzHeader;
+  PIMAGE_NT_HEADERS mPeHeader;
+  void*             mImageLimit;
+};
+
+inline HANDLE
+RtlGetProcessHeap()
+{
+  PTEB teb = ::NtCurrentTeb();
+  PPEB peb = teb->ProcessEnvironmentBlock;
+  return peb->Reserved4[1];
+}
+
+} // namespace nt
+} // namespace mozilla
+
+#endif // mozilla_NativeNt_h
rename from browser/app/ProcThreadAttributes.h
rename to browser/app/winlauncher/ProcThreadAttributes.h
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/moz.build
@@ -0,0 +1,27 @@
+# -*- 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/.
+
+Library('winlauncher')
+
+FORCE_STATIC_LIB = True
+
+UNIFIED_SOURCES += [
+    'DllBlocklistWin.cpp',
+    'LauncherProcessWin.cpp',
+    'LaunchUnelevated.cpp',
+]
+
+OS_LIBS += [
+    'ntdll',
+    'oleaut32',
+    'ole32',
+]
+
+TEST_DIRS += [
+    'test',
+]
+
+DisableStlWrapping()
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/test/TestNativeNt.cpp
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#include "NativeNt.h"
+
+#include <stdio.h>
+
+const wchar_t kNormal[] = L"Foo.dll";
+const wchar_t kHex12[] = L"Foo.ABCDEF012345.dll";
+const wchar_t kHex15[] = L"ABCDEF012345678.dll";
+const wchar_t kHex16[] = L"ABCDEF0123456789.dll";
+const wchar_t kHex17[] = L"ABCDEF0123456789a.dll";
+const wchar_t kHex24[] = L"ABCDEF0123456789cdabef98.dll";
+const wchar_t kHex8[] = L"01234567.dll";
+const wchar_t kNonHex12[] = L"Foo.ABCDEFG12345.dll";
+const wchar_t kHex13[] = L"Foo.ABCDEF0123456.dll";
+const wchar_t kHex11[] = L"Foo.ABCDEF01234.dll";
+const wchar_t kPrefixedHex16[] = L"Pabcdef0123456789.dll";
+
+const char kFailFmt[] = "TEST-FAILED | NativeNt | %s(%s) should have returned %s but did not\n";
+
+#define RUN_TEST(fn, varName, expected) \
+  if (fn(varName) == !expected) { \
+    printf(kFailFmt, #fn, #varName, #expected); \
+    return 1; \
+  }
+
+#define EXPECT_FAIL(fn, varName) \
+  RUN_TEST(fn, varName, false) \
+
+#define EXPECT_SUCCESS(fn, varName) \
+  RUN_TEST(fn, varName, true)
+
+using namespace mozilla::nt;
+
+int main(int argc, char* argv[])
+{
+  UNICODE_STRING normal;
+  ::RtlInitUnicodeString(&normal, kNormal);
+
+  UNICODE_STRING hex12;
+  ::RtlInitUnicodeString(&hex12, kHex12);
+
+  UNICODE_STRING hex16;
+  ::RtlInitUnicodeString(&hex16, kHex16);
+
+  UNICODE_STRING hex24;
+  ::RtlInitUnicodeString(&hex24, kHex24);
+
+  UNICODE_STRING hex8;
+  ::RtlInitUnicodeString(&hex8, kHex8);
+
+  UNICODE_STRING nonHex12;
+  ::RtlInitUnicodeString(&nonHex12, kNonHex12);
+
+  UNICODE_STRING hex13;
+  ::RtlInitUnicodeString(&hex13, kHex13);
+
+  UNICODE_STRING hex11;
+  ::RtlInitUnicodeString(&hex11, kHex11);
+
+  UNICODE_STRING hex15;
+  ::RtlInitUnicodeString(&hex15, kHex15);
+
+  UNICODE_STRING hex17;
+  ::RtlInitUnicodeString(&hex17, kHex17);
+
+  UNICODE_STRING prefixedHex16;
+  ::RtlInitUnicodeString(&prefixedHex16, kPrefixedHex16);
+
+  EXPECT_FAIL(Contains12DigitHexString, normal);
+  EXPECT_SUCCESS(Contains12DigitHexString, hex12);
+  EXPECT_FAIL(Contains12DigitHexString, hex13);
+  EXPECT_FAIL(Contains12DigitHexString, hex11);
+  EXPECT_FAIL(Contains12DigitHexString, hex16);
+  EXPECT_FAIL(Contains12DigitHexString, nonHex12);
+
+  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, normal);
+  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex12);
+  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex24);
+  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex16);
+  EXPECT_SUCCESS(IsFileNameAtLeast16HexDigits, hex17);
+  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex8);
+  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, hex15);
+  EXPECT_FAIL(IsFileNameAtLeast16HexDigits, prefixedHex16);
+
+  if (RtlGetProcessHeap() != ::GetProcessHeap()) {
+    printf("TEST-FAILED | NativeNt | RtlGetProcessHeap() is broken\n");
+    return 1;
+  }
+
+  return 0;
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/app/winlauncher/test/moz.build
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+DisableStlWrapping()
+
+CppUnitTests(['TestNativeNt'])
+
+LOCAL_INCLUDES += [
+    '/browser/app/winlauncher',
+]
+
+OS_LIBS += [
+    'ntdll',
+]
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -2,49 +2,42 @@
 /* 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/. */
 
 /* This content script should work in any browser or iframe and should not
  * depend on the frame being contained in tabbrowser. */
 
 /* eslint-env mozilla/frame-script */
+/* eslint no-unused-vars: ["error", {args: "none"}] */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 // TabChildGlobal
 var global = this;
 
 XPCOMUtils.defineLazyModuleGetters(this, {
+  BlockedSiteContent: "resource:///modules/BlockedSiteContent.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
   ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
   ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
   LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
   LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
   InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
   PluginContent: "resource:///modules/PluginContent.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
   FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
+  NetErrorContent: "resource:///modules/NetErrorContent.jsm",
   PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
-  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   ContextMenu: "resource:///modules/ContextMenu.jsm",
 });
 
-XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
-  return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
-});
-XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
-  return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
-});
-
 XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
   return new ContextMenu(global);
 });
 
 XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
   return new FormSubmitObserver(content, this);
 }, {
   // stub QI
@@ -83,186 +76,47 @@ addEventListener("pageshow", function(ev
 });
 addEventListener("DOMAutoComplete", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 addEventListener("blur", function(event) {
   LoginManagerContent.onUsernameInput(event);
 });
 
-const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
-const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
-
-const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
-const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
-const SEC_ERROR_UNTRUSTED_ISSUER                   = SEC_ERROR_BASE + 20;
-const SEC_ERROR_UNTRUSTED_CERT                     = SEC_ERROR_BASE + 21;
-const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
-const SEC_ERROR_CA_CERT_INVALID                    = SEC_ERROR_BASE + 36;
-const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
-const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
-const SEC_ERROR_REUSED_ISSUER_AND_SERIAL           = SEC_ERROR_BASE + 138;
-const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED  = SEC_ERROR_BASE + 176;
-const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
-const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
-const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT          = MOZILLA_PKIX_ERROR_BASE + 14;
-const MOZILLA_PKIX_ERROR_MITM_DETECTED             = MOZILLA_PKIX_ERROR_BASE + 15;
-
-
-const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
-const SSL_ERROR_SSL_DISABLED  = SSL_ERROR_BASE + 20;
-const SSL_ERROR_SSL2_DISABLED  = SSL_ERROR_BASE + 14;
-
-const PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS = "services.settings.clock_skew_seconds";
-const PREF_SERVICES_SETTINGS_LAST_FETCHED       = "services.settings.last_update_seconds";
-
-const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
-
-
-function getSerializedSecurityInfo(docShell) {
-  let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
-                    .getService(Ci.nsISerializationHelper);
-
-  let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
-  if (!securityInfo) {
-    return "";
-  }
-  securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
-              .QueryInterface(Ci.nsISerializable);
-
-  return serhelper.serializeToString(securityInfo);
-}
-
-function getSiteBlockedErrorDetails(docShell) {
-  let blockedInfo = {};
-  if (docShell.failedChannel) {
-    let classifiedChannel = docShell.failedChannel.
-                            QueryInterface(Ci.nsIClassifiedChannel);
-    if (classifiedChannel) {
-      let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
-
-      let reportUri = httpChannel.URI.clone();
-
-      // Remove the query to avoid leaking sensitive data
-      if (reportUri instanceof Ci.nsIURL) {
-        reportUri = reportUri.mutate()
-                             .setQuery("")
-                             .finalize();
-      }
-
-      blockedInfo = { list: classifiedChannel.matchedList,
-                      provider: classifiedChannel.matchedProvider,
-                      uri: reportUri.asciiSpec };
-    }
-  }
-  return blockedInfo;
-}
-
 var AboutBlockedSiteListener = {
   init(chromeGlobal) {
     addMessageListener("DeceptiveBlockedDetails", this);
     chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
   },
 
   get isBlockedSite() {
     return content.document.documentURI.startsWith("about:blocked");
   },
 
   receiveMessage(msg) {
     if (!this.isBlockedSite) {
       return;
     }
 
-    if (msg.name == "DeceptiveBlockedDetails") {
-      sendAsyncMessage("DeceptiveBlockedDetails:Result", {
-        blockedInfo: getSiteBlockedErrorDetails(docShell),
-      });
-    }
+    BlockedSiteContent.receiveMessage(global, msg);
   },
 
   handleEvent(aEvent) {
     if (!this.isBlockedSite) {
       return;
     }
 
     if (aEvent.type != "AboutBlockedLoaded") {
       return;
     }
 
-    let blockedInfo = getSiteBlockedErrorDetails(docShell);
-    let provider = blockedInfo.provider || "";
-
-    let doc = content.document;
-
-    /**
-    * Set error description link in error details.
-    * For example, the "reported as a deceptive site" link for
-    * blocked phishing pages.
-    */
-    let desc = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".reportURL", "");
-    if (desc) {
-      doc.getElementById("error_desc_link").setAttribute("href", desc + aEvent.detail.url);
-    }
-
-    // Set other links in error details.
-    switch (aEvent.detail.err) {
-      case "malware":
-        doc.getElementById("report_detection").setAttribute("href",
-          (SafeBrowsing.getReportURL("MalwareMistake", blockedInfo) ||
-           "https://www.stopbadware.org/firefox"));
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.stopbadware.org/firefox");
-        break;
-      case "unwanted":
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.google.com/about/unwanted-software-policy.html");
-        break;
-      case "phishing":
-        doc.getElementById("report_detection").setAttribute("href",
-          (SafeBrowsing.getReportURL("PhishMistake", blockedInfo) ||
-           "https://safebrowsing.google.com/safebrowsing/report_error/?tpl=mozilla"));
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.antiphishing.org//");
-        break;
-    }
-
-    // Set the firefox support url.
-    doc.getElementById("firefox_support").setAttribute("href",
-      Services.urlFormatter.formatURLPref("app.support.baseURL") + "phishing-malware");
-
-    // Show safe browsing details on load if the pref is set to true.
-    let showDetails = Services.prefs.getBoolPref("browser.xul.error_pages.show_safe_browsing_details_on_load");
-    if (showDetails) {
-      let details = content.document.getElementById("errorDescriptionContainer");
-      details.removeAttribute("hidden");
-    }
-
-    // Set safe browsing advisory link.
-    let advisoryUrl = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".advisoryURL", "");
-    if (!advisoryUrl) {
-      let el = content.document.getElementById("advisoryDesc");
-      el.remove();
-      return;
-    }
-
-    let advisoryLinkText = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".advisoryName", "");
-    if (!advisoryLinkText) {
-      let el = content.document.getElementById("advisoryDesc");
-      el.remove();
-      return;
-    }
-
-    let anchorEl = content.document.getElementById("advisory_provider");
-    anchorEl.setAttribute("href", advisoryUrl);
-    anchorEl.textContent = advisoryLinkText;
+    BlockedSiteContent.handleEvent(global, aEvent);
   },
 };
+AboutBlockedSiteListener.init(this);
 
 var AboutNetAndCertErrorListener = {
   init(chromeGlobal) {
     addMessageListener("CertErrorDetails", this);
     addMessageListener("Browser:CaptivePortalFreed", this);
     chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
     chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
     chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
@@ -281,467 +135,43 @@ var AboutNetAndCertErrorListener = {
     if (msg.name == "CertErrorDetails") {
       let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
       // We need nsIWebNavigation to access docShell.document.
       frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
       if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
         return;
       }
 
-      this.onCertErrorDetails(msg, frameDocShell);
+      NetErrorContent.onCertErrorDetails(global, msg, frameDocShell);
     } else if (msg.name == "Browser:CaptivePortalFreed") {
       // TODO: This check is not correct for frames.
       if (!this.isAboutCertError(content.document)) {
         return;
       }
 
       this.onCaptivePortalFreed(msg);
     }
   },
 
-  _getCertValidityRange(docShell) {
-    let {securityInfo} = docShell.failedChannel;
-    securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-    let certs = securityInfo.failedCertChain.getEnumerator();
-    let notBefore = 0;
-    let notAfter = Number.MAX_SAFE_INTEGER;
-    while (certs.hasMoreElements()) {
-      let cert = certs.getNext();
-      cert.QueryInterface(Ci.nsIX509Cert);
-      notBefore = Math.max(notBefore, cert.validity.notBefore);
-      notAfter = Math.min(notAfter, cert.validity.notAfter);
-    }
-    // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
-    notBefore /= 1000;
-    notAfter /= 1000;
-    return {notBefore, notAfter};
-  },
-
-  _setTechDetails(input, doc) {
-    // CSS class and error code are set from nsDocShell.
-    let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
-    let cssClass = searchParams.get("s");
-    let error = searchParams.get("e");
-    let technicalInfo = doc.getElementById("badCertTechnicalInfo");
-    technicalInfo.textContent = "";
-
-    let uri = Services.io.newURI(input.data.url);
-    let hostString = uri.host;
-    if (uri.port != 443 && uri.port != -1) {
-      hostString = uri.hostPort;
-    }
-
-    let msg1 = gPipNSSBundle.formatStringFromName("certErrorIntro",
-                                                  [hostString], 1);
-    msg1 += "\n\n";
-
-    if (input.data.certIsUntrusted) {
-      switch (input.data.code) {
-        // We only want to measure MitM rates for now. Treat it as unkown issuer.
-        case MOZILLA_PKIX_ERROR_MITM_DETECTED:
-        case SEC_ERROR_UNKNOWN_ISSUER:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
-          break;
-        case SEC_ERROR_CA_CERT_INVALID:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_CaInvalid") + "\n";
-          break;
-        case SEC_ERROR_UNTRUSTED_ISSUER:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Issuer") + "\n";
-          break;
-        case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SignatureAlgorithmDisabled") + "\n";
-          break;
-        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") + "\n";
-          break;
-        case MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
-          break;
-        default:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
-      }
-    }
-
-    technicalInfo.appendChild(doc.createTextNode(msg1));
-
-    if (input.data.isDomainMismatch) {
-      let subjectAltNames = input.data.certSubjectAltNames.split(",");
-      let numSubjectAltNames = subjectAltNames.length;
-      let msgPrefix = "";
-      if (numSubjectAltNames != 0) {
-        if (numSubjectAltNames == 1) {
-          msgPrefix = gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
-
-          // Let's check if we want to make this a link.
-          let okHost = input.data.certSubjectAltNames;
-          let href = "";
-          let thisHost = doc.location.hostname;
-          let proto = doc.location.protocol + "//";
-          // If okHost is a wildcard domain ("*.example.com") let's
-          // use "www" instead.  "*.example.com" isn't going to
-          // get anyone anywhere useful. bug 432491
-          okHost = okHost.replace(/^\*\./, "www.");
-          /* case #1:
-           * example.com uses an invalid security certificate.
-           *
-           * The certificate is only valid for www.example.com
-           *
-           * Make sure to include the "." ahead of thisHost so that
-           * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
-           *
-           * We'd normally just use a RegExp here except that we lack a
-           * library function to escape them properly (bug 248062), and
-           * domain names are famous for having '.' characters in them,
-           * which would allow spurious and possibly hostile matches.
-           */
-          if (okHost.endsWith("." + thisHost)) {
-            href = proto + okHost;
-          }
-          /* case #2:
-           * browser.garage.maemo.org uses an invalid security certificate.
-           *
-           * The certificate is only valid for garage.maemo.org
-           */
-          if (thisHost.endsWith("." + okHost)) {
-            href = proto + okHost;
-          }
-
-          // If we set a link, meaning there's something helpful for
-          // the user here, expand the section by default
-          if (href && cssClass != "expertBadCert") {
-            doc.getElementById("badCertAdvancedPanel").style.display = "block";
-            if (error == "nssBadCert") {
-              // Toggling the advanced panel must ensure that the debugging
-              // information panel is hidden as well, since it's opened by the
-              // error code link in the advanced panel.
-              var div = doc.getElementById("certificateErrorDebugInformation");
-              div.style.display = "none";
-            }
-          }
-
-          // Set the link if we want it.
-          if (href) {
-            let referrerlink = doc.createElement("a");
-            referrerlink.append(input.data.certSubjectAltNames);
-            referrerlink.title = input.data.certSubjectAltNames;
-            referrerlink.id = "cert_domain_link";
-            referrerlink.href = href;
-            let fragment = BrowserUtils.getLocalizedFragment(doc, msgPrefix,
-                                                             referrerlink);
-            technicalInfo.appendChild(fragment);
-          } else {
-            let fragment = BrowserUtils.getLocalizedFragment(doc,
-                                                             msgPrefix,
-                                                             input.data.certSubjectAltNames);
-            technicalInfo.appendChild(fragment);
-          }
-          technicalInfo.append("\n");
-        } else {
-          let msg = gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
-          for (let i = 0; i < numSubjectAltNames; i++) {
-            msg += subjectAltNames[i];
-            if (i != (numSubjectAltNames - 1)) {
-              msg += ", ";
-            }
-          }
-          technicalInfo.append(msg + "\n");
-        }
-      } else {
-        let msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
-                                                     [hostString], 1);
-        technicalInfo.append(msg + "\n");
-      }
-    }
-
-    if (input.data.isNotValidAtThisTime) {
-      let nowTime = new Date().getTime() * 1000;
-      let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
-      let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
-      let msg = "";
-      if (input.data.validity.notBefore) {
-        if (nowTime > input.data.validity.notAfter) {
-          msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                    [input.data.validity.notAfterLocalTime, now], 2) + "\n";
-        } else {
-          msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
-                                                    [input.data.validity.notBeforeLocalTime, now], 2) + "\n";
-        }
-      } else {
-        // If something goes wrong, we assume the cert expired.
-        msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                  ["", now], 2) + "\n";
-      }
-      technicalInfo.append(msg);
-    }
-    technicalInfo.append("\n");
-
-    // Add link to certificate and error message.
-    let linkPrefix = gPipNSSBundle.GetStringFromName("certErrorCodePrefix3");
-    let detailLink = doc.createElement("a");
-    detailLink.append(input.data.codeString);
-    detailLink.title = input.data.codeString;
-    detailLink.id = "errorCode";
-    let fragment = BrowserUtils.getLocalizedFragment(doc, linkPrefix, detailLink);
-    technicalInfo.appendChild(fragment);
-    var errorCode = doc.getElementById("errorCode");
-    if (errorCode) {
-      errorCode.href = "javascript:void(0)";
-      errorCode.addEventListener("click", () => {
-        let debugInfo = doc.getElementById("certificateErrorDebugInformation");
-        debugInfo.style.display = "block";
-        debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
-      });
-    }
-  },
-
-  onCertErrorDetails(msg, docShell) {
-    let doc = docShell.document;
-
-    let div = doc.getElementById("certificateErrorText");
-    div.textContent = msg.data.info;
-    this._setTechDetails(msg, doc);
-    let learnMoreLink = doc.getElementById("learnMoreLink");
-    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
-
-    switch (msg.data.code) {
-      case SEC_ERROR_UNKNOWN_ISSUER:
-      case MOZILLA_PKIX_ERROR_MITM_DETECTED:
-      case MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
-        learnMoreLink.href = baseURL + "security-error";
-        break;
-
-      // In case the certificate expired we make sure the system clock
-      // matches the remote-settings service (blocklist via Kinto) ping time
-      // and is not before the build date.
-      case SEC_ERROR_EXPIRED_CERTIFICATE:
-      case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-      case SEC_ERROR_OCSP_FUTURE_RESPONSE:
-      case SEC_ERROR_OCSP_OLD_RESPONSE:
-      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
-      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:
-
-        // We check against the remote-settings server time first if available, because that allows us
-        // to give the user an approximation of what the correct time is.
-        let difference = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, 0);
-        let lastFetched = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_LAST_FETCHED, 0) * 1000;
-
-        let now = Date.now();
-        let certRange = this._getCertValidityRange(docShell);
-
-        let approximateDate = now - difference * 1000;
-        // If the difference is more than a day, we last fetched the date in the last 5 days,
-        // and adjusting the date per the interval would make the cert valid, warn the user:
-        if (Math.abs(difference) > 60 * 60 * 24 && (now - lastFetched) <= 60 * 60 * 24 * 5 &&
-            certRange.notBefore < approximateDate && certRange.notAfter > approximateDate) {
-          let formatter = new Services.intl.DateTimeFormat(undefined, {
-            dateStyle: "short"
-          });
-          let systemDate = formatter.format(new Date());
-          // negative difference means local time is behind server time
-          approximateDate = formatter.format(new Date(approximateDate));
-
-          doc.getElementById("wrongSystemTime_URL").textContent = doc.location.hostname;
-          doc.getElementById("wrongSystemTime_systemDate").textContent = systemDate;
-          doc.getElementById("wrongSystemTime_actualDate").textContent = approximateDate;
-
-          doc.getElementById("errorShortDesc").style.display = "none";
-          doc.getElementById("wrongSystemTimePanel").style.display = "block";
-
-        // If there is no clock skew with Kinto servers, check against the build date.
-        // (The Kinto ping could have happened when the time was still right, or not at all)
-        } else {
-          let appBuildID = Services.appinfo.appBuildID;
-
-          let year = parseInt(appBuildID.substr(0, 4), 10);
-          let month = parseInt(appBuildID.substr(4, 2), 10) - 1;
-          let day = parseInt(appBuildID.substr(6, 2), 10);
-
-          let buildDate = new Date(year, month, day);
-          let systemDate = new Date();
-
-          // We don't check the notBefore of the cert with the build date,
-          // as it is of course almost certain that it is now later than the build date,
-          // so we shouldn't exclude the possibility that the cert has become valid
-          // since the build date.
-          if (buildDate > systemDate && new Date(certRange.notAfter) > buildDate) {
-            let formatter = new Services.intl.DateTimeFormat(undefined, {
-              dateStyle: "short"
-            });
-
-            doc.getElementById("wrongSystemTimeWithoutReference_URL")
-              .textContent = doc.location.hostname;
-            doc.getElementById("wrongSystemTimeWithoutReference_systemDate")
-              .textContent = formatter.format(systemDate);
-
-            doc.getElementById("errorShortDesc").style.display = "none";
-            doc.getElementById("wrongSystemTimeWithoutReferencePanel").style.display = "block";
-          }
-        }
-        learnMoreLink.href = baseURL + "time-errors";
-        break;
-    }
-  },
-
   onCaptivePortalFreed(msg) {
     content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
   },
 
   handleEvent(aEvent) {
     // Documents have a null ownerDocument.
     let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
 
     if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
       return;
     }
 
-    switch (aEvent.type) {
-    case "AboutNetErrorLoad":
-      this.onPageLoad(aEvent.originalTarget, doc.defaultView);
-      break;
-    case "AboutNetErrorOpenCaptivePortal":
-      this.openCaptivePortalPage(aEvent);
-      break;
-    case "AboutNetErrorSetAutomatic":
-      this.onSetAutomatic(aEvent);
-      break;
-    case "AboutNetErrorResetPreferences":
-      this.onResetPreferences(aEvent);
-      break;
-    }
-  },
-
-  changedCertPrefs() {
-    let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
-       return prefs.concat(Services.prefs.getChildList(root));
-    }, []);
-    for (let prefName of prefSSLImpact) {
-      if (Services.prefs.prefHasUserValue(prefName)) {
-        return true;
-      }
-    }
-
-    return false;
-  },
-
-   _getErrorMessageFromCode(securityInfo, doc) {
-     let uri = Services.io.newURI(doc.location);
-     let hostString = uri.host;
-     if (uri.port != 443 && uri.port != -1) {
-       hostString = uri.hostPort;
-     }
-
-     let id_str = "";
-     switch (securityInfo.errorCode) {
-       case SSL_ERROR_SSL_DISABLED:
-         id_str = "PSMERR_SSL_Disabled";
-         break;
-       case SSL_ERROR_SSL2_DISABLED:
-         id_str = "PSMERR_SSL2_Disabled";
-         break;
-       case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
-         id_str = "PSMERR_HostReusedIssuerSerial";
-         break;
-     }
-     let nss_error_id_str = securityInfo.errorCodeString;
-     let msg2 = "";
-     if (id_str) {
-       msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
-     } else if (nss_error_id_str) {
-       msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
-     }
-
-     if (!msg2) {
-       // We couldn't get an error message. Use the error string.
-       // Note that this is different from before where we used PR_ErrorToString.
-       msg2 = nss_error_id_str;
-     }
-     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
-                                                  [hostString, msg2], 2);
-
-     if (nss_error_id_str) {
-       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                 [nss_error_id_str], 1) + "\n";
-     }
-     return msg;
-   },
-
-  onPageLoad(originalTarget, win) {
-    // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
-    const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
-
-    let hideAddExceptionButton = false;
-
-    if (this.isAboutCertError(win.document)) {
-      ClickEventHandler.onCertError(originalTarget, win);
-      hideAddExceptionButton =
-        Services.prefs.getBoolPref("security.certerror.hideAddException", false);
-    }
-    if (this.isAboutNetError(win.document)) {
-      let docShell = win.document.docShell;
-      if (docShell) {
-        let {securityInfo} = docShell.failedChannel;
-        // We don't have a securityInfo when this is for example a DNS error.
-        if (securityInfo) {
-          securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-          let msg = this._getErrorMessageFromCode(securityInfo,
-                                                  win.document);
-          let id = win.document.getElementById("errorShortDescText");
-          id.textContent = msg;
-        }
-      }
-    }
-
-    let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
-    win.dispatchEvent(new win.CustomEvent("AboutNetErrorOptions", {
-      detail: JSON.stringify({
-        enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
-        changedCertPrefs: this.changedCertPrefs(),
-        automatic,
-        hideAddExceptionButton,
-      })
-    }));
-
-    sendAsyncMessage("Browser:SSLErrorReportTelemetry",
-                     {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
-  },
-
-  openCaptivePortalPage(evt) {
-    sendAsyncMessage("Browser:OpenCaptivePortalPage");
-  },
-
-
-  onResetPreferences(evt) {
-    sendAsyncMessage("Browser:ResetSSLPreferences");
-  },
-
-  onSetAutomatic(evt) {
-    sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
-      automatic: evt.detail
-    });
-
-    // If we're enabling reports, send a report for this failure.
-    if (evt.detail) {
-      let win = evt.originalTarget.ownerGlobal;
-      let docShell = win.document.docShell;
-
-      let {securityInfo} = docShell.failedChannel;
-      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-      let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
-
-      let errorReporter = Cc["@mozilla.org/securityreporter;1"]
-                            .getService(Ci.nsISecurityReporter);
-      errorReporter.reportTLSError(securityInfo, host, port);
-    }
+    NetErrorContent.handleEvent(global, aEvent);
   },
 };
-
 AboutNetAndCertErrorListener.init(this);
-AboutBlockedSiteListener.init(this);
 
 var ClickEventHandler = {
   init: function init() {
     Services.els.addSystemEventListener(global, "click", this, true);
   },
 
   handleEvent(event) {
     if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
@@ -752,23 +182,23 @@ var ClickEventHandler = {
     let ownerDoc = originalTarget.ownerDocument;
     if (!ownerDoc) {
       return;
     }
 
     // Handle click events from about pages
     if (event.button == 0) {
       if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        this.onCertError(originalTarget, ownerDoc.defaultView);
+        NetErrorContent.onCertError(global, originalTarget, ownerDoc.defaultView);
         return;
       } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        this.onAboutBlocked(originalTarget, ownerDoc);
+        BlockedSiteContent.onAboutBlocked(global, originalTarget, ownerDoc);
         return;
       } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        this.onAboutNetError(event, ownerDoc.documentURI);
+        NetErrorContent.onAboutNetError(global, event, ownerDoc.documentURI);
         return;
       }
     }
 
     let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
 
     // get referrer attribute from clicked link and parse it
     // if per element referrer is enabled, the element referrer overrules
@@ -833,68 +263,16 @@ var ClickEventHandler = {
     }
 
     // This might be middle mouse navigation.
     if (event.button == 1) {
       sendAsyncMessage("Content:Click", json);
     }
   },
 
-  onCertError(targetElement, win) {
-    let docShell = win.document.docShell;
-    sendAsyncMessage("Browser:CertExceptionError", {
-      frameId: WebNavigationFrames.getFrameId(win),
-      location: win.document.location.href,
-      elementId: targetElement.getAttribute("id"),
-      isTopFrame: (win.parent === win),
-      securityInfoAsString: getSerializedSecurityInfo(docShell),
-    });
-  },
-
-  onAboutBlocked(targetElement, ownerDoc) {
-    var reason = "phishing";
-    if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
-      reason = "malware";
-    } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
-      reason = "unwanted";
-    } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
-      reason = "harmful";
-    }
-
-    let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                       .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIDocShell);
-
-    sendAsyncMessage("Browser:SiteBlockedError", {
-      location: ownerDoc.location.href,
-      reason,
-      elementId: targetElement.getAttribute("id"),
-      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
-      blockedInfo: getSiteBlockedErrorDetails(docShell),
-    });
-  },
-
-  onAboutNetError(event, documentURI) {
-    let elmId = event.originalTarget.getAttribute("id");
-    if (elmId == "returnButton") {
-      sendAsyncMessage("Browser:SSLErrorGoBack", {});
-      return;
-    }
-    if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
-      return;
-    }
-    // browser front end will handle clearing offline mode and refreshing
-    // the page *if* we're in offline mode now. Otherwise let the error page
-    // handle the click.
-    if (Services.io.offline) {
-      event.preventDefault();
-      sendAsyncMessage("Browser:EnableOnlineMode", {});
-    }
-  },
-
   /**
    * Extracts linkNode and href for the current click target.
    *
    * @param event
    *        The click event.
    * @return [href, linkNode, linkPrincipal].
    *
    * @note linkNode will be null if the click wasn't on an anchor
@@ -942,17 +320,17 @@ var ClickEventHandler = {
   }
 };
 ClickEventHandler.init();
 
 ContentLinkHandler.init(this);
 ContentMetaHandler.init(this);
 
 // TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
-var pluginContent = new PluginContent(global);
+void new PluginContent(global);
 
 addEventListener("DOMWindowFocus", function(event) {
   sendAsyncMessage("DOMWindowFocus", {});
 }, false);
 
 // We use this shim so that ContentWebRTC.jsm will not be loaded until
 // it is actually needed.
 var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
--- a/browser/components/customizableui/CustomizeMode.jsm
+++ b/browser/components/customizableui/CustomizeMode.jsm
@@ -22,17 +22,17 @@ const kDownloadAutohidePanelId = "downlo
 const kDownloadAutoHidePref = "browser.download.autohideButton";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource:///modules/CustomizableUI.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 
-Cu.importGlobalProperties(["CSS"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["CSS"]);
 
 ChromeUtils.defineModuleGetter(this, "DragPositionManager",
                                "resource:///modules/DragPositionManager.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUITelemetry",
                                "resource:///modules/BrowserUITelemetry.jsm");
 ChromeUtils.defineModuleGetter(this, "BrowserUtils",
                                "resource://gre/modules/BrowserUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
--- a/browser/components/enterprisepolicies/helpers/ProxyPolicies.jsm
+++ b/browser/components/enterprisepolicies/helpers/ProxyPolicies.jsm
@@ -1,17 +1,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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const PREF_LOGLEVEL = "browser.policies.loglevel";
 
 XPCOMUtils.defineLazyGetter(this, "log", () => {
   let { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   return new ConsoleAPI({
     prefix: "ProxyPolicies.jsm",
     // tip: set maxLogLevel to "debug" and use log.debug() to create detailed
--- a/browser/components/extensions/parent/ext-browserAction.js
+++ b/browser/components/extensions/parent/ext-browserAction.js
@@ -20,17 +20,17 @@ var {
 
 ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
 
 var {
   IconDetails,
   StartupCache,
 } = ExtensionParent;
 
-Cu.importGlobalProperties(["InspectorUtils"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]);
 
 const POPUP_PRELOAD_TIMEOUT_MS = 200;
 const POPUP_OPEN_MS_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_OPEN_MS";
 const POPUP_RESULT_HISTOGRAM = "WEBEXT_BROWSERACTION_POPUP_PRELOAD_RESULT_COUNT";
 
 var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // WeakMap[Extension -> BrowserAction]
--- a/browser/components/extensions/parent/ext-geckoProfiler.js
+++ b/browser/components/extensions/parent/ext-geckoProfiler.js
@@ -1,14 +1,14 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-Cu.importGlobalProperties(["TextEncoder", "TextDecoder"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["TextEncoder", "TextDecoder"]);
 
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "Subprocess", "resource://gre/modules/Subprocess.jsm");
 
 const PREF_ASYNC_STACK = "javascript.options.asyncstack";
 const PREF_GET_SYMBOL_RULES = "extensions.geckoProfiler.getSymbolRules";
 
 const ASYNC_STACKS_ENABLED = Services.prefs.getBoolPref(PREF_ASYNC_STACK, false);
--- a/browser/components/migration/360seProfileMigrator.js
+++ b/browser/components/migration/360seProfileMigrator.js
@@ -11,17 +11,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/osfile.jsm");
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Sqlite",
                                "resource://gre/modules/Sqlite.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const kBookmarksFileName = "360sefav.db";
 
 function copyToTempUTF8File(file, charset) {
   let inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
                       .createInstance(Ci.nsIFileInputStream);
   inputStream.init(file, -1, -1, 0);
   let inputStr = NetUtil.readInputStreamToString(
--- a/browser/components/migration/AutoMigrate.jsm
+++ b/browser/components/migration/AutoMigrate.jsm
@@ -40,17 +40,17 @@ ChromeUtils.defineModuleGetter(this, "Pl
 ChromeUtils.defineModuleGetter(this, "TelemetryStopwatch",
                                "resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function() {
   const kBrandBundle = "chrome://branding/locale/brand.properties";
   return Services.strings.createBundle(kBrandBundle);
 });
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 XPCOMUtils.defineLazyGetter(this, "kUndoStateFullPath", function() {
   return OS.Path.join(OS.Constants.Path.profileDir, "initialMigrationMetadata.jsonlz4");
 });
 
 const AutoMigrate = {
   get resourceTypesToUse() {
     let {BOOKMARKS, HISTORY, PASSWORDS} = Ci.nsIBrowserProfileMigrator;
--- a/browser/components/migration/EdgeProfileMigrator.js
+++ b/browser/components/migration/EdgeProfileMigrator.js
@@ -10,17 +10,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 ChromeUtils.import("resource:///modules/MSMigrationUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "ESEDBReader",
                                "resource:///modules/ESEDBReader.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const kEdgeRegistryRoot = "SOFTWARE\\Classes\\Local Settings\\Software\\" +
   "Microsoft\\Windows\\CurrentVersion\\AppContainer\\Storage\\" +
   "microsoft.microsoftedge_8wekyb3d8bbwe\\MicrosoftEdge";
 const kEdgeDatabasePath = "AC\\MicrosoftEdge\\User\\Default\\DataStore\\Data\\";
 
 XPCOMUtils.defineLazyGetter(this, "gEdgeDatabase", function() {
   let edgeDir = MSMigrationUtils.getEdgeLocalDataFolder();
--- a/browser/components/migration/IEProfileMigrator.js
+++ b/browser/components/migration/IEProfileMigrator.js
@@ -16,17 +16,17 @@ ChromeUtils.import("resource:///modules/
 
 ChromeUtils.defineModuleGetter(this, "ctypes",
                                "resource://gre/modules/ctypes.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "OSCrypto",
                                "resource://gre/modules/OSCrypto.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 // Resources
 
 function History() {
 }
 
 History.prototype = {
   type: MigrationUtils.resourceTypes.HISTORY,
--- a/browser/components/migration/MSMigrationUtils.jsm
+++ b/browser/components/migration/MSMigrationUtils.jsm
@@ -6,17 +6,17 @@
 
 var EXPORTED_SYMBOLS = ["MSMigrationUtils"];
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 
-Cu.importGlobalProperties(["FileReader"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["FileReader"]);
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "WindowsRegistry",
                                "resource://gre/modules/WindowsRegistry.jsm");
 ChromeUtils.defineModuleGetter(this, "ctypes",
                                "resource://gre/modules/ctypes.jsm");
 
@@ -30,17 +30,17 @@ const INTERNET_EXPLORER_EDGE_GUID = [0x3
                                      0x553BDD88];
 const RESULT_SUCCESS = 0;
 const VAULT_ENUMERATE_ALL_ITEMS = 512;
 const WEB_CREDENTIALS_VAULT_ID = [0x4BF4C442,
                                   0x41A09B8A,
                                   0x4ADD80B3,
                                   0x28DB4D70];
 
-Cu.importGlobalProperties(["File"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["File"]);
 
 const wintypes = {
   BOOL: ctypes.int,
   DWORD: ctypes.uint32_t,
   DWORDLONG: ctypes.uint64_t,
   CHAR: ctypes.char,
   PCHAR: ctypes.char.ptr,
   LPCWSTR: ctypes.char16_t.ptr,
--- a/browser/components/migration/MigrationUtils.jsm
+++ b/browser/components/migration/MigrationUtils.jsm
@@ -9,17 +9,17 @@ var EXPORTED_SYMBOLS = ["MigrationUtils"
 const TOPIC_WILL_IMPORT_BOOKMARKS = "initial-migration-will-import-default-bookmarks";
 const TOPIC_DID_IMPORT_BOOKMARKS = "initial-migration-did-import-default-bookmarks";
 const TOPIC_PLACES_DEFAULTS_FINISHED = "places-browser-init-complete";
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 ChromeUtils.defineModuleGetter(this, "AutoMigrate",
                                "resource:///modules/AutoMigrate.jsm");
 ChromeUtils.defineModuleGetter(this, "BookmarkHTMLUtils",
                                "resource://gre/modules/BookmarkHTMLUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "LoginHelper",
                                "resource://gre/modules/LoginHelper.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -13,17 +13,17 @@ ChromeUtils.import("resource:///modules/
 
 ChromeUtils.defineModuleGetter(this, "PropertyListUtils",
                                "resource://gre/modules/PropertyListUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
                                "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "FormHistory",
                                "resource://gre/modules/FormHistory.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 function Bookmarks(aBookmarksFile) {
   this._file = aBookmarksFile;
 }
 Bookmarks.prototype = {
   type: MigrationUtils.resourceTypes.BOOKMARKS,
 
   migrate: function B_migrate(aCallback) {
--- a/browser/components/migration/tests/unit/head_migration.js
+++ b/browser/components/migration/tests/unit/head_migration.js
@@ -1,25 +1,25 @@
 "use strict";
 
-/* exported gProfD, promiseMigration, registerFakePath */
-
-Cu.importGlobalProperties([ "URL" ]);
+/* exported gProfD, promiseMigration, registerFakePath, URL */
 
 ChromeUtils.import("resource:///modules/MigrationUtils.jsm");
 ChromeUtils.import("resource://gre/modules/LoginHelper.jsm");
 ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
 ChromeUtils.import("resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 ChromeUtils.import("resource://testing-common/PlacesTestUtils.jsm");
 
+XPCOMUtils.defineLazyGlobalGetters(this, [ "URL" ]);
+
 // eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "FileUtils",
                                "resource://gre/modules/FileUtils.jsm");
 // eslint-disable-next-line no-unused-vars
 ChromeUtils.defineModuleGetter(this, "Sqlite",
                                "resource://gre/modules/Sqlite.jsm");
 
 // Initialize profile.
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -71,17 +71,17 @@ ChromeUtils.import("resource://gre/modul
   // The window becomes visible after OnStopRequest, so make this happen now.
   win.stop();
 
   let { TelemetryTimestamps } =
     ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", {});
   TelemetryTimestamps.add("blankWindowShown");
 })();
 
-Cu.importGlobalProperties(["fetch"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 XPCOMUtils.defineLazyServiceGetters(this, {
   WindowsUIUtils: ["@mozilla.org/windows-ui-utils;1", "nsIWindowsUIUtils"],
   aboutNewTabService: ["@mozilla.org/browser/aboutnewtab-service;1", "nsIAboutNewTabService"]
 });
 XPCOMUtils.defineLazyGetter(this, "WeaveService", () =>
   Cc["@mozilla.org/weave/service;1"].getService().wrappedJSObject
 );
--- a/browser/components/places/PlacesUIUtils.jsm
+++ b/browser/components/places/PlacesUIUtils.jsm
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var EXPORTED_SYMBOLS = ["PlacesUIUtils"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
-Cu.importGlobalProperties(["Element"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["Element"]);
 
 XPCOMUtils.defineLazyModuleGetters(this, {
   AppConstants: "resource://gre/modules/AppConstants.jsm",
   BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
   OpenInTabsUtils: "resource:///modules/OpenInTabsUtils.jsm",
   PlacesTransactions: "resource://gre/modules/PlacesTransactions.jsm",
   PlacesUtils: "resource://gre/modules/PlacesUtils.jsm",
   PluralForm: "resource://gre/modules/PluralForm.jsm",
--- a/browser/components/places/tests/unit/head_bookmarks.js
+++ b/browser/components/places/tests/unit/head_bookmarks.js
@@ -10,20 +10,18 @@ ChromeUtils.import("resource://gre/modul
 var commonFile = do_get_file("../../../../../toolkit/components/places/tests/head_common.js", false);
 if (commonFile) {
   let uri = Services.io.newFileURI(commonFile);
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
-XPCOMUtils.defineLazyGetter(this, "PlacesUIUtils", function() {
-  ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
-  return PlacesUIUtils;
-});
+ChromeUtils.defineModuleGetter(this, "PlacesUIUtils",
+                               "resource:///modules/PlacesUIUtils.jsm");
 
 // Needed by some test that relies on having an app registered.
 ChromeUtils.import("resource://testing-common/AppInfo.jsm", this);
 updateAppInfo({
   name: "PlacesTest",
   ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}",
   version: "1",
   platformVersion: "",
--- a/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
+++ b/browser/components/places/tests/unit/test_PUIU_batchUpdatesForNode.js
@@ -1,18 +1,16 @@
 // ================================================
 // Load mocking/stubbing library, sinon
 // docs: http://sinonjs.org/releases/v2.3.2/
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 Services.scriptloader.loadSubScript("resource://testing-common/sinon-2.3.2.js", this);
 /* globals sinon */
 // ================================================
 
-ChromeUtils.import("resource:///modules/PlacesUIUtils.jsm");
-
 /* eslint-disable mozilla/use-chromeutils-generateqi */
 
 add_task(async function test_no_result_node() {
   let functionSpy = sinon.stub().returns(Promise.resolve());
 
   await PlacesUIUtils.batchUpdatesForNode(null, 1, functionSpy);
 
   Assert.ok(functionSpy.calledOnce,
--- a/browser/components/translation/BingTranslator.jsm
+++ b/browser/components/translation/BingTranslator.jsm
@@ -6,18 +6,19 @@
 
 var EXPORTED_SYMBOLS = [ "BingTranslator" ];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.import("resource://services-common/async.js");
 ChromeUtils.import("resource://gre/modules/Http.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 // The maximum amount of net data allowed per request on Bing's API.
 const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere
                                // close to that is refused by the service.
 
 // The maximum number of chunks allowed to be translated in a single
 // request.
 const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000.
--- a/browser/components/translation/TranslationDocument.jsm
+++ b/browser/components/translation/TranslationDocument.jsm
@@ -2,17 +2,18 @@
  * 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/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = [ "TranslationDocument" ];
 
 ChromeUtils.import("resource://services-common/async.js");
-Cu.importGlobalProperties(["DOMParser"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ["DOMParser"]);
 
 /**
  * This class represents a document that is being translated,
  * and it is responsible for parsing the document,
  * generating the data structures translation (the list of
  * translation items and roots), and managing the original
  * and translated texts on the translation items.
  *
--- a/browser/components/translation/YandexTranslator.jsm
+++ b/browser/components/translation/YandexTranslator.jsm
@@ -6,18 +6,19 @@
 
 var EXPORTED_SYMBOLS = [ "YandexTranslator" ];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
 ChromeUtils.import("resource://services-common/async.js");
 ChromeUtils.import("resource://gre/modules/Http.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 // The maximum amount of net data allowed per request on Bing's API.
 const MAX_REQUEST_DATA = 5000; // Documentation says 10000 but anywhere
                                // close to that is refused by the service.
 
 // The maximum number of chunks allowed to be translated in a single
 // request.
 const MAX_REQUEST_CHUNKS = 1000; // Documentation says 2000.
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -7,17 +7,17 @@
 var EXPORTED_SYMBOLS = ["UITour"];
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/TelemetryController.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 ChromeUtils.defineModuleGetter(this, "BrowserUITelemetry",
   "resource:///modules/BrowserUITelemetry.jsm");
 ChromeUtils.defineModuleGetter(this, "CustomizableUI",
   "resource:///modules/CustomizableUI.jsm");
 ChromeUtils.defineModuleGetter(this, "FxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 ChromeUtils.defineModuleGetter(this, "LightweightThemeManager",
--- a/browser/extensions/activity-stream/bootstrap.js
+++ b/browser/extensions/activity-stream/bootstrap.js
@@ -1,15 +1,15 @@
 /* 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/. */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.importGlobalProperties(["fetch"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 ChromeUtils.defineModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "resProto",
                                    "@mozilla.org/network/protocol;1?name=resource",
                                    "nsISubstitutingProtocolHandler");
 
--- a/browser/extensions/activity-stream/lib/ASRouter.jsm
+++ b/browser/extensions/activity-stream/lib/ASRouter.jsm
@@ -1,15 +1,16 @@
 /* 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/. */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-Cu.importGlobalProperties(["fetch"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 const {ASRouterActions: ra} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
 const {OnboardingMessageProvider} = ChromeUtils.import("resource://activity-stream/lib/OnboardingMessageProvider.jsm", {});
 
 ChromeUtils.defineModuleGetter(this, "ASRouterTargeting",
   "resource://activity-stream/lib/ASRouterTargeting.jsm");
 
 const INCOMING_MESSAGE_NAME = "ASRouter:child-to-parent";
 const OUTGOING_MESSAGE_NAME = "ASRouter:parent-to-child";
--- a/browser/extensions/activity-stream/lib/AboutPreferences.jsm
+++ b/browser/extensions/activity-stream/lib/AboutPreferences.jsm
@@ -1,18 +1,20 @@
 /* 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/. */
 "use strict";
 
-Cu.importGlobalProperties(["fetch"]);
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
 
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
+
 const PREFERENCES_LOADED_EVENT = "home-pane-loaded";
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
 // These "section" objects are formatted in a way to be similar to the ones from
 // SectionsManager to construct the preferences view.
 const PREFS_BEFORE_SECTIONS = [
   {
     id: "search",
--- a/browser/extensions/activity-stream/lib/DownloadsManager.jsm
+++ b/browser/extensions/activity-stream/lib/DownloadsManager.jsm
@@ -1,10 +1,11 @@
 ChromeUtils.import("resource://gre/modules/Services.jsm");
-Cu.importGlobalProperties(["URL"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
 
 ChromeUtils.defineModuleGetter(this, "DownloadsViewUI",
   "resource:///modules/DownloadsViewUI.jsm");
 ChromeUtils.defineModuleGetter(this, "DownloadsCommon",
   "resource:///modules/DownloadsCommon.jsm");
 
--- a/browser/extensions/activity-stream/lib/FaviconFeed.jsm
+++ b/browser/extensions/activity-stream/lib/FaviconFeed.jsm
@@ -1,16 +1,16 @@
 /* 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/. */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["fetch", "URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 const {actionTypes: at} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
 const {PersistentCache} = ChromeUtils.import("resource://activity-stream/lib/PersistentCache.jsm", {});
 const {getDomain} = ChromeUtils.import("resource://activity-stream/lib/TippyTopProvider.jsm", {});
 
 ChromeUtils.defineModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "Services",
--- a/browser/extensions/activity-stream/lib/ShortURL.jsm
+++ b/browser/extensions/activity-stream/lib/ShortURL.jsm
@@ -1,14 +1,14 @@
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "IDNService", "@mozilla.org/network/idn-service;1", "nsIIDNService");
 
-Cu.importGlobalProperties(["URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 /**
  * Properly convert internationalized domain names.
  * @param {string} host Domain hostname.
  * @returns {string} Hostname suitable to be displayed.
  */
 function handleIDNHost(hostname) {
   try {
--- a/browser/extensions/activity-stream/lib/TippyTopProvider.jsm
+++ b/browser/extensions/activity-stream/lib/TippyTopProvider.jsm
@@ -1,13 +1,15 @@
 /* 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/. */
 
-Cu.importGlobalProperties(["fetch", "URL"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch", "URL"]);
 
 const TIPPYTOP_JSON_PATH = "resource://activity-stream/data/content/tippytop/top_sites.json";
 const TIPPYTOP_URL_PREFIX = "resource://activity-stream/data/content/tippytop/images/";
 
 function getDomain(url) {
   let domain;
   try {
     domain = new URL(url).hostname;
--- a/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
+++ b/browser/extensions/activity-stream/lib/TopStoriesFeed.jsm
@@ -1,17 +1,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/. */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/NewTabUtils.jsm");
-Cu.importGlobalProperties(["fetch"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 const {actionTypes: at, actionCreators: ac} = ChromeUtils.import("resource://activity-stream/common/Actions.jsm", {});
 const {Prefs} = ChromeUtils.import("resource://activity-stream/lib/ActivityStreamPrefs.jsm", {});
 const {shortURL} = ChromeUtils.import("resource://activity-stream/lib/ShortURL.jsm", {});
 const {SectionsManager} = ChromeUtils.import("resource://activity-stream/lib/SectionsManager.jsm", {});
 const {UserDomainAffinityProvider} = ChromeUtils.import("resource://activity-stream/lib/UserDomainAffinityProvider.jsm", {});
 const {PersistentCache} = ChromeUtils.import("resource://activity-stream/lib/PersistentCache.jsm", {});
 
--- a/browser/extensions/asan-reporter/bootstrap.js
+++ b/browser/extensions/asan-reporter/bootstrap.js
@@ -9,17 +9,17 @@ ChromeUtils.import("resource://gre/modul
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Log.jsm");
 ChromeUtils.import("resource://gre/modules/Preferences.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
-Cu.importGlobalProperties(["TextDecoder", "XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["TextDecoder", "XMLHttpRequest"]);
 
 // Define our prefs
 const PREF_CLIENT_ID = "asanreporter.clientid";
 const PREF_API_URL = "asanreporter.apiurl";
 const PREF_AUTH_TOKEN = "asanreporter.authtoken";
 const PREF_LOG_LEVEL = "asanreporter.loglevel";
 
 // Setup logging
--- a/browser/extensions/followonsearch/content/followonsearch-fs.js
+++ b/browser/extensions/followonsearch/content/followonsearch-fs.js
@@ -2,17 +2,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/. */
 
 /* eslint-env mozilla/frame-script */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.importGlobalProperties(["URLSearchParams"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["URLSearchParams"]);
 
 const kExtensionID = "followonsearch@mozilla.com";
 const kSaveTelemetryMsg = `${kExtensionID}:save-telemetry`;
 const kShutdownMsg = `${kExtensionID}:shutdown`;
 const kLastSearchQueueDepth = 10;
 
 /**
  * A map of search domains with their expected codes.
--- a/browser/extensions/mortar/host/common/ppapi-runtime.jsm
+++ b/browser/extensions/mortar/host/common/ppapi-runtime.jsm
@@ -3,18 +3,19 @@
 /* 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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/ctypes.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://ppapi.js/opengles2-utils.jsm");
-Cu.importGlobalProperties(['URL']);
+XPCOMUtils.defineLazyGlobalGetters(this, ['URL']);
 
 const PP_OK = 0;
 const PP_OK_COMPLETIONPENDING = -1;
 const PP_ERROR_FAILED = -2;
 const PP_ERROR_ABORTED = -3;
 const PP_ERROR_BADARGUMENT = -4;
 const PP_ERROR_BADRESOURCE = -5;
 const PP_ERROR_NOINTERFACE = -6;
--- a/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+++ b/browser/extensions/pdfjs/content/PdfStreamConverter.jsm
@@ -37,17 +37,17 @@ ChromeUtils.defineModuleGetter(this, "Pr
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PdfJsTelemetry",
   "resource://pdf.js/PdfJsTelemetry.jsm");
 
 ChromeUtils.defineModuleGetter(this, "PdfjsContentUtils",
   "resource://pdf.js/PdfjsContentUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 var Svc = {};
 XPCOMUtils.defineLazyServiceGetter(Svc, "mime",
                                    "@mozilla.org/mime;1",
                                    "nsIMIMEService");
 
 function getBoolPref(pref, def) {
   try {
--- a/browser/extensions/pocket/content/pktApi.jsm
+++ b/browser/extensions/pocket/content/pktApi.jsm
@@ -42,17 +42,17 @@
  *      usedTags:         All used tags from within the extension sorted by recency
  */
 
 var EXPORTED_SYMBOLS = ["pktApi"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 var pktApi = (function() {
 
     /**
      * Configuration
      */
 
     // Base url for all api calls
copy from browser/base/content/content.js
copy to browser/modules/BlockedSiteContent.jsm
--- a/browser/base/content/content.js
+++ b/browser/modules/BlockedSiteContent.jsm
@@ -1,140 +1,20 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* This content script should work in any browser or iframe and should not
- * depend on the frame being contained in tabbrowser. */
-
-/* eslint-env mozilla/frame-script */
-
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-// TabChildGlobal
-var global = this;
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
-  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
-  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
-  LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
-  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
-  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
-  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
-  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
-
-XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
-  return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
-});
-XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
-  return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
-});
-
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
-  return new FormSubmitObserver(content, this);
-}, {
-  // stub QI
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
-});
-
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
-Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
-
-addMessageListener("PageInfo:getData", PageInfoListener);
+var EXPORTED_SYMBOLS = ["BlockedSiteContent"];
 
-addMessageListener("RemoteLogins:fillForm", function(message) {
-  // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
-  LoginManagerContent.receiveMessage(message, content);
-});
-addEventListener("DOMFormHasPassword", function(event) {
-  LoginManagerContent.onDOMFormHasPassword(event, content);
-  let formLike = LoginFormFactory.createFromForm(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMInputPasswordAdded", function(event) {
-  LoginManagerContent.onDOMInputPasswordAdded(event, content);
-  let formLike = LoginFormFactory.createFromField(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("pageshow", function(event) {
-  LoginManagerContent.onPageShow(event, content);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-addEventListener("blur", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-
-const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
-const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
-
-const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
-const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
-const SEC_ERROR_UNTRUSTED_ISSUER                   = SEC_ERROR_BASE + 20;
-const SEC_ERROR_UNTRUSTED_CERT                     = SEC_ERROR_BASE + 21;
-const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
-const SEC_ERROR_CA_CERT_INVALID                    = SEC_ERROR_BASE + 36;
-const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
-const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
-const SEC_ERROR_REUSED_ISSUER_AND_SERIAL           = SEC_ERROR_BASE + 138;
-const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED  = SEC_ERROR_BASE + 176;
-const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
-const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
-const MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT          = MOZILLA_PKIX_ERROR_BASE + 14;
-const MOZILLA_PKIX_ERROR_MITM_DETECTED             = MOZILLA_PKIX_ERROR_BASE + 15;
-
-
-const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
-const SSL_ERROR_SSL_DISABLED  = SSL_ERROR_BASE + 20;
-const SSL_ERROR_SSL2_DISABLED  = SSL_ERROR_BASE + 14;
-
-const PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS = "services.settings.clock_skew_seconds";
-const PREF_SERVICES_SETTINGS_LAST_FETCHED       = "services.settings.last_update_seconds";
-
-const PREF_SSL_IMPACT_ROOTS = ["security.tls.version.", "security.ssl3."];
-
-
-function getSerializedSecurityInfo(docShell) {
-  let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
-                    .getService(Ci.nsISerializationHelper);
-
-  let securityInfo = docShell.failedChannel && docShell.failedChannel.securityInfo;
-  if (!securityInfo) {
-    return "";
-  }
-  securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
-              .QueryInterface(Ci.nsISerializable);
-
-  return serhelper.serializeToString(securityInfo);
-}
+ChromeUtils.defineModuleGetter(this, "SafeBrowsing",
+                               "resource://gre/modules/SafeBrowsing.jsm");
 
 function getSiteBlockedErrorDetails(docShell) {
   let blockedInfo = {};
   if (docShell.failedChannel) {
     let classifiedChannel = docShell.failedChannel.
                             QueryInterface(Ci.nsIClassifiedChannel);
     if (classifiedChannel) {
       let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
@@ -151,48 +31,33 @@ function getSiteBlockedErrorDetails(docS
       blockedInfo = { list: classifiedChannel.matchedList,
                       provider: classifiedChannel.matchedProvider,
                       uri: reportUri.asciiSpec };
     }
   }
   return blockedInfo;
 }
 
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
-
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
+var BlockedSiteContent = {
+  receiveMessage(global, msg) {
     if (msg.name == "DeceptiveBlockedDetails") {
-      sendAsyncMessage("DeceptiveBlockedDetails:Result", {
-        blockedInfo: getSiteBlockedErrorDetails(docShell),
+      global.sendAsyncMessage("DeceptiveBlockedDetails:Result", {
+        blockedInfo: getSiteBlockedErrorDetails(global.docShell),
       });
     }
   },
 
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
+  handleEvent(global, aEvent) {
     if (aEvent.type != "AboutBlockedLoaded") {
       return;
     }
 
-    let blockedInfo = getSiteBlockedErrorDetails(docShell);
+    let {content} = global;
+
+    let blockedInfo = getSiteBlockedErrorDetails(global.docShell);
     let provider = blockedInfo.provider || "";
 
     let doc = content.document;
 
     /**
     * Set error description link in error details.
     * For example, the "reported as a deceptive site" link for
     * blocked phishing pages.
@@ -252,862 +117,32 @@ var AboutBlockedSiteListener = {
       el.remove();
       return;
     }
 
     let anchorEl = content.document.getElementById("advisory_provider");
     anchorEl.setAttribute("href", advisoryUrl);
     anchorEl.textContent = advisoryLinkText;
   },
-};
 
-var AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-  },
-
-  isAboutNetError(doc) {
-    return doc.documentURI.startsWith("about:neterror");
-  },
-
-  isAboutCertError(doc) {
-    return doc.documentURI.startsWith("about:certerror");
-  },
-
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
-
-      this.onCertErrorDetails(msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
-  _getCertValidityRange(docShell) {
-    let {securityInfo} = docShell.failedChannel;
-    securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-    let certs = securityInfo.failedCertChain.getEnumerator();
-    let notBefore = 0;
-    let notAfter = Number.MAX_SAFE_INTEGER;
-    while (certs.hasMoreElements()) {
-      let cert = certs.getNext();
-      cert.QueryInterface(Ci.nsIX509Cert);
-      notBefore = Math.max(notBefore, cert.validity.notBefore);
-      notAfter = Math.min(notAfter, cert.validity.notAfter);
-    }
-    // nsIX509Cert reports in PR_Date terms, which uses microseconds. Convert:
-    notBefore /= 1000;
-    notAfter /= 1000;
-    return {notBefore, notAfter};
-  },
-
-  _setTechDetails(input, doc) {
-    // CSS class and error code are set from nsDocShell.
-    let searchParams = new URLSearchParams(doc.documentURI.split("?")[1]);
-    let cssClass = searchParams.get("s");
-    let error = searchParams.get("e");
-    let technicalInfo = doc.getElementById("badCertTechnicalInfo");
-    technicalInfo.textContent = "";
-
-    let uri = Services.io.newURI(input.data.url);
-    let hostString = uri.host;
-    if (uri.port != 443 && uri.port != -1) {
-      hostString = uri.hostPort;
-    }
-
-    let msg1 = gPipNSSBundle.formatStringFromName("certErrorIntro",
-                                                  [hostString], 1);
-    msg1 += "\n\n";
-
-    if (input.data.certIsUntrusted) {
-      switch (input.data.code) {
-        // We only want to measure MitM rates for now. Treat it as unkown issuer.
-        case MOZILLA_PKIX_ERROR_MITM_DETECTED:
-        case SEC_ERROR_UNKNOWN_ISSUER:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer") + "\n";
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer2") + "\n";
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_UnknownIssuer3") + "\n";
-          break;
-        case SEC_ERROR_CA_CERT_INVALID:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_CaInvalid") + "\n";
-          break;
-        case SEC_ERROR_UNTRUSTED_ISSUER:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Issuer") + "\n";
-          break;
-        case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SignatureAlgorithmDisabled") + "\n";
-          break;
-        case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_ExpiredIssuer") + "\n";
-          break;
-        case MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_SelfSigned") + "\n";
-          break;
-        default:
-          msg1 += gPipNSSBundle.GetStringFromName("certErrorTrust_Untrusted") + "\n";
-      }
-    }
-
-    technicalInfo.appendChild(doc.createTextNode(msg1));
-
-    if (input.data.isDomainMismatch) {
-      let subjectAltNames = input.data.certSubjectAltNames.split(",");
-      let numSubjectAltNames = subjectAltNames.length;
-      let msgPrefix = "";
-      if (numSubjectAltNames != 0) {
-        if (numSubjectAltNames == 1) {
-          msgPrefix = gPipNSSBundle.GetStringFromName("certErrorMismatchSinglePrefix");
-
-          // Let's check if we want to make this a link.
-          let okHost = input.data.certSubjectAltNames;
-          let href = "";
-          let thisHost = doc.location.hostname;
-          let proto = doc.location.protocol + "//";
-          // If okHost is a wildcard domain ("*.example.com") let's
-          // use "www" instead.  "*.example.com" isn't going to
-          // get anyone anywhere useful. bug 432491
-          okHost = okHost.replace(/^\*\./, "www.");
-          /* case #1:
-           * example.com uses an invalid security certificate.
-           *
-           * The certificate is only valid for www.example.com
-           *
-           * Make sure to include the "." ahead of thisHost so that
-           * a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
-           *
-           * We'd normally just use a RegExp here except that we lack a
-           * library function to escape them properly (bug 248062), and
-           * domain names are famous for having '.' characters in them,
-           * which would allow spurious and possibly hostile matches.
-           */
-          if (okHost.endsWith("." + thisHost)) {
-            href = proto + okHost;
-          }
-          /* case #2:
-           * browser.garage.maemo.org uses an invalid security certificate.
-           *
-           * The certificate is only valid for garage.maemo.org
-           */
-          if (thisHost.endsWith("." + okHost)) {
-            href = proto + okHost;
-          }
-
-          // If we set a link, meaning there's something helpful for
-          // the user here, expand the section by default
-          if (href && cssClass != "expertBadCert") {
-            doc.getElementById("badCertAdvancedPanel").style.display = "block";
-            if (error == "nssBadCert") {
-              // Toggling the advanced panel must ensure that the debugging
-              // information panel is hidden as well, since it's opened by the
-              // error code link in the advanced panel.
-              var div = doc.getElementById("certificateErrorDebugInformation");
-              div.style.display = "none";
-            }
-          }
-
-          // Set the link if we want it.
-          if (href) {
-            let referrerlink = doc.createElement("a");
-            referrerlink.append(input.data.certSubjectAltNames);
-            referrerlink.title = input.data.certSubjectAltNames;
-            referrerlink.id = "cert_domain_link";
-            referrerlink.href = href;
-            let fragment = BrowserUtils.getLocalizedFragment(doc, msgPrefix,
-                                                             referrerlink);
-            technicalInfo.appendChild(fragment);
-          } else {
-            let fragment = BrowserUtils.getLocalizedFragment(doc,
-                                                             msgPrefix,
-                                                             input.data.certSubjectAltNames);
-            technicalInfo.appendChild(fragment);
-          }
-          technicalInfo.append("\n");
-        } else {
-          let msg = gPipNSSBundle.GetStringFromName("certErrorMismatchMultiple") + "\n";
-          for (let i = 0; i < numSubjectAltNames; i++) {
-            msg += subjectAltNames[i];
-            if (i != (numSubjectAltNames - 1)) {
-              msg += ", ";
-            }
-          }
-          technicalInfo.append(msg + "\n");
-        }
-      } else {
-        let msg = gPipNSSBundle.formatStringFromName("certErrorMismatch",
-                                                     [hostString], 1);
-        technicalInfo.append(msg + "\n");
-      }
-    }
-
-    if (input.data.isNotValidAtThisTime) {
-      let nowTime = new Date().getTime() * 1000;
-      let dateOptions = { year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "numeric" };
-      let now = new Services.intl.DateTimeFormat(undefined, dateOptions).format(new Date());
-      let msg = "";
-      if (input.data.validity.notBefore) {
-        if (nowTime > input.data.validity.notAfter) {
-          msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                    [input.data.validity.notAfterLocalTime, now], 2) + "\n";
-        } else {
-          msg += gPipNSSBundle.formatStringFromName("certErrorNotYetValidNow",
-                                                    [input.data.validity.notBeforeLocalTime, now], 2) + "\n";
-        }
-      } else {
-        // If something goes wrong, we assume the cert expired.
-        msg += gPipNSSBundle.formatStringFromName("certErrorExpiredNow",
-                                                  ["", now], 2) + "\n";
-      }
-      technicalInfo.append(msg);
-    }
-    technicalInfo.append("\n");
-
-    // Add link to certificate and error message.
-    let linkPrefix = gPipNSSBundle.GetStringFromName("certErrorCodePrefix3");
-    let detailLink = doc.createElement("a");
-    detailLink.append(input.data.codeString);
-    detailLink.title = input.data.codeString;
-    detailLink.id = "errorCode";
-    let fragment = BrowserUtils.getLocalizedFragment(doc, linkPrefix, detailLink);
-    technicalInfo.appendChild(fragment);
-    var errorCode = doc.getElementById("errorCode");
-    if (errorCode) {
-      errorCode.href = "javascript:void(0)";
-      errorCode.addEventListener("click", () => {
-        let debugInfo = doc.getElementById("certificateErrorDebugInformation");
-        debugInfo.style.display = "block";
-        debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
-      });
-    }
-  },
-
-  onCertErrorDetails(msg, docShell) {
-    let doc = docShell.document;
-
-    let div = doc.getElementById("certificateErrorText");
-    div.textContent = msg.data.info;
-    this._setTechDetails(msg, doc);
-    let learnMoreLink = doc.getElementById("learnMoreLink");
-    let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
-
-    switch (msg.data.code) {
-      case SEC_ERROR_UNKNOWN_ISSUER:
-      case MOZILLA_PKIX_ERROR_MITM_DETECTED:
-      case MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT:
-        learnMoreLink.href = baseURL + "security-error";
-        break;
-
-      // In case the certificate expired we make sure the system clock
-      // matches the remote-settings service (blocklist via Kinto) ping time
-      // and is not before the build date.
-      case SEC_ERROR_EXPIRED_CERTIFICATE:
-      case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
-      case SEC_ERROR_OCSP_FUTURE_RESPONSE:
-      case SEC_ERROR_OCSP_OLD_RESPONSE:
-      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
-      case MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:
-
-        // We check against the remote-settings server time first if available, because that allows us
-        // to give the user an approximation of what the correct time is.
-        let difference = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_CLOCK_SKEW_SECONDS, 0);
-        let lastFetched = Services.prefs.getIntPref(PREF_SERVICES_SETTINGS_LAST_FETCHED, 0) * 1000;
-
-        let now = Date.now();
-        let certRange = this._getCertValidityRange(docShell);
-
-        let approximateDate = now - difference * 1000;
-        // If the difference is more than a day, we last fetched the date in the last 5 days,
-        // and adjusting the date per the interval would make the cert valid, warn the user:
-        if (Math.abs(difference) > 60 * 60 * 24 && (now - lastFetched) <= 60 * 60 * 24 * 5 &&
-            certRange.notBefore < approximateDate && certRange.notAfter > approximateDate) {
-          let formatter = new Services.intl.DateTimeFormat(undefined, {
-            dateStyle: "short"
-          });
-          let systemDate = formatter.format(new Date());
-          // negative difference means local time is behind server time
-          approximateDate = formatter.format(new Date(approximateDate));
-
-          doc.getElementById("wrongSystemTime_URL").textContent = doc.location.hostname;
-          doc.getElementById("wrongSystemTime_systemDate").textContent = systemDate;
-          doc.getElementById("wrongSystemTime_actualDate").textContent = approximateDate;
-
-          doc.getElementById("errorShortDesc").style.display = "none";
-          doc.getElementById("wrongSystemTimePanel").style.display = "block";
-
-        // If there is no clock skew with Kinto servers, check against the build date.
-        // (The Kinto ping could have happened when the time was still right, or not at all)
-        } else {
-          let appBuildID = Services.appinfo.appBuildID;
-
-          let year = parseInt(appBuildID.substr(0, 4), 10);
-          let month = parseInt(appBuildID.substr(4, 2), 10) - 1;
-          let day = parseInt(appBuildID.substr(6, 2), 10);
-
-          let buildDate = new Date(year, month, day);
-          let systemDate = new Date();
-
-          // We don't check the notBefore of the cert with the build date,
-          // as it is of course almost certain that it is now later than the build date,
-          // so we shouldn't exclude the possibility that the cert has become valid
-          // since the build date.
-          if (buildDate > systemDate && new Date(certRange.notAfter) > buildDate) {
-            let formatter = new Services.intl.DateTimeFormat(undefined, {
-              dateStyle: "short"
-            });
-
-            doc.getElementById("wrongSystemTimeWithoutReference_URL")
-              .textContent = doc.location.hostname;
-            doc.getElementById("wrongSystemTimeWithoutReference_systemDate")
-              .textContent = formatter.format(systemDate);
-
-            doc.getElementById("errorShortDesc").style.display = "none";
-            doc.getElementById("wrongSystemTimeWithoutReferencePanel").style.display = "block";
-          }
-        }
-        learnMoreLink.href = baseURL + "time-errors";
-        break;
-    }
-  },
-
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
-    // Documents have a null ownerDocument.
-    let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
-
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
-    switch (aEvent.type) {
-    case "AboutNetErrorLoad":
-      this.onPageLoad(aEvent.originalTarget, doc.defaultView);
-      break;
-    case "AboutNetErrorOpenCaptivePortal":
-      this.openCaptivePortalPage(aEvent);
-      break;
-    case "AboutNetErrorSetAutomatic":
-      this.onSetAutomatic(aEvent);
-      break;
-    case "AboutNetErrorResetPreferences":
-      this.onResetPreferences(aEvent);
-      break;
-    }
-  },
-
-  changedCertPrefs() {
-    let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
-       return prefs.concat(Services.prefs.getChildList(root));
-    }, []);
-    for (let prefName of prefSSLImpact) {
-      if (Services.prefs.prefHasUserValue(prefName)) {
-        return true;
-      }
-    }
-
-    return false;
-  },
-
-   _getErrorMessageFromCode(securityInfo, doc) {
-     let uri = Services.io.newURI(doc.location);
-     let hostString = uri.host;
-     if (uri.port != 443 && uri.port != -1) {
-       hostString = uri.hostPort;
-     }
-
-     let id_str = "";
-     switch (securityInfo.errorCode) {
-       case SSL_ERROR_SSL_DISABLED:
-         id_str = "PSMERR_SSL_Disabled";
-         break;
-       case SSL_ERROR_SSL2_DISABLED:
-         id_str = "PSMERR_SSL2_Disabled";
-         break;
-       case SEC_ERROR_REUSED_ISSUER_AND_SERIAL:
-         id_str = "PSMERR_HostReusedIssuerSerial";
-         break;
-     }
-     let nss_error_id_str = securityInfo.errorCodeString;
-     let msg2 = "";
-     if (id_str) {
-       msg2 = gPipNSSBundle.GetStringFromName(id_str) + "\n";
-     } else if (nss_error_id_str) {
-       msg2 = gNSSErrorsBundle.GetStringFromName(nss_error_id_str) + "\n";
-     }
-
-     if (!msg2) {
-       // We couldn't get an error message. Use the error string.
-       // Note that this is different from before where we used PR_ErrorToString.
-       msg2 = nss_error_id_str;
-     }
-     let msg = gPipNSSBundle.formatStringFromName("SSLConnectionErrorPrefix2",
-                                                  [hostString, msg2], 2);
-
-     if (nss_error_id_str) {
-       msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
-                                                 [nss_error_id_str], 1) + "\n";
-     }
-     return msg;
-   },
-
-  onPageLoad(originalTarget, win) {
-    // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
-    const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
-
-    let hideAddExceptionButton = false;
-
-    if (this.isAboutCertError(win.document)) {
-      ClickEventHandler.onCertError(originalTarget, win);
-      hideAddExceptionButton =
-        Services.prefs.getBoolPref("security.certerror.hideAddException", false);
-    }
-    if (this.isAboutNetError(win.document)) {
-      let docShell = win.document.docShell;
-      if (docShell) {
-        let {securityInfo} = docShell.failedChannel;
-        // We don't have a securityInfo when this is for example a DNS error.
-        if (securityInfo) {
-          securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-          let msg = this._getErrorMessageFromCode(securityInfo,
-                                                  win.document);
-          let id = win.document.getElementById("errorShortDescText");
-          id.textContent = msg;
-        }
-      }
-    }
-
-    let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
-    win.dispatchEvent(new win.CustomEvent("AboutNetErrorOptions", {
-      detail: JSON.stringify({
-        enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
-        changedCertPrefs: this.changedCertPrefs(),
-        automatic,
-        hideAddExceptionButton,
-      })
-    }));
-
-    sendAsyncMessage("Browser:SSLErrorReportTelemetry",
-                     {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
-  },
-
-  openCaptivePortalPage(evt) {
-    sendAsyncMessage("Browser:OpenCaptivePortalPage");
-  },
-
-
-  onResetPreferences(evt) {
-    sendAsyncMessage("Browser:ResetSSLPreferences");
-  },
-
-  onSetAutomatic(evt) {
-    sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
-      automatic: evt.detail
-    });
-
-    // If we're enabling reports, send a report for this failure.
-    if (evt.detail) {
-      let win = evt.originalTarget.ownerGlobal;
-      let docShell = win.document.docShell;
-
-      let {securityInfo} = docShell.failedChannel;
-      securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
-      let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
-
-      let errorReporter = Cc["@mozilla.org/securityreporter;1"]
-                            .getService(Ci.nsISecurityReporter);
-      errorReporter.reportTLSError(securityInfo, host, port);
-    }
-  },
-};
-
-AboutNetAndCertErrorListener.init(this);
-AboutBlockedSiteListener.init(this);
-
-var ClickEventHandler = {
-  init: function init() {
-    Services.els.addSystemEventListener(global, "click", this, true);
-  },
-
-  handleEvent(event) {
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
-      return;
-    }
-
-    let originalTarget = event.originalTarget;
-    let ownerDoc = originalTarget.ownerDocument;
-    if (!ownerDoc) {
-      return;
-    }
-
-    // Handle click events from about pages
-    if (event.button == 0) {
-      if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        this.onCertError(originalTarget, ownerDoc.defaultView);
-        return;
-      } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        this.onAboutBlocked(originalTarget, ownerDoc);
-        return;
-      } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        this.onAboutNetError(event, ownerDoc.documentURI);
-        return;
-      }
-    }
-
-    let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
-
-    // get referrer attribute from clicked link and parse it
-    // if per element referrer is enabled, the element referrer overrules
-    // the document wide referrer
-    let referrerPolicy = ownerDoc.referrerPolicy;
-    if (node) {
-      let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
-                              getAttribute("referrerpolicy"));
-      if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
-        referrerPolicy = referrerAttrValue;
-      }
-    }
-
-    let frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
-
-    let json = { button: event.button, shiftKey: event.shiftKey,
-                 ctrlKey: event.ctrlKey, metaKey: event.metaKey,
-                 altKey: event.altKey, href: null, title: null,
-                 bookmark: false, frameOuterWindowID, referrerPolicy,
-                 triggeringPrincipal: principal,
-                 originAttributes: principal ? principal.originAttributes : {},
-                 isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
-
-    if (href) {
-      try {
-        BrowserUtils.urlSecurityCheck(href, principal);
-      } catch (e) {
-        return;
-      }
-
-      json.href = href;
-      if (node) {
-        json.title = node.getAttribute("title");
-        if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
-            !event.altKey && !event.metaKey) {
-          json.bookmark = node.getAttribute("rel") == "sidebar";
-          if (json.bookmark) {
-            event.preventDefault(); // Need to prevent the pageload.
-          }
-        }
-      }
-      json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
-
-      // Check if the link needs to be opened with mixed content allowed.
-      // Only when the owner doc has |mixedContentChannel| and the same origin
-      // should we allow mixed content.
-      json.allowMixedContent = false;
-      let docshell = ownerDoc.docShell;
-      if (docShell.mixedContentChannel) {
-        const sm = Services.scriptSecurityManager;
-        try {
-          let targetURI = Services.io.newURI(href);
-          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
-          json.allowMixedContent = true;
-        } catch (e) {}
-      }
-      json.originPrincipal = ownerDoc.nodePrincipal;
-      json.triggeringPrincipal = ownerDoc.nodePrincipal;
-
-      sendAsyncMessage("Content:Click", json);
-      return;
-    }
-
-    // This might be middle mouse navigation.
-    if (event.button == 1) {
-      sendAsyncMessage("Content:Click", json);
-    }
-  },
-
-  onCertError(targetElement, win) {
-    let docShell = win.document.docShell;
-    sendAsyncMessage("Browser:CertExceptionError", {
-      frameId: WebNavigationFrames.getFrameId(win),
-      location: win.document.location.href,
-      elementId: targetElement.getAttribute("id"),
-      isTopFrame: (win.parent === win),
-      securityInfoAsString: getSerializedSecurityInfo(docShell),
-    });
-  },
-
-  onAboutBlocked(targetElement, ownerDoc) {
+  onAboutBlocked(global, targetElement, ownerDoc) {
     var reason = "phishing";
     if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
       reason = "malware";
     } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
       reason = "unwanted";
     } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
       reason = "harmful";
     }
 
     let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
                                        .getInterface(Ci.nsIWebNavigation)
                                       .QueryInterface(Ci.nsIDocShell);
 
-    sendAsyncMessage("Browser:SiteBlockedError", {
+    global.sendAsyncMessage("Browser:SiteBlockedError", {
       location: ownerDoc.location.href,
       reason,
       elementId: targetElement.getAttribute("id"),
       isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
       blockedInfo: getSiteBlockedErrorDetails(docShell),
     });
   },
-
-  onAboutNetError(event, documentURI) {
-    let elmId = event.originalTarget.getAttribute("id");
-    if (elmId == "returnButton") {
-      sendAsyncMessage("Browser:SSLErrorGoBack", {});
-      return;
-    }
-    if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
-      return;
-    }
-    // browser front end will handle clearing offline mode and refreshing
-    // the page *if* we're in offline mode now. Otherwise let the error page
-    // handle the click.
-    if (Services.io.offline) {
-      event.preventDefault();
-      sendAsyncMessage("Browser:EnableOnlineMode", {});
-    }
-  },
-
-  /**
-   * Extracts linkNode and href for the current click target.
-   *
-   * @param event
-   *        The click event.
-   * @return [href, linkNode, linkPrincipal].
-   *
-   * @note linkNode will be null if the click wasn't on an anchor
-   *       element. This includes SVG links, because callers expect |node|
-   *       to behave like an <a> element, which SVG links (XLink) don't.
-   */
-  _hrefAndLinkNodeForClickEvent(event) {
-    function isHTMLLink(aNode) {
-      // Be consistent with what nsContextMenu.js does.
-      return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
-              (aNode instanceof content.HTMLAreaElement && aNode.href) ||
-              aNode instanceof content.HTMLLinkElement);
-    }
-
-    let node = event.target;
-    while (node && !isHTMLLink(node)) {
-      node = node.parentNode;
-    }
-
-    if (node)
-      return [node.href, node, node.ownerDocument.nodePrincipal];
-
-    // If there is no linkNode, try simple XLink.
-    let href, baseURI;
-    node = event.target;
-    while (node && !href) {
-      if (node.nodeType == content.Node.ELEMENT_NODE &&
-          (node.localName == "a" ||
-           node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
-        href = node.getAttribute("href") ||
-               node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
-        if (href) {
-          baseURI = node.ownerDocument.baseURIObject;
-          break;
-        }
-      }
-      node = node.parentNode;
-    }
-
-    // In case of XLink, we don't return the node we got href from since
-    // callers expect <a>-like elements.
-    // Note: makeURI() will throw if aUri is not a valid URI.
-    return [href ? Services.io.newURI(href, null, baseURI).spec : null, null,
-            node && node.ownerDocument.nodePrincipal];
-  }
 };
-ClickEventHandler.init();
-
-ContentLinkHandler.init(this);
-ContentMetaHandler.init(this);
-
-// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
-var pluginContent = new PluginContent(global);
-
-addEventListener("DOMWindowFocus", function(event) {
-  sendAsyncMessage("DOMWindowFocus", {});
-}, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
-
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
-
-addEventListener("pageshow", function(event) {
-  if (event.target == content.document) {
-    sendAsyncMessage("PageVisibility:Show", {
-      persisted: event.persisted,
-    });
-  }
-});
-addEventListener("pagehide", function(event) {
-  if (event.target == content.document) {
-    sendAsyncMessage("PageVisibility:Hide", {
-      persisted: event.persisted,
-    });
-  }
-});
-
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-  },
-  receiveMessage(message) {
-    switch (message.name) {
-      case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
-        break;
-      }
-      case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
-        break;
-      }
-    }
-  }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
-  registerWindow(aWindow) {
-    if (!this._observerAdded) {
-      this._observerAdded = true;
-      Services.obs.addObserver(this, "offline-cache-update-completed", true);
-    }
-    let manifestURI = this._getManifestURI(aWindow);
-    this._docManifestSet.add(manifestURI.spec);
-  },
-
-  handleEvent(event) {
-    if (event.type == "MozApplicationManifest") {
-      this.offlineAppRequested(event.originalTarget.defaultView);
-    }
-  },
-
-  _getManifestURI(aWindow) {
-    if (!aWindow.document.documentElement)
-      return null;
-
-    var attr = aWindow.document.documentElement.getAttribute("manifest");
-    if (!attr)
-      return null;
-
-    try {
-      return Services.io.newURI(attr, aWindow.document.characterSet,
-                                Services.io.newURI(aWindow.location.href));
-    } catch (e) {
-      return null;
-    }
-  },
-
-  offlineAppRequested(aContentWindow) {
-    this.registerWindow(aContentWindow);
-    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
-      return;
-    }
-
-    let currentURI = aContentWindow.document.documentURIObject;
-    // don't bother showing UI if the user has already made a decision
-    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
-      return;
-
-    try {
-      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
-        // all pages can use offline capabilities, no need to ask the user
-        return;
-      }
-    } catch (e) {
-      // this pref isn't set by default, ignore failures
-    }
-    let docId = ++this._docId;
-    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
-      uri: currentURI.spec,
-      docId,
-    });
-  },
-
-  _startFetching(aDocument) {
-    if (!aDocument.documentElement)
-      return;
-
-    let manifestURI = this._getManifestURI(aDocument.defaultView);
-    if (!manifestURI)
-      return;
-
-    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
-                        getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
-                                 aDocument.nodePrincipal, aDocument.defaultView);
-  },
-
-  receiveMessage(aMessage) {
-    if (aMessage.name == "OfflineApps:StartFetching") {
-      let doc = this._docIdMap.get(aMessage.data.docId);
-      doc = doc && doc.get();
-      if (doc) {
-        this._startFetching(doc);
-      }
-      this._docIdMap.delete(aMessage.data.docId);
-    }
-  },
-
-  observe(aSubject, aTopic, aState) {
-    if (aTopic == "offline-cache-update-completed") {
-      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
-      let uri = cacheUpdate.manifestURI;
-      if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
-      }
-    }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
-
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
--- a/browser/modules/BrowserErrorReporter.jsm
+++ b/browser/modules/BrowserErrorReporter.jsm
@@ -5,17 +5,17 @@
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 ChromeUtils.defineModuleGetter(this, "Log", "resource://gre/modules/Log.jsm");
 ChromeUtils.defineModuleGetter(this, "UpdateUtils", "resource://gre/modules/UpdateUtils.jsm");
 
-Cu.importGlobalProperties(["fetch", "URL"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch", "URL"]);
 
 var EXPORTED_SYMBOLS = ["BrowserErrorReporter"];
 
 const CONTEXT_LINES = 5;
 const ERROR_PREFIX_RE = /^[^\W]+:/m;
 const PREF_ENABLED = "browser.chrome.errorReporter.enabled";
 const PREF_LOG_LEVEL = "browser.chrome.errorReporter.logLevel";
 const PREF_PROJECT_ID = "browser.chrome.errorReporter.projectId";
--- a/browser/modules/ContentMetaHandler.jsm
+++ b/browser/modules/ContentMetaHandler.jsm
@@ -1,15 +1,16 @@
 /* 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/. */
 
 "use strict";
-Cu.importGlobalProperties(["URL"]);
 ChromeUtils.import("resource://gre/modules/Services.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
 
 // Debounce time in milliseconds - this should be long enough to account for
 // sync script tags that could appear between desired meta tags
 const TIMEOUT_DELAY = 1000;
 
 const ACCEPTED_PROTOCOLS = ["http:", "https:"];
 
 // Possible description tags, listed in order from least favourable to most favourable
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -5,17 +5,17 @@
 
 var EXPORTED_SYMBOLS = [
   "ContentSearch",
 ];
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 ChromeUtils.defineModuleGetter(this, "FormHistory",
   "resource://gre/modules/FormHistory.jsm");
 ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 ChromeUtils.defineModuleGetter(this, "SearchSuggestionController",
   "resource://gre/modules/SearchSuggestionController.jsm");
 
--- a/browser/modules/ContextMenu.jsm
+++ b/browser/modules/ContextMenu.jsm
@@ -3,21 +3,21 @@
 /* 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/. */
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["ContextMenu"];
 
-Cu.importGlobalProperties(["URL"]);
-
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
+XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
+
 XPCOMUtils.defineLazyModuleGetters(this, {
   E10SUtils: "resource://gre/modules/E10SUtils.jsm",
   BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
   findCssSelector: "resource://gre/modules/css-selector.js",
   SpellCheckHelper: "resource://gre/modules/InlineSpellChecker.jsm",
   LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
   WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
   PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
copy from browser/base/content/content.js
copy to browser/modules/NetErrorContent.jsm
--- a/browser/base/content/content.js
+++ b/browser/modules/NetErrorContent.jsm
@@ -1,105 +1,37 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-/* This content script should work in any browser or iframe and should not
- * depend on the frame being contained in tabbrowser. */
-
-/* eslint-env mozilla/frame-script */
+var EXPORTED_SYMBOLS = ["NetErrorContent"];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-// TabChildGlobal
-var global = this;
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  BrowserUtils: "resource://gre/modules/BrowserUtils.jsm",
-  ContentLinkHandler: "resource:///modules/ContentLinkHandler.jsm",
-  ContentMetaHandler: "resource:///modules/ContentMetaHandler.jsm",
-  ContentWebRTC: "resource:///modules/ContentWebRTC.jsm",
-  InlineSpellCheckerContent: "resource://gre/modules/InlineSpellCheckerContent.jsm",
-  LoginManagerContent: "resource://gre/modules/LoginManagerContent.jsm",
-  LoginFormFactory: "resource://gre/modules/LoginManagerContent.jsm",
-  InsecurePasswordUtils: "resource://gre/modules/InsecurePasswordUtils.jsm",
-  PluginContent: "resource:///modules/PluginContent.jsm",
-  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
-  FormSubmitObserver: "resource:///modules/FormSubmitObserver.jsm",
-  PageMetadata: "resource://gre/modules/PageMetadata.jsm",
-  SafeBrowsing: "resource://gre/modules/SafeBrowsing.jsm",
-  Utils: "resource://gre/modules/sessionstore/Utils.jsm",
-  WebNavigationFrames: "resource://gre/modules/WebNavigationFrames.jsm",
-  ContextMenu: "resource:///modules/ContextMenu.jsm",
-});
+ChromeUtils.defineModuleGetter(this, "BrowserUtils",
+                               "resource://gre/modules/BrowserUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "WebNavigationFrames",
+                               "resource://gre/modules/WebNavigationFrames.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "gPipNSSBundle", function() {
   return Services.strings.createBundle("chrome://pipnss/locale/pipnss.properties");
 });
 XPCOMUtils.defineLazyGetter(this, "gNSSErrorsBundle", function() {
   return Services.strings.createBundle("chrome://pipnss/locale/nsserrors.properties");
 });
 
-XPCOMUtils.defineLazyProxy(this, "contextMenu", () => {
-  return new ContextMenu(global);
-});
-
-XPCOMUtils.defineLazyProxy(this, "formSubmitObserver", () => {
-  return new FormSubmitObserver(content, this);
-}, {
-  // stub QI
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIFormSubmitObserver, Ci.nsISupportsWeakReference])
-});
-
-XPCOMUtils.defineLazyProxy(this, "PageInfoListener",
-                           "resource:///modules/PageInfoListener.jsm");
-
-XPCOMUtils.defineLazyProxy(this, "LightWeightThemeWebInstallListener",
-                           "resource:///modules/LightWeightThemeWebInstallListener.jsm");
-
-Services.els.addSystemEventListener(global, "contextmenu", contextMenu, false);
-
-Services.obs.addObserver(formSubmitObserver, "invalidformsubmit", true);
-
-addMessageListener("PageInfo:getData", PageInfoListener);
-
-addMessageListener("RemoteLogins:fillForm", function(message) {
-  // intercept if ContextMenu.jsm had sent a plain object for remote targets
-  message.objects.inputElement = contextMenu.getTarget(message, "inputElement");
-  LoginManagerContent.receiveMessage(message, content);
-});
-addEventListener("DOMFormHasPassword", function(event) {
-  LoginManagerContent.onDOMFormHasPassword(event, content);
-  let formLike = LoginFormFactory.createFromForm(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("DOMInputPasswordAdded", function(event) {
-  LoginManagerContent.onDOMInputPasswordAdded(event, content);
-  let formLike = LoginFormFactory.createFromField(event.originalTarget);
-  InsecurePasswordUtils.reportInsecurePasswords(formLike);
-});
-addEventListener("pageshow", function(event) {
-  LoginManagerContent.onPageShow(event, content);
-});
-addEventListener("DOMAutoComplete", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
-addEventListener("blur", function(event) {
-  LoginManagerContent.onUsernameInput(event);
-});
 
 const SEC_ERROR_BASE          = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const MOZILLA_PKIX_ERROR_BASE = Ci.nsINSSErrorsService.MOZILLA_PKIX_ERROR_BASE;
 
 const SEC_ERROR_EXPIRED_CERTIFICATE                = SEC_ERROR_BASE + 11;
 const SEC_ERROR_UNKNOWN_ISSUER                     = SEC_ERROR_BASE + 13;
 const SEC_ERROR_UNTRUSTED_ISSUER                   = SEC_ERROR_BASE + 20;
-const SEC_ERROR_UNTRUSTED_CERT                     = SEC_ERROR_BASE + 21;
 const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE         = SEC_ERROR_BASE + 30;
 const SEC_ERROR_CA_CERT_INVALID                    = SEC_ERROR_BASE + 36;
 const SEC_ERROR_OCSP_FUTURE_RESPONSE               = SEC_ERROR_BASE + 131;
 const SEC_ERROR_OCSP_OLD_RESPONSE                  = SEC_ERROR_BASE + 132;
 const SEC_ERROR_REUSED_ISSUER_AND_SERIAL           = SEC_ERROR_BASE + 138;
 const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED  = SEC_ERROR_BASE + 176;
 const MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 5;
 const MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE = MOZILLA_PKIX_ERROR_BASE + 6;
@@ -126,182 +58,25 @@ function getSerializedSecurityInfo(docSh
     return "";
   }
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo)
               .QueryInterface(Ci.nsISerializable);
 
   return serhelper.serializeToString(securityInfo);
 }
 
-function getSiteBlockedErrorDetails(docShell) {
-  let blockedInfo = {};
-  if (docShell.failedChannel) {
-    let classifiedChannel = docShell.failedChannel.
-                            QueryInterface(Ci.nsIClassifiedChannel);
-    if (classifiedChannel) {
-      let httpChannel = docShell.failedChannel.QueryInterface(Ci.nsIHttpChannel);
-
-      let reportUri = httpChannel.URI.clone();
-
-      // Remove the query to avoid leaking sensitive data
-      if (reportUri instanceof Ci.nsIURL) {
-        reportUri = reportUri.mutate()
-                             .setQuery("")
-                             .finalize();
-      }
-
-      blockedInfo = { list: classifiedChannel.matchedList,
-                      provider: classifiedChannel.matchedProvider,
-                      uri: reportUri.asciiSpec };
-    }
-  }
-  return blockedInfo;
-}
-
-var AboutBlockedSiteListener = {
-  init(chromeGlobal) {
-    addMessageListener("DeceptiveBlockedDetails", this);
-    chromeGlobal.addEventListener("AboutBlockedLoaded", this, false, true);
-  },
-
-  get isBlockedSite() {
-    return content.document.documentURI.startsWith("about:blocked");
-  },
-
-  receiveMessage(msg) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (msg.name == "DeceptiveBlockedDetails") {
-      sendAsyncMessage("DeceptiveBlockedDetails:Result", {
-        blockedInfo: getSiteBlockedErrorDetails(docShell),
-      });
-    }
-  },
-
-  handleEvent(aEvent) {
-    if (!this.isBlockedSite) {
-      return;
-    }
-
-    if (aEvent.type != "AboutBlockedLoaded") {
-      return;
-    }
-
-    let blockedInfo = getSiteBlockedErrorDetails(docShell);
-    let provider = blockedInfo.provider || "";
-
-    let doc = content.document;
-
-    /**
-    * Set error description link in error details.
-    * For example, the "reported as a deceptive site" link for
-    * blocked phishing pages.
-    */
-    let desc = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".reportURL", "");
-    if (desc) {
-      doc.getElementById("error_desc_link").setAttribute("href", desc + aEvent.detail.url);
-    }
-
-    // Set other links in error details.
-    switch (aEvent.detail.err) {
-      case "malware":
-        doc.getElementById("report_detection").setAttribute("href",
-          (SafeBrowsing.getReportURL("MalwareMistake", blockedInfo) ||
-           "https://www.stopbadware.org/firefox"));
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.stopbadware.org/firefox");
-        break;
-      case "unwanted":
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.google.com/about/unwanted-software-policy.html");
-        break;
-      case "phishing":
-        doc.getElementById("report_detection").setAttribute("href",
-          (SafeBrowsing.getReportURL("PhishMistake", blockedInfo) ||
-           "https://safebrowsing.google.com/safebrowsing/report_error/?tpl=mozilla"));
-        doc.getElementById("learn_more_link").setAttribute("href",
-          "https://www.antiphishing.org//");
-        break;
-    }
-
-    // Set the firefox support url.
-    doc.getElementById("firefox_support").setAttribute("href",
-      Services.urlFormatter.formatURLPref("app.support.baseURL") + "phishing-malware");
-
-    // Show safe browsing details on load if the pref is set to true.
-    let showDetails = Services.prefs.getBoolPref("browser.xul.error_pages.show_safe_browsing_details_on_load");
-    if (showDetails) {
-      let details = content.document.getElementById("errorDescriptionContainer");
-      details.removeAttribute("hidden");
-    }
-
-    // Set safe browsing advisory link.
-    let advisoryUrl = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".advisoryURL", "");
-    if (!advisoryUrl) {
-      let el = content.document.getElementById("advisoryDesc");
-      el.remove();
-      return;
-    }
-
-    let advisoryLinkText = Services.prefs.getCharPref(
-      "browser.safebrowsing.provider." + provider + ".advisoryName", "");
-    if (!advisoryLinkText) {
-      let el = content.document.getElementById("advisoryDesc");
-      el.remove();
-      return;
-    }
-
-    let anchorEl = content.document.getElementById("advisory_provider");
-    anchorEl.setAttribute("href", advisoryUrl);
-    anchorEl.textContent = advisoryLinkText;
-  },
-};
-
-var AboutNetAndCertErrorListener = {
-  init(chromeGlobal) {
-    addMessageListener("CertErrorDetails", this);
-    addMessageListener("Browser:CaptivePortalFreed", this);
-    chromeGlobal.addEventListener("AboutNetErrorLoad", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorOpenCaptivePortal", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorSetAutomatic", this, false, true);
-    chromeGlobal.addEventListener("AboutNetErrorResetPreferences", this, false, true);
-  },
-
+var NetErrorContent = {
   isAboutNetError(doc) {
     return doc.documentURI.startsWith("about:neterror");
   },
 
   isAboutCertError(doc) {
     return doc.documentURI.startsWith("about:certerror");
   },
 
-  receiveMessage(msg) {
-    if (msg.name == "CertErrorDetails") {
-      let frameDocShell = WebNavigationFrames.findDocShell(msg.data.frameId, docShell);
-      // We need nsIWebNavigation to access docShell.document.
-      frameDocShell && frameDocShell.QueryInterface(Ci.nsIWebNavigation);
-      if (!frameDocShell || !this.isAboutCertError(frameDocShell.document)) {
-        return;
-      }
-
-      this.onCertErrorDetails(msg, frameDocShell);
-    } else if (msg.name == "Browser:CaptivePortalFreed") {
-      // TODO: This check is not correct for frames.
-      if (!this.isAboutCertError(content.document)) {
-        return;
-      }
-
-      this.onCaptivePortalFreed(msg);
-    }
-  },
-
   _getCertValidityRange(docShell) {
     let {securityInfo} = docShell.failedChannel;
     securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
     let certs = securityInfo.failedCertChain.getEnumerator();
     let notBefore = 0;
     let notAfter = Number.MAX_SAFE_INTEGER;
     while (certs.hasMoreElements()) {
       let cert = certs.getNext();
@@ -489,17 +264,17 @@ var AboutNetAndCertErrorListener = {
       errorCode.addEventListener("click", () => {
         let debugInfo = doc.getElementById("certificateErrorDebugInformation");
         debugInfo.style.display = "block";
         debugInfo.scrollIntoView({block: "start", behavior: "smooth"});
       });
     }
   },
 
-  onCertErrorDetails(msg, docShell) {
+  onCertErrorDetails(global, msg, docShell) {
     let doc = docShell.document;
 
     let div = doc.getElementById("certificateErrorText");
     div.textContent = msg.data.info;
     this._setTechDetails(msg, doc);
     let learnMoreLink = doc.getElementById("learnMoreLink");
     let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
 
@@ -577,40 +352,32 @@ var AboutNetAndCertErrorListener = {
             doc.getElementById("wrongSystemTimeWithoutReferencePanel").style.display = "block";
           }
         }
         learnMoreLink.href = baseURL + "time-errors";
         break;
     }
   },
 
-  onCaptivePortalFreed(msg) {
-    content.dispatchEvent(new content.CustomEvent("AboutNetErrorCaptivePortalFreed"));
-  },
-
-  handleEvent(aEvent) {
+  handleEvent(aGlobal, aEvent) {
     // Documents have a null ownerDocument.
     let doc = aEvent.originalTarget.ownerDocument || aEvent.originalTarget;
 
-    if (!this.isAboutNetError(doc) && !this.isAboutCertError(doc)) {
-      return;
-    }
-
     switch (aEvent.type) {
     case "AboutNetErrorLoad":
-      this.onPageLoad(aEvent.originalTarget, doc.defaultView);
+      this.onPageLoad(aGlobal, aEvent.originalTarget, doc.defaultView);
       break;
     case "AboutNetErrorOpenCaptivePortal":
-      this.openCaptivePortalPage(aEvent);
+      this.openCaptivePortalPage(aGlobal, aEvent);
       break;
     case "AboutNetErrorSetAutomatic":
-      this.onSetAutomatic(aEvent);
+      this.onSetAutomatic(aGlobal, aEvent);
       break;
     case "AboutNetErrorResetPreferences":
-      this.onResetPreferences(aEvent);
+      this.onResetPreferences(aGlobal, aEvent);
       break;
     }
   },
 
   changedCertPrefs() {
     let prefSSLImpact = PREF_SSL_IMPACT_ROOTS.reduce((prefs, root) => {
        return prefs.concat(Services.prefs.getChildList(root));
     }, []);
@@ -660,24 +427,24 @@ var AboutNetAndCertErrorListener = {
 
      if (nss_error_id_str) {
        msg += gPipNSSBundle.formatStringFromName("certErrorCodePrefix3",
                                                  [nss_error_id_str], 1) + "\n";
      }
      return msg;
    },
 
-  onPageLoad(originalTarget, win) {
+  onPageLoad(global, originalTarget, win) {
     // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
     const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 
     let hideAddExceptionButton = false;
 
     if (this.isAboutCertError(win.document)) {
-      ClickEventHandler.onCertError(originalTarget, win);
+      this.onCertError(global, originalTarget, win);
       hideAddExceptionButton =
         Services.prefs.getBoolPref("security.certerror.hideAddException", false);
     }
     if (this.isAboutNetError(win.document)) {
       let docShell = win.document.docShell;
       if (docShell) {
         let {securityInfo} = docShell.failedChannel;
         // We don't have a securityInfo when this is for example a DNS error.
@@ -696,31 +463,31 @@ var AboutNetAndCertErrorListener = {
       detail: JSON.stringify({
         enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
         changedCertPrefs: this.changedCertPrefs(),
         automatic,
         hideAddExceptionButton,
       })
     }));
 
-    sendAsyncMessage("Browser:SSLErrorReportTelemetry",
-                     {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
+    global.sendAsyncMessage("Browser:SSLErrorReportTelemetry",
+                            {reportStatus: TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN});
   },
 
-  openCaptivePortalPage(evt) {
-    sendAsyncMessage("Browser:OpenCaptivePortalPage");
+  openCaptivePortalPage(global, evt) {
+    global.sendAsyncMessage("Browser:OpenCaptivePortalPage");
   },
 
 
-  onResetPreferences(evt) {
-    sendAsyncMessage("Browser:ResetSSLPreferences");
+  onResetPreferences(global, evt) {
+    global.sendAsyncMessage("Browser:ResetSSLPreferences");
   },
 
-  onSetAutomatic(evt) {
-    sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
+  onSetAutomatic(global, evt) {
+    global.sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
       automatic: evt.detail
     });
 
     // If we're enabling reports, send a report for this failure.
     if (evt.detail) {
       let win = evt.originalTarget.ownerGlobal;
       let docShell = win.document.docShell;
 
@@ -728,386 +495,38 @@ var AboutNetAndCertErrorListener = {
       securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
       let {host, port} = win.document.mozDocumentURIIfNotForErrorPages;
 
       let errorReporter = Cc["@mozilla.org/securityreporter;1"]
                             .getService(Ci.nsISecurityReporter);
       errorReporter.reportTLSError(securityInfo, host, port);
     }
   },
-};
 
-AboutNetAndCertErrorListener.init(this);
-AboutBlockedSiteListener.init(this);
-
-var ClickEventHandler = {
-  init: function init() {
-    Services.els.addSystemEventListener(global, "click", this, true);
-  },
-
-  handleEvent(event) {
-    if (!event.isTrusted || event.defaultPrevented || event.button == 2) {
-      return;
-    }
-
-    let originalTarget = event.originalTarget;
-    let ownerDoc = originalTarget.ownerDocument;
-    if (!ownerDoc) {
-      return;
-    }
-
-    // Handle click events from about pages
-    if (event.button == 0) {
-      if (AboutNetAndCertErrorListener.isAboutCertError(ownerDoc)) {
-        this.onCertError(originalTarget, ownerDoc.defaultView);
-        return;
-      } else if (ownerDoc.documentURI.startsWith("about:blocked")) {
-        this.onAboutBlocked(originalTarget, ownerDoc);
-        return;
-      } else if (AboutNetAndCertErrorListener.isAboutNetError(ownerDoc)) {
-        this.onAboutNetError(event, ownerDoc.documentURI);
-        return;
-      }
-    }
-
-    let [href, node, principal] = this._hrefAndLinkNodeForClickEvent(event);
-
-    // get referrer attribute from clicked link and parse it
-    // if per element referrer is enabled, the element referrer overrules
-    // the document wide referrer
-    let referrerPolicy = ownerDoc.referrerPolicy;
-    if (node) {
-      let referrerAttrValue = Services.netUtils.parseAttributePolicyString(node.
-                              getAttribute("referrerpolicy"));
-      if (referrerAttrValue !== Ci.nsIHttpChannel.REFERRER_POLICY_UNSET) {
-        referrerPolicy = referrerAttrValue;
-      }
-    }
-
-    let frameOuterWindowID = WebNavigationFrames.getFrameId(ownerDoc.defaultView);
-
-    let json = { button: event.button, shiftKey: event.shiftKey,
-                 ctrlKey: event.ctrlKey, metaKey: event.metaKey,
-                 altKey: event.altKey, href: null, title: null,
-                 bookmark: false, frameOuterWindowID, referrerPolicy,
-                 triggeringPrincipal: principal,
-                 originAttributes: principal ? principal.originAttributes : {},
-                 isContentWindowPrivate: PrivateBrowsingUtils.isContentWindowPrivate(ownerDoc.defaultView)};
-
-    if (href) {
-      try {
-        BrowserUtils.urlSecurityCheck(href, principal);
-      } catch (e) {
-        return;
-      }
-
-      json.href = href;
-      if (node) {
-        json.title = node.getAttribute("title");
-        if (event.button == 0 && !event.ctrlKey && !event.shiftKey &&
-            !event.altKey && !event.metaKey) {
-          json.bookmark = node.getAttribute("rel") == "sidebar";
-          if (json.bookmark) {
-            event.preventDefault(); // Need to prevent the pageload.
-          }
-        }
-      }
-      json.noReferrer = BrowserUtils.linkHasNoReferrer(node);
-
-      // Check if the link needs to be opened with mixed content allowed.
-      // Only when the owner doc has |mixedContentChannel| and the same origin
-      // should we allow mixed content.
-      json.allowMixedContent = false;
-      let docshell = ownerDoc.docShell;
-      if (docShell.mixedContentChannel) {
-        const sm = Services.scriptSecurityManager;
-        try {
-          let targetURI = Services.io.newURI(href);
-          sm.checkSameOriginURI(docshell.mixedContentChannel.URI, targetURI, false);
-          json.allowMixedContent = true;
-        } catch (e) {}
-      }
-      json.originPrincipal = ownerDoc.nodePrincipal;
-      json.triggeringPrincipal = ownerDoc.nodePrincipal;
-
-      sendAsyncMessage("Content:Click", json);
-      return;
-    }
-
-    // This might be middle mouse navigation.
-    if (event.button == 1) {
-      sendAsyncMessage("Content:Click", json);
-    }
-  },
-
-  onCertError(targetElement, win) {
+  onCertError(global, targetElement, win) {
     let docShell = win.document.docShell;
-    sendAsyncMessage("Browser:CertExceptionError", {
+    global.sendAsyncMessage("Browser:CertExceptionError", {
       frameId: WebNavigationFrames.getFrameId(win),
       location: win.document.location.href,
       elementId: targetElement.getAttribute("id"),
       isTopFrame: (win.parent === win),
       securityInfoAsString: getSerializedSecurityInfo(docShell),
     });
   },
 
-  onAboutBlocked(targetElement, ownerDoc) {
-    var reason = "phishing";
-    if (/e=malwareBlocked/.test(ownerDoc.documentURI)) {
-      reason = "malware";
-    } else if (/e=unwantedBlocked/.test(ownerDoc.documentURI)) {
-      reason = "unwanted";
-    } else if (/e=harmfulBlocked/.test(ownerDoc.documentURI)) {
-      reason = "harmful";
-    }
-
-    let docShell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
-                                       .getInterface(Ci.nsIWebNavigation)
-                                      .QueryInterface(Ci.nsIDocShell);
-
-    sendAsyncMessage("Browser:SiteBlockedError", {
-      location: ownerDoc.location.href,
-      reason,
-      elementId: targetElement.getAttribute("id"),
-      isTopFrame: (ownerDoc.defaultView.parent === ownerDoc.defaultView),
-      blockedInfo: getSiteBlockedErrorDetails(docShell),
-    });
-  },
-
-  onAboutNetError(event, documentURI) {
+  onAboutNetError(global, event, documentURI) {
     let elmId = event.originalTarget.getAttribute("id");
     if (elmId == "returnButton") {
-      sendAsyncMessage("Browser:SSLErrorGoBack", {});
+      global.sendAsyncMessage("Browser:SSLErrorGoBack", {});
       return;
     }
     if (elmId != "errorTryAgain" || !/e=netOffline/.test(documentURI)) {
       return;
     }
     // browser front end will handle clearing offline mode and refreshing
     // the page *if* we're in offline mode now. Otherwise let the error page
     // handle the click.
     if (Services.io.offline) {
       event.preventDefault();
-      sendAsyncMessage("Browser:EnableOnlineMode", {});
+      global.sendAsyncMessage("Browser:EnableOnlineMode", {});
     }
   },
-
-  /**
-   * Extracts linkNode and href for the current click target.
-   *
-   * @param event
-   *        The click event.
-   * @return [href, linkNode, linkPrincipal].
-   *
-   * @note linkNode will be null if the click wasn't on an anchor
-   *       element. This includes SVG links, because callers expect |node|
-   *       to behave like an <a> element, which SVG links (XLink) don't.
-   */
-  _hrefAndLinkNodeForClickEvent(event) {
-    function isHTMLLink(aNode) {
-      // Be consistent with what nsContextMenu.js does.
-      return ((aNode instanceof content.HTMLAnchorElement && aNode.href) ||
-              (aNode instanceof content.HTMLAreaElement && aNode.href) ||
-              aNode instanceof content.HTMLLinkElement);
-    }
-
-    let node = event.target;
-    while (node && !isHTMLLink(node)) {
-      node = node.parentNode;
-    }
-
-    if (node)
-      return [node.href, node, node.ownerDocument.nodePrincipal];
-
-    // If there is no linkNode, try simple XLink.
-    let href, baseURI;
-    node = event.target;
-    while (node && !href) {
-      if (node.nodeType == content.Node.ELEMENT_NODE &&
-          (node.localName == "a" ||
-           node.namespaceURI == "http://www.w3.org/1998/Math/MathML")) {
-        href = node.getAttribute("href") ||
-               node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
-        if (href) {
-          baseURI = node.ownerDocument.baseURIObject;
-          break;
-        }
-      }
-      node = node.parentNode;
-    }
-
-    // In case of XLink, we don't return the node we got href from since
-    // callers expect <a>-like elements.
-    // Note: makeURI() will throw if aUri is not a valid URI.
-    return [href ? Services.io.newURI(href, null, baseURI).spec : null, null,
-            node && node.ownerDocument.nodePrincipal];
-  }
 };
-ClickEventHandler.init();
-
-ContentLinkHandler.init(this);
-ContentMetaHandler.init(this);
-
-// TODO: Load this lazily so the JSM is run only if a relevant event/message fires.
-var pluginContent = new PluginContent(global);
-
-addEventListener("DOMWindowFocus", function(event) {
-  sendAsyncMessage("DOMWindowFocus", {});
-}, false);
-
-// We use this shim so that ContentWebRTC.jsm will not be loaded until
-// it is actually needed.
-var ContentWebRTCShim = message => ContentWebRTC.receiveMessage(message);
-
-addMessageListener("rtcpeer:Allow", ContentWebRTCShim);
-addMessageListener("rtcpeer:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:Allow", ContentWebRTCShim);
-addMessageListener("webrtc:Deny", ContentWebRTCShim);
-addMessageListener("webrtc:StopSharing", ContentWebRTCShim);
-
-addEventListener("pageshow", function(event) {
-  if (event.target == content.document) {
-    sendAsyncMessage("PageVisibility:Show", {
-      persisted: event.persisted,
-    });
-  }
-});
-addEventListener("pagehide", function(event) {
-  if (event.target == content.document) {
-    sendAsyncMessage("PageVisibility:Hide", {
-      persisted: event.persisted,
-    });
-  }
-});
-
-var PageMetadataMessenger = {
-  init() {
-    addMessageListener("PageMetadata:GetPageData", this);
-    addMessageListener("PageMetadata:GetMicroformats", this);
-  },
-  receiveMessage(message) {
-    switch (message.name) {
-      case "PageMetadata:GetPageData": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getData(content.document, target);
-        sendAsyncMessage("PageMetadata:PageDataResult", result);
-        break;
-      }
-      case "PageMetadata:GetMicroformats": {
-        let target = contextMenu.getTarget(message);
-        let result = PageMetadata.getMicroformats(content.document, target);
-        sendAsyncMessage("PageMetadata:MicroformatsResult", result);
-        break;
-      }
-    }
-  }
-};
-PageMetadataMessenger.init();
-
-addEventListener("InstallBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstallListener, false, true);
-addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstallListener, false, true);
-
-let OfflineApps = {
-  _docId: 0,
-  _docIdMap: new Map(),
-
-  _docManifestSet: new Set(),
-
-  _observerAdded: false,
-  registerWindow(aWindow) {
-    if (!this._observerAdded) {
-      this._observerAdded = true;
-      Services.obs.addObserver(this, "offline-cache-update-completed", true);
-    }
-    let manifestURI = this._getManifestURI(aWindow);
-    this._docManifestSet.add(manifestURI.spec);
-  },
-
-  handleEvent(event) {
-    if (event.type == "MozApplicationManifest") {
-      this.offlineAppRequested(event.originalTarget.defaultView);
-    }
-  },
-
-  _getManifestURI(aWindow) {
-    if (!aWindow.document.documentElement)
-      return null;
-
-    var attr = aWindow.document.documentElement.getAttribute("manifest");
-    if (!attr)
-      return null;
-
-    try {
-      return Services.io.newURI(attr, aWindow.document.characterSet,
-                                Services.io.newURI(aWindow.location.href));
-    } catch (e) {
-      return null;
-    }
-  },
-
-  offlineAppRequested(aContentWindow) {
-    this.registerWindow(aContentWindow);
-    if (!Services.prefs.getBoolPref("browser.offline-apps.notify")) {
-      return;
-    }
-
-    let currentURI = aContentWindow.document.documentURIObject;
-    // don't bother showing UI if the user has already made a decision
-    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
-      return;
-
-    try {
-      if (Services.prefs.getBoolPref("offline-apps.allow_by_default")) {
-        // all pages can use offline capabilities, no need to ask the user
-        return;
-      }
-    } catch (e) {
-      // this pref isn't set by default, ignore failures
-    }
-    let docId = ++this._docId;
-    this._docIdMap.set(docId, Cu.getWeakReference(aContentWindow.document));
-    sendAsyncMessage("OfflineApps:RequestPermission", {
-      uri: currentURI.spec,
-      docId,
-    });
-  },
-
-  _startFetching(aDocument) {
-    if (!aDocument.documentElement)
-      return;
-
-    let manifestURI = this._getManifestURI(aDocument.defaultView);
-    if (!manifestURI)
-      return;
-
-    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
-                        getService(Ci.nsIOfflineCacheUpdateService);
-    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject,
-                                 aDocument.nodePrincipal, aDocument.defaultView);
-  },
-
-  receiveMessage(aMessage) {
-    if (aMessage.name == "OfflineApps:StartFetching") {
-      let doc = this._docIdMap.get(aMessage.data.docId);
-      doc = doc && doc.get();
-      if (doc) {
-        this._startFetching(doc);
-      }
-      this._docIdMap.delete(aMessage.data.docId);
-    }
-  },
-
-  observe(aSubject, aTopic, aState) {
-    if (aTopic == "offline-cache-update-completed") {
-      let cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
-      let uri = cacheUpdate.manifestURI;
-      if (uri && this._docManifestSet.has(uri.spec)) {
-        sendAsyncMessage("OfflineApps:CheckUsage", {uri: uri.spec});
-      }
-    }
-  },
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
-                                          Ci.nsISupportsWeakReference]),
-};
-
-addEventListener("MozApplicationManifest", OfflineApps, false);
-addMessageListener("OfflineApps:StartFetching", OfflineApps);
--- a/browser/modules/PingCentre.jsm
+++ b/browser/modules/PingCentre.jsm
@@ -1,15 +1,15 @@
 /* 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/. */
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.importGlobalProperties(["fetch"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 ChromeUtils.defineModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 ChromeUtils.defineModuleGetter(this, "ClientID",
   "resource://gre/modules/ClientID.jsm");
 ChromeUtils.defineModuleGetter(this, "TelemetryEnvironment",
   "resource://gre/modules/TelemetryEnvironment.jsm");
 
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -6,18 +6,16 @@
 
 var EXPORTED_SYMBOLS = [ "PluginContent" ];
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.import("resource://gre/modules/BrowserUtils.jsm");
 
-Cu.importGlobalProperties(["InspectorUtils"]);
-
 XPCOMUtils.defineLazyGetter(this, "gNavigatorBundle", function() {
   const url = "chrome://browser/locale/browser.properties";
   return Services.strings.createBundle(url);
 });
 
 ChromeUtils.defineModuleGetter(this, "AppConstants",
   "resource://gre/modules/AppConstants.jsm");
 
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -133,16 +133,17 @@ BROWSER_CHROME_MANIFESTS += [
     'test/browser/formValidation/browser.ini',
 ]
 XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
 
 EXTRA_JS_MODULES += [
     'AboutNewTab.jsm',
     'AsyncTabSwitcher.jsm',
     'AttributionCode.jsm',
+    'BlockedSiteContent.jsm',
     'BrowserErrorReporter.jsm',
     'BrowserUITelemetry.jsm',
     'BrowserUsageTelemetry.jsm',
     'BrowserWindowTracker.jsm',
     'ContentClick.jsm',
     'ContentCrashHandlers.jsm',
     'ContentLinkHandler.jsm',
     'ContentMetaHandler.jsm',
@@ -151,16 +152,17 @@ EXTRA_JS_MODULES += [
     'ContentWebRTC.jsm',
     'ContextMenu.jsm',
     'ExtensionsUI.jsm',
     'Feeds.jsm',
     'FormSubmitObserver.jsm',
     'FormValidationHandler.jsm',
     'LaterRun.jsm',
     'LightWeightThemeWebInstallListener.jsm',
+    'NetErrorContent.jsm',
     'OpenInTabsUtils.jsm',
     'PageActions.jsm',
     'PageInfoListener.jsm',
     'PageStyleHandler.jsm',
     'PermissionUI.jsm',
     'PingCentre.jsm',
     'PluginContent.jsm',
     'ProcessHangMonitor.jsm',
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/configurations/Tabs.jsm
@@ -7,20 +7,21 @@
 var EXPORTED_SYMBOLS = ["Tabs"];
 
 const CUST_TAB = "chrome://browser/skin/customize.svg";
 const PREFS_TAB = "chrome://browser/skin/settings.svg";
 const DEFAULT_FAVICON_TAB = `data:text/html,<meta%20charset="utf-8"><title>No%20favicon</title>`;
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://testing-common/TestUtils.jsm");
 ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
 
-Cu.importGlobalProperties(["InspectorUtils"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]);
 
 var Tabs = {
   init(libDir) {},
 
   configurations: {
     fiveTabs: {
       selectors: ["#tabbrowser-tabs"],
       async applyConfig() {
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-6-linux64.json
@@ -0,0 +1,21 @@
+{
+    "llvm_revision": "326563",
+    "stages": "3",
+    "build_libcxx": true,
+    "build_type": "Release",
+    "assertions": false,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_600/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_600/final",
+    "lld_repo": "https://llvm.org/svn/llvm-project/lld/tags/RELEASE_600/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_600/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_600/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_600/final",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/builds/worker/workspace/build/src/gcc",
+    "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
+    "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
+    "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
+    "patches": [
+      "find_symbolizer_linux.patch"
+    ]
+}
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-6-macosx64.json
@@ -0,0 +1,26 @@
+{
+    "llvm_revision": "326563",
+    "stages": "1",
+    "build_libcxx": true,
+    "build_type": "Release",
+    "assertions": false,
+    "osx_cross_compile": true,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_600/final",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_600/final",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_600/final",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_600/final",
+    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_600/final",
+    "python_path": "/usr/bin/python2.7",
+    "gcc_dir": "/builds/worker/workspace/build/src/gcc",
+    "cc": "/builds/worker/workspace/build/src/clang/bin/clang",
+    "cxx": "/builds/worker/workspace/build/src/clang/bin/clang++",
+    "as": "/builds/worker/workspace/build/src/clang/bin/clang",
+    "ar": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ar",
+    "ranlib": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ranlib",
+    "libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-libtool",
+    "ld": "/builds/worker/workspace/build/src/clang/bin/clang",
+    "patches": [
+      "compiler-rt-cross-compile.patch",
+      "compiler-rt-no-codesign.patch"
+    ]
+}
deleted file mode 100644
--- a/build/build-clang/clang-6-pre-linux64.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
-    "llvm_revision": "317840",
-    "stages": "3",
-    "build_libcxx": true,
-    "build_type": "Release",
-    "assertions": false,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
-    "lld_repo": "https://llvm.org/svn/llvm-project/lld/trunk",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
-    "python_path": "/usr/bin/python2.7",
-    "gcc_dir": "/builds/worker/workspace/build/src/gcc",
-    "cc": "/builds/worker/workspace/build/src/gcc/bin/gcc",
-    "cxx": "/builds/worker/workspace/build/src/gcc/bin/g++",
-    "as": "/builds/worker/workspace/build/src/gcc/bin/gcc",
-    "patches": [
-      "find_symbolizer_linux.patch"
-    ]
-}
deleted file mode 100644
--- a/build/build-clang/clang-6-pre-macosx64.json
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-    "llvm_revision": "317840",
-    "stages": "1",
-    "build_libcxx": true,
-    "build_type": "Release",
-    "assertions": false,
-    "osx_cross_compile": true,
-    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
-    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
-    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
-    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
-    "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
-    "python_path": "/usr/bin/python2.7",
-    "gcc_dir": "/builds/worker/workspace/build/src/gcc",
-    "cc": "/builds/worker/workspace/build/src/clang/bin/clang",
-    "cxx": "/builds/worker/workspace/build/src/clang/bin/clang++",
-    "as": "/builds/worker/workspace/build/src/clang/bin/clang",
-    "ar": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ar",
-    "ranlib": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-ranlib",
-    "libtool": "/builds/worker/workspace/build/src/cctools/bin/x86_64-apple-darwin11-libtool",
-    "ld": "/builds/worker/workspace/build/src/clang/bin/clang",
-    "patches": [
-      "compiler-rt-cross-compile.patch",
-      "compiler-rt-no-codesign.patch",
-      "r321543.patch"
-    ]
-}
deleted file mode 100644
--- a/build/build-clang/r321543.patch
+++ /dev/null
@@ -1,33 +0,0 @@
-From: Yi Kong <yikong@google.com>
-Date: Thu, 28 Dec 2017 23:06:24 +0000
-Subject: [PATCH] Ignore the DISPATCH_NOESCAPE if not defined
-
-This macro is only defined after XCode 8, causing build breakage for
-build systems with prior versions. Ignore DISPATCH_NOESCAPE if not
-defined.
-
-Differential Revision: https://reviews.llvm.org/D41601
-
-
-git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@321543 91177308-0d34-0410-b5e6-96231b3b80d8
----
- lib/tsan/rtl/tsan_libdispatch_mac.cc | 5 +++++
- 1 file changed, 5 insertions(+)
-
-diff --git a/lib/tsan/rtl/tsan_libdispatch_mac.cc b/lib/tsan/rtl/tsan_libdispatch_mac.cc
-index eb22e4baa..d6c1ca662 100644
---- a/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
-+++ b/compiler-rt/lib/tsan/rtl/tsan_libdispatch_mac.cc
-@@ -25,6 +25,11 @@
- #include <dispatch/dispatch.h>
- #include <pthread.h>
- 
-+// DISPATCH_NOESCAPE is not defined prior to XCode 8.
-+#ifndef DISPATCH_NOESCAPE
-+#define DISPATCH_NOESCAPE
-+#endif
-+
- typedef long long_t;  // NOLINT
- 
- namespace __tsan {
-
--- a/dom/manifest/ImageObjectProcessor.jsm
+++ b/dom/manifest/ImageObjectProcessor.jsm
@@ -21,17 +21,19 @@
 /*globals Components */
 'use strict';
 const {
   utils: Cu,
   interfaces: Ci,
   classes: Cc
 } = Components;
 
-Cu.importGlobalProperties(['URL']);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGlobalGetters(this, ['URL']);
 const netutil = Cc['@mozilla.org/network/util;1']
   .getService(Ci.nsINetUtil);
 
 function ImageObjectProcessor(aConsole, aExtractor) {
   this.console = aConsole;
   this.extractor = aExtractor;
 }
 
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -19,17 +19,18 @@
  * TODO: The constructor should accept the UA's supported display modes.
  * TODO: hook up developer tools to console. (1086997).
  */
 /*globals Components, ValueExtractor, ImageObjectProcessor, ConsoleAPI*/
 'use strict';
 const {
   utils: Cu
 } = Components;
-Cu.importGlobalProperties(['URL']);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ['URL']);
 const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
   'browser'
 ]);
 const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
   'portrait-primary', 'portrait-secondary', 'landscape-primary',
   'landscape-secondary'
 ]);
 const textDirections = new Set(['ltr', 'rtl', 'auto']);
--- a/dom/manifest/ValueExtractor.jsm
+++ b/dom/manifest/ValueExtractor.jsm
@@ -8,17 +8,19 @@
 /*globals Components*/
 'use strict';
 const {
   classes: Cc,
   interfaces: Ci,
   utils: Cu
 } = Components;
 
-Cu.importGlobalProperties(["InspectorUtils"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGlobalGetters(this, ["InspectorUtils"]);
 
 function ValueExtractor(aConsole, aBundle) {
   this.console = aConsole;
   this.domBundle = aBundle;
 }
 
 ValueExtractor.prototype = {
   // This function takes a 'spec' object and destructures
--- a/dom/push/PushCrypto.jsm
+++ b/dom/push/PushCrypto.jsm
@@ -6,17 +6,17 @@
 'use strict';
 
 ChromeUtils.import('resource://gre/modules/Services.jsm');
 ChromeUtils.import('resource://gre/modules/XPCOMUtils.jsm');
 
 XPCOMUtils.defineLazyGetter(this, 'gDOMBundle', () =>
   Services.strings.createBundle('chrome://global/locale/dom/dom.properties'));
 
-Cu.importGlobalProperties(['crypto']);
+XPCOMUtils.defineLazyGlobalGetters(this, ['crypto']);
 
 var EXPORTED_SYMBOLS = ['PushCrypto', 'concatArray'];
 
 var UTF8 = new TextEncoder('utf-8');
 
 var ECDH_KEY = { name: 'ECDH', namedCurve: 'P-256' };
 var ECDSA_KEY =  { name: 'ECDSA', namedCurve: 'P-256' };
 
--- a/dom/push/PushDB.jsm
+++ b/dom/push/PushDB.jsm
@@ -2,17 +2,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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/IndexedDBHelper.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.importGlobalProperties(["indexedDB"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["indexedDB"]);
 
 var EXPORTED_SYMBOLS = ["PushDB"];
 
 XPCOMUtils.defineLazyGetter(this, "console", () => {
   let {ConsoleAPI} = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
   return new ConsoleAPI({
     maxLogLevelPref: "dom.push.loglevel",
     prefix: "PushDB",
--- a/dom/system/NetworkGeolocationProvider.js
+++ b/dom/system/NetworkGeolocationProvider.js
@@ -2,17 +2,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/. */
 
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 // PositionError has no interface object, so we can't use that here.
 const POSITION_UNAVAILABLE = 2;
 
 var gLoggingEnabled = false;
 
 /*
    The gLocationRequestTimeout controls how long we wait on receiving an update
--- a/gfx/src/nsCoord.h
+++ b/gfx/src/nsCoord.h
@@ -2,16 +2,18 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSCOORD_H
 #define NSCOORD_H
 
+#include "mozilla/FloatingPoint.h"
+
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsMathUtils.h"
 #include <math.h>
 #include <float.h>
 #include <stdlib.h>
 
 #include "nsDebug.h"
@@ -25,31 +27,19 @@
  * an integer number of device pixels, such at the CSS DPI is as close to
  * 96dpi as possible.
  */
 
 // This controls whether we're using integers or floats for coordinates. We
 // want to eventually use floats.
 //#define NS_COORD_IS_FLOAT
 
-inline float NS_IEEEPositiveInfinity() {
-  union { uint32_t mPRUint32; float mFloat; } pun;
-  pun.mPRUint32 = 0x7F800000;
-  return pun.mFloat;
-}
-inline bool NS_IEEEIsNan(float aF) {
-  union { uint32_t mBits; float mFloat; } pun;
-  pun.mFloat = aF;
-  return (pun.mBits & 0x7F800000) == 0x7F800000 &&
-    (pun.mBits & 0x007FFFFF) != 0;
-}
-
 #ifdef NS_COORD_IS_FLOAT
 typedef float nscoord;
-#define nscoord_MAX NS_IEEEPositiveInfinity()
+#define nscoord_MAX (mozilla::PositiveInfinity<float>())
 #else
 typedef int32_t nscoord;
 #define nscoord_MAX nscoord((1 << 30) - 1)
 #endif
 
 #define nscoord_MIN (-nscoord_MAX)
 
 inline void VERIFY_COORD(nscoord aCoord) {
@@ -233,17 +223,17 @@ NSCoordSaturatingSubtract(nscoord a, nsc
     }
 #endif
   }
 }
 
 inline float NSCoordToFloat(nscoord aCoord) {
   VERIFY_COORD(aCoord);
 #ifdef NS_COORD_IS_FLOAT
-  NS_ASSERTION(!NS_IEEEIsNan(aCoord), "NaN encountered in float conversion");
+  NS_ASSERTION(!mozilla::IsNaN(aCoord), "NaN encountered in float conversion");
 #endif
   return (float)aCoord;
 }
 
 /*
  * Coord Rounding Functions
  */
 inline nscoord NSToCoordFloor(float aValue)
--- a/intl/l10n/L10nRegistry.jsm
+++ b/intl/l10n/L10nRegistry.jsm
@@ -1,12 +1,13 @@
 const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
 const { MessageContext } = ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
-Cu.importGlobalProperties(["fetch"]);
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
 
 /**
  * L10nRegistry is a localization resource management system for Gecko.
  *
  * It manages the list of resource sources provided with the app and allows
  * for additional sources to be added and updated.
  *
  * It's primary purpose is to allow for building an iterator over MessageContext objects
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -8,20 +8,27 @@
 
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 
+#include <cfloat>
 #include <cmath>
 #include <cstdlib>
 #include <ctime>
 
+#if defined(XP_UNIX) && !defined(XP_DARWIN)
+#include <time.h>
+#else
+#include <chrono>
+#endif
+
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #ifdef DEBUG
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpAST.h"
@@ -4994,16 +5001,66 @@ AflLoop(JSContext* cx, unsigned argc, Va
         return false;
 
     args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
     return true;
 }
 #endif
 
 static bool
+MonotonicNow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    double now;
+
+// The std::chrono symbols are too new to be present in STL on all platforms we
+// care about, so use raw POSIX clock APIs when it might be necessary.
+#if defined(XP_UNIX) && !defined(XP_DARWIN)
+    auto ComputeNow = [](const timespec& ts) {
+        return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+    };
+
+    timespec ts;
+    if (false && clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+        // Use a monotonic clock if available.
+        now = ComputeNow(ts);
+    } else {
+        // Use a realtime clock as fallback.
+        if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
+            // Fail if no clock is available.
+            JS_ReportErrorASCII(cx, "can't retrieve system clock");
+            return false;
+        }
+
+        now = ComputeNow(ts);
+
+        // Manually enforce atomicity on a non-monotonic clock.
+        {
+            static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
+            while (!spinLock.compareExchange(false, true))
+                continue;
+
+            static double lastNow = -FLT_MAX;
+            now = lastNow = std::max(now, lastNow);
+
+            spinLock = false;
+        }
+    }
+#else
+    using std::chrono::duration_cast;
+    using std::chrono::milliseconds;
+    using std::chrono::steady_clock;
+    now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
+#endif // XP_UNIX && !XP_DARWIN
+
+    args.rval().setNumber(now);
+    return true;
+}
+
+static bool
 TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     double when = (mozilla::TimeStamp::Now() -
                    mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
     args.rval().setNumber(when);
     return true;
 }
@@ -5890,16 +5947,21 @@ gc::ZealModeHelpText),
 "  Get the value of a bound name in a module environment.\n"),
 
 #if defined(FUZZING) && defined(__AFL_COMPILER)
     JS_FN_HELP("aflloop", AflLoop, 1, 0,
 "aflloop(max_cnt)",
 "  Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
 #endif
 
+    JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
+"monotonicNow()",
+"  Return a timestamp reflecting the current elapsed system time.\n"
+"  This is monotonically increasing.\n"),
+
     JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
 "TimeSinceCreation()",
 "  Returns the time in milliseconds since process creation.\n"
 "  This uses a clock compatible with the profiler.\n"),
 
     JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
 "isConstructor(value)",
 "  Returns whether the value is considered IsConstructor.\n"),
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1783,63 +1783,61 @@ FreshlyInitializeBindings(BindingName* c
     for (const BindingName& binding : bindings)
         new (cursor++) BindingName(binding);
     return cursor;
 }
 
 Maybe<GlobalScope::Data*>
 NewGlobalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
 {
-
-    Vector<BindingName> funs(context);
     Vector<BindingName> vars(context);
     Vector<BindingName> lets(context);
     Vector<BindingName> consts(context);
 
     bool allBindingsClosedOver = pc->sc()->allBindingsClosedOver();
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
-        BindingName binding(bi.name(), allBindingsClosedOver || bi.closedOver());
+        bool closedOver = allBindingsClosedOver || bi.closedOver();
+
         switch (bi.kind()) {
-          case BindingKind::Var:
-            if (bi.declarationKind() == DeclarationKind::BodyLevelFunction) {
-                if (!funs.append(binding))
-                    return Nothing();
-            } else {
-                if (!vars.append(binding))
-                    return Nothing();
-            }
+          case BindingKind::Var: {
+            bool isTopLevelFunction = bi.declarationKind() == DeclarationKind::BodyLevelFunction;
+            BindingName binding(bi.name(), closedOver, isTopLevelFunction);
+            if (!vars.append(binding))
+                return Nothing();
             break;
-          case BindingKind::Let:
+          }
+          case BindingKind::Let: {
+            BindingName binding(bi.name(), closedOver);
             if (!lets.append(binding))
                 return Nothing();
             break;
-          case BindingKind::Const:
+          }
+          case BindingKind::Const: {
+            BindingName binding(bi.name(), closedOver);
             if (!consts.append(binding))
                 return Nothing();
             break;
+          }
           default:
             MOZ_CRASH("Bad global scope BindingKind");
         }
     }
 
     GlobalScope::Data* bindings = nullptr;
-    uint32_t numBindings = funs.length() + vars.length() + lets.length() + consts.length();
+    uint32_t numBindings = vars.length() + lets.length() + consts.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<GlobalScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         // The ordering here is important. See comments in GlobalScope.
         BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        cursor = FreshlyInitializeBindings(cursor, funs);
-
-        bindings->varStart = cursor - start;
         cursor = FreshlyInitializeBindings(cursor, vars);
 
         bindings->letStart = cursor - start;
         cursor = FreshlyInitializeBindings(cursor, lets);
 
         bindings->constStart = cursor - start;
         cursor = FreshlyInitializeBindings(cursor, consts);
 
@@ -1923,49 +1921,39 @@ Maybe<ModuleScope::Data*>
 ParserBase::newModuleScopeData(ParseContext::Scope& scope)
 {
     return NewModuleScopeData(context, scope, alloc, pc);
 }
 
 Maybe<EvalScope::Data*>
 NewEvalScopeData(JSContext* context, ParseContext::Scope& scope, LifoAlloc& alloc, ParseContext* pc)
 {
-    Vector<BindingName> funs(context);
     Vector<BindingName> vars(context);
 
     for (BindingIter bi = scope.bindings(pc); bi; bi++) {
         // Eval scopes only contain 'var' bindings. Make all bindings aliased
         // for now.
         MOZ_ASSERT(bi.kind() == BindingKind::Var);
-        BindingName binding(bi.name(), true);
-        if (bi.declarationKind() == DeclarationKind::BodyLevelFunction) {
-            if (!funs.append(binding))
-                return Nothing();
-        } else {
-            if (!vars.append(binding))
-                return Nothing();
-        }
+        bool isTopLevelFunction = bi.declarationKind() == DeclarationKind::BodyLevelFunction;
+        BindingName binding(bi.name(), true, isTopLevelFunction);
+        if (!vars.append(binding))
+            return Nothing();
     }
 
     EvalScope::Data* bindings = nullptr;
-    uint32_t numBindings = funs.length() + vars.length();
+    uint32_t numBindings = vars.length();
 
     if (numBindings > 0) {
         bindings = NewEmptyBindingData<EvalScope>(context, alloc, numBindings);
         if (!bindings)
             return Nothing();
 
         BindingName* start = bindings->trailingNames.start();
         BindingName* cursor = start;
 
-        // Keep track of what vars are functions. This is only used in BCE to omit
-        // superfluous DEFVARs.
-        cursor = FreshlyInitializeBindings(cursor, funs);
-
-        bindings->varStart = cursor - start;
         cursor = FreshlyInitializeBindings(cursor, vars);
 
         bindings->length = numBindings;
     }
 
     return Some(bindings);
 }
 
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -32,20 +32,20 @@
 #include "util/StringBuffer.h"
 #include "util/Unicode.h"
 #include "vm/HelperThreads.h"
 #include "vm/JSAtom.h"
 #include "vm/JSCompartment.h"
 #include "vm/JSContext.h"
 
 using mozilla::ArrayLength;
+using mozilla::IsAscii;
 using mozilla::IsAsciiAlpha;
 using mozilla::IsAsciiDigit;
 using mozilla::MakeScopeExit;
-using mozilla::PodArrayZero;
 using mozilla::PodCopy;
 
 struct ReservedWordInfo
 {
     const char* chars;         // C string with reserved word text
     js::frontend::TokenKind tokentype;
 };
 
@@ -415,28 +415,23 @@ TokenStreamAnyChars::TokenStreamAnyChars
     prevLinebase(size_t(-1)),
     filename_(options.filename()),
     displayURL_(nullptr),
     sourceMapURL_(nullptr),
     cx(cx),
     mutedErrors(options.mutedErrors()),
     strictModeGetter(smg)
 {
-    // Nb: the following tables could be static, but initializing them here is
-    // much easier.  Don't worry, the time to initialize them for each
-    // TokenStream is trivial.  See bug 639420.
-
-    // See Parser::assignExpr() for an explanation of isExprEnding[].
-    PodArrayZero(isExprEnding);
-    isExprEnding[size_t(TokenKind::Comma)] = 1;
-    isExprEnding[size_t(TokenKind::Semi)] = 1;
-    isExprEnding[size_t(TokenKind::Colon)] = 1;
-    isExprEnding[size_t(TokenKind::Rp)] = 1;
-    isExprEnding[size_t(TokenKind::Rb)] = 1;
-    isExprEnding[size_t(TokenKind::Rc)] = 1;
+    // |isExprEnding| was initially zeroed: overwrite the true entries here.
+    isExprEnding[size_t(TokenKind::Comma)] = true;
+    isExprEnding[size_t(TokenKind::Semi)] = true;
+    isExprEnding[size_t(TokenKind::Colon)] = true;
+    isExprEnding[size_t(TokenKind::Rp)] = true;
+    isExprEnding[size_t(TokenKind::Rb)] = true;
+    isExprEnding[size_t(TokenKind::Rc)] = true;
 }
 
 template<typename CharT>
 TokenStreamCharsBase<CharT>::TokenStreamCharsBase(JSContext* cx, const CharT* chars, size_t length,
                                                   size_t startOffset)
   : sourceUnits(chars, length, startOffset),
     tokenbuf(cx)
 {}
@@ -489,20 +484,22 @@ TokenStreamAnyChars::undoInternalUpdateL
 }
 
 MOZ_ALWAYS_INLINE void
 TokenStreamAnyChars::updateFlagsForEOL()
 {
     flags.isDirtyLine = false;
 }
 
-// This gets the next char, normalizing all EOL sequences to '\n' as it goes.
+// This gets a full code point, starting from an already-consumed leading code
+// unit, normalizing EOL sequences to '\n', also updating line/column info as
+// needed.
 template<class AnyCharsAccess>
 bool
-TokenStreamChars<char16_t, AnyCharsAccess>::getChar(int32_t* cp)
+TokenStreamChars<char16_t, AnyCharsAccess>::getCodePoint(int32_t* cp)
 {
     TokenStreamAnyChars& anyChars = anyCharsAccess();
 
     if (MOZ_UNLIKELY(!sourceUnits.hasRawChars())) {
         anyChars.flags.isEOF = true;
         *cp = EOF;
         return true;
     }
@@ -531,40 +528,92 @@ TokenStreamChars<char16_t, AnyCharsAcces
 
     if (!updateLineInfoForEOL())
         return false;
 
     *cp = '\n';
     return true;
 }
 
-// This gets the next char. It does nothing special with EOL sequences, not
-// even updating the line counters.  It can be used safely if (a) the
-// resulting char is guaranteed to be ungotten (by ungetCharIgnoreEOL()) if
-// it's an EOL, and (b) the line-related state (lineno, linebase) is not used
-// before it's ungotten.
+template<class AnyCharsAccess>
+bool
+TokenStreamChars<char16_t, AnyCharsAccess>::getNonAsciiCodePoint(char16_t lead, int32_t* codePoint)
+{
+    MOZ_ASSERT(!isAsciiCodePoint(lead),
+               "ASCII code unit/point must be handled separately");
+    MOZ_ASSERT(lead == sourceUnits.previousCodeUnit(),
+               "getNonAsciiCodePoint called incorrectly");
+
+    // The code point is usually |lead|: overwrite later if needed.
+    *codePoint = lead;
+
+    // Dispense with single-unit code points ("code points", when a lone
+    // trailing surrogate is encountered).
+    if (MOZ_LIKELY(!unicode::IsLeadSurrogate(lead))) {
+        if (MOZ_UNLIKELY(lead == unicode::LINE_SEPARATOR ||
+                         lead == unicode::PARA_SEPARATOR))
+        {
+            if (!updateLineInfoForEOL()) {
+#ifdef DEBUG
+                *codePoint = EOF; // sentinel value to hopefully cause errors
+#endif
+                MOZ_MAKE_MEM_UNDEFINED(codePoint, sizeof(*codePoint));
+                return false;
+            }
+
+            *codePoint = '\n';
+        } else {
+            MOZ_ASSERT(!SourceUnits::isRawEOLChar(*codePoint));
+        }
+
+        return true;
+    }
+
+    // If there are no more units, or the next unit isn't a trailing surrogate,
+    // it's also a "code point".
+    if (MOZ_UNLIKELY(!sourceUnits.hasRawChars() ||
+                     !unicode::IsTrailSurrogate(sourceUnits.peekCodeUnit())))
+    {
+        MOZ_ASSERT(!SourceUnits::isRawEOLChar(*codePoint));
+        return true;
+    }
+
+    // Otherwise we have a multi-unit code point.
+    *codePoint = unicode::UTF16Decode(lead, sourceUnits.getCodeUnit());
+    MOZ_ASSERT(!SourceUnits::isRawEOLChar(*codePoint));
+    return true;
+}
+
+// This gets the next code unit -- the next numeric sub-unit of source text,
+// possibly smaller than a full code point.  It is simple and stupid, and it
+// doesn't understand EOL, update line counters, or anything like that.  If you
+// use it to consume an EOL sequence, line counters *will not* be correct for
+// subsequent code.
+//
+// Only use this if (a) the resulting code unit is guaranteed to be ungotten
+// (by ungetCodeUnit()) if it's an EOL, and (b) the line-related state (lineno,
+// linebase) is not used before it's ungotten.
 template<typename CharT, class AnyCharsAccess>
 int32_t
-GeneralTokenStreamChars<CharT, AnyCharsAccess>::getCharIgnoreEOL()
+GeneralTokenStreamChars<CharT, AnyCharsAccess>::getCodeUnit()
 {
     if (MOZ_LIKELY(sourceUnits.hasRawChars()))
         return sourceUnits.getCodeUnit();
 
     anyCharsAccess().flags.isEOF = true;
     return EOF;
 }
 
 template<typename CharT, class AnyCharsAccess>
 void
 GeneralTokenStreamChars<CharT, AnyCharsAccess>::ungetChar(int32_t c)
 {
     if (c == EOF)
         return;
 
-    MOZ_ASSERT(!sourceUnits.atStart());
     sourceUnits.ungetCodeUnit();
     if (c == '\n') {
         int32_t c2 = sourceUnits.peekCodeUnit();
         MOZ_ASSERT(SourceUnits::isRawEOLChar(c2));
 
         // If it's a \r\n sequence, also unget the \r.
         if (c2 == CharT('\n') && !sourceUnits.atStart())
             sourceUnits.ungetOptionalCRBeforeLF();
@@ -572,60 +621,74 @@ GeneralTokenStreamChars<CharT, AnyCharsA
         anyCharsAccess().undoInternalUpdateLineInfoForEOL();
     } else {
         MOZ_ASSERT(sourceUnits.peekCodeUnit() == c);
     }
 }
 
 template<typename CharT>
 void
-TokenStreamCharsBase<CharT>::ungetCharIgnoreEOL(int32_t c)
+TokenStreamCharsBase<CharT>::ungetCodeUnit(int32_t c)
 {
     if (c == EOF)
         return;
 
-    MOZ_ASSERT(!sourceUnits.atStart());
     sourceUnits.ungetCodeUnit();
 }
 
 template<class AnyCharsAccess>
 void
 TokenStreamChars<char16_t, AnyCharsAccess>::ungetCodePointIgnoreEOL(uint32_t codePoint)
 {
     MOZ_ASSERT(!sourceUnits.atStart());
 
     unsigned numUnits = 0;
     char16_t units[2];
     unicode::UTF16Encode(codePoint, units, &numUnits);
 
     MOZ_ASSERT(numUnits == 1 || numUnits == 2);
 
     while (numUnits-- > 0)
-        ungetCharIgnoreEOL(units[numUnits]);
+        ungetCodeUnit(units[numUnits]);
+}
+
+template<class AnyCharsAccess>
+void
+TokenStreamChars<char16_t, AnyCharsAccess>::ungetLineTerminator()
+{
+    sourceUnits.ungetCodeUnit();
+
+    char16_t last = sourceUnits.peekCodeUnit();
+    MOZ_ASSERT(SourceUnits::isRawEOLChar(last));
+
+    if (last == '\n')
+        sourceUnits.ungetOptionalCRBeforeLF();
+
+    anyCharsAccess().undoInternalUpdateLineInfoForEOL();
 }
 
 // Return true iff |n| raw characters can be read from this without reading past
 // EOF, and copy those characters into |cp| if so.  The characters are not
 // consumed: use skipChars(n) to do so after checking that the consumed
 // characters had appropriate values.
 template<typename CharT, class AnyCharsAccess>
 bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::peekChars(int n, CharT* cp)
 {
     int i;
     for (i = 0; i < n; i++) {
-        int32_t c = getCharIgnoreEOL();
+        int32_t c = getCodeUnit();
         if (c == EOF)
             break;
 
         cp[i] = char16_t(c);
     }
 
     for (int j = i - 1; j >= 0; j--)
-        ungetCharIgnoreEOL(cp[j]);
+        ungetCodeUnit(cp[j]);
 
     return i == n;
 }
 
 template<typename CharT>
 size_t
 SourceUnits<CharT>::findEOLMax(size_t start, size_t max)
 {
@@ -650,17 +713,17 @@ SourceUnits<CharT>::findEOLMax(size_t st
 
 template<typename CharT, class AnyCharsAccess>
 bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::advance(size_t position)
 {
     const CharT* end = sourceUnits.codeUnitPtrAt(position);
     while (sourceUnits.addressOfNextCodeUnit() < end) {
         int32_t c;
-        if (!getChar(&c))
+        if (!getCodePoint(&c))
             return false;
     }
 
     TokenStreamAnyChars& anyChars = anyCharsAccess();
     Token* cur = const_cast<Token*>(&anyChars.currentToken());
     cur->pos.begin = sourceUnits.offset();
     MOZ_MAKE_MEM_UNDEFINED(&cur->type, sizeof(cur->type));
     anyChars.lookahead = 0;
@@ -1004,80 +1067,80 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 // We have encountered a '\': check for a Unicode escape sequence after it.
 // Return the length of the escape sequence and the character code point (by
 // value) if we found a Unicode escape sequence.  Otherwise, return 0.  In both
 // cases, do not advance along the buffer.
 template<typename CharT, class AnyCharsAccess>
 uint32_t
 TokenStreamSpecific<CharT, AnyCharsAccess>::peekUnicodeEscape(uint32_t* codePoint)
 {
-    int32_t c = getCharIgnoreEOL();
+    int32_t c = getCodeUnit();
     if (c != 'u') {
-        ungetCharIgnoreEOL(c);
+        ungetCodeUnit(c);
         return 0;
     }
 
     CharT cp[3];
     uint32_t length;
-    c = getCharIgnoreEOL();
+    c = getCodeUnit();
     if (JS7_ISHEX(c) && peekChars(3, cp) &&
         JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]))
     {
         *codePoint = (JS7_UNHEX(c) << 12) |
                      (JS7_UNHEX(cp[0]) << 8) |
                      (JS7_UNHEX(cp[1]) << 4) |
                      JS7_UNHEX(cp[2]);
         length = 5;
     } else if (c == '{') {
         length = peekExtendedUnicodeEscape(codePoint);
     } else {
         length = 0;
     }
 
-    ungetCharIgnoreEOL(c);
-    ungetCharIgnoreEOL('u');
+    ungetCodeUnit(c);
+    ungetCodeUnit('u');
     return length;
 }
 
 template<typename CharT, class AnyCharsAccess>
 uint32_t
 TokenStreamSpecific<CharT, AnyCharsAccess>::peekExtendedUnicodeEscape(uint32_t* codePoint)
 {
     // The opening brace character was already read.
-    int32_t c = getCharIgnoreEOL();
+    int32_t c = getCodeUnit();
 
     // Skip leading zeros.
     uint32_t leadingZeros = 0;
     while (c == '0') {
         leadingZeros++;
-        c = getCharIgnoreEOL();
+        c = getCodeUnit();
     }
 
     CharT cp[6];
     size_t i = 0;
     uint32_t code = 0;
     while (JS7_ISHEX(c) && i < 6) {
         cp[i++] = c;
         code = code << 4 | JS7_UNHEX(c);
-        c = getCharIgnoreEOL();
+        c = getCodeUnit();
     }
 
     uint32_t length;
     if (c == '}' && (leadingZeros > 0 || i > 0) && code <= unicode::NonBMPMax) {
         *codePoint = code;
         length = leadingZeros + i + 3;
     } else {
         length = 0;
     }
 
-    ungetCharIgnoreEOL(c);
+    ungetCodeUnit(c);
     while (i--)
-        ungetCharIgnoreEOL(cp[i]);
+        ungetCodeUnit(cp[i]);
     while (leadingZeros--)
-        ungetCharIgnoreEOL('0');
+        ungetCodeUnit('0');
 
     return length;
 }
 
 template<typename CharT, class AnyCharsAccess>
 uint32_t
 TokenStreamSpecific<CharT, AnyCharsAccess>::matchUnicodeEscapeIdStart(uint32_t* codePoint)
 {
@@ -1189,19 +1252,19 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         if (c == EOF || unicode::IsSpaceOrBOM2(c))
             break;
 
         consumeKnownChar(c);
 
         // Debugging directives can occur in both single- and multi-line
         // comments. If we're currently inside a multi-line comment, we also
         // need to recognize multi-line comment terminators.
-        if (isMultiline && c == '*' && matchChar('/')) {
-            ungetCharIgnoreEOL('/');
-            ungetCharIgnoreEOL('*');
+        if (isMultiline && c == '*' && matchCodeUnit('/')) {
+            ungetCodeUnit('/');
+            ungetCodeUnit('*');
             break;
         }
 
         if (!tokenbuf.append(c))
             return false;
     } while (true);
 
     if (tokenbuf.empty()) {
@@ -1314,21 +1377,21 @@ template<class AnyCharsAccess>
 void
 TokenStreamChars<char16_t, AnyCharsAccess>::matchMultiUnitCodePointSlow(char16_t lead,
                                                                         uint32_t* codePoint)
 {
     MOZ_ASSERT(unicode::IsLeadSurrogate(lead),
                "matchMultiUnitCodepoint should have ensured |lead| is a lead "
                "surrogate");
 
-    int32_t maybeTrail = getCharIgnoreEOL();
+    int32_t maybeTrail = getCodeUnit();
     if (MOZ_LIKELY(unicode::IsTrailSurrogate(maybeTrail))) {
         *codePoint = unicode::UTF16Decode(lead, maybeTrail);
     } else {
-        ungetCharIgnoreEOL(maybeTrail);
+        ungetCodeUnit(maybeTrail);
         *codePoint = 0;
     }
 }
 
 template<typename CharT, class AnyCharsAccess>
 bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::putIdentInTokenbuf(const CharT* identStart)
 {
@@ -1337,17 +1400,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
     auto restoreNextRawCharAddress =
         MakeScopeExit([this, originalAddress]() {
             this->sourceUnits.setAddressOfNextCodeUnit(originalAddress);
         });
 
     tokenbuf.clear();
     for (;;) {
-        int32_t c = getCharIgnoreEOL();
+        int32_t c = getCodeUnit();
 
         uint32_t codePoint;
         if (!matchMultiUnitCodePoint(c, &codePoint))
             return false;
         if (codePoint) {
             if (!unicode::IsIdentifierPart(codePoint))
                 break;
         } else {
@@ -1376,17 +1439,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
     // Run the bad-token code for every path out of this function except the
     // two success-cases.
     auto noteBadToken = MakeScopeExit([this]() {
         this->badToken();
     });
 
     int c;
     while (true) {
-        c = getCharIgnoreEOL();
+        c = getCodeUnit();
         if (c == EOF)
             break;
 
         uint32_t codePoint;
         if (!matchMultiUnitCodePoint(c, &codePoint))
             return false;
         if (codePoint) {
             if (!unicode::IsIdentifierPart(codePoint))
@@ -1397,17 +1460,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
         if (!unicode::IsIdentifierPart(char16_t(c))) {
             uint32_t qc;
             if (c != '\\' || !matchUnicodeEscapeIdent(&qc))
                 break;
             escaping = IdentifierEscapes::SawUnicodeEscape;
         }
     }
-    ungetCharIgnoreEOL(c);
+    ungetCodeUnit(c);
 
     const CharT* chars;
     size_t length;
     if (escaping == IdentifierEscapes::SawUnicodeEscape) {
         // Identifiers containing Unicode escapes have to be converted into
         // tokenbuf before atomizing.
         if (!putIdentInTokenbuf(identStart))
             return false;
@@ -1520,80 +1583,80 @@ static_assert(LastCharKind < (1 << (size
               "Elements of firstCharKinds[] are too small");
 
 template<typename CharT, class AnyCharsAccess>
 void
 GeneralTokenStreamChars<CharT, AnyCharsAccess>::consumeRestOfSingleLineComment()
 {
     int32_t c;
     do {
-        c = getCharIgnoreEOL();
+        c = getCodeUnit();
     } while (c != EOF && !SourceUnits::isRawEOLChar(c));
 
-    ungetCharIgnoreEOL(c);
+    ungetCodeUnit(c);
 }
 
 template<typename CharT, class AnyCharsAccess>
 MOZ_MUST_USE bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::decimalNumber(int c, TokenStart start,
                                                           const CharT* numStart,
                                                           Modifier modifier, TokenKind* out)
 {
     // Run the bad-token code for every path out of this function except the
     // one success-case.
     auto noteBadToken = MakeScopeExit([this]() {
         this->badToken();
     });
 
     // Consume integral component digits.
     while (IsAsciiDigit(c))
-        c = getCharIgnoreEOL();
+        c = getCodeUnit();
 
     // Numbers contain no escapes, so we can read directly from |sourceUnits|.
     double dval;
     DecimalPoint decimalPoint = NoDecimal;
     if (c != '.' && c != 'e' && c != 'E') {
-        ungetCharIgnoreEOL(c);
+        ungetCodeUnit(c);
 
         // Most numbers are pure decimal integers without fractional component
         // or exponential notation.  Handle that with optimized code.
         if (!GetDecimalInteger(anyCharsAccess().cx, numStart, sourceUnits.addressOfNextCodeUnit(),
                                &dval))
         {
             return false;
         }
     } else {
         // Consume any decimal dot and fractional component.
         if (c == '.') {
             decimalPoint = HasDecimal;
             do {
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
             } while (IsAsciiDigit(c));
         }
 
         // Consume any exponential notation.
         if (c == 'e' || c == 'E') {
-            c = getCharIgnoreEOL();
+            c = getCodeUnit();
             if (c == '+' || c == '-')
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
 
             // Exponential notation must contain at least one digit.
             if (!IsAsciiDigit(c)) {
-                ungetCharIgnoreEOL(c);
+                ungetCodeUnit(c);
                 error(JSMSG_MISSING_EXPONENT);
                 return false;
             }
 
             // Consume exponential digits.
             do {
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
             } while (IsAsciiDigit(c));
         }
 
-        ungetCharIgnoreEOL(c);
+        ungetCodeUnit(c);
 
         const CharT* dummy;
         if (!js_strtod(anyCharsAccess().cx, numStart, sourceUnits.addressOfNextCodeUnit(), &dummy,
                        &dval))
         {
            return false;
         }
     }
@@ -1620,27 +1683,137 @@ TokenStreamSpecific<CharT, AnyCharsAcces
             if (unicode::IsIdentifierStart(codePoint)) {
                 // This will properly point at the start of the code point.
                 error(JSMSG_IDSTART_AFTER_NUMBER);
                 return false;
             }
         } else {
             // If not a multi-unit code point, we only need to unget the single
             // code unit consumed.
-            ungetCharIgnoreEOL(c);
+            ungetCodeUnit(c);
         }
     }
 
     noteBadToken.release();
     newNumberToken(dval, decimalPoint, start, modifier, out);
     return true;
 }
 
 template<typename CharT, class AnyCharsAccess>
 MOZ_MUST_USE bool
+TokenStreamSpecific<CharT, AnyCharsAccess>::regexpLiteral(TokenStart start, TokenKind* out)
+{
+    MOZ_ASSERT(sourceUnits.previousCodeUnit() == '/');
+    tokenbuf.clear();
+
+    auto ProcessNonAsciiCodePoint = [this](CharT lead) {
+        int32_t codePoint;
+        if (!this->getNonAsciiCodePoint(lead, &codePoint))
+            return false;
+
+        if (codePoint == '\n') {
+            this->ungetLineTerminator();
+            this->reportError(JSMSG_UNTERMINATED_REGEXP);
+            return false;
+        }
+
+        return this->appendCodePointToTokenbuf(codePoint);
+    };
+
+    auto ReportUnterminatedRegExp = [this](CharT unit) {
+        this->ungetCodeUnit(unit);
+        this->error(JSMSG_UNTERMINATED_REGEXP);
+    };
+
+    bool inCharClass = false;
+    do {
+        int32_t unit = getCodeUnit();
+        if (unit == EOF) {
+            ReportUnterminatedRegExp(unit);
+            return badToken();
+        }
+
+        if (MOZ_LIKELY(isAsciiCodePoint(unit))) {
+            if (unit == '\\')  {
+                if (!tokenbuf.append(unit))
+                    return badToken();
+
+                unit = getCodeUnit();
+                if (unit == EOF) {
+                    ReportUnterminatedRegExp(unit);
+                    return badToken();
+                }
+
+                // Fallthrough only handles ASCII code points, so
+                // deal with non-ASCII and skip everything else.
+                if (MOZ_UNLIKELY(!isAsciiCodePoint(unit))) {
+                    if (!ProcessNonAsciiCodePoint(unit))
+                        return badToken();
+
+                    continue;
+                }
+            } else if (unit == '[') {
+                inCharClass = true;
+            } else if (unit == ']') {
+                inCharClass = false;
+            } else if (unit == '/' && !inCharClass) {
+                // For IE compat, allow unescaped / in char classes.
+                break;
+            }
+
+            if (unit == '\r' || unit == '\n') {
+                ReportUnterminatedRegExp(unit);
+                return badToken();
+            }
+
+            if (!tokenbuf.append(unit))
+                return badToken();
+        } else {
+            if (!ProcessNonAsciiCodePoint(unit))
+                return badToken();
+        }
+    } while (true);
+
+    int32_t unit;
+    RegExpFlag reflags = NoFlags;
+    while (true) {
+        RegExpFlag flag;
+        unit = getCodeUnit();
+        if (unit == 'g')
+            flag = GlobalFlag;
+        else if (unit == 'i')
+            flag = IgnoreCaseFlag;
+        else if (unit == 'm')
+            flag = MultilineFlag;
+        else if (unit == 'y')
+            flag = StickyFlag;
+        else if (unit == 'u')
+            flag = UnicodeFlag;
+        else if (IsAsciiAlpha(unit))
+            flag = NoFlags;
+        else
+            break;
+
+        if ((reflags & flag) || flag == NoFlags) {
+            ungetCodeUnit(unit);
+            char buf[2] = { char(unit), '\0' };
+            error(JSMSG_BAD_REGEXP_FLAG, buf);
+            return badToken();
+        }
+
+        reflags = RegExpFlag(reflags | flag);
+    }
+    ungetCodeUnit(unit);
+
+    newRegExpToken(reflags, start, out);
+    return true;
+}
+
+template<typename CharT, class AnyCharsAccess>
+MOZ_MUST_USE bool
 TokenStreamSpecific<CharT, AnyCharsAccess>::getTokenInternal(TokenKind* const ttp,
                                                              const Modifier modifier)
 {
     // Assume we'll fail: success cases will overwrite this.
 #ifdef DEBUG
     *ttp = TokenKind::Limit;
 #endif
     MOZ_MAKE_MEM_UNDEFINED(ttp, sizeof(*ttp));
@@ -1780,59 +1953,59 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         // number starting with '0' that contains '8' or '9' and is treated as
         // decimal) number.
         //
         if (c1kind == ZeroDigit) {
             TokenStart start(sourceUnits, -1);
 
             int radix;
             const CharT* numStart;
-            c = getCharIgnoreEOL();
+            c = getCodeUnit();
             if (c == 'x' || c == 'X') {
                 radix = 16;
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
                 if (!JS7_ISHEX(c)) {
-                    ungetCharIgnoreEOL(c);
+                    ungetCodeUnit(c);
                     reportError(JSMSG_MISSING_HEXDIGITS);
                     return badToken();
                 }
 
                 // one past the '0x'
                 numStart = sourceUnits.addressOfNextCodeUnit() - 1;
 
                 while (JS7_ISHEX(c))
-                    c = getCharIgnoreEOL();
+                    c = getCodeUnit();
             } else if (c == 'b' || c == 'B') {
                 radix = 2;
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
                 if (c != '0' && c != '1') {
-                    ungetCharIgnoreEOL(c);
+                    ungetCodeUnit(c);
                     reportError(JSMSG_MISSING_BINARY_DIGITS);
                     return badToken();
                 }
 
                 // one past the '0b'
                 numStart = sourceUnits.addressOfNextCodeUnit() - 1;
 
                 while (c == '0' || c == '1')
-                    c = getCharIgnoreEOL();
+                    c = getCodeUnit();
             } else if (c == 'o' || c == 'O') {
                 radix = 8;
-                c = getCharIgnoreEOL();
+                c = getCodeUnit();
                 if (c < '0' || c > '7') {
-                    ungetCharIgnoreEOL(c);
+                    ungetCodeUnit(c);
                     reportError(JSMSG_MISSING_OCTAL_DIGITS);
                     return badToken();
                 }
 
                 // one past the '0o'
                 numStart = sourceUnits.addressOfNextCodeUnit() - 1;
 
                 while ('0' <= c && c <= '7')
-                    c = getCharIgnoreEOL();
+                    c = getCodeUnit();
             } else if (IsAsciiDigit(c)) {
                 radix = 8;
                 // one past the '0'
                 numStart = sourceUnits.addressOfNextCodeUnit() - 1;
 
                 do {
                     // Octal integer literals are not permitted in strict mode
                     // code.
@@ -1846,25 +2019,25 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                     if (c >= '8') {
                         if (!warning(JSMSG_BAD_OCTAL, c == '8' ? "08" : "09"))
                             return badToken();
 
                         // Use the decimal scanner for the rest of the number.
                         return decimalNumber(c, start, numStart, modifier, ttp);
                     }
 
-                    c = getCharIgnoreEOL();
+                    c = getCodeUnit();
                 } while (IsAsciiDigit(c));
             } else {
                 // '0' not followed by [XxBbOo0-9];  scan as a decimal number.
                 numStart = sourceUnits.addressOfNextCodeUnit() - 1;
 
                 return decimalNumber(c, start, numStart, modifier, ttp);
             }
-            ungetCharIgnoreEOL(c);
+            ungetCodeUnit(c);
 
             if (c != EOF) {
                 if (unicode::IsIdentifierStart(char16_t(c))) {
                     error(JSMSG_IDSTART_AFTER_NUMBER);
                     return badToken();
                 }
 
                 consumeKnownCharIgnoreEOL(c);
@@ -1881,17 +2054,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                         // This will properly point at the start of the code
                         // point.
                         error(JSMSG_IDSTART_AFTER_NUMBER);
                         return badToken();
                     }
                 } else {
                     // If not a multi-unit code point, we only need to unget
                     // the single code unit consumed.
-                    ungetCharIgnoreEOL(c);
+                    ungetCodeUnit(c);
                 }
             }
 
             double dval;
             const char16_t* dummy;
             if (!GetPrefixInteger(anyCharsAccess().cx, numStart,
                                   sourceUnits.addressOfNextCodeUnit(), radix, &dummy, &dval))
             {
@@ -1911,268 +2084,210 @@ TokenStreamSpecific<CharT, AnyCharsAcces
         //
         TokenStart start(sourceUnits, -1);
         TokenKind simpleKind;
 #ifdef DEBUG
         simpleKind = TokenKind::Limit; // sentinel value for code after switch
 #endif
         switch (c) {
           case '.':
-            c = getCharIgnoreEOL();
+            c = getCodeUnit();
             if (IsAsciiDigit(c)) {
                 return decimalNumber('.', start, sourceUnits.addressOfNextCodeUnit() - 2, modifier,
                                      ttp);
             }
 
             if (c == '.') {
-                if (matchChar('.')) {
+                if (matchCodeUnit('.')) {
                     simpleKind = TokenKind::TripleDot;
                     break;
                 }
             }
-            ungetCharIgnoreEOL(c);
+            ungetCodeUnit(c);
 
             simpleKind = TokenKind::Dot;
             break;
 
           case '=':
-            if (matchChar('='))
-                simpleKind = matchChar('=') ? TokenKind::StrictEq : TokenKind::Eq;
-            else if (matchChar('>'))
+            if (matchCodeUnit('='))
+                simpleKind = matchCodeUnit('=') ? TokenKind::StrictEq : TokenKind::Eq;
+            else if (matchCodeUnit('>'))
                 simpleKind = TokenKind::Arrow;
             else
                 simpleKind = TokenKind::Assign;
             break;
 
           case '+':
-            if (matchChar('+'))
+            if (matchCodeUnit('+'))
                 simpleKind = TokenKind::Inc;
             else
-                simpleKind = matchChar('=') ? TokenKind::AddAssign : TokenKind::Add;
+                simpleKind = matchCodeUnit('=') ? TokenKind::AddAssign : TokenKind::Add;
             break;
 
           case '\\': {
             uint32_t qc;
             if (uint32_t escapeLength = matchUnicodeEscapeIdStart(&qc)) {
                 return identifierName(start,
                                       sourceUnits.addressOfNextCodeUnit() - escapeLength - 1,
                                       IdentifierEscapes::SawUnicodeEscape, modifier, ttp);
             }
 
             // We could point "into" a mistyped escape, e.g. for "\u{41H}" we
             // could point at the 'H'.  But we don't do that now, so the
             // character after the '\' isn't necessarily bad, so just point at
             // the start of the actually-invalid escape.
-            ungetCharIgnoreEOL('\\');
+            ungetCodeUnit('\\');
             error(JSMSG_BAD_ESCAPE);
             return badToken();
           }
 
           case '|':
-            if (matchChar('|'))
+            if (matchCodeUnit('|'))
                 simpleKind = TokenKind::Or;
 #ifdef ENABLE_PIPELINE_OPERATOR
-            else if (matchChar('>'))
+            else if (matchCodeUnit('>'))
                 simpleKind = TokenKind::Pipeline;
 #endif
             else
-                simpleKind = matchChar('=') ? TokenKind::BitOrAssign : TokenKind::BitOr;
+                simpleKind = matchCodeUnit('=') ? TokenKind::BitOrAssign : TokenKind::BitOr;
             break;
 
           case '^':
-            simpleKind = matchChar('=') ? TokenKind::BitXorAssign : TokenKind::BitXor;
+            simpleKind = matchCodeUnit('=') ? TokenKind::BitXorAssign : TokenKind::BitXor;
             break;
 
           case '&':
-            if (matchChar('&'))
+            if (matchCodeUnit('&'))
                 simpleKind = TokenKind::And;
             else
-                simpleKind = matchChar('=') ? TokenKind::BitAndAssign : TokenKind::BitAnd;
+                simpleKind = matchCodeUnit('=') ? TokenKind::BitAndAssign : TokenKind::BitAnd;
             break;
 
           case '!':
-            if (matchChar('='))
-                simpleKind = matchChar('=') ? TokenKind::StrictNe : TokenKind::Ne;
+            if (matchCodeUnit('='))
+                simpleKind = matchCodeUnit('=') ? TokenKind::StrictNe : TokenKind::Ne;
             else
                 simpleKind = TokenKind::Not;
             break;
 
           case '<':
             if (anyCharsAccess().options().allowHTMLComments) {
                 // Treat HTML begin-comment as comment-till-end-of-line.
-                if (matchChar('!')) {
-                    if (matchChar('-')) {
-                        if (matchChar('-')) {
+                if (matchCodeUnit('!')) {
+                    if (matchCodeUnit('-')) {
+                        if (matchCodeUnit('-')) {
                             consumeRestOfSingleLineComment();
                             continue;
                         }
-                        ungetCharIgnoreEOL('-');
+                        ungetCodeUnit('-');
                     }
-                    ungetCharIgnoreEOL('!');
+                    ungetCodeUnit('!');
                 }
             }
-            if (matchChar('<'))
-                simpleKind = matchChar('=') ? TokenKind::LshAssign : TokenKind::Lsh;
+            if (matchCodeUnit('<'))
+                simpleKind = matchCodeUnit('=') ? TokenKind::LshAssign : TokenKind::Lsh;
             else
-                simpleKind = matchChar('=') ? TokenKind::Le : TokenKind::Lt;
+                simpleKind = matchCodeUnit('=') ? TokenKind::Le : TokenKind::Lt;
             break;
 
           case '>':
-            if (matchChar('>')) {
-                if (matchChar('>'))
-                    simpleKind = matchChar('=') ? TokenKind::UrshAssign : TokenKind::Ursh;
+            if (matchCodeUnit('>')) {
+                if (matchCodeUnit('>'))
+                    simpleKind = matchCodeUnit('=') ? TokenKind::UrshAssign : TokenKind::Ursh;
                 else
-                    simpleKind = matchChar('=') ? TokenKind::RshAssign : TokenKind::Rsh;
+                    simpleKind = matchCodeUnit('=') ? TokenKind::RshAssign : TokenKind::Rsh;
             } else {
-                simpleKind = matchChar('=') ? TokenKind::Ge : TokenKind::Gt;
+                simpleKind = matchCodeUnit('=') ? TokenKind::Ge : TokenKind::Gt;
             }
             break;
 
           case '*':
-            if (matchChar('*'))
-                simpleKind = matchChar('=') ? TokenKind::PowAssign : TokenKind::Pow;
+            if (matchCodeUnit('*'))
+                simpleKind = matchCodeUnit('=') ? TokenKind::PowAssign : TokenKind::Pow;
             else
-                simpleKind = matchChar('=') ? TokenKind::MulAssign : TokenKind::Mul;
+                simpleKind = matchCodeUnit('=') ? TokenKind::MulAssign : TokenKind::Mul;
             break;
 
           case '/':
             // Look for a single-line comment.
-            if (matchChar('/')) {
-                c = getCharIgnoreEOL();
+            if (matchCodeUnit('/')) {
+                c = getCodeUnit();
                 if (c == '@' || c == '#') {
                     bool shouldWarn = c == '@';
                     if (!getDirectives(false, shouldWarn))
                         return false;
                 } else {
-                    ungetCharIgnoreEOL(c);
+                    ungetCodeUnit(c);
                 }
 
                 consumeRestOfSingleLineComment();
                 continue;
             }
 
             // Look for a multi-line comment.
-            if (matchChar('*')) {
+            if (matchCodeUnit('*')) {
                 TokenStreamAnyChars& anyChars = anyCharsAccess();
                 unsigned linenoBefore = anyChars.lineno;
 
                 do {
-                    if (!getChar(&c))
-                        return badToken();
-
-                    if (c == EOF) {
+                    int32_t unit = getCodeUnit();
+                    if (unit == EOF) {
                         reportError(JSMSG_UNTERMINATED_COMMENT);
                         return badToken();
                     }
 
-                    if (c == '*' && matchChar('/'))
+                    if (unit == '*' && matchCodeUnit('/'))
                         break;
 
-                    if (c == '@' || c == '#') {
-                        bool shouldWarn = c == '@';
+                    if (unit == '@' || unit == '#') {
+                        bool shouldWarn = unit == '@';
                         if (!getDirectives(true, shouldWarn))
-                            return false;
+                            return badToken();
+                    } else if (MOZ_LIKELY(isAsciiCodePoint(unit))) {
+                        int32_t codePoint;
+                        if (!getFullAsciiCodePoint(unit, &codePoint))
+                            return badToken();
+                    } else {
+                        int32_t codePoint;
+                        if (!getNonAsciiCodePoint(unit, &codePoint))
+                            return badToken();
                     }
                 } while (true);
 
                 if (linenoBefore != anyChars.lineno)
                     anyChars.updateFlagsForEOL();
 
                 continue;
             }
 
             // Look for a regexp.
-            if (modifier == Operand) {
-                tokenbuf.clear();
-
-                bool inCharClass = false;
-                do {
-                    if (!getChar(&c))
-                        return badToken();
-
-                    if (c == '\\') {
-                        if (!tokenbuf.append(c))
-                            return badToken();
-
-                        if (!getChar(&c))
-                            return badToken();
-                    } else if (c == '[') {
-                        inCharClass = true;
-                    } else if (c == ']') {
-                        inCharClass = false;
-                    } else if (c == '/' && !inCharClass) {
-                        // For IE compat, allow unescaped / in char classes.
-                        break;
-                    }
-
-                    if (c == '\n' || c == EOF) {
-                        ungetChar(c);
-                        reportError(JSMSG_UNTERMINATED_REGEXP);
-                        return badToken();
-                    }
-
-                    if (!tokenbuf.append(c))
-                        return badToken();
-                } while (true);
-
-                RegExpFlag reflags = NoFlags;
-                while (true) {
-                    RegExpFlag flag;
-                    c = getCharIgnoreEOL();
-                    if (c == 'g')
-                        flag = GlobalFlag;
-                    else if (c == 'i')
-                        flag = IgnoreCaseFlag;
-                    else if (c == 'm')
-                        flag = MultilineFlag;
-                    else if (c == 'y')
-                        flag = StickyFlag;
-                    else if (c == 'u')
-                        flag = UnicodeFlag;
-                    else if (IsAsciiAlpha(c))
-                        flag = NoFlags;
-                    else
-                        break;
-
-                    if ((reflags & flag) || flag == NoFlags) {
-                        MOZ_ASSERT(sourceUnits.offset() > 0);
-                        char buf[2] = { char(c), '\0' };
-                        errorAt(sourceUnits.offset() - 1, JSMSG_BAD_REGEXP_FLAG, buf);
-                        return badToken();
-                    }
-
-                    reflags = RegExpFlag(reflags | flag);
-                }
-                ungetCharIgnoreEOL(c);
-
-                newRegExpToken(reflags, start, modifier, ttp);
-                return true;
-            }
-
-            simpleKind = matchChar('=') ? TokenKind::DivAssign : TokenKind::Div;
+            if (modifier == Operand)
+                return regexpLiteral(start, ttp);
+
+            simpleKind = matchCodeUnit('=') ? TokenKind::DivAssign : TokenKind::Div;
             break;
 
           case '%':
-            simpleKind = matchChar('=') ? TokenKind::ModAssign : TokenKind::Mod;
+            simpleKind = matchCodeUnit('=') ? TokenKind::ModAssign : TokenKind::Mod;
             break;
 
           case '-':
-            if (matchChar('-')) {
+            if (matchCodeUnit('-')) {
                 if (anyCharsAccess().options().allowHTMLComments &&
                     !anyCharsAccess().flags.isDirtyLine)
                 {
-                    if (matchChar('>')) {
+                    if (matchCodeUnit('>')) {
                         consumeRestOfSingleLineComment();
                         continue;
                     }
                 }
 
                 simpleKind = TokenKind::Dec;
             } else {
-                simpleKind = matchChar('=') ? TokenKind::SubAssign : TokenKind::Sub;
+                simpleKind = matchCodeUnit('=') ? TokenKind::SubAssign : TokenKind::Sub;
             }
             break;
 
           default:
             // We consumed a bad character/code point.  Put it back so the
             // error location is the bad character.
             ungetCodePointIgnoreEOL(c);
             error(JSMSG_ILLEGAL_CHARACTER);
@@ -2208,20 +2323,20 @@ TokenStreamSpecific<CharT, AnyCharsAcces
     // Run the bad-token code for every path out of this function except the
     // one success-case.
     auto noteBadToken = MakeScopeExit([this]() {
         this->badToken();
     });
 
     // We need to detect any of these chars:  " or ', \n (or its
     // equivalents), \\, EOF.  Because we detect EOL sequences here and
-    // put them back immediately, we can use getCharIgnoreEOL().
-    while ((c = getCharIgnoreEOL()) != untilChar) {
+    // put them back immediately, we can use getCodeUnit().
+    while ((c = getCodeUnit()) != untilChar) {
         if (c == EOF) {
-            ungetCharIgnoreEOL(c);
+            ungetCodeUnit(c);
             const char delimiters[] = { untilChar, untilChar, '\0' };
             error(JSMSG_EOF_BEFORE_END_OF_LITERAL, delimiters);
             return false;
         }
 
         if (c == '\\') {
             // When parsing templates, we don't immediately report errors for
             // invalid escapes; these are handled by the parser.
@@ -2246,24 +2361,24 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
               case '\n':
                 // ES5 7.8.4: an escaped line terminator represents
                 // no character.
                 continue;
 
               // Unicode character specification.
               case 'u': {
-                int32_t c2 = getCharIgnoreEOL();
+                int32_t c2 = getCodeUnit();
                 if (c2 == '{') {
                     uint32_t start = sourceUnits.offset() - 3;
                     uint32_t code = 0;
                     bool first = true;
                     bool valid = true;
                     do {
-                        int32_t c = getCharIgnoreEOL();
+                        int32_t c = getCodeUnit();
                         if (c == EOF) {
                             if (parsingTemplate) {
                                 TokenStreamAnyChars& anyChars = anyCharsAccess();
                                 anyChars.setInvalidTemplateEscape(start,
                                                                   InvalidEscapeType::Unicode);
                                 valid = false;
                                 break;
                             }
@@ -2285,17 +2400,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                             break;
                         }
 
                         if (!JS7_ISHEX(c)) {
                             if (parsingTemplate) {
                                 // We put the character back so that we read
                                 // it on the next pass, which matters if it
                                 // was '`' or '\'.
-                                ungetCharIgnoreEOL(c);
+                                ungetCodeUnit(c);
 
                                 TokenStreamAnyChars& anyChars = anyCharsAccess();
                                 anyChars.setInvalidTemplateEscape(start,
                                                                   InvalidEscapeType::Unicode);
                                 valid = false;
                                 break;
                             }
                             reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
@@ -2337,17 +2452,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
                     JS7_ISHEX(cp[0]) && JS7_ISHEX(cp[1]) && JS7_ISHEX(cp[2]))
                 {
                     c = (JS7_UNHEX(c2) << 12) |
                         (JS7_UNHEX(cp[0]) << 8) |
                         (JS7_UNHEX(cp[1]) << 4) |
                         JS7_UNHEX(cp[2]);
                     skipChars(3);
                 } else {
-                    ungetCharIgnoreEOL(c2);
+                    ungetCodeUnit(c2);
                     uint32_t start = sourceUnits.offset() - 2;
                     if (parsingTemplate) {
                         TokenStreamAnyChars& anyChars = anyCharsAccess();
                         anyChars.setInvalidTemplateEscape(start, InvalidEscapeType::Unicode);
                         continue;
                     }
                     reportInvalidEscapeError(start, InvalidEscapeType::Unicode);
                     return false;
@@ -2412,17 +2527,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
 
                     c = char16_t(val);
                 }
                 break;
             }
         } else if (c == '\r' || c == '\n') {
             if (!parsingTemplate) {
                 // String literals don't allow ASCII line breaks.
-                ungetCharIgnoreEOL(c);
+                ungetCodeUnit(c);
                 const char delimiters[] = { untilChar, untilChar, '\0' };
                 error(JSMSG_EOL_BEFORE_END_OF_STRING, delimiters);
                 return false;
             }
 
             if (c == '\r') {
                 c = '\n';
 
@@ -2439,17 +2554,17 @@ TokenStreamSpecific<CharT, AnyCharsAcces
             // U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR encode
             // their literal values in template literals and (as of fairly
             // recently) string literals, but they still count as line
             // terminators when computing line/column coordinates.
             if (!updateLineInfoForEOL())
                 return false;
 
             anyCharsAccess().updateFlagsForEOL();
-        } else if (parsingTemplate && c == '$' && matchChar('{')) {
+        } else if (parsingTemplate && c == '$' && matchCodeUnit('{')) {
             templateHead = true;
             break;
         }
 
         if (!tokenbuf.append(c)) {
             ReportOutOfMemory(anyCharsAccess().cx);
             return false;
         }
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -161,16 +161,17 @@
  */
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/TextUtils.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/Unused.h"
 
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 
 #include "jspubtd.h"
@@ -850,17 +851,40 @@ class TokenStreamAnyChars
     unsigned            lookahead;          // count of lookahead tokens
     unsigned            lineno;             // current line number
     TokenStreamFlags    flags;              // flags -- see above
     size_t              linebase;           // start of current line
     size_t              prevLinebase;       // start of previous line;  size_t(-1) if on the first line
     const char*         filename_;          // input filename or null
     UniqueTwoByteChars  displayURL_;        // the user's requested source URL or null
     UniqueTwoByteChars  sourceMapURL_;      // source map's filename or null
-    uint8_t             isExprEnding[size_t(TokenKind::Limit)];// which tokens definitely terminate exprs?
+
+    /**
+     * An array storing whether a TokenKind observed while attempting to extend
+     * a valid AssignmentExpression into an even longer AssignmentExpression
+     * (e.g., extending '3' to '3 + 5') will terminate it without error.
+     *
+     * For example, ';' always ends an AssignmentExpression because it ends a
+     * Statement or declaration.  '}' always ends an AssignmentExpression
+     * because it terminates BlockStatement, FunctionBody, and embedded
+     * expressions in TemplateLiterals.  Therefore both entries are set to true
+     * in TokenStreamAnyChars construction.
+     *
+     * But e.g. '+' *could* extend an AssignmentExpression, so its entry here
+     * is false.  Meanwhile 'this' can't extend an AssignmentExpression, but
+     * it's only valid after a line break, so its entry here must be false.
+     *
+     * NOTE: This array could be static, but without C99's designated
+     *       initializers it's easier zeroing here and setting the true entries
+     *       in the constructor body.  (Having this per-instance might also aid
+     *       locality.)  Don't worry!  Initialization time for each TokenStream
+     *       is trivial.  See bug 639420.
+     */
+    bool isExprEnding[size_t(TokenKind::Limit)] = {}; // all-false initially
+
     JSContext* const    cx;
     bool                mutedErrors;
     StrictModeGetter*   strictModeGetter;  // used to test for strict mode
 };
 
 // This is the low-level interface to the JS source code buffer.  It just gets
 // raw Unicode code units -- 16-bit char16_t units of source text that are not
 // (always) full code points, and 8-bit units of UTF-8 source text soon.
@@ -905,16 +929,22 @@ class SourceUnits
         MOZ_ASSERT(offset - startOffset_ <= mozilla::PointerRangeSize(base_, limit_));
         return base_ + (offset - startOffset_);
     }
 
     const CharT* limit() const {
         return limit_;
     }
 
+    CharT previousCodeUnit() {
+        MOZ_ASSERT(ptr, "can't get previous code unit if poisoned");
+        MOZ_ASSERT(!atStart(), "must have a previous code unit to get");
+        return *(ptr - 1);
+    }
+
     CharT getCodeUnit() {
         return *ptr++;      // this will nullptr-crash if poisoned
     }
 
     CharT peekCodeUnit() const {
         return *ptr;        // this will nullptr-crash if poisoned
     }
 
@@ -937,16 +967,17 @@ class SourceUnits
                    "function should only be called when a '\\n' was just "
                    "ungotten, and any '\\r' preceding it must also be "
                    "ungotten");
         if (*(ptr - 1) == CharT('\r'))
             ptr--;
     }
 
     void ungetCodeUnit() {
+        MOZ_ASSERT(!atStart(), "can't unget if currently at start");
         MOZ_ASSERT(ptr);     // make sure it hasn't been poisoned
         ptr--;
     }
 
     const CharT* addressOfNextCodeUnit(bool allowPoisoned = false) const {
         MOZ_ASSERT_IF(!allowPoisoned, ptr);     // make sure it hasn't been poisoned
         return ptr;
     }
@@ -988,17 +1019,17 @@ class SourceUnits
     /** Next char to get. */
     const CharT* ptr;
 };
 
 template<typename CharT>
 class TokenStreamCharsBase
 {
   protected:
-    void ungetCharIgnoreEOL(int32_t c);
+    void ungetCodeUnit(int32_t c);
 
   public:
     using CharBuffer = Vector<CharT, 32>;
 
     TokenStreamCharsBase(JSContext* cx, const CharT* chars, size_t length, size_t startOffset);
 
     static MOZ_ALWAYS_INLINE JSAtom*
     atomizeChars(JSContext* cx, const CharT* chars, size_t length);
@@ -1007,16 +1038,23 @@ class TokenStreamCharsBase
 
     MOZ_MUST_USE bool copyTokenbufTo(JSContext* cx,
                                      UniquePtr<char16_t[], JS::FreePolicy>* destination);
 
     using SourceUnits = frontend::SourceUnits<CharT>;
 
     MOZ_MUST_USE bool appendCodePointToTokenbuf(uint32_t codePoint);
 
+    // |expect| cannot be an EOL char.
+    bool matchCodeUnit(int32_t expect) {
+        MOZ_ASSERT(expect != EOF, "shouldn't be matching EOFs");
+        MOZ_ASSERT(!SourceUnits::isRawEOLChar(expect));
+        return MOZ_LIKELY(sourceUnits.hasRawChars()) && sourceUnits.matchCodeUnit(expect);
+    }
+
   protected:
     MOZ_MUST_USE bool
     fillWithTemplateStringContents(CharBuffer& charbuf, const CharT* cur, const CharT* end) {
         while (cur < end) {
             // U+2028 LINE SEPARATOR and U+2029 PARAGRAPH SEPARATOR are
             // interpreted literally inside template literal contents; only
             // literal CRLF sequences are normalized to '\n'.  See
             // <https://tc39.github.io/ecma262/#sec-static-semantics-tv-and-trv>.
@@ -1031,16 +1069,25 @@ class TokenStreamCharsBase
                 return false;
 
             cur++;
         }
 
         return true;
     }
 
+    /**
+     * Determine whether a code unit constitutes a complete ASCII code point.
+     * (The code point's exact value might not be used, however, if subsequent
+     * code observes that |unit| is part of a LineTerminatorSequence.)
+     */
+    static MOZ_MUST_USE MOZ_ALWAYS_INLINE bool isAsciiCodePoint(CharT unit) {
+        return mozilla::IsAscii(unit);
+    }
+
   protected:
     /** Code units in the source code being tokenized. */
     SourceUnits sourceUnits;
 
     /** Current token string buffer. */
     CharBuffer tokenbuf;
 };
 
@@ -1099,17 +1146,16 @@ class GeneralTokenStreamChars
 
         return token;
     }
 
   protected:
     using typename CharsSharedBase::SourceUnits;
 
     using CharsSharedBase::sourceUnits;
-    using CharsSharedBase::ungetCharIgnoreEOL;
 
   public:
     using CharsSharedBase::CharsSharedBase;
 
     TokenStreamAnyChars& anyCharsAccess() {
         return AnyCharsAccess::anyChars(this);
     }
 
@@ -1152,26 +1198,31 @@ class GeneralTokenStreamChars
 
     void newNameToken(PropertyName* name, TokenStart start, TokenStreamShared::Modifier modifier,
                       TokenKind* out)
     {
         Token* token = newToken(TokenKind::Name, start, modifier, out);
         token->setName(name);
     }
 
-    void newRegExpToken(RegExpFlag reflags, TokenStart start,
-                        TokenStreamShared::Modifier modifier, TokenKind* out)
+    void newRegExpToken(RegExpFlag reflags, TokenStart start, TokenKind* out)
     {
-        Token* token = newToken(TokenKind::RegExp, start, modifier, out);
+        Token* token = newToken(TokenKind::RegExp, start, TokenStreamShared::Operand, out);
         token->setRegExpFlags(reflags);
     }
 
     MOZ_COLD bool badToken();
 
-    int32_t getCharIgnoreEOL();
+    int32_t getCodeUnit();
+
+    void ungetCodeUnit(int32_t c) {
+        MOZ_ASSERT_IF(c == EOF, anyCharsAccess().flags.isEOF);
+
+        CharsSharedBase::ungetCodeUnit(c);
+    }
 
     void ungetChar(int32_t c);
 
     /**
      * Consume characters til EOL/EOF following the start of a single-line
      * comment, without consuming the EOL/EOF.
      */
     void consumeRestOfSingleLineComment();
@@ -1195,21 +1246,24 @@ class TokenStreamChars<char16_t, AnyChar
     using GeneralCharsBase::asSpecific;
 
     using typename GeneralCharsBase::TokenStreamSpecific;
 
     void matchMultiUnitCodePointSlow(char16_t lead, uint32_t* codePoint);
 
   protected:
     using GeneralCharsBase::anyCharsAccess;
-    using GeneralCharsBase::getCharIgnoreEOL;
+    using GeneralCharsBase::getCodeUnit;
+    using CharsSharedBase::isAsciiCodePoint;
     using GeneralCharsBase::sourceUnits;
-    using CharsSharedBase::ungetCharIgnoreEOL;
+    using CharsSharedBase::ungetCodeUnit;
     using GeneralCharsBase::updateLineInfoForEOL;
 
+    using typename GeneralCharsBase::SourceUnits;
+
     using GeneralCharsBase::GeneralCharsBase;
 
     // |c| must be the code unit just gotten.  If it and the subsequent code
     // unit form a valid surrogate pair, get the second code unit, set
     // |*codePoint| to the code point encoded by the surrogate pair, and return
     // true.  Otherwise do not get a second code unit, set |*codePoint = 0|,
     // and return true.
     //
@@ -1227,23 +1281,83 @@ class TokenStreamChars<char16_t, AnyChar
     MOZ_ALWAYS_INLINE bool matchMultiUnitCodePoint(char16_t c, uint32_t* codePoint) {
         if (MOZ_LIKELY(!unicode::IsLeadSurrogate(c)))
             *codePoint = 0;
         else
             matchMultiUnitCodePointSlow(c, codePoint);
         return true;
     }
 
-    // Try to get the next character, normalizing '\r', '\r\n', and '\n' into
-    // '\n'.  Also updates internal line-counter state.  Return true on success
-    // and store the character in |*c|.  Return false and leave |*c| undefined
-    // on failure.
-    MOZ_MUST_USE bool getChar(int32_t* cp);
+    // Try to get the next code point, normalizing '\r', '\r\n', '\n', and the
+    // Unicode line/paragraph separators into '\n'.  Also updates internal
+    // line-counter state.  Return true on success and store the character in
+    // |*c|.  Return false and leave |*c| undefined on failure.
+    MOZ_MUST_USE bool getCodePoint(int32_t* cp);
+
+    // A deprecated alias for |getCodePoint|: most code using this is being
+    // replaced with different approaches.
+    MOZ_MUST_USE bool getChar(int32_t* cp) {
+        return getCodePoint(cp);
+    }
+
+    /**
+     * Given a just-consumed ASCII code unit/point |lead|, consume a full code
+     * point or LineTerminatorSequence (normalizing it to '\n') and store it in
+     * |*codePoint|.  Return true on success, otherwise return false and leave
+     * |*codePoint| undefined on failure.
+     *
+     * If a LineTerminatorSequence was consumed, also update line/column info.
+     *
+     * This may change the current |sourceUnits| offset.
+     */
+    MOZ_MUST_USE bool getFullAsciiCodePoint(char16_t lead, int32_t* codePoint) {
+        MOZ_ASSERT(isAsciiCodePoint(lead),
+                   "non-ASCII code units must be handled separately");
+        MOZ_ASSERT(lead == sourceUnits.previousCodeUnit(),
+                   "getFullAsciiCodePoint called incorrectly");
+
+        if (MOZ_UNLIKELY(lead == '\r')) {
+            if (MOZ_LIKELY(sourceUnits.hasRawChars()))
+                sourceUnits.matchCodeUnit('\n');
+        } else if (MOZ_LIKELY(lead != '\n')) {
+            *codePoint = lead;
+            return true;
+        }
+
+        *codePoint = '\n';
+        bool ok = updateLineInfoForEOL();
+        if (!ok) {
+#ifdef DEBUG
+            *codePoint = EOF; // sentinel value to hopefully cause errors
+#endif
+            MOZ_MAKE_MEM_UNDEFINED(codePoint, sizeof(*codePoint));
+        }
+        return ok;
+    }
+
+    /**
+     * Given a just-consumed non-ASCII code unit (and maybe point) |lead|,
+     * consume a full code point or LineTerminatorSequence (normalizing it to
+     * '\n') and store it in |*codePoint|.  Return true on success, otherwise
+     * return false and leave |*codePoint| undefined on failure.
+     *
+     * If a LineTerminatorSequence was consumed, also update line/column info.
+     *
+     * This may change the current |sourceUnits| offset.
+     */
+    MOZ_MUST_USE bool getNonAsciiCodePoint(char16_t lead, int32_t* cp);
 
     void ungetCodePointIgnoreEOL(uint32_t codePoint);
+
+    /**
+     * Unget a just-gotten LineTerminator sequence: '\r', '\n', '\r\n', or
+     * a Unicode line/paragraph separator, also undoing line/column information
+     * changes reflecting that LineTerminator.
+     */
+    void ungetLineTerminator();
 };
 
 // TokenStream is the lexical scanner for JavaScript source text.
 //
 // It takes a buffer of CharT characters (currently only char16_t encoding
 // UTF-16, but we're adding either UTF-8 or Latin-1 single-byte text soon) and
 // linearly scans it into |Token|s.
 //
@@ -1317,28 +1431,33 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
   private:
     using CharsSharedBase::appendCodePointToTokenbuf;
     using CharsSharedBase::atomizeChars;
     using GeneralCharsBase::badToken;
     using GeneralCharsBase::consumeRestOfSingleLineComment;
     using CharsSharedBase::copyTokenbufTo;
     using CharsSharedBase::fillWithTemplateStringContents;
     using CharsBase::getChar;
-    using GeneralCharsBase::getCharIgnoreEOL;
+    using CharsBase::getCodePoint;
+    using GeneralCharsBase::getCodeUnit;
+    using CharsBase::getFullAsciiCodePoint;
+    using CharsBase::getNonAsciiCodePoint;
+    using CharsSharedBase::isAsciiCodePoint;
+    using CharsSharedBase::matchCodeUnit;
     using CharsBase::matchMultiUnitCodePoint;
     using GeneralCharsBase::newAtomToken;
     using GeneralCharsBase::newNameToken;
     using GeneralCharsBase::newNumberToken;
     using GeneralCharsBase::newRegExpToken;
     using GeneralCharsBase::newSimpleToken;
     using CharsSharedBase::sourceUnits;
     using CharsSharedBase::tokenbuf;
     using GeneralCharsBase::ungetChar;
-    using CharsSharedBase::ungetCharIgnoreEOL;
     using CharsBase::ungetCodePointIgnoreEOL;
+    using CharsSharedBase::ungetCodeUnit;
     using GeneralCharsBase::updateLineInfoForEOL;
 
     template<typename CharU> friend class TokenStreamPosition;
 
   public:
     TokenStreamSpecific(JSContext* cx, const ReadOnlyCompileOptions& options,
                         const CharT* base, size_t length);
 
@@ -1475,47 +1594,50 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
      * token.
      *
      * |c| must be one of these values:
      *
      *   1. The first decimal digit in the integral part of a decimal number
      *      not starting with '0' or '.', e.g. '1' for "17", '3' for "3.14", or
      *      '8' for "8.675309e6".
      *
-     *   In this case, the next |getCharIgnoreEOL()| must return the code unit
-     *   after |c| in the overall number.
+     *   In this case, the next |getCodeUnit()| must return the code unit after
+     *   |c| in the overall number.
      *
      *   2. The '.' in a "."/"0."-prefixed decimal number or the 'e'/'E' in a
      *      "0e"/"0E"-prefixed decimal number, e.g. ".17", "0.42", or "0.1e3".
      *
-     *   In this case, the next |getCharIgnoreEOL()| must return the code unit
+     *   In this case, the next |getCodeUnit()| must return the code unit
      *   *after* the first decimal digit *after* the '.'.  So the next code
      *   unit would be '7' in ".17", '2' in "0.42", 'e' in "0.4e+8", or '/' in
      *   "0.5/2" (three separate tokens).
      *
      *   3. The code unit after the '0' where "0" is the entire number token.
      *
-     *   In this case, the next |getCharIgnoreEOL()| returns the code unit
-     *   after |c|.
+     *   In this case, the next |getCodeUnit()| returns the code unit after
+     *   |c|.
      *
      *   4. (Non-strict mode code only)  The first '8' or '9' in a "noctal"
      *      number that begins with a '0' but contains a non-octal digit in its
      *      integer part so is interpreted as decimal, e.g. '9' in "09.28" or
      *      '8' in "0386" or '9' in "09+7" (three separate tokens").
      *
-     *   In this case, the next |getCharIgnoreEOL()| returns the code unit
-     *   after |c|: '.', '6', or '+' in the examples above.
+     *   In this case, the next |getCodeUnit()| returns the code unit after
+     *   |c|: '.', '6', or '+' in the examples above.
      *
      * This interface is super-hairy and horribly stateful.  Unfortunately, its
      * hair merely reflects the intricacy of ECMAScript numeric literal syntax.
      * And incredibly, it *improves* on the goto-based horror that predated it.
      */
     MOZ_MUST_USE bool decimalNumber(int c, TokenStart start, const CharT* numStart,
                                     Modifier modifier, TokenKind* out);
 
+    /** Tokenize a regular expression literal beginning at |start|. */
+    MOZ_MUST_USE bool regexpLiteral(TokenStart start, TokenKind* out);
+
   public:
     // Advance to the next token.  If the token stream encountered an error,
     // return false.  Otherwise return true and store the token kind in |*ttp|.
     MOZ_MUST_USE bool getToken(TokenKind* ttp, Modifier modifier = None) {
         // Check for a pushed-back token resulting from mismatching lookahead.
         TokenStreamAnyChars& anyChars = anyCharsAccess();
         if (anyChars.lookahead != 0) {
             MOZ_ASSERT(!anyChars.flags.hadError);
@@ -1687,47 +1809,41 @@ class MOZ_STACK_CLASS TokenStreamSpecifi
     MOZ_MUST_USE bool getDirectives(bool isMultiline, bool shouldWarnDeprecated);
     MOZ_MUST_USE bool getDirective(bool isMultiline, bool shouldWarnDeprecated,
                                    const char* directive, uint8_t directiveLength,
                                    const char* errorMsgPragma,
                                    UniquePtr<char16_t[], JS::FreePolicy>* destination);
     MOZ_MUST_USE bool getDisplayURL(bool isMultiline, bool shouldWarnDeprecated);
     MOZ_MUST_USE bool getSourceMappingURL(bool isMultiline, bool shouldWarnDeprecated);
 
-    // |expect| cannot be an EOL char.
-    bool matchChar(int32_t expect) {
-        MOZ_ASSERT(!SourceUnits::isRawEOLChar(expect));
-        return MOZ_LIKELY(sourceUnits.hasRawChars()) && sourceUnits.matchCodeUnit(expect);
-    }
-
     void consumeKnownChar(int32_t expect) {
         int32_t c;
         MOZ_ALWAYS_TRUE(getChar(&c));
         MOZ_ASSERT(c == expect);
     }
 
     void consumeKnownCharIgnoreEOL(int32_t expect) {
 #ifdef DEBUG
         auto c =
 #endif
-            getCharIgnoreEOL();
+            getCodeUnit();
         MOZ_ASSERT(c == expect);
     }
 
     MOZ_MUST_USE bool peekChar(int32_t* c) {
         if (!getChar(c))
             return false;
         ungetChar(*c);
         return true;
     }
 
     void skipChars(uint32_t n) {
         while (n-- > 0) {
             MOZ_ASSERT(sourceUnits.hasRawChars());
-            mozilla::DebugOnly<int32_t> c = getCharIgnoreEOL();
+            mozilla::DebugOnly<int32_t> c = getCodeUnit();
             MOZ_ASSERT(!SourceUnits::isRawEOLChar(c));
         }
     }
 };
 
 // It's preferable to define this in TokenStream.cpp, but its template-ness
 // means we'd then have to *instantiate* this constructor for all possible
 // (CharT, AnyCharsAccess) pairs -- and that gets super-messy as AnyCharsAccess
--- a/js/src/tests/test262-host.js
+++ b/js/src/tests/test262-host.js
@@ -8,16 +8,17 @@
     var ReflectApply = global.Reflect.apply;
     var NewGlobal = global.newGlobal;
     var Atomics = global.Atomics;
     var SharedArrayBuffer = global.SharedArrayBuffer;
     var Int32Array = global.Int32Array;
     var setSharedArrayBuffer = global.setSharedArrayBuffer;
     var getSharedArrayBuffer = global.getSharedArrayBuffer;
     var evalInWorker = global.evalInWorker;
+    var monotonicNow = global.monotonicNow;
 
     var hasCreateIsHTMLDDA = "createIsHTMLDDA" in global;
     var hasThreads = ("helperThreadCount" in global ? global.helperThreadCount() > 0 : true);
     var hasMailbox = typeof setSharedArrayBuffer == "function" && typeof getSharedArrayBuffer == "function";
     var hasEvalInWorker = typeof evalInWorker == "function";
 
     if (!hasCreateIsHTMLDDA && !("document" in global && "all" in global.document))
         throw new Error("no [[IsHTMLDDA]] object available for testing");
@@ -123,17 +124,19 @@
             Atomics.add(_ia, ${_NUMTXT_LOC}, 1);
             Atomics.store(_ia, ${_LOCKTXT_LOC}, 0);
         },
 
         sleep(s) {
             Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s);
         },
 
-        leaving() {}
+        leaving() {},
+
+        monotonicNow,
     };
     Atomics.add(_ia, ${_RDY_LOC}, 1);
     return agent;
 })();`;
 // END WORKER PREFIX
 
             return {
                 _numWorkers: 0,
@@ -185,16 +188,18 @@
                     this._numReports++;
                     return s;
                 },
 
                 sleep(s) {
                     this._bailIfNotAvailable();
                     Atomics.wait(_ia, _SLEEP_LOC, 0, s);
                 },
+
+                monotonicNow,
             };
         })()
     };
 })(this);
 
 var $mozAsyncTestDone = false;
 function $DONE(failure) {
     // This function is generally called from within a Promise handler, so any
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -988,23 +988,21 @@ GlobalScope::XDR(XDRState<mode>* xdr, Sc
     Rooted<Data*> data(cx);
     MOZ_TRY(XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data));
 
     {
         Maybe<Rooted<UniquePtr<Data>>> uniqueData;
         if (mode == XDR_DECODE)
             uniqueData.emplace(cx, data);
 
-        MOZ_TRY(xdr->codeUint32(&data->varStart));
         MOZ_TRY(xdr->codeUint32(&data->letStart));
         MOZ_TRY(xdr->codeUint32(&data->constStart));
 
         if (mode == XDR_DECODE) {
             if (!data->length) {
-                MOZ_ASSERT(!data->varStart);
                 MOZ_ASSERT(!data->letStart);
                 MOZ_ASSERT(!data->constStart);
             }
 
             scope.set(createWithData(cx, kind, &uniqueData.ref()));
             if (!scope)
                 return xdr->fail(JS::TranscodeResult_Throw);
         }
@@ -1413,82 +1411,78 @@ BindingIter::BindingIter(JSScript* scrip
 void
 BindingIter::init(LexicalScope::Data& data, uint32_t firstFrameSlot, uint8_t flags)
 {
     // Named lambda scopes can only have environment slots. If the callee
     // isn't closed over, it is accessed via JSOP_CALLEE.
     if (flags & IsNamedLambda) {
         // Named lambda binding is weird. Normal BindingKind ordering rules
         // don't apply.
-        init(0, 0, 0, 0, 0, 0,
+        init(0, 0, 0, 0, 0,
              CanHaveEnvironmentSlots | flags,
              firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
              data.trailingNames.start(), data.length);
     } else {
         //            imports - [0, 0)
         // positional formals - [0, 0)
         //      other formals - [0, 0)
-        //    top-level funcs - [0, 0)
         //               vars - [0, 0)
         //               lets - [0, data.constStart)
         //             consts - [data.constStart, data.length)
-        init(0, 0, 0, 0, 0, data.constStart,
+        init(0, 0, 0, 0, data.constStart,
              CanHaveFrameSlots | CanHaveEnvironmentSlots | flags,
              firstFrameSlot, JSSLOT_FREE(&LexicalEnvironmentObject::class_),
              data.trailingNames.start(), data.length);
     }
 }
 
 void
 BindingIter::init(FunctionScope::Data& data, uint8_t flags)
 {
     flags = CanHaveFrameSlots | CanHaveEnvironmentSlots | flags;
     if (!(flags & HasFormalParameterExprs))
         flags |= CanHaveArgumentSlots;
 
     //            imports - [0, 0)
     // positional formals - [0, data.nonPositionalFormalStart)
     //      other formals - [data.nonPositionalParamStart, data.varStart)
-    //    top-level funcs - [data.varStart, data.varStart)
     //               vars - [data.varStart, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
-    init(0, data.nonPositionalFormalStart, data.varStart, data.varStart, data.length, data.length,
+    init(0, data.nonPositionalFormalStart, data.varStart, data.length, data.length,
          flags,
          0, JSSLOT_FREE(&CallObject::class_),
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(VarScope::Data& data, uint32_t firstFrameSlot)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
-    //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
-    init(0, 0, 0, 0, data.length, data.length,
+    init(0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          firstFrameSlot, JSSLOT_FREE(&VarEnvironmentObject::class_),
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(GlobalScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
-    //    top-level funcs - [0, data.varStart)
-    //               vars - [data.varStart, data.letStart)
+    //               vars - [0, data.letStart)
     //               lets - [data.letStart, data.constStart)
     //             consts - [data.constStart, data.length)
-    init(0, 0, 0, data.varStart, data.letStart, data.constStart,
+    init(0, 0, 0, data.letStart, data.constStart,
          CannotHaveSlots,
          UINT32_MAX, UINT32_MAX,
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(EvalScope::Data& data, bool strict)
 {
@@ -1503,68 +1497,64 @@ BindingIter::init(EvalScope::Data& data,
         flags = CannotHaveSlots;
         firstFrameSlot = UINT32_MAX;
         firstEnvironmentSlot = UINT32_MAX;
     }
 
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
-    //    top-level funcs - [0, data.varStart)
-    //               vars - [data.varStart, data.length)
+    //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
-    init(0, 0, 0, data.varStart, data.length, data.length,
+    init(0, 0, 0, data.length, data.length,
          flags, firstFrameSlot, firstEnvironmentSlot,
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(ModuleScope::Data& data)
 {
     //            imports - [0, data.varStart)
     // positional formals - [data.varStart, data.varStart)
     //      other formals - [data.varStart, data.varStart)
-    //    top-level funcs - [data.varStart, data.varStart)
     //               vars - [data.varStart, data.letStart)
     //               lets - [data.letStart, data.constStart)
     //             consts - [data.constStart, data.length)
-    init(data.varStart, data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
+    init(data.varStart, data.varStart, data.varStart, data.letStart, data.constStart,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          0, JSSLOT_FREE(&ModuleEnvironmentObject::class_),
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(WasmInstanceScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
-    //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
-    init(0, 0, 0, 0, data.length, data.length,
+    init(0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          UINT32_MAX, UINT32_MAX,
          data.trailingNames.start(), data.length);
 }
 
 void
 BindingIter::init(WasmFunctionScope::Data& data)
 {
     //            imports - [0, 0)
     // positional formals - [0, 0)
     //      other formals - [0, 0)
-    //    top-level funcs - [0, 0)
     //               vars - [0, data.length)
     //               lets - [data.length, data.length)
     //             consts - [data.length, data.length)
-    init(0, 0, 0, 0, data.length, data.length,
+    init(0, 0, 0, data.length, data.length,
          CanHaveFrameSlots | CanHaveEnvironmentSlots,
          UINT32_MAX, UINT32_MAX,
          data.trailingNames.start(), data.length);
 }
 
 PositionalFormalParameterIter::PositionalFormalParameterIter(JSScript* script)
   : BindingIter(script)
 {
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -99,40 +99,56 @@ ScopeKindIsInBody(ScopeKind kind)
            kind == ScopeKind::ParameterExpressionVar;
 }
 
 const char* BindingKindString(BindingKind kind);
 const char* ScopeKindString(ScopeKind kind);
 
 class BindingName
 {
-    // A JSAtom* with its low bit used as a tag for whether it is closed over
-    // (i.e., exists in the environment shape).
+    // A JSAtom* with its low bit used as a tag for the:
+    //  * whether it is closed over (i.e., exists in the environment shape)
+    //  * whether it is a top-level function binding in global or eval scope,
+    //    instead of var binding (both are in the same range in Scope data)
     uintptr_t bits_;
 
     static const uintptr_t ClosedOverFlag = 0x1;
-    static const uintptr_t FlagMask = 0x1;
+    // TODO: We should reuse this bit for let vs class distinction to
+    //       show the better redeclaration error message (bug 1428672).
+    static const uintptr_t TopLevelFunctionFlag = 0x2;
+    static const uintptr_t FlagMask = 0x3;
 
   public:
     BindingName()
       : bits_(0)
     { }
 
-    BindingName(JSAtom* name, bool closedOver)
-      : bits_(uintptr_t(name) | (closedOver ? ClosedOverFlag : 0x0))
+    BindingName(JSAtom* name, bool closedOver, bool isTopLevelFunction = false)
+      : bits_(uintptr_t(name) |
+              (closedOver ? ClosedOverFlag : 0x0) |
+              (isTopLevelFunction? TopLevelFunctionFlag : 0x0))
     { }
 
     JSAtom* name() const {
         return reinterpret_cast<JSAtom*>(bits_ & ~FlagMask);
     }
 
     bool closedOver() const {
         return bits_ & ClosedOverFlag;
     }
 
+  private:
+    friend class BindingIter;
+    // This method should be called only for binding names in `vars` range in
+    // BindingIter.
+    bool isTopLevelFunction() const {
+        return bits_ & TopLevelFunctionFlag;
+    }
+
+  public:
     void trace(JSTracer* trc);
 };
 
 /**
  * The various {Global,Module,...}Scope::Data classes consist of always-present
  * bits, then a trailing array of BindingNames.  The various Data classes all
  * end in a TrailingNamesArray that contains sized/aligned space for *one*
  * BindingName.  Data instances that contain N BindingNames, are then allocated
@@ -742,22 +758,22 @@ class GlobalScope : public Scope
     friend class BindingIter;
 
   public:
     // Data is public because it is created by the frontend. See
     // Parser<FullParseHandler>::newGlobalScopeData.
     struct Data
     {
         // Bindings are sorted by kind.
+        // `vars` includes top-level functions which is distinguished by a bit
+        // on the BindingName.
         //
-        // top-level funcs - [0, varStart)
-        //            vars - [varStart, letStart)
+        //            vars - [0, letStart)
         //            lets - [letStart, constStart)
         //          consts - [constStart, length)
-        uint32_t varStart = 0;
         uint32_t letStart = 0;
         uint32_t constStart = 0;
         uint32_t length = 0;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
         // struct.
         TrailingNamesArray trailingNames;
 
@@ -848,21 +864,21 @@ class EvalScope : public Scope
 
   public:
     // Data is public because it is created by the frontend. See
     // Parser<FullParseHandler>::newEvalScopeData.
     struct Data
     {
         // All bindings in an eval script are 'var' bindings. The implicit
         // lexical scope around the eval is present regardless of strictness
-        // and is its own LexicalScope. However, we need to track top-level
-        // functions specially for redeclaration checks.
+        // and is its own LexicalScope.
+        // `vars` includes top-level functions which is distinguished by a bit
+        // on the BindingName.
         //
-        // top-level funcs - [0, varStart)
-        //            vars - [varStart, length)
+        //            vars - [0, length)
         uint32_t varStart = 0;
         uint32_t length = 0;
 
         // Frame slots [0, nextFrameSlot) are live when this is the innermost
         // scope.
         uint32_t nextFrameSlot = 0;
 
         // The array of tagged JSAtom* names, allocated beyond the end of the
@@ -1147,44 +1163,40 @@ class BindingIter
   protected:
     // Bindings are sorted by kind. Because different Scopes have differently
     // laid out Data for packing, BindingIter must handle all binding kinds.
     //
     // Kind ranges:
     //
     //            imports - [0, positionalFormalStart)
     // positional formals - [positionalFormalStart, nonPositionalFormalStart)
-    //      other formals - [nonPositionalParamStart, topLevelFunctionStart)
-    //    top-level funcs - [topLevelFunctionStart, varStart)
+    //      other formals - [nonPositionalParamStart, varStart)
     //               vars - [varStart, letStart)
     //               lets - [letStart, constStart)
     //             consts - [constStart, length)
     //
     // Access method when not closed over:
     //
     //            imports - name
     // positional formals - argument slot
     //      other formals - frame slot
-    //    top-level funcs - frame slot
     //               vars - frame slot
     //               lets - frame slot
     //             consts - frame slot
     //
     // Access method when closed over:
     //
     //            imports - name
     // positional formals - environment slot or name
     //      other formals - environment slot or name
-    //    top-level funcs - environment slot or name
     //               vars - environment slot or name
     //               lets - environment slot or name
     //             consts - environment slot or name
     MOZ_INIT_OUTSIDE_CTOR uint32_t positionalFormalStart_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t nonPositionalFormalStart_;
-    MOZ_INIT_OUTSIDE_CTOR uint32_t topLevelFunctionStart_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t varStart_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t letStart_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t constStart_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t length_;
 
     MOZ_INIT_OUTSIDE_CTOR uint32_t index_;
 
     enum Flags : uint8_t {
@@ -1206,24 +1218,22 @@ class BindingIter
     MOZ_INIT_OUTSIDE_CTOR uint8_t flags_;
     MOZ_INIT_OUTSIDE_CTOR uint16_t argumentSlot_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t frameSlot_;
     MOZ_INIT_OUTSIDE_CTOR uint32_t environmentSlot_;
 
     MOZ_INIT_OUTSIDE_CTOR BindingName* names_;
 
     void init(uint32_t positionalFormalStart, uint32_t nonPositionalFormalStart,
-              uint32_t topLevelFunctionStart, uint32_t varStart,
-              uint32_t letStart, uint32_t constStart,
+              uint32_t varStart, uint32_t letStart, uint32_t constStart,
               uint8_t flags, uint32_t firstFrameSlot, uint32_t firstEnvironmentSlot,
               BindingName* names, uint32_t length)
     {
         positionalFormalStart_ = positionalFormalStart;
         nonPositionalFormalStart_ = nonPositionalFormalStart;
-        topLevelFunctionStart_ = topLevelFunctionStart;
         varStart_ = varStart;
         letStart_ = letStart;
         constStart_ = constStart;
         length_ = length;
         index_ = 0;
         flags_ = flags;
         argumentSlot_ = 0;
         frameSlot_ = firstFrameSlot;
@@ -1380,17 +1390,17 @@ class BindingIter
         MOZ_ASSERT(isNamedLambda());
         return BindingLocation::NamedLambdaCallee();
     }
 
     BindingKind kind() const {
         MOZ_ASSERT(!done());
         if (index_ < positionalFormalStart_)
             return BindingKind::Import;
-        if (index_ < topLevelFunctionStart_) {
+        if (index_ < varStart_) {
             // When the parameter list has expressions, the parameters act
             // like lexical bindings and have TDZ.
             if (hasFormalParameterExprs())
                 return BindingKind::Let;
             return BindingKind::FormalParameter;
         }
         if (index_ < letStart_)
             return BindingKind::Var;
@@ -1398,17 +1408,19 @@ class BindingIter
             return BindingKind::Let;
         if (isNamedLambda())
             return BindingKind::NamedLambdaCallee;
         return BindingKind::Const;
     }
 
     bool isTopLevelFunction() const {
         MOZ_ASSERT(!done());
-        return index_ >= topLevelFunctionStart_ && index_ < varStart_;
+        bool result = names_[index_].isTopLevelFunction();
+        MOZ_ASSERT_IF(result, kind() == BindingKind::Var);
+        return result;
     }
 
     bool hasArgumentSlot() const {
         MOZ_ASSERT(!done());
         if (hasFormalParameterExprs())
             return false;
         return index_ >= positionalFormalStart_ && index_ < nonPositionalFormalStart_;
     }
--- a/js/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/xpconnect/loader/XPCOMUtils.jsm
@@ -85,16 +85,33 @@
  *
  * 3. Define the NSGetFactory entry point:
  *  this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components);
  */
 
 
 var EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
 
+let global = Cu.getGlobalForObject({});
+
+/**
+ * Redefines the given property on the given object with the given
+ * value. This can be used to redefine getter properties which do not
+ * implement setters.
+ */
+function redefine(object, prop, value) {
+  Object.defineProperty(object, prop, {
+    configurable: true,
+    enumerable: true,
+    value,
+    writable: true,
+  });
+  return value;
+}
+
 var XPCOMUtils = {
   /**
    * Generate a QueryInterface implementation. The returned function must be
    * assigned to the 'QueryInterface' property of a JS object. When invoked on
    * that object, it checks if the given iid is listed in the |interfaces|
    * param, and if it is, returns |this| (the object it was called on).
    * If the JS object has a classInfo property it'll be returned for the
    * nsIClassInfo IID, generateCI can be used to generate the classInfo
@@ -168,31 +185,25 @@ var XPCOMUtils = {
    * @param aName
    *        The name of the getter to define on aObject.
    * @param aLambda
    *        A function that returns what the getter should return.  This will
    *        only ever be called once.
    */
   defineLazyGetter: function XPCU_defineLazyGetter(aObject, aName, aLambda)
   {
+    let redefining = false;
     Object.defineProperty(aObject, aName, {
       get: function () {
-        // Redefine this accessor property as a data property.
-        // Delete it first, to rule out "too much recursion" in case aObject is
-        // a proxy whose defineProperty handler might unwittingly trigger this
-        // getter again.
-        delete aObject[aName];
-        let value = aLambda.apply(aObject);
-        Object.defineProperty(aObject, aName, {
-          value,
-          writable: true,
-          configurable: true,
-          enumerable: true
-        });
-        return value;
+        if (!redefining) {
+          // Make sure we don't get into an infinite recursion loop if
+          // the getter lambda does something shady.
+          redefining = true;
+          return redefine(aObject, aName, aLambda.apply(aObject));
+        }
       },
       configurable: true,
       enumerable: true
     });
   },
 
   /**
    * Defines a getter on a specified object for a script.  The script will not
@@ -212,29 +223,52 @@ var XPCOMUtils = {
                                                                aResource)
   {
     if (!Array.isArray(aNames)) {
       aNames = [aNames];
     }
     for (let name of aNames) {
       Object.defineProperty(aObject, name, {
         get: function() {
-          for (let n of aNames) {
-            delete aObject[n];
-          }
           Services.scriptloader.loadSubScript(aResource, aObject);
           return aObject[name];
         },
+        set(value) {
+          redefine(aObject, name, value);
+        },
         configurable: true,
         enumerable: true
       });
     }
   },
 
   /**
+   * Defines a getter property on the given object for each of the given
+   * global names as accepted by Cu.importGlobalProperties. These
+   * properties are imported into the shared JSM module global, and then
+   * copied onto the given object, no matter which global the object
+   * belongs to.
+   *
+   * @param {object} aObject
+   *        The object on which to define the properties.
+   * @param {string[]} aNames
+   *        The list of global properties to define.
+   */
+  defineLazyGlobalGetters(aObject, aNames) {
+    for (let name of aNames) {
+      this.defineLazyGetter(aObject, name, () => {
+        if (!(name in global)) {
+          Cu.importGlobalProperties([name]);
+        }
+        return global[name];
+      });
+    }
+  },
+
+  /**
    * Defines a getter on a specified object for a service.  The service will not
    * be obtained until first use.
    *
    * @param aObject
    *        The object to define the lazy getter on.
    * @param aName
    *        The name of the getter to define on aObject for the service.
    * @param aContract
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1684,29 +1684,17 @@ HasNativeProperty(JSContext* cx, HandleO
     if (!traits->resolveOwnProperty(cx, wrapper, target, holder, id, &desc))
         return false;
     if (desc.object()) {
         *hasProp = true;
         return true;
     }
 
     // Try the holder.
-    bool found = false;
-    if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found))
-        return false;
-    if (found) {
-        *hasProp = true;
-        return true;
-    }
-
-    // Try resolveNativeProperty.
-    if (!traits->resolveNativeProperty(cx, wrapper, holder, id, &desc))
-        return false;
-    *hasProp = !!desc.object();
-    return true;
+    return JS_AlreadyHasOwnPropertyById(cx, holder, id, hasProp);
 }
 
 } // namespace XrayUtils
 
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::preventExtensions(JSContext* cx, HandleObject wrapper,
@@ -1753,42 +1741,35 @@ XrayWrapper<Base, Traits>::getPropertyDe
     // shadows a previously-resolved non-own property that we cached on the
     // holder. This can happen with indexed properties on NodeLists, for example,
     // which are |own| value props.
     //
     // resolveOwnProperty may or may not cache what it finds on the holder,
     // depending on how ephemeral it decides the property is. This means that we have to
     // first check the result of resolveOwnProperty, and _then_, if that comes up blank,
     // check the holder for any cached native properties.
-    //
-    // Finally, we call resolveNativeProperty, which checks non-own properties,
-    // and unconditionally caches what it finds on the holder.
 
     // Check resolveOwnProperty.
     if (!Traits::singleton.resolveOwnProperty(cx, wrapper, target, holder, id, desc))
         return false;
 
     // Check the holder.
     if (!desc.object() && !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
         return false;
     if (desc.object()) {
         desc.object().set(wrapper);
         return true;
     }
 
-    // Nothing in the cache. Call through, and cache the result.
-    if (!Traits::singleton.resolveNativeProperty(cx, wrapper, holder, id, desc))
-        return false;
-
     // We need to handle named access on the Window somewhere other than
     // Traits::resolveOwnProperty, because per spec it happens on the Global
     // Scope Polluter and thus the resulting properties are non-|own|. However,
-    // we're set up (above) to cache (on the holder) anything that comes out of
-    // resolveNativeProperty, which we don't want for something dynamic like
-    // named access. So we just handle it separately here.  Note that this is
+    // we're set up (above) to cache (on the holder),
+    // which we don't want for something dynamic like named access.
+    // So we just handle it separately here.  Note that this is
     // only relevant for CrossOriginXrayWrapper, which calls
     // getPropertyDescriptor from getOwnPropertyDescriptor.
     nsGlobalWindowInner* win = nullptr;
     if (!desc.object() &&
         JSID_IS_STRING(id) &&
         (win = AsWindow(cx, wrapper)))
     {
         nsAutoJSString name;
@@ -1801,27 +1782,18 @@ XrayWrapper<Base, Traits>::getPropertyDe
                 return xpc::Throw(cx, NS_ERROR_FAILURE);
             ExposeObjectToActiveJS(childObj);
             FillPropertyDescriptor(desc, wrapper, ObjectValue(*childObj),
                                    /* readOnly = */ true);
             return JS_WrapPropertyDescriptor(cx, desc);
         }
     }
 
-    // If we still have nothing, we're done.
-    if (!desc.object())
-        return true;
-
-    if (!JS_DefinePropertyById(cx, holder, id, desc) ||
-        !JS_GetOwnPropertyDescriptorById(cx, holder, id, desc))
-    {
-        return false;
-    }
-    MOZ_ASSERT(desc.object());
-    desc.object().set(wrapper);
+    // We found nothing, we're done.
+    MOZ_ASSERT(!desc.object());
     return true;
 }
 
 template <typename Base, typename Traits>
 bool
 XrayWrapper<Base, Traits>::getOwnPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
                                                     JS::MutableHandle<PropertyDescriptor> desc)
                                                     const
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -56,19 +56,16 @@ public:
 
     static JSObject* getTargetObject(JSObject* wrapper) {
         JSObject* target = js::UncheckedUnwrap(wrapper, /* stopAtWindowProxy = */ false);
         if (target)
             JS::ExposeObjectToActiveJS(target);
         return target;
     }
 
-    virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
-                                       JS::HandleObject holder, JS::HandleId id,
-                                       JS::MutableHandle<JS::PropertyDescriptor> desc) = 0;
     // NB: resolveOwnProperty may decide whether or not to cache what it finds
     // on the holder. If the result is not cached, the lookup will happen afresh
     // for each access, which is the right thing for things like dynamic NodeList
     // properties.
     virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper,
                                     JS::HandleObject target, JS::HandleObject holder,
                                     JS::HandleId id, JS::MutableHandle<JS::PropertyDescriptor> desc);
 
@@ -140,31 +137,16 @@ private:
 
 class DOMXrayTraits : public XrayTraits
 {
 public:
     constexpr DOMXrayTraits() = default;
 
     static const XrayType Type = XrayForDOMObject;
 
-    virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
-                                       JS::HandleObject holder, JS::HandleId id,
-                                       JS::MutableHandle<JS::PropertyDescriptor> desc) override
-    {
-        // Xrays for DOM binding objects have a prototype chain that consists of
-        // Xrays for the prototypes of the DOM binding object (ignoring changes
-        // in the prototype chain made by script, plugins or XBL). All properties for
-        // these Xrays are really own properties, either of the instance object or
-        // of the prototypes.
-        // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=1072482
-        //       This should really be:
-        // MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
-        //       but we can't do that yet because XrayUtils::HasNativeProperty calls this.
-        return true;
-    }
     virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
                                     JS::HandleObject holder, JS::HandleId id,
                                     JS::MutableHandle<JS::PropertyDescriptor> desc) override;
 
     bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
 
     bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
                         JS::Handle<JS::PropertyDescriptor> desc,
@@ -192,23 +174,16 @@ protected:
                                            JS::HandleObject target) const override;
 };
 
 class JSXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForJSObject;
 
-    virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
-                                       JS::HandleObject holder, JS::HandleId id,
-                                       JS::MutableHandle<JS::PropertyDescriptor> desc) override
-    {
-        MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
-    }
-
     virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
                                     JS::HandleObject holder, JS::HandleId id,
                                     JS::MutableHandle<JS::PropertyDescriptor> desc) override;
 
     bool delete_(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id, JS::ObjectOpResult& result);
 
     bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
                         JS::Handle<JS::PropertyDescriptor> desc,
@@ -306,23 +281,16 @@ public:
 // These traits are used when the target is not Xrayable and we therefore want
 // to make it opaque modulo the usual Xray machinery (like expandos and
 // .wrappedJSObject).
 class OpaqueXrayTraits : public XrayTraits
 {
 public:
     static const XrayType Type = XrayForOpaqueObject;
 
-    virtual bool resolveNativeProperty(JSContext* cx, JS::HandleObject wrapper,
-                                       JS::HandleObject holder, JS::HandleId id,
-                                       JS::MutableHandle<JS::PropertyDescriptor> desc) override
-    {
-        MOZ_CRASH("resolveNativeProperty hook should never be called with HasPrototype = 1");
-    }
-
     virtual bool resolveOwnProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleObject target,
                                     JS::HandleObject holder, JS::HandleId id,
                                     JS::MutableHandle<JS::PropertyDescriptor> desc) override;
 
     bool defineProperty(JSContext* cx, JS::HandleObject wrapper, JS::HandleId id,
                         JS::Handle<JS::PropertyDescriptor> desc,
                         JS::Handle<JS::PropertyDescriptor> existingDesc,
                         JS::ObjectOpResult& result, bool* defined)
--- a/layout/base/nsPresArena.cpp
+++ b/layout/base/nsPresArena.cpp
@@ -81,16 +81,17 @@ nsPresArena::ClearArenaRefPtrs(ArenaObje
       iter.Remove();
     }
   }
 }
 
 void*
 nsPresArena::Allocate(uint32_t aCode, size_t aSize)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aSize > 0, "PresArena cannot allocate zero bytes");
   MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
 
   // We only hand out aligned sizes
   aSize = mPool.AlignedSize(aSize);
 
   FreeList* list = &mFreeLists[aCode];
 
@@ -145,16 +146,17 @@ nsPresArena::Allocate(uint32_t aCode, si
   // Allocate a new chunk from the arena
   list->mEntriesEverAllocated++;
   return mPool.Allocate(aSize);
 }
 
 void
 nsPresArena::Free(uint32_t aCode, void* aPtr)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCode < ArrayLength(mFreeLists));
 
   // Try to recycle this entry.
   FreeList* list = &mFreeLists[aCode];
   MOZ_ASSERT(list->mEntrySize > 0, "object of this type was never allocated");
 
   mozWritePoison(aPtr, list->mEntrySize);
 
--- a/layout/mathml/nsMathMLOperators.h
+++ b/layout/mathml/nsMathMLOperators.h
@@ -52,17 +52,17 @@ enum {
 
   // Additional bits not stored in the dictionary
   NS_MATHML_OPERATOR_MINSIZE_ABSOLUTE   = 1<<13,
   NS_MATHML_OPERATOR_MAXSIZE_ABSOLUTE   = 1<<14,
   NS_MATHML_OPERATOR_LSPACE_ATTR     = 1<<15,
   NS_MATHML_OPERATOR_RSPACE_ATTR    = 1<<16
 };
 
-#define NS_MATHML_OPERATOR_SIZE_INFINITY NS_IEEEPositiveInfinity()
+#define NS_MATHML_OPERATOR_SIZE_INFINITY (mozilla::PositiveInfinity<float>())
 
 class nsMathMLOperators {
 public:
   static void AddRefTable(void);
   static void ReleaseTable(void);
   static void CleanUp();
 
   // LookupOperator:
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -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/. */
 
 /* DOM object returned from element.getComputedStyle() */
 
 #include "nsComputedDOMStyle.h"
 
 #include "mozilla/ArrayUtils.h"
+#include "mozilla/FloatingPoint.h"
 #include "mozilla/FontPropertyTypes.h"
 #include "mozilla/Preferences.h"
 
 #include "nsError.h"
 #include "nsIFrame.h"
 #include "nsIFrameInlines.h"
 #include "mozilla/ComputedStyle.h"
 #include "nsIScrollableFrame.h"
@@ -7147,25 +7148,17 @@ nsComputedDOMStyle::DoGetAnimationIterat
   MOZ_ASSERT(display->mAnimationIterationCountCount > 0,
              "first item must be explicit");
   uint32_t i = 0;
   do {
     const StyleAnimation *animation = &display->mAnimations[i];
     RefPtr<nsROCSSPrimitiveValue> iterationCount = new nsROCSSPrimitiveValue;
 
     float f = animation->GetIterationCount();
-    /* Need a nasty hack here to work around an optimizer bug in gcc
-       4.2 on Mac, which somehow gets confused when directly comparing
-       a float to the return value of NS_IEEEPositiveInfinity when
-       building 32-bit builds. */
-#ifdef XP_MACOSX
-    volatile
-#endif
-      float inf = NS_IEEEPositiveInfinity();
-    if (f == inf) {
+    if (f == PositiveInfinity<float>()) {
       iterationCount->SetIdent(eCSSKeyword_infinite);
     } else {
       iterationCount->SetNumber(f);
     }
     valueList->AppendCSSValue(iterationCount.forget());
   } while (++i < display->mAnimationIterationCountCount);
 
   return valueList.forget();
--- a/mfbt/Casting.h
+++ b/mfbt/Casting.h
@@ -7,17 +7,19 @@
 /* Cast operations to supplement the built-in casting operations. */
 
 #ifndef mozilla_Casting_h
 #define mozilla_Casting_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TypeTraits.h"
 
+#include <cstring>
 #include <limits.h>
+#include <type_traits>
 
 namespace mozilla {
 
 /**
  * Sets the outparam value of type |To| with the same underlying bit pattern of
  * |aFrom|.
  *
  * |To| and |From| must be types of the same size; be careful of cross-platform
@@ -38,23 +40,29 @@ namespace mozilla {
  * you should use the direct return version.
  */
 template<typename To, typename From>
 inline void
 BitwiseCast(const From aFrom, To* aResult)
 {
   static_assert(sizeof(From) == sizeof(To),
                 "To and From must have the same size");
-  union
-  {
-    From mFrom;
-    To mTo;
-  } u;
-  u.mFrom = aFrom;
-  *aResult = u.mTo;
+
+  // We could maybe downgrade these to std::is_trivially_copyable, but the
+  // various STLs we use don't all provide it.
+  static_assert(std::is_trivial<From>::value,
+                "shouldn't bitwise-copy a type having non-trivial "
+                "initialization");
+  static_assert(std::is_trivial<To>::value,
+                "shouldn't bitwise-copy a type having non-trivial "
+                "initialization");
+
+  std::memcpy(static_cast<void*>(aResult),
+              static_cast<const void*>(&aFrom),
+              sizeof(From));
 }
 
 template<typename To, typename From>
 inline To
 BitwiseCast(const From aFrom)
 {
   To temp;
   BitwiseCast<To, From>(aFrom, &temp);
--- a/mfbt/FloatingPoint.cpp
+++ b/mfbt/FloatingPoint.cpp
@@ -3,19 +3,41 @@
 /* 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/. */
 
 /* Implementations of FloatingPoint functions */
 
 #include "mozilla/FloatingPoint.h"
 
+#include <cfloat> // for FLT_MAX
+
 namespace mozilla {
 
 bool
-IsFloat32Representable(double aFloat32)
+IsFloat32Representable(double aValue)
 {
-  float asFloat = static_cast<float>(aFloat32);
-  double floatAsDouble = static_cast<double>(asFloat);
-  return floatAsDouble == aFloat32;
+  // NaNs and infinities are representable.
+  if (!IsFinite(aValue)) {
+    return true;
+  }
+
+  // If it exceeds finite |float| range, casting to |double| is always undefined
+  // behavior per C++11 [conv.double]p1 last sentence.
+  if (Abs(aValue) > FLT_MAX) {
+    return false;
+  }
+
+  // But if it's within finite range, then either it's 1) an exact value and so
+  // representable, or 2) it's "between two adjacent destination values" and
+  // safe to cast to "an implementation-defined choice of either of those
+  // values".
+  auto valueAsFloat = static_cast<float>(aValue);
+
+  // Per [conv.fpprom] this never changes value.
+  auto valueAsFloatAsDouble = static_cast<double>(valueAsFloat);
+
+  // Finally, in 1) exact representable value equals exact representable value,
+  // or 2) *changed* value does not equal original value, ergo unrepresentable.
+  return valueAsFloatAsDouble == aValue;
 }
 
 } /* namespace mozilla */
--- a/mfbt/FloatingPoint.h
+++ b/mfbt/FloatingPoint.h
@@ -557,22 +557,20 @@ FuzzyEqualsMultiplicative(T aValue1, T a
 {
   static_assert(IsFloatingPoint<T>::value, "floating point type required");
   // can't use std::min because of bug 965340
   T smaller = Abs(aValue1) < Abs(aValue2) ? Abs(aValue1) : Abs(aValue2);
   return Abs(aValue1 - aValue2) <= aEpsilon * smaller;
 }
 
 /**
- * Returns true if the given value can be losslessly represented as an IEEE-754
- * single format number, false otherwise.  All NaN values are considered
- * representable (notwithstanding that the exact bit pattern of a double format
- * NaN value can't be exactly represented in single format).
- *
- * This function isn't inlined to avoid buggy optimizations by MSVC.
+ * Returns true if |aValue| can be losslessly represented as an IEEE-754 single
+ * precision number, false otherwise.  All NaN values are considered
+ * representable (even though the bit patterns of double precision NaNs can't
+ * all be exactly represented in single precision).
  */
 MOZ_MUST_USE
 extern MFBT_API bool
-IsFloat32Representable(double aFloat32);
+IsFloat32Representable(double aValue);
 
 } /* namespace mozilla */
 
 #endif /* mozilla_FloatingPoint_h */
--- a/mfbt/SmallPointerArray.h
+++ b/mfbt/SmallPointerArray.h
@@ -1,27 +1,30 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* A vector of pointers space-optimized for a small number of elements. */
+
 #ifndef mozilla_SmallPointerArray_h
 #define mozilla_SmallPointerArray_h
 
 #include "mozilla/Assertions.h"
+
 #include <algorithm>
 #include <iterator>
+#include <new>
 #include <vector>
 
 namespace mozilla {
 
-// Array class for situations where a small number of elements (<= 2) is
-// expected, a large number of elements must be accomodated if necessary,
+// Array class for situations where a small number of NON-NULL elements (<= 2)
+// is expected, a large number of elements must be accomodated if necessary,
 // and the size of the class must be minimal. Typical vector implementations
 // will fulfill the first two requirements by simply adding inline storage
 // alongside the rest of their member variables. While this strategy works,
 // it brings unnecessary storage overhead for vectors with an expected small
 // number of elements. This class is intended to deal with that problem.
 //
 // This class is similar in performance to a vector class. Accessing its
 // elements when it has not grown over a size of 2 does not require an extra
@@ -31,165 +34,159 @@ namespace mozilla {
 //
 // Any modification of the array invalidates any outstanding iterators.
 template<typename T>
 class SmallPointerArray
 {
 public:
   SmallPointerArray()
   {
-    mInlineElements[0] = mInlineElements[1] = nullptr;
-    static_assert(sizeof(SmallPointerArray<T>) == (2 * sizeof(void*)),
-      "SmallPointerArray must compile to the size of 2 pointers");
-    static_assert(offsetof(SmallPointerArray<T>, mArray) ==
-                  offsetof(SmallPointerArray<T>, mInlineElements) + sizeof(T*),
-      "mArray and mInlineElements[1] are expected to overlap in memory");
-    static_assert(offsetof(SmallPointerArray<T>, mPadding) ==
-      offsetof(SmallPointerArray<T>, mInlineElements),
-      "mPadding and mInlineElements[0] are expected to overlap in memory");
+    // List-initialization would be nicer, but it only lets you initialize the
+    // first union member.
+    mArray[0].mValue = nullptr;
+    mArray[1].mVector = nullptr;
   }
+
   ~SmallPointerArray()
   {
-    if (!mInlineElements[0] && mArray) {
-      delete mArray;
+    if (!first()) {
+      delete maybeVector();
     }
   }
 
   void Clear() {
-    if (!mInlineElements[0] && mArray) {
-      delete mArray;
-      mArray = nullptr;
+    if (first()) {
+      first() = nullptr;
+      new (&mArray[1].mValue) std::vector<T*>*(nullptr);
       return;
     }
-    mInlineElements[0] = mInlineElements[1] = nullptr;
+
+    delete maybeVector();
+    mArray[1].mVector = nullptr;
   }
 
   void AppendElement(T* aElement) {
     // Storing nullptr as an element is not permitted, but we do check for it
     // to avoid corruption issues in non-debug builds.
 
     // In addition to this we assert in debug builds to point out mistakes to
     // users of the class.
     MOZ_ASSERT(aElement != nullptr);
-    if (!mInlineElements[0]) {
-      if (!mArray) {
-        mInlineElements[0] = aElement;
-        // Harmless if aElement == nullptr;
-        return;
-      }
+    if (aElement == nullptr) {
+      return;
+    }
 
-      if (!aElement) {
+    if (!first()) {
+      auto* vec = maybeVector();
+      if (!vec) {
+        first() = aElement;
+        new (&mArray[1].mValue) T*(nullptr);
         return;
       }
 
-      mArray->push_back(aElement);
-      return;
-    }
-
-    if (!aElement) {
+      vec->push_back(aElement);
       return;
     }
 
-    if (!mInlineElements[1]) {
-      mInlineElements[1] = aElement;
+    if (!second()) {
+      second() = aElement;
       return;
     }
 
-    mArray = new std::vector<T*>({ mInlineElements[0], mInlineElements[1], aElement });
-    mInlineElements[0] = nullptr;
+    auto* vec = new std::vector<T*>({ first(), second(), aElement });
+    first() = nullptr;
+    new (&mArray[1].mVector) std::vector<T*>*(vec);
   }
 
   bool RemoveElement(T* aElement) {
     MOZ_ASSERT(aElement != nullptr);
     if (aElement == nullptr) {
       return false;
     }
 
-    if (mInlineElements[0] == aElement) {
-      // Expectected case.
-      mInlineElements[0] = mInlineElements[1];
-      mInlineElements[1] = nullptr;
+    if (first() == aElement) {
+      // Expected case.
+      T* maybeSecond = second();
+      first() = maybeSecond;
+      if (maybeSecond) {
+        second() = nullptr;
+      } else {
+        new (&mArray[1].mVector) std::vector<T*>*(nullptr);
+      }
+
       return true;
     }
 
-    if (mInlineElements[0]) {
-      if (mInlineElements[1] == aElement) {
-        mInlineElements[1] = nullptr;
+    if (first()) {
+      if (second() == aElement) {
+        second() = nullptr;
         return true;
       }
       return false;
     }
 
-    if (mArray) {
-      for (auto iter = mArray->begin(); iter != mArray->end(); iter++) {
+    if (auto* vec = maybeVector()) {
+      for (auto iter = vec->begin(); iter != vec->end(); iter++) {
         if (*iter == aElement) {
-          mArray->erase(iter);
+          vec->erase(iter);
           return true;
         }
       }
     }
     return false;
   }
 
   bool Contains(T* aElement) const {
     MOZ_ASSERT(aElement != nullptr);
     if (aElement == nullptr) {
       return false;
     }
 
-    if (mInlineElements[0] == aElement) {
-      return true;
+    if (T* v = first()) {
+      return v == aElement || second() == aElement;
     }
 
-    if (mInlineElements[0]) {
-      if (mInlineElements[1] == aElement) {
-        return true;
-      }
-      return false;
+    if (auto* vec = maybeVector()) {
+      return std::find(vec->begin(), vec->end(), aElement) != vec->end();
     }
 
-    if (mArray) {
-      return std::find(mArray->begin(), mArray->end(), aElement) != mArray->end();
-    }
     return false;
-
   }
 
   size_t Length() const
   {
-    if (mInlineElements[0]) {
-      if (!mInlineElements[1]) {
-        return 1;
-      }
-      return 2;
+    if (first()) {
+      return second() ? 2 : 1;
     }
 
-    if (mArray) {
-      return mArray->size();
+    if (auto* vec = maybeVector()) {
+      return vec->size();
     }
 
     return 0;
   }
 
   T* ElementAt(size_t aIndex) const {
     MOZ_ASSERT(aIndex < Length());
-    if (mInlineElements[0]) {
-      return mInlineElements[aIndex];
+    if (first()) {
+      return mArray[aIndex].mValue;
     }
 
-    return (*mArray)[aIndex];
+    auto* vec = maybeVector();
+    MOZ_ASSERT(vec, "must have backing vector if accessing an element");
+    return (*vec)[aIndex];
   }
 
   T* operator[](size_t aIndex) const
   {
     return ElementAt(aIndex);
   }
 
-  typedef T**                        iterator;
-  typedef const T**                  const_iterator;
+  using iterator = T**;
+  using const_iterator = const T**;
 
   // Methods for range-based for loops. Manipulation invalidates these.
   iterator begin() {
     return beginInternal();
   }
   const_iterator begin() const {
     return beginInternal();
   }
@@ -199,44 +196,78 @@ public:
   }
   const_iterator end() const {
     return beginInternal() + Length();
   }
   const_iterator cend() const { return end(); }
 
 private:
   T** beginInternal() const {
-    if (mInlineElements[0] || !mArray) {
-      return const_cast<T**>(&mInlineElements[0]);
+    if (first()) {
+      static_assert(sizeof(T*) == sizeof(Element),
+                    "pointer ops on &first() must produce adjacent "
+                    "Element::mValue arms");
+      return &first();
     }
 
-    if (mArray->empty()) {
+    auto* vec = maybeVector();
+    if (!vec) {
+      return &first();
+    }
+
+    if (vec->empty()) {
       return nullptr;
     }
 
-    return &(*mArray)[0];
+    return &(*vec)[0];
+  }
+
+  // Accessors for |mArray| element union arms.
+
+  T*& first() const {
+    return const_cast<T*&>(mArray[0].mValue);
+  }
+
+  T*& second() const {
+    MOZ_ASSERT(first(), "first() must be non-null to have a T* second pointer");
+    return const_cast<T*&>(mArray[1].mValue);
+  }
+
+  std::vector<T*>* maybeVector() const {
+    MOZ_ASSERT(!first(),
+               "function must only be called when this is either empty or has "
+               "std::vector-backed elements");
+    return mArray[1].mVector;
   }
 
-  // mArray and mInlineElements[1] share the same area in memory.
+  // In C++ active-union-arm terms:
   //
-  // When !mInlineElements[0] && !mInlineElements[1] the array is empty.
+  //   - mArray[0].mValue is always active: a possibly null T*;
+  //   - if mArray[0].mValue is null, mArray[1].mVector is active: a possibly
+  //     null std::vector<T*>*; if mArray[0].mValue isn't null, mArray[1].mValue
+  //     is active: a possibly null T*.
   //
-  // When mInlineElements[0] && !mInlineElements[1], mInlineElements[0]
-  // contains the first element. The array is of size 1.
+  // SmallPointerArray begins empty, with mArray[1].mVector active and null.
+  // Code that makes mArray[0].mValue non-null, i.e. assignments to first(),
+  // must placement-new mArray[1].mValue with the proper value; code that goes
+  // the opposite direction, making mArray[0].mValue null, must placement-new
+  // mArray[1].mVector with the proper value.
   //
-  // When mInlineElements[0] && mInlineElements[1], mInlineElements[0]
-  // contains the first element and mInlineElements[1] the second. The
-  // array is of size 2.
+  // When !mArray[0].mValue && !mArray[1].mVector, the array is empty.
+  //
+  // When mArray[0].mValue && !mArray[1].mValue, the array has size 1 and
+  // contains mArray[0].mValue.
+  //
+  // When mArray[0] && mArray[1], the array has size 2 and contains
+  // mArray[0].mValue and mArray[1].mValue.
   //
-  // When !mInlineElements[0] && mArray, mArray contains the full contents
-  // of the array and is of arbitrary size.
-  union {
-    T* mInlineElements[2];
-    struct {
-      void* mPadding;
-      std::vector<T*>* mArray;
-    };
-  };
+  // When !mArray[0].mValue && mArray[1].mVector, mArray[1].mVector contains
+  // the contents of an array of arbitrary size (even less than two if it ever
+  // contained three elements and elements were removed).
+  union Element {
+    T* mValue;
+    std::vector<T*>* mVector;
+  } mArray[2];
 };
 
 } // namespace mozilla
 
 #endif // mozilla_SmallPointerArray_h
--- a/mfbt/TextUtils.h
+++ b/mfbt/TextUtils.h
@@ -32,16 +32,26 @@ template<>
 class MakeUnsignedChar<char32_t>
 {
 public:
   using Type = char32_t;
 };
 
 } // namespace detail
 
+/** Returns true iff |aChar| is ASCII, i.e. in the range [0, 0x80). */
+template<typename Char>
+constexpr bool
+IsAscii(Char aChar)
+{
+  using UnsignedChar = typename detail::MakeUnsignedChar<Char>::Type;
+  auto uc = static_cast<UnsignedChar>(aChar);
+  return uc < 0x80;
+}
+
 /**
  * Returns true iff |aChar| matches [a-z].
  *
  * This function is basically what you thought islower was, except its behavior
  * doesn't depend on the user's current locale.
  */
 template<typename Char>
 constexpr bool
--- a/mfbt/UniquePtr.h
+++ b/mfbt/UniquePtr.h
@@ -309,17 +309,17 @@ public:
   }
 
   UniquePtr& operator=(decltype(nullptr))
   {
     reset(nullptr);
     return *this;
   }
 
-  T& operator*() const { return *get(); }
+  typename AddLvalueReference<T>::Type operator*() const { return *get(); }
   Pointer operator->() const
   {
     MOZ_ASSERT(get(), "dereferencing a UniquePtr containing nullptr");
     return get();
   }
 
   explicit operator bool() const { return get() != nullptr; }
 
--- a/mfbt/tests/TestFloatingPoint.cpp
+++ b/mfbt/tests/TestFloatingPoint.cpp
@@ -10,16 +10,17 @@
 #include <float.h>
 #include <math.h>
 
 using mozilla::ExponentComponent;
 using mozilla::FloatingPoint;
 using mozilla::FuzzyEqualsAdditive;
 using mozilla::FuzzyEqualsMultiplicative;
 using mozilla::IsFinite;
+using mozilla::IsFloat32Representable;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegative;
 using mozilla::IsNegativeZero;
 using mozilla::IsPositiveZero;
 using mozilla::NegativeInfinity;
 using mozilla::NumberEqualsInt32;
 using mozilla::NumberIsInt32;
@@ -625,19 +626,94 @@ TestDoublesAreApproximatelyEqual()
 
 static void
 TestAreApproximatelyEqual()
 {
   TestFloatsAreApproximatelyEqual();
   TestDoublesAreApproximatelyEqual();
 }
 
+static void
+TestIsFloat32Representable()
+{
+  // Zeroes are representable.
+  A(IsFloat32Representable(+0.0));
+  A(IsFloat32Representable(-0.0));
+
+  // NaN and infinities are representable.
+  A(IsFloat32Representable(UnspecifiedNaN<double>()));
+  A(IsFloat32Representable(SpecificNaN<double>(0, 1)));
+  A(IsFloat32Representable(SpecificNaN<double>(0, 71389)));
+  A(IsFloat32Representable(SpecificNaN<double>(0, (uint64_t(1) << 52) - 2)));
+  A(IsFloat32Representable(SpecificNaN<double>(1, 1)));
+  A(IsFloat32Representable(SpecificNaN<double>(1, 71389)));
+  A(IsFloat32Representable(SpecificNaN<double>(1, (uint64_t(1) << 52) - 2)));
+  A(IsFloat32Representable(PositiveInfinity<double>()));
+  A(IsFloat32Representable(NegativeInfinity<double>()));
+
+  // MSVC seems to compile 2**-1075, which should be half of the smallest
+  // IEEE-754 double precision value, to equal 2**-1074 right now.  This might
+  // be the result of a missing compiler flag to force more-accurate floating
+  // point calculations; bug 1440184 has been filed as a followup to fix this,
+  // so that only the first half of this condition is necessary.
+  A(pow(2.0, -1075.0) == 0.0 ||
+        (MOZ_IS_MSVC && pow(2.0, -1075.0) == pow(2.0, -1074.0)));
+
+  A(powf(2.0f, -150.0f) == 0.0);
+  A(powf(2.0f, -149.0f) != 0.0);
+
+  for (double littleExp = -1074.0; littleExp < -149.0; littleExp++) {
+    // Powers of two below the available range aren't representable.
+    A(!IsFloat32Representable(pow(2.0, littleExp)));
+  }
+
+  // Exact powers of two within the available range are representable.
+  for (double exponent = -149.0; exponent < 128.0; exponent++) {
+    A(IsFloat32Representable(pow(2.0, exponent)));
+  }
+
+  // Powers of two above the available range aren't representable.
+  for (double bigExp = 128.0; bigExp < 1024.0; bigExp++) {
+    A(!IsFloat32Representable(pow(2.0, bigExp)));
+  }
+
+  // Various denormal (i.e. super-small) doubles with MSB and LSB as far apart
+  // as possible are representable (but taken one bit further apart are not
+  // representable).
+  //
+  // Note that the final iteration tests non-denormal with exponent field
+  // containing (biased) 1, as |oneTooSmall| and |widestPossible| happen still
+  // to be correct for that exponent due to the extra bit of precision in the
+  // implicit-one bit.
+  double oneTooSmall = pow(2.0, -150.0);
+  for (double denormExp = -149.0;
+       denormExp < 1 - double(FloatingPoint<double>::kExponentBias) + 1;
+       denormExp++)
+  {
+    double baseDenorm = pow(2.0, denormExp);
+    double tooWide = baseDenorm + oneTooSmall;
+    A(!IsFloat32Representable(tooWide));
+
+    double widestPossible = baseDenorm;
+    if (oneTooSmall * 2.0 != baseDenorm) {
+      widestPossible += oneTooSmall * 2.0;
+    }
+
+    A(IsFloat32Representable(widestPossible));
+  }
+
+  // Finally, check certain interesting/special values for basic sanity.
+  A(!IsFloat32Representable(2147483647.0));
+  A(!IsFloat32Representable(-2147483647.0));
+}
+
 #undef A
 
 int
 main()
 {
   TestAreIdentical();
   TestExponentComponent();
   TestPredicates();
   TestAreApproximatelyEqual();
+  TestIsFloat32Representable();
   return 0;
 }
--- a/mfbt/tests/TestTextUtils.cpp
+++ b/mfbt/tests/TestTextUtils.cpp
@@ -3,23 +3,112 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/TextUtils.h"
 
 using mozilla::AsciiAlphanumericToNumber;
+using mozilla::IsAscii;
 using mozilla::IsAsciiAlpha;
 using mozilla::IsAsciiAlphanumeric;
 using mozilla::IsAsciiDigit;
 using mozilla::IsAsciiLowercaseAlpha;
 using mozilla::IsAsciiUppercaseAlpha;
 
 static void
+TestIsAscii()
+{
+  // char
+
+  static_assert(!IsAscii(char(-1)), "char(-1) isn't ASCII");
+
+  static_assert(IsAscii('\0'), "nul is ASCII");
+
+  static_assert(IsAscii('A'), "'A' is ASCII");
+  static_assert(IsAscii('B'), "'B' is ASCII");
+  static_assert(IsAscii('M'), "'M' is ASCII");
+  static_assert(IsAscii('Y'), "'Y' is ASCII");
+  static_assert(IsAscii('Z'), "'Z' is ASCII");
+
+  static_assert(IsAscii('['), "'[' is ASCII");
+  static_assert(IsAscii('`'), "'`' is ASCII");
+
+  static_assert(IsAscii('a'), "'a' is ASCII");
+  static_assert(IsAscii('b'), "'b' is ASCII");
+  static_assert(IsAscii('m'), "'m' is ASCII");
+  static_assert(IsAscii('y'), "'y' is ASCII");
+  static_assert(IsAscii('z'), "'z' is ASCII");
+
+  static_assert(IsAscii('{'), "'{' is ASCII");
+
+  static_assert(IsAscii('5'), "'5' is ASCII");
+
+  static_assert(IsAscii('\x7F'), "'\\x7F' is ASCII");
+  static_assert(!IsAscii('\x80'), "'\\x80' isn't ASCII");
+
+  // char16_t
+
+  static_assert(!IsAscii(char16_t(-1)), "char16_t(-1) isn't ASCII");
+
+  static_assert(IsAscii(u'\0'), "nul is ASCII");
+
+  static_assert(IsAscii(u'A'), "u'A' is ASCII");
+  static_assert(IsAscii(u'B'), "u'B' is ASCII");
+  static_assert(IsAscii(u'M'), "u'M' is ASCII");
+  static_assert(IsAscii(u'Y'), "u'Y' is ASCII");
+  static_assert(IsAscii(u'Z'), "u'Z' is ASCII");
+
+  static_assert(IsAscii(u'['), "u'[' is ASCII");
+  static_assert(IsAscii(u'`'), "u'`' is ASCII");
+
+  static_assert(IsAscii(u'a'), "u'a' is ASCII");
+  static_assert(IsAscii(u'b'), "u'b' is ASCII");
+  static_assert(IsAscii(u'm'), "u'm' is ASCII");
+  static_assert(IsAscii(u'y'), "u'y' is ASCII");
+  static_assert(IsAscii(u'z'), "u'z' is ASCII");
+
+  static_assert(IsAscii(u'{'), "u'{' is ASCII");
+
+  static_assert(IsAscii(u'5'), "u'5' is ASCII");
+
+  static_assert(IsAscii(u'\x7F'), "u'\\x7F' is ASCII");
+  static_assert(!IsAscii(u'\x80'), "u'\\x80' isn't ASCII");
+
+  // char32_t
+
+  static_assert(!IsAscii(char32_t(-1)), "char32_t(-1) isn't ASCII");
+
+  static_assert(IsAscii(U'\0'), "nul is ASCII");
+
+  static_assert(IsAscii(U'A'), "U'A' is ASCII");
+  static_assert(IsAscii(U'B'), "U'B' is ASCII");
+  static_assert(IsAscii(U'M'), "U'M' is ASCII");
+  static_assert(IsAscii(U'Y'), "U'Y' is ASCII");
+  static_assert(IsAscii(U'Z'), "U'Z' is ASCII");
+
+  static_assert(IsAscii(U'['), "U'[' is ASCII");
+  static_assert(IsAscii(U'`'), "U'`' is ASCII");
+
+  static_assert(IsAscii(U'a'), "U'a' is ASCII");
+  static_assert(IsAscii(U'b'), "U'b' is ASCII");
+  static_assert(IsAscii(U'm'), "U'm' is ASCII");
+  static_assert(IsAscii(U'y'), "U'y' is ASCII");
+  static_assert(IsAscii(U'z'), "U'z' is ASCII");
+
+  static_assert(IsAscii(U'{'), "U'{' is ASCII");
+
+  static_assert(IsAscii(U'5'), "U'5' is ASCII");
+
+  static_assert(IsAscii(U'\x7F'), "U'\\x7F' is ASCII");
+  static_assert(!IsAscii(U'\x80'), "U'\\x80' isn't ASCII");
+}
+
+static void
 TestIsAsciiAlpha()
 {
   // char
 
   static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha");
   static_assert('@' == 0x40, "'@' has value 0x40");
 
   static_assert('A' == 0x41, "'A' has value 0x41");
@@ -704,15 +793,16 @@ TestIsAsciiDigit()
   static_assert(!IsAsciiDigit(U'y'), "U'y' isn't an ASCII digit");
   static_assert(!IsAsciiDigit(U'z'), "U'z' isn't an ASCII digit");
   static_assert(!IsAsciiDigit(U'{'), "U'{' isn't an ASCII digit");
 }
 
 int
 main()
 {
+  TestIsAscii();
   TestIsAsciiAlpha();
   TestIsAsciiUppercaseAlpha();
   TestIsAsciiLowercaseAlpha();
   TestIsAsciiAlphanumeric();
   TestAsciiAlphanumericToNumber();
   TestIsAsciiDigit();
 }
--- a/mfbt/tests/TestUniquePtr.cpp
+++ b/mfbt/tests/TestUniquePtr.cpp
@@ -4,25 +4,27 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Compiler.h"
 #include "mozilla/Move.h"
 #include "mozilla/TypeTraits.h"
 #include "mozilla/UniquePtr.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/Vector.h"
 
 #include <stddef.h>
 
 using mozilla::DefaultDelete;
 using mozilla::IsSame;
 using mozilla::MakeUnique;
 using mozilla::Swap;
 using mozilla::UniquePtr;
+using mozilla::UniqueFreePtr;
 using mozilla::Vector;
 
 #define CHECK(c) \
   do { \
     bool cond = !!(c); \
     MOZ_ASSERT(cond, "Failed assertion: " #c); \
     if (!cond) { \
       return false; \
@@ -557,16 +559,46 @@ TestMakeUnique()
   // various type mismatching to test "fuzzy" forwarding
   UniquePtr<Q> q4(MakeUnique<Q>('s', 66LL, 3.141592654, &q3));
 
   UniquePtr<char[]> c1(MakeUnique<char[]>(5));
 
   return true;
 }
 
+static bool
+TestVoid()
+{
+  // UniquePtr<void> supports all operations except operator*() and
+  // operator->().
+  UniqueFreePtr<void> p1(malloc(1));
+  UniqueFreePtr<void> p2;
+
+  auto x = p1.get();
+  CHECK(x != nullptr);
+  CHECK((IsSame<decltype(x), void*>::value));
+
+  p2.reset(p1.release());
+  CHECK(p1.get() == nullptr);
+  CHECK(p2.get() != nullptr);
+
+  p1 = std::move(p2);
+  CHECK(p1);
+  CHECK(!p2);
+
+  p1.swap(p2);
+  CHECK(!p1);
+  CHECK(p2);
+
+  p2 = nullptr;
+  CHECK(!p2);
+
+  return true;
+}
+
 int
 main()
 {
   TestDeleterType();
 
   if (!TestDefaultFree()) {
     return 1;
   }
@@ -583,10 +615,13 @@ main()
     return 1;
   }
   if (!TestArray()) {
     return 1;
   }
   if (!TestMakeUnique()) {
     return 1;
   }
+  if (!TestVoid()) {
+    return 1;
+  }
   return 0;
 }
--- a/mobile/android/components/FilePicker.js
+++ b/mobile/android/components/FilePicker.js
@@ -2,17 +2,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/. */
 
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Messaging.jsm");
 
-Cu.importGlobalProperties(["File"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["File"]);
 
 function FilePicker() {
 }
 
 FilePicker.prototype = {
   _mimeTypeFilter: 0,
   _extensionsFilter: "",
   _defaultString: "",
--- a/mobile/android/components/ImageBlockingPolicy.js
+++ b/mobile/android/components/ImageBlockingPolicy.js
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cm = Components.manager;
 
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 ChromeUtils.import("resource://gre/modules/Timer.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 // //////////////////////////////////////////////////////////////////////////////
 // // Constants
 
 // // SVG placeholder image for blocked image content
 const PLACEHOLDER_IMG = "chrome://browser/skin/images/placeholder_image.svg";
 
 // // Telemetry
--- a/mobile/android/components/Snippets.js
+++ b/mobile/android/components/Snippets.js
@@ -1,17 +1,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/. */
 
 ChromeUtils.import("resource://gre/modules/Accounts.jsm");
 ChromeUtils.import("resource://gre/modules/Services.jsm");
 ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 ChromeUtils.defineModuleGetter(this, "Home", "resource://gre/modules/Home.jsm");
 ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 ChromeUtils.defineModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
 ChromeUtils.defineModuleGetter(this, "UITelemetry", "resource://gre/modules/UITelemetry.jsm");
 
 
 XPCOMUtils.defineLazyGetter(this, "gEncoder", function() { return new gChromeWin.TextEncoder(); });
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -538,20 +538,19 @@ class AndroidEmulatorCommands(MachComman
        Run the Android emulator with one of the AVDs used in the Mozilla
        automated test environment. If necessary, the AVD is fetched from
        the tooltool server and installed.
     """
     @Command('android-emulator', category='devenv',
              conditions=[],
              description='Run the Android emulator with an AVD from test automation.')
     @CommandArgument('--version', metavar='VERSION',
-                     choices=['4.3', '6.0', '7.0', 'x86', 'x86-6.0', 'x86-7.0'],
+                     choices=['4.3', '7.0', 'x86', 'x86-7.0'],
                      help='Specify Android version to run in emulator. '
-                     'One of "4.3", "6.0", "7.0", "x86", "x86-6.0", or "x86-7.0".',
-                     default='4.3')
+                     'One of "4.3", "7.0", "x86", or "x86-7.0".')
     @CommandArgument('--wait', action='store_true',
                      help='Wait for emulator to be closed.')
     @CommandArgument('--force-update', action='store_true',
                      help='Update AVD definition even when AVD is already installed.')
     @CommandArgument('--verbose', action='store_true',
                      help='Log informative status messages.')
     def emulator(self, version, wait=False, force_update=False, verbose=False):
         from mozrunner.devices.android_device import AndroidEmulator
--- a/mobile/android/modules/SSLExceptions.jsm
+++ b/mobile/android/modules/SSLExceptions.jsm
@@ -1,16 +1,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/. */
 "use strict";
 
 ChromeUtils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
-Cu.importGlobalProperties(["XMLHttpRequest"]);
+XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
 
 var EXPORTED_SYMBOLS = ["SSLExceptions"];
 
 /**
   A class to add exceptions to override SSL certificate problems. The functionality
   itself is borrowed from exceptionDialog.js.
 */
 function SSLExceptions() {
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -33,256 +33,20 @@
 #include "mozilla/WindowsVersion.h"
 #include "nsWindowsHelpers.h"
 #include "WindowsDllBlocklist.h"
 #include "mozilla/AutoProfilerLabel.h"
 #include "mozilla/glue/WindowsDllServices.h"
 
 using namespace mozilla;
 
-#define ALL_VERSIONS   ((unsigned long long)-1LL)
-
-// DLLs sometimes ship without a version number, particularly early
-// releases. Blocking "version <= 0" has the effect of blocking unversioned
-// DLLs (since the call to get version info fails), but not blocking
-// any versioned instance.
-#define UNVERSIONED    ((unsigned long long)0LL)
-
-// Convert the 4 (decimal) components of a DLL version number into a
-// single unsigned long long, as needed by the blocklist
-#define MAKE_VERSION(a,b,c,d)\
-  ((a##ULL << 48) + (b##ULL << 32) + (c##ULL << 16) + d##ULL)
-
-struct DllBlockInfo {
-  // The name of the DLL -- in LOWERCASE!  It will be compared to
-  // a lowercase version of the DLL name only.
-  const char *name;
-
-  // If maxVersion is ALL_VERSIONS, we'll block all versions of this
-  // dll.  Otherwise, we'll block all versions less than or equal to
-  // the given version, as queried by GetFileVersionInfo and
-  // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
-  //
-  // Note that the version is usually 4 components, which is A.B.C.D
-  // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
-  // but it's not required to be of that format.
-  //
-  // If the USE_TIMESTAMP flag is set, then we use the timestamp from
-  // the IMAGE_FILE_HEADER in lieu of a version number.
-  //
-  // If the CHILD_PROCESSES_ONLY flag is set, then the dll is blocked
-  // only when we are a child process.
-  unsigned long long maxVersion;
-
-  enum {
-    FLAGS_DEFAULT = 0,
-    BLOCK_WIN8PLUS_ONLY = 1,
-    BLOCK_WIN8_ONLY = 2,
-    USE_TIMESTAMP = 4,
-    CHILD_PROCESSES_ONLY = 8
-  } flags;
-};
-
-static const DllBlockInfo sWindowsDllBlocklist[] = {
-  // EXAMPLE:
-  // { "uxtheme.dll", ALL_VERSIONS },
-  // { "uxtheme.dll", 0x0000123400000000ULL },
-  // The DLL name must be in lowercase!
-  // The version field is a maximum, that is, we block anything that is
-  // less-than or equal to that version.
-
-  // NPFFAddon - Known malware
-  { "npffaddon.dll", ALL_VERSIONS},
-
-  // AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
-  {"avgrsstx.dll", MAKE_VERSION(8,5,0,401)},
-
-  // calc.dll - Suspected malware
-  {"calc.dll", MAKE_VERSION(1,0,0,1)},
-
-  // hook.dll - Suspected malware
-  {"hook.dll", ALL_VERSIONS},
-
-  // GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
-  // of this DLL cause crashes
-  {"googledesktopnetwork3.dll", UNVERSIONED},
-
-  // rdolib.dll - Suspected malware
-  {"rdolib.dll", MAKE_VERSION(6,0,88,4)},
-
-  // fgjk4wvb.dll - Suspected malware
-  {"fgjk4wvb.dll", MAKE_VERSION(8,8,8,8)},
-
-  // radhslib.dll - Naomi internet filter - unmaintained since 2006
-  {"radhslib.dll", UNVERSIONED},
-
-  // Music download filter for vkontakte.ru - old instances
-  // of this DLL cause crashes
-  {"vksaver.dll", MAKE_VERSION(2,2,2,0)},
-
-  // Topcrash in Firefox 4.0b1
-  {"rlxf.dll", MAKE_VERSION(1,2,323,1)},
-
-  // psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
-  // Adobe photoshop library, now redundant in later installations
-  {"psicon.dll", ALL_VERSIONS},
-
-  // Topcrash in Firefox 4 betas (bug 618899)
-  {"accelerator.dll", MAKE_VERSION(3,2,1,6)},
-
-  // Topcrash with Roboform in Firefox 8 (bug 699134)
-  {"rf-firefox.dll", MAKE_VERSION(7,6,1,0)},
-  {"roboform.dll", MAKE_VERSION(7,6,1,0)},
-
-  // Topcrash with Babylon Toolbar on FF16+ (bug 721264)
-  {"babyfox.dll", ALL_VERSIONS},
-
-  // sprotector.dll crashes, bug 957258
-  {"sprotector.dll", ALL_VERSIONS},
-
-  // leave these two in always for tests
-  { "mozdllblockingtest.dll", ALL_VERSIONS },
-  { "mozdllblockingtest_versioned.dll", 0x0000000400000000ULL },
-
-  // Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
-  { "mfflac.dll", ALL_VERSIONS },
-
-  // Older Relevant Knowledge DLLs cause us to crash (bug 904001).
-  { "rlnx.dll", MAKE_VERSION(1, 3, 334, 9) },
-  { "pmnx.dll", MAKE_VERSION(1, 3, 334, 9) },
-  { "opnx.dll", MAKE_VERSION(1, 3, 334, 9) },
-  { "prnx.dll", MAKE_VERSION(1, 3, 334, 9) },
-
-  // Older belgian ID card software causes Firefox to crash or hang on
-  // shutdown, bug 831285 and 918399.
-  { "beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968) },
-
-  // bug 925459, bitguard crashes
-  { "bitguard.dll", ALL_VERSIONS },
-
-  // bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
-  // Software is discontinued/unsupported
-  { "atkdx11disp.dll", ALL_VERSIONS },
-
-  // Topcrash with Conduit SearchProtect, bug 944542
-  { "spvc32.dll", ALL_VERSIONS },
-
-  // Topcrash with V-bates, bug 1002748 and bug 1023239
-  { "libinject.dll", UNVERSIONED },
-  { "libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP },
-  { "libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP },
-
-  // Crashes with RoboForm2Go written against old SDK, bug 988311/1196859
-  { "rf-firefox-22.dll", ALL_VERSIONS },
-  { "rf-firefox-40.dll", ALL_VERSIONS },
-
-  // Crashes with DesktopTemperature, bug 1046382
-  { "dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP },
-
-  // Startup crashes with Lenovo Onekey Theater, bug 1123778
-  { "activedetect32.dll", UNVERSIONED },
-  { "activedetect64.dll", UNVERSIONED },
-  { "windowsapihookdll32.dll", UNVERSIONED },
-  { "windowsapihookdll64.dll", UNVERSIONED },
-
-  // Flash crashes with RealNetworks RealDownloader, bug 1132663
-  { "rndlnpshimswf.dll", ALL_VERSIONS },
-  { "rndlmainbrowserrecordplugin.dll", ALL_VERSIONS },
-
-  // Startup crashes with RealNetworks Browser Record Plugin, bug 1170141
-  { "nprpffbrowserrecordext.dll", ALL_VERSIONS },
-  { "nprndlffbrowserrecordext.dll", ALL_VERSIONS },
-
-  // Crashes with CyberLink YouCam, bug 1136968
-  { "ycwebcamerasource.ax", MAKE_VERSION(2, 0, 0, 1611) },
-
-  // Old version of WebcamMax crashes WebRTC, bug 1130061
-  { "vwcsource.ax", MAKE_VERSION(1, 5, 0, 0) },
-
-  // NetOp School, discontinued product, bug 763395
-  { "nlsp.dll", MAKE_VERSION(6, 23, 2012, 19) },
-
-  // Orbit Downloader, bug 1222819
-  { "grabdll.dll", MAKE_VERSION(2, 6, 1, 0) },
-  { "grabkernel.dll", MAKE_VERSION(1, 0, 0, 1) },
-
-  // ESET, bug 1229252
-  { "eoppmonitor.dll", ALL_VERSIONS },
-
-  // SS2OSD, bug 1262348
-  { "ss2osd.dll", ALL_VERSIONS },
-  { "ss2devprops.dll", ALL_VERSIONS },
-
-  // NHASUSSTRIXOSD.DLL, bug 1269244
-  { "nhasusstrixosd.dll", ALL_VERSIONS },
-  { "nhasusstrixdevprops.dll", ALL_VERSIONS },
-
-  // Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
-  { "opls.dll", ALL_VERSIONS },
-  { "opls64.dll", ALL_VERSIONS },
-  { "pmls.dll", ALL_VERSIONS },
-  { "pmls64.dll", ALL_VERSIONS },
-  { "prls.dll", ALL_VERSIONS },
-  { "prls64.dll", ALL_VERSIONS },
-  { "rlls.dll", ALL_VERSIONS },
-  { "rlls64.dll", ALL_VERSIONS },
-
-  // Vorbis DirectShow filters, bug 1239690.
-  { "vorbis.acm", MAKE_VERSION(0, 0, 3, 6) },
-
-  // AhnLab Internet Security, bug 1311969
-  { "nzbrcom.dll", ALL_VERSIONS },
-
-  // K7TotalSecurity, bug 1339083.
-  { "k7pswsen.dll", MAKE_VERSION(15, 2, 2, 95) },
-
-  // smci*.dll - goobzo crashware (bug 1339908)
-  { "smci32.dll", ALL_VERSIONS },
-  { "smci64.dll", ALL_VERSIONS },
-
-  // Crashes with Internet Download Manager, bug 1333486
-  { "idmcchandler7.dll", ALL_VERSIONS },
-  { "idmcchandler7_64.dll", ALL_VERSIONS },
-  { "idmcchandler5.dll", ALL_VERSIONS },
-  { "idmcchandler5_64.dll", ALL_VERSIONS },
-
-  // Nahimic 2 breaks applicaton update (bug 1356637)
-  { "nahimic2devprops.dll", MAKE_VERSION(2, 5, 19, 0xffff) },
-  // Nahimic is causing crashes, bug 1233556
-  { "nahimicmsiosd.dll", UNVERSIONED },
-  // Nahimic is causing crashes, bug 1360029
-  { "nahimicvrdevprops.dll", UNVERSIONED },
-  { "nahimic2osd.dll", MAKE_VERSION(2, 5, 19, 0xffff) },
-  { "nahimicmsidevprops.dll", UNVERSIONED },
-
-  // Bug 1268470 - crashes with Kaspersky Lab on Windows 8
-  { "klsihk64.dll", MAKE_VERSION(14, 0, 456, 0xffff), DllBlockInfo::BLOCK_WIN8_ONLY },
-
-  // Bug 1407337, crashes with OpenSC < 0.16.0
-  { "onepin-opensc-pkcs11.dll", MAKE_VERSION(0, 15, 0xffff, 0xffff) },
-
-  // Avecto Privilege Guard causes crashes, bug 1385542
-  { "pghook.dll", ALL_VERSIONS },
-
-  // Old versions of G DATA BankGuard, bug 1421991
-  { "banksafe64.dll", MAKE_VERSION(1, 2, 15299, 65535) },
-
-  // Old versions of G DATA, bug 1043775
-  { "gdkbfltdll64.dll", MAKE_VERSION(1, 0, 14141, 240) },
-
-  // Dell Backup and Recovery tool causes crashes, bug 1433408
-  { "dbroverlayiconnotbackuped.dll", MAKE_VERSION(1, 8, 0, 9) },
-  { "dbroverlayiconbackuped.dll", MAKE_VERSION(1, 8, 0, 9) },
-
-  { nullptr, 0 }
-};
-
-#ifndef STATUS_DLL_NOT_FOUND
-#define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
-#endif
+#define DLL_BLOCKLIST_ENTRY(name, ...) \
+  { name, __VA_ARGS__ },
+#define DLL_BLOCKLIST_CHAR_TYPE char
+#include "mozilla/WindowsDllBlocklistDefs.h"
 
 // define this for very verbose dll load debug spew
 #undef DEBUG_very_verbose
 
 static const char kBlockedDllsParameter[] = "BlockedDllList=";
 static const int kBlockedDllsParameterLen =
   sizeof(kBlockedDllsParameter) - 1;
 
@@ -499,17 +263,17 @@ public:
 private:
   DllBlockSet(const char* name, unsigned long long version)
     : mName(name)
     , mVersion(version)
     , mNext(nullptr)
   {
   }
 
-  const char* mName; // points into the sWindowsDllBlocklist string
+  const char* mName; // points into the gWindowsDllBlocklist string
   unsigned long long mVersion;
   DllBlockSet* mNext;
 
   static DllBlockSet* gFirst;
 };
 
 DllBlockSet* DllBlockSet::gFirst;
 
@@ -618,18 +382,16 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
   char dllName[DLLNAME_MAX+1];
   wchar_t *dll_part;
   char *dot;
 
   int len = moduleFileName->Length / 2;
   wchar_t *fname = moduleFileName->Buffer;
   UniquePtr<wchar_t[]> full_fname;
 
-  const DllBlockInfo* info = &sWindowsDllBlocklist[0];
-
   // The filename isn't guaranteed to be null terminated, but in practice
   // it always will be; ensure that this is so, and bail if not.
   // This is done instead of the more robust approach because of bug 527122,
   // where lots of weird things were happening when we tried to make a copy.
   if (moduleFileName->MaximumLength < moduleFileName->Length+2 ||
       fname[len] != 0)
   {
 #ifdef DEBUG
@@ -678,124 +440,127 @@ patched_LdrLoadDll (PWCHAR filePath, PUL
   }
 
   dllName[len] = 0;
 
 #ifdef DEBUG_very_verbose
   printf_stderr("LdrLoadDll: dll name '%s'\n", dllName);
 #endif
 
-  // Block a suspicious binary that uses various 12-digit hex strings
-  // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
-  dot = strchr(dllName, '.');
-  if (dot && (strchr(dot+1, '.') == dot+13)) {
-    char * end = nullptr;
-    _strtoui64(dot+1, &end, 16);
-    if (end == dot+13) {
-      return STATUS_DLL_NOT_FOUND;
-    }
-  }
-  // Block binaries where the filename is at least 16 hex digits
-  if (dot && ((dot - dllName) >= 16)) {
-    char * current = dllName;
-    while (current < dot && isxdigit(*current)) {
-      current++;
+  if (!(sInitFlags & eDllBlocklistInitFlagWasBootstrapped)) {
+    // Block a suspicious binary that uses various 12-digit hex strings
+    // e.g. MovieMode.48CA2AEFA22D.dll (bug 973138)
+    dot = strchr(dllName, '.');
+    if (dot && (strchr(dot+1, '.') == dot+13)) {
+      char * end = nullptr;
+      _strtoui64(dot+1, &end, 16);
+      if (end == dot+13) {
+        return STATUS_DLL_NOT_FOUND;
+      }
     }
-    if (current == dot) {
-      return STATUS_DLL_NOT_FOUND;
+    // Block binaries where the filename is at least 16 hex digits
+    if (dot && ((dot - dllName) >= 16)) {
+      char * current = dllName;
+      while (current < dot && isxdigit(*current)) {
+        current++;
+      }
+      if (current == dot) {
+        return STATUS_DLL_NOT_FOUND;
+      }
     }
-  }
 
-  // then compare to everything on the blocklist
-  while (info->name) {
-    if (strcmp(info->name, dllName) == 0)
-      break;
+    // then compare to everything on the blocklist
+    DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(info);
+    while (info->name) {
+      if (strcmp(info->name, dllName) == 0)
+        break;
 
-    info++;
-  }
+      info++;
+    }
 
-  if (info->name) {
-    bool load_ok = false;
+    if (info->name) {
+      bool load_ok = false;
 
 #ifdef DEBUG_very_verbose
-    printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
+      printf_stderr("LdrLoadDll: info->name: '%s'\n", info->name);
 #endif
 
-    if ((info->flags & DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
-        !IsWin8OrLater()) {
-      goto continue_loading;
-    }
-
-    if ((info->flags & DllBlockInfo::BLOCK_WIN8_ONLY) &&
-        (!IsWin8OrLater() || IsWin8Point1OrLater())) {
-      goto continue_loading;
-    }
+      if ((info->flags & DllBlockInfo::BLOCK_WIN8PLUS_ONLY) &&
+          !IsWin8OrLater()) {
+        goto continue_loading;
+      }
 
-    if ((info->flags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
-        !(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
-      goto continue_loading;
-    }
+      if ((info->flags & DllBlockInfo::BLOCK_WIN8_ONLY) &&
+          (!IsWin8OrLater() || IsWin8Point1OrLater())) {
+        goto continue_loading;
+      }
 
-    unsigned long long fVersion = ALL_VERSIONS;
-
-    if (info->maxVersion != ALL_VERSIONS) {
-      ReentrancySentinel sentinel(dllName);
-      if (sentinel.BailOut()) {
+      if ((info->flags & DllBlockInfo::CHILD_PROCESSES_ONLY) &&
+          !(sInitFlags & eDllBlocklistInitFlagIsChildProcess)) {
         goto continue_loading;
       }
 
-      full_fname = getFullPath(filePath, fname);
-      if (!full_fname) {
-        // uh, we couldn't find the DLL at all, so...
-        printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
-        return STATUS_DLL_NOT_FOUND;
-      }
+      unsigned long long fVersion = ALL_VERSIONS;
 
-      if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
-        fVersion = GetTimestamp(full_fname.get());
-        if (fVersion > info->maxVersion) {
-          load_ok = true;
+      if (info->maxVersion != ALL_VERSIONS) {
+        ReentrancySentinel sentinel(dllName);
+        if (sentinel.BailOut()) {
+          goto continue_loading;
         }
-      } else {
-        DWORD zero;
-        DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero);
+
+        full_fname = getFullPath(filePath, fname);
+        if (!full_fname) {
+          // uh, we couldn't find the DLL at all, so...
+          printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
+          return STATUS_DLL_NOT_FOUND;
+        }
 
-        // If we failed to get the version information, we block.
+        if (info->flags & DllBlockInfo::USE_TIMESTAMP) {
+          fVersion = GetTimestamp(full_fname.get());
+          if (fVersion > info->maxVersion) {
+            load_ok = true;
+          }
+        } else {
+          DWORD zero;
+          DWORD infoSize = GetFileVersionInfoSizeW(full_fname.get(), &zero);
 
-        if (infoSize != 0) {
-          auto infoData = MakeUnique<unsigned char[]>(infoSize);
-          VS_FIXEDFILEINFO *vInfo;
-          UINT vInfoLen;
+          // If we failed to get the version information, we block.
 
-          if (GetFileVersionInfoW(full_fname.get(), 0, infoSize, infoData.get()) &&
-              VerQueryValueW(infoData.get(), L"\\", (LPVOID*) &vInfo, &vInfoLen))
-          {
-            fVersion =
-              ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
-              ((unsigned long long)vInfo->dwFileVersionLS);
+          if (infoSize != 0) {
+            auto infoData = MakeUnique<unsigned char[]>(infoSize);
+            VS_FIXEDFILEINFO *vInfo;
+            UINT vInfoLen;
 
-            // finally do the version check, and if it's greater than our block
-            // version, keep loading
-            if (fVersion > info->maxVersion)
-              load_ok = true;
+            if (GetFileVersionInfoW(full_fname.get(), 0, infoSize, infoData.get()) &&
+                VerQueryValueW(infoData.get(), L"\\", (LPVOID*) &vInfo, &vInfoLen))
+            {
+              fVersion =
+                ((unsigned long long)vInfo->dwFileVersionMS) << 32 |
+                ((unsigned long long)vInfo->dwFileVersionLS);
+
+              // finally do the version check, and if it's greater than our block
+              // version, keep loading
+              if (fVersion > info->maxVersion)
+                load_ok = true;
+            }
           }
         }
       }
-    }
 
-    if (!load_ok) {
-      printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
-      DllBlockSet::Add(info->name, fVersion);
-      return STATUS_DLL_NOT_FOUND;
+      if (!load_ok) {
+        printf_stderr("LdrLoadDll: Blocking load of '%s' -- see http://www.mozilla.com/en-US/blocklist/\n", dllName);
+        DllBlockSet::Add(info->name, fVersion);
+        return STATUS_DLL_NOT_FOUND;
+      }
     }
   }
 
 continue_loading:
 #ifdef DEBUG_very_verbose
-  printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
+    printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
 #endif
 
   // A few DLLs such as xul.dll and nss3.dll get loaded before mozglue's
   // AutoProfilerLabel is initialized, and this is a no-op in those cases. But
   // the vast majority of DLLs do get labelled here.
   AutoProfilerLabel label("WindowsDllBlocklist::patched_LdrLoadDll", dllName,
                           __LINE__);
 
@@ -856,23 +621,31 @@ patched_BaseThreadInitThunk(BOOL aIsInit
 
   stub_BaseThreadInitThunk(aIsInitialThread, aStartAddress, aThreadParam);
 }
 
 
 static WindowsDllInterceptor NtDllIntercept;
 static WindowsDllInterceptor Kernel32Intercept;
 
+static void
+GetNativeNtBlockSetWriter();
+
 MFBT_API void
 DllBlocklist_Initialize(uint32_t aInitFlags)
 {
   if (sBlocklistInitAttempted) {
     return;
   }
   sInitFlags = aInitFlags;
+
+  if (sInitFlags & eDllBlocklistInitFlagWasBootstrapped) {
+    GetNativeNtBlockSetWriter();
+  }
+
   sBlocklistInitAttempted = true;
 #if defined(NIGHTLY_BUILD)
   gStartAddressesToBlock = new mozilla::Vector<void*, 4>;
 #endif
 
   // In order to be effective against AppInit DLLs, the blocklist must be
   // initialized before user32.dll is loaded into the process (bug 932100).
   if (GetModuleHandleA("user32.dll")) {
@@ -918,17 +691,17 @@ DllBlocklist_Initialize(uint32_t aInitFl
 
   // 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)) {
 #ifdef DEBUG
-      printf_stderr("BaseThreadInitThunk hook failed\n");
+    printf_stderr("BaseThreadInitThunk hook failed\n");
 #endif
     }
   }
 
 #if defined(NIGHTLY_BUILD)
   // Populate a list of thread start addresses to block.
   HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
   if (hKernel) {
@@ -952,18 +725,18 @@ DllBlocklist_Initialize(uint32_t aInitFl
     pProc = (void*)GetProcAddress(hKernel, "LoadLibraryExW");
     if (pProc) {
       gStartAddressesToBlock->append(pProc);
     }
   }
 #endif
 }
 
-MFBT_API void
-DllBlocklist_WriteNotes(HANDLE file)
+static void
+InternalWriteNotes(HANDLE file)
 {
   DWORD nBytes;
 
   WriteFile(file, kBlockedDllsParameter, kBlockedDllsParameterLen, &nBytes, nullptr);
   DllBlockSet::Write(file);
   WriteFile(file, "\n", 1, &nBytes, nullptr);
 
   if (sBlocklistInitFailed) {
@@ -972,16 +745,36 @@ DllBlocklist_WriteNotes(HANDLE file)
   }
 
   if (sUser32BeforeBlocklist) {
     WriteFile(file, kUser32BeforeBlocklistParameter,
               kUser32BeforeBlocklistParameterLen, &nBytes, nullptr);
   }
 }
 
+using WriterFn = void (*)(HANDLE);
+static WriterFn gWriterFn = &InternalWriteNotes;
+
+static void
+GetNativeNtBlockSetWriter()
+{
+  auto nativeWriter = reinterpret_cast<WriterFn>(
+    ::GetProcAddress(::GetModuleHandleW(nullptr), "NativeNtBlockSet_Write"));
+  if (nativeWriter) {
+    gWriterFn = nativeWriter;
+  }
+}
+
+MFBT_API void
+DllBlocklist_WriteNotes(HANDLE file)
+{
+  MOZ_ASSERT(gWriterFn);
+  gWriterFn(file);
+}
+
 MFBT_API bool
 DllBlocklist_CheckStatus()
 {
   if (sBlocklistInitFailed || sUser32BeforeBlocklist)
     return false;
   return true;
 }
 
--- a/mozglue/build/WindowsDllBlocklist.h
+++ b/mozglue/build/WindowsDllBlocklist.h
@@ -12,17 +12,18 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/Types.h"
 
 #define HAS_DLL_BLOCKLIST
 
 enum DllBlocklistInitFlags
 {
   eDllBlocklistInitFlagDefault = 0,
-  eDllBlocklistInitFlagIsChildProcess = 1
+  eDllBlocklistInitFlagIsChildProcess = 1,
+  eDllBlocklistInitFlagWasBootstrapped = 2
 };
 
 MFBT_API void DllBlocklist_Initialize(uint32_t aInitFlags = eDllBlocklistInitFlagDefault);
 MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
 MFBT_API bool DllBlocklist_CheckStatus();
 
 // Forward declaration
 namespace mozilla {
new file mode 100644
--- /dev/null
+++ b/mozglue/build/WindowsDllBlocklistCommon.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_WindowsDllBlocklistCommon_h
+#define mozilla_WindowsDllBlocklistCommon_h
+
+#include <stdint.h>
+
+namespace mozilla {
+
+template <typename CharType>
+struct DllBlockInfoT {
+  // The name of the DLL -- in LOWERCASE!  It will be compared to
+  // a lowercase version of the DLL name only.
+  const CharType* name;
+
+  // If maxVersion is ALL_VERSIONS, we'll block all versions of this
+  // dll.  Otherwise, we'll block all versions less than or equal to
+  // the given version, as queried by GetFileVersionInfo and
+  // VS_FIXEDFILEINFO's dwFileVersionMS and dwFileVersionLS fields.
+  //
+  // Note that the version is usually 4 components, which is A.B.C.D
+  // encoded as 0x AAAA BBBB CCCC DDDD ULL (spaces added for clarity),
+  // but it's not required to be of that format.
+  uint64_t maxVersion;
+
+  // If the USE_TIMESTAMP flag is set, then we use the timestamp from
+  // the IMAGE_FILE_HEADER in lieu of a version number.
+  //
+  // If the CHILD_PROCESSES_ONLY flag is set, then the dll is blocked
+  // only when we are a child process.
+  enum Flags {
+    FLAGS_DEFAULT = 0,
+    BLOCK_WIN8PLUS_ONLY = 1,
+    BLOCK_WIN8_ONLY = 2,
+    USE_TIMESTAMP = 4,
+    CHILD_PROCESSES_ONLY = 8
+  } flags;
+
+  static const uint64_t ALL_VERSIONS = (uint64_t) -1LL;
+
+  // DLLs sometimes ship without a version number, particularly early
+  // releases. Blocking "version <= 0" has the effect of blocking unversioned
+  // DLLs (since the call to get version info fails), but not blocking
+  // any versioned instance.
+  static const uint64_t UNVERSIONED = 0ULL;
+};
+
+} // namespace mozilla
+
+// Convert the 4 (decimal) components of a DLL version number into a
+// single unsigned long long, as needed by the blocklist
+static inline constexpr uint64_t
+MAKE_VERSION(uint16_t a, uint16_t b, uint16_t c, uint16_t d)
+{
+  return static_cast<uint64_t>(a) << 48 |
+         static_cast<uint64_t>(b) << 32 |
+         static_cast<uint64_t>(c) << 16 |
+         static_cast<uint64_t>(d);
+}
+
+#if !defined(DLL_BLOCKLIST_CHAR_TYPE)
+#error "You must define DLL_BLOCKLIST_CHAR_TYPE"
+#endif // !defined(DLL_BLOCKLIST_CHAR_TYPE)
+
+#define DLL_BLOCKLIST_DEFINITIONS_BEGIN \
+  using DllBlockInfo = mozilla::DllBlockInfoT<DLL_BLOCKLIST_CHAR_TYPE>; \
+  static const DllBlockInfo gWindowsDllBlocklist[] = {
+
+#define ALL_VERSIONS DllBlockInfo::ALL_VERSIONS
+#define UNVERSIONED DllBlockInfo::UNVERSIONED
+
+#define DLL_BLOCKLIST_DEFINITIONS_END \
+    {nullptr, 0} \
+  };
+
+#define DECLARE_POINTER_TO_FIRST_DLL_BLOCKLIST_ENTRY(name) \
+  const DllBlockInfo* name = &gWindowsDllBlocklist[0]
+
+#endif // mozilla_WindowsDllBlocklistCommon_h
new file mode 100644
--- /dev/null
+++ b/mozglue/build/WindowsDllBlocklistDefs.h
@@ -0,0 +1,206 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_WindowsDllBlocklistDefs_h
+#define mozilla_WindowsDllBlocklistDefs_h
+
+#include "mozilla/WindowsDllBlocklistCommon.h"
+
+DLL_BLOCKLIST_DEFINITIONS_BEGIN
+
+// EXAMPLE:
+// DLL_BLOCKLIST_ENTRY("uxtheme.dll", ALL_VERSIONS)
+// DLL_BLOCKLIST_ENTRY("uxtheme.dll", 0x0000123400000000ULL)
+// The DLL name must be in lowercase!
+// The version field is a maximum, that is, we block anything that is
+// less-than or equal to that version.
+
+// NPFFAddon - Known malware
+DLL_BLOCKLIST_ENTRY("npffaddon.dll", ALL_VERSIONS)
+
+// AVG 8 - Antivirus vendor AVG, old version, plugin already blocklisted
+DLL_BLOCKLIST_ENTRY("avgrsstx.dll", MAKE_VERSION(8,5,0,401))
+
+// calc.dll - Suspected malware
+DLL_BLOCKLIST_ENTRY("calc.dll", MAKE_VERSION(1,0,0,1))
+
+// hook.dll - Suspected malware
+DLL_BLOCKLIST_ENTRY("hook.dll", ALL_VERSIONS)
+
+// GoogleDesktopNetwork3.dll - Extremely old, unversioned instances
+// of this DLL cause crashes
+DLL_BLOCKLIST_ENTRY("googledesktopnetwork3.dll", UNVERSIONED)
+
+// rdolib.dll - Suspected malware
+DLL_BLOCKLIST_ENTRY("rdolib.dll", MAKE_VERSION(6,0,88,4))
+
+// fgjk4wvb.dll - Suspected malware
+DLL_BLOCKLIST_ENTRY("fgjk4wvb.dll", MAKE_VERSION(8,8,8,8))
+
+// radhslib.dll - Naomi internet filter - unmaintained since 2006
+DLL_BLOCKLIST_ENTRY("radhslib.dll", UNVERSIONED)
+
+// Music download filter for vkontakte.ru - old instances
+// of this DLL cause crashes
+DLL_BLOCKLIST_ENTRY("vksaver.dll", MAKE_VERSION(2,2,2,0))
+
+// Topcrash in Firefox 4.0b1
+DLL_BLOCKLIST_ENTRY("rlxf.dll", MAKE_VERSION(1,2,323,1))
+
+// psicon.dll - Topcrashes in Thunderbird, and some crashes in Firefox
+// Adobe photoshop library, now redundant in later installations
+DLL_BLOCKLIST_ENTRY("psicon.dll", ALL_VERSIONS)
+
+// Topcrash in Firefox 4 betas (bug 618899)
+DLL_BLOCKLIST_ENTRY("accelerator.dll", MAKE_VERSION(3,2,1,6))
+
+// Topcrash with Roboform in Firefox 8 (bug 699134)
+DLL_BLOCKLIST_ENTRY("rf-firefox.dll", MAKE_VERSION(7,6,1,0))
+DLL_BLOCKLIST_ENTRY("roboform.dll", MAKE_VERSION(7,6,1,0))
+
+// Topcrash with Babylon Toolbar on FF16+ (bug 721264)
+DLL_BLOCKLIST_ENTRY("babyfox.dll", ALL_VERSIONS)
+
+// sprotector.dll crashes, bug 957258
+DLL_BLOCKLIST_ENTRY("sprotector.dll", ALL_VERSIONS)
+
+// leave these two in always for tests
+DLL_BLOCKLIST_ENTRY("mozdllblockingtest.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("mozdllblockingtest_versioned.dll", 0x0000000400000000ULL)
+
+// Windows Media Foundation FLAC decoder and type sniffer (bug 839031).
+DLL_BLOCKLIST_ENTRY("mfflac.dll", ALL_VERSIONS)
+
+// Older Relevant Knowledge DLLs cause us to crash (bug 904001).
+DLL_BLOCKLIST_ENTRY("rlnx.dll", MAKE_VERSION(1, 3, 334, 9))
+DLL_BLOCKLIST_ENTRY("pmnx.dll", MAKE_VERSION(1, 3, 334, 9))
+DLL_BLOCKLIST_ENTRY("opnx.dll", MAKE_VERSION(1, 3, 334, 9))
+DLL_BLOCKLIST_ENTRY("prnx.dll", MAKE_VERSION(1, 3, 334, 9))
+
+// Older belgian ID card software causes Firefox to crash or hang on
+// shutdown, bug 831285 and 918399.
+DLL_BLOCKLIST_ENTRY("beid35cardlayer.dll", MAKE_VERSION(3, 5, 6, 6968))
+
+// bug 925459, bitguard crashes
+DLL_BLOCKLIST_ENTRY("bitguard.dll", ALL_VERSIONS)
+
+// bug 812683 - crashes in Windows library when Asus Gamer OSD is installed
+// Software is discontinued/unsupported
+DLL_BLOCKLIST_ENTRY("atkdx11disp.dll", ALL_VERSIONS)
+
+// Topcrash with Conduit SearchProtect, bug 944542
+DLL_BLOCKLIST_ENTRY("spvc32.dll", ALL_VERSIONS)
+
+// Topcrash with V-bates, bug 1002748 and bug 1023239
+DLL_BLOCKLIST_ENTRY("libinject.dll", UNVERSIONED)
+DLL_BLOCKLIST_ENTRY("libinject2.dll", 0x537DDC93, DllBlockInfo::USE_TIMESTAMP)
+DLL_BLOCKLIST_ENTRY("libredir2.dll", 0x5385B7ED, DllBlockInfo::USE_TIMESTAMP)
+
+// Crashes with RoboForm2Go written against old SDK, bug 988311/1196859
+DLL_BLOCKLIST_ENTRY("rf-firefox-22.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("rf-firefox-40.dll", ALL_VERSIONS)
+
+// Crashes with DesktopTemperature, bug 1046382
+DLL_BLOCKLIST_ENTRY("dtwxsvc.dll", 0x53153234, DllBlockInfo::USE_TIMESTAMP)
+
+// Startup crashes with Lenovo Onekey Theater, bug 1123778
+DLL_BLOCKLIST_ENTRY("activedetect32.dll", UNVERSIONED)
+DLL_BLOCKLIST_ENTRY("activedetect64.dll", UNVERSIONED)
+DLL_BLOCKLIST_ENTRY("windowsapihookdll32.dll", UNVERSIONED)
+DLL_BLOCKLIST_ENTRY("windowsapihookdll64.dll", UNVERSIONED)
+
+// Flash crashes with RealNetworks RealDownloader, bug 1132663
+DLL_BLOCKLIST_ENTRY("rndlnpshimswf.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("rndlmainbrowserrecordplugin.dll", ALL_VERSIONS)
+
+// Startup crashes with RealNetworks Browser Record Plugin, bug 1170141
+DLL_BLOCKLIST_ENTRY("nprpffbrowserrecordext.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("nprndlffbrowserrecordext.dll", ALL_VERSIONS)
+
+// Crashes with CyberLink YouCam, bug 1136968
+DLL_BLOCKLIST_ENTRY("ycwebcamerasource.ax", MAKE_VERSION(2, 0, 0, 1611))
+
+// Old version of WebcamMax crashes WebRTC, bug 1130061
+DLL_BLOCKLIST_ENTRY("vwcsource.ax", MAKE_VERSION(1, 5, 0, 0))
+
+// NetOp School, discontinued product, bug 763395
+DLL_BLOCKLIST_ENTRY("nlsp.dll", MAKE_VERSION(6, 23, 2012, 19))
+
+// Orbit Downloader, bug 1222819
+DLL_BLOCKLIST_ENTRY("grabdll.dll", MAKE_VERSION(2, 6, 1, 0))
+DLL_BLOCKLIST_ENTRY("grabkernel.dll", MAKE_VERSION(1, 0, 0, 1))
+
+// ESET, bug 1229252
+DLL_BLOCKLIST_ENTRY("eoppmonitor.dll", ALL_VERSIONS)
+
+// SS2OSD, bug 1262348
+DLL_BLOCKLIST_ENTRY("ss2osd.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("ss2devprops.dll", ALL_VERSIONS)
+
+// NHASUSSTRIXOSD.DLL, bug 1269244
+DLL_BLOCKLIST_ENTRY("nhasusstrixosd.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("nhasusstrixdevprops.dll", ALL_VERSIONS)
+
+// Crashes with PremierOpinion/RelevantKnowledge, bug 1277846
+DLL_BLOCKLIST_ENTRY("opls.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("opls64.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("pmls.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("pmls64.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("prls.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("prls64.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("rlls.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("rlls64.dll", ALL_VERSIONS)
+
+// Vorbis DirectShow filters, bug 1239690.
+DLL_BLOCKLIST_ENTRY("vorbis.acm", MAKE_VERSION(0, 0, 3, 6))
+
+// AhnLab Internet Security, bug 1311969
+DLL_BLOCKLIST_ENTRY("nzbrcom.dll", ALL_VERSIONS)
+
+// K7TotalSecurity, bug 1339083.
+DLL_BLOCKLIST_ENTRY("k7pswsen.dll", MAKE_VERSION(15, 2, 2, 95))
+
+// smci*.dll - goobzo crashware (bug 1339908)
+DLL_BLOCKLIST_ENTRY("smci32.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("smci64.dll", ALL_VERSIONS)
+
+// Crashes with Internet Download Manager, bug 1333486
+DLL_BLOCKLIST_ENTRY("idmcchandler7.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("idmcchandler7_64.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("idmcchandler5.dll", ALL_VERSIONS)
+DLL_BLOCKLIST_ENTRY("idmcchandler5_64.dll", ALL_VERSIONS)
+
+// Nahimic 2 breaks applicaton update (bug 1356637)
+DLL_BLOCKLIST_ENTRY("nahimic2devprops.dll", MAKE_VERSION(2, 5, 19, 0xffff))
+// Nahimic is causing crashes, bug 1233556
+DLL_BLOCKLIST_ENTRY("nahimicmsiosd.dll", UNVERSIONED)
+// Nahimic is causing crashes, bug 1360029
+DLL_BLOCKLIST_ENTRY("nahimicvrdevprops.dll", UNVERSIONED)
+DLL_BLOCKLIST_ENTRY("nahimic2osd.dll", MAKE_VERSION(2, 5, 19, 0xffff))
+DLL_BLOCKLIST_ENTRY("nahimicmsidevprops.dll", UNVERSIONED)
+
+// Bug 1268470 - crashes with Kaspersky Lab on Windows 8
+DLL_BLOCKLIST_ENTRY("klsihk64.dll", MAKE_VERSION(14, 0, 456, 0xffff), DllBlockInfo::BLOCK_WIN8_ONLY)
+
+// Bug 1407337, crashes with OpenSC < 0.16.0
+DLL_BLOCKLIST_ENTRY("onepin-opensc-pkcs11.dll", MAKE_VERSION(0, 15, 0xffff, 0xffff))
+
+// Avecto Privilege Guard causes crashes, bug 1385542
+DLL_BLOCKLIST_ENTRY("pghook.dll", ALL_VERSIONS)
+
+// Old versions of G DATA BankGuard, bug 1421991
+DLL_BLOCKLIST_ENTRY("banksafe64.dll", MAKE_VERSION(1, 2, 15299, 65535))
+
+// Old versions of G DATA, bug 1043775
+DLL_BLOCKLIST_ENTRY("gdkbfltdll64.dll", MAKE_VERSION(1, 0, 14141, 240))
+
+// Dell Backup and Recovery tool causes crashes, bug 1433408
+DLL_BLOCKLIST_ENTRY("dbroverlayiconnotbackuped.dll", MAKE_VERSION(1, 8, 0, 9))
+DLL_BLOCKLIST_ENTRY("dbroverlayiconbackuped.dll", MAKE_VERSION(1, 8, 0, 9))
+
+DLL_BLOCKLIST_DEFINITIONS_END
+
+#endif // mozilla_WindowsDllBlocklistDefs_h
--- a/mozglue/build/moz.build
+++ b/mozglue/build/moz.build
@@ -67,16 +67,18 @@ if CONFIG['MOZ_WIDGET_TOOLKIT']:
             'wintrust',
         ]
         DELAYLOAD_DLLS += [
             'crypt32.dll',
             'wintrust.dll',
         ]
         EXPORTS.mozilla += [
             'Authenticode.h',
+            'WindowsDllBlocklistCommon.h',
+            'WindowsDllBlocklistDefs.h',
         ]
         EXPORTS.mozilla.glue += [
             'WindowsDllServices.h',
         ]
 
     EXPORTS.mozilla += [
         'arm.h',
         'mips.h',
--- a/netwerk/protocol/http/UserAgentUpdates.jsm
+++ b/netwerk/protocol/http/UserAgentUpdates.jsm
@@ -5,17 +5,17 @@
 "use strict";
 
 var EXPORTED_SYMBOLS = ["UserAgentUpdates"];
 
 ChromeUtils.import("resource://gre/modules/AppConstants.jsm&q