Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Tue, 13 Dec 2016 19:25:01 -0800
changeset 325782 1ea0c60db5d25a7d522e2f252c1978ff4fc7538e
parent 325781 0c7a106074f7267cbd1c8bb9039625f00c38063b (current diff)
parent 325743 6f6c33db62e18c6c9d6d4223f7b19406147a9b51 (diff)
child 325783 896c2e2190869600e65d5d8c5c4365e4a0bcc9b3
child 325825 8cfab9e0752b5401342846a696601cfe73a4f04f
child 325843 aed4ce9487202cfa96f4e401f998f1ec59ac6fd7
child 342003 7604973adccb02cd3023259404aee502ff9da87f
push id84796
push userphilringnalda@gmail.com
push dateWed, 14 Dec 2016 03:30:34 +0000
treeherdermozilla-inbound@896c2e219086 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
1ea0c60db5d2 / 53.0a1 / 20161214030231 / files
nightly linux64
1ea0c60db5d2 / 53.0a1 / 20161214030231 / files
nightly mac
1ea0c60db5d2 / 53.0a1 / 20161214030231 / files
nightly win32
1ea0c60db5d2 / 53.0a1 / 20161214030231 / files
nightly win64
1ea0c60db5d2 / 53.0a1 / 20161214030231 / 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 m-i to m-c, a=merge MozReview-Commit-ID: ODjsKqdMLM
js/src/jit-test/tests/basic/arraybuffer-slice-warn.js
toolkit/crashreporter/test/unit/test_crash_runtimeabort.js
toolkit/crashreporter/test/unit/test_crashreporter_crash_profile_lock.js
xpcom/build/nsXREAppData.h
xpcom/glue/AppData.cpp
xpcom/glue/AppData.h
--- a/accessible/aom/AccessibleNode.cpp
+++ b/accessible/aom/AccessibleNode.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AccessibleNode.h"
 #include "mozilla/dom/AccessibleNodeBinding.h"
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DOMStringList.h"
 
 #include "Accessible-inl.h"
 #include "nsAccessibilityService.h"
 #include "DocAccessible.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::dom;
@@ -56,13 +57,27 @@ AccessibleNode::GetRole(nsAString& aRole
   if (mIntl) {
     GetOrCreateAccService()->GetStringRole(mIntl->Role(), aRole);
     return;
   }
 
   aRole.AssignLiteral("unknown");
 }
 
+void
+AccessibleNode::GetStates(nsTArray<nsString>& aStates)
+{
+  if (mIntl) {
+    if (!mStates) {
+      mStates = GetOrCreateAccService()->GetStringStates(mIntl->State());
+    }
+    aStates = mStates->StringArray();
+    return;
+  }
+
+  mStates->Add(NS_LITERAL_STRING("defunct"));
+}
+
 nsINode*
 AccessibleNode::GetDOMNode()
 {
   return mDOMNode;
 }
--- a/accessible/aom/AccessibleNode.h
+++ b/accessible/aom/AccessibleNode.h
@@ -4,50 +4,57 @@
  * 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 A11Y_AOM_ACCESSIBLENODE_H
 #define A11Y_AOM_ACCESSIBLENODE_H
 
 #include "nsWrapperCache.h"
 #include "mozilla/RefPtr.h"
+#include "nsTArray.h"
+#include "nsString.h"
 
 class nsINode;
 
 namespace mozilla {
 
 namespace a11y {
   class Accessible;
 }
 
 namespace dom {
 
+class DOMStringList;
 struct ParentObject;
 
 class AccessibleNode : public nsISupports,
                        public nsWrapperCache
 {
 public:
   explicit AccessibleNode(nsINode* aNode);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS;
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(AccessibleNode);
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override final;
   virtual dom::ParentObject GetParentObject() const final;
 
   void GetRole(nsAString& aRole);
+  void GetStates(nsTArray<nsString>& aStates);
   nsINode* GetDOMNode();
 
+  a11y::Accessible* Internal() const { return mIntl; }
+
 protected:
   AccessibleNode(const AccessibleNode& aCopy) = delete;
   AccessibleNode& operator=(const AccessibleNode& aCopy) = delete;
   virtual ~AccessibleNode();
 
   RefPtr<a11y::Accessible> mIntl;
   RefPtr<nsINode> mDOMNode;
+  RefPtr<dom::DOMStringList> mStates;
 };
 
 } // dom
 } // mozilla
 
 
 #endif // A11Y_JSAPI_ACCESSIBLENODE
--- a/accessible/aom/moz.build
+++ b/accessible/aom/moz.build
@@ -1,20 +1,20 @@
 # -*- 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/.
 
 EXPORTS.mozilla.dom += [
-    'AccessibleNode.h',
+    'AccessibleNode.h'
 ]
 
 UNIFIED_SOURCES += [
-    'AccessibleNode.cpp',
+    'AccessibleNode.cpp'
 ]
 
 LOCAL_INCLUDES += [
     '/accessible/base',
     '/accessible/generic',
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
--- a/accessible/base/nsAccessibilityService.cpp
+++ b/accessible/base/nsAccessibilityService.cpp
@@ -746,175 +746,179 @@ nsAccessibilityService::GetStringRole(ui
       return;
   }
 
 #undef ROLE
 }
 
 void
 nsAccessibilityService::GetStringStates(uint32_t aState, uint32_t aExtraState,
-                                        nsISupports **aStringStates)
+                                        nsISupports** aStringStates)
 {
-  RefPtr<DOMStringList> stringStates = new DOMStringList();
-
-  uint64_t state = nsAccUtils::To64State(aState, aExtraState);
+  RefPtr<DOMStringList> stringStates = 
+    GetStringStates(nsAccUtils::To64State(aState, aExtraState));
 
-  // states
-  if (state & states::UNAVAILABLE) {
-    stringStates->Add(NS_LITERAL_STRING("unavailable"));
-  }
-  if (state & states::SELECTED) {
-    stringStates->Add(NS_LITERAL_STRING("selected"));
-  }
-  if (state & states::FOCUSED) {
-    stringStates->Add(NS_LITERAL_STRING("focused"));
-  }
-  if (state & states::PRESSED) {
-    stringStates->Add(NS_LITERAL_STRING("pressed"));
-  }
-  if (state & states::CHECKED) {
-    stringStates->Add(NS_LITERAL_STRING("checked"));
-  }
-  if (state & states::MIXED) {
-    stringStates->Add(NS_LITERAL_STRING("mixed"));
-  }
-  if (state & states::READONLY) {
-    stringStates->Add(NS_LITERAL_STRING("readonly"));
-  }
-  if (state & states::HOTTRACKED) {
-    stringStates->Add(NS_LITERAL_STRING("hottracked"));
-  }
-  if (state & states::DEFAULT) {
-    stringStates->Add(NS_LITERAL_STRING("default"));
-  }
-  if (state & states::EXPANDED) {
-    stringStates->Add(NS_LITERAL_STRING("expanded"));
-  }
-  if (state & states::COLLAPSED) {
-    stringStates->Add(NS_LITERAL_STRING("collapsed"));
-  }
-  if (state & states::BUSY) {
-    stringStates->Add(NS_LITERAL_STRING("busy"));
-  }
-  if (state & states::FLOATING) {
-    stringStates->Add(NS_LITERAL_STRING("floating"));
-  }
-  if (state & states::ANIMATED) {
-    stringStates->Add(NS_LITERAL_STRING("animated"));
-  }
-  if (state & states::INVISIBLE) {
-    stringStates->Add(NS_LITERAL_STRING("invisible"));
-  }
-  if (state & states::OFFSCREEN) {
-    stringStates->Add(NS_LITERAL_STRING("offscreen"));
-  }
-  if (state & states::SIZEABLE) {
-    stringStates->Add(NS_LITERAL_STRING("sizeable"));
-  }
-  if (state & states::MOVEABLE) {
-    stringStates->Add(NS_LITERAL_STRING("moveable"));
-  }
-  if (state & states::SELFVOICING) {
-    stringStates->Add(NS_LITERAL_STRING("selfvoicing"));
-  }
-  if (state & states::FOCUSABLE) {
-    stringStates->Add(NS_LITERAL_STRING("focusable"));
-  }
-  if (state & states::SELECTABLE) {
-    stringStates->Add(NS_LITERAL_STRING("selectable"));
-  }
-  if (state & states::LINKED) {
-    stringStates->Add(NS_LITERAL_STRING("linked"));
-  }
-  if (state & states::TRAVERSED) {
-    stringStates->Add(NS_LITERAL_STRING("traversed"));
-  }
-  if (state & states::MULTISELECTABLE) {
-    stringStates->Add(NS_LITERAL_STRING("multiselectable"));
-  }
-  if (state & states::EXTSELECTABLE) {
-    stringStates->Add(NS_LITERAL_STRING("extselectable"));
-  }
-  if (state & states::PROTECTED) {
-    stringStates->Add(NS_LITERAL_STRING("protected"));
-  }
-  if (state & states::HASPOPUP) {
-    stringStates->Add(NS_LITERAL_STRING("haspopup"));
-  }
-  if (state & states::REQUIRED) {
-    stringStates->Add(NS_LITERAL_STRING("required"));
-  }
-  if (state & states::ALERT) {
-    stringStates->Add(NS_LITERAL_STRING("alert"));
-  }
-  if (state & states::INVALID) {
-    stringStates->Add(NS_LITERAL_STRING("invalid"));
-  }
-  if (state & states::CHECKABLE) {
-    stringStates->Add(NS_LITERAL_STRING("checkable"));
-  }
-
-  // extraStates
-  if (state & states::SUPPORTS_AUTOCOMPLETION) {
-    stringStates->Add(NS_LITERAL_STRING("autocompletion"));
-  }
-  if (state & states::DEFUNCT) {
-    stringStates->Add(NS_LITERAL_STRING("defunct"));
-  }
-  if (state & states::SELECTABLE_TEXT) {
-    stringStates->Add(NS_LITERAL_STRING("selectable text"));
-  }
-  if (state & states::EDITABLE) {
-    stringStates->Add(NS_LITERAL_STRING("editable"));
-  }
-  if (state & states::ACTIVE) {
-    stringStates->Add(NS_LITERAL_STRING("active"));
-  }
-  if (state & states::MODAL) {
-    stringStates->Add(NS_LITERAL_STRING("modal"));
-  }
-  if (state & states::MULTI_LINE) {
-    stringStates->Add(NS_LITERAL_STRING("multi line"));
-  }
-  if (state & states::HORIZONTAL) {
-    stringStates->Add(NS_LITERAL_STRING("horizontal"));
-  }
-  if (state & states::OPAQUE1) {
-    stringStates->Add(NS_LITERAL_STRING("opaque"));
-  }
-  if (state & states::SINGLE_LINE) {
-    stringStates->Add(NS_LITERAL_STRING("single line"));
-  }
-  if (state & states::TRANSIENT) {
-    stringStates->Add(NS_LITERAL_STRING("transient"));
-  }
-  if (state & states::VERTICAL) {
-    stringStates->Add(NS_LITERAL_STRING("vertical"));
-  }
-  if (state & states::STALE) {
-    stringStates->Add(NS_LITERAL_STRING("stale"));
-  }
-  if (state & states::ENABLED) {
-    stringStates->Add(NS_LITERAL_STRING("enabled"));
-  }
-  if (state & states::SENSITIVE) {
-    stringStates->Add(NS_LITERAL_STRING("sensitive"));
-  }
-  if (state & states::EXPANDABLE) {
-    stringStates->Add(NS_LITERAL_STRING("expandable"));
-  }
-
-  //unknown states
+  // unknown state
   if (!stringStates->Length()) {
     stringStates->Add(NS_LITERAL_STRING("unknown"));
   }
 
   stringStates.forget(aStringStates);
 }
 
+already_AddRefed<DOMStringList>
+nsAccessibilityService::GetStringStates(uint64_t aStates) const
+{
+  RefPtr<DOMStringList> stringStates = new DOMStringList();
+
+  if (aStates & states::UNAVAILABLE) {
+    stringStates->Add(NS_LITERAL_STRING("unavailable"));
+  }
+  if (aStates & states::SELECTED) {
+    stringStates->Add(NS_LITERAL_STRING("selected"));
+  }
+  if (aStates & states::FOCUSED) {
+    stringStates->Add(NS_LITERAL_STRING("focused"));
+  }
+  if (aStates & states::PRESSED) {
+    stringStates->Add(NS_LITERAL_STRING("pressed"));
+  }
+  if (aStates & states::CHECKED) {
+    stringStates->Add(NS_LITERAL_STRING("checked"));
+  }
+  if (aStates & states::MIXED) {
+    stringStates->Add(NS_LITERAL_STRING("mixed"));
+  }
+  if (aStates & states::READONLY) {
+    stringStates->Add(NS_LITERAL_STRING("readonly"));
+  }
+  if (aStates & states::HOTTRACKED) {
+    stringStates->Add(NS_LITERAL_STRING("hottracked"));
+  }
+  if (aStates & states::DEFAULT) {
+    stringStates->Add(NS_LITERAL_STRING("default"));
+  }
+  if (aStates & states::EXPANDED) {
+    stringStates->Add(NS_LITERAL_STRING("expanded"));
+  }
+  if (aStates & states::COLLAPSED) {
+    stringStates->Add(NS_LITERAL_STRING("collapsed"));
+  }
+  if (aStates & states::BUSY) {
+    stringStates->Add(NS_LITERAL_STRING("busy"));
+  }
+  if (aStates & states::FLOATING) {
+    stringStates->Add(NS_LITERAL_STRING("floating"));
+  }
+  if (aStates & states::ANIMATED) {
+    stringStates->Add(NS_LITERAL_STRING("animated"));
+  }
+  if (aStates & states::INVISIBLE) {
+    stringStates->Add(NS_LITERAL_STRING("invisible"));
+  }
+  if (aStates & states::OFFSCREEN) {
+    stringStates->Add(NS_LITERAL_STRING("offscreen"));
+  }
+  if (aStates & states::SIZEABLE) {
+    stringStates->Add(NS_LITERAL_STRING("sizeable"));
+  }
+  if (aStates & states::MOVEABLE) {
+    stringStates->Add(NS_LITERAL_STRING("moveable"));
+  }
+  if (aStates & states::SELFVOICING) {
+    stringStates->Add(NS_LITERAL_STRING("selfvoicing"));
+  }
+  if (aStates & states::FOCUSABLE) {
+    stringStates->Add(NS_LITERAL_STRING("focusable"));
+  }
+  if (aStates & states::SELECTABLE) {
+    stringStates->Add(NS_LITERAL_STRING("selectable"));
+  }
+  if (aStates & states::LINKED) {
+    stringStates->Add(NS_LITERAL_STRING("linked"));
+  }
+  if (aStates & states::TRAVERSED) {
+    stringStates->Add(NS_LITERAL_STRING("traversed"));
+  }
+  if (aStates & states::MULTISELECTABLE) {
+    stringStates->Add(NS_LITERAL_STRING("multiselectable"));
+  }
+  if (aStates & states::EXTSELECTABLE) {
+    stringStates->Add(NS_LITERAL_STRING("extselectable"));
+  }
+  if (aStates & states::PROTECTED) {
+    stringStates->Add(NS_LITERAL_STRING("protected"));
+  }
+  if (aStates & states::HASPOPUP) {
+    stringStates->Add(NS_LITERAL_STRING("haspopup"));
+  }
+  if (aStates & states::REQUIRED) {
+    stringStates->Add(NS_LITERAL_STRING("required"));
+  }
+  if (aStates & states::ALERT) {
+    stringStates->Add(NS_LITERAL_STRING("alert"));
+  }
+  if (aStates & states::INVALID) {
+    stringStates->Add(NS_LITERAL_STRING("invalid"));
+  }
+  if (aStates & states::CHECKABLE) {
+    stringStates->Add(NS_LITERAL_STRING("checkable"));
+  }
+  if (aStates & states::SUPPORTS_AUTOCOMPLETION) {
+    stringStates->Add(NS_LITERAL_STRING("autocompletion"));
+  }
+  if (aStates & states::DEFUNCT) {
+    stringStates->Add(NS_LITERAL_STRING("defunct"));
+  }
+  if (aStates & states::SELECTABLE_TEXT) {
+    stringStates->Add(NS_LITERAL_STRING("selectable text"));
+  }
+  if (aStates & states::EDITABLE) {
+    stringStates->Add(NS_LITERAL_STRING("editable"));
+  }
+  if (aStates & states::ACTIVE) {
+    stringStates->Add(NS_LITERAL_STRING("active"));
+  }
+  if (aStates & states::MODAL) {
+    stringStates->Add(NS_LITERAL_STRING("modal"));
+  }
+  if (aStates & states::MULTI_LINE) {
+    stringStates->Add(NS_LITERAL_STRING("multi line"));
+  }
+  if (aStates & states::HORIZONTAL) {
+    stringStates->Add(NS_LITERAL_STRING("horizontal"));
+  }
+  if (aStates & states::OPAQUE1) {
+    stringStates->Add(NS_LITERAL_STRING("opaque"));
+  }
+  if (aStates & states::SINGLE_LINE) {
+    stringStates->Add(NS_LITERAL_STRING("single line"));
+  }
+  if (aStates & states::TRANSIENT) {
+    stringStates->Add(NS_LITERAL_STRING("transient"));
+  }
+  if (aStates & states::VERTICAL) {
+    stringStates->Add(NS_LITERAL_STRING("vertical"));
+  }
+  if (aStates & states::STALE) {
+    stringStates->Add(NS_LITERAL_STRING("stale"));
+  }
+  if (aStates & states::ENABLED) {
+    stringStates->Add(NS_LITERAL_STRING("enabled"));
+  }
+  if (aStates & states::SENSITIVE) {
+    stringStates->Add(NS_LITERAL_STRING("sensitive"));
+  }
+  if (aStates & states::EXPANDABLE) {
+    stringStates->Add(NS_LITERAL_STRING("expandable"));
+  }
+
+  return stringStates.forget();
+}
+
 void
 nsAccessibilityService::GetStringEventType(uint32_t aEventType,
                                            nsAString& aString)
 {
   NS_ASSERTION(nsIAccessibleEvent::EVENT_LAST_ENTRY == ArrayLength(kEventTypeNames),
                "nsIAccessibleEvent constants are out of sync to kEventTypeNames");
 
   if (aEventType >= ArrayLength(kEventTypeNames)) {
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -19,16 +19,21 @@
 
 class nsImageFrame;
 class nsIArray;
 class nsIPersistentProperties;
 class nsPluginFrame;
 class nsITreeView;
 
 namespace mozilla {
+
+namespace dom {
+  class DOMStringList;
+}
+
 namespace a11y {
 
 class ApplicationAccessible;
 class xpcAccessibleApplication;
 
 /**
  * Return focus manager.
  */
@@ -103,16 +108,18 @@ public:
   /**
    * Get a string equivalent for an accessilbe role value.
    */
   void GetStringRole(uint32_t aRole, nsAString& aString);
 
   /**
    * Get a string equivalent for an accessible state/extra state.
    */
+  already_AddRefed<mozilla::dom::DOMStringList>
+    GetStringStates(uint64_t aStates) const;
   void GetStringStates(uint32_t aState, uint32_t aExtraState,
                        nsISupports **aStringStates);
 
   /**
    * Get a string equivalent for an accessible event value.
    */
   void GetStringEventType(uint32_t aEventType, nsAString& aString);
 
--- a/accessible/tests/mochitest/aom/test_general.html
+++ b/accessible/tests/mochitest/aom/test_general.html
@@ -44,12 +44,40 @@
   // Check that the WebIDL is as expected.
   function checkImplementation(ifrDoc) {
     let anode = ifrDoc.accessibleNode;
     ok(anode, "DOM document has accessible node");
 
     is(anode.role, 'document', 'correct role of a document accessible node');
     is(anode.DOMNode, ifrDoc, 'correct DOM Node of a document accessible node');
 
+    // States may differ depending on the document state, for example, if it is
+    // loaded or is loading still.
+    var states = null;
+    switch (anode.states.length) {
+      case 5:
+        states = [
+          'readonly', 'focusable', 'opaque', 'enabled', 'sensitive'
+        ];
+        break;
+      case 6:
+        states = [
+          'readonly', 'busy', 'focusable', 'opaque', 'enabled', 'sensitive'
+        ];
+        break;
+      case 7:
+        states = [
+          'readonly', 'busy', 'focusable', 'opaque', 'stale', 'enabled', 'sensitive'
+        ];
+        break;
+      default:
+        ok(false, 'Unexpected amount of states');
+    }
+    if (states) {
+      for (var i = 0; i < states.length; i++) {
+        is(anode.states[i], states[i], `${states[i]} state is expected at ${i}th index`);
+      }
+    }
+
     finish();
   }
   </script>
 </head>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsXULAppAPI.h"
-#include "mozilla/AppData.h"
+#include "mozilla/XREAppData.h"
 #include "application.ini.h"
 #include "nsXPCOMGlue.h"
 #if defined(XP_WIN)
 #include <windows.h>
 #include <stdlib.h>
 #elif defined(XP_UNIX)
 #include <sys/resource.h>
 #include <unistd.h>
@@ -158,36 +158,34 @@ static bool IsArg(const char* arg, const
   if (*arg == '/')
     return !strcasecmp(++arg, s);
 #endif
 
   return false;
 }
 
 XRE_GetFileFromPathType XRE_GetFileFromPath;
-XRE_CreateAppDataType XRE_CreateAppData;
-XRE_FreeAppDataType XRE_FreeAppData;
+XRE_ParseAppDataType XRE_ParseAppData;
 XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
 XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
 XRE_mainType XRE_main;
 XRE_StopLateWriteChecksType XRE_StopLateWriteChecks;
 XRE_XPCShellMainType XRE_XPCShellMain;
 XRE_GetProcessTypeType XRE_GetProcessType;
 XRE_SetProcessTypeType XRE_SetProcessType;
 XRE_InitChildProcessType XRE_InitChildProcess;
 XRE_EnableSameExecutableForContentProcType XRE_EnableSameExecutableForContentProc;
 #ifdef LIBFUZZER
 XRE_LibFuzzerSetMainType XRE_LibFuzzerSetMain;
 XRE_LibFuzzerGetFuncsType XRE_LibFuzzerGetFuncs;
 #endif
 
 static const nsDynamicFunctionLoad kXULFuncs[] = {
     { "XRE_GetFileFromPath", (NSFuncPtr*) &XRE_GetFileFromPath },
-    { "XRE_CreateAppData", (NSFuncPtr*) &XRE_CreateAppData },
-    { "XRE_FreeAppData", (NSFuncPtr*) &XRE_FreeAppData },
+    { "XRE_ParseAppData", (NSFuncPtr*) &XRE_ParseAppData },
     { "XRE_TelemetryAccumulate", (NSFuncPtr*) &XRE_TelemetryAccumulate },
     { "XRE_StartupTimelineRecord", (NSFuncPtr*) &XRE_StartupTimelineRecord },
     { "XRE_main", (NSFuncPtr*) &XRE_main },
     { "XRE_StopLateWriteChecks", (NSFuncPtr*) &XRE_StopLateWriteChecks },
     { "XRE_XPCShellMain", (NSFuncPtr*) &XRE_XPCShellMain },
     { "XRE_GetProcessType", (NSFuncPtr*) &XRE_GetProcessType },
     { "XRE_SetProcessType", (NSFuncPtr*) &XRE_SetProcessType },
     { "XRE_InitChildProcess", (NSFuncPtr*) &XRE_InitChildProcess },
@@ -256,59 +254,53 @@ static int do_main(int argc, char* argv[
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     shellData.sandboxBrokerServices =
       sandboxing::GetInitializedBrokerServices();
 #endif
 
     return XRE_XPCShellMain(--argc, argv, envp, &shellData);
   }
 
+  XREAppData appData;
+  appData.xreDirectory = xreDirectory;
+
   if (appini) {
-    nsXREAppData *appData;
-    rv = XRE_CreateAppData(appini, &appData);
+    rv = XRE_ParseAppData(appini, appData);
     if (NS_FAILED(rv)) {
       Output("Couldn't read application.ini");
       return 255;
     }
-#if defined(HAS_DLL_BLOCKLIST)
-    // The dll blocklist operates in the exe vs. xullib. Pass a flag to
-    // xullib so automated tests can check the result once the browser
-    // is up and running.
-    appData->flags |=
-      DllBlocklist_CheckStatus() ? NS_XRE_DLL_BLOCKLIST_ENABLED : 0;
+
+    appini->GetParent(getter_AddRefs(appData.directory));
+  } else {
+    // no -app flag so we use the compiled-in app data
+    appData = sAppData;
+
+    nsCOMPtr<nsIFile> exeFile;
+    rv = mozilla::BinaryPath::GetFile(argv[0], getter_AddRefs(exeFile));
+    if (NS_FAILED(rv)) {
+      Output("Couldn't find the application directory.\n");
+      return 255;
+    }
+
+    nsCOMPtr<nsIFile> greDir;
+    exeFile->GetParent(getter_AddRefs(greDir));
+#ifdef XP_MACOSX
+    greDir->SetNativeLeafName(NS_LITERAL_CSTRING(kOSXResourcesFolder));
 #endif
-    // xreDirectory already has a refcount from NS_NewLocalFile
-    appData->xreDirectory = xreDirectory;
-    int result = XRE_main(argc, argv, appData, mainFlags);
-    XRE_FreeAppData(appData);
-    return result;
+    nsCOMPtr<nsIFile> appSubdir;
+    greDir->Clone(getter_AddRefs(appSubdir));
+    appSubdir->Append(NS_LITERAL_STRING(kDesktopFolder));
+    appData.directory = appSubdir;
   }
 
-  ScopedAppData appData(&sAppData);
-  nsCOMPtr<nsIFile> exeFile;
-  rv = mozilla::BinaryPath::GetFile(argv[0], getter_AddRefs(exeFile));
-  if (NS_FAILED(rv)) {
-    Output("Couldn't find the application directory.\n");
-    return 255;
-  }
-
-  nsCOMPtr<nsIFile> greDir;
-  exeFile->GetParent(getter_AddRefs(greDir));
-#ifdef XP_MACOSX
-  greDir->SetNativeLeafName(NS_LITERAL_CSTRING(kOSXResourcesFolder));
-#endif
-  nsCOMPtr<nsIFile> appSubdir;
-  greDir->Clone(getter_AddRefs(appSubdir));
-  appSubdir->Append(NS_LITERAL_STRING(kDesktopFolder));
-
-  SetStrongPtr(appData.directory, static_cast<nsIFile*>(appSubdir.get()));
-  // xreDirectory already has a refcount from NS_NewLocalFile
-  appData.xreDirectory = xreDirectory;
-
 #if defined(HAS_DLL_BLOCKLIST)
+  // The dll blocklist operates in the exe vs. xullib. Pass a flag to
+  // xullib so automated tests can check the result once the browser
+  // is up and running.
   appData.flags |=
     DllBlocklist_CheckStatus() ? NS_XRE_DLL_BLOCKLIST_ENABLED : 0;
 #endif
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   sandbox::BrokerServices* brokerServices =
     sandboxing::GetInitializedBrokerServices();
 #if defined(MOZ_CONTENT_SANDBOX)
@@ -320,17 +312,17 @@ static int do_main(int argc, char* argv[
   appData.sandboxBrokerServices = brokerServices;
 #endif
 
 #ifdef LIBFUZZER
   if (getenv("LIBFUZZER"))
     XRE_LibFuzzerSetMain(argc, argv, libfuzzer_main);
 #endif
 
-  return XRE_main(argc, argv, &appData, mainFlags);
+  return XRE_main(argc, argv, appData, mainFlags);
 }
 
 static bool
 FileExists(const char *path)
 {
 #ifdef XP_WIN
   wchar_t wideDir[MAX_PATH];
   MultiByteToWideChar(CP_UTF8, 0, path, -1, wideDir, MAX_PATH);
@@ -440,31 +432,32 @@ int main(int argc, char* argv[], char* e
     // InitXPCOMGlue calls NS_LogInit, so we need to balance it here.
     NS_LogTerm();
 
     return result;
   }
 #endif
 
 
-  nsIFile *xreDirectory;
+  nsCOMPtr<nsIFile> xreDirectory;
 
-  nsresult rv = InitXPCOMGlue(argv[0], &xreDirectory);
+  nsresult rv = InitXPCOMGlue(argv[0], getter_AddRefs(xreDirectory));
   if (NS_FAILED(rv)) {
     return 255;
   }
 
   XRE_StartupTimelineRecord(mozilla::StartupTimeline::START, start);
 
 #ifdef MOZ_BROWSER_CAN_BE_CONTENTPROC
   XRE_EnableSameExecutableForContentProc();
 #endif
 
   int result = do_main(argc, argv, envp, xreDirectory);
 
+  xreDirectory = nullptr;
   NS_LogTerm();
 
 #ifdef XP_MACOSX
   // Allow writes again. While we would like to catch writes from static
   // destructors to allow early exits to use _exit, we know that there is
   // at least one such write that we don't control (see bug 826029). For
   // now we enable writes again and early exits will have to use exit instead
   // of _exit.
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -242,17 +242,16 @@ var ctrlTab = {
     if (aPreview.firstChild)
       aPreview.removeChild(aPreview.firstChild);
     if (aTab) {
       let canvasWidth = this.canvasWidth;
       let canvasHeight = this.canvasHeight;
       aPreview.appendChild(tabPreviews.get(aTab));
       aPreview.setAttribute("label", aTab.label);
       aPreview.setAttribute("tooltiptext", aTab.label);
-      aPreview.setAttribute("crop", aTab.crop);
       aPreview.setAttribute("canvaswidth", canvasWidth);
       aPreview.setAttribute("canvasstyle",
                             "max-width:" + canvasWidth + "px;" +
                             "min-width:" + canvasWidth + "px;" +
                             "max-height:" + canvasHeight + "px;" +
                             "min-height:" + canvasHeight + "px;");
       if (aTab.image)
         aPreview.setAttribute("image", aTab.image);
@@ -469,17 +468,17 @@ var ctrlTab = {
   },
 
   handleEvent: function ctrlTab_handleEvent(event) {
     switch (event.type) {
       case "SSWindowRestored":
         this._initRecentlyUsedTabs();
         break;
       case "TabAttrModified":
-        // tab attribute modified (e.g. label, crop, busy, image, selected)
+        // tab attribute modified (e.g. label, busy, image, selected)
         for (let i = this.previews.length - 1; i >= 0; i--) {
           if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
             this.updatePreview(this.previews[i], event.target);
             break;
           }
         }
         break;
       case "TabSelect":
--- a/browser/base/content/browser-tabPreviews.xml
+++ b/browser/base/content/browser-tabPreviews.xml
@@ -12,17 +12,17 @@
   <binding id="ctrlTab-preview" extends="chrome://global/content/bindings/button.xml#button-base">
     <content pack="center">
       <xul:stack>
         <xul:vbox class="ctrlTab-preview-inner" align="center" pack="center"
                   xbl:inherits="width=canvaswidth">
           <xul:hbox class="tabPreview-canvas" xbl:inherits="style=canvasstyle">
             <children/>
           </xul:hbox>
-          <xul:label xbl:inherits="value=label,crop" class="plain"/>
+          <xul:label xbl:inherits="value=label" crop="end" class="plain"/>
         </xul:vbox>
         <xul:hbox class="ctrlTab-favicon-container" xbl:inherits="hidden=noicon">
           <xul:image class="ctrlTab-favicon" xbl:inherits="src=image"/>
         </xul:hbox>
       </xul:stack>
     </content>
     <handlers>
       <handler event="mouseover" action="ctrlTab._mouseOverFocus(this);"/>
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -481,13 +481,14 @@ tags = mcb
 [browser_windowactivation.js]
 [browser_contextmenu_childprocess.js]
 [browser_bug963945.js]
 [browser_domFullscreen_fullscreenMode.js]
 tags = fullscreen
 [browser_menuButtonBadgeManager.js]
 [browser_newTabDrop.js]
 [browser_newWindowDrop.js]
+skip-if = true # bug 1323276
 [browser_csp_block_all_mixedcontent.js]
 tags = mcb
 [browser_newwindow_focus.js]
 skip-if = (os == "linux" && !e10s) # Bug 1263254 - Perma fails on Linux without e10s for some reason.
 [browser_bug1299667.js]
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -771,23 +771,22 @@ menuitem.bookmark-item {
   background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings:hover:active {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 #urlbar-search-splitter {
-  -moz-appearance: none;
+  /* The splitter width should equal the location and search bars' combined
+     neighboring margin and border width. */
   width: 8px;
-  margin-inline-start: -4px;
-}
-
-#urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
-  margin-inline-start: 0;
+  margin: 0 -4px;
+  position: relative;
+  -moz-appearance: none;
 }
 
 #urlbar-display-box {
   margin-top: -1px;
   margin-bottom: -1px;
 }
 
 .urlbar-display {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -235,21 +235,16 @@
     z-index: 1;
   }
 
   #main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:-moz-window-inactive:not(:-moz-lwtheme) {
     border-top-color: hsla(0,0%,0%,.05);
   }
 }
 
-#nav-bar-customization-target {
-  /* This shouldn't really be here. See bug 1322953. */
-  padding-inline-end: 4px;
-}
-
 #PersonalToolbar {
   padding: 0 4px 4px;
 }
 
 #PersonalToolbar:not([collapsed=true]) {
   /* 4px padding ^  plus 19px personal-bookmarks (see below) */
   min-height: 23px;
 }
@@ -1705,17 +1700,16 @@ toolbar .toolbarbutton-1 > .toolbarbutto
 }
 
 #urlbar-search-splitter {
   min-width: 8px;
   width: 8px;
   background-image: none;
   margin: 0 -4px;
   position: relative;
-  height: 22px;
 }
 
 #search-container {
   min-width: calc(54px + 11ch);
 }
 
 #wrapper-urlbar-container[place="palette"] {
   max-width: 20em;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1445,26 +1445,25 @@ html|*.urlbar-input:-moz-lwtheme::placeh
   background-color: var(--arrowpanel-dimmed);
 }
 
 #urlbar-search-settings:hover:active {
   background-color: var(--arrowpanel-dimmed-further);
 }
 
 #urlbar-search-splitter {
-  min-width: 6px;
-  margin-inline-start: -3px;
+  /* The splitter width should equal the location and search bars' combined
+     neighboring margin and border width. */
+  min-width: 8px;
+  margin: 0 -4px;
+  position: relative;
   border: none;
   background: transparent;
 }
 
-#urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
-  margin-inline-start: 0;
-}
-
 .urlbar-display {
   margin-top: 0;
   margin-bottom: 0;
   margin-inline-start: 0;
   color: GrayText;
 }
 
 %include ../shared/urlbarSearchSuggestionsNotification.inc.css
--- a/build/appini_header.py
+++ b/build/appini_header.py
@@ -1,14 +1,14 @@
 # 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/.
 
 '''Parses a given application.ini file and outputs the corresponding
-   XULAppData structure as a C++ header file'''
+   StaticXREAppData structure as a C++ header file'''
 
 import ConfigParser
 import sys
 
 def main(output, file):
     config = ConfigParser.RawConfigParser()
     config.read(file)
     flags = set()
@@ -29,29 +29,26 @@ def main(output, file):
     if missing:
         print >>sys.stderr, \
             "Missing values in %s: %s" % (file, ', '.join(missing))
         sys.exit(1)
 
     if not 'Crash Reporter:serverurl' in appdata:
         appdata['Crash Reporter:serverurl'] = ''
 
-    output.write('''#include "nsXREAppData.h"
-             static const nsXREAppData sAppData = {
-                 sizeof(nsXREAppData),
-                 NULL, // directory
+    output.write('''#include "mozilla/XREAppData.h"
+             static const mozilla::StaticXREAppData sAppData = {
                  "%(App:vendor)s",
                  "%(App:name)s",
                  "%(App:remotingname)s",
                  "%(App:version)s",
                  "%(App:buildid)s",
                  "%(App:id)s",
                  NULL, // copyright
                  %(flags)s,
-                 NULL, // xreDirectory
                  "%(Gecko:minversion)s",
                  "%(Gecko:maxversion)s",
                  "%(Crash Reporter:serverurl)s",
                  %(App:profile)s
              };''' % appdata)
 
 if __name__ == '__main__':
     if len(sys.argv) != 1:
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -11,16 +11,17 @@ import posixpath
 import tempfile
 import shutil
 import subprocess
 import sys
 
 from automation import Automation
 from mozdevice import DMError, DeviceManager
 from mozlog import get_default_logger
+from mozscreenshot import dump_screen
 import mozcrash
 
 # signatures for logcat messages that we don't care about much
 fennecLogcatFilters = [ "The character encoding of the HTML document was not declared",
                         "Use of Mutation Events is deprecated. Use MutationObserver instead.",
                         "Unexpected value from nativeGetEnabledTags: 0" ]
 
 class RemoteAutomation(Automation):
@@ -103,16 +104,17 @@ class RemoteAutomation(Automation):
 
         return env
 
     def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
         """ Wait for tests to finish.
             If maxTime seconds elapse or no output is detected for timeout
             seconds, kill the process and fail the test.
         """
+        proc.utilityPath = utilityPath
         # maxTime is used to override the default timeout, we should honor that
         status = proc.wait(timeout = maxTime, noOutputTimeout = timeout)
         self.lastTestSeen = proc.getLastTestSeen
 
         topActivity = self._devicemanager.getTopActivity()
         if topActivity == proc.procName:
             proc.kill(True)
         if status == 1:
@@ -279,16 +281,17 @@ class RemoteAutomation(Automation):
         dm = None
         def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None,
                      messageLogger=None):
             self.dm = dm
             self.stdoutlen = 0
             self.lastTestSeen = "remoteautomation.py"
             self.proc = dm.launchProcess(cmd, stdout, cwd, env, True)
             self.messageLogger = messageLogger
+            self.utilityPath = None
 
             if (self.proc is None):
                 if cmd[0] == 'am':
                     self.proc = stdout
                 else:
                     raise Exception("unable to launch process")
             self.procName = cmd[0].split('/')[-1]
             if cmd[0] == 'am' and cmd[1] in RemoteAutomation._specialAmCommands:
@@ -403,16 +406,23 @@ class RemoteAutomation(Automation):
                     status = 2
                     break
                 top = self.dm.getTopActivity()
             # Flush anything added to stdout during the sleep
             self.read_stdout()
             return status
 
         def kill(self, stagedShutdown = False):
+            if self.utilityPath:
+                # Take a screenshot to capture the screen state just before
+                # the application is killed. There are on-device screenshot
+                # options but they rarely work well with Firefox on the
+                # Android emulator. dump_screen provides an effective
+                # screenshot of the emulator and its host desktop.
+                dump_screen(self.utilityPath, get_default_logger())
             if stagedShutdown:
                 # Trigger an ANR report with "kill -3" (SIGQUIT)
                 self.dm.killProcess(self.procName, 3)
                 time.sleep(3)
                 # Trigger a breakpad dump with "kill -6" (SIGABRT)
                 self.dm.killProcess(self.procName, 6)
                 # Wait for process to end
                 retries = 0
--- a/dom/base/IdleRequest.cpp
+++ b/dom/base/IdleRequest.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "IdleRequest.h"
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/dom/IdleDeadline.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/PerformanceTiming.h"
+#include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "nsComponentManagerUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsISupportsPrimitives.h"
 #include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
@@ -58,17 +59,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsITimeoutHandler)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimeoutHandler)
 NS_INTERFACE_MAP_END
 
 nsresult
 IdleRequest::SetTimeout(uint32_t aTimeout)
 {
   int32_t handle;
-  nsresult rv = nsGlobalWindow::Cast(mWindow)->SetTimeoutOrInterval(
+  nsresult rv = mWindow->TimeoutManager().SetTimeout(
     this, aTimeout, false, Timeout::Reason::eIdleCallbackTimeout, &handle);
   mTimeoutHandle = Some(handle);
 
   return rv;
 }
 
 nsresult
 IdleRequest::Run()
@@ -120,17 +121,17 @@ IdleRequest::RunIdleRequestCallback(bool
 
   return error.StealNSResult();
 }
 
 void
 IdleRequest::CancelTimeout()
 {
   if (mTimeoutHandle.isSome()) {
-    nsGlobalWindow::Cast(mWindow)->ClearTimeoutOrInterval(
+    mWindow->TimeoutManager().ClearTimeout(
       mTimeoutHandle.value(), Timeout::Reason::eIdleCallbackTimeout);
   }
 }
 
 nsresult
 IdleRequest::Call()
 {
   SetDeadline(TimeStamp::Now());
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.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/. */
 
 #include "Timeout.h"
 
 #include "nsGlobalWindow.h"
 #include "nsITimeoutHandler.h"
 #include "nsITimer.h"
+#include "mozilla/dom/TimeoutManager.h"
 
 namespace mozilla {
 namespace dom {
 
 Timeout::Timeout()
   : mCleared(false),
     mRunning(false),
     mIsInterval(false),
@@ -55,17 +56,17 @@ NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(Tim
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(Timeout, Release)
 
 namespace {
 
 void
 TimerCallback(nsITimer*, void* aClosure)
 {
   RefPtr<Timeout> timeout = (Timeout*)aClosure;
-  timeout->mWindow->RunTimeout(timeout);
+  timeout->mWindow->AsInner()->TimeoutManager().RunTimeout(timeout);
 }
 
 void
 TimerNameCallback(nsITimer* aTimer, void* aClosure, char* aBuf, size_t aLen)
 {
   RefPtr<Timeout> timeout = (Timeout*)aClosure;
 
   const char* filename;
new file mode 100644
--- /dev/null
+++ b/dom/base/TimeoutManager.cpp
@@ -0,0 +1,897 @@
+/* -*- 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/. */
+
+#include "TimeoutManager.h"
+#include "nsGlobalWindow.h"
+#include "mozilla/ThrottledEventQueue.h"
+#include "mozilla/TimeStamp.h"
+#include "nsITimeoutHandler.h"
+#include "mozilla/dom/TabGroup.h"
+
+using namespace mozilla::dom;
+
+static int32_t              gRunningTimeoutDepth       = 0;
+
+// The default shortest interval/timeout we permit
+#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
+#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
+static int32_t gMinTimeoutValue;
+static int32_t gMinBackgroundTimeoutValue;
+int32_t
+TimeoutManager::DOMMinTimeoutValue() const {
+  // First apply any back pressure delay that might be in effect.
+  int32_t value = std::max(mBackPressureDelayMS, 0);
+  // Don't use the background timeout value when there are audio contexts
+  // present, so that background audio can keep running smoothly. (bug 1181073)
+  bool isBackground = !mWindow.AsInner()->HasAudioContexts() &&
+    mWindow.IsBackgroundInternal();
+  return
+    std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value);
+}
+
+// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
+// uses 5.
+#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
+
+// The longest interval (as PRIntervalTime) we permit, or that our
+// timer code can handle, really. See DELAY_INTERVAL_LIMIT in
+// nsTimerImpl.h for details.
+#define DOM_MAX_TIMEOUT_VALUE    DELAY_INTERVAL_LIMIT
+
+uint32_t TimeoutManager::sNestingLevel = 0;
+
+namespace {
+
+// The number of queued runnables within the TabGroup ThrottledEventQueue
+// at which to begin applying back pressure to the window.
+const uint32_t kThrottledEventQueueBackPressure = 5000;
+
+// The amount of delay to apply to timers when back pressure is triggered.
+// As the length of the ThrottledEventQueue grows delay is increased.  The
+// delay is scaled such that every kThrottledEventQueueBackPressure runnables
+// in the queue equates to an additional kBackPressureDelayMS.
+const double kBackPressureDelayMS = 500;
+
+// Convert a ThrottledEventQueue length to a timer delay in milliseconds.
+// This will return a value between kBackPressureDelayMS and INT32_MAX.
+int32_t
+CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth)
+{
+  // The calculations here assume we are only operating while in back
+  // pressure conditions.
+  MOZ_ASSERT(aBacklogDepth >= kThrottledEventQueueBackPressure);
+  double multiplier = static_cast<double>(aBacklogDepth) /
+                      static_cast<double>(kThrottledEventQueueBackPressure);
+  double value = kBackPressureDelayMS * multiplier;
+  if (value > INT32_MAX) {
+    value = INT32_MAX;
+  }
+  return static_cast<int32_t>(value);
+}
+
+} // anonymous namespace
+
+TimeoutManager::TimeoutManager(nsGlobalWindow& aWindow)
+  : mWindow(aWindow),
+    mTimeoutInsertionPoint(nullptr),
+    mTimeoutIdCounter(1),
+    mTimeoutFiringDepth(0),
+    mRunningTimeout(nullptr),
+    mIdleCallbackTimeoutCounter(1),
+    mBackPressureDelayMS(0)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aWindow.IsInnerWindow());
+}
+
+/* static */
+void
+TimeoutManager::Initialize()
+{
+  Preferences::AddIntVarCache(&gMinTimeoutValue,
+                              "dom.min_timeout_value",
+                              DEFAULT_MIN_TIMEOUT_VALUE);
+  Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
+                              "dom.min_background_timeout_value",
+                              DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
+}
+
+uint32_t
+TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
+{
+  switch (aReason) {
+    case Timeout::Reason::eIdleCallbackTimeout:
+      return ++mIdleCallbackTimeoutCounter;
+    case Timeout::Reason::eTimeoutOrInterval:
+    default:
+      return ++mTimeoutIdCounter;
+  }
+}
+
+nsresult
+TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
+                           int32_t interval, bool aIsInterval,
+                           Timeout::Reason aReason, int32_t* aReturn)
+{
+  // If we don't have a document (we could have been unloaded since
+  // the call to setTimeout was made), do nothing.
+  if (!mWindow.GetExtantDoc()) {
+    return NS_OK;
+  }
+
+  // Disallow negative intervals.  If aIsInterval also disallow 0,
+  // because we use that as a "don't repeat" flag.
+  interval = std::max(aIsInterval ? 1 : 0, interval);
+
+  // Make sure we don't proceed with an interval larger than our timer
+  // code can handle. (Note: we already forced |interval| to be non-negative,
+  // so the uint32_t cast (to avoid compiler warnings) is ok.)
+  uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
+  if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
+    interval = maxTimeoutMs;
+  }
+
+  RefPtr<Timeout> timeout = new Timeout();
+  timeout->mIsInterval = aIsInterval;
+  timeout->mInterval = interval;
+  timeout->mScriptHandler = aHandler;
+  timeout->mReason = aReason;
+
+  // Now clamp the actual interval we will use for the timer based on
+  uint32_t nestingLevel = sNestingLevel + 1;
+  uint32_t realInterval = interval;
+  if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL ||
+      mBackPressureDelayMS > 0 || mWindow.IsBackgroundInternal()) {
+    // Don't allow timeouts less than DOMMinTimeoutValue() from
+    // now...
+    realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
+  }
+
+  TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
+
+  if (mWindow.IsFrozen()) {
+    // If we are frozen simply set timeout->mTimeRemaining to be the
+    // "time remaining" in the timeout (i.e., the interval itself).  This
+    // will be used to create a new mWhen time when the window is thawed.
+    // The end effect is that time does not appear to pass for frozen windows.
+    timeout->mTimeRemaining = delta;
+  } else {
+    // Since we are not frozen we must set a precise mWhen target wakeup
+    // time.  Even if we are suspended we want to use this target time so
+    // that it appears time passes while suspended.
+    timeout->mWhen = TimeStamp::Now() + delta;
+  }
+
+  // If we're not suspended, then set the timer.
+  if (!mWindow.IsSuspended()) {
+    MOZ_ASSERT(!timeout->mWhen.IsNull());
+
+    nsresult rv;
+    timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    RefPtr<Timeout> copy = timeout;
+
+    rv = timeout->InitTimer(mWindow.GetThrottledEventQueue(), realInterval);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // The timeout is now also held in the timer's closure.
+    Unused << copy.forget();
+  }
+
+  timeout->mWindow = &mWindow;
+
+  if (!aIsInterval) {
+    timeout->mNestingLevel = nestingLevel;
+  }
+
+  // No popups from timeouts by default
+  timeout->mPopupState = openAbused;
+
+  if (gRunningTimeoutDepth == 0 &&
+      mWindow.GetPopupControlState() < openAbused) {
+    // This timeout is *not* set from another timeout and it's set
+    // while popups are enabled. Propagate the state to the timeout if
+    // its delay (interval) is equal to or less than what
+    // "dom.disable_open_click_delay" is set to (in ms).
+
+    int32_t delay =
+      Preferences::GetInt("dom.disable_open_click_delay");
+
+    // This is checking |interval|, not realInterval, on purpose,
+    // because our lower bound for |realInterval| could be pretty high
+    // in some cases.
+    if (interval <= delay) {
+      timeout->mPopupState = mWindow.GetPopupControlState();
+    }
+  }
+
+  InsertTimeoutIntoList(timeout);
+
+  timeout->mTimeoutId = GetTimeoutId(aReason);
+  *aReturn = timeout->mTimeoutId;
+
+  return NS_OK;
+}
+
+void
+TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
+{
+  uint32_t timerId = (uint32_t)aTimerId;
+  Timeout* timeout;
+
+  for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
+    if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
+      if (timeout->mRunning) {
+        /* We're running from inside the timeout. Mark this
+           timeout for deferred deletion by the code in
+           RunTimeout() */
+        timeout->mIsInterval = false;
+      }
+      else {
+        /* Delete the timeout from the pending timeout list */
+        timeout->remove();
+
+        if (timeout->mTimer) {
+          timeout->mTimer->Cancel();
+          timeout->mTimer = nullptr;
+          timeout->Release();
+        }
+        timeout->Release();
+      }
+      break;
+    }
+  }
+}
+
+void
+TimeoutManager::RunTimeout(Timeout* aTimeout)
+{
+  if (mWindow.IsSuspended()) {
+    return;
+  }
+
+  NS_ASSERTION(!mWindow.IsFrozen(), "Timeout running on a window in the bfcache!");
+
+  Timeout* nextTimeout;
+  Timeout* last_expired_timeout;
+  Timeout* last_insertion_point;
+  uint32_t firingDepth = mTimeoutFiringDepth + 1;
+
+  // Make sure that the window and the script context don't go away as
+  // a result of running timeouts
+  nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(&mWindow);
+  // Silence the static analysis error about windowKungFuDeathGrip.  Accessing
+  // members of mWindow here is safe, because the lifetime of TimeoutManager is
+  // the same as the lifetime of the containing nsGlobalWindow.
+  Unused << windowKungFuDeathGrip;
+
+  // A native timer has gone off. See which of our timeouts need
+  // servicing
+  TimeStamp now = TimeStamp::Now();
+  TimeStamp deadline;
+
+  if (aTimeout && aTimeout->mWhen > now) {
+    // The OS timer fired early (which can happen due to the timers
+    // having lower precision than TimeStamp does).  Set |deadline| to
+    // be the time when the OS timer *should* have fired so that any
+    // timers that *should* have fired before aTimeout *will* be fired
+    // now.
+
+    deadline = aTimeout->mWhen;
+  } else {
+    deadline = now;
+  }
+
+  // The timeout list is kept in deadline order. Discover the latest timeout
+  // whose deadline has expired. On some platforms, native timeout events fire
+  // "early", but we handled that above by setting deadline to aTimeout->mWhen
+  // if the timer fired early.  So we can stop walking if we get to timeouts
+  // whose mWhen is greater than deadline, since once that happens we know
+  // nothing past that point is expired.
+  last_expired_timeout = nullptr;
+  for (Timeout* timeout = mTimeouts.getFirst();
+       timeout && timeout->mWhen <= deadline;
+       timeout = timeout->getNext()) {
+    if (timeout->mFiringDepth == 0) {
+      // Mark any timeouts that are on the list to be fired with the
+      // firing depth so that we can reentrantly run timeouts
+      timeout->mFiringDepth = firingDepth;
+      last_expired_timeout = timeout;
+
+      // Run available timers until we see our target timer.  After
+      // that, however, stop coalescing timers so we can yield the
+      // main thread.  Further timers that are ready will get picked
+      // up by their own nsITimer runnables when they execute.
+      //
+      // For chrome windows, however, we do coalesce all timers and
+      // do not yield the main thread.  This is partly because we
+      // trust chrome windows not to misbehave and partly because a
+      // number of browser chrome tests have races that depend on this
+      // coalescing.
+      if (timeout == aTimeout && !mWindow.IsChromeWindow()) {
+        break;
+      }
+    }
+  }
+
+  // Maybe the timeout that the event was fired for has been deleted
+  // and there are no others timeouts with deadlines that make them
+  // eligible for execution yet. Go away.
+  if (!last_expired_timeout) {
+    return;
+  }
+
+  // Insert a dummy timeout into the list of timeouts between the
+  // portion of the list that we are about to process now and those
+  // timeouts that will be processed in a future call to
+  // win_run_timeout(). This dummy timeout serves as the head of the
+  // list for any timeouts inserted as a result of running a timeout.
+  RefPtr<Timeout> dummy_timeout = new Timeout();
+  dummy_timeout->mFiringDepth = firingDepth;
+  dummy_timeout->mWhen = now;
+  last_expired_timeout->setNext(dummy_timeout);
+  RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
+
+  last_insertion_point = mTimeoutInsertionPoint;
+  // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
+  // the logic in ResetTimersForThrottleReduction will need to change.
+  mTimeoutInsertionPoint = dummy_timeout;
+
+  for (Timeout* timeout = mTimeouts.getFirst();
+       timeout != dummy_timeout && !mWindow.IsFrozen();
+       timeout = nextTimeout) {
+    nextTimeout = timeout->getNext();
+
+    if (timeout->mFiringDepth != firingDepth) {
+      // We skip the timeout since it's on the list to run at another
+      // depth.
+
+      continue;
+    }
+
+    if (mWindow.IsSuspended()) {
+      // Some timer did suspend us. Make sure the
+      // rest of the timers get executed later.
+      timeout->mFiringDepth = 0;
+      continue;
+    }
+
+    // The timeout is on the list to run at this depth, go ahead and
+    // process it.
+
+    // Get the script context (a strong ref to prevent it going away)
+    // for this timeout and ensure the script language is enabled.
+    nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal();
+
+    if (!scx) {
+      // No context means this window was closed or never properly
+      // initialized for this language.
+      continue;
+    }
+
+    // This timeout is good to run
+    bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
+
+    if (timeout_was_cleared) {
+      // The running timeout's window was cleared, this means that
+      // ClearAllTimeouts() was called from a *nested* call, possibly
+      // through a timeout that fired while a modal (to this window)
+      // dialog was open or through other non-obvious paths.
+      MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
+      Unused << timeoutExtraRef.forget().take();
+
+      mTimeoutInsertionPoint = last_insertion_point;
+
+      return;
+    }
+
+    // If we have a regular interval timer, we re-schedule the
+    // timeout, accounting for clock drift.
+    bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
+
+    // Running a timeout can cause another timeout to be deleted, so
+    // we need to reset the pointer to the following timeout.
+    nextTimeout = timeout->getNext();
+
+    timeout->remove();
+
+    if (needsReinsertion) {
+      // Insert interval timeout onto list sorted in deadline order.
+      // AddRefs timeout.
+      InsertTimeoutIntoList(timeout);
+    }
+
+    // Release the timeout struct since it's possibly out of the list
+    timeout->Release();
+  }
+
+  // Take the dummy timeout off the head of the list
+  dummy_timeout->remove();
+  timeoutExtraRef = nullptr;
+  MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
+
+  mTimeoutInsertionPoint = last_insertion_point;
+
+  MaybeApplyBackPressure();
+}
+
+void
+TimeoutManager::MaybeApplyBackPressure()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // If we are already in back pressure then we don't need to apply back
+  // pressure again.  We also shouldn't need to apply back pressure while
+  // the window is suspended.
+  if (mBackPressureDelayMS > 0 || mWindow.IsSuspended()) {
+    return;
+  }
+
+  RefPtr<ThrottledEventQueue> queue = mWindow.TabGroup()->GetThrottledEventQueue();
+  if (!queue) {
+    return;
+  }
+
+  // Only begin back pressure if the window has greatly fallen behind the main
+  // thread.  This is a somewhat arbitrary threshold chosen such that it should
+  // rarely fire under normaly circumstances.  Its low enough, though,
+  // that we should have time to slow new runnables from being added before an
+  // OOM occurs.
+  if (queue->Length() < kThrottledEventQueueBackPressure) {
+    return;
+  }
+
+  // First attempt to dispatch a runnable to update our back pressure state.  We
+  // do this first in order to verify we can dispatch successfully before
+  // entering the back pressure state.
+  nsCOMPtr<nsIRunnable> r =
+    NewNonOwningRunnableMethod<StorensRefPtrPassByPtr<nsGlobalWindow>>(this,
+      &TimeoutManager::CancelOrUpdateBackPressure, &mWindow);
+  nsresult rv = queue->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
+  NS_ENSURE_SUCCESS_VOID(rv);
+
+  // Since the callback was scheduled successfully we can now persist the
+  // backpressure value.
+  mBackPressureDelayMS = CalculateNewBackPressureDelayMS(queue->Length());
+}
+
+void
+TimeoutManager::CancelOrUpdateBackPressure(nsGlobalWindow* aWindow)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(aWindow == &mWindow);
+  MOZ_ASSERT(mBackPressureDelayMS > 0);
+
+  // First, check to see if we are still in back pressure.  If we've dropped
+  // below the threshold we can simply drop our back pressure delay.  We
+  // must also reset timers to remove the old back pressure delay in order to
+  // avoid out-of-order timer execution.
+  RefPtr<ThrottledEventQueue> queue = mWindow.TabGroup()->GetThrottledEventQueue();
+  if (!queue || queue->Length() < kThrottledEventQueueBackPressure) {
+    int32_t oldBackPressureDelayMS = mBackPressureDelayMS;
+    mBackPressureDelayMS = 0;
+    ResetTimersForThrottleReduction(oldBackPressureDelayMS);
+    return;
+  }
+
+  // Otherwise we are still in back pressure mode.
+
+  // Re-calculate the back pressure delay.
+  int32_t oldBackPressureDelayMS = mBackPressureDelayMS;
+  mBackPressureDelayMS = CalculateNewBackPressureDelayMS(queue->Length());
+
+  // If the back pressure delay has gone down we must reset any existing
+  // timers to use the new value.  Otherwise we run the risk of executing
+  // timer callbacks out-of-order.
+  if (mBackPressureDelayMS < oldBackPressureDelayMS) {
+    ResetTimersForThrottleReduction(oldBackPressureDelayMS);
+  }
+
+  // Dispatch another runnable to update the back pressure state again.
+  nsCOMPtr<nsIRunnable> r =
+    NewNonOwningRunnableMethod<StorensRefPtrPassByPtr<nsGlobalWindow>>(this,
+      &TimeoutManager::CancelOrUpdateBackPressure, &mWindow);
+  MOZ_ALWAYS_SUCCEEDS(queue->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
+}
+
+bool
+TimeoutManager::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now,
+                                  bool aRunningPendingTimeouts)
+{
+  if (!aTimeout->mIsInterval) {
+    if (aTimeout->mTimer) {
+      // The timeout still has an OS timer, and it's not an interval,
+      // that means that the OS timer could still fire; cancel the OS
+      // timer and release its reference to the timeout.
+      aTimeout->mTimer->Cancel();
+      aTimeout->mTimer = nullptr;
+      aTimeout->Release();
+    }
+    return false;
+  }
+
+  // Compute time to next timeout for interval timer.
+  // Make sure nextInterval is at least DOMMinTimeoutValue().
+  TimeDuration nextInterval =
+    TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval,
+                                          uint32_t(DOMMinTimeoutValue())));
+
+  // If we're running pending timeouts, set the next interval to be
+  // relative to "now", and not to when the timeout that was pending
+  // should have fired.
+  TimeStamp firingTime;
+  if (aRunningPendingTimeouts) {
+    firingTime = now + nextInterval;
+  } else {
+    firingTime = aTimeout->mWhen + nextInterval;
+  }
+
+  TimeStamp currentNow = TimeStamp::Now();
+  TimeDuration delay = firingTime - currentNow;
+
+  // And make sure delay is nonnegative; that might happen if the timer
+  // thread is firing our timers somewhat early or if they're taking a long
+  // time to run the callback.
+  if (delay < TimeDuration(0)) {
+    delay = TimeDuration(0);
+  }
+
+  if (!aTimeout->mTimer) {
+    NS_ASSERTION(mWindow.IsFrozen() || mWindow.IsSuspended(),
+                 "How'd our timer end up null if we're not frozen or "
+                 "suspended?");
+
+    aTimeout->mTimeRemaining = delay;
+    return true;
+  }
+
+  aTimeout->mWhen = currentNow + delay;
+
+  // Reschedule the OS timer. Don't bother returning any error codes if
+  // this fails since the callers of this method don't care about them.
+  nsresult rv = aTimeout->InitTimer(mWindow.GetThrottledEventQueue(),
+                                    delay.ToMilliseconds());
+
+  if (NS_FAILED(rv)) {
+    NS_ERROR("Error initializing timer for DOM timeout!");
+
+    // We failed to initialize the new OS timer, this timer does
+    // us no good here so we just cancel it (just in case) and
+    // null out the pointer to the OS timer, this will release the
+    // OS timer. As we continue executing the code below we'll end
+    // up deleting the timeout since it's not an interval timeout
+    // any more (since timeout->mTimer == nullptr).
+    aTimeout->mTimer->Cancel();
+    aTimeout->mTimer = nullptr;
+
+    // Now that the OS timer no longer has a reference to the
+    // timeout we need to drop that reference.
+    aTimeout->Release();
+
+    return false;
+  }
+
+  return true;
+}
+
+nsresult
+TimeoutManager::ResetTimersForThrottleReduction()
+{
+  return ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
+}
+
+nsresult
+TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS)
+{
+  MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
+
+  if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
+    return NS_OK;
+  }
+
+  TimeStamp now = TimeStamp::Now();
+
+  // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
+  // timers and the timers we're planning to fire all come before
+  // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
+  // with an mWhen that may be semi-bogus.  In that case, we don't need to do
+  // anything with mTimeoutInsertionPoint or anything before it, so should
+  // start at the timer after mTimeoutInsertionPoint, if there is one.
+  // Otherwise, start at the beginning of the list.
+  for (Timeout* timeout = mTimeoutInsertionPoint ?
+         mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
+       timeout; ) {
+    // It's important that this check be <= so that we guarantee that
+    // taking std::max with |now| won't make a quantity equal to
+    // timeout->mWhen below.
+    if (timeout->mWhen <= now) {
+      timeout = timeout->getNext();
+      continue;
+    }
+
+    if (timeout->mWhen - now >
+        TimeDuration::FromMilliseconds(aPreviousThrottleDelayMS)) {
+      // No need to loop further.  Timeouts are sorted in mWhen order
+      // and the ones after this point were all set up for at least
+      // gMinBackgroundTimeoutValue ms and hence were not clamped.
+      break;
+    }
+
+    // We reduced our throttled delay. Re-init the timer appropriately.
+    // Compute the interval the timer should have had if it had not been set in a
+    // background window
+    TimeDuration interval =
+      TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
+                                            uint32_t(DOMMinTimeoutValue())));
+    uint32_t oldIntervalMillisecs = 0;
+    timeout->mTimer->GetDelay(&oldIntervalMillisecs);
+    TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
+    if (oldInterval > interval) {
+      // unclamp
+      TimeStamp firingTime =
+        std::max(timeout->mWhen - oldInterval + interval, now);
+
+      NS_ASSERTION(firingTime < timeout->mWhen,
+                   "Our firing time should strictly decrease!");
+
+      TimeDuration delay = firingTime - now;
+      timeout->mWhen = firingTime;
+
+      // Since we reset mWhen we need to move |timeout| to the right
+      // place in the list so that it remains sorted by mWhen.
+
+      // Get the pointer to the next timeout now, before we move the
+      // current timeout in the list.
+      Timeout* nextTimeout = timeout->getNext();
+
+      // It is safe to remove and re-insert because mWhen is now
+      // strictly smaller than it used to be, so we know we'll insert
+      // |timeout| before nextTimeout.
+      NS_ASSERTION(!nextTimeout ||
+                   timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
+      timeout->remove();
+      // InsertTimeoutIntoList will addref |timeout| and reset
+      // mFiringDepth.  Make sure to undo that after calling it.
+      uint32_t firingDepth = timeout->mFiringDepth;
+      InsertTimeoutIntoList(timeout);
+      timeout->mFiringDepth = firingDepth;
+      timeout->Release();
+
+      nsresult rv = timeout->InitTimer(mWindow.GetThrottledEventQueue(),
+                                       delay.ToMilliseconds());
+
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Error resetting non background timer for DOM timeout!");
+        return rv;
+      }
+
+      timeout = nextTimeout;
+    } else {
+      timeout = timeout->getNext();
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+TimeoutManager::ClearAllTimeouts()
+{
+  Timeout* timeout;
+  Timeout* nextTimeout;
+
+  for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
+    /* If RunTimeout() is higher up on the stack for this
+       window, e.g. as a result of document.write from a timeout,
+       then we need to reset the list insertion point for
+       newly-created timeouts in case the user adds a timeout,
+       before we pop the stack back to RunTimeout. */
+    if (mRunningTimeout == timeout)
+      mTimeoutInsertionPoint = nullptr;
+
+    nextTimeout = timeout->getNext();
+
+    if (timeout->mTimer) {
+      timeout->mTimer->Cancel();
+      timeout->mTimer = nullptr;
+
+      // Drop the count since the timer isn't going to hold on
+      // anymore.
+      timeout->Release();
+    }
+
+    // Set timeout->mCleared to true to indicate that the timeout was
+    // cleared and taken out of the list of timeouts
+    timeout->mCleared = true;
+
+    // Drop the count since we're removing it from the list.
+    timeout->Release();
+  }
+
+  // Clear out our list
+  mTimeouts.clear();
+}
+
+void
+TimeoutManager::InsertTimeoutIntoList(Timeout* aTimeout)
+{
+  // Start at mLastTimeout and go backwards.  Don't go further than
+  // mTimeoutInsertionPoint, though.  This optimizes for the common case of
+  // insertion at the end.
+  Timeout* prevSibling;
+  for (prevSibling = mTimeouts.getLast();
+       prevSibling && prevSibling != mTimeoutInsertionPoint &&
+         // This condition needs to match the one in SetTimeoutOrInterval that
+         // determines whether to set mWhen or mTimeRemaining.
+         (mWindow.IsFrozen() ?
+          prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
+          prevSibling->mWhen > aTimeout->mWhen);
+       prevSibling = prevSibling->getPrevious()) {
+    /* Do nothing; just searching */
+  }
+
+  // Now link in aTimeout after prevSibling.
+  if (prevSibling) {
+    prevSibling->setNext(aTimeout);
+  } else {
+    mTimeouts.insertFront(aTimeout);
+  }
+
+  aTimeout->mFiringDepth = 0;
+
+  // Increment the timeout's reference count since it's now held on to
+  // by the list
+  aTimeout->AddRef();
+}
+
+Timeout*
+TimeoutManager::BeginRunningTimeout(Timeout* aTimeout)
+{
+  Timeout* currentTimeout = mRunningTimeout;
+  mRunningTimeout = aTimeout;
+
+  ++gRunningTimeoutDepth;
+  ++mTimeoutFiringDepth;
+
+  return currentTimeout;
+}
+
+void
+TimeoutManager::EndRunningTimeout(Timeout* aTimeout)
+{
+  --mTimeoutFiringDepth;
+  --gRunningTimeoutDepth;
+
+  mRunningTimeout = aTimeout;
+}
+
+void
+TimeoutManager::UnmarkGrayTimers()
+{
+  for (Timeout* timeout = mTimeouts.getFirst();
+       timeout;
+       timeout = timeout->getNext()) {
+    if (timeout->mScriptHandler) {
+      timeout->mScriptHandler->MarkForCC();
+    }
+  }
+}
+
+void
+TimeoutManager::Suspend()
+{
+  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+    // Leave the timers with the current time remaining.  This will
+    // cause the timers to potentially fire when the window is
+    // Resume()'d.  Time effectively passes while suspended.
+
+    // Drop the XPCOM timer; we'll reschedule when restoring the state.
+    if (t->mTimer) {
+      t->mTimer->Cancel();
+      t->mTimer = nullptr;
+
+      // Drop the reference that the timer's closure had on this timeout, we'll
+      // add it back in Resume().
+      t->Release();
+    }
+  }
+}
+
+void
+TimeoutManager::Resume()
+{
+  TimeStamp now = TimeStamp::Now();
+  DebugOnly<bool> _seenDummyTimeout = false;
+
+  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
+    // There's a chance we're being called with RunTimeout on the stack in which
+    // case we have a dummy timeout in the list that *must not* be resumed. It
+    // can be identified by a null mWindow.
+    if (!t->mWindow) {
+      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
+      _seenDummyTimeout = true;
+      continue;
+    }
+
+    MOZ_ASSERT(!t->mTimer);
+
+    // The timeout mWhen is set to the absolute time when the timer should
+    // fire.  Recalculate the delay from now until that deadline.  If the
+    // the deadline has already passed or falls within our minimum delay
+    // deadline, then clamp the resulting value to the minimum delay.  The
+    // mWhen will remain at its absolute time, but we won't fire the OS
+    // timer until our calculated delay has passed.
+    int32_t remaining = 0;
+    if (t->mWhen > now) {
+      remaining = static_cast<int32_t>((t->mWhen - now).ToMilliseconds());
+    }
+    uint32_t delay = std::max(remaining, DOMMinTimeoutValue());
+
+    t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
+    if (!t->mTimer) {
+      t->remove();
+      continue;
+    }
+
+    nsresult rv = t->InitTimer(mWindow.GetThrottledEventQueue(), delay);
+    if (NS_FAILED(rv)) {
+      t->mTimer = nullptr;
+      t->remove();
+      continue;
+    }
+
+    // Add a reference for the new timer's closure.
+    t->AddRef();
+  }
+}
+
+void
+TimeoutManager::Freeze()
+{
+  TimeStamp now = TimeStamp::Now();
+  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+    // Save the current remaining time for this timeout.  We will
+    // re-apply it when the window is Thaw()'d.  This effectively
+    // shifts timers to the right as if time does not pass while
+    // the window is frozen.
+    if (t->mWhen > now) {
+      t->mTimeRemaining = t->mWhen - now;
+    } else {
+      t->mTimeRemaining = TimeDuration(0);
+    }
+
+    // Since we are suspended there should be no OS timer set for
+    // this timeout entry.
+    MOZ_ASSERT(!t->mTimer);
+  }
+}
+
+void
+TimeoutManager::Thaw()
+{
+  TimeStamp now = TimeStamp::Now();
+  DebugOnly<bool> _seenDummyTimeout = false;
+
+  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
+    // There's a chance we're being called with RunTimeout on the stack in which
+    // case we have a dummy timeout in the list that *must not* be resumed. It
+    // can be identified by a null mWindow.
+    if (!t->mWindow) {
+      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
+      _seenDummyTimeout = true;
+      continue;
+    }
+
+    // Set mWhen back to the time when the timer is supposed to fire.
+    t->mWhen = now + t->mTimeRemaining;
+
+    MOZ_ASSERT(!t->mTimer);
+  }
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/TimeoutManager.h
@@ -0,0 +1,128 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_TimeoutManager_h__
+#define mozilla_dom_TimeoutManager_h__
+
+#include "mozilla/dom/Timeout.h"
+
+class nsITimeoutHandler;
+class nsGlobalWindow;
+
+namespace mozilla {
+namespace dom {
+
+// This class manages the timeouts in a Window's setTimeout/setInterval pool.
+class TimeoutManager final
+{
+public:
+  explicit TimeoutManager(nsGlobalWindow& aWindow);
+  TimeoutManager(const TimeoutManager& rhs) = delete;
+  void operator=(const TimeoutManager& rhs) = delete;
+
+  bool IsRunningTimeout() const { return mTimeoutFiringDepth > 0; }
+
+  static uint32_t GetNestingLevel() { return sNestingLevel; }
+  static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; }
+
+  bool HasTimeouts() const { return !mTimeouts.isEmpty(); }
+
+  nsresult SetTimeout(nsITimeoutHandler* aHandler,
+                      int32_t interval, bool aIsInterval,
+                      mozilla::dom::Timeout::Reason aReason,
+                      int32_t* aReturn);
+  void ClearTimeout(int32_t aTimerId,
+                    mozilla::dom::Timeout::Reason aReason);
+
+  // The timeout implementation functions.
+  void RunTimeout(mozilla::dom::Timeout* aTimeout);
+  // Return true if |aTimeout| needs to be reinserted into the timeout list.
+  bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, const TimeStamp& now,
+                         bool aRunningPendingTimeouts);
+
+  void ClearAllTimeouts();
+  // Insert aTimeout into the list, before all timeouts that would
+  // fire after it, but no earlier than mTimeoutInsertionPoint, if any.
+  void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
+  uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
+
+  // Apply back pressure to the window if the TabGroup ThrottledEventQueue
+  // exists and has too many runnables waiting to run.  For example, increase
+  // the minimum timer delay, etc.
+  void MaybeApplyBackPressure();
+
+  // Check the current ThrottledEventQueue depth and update the back pressure
+  // state.  If the queue has drained back pressure may be canceled.
+  void CancelOrUpdateBackPressure(nsGlobalWindow* aWindow);
+
+  // When timers are being throttled and we reduce the thottle delay we must
+  // reschedule.  The amount of the old throttle delay must be provided in
+  // order to bound how many timers must be examined.
+  nsresult ResetTimersForThrottleReduction();
+
+  int32_t DOMMinTimeoutValue() const;
+
+  // aTimeout is the timeout that we're about to start running.  This function
+  // returns the current timeout.
+  mozilla::dom::Timeout* BeginRunningTimeout(mozilla::dom::Timeout* aTimeout);
+  // aTimeout is the last running timeout.
+  void EndRunningTimeout(mozilla::dom::Timeout* aTimeout);
+
+  void UnmarkGrayTimers();
+
+  // These four methods are intended to be called from the corresponding methods
+  // on nsGlobalWindow.
+  void Suspend();
+  void Resume();
+  void Freeze();
+  void Thaw();
+
+  // Initialize TimeoutManager before the first time it is accessed.
+  static void Initialize();
+
+  // Run some code for each Timeout in our list.
+  template <class Callable>
+  void ForEachTimeout(Callable c)
+  {
+    for (Timeout* timeout = mTimeouts.getFirst();
+         timeout;
+         timeout = timeout->getNext()) {
+      c(timeout);
+    }
+  }
+
+private:
+  nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS);
+
+private:
+  // Each nsGlobalWindow object has a TimeoutManager member.  This reference
+  // points to that holder object.
+  nsGlobalWindow&             mWindow;
+  // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is
+  // non-null.  In that case, the dummy timeout pointed to by
+  // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
+  // that come after it.
+  mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
+  // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
+  // This is a dummy timeout at the moment; if that ever changes, the logic in
+  // ResetTimersForThrottleReduction needs to change.
+  mozilla::dom::Timeout*      mTimeoutInsertionPoint;
+  uint32_t                    mTimeoutIdCounter;
+  uint32_t                    mTimeoutFiringDepth;
+  mozilla::dom::Timeout*      mRunningTimeout;
+
+   // The current idle request callback timeout handle
+  uint32_t                    mIdleCallbackTimeoutCounter;
+
+  int32_t                     mBackPressureDelayMS;
+
+  static uint32_t             sNestingLevel;
+};
+
+}
+}
+
+#endif
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -200,16 +200,17 @@ EXPORTS.mozilla.dom += [
     'ShadowRoot.h',
     'StructuredCloneHolder.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'TabGroup.h',
     'Text.h',
     'Timeout.h',
+    'TimeoutManager.h',
     'TreeWalker.h',
     'WebKitCSSMatrix.h',
     'WebSocket.h',
     'WindowOrientationObserver.h',
 ]
 
 UNIFIED_SOURCES += [
     'AnonymousContent.cpp',
@@ -339,16 +340,17 @@ UNIFIED_SOURCES += [
     'StructuredCloneHolder.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'TabGroup.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'Timeout.cpp',
+    'TimeoutManager.cpp',
     'TreeWalker.cpp',
     'WebKitCSSMatrix.cpp',
     'WebSocket.cpp',
     'WindowNamedPropertiesHandler.cpp',
     'WindowOrientationObserver.cpp',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
--- a/dom/base/nsCCUncollectableMarker.cpp
+++ b/dom/base/nsCCUncollectableMarker.cpp
@@ -27,16 +27,17 @@
 #include "nsGlobalWindow.h"
 #include "nsJSEnvironment.h"
 #include "nsInProcessTabChildGlobal.h"
 #include "nsFrameLoader.h"
 #include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ProcessGlobal.h"
+#include "mozilla/dom/TimeoutManager.h"
 #include "xpcpublic.h"
 #include "nsObserverService.h"
 #include "nsFocusManager.h"
 #include "nsIInterfaceRequestorUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -205,17 +206,18 @@ MarkContentViewer(nsIContentViewer* aVie
         elm->MarkForCC();
       }
       nsCOMPtr<EventTarget> win = do_QueryInterface(doc->GetInnerWindow());
       if (win) {
         elm = win->GetExistingListenerManager();
         if (elm) {
           elm->MarkForCC();
         }
-        static_cast<nsGlobalWindow*>(win.get())->UnmarkGrayTimers();
+        static_cast<nsGlobalWindow*>(win.get())->AsInner()->
+          TimeoutManager().UnmarkGrayTimers();
       }
     } else if (aPrepareForCC) {
       // Unfortunately we need to still mark user data just before running CC so
       // that it has the right generation.
       doc->PropertyTable(DOM_USER_DATA)->
         EnumerateAll(MarkUserData, &nsCCUncollectableMarker::sGeneration);
     }
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -18,16 +18,17 @@
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
 #include "mozilla/dom/DOMStorage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/Timeout.h"
+#include "mozilla/dom/TimeoutManager.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #if defined(MOZ_WIDGET_ANDROID) || defined(MOZ_WIDGET_GONK)
 #include "mozilla/dom/WindowOrientationObserver.h"
 #endif
 #include "nsDOMOfflineResourceList.h"
 #include "nsError.h"
 #include "nsIIdleService.h"
 #include "nsISizeOfEventTarget.h"
@@ -285,59 +286,31 @@ static LazyLogModule gDOMLeakPRLog("DOML
 
 nsGlobalWindow::WindowByIdTable *nsGlobalWindow::sWindowsById = nullptr;
 bool nsGlobalWindow::sWarnedAboutWindowInternal = false;
 bool nsGlobalWindow::sIdleObserversAPIFuzzTimeDisabled = false;
 
 static int32_t              gRefCnt                    = 0;
 static int32_t              gOpenPopupSpamCount        = 0;
 static PopupControlState    gPopupControlState         = openAbused;
-static int32_t              gRunningTimeoutDepth       = 0;
 static bool                 gMouseDown                 = false;
 static bool                 gDragServiceDisabled       = false;
 static FILE                *gDumpFile                  = nullptr;
 static uint32_t             gSerialCounter             = 0;
-static TimeStamp            gLastRecordedRecentTimeouts;
-#define STATISTICS_INTERVAL (30 * PR_MSEC_PER_SEC)
 
 #ifdef DEBUG_jst
 int32_t gTimeoutCnt                                    = 0;
 #endif
 
 #if defined(DEBUG_bryner) || defined(DEBUG_chb)
 #define DEBUG_PAGE_CACHE
 #endif
 
 #define DOM_TOUCH_LISTENER_ADDED "dom-touch-listener-added"
 
-// The default shortest interval/timeout we permit
-#define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
-#define DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE 1000 // 1000ms
-static int32_t gMinTimeoutValue;
-static int32_t gMinBackgroundTimeoutValue;
-inline int32_t
-nsGlobalWindow::DOMMinTimeoutValue() const {
-  // First apply any back pressure delay that might be in effect.
-  int32_t value = std::max(mBackPressureDelayMS, 0);
-  // Don't use the background timeout value when there are audio contexts
-  // present, so that baackground audio can keep running smoothly. (bug 1181073)
-  bool isBackground = mAudioContexts.IsEmpty() && IsBackgroundInternal();
-  return
-    std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value);
-}
-
-// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
-// uses 5.
-#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
-
-// The longest interval (as PRIntervalTime) we permit, or that our
-// timer code can handle, really. See DELAY_INTERVAL_LIMIT in
-// nsTimerImpl.h for details.
-#define DOM_MAX_TIMEOUT_VALUE    DELAY_INTERVAL_LIMIT
-
 // The interval at which we execute idle callbacks
 static uint32_t gThrottledIdlePeriodLength;
 
 #define DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH 10000
 
 #define FORWARD_TO_OUTER(method, args, err_rval)                              \
   PR_BEGIN_MACRO                                                              \
   if (IsInnerWindow()) {                                                      \
@@ -664,33 +637,38 @@ namespace dom {
 extern uint64_t
 NextWindowID();
 } // namespace dom
 } // namespace mozilla
 
 template<class T>
 nsPIDOMWindow<T>::nsPIDOMWindow(nsPIDOMWindowOuter *aOuterWindow)
 : mFrameElement(nullptr), mDocShell(nullptr), mModalStateDepth(0),
-  mRunningTimeout(nullptr), mMutationBits(0), mIsDocumentLoaded(false),
+  mMutationBits(0), mIsDocumentLoaded(false),
   mIsHandlingResizeEvent(false), mIsInnerWindow(aOuterWindow != nullptr),
   mMayHavePaintEventListener(false), mMayHaveTouchEventListener(false),
   mMayHaveMouseEnterLeaveEventListener(false),
   mMayHavePointerEnterLeaveEventListener(false),
   mInnerObjectsFreed(false),
   mIsModalContentWindow(false),
   mIsActive(false), mIsBackground(false),
   mMediaSuspend(Preferences::GetBool("media.block-autoplay-until-in-foreground", true) ?
     nsISuspendedTypes::SUSPENDED_BLOCK : nsISuspendedTypes::NONE_SUSPENDED),
   mAudioMuted(false), mAudioVolume(1.0), mAudioCaptured(false),
   mDesktopModeViewport(false), mIsRootOuterWindow(false), mInnerWindow(nullptr),
   mOuterWindow(aOuterWindow),
   // Make sure no actual window ends up with mWindowID == 0
   mWindowID(NextWindowID()), mHasNotifiedGlobalCreated(false),
   mMarkedCCGeneration(0), mServiceWorkersTestingEnabled(false)
- {}
+{
+  if (aOuterWindow) {
+    mTimeoutManager =
+      MakeUnique<mozilla::dom::TimeoutManager>(*nsGlobalWindow::Cast(AsInner()));
+  }
+}
 
 template<class T>
 nsPIDOMWindow<T>::~nsPIDOMWindow() {}
 
 /* static */
 nsPIDOMWindowOuter*
 nsPIDOMWindowOuter::GetFromCurrentInner(nsPIDOMWindowInner* aInner)
 {
@@ -1261,25 +1239,20 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
     mFocusByKeyOccurred(false),
     mHasGamepad(false),
     mHasVREvents(false),
 #ifdef MOZ_GAMEPAD
     mHasSeenGamepadInput(false),
 #endif
     mNotifiedIDDestroyed(false),
     mAllowScriptsToClose(false),
-    mTimeoutInsertionPoint(nullptr),
-    mTimeoutIdCounter(1),
-    mTimeoutFiringDepth(0),
     mSuspendDepth(0),
     mFreezeDepth(0),
-    mBackPressureDelayMS(0),
     mFocusMethod(0),
     mSerial(0),
-    mIdleCallbackTimeoutCounter(1),
     mIdleRequestCallbackCounter(1),
 #ifdef DEBUG
     mSetOpenerWindowCalled(false),
 #endif
 #ifdef MOZ_B2G
     mNetworkUploadObserverEnabled(false),
     mNetworkDownloadObserverEnabled(false),
 #endif
@@ -1329,22 +1302,17 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalW
   // We could have failed the first time through trying
   // to create the entropy collector, so we should
   // try to get one until we succeed.
 
   gRefCnt++;
 
   static bool sFirstTime = true;
   if (sFirstTime) {
-    Preferences::AddIntVarCache(&gMinTimeoutValue,
-                                "dom.min_timeout_value",
-                                DEFAULT_MIN_TIMEOUT_VALUE);
-    Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
-                                "dom.min_background_timeout_value",
-                                DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
+    TimeoutManager::Initialize();
     Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
                                  "dom.idle-observers-api.fuzz_time.disabled",
                                  false);
 
     Preferences::AddUintVarCache(&gThrottledIdlePeriodLength,
                                  "dom.idle_period.throttled_length",
                                  DEFAULT_THROTTLED_IDLE_PERIOD_LENGTH);
     sFirstTime = false;
@@ -1761,17 +1729,19 @@ nsGlobalWindow::FreeInnerObjects()
     reporter->ObserveDOMWindowDetached(this);
   }
 
   mInnerObjectsFreed = true;
 
   // Kill all of the workers for this window.
   mozilla::dom::workers::CancelWorkersForWindow(AsInner());
 
-  ClearAllTimeouts();
+  if (mTimeoutManager) {
+    mTimeoutManager->ClearAllTimeouts();
+  }
 
   if (mIdleTimer) {
     mIdleTimer->Cancel();
     mIdleTimer = nullptr;
   }
 
   mIdleObservers.Clear();
 
@@ -1903,17 +1873,19 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(
            !iter.Done();
            iter.Next()) {
         iter.Data().exposeToActiveJS();
       }
     }
     if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
       elm->MarkForCC();
     }
-    tmp->UnmarkGrayTimers();
+    if (tmp->mTimeoutManager) {
+      tmp->mTimeoutManager->UnmarkGrayTimers();
+    }
     return true;
   }
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsGlobalWindow)
   return tmp->IsBlackForCC(true);
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
 
@@ -1961,20 +1933,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
 
-  for (Timeout* timeout = tmp->mTimeouts.getFirst();
-       timeout;
-       timeout = timeout->getNext()) {
-    cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
+  if (tmp->mTimeoutManager) {
+    tmp->mTimeoutManager->ForEachTimeout([&cb](Timeout* timeout) {
+      cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
+    });
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStorage)
@@ -2133,28 +2105,16 @@ nsGlobalWindow::IsBlackForCC(bool aTraci
   }
 
   return (nsCCUncollectableMarker::InGeneration(GetMarkedCCGeneration()) ||
           IsBlack()) &&
          (!aTracingNeeded ||
           HasNothingToTrace(static_cast<nsIDOMEventTarget*>(this)));
 }
 
-void
-nsGlobalWindow::UnmarkGrayTimers()
-{
-  for (Timeout* timeout = mTimeouts.getFirst();
-       timeout;
-       timeout = timeout->getNext()) {
-    if (timeout->mScriptHandler) {
-      timeout->mScriptHandler->MarkForCC();
-    }
-  }
-}
-
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptGlobalObject
 //*****************************************************************************
 
 nsresult
 nsGlobalWindow::EnsureScriptEnvironment()
 {
   nsGlobalWindow* outer = GetOuterWindowInternal();
@@ -3159,18 +3119,16 @@ nsGlobalWindow::DetachFromDocShell()
 {
   NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
 
   // DetachFromDocShell means the window is being torn down. Drop our
   // reference to the script context, allowing it to be deleted
   // later. Meanwhile, keep our weak reference to the script object
   // so that it can be retrieved later (until it is finalized by the JS GC).
 
-  NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
-
   // Call FreeInnerObjects on all inner windows, not just the current
   // one, since some could be held by WindowStateHolder objects that
   // are GC-owned.
   for (RefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
        inner != this;
        inner = (nsGlobalWindow*)PR_NEXT_LINK(inner)) {
     MOZ_ASSERT(!inner->mOuterWindow || inner->mOuterWindow == AsOuter());
     inner->FreeInnerObjects();
@@ -3650,123 +3608,16 @@ nsGlobalWindow::DefineArgumentsProperty(
 
   nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
   NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
 
   JS::Rooted<JSObject*> obj(RootingCx(), GetWrapperPreserveColor());
   return ctx->SetProperty(obj, "arguments", aArguments);
 }
 
-namespace {
-
-// The number of queued runnables within the TabGroup ThrottledEventQueue
-// at which to begin applying back pressure to the window.
-const uint32_t kThrottledEventQueueBackPressure = 5000;
-
-// The amount of delay to apply to timers when back pressure is triggered.
-// As the length of the ThrottledEventQueue grows delay is increased.  The
-// delay is scaled such that every kThrottledEventQueueBackPressure runnables
-// in the queue equates to an additional kBackPressureDelayMS.
-const double kBackPressureDelayMS = 500;
-
-// Convert a ThrottledEventQueue length to a timer delay in milliseconds.
-// This will return a value between kBackPressureDelayMS and INT32_MAX.
-int32_t
-CalculateNewBackPressureDelayMS(uint32_t aBacklogDepth)
-{
-  // The calculations here assume we are only operating while in back
-  // pressure conditions.
-  MOZ_ASSERT(aBacklogDepth >= kThrottledEventQueueBackPressure);
-  double multiplier = static_cast<double>(aBacklogDepth) /
-                      static_cast<double>(kThrottledEventQueueBackPressure);
-  double value = kBackPressureDelayMS * multiplier;
-  if (value > INT32_MAX) {
-    value = INT32_MAX;
-  }
-  return static_cast<int32_t>(value);
-}
-
-} // anonymous namespace
-
-void
-nsGlobalWindow::MaybeApplyBackPressure()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  // If we are already in back pressure then we don't need to apply back
-  // pressure again.  We also shouldn't need to apply back pressure while
-  // the window is suspended.
-  if (mBackPressureDelayMS > 0 || IsSuspended()) {
-    return;
-  }
-
-  RefPtr<ThrottledEventQueue> queue = TabGroup()->GetThrottledEventQueue();
-  if (!queue) {
-    return;
-  }
-
-  // Only begin back pressure if the window has greatly fallen behind the main
-  // thread.  This is a somewhat arbitrary threshold chosen such that it should
-  // rarely fire under normaly circumstances.  Its low enough, though,
-  // that we should have time to slow new runnables from being added before an
-  // OOM occurs.
-  if (queue->Length() < kThrottledEventQueueBackPressure) {
-    return;
-  }
-
-  // First attempt to dispatch a runnable to update our back pressure state.  We
-  // do this first in order to verify we can dispatch successfully before
-  // entering the back pressure state.
-  nsCOMPtr<nsIRunnable> r =
-    NewRunnableMethod(this, &nsGlobalWindow::CancelOrUpdateBackPressure);
-  nsresult rv = queue->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
-  NS_ENSURE_SUCCESS_VOID(rv);
-
-  // Since the callback was scheduled successfully we can now persist the
-  // backpressure value.
-  mBackPressureDelayMS = CalculateNewBackPressureDelayMS(queue->Length());
-}
-
-void
-nsGlobalWindow::CancelOrUpdateBackPressure()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(mBackPressureDelayMS > 0);
-
-  // First, check to see if we are still in back pressure.  If we've dropped
-  // below the threshold we can simply drop our back pressure delay.  We
-  // must also reset timers to remove the old back pressure delay in order to
-  // avoid out-of-order timer execution.
-  RefPtr<ThrottledEventQueue> queue = TabGroup()->GetThrottledEventQueue();
-  if (!queue || queue->Length() < kThrottledEventQueueBackPressure) {
-    int32_t oldBackPressureDelayMS = mBackPressureDelayMS;
-    mBackPressureDelayMS = 0;
-    ResetTimersForThrottleReduction(oldBackPressureDelayMS);
-    return;
-  }
-
-  // Otherwise we are still in back pressure mode.
-
-  // Re-calculate the back pressure delay.
-  int32_t oldBackPressureDelayMS = mBackPressureDelayMS;
-  mBackPressureDelayMS = CalculateNewBackPressureDelayMS(queue->Length());
-
-  // If the back pressure delay has gone down we must reset any existing
-  // timers to use the new value.  Otherwise we run the risk of executing
-  // timer callbacks out-of-order.
-  if (mBackPressureDelayMS < oldBackPressureDelayMS) {
-    ResetTimersForThrottleReduction(oldBackPressureDelayMS);
-  }
-
-  // Dispatch another runnable to update the back pressure state again.
-  nsCOMPtr<nsIRunnable> r =
-    NewRunnableMethod(this, &nsGlobalWindow::CancelOrUpdateBackPressure);
-  MOZ_ALWAYS_SUCCEEDS(queue->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
-}
-
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptObjectPrincipal
 //*****************************************************************************
 
 nsIPrincipal*
 nsGlobalWindow::GetPrincipal()
 {
   if (mDoc) {
@@ -4067,16 +3918,34 @@ nsPIDOMWindowInner::Thaw()
 }
 
 void
 nsPIDOMWindowInner::SyncStateFromParentWindow()
 {
   nsGlobalWindow::Cast(this)->SyncStateFromParentWindow();
 }
 
+bool
+nsPIDOMWindowInner::HasAudioContexts() const
+{
+  return !mAudioContexts.IsEmpty();
+}
+
+mozilla::dom::TimeoutManager&
+nsPIDOMWindowInner::TimeoutManager()
+{
+  return *mTimeoutManager;
+}
+
+bool
+nsPIDOMWindowInner::IsRunningTimeout()
+{
+  return TimeoutManager().IsRunningTimeout();
+}
+
 SuspendTypes
 nsPIDOMWindowOuter::GetMediaSuspend() const
 {
   if (IsInnerWindow()) {
     return mOuterWindow->GetMediaSuspend();
   }
 
   return mMediaSuspend;
@@ -8126,25 +7995,27 @@ nsGlobalWindow::MozRequestOverfill(Overf
 }
 
 void
 nsGlobalWindow::ClearTimeout(int32_t aHandle)
 {
   MOZ_RELEASE_ASSERT(IsInnerWindow());
 
   if (aHandle > 0) {
-    ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
+    mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void
 nsGlobalWindow::ClearInterval(int32_t aHandle)
 {
+  MOZ_RELEASE_ASSERT(IsInnerWindow());
+
   if (aHandle > 0) {
-    ClearTimeoutOrInterval(aHandle, Timeout::Reason::eTimeoutOrInterval);
+    mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void
 nsGlobalWindow::SetResizable(bool aResizable) const
 {
   // nop
 }
@@ -10125,33 +9996,35 @@ nsGlobalWindow::IsTopLevelWindowActive()
 }
 
 void nsGlobalWindow::SetIsBackground(bool aIsBackground)
 {
   MOZ_ASSERT(IsOuterWindow());
 
   bool resetTimers = (!aIsBackground && AsOuter()->IsBackground());
   nsPIDOMWindow::SetIsBackground(aIsBackground);
+
+  if (aIsBackground) {
+    MOZ_ASSERT(!resetTimers);
+    return;
+  }
+
+  nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
+  if (!inner) {
+    return;
+  }
+
   if (resetTimers) {
-    ResetTimersForThrottleReduction(gMinBackgroundTimeoutValue);
-  }
-
-  if (!aIsBackground) {
-    nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
-    if (inner) {
-      inner->UnthrottleIdleCallbackRequests();
-    }
-  }
+    inner->mTimeoutManager->ResetTimersForThrottleReduction();
+  }
+
+  inner->UnthrottleIdleCallbackRequests();
+
 #ifdef MOZ_GAMEPAD
-  if (!aIsBackground) {
-    nsGlobalWindow* inner = GetCurrentInnerWindowInternal();
-    if (inner) {
-      inner->SyncGamepadState();
-    }
-  }
+  inner->SyncGamepadState();
 #endif
 }
 
 void nsGlobalWindow::MaybeUpdateTouchState()
 {
   FORWARD_TO_INNER_VOID(MaybeUpdateTouchState, ());
 
   if (mMayHaveTouchEventListener) {
@@ -11939,31 +11812,17 @@ nsGlobalWindow::Suspend()
     for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
       ac->RemoveWindowListener(mEnabledSensors[i], this);
   }
   DisableGamepadUpdates();
   DisableVRUpdates();
 
   mozilla::dom::workers::SuspendWorkersForWindow(AsInner());
 
-  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
-    // Leave the timers with the current time remaining.  This will
-    // cause the timers to potentially fire when the window is
-    // Resume()'d.  Time effectively passes while suspended.
-
-    // Drop the XPCOM timer; we'll reschedule when restoring the state.
-    if (t->mTimer) {
-      t->mTimer->Cancel();
-      t->mTimer = nullptr;
-
-      // Drop the reference that the timer's closure had on this timeout, we'll
-      // add it back in Resume().
-      t->Release();
-    }
-  }
+  mTimeoutManager->Suspend();
 
   // Suspend all of the AudioContexts for this window
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     ErrorResult dummy;
     RefPtr<Promise> d = mAudioContexts[i]->Suspend(dummy);
   }
 }
 
@@ -12005,59 +11864,17 @@ nsGlobalWindow::Resume()
   EnableVRUpdates();
 
   // Resume all of the AudioContexts for this window
   for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
     ErrorResult dummy;
     RefPtr<Promise> d = mAudioContexts[i]->Resume(dummy);
   }
 
-  TimeStamp now = TimeStamp::Now();
-  DebugOnly<bool> _seenDummyTimeout = false;
-
-  for (Timeout* t = mTimeouts.getFirst(); t; t = t->getNext()) {
-    // There's a chance we're being called with RunTimeout on the stack in which
-    // case we have a dummy timeout in the list that *must not* be resumed. It
-    // can be identified by a null mWindow.
-    if (!t->mWindow) {
-      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
-      _seenDummyTimeout = true;
-      continue;
-    }
-
-    MOZ_ASSERT(!t->mTimer);
-
-    // The timeout mWhen is set to the absolute time when the timer should
-    // fire.  Recalculate the delay from now until that deadline.  If the
-    // the deadline has already passed or falls within our minimum delay
-    // deadline, then clamp the resulting value to the minimum delay.  The
-    // mWhen will remain at its absolute time, but we won't fire the OS
-    // timer until our calculated delay has passed.
-    int32_t remaining = 0;
-    if (t->mWhen > now) {
-      remaining = static_cast<int32_t>((t->mWhen - now).ToMilliseconds());
-    }
-    uint32_t delay = std::max(remaining, DOMMinTimeoutValue());
-
-    t->mTimer = do_CreateInstance("@mozilla.org/timer;1");
-    if (!t->mTimer) {
-      t->remove();
-      continue;
-    }
-
-    nsresult rv = t->InitTimer(GetThrottledEventQueue(), delay);
-    if (NS_FAILED(rv)) {
-      t->mTimer = nullptr;
-      t->remove();
-      continue;
-    }
-
-    // Add a reference for the new timer's closure.
-    t->AddRef();
-  }
+  mTimeoutManager->Resume();
 
   // Resume all of the workers for this window.  We must do this
   // after timeouts since workers may have queued events that can trigger
   // a setTimeout().
   mozilla::dom::workers::ResumeWorkersForWindow(AsInner());
 }
 
 bool
@@ -12095,32 +11912,17 @@ nsGlobalWindow::FreezeInternal()
   mFreezeDepth += 1;
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 1) {
     return;
   }
 
   mozilla::dom::workers::FreezeWorkersForWindow(AsInner());
 
-  TimeStamp now = TimeStamp::Now();
-  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
-    // Save the current remaining time for this timeout.  We will
-    // re-apply it when the window is Thaw()'d.  This effectively
-    // shifts timers to the right as if time does not pass while
-    // the window is frozen.
-    if (t->mWhen > now) {
-      t->mTimeRemaining = t->mWhen - now;
-    } else {
-      t->mTimeRemaining = TimeDuration(0);
-    }
-
-    // Since we are suspended there should be no OS timer set for
-    // this timeout entry.
-    MOZ_ASSERT(!t->mTimer);
-  }
+  mTimeoutManager->Freeze();
 
   NotifyDOMWindowFrozen(this);
 }
 
 void
 nsGlobalWindow::Thaw()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -12140,34 +11942,17 @@ nsGlobalWindow::ThawInternal()
 
   MOZ_ASSERT(mFreezeDepth != 0);
   mFreezeDepth -= 1;
   MOZ_ASSERT(mSuspendDepth >= mFreezeDepth);
   if (mFreezeDepth != 0) {
     return;
   }
 
-  TimeStamp now = TimeStamp::Now();
-  DebugOnly<bool> _seenDummyTimeout = false;
-
-  for (Timeout *t = mTimeouts.getFirst(); t; t = t->getNext()) {
-    // There's a chance we're being called with RunTimeout on the stack in which
-    // case we have a dummy timeout in the list that *must not* be resumed. It
-    // can be identified by a null mWindow.
-    if (!t->mWindow) {
-      NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
-      _seenDummyTimeout = true;
-      continue;
-    }
-
-    // Set mWhen back to the time when the timer is supposed to fire.
-    t->mWhen = now + t->mTimeRemaining;
-
-    MOZ_ASSERT(!t->mTimer);
-  }
+  mTimeoutManager->Thaw();
 
   mozilla::dom::workers::ThawWorkersForWindow(AsInner());
 
   NotifyDOMWindowThawed(this);
 }
 
 bool
 nsGlobalWindow::IsFrozen() const
@@ -12569,30 +12354,16 @@ nsGlobalWindow::OpenInternal(const nsASt
 
   return rv;
 }
 
 //*****************************************************************************
 // nsGlobalWindow: Timeout Functions
 //*****************************************************************************
 
-uint32_t sNestingLevel;
-
-uint32_t
-nsGlobalWindow::GetTimeoutId(Timeout::Reason aReason)
-{
-  switch (aReason) {
-    case Timeout::Reason::eIdleCallbackTimeout:
-      return ++mIdleCallbackTimeoutCounter;
-    case Timeout::Reason::eTimeoutOrInterval:
-    default:
-      return ++mTimeoutIdCounter;
-  }
-}
-
 nsGlobalWindow*
 nsGlobalWindow::InnerForSetTimeoutOrInterval(ErrorResult& aError)
 {
   nsGlobalWindow* currentInner;
   nsGlobalWindow* forwardTo;
   if (IsInnerWindow()) {
     nsGlobalWindow* outer = GetOuterWindowInternal();
     currentInner = outer ? outer->GetCurrentInnerWindowInternal() : this;
@@ -12689,127 +12460,16 @@ nsGlobalWindow::SetInterval(JSContext* a
                             const Sequence<JS::Value>& /* unused */,
                             ErrorResult& aError)
 {
   int32_t timeout;
   bool isInterval = IsInterval(aTimeout, timeout);
   return SetTimeoutOrInterval(aCx, aHandler, timeout, isInterval, aError);
 }
 
-nsresult
-nsGlobalWindow::SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
-                                     int32_t interval, bool aIsInterval,
-                                     Timeout::Reason aReason, int32_t* aReturn)
-{
-  MOZ_ASSERT(IsInnerWindow());
-
-  // If we don't have a document (we could have been unloaded since
-  // the call to setTimeout was made), do nothing.
-  if (!mDoc) {
-    return NS_OK;
-  }
-
-  // Disallow negative intervals.  If aIsInterval also disallow 0,
-  // because we use that as a "don't repeat" flag.
-  interval = std::max(aIsInterval ? 1 : 0, interval);
-
-  // Make sure we don't proceed with an interval larger than our timer
-  // code can handle. (Note: we already forced |interval| to be non-negative,
-  // so the uint32_t cast (to avoid compiler warnings) is ok.)
-  uint32_t maxTimeoutMs = PR_IntervalToMilliseconds(DOM_MAX_TIMEOUT_VALUE);
-  if (static_cast<uint32_t>(interval) > maxTimeoutMs) {
-    interval = maxTimeoutMs;
-  }
-
-  RefPtr<Timeout> timeout = new Timeout();
-  timeout->mIsInterval = aIsInterval;
-  timeout->mInterval = interval;
-  timeout->mScriptHandler = aHandler;
-  timeout->mReason = aReason;
-
-  // Now clamp the actual interval we will use for the timer based on
-  uint32_t nestingLevel = sNestingLevel + 1;
-  uint32_t realInterval = interval;
-  if (aIsInterval || nestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL ||
-      mBackPressureDelayMS > 0 || IsBackgroundInternal()) {
-    // Don't allow timeouts less than DOMMinTimeoutValue() from
-    // now...
-    realInterval = std::max(realInterval, uint32_t(DOMMinTimeoutValue()));
-  }
-
-  TimeDuration delta = TimeDuration::FromMilliseconds(realInterval);
-
-  if (IsFrozen()) {
-    // If we are frozen simply set timeout->mTimeRemaining to be the
-    // "time remaining" in the timeout (i.e., the interval itself).  This
-    // will be used to create a new mWhen time when the window is thawed.
-    // The end effect is that time does not appear to pass for frozen windows.
-    timeout->mTimeRemaining = delta;
-  } else {
-    // Since we are not frozen we must set a precise mWhen target wakeup
-    // time.  Even if we are suspended we want to use this target time so
-    // that it appears time passes while suspended.
-    timeout->mWhen = TimeStamp::Now() + delta;
-  }
-
-  // If we're not suspended, then set the timer.
-  if (!IsSuspended()) {
-    MOZ_ASSERT(!timeout->mWhen.IsNull());
-
-    nsresult rv;
-    timeout->mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    RefPtr<Timeout> copy = timeout;
-
-    rv = timeout->InitTimer(GetThrottledEventQueue(), realInterval);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-
-    // The timeout is now also held in the timer's closure.
-    Unused << copy.forget();
-  }
-
-  timeout->mWindow = this;
-
-  if (!aIsInterval) {
-    timeout->mNestingLevel = nestingLevel;
-  }
-
-  // No popups from timeouts by default
-  timeout->mPopupState = openAbused;
-
-  if (gRunningTimeoutDepth == 0 && gPopupControlState < openAbused) {
-    // This timeout is *not* set from another timeout and it's set
-    // while popups are enabled. Propagate the state to the timeout if
-    // its delay (interval) is equal to or less than what
-    // "dom.disable_open_click_delay" is set to (in ms).
-
-    int32_t delay =
-      Preferences::GetInt("dom.disable_open_click_delay");
-
-    // This is checking |interval|, not realInterval, on purpose,
-    // because our lower bound for |realInterval| could be pretty high
-    // in some cases.
-    if (interval <= delay) {
-      timeout->mPopupState = gPopupControlState;
-    }
-  }
-
-  InsertTimeoutIntoList(timeout);
-
-  timeout->mTimeoutId = GetTimeoutId(aReason);
-  *aReturn = timeout->mTimeoutId;
-
-  return NS_OK;
-}
-
 int32_t
 nsGlobalWindow::SetTimeoutOrInterval(JSContext *aCx, Function& aFunction,
                                      int32_t aTimeout,
                                      const Sequence<JS::Value>& aArguments,
                                      bool aIsInterval, ErrorResult& aError)
 {
   nsGlobalWindow* inner = InnerForSetTimeoutOrInterval(aError);
   if (!inner) {
@@ -12823,18 +12483,19 @@ nsGlobalWindow::SetTimeoutOrInterval(JSC
 
   nsCOMPtr<nsIScriptTimeoutHandler> handler =
     NS_CreateJSTimeoutHandler(aCx, this, aFunction, aArguments, aError);
   if (!handler) {
     return 0;
   }
 
   int32_t result;
-  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
-                                Timeout::Reason::eTimeoutOrInterval, &result);
+  aError = mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
+                                      Timeout::Reason::eTimeoutOrInterval,
+                                      &result);
   return result;
 }
 
 int32_t
 nsGlobalWindow::SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                      int32_t aTimeout, bool aIsInterval,
                                      ErrorResult& aError)
 {
@@ -12850,50 +12511,49 @@ nsGlobalWindow::SetTimeoutOrInterval(JSC
 
   nsCOMPtr<nsIScriptTimeoutHandler> handler =
     NS_CreateJSTimeoutHandler(aCx, this, aHandler, aError);
   if (!handler) {
     return 0;
   }
 
   int32_t result;
-  aError = SetTimeoutOrInterval(handler, aTimeout, aIsInterval,
-                                Timeout::Reason::eTimeoutOrInterval, &result);
+  aError = mTimeoutManager->SetTimeout(handler, aTimeout, aIsInterval,
+                                      Timeout::Reason::eTimeoutOrInterval,
+                                      &result);
   return result;
 }
 
 bool
 nsGlobalWindow::RunTimeoutHandler(Timeout* aTimeout,
                                   nsIScriptContext* aScx)
 {
+  MOZ_ASSERT(IsInnerWindow());
+
   // Hold on to the timeout in case mExpr or mFunObj releases its
   // doc.
   RefPtr<Timeout> timeout = aTimeout;
-  Timeout* last_running_timeout = mRunningTimeout;
-  mRunningTimeout = timeout;
+  Timeout* last_running_timeout = mTimeoutManager->BeginRunningTimeout(timeout);
   timeout->mRunning = true;
 
   // Push this timeout's popup control state, which should only be
   // eabled the first time a timeout fires that was created while
   // popups were enabled and with a delay less than
   // "dom.disable_open_click_delay".
   nsAutoPopupStatePusher popupStatePusher(timeout->mPopupState);
 
   // Clear the timeout's popup state, if any, to prevent interval
   // timeouts from repeatedly opening poups.
   timeout->mPopupState = openAbused;
 
-  ++gRunningTimeoutDepth;
-  ++mTimeoutFiringDepth;
-
   bool trackNestingLevel = !timeout->mIsInterval;
   uint32_t nestingLevel;
   if (trackNestingLevel) {
-    nestingLevel = sNestingLevel;
-    sNestingLevel = timeout->mNestingLevel;
+    nestingLevel = TimeoutManager::GetNestingLevel();
+    TimeoutManager::SetNestingLevel(timeout->mNestingLevel);
   }
 
   const char *reason;
   if (timeout->mIsInterval) {
     reason = "setInterval handler";
   } else {
     reason = "setTimeout handler";
   }
@@ -12963,484 +12623,25 @@ nsGlobalWindow::RunTimeoutHandler(Timeou
 
   // Since we might be processing more timeouts, go ahead and flush the promise
   // queue now before we do that.  We need to do that while we're still in our
   // "running JS is safe" state (e.g. mRunningTimeout is set, timeout->mRunning
   // is false).
   Promise::PerformMicroTaskCheckpoint();
 
   if (trackNestingLevel) {
-    sNestingLevel = nestingLevel;
-  }
-
-  --mTimeoutFiringDepth;
-  --gRunningTimeoutDepth;
-
-  mRunningTimeout = last_running_timeout;
+    TimeoutManager::SetNestingLevel(nestingLevel);
+  }
+
+  mTimeoutManager->EndRunningTimeout(last_running_timeout);
   timeout->mRunning = false;
 
   return timeout->mCleared;
 }
 
-bool
-nsGlobalWindow::RescheduleTimeout(Timeout* aTimeout, const TimeStamp& now,
-                                  bool aRunningPendingTimeouts)
-{
-  if (!aTimeout->mIsInterval) {
-    if (aTimeout->mTimer) {
-      // The timeout still has an OS timer, and it's not an interval,
-      // that means that the OS timer could still fire; cancel the OS
-      // timer and release its reference to the timeout.
-      aTimeout->mTimer->Cancel();
-      aTimeout->mTimer = nullptr;
-      aTimeout->Release();
-    }
-    return false;
-  }
-
-  // Compute time to next timeout for interval timer.
-  // Make sure nextInterval is at least DOMMinTimeoutValue().
-  TimeDuration nextInterval =
-    TimeDuration::FromMilliseconds(std::max(aTimeout->mInterval,
-                                          uint32_t(DOMMinTimeoutValue())));
-
-  // If we're running pending timeouts, set the next interval to be
-  // relative to "now", and not to when the timeout that was pending
-  // should have fired.
-  TimeStamp firingTime;
-  if (aRunningPendingTimeouts) {
-    firingTime = now + nextInterval;
-  } else {
-    firingTime = aTimeout->mWhen + nextInterval;
-  }
-
-  TimeStamp currentNow = TimeStamp::Now();
-  TimeDuration delay = firingTime - currentNow;
-
-  // And make sure delay is nonnegative; that might happen if the timer
-  // thread is firing our timers somewhat early or if they're taking a long
-  // time to run the callback.
-  if (delay < TimeDuration(0)) {
-    delay = TimeDuration(0);
-  }
-
-  if (!aTimeout->mTimer) {
-    NS_ASSERTION(IsFrozen() || IsSuspended(),
-                 "How'd our timer end up null if we're not frozen or "
-                 "suspended?");
-
-    aTimeout->mTimeRemaining = delay;
-    return true;
-  }
-
-  aTimeout->mWhen = currentNow + delay;
-
-  // Reschedule the OS timer. Don't bother returning any error codes if
-  // this fails since the callers of this method don't care about them.
-  nsresult rv = aTimeout->InitTimer(GetThrottledEventQueue(),
-                                    delay.ToMilliseconds());
-
-  if (NS_FAILED(rv)) {
-    NS_ERROR("Error initializing timer for DOM timeout!");
-
-    // We failed to initialize the new OS timer, this timer does
-    // us no good here so we just cancel it (just in case) and
-    // null out the pointer to the OS timer, this will release the
-    // OS timer. As we continue executing the code below we'll end
-    // up deleting the timeout since it's not an interval timeout
-    // any more (since timeout->mTimer == nullptr).
-    aTimeout->mTimer->Cancel();
-    aTimeout->mTimer = nullptr;
-
-    // Now that the OS timer no longer has a reference to the
-    // timeout we need to drop that reference.
-    aTimeout->Release();
-
-    return false;
-  }
-
-  return true;
-}
-
-void
-nsGlobalWindow::RunTimeout(Timeout* aTimeout)
-{
-  if (IsSuspended()) {
-    return;
-  }
-
-  NS_ASSERTION(IsInnerWindow(), "Timeout running on outer window!");
-  NS_ASSERTION(!IsFrozen(), "Timeout running on a window in the bfcache!");
-
-  Timeout* nextTimeout;
-  Timeout* last_expired_timeout;
-  Timeout* last_insertion_point;
-  uint32_t firingDepth = mTimeoutFiringDepth + 1;
-
-  // Make sure that the window and the script context don't go away as
-  // a result of running timeouts
-  nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(this);
-
-  // A native timer has gone off. See which of our timeouts need
-  // servicing
-  TimeStamp now = TimeStamp::Now();
-  TimeStamp deadline;
-
-  if (aTimeout && aTimeout->mWhen > now) {
-    // The OS timer fired early (which can happen due to the timers
-    // having lower precision than TimeStamp does).  Set |deadline| to
-    // be the time when the OS timer *should* have fired so that any
-    // timers that *should* have fired before aTimeout *will* be fired
-    // now.
-
-    deadline = aTimeout->mWhen;
-  } else {
-    deadline = now;
-  }
-
-  // The timeout list is kept in deadline order. Discover the latest timeout
-  // whose deadline has expired. On some platforms, native timeout events fire
-  // "early", but we handled that above by setting deadline to aTimeout->mWhen
-  // if the timer fired early.  So we can stop walking if we get to timeouts
-  // whose mWhen is greater than deadline, since once that happens we know
-  // nothing past that point is expired.
-  last_expired_timeout = nullptr;
-  for (Timeout* timeout = mTimeouts.getFirst();
-       timeout && timeout->mWhen <= deadline;
-       timeout = timeout->getNext()) {
-    if (timeout->mFiringDepth == 0) {
-      // Mark any timeouts that are on the list to be fired with the
-      // firing depth so that we can reentrantly run timeouts
-      timeout->mFiringDepth = firingDepth;
-      last_expired_timeout = timeout;
-
-      // Run available timers until we see our target timer.  After
-      // that, however, stop coalescing timers so we can yield the
-      // main thread.  Further timers that are ready will get picked
-      // up by their own nsITimer runnables when they execute.
-      //
-      // For chrome windows, however, we do coalesce all timers and
-      // do not yield the main thread.  This is partly because we
-      // trust chrome windows not to misbehave and partly because a
-      // number of browser chrome tests have races that depend on this
-      // coalescing.
-      if (timeout == aTimeout && !IsChromeWindow()) {
-        break;
-      }
-    }
-  }
-
-  // Maybe the timeout that the event was fired for has been deleted
-  // and there are no others timeouts with deadlines that make them
-  // eligible for execution yet. Go away.
-  if (!last_expired_timeout) {
-    return;
-  }
-
-  // Record telemetry information about timers set recently.
-  TimeDuration recordingInterval = TimeDuration::FromMilliseconds(STATISTICS_INTERVAL);
-  if (gLastRecordedRecentTimeouts.IsNull() ||
-      now - gLastRecordedRecentTimeouts > recordingInterval) {
-    gLastRecordedRecentTimeouts = now;
-  }
-
-  // Insert a dummy timeout into the list of timeouts between the
-  // portion of the list that we are about to process now and those
-  // timeouts that will be processed in a future call to
-  // win_run_timeout(). This dummy timeout serves as the head of the
-  // list for any timeouts inserted as a result of running a timeout.
-  RefPtr<Timeout> dummy_timeout = new Timeout();
-  dummy_timeout->mFiringDepth = firingDepth;
-  dummy_timeout->mWhen = now;
-  last_expired_timeout->setNext(dummy_timeout);
-  RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
-
-  last_insertion_point = mTimeoutInsertionPoint;
-  // If we ever start setting mTimeoutInsertionPoint to a non-dummy timeout,
-  // the logic in ResetTimersForThrottleReduction will need to change.
-  mTimeoutInsertionPoint = dummy_timeout;
-
-  for (Timeout* timeout = mTimeouts.getFirst();
-       timeout != dummy_timeout && !IsFrozen();
-       timeout = nextTimeout) {
-    nextTimeout = timeout->getNext();
-
-    if (timeout->mFiringDepth != firingDepth) {
-      // We skip the timeout since it's on the list to run at another
-      // depth.
-
-      continue;
-    }
-
-    if (IsSuspended()) {
-      // Some timer did suspend us. Make sure the
-      // rest of the timers get executed later.
-      timeout->mFiringDepth = 0;
-      continue;
-    }
-
-    // The timeout is on the list to run at this depth, go ahead and
-    // process it.
-
-    // Get the script context (a strong ref to prevent it going away)
-    // for this timeout and ensure the script language is enabled.
-    nsCOMPtr<nsIScriptContext> scx = GetContextInternal();
-
-    if (!scx) {
-      // No context means this window was closed or never properly
-      // initialized for this language.
-      continue;
-    }
-
-    // This timeout is good to run
-    bool timeout_was_cleared = RunTimeoutHandler(timeout, scx);
-
-    if (timeout_was_cleared) {
-      // The running timeout's window was cleared, this means that
-      // ClearAllTimeouts() was called from a *nested* call, possibly
-      // through a timeout that fired while a modal (to this window)
-      // dialog was open or through other non-obvious paths.
-      MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
-      Unused << timeoutExtraRef.forget().take();
-
-      mTimeoutInsertionPoint = last_insertion_point;
-
-      return;
-    }
-
-    // If we have a regular interval timer, we re-schedule the
-    // timeout, accounting for clock drift.
-    bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
-
-    // Running a timeout can cause another timeout to be deleted, so
-    // we need to reset the pointer to the following timeout.
-    nextTimeout = timeout->getNext();
-
-    timeout->remove();
-
-    if (needsReinsertion) {
-      // Insert interval timeout onto list sorted in deadline order.
-      // AddRefs timeout.
-      InsertTimeoutIntoList(timeout);
-    }
-
-    // Release the timeout struct since it's possibly out of the list
-    timeout->Release();
-  }
-
-  // Take the dummy timeout off the head of the list
-  dummy_timeout->remove();
-  timeoutExtraRef = nullptr;
-  MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
-
-  mTimeoutInsertionPoint = last_insertion_point;
-
-  MaybeApplyBackPressure();
-}
-
-void
-nsGlobalWindow::ClearTimeoutOrInterval(int32_t aTimerId, Timeout::Reason aReason)
-{
-  MOZ_RELEASE_ASSERT(IsInnerWindow());
-
-  uint32_t timerId = (uint32_t)aTimerId;
-  Timeout* timeout;
-
-  for (timeout = mTimeouts.getFirst(); timeout; timeout = timeout->getNext()) {
-    if (timeout->mTimeoutId == timerId && timeout->mReason == aReason) {
-      if (timeout->mRunning) {
-        /* We're running from inside the timeout. Mark this
-           timeout for deferred deletion by the code in
-           RunTimeout() */
-        timeout->mIsInterval = false;
-      }
-      else {
-        /* Delete the timeout from the pending timeout list */
-        timeout->remove();
-
-        if (timeout->mTimer) {
-          timeout->mTimer->Cancel();
-          timeout->mTimer = nullptr;
-          timeout->Release();
-        }
-        timeout->Release();
-      }
-      break;
-    }
-  }
-}
-
-nsresult nsGlobalWindow::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS)
-{
-  FORWARD_TO_INNER(ResetTimersForThrottleReduction, (aPreviousThrottleDelayMS),
-                   NS_ERROR_NOT_INITIALIZED);
-  MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
-
-  if (IsFrozen() || IsSuspended()) {
-    return NS_OK;
-  }
-
-  TimeStamp now = TimeStamp::Now();
-
-  // If mTimeoutInsertionPoint is non-null, we're in the middle of firing
-  // timers and the timers we're planning to fire all come before
-  // mTimeoutInsertionPoint; mTimeoutInsertionPoint itself is a dummy timeout
-  // with an mWhen that may be semi-bogus.  In that case, we don't need to do
-  // anything with mTimeoutInsertionPoint or anything before it, so should
-  // start at the timer after mTimeoutInsertionPoint, if there is one.
-  // Otherwise, start at the beginning of the list.
-  for (Timeout* timeout = mTimeoutInsertionPoint ?
-         mTimeoutInsertionPoint->getNext() : mTimeouts.getFirst();
-       timeout; ) {
-    // It's important that this check be <= so that we guarantee that
-    // taking std::max with |now| won't make a quantity equal to
-    // timeout->mWhen below.
-    if (timeout->mWhen <= now) {
-      timeout = timeout->getNext();
-      continue;
-    }
-
-    if (timeout->mWhen - now >
-        TimeDuration::FromMilliseconds(aPreviousThrottleDelayMS)) {
-      // No need to loop further.  Timeouts are sorted in mWhen order
-      // and the ones after this point were all set up for at least
-      // gMinBackgroundTimeoutValue ms and hence were not clamped.
-      break;
-    }
-
-    // We reduced our throttled delay. Re-init the timer appropriately.
-    // Compute the interval the timer should have had if it had not been set in a
-    // background window
-    TimeDuration interval =
-      TimeDuration::FromMilliseconds(std::max(timeout->mInterval,
-                                            uint32_t(DOMMinTimeoutValue())));
-    uint32_t oldIntervalMillisecs = 0;
-    timeout->mTimer->GetDelay(&oldIntervalMillisecs);
-    TimeDuration oldInterval = TimeDuration::FromMilliseconds(oldIntervalMillisecs);
-    if (oldInterval > interval) {
-      // unclamp
-      TimeStamp firingTime =
-        std::max(timeout->mWhen - oldInterval + interval, now);
-
-      NS_ASSERTION(firingTime < timeout->mWhen,
-                   "Our firing time should strictly decrease!");
-
-      TimeDuration delay = firingTime - now;
-      timeout->mWhen = firingTime;
-
-      // Since we reset mWhen we need to move |timeout| to the right
-      // place in the list so that it remains sorted by mWhen.
-
-      // Get the pointer to the next timeout now, before we move the
-      // current timeout in the list.
-      Timeout* nextTimeout = timeout->getNext();
-
-      // It is safe to remove and re-insert because mWhen is now
-      // strictly smaller than it used to be, so we know we'll insert
-      // |timeout| before nextTimeout.
-      NS_ASSERTION(!nextTimeout ||
-                   timeout->mWhen < nextTimeout->mWhen, "How did that happen?");
-      timeout->remove();
-      // InsertTimeoutIntoList will addref |timeout| and reset
-      // mFiringDepth.  Make sure to undo that after calling it.
-      uint32_t firingDepth = timeout->mFiringDepth;
-      InsertTimeoutIntoList(timeout);
-      timeout->mFiringDepth = firingDepth;
-      timeout->Release();
-
-      nsresult rv = timeout->InitTimer(GetThrottledEventQueue(),
-                                       delay.ToMilliseconds());
-
-      if (NS_FAILED(rv)) {
-        NS_WARNING("Error resetting non background timer for DOM timeout!");
-        return rv;
-      }
-
-      timeout = nextTimeout;
-    } else {
-      timeout = timeout->getNext();
-    }
-  }
-
-  return NS_OK;
-}
-
-void
-nsGlobalWindow::ClearAllTimeouts()
-{
-  Timeout* timeout;
-  Timeout* nextTimeout;
-
-  for (timeout = mTimeouts.getFirst(); timeout; timeout = nextTimeout) {
-    /* If RunTimeout() is higher up on the stack for this
-       window, e.g. as a result of document.write from a timeout,
-       then we need to reset the list insertion point for
-       newly-created timeouts in case the user adds a timeout,
-       before we pop the stack back to RunTimeout. */
-    if (mRunningTimeout == timeout)
-      mTimeoutInsertionPoint = nullptr;
-
-    nextTimeout = timeout->getNext();
-
-    if (timeout->mTimer) {
-      timeout->mTimer->Cancel();
-      timeout->mTimer = nullptr;
-
-      // Drop the count since the timer isn't going to hold on
-      // anymore.
-      timeout->Release();
-    }
-
-    // Set timeout->mCleared to true to indicate that the timeout was
-    // cleared and taken out of the list of timeouts
-    timeout->mCleared = true;
-
-    // Drop the count since we're removing it from the list.
-    timeout->Release();
-  }
-
-  // Clear out our list
-  mTimeouts.clear();
-}
-
-void
-nsGlobalWindow::InsertTimeoutIntoList(Timeout* aTimeout)
-{
-  NS_ASSERTION(IsInnerWindow(),
-               "InsertTimeoutIntoList() called on outer window!");
-
-  // Start at mLastTimeout and go backwards.  Don't go further than
-  // mTimeoutInsertionPoint, though.  This optimizes for the common case of
-  // insertion at the end.
-  Timeout* prevSibling;
-  for (prevSibling = mTimeouts.getLast();
-       prevSibling && prevSibling != mTimeoutInsertionPoint &&
-         // This condition needs to match the one in SetTimeoutOrInterval that
-         // determines whether to set mWhen or mTimeRemaining.
-         (IsFrozen() ?
-          prevSibling->mTimeRemaining > aTimeout->mTimeRemaining :
-          prevSibling->mWhen > aTimeout->mWhen);
-       prevSibling = prevSibling->getPrevious()) {
-    /* Do nothing; just searching */
-  }
-
-  // Now link in aTimeout after prevSibling.
-  if (prevSibling) {
-    prevSibling->setNext(aTimeout);
-  } else {
-    mTimeouts.insertFront(aTimeout);
-  }
-
-  aTimeout->mFiringDepth = 0;
-
-  // Increment the timeout's reference count since it's now held on to
-  // by the list
-  aTimeout->AddRef();
-}
-
 //*****************************************************************************
 // nsGlobalWindow: Helper Functions
 //*****************************************************************************
 
 already_AddRefed<nsIDocShellTreeOwner>
 nsGlobalWindow::GetTreeOwner()
 {
   FORWARD_TO_OUTER(GetTreeOwner, (), nullptr);
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -51,17 +51,17 @@
 #include "nsIDocument.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "Units.h"
 #include "nsComponentManagerUtils.h"
 #include "nsSize.h"
 #include "nsCheapSets.h"
 #include "mozilla/dom/ImageBitmapSource.h"
-#include "mozilla/dom/Timeout.h"
+#include "mozilla/UniquePtr.h"
 
 #define DEFAULT_HOME_PAGE "www.mozilla.org"
 #define PREF_BROWSER_STARTUP_HOMEPAGE "browser.startup.homepage"
 
 // Amount of time allowed between alert/prompt/confirm before enabling
 // the stop dialog checkbox.
 #define DEFAULT_SUCCESSIVE_DIALOG_TIME_LIMIT 3 // 3 sec
 
@@ -390,17 +390,16 @@ public:
   virtual void Resume();
   virtual bool IsSuspended() const override;
   virtual void Freeze();
   virtual void Thaw();
   virtual bool IsFrozen() const override;
   virtual void SyncStateFromParentWindow();
 
   virtual nsresult FireDelayedDOMEvents() override;
-  virtual bool IsRunningTimeout() override { return mTimeoutFiringDepth > 0; }
 
   // Outer windows only.
   virtual bool WouldReuseInnerWindow(nsIDocument* aNewDocument) override;
 
   virtual void SetDocShell(nsIDocShell* aDocShell) override;
   virtual void DetachFromDocShell() override;
   virtual nsresult SetNewDocument(nsIDocument *aDocument,
                                   nsISupports *aState,
@@ -702,18 +701,16 @@ public:
   static WindowByIdTable* GetWindowsTable() {
     AssertIsOnMainThread();
 
     return sWindowsById;
   }
 
   void AddSizeOfIncludingThis(nsWindowSizes* aWindowSizes) const;
 
-  void UnmarkGrayTimers();
-
   // Inner windows only.
   void AddEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
   void RemoveEventTargetObject(mozilla::DOMEventTargetHelper* aObject);
 
   void NotifyIdleObserver(IdleObserverHolder* aIdleObserverHolder,
                           bool aCallOnidle);
   nsresult HandleIdleActiveEvent();
   bool ContainsIdleObserver(nsIIdleObserver* aIdleObserver, uint32_t timeInS);
@@ -1252,16 +1249,17 @@ public:
   void GetInterface(JSContext* aCx, nsIJSID* aIID,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
 
   already_AddRefed<nsWindowRoot> GetWindowRootOuter();
   already_AddRefed<nsWindowRoot> GetWindowRoot(mozilla::ErrorResult& aError);
 
   mozilla::dom::Performance* GetPerformance();
+
 protected:
   // Web IDL helpers
 
   // Redefine the property called aPropName on this window object to be a value
   // property with the value aValue, much like we would do for a [Replaceable]
   // property in IDL.
   void RedefineProperty(JSContext* aCx, const char* aPropName,
                         JS::Handle<JS::Value> aValue,
@@ -1460,47 +1458,28 @@ private:
   template<typename Method>
   void CallOnChildren(Method aMethod);
 
   void FreezeInternal();
   void ThawInternal();
 
 public:
   // Timeout Functions
-  // Language agnostic timeout function (all args passed).
   // |interval| is in milliseconds.
-  nsresult SetTimeoutOrInterval(nsITimeoutHandler* aHandler,
-                                int32_t interval, bool aIsInterval,
-                                mozilla::dom::Timeout::Reason aReason,
-                                int32_t* aReturn);
   int32_t SetTimeoutOrInterval(JSContext* aCx,
                                mozilla::dom::Function& aFunction,
                                int32_t aTimeout,
                                const mozilla::dom::Sequence<JS::Value>& aArguments,
                                bool aIsInterval, mozilla::ErrorResult& aError);
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                int32_t aTimeout, bool aIsInterval,
                                mozilla::ErrorResult& aError);
-  void ClearTimeoutOrInterval(int32_t aTimerId,
-                              mozilla::dom::Timeout::Reason aReason);
 
-  // The timeout implementation functions.
-  void RunTimeout(mozilla::dom::Timeout* aTimeout);
-  void RunTimeout() { RunTimeout(nullptr); }
   // Return true if |aTimeout| was cleared while its handler ran.
   bool RunTimeoutHandler(mozilla::dom::Timeout* aTimeout, nsIScriptContext* aScx);
-  // Return true if |aTimeout| needs to be reinserted into the timeout list.
-  bool RescheduleTimeout(mozilla::dom::Timeout* aTimeout, const TimeStamp& now,
-                         bool aRunningPendingTimeouts);
-
-  void ClearAllTimeouts();
-  // Insert aTimeout into the list, before all timeouts that would
-  // fire after it, but no earlier than mTimeoutInsertionPoint, if any.
-  void InsertTimeoutIntoList(mozilla::dom::Timeout* aTimeout);
-  uint32_t GetTimeoutId(mozilla::dom::Timeout::Reason aReason);
 
   // Helper Functions
   already_AddRefed<nsIDocShellTreeOwner> GetTreeOwner();
   already_AddRefed<nsIBaseWindow> GetTreeOwnerWindow();
   already_AddRefed<nsIWebBrowserChrome> GetWebBrowserChrome();
   nsresult SecurityCheckURL(const char *aURL);
   bool IsPrivateBrowsing();
 
@@ -1612,18 +1591,16 @@ protected:
 
   static void NotifyDOMWindowFrozen(nsGlobalWindow* aWindow);
   static void NotifyDOMWindowThawed(nsGlobalWindow* aWindow);
 
   void ClearStatus();
 
   virtual void UpdateParentTarget() override;
 
-  inline int32_t DOMMinTimeoutValue() const;
-
   void InitializeShowFocusRings();
 
   // Clear the document-dependent slots on our JS wrapper.  Inner windows only.
   void ClearDocumentDependentSlots(JSContext* aCx);
 
   // Inner windows only.
   already_AddRefed<mozilla::dom::StorageEvent>
   CloneStorageEvent(const nsAString& aType,
@@ -1700,32 +1677,16 @@ private:
   // IsSecureContext() for the inner window that corresponds to aDocument.
   bool ComputeIsSecureContext(nsIDocument* aDocument);
 
   // nsPIDOMWindow<T> should be able to see these helper methods.
   friend class nsPIDOMWindow<mozIDOMWindowProxy>;
   friend class nsPIDOMWindow<mozIDOMWindow>;
   friend class nsPIDOMWindow<nsISupports>;
 
-  // Apply back pressure to the window if the TabGroup ThrottledEventQueue
-  // exists and has too many runnables waiting to run.  For example, increase
-  // the minimum timer delay, etc.
-  void
-  MaybeApplyBackPressure();
-
-  // Check the current ThrottledEventQueue depth and update the back pressure
-  // state.  If the queue has drained back pressure may be canceled.
-  void
-  CancelOrUpdateBackPressure();
-
-  // When timers are being throttled and we reduce the thottle delay we must
-  // reschedule.  The amount of the old throttle delay must be provided in
-  // order to bound how many timers must be examined.
-  nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS);
-
   mozilla::dom::TabGroup* TabGroupInner();
   mozilla::dom::TabGroup* TabGroupOuter();
 
   bool IsBackgroundInternal() const;
 
 public:
   // Dispatch a runnable related to the global.
   virtual nsresult Dispatch(const char* aName,
@@ -1848,58 +1809,44 @@ protected:
 
   RefPtr<mozilla::dom::MozSelfSupport> mMozSelfSupport;
 
   RefPtr<mozilla::dom::DOMStorage> mLocalStorage;
   RefPtr<mozilla::dom::DOMStorage> mSessionStorage;
 
   // These member variable are used only on inner windows.
   RefPtr<mozilla::EventListenerManager> mListenerManager;
-  // mTimeouts is generally sorted by mWhen, unless mTimeoutInsertionPoint is
-  // non-null.  In that case, the dummy timeout pointed to by
-  // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
-  // that come after it.
-  mozilla::LinkedList<mozilla::dom::Timeout> mTimeouts;
-  // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
-  // This is a dummy timeout at the moment; if that ever changes, the logic in
-  // ResetTimersForThrottleReduction needs to change.
-  mozilla::dom::Timeout*      mTimeoutInsertionPoint;
-  uint32_t                    mTimeoutIdCounter;
-  uint32_t                    mTimeoutFiringDepth;
   RefPtr<mozilla::dom::Location> mLocation;
   RefPtr<nsHistory>           mHistory;
   RefPtr<mozilla::dom::CustomElementRegistry> mCustomElements;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
   typedef nsTArray<RefPtr<mozilla::dom::StorageEvent>> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
+
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
-  int32_t mBackPressureDelayMS;
-
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
   uint32_t mSerial;
 
   void DisableIdleCallbackRequests();
   void UnthrottleIdleCallbackRequests();
 
   void PostThrottledIdleCallback();
 
   typedef mozilla::LinkedList<mozilla::dom::IdleRequest> IdleRequests;
   static void InsertIdleCallbackIntoList(mozilla::dom::IdleRequest* aRequest,
                                          IdleRequests& aList);
 
-   // The current idle request callback timeout handle
-  uint32_t mIdleCallbackTimeoutCounter;
   // The current idle request callback handle
   uint32_t mIdleRequestCallbackCounter;
   IdleRequests mIdleRequestCallbacks;
   IdleRequests mThrottledIdleRequestCallbacks;
 
 #ifdef DEBUG
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
@@ -1969,16 +1916,17 @@ protected:
   nsTArray<RefPtr<mozilla::dom::VRDisplay>> mVRDisplays;
 
   nsAutoPtr<mozilla::dom::VREventObserver> mVREventObserver;
 
   friend class nsDOMScriptableHelper;
   friend class nsDOMWindowUtils;
   friend class mozilla::dom::PostMessageEvent;
   friend class DesktopNotification;
+  friend class mozilla::dom::TimeoutManager;
 
   static WindowByIdTable* sWindowsById;
   static bool sWarnedAboutWindowInternal;
 };
 
 inline nsISupports*
 ToSupports(nsGlobalWindow *p)
 {
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -44,16 +44,17 @@ class ThrottledEventQueue;
 namespace dom {
 class AudioContext;
 class DocGroup;
 class TabGroup;
 class Element;
 class Performance;
 class ServiceWorkerRegistration;
 class Timeout;
+class TimeoutManager;
 class CustomElementRegistry;
 } // namespace dom
 } // namespace mozilla
 
 // Popup control state enum. The values in this enum must go from most
 // permissive to least permissive so that it's safe to push state in
 // all situations. Pushing popup state onto the stack never makes the
 // current popup state less permissive (see
@@ -182,18 +183,16 @@ public:
   nsIDocument* GetDoc()
   {
     if (!mDoc) {
       MaybeCreateDoc();
     }
     return mDoc;
   }
 
-  virtual bool IsRunningTimeout() = 0;
-
 protected:
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
 public:
   // Check whether a document is currently loading
   inline bool IsLoading() const;
@@ -614,27 +613,27 @@ protected:
   nsCOMPtr<mozilla::dom::Element> mFrameElement;
   // This reference is used by the subclass nsGlobalWindow, and cleared in it's
   // DetachFromDocShell() method. This method is called by nsDocShell::Destroy(),
   // which is called before the nsDocShell is destroyed.
   nsIDocShell* MOZ_NON_OWNING_REF mDocShell;  // Weak Reference
 
   // mPerformance is only used on inner windows.
   RefPtr<mozilla::dom::Performance> mPerformance;
+  // mTimeoutManager is only useed on inner windows.
+  mozilla::UniquePtr<mozilla::dom::TimeoutManager> mTimeoutManager;
 
   typedef nsRefPtrHashtable<nsStringHashKey,
                             mozilla::dom::ServiceWorkerRegistration>
           ServiceWorkerRegistrationTable;
   ServiceWorkerRegistrationTable mServiceWorkerRegistrationTable;
 
   uint32_t               mModalStateDepth;
 
   // These variables are only used on inner windows.
-  mozilla::dom::Timeout *mRunningTimeout;
-
   uint32_t               mMutationBits;
 
   bool                   mIsDocumentLoaded;
   bool                   mIsHandlingResizeEvent;
   bool                   mIsInnerWindow;
   bool                   mMayHavePaintEventListener;
   bool                   mMayHaveTouchEventListener;
   bool                   mMayHaveMouseEnterLeaveEventListener;
@@ -845,16 +844,22 @@ public:
   // calls.
   void Freeze();
   void Thaw();
 
   // Apply the parent window's suspend, freeze, and modal state to the current
   // window.
   void SyncStateFromParentWindow();
 
+  bool HasAudioContexts() const;
+
+  mozilla::dom::TimeoutManager& TimeoutManager();
+
+  bool IsRunningTimeout();
+
 protected:
   void CreatePerformanceObjectIfNeeded();
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 // NB: It's very very important that these two classes have identical vtables
 // and memory layout!
--- a/dom/webidl/AccessibleNode.webidl
+++ b/dom/webidl/AccessibleNode.webidl
@@ -2,10 +2,12 @@
 /* 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/.
  */
 
 [Pref="accessibility.AOM.enabled"]
 interface AccessibleNode {
   readonly attribute DOMString role;
+  [Frozen, Cached, Pure]
+  readonly attribute sequence<DOMString> states;
   readonly attribute Node? DOMNode;
 };
--- a/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/README_en_US.txt
@@ -1,29 +1,31 @@
 en_US-mozilla Hunspell Dictionary
-Generated from SCOWL Version 2016.06.26
-Tue Jul 26 12:40:24 EDT 2016
+Generated from SCOWL Version 2016.11.20
+Tue Dec 13 14:42:45 EST 2016
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
 The following dictionaries are available:
 
   en_US (American)
   en_CA (Canadian)
   en_GB-ise (British with "ise" spelling)
   en_GB-ize (British with "ize" spelling)
+  en_AU (Australian)
 
   en_US-large
   en_CA-large
   en_GB-large (with both "ise" and "ize" spelling)
+  en_AU-large
 
 The normal (non-large) dictionaries correspond to SCOWL size 60 and,
 to encourage consistent spelling, generally only include one spelling
 variant for a word.  The large dictionaries correspond to SCOWL size
 70 and may include multiple spelling for a word when both variants are
 considered almost equal.  The larger dictionaries however (1) have not
 been as carefully checked for errors as the normal dictionaries and
 thus may contain misspelled or invalid words; and (2) contain
@@ -34,18 +36,18 @@ likely to be misspellings of more common
 To get an idea of the difference in size, here are 25 random words
 only found in the large dictionary for American English:
 
   Bermejo Freyr's Guenevere Hatshepsut Nottinghamshire arrestment
   crassitudes crural dogwatches errorless fetial flaxseeds godroon
   incretion jalapeño's kelpie kishkes neuroglias pietisms pullulation
   stemwinder stenoses syce thalassic zees
 
-The en_US and en_CA are the official dictionaries for Hunspell.  The
-en_GB and large dictionaries are made available on an experimental
+The en_US, en_CA and en_AU are the official dictionaries for Hunspell.
+The en_GB and large dictionaries are made available on an experimental
 basis.  If you find them useful please send me a quick email at
 kevina@gnu.org.
 
 If none of these dictionaries suite you (for example, maybe you want
 the normal dictionary that also includes common variants) additional
 dictionaries can be generated at http://app.aspell.net/create or by
 modifying speller/make-hunspell-dict in SCOWL.  Please do let me know
 if you end up publishing a customized dictionary.
@@ -55,17 +57,22 @@ shouldn't be, you can lookup the word up
 to help determine why that is.
 
 General comments on these list can be sent directly to me at
 kevina@gnu.org or to the wordlist-devel mailing lists
 (https://lists.sourceforge.net/lists/listinfo/wordlist-devel).  If you
 have specific issues with any of these dictionaries please file a bug
 report at https://github.com/kevina/wordlist/issues.
 
-IMPORTANT CHANGES INTRODUCED IN 2015.04.24:
+IMPORTANT CHANGES INTRODUCED In 2016.11.20:
+
+New Australian dictionaries thanks to the work of Benjamin Titze
+(btitze@protonmail.ch).
+
+IMPORTANT CHANGES INTRODUCED IN 2016.04.24:
 
 The dictionaries are now in UTF-8 format instead of ISO-8859-1.  This
 was required to handle smart quotes correctly.
 
 IMPORTANT CHANGES INTRODUCED IN 2016.01.19:
 
 "SET UTF8" was changes to "SET UTF-8" in the affix file as some
 versions of Hunspell do not recognize "UTF8".
@@ -83,20 +90,20 @@ COPYRIGHT, SOURCES, and CREDITS:
 
 The English dictionaries come directly from SCOWL 
 and is thus under the same copyright of SCOWL.  The affix file is
 a heavily modified version of the original english.aff file which was
 released as part of Geoff Kuenning's Ispell and as such is covered by
 his BSD license.  Part of SCOWL is also based on Ispell thus the
 Ispell copyright is included with the SCOWL copyright.
 
-The collective work is Copyright 2000-2015 by Kevin Atkinson as well
+The collective work is Copyright 2000-2016 by Kevin Atkinson as well
 as any of the copyrights mentioned below:
 
-  Copyright 2000-2015 by Kevin Atkinson
+  Copyright 2000-2016 by Kevin Atkinson
 
   Permission to use, copy, modify, distribute and sell these word
   lists, the associated scripts, the output created from the scripts,
   and its documentation for any purpose is hereby granted without fee,
   provided that the above copyright notice appears in all copies and
   that both that copyright notice and this permission notice appear in
   supporting documentation. Kevin Atkinson makes no representations
   about the suitability of this array for any purpose. It is provided
@@ -274,21 +281,40 @@ following copyright:
 The 95 level includes the 354,984 single words, 256,772 compound
 words, 4,946 female names and the 3,897 male names, and 21,986 names
 from the MWords package, ABLE.LST from the ENABLE Supplement, and some
 additional words found in my part-of-speech database that were not
 found anywhere else.
 
 Accent information was taken from UKACD.
 
-My VARCON package was used to create the American, British, and
-Canadian word list. 
+The VarCon package was used to create the American, British, Canadian,
+and Australian word list.  It is under the following copyright:
+
+  Copyright 2000-2016 by Kevin Atkinson
 
-Since the original word lists used in the VARCON package came
-from the Ispell distribution they are under the Ispell copyright:
+  Permission to use, copy, modify, distribute and sell this array, the
+  associated software, and its documentation for any purpose is hereby
+  granted without fee, provided that the above copyright notice appears
+  in all copies and that both that copyright notice and this permission
+  notice appear in supporting documentation. Kevin Atkinson makes no
+  representations about the suitability of this array for any
+  purpose. It is provided "as is" without express or implied warranty.
+
+  Copyright 2016 by Benjamin Titze
+
+  Permission to use, copy, modify, distribute and sell this array, the
+  associated software, and its documentation for any purpose is hereby
+  granted without fee, provided that the above copyright notice appears
+  in all copies and that both that copyright notice and this permission
+  notice appear in supporting documentation. Benjamin Titze makes no
+  representations about the suitability of this array for any
+  purpose. It is provided "as is" without express or implied warranty.
+
+  Since the original words lists come from the Ispell distribution:
 
   Copyright 1993, Geoff Kuenning, Granada Hills, CA
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:
 
@@ -301,22 +327,21 @@ from the Ispell distribution they are un
      such.  Binary redistributions based on modified source code
      must be clearly marked as modified versions in the documentation
      and/or other materials provided with the distribution.
   (clause 4 removed with permission from Geoff Kuenning)
   5. The name of Geoff Kuenning may not be used to endorse or promote
      products derived from this software without specific prior
      written permission.
 
-  THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS
-  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-  FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF
-  KUENNING OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-  POSSIBILITY OF SUCH DAMAGE.
+  THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
 
-Build Date: Tue Jul 26 12:40:24 EDT 2016
+Build Date: Tue Dec 13 14:42:45 EST 2016
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/5-mozilla-added
@@ -5182,16 +5182,19 @@ Zune's
 abridgement
 abridgement's
 abridgements
 absorbances
 absorbancy
 absorbancy's
 actin
 admin's
+adoptee
+adoptee's
+adoptees
 advocator
 advocator's
 advocators
 adware's
 adwares
 aggregator
 aggregator's
 aggregators
@@ -5249,23 +5252,28 @@ blogrolls
 bloviate
 bloviated
 bloviates
 bloviating
 bloviation
 bloviator
 bloviator's
 bloviators
-blowjob's
-blowjobs
 bookselling
 broadcasted
 cDNA
 canceller
 canceller's
+canonicalization
+canonicalization's
+canonicalizations
+canonicalize
+canonicalized
+canonicalizes
+canonicalizing
 capita
 carboxylic
 carnitas
 cerevisiae
 cerevisiae's
 cerevisiaes
 charcuterie
 chemistries
@@ -5303,17 +5311,16 @@ crimeware
 crimeware's
 cryonic
 cryptologist
 cryptologist's
 cryptologists
 cryptosystem
 cryptosystems
 cul-de-sac
-cultivar's
 cyber
 cytokine
 cytokine's
 datasheet
 datasheet's
 datasheets
 decertification
 decertifications
@@ -5376,18 +5383,16 @@ eschatologists
 exacta
 exactable
 exactas
 exactingness
 exactions
 exactor
 exactor's
 exactors
-exon's
-exons
 experimentalism
 filesystem
 filesystem's
 filesystems
 filmography
 financials
 fluidize
 fluidizes
@@ -5422,17 +5427,16 @@ greyed
 greyer
 greyest
 greying
 greyness's
 greys
 hentai
 hexane's
 hexanes
-hijabs
 hippopotami
 holdem
 iPods
 idolator
 idolator's
 idolators
 inactives
 inactivities
@@ -5484,18 +5488,16 @@ limnologist
 limnologist's
 limnologists
 limnology
 limnology's
 linguistical
 mRNA
 malwares
 mammalia
-meetup's
-meetups
 megajoule
 megajoule's
 mesothelioma
 mesothelioma's
 metadata's
 methoxy
 migrator
 migrator's
@@ -5539,16 +5541,19 @@ neurosciences
 neuroscientist
 neuroscientist's
 neuroscientists
 newswires
 octopi
 oligo
 opposable
 opposer
+outlier
+outlier's
+outliers
 parallelization
 parallelization's
 parallelizations
 parallelize
 parallelized
 parallelizes
 parallelizing
 permalink
@@ -5608,20 +5613,16 @@ reactivity's
 reappointments
 rebroadcasted
 recency
 recompilation's
 recurse
 recursed
 recurses
 recursing
-recused
-recuses
-recusing
-reductase's
 reflux's
 relocations
 renominations
 repartitions
 resizable
 resizer
 resubmission's
 retransmission's
@@ -5633,18 +5634,16 @@ rheumatology
 rheumatology's
 rotatably
 sativa
 savoir
 schnaps
 schrod
 schrods
 scot-free
-screensaver's
-screensavers
 screenshot's
 searchable
 selfing
 selfism
 selfist
 selfists
 seraphim
 shemale
@@ -5694,17 +5693,16 @@ testcases
 testsuite
 testsuite's
 testsuites
 textbox
 textbox's
 textboxes
 thaliana
 therebetween
-toolbars
 traceur
 traceur's
 traceurs
 trackback
 trackback's
 trackbacks
 transfect
 transfected
@@ -5730,20 +5728,15 @@ validator
 validators
 vertebrata
 volcanological
 volcanologist
 volcanologist's
 volcanologists
 volcanology
 volcanology's
-weaponized
-weaponizes
-weaponizing
 webdesign
 webdesign's
 webdesigns
 whitepaper
 whitepaper's
 whitepapers
-wildcard's
-wildcards
 wop's
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US-custom.txt
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/README_en_US-custom.txt
@@ -1,29 +1,31 @@
 en_US-custom Hunspell Dictionary
-Generated from SCOWL Version 2016.06.26
-Tue Jul 26 12:40:23 EDT 2016
+Generated from SCOWL Version 2016.11.20
+Tue Dec 13 14:42:42 EST 2016
 
 http://wordlist.sourceforge.net
 
 README file for English Hunspell dictionaries derived from SCOWL.
 
 These dictionaries are created using the speller/make-hunspell-dict
 script in SCOWL.
 
 The following dictionaries are available:
 
   en_US (American)
   en_CA (Canadian)
   en_GB-ise (British with "ise" spelling)
   en_GB-ize (British with "ize" spelling)
+  en_AU (Australian)
 
   en_US-large
   en_CA-large
   en_GB-large (with both "ise" and "ize" spelling)
+  en_AU-large
 
 The normal (non-large) dictionaries correspond to SCOWL size 60 and,
 to encourage consistent spelling, generally only include one spelling
 variant for a word.  The large dictionaries correspond to SCOWL size
 70 and may include multiple spelling for a word when both variants are
 considered almost equal.  The larger dictionaries however (1) have not
 been as carefully checked for errors as the normal dictionaries and
 thus may contain misspelled or invalid words; and (2) contain
@@ -34,18 +36,18 @@ likely to be misspellings of more common
 To get an idea of the difference in size, here are 25 random words
 only found in the large dictionary for American English:
 
   Bermejo Freyr's Guenevere Hatshepsut Nottinghamshire arrestment
   crassitudes crural dogwatches errorless fetial flaxseeds godroon
   incretion jalapeño's kelpie kishkes neuroglias pietisms pullulation
   stemwinder stenoses syce thalassic zees
 
-The en_US and en_CA are the official dictionaries for Hunspell.  The
-en_GB and large dictionaries are made available on an experimental
+The en_US, en_CA and en_AU are the official dictionaries for Hunspell.
+The en_GB and large dictionaries are made available on an experimental
 basis.  If you find them useful please send me a quick email at
 kevina@gnu.org.
 
 If none of these dictionaries suite you (for example, maybe you want
 the normal dictionary that also includes common variants) additional
 dictionaries can be generated at http://app.aspell.net/create or by
 modifying speller/make-hunspell-dict in SCOWL.  Please do let me know
 if you end up publishing a customized dictionary.
@@ -55,17 +57,22 @@ shouldn't be, you can lookup the word up
 to help determine why that is.
 
 General comments on these list can be sent directly to me at
 kevina@gnu.org or to the wordlist-devel mailing lists
 (https://lists.sourceforge.net/lists/listinfo/wordlist-devel).  If you
 have specific issues with any of these dictionaries please file a bug
 report at https://github.com/kevina/wordlist/issues.
 
-IMPORTANT CHANGES INTRODUCED IN 2015.04.24:
+IMPORTANT CHANGES INTRODUCED In 2016.11.20:
+
+New Australian dictionaries thanks to the work of Benjamin Titze
+(btitze@protonmail.ch).
+
+IMPORTANT CHANGES INTRODUCED IN 2016.04.24:
 
 The dictionaries are now in UTF-8 format instead of ISO-8859-1.  This
 was required to handle smart quotes correctly.
 
 IMPORTANT CHANGES INTRODUCED IN 2016.01.19:
 
 "SET UTF8" was changes to "SET UTF-8" in the affix file as some
 versions of Hunspell do not recognize "UTF8".
@@ -83,20 +90,20 @@ COPYRIGHT, SOURCES, and CREDITS:
 
 The English dictionaries come directly from SCOWL 
 and is thus under the same copyright of SCOWL.  The affix file is
 a heavily modified version of the original english.aff file which was
 released as part of Geoff Kuenning's Ispell and as such is covered by
 his BSD license.  Part of SCOWL is also based on Ispell thus the
 Ispell copyright is included with the SCOWL copyright.
 
-The collective work is Copyright 2000-2015 by Kevin Atkinson as well
+The collective work is Copyright 2000-2016 by Kevin Atkinson as well
 as any of the copyrights mentioned below:
 
-  Copyright 2000-2015 by Kevin Atkinson
+  Copyright 2000-2016 by Kevin Atkinson
 
   Permission to use, copy, modify, distribute and sell these word
   lists, the associated scripts, the output created from the scripts,
   and its documentation for any purpose is hereby granted without fee,
   provided that the above copyright notice appears in all copies and
   that both that copyright notice and this permission notice appear in
   supporting documentation. Kevin Atkinson makes no representations
   about the suitability of this array for any purpose. It is provided
@@ -274,21 +281,40 @@ following copyright:
 The 95 level includes the 354,984 single words, 256,772 compound
 words, 4,946 female names and the 3,897 male names, and 21,986 names
 from the MWords package, ABLE.LST from the ENABLE Supplement, and some
 additional words found in my part-of-speech database that were not
 found anywhere else.
 
 Accent information was taken from UKACD.
 
-My VARCON package was used to create the American, British, and
-Canadian word list. 
+The VarCon package was used to create the American, British, Canadian,
+and Australian word list.  It is under the following copyright:
+
+  Copyright 2000-2016 by Kevin Atkinson
 
-Since the original word lists used in the VARCON package came
-from the Ispell distribution they are under the Ispell copyright:
+  Permission to use, copy, modify, distribute and sell this array, the
+  associated software, and its documentation for any purpose is hereby
+  granted without fee, provided that the above copyright notice appears
+  in all copies and that both that copyright notice and this permission
+  notice appear in supporting documentation. Kevin Atkinson makes no
+  representations about the suitability of this array for any
+  purpose. It is provided "as is" without express or implied warranty.
+
+  Copyright 2016 by Benjamin Titze
+
+  Permission to use, copy, modify, distribute and sell this array, the
+  associated software, and its documentation for any purpose is hereby
+  granted without fee, provided that the above copyright notice appears
+  in all copies and that both that copyright notice and this permission
+  notice appear in supporting documentation. Benjamin Titze makes no
+  representations about the suitability of this array for any
+  purpose. It is provided "as is" without express or implied warranty.
+
+  Since the original words lists come from the Ispell distribution:
 
   Copyright 1993, Geoff Kuenning, Granada Hills, CA
   All rights reserved.
 
   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:
 
@@ -301,23 +327,22 @@ from the Ispell distribution they are un
      such.  Binary redistributions based on modified source code
      must be clearly marked as modified versions in the documentation
      and/or other materials provided with the distribution.
   (clause 4 removed with permission from Geoff Kuenning)
   5. The name of Geoff Kuenning may not be used to endorse or promote
      products derived from this software without specific prior
      written permission.
 
-  THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS
-  IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
-  FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF
-  KUENNING OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
-  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-  POSSIBILITY OF SUCH DAMAGE.
+  THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND
+  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE
+  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+  OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+  SUCH DAMAGE.
 
-Build Date: Tue Jul 26 12:40:23 EDT 2016
+Build Date: Tue Dec 13 14:42:42 EST 2016
 With Input Command: ../mk-list -v1 --accents=both en_US 60
--- a/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US-custom.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/dictionary-sources/orig/en_US-custom.dic
@@ -1,9 +1,9 @@
-49464
+49461
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -11816,17 +11816,17 @@ anemically
 anemometer/SM
 anemone/SM
 anent
 anesthesia/M
 anesthesiologist/SM
 anesthesiology/M
 anesthetic/SM
 anesthetist/MS
-anesthetization/M
+anesthetization/SM
 anesthetize/GDS
 aneurysm/SM
 anew
 angel/MS
 angelfish/MS
 angelic
 angelica/M
 angelical/Y
@@ -14248,17 +14248,17 @@ blotting
 blotto
 blouse/MGDS
 blow/SZGMR
 blower/M
 blowfly/SM
 blowgun/MS
 blowhard/MS
 blowhole/S
-blowjob
+blowjob/SM
 blowlamp/S
 blown
 blowout/SM
 blowpipe/SM
 blowsy/RT
 blowtorch/MS
 blowup/MS
 blowy/TR
@@ -15157,17 +15157,16 @@ burnable/SM
 burner/M
 burnish/ZGMDRS
 burnisher/M
 burnoose/MS
 burnous/MS
 burnout/MS
 burnt
 burp/MDGS
-burqa/SM
 burr/MDGS
 burrito/MS
 burro/SM
 burrow/SMDRZG
 burrower/M
 bursa/M
 bursae
 bursar/SM
@@ -18903,17 +18902,17 @@ culotte/SM
 culpability/M
 culpable/I
 culpably
 culprit/SM
 cult/MS
 cultism/M
 cultist/MS
 cultivable
-cultivar/S
+cultivar/SM
 cultivate/BDSGN
 cultivated/U
 cultivation/M
 cultivator/MS
 cultural/Y
 culture/MGDS
 cultured/U
 culvert/MS
@@ -22526,17 +22525,17 @@ existence/MS
 existent
 existential/Y
 existentialism/M
 existentialist/MS
 exit/MDGS
 exobiology/M
 exodus/MS
 exogenous
-exon
+exon/MS
 exonerate/GNDS
 exoneration/M
 exoplanet/MS
 exorbitance/M
 exorbitant/Y
 exorcise/DSG
 exorcism/SM
 exorcist/SM
@@ -26660,17 +26659,17 @@ highlight/SMDRZG
 highlighter/M
 highness/M
 highroad/MS
 highs
 hightail/DSG
 highway/MS
 highwayman/M
 highwaymen
-hijab
+hijab/SM
 hijack/SJZGMDR
 hijacker/M
 hijacking/M
 hike/MZGDRS
 hiker/M
 hiking/M
 hilarious/PY
 hilariousness/M
@@ -29750,17 +29749,16 @@ larkspur/SM
 larva/M
 larvae
 larval
 laryngeal
 larynges
 laryngitis/M
 larynx/M
 lasagna/MS
-lasagne/MS
 lascivious/YP
 lasciviousness/M
 lase/ZGDRS
 laser/M
 lash/MDSGJ
 lashing/M
 lass/MS
 lassie/SM
@@ -29897,16 +29895,17 @@ leaky/PRT
 lean/MDRSTGJP
 leaning/M
 leanness/M
 leap/MDRSZG
 leaper/M
 leapfrog/MS
 leapfrogged
 leapfrogging
+leapt
 learn/AUGDS
 learnedly
 learner/MS
 learning's
 lease/ADSMG
 leaseback/SM
 leasehold/MRSZ
 leaseholder/M
@@ -31338,21 +31337,19 @@ materialist/SM
 materialistic
 materialistically
 materialization/M
 materialize/DSG
 materiel/M
 maternal/Y
 maternity/M
 matey/S
-math/M
 mathematical/Y
 mathematician/SM
 mathematics/M
-maths
 matinee/SM
 mating/M
 matins/M
 matinée/SM
 matriarch/M
 matriarchal
 matriarchs
 matriarchy/SM
@@ -31510,17 +31507,17 @@ medusa
 medusae
 meed/M
 meek/RYPT
 meekness/M
 meerschaum/SM
 meet/MJSG
 meeting/M
 meetinghouse/SM
-meetup
+meetup/MS
 meg/S
 mega
 megabit/SM
 megabucks/M
 megabyte/MS
 megachurch/MS
 megacycle/SM
 megadeath/M
@@ -32813,17 +32810,17 @@ multistory
 multitask/GS
 multitasking/M
 multitude/SM
 multitudinous
 multivariate
 multiverse/SM
 multivitamin/MS
 multiyear
-mum/SM
+mum
 mumble/MZGDRS
 mumbler/M
 mumbletypeg/M
 mummer/MS
 mummery/M
 mummification/M
 mummify/GNDS
 mummy/SM
@@ -34587,17 +34584,17 @@ osteopath/M
 osteopathic
 osteopaths
 osteopathy/M
 osteoporosis/M
 ostler/S
 ostracism/M
 ostracize/GDS
 ostrich/MS
-other/SP
+other/MSP
 otherwise
 otherworldly
 otiose
 otter/MS
 ottoman/MS
 oubliette/MS
 ouch
 ought
@@ -38924,17 +38921,17 @@ rectum/SM
 recumbent
 recuperate/GNVDS
 recuperation/M
 recur/S
 recurred
 recurrence/SM
 recurring
 recursion/S
-recuse
+recuse/DSG
 recyclable/SM
 recycling/M
 red/PSM
 redact/SDG
 redaction/M
 redactor/SM
 redbird/SM
 redbreast/MS
@@ -38963,17 +38960,17 @@ redolent
 redoubt/SBM
 redoubtably
 redound/SDG
 redraw/SG
 redskin/SM
 reduce/DRSZG
 reducer/M
 reducible
-reductase
+reductase/M
 reduction/SM
 reductionist
 reductive
 redundancy/SM
 redundant/Y
 redwood/SM
 redye/DS
 reediness/M
@@ -40719,17 +40716,17 @@ screamer/M
 screaming/Y
 scree/MDS
 screech/GMDS
 screechy/TR
 screed/S
 screen/SJMDG
 screening/M
 screenplay/SM
-screensaver
+screensaver/SM
 screenshot/S
 screenwriter/SM
 screenwriting/M
 screw's
 screw/UDSG
 screwball/MS
 screwdriver/MS
 screwiness/M
@@ -41977,17 +41974,16 @@ skibob/S
 skid/MS
 skidded
 skidding
 skidpan/S
 skier/M
 skiff/SM
 skiffle
 skiing/M
-skilfully
 skill's
 skill/CSD
 skilled/U
 skillet/SM
 skillful/UY
 skillfulness/M
 skim/MS
 skimmed
@@ -45683,16 +45679,17 @@ timeliness/UM
 timely/UPRT
 timeout/SM
 timepiece/MS
 timer/M
 timescale/S
 timeserver/SM
 timeserving/M
 timeshare/S
+timestamp/SMD
 timetable/DSMG
 timeworn
 timezone
 timid/RYTP
 timidity/M
 timidness/M
 timing/M
 timorous/PY
@@ -45884,17 +45881,17 @@ tonsillectomy/SM
 tonsillitis/M
 tonsorial
 tonsure/DSMG
 tony/RT
 too
 took/A
 tool's
 tool/ADGS
-toolbar/M
+toolbar/SM
 toolbox/MS
 toolkit
 toolmaker/MS
 toot/MDRZGS
 tooter/M
 tooth/MD
 toothache/MS
 toothbrush/MS
@@ -48383,17 +48380,17 @@ weakish
 weakling/SM
 weakness/MS
 weal/MHS
 wealth/M
 wealthiness/M
 wealthy/TRP
 wean/DGS
 weapon/MS
-weaponize
+weaponize/GDS
 weaponless
 weaponry/M
 wear/MRBJSZG
 wearable/U
 wearer/M
 wearied/U
 wearily
 weariness/M
@@ -48745,17 +48742,17 @@ wiggly/TR
 wight/SM
 wiglet/SM
 wigwag/SM
 wigwagged
 wigwagging
 wigwam/SM
 wiki/MS
 wild/MRYSTP
-wildcard
+wildcard/MS
 wildcat/MS
 wildcatted
 wildcatter/MS
 wildcatting
 wildebeest/MS
 wilderness/MS
 wildfire/MS
 wildflower/SM
--- a/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
+++ b/extensions/spellcheck/locales/en-US/hunspell/en-US.dic
@@ -1,9 +1,9 @@
-52346
+52343
 0/nm
 0th/pt
 1/n1
 1st/p
 1th/tc
 2/nm
 2nd/p
 2th/tc
@@ -14494,17 +14494,17 @@ anemically
 anemometer/SM
 anemone/SM
 anent
 anesthesia/M
 anesthesiologist/SM
 anesthesiology/M
 anesthetic/SM
 anesthetist/MS
-anesthetization/M
+anesthetization/SM
 anesthetize/GDS
 aneurysm/SM
 anew
 angel/MS
 angelfish/MS
 angelic
 angelica/M
 angelical/Y
@@ -17860,17 +17860,16 @@ burnable/SM
 burner/M
 burnish/ZGMDRS
 burnisher/M
 burnoose/MS
 burnous/MS
 burnout/MS
 burnt
 burp/MDGS
-burqa/SM
 burr/MDGS
 burrito/MS
 burro/SM
 burrow/SMDRZG
 burrower/M
 bursa/M
 bursae
 bursar/SM
@@ -18232,17 +18231,17 @@ cannot
 canny/UTR
 canoe/MDS
 canoeing
 canoeist/SM
 canola/M
 canon/MS
 canonical/Y
 canonicalization/MS
-canonicalize/DGS
+canonicalize/GDS
 canonization/SM
 canonize/DSG
 canoodle/DSG
 canopy/GDSM
 canst
 cant's
 cant/CZRDGS
 cantabile
@@ -29421,17 +29420,17 @@ highlight/SMDRZG
 highlighter/M
 highness/M
 highroad/MS
 highs
 hightail/DSG
 highway/MS
 highwayman/M
 highwaymen
-hijab/S
+hijab/SM
 hijack/SJZGMDR
 hijacker/M
 hijacking/M
 hike/MZGDRS
 hiker/M
 hiking/M
 hilarious/PY
 hilariousness/M
@@ -32528,17 +32527,16 @@ larkspur/SM
 larva/M
 larvae
 larval
 laryngeal
 larynges
 laryngitis/M
 larynx/M
 lasagna/MS
-lasagne/MS
 lascivious/YP
 lasciviousness/M
 lase/ZGDRS
 laser/M
 lash/MDSGJ
 lashing/M
 lass/MS
 lassie/SM
@@ -32675,16 +32673,17 @@ leaky/PRT
 lean/MDRSTGJP
 leaning/M
 leanness/M
 leap/MDRSZG
 leaper/M
 leapfrog/MS
 leapfrogged
 leapfrogging
+leapt
 learn/AUGDS
 learnedly
 learner/MS
 learning's
 lease/ADSMG
 leaseback/SM
 leasehold/MRSZ
 leaseholder/M
@@ -34123,21 +34122,19 @@ materialist/SM
 materialistic
 materialistically
 materialization/M
 materialize/DSG
 materiel/M
 maternal/Y
 maternity/M
 matey/S
-math/M
 mathematical/Y
 mathematician/SM
 mathematics/M
-maths
 matinee/SM
 mating/M
 matins/M
 matine/SM
 matriarch/M
 matriarchal
 matriarchs
 matriarchy/SM
@@ -35609,17 +35606,17 @@ multistory
 multitask/GS
 multitasking/M
 multitude/SM
 multitudinous
 multivariate
 multiverse/SM
 multivitamin/MS
 multiyear
-mum/SM
+mum
 mumble/MZGDRS
 mumbler/M
 mumbletypeg/M
 mummer/MS
 mummery/M
 mummification/M
 mummify/GNDS
 mummy/SM
@@ -37396,17 +37393,17 @@ osteopath/M
 osteopathic
 osteopaths
 osteopathy/M
 osteoporosis/M
 ostler/S
 ostracism/M
 ostracize/GDS
 ostrich/MS
-other/SP
+other/MSP
 otherwise
 otherworldly
 otiose
 otter/MS
 ottoman/MS
 oubliette/MS
 ouch
 ought
@@ -37481,17 +37478,17 @@ outhouse/SM
 outing/M
 outlaid
 outlandish/PY
 outlandishness/M
 outlast/DSG
 outlaw/SGMD
 outlay/SGM
 outlet/SM
-outlier/SM
+outlier/MS
 outline/MGDS
 outlive/GDS
 outlook/MS
 outlying
 outmaneuver/GDS
 outmatch/GDS
 outmoded
 outnumber/DSG
@@ -44828,17 +44825,16 @@ skibob/S
 skid/MS
 skidded
 skidding
 skidpan/S
 skier/M
 skiff/SM
 skiffle
 skiing/M
-skilfully
 skill's
 skill/CSD
 skilled/U
 skillet/SM
 skillful/UY
 skillfulness/M
 skim/MS
 skimmed
@@ -48552,16 +48548,17 @@ timeliness/UM
 timely/UPRT
 timeout/SM
 timepiece/MS
 timer/M
 timescale/S
 timeserver/SM
 timeserving/M
 timeshare/S
+timestamp/SMD
 timetable/DSMG
 timeworn
 timezone
 timid/RYTP
 timidity/M
 timidness/M
 timing/M
 timorous/PY
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -20,16 +20,30 @@
 #include "nsHashKeys.h"                 // for nsPtrHashKey
 #include "nsISupportsImpl.h"            // for Layer::AddRef, etc
 #include "nsRect.h"                     // for IntRect
 #include "nsTArray.h"                   // for AutoTArray, nsTArray_Impl
 #include "mozilla/Poison.h"
 #include "mozilla/layers/ImageHost.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "TreeTraversal.h"              // for ForEachNode
+#include "LayersLogging.h"
+
+// LayerTreeInvalidation debugging
+#define LTI_DEBUG 0
+
+#if LTI_DEBUG
+#  define LTI_DEEPER(aPrefix) nsPrintfCString("%s  ", aPrefix).get()
+#  define LTI_DUMP(rgn, label) if (!(rgn).IsEmpty()) printf_stderr("%s%p: " label " portion is %s\n", aPrefix, mLayer.get(), Stringify(rgn).c_str());
+#  define LTI_LOG(...) printf_stderr(__VA_ARGS__)
+#else
+#  define LTI_DEEPER(aPrefix) nullptr
+#  define LTI_DUMP(rgn, label)
+#  define LTI_LOG(...)
+#endif
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
 
 struct LayerPropertiesBase;
 UniquePtr<LayerPropertiesBase> CloneLayerTreePropertiesInternal(Layer* aRoot, bool aIsMask = false);
@@ -168,17 +182,18 @@ protected:
 
 public:
   nsIntRegion ComputeDifferences(Layer* aRoot,
                                  NotifySubDocInvalidationFunc aCallback,
                                  bool* aGeometryChanged) override;
 
   void MoveBy(const IntPoint& aOffset) override;
 
-  nsIntRegion ComputeChange(NotifySubDocInvalidationFunc aCallback,
+  nsIntRegion ComputeChange(const char* aPrefix,
+                            NotifySubDocInvalidationFunc aCallback,
                             bool& aGeometryChanged)
   {
     // Bug 1251615: This canary is sometimes hit. We're still not sure why.
     mCanary.Check();
     bool transformChanged = !mTransform.FuzzyEqual(GetTransformForInvalidation(mLayer)) ||
                              mLayer->GetPostXScale() != mPostXScale ||
                              mLayer->GetPostYScale() != mPostYScale;
     const Maybe<ParentLayerIntRect>& otherClip = mLayer->GetLocalClipRect();
@@ -198,43 +213,50 @@ public:
     if ((mMaskLayer ? mMaskLayer->mLayer : nullptr) != otherMask ||
         ancestorMaskChanged ||
         (mUseClipRect != !!otherClip) ||
         mLayer->GetLocalOpacity() != mOpacity ||
         transformChanged)
     {
       aGeometryChanged = true;
       result = OldTransformedBounds();
+      LTI_DUMP(result, "oldtransform");
+      LTI_DUMP(NewTransformedBounds(), "newtransform");
       AddRegion(result, NewTransformedBounds());
 
       // We can't bail out early because we need to update mChildrenChanged.
     }
 
-    AddRegion(result, ComputeChangeInternal(aCallback, aGeometryChanged));
+    nsIntRegion internal = ComputeChangeInternal(aPrefix, aCallback, aGeometryChanged);
+    LTI_DUMP(internal, "internal");
+    AddRegion(result, internal);
+    LTI_DUMP(mLayer->GetInvalidRegion().GetRegion(), "invalid");
     AddTransformedRegion(result, mLayer->GetInvalidRegion().GetRegion(), mTransform);
 
     if (mMaskLayer && otherMask) {
-      AddTransformedRegion(result, mMaskLayer->ComputeChange(aCallback, aGeometryChanged),
-                           mTransform);
+      nsIntRegion mask = mMaskLayer->ComputeChange(aPrefix, aCallback, aGeometryChanged);
+      LTI_DUMP(mask, "mask");
+      AddTransformedRegion(result, mask, mTransform);
     }
 
     for (size_t i = 0;
          i < std::min(mAncestorMaskLayers.Length(), mLayer->GetAncestorMaskLayerCount());
          i++)
     {
-      AddTransformedRegion(result,
-                           mAncestorMaskLayers[i]->ComputeChange(aCallback, aGeometryChanged),
-                           mTransform);
+      nsIntRegion mask = mAncestorMaskLayers[i]->ComputeChange(aPrefix, aCallback, aGeometryChanged);
+      LTI_DUMP(mask, "ancestormask");
+      AddTransformedRegion(result, mask, mTransform);
     }
 
     if (mUseClipRect && otherClip) {
       if (!mClipRect.IsEqualInterior(*otherClip)) {
         aGeometryChanged = true;
         nsIntRegion tmp;
         tmp.Xor(mClipRect.ToUnknownRect(), otherClip->ToUnknownRect());
+        LTI_DUMP(tmp, "clip");
         AddRegion(result, tmp);
       }
     }
 
     mLayer->ClearInvalidRect();
     return result;
   }
 
@@ -250,17 +272,18 @@ public:
                          GetTransformForInvalidation(mLayer));
   }
 
   virtual IntRect OldTransformedBounds()
   {
     return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
   }
 
-  virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+  virtual nsIntRegion ComputeChangeInternal(const char* aPrefix,
+                                            NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     return IntRect();
   }
 
   RefPtr<Layer> mLayer;
   UniquePtr<LayerPropertiesBase> mMaskLayer;
   nsTArray<UniquePtr<LayerPropertiesBase>> mAncestorMaskLayers;
@@ -287,17 +310,18 @@ struct ContainerLayerProperties : public
     }
   }
 
 protected:
   ContainerLayerProperties(const ContainerLayerProperties& a) = delete;
   ContainerLayerProperties& operator=(const ContainerLayerProperties& a) = delete;
 
 public:
-  nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+  nsIntRegion ComputeChangeInternal(const char *aPrefix,
+                                    NotifySubDocInvalidationFunc aCallback,
                                     bool& aGeometryChanged) override
   {
     // Make sure we got our virtual call right
     mSubtypeCanary.Check();
     ContainerLayer* container = mLayer->AsContainerLayer();
     nsIntRegion invalidOfLayer; // Invalid regions of this layer.
     nsIntRegion result;         // Invliad regions for children only.
 
@@ -338,26 +362,29 @@ public:
         if (oldIndexMap.Get(child, &childsOldIndex)) {
           if (childsOldIndex >= i) {
             // Invalidate the old areas of layers that used to be between the
             // current |child| and the previous |child| that was also in the
             // old list mChildren (if any of those children have been reordered
             // rather than removed, we will invalidate their new area when we
             // encounter them in the new list):
             for (uint32_t j = i; j < childsOldIndex; ++j) {
+              LTI_DUMP(mChildren[j]->OldTransformedBounds(), "reordered child");
               AddRegion(result, mChildren[j]->OldTransformedBounds());
               childrenChanged |= true;
             }
             if (childsOldIndex >= mChildren.Length()) {
               MOZ_CRASH("Out of bounds");
             }
             // Invalidate any regions of the child that have changed:
-            nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(aCallback, aGeometryChanged);
+            nsIntRegion region = mChildren[childsOldIndex]->ComputeChange(LTI_DEEPER(aPrefix), aCallback, aGeometryChanged);
             i = childsOldIndex + 1;
             if (!region.IsEmpty()) {
+              LTI_LOG("%s%p: child %p produced %s\n", aPrefix, mLayer.get(),
+                mChildren[childsOldIndex]->mLayer.get(), Stringify(region).c_str());
               AddRegion(result, region);
               childrenChanged |= true;
             }
           } else {
             // We've already seen this child in mChildren (which means it must
             // have been reordered) and invalidated its old area. We need to
             // invalidate its new area too:
             invalidateChildsCurrentArea = true;
@@ -367,30 +394,32 @@ public:
           invalidateChildsCurrentArea = true;
         }
       } else {
         // |child| is new, or was reordered to a higher index
         invalidateChildsCurrentArea = true;
       }
       if (invalidateChildsCurrentArea) {
         aGeometryChanged = true;
+        LTI_DUMP(child->GetLocalVisibleRegion().ToUnknownRegion(), "invalidateChidlsCurrentArea");
         AddTransformedRegion(result, child->GetLocalVisibleRegion().ToUnknownRegion(),
                              GetTransformForInvalidation(child));
         if (aCallback) {
           NotifySubdocumentInvalidation(child, aCallback);
         } else {
           ClearInvalidations(child);
         }
       }
       childrenChanged |= invalidateChildsCurrentArea;
     }
 
     // Process remaining removed children.
     while (i < mChildren.Length()) {
       childrenChanged |= true;
+      LTI_DUMP(mChildren[i]->OldTransformedBounds(), "removed child");
       AddRegion(result, mChildren[i]->OldTransformedBounds());
       i++;
     }
 
     if (aCallback) {
       aCallback(container, result);
     }
 
@@ -399,16 +428,17 @@ public:
     }
 
     if (!mLayer->Extend3DContext()) {
       // |result| contains invalid regions only of children.
       result.Transform(GetTransformForInvalidation(mLayer));
     }
     // else, effective transforms have applied on children.
 
+    LTI_DUMP(invalidOfLayer, "invalidOfLayer");
     result.OrWith(invalidOfLayer);
 
     return result;
   }
 
   IntRect NewTransformedBounds() override
   {
     if (mLayer->Extend3DContext()) {
@@ -449,28 +479,31 @@ struct ColorLayerProperties : public Lay
     , mBounds(aLayer->GetBounds())
   { }
 
 protected:
   ColorLayerProperties(const ColorLayerProperties& a) = delete;
   ColorLayerProperties& operator=(const ColorLayerProperties& a) = delete;
 
 public:
-  nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+  nsIntRegion ComputeChangeInternal(const char* aPrefix,
+                                    NotifySubDocInvalidationFunc aCallback,
                                     bool& aGeometryChanged) override
   {
     ColorLayer* color = static_cast<ColorLayer*>(mLayer.get());
 
     if (mColor != color->GetColor()) {
       aGeometryChanged = true;
+      LTI_DUMP(NewTransformedBounds(), "color");
       return NewTransformedBounds();
     }
 
     nsIntRegion boundsDiff;
     boundsDiff.Xor(mBounds, color->GetBounds());
+    LTI_DUMP(boundsDiff, "colorbounds");
 
     nsIntRegion result;
     AddTransformedRegion(result, boundsDiff, mTransform);
 
     return result;
   }
 
   Color mColor;
@@ -500,17 +533,18 @@ struct ImageLayerProperties : public Lay
     , mIsMask(aIsMask)
   {
     if (mImageHost) {
       mLastProducerID = mImageHost->GetLastProducerID();
       mLastFrameID = mImageHost->GetLastFrameID();
     }
   }
 
-  nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+  nsIntRegion ComputeChangeInternal(const char* aPrefix,
+                                    NotifySubDocInvalidationFunc aCallback,
                                     bool& aGeometryChanged) override
   {
     ImageLayer* imageLayer = static_cast<ImageLayer*>(mLayer.get());
 
     if (!imageLayer->GetLocalVisibleRegion().ToUnknownRegion().IsEqual(mVisibleRegion)) {
       aGeometryChanged = true;
       IntRect result = NewTransformedBounds();
       result = result.Union(OldTransformedBounds());
@@ -534,18 +568,20 @@ struct ImageLayerProperties : public Lay
         IntSize size;
         if (container) {
           size = container->GetCurrentSize();
         }
         if (host) {
           size = host->GetImageSize();
         }
         IntRect rect(0, 0, size.width, size.height);
+        LTI_DUMP(rect, "mask");
         return TransformRect(rect, GetTransformForInvalidation(mLayer));
       }
+      LTI_DUMP(NewTransformedBounds(), "bounds");
       return NewTransformedBounds();
     }
 
     return IntRect();
   }
 
   RefPtr<ImageContainer> mContainer;
   RefPtr<ImageHost> mImageHost;
@@ -561,25 +597,27 @@ struct CanvasLayerProperties : public La
 {
   explicit CanvasLayerProperties(CanvasLayer* aCanvas)
     : LayerPropertiesBase(aCanvas)
     , mImageHost(GetImageHost(aCanvas))
   {
     mFrameID = mImageHost ? mImageHost->GetFrameID() : -1;
   }
 
-  nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+  nsIntRegion ComputeChangeInternal(const char* aPrefix,
+                                    NotifySubDocInvalidationFunc aCallback,
                                     bool& aGeometryChanged) override
   {
     CanvasLayer* canvasLayer = static_cast<CanvasLayer*>(mLayer.get());
 
     ImageHost* host = GetImageHost(canvasLayer);
     if (host && host->GetFrameID() != mFrameID) {
       aGeometryChanged = true;
 
+      LTI_DUMP(NewTransformedBounds(), "frameId");
       return NewTransformedBounds();
     }
 
     return IntRect();
   }
 
   RefPtr<ImageHost> mImageHost;
   int32_t mFrameID;
@@ -658,17 +696,17 @@ LayerPropertiesBase::ComputeDifferences(
                                      aRoot->GetLocalTransform());
     result = result.Union(OldTransformedBounds());
     if (aGeometryChanged != nullptr) {
       *aGeometryChanged = true;
     }
     return result;
   } else {
     bool geometryChanged = (aGeometryChanged != nullptr) ? *aGeometryChanged : false;
-    nsIntRegion invalid = ComputeChange(aCallback, geometryChanged);
+    nsIntRegion invalid = ComputeChange("  ", aCallback, geometryChanged);
     if (aGeometryChanged != nullptr) {
       *aGeometryChanged = geometryChanged;
     }
     return invalid;
   }
 }
 
 void
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/arraybuffer-slice-warn.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// ArrayBuffer.slice should be warned once and only once.
-
-enableLastWarning();
-
-ArrayBuffer.slice(new ArrayBuffer(10), 1);
-var warning = getLastWarning();
-assertEq(warning !== null, true, "warning should be generated");
-assertEq(warning.name, "Warning");
-
-clearLastWarning();
-ArrayBuffer.slice(new ArrayBuffer(10), 1);
-warning = getLastWarning();
-assertEq(warning, null, "warning should not generated for 2nd ocurrence");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -67,17 +67,16 @@ MSG_DEF(JSMSG_REDECLARED_VAR,          2
 MSG_DEF(JSMSG_UNDECLARED_VAR,          1, JSEXN_REFERENCEERR, "assignment to undeclared variable {0}")
 MSG_DEF(JSMSG_GETTER_ONLY,             0, JSEXN_TYPEERR, "setting a property that has only a getter")
 MSG_DEF(JSMSG_OVERWRITING_ACCESSOR,    1, JSEXN_TYPEERR, "can't overwrite accessor property {0}")
 MSG_DEF(JSMSG_UNDEFINED_PROP,          1, JSEXN_REFERENCEERR, "reference to undefined property {0}")
 MSG_DEF(JSMSG_INVALID_MAP_ITERABLE,    1, JSEXN_TYPEERR, "iterable for {0} should have array-like objects")
 MSG_DEF(JSMSG_NESTING_GENERATOR,       0, JSEXN_TYPEERR, "already executing generator")
 MSG_DEF(JSMSG_INCOMPATIBLE_METHOD,     3, JSEXN_TYPEERR, "{0} {1} called on incompatible {2}")
 MSG_DEF(JSMSG_OBJECT_WATCH_DEPRECATED, 0, JSEXN_WARN, "Object.prototype.watch and unwatch are very slow, non-standard, and deprecated; use a getter/setter instead")
-MSG_DEF(JSMSG_ARRAYBUFFER_SLICE_DEPRECATED, 0, JSEXN_WARN, "ArrayBuffer.slice is deprecated; use ArrayBuffer.prototype.slice instead")
 MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,      1, JSEXN_TYPEERR, "bad surrogate character {0}")
 MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE,     1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
 MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR,     1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
 MSG_DEF(JSMSG_BUILTIN_CTOR_NO_NEW,     1, JSEXN_TYPEERR, "calling a builtin {0} constructor without new is forbidden")
 MSG_DEF(JSMSG_BAD_GENERATOR_YIELD,     1, JSEXN_TYPEERR, "yield from closing generator {0}")
 MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE,      0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
 MSG_DEF(JSMSG_UNEXPECTED_TYPE,         2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,         2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -39,17 +39,16 @@
 
 #include "gc/Barrier.h"
 #include "gc/Marking.h"
 #include "gc/Memory.h"
 #include "js/Conversions.h"
 #include "js/MemoryMetrics.h"
 #include "vm/GlobalObject.h"
 #include "vm/Interpreter.h"
-#include "vm/SelfHosting.h"
 #include "vm/SharedArrayObject.h"
 #include "vm/WrapperObject.h"
 #include "wasm/WasmSignalHandlers.h"
 #include "wasm/WasmTypes.h"
 
 #include "jsatominlines.h"
 
 #include "vm/NativeObject-inl.h"
@@ -83,35 +82,16 @@ js::ToClampedIndex(JSContext* cx, Handle
             result = 0;
     } else if (uint32_t(result) > length) {
         result = length;
     }
     *out = uint32_t(result);
     return true;
 }
 
-static bool
-arraybuffer_static_slice(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    if (args.length() < 1) {
-        ReportMissingArg(cx, args.calleev(), 1);
-        return false;
-    }
-
-    if (!GlobalObject::warnOnceAboutArrayBufferSlice(cx, cx->global()))
-        return false;
-
-    FixedInvokeArgs<2> args2(cx);
-    args2[0].set(args.get(1));
-    args2[1].set(args.get(2));
-    return CallSelfHostedFunction(cx, "ArrayBufferSlice", args[0], args2, args.rval());
-}
-
 /*
  * ArrayBufferObject
  *
  * This class holds the underlying raw buffer that the TypedArrayObject classes
  * access.  It can be created explicitly and passed to a TypedArrayObject, or
  * can be created implicitly by constructing a TypedArrayObject with a size.
  */
 
@@ -137,17 +117,16 @@ static const ClassOps ArrayBufferObjectC
     nullptr,        /* call        */
     nullptr,        /* hasInstance */
     nullptr,        /* construct   */
     ArrayBufferObject::trace,
 };
 
 static const JSFunctionSpec static_functions[] = {
     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
-    JS_FN("slice", arraybuffer_static_slice, 3, 0),
     JS_FS_END
 };
 
 static const JSPropertySpec static_properties[] = {
     JS_SELF_HOSTED_SYM_GET(species, "ArrayBufferSpecies", 0),
     JS_PS_END
 };
 
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -125,17 +125,16 @@ class GlobalObject : public NativeObject
      * we won't expose GlobalObject, so just assert that the two values are
      * synchronized.
      */
     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
                   "global object slot counts are inconsistent");
 
     enum WarnOnceFlag : int32_t {
         WARN_WATCH_DEPRECATED                   = 1 << 0,
-        WARN_ARRAYBUFFER_SLICE_DEPRECATED       = 1 << 1,
     };
 
     // Emit the specified warning if the given slot in |obj|'s global isn't
     // true, then set the slot to true.  Thus calling this method warns once
     // for each global object it's called on, and every other call does
     // nothing.
     static bool
     warnOnceAbout(JSContext* cx, HandleObject obj, WarnOnceFlag flag, unsigned errorNumber);
@@ -733,22 +732,16 @@ class GlobalObject : public NativeObject
     // in which |obj| was created, if no prior warning was given.
     static bool warnOnceAboutWatch(JSContext* cx, HandleObject obj) {
         // Temporarily disabled until we've provided a watch/unwatch workaround for
         // debuggers like Firebug (bug 934669).
         //return warnOnceAbout(cx, obj, WARN_WATCH_DEPRECATED, JSMSG_OBJECT_WATCH_DEPRECATED);
         return true;
     }
 
-    // Warn about use of the deprecated (static) ArrayBuffer.slice method.
-    static bool warnOnceAboutArrayBufferSlice(JSContext* cx, HandleObject obj) {
-        return warnOnceAbout(cx, obj, WARN_ARRAYBUFFER_SLICE_DEPRECATED,
-                             JSMSG_ARRAYBUFFER_SLICE_DEPRECATED);
-    }
-
     static bool getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global,
                                 MutableHandleObject eval);
 
     // Infallibly test whether the given value is the eval function for this global.
     bool valueIsEval(const Value& val);
 
     // Implemented in jsiter.cpp.
     static bool initIteratorProto(JSContext* cx, Handle<GlobalObject*> global);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.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/. */
 
 /* High level class and public functions implementation. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/Likely.h"
+#include "mozilla/Unused.h"
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "jsfriendapi.h"
 #include "nsJSEnvironment.h"
 #include "nsThreadUtils.h"
 #include "nsDOMJSUtils.h"
 
@@ -336,22 +337,22 @@ xpc_MarkInCCGeneration(nsISupports* aVar
           weak->RemovePurple();
         }
     }
 }
 
 void
 xpc_TryUnmarkWrappedGrayObject(nsISupports* aWrappedJS)
 {
-#ifdef DEBUG
+    // QIing to nsIXPConnectWrappedJSUnmarkGray may have side effects!
     nsCOMPtr<nsIXPConnectWrappedJSUnmarkGray> wjsug =
       do_QueryInterface(aWrappedJS);
+    Unused << wjsug;
     MOZ_ASSERT(!wjsug, "One should never be able to QI to "
                        "nsIXPConnectWrappedJSUnmarkGray successfully!");
-#endif
 }
 
 /***************************************************************************/
 /***************************************************************************/
 // nsIXPConnect interface methods...
 
 template<typename T>
 static inline T UnexpectedFailure(T rv)
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -249,17 +249,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   gPrototypeProperties['Promise'] =
     ["constructor", "catch", "then", Symbol.toStringTag];
   gConstructorProperties['Promise'] =
     constructorProps(["resolve", "reject", "all", "race", Symbol.species]);
 
   gPrototypeProperties['ArrayBuffer'] =
     ["constructor", "byteLength", "slice", Symbol.toStringTag];
   gConstructorProperties['ArrayBuffer'] =
-    constructorProps(["isView", "slice", Symbol.species]);
+    constructorProps(["isView", Symbol.species]);
 
   if (!isReleaseOrBeta) {
     gPrototypeProperties['SharedArrayBuffer'] = ["constructor", "slice", "byteLength", Symbol.toStringTag];
     gConstructorProperties['SharedArrayBuffer'] = constructorProps([Symbol.species]);
   } else {
     is(typeof SharedArrayBuffer, "undefined", "Enable tests!");
   }
 
@@ -923,21 +923,16 @@ for (var prop of props) {
 
       var t = new iwin[c](12);
       is(t.byteLength, 12, `${c} byteLength is correct`);
 
       is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
       is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
       is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
 
-      // SharedArrayBuffer does not have static slice method
-      if (c === 'ArrayBuffer') {
-        is(ArrayBuffer.slice(t, 4).byteLength, 8, `${c}.slice (deprecated) works`);
-      }
-
       var i32Array = new Int32Array(t);
       // i32Array is going to be created in the buffer's target compartment,
       // but usually this is unobservable, because the proto is set to
       // the current compartment's prototype.
       // However Xrays ignore the object's proto and claim its proto is
       // the default proto for that class in the relevant compartment,
       // so see through this proto hack.
       todo_is(Object.getPrototypeOf(i32Array), Int32Array.prototype, "Int32Array has correct proto");
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1698,16 +1698,32 @@ DOMXrayTraits::defineProperty(JSContext*
     JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
     return XrayDefineProperty(cx, wrapper, obj, id, desc, result, defined);
 }
 
 bool
 DOMXrayTraits::enumerateNames(JSContext* cx, HandleObject wrapper, unsigned flags,
                               AutoIdVector& props)
 {
+    // Put the indexed properties for a window first.
+    nsGlobalWindow* win = AsWindow(cx, wrapper);
+    if (win) {
+        uint32_t length = win->Length();
+        if (!props.reserve(props.length() + length)) {
+            return false;
+        }
+        JS::RootedId indexId(cx);
+        for (uint32_t i = 0; i < length; ++i) {
+            if (!JS_IndexToId(cx, i, &indexId)) {
+                return false;
+            }
+            props.infallibleAppend(indexId);
+        }
+    }
+
     JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
     return XrayOwnPropertyKeys(cx, wrapper, obj, flags, props);
 }
 
 bool
 DOMXrayTraits::call(JSContext* cx, HandleObject wrapper,
                     const JS::CallArgs& args, const js::Wrapper& baseInstance)
 {
--- a/mozglue/android/APKOpen.cpp
+++ b/mozglue/android/APKOpen.cpp
@@ -439,17 +439,17 @@ FreeArgv(char** argv, int argc)
 {
   for (int ix=0; ix < argc; ix++) {
     // String was allocated with strndup, so need to use free to deallocate.
     free(argv[ix]);
   }
   delete[](argv);
 }
 
-typedef void (*GeckoStart_t)(JNIEnv*, char**, int, const nsXREAppData*);
+typedef void (*GeckoStart_t)(JNIEnv*, char**, int, const StaticXREAppData&);
 typedef int GeckoProcessType;
 
 extern "C" APKOPEN_EXPORT void MOZ_JNICALL
 Java_org_mozilla_gecko_mozglue_GeckoLoader_nativeRun(JNIEnv *jenv, jclass jc, jobjectArray jargs, int crashFd, int ipcFd)
 {
   int argc = 0;
   char** argv = CreateArgvFromObjectArray(jenv, jargs, &argc);
 
@@ -458,17 +458,17 @@ Java_org_mozilla_gecko_mozglue_GeckoLoad
     xul_dlsym("GeckoStart", &GeckoStart);
 
     if (GeckoStart == nullptr) {
       FreeArgv(argv, argc);
       return;
     }
 
     ElfLoader::Singleton.ExpectShutdown(false);
-    GeckoStart(jenv, argv, argc, &sAppData);
+    GeckoStart(jenv, argv, argc, sAppData);
     ElfLoader::Singleton.ExpectShutdown(true);
   } else {
     void (*fXRE_SetAndroidChildFds)(int, int);
     xul_dlsym("XRE_SetAndroidChildFds", &fXRE_SetAndroidChildFds);
 
     void (*fXRE_SetProcessType)(char*);
     xul_dlsym("XRE_SetProcessType", &fXRE_SetProcessType);
 
--- a/testing/mozbase/mozdevice/mozdevice/droid.py
+++ b/testing/mozbase/mozdevice/mozdevice/droid.py
@@ -158,18 +158,19 @@ class DroidMixin(object):
 class DroidADB(DeviceManagerADB, DroidMixin):
 
     _stopApplicationNeedsRoot = False
 
     def getTopActivity(self):
         package = None
         data = None
         try:
+            # Increased timeout to 60 seconds after intermittent timeouts at 30.
             data = self.shellCheckOutput(
-                ["dumpsys", "window", "windows"], timeout=self.short_timeout)
+                ["dumpsys", "window", "windows"], timeout=60)
         except:
             # dumpsys seems to intermittently fail (seen on 4.3 emulator), producing
             # no output.
             return ""
         # "dumpsys window windows" produces many lines of input. The top/foreground
         # activity is indicated by something like:
         #   mFocusedApp=AppWindowToken{483e6db0 token=HistoryRecord{484dcad8 com.mozilla.SUTAgentAndroid/.SUTAgentAndroid}} # noqa
         # or, on other devices:
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/cross-origin-objects.html
@@ -65,20 +65,25 @@ addTest(function() {
 }, "Basic sanity-checking");
 
 /*
  * Whitelist behavior.
  *
  * Also tests for [[GetOwnProperty]] and [[HasOwnProperty]] behavior.
  */
 
+var whitelistedWindowIndices = ['0', '1'];
 var whitelistedWindowPropNames = ['location', 'postMessage', 'window', 'frames', 'self', 'top', 'parent',
                                   'opener', 'closed', 'close', 'blur', 'focus', 'length'];
-var whitelistedSymbols = [Symbol.isConcatSpreadable, Symbol.toStringTag,
-                          Symbol.hasInstance];
+whitelistedWindowPropNames = whitelistedWindowPropNames.concat(whitelistedWindowIndices);
+whitelistedWindowPropNames.sort();
+var whitelistedLocationPropNames = ['href', 'replace'];
+whitelistedLocationPropNames.sort();
+var whitelistedSymbols = [Symbol.toStringTag, Symbol.hasInstance,
+                          Symbol.isConcatSpreadable];
 var whitelistedWindowProps = whitelistedWindowPropNames.concat(whitelistedSymbols);
 
 addTest(function() {
   for (var prop in window) {
     if (whitelistedWindowProps.indexOf(prop) != -1) {
       C[prop]; // Shouldn't throw.
       Object.getOwnPropertyDescriptor(C, prop); // Shouldn't throw.
       assert_true(Object.prototype.hasOwnProperty.call(C, prop), "hasOwnProperty for " + String(prop));
@@ -255,44 +260,52 @@ addTest(function() {
     assert_unreached("Shouldn't have been able to enumerate " + prop + " on cross-origin Location");
 }, "[[Enumerate]] should return an empty iterator");
 
 /*
  * [[OwnPropertyKeys]]
  */
 
 addTest(function() {
-  assert_array_equals(whitelistedWindowPropNames.sort(),
-                      Object.getOwnPropertyNames(C).sort(),
+  assert_array_equals(Object.getOwnPropertyNames(C).sort(),
+                      whitelistedWindowPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Window");
-  assert_array_equals(Object.getOwnPropertyNames(C.location).sort(), ['href', 'replace'],
+  assert_array_equals(Object.getOwnPropertyNames(C.location).sort(),
+                      whitelistedLocationPropNames,
                       "Object.getOwnPropertyNames() gives the right answer for cross-origin Location");
 }, "[[OwnPropertyKeys]] should return all properties from cross-origin objects");
 
-// Compare two arrays that need to have the same elements but may be in
-// different orders.  The problem is that there's no good way to sort arrays
-// containing Symbols.
-function assert_arrays_equal_up_to_order(arr1, arr2, desc) {
-  for (let item of arr1) {
-    assert_in_array(item, arr2, desc);
-  }
-
-  for (let item of arr2) {
-    assert_in_array(item, arr1, desc);
-  }
-}
+addTest(function() {
+  assert_array_equals(Object.getOwnPropertySymbols(C), whitelistedSymbols,
+    "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window");
+  assert_array_equals(Object.getOwnPropertySymbols(C.location),
+                      whitelistedSymbols,
+    "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location");
+}, "[[OwnPropertyKeys]] should return the right symbol-named properties for cross-origin objects");
 
 addTest(function() {
-  assert_arrays_equal_up_to_order(Object.getOwnPropertySymbols(C),
-                                  whitelistedSymbols,
-    "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Window");
-  assert_arrays_equal_up_to_order(Object.getOwnPropertySymbols(C.location),
-                                  whitelistedSymbols,
-    "Object.getOwnPropertySymbols() should return the three symbol-named properties that are exposed on a cross-origin Location");
-}, "[[OwnPropertyKeys]] should return the right symbol-named properties for cross-origin objects");
+  var allWindowProps = Reflect.ownKeys(C);
+  indexedWindowProps = allWindowProps.slice(0, whitelistedWindowIndices.length);
+  stringWindowProps = allWindowProps.slice(0, -1 * whitelistedSymbols.length);
+  symbolWindowProps = allWindowProps.slice(-1 * whitelistedSymbols.length);
+  assert_array_equals(indexedWindowProps, whitelistedWindowIndices,
+                      "Reflect.ownKeys should start with the indices exposed on the cross-origin window.");
+  assert_array_equals(stringWindowProps.sort(), whitelistedWindowPropNames,
+                      "Reflect.ownKeys should continue with the cross-origin window properties for a cross-origin Window.");
+  assert_array_equals(symbolWindowProps, whitelistedSymbols,
+                      "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Window.");
+
+  var allLocationProps = Reflect.ownKeys(C.location);
+  stringLocationProps = allLocationProps.slice(0, -1 * whitelistedSymbols.length);
+  symbolLocationProps = allLocationProps.slice(-1 * whitelistedSymbols.length);
+  assert_array_equals(stringLocationProps.sort(), whitelistedLocationPropNames,
+                      "Reflect.ownKeys should start with the cross-origin window properties for a cross-origin Location.")
+  assert_array_equals(symbolLocationProps, whitelistedSymbols,
+                      "Reflect.ownKeys should end with the cross-origin symbols for a cross-origin Location.")
+}, "[[OwnPropertyKeys]] should place the symbols after the property names after the subframe indices");
 
 addTest(function() {
   assert_true(B.eval('parent.C') === C, "A and B observe the same identity for C's Window");
   assert_true(B.eval('parent.C.location') === C.location, "A and B observe the same identity for C's Location");
 }, "A and B jointly observe the same identity for cross-origin Window and Location");
 
 function checkFunction(f, proto) {
   var name = f.name || '<missing name>';
--- a/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
+++ b/testing/web-platform/tests/html/browsers/origin/cross-origin-objects/frame.html
@@ -30,10 +30,13 @@
       if (locationReferences[i] != parent[i].location)
         throw new Error("Location references don't match for " + i + " after document.domain");
     }
     return true;
   }
 </script>
 </head>
 <body>
+  <!-- Two subframes to give us some indexed properties -->
+  <iframe></iframe>
+  <iframe></iframe>
 </body>
 </html>
--- a/toolkit/crashreporter/client/crashreporter.cpp
+++ b/toolkit/crashreporter/client/crashreporter.cpp
@@ -508,27 +508,31 @@ void RewriteStrings(StringTable& queryPa
 
 bool CheckEndOfLifed(string version)
 {
   string reportPath =
     gSettingsPath + UI_DIR_SEPARATOR + "EndOfLife" + version;
   return UIFileExists(reportPath);
 }
 
+#ifndef RELEASE_OR_BETA
+
 static string
 GetMinidumpAnalyzerPath()
 {
   string path = gArgv[0];
   size_t pos = path.rfind(UI_CRASH_REPORTER_FILENAME BIN_SUFFIX);
   path.erase(pos);
   path.append(UI_MINIDUMP_ANALYZER_FILENAME BIN_SUFFIX);
 
   return path;
 }
 
+#endif
+
 int main(int argc, char** argv)
 {
   gArgc = argc;
   gArgv = argv;
 
   if (!ReadConfig()) {
     UIError("Couldn't read configuration.");
     return 0;
@@ -540,18 +544,21 @@ int main(int argc, char** argv)
   if (argc > 1) {
     gReporterDumpFile = argv[1];
   }
 
   if (gReporterDumpFile.empty()) {
     // no dump file specified, run the default UI
     UIShowDefaultUI();
   } else {
-    // start by running minidump analyzer
+#ifndef RELEASE_OR_BETA
+    // start by running minidump analyzer, this is currently enabled only in
+    // nightly and aurora
     UIRunMinidumpAnalyzer(GetMinidumpAnalyzerPath(), gReporterDumpFile);
+#endif
 
     // go ahead with the crash reporter
     gExtraFile = GetAdditionalFilename(gReporterDumpFile, kExtraDataExtension);
     if (gExtraFile.empty()) {
       UIError(gStrings[ST_ERROR_BADARGUMENTS]);
       return 0;
     }
 
--- a/toolkit/crashreporter/moz.build
+++ b/toolkit/crashreporter/moz.build
@@ -46,24 +46,20 @@ elif CONFIG['OS_ARCH'] == 'SunOS':
         'google-breakpad/src/common/solaris',
         'google-breakpad/src/client',
         'google-breakpad/src/client/solaris/handler',
         'google-breakpad/src/tools/solaris/dump_syms',
     ]
 
 DIRS += [
     'client',
+    'jsoncpp/src/lib_json',
+    'minidump-analyzer',
 ]
 
-if CONFIG['NIGHTLY_BUILD']:
-    DIRS += [
-        'jsoncpp/src/lib_json',
-        'minidump-analyzer',
-    ]
-
 if CONFIG['MOZ_CRASHREPORTER_INJECTOR']:
     DIRS += ['injector']
     UNIFIED_SOURCES += [
         'InjectCrashReporter.cpp',
         'LoadLibraryRemote.cpp',
     ]
 
 TEST_DIRS += ['test']
--- a/toolkit/crashreporter/test/CrashTestUtils.jsm
+++ b/toolkit/crashreporter/test/CrashTestUtils.jsm
@@ -1,17 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 this.EXPORTED_SYMBOLS = ["CrashTestUtils"];
 
 this.CrashTestUtils = {
   // These will be defined using ctypes APIs below.
   crash: null,
-  lockDir: null,
   dumpHasStream: null,
   dumpHasInstructionPointerMemory: null,
 
   // Constants for crash()
   // Keep these in sync with nsTestCrasher.cpp!
   CRASH_INVALID_POINTER_DEREF: 0,
   CRASH_PURE_VIRTUAL_CALL:     1,
   CRASH_RUNTIMEABORT:          2,
@@ -36,22 +35,16 @@ var lib = ctypes.open(file.path);
 CrashTestUtils.crash = lib.declare("Crash",
                                    ctypes.default_abi,
                                    ctypes.void_t,
                                    ctypes.int16_t);
 CrashTestUtils.saveAppMemory = lib.declare("SaveAppMemory",
                                            ctypes.default_abi,
                                            ctypes.uint64_t);
 
-CrashTestUtils.lockDir = lib.declare("LockDir",
-                                     ctypes.default_abi,
-                                     ctypes.voidptr_t,   // nsILocalFile*
-                                     ctypes.voidptr_t);  // nsISupports*
-
-
 try {
   CrashTestUtils.TryOverrideExceptionHandler = lib.declare("TryOverrideExceptionHandler",
                                                            ctypes.default_abi,
                                                            ctypes.void_t);
 }
 catch (ex) {}
 
 CrashTestUtils.dumpHasStream = lib.declare("DumpHasStream",
--- a/toolkit/crashreporter/test/nsTestCrasher.cpp
+++ b/toolkit/crashreporter/test/nsTestCrasher.cpp
@@ -1,14 +1,13 @@
 #include "mozilla/Assertions.h"
 
 #include <stdio.h>
 
 #include "nscore.h"
-#include "nsXULAppAPI.h"
 #include "nsExceptionHandler.h"
 #include "mozilla/Unused.h"
 
 /*
  * This pure virtual call example is from MSDN
  */
 class A;
 
@@ -38,17 +37,16 @@ void PureVirtualCall()
   // generates a pure virtual function call
   B b;
   b.use(); // make sure b's actually used
 }
 
 // Keep these in sync with CrashTestUtils.jsm!
 const int16_t CRASH_INVALID_POINTER_DEREF = 0;
 const int16_t CRASH_PURE_VIRTUAL_CALL     = 1;
-const int16_t CRASH_RUNTIMEABORT          = 2;
 const int16_t CRASH_OOM                   = 3;
 const int16_t CRASH_MOZ_CRASH             = 4;
 const int16_t CRASH_ABORT                 = 5;
 
 extern "C" NS_EXPORT
 void Crash(int16_t how)
 {
   switch (how) {
@@ -58,20 +56,16 @@ void Crash(int16_t how)
     // not reached
     break;
   }
   case CRASH_PURE_VIRTUAL_CALL: {
     PureVirtualCall();
     // not reached
     break;
   }
-  case CRASH_RUNTIMEABORT: {
-    NS_RUNTIMEABORT("Intentional crash");
-    break;
-  }
   case CRASH_OOM: {
     mozilla::Unused << moz_xmalloc((size_t) -1);
     mozilla::Unused << moz_xmalloc((size_t) -1);
     mozilla::Unused << moz_xmalloc((size_t) -1);
     break;
   }
   case CRASH_MOZ_CRASH: {
     MOZ_CRASH();
@@ -81,24 +75,16 @@ void Crash(int16_t how)
     abort();
     break;
   }
   default:
     break;
   }
 }
 
-extern "C" NS_EXPORT
-nsISupports* LockDir(nsIFile *directory)
-{
-  nsISupports* lockfile = nullptr;
-  XRE_LockProfileDirectory(directory, &lockfile);
-  return lockfile;
-}
-
 char testData[32];
 
 extern "C" NS_EXPORT
 uint64_t SaveAppMemory()
 {
   for (size_t i=0; i<sizeof(testData); i++)
     testData[i] = i;
 
deleted file mode 100644
--- a/toolkit/crashreporter/test/unit/test_crash_runtimeabort.js
+++ /dev/null
@@ -1,21 +0,0 @@
-function run_test()
-{
-  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
-    dump("INFO | test_crash_runtimeabort.js | Can't test crashreporter in a non-libxul build.\n");
-    return;
-  }
-
-  // Try crashing with a runtime abort
-  do_crash(function() {
-             crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
-             crashReporter.annotateCrashReport("TestKey", "TestValue");
-           },
-           function(mdump, extra) {
-             do_check_eq(extra.TestKey, "TestValue");
-             do_check_true(/xpcom_runtime_abort/.test(extra.Notes));
-             do_check_false("OOMAllocationSize" in extra);
-             do_check_true(/Intentional crash/.test(extra.AbortMessage));
-           },
-          // process will exit with a zero exit status
-          true);
-}
deleted file mode 100644
--- a/toolkit/crashreporter/test/unit/test_crashreporter_crash_profile_lock.js
+++ /dev/null
@@ -1,26 +0,0 @@
-function run_test()
-{
-  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
-    dump("INFO | test_crashreporter.js | Can't test crashreporter in a non-libxul build.\n");
-    return;
-  }
-
-  // lock a profile directory, crash, and ensure that
-  // the profile lock signal handler doesn't interfere with
-  // writing a minidump
-  do_crash(function() {
-             let env = Components.classes["@mozilla.org/process/environment;1"]
-               .getService(Components.interfaces.nsIEnvironment);
-             // the python harness sets this in the environment for us
-             let profd = env.get("XPCSHELL_TEST_PROFILE_DIR");
-             let dir = Components.classes["@mozilla.org/file/local;1"]
-               .createInstance(Components.interfaces.nsILocalFile);
-             dir.initWithPath(profd);
-             CrashTestUtils.lockDir(dir);
-             // when we crash, the lock file should be cleaned up
-           },
-           function(mdump, extra) {
-             // if we got here, we have a minidump, so that's all we wanted
-             do_check_true(true);
-           });
-}
--- a/toolkit/crashreporter/test/unit/test_event_files.js
+++ b/toolkit/crashreporter/test/unit/test_event_files.js
@@ -25,17 +25,17 @@ add_task(function* test_main_process_cra
   let basename;
   let deferred = Promise.defer();
   do_crash(
     function() {
       // TelemetrySession setup will trigger the session annotation
       let scope = {};
       Components.utils.import("resource://gre/modules/TelemetryController.jsm", scope);
       scope.TelemetryController.testSetup();
-      crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
+      crashType = CrashTestUtils.CRASH_MOZ_CRASH;
       crashReporter.annotateCrashReport("ShutdownProgress", "event-test");
     },
     (minidump, extra) => {
       basename = minidump.leafName;
       cm._eventsDirs = [getEventDir()];
       cm.aggregateEventsFiles().then(deferred.resolve, deferred.reject);
     },
     true);
--- a/toolkit/crashreporter/test/unit/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit/xpcshell.ini
@@ -3,33 +3,31 @@ head = head_crashreporter.js
 tail =
 skip-if = toolkit == 'android'
 support-files =
   crasher_subprocess_head.js
   crasher_subprocess_tail.js
 
 [test_crash_moz_crash.js]
 [test_crash_purevirtual.js]
-[test_crash_runtimeabort.js]
 [test_crash_after_js_oom_reported.js]
 [test_crash_after_js_oom_recovered.js]
 [test_crash_after_js_oom_reported_2.js]
 [test_crash_after_js_large_allocation_failure.js]
 [test_crash_after_js_large_allocation_failure_reporting.js]
 [test_crash_oom.js]
 [test_oom_annotation_windows.js]
 skip-if = os != 'win'
 
 [test_crash_abort.js]
 skip-if = os == 'win'
 
 [test_crash_with_memory_report.js]
 [test_crashreporter.js]
 [test_crashreporter_crash.js]
-[test_crashreporter_crash_profile_lock.js]
 [test_override_exception_handler.js]
 skip-if = os != 'win'
 
 [test_crashreporter_appmem.js]
 # we need to skip this due to bug 838613
 skip-if = (os != 'win' && os != 'linux') || (os=='linux' && bits==32)
 
 [test_crash_AsyncShutdown.js]
--- a/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
@@ -4,17 +4,17 @@ function run_test()
 {
   if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
     dump("INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n");
     return;
   }
 
   // Try crashing with a runtime abort
   do_content_crash(function() {
-                     crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
+                     crashType = CrashTestUtils.CRASH_MOZ_CRASH;
                      crashReporter.annotateCrashReport("TestKey", "TestValue");
                      crashReporter.appendAppNotesToCrashReport("!!!foo!!!");
                    },
                    function(mdump, extra) {
                      do_check_eq(extra.TestKey, "TestValue");
                      do_check_true('StartupTime' in extra);
                      do_check_true('ProcessType' in extra);
                      do_check_neq(extra.Notes.indexOf("!!!foo!!!"), -1);
--- a/toolkit/xre/CreateAppData.cpp
+++ b/toolkit/xre/CreateAppData.cpp
@@ -2,164 +2,76 @@
 /* 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 "nsXULAppAPI.h"
 #include "nsINIParser.h"
 #include "nsIFile.h"
 #include "nsAutoPtr.h"
-#include "mozilla/AppData.h"
+#include "mozilla/XREAppData.h"
 
 using namespace mozilla;
 
-nsresult
-XRE_CreateAppData(nsIFile* aINIFile, nsXREAppData **aAppData)
+static void
+ReadString(nsINIParser &parser, const char* section,
+           const char* key, XREAppData::CharPtr& result)
 {
-  NS_ENSURE_ARG(aINIFile && aAppData);
-
-  nsAutoPtr<ScopedAppData> data(new ScopedAppData());
-  if (!data)
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  nsresult rv = XRE_ParseAppData(aINIFile, data);
-  if (NS_FAILED(rv))
-    return rv;
-
-  if (!data->directory) {
-    nsCOMPtr<nsIFile> appDir;
-    rv = aINIFile->GetParent(getter_AddRefs(appDir));
-    if (NS_FAILED(rv))
-      return rv;
-
-    appDir.forget(&data->directory);
-  }
-
-  *aAppData = data.forget();
-  return NS_OK;
-}
-
-struct ReadString {
-  const char *section;
-  const char *key;
-  const char **buffer;
-};
-
-static void
-ReadStrings(nsINIParser &parser, const ReadString *reads)
-{
-  nsresult rv;
   nsCString str;
-
-  while (reads->section) {
-    rv = parser.GetString(reads->section, reads->key, str);
-    if (NS_SUCCEEDED(rv)) {
-      SetAllocatedString(*reads->buffer, str);
-    }
-
-    ++reads;
+  nsresult rv = parser.GetString(section, key, str);
+  if (NS_SUCCEEDED(rv)) {
+    result = str.get();
   }
 }
 
 struct ReadFlag {
   const char *section;
   const char *key;
   uint32_t flag;
 };
 
 static void
-ReadFlags(nsINIParser &parser, const ReadFlag *reads, uint32_t *buffer)
+ReadFlag(nsINIParser &parser, const char* section,
+         const char* key, uint32_t flag, uint32_t& result)
 {
-  nsresult rv;
   char buf[6]; // large enough to hold "false"
-
-  while (reads->section) {
-    rv = parser.GetString(reads->section, reads->key, buf, sizeof(buf));
-    if (NS_SUCCEEDED(rv) || rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
-      if (buf[0] == '1' || buf[0] == 't' || buf[0] == 'T') {
-        *buffer |= reads->flag;
-      }
-      if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
-        *buffer &= ~reads->flag;
-      }
+  nsresult rv = parser.GetString(section, key, buf, sizeof(buf));
+  if (NS_SUCCEEDED(rv) || rv == NS_ERROR_LOSS_OF_SIGNIFICANT_DATA) {
+    if (buf[0] == '1' || buf[0] == 't' || buf[0] == 'T') {
+      result |= flag;
     }
-
-    ++reads;
+    if (buf[0] == '0' || buf[0] == 'f' || buf[0] == 'F') {
+      result &= ~flag;
+    }
   }
 }
 
 nsresult
-XRE_ParseAppData(nsIFile* aINIFile, nsXREAppData *aAppData)
+XRE_ParseAppData(nsIFile* aINIFile, XREAppData& aAppData)
 {
-  NS_ENSURE_ARG(aINIFile && aAppData);
+  NS_ENSURE_ARG(aINIFile);
 
   nsresult rv;
 
   nsINIParser parser;
   rv = parser.Init(aINIFile);
   if (NS_FAILED(rv))
     return rv;
 
-  nsCString str;
-
-  ReadString strings[] = {
-    { "App", "Vendor",        &aAppData->vendor },
-    { "App", "Name",          &aAppData->name },
-    { "App", "RemotingName",  &aAppData->remotingName },
-    { "App", "Version",       &aAppData->version },
-    { "App", "BuildID",       &aAppData->buildID },
-    { "App", "ID",            &aAppData->ID },
-    { "App", "Copyright",     &aAppData->copyright },
-    { "App", "Profile",       &aAppData->profile },
-    { nullptr }
-  };
-  ReadStrings(parser, strings);
-
-  ReadFlag flags[] = {
-    { "XRE", "EnableProfileMigrator", NS_XRE_ENABLE_PROFILE_MIGRATOR },
-    { nullptr }
-  };
-  ReadFlags(parser, flags, &aAppData->flags);
-
-  if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
-    ReadString strings2[] = {
-      { "Gecko", "MinVersion", &aAppData->minVersion },
-      { "Gecko", "MaxVersion", &aAppData->maxVersion },
-      { nullptr }
-    };
-    ReadStrings(parser, strings2);
-  }
-
-  if (aAppData->size > offsetof(nsXREAppData, crashReporterURL)) {
-    ReadString strings3[] = {
-      { "Crash Reporter", "ServerURL", &aAppData->crashReporterURL },
-      { nullptr }
-    };
-    ReadStrings(parser, strings3);
-    ReadFlag flags2[] = {
-      { "Crash Reporter", "Enabled", NS_XRE_ENABLE_CRASH_REPORTER },
-      { nullptr }
-    };
-    ReadFlags(parser, flags2, &aAppData->flags);
-  }
-
-  if (aAppData->size > offsetof(nsXREAppData, UAName)) {
-    ReadString strings4[] = {
-      { "App", "UAName",    &aAppData->UAName },
-      { nullptr }
-    };
-    ReadStrings(parser, strings4);
-  }
+  ReadString(parser, "App", "Vendor", aAppData.vendor);
+  ReadString(parser, "App", "Name", aAppData.name),
+  ReadString(parser, "App", "RemotingName", aAppData.remotingName);
+  ReadString(parser, "App", "Version", aAppData.version);
+  ReadString(parser, "App", "BuildID", aAppData.buildID);
+  ReadString(parser, "App", "ID", aAppData.ID);
+  ReadString(parser, "App", "Copyright", aAppData.copyright);
+  ReadString(parser, "App", "Profile", aAppData.profile);
+  ReadString(parser, "Gecko", "MinVersion", aAppData.minVersion);
+  ReadString(parser, "Gecko", "MaxVersion", aAppData.maxVersion);
+  ReadString(parser, "Crash Reporter", "ServerURL", aAppData.crashReporterURL);
+  ReadString(parser, "App", "UAName", aAppData.UAName);
+  ReadFlag(parser, "XRE", "EnableProfileMigrator",
+           NS_XRE_ENABLE_PROFILE_MIGRATOR, aAppData.flags);
+  ReadFlag(parser, "Crash Reporter", "Enabled",
+           NS_XRE_ENABLE_CRASH_REPORTER, aAppData.flags);
 
   return NS_OK;
 }
-
-void
-XRE_FreeAppData(nsXREAppData *aAppData)
-{
-  if (!aAppData) {
-    NS_ERROR("Invalid arg");
-    return;
-  }
-
-  ScopedAppData* sad = static_cast<ScopedAppData*>(aAppData);
-  delete sad;
-}
--- a/toolkit/xre/ProfileReset.cpp
+++ b/toolkit/xre/ProfileReset.cpp
@@ -10,24 +10,27 @@
 #include "nsIWindowWatcher.h"
 
 #include "ProfileReset.h"
 
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsPIDOMWindow.h"
 #include "nsPrintfCString.h"
+#include "nsString.h"
 #include "nsToolkitCompsCID.h"
 #include "nsXPCOMCIDInternal.h"
-#include "nsXREAppData.h"
+#include "mozilla/XREAppData.h"
 
 #include "mozilla/Services.h"
 #include "prtime.h"
 
-extern const nsXREAppData* gAppData;
+using namespace mozilla;
+
+extern const XREAppData* gAppData;
 
 static const char kProfileProperties[] =
   "chrome://mozapps/locale/profile/profileSelection.properties";
 
 /**
  * Creates a new profile with a timestamp in the name to use for profile reset.
  */
 nsresult
--- a/toolkit/xre/nsAndroidStartup.cpp
+++ b/toolkit/xre/nsAndroidStartup.cpp
@@ -16,18 +16,20 @@
 #include "nsString.h"
 #include "nsIFile.h"
 #include "nsAppRunner.h"
 #include "APKOpen.h"
 #include "nsExceptionHandler.h"
 
 #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, MOZ_APP_NAME, args)
 
+using namespace mozilla;
+
 extern "C" NS_EXPORT void
-GeckoStart(JNIEnv* env, char** argv, int argc, const nsXREAppData* appData)
+GeckoStart(JNIEnv* env, char** argv, int argc, const StaticXREAppData& aAppData)
 {
     mozilla::jni::SetGeckoThreadEnv(env);
 
 #ifdef MOZ_CRASHREPORTER
     const struct mapping_info *info = getLibraryMapping();
     while (info->name) {
       CrashReporter::AddLibraryMapping(info->name, info->base,
                                        info->len, info->offset);
@@ -35,13 +37,16 @@ GeckoStart(JNIEnv* env, char** argv, int
     }
 #endif
 
     if (!argv) {
         LOG("Failed to get arguments for GeckoStart\n");
         return;
     }
 
+    XREAppData appData;
+    appData = aAppData;
+
     int result = XRE_main(argc, argv, appData, 0);
 
     if (result)
         LOG("XRE_main returned %d", result);
 }
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -16,17 +16,17 @@
 #include "mozilla/Poison.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/Services.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/Telemetry.h"
 
 #include "nsAppRunner.h"
-#include "mozilla/AppData.h"
+#include "mozilla/XREAppData.h"
 #if defined(MOZ_UPDATER) && !defined(MOZ_WIDGET_ANDROID)
 #include "nsUpdateDriver.h"
 #endif
 #include "ProfileReset.h"
 
 #ifdef MOZ_INSTRUMENT_EVENT_LOOP
 #include "EventTracer.h"
 #endif
@@ -1603,33 +1603,33 @@ DumpHelp()
 
 #ifdef MOZ_X11
   printf("X11 options\n"
          "  --display=DISPLAY  X display to use\n"
          "  --sync             Make X calls synchronous\n");
 #endif
 #ifdef XP_UNIX
   printf("  --g-fatal-warnings Make all warnings fatal\n"
-         "\n%s options\n", gAppData->name);
+         "\n%s options\n", (const char*) gAppData->name);
 #endif
 
   printf("  -h or --help       Print this message.\n"
          "  -v or --version    Print %s version.\n"
          "  -P <profile>       Start with <profile>.\n"
          "  --profile <path>   Start with profile at <path>.\n"
          "  --migration        Start with migration wizard.\n"
          "  --ProfileManager   Start with ProfileManager.\n"
          "  --no-remote        Do not accept or send remote commands; implies\n"
          "                     --new-instance.\n"
          "  --new-instance     Open new instance, not a new window in running instance.\n"
          "  --UILocale <locale> Start with <locale> resources as UI Locale.\n"
-         "  --safe-mode        Disables extensions and themes for this session.\n", gAppData->name);
+         "  --safe-mode        Disables extensions and themes for this session.\n", (const char*) gAppData->name);
 
 #if defined(XP_WIN)
-  printf("  --console          Start %s with a debugging console.\n", gAppData->name);
+  printf("  --console          Start %s with a debugging console.\n", (const char*) gAppData->name);
 #endif
 
   // this works, but only after the components have registered.  so if you drop in a new command line handler, --help
   // won't not until the second run.
   // out of the bug, because we ship a component.reg file, it works correctly.
   DumpArbitraryHelp();
 }
 
@@ -1674,20 +1674,20 @@ static int MSCRTReportHook( int aReportT
 }
 
 #endif
 
 static inline void
 DumpVersion()
 {
   if (gAppData->vendor)
-    printf("%s ", gAppData->vendor);
-  printf("%s %s", gAppData->name, gAppData->version);
+    printf("%s ", (const char*) gAppData->vendor);
+  printf("%s %s", (const char*) gAppData->name, (const char*) gAppData->version);
   if (gAppData->copyright)
-      printf(", %s", gAppData->copyright);
+      printf(", %s", (const char*) gAppData->copyright);
   printf("\n");
 }
 
 #ifdef MOZ_ENABLE_XREMOTE
 static RemoteResult
 ParseRemoteCommandLine(nsCString& program,
                        const char** profile,
                        const char** username)
@@ -2798,17 +2798,17 @@ static void MakeOrSetMinidumpPath(nsIFil
 
     nsAutoString pathStr;
     if (NS_SUCCEEDED(dumpD->GetPath(pathStr)))
       CrashReporter::SetMinidumpPath(pathStr);
   }
 }
 #endif
 
-const nsXREAppData* gAppData = nullptr;
+const XREAppData* gAppData = nullptr;
 
 #ifdef MOZ_WIDGET_GTK
 static void MOZ_gdk_display_close(GdkDisplay *display)
 {
 #if CLEANUP_MEMORY
   // XXX wallpaper for bug 417163: don't close the Display if we're using the
   // Qt theme because we crash (in Qt code) when using jemalloc.
   bool skip_display_close = false;
@@ -3015,34 +3015,34 @@ public:
 #endif
   {};
 
   ~XREMain() {
     mScopedXPCOM = nullptr;
     mAppData = nullptr;
   }
 
-  int XRE_main(int argc, char* argv[], const nsXREAppData* aAppData);
+  int XRE_main(int argc, char* argv[], const XREAppData& aAppData);
   int XRE_mainInit(bool* aExitFlag);
   int XRE_mainStartup(bool* aExitFlag);
   nsresult XRE_mainRun();
 
   nsCOMPtr<nsINativeAppSupport> mNativeApp;
   nsCOMPtr<nsIToolkitProfileService> mProfileSvc;
   nsCOMPtr<nsIFile> mProfD;
   nsCOMPtr<nsIFile> mProfLD;
   nsCOMPtr<nsIProfileLock> mProfileLock;
 #ifdef MOZ_ENABLE_XREMOTE
   nsCOMPtr<nsIRemoteService> mRemoteService;
   nsProfileLock mRemoteLock;
   nsCOMPtr<nsIFile> mRemoteLockDir;
 #endif
 
   UniquePtr<ScopedXPCOMStartup> mScopedXPCOM;
-  nsAutoPtr<mozilla::ScopedAppData> mAppData;
+  UniquePtr<XREAppData> mAppData;
 
   nsXREDirProvider mDirProvider;
   nsAutoCString mProfileName;
   nsAutoCString mDesktopStartupID;
 
   bool mStartOffline;
   bool mShuttingDown;
 #ifdef MOZ_ENABLE_XREMOTE
@@ -3154,17 +3154,17 @@ XREMain::XRE_mainInit(bool* aExitFlag)
   else if (ar == ARG_FOUND) {
     nsCOMPtr<nsIFile> overrideLF;
     rv = XRE_GetFileFromPath(override, getter_AddRefs(overrideLF));
     if (NS_FAILED(rv)) {
       Output(true, "Error: unrecognized override.ini path.\n");
       return 1;
     }
 
-    rv = XRE_ParseAppData(overrideLF, mAppData.get());
+    rv = XRE_ParseAppData(overrideLF, *mAppData);
     if (NS_FAILED(rv)) {
       Output(true, "Couldn't read override.ini");
       return 1;
     }
   }
 
   // Check sanity and correctness of app data.
 
@@ -3193,43 +3193,41 @@ XREMain::XRE_mainInit(bool* aExitFlag)
 
 #ifdef XP_MACOSX
     nsCOMPtr<nsIFile> parent;
     greDir->GetParent(getter_AddRefs(parent));
     greDir = parent.forget();
     greDir->AppendNative(NS_LITERAL_CSTRING("Resources"));
 #endif
 
-    greDir.forget(&mAppData->xreDirectory);
+    mAppData->xreDirectory = greDir;
   }
 
   if (!mAppData->directory) {
-    NS_IF_ADDREF(mAppData->directory = mAppData->xreDirectory);
+    mAppData->directory = mAppData->xreDirectory;
+  }
+
+  if (!mAppData->minVersion) {
+    Output(true, "Error: Gecko:MinVersion not specified in application.ini\n");
+    return 1;
   }
 
-  if (mAppData->size > offsetof(nsXREAppData, minVersion)) {
-    if (!mAppData->minVersion) {
-      Output(true, "Error: Gecko:MinVersion not specified in application.ini\n");
-      return 1;
-    }
-
-    if (!mAppData->maxVersion) {
-      // If no maxVersion is specified, we assume the app is only compatible
-      // with the initial preview release. Do not increment this number ever!
-      SetAllocatedString(mAppData->maxVersion, "1.*");
-    }
-
-    if (mozilla::Version(mAppData->minVersion) > gToolkitVersion ||
-        mozilla::Version(mAppData->maxVersion) < gToolkitVersion) {
-      Output(true, "Error: Platform version '%s' is not compatible with\n"
-             "minVersion >= %s\nmaxVersion <= %s\n",
-             gToolkitVersion,
-             mAppData->minVersion, mAppData->maxVersion);
-      return 1;
-    }
+  if (!mAppData->maxVersion) {
+    // If no maxVersion is specified, we assume the app is only compatible
+    // with the initial preview release. Do not increment this number ever!
+    mAppData->maxVersion = "1.*";
+  }
+
+  if (mozilla::Version(mAppData->minVersion) > gToolkitVersion ||
+      mozilla::Version(mAppData->maxVersion) < gToolkitVersion) {
+    Output(true, "Error: Platform version '%s' is not compatible with\n"
+           "minVersion >= %s\nmaxVersion <= %s\n",
+           (const char*) gToolkitVersion, (const char*) mAppData->minVersion,
+           (const char*) mAppData->maxVersion);
+    return 1;
   }
 
   rv = mDirProvider.Initialize(mAppData->directory, mAppData->xreDirectory);
   if (NS_FAILED(rv))
     return 1;
 
 #ifdef MOZ_CRASHREPORTER
   if (EnvHasValue("MOZ_CRASHREPORTER")) {
@@ -4527,17 +4525,17 @@ XRE_CreateStatsObject()
 }
 
 /*
  * XRE_main - A class based main entry point used by most platforms.
  *            Note that on OSX, aAppData->xreDirectory will point to
  *            .app/Contents/Resources.
  */
 int
-XREMain::XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
+XREMain::XRE_main(int argc, char* argv[], const XREAppData& aAppData)
 {
   ScopedLogging log;
 
   // NB: this must happen after the creation of |ScopedLogging log| since
   // ScopedLogging::ScopedLogging calls NS_LogInit, and
   // XRE_CreateStatsObject calls Telemetry::CreateStatisticsRecorder,
   // and NS_LogInit must be called before Telemetry::CreateStatisticsRecorder.
   // NS_LogInit must be called before Telemetry::CreateStatisticsRecorder
@@ -4556,26 +4554,22 @@ XREMain::XRE_main(int argc, char* argv[]
   PROFILER_LABEL("Startup", "XRE_Main",
     js::ProfileEntry::Category::OTHER);
 
   nsresult rv = NS_OK;
 
   gArgc = argc;
   gArgv = argv;
 
-  NS_ENSURE_TRUE(aAppData, 2);
-
-  mAppData = new ScopedAppData(aAppData);
-  if (!mAppData)
-    return 1;
+  mAppData = MakeUnique<XREAppData>(aAppData);
   if (!mAppData->remotingName) {
-    SetAllocatedString(mAppData->remotingName, mAppData->name);
+    mAppData->remotingName = mAppData->name;
   }
   // used throughout this file
-  gAppData = mAppData;
+  gAppData = mAppData.get();
 
   nsCOMPtr<nsIFile> binFile;
   rv = XRE_GetBinaryPath(argv[0], getter_AddRefs(binFile));
   NS_ENSURE_SUCCESS(rv, 1);
 
   rv = binFile->GetPath(gAbsoluteArgv0Path);
   NS_ENSURE_SUCCESS(rv, 1);
 
@@ -4697,17 +4691,17 @@ XREMain::XRE_main(int argc, char* argv[]
 }
 
 void
 XRE_StopLateWriteChecks(void) {
   mozilla::StopLateWriteChecks();
 }
 
 int
-XRE_main(int argc, char* argv[], const nsXREAppData* aAppData, uint32_t aFlags)
+XRE_main(int argc, char* argv[], const XREAppData& aAppData, uint32_t aFlags)
 {
   XREMain main;
 
   int result = main.XRE_main(argc, argv, aAppData);
   mozilla::RecordShutdownEndTimeStamp();
   return result;
 }
 
--- a/toolkit/xre/nsAppRunner.h
+++ b/toolkit/xre/nsAppRunner.h
@@ -37,20 +37,18 @@ class nsIToolkitProfileService;
 class nsIFile;
 class nsIProfileLock;
 class nsIProfileUnlocker;
 class nsIFactory;
 class nsString;
 
 extern nsXREDirProvider* gDirServiceProvider;
 
-// NOTE: gAppData will be null in embedded contexts. The "size" parameter
-// will be the size of the original structure passed to XRE_main, but the
-// structure will have all of the members available.
-extern const nsXREAppData* gAppData;
+// NOTE: gAppData will be null in embedded contexts.
+extern const mozilla::XREAppData* gAppData;
 extern bool gSafeMode;
 
 extern int    gArgc;
 extern char **gArgv;
 extern int    gRestartArgc;
 extern char **gRestartArgv;
 extern bool gLogConsoleErrors;
 extern nsString gAbsoluteArgv0Path;
--- a/toolkit/xre/nsNativeAppSupportWin.cpp
+++ b/toolkit/xre/nsNativeAppSupportWin.cpp
@@ -680,17 +680,17 @@ nsNativeAppSupportWin::StartDDE() {
     // Initialize DDE.
     NS_ENSURE_TRUE( DMLERR_NO_ERROR == DdeInitialize( &mInstance,
                                                       nsNativeAppSupportWin::HandleDDENotification,
                                                       APPCLASS_STANDARD,
                                                       0 ),
                     NS_ERROR_FAILURE );
 
     // Allocate DDE strings.
-    NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
+    NS_ENSURE_TRUE( ( mApplication = DdeCreateStringHandleA( mInstance, (char*)(const char*) gAppData->name, CP_WINANSI ) ) && InitTopicStrings(),
                     NS_ERROR_FAILURE );
 
     // Next step is to register a DDE service.
     NS_ENSURE_TRUE( DdeNameService( mInstance, mApplication, 0, DNS_REGISTER ), NS_ERROR_FAILURE );
 
 #if MOZ_DEBUG_DDE
     printf( "DDE server started\n" );
 #endif
rename from xpcom/build/nsXREAppData.h
rename to xpcom/build/XREAppData.h
--- a/xpcom/build/nsXREAppData.h
+++ b/xpcom/build/XREAppData.h
@@ -4,147 +4,196 @@
  * 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 nsXREAppData_h
 #define nsXREAppData_h
 
 #include <stdint.h>
 #include "mozilla/Attributes.h"
-
-class nsIFile;
+#include "nsCOMPtr.h"
+#include "nsCRTGlue.h"
+#include "nsIFile.h"
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 namespace sandbox {
 class BrokerServices;
 }
 #endif
 
+namespace mozilla {
+
+struct StaticXREAppData;
+
 /**
  * Application-specific data needed to start the apprunner.
- *
- * @note When this structure is allocated and manipulated by XRE_CreateAppData,
- *       string fields will be allocated with moz_xmalloc, and interface pointers
- *       are strong references.
  */
-struct nsXREAppData
+class XREAppData
 {
-  /**
-   * This should be set to sizeof(nsXREAppData). This structure may be
-   * extended in future releases, and this ensures that binary compatibility
-   * is maintained.
-   */
-  uint32_t size;
+public:
+  XREAppData() { }
+  ~XREAppData() { }
+  XREAppData(const XREAppData& aOther)
+  {
+    *this = aOther;
+  }
+
+  XREAppData& operator=(const StaticXREAppData& aOther);
+  XREAppData& operator=(const XREAppData& aOther);
+  XREAppData& operator=(XREAppData&& aOther) = default;
+
+  struct NSFreePolicy
+  {
+    void operator()(const void* ptr) {
+      NS_Free(const_cast<void*>(ptr));
+    }
+  };
+
+  // Lots of code reads these fields directly like a struct, so rather
+  // than using UniquePtr directly, use an auto-converting wrapper.
+  class CharPtr
+  {
+  public:
+    explicit CharPtr() = default;
+    explicit CharPtr(const char* v)
+    {
+      *this = v;
+    }
+    CharPtr(CharPtr&&) = default;
+    ~CharPtr() = default;
+
+    CharPtr& operator=(const char* v)
+    {
+      if (v) {
+        mValue.reset(NS_strdup(v));
+      } else {
+        mValue = nullptr;
+      }
+      return *this;
+    }
+    CharPtr& operator=(const CharPtr& v)
+    {
+      *this = (const char*) v;
+      return *this;
+    }
+
+    operator const char*() const {
+      return mValue.get();
+    }
+
+  private:
+    UniquePtr<const char, NSFreePolicy> mValue;
+  };
 
   /**
    * The directory of the application to be run. May be null if the
    * xulrunner and the app are installed into the same directory.
    */
-  nsIFile* MOZ_NON_OWNING_REF directory;
+  nsCOMPtr<nsIFile> directory;
 
   /**
    * The name of the application vendor. This must be ASCII, and is normally
    * mixed-case, e.g. "Mozilla". Optional (may be null), but highly
    * recommended. Must not be the empty string.
    */
-  const char* vendor;
+  CharPtr vendor;
 
   /**
    * The name of the application. This must be ASCII, and is normally
    * mixed-case, e.g. "Firefox". Required (must not be null or an empty
    * string).
    */
-  const char* name;
+  CharPtr name;
 
   /**
    * The internal name of the application for remoting purposes. When left
    * unspecified, "name" is used instead. This must be ASCII, and is normally
    * lowercase, e.g. "firefox". Optional (may be null but not an empty string).
    */
-  const char* remotingName;
+  CharPtr remotingName;
 
   /**
    * The major version, e.g. "0.8.0+". Optional (may be null), but
    * required for advanced application features such as the extension
    * manager and update service. Must not be the empty string.
    */
-  const char* version;
+  CharPtr version;
 
   /**
    * The application's build identifier, e.g. "2004051604"
    */
-  const char* buildID;
+  CharPtr buildID;
 
   /**
    * The application's UUID. Used by the extension manager to determine
    * compatible extensions. Optional, but required for advanced application
    * features such as the extension manager and update service.
    *
    * This has traditionally been in the form
    * "{AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE}" but for new applications
    * a more readable form is encouraged: "appname@vendor.tld". Only
    * the following characters are allowed: a-z A-Z 0-9 - . @ _ { } *
    */
-  const char* ID;
+  CharPtr ID;
 
   /**
    * The copyright information to print for the -h commandline flag,
    * e.g. "Copyright (c) 2003 mozilla.org".
    */
-  const char* copyright;
+  CharPtr copyright;
 
   /**
    * Combination of NS_XRE_ prefixed flags (defined below).
    */
-  uint32_t flags;
+  uint32_t flags = 0;
 
   /**
    * The location of the XRE. XRE_main may not be able to figure this out
    * programatically.
    */
-  nsIFile* MOZ_NON_OWNING_REF xreDirectory;
+  nsCOMPtr<nsIFile> xreDirectory;
 
   /**
    * The minimum/maximum compatible XRE version.
    */
-  const char* minVersion;
-  const char* maxVersion;
+  CharPtr minVersion;
+  CharPtr maxVersion;
 
   /**
    * The server URL to send crash reports to.
    */
-  const char* crashReporterURL;
+  CharPtr crashReporterURL;
 
   /**
    * The profile directory that will be used. Optional (may be null). Must not
    * be the empty string, must be ASCII. The path is split into components
    * along the path separator characters '/' and '\'.
    *
    * The application data directory ("UAppData", see below) is normally
    * composed as follows, where $HOME is platform-specific:
    *
    *   UAppData = $HOME[/$vendor]/$name
    *
    * If present, the 'profile' string will be used instead of the combination of
    * vendor and name as follows:
    *
    *   UAppData = $HOME/$profile
    */
-  const char* profile;
+  CharPtr profile;
 
   /**
    * The application name to use in the User Agent string.
    */
-  const char* UAName;
+  CharPtr UAName;
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
   /**
    * Chromium sandbox BrokerServices.
    */
-  sandbox::BrokerServices* sandboxBrokerServices;
+  sandbox::BrokerServices* sandboxBrokerServices = nullptr;
 #endif
 };
 
 /**
  * Indicates whether or not the profile migrator service may be
  * invoked at startup when creating a profile.
  */
 #define NS_XRE_ENABLE_PROFILE_MIGRATOR (1 << 1)
@@ -156,9 +205,34 @@ struct nsXREAppData
  */
 #define NS_XRE_DLL_BLOCKLIST_ENABLED (1 << 2)
 
 /**
  * Indicates whether or not to use Breakpad crash reporting.
  */
 #define NS_XRE_ENABLE_CRASH_REPORTER (1 << 3)
 
-#endif // nsXREAppData_h
+/**
+ * A static version of the XRE app data is compiled into the application
+ * so that it is not necessary to read application.ini at startup.
+ *
+ * This structure is initialized into and matches nsXREAppData
+ */
+struct StaticXREAppData
+{
+  const char* vendor;
+  const char* name;
+  const char* remotingName;
+  const char* version;
+  const char* buildID;
+  const char* ID;
+  const char* copyright;
+  uint32_t flags;
+  const char* minVersion;
+  const char* maxVersion;
+  const char* crashReporterURL;
+  const char* profile;
+  const char* UAName;
+};
+
+} // namespace mozilla
+
+#endif // XREAppData_h
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -3,32 +3,32 @@
 # 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/.
 
 EXPORTS += [
     'nsXPCOM.h',
     'nsXPCOMCID.h',
     'nsXPCOMCIDInternal.h',
-    'nsXREAppData.h',
     'nsXULAppAPI.h',
     'XREChildData.h',
     'xrecore.h',
     'XREShellData.h',
 ]
 
 EXPORTS.mozilla += [
     'FileLocation.h',
     'IOInterposer.h',
     'LateWriteChecks.h',
     'Omnijar.h',
     'PoisonIOInterposer.h',
     'ServiceList.h',
     'Services.h',
     'XPCOM.h',
+    'XREAppData.h',
 ]
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     EXPORTS += ['nsWindowsDllInterceptor.h']
     EXPORTS.mozilla += ['perfprobe.h']
     SOURCES += [
         'perfprobe.cpp',
         'PoisonIOInterposerBase.cpp',
--- a/xpcom/build/nsXULAppAPI.h
+++ b/xpcom/build/nsXULAppAPI.h
@@ -7,17 +7,17 @@
 #ifndef _nsXULAppAPI_h__
 #define _nsXULAppAPI_h__
 
 #include "nsID.h"
 #include "xrecore.h"
 #include "nsXPCOM.h"
 #include "nsISupports.h"
 #include "mozilla/Logging.h"
-#include "nsXREAppData.h"
+#include "mozilla/XREAppData.h"
 #include "js/TypeDecls.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Vector.h"
 #include "mozilla/TimeStamp.h"
 #include "XREChildData.h"
 #include "XREShellData.h"
@@ -199,17 +199,17 @@
  * @param aFlags    Platform specific flags.
  *
  * @return         A native result code suitable for returning from main().
  *
  * @note           If the binary is linked against the standalone XPCOM glue,
  *                 XPCOMGlueStartup() should be called before this method.
  */
 XRE_API(int,
-        XRE_main, (int argc, char* argv[], const nsXREAppData* aAppData,
+        XRE_main, (int argc, char* argv[], const mozilla::XREAppData& aAppData,
                    uint32_t aFlags))
 
 /**
  * Given a path relative to the current working directory (or an absolute
  * path), return an appropriate nsIFile object.
  *
  * @note Pass UTF8 strings on Windows... native charset on other platforms.
  */
@@ -357,43 +357,25 @@ XRE_API(void,
 
 /**
  * Terminate embedding started with XRE_InitEmbedding or XRE_InitEmbedding2
  */
 XRE_API(void,
         XRE_TermEmbedding, ())
 
 /**
- * Create a new nsXREAppData structure from an application.ini file.
- *
- * @param aINIFile The application.ini file to parse.
- * @param aAppData A newly-allocated nsXREAppData structure. The caller is
- *                 responsible for freeing this structure using
- *                 XRE_FreeAppData.
- */
-XRE_API(nsresult,
-        XRE_CreateAppData, (nsIFile* aINIFile,
-                            nsXREAppData** aAppData))
-
-/**
  * Parse an INI file (application.ini or override.ini) into an existing
  * nsXREAppData structure.
  *
  * @param aINIFile The INI file to parse
  * @param aAppData The nsXREAppData structure to fill.
  */
 XRE_API(nsresult,
         XRE_ParseAppData, (nsIFile* aINIFile,
-                           nsXREAppData* aAppData))
-
-/**
- * Free a nsXREAppData structure that was allocated with XRE_CreateAppData.
- */
-XRE_API(void,
-        XRE_FreeAppData, (nsXREAppData* aAppData))
+                           mozilla::XREAppData& aAppData))
 
 enum GeckoProcessType
 {
   GeckoProcessType_Default = 0,
 
   GeckoProcessType_Plugin,
   GeckoProcessType_Content,
 
deleted file mode 100644
--- a/xpcom/glue/AppData.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/* -*- 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/. */
-
-#ifndef mozilla_AppData_h
-#define mozilla_AppData_h
-
-#include "nsXREAppData.h"
-#include "nscore.h"
-#include "nsStringGlue.h"
-#include "nsISupportsUtils.h"
-
-namespace mozilla {
-
-// Like nsXREAppData, but releases all strong refs/allocated memory
-// in the destructor.
-class ScopedAppData : public nsXREAppData
-{
-public:
-  ScopedAppData()
-  {
-    Zero();
-    this->size = sizeof(*this);
-  }
-
-  explicit ScopedAppData(const nsXREAppData* aAppData);
-
-  void Zero() { memset(this, 0, sizeof(*this)); }
-
-  ~ScopedAppData();
-};
-
-/**
- * Given |aStr| is holding a string allocated with NS_Alloc, or null:
- * replace the value in |aStr| with a new value.
- *
- * @param aNewValue Null is permitted. The string is cloned with NS_strdup.
- */
-void SetAllocatedString(const char*& aStr, const char* aNewValue);
-
-/**
- * Given "str" is holding a string allocated with NS_Alloc, or null:
- * replace the value in "str" with a new value.
- *
- * @param aNewValue If |aNewValue| is the empty string, |aStr| will be set
- *                  to null.
- */
-void SetAllocatedString(const char*& aStr, const nsACString& aNewValue);
-
-template<class T>
-void
-SetStrongPtr(T*& aPtr, T* aNewValue)
-{
-  NS_IF_RELEASE(aPtr);
-  aPtr = aNewValue;
-  NS_IF_ADDREF(aPtr);
-}
-
-} // namespace mozilla
-
-#endif
rename from xpcom/glue/AppData.cpp
rename to xpcom/glue/XREAppData.cpp
--- a/xpcom/glue/AppData.cpp
+++ b/xpcom/glue/XREAppData.cpp
@@ -1,95 +1,56 @@
 /* -*- 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/. */
 
-#include "mozilla/AppData.h"
-#include "nsXULAppAPI.h"
-#include "nsINIParser.h"
-#include "nsIFile.h"
+#include "mozilla/XREAppData.h"
 #include "nsCRTGlue.h"
-#include "nsAutoPtr.h"
 
 namespace mozilla {
 
-void
-SetAllocatedString(const char*& aStr, const char* aNewValue)
+XREAppData&
+XREAppData::operator=(const StaticXREAppData& aOther)
 {
-  NS_Free(const_cast<char*>(aStr));
-  if (aNewValue) {
-    aStr = NS_strdup(aNewValue);
-  } else {
-    aStr = nullptr;
-  }
-}
+  vendor = aOther.vendor;
+  name = aOther.name;
+  remotingName = aOther.remotingName;
+  version = aOther.version;
+  buildID = aOther.buildID;
+  ID = aOther.ID;
+  copyright = aOther.copyright;
+  flags = aOther.flags;
+  minVersion = aOther.minVersion;
+  maxVersion = aOther.maxVersion;
+  crashReporterURL = aOther.crashReporterURL;
+  profile = aOther.profile;
+  UAName = aOther.UAName;
 
-void
-SetAllocatedString(const char*& aStr, const nsACString& aNewValue)
-{
-  NS_Free(const_cast<char*>(aStr));
-  if (aNewValue.IsEmpty()) {
-    aStr = nullptr;
-  } else {
-    aStr = ToNewCString(aNewValue);
-  }
+  return *this;
 }
 
-ScopedAppData::ScopedAppData(const nsXREAppData* aAppData)
+XREAppData&
+XREAppData::operator=(const XREAppData& aOther)
 {
-  Zero();
-
-  this->size = aAppData->size;
-
-  SetAllocatedString(this->vendor, aAppData->vendor);
-  SetAllocatedString(this->name, aAppData->name);
-  SetAllocatedString(this->remotingName, aAppData->remotingName);
-  SetAllocatedString(this->version, aAppData->version);
-  SetAllocatedString(this->buildID, aAppData->buildID);
-  SetAllocatedString(this->ID, aAppData->ID);
-  SetAllocatedString(this->copyright, aAppData->copyright);
-  SetAllocatedString(this->profile, aAppData->profile);
-  SetStrongPtr(this->directory, aAppData->directory);
-  this->flags = aAppData->flags;
-
-  if (aAppData->size > offsetof(nsXREAppData, xreDirectory)) {
-    SetStrongPtr(this->xreDirectory, aAppData->xreDirectory);
-    SetAllocatedString(this->minVersion, aAppData->minVersion);
-    SetAllocatedString(this->maxVersion, aAppData->maxVersion);
-  }
-
-  if (aAppData->size > offsetof(nsXREAppData, crashReporterURL)) {
-    SetAllocatedString(this->crashReporterURL, aAppData->crashReporterURL);
-  }
-
-  if (aAppData->size > offsetof(nsXREAppData, UAName)) {
-    SetAllocatedString(this->UAName, aAppData->UAName);
-  }
-
+  directory = aOther.directory;
+  vendor = aOther.vendor;
+  name = aOther.name;
+  remotingName = aOther.remotingName;
+  version = aOther.version;
+  buildID = aOther.buildID;
+  ID = aOther.ID;
+  copyright = aOther.copyright;
+  flags = aOther.flags;
+  xreDirectory = aOther.xreDirectory;
+  minVersion = aOther.minVersion;
+  maxVersion = aOther.maxVersion;
+  crashReporterURL = aOther.crashReporterURL;
+  profile = aOther.profile;
+  UAName = aOther.UAName;
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
-  sandboxBrokerServices = aAppData->sandboxBrokerServices;
+  sandboxBrokerServices = aOther.sandboxBrokerServices;
 #endif
-}
-
-ScopedAppData::~ScopedAppData()
-{
-  SetAllocatedString(this->vendor, nullptr);
-  SetAllocatedString(this->name, nullptr);
-  SetAllocatedString(this->remotingName, nullptr);
-  SetAllocatedString(this->version, nullptr);
-  SetAllocatedString(this->buildID, nullptr);
-  SetAllocatedString(this->ID, nullptr);
-  SetAllocatedString(this->copyright, nullptr);
-  SetAllocatedString(this->profile, nullptr);
-
-  NS_IF_RELEASE(this->directory);
-
-  SetStrongPtr(this->xreDirectory, (nsIFile*)nullptr);
-  SetAllocatedString(this->minVersion, nullptr);
-  SetAllocatedString(this->maxVersion, nullptr);
-
-  SetAllocatedString(this->crashReporterURL, nullptr);
-  SetAllocatedString(this->UAName, nullptr);
+  return *this;
 }
 
 } // namespace mozilla
--- a/xpcom/glue/moz.build
+++ b/xpcom/glue/moz.build
@@ -62,17 +62,16 @@ EXPORTS += [
     'nsTWeakRef.h',
     'nsVersionComparator.h',
     'nsWeakReference.h',
     'nsXPTCUtils.h',
     'PLDHashTable.h',
 ]
 
 EXPORTS.mozilla += [
-    'AppData.h',
     'AutoRestore.h',
     'BlockingResourceBase.h',
     'CondVar.h',
     'DeadlockDetector.h',
     'EnumeratedArrayCycleCollection.h',
     'FileUtils.h',
     'GenericFactory.h',
     'IntentionalCrash.h',
--- a/xpcom/glue/objs.mozbuild
+++ b/xpcom/glue/objs.mozbuild
@@ -1,16 +1,15 @@
 # -*- 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/.
 
 xpcom_glue_src_lcppsrcs = [
-    'AppData.cpp',
     'FileUtils.cpp',
     'nsArrayEnumerator.cpp',
     'nsArrayUtils.cpp',
     'nsCategoryCache.cpp',
     'nsClassInfoImpl.cpp',
     'nsCOMArray.cpp',
     'nsComponentManagerUtils.cpp',
     'nsCOMPtr.cpp',
@@ -25,16 +24,17 @@ xpcom_glue_src_lcppsrcs = [
     'nsMemory.cpp',
     'nsQuickSort.cpp',
     'nsTArray.cpp',
     'nsThreadUtils.cpp',
     'nsTObserverArray.cpp',
     'nsVersionComparator.cpp',
     'nsWeakReference.cpp',
     'PLDHashTable.cpp',
+    'XREAppData.cpp',
 ]
 
 xpcom_glue_src_cppsrcs = [
     '/xpcom/glue/%s' % s for s in xpcom_glue_src_lcppsrcs
 ]
 
 xpcom_gluens_src_lcppsrcs = [
     'BlockingResourceBase.cpp',
--- a/xpcom/string/nsUTF8Utils.h
+++ b/xpcom/string/nsUTF8Utils.h
@@ -12,16 +12,22 @@
 
 #include "nscore.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/SSE.h"
 #include "mozilla/TypeTraits.h"
 
 #include "nsCharTraits.h"
 
+#ifdef MOZILLA_INTERNAL_API
+#define UTF8UTILS_WARNING(msg) NS_WARNING(msg)
+#else
+#define UTF8UTILS_WARNING(msg)
+#endif
+
 class UTF8traits
 {
 public:
   static bool isASCII(char aChar)
   {
     return (aChar & 0x80) == 0x00;
   }
   static bool isInSeq(char aChar)
@@ -205,17 +211,17 @@ public:
       *aBuffer = p;
       return c;
     } else if (NS_IS_HIGH_SURROGATE(c)) { // U+D800 - U+DBFF
       if (p == aEnd) {
         // Found a high surrogate at the end of the buffer. Flag this
         // as an error and return the Unicode replacement
         // character 0xFFFD.
 
-        NS_WARNING("Unexpected end of buffer after high surrogate");
+        UTF8UTILS_WARNING("Unexpected end of buffer after high surrogate");
 
         if (aErr) {
           *aErr = true;
         }
         *aBuffer = p;
         return 0xFFFD;
       }
 
@@ -238,32 +244,32 @@ public:
         // a low surrogate. Flag this as an error and return the
         // Unicode replacement character 0xFFFD.  Note that the
         // pointer to the next character points to the second 16-bit
         // value, not beyond it, as per Unicode 5.0.0 Chapter 3 C10,
         // only the first code unit of an illegal sequence must be
         // treated as an illegally terminated code unit sequence
         // (also Chapter 3 D91, "isolated [not paired and ill-formed]
         // UTF-16 code units in the range D800..DFFF are ill-formed").
-        NS_WARNING("got a High Surrogate but no low surrogate");
+        UTF8UTILS_WARNING("got a High Surrogate but no low surrogate");
 
         if (aErr) {
           *aErr = true;
         }
         *aBuffer = p - 1;
         return 0xFFFD;
       }
     } else { // U+DC00 - U+DFFF
       // DC00- DFFF - Low Surrogate
 
       // Found a low surrogate w/o a preceding high surrogate. Flag
       // this as an error and return the Unicode replacement
       // character 0xFFFD.
 
-      NS_WARNING("got a low Surrogate but no high surrogate");
+      UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
       if (aErr) {
         *aErr = true;
       }
       *aBuffer = p;
       return 0xFFFD;
     }
 
     MOZ_ASSERT_UNREACHABLE("Impossible UCS-2 character value.");
@@ -487,17 +493,17 @@ public:
         if (p == end) {
           // Treat broken characters as the Unicode
           // replacement character 0xFFFD (0xEFBFBD in
           // UTF-8)
           *out++ = '\xEF';
           *out++ = '\xBF';
           *out++ = '\xBD';
 
-          NS_WARNING("String ending in half a surrogate pair!");
+          UTF8UTILS_WARNING("String ending in half a surrogate pair!");
 
           break;
         }
         c = *p;
 
         if (NS_IS_LOW_SURROGATE(c)) {
           // DC00- DFFF - Low Surrogate
           // N = (H - D800) *400 + 10000 + ( L - DC00 )
@@ -520,27 +526,27 @@ public:
           // 16-bit value, not beyond it, as per Unicode 5.0.0
           // Chapter 3 C10, only the first code unit of an illegal
           // sequence must be treated as an illegally terminated
           // code unit sequence (also Chapter 3 D91, "isolated [not
           // paired and ill-formed] UTF-16 code units in the range
           // D800..DFFF are ill-formed").
           p--;
 
-          NS_WARNING("got a High Surrogate but no low surrogate");
+          UTF8UTILS_WARNING("got a High Surrogate but no low surrogate");
         }
       } else { // U+DC00 - U+DFFF
         // Treat broken characters as the Unicode replacement
         // character 0xFFFD (0xEFBFBD in UTF-8)
         *out++ = '\xEF';
         *out++ = '\xBF';
         *out++ = '\xBD';
 
         // DC00- DFFF - Low Surrogate
-        NS_WARNING("got a low Surrogate but no high surrogate");
+        UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
       }
     }
 
     mBuffer = out;
   }
 
   void write_terminator()
   {
@@ -586,17 +592,17 @@ public:
       } else if (0xD800 == (0xFC00 & c)) { // U+D800 - U+DBFF
         ++p;
         if (p == end) {
           // Treat broken characters as the Unicode
           // replacement character 0xFFFD (0xEFBFBD in
           // UTF-8)
           mSize += 3;
 
-          NS_WARNING("String ending in half a surrogate pair!");
+          UTF8UTILS_WARNING("String ending in half a surrogate pair!");
 
           break;
         }
         c = *p;
 
         if (0xDC00 == (0xFC00 & c)) {
           mSize += 4;
         } else {
@@ -609,24 +615,24 @@ public:
           // the one beyond it, as per Unicode 5.0.0 Chapter 3 C10,
           // only the first code unit of an illegal sequence must
           // be treated as an illegally terminated code unit
           // sequence (also Chapter 3 D91, "isolated [not paired and
           // ill-formed] UTF-16 code units in the range D800..DFFF
           // are ill-formed").
           p--;
 
-          NS_WARNING("got a high Surrogate but no low surrogate");
+          UTF8UTILS_WARNING("got a high Surrogate but no low surrogate");
         }
       } else { // U+DC00 - U+DFFF
         // Treat broken characters as the Unicode replacement
         // character 0xFFFD (0xEFBFBD in UTF-8)
         mSize += 3;
 
-        NS_WARNING("got a low Surrogate but no high surrogate");
+        UTF8UTILS_WARNING("got a low Surrogate but no high surrogate");
       }
     }
   }
 
 private:
   size_t mSize;
 };
 
@@ -734,9 +740,11 @@ RewindToPriorUTF8Codepoint(const Char* u
                 "UTF-8 data must be in 8-bit units");
   static_assert(mozilla::IsUnsigned<UnsignedT>::value, "index type must be unsigned");
   while (index > 0 && (utf8Chars[index] & 0xC0) == 0x80)
     --index;
 
   return index;
 }
 
+#undef UTF8UTILS_WARNING
+
 #endif /* !defined(nsUTF8Utils_h_) */
--- a/xpcom/system/nsIXULAppInfo.idl
+++ b/xpcom/system/nsIXULAppInfo.idl
@@ -8,46 +8,46 @@
  * A scriptable interface to the nsXULAppAPI structure. See nsXULAppAPI.h for
  * a detailed description of each attribute.
  */
 
 [scriptable, uuid(ddea4f31-3c5e-4769-ac68-21ab4b3d7845)]
 interface nsIXULAppInfo : nsIPlatformInfo
 {
   /**
-   * @see nsXREAppData.vendor
-   * @returns an empty string if nsXREAppData.vendor is not set.
+   * @see XREAppData.vendor
+   * @returns an empty string if XREAppData.vendor is not set.
    */
   readonly attribute ACString vendor;
 
   /**
-   * @see nsXREAppData.name
+   * @see XREAppData.name
    */
   readonly attribute ACString name;
 
   /**
-   * @see nsXREAppData.ID
-   * @returns an empty string if nsXREAppData.ID is not set.
+   * @see XREAppData.ID
+   * @returns an empty string if XREAppData.ID is not set.
    */
   readonly attribute ACString ID;
 
   /**
    * The version of the XUL application. It is different than the
    * version of the XULRunner platform. Be careful about which one you want.
    *
-   * @see nsXREAppData.version
-   * @returns an empty string if nsXREAppData.version is not set.
+   * @see XREAppData.version
+   * @returns an empty string if XREAppData.version is not set.
    */
   readonly attribute ACString version;
 
   /**
    * The build ID/date of the application. For xulrunner applications,
    * this will be different than the build ID of the platform. Be careful
    * about which one you want.
    */
   readonly attribute ACString appBuildID;
 
   /**
-   * @see nsXREAppData.UAName
-   * @returns an empty string if nsXREAppData.UAName is not set.
+   * @see XREAppData.UAName
+   * @returns an empty string if XREAppData.UAName is not set.
    */
   readonly attribute ACString UAName;
 };