Merge m-c to fx-team, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 01 Mar 2016 15:30:35 -0800
changeset 322665 f4fdf1a5e7e71258e7e50a2c504d9b640ed4ac34
parent 322664 952e06e0b849d497546ad42bfff4d3a7873e810f (current diff)
parent 322638 eb25b90a05c194bfd4f498ff3ffee7440f85f1cd (diff)
child 322666 2750be25944cafd09d3d793a4ecc33b11ae5f44b
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team, a=merge MozReview-Commit-ID: LQ4NtghbPdw
dom/workers/test/serviceworkers/interception_featuredetect.js
dom/workers/test/serviceworkers/test_interception_featuredetect.html
editor/composer/res/accessiblecaret.png
editor/composer/res/accessiblecaret@1.5x.png
editor/composer/res/accessiblecaret@2.25x.png
editor/composer/res/accessiblecaret@2x.png
editor/composer/res/accessiblecaret_tilt_left.png
editor/composer/res/accessiblecaret_tilt_left@1.5x.png
editor/composer/res/accessiblecaret_tilt_left@2.25x.png
editor/composer/res/accessiblecaret_tilt_left@2x.png
editor/composer/res/accessiblecaret_tilt_right.png
editor/composer/res/accessiblecaret_tilt_right@1.5x.png
editor/composer/res/accessiblecaret_tilt_right@2.25x.png
editor/composer/res/accessiblecaret_tilt_right@2x.png
extensions/pref/autoconfig/src/Makefile.in
mozglue/android/Makefile.in
netwerk/Makefile.in
testing/taskcluster/tasks/tests/fx_linux64_mochitest_media.yml
testing/taskcluster/tasks/tests/fx_linux64_mochitest_media_e10s.yml
testing/web-platform/meta/IndexedDB/idbindex_openCursor2.htm.ini
testing/web-platform/meta/IndexedDB/idbindex_openKeyCursor3.htm.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-http/fetch-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-http/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-http/script-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/http-csp/cross-origin/http-http/xhr-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-http/fetch-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-http/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-http/script-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-csp/cross-origin/http-http/xhr-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/fetch-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/iframe-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/script-tag/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/referrer-policy/origin-when-cross-origin/meta-referrer/cross-origin/http-http/xhr-request/cross-origin.swap-origin-redirect.http.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-001.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-002.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-003.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-005.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-007.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-008.html.ini
testing/web-platform/meta/shadow-dom/untriaged/events/events-that-are-always-stopped/test-009.html.ini
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-001.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-002.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-003.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-004.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-005.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-006.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-007.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-008.html
testing/web-platform/tests/shadow-dom/untriaged/events/events-that-are-always-stopped/test-009.html
toolkit/mozapps/downloads/tests/Makefile.in
xpcom/Makefile.in
--- a/accessible/base/Logging.cpp
+++ b/accessible/base/Logging.cpp
@@ -40,24 +40,26 @@ struct ModuleRep {
 static ModuleRep sModuleMap[] = {
   { "docload", logging::eDocLoad },
   { "doccreate", logging::eDocCreate },
   { "docdestroy", logging::eDocDestroy },
   { "doclifecycle", logging::eDocLifeCycle },
 
   { "events", logging::eEvents },
   { "platforms", logging::ePlatforms },
-  { "stack", logging::eStack },
   { "text", logging::eText },
   { "tree", logging::eTree },
 
   { "DOMEvents", logging::eDOMEvents },
   { "focus", logging::eFocus },
   { "selection", logging::eSelection },
-  { "notifications", logging::eNotifications }
+  { "notifications", logging::eNotifications },
+
+  { "stack", logging::eStack },
+  { "verbose", logging::eVerbose }
 };
 
 static void
 EnableLogging(const char* aModulesStr)
 {
   sModules = 0;
   if (!aModulesStr)
     return;
@@ -602,16 +604,71 @@ logging::SelChange(nsISelection* aSelect
   bool isIgnored = !aDocument || !aDocument->IsContentLoaded();
   printf("\nSelection changed, selection type: %s, notification %s, reason: %d\n",
          strType, (isIgnored ? "ignored" : "pending"), aReason);
 
   Stack();
 }
 
 void
+logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...)
+{
+  if (IsEnabledAll(logging::eTree | aExtraFlags)) {
+    MsgBegin("TREE", aMsg);
+
+    va_list vl;
+    va_start(vl, aExtraFlags);
+    const char* descr = nullptr;
+    while ((descr = va_arg(vl, const char*))) {
+      AccessibleInfo(descr, va_arg(vl, Accessible*));
+    }
+    va_end(vl);
+
+    MsgEnd();
+
+    if (aExtraFlags & eStack) {
+      Stack();
+    }
+  }
+}
+
+void
+logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags,
+                  const char* aMsg1, Accessible* aAcc,
+                  const char* aMsg2, nsINode* aNode)
+{
+  if (IsEnabledAll(logging::eTree | logging::eVerbose)) {
+    MsgBegin("TREE", aMsg);
+    AccessibleInfo(aMsg1, aAcc);
+    Accessible* acc = aAcc->Document()->GetAccessible(aNode);
+    if (acc) {
+      AccessibleInfo(aMsg2, acc);
+    }
+    else {
+      Node(aMsg2, aNode);
+    }
+    MsgEnd();
+  }
+}
+
+
+void
+logging::TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent)
+{
+  if (IsEnabledAll(logging::eTree | aExtraFlags)) {
+    MsgBegin("TREE", aMsg);
+    AccessibleInfo("container", aParent);
+    for (uint32_t idx = 0; idx < aParent->ChildCount(); idx++) {
+      AccessibleInfo("child", aParent->GetChildAt(idx));
+    }
+    MsgEnd();
+  }
+}
+
+void
 logging::MsgBegin(const char* aTitle, const char* aMsgText, ...)
 {
   printf("\nA11Y %s: ", aTitle);
 
   va_list argptr;
   va_start(argptr, aMsgText);
   vprintf(aMsgText, argptr);
   va_end(argptr);
@@ -732,16 +789,72 @@ logging::Document(DocAccessible* aDocume
          static_cast<void*>(aDocument->DocumentNode()));
 
   printf("    Document ");
   LogDocURI(aDocument->DocumentNode());
   printf("\n");
 }
 
 void
+logging::AccessibleInfo(const char* aDescr, Accessible* aAccessible)
+{
+  printf("    %s: %p; ", aDescr, static_cast<void*>(aAccessible));
+  if (!aAccessible) {
+    printf("\n");
+    return;
+  }
+  if (aAccessible->IsDefunct()) {
+    printf("defunct\n");
+    return;
+  }
+  if (!aAccessible->Document() || aAccessible->Document()->IsDefunct()) {
+    printf("document is shutting down, no info\n");
+    return;
+  }
+
+  nsAutoString role;
+  GetAccService()->GetStringRole(aAccessible->Role(), role);
+  printf("role: %s", NS_ConvertUTF16toUTF8(role).get());
+
+  nsAutoString name;
+  aAccessible->Name(name);
+  if (!name.IsEmpty()) {
+    printf(", name: '%s'", NS_ConvertUTF16toUTF8(name).get());
+  }
+
+  printf(", idx: %d", aAccessible->IndexInParent());
+
+  nsINode* node = aAccessible->GetNode();
+  if (!node) {
+    printf(", node: null\n");
+  }
+  else if (node->IsNodeOfType(nsINode::eDOCUMENT)) {
+    printf(", document node: %p\n", static_cast<void*>(node));
+  }
+  else if (node->IsNodeOfType(nsINode::eTEXT)) {
+    printf(", text node: %p\n", static_cast<void*>(node));
+  }
+  else if (node->IsElement()) {
+    dom::Element* el = node->AsElement();
+
+    nsAutoCString tag;
+    el->NodeInfo()->NameAtom()->ToUTF8String(tag);
+
+    nsIAtom* idAtom = el->GetID();
+    nsAutoCString id;
+    if (idAtom) {
+      idAtom->ToUTF8String(id);
+    }
+
+    printf(", element node: %p, %s@id='%s'\n",
+           static_cast<void*>(el), tag.get(), id.get());
+  }
+}
+
+void
 logging::AccessibleNNode(const char* aDescr, Accessible* aAccessible)
 {
   printf("    %s: %p; ", aDescr, static_cast<void*>(aAccessible));
   if (!aAccessible)
     return;
 
   nsAutoString role;
   GetAccService()->GetStringRole(aAccessible->Role(), role);
@@ -810,16 +923,22 @@ logging::Stack()
 
 bool
 logging::IsEnabled(uint32_t aModules)
 {
   return sModules & aModules;
 }
 
 bool
+logging::IsEnabledAll(uint32_t aModules)
+{
+  return (sModules & aModules) == aModules;
+}
+
+bool
 logging::IsEnabled(const nsAString& aModuleStr)
 {
   for (unsigned int idx = 0; idx < ArrayLength(sModuleMap); idx++) {
     if (aModuleStr.EqualsASCII(sModuleMap[idx].mStr))
       return sModules & sModuleMap[idx].mModule;
   }
 
   return false;
--- a/accessible/base/Logging.h
+++ b/accessible/base/Logging.h
@@ -30,32 +30,40 @@ namespace logging {
 enum EModules {
   eDocLoad = 1 << 0,
   eDocCreate = 1 << 1,
   eDocDestroy = 1 << 2,
   eDocLifeCycle = eDocLoad | eDocCreate | eDocDestroy,
 
   eEvents = 1 << 3,
   ePlatforms = 1 << 4,
-  eStack = 1 << 5,
-  eText = 1 << 6,
-  eTree = 1 << 7,
+  eText = 1 << 5,
+  eTree = 1 << 6,
 
-  eDOMEvents = 1 << 8,
-  eFocus = 1 << 9,
-  eSelection = 1 << 10,
-  eNotifications = eDOMEvents | eSelection | eFocus
+  eDOMEvents = 1 << 7,
+  eFocus = 1 << 8,
+  eSelection = 1 << 9,
+  eNotifications = eDOMEvents | eSelection | eFocus,
+
+  // extras
+  eStack = 1 << 10,
+  eVerbose = 1 << 11
 };
 
 /**
  * Return true if any of the given modules is logged.
  */
 bool IsEnabled(uint32_t aModules);
 
 /**
+ * Return true if all of the given modules are logged.
+ */
+bool IsEnabledAll(uint32_t aModules);
+
+/**
  * Return true if the given module is logged.
  */
 bool IsEnabled(const nsAString& aModules);
 
 /**
  * Log the document loading progress.
  */
 void DocLoad(const char* aMsg, nsIWebProgress* aWebProgress,
@@ -117,16 +125,25 @@ void FocusDispatched(Accessible* aTarget
 
 /**
  * Log the selection change.
  */
 void SelChange(nsISelection* aSelection, DocAccessible* aDocument,
                int16_t aReason);
 
 /**
+ * Log the given accessible elements info.
+ */
+void TreeInfo(const char* aMsg, uint32_t aExtraFlags, ...);
+void TreeInfo(const char* aMsg, uint32_t aExtraFlags,
+              const char* aMsg1, Accessible* aAcc,
+              const char* aMsg2, nsINode* aNode);
+void TreeInfo(const char* aMsg, uint32_t aExtraFlags, Accessible* aParent);
+
+/**
  * Log the message ('title: text' format) on new line. Print the start and end
  * boundaries of the message body designated by '{' and '}' (2 spaces indent for
  * body).
  */
 void MsgBegin(const char* aTitle, const char* aMsgText, ...);
 void MsgEnd();
 
 /**
@@ -159,16 +176,17 @@ void Node(const char* aDescr, nsINode* a
 /**
  * Log the document accessible info as message entry.
  */
 void Document(DocAccessible* aDocument);
 
 /**
  * Log the accessible and its DOM node as a message entry.
  */
+void AccessibleInfo(const char* aDescr, Accessible* aAccessible);
 void AccessibleNNode(const char* aDescr, Accessible* aAccessible);
 void AccessibleNNode(const char* aDescr, nsINode* aNode);
 
 /**
  * Log the DOM event.
  */
 void DOMEvent(const char* aDescr, nsINode* aOrigTarget,
               const nsAString& aEventType);
@@ -184,14 +202,15 @@ void Stack();
 void Enable(const nsAFlatCString& aModules);
 
 /**
  * Enable logging of modules specified by A11YLOG environment variable,
  * all other modules aren't logged.
  */
 void CheckEnv();
 
-} // namespace logs
+} // namespace logging
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -585,16 +585,17 @@ public:
   bool IsDoc() const { return HasGenericType(eDocument); }
   DocAccessible* AsDoc();
 
   bool IsGenericHyperText() const { return mType == eHyperTextType; }
   bool IsHyperText() const { return HasGenericType(eHyperText); }
   HyperTextAccessible* AsHyperText();
 
   bool IsHTMLBr() const { return mType == eHTMLBRType; }
+  bool IsHTMLCaption() const { return mType == eHTMLCaptionType; }
   bool IsHTMLCombobox() const { return mType == eHTMLComboboxType; }
   bool IsHTMLFileInput() const { return mType == eHTMLFileInputType; }
 
   bool IsHTMLListItem() const { return mType == eHTMLLiType; }
   HTMLLIAccessible* AsHTMLListItem();
 
   bool IsHTMLOptGroup() const { return mType == eHTMLOptGroupType; }
 
--- a/accessible/html/HTMLTableAccessible.cpp
+++ b/accessible/html/HTMLTableAccessible.cpp
@@ -388,34 +388,24 @@ HTMLTableRowAccessible::GroupPosition()
 // HTMLTableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 NS_IMPL_ISUPPORTS_INHERITED0(HTMLTableAccessible, Accessible)
 
 ////////////////////////////////////////////////////////////////////////////////
 // HTMLTableAccessible: Accessible
 
-void
-HTMLTableAccessible::CacheChildren()
+bool
+HTMLTableAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
 {
   // Move caption accessible so that it's the first child. Check for the first
   // caption only, because nsAccessibilityService ensures we don't create
   // accessibles for the other captions, since only the first is actually
   // visible.
-  TreeWalker walker(this, mContent);
-
-  Accessible* child = nullptr;
-  while ((child = walker.Next())) {
-    if (child->Role() == roles::CAPTION) {
-      InsertChildAt(0, child);
-      while ((child = walker.Next()) && AppendChild(child));
-      break;
-    }
-    AppendChild(child);
-  }
+  return Accessible::InsertChildAt(aChild->IsHTMLCaption() ? 0 : aIndex, aChild);
 }
 
 role
 HTMLTableAccessible::NativeRole()
 {
   if (mContent->IsMathMLElement(nsGkAtoms::mtable_)) {
     return roles::MATHML_TABLE;
   }
--- a/accessible/html/HTMLTableAccessible.h
+++ b/accessible/html/HTMLTableAccessible.h
@@ -152,22 +152,23 @@ public:
   // Accessible
   virtual TableAccessible* AsTable() override { return this; }
   virtual void Description(nsString& aDescription) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual Relation RelationByType(RelationType aRelationType) override;
 
+  bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
+
 protected:
   virtual ~HTMLTableAccessible() {}
 
   // Accessible
   virtual ENameValueFlag NativeName(nsString& aName) override;
-  virtual void CacheChildren() override;
 
   // HTMLTableAccessible
 
   /**
    * Add row or column to selection.
    *
    * @param aIndex   [in] index of row or column to be selected
    * @param aTarget  [in] indicates what should be selected, either row or column
@@ -204,17 +205,17 @@ protected:
 
 /**
  * HTML caption accessible (html:caption).
  */
 class HTMLCaptionAccessible : public HyperTextAccessibleWrap
 {
 public:
   HTMLCaptionAccessible(nsIContent* aContent, DocAccessible* aDoc) :
-    HyperTextAccessibleWrap(aContent, aDoc) { }
+    HyperTextAccessibleWrap(aContent, aDoc) { mType = eHTMLCaptionType; }
 
   // Accessible
   virtual a11y::role NativeRole() override;
   virtual Relation RelationByType(RelationType aRelationType) override;
 
 protected:
   virtual ~HTMLCaptionAccessible() { }
 };
--- a/accessible/tests/mochitest/treeupdate/a11y.ini
+++ b/accessible/tests/mochitest/treeupdate/a11y.ini
@@ -24,11 +24,12 @@ skip-if = buildapp == "mulet"
 [test_list_editabledoc.html]
 [test_listbox.xul]
 [test_menu.xul]
 [test_menubutton.xul]
 [test_optgroup.html]
 [test_recreation.html]
 [test_select.html]
 [test_shutdown.xul]
+[test_table.html]
 [test_textleaf.html]
 [test_visibility.html]
 [test_whitespace.html]
new file mode 100644
--- /dev/null
+++ b/accessible/tests/mochitest/treeupdate/test_table.html
@@ -0,0 +1,81 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Table update tests</title>
+  <link rel="stylesheet" type="text/css"
+        href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+  <script type="application/javascript"
+          src="../common.js"></script>
+  <script type="application/javascript"
+          src="../role.js"></script>
+  <script type="application/javascript"
+          src="../events.js"></script>
+
+  <script type="application/javascript">
+
+    function appendCaption(aTableID)
+    {
+      this.invoke = function appendCaption_invoke()
+      {
+        // append a caption, it should appear as a first element in the
+        // accessible tree.
+        var caption = document.createElement("caption");
+        caption.textContent = "table caption";
+        getNode(aTableID).appendChild(caption);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_REORDER, aTableID)
+      ];
+
+      this.finalCheck = function appendCaption_finalCheck()
+      {
+        var tree =
+          { TABLE: [
+            { CAPTION: [ 
+              { TEXT_LEAF: [] }
+            ] },
+            { ROW: [ 
+              { CELL: [ {TEXT_LEAF: [] }]},
+              { CELL: [ {TEXT_LEAF: [] }]}
+            ] }
+          ] };
+        testAccessibleTree(aTableID, tree);
+      }
+
+      this.getID = function appendCaption_getID()
+      {
+        return "append caption";
+      }
+    }
+
+    function doTest()
+    {
+      gQueue = new eventQueue();
+      gQueue.push(new appendCaption("table"));
+      gQueue.invoke(); // Will call SimpleTest.finish();
+
+    }
+
+    SimpleTest.waitForExplicitFinish();
+    addA11yLoadEvent(doTest);
+  </script>
+</head>
+<body>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  </pre>
+
+  <table id="table">
+    <tr>
+      <td>cell1</td>
+      <td>cell2</td>
+    </tr>
+  </table>
+</body>
+</html>
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -645,17 +645,16 @@ var settingsToObserve = {
   'devtools.telemetry.supported_performance_marks': {
     resetToPref: true
   },
 
   'dom.mozApps.use_reviewer_certs': false,
   'dom.mozApps.signed_apps_installable_from': 'https://marketplace.firefox.com',
   'dom.presentation.discovery.enabled': false,
   'dom.presentation.discoverable': false,
-  'dom.serviceWorkers.interception.enabled': true,
   'dom.serviceWorkers.testing.enabled': false,
   'gfx.layerscope.enabled': false,
   'layers.draw-borders': false,
   'layers.draw-tile-borders': false,
   'layers.dump': false,
   'layers.enable-tiles': AppConstants.platform !== "win",
   'layers.enable-tiles': true,
   'layers.effect.invert': false,
--- a/b2g/components/test/mochitest/test_aboutserviceworkers.html
+++ b/b2g/components/test/mochitest/test_aboutserviceworkers.html
@@ -205,17 +205,16 @@ function setup() {
   info("Setting up");
   return new Promise((resolve, reject) => {
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.mozBrowserFramesEnabled", true],
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.interception.enabled", true]
     ]}, () => {
       SpecialPowers.pushPermissions([
         { "type": "webapps-manage", "allow": 1, "context": document },
         { "type": "browser", "allow": 1, "context": document },
         { "type": "embed-apps", "allow": 1, "context": document }
       ], () => {
         SpecialPowers.autoConfirmAppInstall(() => {
           SpecialPowers.autoConfirmAppUninstall(resolve);
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -787,28 +787,16 @@
 @RESPATH@/res/table-add-row-before-hover.gif
 @RESPATH@/res/table-add-row-before.gif
 @RESPATH@/res/table-remove-column-active.gif
 @RESPATH@/res/table-remove-column-hover.gif
 @RESPATH@/res/table-remove-column.gif
 @RESPATH@/res/table-remove-row-active.gif
 @RESPATH@/res/table-remove-row-hover.gif
 @RESPATH@/res/table-remove-row.gif
-@RESPATH@/res/accessiblecaret.png
-@RESPATH@/res/accessiblecaret@1.5x.png
-@RESPATH@/res/accessiblecaret@2.25x.png
-@RESPATH@/res/accessiblecaret@2x.png
-@RESPATH@/res/accessiblecaret_tilt_left.png
-@RESPATH@/res/accessiblecaret_tilt_left@1.5x.png
-@RESPATH@/res/accessiblecaret_tilt_left@2.25x.png
-@RESPATH@/res/accessiblecaret_tilt_left@2x.png
-@RESPATH@/res/accessiblecaret_tilt_right.png
-@RESPATH@/res/accessiblecaret_tilt_right@1.5x.png
-@RESPATH@/res/accessiblecaret_tilt_right@2.25x.png
-@RESPATH@/res/accessiblecaret_tilt_right@2x.png
 @RESPATH@/res/grabber.gif
 #ifdef XP_MACOSX
 @RESPATH@/res/cursors/*
 #endif
 @RESPATH@/res/fonts/*
 @RESPATH@/res/dtd/*
 @RESPATH@/res/html/*
 @RESPATH@/res/language.properties
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1630,17 +1630,16 @@ pref("reader.parse-node-limit", 0);
 
 // On desktop, we want the URLs to be included here for ease of debugging,
 // and because (normally) these errors are not persisted anywhere.
 pref("reader.errors.includeURLs", true);
 
 pref("view_source.tab", true);
 
 pref("dom.serviceWorkers.enabled", true);
-pref("dom.serviceWorkers.interception.enabled", true);
 pref("dom.serviceWorkers.openWindow.enabled", true);
 pref("dom.webnotifications.serviceworker.enabled", true);
 
 // Enable Push API.
 pref("dom.push.enabled", true);
 
 // These are the thumbnail width/height set in about:newtab.
 // If you change this, ENSURE IT IS THE SAME SIZE SET
--- a/browser/components/sessionstore/SessionStorage.jsm
+++ b/browser/components/sessionstore/SessionStorage.jsm
@@ -103,17 +103,18 @@ var SessionStorageInternal = {
    */
   restore: function (aDocShell, aStorageData) {
     for (let origin of Object.keys(aStorageData)) {
       let data = aStorageData[origin];
 
       let principal;
 
       try {
-        let attrs = ChromeUtils.createOriginAttributesWithUserContextId(origin, aDocShell.userContextId);
+        let attrs = ChromeUtils.createDefaultOriginAttributes();
+        attrs.userContextId = aDocShell.userContextId;
         let originURI = Services.io.newURI(origin, null, null);
         principal = Services.scriptSecurityManager.createCodebasePrincipal(originURI, attrs);
       } catch (e) {
         console.error(e);
         continue;
       }
 
       let storageManager = aDocShell.QueryInterface(Ci.nsIDOMStorageManager);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -730,28 +730,16 @@
 @RESPATH@/res/table-add-row-before-hover.gif
 @RESPATH@/res/table-add-row-before.gif
 @RESPATH@/res/table-remove-column-active.gif
 @RESPATH@/res/table-remove-column-hover.gif
 @RESPATH@/res/table-remove-column.gif
 @RESPATH@/res/table-remove-row-active.gif
 @RESPATH@/res/table-remove-row-hover.gif
 @RESPATH@/res/table-remove-row.gif
-@RESPATH@/res/accessiblecaret.png
-@RESPATH@/res/accessiblecaret@1.5x.png
-@RESPATH@/res/accessiblecaret@2.25x.png
-@RESPATH@/res/accessiblecaret@2x.png
-@RESPATH@/res/accessiblecaret_tilt_left.png
-@RESPATH@/res/accessiblecaret_tilt_left@1.5x.png
-@RESPATH@/res/accessiblecaret_tilt_left@2.25x.png
-@RESPATH@/res/accessiblecaret_tilt_left@2x.png
-@RESPATH@/res/accessiblecaret_tilt_right.png
-@RESPATH@/res/accessiblecaret_tilt_right@1.5x.png
-@RESPATH@/res/accessiblecaret_tilt_right@2.25x.png
-@RESPATH@/res/accessiblecaret_tilt_right@2x.png
 @RESPATH@/res/grabber.gif
 #ifdef XP_MACOSX
 @RESPATH@/res/cursors/*
 #endif
 @RESPATH@/res/fonts/*
 @RESPATH@/res/dtd/*
 @RESPATH@/res/html/*
 #if defined(XP_MACOSX) || defined(XP_WIN)
--- a/caps/tests/unit/test_origin.js
+++ b/caps/tests/unit/test_origin.js
@@ -28,19 +28,38 @@ function checkOriginAttributes(prin, att
   do_check_eq(prin.originSuffix, suffix || '');
   do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), suffix || '');
   do_check_true(ChromeUtils.originAttributesMatchPattern(prin.originAttributes, attrs));
   if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
     do_check_true(ssm.createCodebasePrincipalFromOrigin(prin.origin).equals(prin));
   } else {
     checkThrows(() => ssm.createCodebasePrincipalFromOrigin(prin.origin));
   }
+}
 
-  do_check_eq(ChromeUtils.createOriginAttributesWithUserContextId("http://example.org", 2).userContextId, 2);
-  do_check_eq(ChromeUtils.createOriginAttributesWithUserContextId("https://www.example.com:123^userContextId=4", 2).userContextId, 2);
+// utility function useful for debugging
+function printAttrs(name, attrs) {
+  do_print(name + " {\n" +
+           "\tappId: " + attrs.appId + ",\n" +
+           "\tuserContextId: " + attrs.userContextId + ",\n" +
+           "\tinBrowser: " + attrs.inBrowser + ",\n" +
+           "\taddonId: '" + attrs.addonId + "',\n" +
+           "\tsignedPkg: '" + attrs.signedPkg + "'\n}");
+}
+
+
+function checkValues(attrs, values) {
+  values = values || {};
+  //printAttrs("attrs", attrs);
+  //printAttrs("values", values);
+  do_check_eq(attrs.appId, values.appId || 0);
+  do_check_eq(attrs.userContextId, values.userContextId || 0);
+  do_check_eq(attrs.inBrowser, values.inBrowser || false);
+  do_check_eq(attrs.addonId, values.addonId || '');
+  do_check_eq(attrs.signedPkg, values.signedPkg || '');
 }
 
 function run_test() {
   // Attributeless origins.
   do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
   checkOriginAttributes(ssm.getSystemPrincipal());
   var exampleOrg = ssm.createCodebasePrincipal(makeURI('http://example.org'), {});
   do_check_eq(exampleOrg.origin, 'http://example.org');
@@ -172,9 +191,88 @@ function run_test() {
     do_check_eq(prin.isCodebasePrincipal, kind == 'codebasePrincipal');
     do_check_eq(prin.isExpandedPrincipal, kind == 'expandedPrincipal');
     do_check_eq(prin.isSystemPrincipal, kind == 'systemPrincipal');
   }
   checkKind(ssm.createNullPrincipal({}), 'nullPrincipal');
   checkKind(ssm.createCodebasePrincipal(makeURI('http://www.example.com'), {}), 'codebasePrincipal');
   checkKind(ssm.createExpandedPrincipal([ssm.createCodebasePrincipal(makeURI('http://www.example.com'), {})]), 'expandedPrincipal');
   checkKind(ssm.getSystemPrincipal(), 'systemPrincipal');
+
+  //
+  // Test Origin Attribute Manipulation
+  //
+
+  // check that we can create an empty origin attributes dict with default
+  // members and values.
+  emptyAttrs = ChromeUtils.createDefaultOriginAttributes();
+  checkValues(emptyAttrs);
+
+  var uri = "http://example.org";
+  var tests = [
+    [ "", {} ],
+    [ "^appId=5", {appId: 5} ],
+    [ "^userContextId=3", {userContextId: 3} ],
+    [ "^addonId=fooBar", {addonId: "fooBar"} ],
+    [ "^inBrowser=1", {inBrowser: true} ],
+    [ "^signedPkg=bazQux", {signedPkg: "bazQux"} ],
+    [ "^appId=3&inBrowser=1&userContextId=6",
+      {appId: 3, userContextId: 6, inBrowser: true} ] ];
+
+  // check that we can create an origin attributes from an origin properly
+  tests.forEach(function(t) {
+    let attrs = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+    checkValues(attrs, t[1]);
+    do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
+  });
+
+  // check that we can create an origin attributes from a dict properly
+  tests.forEach(function(t) {
+    let attrs = ChromeUtils.createOriginAttributesFromDict(t[1]);
+    checkValues(attrs, t[1]);
+    do_check_eq(ChromeUtils.originAttributesToSuffix(attrs), t[0]);
+  });
+
+  // each row in the set_tests array has these values:
+  // [0] - the suffix used to create an origin attribute from
+  // [1] - the expected result of creating an origin attribute from [0]
+  // [2] - the pattern to set on the origin attributes
+  // [3] - the expected result of setting [2] values on [1]
+  // [4] - the expected result of creating a suffix from [3]
+  var set_tests = [
+    [ "", {}, {appId: 5}, {appId: 5}, "^appId=5" ],
+    [ "^appId=5", {appId: 5}, {appId: 3}, {appId: 3}, "^appId=3" ],
+    [ "^appId=5", {appId: 5}, {userContextId: 3}, {appId: 5, userContextId: 3}, "^appId=5&userContextId=3" ],
+    [ "^appId=5", {appId: 5}, {appId: 3, userContextId: 7}, {appId: 3, userContextId: 7}, "^appId=3&userContextId=7" ] ];
+
+  // check that we can set origin attributes values properly
+  set_tests.forEach(function(t) {
+    let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+    checkValues(orig, t[1]);
+    let mod = orig;
+    for (var key in t[2]) {
+      mod[key] = t[2][key];
+    }
+    checkValues(mod, t[3]);
+    do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[4]);
+  });
+
+  // each row in the dflt_tests array has these values:
+  // [0] - the suffix used to create an origin attribute from
+  // [1] - the expected result of creating an origin attributes from [0]
+  // [2] - the expected result after setting userContextId to the default
+  // [3] - the expected result of creating a suffix from [2]
+  var dflt_tests = [
+    [ "", {}, {}, "" ],
+    [ "^userContextId=3", {userContextId: 3}, {}, "" ],
+    [ "^appId=5", {appId: 5}, {appId: 5}, "^appId=5" ],
+    [ "^appId=5&userContextId=3", {appId: 5, userContextId: 3}, {appId: 5}, "^appId=5" ] ];
+
+  // check that we can set the userContextId to default properly
+  dflt_tests.forEach(function(t) {
+    let orig = ChromeUtils.createOriginAttributesFromOrigin(uri + t[0]);
+    checkValues(orig, t[1]);
+    let mod = orig;
+    mod['userContextId'] = 0;
+    checkValues(mod, t[2]);
+    do_check_eq(ChromeUtils.originAttributesToSuffix(mod), t[3]);
+  });
 }
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1154,28 +1154,16 @@ PREF_DIR = defaults/pref
 # If DIST_SUBDIR is defined it indicates that app and gre dirs are
 # different and that we are building app related resources. Hence,
 # PREF_DIR should point to the app prefs location.
 ifneq (,$(DIST_SUBDIR)$(XPI_NAME))
 PREF_DIR = defaults/preferences
 endif
 
 ################################################################################
-# Copy each element of AUTOCFG_JS_EXPORTS to $(FINAL_TARGET)/defaults/autoconfig
-
-ifneq ($(AUTOCFG_JS_EXPORTS),)
-ifndef NO_DIST_INSTALL
-AUTOCFG_JS_EXPORTS_FILES := $(AUTOCFG_JS_EXPORTS)
-AUTOCFG_JS_EXPORTS_DEST := $(FINAL_TARGET)/defaults/autoconfig
-AUTOCFG_JS_EXPORTS_TARGET := export
-INSTALL_TARGETS += AUTOCFG_JS_EXPORTS
-endif
-endif
-
-################################################################################
 # SDK
 
 ifneq (,$(SDK_LIBRARY))
 ifndef NO_DIST_INSTALL
 SDK_LIBRARY_FILES := $(SDK_LIBRARY)
 SDK_LIBRARY_DEST := $(SDK_LIB_DIR)
 SDK_LIBRARY_TARGET := target
 INSTALL_TARGETS += SDK_LIBRARY
--- a/devtools/client/aboutdebugging/test/browser_service_workers.js
+++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
@@ -11,16 +11,17 @@
 const HTTP_ROOT = CHROME_ROOT.replace("chrome://mochitests/content/",
                                       "http://mochi.test:8888/");
 const SERVICE_WORKER = HTTP_ROOT + "service-workers/empty-sw.js";
 const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
 
 add_task(function* () {
   yield new Promise(done => {
     let options = {"set": [
+      ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]};
     SpecialPowers.pushPrefEnv(options, done);
   });
 
   let { tab, document } = yield openAboutDebugging("workers");
 
   let swTab = yield addTab(TAB_URL);
--- a/devtools/client/locales/en-US/markers.properties
+++ b/devtools/client/locales/en-US/markers.properties
@@ -27,16 +27,17 @@ marker.label.consoleTime=Console
 marker.label.garbageCollection2=Garbage Collection
 marker.label.garbageCollection.incremental=Incremental GC
 marker.label.garbageCollection.nonIncremental=Non-incremental GC
 marker.label.minorGC=Minor GC
 marker.label.cycleCollection=Cycle Collection
 marker.label.cycleCollection.forgetSkippable=CC Graph Reduction
 marker.label.timestamp=Timestamp
 marker.label.worker=Worker
+marker.label.messagePort=MessagePort
 marker.label.unknown=Unknown
 
 # LOCALIZATION NOTE (marker.label.javascript.*):
 # These strings are displayed as JavaScript markers that have special
 # reasons that can be translated.
 marker.label.javascript.scriptElement=Script Tag
 marker.label.javascript.promiseCallback=Promise Callback
 marker.label.javascript.promiseInit=Promise Init
@@ -77,16 +78,19 @@ marker.field.restyleHint=Restyle Hint:
 marker.field.causeName=Cause:
 # General "type" for a marker (Cycle Collection, Garbage Collection)
 marker.field.type=Type:
 # The type of operation performed by a Worker.
 marker.worker.serializeDataOffMainThread=Serialize data in Worker
 marker.worker.serializeDataOnMainThread=Serialize data on the main thread
 marker.worker.deserializeDataOffMainThread=Deserialize data in Worker
 marker.worker.deserializeDataOnMainThread=Deserialize data on the main thread
+# The type of operation performed by a MessagePort
+marker.messagePort.serializeData=Serialize data
+marker.messagePort.deserializeData=Deserialize data
 
 # Strings used in the waterfall sidebar as values.
 marker.value.unknownFrame=<unknown location>
 marker.value.DOMEventTargetPhase=Target
 marker.value.DOMEventCapturingPhase=Capture
 marker.value.DOMEventBubblingPhase=Bubbling
 
 # LOCALIZATION NOTE (marker.gcreason.label.*):
--- a/devtools/client/performance/modules/logic/marker-formatters.js
+++ b/devtools/client/performance/modules/logic/marker-formatters.js
@@ -142,12 +142,19 @@ const Formatters = {
     };
   },
 
   WorkerFields: function(marker) {
     return {
       [L10N.getStr("marker.field.type")]:
         L10N.getStr(`marker.worker.${marker.workerOperation}`)
     };
+  },
+
+  MessagePortFields: function(marker) {
+    return {
+      [L10N.getStr("marker.field.type")]:
+        L10N.getStr(`marker.messagePort.${marker.messagePortOperation}`)
+    };
   }
 };
 
 exports.Formatters = Formatters;
--- a/devtools/client/performance/modules/markers.js
+++ b/devtools/client/performance/modules/markers.js
@@ -142,16 +142,22 @@ const TIMELINE_BLUEPRINT = {
     fields: Formatters.CycleCollectionFields,
   },
   "Worker": {
     group: 1,
     colorName: "graphs-orange",
     label: L10N.getStr("marker.label.worker"),
     fields: Formatters.WorkerFields
   },
+  "MessagePort": {
+    group: 1,
+    colorName: "graphs-orange",
+    label: L10N.getStr("marker.label.messagePort"),
+    fields: Formatters.MessagePortFields
+  },
 
   /* Group 2 - User Controlled */
   "ConsoleTime": {
     group: 2,
     colorName: "graphs-blue",
     label: sublabelForProperty(L10N.getStr("marker.label.consoleTime"), "causeName"),
     fields: [{
       property: "causeName",
--- a/devtools/client/responsive.html/index.css
+++ b/devtools/client/responsive.html/index.css
@@ -100,17 +100,17 @@ body {
 }
 
 .viewport-toolbar-button:active {
   background-color: var(--theme-selection-background);
   opacity: 1;
 }
 
 .viewport-rotate-button {
-  mask-image: url("./images/rotate-viewport.svg");
+  mask: url("./images/rotate-viewport.svg");
 }
 
 /**
  * Viewport Browser
  */
 
 .browser {
   display: block;
--- a/devtools/shared/webconsole/test/test_console_serviceworker.html
+++ b/devtools/shared/webconsole/test/test_console_serviceworker.html
@@ -51,16 +51,17 @@ let expectedConsoleCalls = [
 ];
 let consoleCalls = [];
 
 let startTest = Task.async(function*() {
   removeEventListener("load", startTest);
 
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [
+      ["dom.serviceWorkers.enabled", true],
       ["devtools.webconsole.filter.serviceworkers", true]
     ]}, resolve);
   });
 
   attachConsoleToTab(["ConsoleAPI"], onAttach);
 });
 addEventListener("load", startTest);
 
--- a/devtools/shared/webconsole/test/test_console_serviceworker_cached.html
+++ b/devtools/shared/webconsole/test/test_console_serviceworker_cached.html
@@ -44,16 +44,17 @@ let secondTabExpectedCalls = [
   }
 ];
 
 let startTest = Task.async(function*() {
   removeEventListener("load", startTest);
 
   yield new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [
+      ["dom.serviceWorkers.enabled", true],
       ["devtools.webconsole.filter.serviceworkers", true]
     ]}, resolve);
   });
 
   info("Adding a tab and attaching a service worker");
   let tab1 = yield addTab(FRAME_URL);
   let swr = yield withActiveServiceWorker(tab1.linkedBrowser.contentWindow,
                                 SERVICE_WORKER_URL);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -14152,21 +14152,16 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
 #endif
 }
 
 NS_IMETHODIMP
 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
-  // Preffed off.
-  if (!nsContentUtils::ServiceWorkerInterceptionEnabled()) {
-    return NS_OK;
-  }
-
   // No in private browsing
   if (mInPrivateBrowsing) {
     return NS_OK;
   }
 
   if (mSandboxFlags) {
     // If we're sandboxed, don't intercept.
     return NS_OK;
--- a/docshell/base/timeline/AbstractTimelineMarker.h
+++ b/docshell/base/timeline/AbstractTimelineMarker.h
@@ -25,22 +25,22 @@ struct ProfileTimelineMarker;
 class AbstractTimelineMarker
 {
 private:
   AbstractTimelineMarker() = delete;
   AbstractTimelineMarker(const AbstractTimelineMarker& aOther) = delete;
   void operator=(const AbstractTimelineMarker& aOther) = delete;
 
 public:
-  explicit AbstractTimelineMarker(const char* aName,
-                                  MarkerTracingType aTracingType);
+  AbstractTimelineMarker(const char* aName,
+                         MarkerTracingType aTracingType);
 
-  explicit AbstractTimelineMarker(const char* aName,
-                                  const TimeStamp& aTime,
-                                  MarkerTracingType aTracingType);
+  AbstractTimelineMarker(const char* aName,
+                         const TimeStamp& aTime,
+                         MarkerTracingType aTracingType);
 
   virtual ~AbstractTimelineMarker();
 
   virtual UniquePtr<AbstractTimelineMarker> Clone();
   virtual bool Equals(const AbstractTimelineMarker& aOther);
 
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
   virtual JSObject* GetStack() = 0;
--- a/docshell/base/timeline/AutoTimelineMarker.h
+++ b/docshell/base/timeline/AutoTimelineMarker.h
@@ -33,18 +33,18 @@ class MOZ_RAII AutoTimelineMarker
 
   // The name of the marker we are adding.
   const char* mName;
 
   // The docshell that is associated with this marker.
   RefPtr<nsIDocShell> mDocShell;
 
 public:
-  explicit AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
-                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
+  AutoTimelineMarker(nsIDocShell* aDocShell,
+                     const char* aName MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~AutoTimelineMarker();
 
   AutoTimelineMarker(const AutoTimelineMarker& aOther) = delete;
   void operator=(const AutoTimelineMarker& aOther) = delete;
 };
 
 } // namespace mozilla
 
--- a/docshell/base/timeline/CompositeTimelineMarker.h
+++ b/docshell/base/timeline/CompositeTimelineMarker.h
@@ -10,18 +10,18 @@
 #include "TimelineMarker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 namespace mozilla {
 
 class CompositeTimelineMarker : public TimelineMarker
 {
 public:
-  explicit CompositeTimelineMarker(const TimeStamp& aTime,
-                                   MarkerTracingType aTracingType)
+  CompositeTimelineMarker(const TimeStamp& aTime,
+                          MarkerTracingType aTracingType)
     : TimelineMarker("Composite", aTime, aTracingType)
   {
     // Even though these markers end up being created on the main thread in the
     // content or chrome processes, they actually trace down code in the
     // compositor parent process. All the information for creating these markers
     // is sent along via IPC to an nsView when a composite finishes.
     // Mark this as 'off the main thread' to style it differently in frontends.
     SetOffMainThread(true);
--- a/docshell/base/timeline/ConsoleTimelineMarker.h
+++ b/docshell/base/timeline/ConsoleTimelineMarker.h
@@ -10,18 +10,18 @@
 #include "TimelineMarker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 namespace mozilla {
 
 class ConsoleTimelineMarker : public TimelineMarker
 {
 public:
-  explicit ConsoleTimelineMarker(const nsAString& aCause,
-                                 MarkerTracingType aTracingType)
+  ConsoleTimelineMarker(const nsAString& aCause,
+                        MarkerTracingType aTracingType)
     : TimelineMarker("ConsoleTime", aTracingType)
     , mCause(aCause)
   {
     // Stack is captured by default on the "start" marker. Explicitly also
     // capture stack on the "end" marker.
     if (aTracingType == MarkerTracingType::END) {
       CaptureStack();
     }
--- a/docshell/base/timeline/EventTimelineMarker.h
+++ b/docshell/base/timeline/EventTimelineMarker.h
@@ -10,19 +10,19 @@
 #include "TimelineMarker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 namespace mozilla {
 
 class EventTimelineMarker : public TimelineMarker
 {
 public:
-  explicit EventTimelineMarker(const nsAString& aType,
-                               uint16_t aPhase,
-                               MarkerTracingType aTracingType)
+  EventTimelineMarker(const nsAString& aType,
+                      uint16_t aPhase,
+                      MarkerTracingType aTracingType)
     : TimelineMarker("DOMEvent", aTracingType)
     , mType(aType)
     , mPhase(aPhase)
   {}
 
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
   {
     TimelineMarker::AddDetails(aCx, aMarker);
--- a/docshell/base/timeline/JavascriptTimelineMarker.h
+++ b/docshell/base/timeline/JavascriptTimelineMarker.h
@@ -12,23 +12,23 @@
 #include "mozilla/dom/RootedDictionary.h"
 #include "mozilla/dom/ToJSValue.h"
 
 namespace mozilla {
 
 class JavascriptTimelineMarker : public TimelineMarker
 {
 public:
-  explicit JavascriptTimelineMarker(const char* aReason,
-                                    const char16_t* aFunctionName,
-                                    const char16_t* aFileName,
-                                    uint32_t aLineNumber,
-                                    MarkerTracingType aTracingType,
-                                    JS::Handle<JS::Value> aAsyncStack,
-                                    JS::Handle<JS::Value> aAsyncCause)
+  JavascriptTimelineMarker(const char* aReason,
+                           const char16_t* aFunctionName,
+                           const char16_t* aFileName,
+                           uint32_t aLineNumber,
+                           MarkerTracingType aTracingType,
+                           JS::Handle<JS::Value> aAsyncStack,
+                           JS::Handle<JS::Value> aAsyncCause)
     : TimelineMarker("Javascript", aTracingType, MarkerStackRequest::NO_STACK)
     , mCause(NS_ConvertUTF8toUTF16(aReason))
     , mFunctionName(aFunctionName)
     , mFileName(aFileName)
     , mLineNumber(aLineNumber)
   {
     JSContext* ctx = nsContentUtils::GetCurrentJSContext();
     if (ctx) {
new file mode 100644
--- /dev/null
+++ b/docshell/base/timeline/MessagePortTimelineMarker.h
@@ -0,0 +1,47 @@
+/* -*- 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_MessagePortTimelineMarker_h_
+#define mozilla_MessagePortTimelineMarker_h_
+
+#include "TimelineMarker.h"
+#include "mozilla/dom/ProfileTimelineMarkerBinding.h"
+
+namespace mozilla {
+
+class MessagePortTimelineMarker : public TimelineMarker
+{
+public:
+  MessagePortTimelineMarker(dom::ProfileTimelineMessagePortOperationType aOperationType,
+                            MarkerTracingType aTracingType)
+    : TimelineMarker("MessagePort", aTracingType, MarkerStackRequest::NO_STACK)
+    , mOperationType(aOperationType)
+  {}
+
+  virtual UniquePtr<AbstractTimelineMarker> Clone() override
+  {
+    MessagePortTimelineMarker* clone =
+      new MessagePortTimelineMarker(mOperationType, GetTracingType());
+    clone->SetCustomTime(GetTime());
+    return UniquePtr<AbstractTimelineMarker>(clone);
+  }
+
+  virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
+  {
+    TimelineMarker::AddDetails(aCx, aMarker);
+
+    if (GetTracingType() == MarkerTracingType::START) {
+      aMarker.mMessagePortOperation.Construct(mOperationType);
+    }
+  }
+
+private:
+  dom::ProfileTimelineMessagePortOperationType mOperationType;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_MessagePortTimelineMarker_h_ */
--- a/docshell/base/timeline/RestyleTimelineMarker.h
+++ b/docshell/base/timeline/RestyleTimelineMarker.h
@@ -10,18 +10,18 @@
 #include "TimelineMarker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 namespace mozilla {
 
 class RestyleTimelineMarker : public TimelineMarker
 {
 public:
-  explicit RestyleTimelineMarker(nsRestyleHint aRestyleHint,
-                                 MarkerTracingType aTracingType)
+  RestyleTimelineMarker(nsRestyleHint aRestyleHint,
+                        MarkerTracingType aTracingType)
     : TimelineMarker("Styles", aTracingType)
   {
     if (aRestyleHint) {
       mRestyleHint.AssignWithConversion(RestyleManager::RestyleHintToString(aRestyleHint));
     }
   }
 
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override
--- a/docshell/base/timeline/TimelineMarker.h
+++ b/docshell/base/timeline/TimelineMarker.h
@@ -13,24 +13,24 @@
 namespace mozilla {
 
 // Objects of this type can be added to the timeline if there is an interested
 // consumer. The class can also be subclassed to let a given marker creator
 // provide custom details.
 class TimelineMarker : public AbstractTimelineMarker
 {
 public:
-  explicit TimelineMarker(const char* aName,
-                          MarkerTracingType aTracingType,
-                          MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+  TimelineMarker(const char* aName,
+                 MarkerTracingType aTracingType,
+                 MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
 
-  explicit TimelineMarker(const char* aName,
-                          const TimeStamp& aTime,
-                          MarkerTracingType aTracingType,
-                          MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
+  TimelineMarker(const char* aName,
+                 const TimeStamp& aTime,
+                 MarkerTracingType aTracingType,
+                 MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
 
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
   virtual JSObject* GetStack() override;
 
 protected:
   void CaptureStack();
 
 private:
--- a/docshell/base/timeline/WorkerTimelineMarker.h
+++ b/docshell/base/timeline/WorkerTimelineMarker.h
@@ -10,18 +10,18 @@
 #include "TimelineMarker.h"
 #include "mozilla/dom/ProfileTimelineMarkerBinding.h"
 
 namespace mozilla {
 
 class WorkerTimelineMarker : public TimelineMarker
 {
 public:
-  explicit WorkerTimelineMarker(ProfileTimelineWorkerOperationType aOperationType,
-                                MarkerTracingType aTracingType)
+  WorkerTimelineMarker(ProfileTimelineWorkerOperationType aOperationType,
+                       MarkerTracingType aTracingType)
     : TimelineMarker("Worker", aTracingType, MarkerStackRequest::NO_STACK)
     , mOperationType(aOperationType)
   {}
 
   virtual UniquePtr<AbstractTimelineMarker> Clone() override
   {
     WorkerTimelineMarker* clone = new WorkerTimelineMarker(mOperationType, GetTracingType());
     clone->SetCustomTime(GetTime());
--- a/docshell/base/timeline/moz.build
+++ b/docshell/base/timeline/moz.build
@@ -10,16 +10,17 @@ EXPORTS.mozilla += [
     'AutoTimelineMarker.h',
     'CompositeTimelineMarker.h',
     'ConsoleTimelineMarker.h',
     'DocLoadingTimelineMarker.h',
     'EventTimelineMarker.h',
     'JavascriptTimelineMarker.h',
     'LayerTimelineMarker.h',
     'MarkersStorage.h',
+    'MessagePortTimelineMarker.h',
     'ObservedDocShell.h',
     'RestyleTimelineMarker.h',
     'TimelineConsumers.h',
     'TimelineMarker.h',
     'TimelineMarkerEnums.h',
     'TimestampTimelineMarker.h',
     'WorkerTimelineMarker.h',
 ]
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -174,19 +174,17 @@ const mozilla::Module::ContractIDEntry k
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "license", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "logo", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "memory", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "mozilla", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "neterror", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "networking", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newaddon", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
-#ifdef NIGHTLY_BUILD
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "performance", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
-#endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "plugins", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "serviceworkers", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #ifndef ANDROID
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "profiles", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
 #endif
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "srcdoc", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "support", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
   { NS_ABOUT_MODULE_CONTRACTID_PREFIX "telemetry", &kNS_ABOUT_REDIRECTOR_MODULE_CID },
--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -45,17 +45,17 @@ ThreadSafeChromeUtils::NondeterministicG
     if (!JS_NondeterministicGetWeakSetKeys(cx, setObj, &objRet)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     } else {
       aRetval.set(objRet ? JS::ObjectValue(*objRet) : JS::UndefinedValue());
     }
   }
 }
 
-  /* static */ void
+/* static */ void
 ChromeUtils::OriginAttributesToSuffix(dom::GlobalObject& aGlobal,
                                       const dom::OriginAttributesDictionary& aAttrs,
                                       nsCString& aSuffix)
 
 {
   GenericOriginAttributes attrs(aAttrs);
   attrs.CreateSuffix(aSuffix);
 }
@@ -66,33 +66,46 @@ ChromeUtils::OriginAttributesMatchPatter
                                           const dom::OriginAttributesPatternDictionary& aPattern)
 {
   GenericOriginAttributes attrs(aAttrs);
   OriginAttributesPattern pattern(aPattern);
   return pattern.Matches(attrs);
 }
 
 /* static */ void
-ChromeUtils::CreateOriginAttributesWithUserContextId(dom::GlobalObject& aGlobal,
-                                                     const nsAString& aOrigin,
-                                                     uint32_t aUserContextId,
-                                                     dom::OriginAttributesDictionary& aAttrs,
-                                                     ErrorResult& aRv)
+ChromeUtils::CreateDefaultOriginAttributes(dom::GlobalObject& aGlobal,
+                                      dom::OriginAttributesDictionary& aAttrs)
+{
+  aAttrs = GenericOriginAttributes();
+}
+
+/* static */ void
+ChromeUtils::CreateOriginAttributesFromOrigin(dom::GlobalObject& aGlobal,
+                                       const nsAString& aOrigin,
+                                       dom::OriginAttributesDictionary& aAttrs,
+                                       ErrorResult& aRv)
 {
   GenericOriginAttributes attrs;
   nsAutoCString suffix;
   if (!attrs.PopulateFromOrigin(NS_ConvertUTF16toUTF8(aOrigin), suffix)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
-
-  attrs.mUserContextId = aUserContextId;
   aAttrs = attrs;
 }
 
+/* static */ void
+ChromeUtils::CreateOriginAttributesFromDict(dom::GlobalObject& aGlobal,
+                                 const dom::OriginAttributesDictionary& aAttrs,
+                                 dom::OriginAttributesDictionary& aNewAttrs)
+{
+  aNewAttrs = aAttrs;
+}
+
+
 /* static */ bool
 ChromeUtils::IsOriginAttributesEqual(dom::GlobalObject& aGlobal,
                                      const dom::OriginAttributesDictionary& aA,
                                      const dom::OriginAttributesDictionary& aB)
 {
   return aA.mAddonId == aB.mAddonId &&
          aA.mAppId == aB.mAppId &&
          aA.mInBrowser == aB.mInBrowser &&
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -54,21 +54,29 @@ public:
                            nsCString& aSuffix);
 
   static bool
   OriginAttributesMatchPattern(dom::GlobalObject& aGlobal,
                                const dom::OriginAttributesDictionary& aAttrs,
                                const dom::OriginAttributesPatternDictionary& aPattern);
 
   static void
-  CreateOriginAttributesWithUserContextId(dom::GlobalObject& aGlobal,
-                                          const nsAString& aOrigin,
-                                          uint32_t aUserContextId,
-                                          dom::OriginAttributesDictionary& aAttrs,
-                                          ErrorResult& aRv);
+  CreateDefaultOriginAttributes(dom::GlobalObject& aGlobal,
+                                dom::OriginAttributesDictionary& aAttrs);
+
+  static void
+  CreateOriginAttributesFromOrigin(dom::GlobalObject& aGlobal,
+                                   const nsAString& aOrigin,
+                                   dom::OriginAttributesDictionary& aAttrs,
+                                   ErrorResult& aRv);
+
+  static void
+  CreateOriginAttributesFromDict(dom::GlobalObject& aGlobal,
+                                 const dom::OriginAttributesDictionary& aAttrs,
+                                 dom::OriginAttributesDictionary& aNewAttrs);
 
   static bool
   IsOriginAttributesEqual(dom::GlobalObject& aGlobal,
                           const dom::OriginAttributesDictionary& aA,
                           const dom::OriginAttributesDictionary& aB);
 };
 
 } // namespace dom
--- a/dom/base/StructuredCloneHolder.cpp
+++ b/dom/base/StructuredCloneHolder.cpp
@@ -290,16 +290,17 @@ StructuredCloneHolder::Write(JSContext* 
 void
 StructuredCloneHolder::Read(nsISupports* aParent,
                             JSContext* aCx,
                             JS::MutableHandle<JS::Value> aValue,
                             ErrorResult& aRv)
 {
   MOZ_ASSERT_IF(mSupportedContext == SameProcessSameThread,
                 mCreationThread == NS_GetCurrentThread());
+  MOZ_ASSERT(aParent);
 
   mozilla::AutoRestore<nsISupports*> guard(mParent);
   mParent = aParent;
 
   if (!StructuredCloneHolderBase::Read(aCx, aValue)) {
     JS_ClearPendingException(aCx);
     aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
   }
@@ -1039,19 +1040,21 @@ StructuredCloneHolder::CustomReadTransfe
                                                  JS::MutableHandleObject aReturnObject)
 {
   MOZ_ASSERT(mSupportsTransferring);
 
   if (aTag == SCTAG_DOM_MAP_MESSAGEPORT) {
     MOZ_ASSERT(aExtraData < mPortIdentifiers.Length());
     const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData];
 
+    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent);
+
     ErrorResult rv;
     RefPtr<MessagePort> port =
-      MessagePort::Create(mParent, portIdentifier, rv);
+      MessagePort::Create(global, portIdentifier, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return false;
     }
 
     mTransferredPorts.AppendElement(port);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!GetOrCreateDOMReflector(aCx, port, &value)) {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -269,17 +269,16 @@ bool nsContentUtils::sIsFrameTimingPrefE
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sEncodeDecodeURLHash = false;
 bool nsContentUtils::sGettersDecodeURLHash = false;
 bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
-bool nsContentUtils::sSWInterceptionEnabled = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
 uint32_t nsContentUtils::sCookiesLifetimePolicy = nsICookieService::ACCEPT_NORMALLY;
 uint32_t nsContentUtils::sCookiesBehavior = nsICookieService::BEHAVIOR_ACCEPT;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
@@ -564,20 +563,16 @@ nsContentUtils::Init()
                                "dom.url.encode_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
                                "dom.url.getters_decode_hash", false);
 
   Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
                                "privacy.resistFingerprinting", false);
 
-  Preferences::AddBoolVarCache(&sSWInterceptionEnabled,
-                               "dom.serviceWorkers.interception.enabled",
-                               false);
-
   Preferences::AddUintVarCache(&sHandlingInputTimeout,
                                "dom.event.handling-user-input-time-limit",
                                1000);
 
   Preferences::AddBoolVarCache(&sSendPerformanceTimingNotifications,
                                "dom.performance.enable_notify_performance_timing", false);
 
   Preferences::AddUintVarCache(&sCookiesLifetimePolicy,
@@ -1749,18 +1744,17 @@ nsContentUtils::ParseLegacyFontSize(cons
 
   return clamped(value, 1, 7);
 }
 
 /* static */
 bool
 nsContentUtils::IsControlledByServiceWorker(nsIDocument* aDocument)
 {
-  if (!ServiceWorkerInterceptionEnabled() ||
-      nsContentUtils::IsInPrivateBrowsing(aDocument)) {
+  if (nsContentUtils::IsInPrivateBrowsing(aDocument)) {
     return false;
   }
 
   RefPtr<workers::ServiceWorkerManager> swm =
     workers::ServiceWorkerManager::GetInstance();
   MOZ_ASSERT(swm);
 
   ErrorResult rv;
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -1992,24 +1992,16 @@ public:
    * Returns true if notification should be sent for peformance timing events.
    */
   static bool SendPerformanceTimingNotifications()
   {
     return sSendPerformanceTimingNotifications;
   }
 
   /*
-   * Returns true if ServiceWorker Interception is enabled by pref.
-   */
-  static bool ServiceWorkerInterceptionEnabled()
-  {
-    return sSWInterceptionEnabled;
-  }
-
-  /*
    * Returns true if the frame timing APIs are enabled.
    */
   static bool IsFrameTimingEnabled();
 
   /*
    * Returns true if URL setters should percent encode the Hash/Ref segment
    * and getters should return the percent decoded value of the segment
    */
@@ -2701,17 +2693,16 @@ private:
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
   static bool sEncodeDecodeURLHash;
   static bool sGettersDecodeURLHash;
   static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
-  static bool sSWInterceptionEnabled;
   static uint32_t sCookiesLifetimePolicy;
   static uint32_t sCookiesBehavior;
 
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
--- a/dom/canvas/test/test_offscreencanvas_serviceworker.html
+++ b/dom/canvas/test/test_offscreencanvas_serviceworker.html
@@ -38,16 +38,15 @@ function runTest() {
       document.body.appendChild(iframe);
     })
 }
 
 SpecialPowers.pushPrefEnv({'set': [
   ['gfx.offscreencanvas.enabled', true],
   ['webgl.force-enabled', true],
   ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-  ["dom.serviceWorkers.interception.enabled", true],
   ["dom.serviceWorkers.enabled", true],
   ["dom.serviceWorkers.testing.enabled", true]
 ]}, runTest);
 
 </script>
 </body>
 </html>
--- a/dom/events/test/test_bug238987.html
+++ b/dom/events/test/test_bug238987.html
@@ -26,26 +26,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   var backwardFocusArray = expectedResult.split(",");
   var forwardBlurArray = expectedResult.split(",");
   var backwardBlurArray = expectedResult.split(",");
   // Adding 3 for "begin", "end", "begin" and one for the <a> in the Mochitest template,
   var expectedWindowFocusCount = forwardFocusArray.length + backwardFocusArray.length + 4;
   // but the last blur event goes to i1, not "begin".
   var expectedWindowBlurCount = forwardFocusArray.length + backwardFocusArray.length + 3;
 
-  // accessibility.tabfocus must be set to value 7 before running test also
-  // on a mac.
-  function setOrRestoreTabFocus(newValue) {
-    if (!newValue) {
-      SpecialPowers.clearUserPref("accessibility.tabfocus");
-    } else {
-      SpecialPowers.setIntPref("accessibility.tabfocus", newValue);
-    }
-  }
-
   function handleFocus(e) {
     if (e.target.id == "begin") {
       // if the modifier is set, the test is coming back from the end.
       if (modifier) {
         shouldStop = true;
       }
     } else if (e.target.id == "end") {
       modifier = Components.interfaces.nsIDOMEvent.SHIFT_MASK;
@@ -123,17 +113,16 @@ https://bugzilla.mozilla.org/show_bug.cg
          forwardBlurArray.toString());
       is(backwardBlurArray.length, 0,
          "Not all backward tabbing blur tests were run, " +
          backwardBlurArray.toString());
       is(expectedWindowBlurCount, 0,
          "|window| didn't get the right amount of blur events");
 
       // Cleanup
-      setOrRestoreTabFocus(0);
       window.removeEventListener("focus", handleWindowFocus, true);
       window.removeEventListener("focus", handleWindowFocus, false);
       window.removeEventListener("blur", handleWindowBlur, true);
       window.removeEventListener("blur", handleWindowBlur, false);
       var elements = document.getElementsByTagName("*");
       for (var i = 0; i < elements.length; ++i) {
         if (elements[i].hasAttribute("id")) {
           elements[i].removeEventListener("focus", handleFocus, false);
@@ -161,19 +150,20 @@ https://bugzilla.mozilla.org/show_bug.cg
       }
       if (elements[i].getAttribute("tabindex") == "1") {
         elements[i].setAttribute("tabindex", "-1");
       }
     }
     tab();
   }
 
+  // accessibility.tabfocus must be set to value 7 before running test also
+  // on a mac.
   function doTest() {
-    setOrRestoreTabFocus(7);
-    setTimeout(start, 0);
+    SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]}, start);
   }
 
   SimpleTest.waitForExplicitFinish();
   addLoadEvent(doTest);
 
 </script>
 </pre>
   <h4 tabindex="0" id="begin">Test:</h4>
--- a/dom/events/test/test_bug409604.html
+++ b/dom/events/test/test_bug409604.html
@@ -85,25 +85,16 @@ https://bugzilla.mozilla.org/show_bug.cg
       "tbody",
       "td",
       "tfoot",
       "th",
       "thead",
       "tr"
     ];
 
-  // ui.key.contentAccess must be set to value 5 before running the test.
-  function setOrRestoreContentAccess(newValue) {
-    if (!newValue) {
-      SpecialPowers.clearUserPref("ui.key.contentAccess");
-    } else {
-      SpecialPowers.setIntPref("ui.key.contentAccess", newValue);
-    }
-  }
-
   function handleFocus(e) {
     ok("accessKey" in e, "(focus) accesskey property not found on element");
     var expected = focusArray.shift();
     // "k" and "n" are a special cases because the element receiving the focus
     // is not the element which has the accesskey.
     if (expected == "k" || expected == "n") {
       ok(e.value == "test for label", "(focus) unexpected element: " + e.value +
          " expected: " + "test for label");
@@ -213,27 +204,24 @@ https://bugzilla.mozilla.org/show_bug.cg
       e.removeAttribute("onclick");
       e.removeAttribute("onfocus");
     }
   }
 
   function start() {
     testFocusableElements();
     testUnfocusableElements();
-    setOrRestoreContentAccess(0);
     SimpleTest.finish();
   }
 
   function doTest() {
-    setOrRestoreContentAccess(5);
-    setTimeout(start, 100);
+    SpecialPowers.pushPrefEnv({"set": [["ui.key.contentAccess", 5]]}, start);
   }
 
   SimpleTest.waitForExplicitFinish();
-  SimpleTest.requestFlakyTimeout("untriaged");
   addLoadEvent(doTest);
 
 </script>
 </pre>
   <table id="table">
     <thead id="thead">
       <tr id="tr"><th id="th">Test header</th><th></th></tr>
     </thead>
--- a/dom/events/test/test_bug457672.html
+++ b/dom/events/test/test_bug457672.html
@@ -16,45 +16,37 @@ https://bugzilla.mozilla.org/show_bug.cg
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 457672 **/
 
 var windowBlurCount = 0;
 
-function setUserPref(reset) {
-  if (reset) {
-    SpecialPowers.clearUserPref("browser.link.open_newwindow");
-  } else {
-    SpecialPowers.setIntPref("browser.link.open_newwindow", 3);
-  }
-}
-
 function listener(evt) {
   if (evt.type == "focus") {
     is(windowBlurCount, 1,
        "Window should have got blur event when opening a new tab!");
-    setUserPref(true);
     document.getElementsByTagName("a")[0].focus();
     SimpleTest.finish();
   } else if (evt.type == "blur") {
     ++windowBlurCount;
   }
   document.getElementById('log').textContent += evt.target + ":" + evt.type + "\n";
 }
 
 function startTest() {
-  setUserPref(false);
-  document.getElementsByTagName("a")[0].focus();
-  // Note, focus/blur don't bubble
-  window.addEventListener("focus", listener, false);
-  window.addEventListener("blur", listener, false);
-  var subwin = window.open("about:blank", "", "");
-  subwin.addEventListener("focus", function(e) { subwin.close(); }, false);
+  SpecialPowers.pushPrefEnv({"set": [["browser.link.open_newwindow", 3]]}, function() {
+    document.getElementsByTagName("a")[0].focus();
+    // Note, focus/blur don't bubble
+    window.addEventListener("focus", listener, false);
+    window.addEventListener("blur", listener, false);
+    var subwin = window.open("about:blank", "", "");
+    subwin.addEventListener("focus", function(e) { subwin.close(); }, false);
+  });
 }
 
 addLoadEvent(startTest);
 SimpleTest.waitForExplicitFinish();
 
 </script>
 </pre>
 <pre id="log">
--- a/dom/events/test/test_moz_mouse_pixel_scroll_event.html
+++ b/dom/events/test/test_moz_mouse_pixel_scroll_event.html
@@ -40,17 +40,17 @@
 </div>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
-SimpleTest.waitForFocus(runTest, window);
+SimpleTest.waitForFocus(startTest, window);
 
 var gScrollable128 = document.getElementById("Scrollable128");
 var gScrollable96 = document.getElementById("Scrollable96");
 var gScrollable64 = document.getElementById("Scrollable64");
 var gScrollable32 = document.getElementById("Scrollable32");
 var gRoot = document.documentElement;
 
 function* prepareScrollUnits()
@@ -1310,71 +1310,54 @@ function* doTests()
     currentTest.cleanup();
   }
 
   window.removeEventListener("MozMousePixelScroll", handler, true);
 }
 
 function* testBody()
 {
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.default.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_alt.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_control.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_meta.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_shift.delta_multiplier_z", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_x", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_y", 100);
-  SpecialPowers.setIntPref("mousewheel.with_win.delta_multiplier_z", 100);
-  // If APZ is enabled we should ensure the preventDefault calls work even
-  // if the test is running slowly.
-  SpecialPowers.setIntPref("apz.content_response_timeout", 2000);
-
   yield* prepareScrollUnits();
   yield* doTests();
-
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.default.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_alt.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_control.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_meta.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_shift.delta_multiplier_z");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_x");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_y");
-  SpecialPowers.clearUserPref("mousewheel.with_win.delta_multiplier_z");
-  SpecialPowers.clearUserPref("apz.content_response_timeout");
 }
 
 var gTestContinuation = null;
 
 function runTest()
 {
   if (!gTestContinuation) {
     gTestContinuation = testBody();
   }
   var ret = gTestContinuation.next();
   if (ret.done) {
     SimpleTest.finish();
   }
 }
 
+function startTest() {
+  SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+                                     ["mousewheel.default.delta_multiplier_y", 100],
+                                     ["mousewheel.default.delta_multiplier_z", 100],
+                                     ["mousewheel.with_alt.delta_multiplier_x", 100],
+                                     ["mousewheel.with_alt.delta_multiplier_y", 100],
+                                     ["mousewheel.with_alt.delta_multiplier_z", 100],
+                                     ["mousewheel.with_control.delta_multiplier_x", 100],
+                                     ["mousewheel.with_control.delta_multiplier_y", 100],
+                                     ["mousewheel.with_control.delta_multiplier_z", 100],
+                                     ["mousewheel.with_meta.delta_multiplier_x", 100],
+                                     ["mousewheel.with_meta.delta_multiplier_y", 100],
+                                     ["mousewheel.with_meta.delta_multiplier_z", 100],
+                                     ["mousewheel.with_shift.delta_multiplier_x", 100],
+                                     ["mousewheel.with_shift.delta_multiplier_y", 100],
+                                     ["mousewheel.with_shift.delta_multiplier_z", 100],
+                                     ["mousewheel.with_win.delta_multiplier_x", 100],
+                                     ["mousewheel.with_win.delta_multiplier_y", 100],
+                                     ["mousewheel.with_win.delta_multiplier_z", 100],
+  // If APZ is enabled we should ensure the preventDefault calls work even
+  // if the test is running slowly.
+                                     ["apz.content_response_timeout", 2000],
+                                    ]}, runTest);
+}
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/events/test/window_bug659071.html
+++ b/dom/events/test/window_bug659071.html
@@ -5,17 +5,17 @@
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <video id="v" controls></video>
 <script type="application/javascript">
 
-SimpleTest.waitForFocus(runTests, window);
+SimpleTest.waitForFocus(startTests, window);
 SimpleTest.requestFlakyTimeout("untriaged");
 
 function is()
 {
   window.opener.is.apply(window.opener, arguments);
 }
 
 function isnot()
@@ -27,19 +27,22 @@ function hitEventLoop(aFunc, aTimes)
 {
   if (--aTimes) {
     setTimeout(hitEventLoop, 0, aFunc, aTimes);
   } else {
     setTimeout(aFunc, 20);
   }
 }
 
+function startTests() {
+  SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_control.action", 3]]}, runTests);
+}
+
 function runTests()
 {
-  SpecialPowers.setIntPref("mousewheel.with_control.action", 3);
   synthesizeKey("0", { accelKey: true });
 
   var video = document.getElementById("v");
   hitEventLoop(function () {
     is(SpecialPowers.getFullZoom(window), 1.0,
        "failed to reset zoom");
     synthesizeWheel(video, 10, 10,
       { deltaMode: WheelEvent.DOM_DELTA_LINE, ctrlKey: true,
@@ -50,18 +53,16 @@ function runTests()
 
       synthesizeWheel(video, 10, 10,
         { deltaMode: WheelEvent.DOM_DELTA_LINE, ctrlKey: true,
           deltaX: 0, deltaY: 1.0, lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 });
       hitEventLoop(function () {
         is(SpecialPowers.getFullZoom(window), 1.0,
            "failed to reset zoom");
 
-        SpecialPowers.clearUserPref("mousewheel.with_control.action");
-
         hitEventLoop(window.opener.finish, 20);
       }, 20);
     }, 20);
   }, 20);
 }
 
 </script>
 </body>
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -43,16 +43,17 @@
 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/dom/quota/Client.h"
 #include "mozilla/dom/quota/FileStreams.h"
+#include "mozilla/dom/quota/OriginScope.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/UsageInfo.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/InputStreamParams.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/PBackground.h"
 #include "mozilla/storage/Variant.h"
@@ -123,19 +124,21 @@ using namespace mozilla::dom::quota;
 using namespace mozilla::ipc;
 
 namespace {
 
 class ConnectionPool;
 class Cursor;
 class Database;
 struct DatabaseActorInfo;
+class DatabaseFile;
 class DatabaseLoggingInfo;
-class DatabaseFile;
+class DatabaseMaintenance;
 class Factory;
+class Maintenance;
 class MutableFile;
 class OpenDatabaseOp;
 class TransactionBase;
 class TransactionDatabaseOperationBase;
 class VersionChangeTransaction;
 
 /*******************************************************************************
  * Constants
@@ -7174,16 +7177,17 @@ protected:
 
   RefPtr<FactoryOp> mDelayedOp;
   nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
 
   const CommonFactoryRequestParams mCommonParams;
   nsCString mGroup;
   nsCString mOrigin;
   nsCString mDatabaseId;
+  nsString mDatabaseFilePath;
   State mState;
   bool mIsApp;
   bool mEnforcingQuota;
   const bool mDeleting;
   bool mBlockedDatabaseOpen;
   bool mChromeWriteAccessAllowed;
   bool mFileHandleDisabled;
 
@@ -7197,16 +7201,22 @@ public:
 #ifdef DEBUG
   bool
   HasBlockedDatabases() const
   {
     return !mMaybeBlockedDatabases.IsEmpty();
   }
 #endif
 
+  const nsString&
+  DatabaseFilePath() const
+  {
+    return mDatabaseFilePath;
+  }
+
 protected:
   FactoryOp(Factory* aFactory,
             already_AddRefed<ContentParent> aContentParent,
             const CommonFactoryRequestParams& aCommonParams,
             bool aDeleting);
 
   virtual
   ~FactoryOp()
@@ -7287,23 +7297,23 @@ private:
   nsresult
   CheckPermission(ContentParent* aContentParent,
                   PermissionRequestBase::PermissionValue* aPermission);
 
   static bool
   CheckAtLeastOneAppHasPermission(ContentParent* aContentParent,
                                   const nsACString& aPermissionString);
 
-  void
+  nsresult
   FinishOpen();
 
   nsresult
   QuotaManagerOpen();
 
-  void
+  nsresult
   OpenDirectory();
 
   // Test whether this FactoryOp needs to wait for the given op.
   bool
   MustWaitFor(const FactoryOp& aExistingOp);
 };
 
 struct FactoryOp::MaybeBlockedDatabaseInfo final
@@ -7352,17 +7362,16 @@ class OpenDatabaseOp final
 
   class VersionChangeOp;
 
   OptionalContentId mOptionalContentParentId;
 
   RefPtr<FullDatabaseMetadata> mMetadata;
 
   uint64_t mRequestedVersion;
-  nsString mDatabaseFilePath;
   RefPtr<FileManager> mFileManager;
 
   RefPtr<Database> mDatabase;
   RefPtr<VersionChangeTransaction> mVersionChangeTransaction;
 
   // This is only set while a VersionChangeOp is live. It holds a strong
   // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
   // cycles.
@@ -8752,60 +8761,22 @@ private:
   }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(BlobImplStoredFile, BLOB_IMPL_STORED_FILE_IID)
 
 class QuotaClient final
   : public mozilla::dom::quota::Client
 {
-  // The minimum amount of time that has passed since the last vacuum before we
-  // will attempt to analyze the database for fragmentation.
-  static const PRTime kMinVacuumAge =
-    PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7;
-
-  // If the percent of database pages that are not in contiguous order is higher
-  // than this percentage we will attempt a vacuum.
-  static const int32_t kPercentUnorderedThreshold = 30;
-
-  // If the percent of file size growth since the last vacuum is higher than
-  // this percentage we will attempt a vacuum.
-  static const int32_t kPercentFileSizeGrowthThreshold = 10;
-
-  // The number of freelist pages beyond which we will favor an incremental
-  // vacuum over a full vacuum.
-  static const int32_t kMaxFreelistThreshold = 5;
-
-  // If the percent of unused file bytes in the database exceeds this percentage
-  // then we will attempt a full vacuum.
-  static const int32_t kPercentUnusedThreshold = 20;
-
-  class AutoProgressHandler;
-  class GetDirectoryLockListener;
-  struct MaintenanceInfoBase;
-  struct MultipleMaintenanceInfo;
-  struct SingleMaintenanceInfo;
-
-  typedef nsClassHashtable<nsCStringHashKey, MultipleMaintenanceInfo>
-          MaintenanceInfoHashtable;
-
-  enum class MaintenanceAction
-  {
-    Nothing = 0,
-    IncrementalVacuum,
-    FullVacuum
-  };
-
   static QuotaClient* sInstance;
 
   nsCOMPtr<nsIEventTarget> mBackgroundThread;
+  nsTArray<RefPtr<Maintenance>> mMaintenanceQueue;
+  RefPtr<Maintenance> mCurrentMaintenance;
   RefPtr<nsThreadPool> mMaintenanceThreadPool;
-  PRTime mMaintenanceStartTime;
-  Atomic<uint32_t> mMaintenanceRunId;
-  UniquePtr<MaintenanceInfoHashtable> mMaintenanceInfoHashtable;
   bool mShutdownRequested;
 
 public:
   QuotaClient();
 
   static QuotaClient*
   GetInstance()
   {
@@ -8829,34 +8800,51 @@ public:
   static bool
   IsShuttingDownOnNonBackgroundThread()
   {
     MOZ_ASSERT(!IsOnBackgroundThread());
 
     return QuotaManager::IsShuttingDown();
   }
 
+  nsIEventTarget*
+  BackgroundThread() const
+  {
+    MOZ_ASSERT(mBackgroundThread);
+    return mBackgroundThread;
+  }
+
   bool
   IsShuttingDown() const
   {
     AssertIsOnBackgroundThread();
 
     return mShutdownRequested;
   }
 
-  bool
-  IdleMaintenanceMustEnd(uint32_t aRunId) const
-  {
-    if (mMaintenanceRunId != aRunId) {
-      MOZ_ASSERT(mMaintenanceRunId > aRunId);
-      return true;
-    }
-
-    return false;
-  }
+  already_AddRefed<Maintenance>
+  GetCurrentMaintenance() const
+  {
+    RefPtr<Maintenance> result = mCurrentMaintenance;
+    return result.forget();
+  }
+
+  void
+  NoteFinishedMaintenance(Maintenance* aMaintenance)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aMaintenance);
+    MOZ_ASSERT(mCurrentMaintenance = aMaintenance);
+
+    mCurrentMaintenance = nullptr;
+    ProcessMaintenanceQueue();
+  }
+
+  nsThreadPool*
+  GetOrCreateThreadPool();
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(QuotaClient, override)
 
   virtual mozilla::dom::quota::Client::Type
   GetType() override;
 
   virtual nsresult
   InitOrigin(PersistenceType aPersistenceType,
@@ -8901,112 +8889,387 @@ private:
                const nsACString& aOrigin,
                nsIFile** aDirectory);
 
   nsresult
   GetUsageForDirectoryInternal(nsIFile* aDirectory,
                                UsageInfo* aUsageInfo,
                                bool aDatabaseFiles);
 
-  void
-  CreateManager();
-
-  void
-  StartIdleMaintenanceInternal();
-
-  // Runs on mMaintenanceThreadPool. Once it finds databases it will queue a
-  // runnable that calls GetDirectoryLockForIdleMaintenance.
-  void
-  FindDatabasesForIdleMaintenance(uint32_t aRunId);
-
-  // Runs on the main thread. Once QuotaManager has given a lock it will call
-  // ScheduleIdleMaintenance.
-  void
-  GetDirectoryLockForIdleMaintenance(
-                                    uint32_t aRunId,
-                                    MultipleMaintenanceInfo&& aMaintenanceInfo);
-
-  // Runs on the main thread. It dispatches a runnable for each database that
-  // will call PerformIdleMaintenanceOnDatabase.
-  void
-  ScheduleIdleMaintenance(uint32_t aRunId,
-                          const nsACString& aKey,
-                          const MultipleMaintenanceInfo& aMaintenanceInfo);
-
-  // Runs on mMaintenanceThreadPool. Does maintenance on one database and then
-  // dispatches a runnable back to the main thread to call
-  // MaybeReleaseDirectoryLockForIdleMaintenance.
-  void
-  PerformIdleMaintenanceOnDatabase(uint32_t aRunId,
-                                   const nsACString& aKey,
-                                   SingleMaintenanceInfo&& aMaintenanceInfo);
-
-  // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase.
-  void
-  PerformIdleMaintenanceOnDatabaseInternal(
-                                 uint32_t aRunId,
-                                 const SingleMaintenanceInfo& aMaintenanceInfo);
-
-  // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase.
+  // Runs on the PBackground thread. Checks to see if there's a queued
+  // Maintenance to run.
+  void
+  ProcessMaintenanceQueue();
+};
+
+class Maintenance final
+  : public nsRunnable
+  , public OpenDirectoryListener
+{
+  struct DirectoryInfo;
+
+  enum class State
+  {
+    // Newly created on the PBackground thread. Will proceed immediately or be
+    // added to the maintenance queue. The next step is either
+    // DirectoryOpenPending if IndexedDatabaseManager is running, or
+    // CreateIndexedDatabaseManager if not.
+    Initial = 0,
+
+    // Create IndexedDatabaseManager on the main thread. The next step is either
+    // Finishing if IndexedDatabaseManager initialization fails, or
+    // IndexedDatabaseManagerOpen if initialization succeeds.
+    CreateIndexedDatabaseManager,
+
+    // Call OpenDirectory() on the PBackground thread. The next step is
+    // DirectoryOpenPending.
+    IndexedDatabaseManagerOpen,
+
+    // Waiting for directory open allowed on the PBackground thread. The next
+    // step is either Finishing if directory lock failed to acquire, or
+    // DirectoryWorkOpen if directory lock is acquired.
+    DirectoryOpenPending,
+
+    // Waiting to do/doing work on the QuotaManager IO thread. The next step is
+    // BeginDatabaseMaintenance.
+    DirectoryWorkOpen,
+
+    // Dispatching a runnable for each database on the PBackground thread. The
+    // next state is either WaitingForDatabaseMaintenancesToComplete if at least
+    // one runnable has been dispatched, or Finishing otherwise.
+    BeginDatabaseMaintenance,
+
+    // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
+    // The next state is Finishing if the last runnable has finished.
+    WaitingForDatabaseMaintenancesToComplete,
+
+    // Waiting to finish/finishing on the PBackground thread. The next step is
+    // Completed.
+    Finishing,
+
+    // All done.
+    Complete
+  };
+
+  RefPtr<QuotaClient> mQuotaClient;
+  PRTime mStartTime;
+  RefPtr<DirectoryLock> mDirectoryLock;
+  nsTArray<DirectoryInfo> mDirectoryInfos;
+  nsDataHashtable<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
+  Atomic<bool> mAborted;
+  State mState;
+
+public:
+  explicit Maintenance(QuotaClient* aQuotaClient)
+    : mQuotaClient(aQuotaClient)
+    , mStartTime(PR_Now())
+    , mAborted(false)
+    , mState(State::Initial)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(aQuotaClient);
+    MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient);
+    MOZ_ASSERT(mStartTime);
+  }
+
+  nsIEventTarget*
+  BackgroundThread() const
+  {
+    MOZ_ASSERT(mQuotaClient);
+    return mQuotaClient->BackgroundThread();
+  }
+
+  PRTime
+  StartTime() const
+  {
+    return mStartTime;
+  }
+
+  bool
+  IsAborted() const
+  {
+    return mAborted;
+  }
+
+  void
+  RunImmediately()
+  {
+    MOZ_ASSERT(mState == State::Initial);
+
+    Unused << this->Run();
+  }
+
+  void
+  Abort()
+  {
+    AssertIsOnBackgroundThread();
+
+    mAborted = true;
+  }
+
+  void
+  RegisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
+
+  void
+  UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
+
+  already_AddRefed<DatabaseMaintenance>
+  GetDatabaseMaintenance(const nsAString& aDatabasePath) const
+  {
+    AssertIsOnBackgroundThread();
+
+    RefPtr<DatabaseMaintenance> result =
+      mDatabaseMaintenances.Get(aDatabasePath);
+    return result.forget();
+  }
+
+private:
+  ~Maintenance()
+  {
+    MOZ_ASSERT(mState == State::Complete);
+    MOZ_ASSERT(!mDatabaseMaintenances.Count());
+  }
+
+  // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
+  // running. Calls OpenDirectory() or dispatches to the main thread on which
+  // CreateIndexedDatabaseManager() is called.
+  nsresult
+  Start();
+
+  // Runs on the main thread. Once IndexedDatabaseManager is created it will
+  // dispatch to the PBackground thread on which OpenDirectory() is called.
+  nsresult
+  CreateIndexedDatabaseManager();
+
+  // Runs on the PBackground thread. Once QuotaManager has given a lock it will
+  // call DirectoryOpen().
+  nsresult
+  OpenDirectory();
+
+  // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
+  nsresult
+  DirectoryOpen();
+
+  // Runs on the QuotaManager I/O thread. Once it finds databases it will
+  // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
+  // is called.
+  nsresult
+  DirectoryWork();
+
+  // Runs on the PBackground thread. It dispatches a runnable for each database.
+  nsresult
+  BeginDatabaseMaintenance();
+
+  // Runs on the PBackground thread. Called when the maintenance is finished or
+  // if any of above methods fails.
+  void
+  Finish();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIRUNNABLE
+
+  // OpenDirectoryListener overrides.
+  virtual void
+  DirectoryLockAcquired(DirectoryLock* aLock) override;
+
+  virtual void
+  DirectoryLockFailed() override;
+};
+
+struct Maintenance::DirectoryInfo final
+{
+  const nsCString mGroup;
+  const nsCString mOrigin;
+  nsTArray<nsString> mDatabasePaths;
+  const PersistenceType mPersistenceType;
+
+  DirectoryInfo(PersistenceType aPersistenceType,
+                const nsACString& aGroup,
+                const nsACString& aOrigin,
+                nsTArray<nsString>&& aDatabasePaths)
+   : mGroup(aGroup)
+   , mOrigin(aOrigin)
+   , mDatabasePaths(Move(aDatabasePaths))
+   , mPersistenceType(aPersistenceType)
+  {
+    MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
+    MOZ_ASSERT(!aGroup.IsEmpty());
+    MOZ_ASSERT(!aOrigin.IsEmpty());
+#ifdef DEBUG
+    MOZ_ASSERT(!mDatabasePaths.IsEmpty());
+    for (const nsString& databasePath : mDatabasePaths) {
+      MOZ_ASSERT(!databasePath.IsEmpty());
+    }
+#endif
+
+    MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
+  }
+
+  DirectoryInfo(DirectoryInfo&& aOther)
+    : mGroup(Move(aOther.mGroup))
+    , mOrigin(Move(aOther.mOrigin))
+    , mDatabasePaths(Move(aOther.mDatabasePaths))
+    , mPersistenceType(Move(aOther.mPersistenceType))
+  {
+#ifdef DEBUG
+    MOZ_ASSERT(!mDatabasePaths.IsEmpty());
+    for (const nsString& databasePath : mDatabasePaths) {
+      MOZ_ASSERT(!databasePath.IsEmpty());
+    }
+#endif
+
+    MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
+  }
+
+  ~DirectoryInfo()
+  {
+    MOZ_COUNT_DTOR(Maintenance::DirectoryInfo);
+  }
+
+  DirectoryInfo(const DirectoryInfo& aOther) = delete;
+};
+
+class DatabaseMaintenance final
+  : public nsRunnable
+{
+  // The minimum amount of time that has passed since the last vacuum before we
+  // will attempt to analyze the database for fragmentation.
+  static const PRTime kMinVacuumAge =
+    PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7;
+
+  // If the percent of database pages that are not in contiguous order is higher
+  // than this percentage we will attempt a vacuum.
+  static const int32_t kPercentUnorderedThreshold = 30;
+
+  // If the percent of file size growth since the last vacuum is higher than
+  // this percentage we will attempt a vacuum.
+  static const int32_t kPercentFileSizeGrowthThreshold = 10;
+
+  // The number of freelist pages beyond which we will favor an incremental
+  // vacuum over a full vacuum.
+  static const int32_t kMaxFreelistThreshold = 5;
+
+  // If the percent of unused file bytes in the database exceeds this percentage
+  // then we will attempt a full vacuum.
+  static const int32_t kPercentUnusedThreshold = 20;
+
+  class AutoProgressHandler;
+
+  enum class MaintenanceAction
+  {
+    Nothing = 0,
+    IncrementalVacuum,
+    FullVacuum
+  };
+
+  RefPtr<Maintenance> mMaintenance;
+  const nsCString mGroup;
+  const nsCString mOrigin;
+  const nsString mDatabasePath;
+  nsCOMPtr<nsIRunnable> mCompleteCallback;
+  const PersistenceType mPersistenceType;
+
+public:
+  DatabaseMaintenance(Maintenance* aMaintenance,
+                      PersistenceType aPersistenceType,
+                      const nsCString& aGroup,
+                      const nsCString& aOrigin,
+                      const nsString& aDatabasePath)
+    : mMaintenance(aMaintenance)
+    , mGroup(aGroup)
+    , mOrigin(aOrigin)
+    , mDatabasePath(aDatabasePath)
+    , mPersistenceType(aPersistenceType)
+  { }
+
+  const nsString&
+  DatabasePath() const
+  {
+    return mDatabasePath;
+  }
+
+  void
+  WaitForCompletion(nsIRunnable* aCallback)
+  {
+    AssertIsOnBackgroundThread();
+    MOZ_ASSERT(!mCompleteCallback);
+
+    mCompleteCallback = aCallback;
+  }
+
+private:
+  ~DatabaseMaintenance()
+  { }
+
+  // Runs on maintenance thread pool. Does maintenance on the database.
+  void
+  PerformMaintenanceOnDatabase();
+
+  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   nsresult
   CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk);
 
-  // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase.
+  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   nsresult
   DetermineMaintenanceAction(mozIStorageConnection* aConnection,
                              nsIFile* aDatabaseFile,
                              MaintenanceAction* aMaintenanceAction);
 
-  // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase.
+  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   void
   IncrementalVacuum(mozIStorageConnection* aConnection);
 
-  // Runs on mMaintenanceThreadPool as part of PerformIdleMaintenanceOnDatabase.
+  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   void
   FullVacuum(mozIStorageConnection* aConnection,
              nsIFile* aDatabaseFile);
 
-  // Runs on the main thread. Checks to see if all database maintenance has
-  // finished and then releases the directory lock.
-  void
-  MaybeReleaseDirectoryLockForIdleMaintenance(
-                                     const nsACString& aKey,
-                                     const nsAString& aDatabasePath);
-};
-
-class MOZ_STACK_CLASS QuotaClient::AutoProgressHandler final
+  // Runs on the PBackground thread. It dispatches a complete callback and
+  // unregisters from Maintenance.
+  void
+  RunOnOwningThread();
+
+  // Runs on maintenance thread pool. Once it performs database maintenance
+  // it will dispatch to the PBackground thread on which RunOnOwningThread()
+  // is called.
+  void
+  RunOnConnectionThread();
+
+  NS_DECL_NSIRUNNABLE
+};
+
+class MOZ_STACK_CLASS DatabaseMaintenance::AutoProgressHandler final
   : public mozIStorageProgressHandler
 {
-  QuotaClient* mQuotaClient;
+  Maintenance* mMaintenance;
   mozIStorageConnection* mConnection;
-  uint32_t mRunId;
 
   NS_DECL_OWNINGTHREAD
 
   // This class is stack-based so we never actually allow AddRef/Release to do
   // anything. But we need to know if any consumer *thinks* that they have a
   // reference to this object so we track the reference countin DEBUG builds.
   DebugOnly<nsrefcnt> mDEBUGRefCnt;
 
 public:
-  AutoProgressHandler(QuotaClient* aQuotaClient, uint32_t aRunId)
-    : mQuotaClient(aQuotaClient)
+  explicit AutoProgressHandler(Maintenance* aMaintenance)
+    : mMaintenance(aMaintenance)
     , mConnection(nullptr)
-    , mRunId(aRunId)
     , mDEBUGRefCnt(0)
   {
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(!IsOnBackgroundThread());
-    NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler);
-    MOZ_ASSERT(aQuotaClient);
+    NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
+    MOZ_ASSERT(aMaintenance);
   }
 
   ~AutoProgressHandler()
   {
-    NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler);
+    NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
 
     if (mConnection) {
       Unregister();
     }
 
     MOZ_ASSERT(!mDEBUGRefCnt);
   }
 
@@ -9029,155 +9292,16 @@ private:
   void*
   operator new[](size_t) = delete;
   void
   operator delete(void*) = delete;
   void
   operator delete[](void*) = delete;
 };
 
-struct QuotaClient::MaintenanceInfoBase
-{
-  const nsCString mGroup;
-  const nsCString mOrigin;
-  const PersistenceType mPersistenceType;
-
-protected:
-  MaintenanceInfoBase(const nsACString& aGroup,
-                      const nsACString& aOrigin,
-                      PersistenceType aPersistenceType)
-    : mGroup(aGroup)
-    , mOrigin(aOrigin)
-    , mPersistenceType(aPersistenceType)
-  {
-    MOZ_ASSERT(!aGroup.IsEmpty());
-    MOZ_ASSERT(!aOrigin.IsEmpty());
-    MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
-
-    MOZ_COUNT_CTOR(QuotaClient::MaintenanceInfoBase);
-  }
-
-  MaintenanceInfoBase(MaintenanceInfoBase&& aOther)
-    : mGroup(Move(aOther.mGroup))
-    , mOrigin(Move(aOther.mOrigin))
-    , mPersistenceType(Move(aOther.mPersistenceType))
-  {
-    MOZ_COUNT_CTOR(QuotaClient::MaintenanceInfoBase);
-  }
-
-  ~MaintenanceInfoBase()
-  {
-    MOZ_COUNT_DTOR(QuotaClient::MaintenanceInfoBase);
-  }
-
-  MaintenanceInfoBase(const MaintenanceInfoBase& aOther) = delete;
-};
-
-struct QuotaClient::SingleMaintenanceInfo final
-  : public MaintenanceInfoBase
-{
-  const nsString mDatabasePath;
-
-  SingleMaintenanceInfo(const nsACString& aGroup,
-                        const nsACString& aOrigin,
-                        PersistenceType aPersistenceType,
-                        const nsAString& aDatabasePath)
-   : MaintenanceInfoBase(aGroup, aOrigin, aPersistenceType)
-   , mDatabasePath(aDatabasePath)
-  {
-    MOZ_ASSERT(!aDatabasePath.IsEmpty());
-  }
-
-  SingleMaintenanceInfo(SingleMaintenanceInfo&& aOther)
-    : MaintenanceInfoBase(Move(aOther))
-    , mDatabasePath(Move(aOther.mDatabasePath))
-  {
-    MOZ_ASSERT(!mDatabasePath.IsEmpty());
-  }
-
-  SingleMaintenanceInfo(const SingleMaintenanceInfo& aOther) = delete;
-};
-
-struct QuotaClient::MultipleMaintenanceInfo final
-  : public MaintenanceInfoBase
-{
-  nsTArray<nsString> mDatabasePaths;
-  RefPtr<DirectoryLock> mDirectoryLock;
-  const bool mIsApp;
-
-  MultipleMaintenanceInfo(const nsACString& aGroup,
-                          const nsACString& aOrigin,
-                          PersistenceType aPersistenceType,
-                          bool aIsApp,
-                          nsTArray<nsString>&& aDatabasePaths)
-   : MaintenanceInfoBase(aGroup, aOrigin, aPersistenceType)
-   , mDatabasePaths(Move(aDatabasePaths))
-   , mIsApp(aIsApp)
-  {
-#ifdef DEBUG
-    MOZ_ASSERT(!mDatabasePaths.IsEmpty());
-    for (const nsString& databasePath : mDatabasePaths) {
-      MOZ_ASSERT(!databasePath.IsEmpty());
-    }
-#endif
-  }
-
-  MultipleMaintenanceInfo(MultipleMaintenanceInfo&& aOther)
-    : MaintenanceInfoBase(Move(aOther))
-    , mDatabasePaths(Move(aOther.mDatabasePaths))
-    , mDirectoryLock(Move(aOther.mDirectoryLock))
-    , mIsApp(Move(aOther.mIsApp))
-  {
-#ifdef DEBUG
-    MOZ_ASSERT(!mDatabasePaths.IsEmpty());
-    for (const nsString& databasePath : mDatabasePaths) {
-      MOZ_ASSERT(!databasePath.IsEmpty());
-    }
-#endif
-  }
-
-  MultipleMaintenanceInfo(const MultipleMaintenanceInfo& aOther) = delete;
-};
-
-class QuotaClient::GetDirectoryLockListener final
-  : public OpenDirectoryListener
-{
-  RefPtr<QuotaClient> mQuotaClient;
-  const uint32_t mRunId;
-  const nsCString mKey;
-
-public:
-  GetDirectoryLockListener(QuotaClient* aQuotaClient,
-                           uint32_t aRunId,
-                           const nsACString& aKey)
-    : mQuotaClient(aQuotaClient)
-    , mRunId(aRunId)
-    , mKey(aKey)
-  {
-    AssertIsOnBackgroundThread();
-    MOZ_ASSERT(aQuotaClient);
-    MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient);
-  }
-
-  NS_INLINE_DECL_REFCOUNTING(QuotaClient::GetDirectoryLockListener, override)
-
-private:
-  ~GetDirectoryLockListener()
-  {
-    AssertIsOnBackgroundThread();
-  }
-
-  // OpenDirectoryListener overrides.
-  virtual void
-  DirectoryLockAcquired(DirectoryLock* aLock) override;
-
-  virtual void
-  DirectoryLockFailed() override;
-};
-
 class IntString : public nsAutoString
 {
 public:
   explicit
   IntString(int64_t aInteger)
   {
     AppendInt(aInteger);
   }
@@ -16300,19 +16424,17 @@ NS_IMPL_ISUPPORTS_INHERITED(BlobImplStor
 
 /*******************************************************************************
  * QuotaClient
  ******************************************************************************/
 
 QuotaClient* QuotaClient::sInstance = nullptr;
 
 QuotaClient::QuotaClient()
-  : mMaintenanceStartTime(0)
-  , mMaintenanceRunId(0)
-  , mShutdownRequested(false)
+  : mShutdownRequested(false)
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
   MOZ_ASSERT(!gTelemetryIdMutex);
 
   // Always create this so that later access to gTelemetryIdHashtable can be
   // properly synchronized.
   gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex");
@@ -16321,26 +16443,61 @@ QuotaClient::QuotaClient()
 }
 
 QuotaClient::~QuotaClient()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
   MOZ_ASSERT(gTelemetryIdMutex);
   MOZ_ASSERT(!mMaintenanceThreadPool);
-  MOZ_ASSERT_IF(mMaintenanceInfoHashtable, !mMaintenanceInfoHashtable->Count());
 
   // No one else should be able to touch gTelemetryIdHashtable now that the
   // QuotaClient has gone away.
   gTelemetryIdHashtable = nullptr;
   gTelemetryIdMutex = nullptr;
 
   sInstance = nullptr;
 }
 
+nsThreadPool*
+QuotaClient::GetOrCreateThreadPool()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(!mShutdownRequested);
+
+  if (!mMaintenanceThreadPool) {
+    RefPtr<nsThreadPool> threadPool = new nsThreadPool();
+
+    // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
+    // don't set some huge number here. We add 2 in case some threads block on
+    // the disk I/O.
+    const uint32_t threadCount =
+      std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) +
+      2;
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      threadPool->SetThreadLimit(threadCount)));
+
+    // Don't keep more than one idle thread.
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      threadPool->SetIdleThreadLimit(1)));
+
+    // Don't keep idle threads alive very long.
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      threadPool->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC)));
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      threadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Mnt"))));
+
+    mMaintenanceThreadPool = Move(threadPool);
+  }
+
+  return mMaintenanceThreadPool;
+}
+
 mozilla::dom::quota::Client::Type
 QuotaClient::GetType()
 {
   return QuotaClient::IDB;
 }
 
 struct FileManagerInitInfo
 {
@@ -16740,35 +16897,35 @@ QuotaClient::AbortOperationsForProcess(C
 void
 QuotaClient::StartIdleMaintenance()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mShutdownRequested);
 
   mBackgroundThread = do_GetCurrentThread();
 
-  if (!IndexedDatabaseManager::Get()) {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethod(this, &QuotaClient::CreateManager);
-    MOZ_ASSERT(runnable);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
-    return;
-  }
-
-  StartIdleMaintenanceInternal();
+  RefPtr<Maintenance> maintenance = new Maintenance(this);
+
+  mMaintenanceQueue.AppendElement(maintenance.forget());
+  ProcessMaintenanceQueue();
 }
 
 void
 QuotaClient::StopIdleMaintenance()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mShutdownRequested);
 
-  mMaintenanceRunId++;
+  if (mCurrentMaintenance) {
+    mCurrentMaintenance->Abort();
+  }
+
+  for (RefPtr<Maintenance>& maintenance : mMaintenanceQueue) {
+    maintenance->Abort();
+  }
 }
 
 void
 QuotaClient::ShutdownWorkThreads()
 {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(!mShutdownRequested);
 
@@ -16920,120 +17077,191 @@ QuotaClient::GetUsageForDirectoryInterna
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 void
-QuotaClient::CreateManager()
-{
-  MOZ_ASSERT(NS_IsMainThread());
+QuotaClient::ProcessMaintenanceQueue()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mCurrentMaintenance || mMaintenanceQueue.IsEmpty()) {
+    return;
+  }
+
+  mCurrentMaintenance = mMaintenanceQueue[0];
+  mMaintenanceQueue.RemoveElementAt(0);
+
+  mCurrentMaintenance->RunImmediately();
+}
+
+void
+Maintenance::RegisterDatabaseMaintenance(
+                                      DatabaseMaintenance* aDatabaseMaintenance)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aDatabaseMaintenance);
+  MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
+  MOZ_ASSERT(!mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
+
+  mDatabaseMaintenances.Put(aDatabaseMaintenance->DatabasePath(),
+                            aDatabaseMaintenance);
+}
+
+void
+Maintenance::UnregisterDatabaseMaintenance(
+                                      DatabaseMaintenance* aDatabaseMaintenance)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(aDatabaseMaintenance);
+  MOZ_ASSERT(mState == State::WaitingForDatabaseMaintenancesToComplete);
+  MOZ_ASSERT(mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
+
+  mDatabaseMaintenances.Remove(aDatabaseMaintenance->DatabasePath());
+
+  if (mDatabaseMaintenances.Count()) {
+    return;
+  }
+
+  mState = State::Finishing;
+  Finish();
+}
+
+nsresult
+Maintenance::Start()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::Initial);
+
+  if (IsAborted()) {
+    return NS_ERROR_ABORT;
+  }
 
   // Make sure that the IndexedDatabaseManager is running so that we can check
   // for low disk space mode.
+
+  if (IndexedDatabaseManager::Get()) {
+    OpenDirectory();
+    return NS_OK;
+  }
+
+  mState = State::CreateIndexedDatabaseManager;
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(this)));
+
+  return NS_OK;
+}
+
+nsresult
+Maintenance::CreateIndexedDatabaseManager()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(mState == State::CreateIndexedDatabaseManager);
+
+  if (IsAborted()) {
+    return NS_ERROR_ABORT;
+  }
+
   IndexedDatabaseManager* mgr = IndexedDatabaseManager::GetOrCreate();
   if (NS_WARN_IF(!mgr)) {
-    return;
-  }
-
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethod(this, &QuotaClient::StartIdleMaintenanceInternal);
-  MOZ_ASSERT(runnable);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mBackgroundThread->Dispatch(runnable,
-                                                           NS_DISPATCH_NORMAL)));
-}
-
-void
-QuotaClient::StartIdleMaintenanceInternal()
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!mShutdownRequested);
-
-  if (!mMaintenanceThreadPool) {
-    RefPtr<nsThreadPool> threadPool = new nsThreadPool();
-
-    // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
-    // don't set some huge number here. We add 2 in case some threads block on
-    // the disk I/O.
-    const uint32_t threadCount =
-      std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) +
-      2;
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      threadPool->SetThreadLimit(threadCount)));
-
-    // Don't keep more than one idle thread.
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      threadPool->SetIdleThreadLimit(1)));
-
-    // Don't keep idle threads alive very long.
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      threadPool->SetIdleThreadTimeout(5 * PR_MSEC_PER_SEC)));
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      threadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Mnt"))));
-
-    mMaintenanceThreadPool = Move(threadPool);
-  }
-
-  mMaintenanceStartTime = PR_Now();
-  MOZ_ASSERT(mMaintenanceStartTime);
-
-  if (!mMaintenanceInfoHashtable) {
-    mMaintenanceInfoHashtable = MakeUnique<MaintenanceInfoHashtable>();
-  }
-
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethodWithArg<uint32_t>(
-      this,
-      &QuotaClient::FindDatabasesForIdleMaintenance,
-      mMaintenanceRunId);
-  MOZ_ASSERT(runnable);
-
+    return NS_ERROR_FAILURE;
+  }
+
+  mState = State::IndexedDatabaseManagerOpen;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    mMaintenanceThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-}
-
-void
-QuotaClient::FindDatabasesForIdleMaintenance(uint32_t aRunId)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(mMaintenanceThreadPool);
+    mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL)));
+
+  return NS_OK;
+}
+
+nsresult
+Maintenance::OpenDirectory()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::Initial ||
+             mState == State::IndexedDatabaseManagerOpen);
+  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_ASSERT(QuotaManager::Get());
+
+  if (IsAborted()) {
+    return NS_ERROR_ABORT;
+  }
+
+  // Get a shared lock for <profile>/storage/*/*/idb
+
+  mState = State::DirectoryOpenPending;
+  QuotaManager::Get()->OpenDirectoryInternal(
+                                           Nullable<PersistenceType>(),
+                                           OriginScope::FromNull(),
+                                           Nullable<Client::Type>(Client::IDB),
+                                           /* aExclusive */ false,
+                                           this);
+
+  return NS_OK;
+}
+
+nsresult
+Maintenance::DirectoryOpen()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+  MOZ_ASSERT(mDirectoryLock);
+
+  if (IsAborted()) {
+    return NS_ERROR_ABORT;
+  }
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  mState = State::DirectoryWorkOpen;
+
+  nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+nsresult
+Maintenance::DirectoryWork()
+{
+  AssertIsOnIOThread();
+  MOZ_ASSERT(mState == State::DirectoryWorkOpen);
 
   // The storage directory is structured like this:
   //
   //   <profile>/storage/<persistence>/<origin>/idb/*.sqlite
   //
   // We have to find all database files that match any persistence type and any
   // origin. We ignore anything out of the ordinary for now.
 
-  if (IdleMaintenanceMustEnd(aRunId)) {
-    return;
+  if (IsAborted()) {
+    return NS_ERROR_ABORT;
   }
 
   QuotaManager* quotaManager = QuotaManager::Get();
   MOZ_ASSERT(quotaManager);
 
   nsCOMPtr<nsIFile> storageDir = GetFileForPath(quotaManager->GetStoragePath());
   MOZ_ASSERT(storageDir);
 
   bool exists;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(storageDir->Exists(&exists)));
   if (!exists) {
-    return;
+    return NS_ERROR_NOT_AVAILABLE;
   }
 
   bool isDirectory;
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(storageDir->IsDirectory(&isDirectory)));
   if (NS_WARN_IF(!isDirectory)) {
-    return;
+    return NS_ERROR_FAILURE;
   }
 
   // There are currently only 3 persistence types, and we want to iterate them
   // in this order:
   static const PersistenceType kPersistenceTypes[] = {
     PERSISTENCE_TYPE_PERSISTENT,
     PERSISTENCE_TYPE_DEFAULT,
     PERSISTENCE_TYPE_TEMPORARY
@@ -17043,18 +17271,18 @@ QuotaClient::FindDatabasesForIdleMainten
                   size_t(PERSISTENCE_TYPE_INVALID),
                 "Something changed with available persistence types!");
 
   NS_NAMED_LITERAL_STRING(idbDirName, IDB_DIRECTORY_NAME);
   NS_NAMED_LITERAL_STRING(sqliteExtension, ".sqlite");
 
   for (const PersistenceType persistenceType : kPersistenceTypes) {
     // Loop over "<persistence>" directories.
-    if (IdleMaintenanceMustEnd(aRunId)) {
-      return;
+    if (IsAborted()) {
+      return NS_ERROR_ABORT;
     }
 
     nsAutoCString persistenceTypeString;
     if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
       // XXX This shouldn't be a special case...
       persistenceTypeString.AssignLiteral("permanent");
     } else {
       PersistenceTypeToText(persistenceType, persistenceTypeString);
@@ -17081,18 +17309,18 @@ QuotaClient::FindDatabasesForIdleMainten
       persistenceDir->GetDirectoryEntries(
         getter_AddRefs(persistenceDirEntries))));
     if (!persistenceDirEntries) {
       continue;
     }
 
     while (true) {
       // Loop over "<origin>/idb" directories.
-      if (IdleMaintenanceMustEnd(aRunId)) {
-        return;
+      if (IsAborted()) {
+        return NS_ERROR_ABORT;
       }
 
       bool persistenceDirHasMoreEntries;
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
         persistenceDirEntries->HasMoreElements(&persistenceDirHasMoreEntries)));
 
       if (!persistenceDirHasMoreEntries) {
         break;
@@ -17132,23 +17360,22 @@ QuotaClient::FindDatabasesForIdleMainten
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
         idbDir->GetDirectoryEntries(getter_AddRefs(idbDirEntries))));
       if (!idbDirEntries) {
         continue;
       }
 
       nsCString group;
       nsCString origin;
-      bool isApp;
       nsTArray<nsString> databasePaths;
 
       while (true) {
         // Loop over files in the "idb" directory.
-        if (IdleMaintenanceMustEnd(aRunId)) {
-          return;
+        if (IsAborted()) {
+          return NS_ERROR_ABORT;
         }
 
         bool idbDirHasMoreEntries;
         MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
           idbDirEntries->HasMoreElements(&idbDirHasMoreEntries)));
 
         if (!idbDirHasMoreEntries) {
           break;
@@ -17177,163 +17404,232 @@ QuotaClient::FindDatabasesForIdleMainten
         }
 
         // Found a database.
         if (databasePaths.IsEmpty()) {
           MOZ_ASSERT(group.IsEmpty());
           MOZ_ASSERT(origin.IsEmpty());
 
           int64_t dummyTimeStamp;
+          bool dummyIsApp;
           if (NS_WARN_IF(NS_FAILED(
                 QuotaManager::GetDirectoryMetadata(originDir,
                                                    &dummyTimeStamp,
                                                    group,
                                                    origin,
-                                                   &isApp)))) {
+                                                   &dummyIsApp)))) {
             // Not much we can do here...
             continue;
           }
         }
 
         MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
 
         databasePaths.AppendElement(idbFilePath);
       }
 
       if (!databasePaths.IsEmpty()) {
-        nsCOMPtr<nsIRunnable> runnable =
-          NS_NewRunnableMethodWithArgs<uint32_t, MultipleMaintenanceInfo&&>(
-            this,
-            &QuotaClient::GetDirectoryLockForIdleMaintenance,
-            aRunId,
-            MultipleMaintenanceInfo(group,
-                                    origin,
-                                    persistenceType,
-                                    isApp,
-                                    Move(databasePaths)));
-        MOZ_ASSERT(runnable);
+        mDirectoryInfos.AppendElement(DirectoryInfo(persistenceType,
+                                                    group,
+                                                    origin,
+                                                    Move(databasePaths)));
+      }
+    }
+  }
+
+  mState = State::BeginDatabaseMaintenance;
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL)));
+
+  return NS_OK;
+}
+
+nsresult
+Maintenance::BeginDatabaseMaintenance()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
+
+  class MOZ_STACK_CLASS Helper final
+  {
+  public:
+    static bool
+    IsSafeToRunMaintenance(const nsAString& aDatabasePath)
+    {
+      if (gFactoryOps) {
+        for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
+          RefPtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
+
+          MOZ_ASSERT(!existingOp->DatabaseFilePath().IsEmpty());
+
+          if (existingOp->DatabaseFilePath() == aDatabasePath) {
+            return false;
+          }
+        }
+      }
+
+      if (gLiveDatabaseHashtable) {
+        for (auto iter = gLiveDatabaseHashtable->ConstIter();
+             !iter.Done(); iter.Next()) {
+          for (Database* database : iter.Data()->mLiveDatabases) {
+            if (database->FilePath() == aDatabasePath) {
+              return false;
+            }
+          }
+        }
+      }
+
+      return true;
+    }
+  };
+
+  RefPtr<nsThreadPool> threadPool;
+
+  for (DirectoryInfo& directoryInfo : mDirectoryInfos) {
+    for (const nsString& databasePath : directoryInfo.mDatabasePaths) {
+      if (Helper::IsSafeToRunMaintenance(databasePath)) {
+        RefPtr<DatabaseMaintenance> databaseMaintenance =
+          new DatabaseMaintenance(this,
+                                  directoryInfo.mPersistenceType,
+                                  directoryInfo.mGroup,
+                                  directoryInfo.mOrigin,
+                                  databasePath);
+
+        if (!threadPool) {
+          threadPool = mQuotaClient->GetOrCreateThreadPool();
+          MOZ_ASSERT(threadPool);
+        }
 
         MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-          mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-      }
-    }
-  }
-}
-
-void
-QuotaClient::GetDirectoryLockForIdleMaintenance(
-                                     uint32_t aRunId,
-                                     MultipleMaintenanceInfo&& aMaintenanceInfo)
-{
-  AssertIsOnBackgroundThread();
-
-  if (IdleMaintenanceMustEnd(aRunId)) {
-    return;
-  }
-
-  MOZ_ASSERT(mMaintenanceInfoHashtable);
-
-  nsAutoCString key;
-  key.AppendInt(aMaintenanceInfo.mPersistenceType);
-  key.Append('*');
-  key.Append(aMaintenanceInfo.mOrigin);
-
-  MOZ_ASSERT(!mMaintenanceInfoHashtable->Get(key));
-
-  MultipleMaintenanceInfo* maintenanceInfo =
-    new MultipleMaintenanceInfo(Move(aMaintenanceInfo));
-
-  mMaintenanceInfoHashtable->Put(key, maintenanceInfo);
-
-  RefPtr<GetDirectoryLockListener> listener =
-    new GetDirectoryLockListener(this, aRunId, key);
-
-  QuotaManager* quotaManager = QuotaManager::Get();
-  MOZ_ASSERT(quotaManager);
-
-  // FIXME: This will update origin access time!
-  quotaManager->OpenDirectory(maintenanceInfo->mPersistenceType,
-                              maintenanceInfo->mGroup,
-                              maintenanceInfo->mOrigin,
-                              maintenanceInfo->mIsApp,
-                              Client::IDB,
-                              /* aExclusive */ false,
-                              listener);
-}
-
-void
-QuotaClient::ScheduleIdleMaintenance(uint32_t aRunId,
-                                     const nsACString& aKey,
-                                     const MultipleMaintenanceInfo& aMaintenanceInfo)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!aKey.IsEmpty());
-
-  MOZ_ASSERT(mMaintenanceThreadPool);
-
-  for (const nsString& databasePath : aMaintenanceInfo.mDatabasePaths) {
-    nsCOMPtr<nsIRunnable> runnable =
-      NS_NewRunnableMethodWithArgs<uint32_t,
-                                   nsCString,
-                                   SingleMaintenanceInfo&&>(
-        this,
-        &QuotaClient::PerformIdleMaintenanceOnDatabase,
-        aRunId,
-        aKey,
-        SingleMaintenanceInfo(aMaintenanceInfo.mGroup,
-                              aMaintenanceInfo.mOrigin,
-                              aMaintenanceInfo.mPersistenceType,
-                              databasePath));
-    MOZ_ASSERT(runnable);
-
-    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-      mMaintenanceThreadPool->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-  }
-}
-
-void
-QuotaClient::PerformIdleMaintenanceOnDatabase(
-                                       uint32_t aRunId,
-                                       const nsACString& aKey,
-                                       SingleMaintenanceInfo&& aMaintenanceInfo)
+          threadPool->Dispatch(databaseMaintenance, NS_DISPATCH_NORMAL)));
+
+        RegisterDatabaseMaintenance(databaseMaintenance);
+      }
+    }
+  }
+
+  mDirectoryInfos.Clear();
+
+  if (mDatabaseMaintenances.Count()) {
+    mState = State::WaitingForDatabaseMaintenancesToComplete;
+  } else {
+    mState = State::Finishing;
+    Finish();
+  }
+
+  return NS_OK;
+}
+
+void
+Maintenance::Finish()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::Finishing);
+
+  mDirectoryLock = nullptr;
+
+  mQuotaClient->NoteFinishedMaintenance(this);
+
+  mState = State::Complete;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(Maintenance, nsRunnable)
+
+NS_IMETHODIMP
+Maintenance::Run()
+{
+  MOZ_ASSERT(mState != State::Complete);
+
+  nsresult rv;
+
+  switch (mState) {
+    case State::Initial:
+      rv = Start();
+      break;
+
+    case State::CreateIndexedDatabaseManager:
+      rv = CreateIndexedDatabaseManager();
+      break;
+
+    case State::IndexedDatabaseManagerOpen:
+      rv = OpenDirectory();
+      break;
+
+    case State::DirectoryWorkOpen:
+      rv = DirectoryWork();
+      break;
+
+    case State::BeginDatabaseMaintenance:
+      rv = BeginDatabaseMaintenance();
+      break;
+
+    case State::Finishing:
+      Finish();
+      return NS_OK;
+
+    default:
+      MOZ_CRASH("Bad state!");
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::Finishing) {
+    // Must set mState before dispatching otherwise we will race with the owning
+    // thread.
+    mState = State::Finishing;
+
+    if (IsOnBackgroundThread()) {
+      Finish();
+    } else {
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+        mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL)));
+    }
+  }
+
+  return NS_OK;
+}
+
+void
+Maintenance::DirectoryLockAcquired(DirectoryLock* aLock)
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+  MOZ_ASSERT(!mDirectoryLock);
+
+  mDirectoryLock = aLock;
+
+  nsresult rv = DirectoryOpen();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mState = State::Finishing;
+    Finish();
+
+    return;
+  }
+}
+
+void
+Maintenance::DirectoryLockFailed()
+{
+  AssertIsOnBackgroundThread();
+  MOZ_ASSERT(mState == State::DirectoryOpenPending);
+  MOZ_ASSERT(!mDirectoryLock);
+
+  mState = State::Finishing;
+  Finish();
+}
+
+void
+DatabaseMaintenance::PerformMaintenanceOnDatabase()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(mMaintenanceThreadPool);
-  MOZ_ASSERT(mMaintenanceStartTime);
-  MOZ_ASSERT(!aMaintenanceInfo.mDatabasePath.IsEmpty());
-  MOZ_ASSERT(!aMaintenanceInfo.mGroup.IsEmpty());
-  MOZ_ASSERT(!aMaintenanceInfo.mOrigin.IsEmpty());
-
-  PerformIdleMaintenanceOnDatabaseInternal(aRunId, aMaintenanceInfo);
-
-  nsCOMPtr<nsIRunnable> runnable =
-    NS_NewRunnableMethodWithArgs<nsCString, nsString>(
-      this,
-      &QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance,
-      aKey,
-      aMaintenanceInfo.mDatabasePath);
-  MOZ_ASSERT(runnable);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
-    mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL)));
-}
-
-void
-QuotaClient::PerformIdleMaintenanceOnDatabaseInternal(
-                                  uint32_t aRunId,
-                                  const SingleMaintenanceInfo& aMaintenanceInfo)
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!IsOnBackgroundThread());
-  MOZ_ASSERT(mMaintenanceThreadPool);
-  MOZ_ASSERT(mMaintenanceStartTime);
-  MOZ_ASSERT(!aMaintenanceInfo.mDatabasePath.IsEmpty());
-  MOZ_ASSERT(!aMaintenanceInfo.mGroup.IsEmpty());
-  MOZ_ASSERT(!aMaintenanceInfo.mOrigin.IsEmpty());
+  MOZ_ASSERT(mMaintenance);
+  MOZ_ASSERT(mMaintenance->StartTime());
+  MOZ_ASSERT(!mDatabasePath.IsEmpty());
+  MOZ_ASSERT(!mGroup.IsEmpty());
+  MOZ_ASSERT(!mOrigin.IsEmpty());
 
   class MOZ_STACK_CLASS AutoClose final
   {
     nsCOMPtr<mozIStorageConnection> mConnection;
 
   public:
     explicit AutoClose(mozIStorageConnection* aConnection)
       : mConnection(aConnection)
@@ -17344,38 +17640,37 @@ QuotaClient::PerformIdleMaintenanceOnDat
     ~AutoClose()
     {
       MOZ_ASSERT(mConnection);
 
       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(mConnection->Close()));
     }
   };
 
-  nsCOMPtr<nsIFile> databaseFile =
-    GetFileForPath(aMaintenanceInfo.mDatabasePath);
+  nsCOMPtr<nsIFile> databaseFile = GetFileForPath(mDatabasePath);
   MOZ_ASSERT(databaseFile);
 
   nsCOMPtr<mozIStorageConnection> connection;
   nsresult rv = GetStorageConnection(databaseFile,
-                                     aMaintenanceInfo.mPersistenceType,
-                                     aMaintenanceInfo.mGroup,
-                                     aMaintenanceInfo.mOrigin,
+                                     mPersistenceType,
+                                     mGroup,
+                                     mOrigin,
                                      TelemetryIdForFile(databaseFile),
                                      getter_AddRefs(connection));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
   AutoClose autoClose(connection);
 
-  if (IdleMaintenanceMustEnd(aRunId)) {
-    return;
-  }
-
-  AutoProgressHandler progressHandler(this, aRunId);
+  if (mMaintenance->IsAborted()) {
+    return;
+  }
+
+  AutoProgressHandler progressHandler(mMaintenance);
   if (NS_WARN_IF(NS_FAILED(progressHandler.Register(connection)))) {
     return;
   }
 
   bool databaseIsOk;
   rv = CheckIntegrity(connection, &databaseIsOk);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
@@ -17383,27 +17678,27 @@ QuotaClient::PerformIdleMaintenanceOnDat
 
   if (NS_WARN_IF(!databaseIsOk)) {
     // XXX Handle this somehow! Probably need to clear all storage for the
     //     origin. Needs followup.
     MOZ_ASSERT(false, "Database corruption detected!");
     return;
   }
 
-  if (IdleMaintenanceMustEnd(aRunId)) {
+  if (mMaintenance->IsAborted()) {
     return;
   }
 
   MaintenanceAction maintenanceAction;
   rv = DetermineMaintenanceAction(connection, databaseFile, &maintenanceAction);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  if (IdleMaintenanceMustEnd(aRunId)) {
+  if (mMaintenance->IsAborted()) {
     return;
   }
 
   switch (maintenanceAction) {
     case MaintenanceAction::Nothing:
       break;
 
     case MaintenanceAction::IncrementalVacuum:
@@ -17415,17 +17710,18 @@ QuotaClient::PerformIdleMaintenanceOnDat
       break;
 
     default:
       MOZ_CRASH("Unknown MaintenanceAction!");
   }
 }
 
 nsresult
-QuotaClient::CheckIntegrity(mozIStorageConnection* aConnection, bool* aOk)
+DatabaseMaintenance::CheckIntegrity(mozIStorageConnection* aConnection,
+                                    bool* aOk)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(aConnection);
   MOZ_ASSERT(aOk);
 
   nsresult rv;
 
@@ -17538,19 +17834,20 @@ QuotaClient::CheckIntegrity(mozIStorageC
     }
   }
 
   *aOk = true;
   return NS_OK;
 }
 
 nsresult
-QuotaClient::DetermineMaintenanceAction(mozIStorageConnection* aConnection,
-                                        nsIFile* aDatabaseFile,
-                                        MaintenanceAction* aMaintenanceAction)
+DatabaseMaintenance::DetermineMaintenanceAction(
+                                          mozIStorageConnection* aConnection,
+                                          nsIFile* aDatabaseFile,
+                                          MaintenanceAction* aMaintenanceAction)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(aConnection);
   MOZ_ASSERT(aDatabaseFile);
   MOZ_ASSERT(aMaintenanceAction);
 
   int32_t schemaVersion;
@@ -17616,33 +17913,31 @@ QuotaClient::DetermineMaintenanceAction(
   int64_t lastVacuumSize;
   rv = stmt->GetInt64(1, &lastVacuumSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   NS_ASSERTION(lastVacuumSize > 0, "Thy last vacuum size shall be greater than zero, less than zero shall thy last vacuum size not be. Zero is right out.");
 
+  PRTime startTime = mMaintenance->StartTime();
+
   // This shouldn't really be possible...
-  if (NS_WARN_IF(mMaintenanceStartTime <= lastVacuumTime)) {
+  if (NS_WARN_IF(startTime <= lastVacuumTime)) {
     *aMaintenanceAction = MaintenanceAction::Nothing;
     return NS_OK;
   }
 
-  if (mMaintenanceStartTime - lastVacuumTime < kMinVacuumAge) {
+  if (startTime - lastVacuumTime < kMinVacuumAge) {
     *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
     return NS_OK;
   }
 
   // It has been more than a week since the database was vacuumed, so gather
   // statistics on its usage to see if vacuuming is worthwhile.
-  rv = aConnection->EnableModule(NS_LITERAL_CSTRING("dbstat"));
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
 
   // Create a temporary copy of the dbstat table to speed up the queries that
   // come later.
   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
     "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -17753,33 +18048,33 @@ QuotaClient::DetermineMaintenanceAction(
 
   *aMaintenanceAction = percentUnused >= kPercentUnusedThreshold ?
                         MaintenanceAction::FullVacuum :
                         MaintenanceAction::IncrementalVacuum;
   return NS_OK;
 }
 
 void
-QuotaClient::IncrementalVacuum(mozIStorageConnection* aConnection)
+DatabaseMaintenance::IncrementalVacuum(mozIStorageConnection* aConnection)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(aConnection);
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "PRAGMA incremental_vacuum;"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 }
 
 void
-QuotaClient::FullVacuum(mozIStorageConnection* aConnection,
-                        nsIFile* aDatabaseFile)
+DatabaseMaintenance::FullVacuum(mozIStorageConnection* aConnection,
+                                nsIFile* aDatabaseFile)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(aConnection);
   MOZ_ASSERT(aDatabaseFile);
 
   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "VACUUM;"
@@ -17821,42 +18116,55 @@ QuotaClient::FullVacuum(mozIStorageConne
 
   rv = stmt->Execute();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 }
 
 void
-QuotaClient::MaybeReleaseDirectoryLockForIdleMaintenance(
-                                                 const nsACString& aKey,
-                                                 const nsAString& aDatabasePath)
-{
-  AssertIsOnBackgroundThread();
-  MOZ_ASSERT(!aKey.IsEmpty());
-  MOZ_ASSERT(!aDatabasePath.IsEmpty());
-  MOZ_ASSERT(mMaintenanceInfoHashtable);
-
-  MultipleMaintenanceInfo* maintenanceInfo;
-  MOZ_ALWAYS_TRUE(mMaintenanceInfoHashtable->Get(aKey, &maintenanceInfo));
-  MOZ_ASSERT(maintenanceInfo);
-
-  MOZ_ALWAYS_TRUE(maintenanceInfo->mDatabasePaths.RemoveElement(aDatabasePath));
-
-  if (maintenanceInfo->mDatabasePaths.IsEmpty()) {
-    // That's it!
-    maintenanceInfo->mDirectoryLock = nullptr;
-
-    // This will delete |maintenanceInfo|.
-    mMaintenanceInfoHashtable->Remove(aKey);
-  }
-}
-
-nsresult
-QuotaClient::
+DatabaseMaintenance::RunOnOwningThread()
+{
+  AssertIsOnBackgroundThread();
+
+  if (mCompleteCallback) {
+    MOZ_ALWAYS_TRUE(
+      NS_SUCCEEDED(NS_DispatchToCurrentThread(mCompleteCallback)));
+    mCompleteCallback = nullptr;
+  }
+
+  mMaintenance->UnregisterDatabaseMaintenance(this);
+}
+
+void
+DatabaseMaintenance::RunOnConnectionThread()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!IsOnBackgroundThread());
+
+  PerformMaintenanceOnDatabase();
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+    mMaintenance->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL)));
+}
+
+NS_IMETHODIMP
+DatabaseMaintenance::Run()
+{
+  if (IsOnBackgroundThread()) {
+    RunOnOwningThread();
+  } else {
+    RunOnConnectionThread();
+  }
+
+  return NS_OK;
+}
+
+nsresult
+DatabaseMaintenance::
 AutoProgressHandler::Register(mozIStorageConnection* aConnection)
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(aConnection);
 
   // We want to quickly bail out of any operation if the user becomes active, so
   // use a small granularity here since database performance isn't critical.
@@ -17872,106 +18180,66 @@ AutoProgressHandler::Register(mozIStorag
 
   MOZ_ASSERT(!oldHandler);
   mConnection = aConnection;
 
   return NS_OK;
 }
 
 void
-QuotaClient::
+DatabaseMaintenance::
 AutoProgressHandler::Unregister()
 {
   MOZ_ASSERT(!NS_IsMainThread());
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(mConnection);
 
   nsCOMPtr<mozIStorageProgressHandler> oldHandler;
   nsresult rv = mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler));
   Unused << NS_WARN_IF(NS_FAILED(rv));
 
   MOZ_ASSERT_IF(NS_SUCCEEDED(rv), oldHandler == this);
 }
 
 NS_IMETHODIMP_(MozExternalRefCountType)
-QuotaClient::
+DatabaseMaintenance::
 AutoProgressHandler::AddRef()
 {
-  NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler);
+  NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
 
   mDEBUGRefCnt++;
   return 2;
 }
 
 NS_IMETHODIMP_(MozExternalRefCountType)
-QuotaClient::
+DatabaseMaintenance::
 AutoProgressHandler::Release()
 {
-  NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler);
+  NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
 
   mDEBUGRefCnt--;
   return 1;
 }
 
-NS_IMPL_QUERY_INTERFACE(QuotaClient::AutoProgressHandler,
+NS_IMPL_QUERY_INTERFACE(DatabaseMaintenance::AutoProgressHandler,
                         mozIStorageProgressHandler)
 
 NS_IMETHODIMP
-QuotaClient::
+DatabaseMaintenance::
 AutoProgressHandler::OnProgress(mozIStorageConnection* aConnection,
                                 bool* _retval)
 {
-  NS_ASSERT_OWNINGTHREAD(QuotaClient::AutoProgressHandler);
+  NS_ASSERT_OWNINGTHREAD(DatabaseMaintenance::AutoProgressHandler);
   MOZ_ASSERT(aConnection);
   MOZ_ASSERT(mConnection == aConnection);
   MOZ_ASSERT(_retval);
 
-  *_retval = mQuotaClient->IdleMaintenanceMustEnd(mRunId);
-  return NS_OK;
-}
-
-void
-QuotaClient::
-GetDirectoryLockListener::DirectoryLockAcquired(DirectoryLock* aLock)
-{
-  AssertIsOnBackgroundThread();
-
-  MultipleMaintenanceInfo* maintenanceInfo;
-  MOZ_ALWAYS_TRUE(
-    mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo));
-  MOZ_ASSERT(maintenanceInfo);
-  MOZ_ASSERT(!maintenanceInfo->mDirectoryLock);
-
-  if (mQuotaClient->IdleMaintenanceMustEnd(mRunId)) {
-#ifdef DEBUG
-    maintenanceInfo->mDatabasePaths.Clear();
-#endif
-
-    mQuotaClient->mMaintenanceInfoHashtable->Remove(mKey);
-    return;
-  }
-
-  maintenanceInfo->mDirectoryLock = aLock;
-
-  mQuotaClient->ScheduleIdleMaintenance(mRunId, mKey, *maintenanceInfo);
-}
-
-void
-QuotaClient::
-GetDirectoryLockListener::DirectoryLockFailed()
-{
-  AssertIsOnBackgroundThread();
-
-  DebugOnly<MultipleMaintenanceInfo*> maintenanceInfo;
-  MOZ_ASSERT(
-    mQuotaClient->mMaintenanceInfoHashtable->Get(mKey, &maintenanceInfo));
-  MOZ_ASSERT(maintenanceInfo);
-  MOZ_ASSERT(!maintenanceInfo->mDirectoryLock);
-
-  mQuotaClient->mMaintenanceInfoHashtable->Remove(mKey);
+  *_retval = mMaintenance->IsAborted();
+
+  return NS_OK;
 }
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 NS_IMPL_ISUPPORTS(CompressDataBlobsFunction, mozIStorageFunction)
 NS_IMPL_ISUPPORTS(EncodeKeysFunction, mozIStorageFunction)
@@ -19298,16 +19566,17 @@ FactoryOp::RetryCheckPermission()
 }
 
 nsresult
 FactoryOp::DirectoryOpen()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::DirectoryOpenPending);
   MOZ_ASSERT(mDirectoryLock);
+  MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
 
   // gFactoryOps could be null here if the child process crashed or something
   // and that cleaned up the last Factory actor.
   if (!gFactoryOps) {
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   // See if this FactoryOp needs to wait.
@@ -19322,16 +19591,30 @@ FactoryOp::DirectoryOpen()
       break;
     }
   }
 
   // Adding this to the factory ops list will block any additional ops from
   // proceeding until this one is done.
   gFactoryOps->AppendElement(this);
 
+  if (!delayed) {
+    QuotaClient* quotaClient = QuotaClient::GetInstance();
+    MOZ_ASSERT(quotaClient);
+
+    if (RefPtr<Maintenance> currentMaintenance =
+          quotaClient->GetCurrentMaintenance()) {
+      if (RefPtr<DatabaseMaintenance> databaseMaintenance =
+            currentMaintenance->GetDatabaseMaintenance(mDatabaseFilePath)) {
+        databaseMaintenance->WaitForCompletion(this);
+        delayed = true;
+      }
+    }
+  }
+
   mBlockedDatabaseOpen = true;
 
   mState = State::DatabaseOpenPending;
   if (!delayed) {
     nsresult rv = DatabaseOpen();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
@@ -19680,68 +19963,111 @@ FactoryOp::CheckAtLeastOneAppHasPermissi
   }
 
   return false;
 #else
   return true;
 #endif // MOZ_CHILD_PERMISSIONS
 }
 
-void
+nsresult
 FactoryOp::FinishOpen()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::FinishOpen);
   MOZ_ASSERT(!mContentParent);
 
   if (QuotaManager::Get()) {
-    OpenDirectory();
-
-    return;
+    nsresult rv = OpenDirectory();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
   }
 
   mState = State::QuotaManagerPending;
   QuotaManager::GetOrCreate(this);
+
+  return NS_OK;
 }
 
 nsresult
 FactoryOp::QuotaManagerOpen()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::QuotaManagerPending);
 
   if (NS_WARN_IF(!QuotaManager::Get())) {
     return NS_ERROR_FAILURE;
   }
 
-  OpenDirectory();
-
-  return NS_OK;
-}
-
-void
+  nsresult rv = OpenDirectory();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  return NS_OK;
+}
+
+nsresult
 FactoryOp::OpenDirectory()
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(mState == State::FinishOpen ||
              mState == State::QuotaManagerPending);
   MOZ_ASSERT(!mOrigin.IsEmpty());
   MOZ_ASSERT(!mDirectoryLock);
   MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   MOZ_ASSERT(QuotaManager::Get());
 
+  // Need to get database file path in advance.
+  const nsString& databaseName = mCommonParams.metadata().name();
+  PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
+
+  QuotaManager* quotaManager = QuotaManager::Get();
+  MOZ_ASSERT(quotaManager);
+
+  nsCOMPtr<nsIFile> dbFile;
+  nsresult rv = quotaManager->GetDirectoryForOrigin(persistenceType,
+                                                    mOrigin,
+                                                    getter_AddRefs(dbFile));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbFile->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsAutoString filename;
+  GetDatabaseFilename(databaseName, filename);
+
+  rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  rv = dbFile->GetPath(mDatabaseFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
   mState = State::DirectoryOpenPending;
 
-  QuotaManager::Get()->OpenDirectory(mCommonParams.metadata().persistenceType(),
-                                     mGroup,
-                                     mOrigin,
-                                     mIsApp,
-                                     Client::IDB,
-                                     /* aExclusive */ false,
-                                     this);
+  quotaManager->OpenDirectory(persistenceType,
+                              mGroup,
+                              mOrigin,
+                              mIsApp,
+                              Client::IDB,
+                              /* aExclusive */ false,
+                              this);
+
+  return NS_OK;
 }
 
 bool
 FactoryOp::MustWaitFor(const FactoryOp& aExistingOp)
 {
   AssertIsOnOwningThread();
 
   // Things for the same persistence type, the same origin and the same
@@ -19799,18 +20125,18 @@ FactoryOp::Run()
       rv = ChallengePermission();
       break;
 
     case State::PermissionRetry:
       rv = RetryCheckPermission();
       break;
 
     case State::FinishOpen:
-      FinishOpen();
-      return NS_OK;
+      rv = FinishOpen();
+      break;
 
     case State::QuotaManagerPending:
       rv = QuotaManagerOpen();
       break;
 
     case State::DatabaseOpenPending:
       rv = DatabaseOpen();
       break;
@@ -20030,20 +20356,25 @@ OpenDatabaseOp::DoDatabaseWork()
 
   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   mTelemetryId = TelemetryIdForFile(dbFile);
 
-  rv = dbFile->GetPath(mDatabaseFilePath);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
+#ifdef DEBUG
+  nsString databaseFilePath;
+  rv = dbFile->GetPath(databaseFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
+#endif
 
   nsCOMPtr<nsIFile> fmDirectory;
   rv = dbDirectory->Clone(getter_AddRefs(fmDirectory));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   const NS_ConvertASCIItoUTF16 filesSuffix(kFileManagerDirectoryNameSuffix);
@@ -21360,16 +21691,26 @@ DeleteDatabaseOp::DoDatabaseWork()
     return rv;
   }
 
   rv = dbFile->Append(filename + NS_LITERAL_STRING(".sqlite"));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+#ifdef DEBUG
+  nsString databaseFilePath;
+  rv = dbFile->GetPath(databaseFilePath);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
+#endif
+
   bool exists;
   rv = dbFile->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (exists) {
     // Parts of this function may fail but that shouldn't prevent us from
--- a/dom/indexedDB/test/unit/test_idle_maintenance.js
+++ b/dom/indexedDB/test/unit/test_idle_maintenance.js
@@ -30,16 +30,45 @@ function testSteps()
 
   do_get_idle();
 
   info("Creating databases");
 
   let quotaManagerService = Cc["@mozilla.org/dom/quota-manager-service;1"].
                             getService(Ci.nsIQuotaManagerService);
 
+  // Keep at least one database open.
+  let req = indexedDB.open("foo-a", 1);
+  req.onerror = errorHandler;
+  req.onsuccess = grabEventAndContinueHandler;
+  let event = yield undefined;
+
+  let dbA = event.target.result;
+
+  // Keep at least one factory operation alive by deleting a database that is
+  // stil open.
+  req = indexedDB.open("foo-b", 1);
+  req.onerror = errorHandler;
+  req.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  let dbB = event.target.result;
+
+  indexedDB.deleteDatabase("foo-b");
+
+  // Create a database which we will later try to open while maintenance is
+  // performed.
+  req = indexedDB.open("foo-c", 1);
+  req.onerror = errorHandler;
+  req.onsuccess = grabEventAndContinueHandler;
+  event = yield undefined;
+
+  let dbC = event.target.result;
+  dbC.close();
+
   let dbCount = 0;
 
   for (let persistence of ["persistent", "temporary", "default"]) {
     for (let i = 1; i <= 5; i++) {
       let dbName = "foo-" + i;
       let dbPersistence = persistence;
       let req = indexedDB.openForPrincipal(principal,
                                            dbName,
@@ -99,16 +128,23 @@ function testSteps()
   });
   yield undefined;
 
   info("Sending fake 'idle-daily' notification to QuotaManager");
 
   let observer = quotaManagerService.QueryInterface(Ci.nsIObserver);
   observer.observe(null, "idle-daily", "");
 
+  info("Opening database while maintenance is performed");
+
+  req = indexedDB.open("foo-c", 1);
+  req.onerror = errorHandler;
+  req.onsuccess = grabEventAndContinueHandler;
+  yield undefined;
+
   info("Waiting for maintenance to start");
 
   // This time is totally arbitrary. Most likely directory scanning will have
   // completed, QuotaManager locks will be acquired, and  maintenance tasks will
   // be scheduled before this time has elapsed, so we will be testing the
   // maintenance code. However, if something is slow then this will test
   // shutting down in the middle of maintenance.
   setTimeout(continueToNextStep, 10000);
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -168,18 +168,16 @@ PannerNodeDopplerWarning=Use of setVeloc
 # LOCALIZATION NOTE: Do not translate "Application Cache API", "AppCache" and "ServiceWorker".
 AppCacheWarning=The Application Cache API (AppCache) is deprecated and will be removed at a future date.  Please consider using ServiceWorker for offline support.
 # LOCALIZATION NOTE: Do not translate "Worker".
 EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
 WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated.
 NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia
 # LOCALIZATION NOTE: Do not translate "ServiceWorker". %S is a URL.
 InterceptionFailedWithURL=Failed to load '%S'. A ServiceWorker intercepted the request and encountered an unexpected error.
-# LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "opaque", or "Response". %S is a URL.
-OpaqueInterceptionDisabledWithURL=Failed to load '%S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while opaque interception is disabled.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "FetchEvent.respondWith()", "FetchEvent", "no-cors", "opaque", "Response", or "RequestMode". %1$S is a URL. %2$S is a RequestMode value.
 BadOpaqueInterceptionRequestModeWithURL=Failed to load '%1$S'. A ServiceWorker passed an opaque Response to FetchEvent.respondWith() while handling a '%2$S' FetchEvent. Opaque Response objects are only valid when the RequestMode is 'no-cors'.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Error", "Response", "FetchEvent.respondWith()", or "fetch()". %S is a URL.
 InterceptedErrorResponseWithURL=Failed to load '%S'. A ServiceWorker passed an Error Response to FetchEvent.respondWith(). This typically means the ServiceWorker performed an invalid fetch() call.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "Response", "FetchEvent.respondWith()", or "Response.clone()". %S is a URL.
 InterceptedUsedResponseWithURL=Failed to load '%S'. A ServiceWorker passed a used Response to FetchEvent.respondWith(). The body of a Response may only be read once. Use Response.clone() to access the body multiple times.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker", "opaqueredirect", "Response", "FetchEvent.respondWith()", or "FetchEvent". %s is a URL.
 BadOpaqueRedirectInterceptionWithURL=Failed to load '%S'. A ServiceWorker passed an opaqueredirect Response to FetchEvent.respondWith() while handling a non-navigation FetchEvent.
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -321,18 +321,16 @@ MediaDecoderStateMachine::MediaDecoderSt
 #endif
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
 
-  mReader = nullptr;
-
 #ifdef XP_WIN
   timeEndPeriod(1);
 #endif
 }
 
 void
 MediaDecoderStateMachine::InitializationTask(MediaDecoder* aDecoder)
 {
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -207,22 +207,20 @@ public:
       if (self->mAudioOffloading != aAudioOffloading) {
         self->mAudioOffloading = aAudioOffloading;
         self->ScheduleStateMachine();
       }
     });
     OwnerThread()->Dispatch(r.forget());
   }
 
-  // Drop reference to mReader and mResource. Only called during shutdown dance.
+  // Drop reference to mResource. Only called during shutdown dance.
   void BreakCycles() {
     MOZ_ASSERT(NS_IsMainThread());
-    if (mReader) {
-      mReader->BreakCycles();
-    }
+    mReader->BreakCycles();
     mResource = nullptr;
   }
 
   TimedMetadataEventSource& TimedMetadataEvent() {
     return mMetadataManager.TimedMetadataEvent();
   }
 
   MediaEventSource<void>& OnMediaNotSeekable() {
@@ -243,27 +241,21 @@ public:
 
   MediaEventSource<MediaDecoderEventVisibility>&
   OnSeekingStart() { return mOnSeekingStart; }
 
   // Immutable after construction - may be called on any thread.
   bool IsRealTime() const { return mRealTime; }
 
   size_t SizeOfVideoQueue() {
-    if (mReader) {
-      return mReader->SizeOfVideoQueueInBytes();
-    }
-    return 0;
+    return mReader->SizeOfVideoQueueInBytes();
   }
 
   size_t SizeOfAudioQueue() {
-    if (mReader) {
-      return mReader->SizeOfAudioQueueInBytes();
-    }
-    return 0;
+    return mReader->SizeOfAudioQueueInBytes();
   }
 
 private:
   // Functions used by assertions to ensure we're calling things
   // on the appropriate threads.
   bool OnTaskQueue() const;
 
   // Initialization that needs to happen on the task queue. This is the first
@@ -898,17 +890,17 @@ private:
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the state machine's constructor.
-  RefPtr<MediaDecoderReader> mReader;
+  const RefPtr<MediaDecoderReader> mReader;
 
   // The end time of the last audio frame that's been pushed onto the media sink
   // in microseconds. This will approximately be the end time
   // of the audio stream, unless another frame is pushed to the hardware.
   int64_t AudioEndTime() const;
 
   // The end time of the last rendered video frame that's been sent to
   // compositor.
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -1,11 +1,10 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' # b2g( ReferenceError: MediaSource is not defined)
-subsuite = media
 support-files =
   mediasource.js
   seek.webm seek.webm^headers^
   seek_lowres.webm seek_lowres.webm^headers^
   bipbop/bipbop2s.mp4 bipbop/bipbop2s.mp4^headers^
   bipbop/bipbopinit.mp4 bipbop/bipbop_audioinit.mp4 bipbop/bipbop_videoinit.mp4
   bipbop/bipbop1.m4s bipbop/bipbop_audio1.m4s bipbop/bipbop_video1.m4s
   bipbop/bipbop2.m4s bipbop/bipbop_audio2.m4s bipbop/bipbop_video2.m4s
--- a/dom/media/omx/MediaOmxCommonDecoder.cpp
+++ b/dom/media/omx/MediaOmxCommonDecoder.cpp
@@ -122,23 +122,17 @@ MediaOmxCommonDecoder::FirstFrameLoaded(
 }
 
 void
 MediaOmxCommonDecoder::PauseStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG(LogLevel::Debug, ("%s", __PRETTY_FUNCTION__));
 
-  if (mShuttingDown) {
-    return;
-  }
-
-  if (!GetStateMachine()) {
-    return;
-  }
+  MOZ_ASSERT(GetStateMachine());
   // enter dormant state
   GetStateMachine()->DispatchSetDormant(true);
 }
 
 void
 MediaOmxCommonDecoder::ResumeStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -18,17 +18,16 @@
 # gErrorTests in manifest.js.
 
 # To test for a specific bug in handling a specific resource type, make the
 # test first check canPlayType for the type, and if it's not supported, just
 # do ok(true, "Type not supported") and stop the test.
 
 [DEFAULT]
 skip-if = buildapp == 'mulet' || (os == 'win' && strictContentSandbox) || android_version == '18' # strictContentSandbox (Bug 1042735)
-subsuite = media
 support-files =
   16bit_wave_extrametadata.wav
   16bit_wave_extrametadata.wav^headers^
   320x240.ogv
   320x240.ogv^headers^
   448636.ogv
   448636.ogv^headers^
   VID_0001.ogg
--- a/dom/media/tests/mochitest/identity/mochitest.ini
+++ b/dom/media/tests/mochitest/identity/mochitest.ini
@@ -1,14 +1,13 @@
 [DEFAULT]
 # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881
 # won't run on b2g desktop tests - bug 1119993
 # broken HTTPS on b2g emulator - bug 1135339
 skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk') || buildapp == 'mulet'
-subsuite = media
 support-files =
   /.well-known/idp-proxy/idp.js
   identityPcTest.js
 tags = msg
 
 [test_idpproxy.html]
 support-files =
   /.well-known/idp-proxy/idp-redirect-http.js
--- a/dom/media/tests/mochitest/ipc/mochitest.ini
+++ b/dom/media/tests/mochitest/ipc/mochitest.ini
@@ -1,10 +1,9 @@
 [DEFAULT]
 tags=msg
-subsuite=media
 support-files =
   ipc.json
 
 skip-if = e10s
 
 [test_ipc.html]
 skip-if =  buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' #bug 910661 # b2g(nested ipc not working) b2g-debug(debug-only failure) b2g-desktop(nested ipc not working)
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881
 tags = msg webrtc
-subsuite = media
 skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || (buildapp == 'mulet') || (toolkit == 'gonk' && debug) # b2g(Either bug 1171118 or bug 1169838, take your pick)
 support-files =
   head.js
   dataChannel.js
   mediaStreamPlayback.js
   network.js
   nonTrickleIce.js
   pc.js
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 tags=msg
 tags = webaudio
-subsuite = media
 skip-if = ((buildapp == 'b2g') && (toolkit != 'gonk' || debug)) || (os == 'win' && strictContentSandbox) #b2g-debug,b2g-desktop(bug 916135); strictContentSandbox(Bug 1042735)
 support-files =
   audio-expected.wav
   audio-mono-expected-2.wav
   audio-mono-expected.wav
   audio-quad.wav
   audio.ogv
   audioBufferSourceNodeDetached_worker.js
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 tags=msg
 skip-if = buildapp == 'b2g' # Bug 1191270, bug 1037287, bug 967606, bug 1096400, etc
-subsuite = media
 support-files =
   head.js
   hello.ogg
   hello.ogg^headers^
   silence.ogg
   silence.ogg^headers^
 
 [test_abort.html]
--- a/dom/media/webspeech/synth/test/mochitest.ini
+++ b/dom/media/webspeech/synth/test/mochitest.ini
@@ -1,11 +1,10 @@
 [DEFAULT]
 tags=msg
-subsuite = media
 support-files =
   common.js
   file_bfcache_frame.html
   file_setup.html
   file_speech_queue.html
   file_speech_simple.html
   file_speech_cancel.html
   file_speech_error.html
--- a/dom/messagechannel/MessageChannel.cpp
+++ b/dom/messagechannel/MessageChannel.cpp
@@ -8,78 +8,80 @@
 
 #include "mozilla/dom/MessageChannelBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/Navigator.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
+#include "nsIGlobalObject.h"
 #include "nsIPrincipal.h"
-#include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mWindow, mPort1, mPort2)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MessageChannel, mGlobal, mPort1, mPort2)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MessageChannel)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MessageChannel)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MessageChannel)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-MessageChannel::MessageChannel(nsPIDOMWindowInner* aWindow)
-  : mWindow(aWindow)
+MessageChannel::MessageChannel(nsIGlobalObject* aGlobal)
+  : mGlobal(aGlobal)
 {
+  MOZ_ASSERT(aGlobal);
 }
 
 MessageChannel::~MessageChannel()
 {
 }
 
 JSObject*
 MessageChannel::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MessageChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
 /* static */ already_AddRefed<MessageChannel>
 MessageChannel::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
 {
-  // window can be null in workers.
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports());
-  return Constructor(window, aRv);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
+  return Constructor(global, aRv);
 }
 
 /* static */ already_AddRefed<MessageChannel>
-MessageChannel::Constructor(nsPIDOMWindowInner* aWindow, ErrorResult& aRv)
+MessageChannel::Constructor(nsIGlobalObject* aGlobal, ErrorResult& aRv)
 {
+  MOZ_ASSERT(aGlobal);
+
   nsID portUUID1;
   aRv = nsContentUtils::GenerateUUIDInPlace(portUUID1);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   nsID portUUID2;
   aRv = nsContentUtils::GenerateUUIDInPlace(portUUID2);
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  RefPtr<MessageChannel> channel = new MessageChannel(aWindow);
+  RefPtr<MessageChannel> channel = new MessageChannel(aGlobal);
 
-  channel->mPort1 = MessagePort::Create(aWindow, portUUID1, portUUID2, aRv);
+  channel->mPort1 = MessagePort::Create(aGlobal, portUUID1, portUUID2, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  channel->mPort2 = MessagePort::Create(aWindow, portUUID2, portUUID1, aRv);
+  channel->mPort2 = MessagePort::Create(aGlobal, portUUID2, portUUID1, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   channel->mPort1->UnshippedEntangle(channel->mPort2);
   channel->mPort2->UnshippedEntangle(channel->mPort1);
 
   return channel.forget();
--- a/dom/messagechannel/MessageChannel.h
+++ b/dom/messagechannel/MessageChannel.h
@@ -9,62 +9,62 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 #include "nsCOMPtr.h"
 
-class nsPIDOMWindowInner;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class MessagePort;
 
 class MessageChannel final : public nsISupports
                            , public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MessageChannel)
 
-  nsPIDOMWindowInner*
+  nsIGlobalObject*
   GetParentObject() const
   {
-    return mWindow;
+    return mGlobal;
   }
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static already_AddRefed<MessageChannel>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
   static already_AddRefed<MessageChannel>
-  Constructor(nsPIDOMWindowInner* aWindow, ErrorResult& aRv);
+  Constructor(nsIGlobalObject* aGlobal, ErrorResult& aRv);
 
   MessagePort*
   Port1() const
   {
     return mPort1;
   }
 
   MessagePort*
   Port2() const
   {
     return mPort2;
   }
 
 private:
-  explicit MessageChannel(nsPIDOMWindowInner* aWindow);
+  explicit MessageChannel(nsIGlobalObject* aGlobal);
   ~MessageChannel();
 
-  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  nsCOMPtr<nsIGlobalObject> mGlobal;
 
   RefPtr<MessagePort> mPort1;
   RefPtr<MessagePort> mPort2;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -17,16 +17,19 @@
 #include "mozilla/dom/MessagePortChild.h"
 #include "mozilla/dom/MessagePortList.h"
 #include "mozilla/dom/PMessagePort.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/MessagePortTimelineMarker.h"
+#include "mozilla/TimelineConsumers.h"
+#include "mozilla/TimelineMarker.h"
 #include "mozilla/unused.h"
 #include "nsContentUtils.h"
 #include "nsGlobalWindow.h"
 #include "nsPresContext.h"
 #include "ScriptSettings.h"
 #include "SharedMessagePortMessage.h"
 
 #include "nsIBFCacheEntry.h"
@@ -81,38 +84,51 @@ public:
     mData = nullptr;
     return NS_OK;
   }
 
 private:
   nsresult
   DispatchMessage() const
   {
-    nsCOMPtr<nsIGlobalObject> globalObject;
-
-    if (NS_IsMainThread()) {
-      globalObject = do_QueryInterface(mPort->GetParentObject());
-    } else {
-      WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
-      MOZ_ASSERT(workerPrivate);
-      globalObject = workerPrivate->GlobalScope();
-    }
+    nsCOMPtr<nsIGlobalObject> globalObject = mPort->GetParentObject();
+    MOZ_ASSERT(globalObject);
 
     AutoJSAPI jsapi;
     if (!globalObject || !jsapi.Init(globalObject)) {
       NS_WARNING("Failed to initialize AutoJSAPI object.");
       return NS_ERROR_FAILURE;
     }
 
     JSContext* cx = jsapi.cx();
 
     ErrorResult rv;
     JS::Rooted<JS::Value> value(cx);
 
+    UniquePtr<AbstractTimelineMarker> start;
+    UniquePtr<AbstractTimelineMarker> end;
+    RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+    bool isTimelineRecording = timelines && !timelines->IsEmpty();
+
+    if (isTimelineRecording) {
+      start = MakeUnique<MessagePortTimelineMarker>(
+        ProfileTimelineMessagePortOperationType::DeserializeData,
+        MarkerTracingType::START);
+    }
+
     mData->Read(mPort->GetParentObject(), cx, &value, rv);
+
+    if (isTimelineRecording) {
+      end = MakeUnique<MessagePortTimelineMarker>(
+        ProfileTimelineMessagePortOperationType::DeserializeData,
+        MarkerTracingType::END);
+      timelines->AddMarkerForAllObservedDocShells(start);
+      timelines->AddMarkerForAllObservedDocShells(end);
+    }
+
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
 
     // Create the event
     nsCOMPtr<mozilla::dom::EventTarget> eventTarget =
       do_QueryInterface(mPort->GetOwner());
     RefPtr<MessageEvent> event =
@@ -254,54 +270,55 @@ private:
 
   const MessagePortIdentifier mIdentifier;
 };
 
 NS_IMPL_ISUPPORTS(ForceCloseHelper, nsIIPCBackgroundChildCreateCallback)
 
 } // namespace
 
-MessagePort::MessagePort(nsISupports* aSupports)
-  : mInnerID(0)
+MessagePort::MessagePort(nsIGlobalObject* aGlobal)
+  : DOMEventTargetHelper(aGlobal)
+  , mInnerID(0)
   , mMessageQueueEnabled(false)
   , mIsKeptAlive(false)
 {
+  MOZ_ASSERT(aGlobal);
+
   mIdentifier = new MessagePortIdentifier();
   mIdentifier->neutered() = true;
   mIdentifier->sequenceId() = 0;
-
-  nsCOMPtr<nsIGlobalObject> globalObject = do_QueryInterface(aSupports);
-  if (NS_WARN_IF(!globalObject)) {
-    return;
-  }
-  BindToOwner(globalObject);
 }
 
 MessagePort::~MessagePort()
 {
   CloseForced();
   MOZ_ASSERT(!mWorkerFeature);
 }
 
 /* static */ already_AddRefed<MessagePort>
-MessagePort::Create(nsISupports* aSupport, const nsID& aUUID,
+MessagePort::Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
                     const nsID& aDestinationUUID, ErrorResult& aRv)
 {
-  RefPtr<MessagePort> mp = new MessagePort(aSupport);
+  MOZ_ASSERT(aGlobal);
+
+  RefPtr<MessagePort> mp = new MessagePort(aGlobal);
   mp->Initialize(aUUID, aDestinationUUID, 1 /* 0 is an invalid sequence ID */,
                  false /* Neutered */, eStateUnshippedEntangled, aRv);
   return mp.forget();
 }
 
 /* static */ already_AddRefed<MessagePort>
-MessagePort::Create(nsISupports* aSupport,
+MessagePort::Create(nsIGlobalObject* aGlobal,
                     const MessagePortIdentifier& aIdentifier,
                     ErrorResult& aRv)
 {
-  RefPtr<MessagePort> mp = new MessagePort(aSupport);
+  MOZ_ASSERT(aGlobal);
+
+  RefPtr<MessagePort> mp = new MessagePort(aGlobal);
   mp->Initialize(aIdentifier.uuid(), aIdentifier.destinationUuid(),
                  aIdentifier.sequenceId(), aIdentifier.neutered(),
                  eStateEntangling, aRv);
   return mp.forget();
 }
 
 void
 MessagePort::UnshippedEntangle(MessagePort* aEntangledPort)
@@ -415,17 +432,37 @@ MessagePort::PostMessage(JSContext* aCx,
       return;
     }
 
     transferable.setObject(*array);
   }
 
   RefPtr<SharedMessagePortMessage> data = new SharedMessagePortMessage();
 
+  UniquePtr<AbstractTimelineMarker> start;
+  UniquePtr<AbstractTimelineMarker> end;
+  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+  bool isTimelineRecording = timelines && !timelines->IsEmpty();
+
+  if (isTimelineRecording) {
+    start = MakeUnique<MessagePortTimelineMarker>(
+      ProfileTimelineMessagePortOperationType::SerializeData,
+      MarkerTracingType::START);
+  }
+
   data->Write(aCx, aMessage, transferable, aRv);
+
+  if (isTimelineRecording) {
+    end = MakeUnique<MessagePortTimelineMarker>(
+      ProfileTimelineMessagePortOperationType::SerializeData,
+      MarkerTracingType::END);
+    timelines->AddMarkerForAllObservedDocShells(start);
+    timelines->AddMarkerForAllObservedDocShells(end);
+  }
+
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   // This message has to be ignored.
   if (mState > eStateEntangled) {
     return;
   }
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -11,17 +11,17 @@
 #include "mozilla/DOMEventTargetHelper.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "nsTArray.h"
 
 #ifdef XP_WIN
 #undef PostMessage
 #endif
 
-class nsPIDOMWindowInner;
+class nsIGlobalObject;
 
 namespace mozilla {
 namespace dom {
 
 class MessagePortChild;
 class MessagePortIdentifier;
 class MessagePortMessage;
 class PostMessageRunnable;
@@ -40,21 +40,22 @@ class MessagePort final : public DOMEven
 public:
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
                                            DOMEventTargetHelper)
 
   static already_AddRefed<MessagePort>
-  Create(nsISupports* aSupport, const nsID& aUUID,
+  Create(nsIGlobalObject* aGlobal, const nsID& aUUID,
          const nsID& aDestinationUUID, ErrorResult& aRv);
 
   static already_AddRefed<MessagePort>
-  Create(nsISupports* aSupport, const MessagePortIdentifier& aIdentifier,
+  Create(nsIGlobalObject* aGlobal,
+         const MessagePortIdentifier& aIdentifier,
          ErrorResult& aRv);
 
   // For IPC.
   static void
   ForceClose(const MessagePortIdentifier& aIdentifier);
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
@@ -83,17 +84,17 @@ public:
   // These methods are useful for MessagePortChild
 
   void Entangled(nsTArray<MessagePortMessage>& aMessages);
   void MessagesReceived(nsTArray<MessagePortMessage>& aMessages);
   void StopSendingDataConfirmed();
   void Closed();
 
 private:
-  explicit MessagePort(nsISupports* nsISupports);
+  explicit MessagePort(nsIGlobalObject* aGlobal);
   ~MessagePort();
 
   enum State {
     // When a port is created by a MessageChannel it is entangled with the
     // other. They both run on the same thread, same event loop and the
     // messages are added to the queues without using PBackground actors.
     // When one of the port is shipped, the state is changed to
     // StateEntangling.
--- a/dom/push/test/test_has_permissions.html
+++ b/dom/push/test/test_has_permissions.html
@@ -63,16 +63,17 @@ http://creativecommons.org/licenses/publ
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.addPermission("desktop-notification", false, document);
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SimpleTest.waitForExplicitFinish();
 
 </script>
 </body>
--- a/dom/push/test/test_multiple_register.html
+++ b/dom/push/test/test_multiple_register.html
@@ -115,16 +115,17 @@ http://creativecommons.org/licenses/publ
     .then(unregister)
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission("desktop-notification", true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
--- a/dom/push/test/test_multiple_register_different_scope.html
+++ b/dom/push/test/test_multiple_register_different_scope.html
@@ -110,16 +110,17 @@ http://creativecommons.org/licenses/publ
     )
     .catch(err => {
       ok(false, "Some test failed with error " + err);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission("desktop-notification", true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
--- a/dom/push/test/test_multiple_register_during_service_activation.html
+++ b/dom/push/test/test_multiple_register_during_service_activation.html
@@ -97,16 +97,17 @@ var defaultServerURL = SpecialPowers.get
     )
     .catch(err => {
       ok(false, "Some test failed with error " + err);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.push.serverURL", "wss://something.org"]
     ]}, runTest);
   SpecialPowers.addPermission("desktop-notification", true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
--- a/dom/push/test/test_serviceworker_lifetime.html
+++ b/dom/push/test/test_serviceworker_lifetime.html
@@ -318,18 +318,18 @@
       .then(unregister)
       .catch(function(e) {
         ok(false, "Some test failed with error " + e)
       }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission('desktop-notification', true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
 </html>
--- a/dom/push/test/test_try_registering_offline_disabled.html
+++ b/dom/push/test/test_try_registering_offline_disabled.html
@@ -284,16 +284,17 @@ http://creativecommons.org/licenses/publ
     .then(_ => runTest4())
     .then(_ => runTest5())
     .then(_ => runTest6())
     .then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission("desktop-notification", true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
--- a/dom/push/test/test_unregister.html
+++ b/dom/push/test/test_unregister.html
@@ -76,16 +76,17 @@ http://creativecommons.org/licenses/publ
     .then(unregisterSW)
     .catch(function(e) {
       ok(false, "Some test failed with error " + e);
     }).then(SimpleTest.finish);
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.push.enabled", true],
+    ["dom.push.connection.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   SpecialPowers.addPermission("desktop-notification", true, document);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
--- a/dom/push/test/test_utils.js
+++ b/dom/push/test/test_utils.js
@@ -14,16 +14,17 @@ function setPushPermission(allow) {
       ], resolve);
   });
 }
 
 function setupPrefs() {
   return new Promise(resolve => {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.push.enabled", true],
+      ["dom.push.connection.enabled", true],
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true]
       ]}, resolve);
   });
 }
 
 function injectControlledFrame(target = document.body) {
--- a/dom/push/test/xpcshell/test_notification_http2.js
+++ b/dom/push/test/xpcshell/test_notification_http2.js
@@ -4,34 +4,41 @@
 'use strict';
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 const {PushDB, PushService, PushServiceHttp2} = serviceExports;
 
 var prefs;
 var tlsProfile;
+var pushEnabled;
+var pushConnectionEnabled;
 
 var serverPort = -1;
 
 function run_test() {
   var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   serverPort = env.get("MOZHTTP2_PORT");
   do_check_neq(serverPort, null);
   dump("using port " + serverPort + "\n");
 
   do_get_profile();
+  setPrefs();
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
 
   tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
+  pushEnabled = prefs.getBoolPref("dom.push.enabled");
+  pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
+  prefs.setBoolPref("dom.push.enabled", true);
+  prefs.setBoolPref("dom.push.connection.enabled", true);
 
   addCertOverride("localhost", serverPort,
                   Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                   Ci.nsICertOverrideService.ERROR_MISMATCH |
                   Ci.nsICertOverrideService.ERROR_TIME);
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
@@ -150,9 +157,11 @@ add_task(function* test_pushNotification
   });
 
   yield waitForPromise(notifyPromise, DEFAULT_TIMEOUT,
     'Timed out waiting for notifications');
 });
 
 add_task(function* test_complete() {
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
+  prefs.setBoolPref("dom.push.enabled", pushEnabled);
+  prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled);
 });
--- a/dom/push/test/xpcshell/test_register_success_http2.js
+++ b/dom/push/test/xpcshell/test_register_success_http2.js
@@ -6,32 +6,38 @@
 Cu.import("resource://gre/modules/Services.jsm");
 
 const {PushDB, PushService, PushServiceHttp2} = serviceExports;
 
 var prefs;
 var tlsProfile;
 var serverURL;
 var serverPort = -1;
+var pushEnabled;
+var pushConnectionEnabled;
 var db;
 
 function run_test() {
   var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   serverPort = env.get("MOZHTTP2_PORT");
   do_check_neq(serverPort, null);
 
   do_get_profile();
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
 
   tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
+  pushEnabled = prefs.getBoolPref("dom.push.enabled");
+  pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
+  prefs.setBoolPref("dom.push.enabled", true);
+  prefs.setBoolPref("dom.push.connection.enabled", true);
 
   addCertOverride("localhost", serverPort,
                   Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                   Ci.nsICertOverrideService.ERROR_MISMATCH |
                   Ci.nsICertOverrideService.ERROR_TIME);
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
@@ -114,9 +120,11 @@ add_task(function* test_pushSubscription
   equal(record.pushReceiptEndpoint, pushReceiptEndpoint,
     'Wrong push endpoint receipt in database record');
   equal(record.scope, 'https://example.org/no_receiptEndpoint',
     'Wrong scope in database record');
 });
 
 add_task(function* test_complete() {
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
+  prefs.setBoolPref("dom.push.enabled", pushEnabled);
+  prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled);
 });
--- a/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
+++ b/dom/push/test/xpcshell/test_resubscribe_4xxCode_http2.js
@@ -48,16 +48,17 @@ httpServer.registerPathHandler("/newSubs
 httpServer.start(-1);
 
 function run_test() {
 
   do_get_profile();
 
   servicePrefs.set('testing.notifyWorkers', false);
   servicePrefs.set('testing.notifyAllObservers', true);
+  setPrefs();
 
   run_next_test();
 }
 
 add_task(function* test1() {
 
   let db = PushServiceHttp2.newPushDB();
   do_register_cleanup(() => {
--- a/dom/push/test/xpcshell/test_unregister_success_http2.js
+++ b/dom/push/test/xpcshell/test_unregister_success_http2.js
@@ -13,33 +13,39 @@ Cu.import("resource://testing-common/Pro
 //
 // Instances of the rejection "record is undefined" may or may not appear.
 PromiseTestUtils.thisTestLeaksUncaughtRejectionsAndShouldBeFixed();
 
 const {PushDB, PushService, PushServiceHttp2} = serviceExports;
 
 var prefs;
 var tlsProfile;
+var pushEnabled;
+var pushConnectionEnabled;
 
 var serverPort = -1;
 
 function run_test() {
   var env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
   serverPort = env.get("MOZHTTP2_PORT");
   do_check_neq(serverPort, null);
 
   do_get_profile();
   prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
 
   tlsProfile = prefs.getBoolPref("network.http.spdy.enforce-tls-profile");
+  pushEnabled = prefs.getBoolPref("dom.push.enabled");
+  pushConnectionEnabled = prefs.getBoolPref("dom.push.connection.enabled");
 
   // Set to allow the cert presented by our H2 server
   var oldPref = prefs.getIntPref("network.http.speculative-parallel-limit");
   prefs.setIntPref("network.http.speculative-parallel-limit", 0);
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", false);
+  prefs.setBoolPref("dom.push.enabled", true);
+  prefs.setBoolPref("dom.push.connection.enabled", true);
 
   addCertOverride("localhost", serverPort,
                   Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                   Ci.nsICertOverrideService.ERROR_MISMATCH |
                   Ci.nsICertOverrideService.ERROR_TIME);
 
   prefs.setIntPref("network.http.speculative-parallel-limit", oldPref);
 
@@ -76,9 +82,11 @@ add_task(function* test_pushUnsubscripti
   });
   let record = yield db.getByKeyID(serverURL + '/subscriptionUnsubscriptionSuccess');
   ok(!record, 'Unregister did not remove record');
 
 });
 
 add_task(function* test_complete() {
   prefs.setBoolPref("network.http.spdy.enforce-tls-profile", tlsProfile);
+  prefs.setBoolPref("dom.push.enabled", pushEnabled);
+  prefs.setBoolPref("dom.push.connection.enabled", pushConnectionEnabled);
 });
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -3768,27 +3768,28 @@ QuotaManager::OpenDirectory(PersistenceT
                         false,
                         aOpenListener);
   MOZ_ASSERT(lock);
 }
 
 void
 QuotaManager::OpenDirectoryInternal(Nullable<PersistenceType> aPersistenceType,
                                     const OriginScope& aOriginScope,
+                                    Nullable<Client::Type> aClientType,
                                     bool aExclusive,
                                     OpenDirectoryListener* aOpenListener)
 {
   AssertIsOnOwningThread();
 
   RefPtr<DirectoryLockImpl> lock =
     CreateDirectoryLock(aPersistenceType,
                         EmptyCString(),
                         aOriginScope,
                         Nullable<bool>(),
-                        Nullable<Client::Type>(),
+                        Nullable<Client::Type>(aClientType),
                         aExclusive,
                         true,
                         aOpenListener);
   MOZ_ASSERT(lock);
 
   if (!aExclusive) {
     return;
   }
@@ -5024,16 +5025,17 @@ NormalOriginOperationBase::Open()
   AssertIsOnOwningThread();
   MOZ_ASSERT(GetState() == State_CreatingQuotaManager);
   MOZ_ASSERT(QuotaManager::Get());
 
   AdvanceState();
 
   QuotaManager::Get()->OpenDirectoryInternal(mPersistenceType,
                                              mOriginScope,
+                                             Nullable<Client::Type>(),
                                              mExclusive,
                                              this);
 }
 
 void
 NormalOriginOperationBase::UnblockOpen()
 {
   AssertIsOnOwningThread();
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -225,16 +225,17 @@ public:
                 Client::Type aClientType,
                 bool aExclusive,
                 OpenDirectoryListener* aOpenListener);
 
   // XXX RemoveMe once bug 1170279 gets fixed.
   void
   OpenDirectoryInternal(Nullable<PersistenceType> aPersistenceType,
                         const OriginScope& aOriginScope,
+                        Nullable<Client::Type> aClientType,
                         bool aExclusive,
                         OpenDirectoryListener* aOpenListener);
 
   // Collect inactive and the least recently used origins.
   uint64_t
   CollectOriginsForEviction(uint64_t aMinSizeToBeFreed,
                             nsTArray<RefPtr<DirectoryLockImpl>>& aLocks);
 
--- a/dom/quota/moz.build
+++ b/dom/quota/moz.build
@@ -11,16 +11,17 @@ XPIDL_SOURCES += [
 ]
 
 XPIDL_MODULE = 'dom_quota'
 
 EXPORTS.mozilla.dom.quota += [
     'ActorsParent.h',
     'Client.h',
     'FileStreams.h',
+    'OriginScope.h',
     'PersistenceType.h',
     'QuotaCommon.h',
     'QuotaManager.h',
     'QuotaManagerService.h',
     'QuotaObject.h',
     'SerializationHelpers.h',
     'UsageInfo.h',
 ]
--- a/dom/security/test/csp/test_child-src_worker.html
+++ b/dom/security/test/csp/test_child-src_worker.html
@@ -130,17 +130,16 @@
           content.appendChild(testframe);
           testframe.src = src;
         }
       }
 
       onload = function() {
         SpecialPowers.pushPrefEnv({"set": [
           ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-          ["dom.serviceWorkers.interception.enabled", true],
           ["dom.serviceWorkers.enabled", true],
           ["dom.serviceWorkers.testing.enabled", true],
           ["dom.caches.enabled", true]
         ]}, loadNextTest);
       };
 
       // start running the tests
       //loadNextTest();
--- a/dom/security/test/csp/test_service_worker.html
+++ b/dom/security/test/csp/test_service_worker.html
@@ -31,17 +31,16 @@ window.addEventListener("message", recei
 function receiveMessage(event) {
   is(event.data.result, curTest.expected, "Should be (" + curTest.expected + ") in Test " + counter + "!");
   loadNextTest();
 }
 
 onload = function() {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.caches.enabled", true]
   ]}, loadNextTest);
 }
 
 function loadNextTest() {
   if (counter == tests.length) {
--- a/dom/tests/mochitest/fetch/sw_reroute.js
+++ b/dom/tests/mochitest/fetch/sw_reroute.js
@@ -7,17 +7,16 @@ function testScript(script) {
     var iframe = document.createElement("iframe");
     iframe.src = "reroute.html?" + script.replace(".js", "");
     document.body.appendChild(iframe);
   }
 
   SpecialPowers.pushPrefEnv({
     "set": [["dom.requestcache.enabled", true],
             ["dom.serviceWorkers.enabled", true],
-            ["dom.serviceWorkers.interception.opaque.enabled", true],
             ["dom.serviceWorkers.testing.enabled", true],
             ["dom.serviceWorkers.exemptFromPerDomainMax", true]]
   }, function() {
     navigator.serviceWorker.ready.then(setupSW);
     var scriptURL = location.href.includes("sw_empty_reroute.html")
                   ? "empty.js" : "reroute.js";
     navigator.serviceWorker.register(scriptURL, {scope: "/"});
   });
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1373,17 +1373,17 @@ var interfaceNamesInGlobalScope =
     {name: "TVProgram", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVScanningStateChangedEvent", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVSource", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "TVTuner", b2g: true, permission: ["tv"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "U2F", release: false},
+    {name: "U2F", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "UDPMessageEvent", b2g: true, permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "UDPSocket", b2g: true, permission: ["udp-socket"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UIEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UndoManager",
--- a/dom/webidl/ChromeUtils.webidl
+++ b/dom/webidl/ChromeUtils.webidl
@@ -25,23 +25,49 @@ interface ChromeUtils : ThreadSafeChrome
    * @param originAttrs       The originAttributes under consideration.
    * @param pattern           The pattern to use for matching.
    */
   static boolean
   originAttributesMatchPattern(optional OriginAttributesDictionary originAttrs,
                                optional OriginAttributesPatternDictionary pattern);
 
   /**
-   * Returns an OriginAttributes dictionary using the origin URI but forcing
-   * the passed userContextId.
+   * Returns an OriginAttributesDictionary with all default attributes added
+   * and assigned default values.
+   *
+   * @returns                 An OriginAttributesDictionary populated with the
+   *                          default attributes added and assigned default values.
+   */
+  static OriginAttributesDictionary
+  createDefaultOriginAttributes();
+
+  /**
+   * Returns an OriginAttributesDictionary with values from the |origin| suffix
+   * and unspecified attributes added and assigned default values.
+   *
+   * @param origin            The origin URI to create from.
+   * @returns                 An OriginAttributesDictionary with values from
+   *                          the origin suffix and unspecified attributes
+   *                          added and assigned default values.
    */
   [Throws]
   static OriginAttributesDictionary
-  createOriginAttributesWithUserContextId(DOMString origin,
-                                          unsigned long userContextId);
+  createOriginAttributesFromOrigin(DOMString origin);
+
+  /**
+   * Returns an OriginAttributesDictionary that is a copy of |originAttrs| with
+   * unspecified attributes added and assigned default values.
+   *
+   * @param originAttrs       The origin attributes to copy.
+   * @returns                 An OriginAttributesDictionary copy of |originAttrs|
+   *                          with unspecified attributes added and assigned
+   *                          default values.
+   */
+  static OriginAttributesDictionary
+  createOriginAttributesFromDict(optional OriginAttributesDictionary originAttrs);
 
   /**
    * Returns true if the 2 OriginAttributes are equal.
    */
   static boolean
   isOriginAttributesEqual(optional OriginAttributesDictionary aA,
                           optional OriginAttributesDictionary aB);
 };
--- a/dom/webidl/ProfileTimelineMarker.webidl
+++ b/dom/webidl/ProfileTimelineMarker.webidl
@@ -20,16 +20,21 @@ dictionary ProfileTimelineStackFrame {
 
 dictionary ProfileTimelineLayerRect {
   long x = 0;
   long y = 0;
   long width = 0;
   long height = 0;
 };
 
+enum ProfileTimelineMessagePortOperationType {
+  "serializeData",
+  "deserializeData",
+};
+
 enum ProfileTimelineWorkerOperationType {
   "serializeDataOffMainThread",
   "serializeDataOnMainThread",
   "deserializeDataOffMainThread",
   "deserializeDataOnMainThread",
 };
 
 dictionary ProfileTimelineMarker {
@@ -56,11 +61,14 @@ dictionary ProfileTimelineMarker {
   unsigned long long unixTime; // in microseconds
 
   /* For Paint markers.  */
   sequence<ProfileTimelineLayerRect> rectangles;
 
   /* For Style markers. */
   DOMString restyleHint;
 
+  /* For MessagePort markers. */
+  ProfileTimelineMessagePortOperationType messagePortOperation;
+
   /* For Worker markers. */
   ProfileTimelineWorkerOperationType workerOperation;
 };
--- a/dom/webidl/ServiceWorkerGlobalScope.webidl
+++ b/dom/webidl/ServiceWorkerGlobalScope.webidl
@@ -17,17 +17,16 @@ interface ServiceWorkerGlobalScope : Wor
   [SameObject] readonly attribute ServiceWorkerRegistration registration;
 
   [Throws, NewObject]
   Promise<void> skipWaiting();
 
   attribute EventHandler oninstall;
   attribute EventHandler onactivate;
 
-  [Func="mozilla::dom::workers::ServiceWorkerGlobalScope::InterceptionEnabled"]
   attribute EventHandler onfetch;
 
   // The event.source of these MessageEvents are instances of Client
   attribute EventHandler onmessage;
 };
 
 // These are from w3c.github.io/push-api/
 partial interface ServiceWorkerGlobalScope {
--- a/dom/webidl/U2F.webidl
+++ b/dom/webidl/U2F.webidl
@@ -63,16 +63,17 @@ dictionary SignResponse {
     // From Error
     ErrorCode? errorCode;
     DOMString? errorMessage;
 };
 
 callback U2FRegisterCallback = void(RegisterResponse response);
 callback U2FSignCallback = void(SignResponse response);
 
+[Pref="security.webauth.u2f"]
 interface U2F {
   // These enumerations are defined in the FIDO U2F Javascript API under the
   // interface "ErrorCode" as constant integers, and also in the U2F.cpp file.
   // Any changes to these must occur in both locations.
   const unsigned short OK = 0;
   const unsigned short OTHER_ERROR = 1;
   const unsigned short BAD_REQUEST = 2;
   const unsigned short CONFIGURATION_UNSUPPORTED = 3;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -2283,23 +2283,24 @@ RuntimeService::CreateSharedWorkerFromLo
     // If we're attaching to an existing SharedWorker private, then we
     // must update the overriden load group to account for our document's
     // load group.
     workerPrivate->UpdateOverridenLoadGroup(aLoadInfo->mLoadGroup);
   }
 
   // We don't actually care about this MessageChannel, but we use it to 'steal'
   // its 2 connected ports.
-  RefPtr<MessageChannel> channel = MessageChannel::Constructor(window, rv);
+  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
+  RefPtr<MessageChannel> channel = MessageChannel::Constructor(global, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return rv.StealNSResult();
   }
 
   RefPtr<SharedWorker> sharedWorker = new SharedWorker(window, workerPrivate,
-                                                         channel->Port1());
+                                                       channel->Port1());
 
   if (!workerPrivate->RegisterSharedWorker(sharedWorker, channel->Port2())) {
     NS_WARNING("Worker is unreachable, this shouldn't happen!");
     sharedWorker->Close();
     return NS_ERROR_FAILURE;
   }
 
   // This is normally handled in RegisterWorker, but that wasn't called if the
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -549,25 +549,16 @@ RespondWithHandler::ResolvedCallback(JSC
                                            mRequestURL, valueString);
     return;
   }
 
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 
-  // Allow opaque response interception to be disabled until we can ensure the
-  // security implications are not a complete disaster.
-  if (response->Type() == ResponseType::Opaque &&
-      !worker->OpaqueInterceptionEnabled()) {
-    autoCancel.SetCancelMessage(
-      NS_LITERAL_CSTRING("OpaqueInterceptionDisabledWithURL"), mRequestURL);
-    return;
-  }
-
   // Section "HTTP Fetch", step 2.2:
   //  If one of the following conditions is true, return a network error:
   //    * response's type is "error".
   //    * request's mode is not "no-cors" and response's type is "opaque".
   //    * request is not a navigation request and response's type is
   //      "opaqueredirect".
 
   if (response->Type() == ResponseType::Error) {
--- a/dom/workers/WorkerPrefs.h
+++ b/dom/workers/WorkerPrefs.h
@@ -26,18 +26,16 @@ WORKER_SIMPLE_PREF("browser.dom.window.d
 #endif
 WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
 WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING)
 WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED)
 WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
 WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
-WORKER_SIMPLE_PREF("dom.serviceWorkers.interception.enabled", InterceptionEnabled, INTERCEPTION_ENABLED)
-WORKER_SIMPLE_PREF("dom.serviceWorkers.interception.opaque.enabled", OpaqueInterceptionEnabled, INTERCEPTION_OPAQUE_ENABLED)
 WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
 WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
 WORKER_SIMPLE_PREF("dom.requestcache.enabled", RequestCacheEnabled, REQUESTCACHE_ENABLED)
 WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
 WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
 WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged)
 WORKER_PREF("intl.accept_languages", PrefLanguagesChanged)
 WORKER_PREF("general.appname.override", AppNameOverrideChanged)
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -663,20 +663,33 @@ public:
   {
     mEventSource = Move(aSource);
   }
 
   bool
   DispatchDOMEvent(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
                    DOMEventTargetHelper* aTarget, bool aIsMainThread)
   {
-    nsCOMPtr<nsPIDOMWindowInner> parent;
-    if (aIsMainThread) {
-      parent = do_QueryInterface(aTarget->GetParentObject());
-    }
+    nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(aTarget->GetParentObject());
+
+    // For some workers without window, parent is null and we try to find it
+    // from the JS Context.
+    if (!parent) {
+      JS::Rooted<JSObject*> globalObject(aCx, JS::CurrentGlobalOrNull(aCx));
+      if (NS_WARN_IF(!globalObject)) {
+        return false;
+      }
+
+      parent = xpc::NativeGlobal(globalObject);
+      if (NS_WARN_IF(!parent)) {
+        return false;
+      }
+    }
+
+    MOZ_ASSERT(parent);
 
     JS::Rooted<JS::Value> messageData(aCx);
     ErrorResult rv;
 
     UniquePtr<AbstractTimelineMarker> start;
     UniquePtr<AbstractTimelineMarker> end;
     RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
     bool isTimelineRecording = timelines && !timelines->IsEmpty();
@@ -6371,17 +6384,17 @@ WorkerPrivate::ConnectMessagePort(JSCont
   WorkerGlobalScope* globalScope = GlobalScope();
 
   JS::Rooted<JSObject*> jsGlobal(aCx, globalScope->GetWrapper());
   MOZ_ASSERT(jsGlobal);
 
   // This MessagePortIdentifier is used to create a new port, still connected
   // with the other one, but in the worker thread.
   ErrorResult rv;
-  RefPtr<MessagePort> port = MessagePort::Create(nullptr, aIdentifier, rv);
+  RefPtr<MessagePort> port = MessagePort::Create(globalScope, aIdentifier, rv);
   if (NS_WARN_IF(rv.Failed())) {
     return false;
   }
 
   GlobalObject globalObject(aCx, jsGlobal);
   if (globalObject.Failed()) {
     return false;
   }
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -628,26 +628,16 @@ ServiceWorkerGlobalScope::SkipWaiting(Er
   RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
     new WorkerScopeSkipWaitingRunnable(promiseProxy,
                                        NS_ConvertUTF16toUTF8(mScope));
 
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable)));
   return promise.forget();
 }
 
-// static
-bool
-ServiceWorkerGlobalScope::InterceptionEnabled(JSContext* aCx, JSObject* aObj)
-{
-  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
-  MOZ_ASSERT(worker);
-  worker->AssertIsOnWorkerThread();
-  return worker->InterceptionEnabled();
-}
-
 bool
 ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
 {
   WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
   return worker->OpenWindowEnabled();
 }
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -235,19 +235,16 @@ public:
 
   ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
 
   virtual bool
   WrapGlobalObject(JSContext* aCx,
                    JS::MutableHandle<JSObject*> aReflector) override;
 
   static bool
-  InterceptionEnabled(JSContext* aCx, JSObject* aObj);
-
-  static bool
   OpenWindowEnabled(JSContext* aCx, JSObject* aObj);
 
   void
   GetScope(nsString& aScope) const
   {
     aScope = mScope;
   }
 
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/StructuredCloneHolder.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsThreadUtils.h"
 #include "nsVariant.h"
 
 #include "RuntimeService.h"
+#include "WorkerScope.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "XMLHttpRequestUpload.h"
 
 #include "mozilla/UniquePtr.h"
 
 using namespace mozilla;
 
@@ -1350,17 +1351,22 @@ EventRunnable::WorkerRun(JSContext* aCx,
     state->mResponseResult = mResponseResult;
 
     if (NS_SUCCEEDED(mResponseResult)) {
       if (HasData()) {
         MOZ_ASSERT(mResponse.isUndefined());
 
         ErrorResult rv;
         JS::Rooted<JS::Value> response(aCx);
-        Read(nullptr, aCx, &response, rv);
+
+        GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
+        nsCOMPtr<nsIGlobalObject> global =
+          do_QueryInterface(globalObj.GetAsSupports());
+
+        Read(global, aCx, &response, rv);
         if (NS_WARN_IF(rv.Failed())) {
           rv.SuppressException();
           return false;
         }
 
         state->mResponse = response;
       }
       else {
@@ -1527,18 +1533,28 @@ SendRunnable::MainThreadRun()
     AutoSafeJSContext cx;
     JSAutoRequest ar(cx);
 
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
     MOZ_ASSERT(xpc);
 
     ErrorResult rv;
 
+    JS::Rooted<JSObject*> globalObject(cx, JS::CurrentGlobalOrNull(cx));
+    if (NS_WARN_IF(!globalObject)) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsCOMPtr<nsIGlobalObject> parent = xpc::NativeGlobal(globalObject);
+    if (NS_WARN_IF(!parent)) {
+      return NS_ERROR_FAILURE;
+    }
+
     JS::Rooted<JS::Value> body(cx);
-    Read(nullptr, cx, &body, rv);
+    Read(parent, cx, &body, rv);
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
 
     rv = xpc->JSValToVariant(cx, body, getter_AddRefs(variant));
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
--- a/dom/workers/test/serviceworkers/browser_download.js
+++ b/dom/workers/test/serviceworkers/browser_download.js
@@ -40,18 +40,17 @@ function windowObserver(win, topic) {
 
 function test() {
   waitForExplicitFinish();
 
   Services.ww.registerNotification(windowObserver);
 
   SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true],
                                      ['dom.serviceWorkers.exemptFromPerDomainMax', true],
-                                     ['dom.serviceWorkers.testing.enabled', true],
-                                     ['dom.serviceWorkers.interception.enabled', true]]},
+                                     ['dom.serviceWorkers.testing.enabled', true]]},
                             function() {
     var url = gTestRoot + 'download/window.html';
     var tab = gBrowser.addTab();
     gBrowser.selectedTab = tab;
 
     Downloads.getList(Downloads.ALL).then(function(downloadList) {
       var downloadListener;
 
--- a/dom/workers/test/serviceworkers/browser_force_refresh.js
+++ b/dom/workers/test/serviceworkers/browser_force_refresh.js
@@ -24,17 +24,16 @@ function frameScript() {
   addEventListener('cached-load', eventHandler, true, true);
 }
 
 function test() {
   waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({'set': [['dom.serviceWorkers.enabled', true],
                                      ['dom.serviceWorkers.exemptFromPerDomainMax', true],
                                      ['dom.serviceWorkers.testing.enabled', true],
-                                     ['dom.serviceWorkers.interception.enabled', true],
                                      ['dom.caches.enabled', true],
                                      ['browser.cache.disk.enable', false],
                                      ['browser.cache.memory.enable', false]]},
                             function() {
     var url = gTestRoot + 'browser_base_force_refresh.html';
     var tab = gBrowser.addTab();
     gBrowser.selectedTab = tab;
 
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/interception_featuredetect.js
+++ /dev/null
@@ -1,4 +0,0 @@
-// Only succeeds if onfetch is available.
-if (!("onfetch" in self)) {
-  throw new Error("Not capable of interception");
-}
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -166,17 +166,16 @@ support-files =
   sw_clients/refresher_compressed.html
   sw_clients/refresher_compressed.html^headers^
   sw_clients/refresher_cached.html
   sw_clients/refresher_cached_compressed.html
   sw_clients/refresher_cached_compressed.html^headers^
   strict_mode_warning.js
   skip_waiting_installed_worker.js
   skip_waiting_scope/index.html
-  interception_featuredetect.js
   thirdparty/iframe1.html
   thirdparty/iframe2.html
   thirdparty/register.html
   thirdparty/unregister.html
   thirdparty/sw.js
   register_https.html
   gzip_redirect_worker.js
   sw_clients/navigator.html
@@ -234,18 +233,16 @@ skip-if = e10s && debug && os == 'win'
 [test_https_synth_fetch_from_cached_sw.html]
 skip-if = e10s && debug && os == 'win'
 [test_importscript.html]
 skip-if = e10s && debug && os == 'win'
 [test_install_event.html]
 skip-if = e10s && debug && os == 'win'
 [test_installation_simple.html]
 skip-if = e10s && debug && os == 'win'
-[test_interception_featuredetect.html]
-skip-if = e10s && debug && os == 'win'
 [test_match_all.html]
 skip-if = e10s && debug && os == 'win'
 [test_match_all_advanced.html]
 skip-if = e10s && debug && os == 'win'
 [test_match_all_client_id.html]
 skip-if = e10s && debug && os == 'win'
 [test_match_all_client_properties.html]
 skip-if = e10s && debug && os == 'win'
--- a/dom/workers/test/serviceworkers/test_bug1151916.html
+++ b/dom/workers/test/serviceworkers/test_bug1151916.html
@@ -89,17 +89,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_claim.html
+++ b/dom/workers/test/serviceworkers/test_claim.html
@@ -157,17 +157,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_claim_fetch.html
+++ b/dom/workers/test/serviceworkers/test_claim_fetch.html
@@ -83,17 +83,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_claim_oninstall.html
+++ b/dom/workers/test/serviceworkers/test_claim_oninstall.html
@@ -63,17 +63,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_client_focus.html
+++ b/dom/workers/test/serviceworkers/test_client_focus.html
@@ -81,17 +81,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_close.html
+++ b/dom/workers/test/serviceworkers/test_close.html
@@ -49,17 +49,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_controller.html
+++ b/dom/workers/test/serviceworkers/test_controller.html
@@ -69,17 +69,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_cross_origin_url_after_redirect.html
@@ -35,17 +35,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html
+++ b/dom/workers/test/serviceworkers/test_csp_upgrade-insecure_intercept.html
@@ -39,17 +39,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       // This is needed so that we can test upgrading a non-secure load inside an https iframe.
       ["security.mixed_content.block_active_content", false],
       ["security.mixed_content.block_display_content", false],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_empty_serviceworker.html
+++ b/dom/workers/test/serviceworkers/test_empty_serviceworker.html
@@ -31,17 +31,16 @@
       SimpleTest.finish();
     });
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_eval_allowed.html
+++ b/dom/workers/test/serviceworkers/test_eval_allowed.html
@@ -39,14 +39,13 @@
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_eventsource_intercept.html
+++ b/dom/workers/test/serviceworkers/test_eventsource_intercept.html
@@ -90,16 +90,14 @@
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_fetch_event.html
+++ b/dom/workers/test/serviceworkers/test_fetch_event.html
@@ -68,18 +68,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_file_blob_upload.html
+++ b/dom/workers/test/serviceworkers/test_file_blob_upload.html
@@ -130,17 +130,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_force_refresh.html
+++ b/dom/workers/test/serviceworkers/test_force_refresh.html
@@ -69,17 +69,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_gzip_redirect.html
+++ b/dom/workers/test/serviceworkers/test_gzip_redirect.html
@@ -72,14 +72,13 @@
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html
+++ b/dom/workers/test/serviceworkers/test_hsts_upgrade_intercept.html
@@ -49,17 +49,16 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       // This is needed so that we can test upgrading a non-secure load inside an https iframe.
       ["security.mixed_content.block_active_content", false],
       ["security.mixed_content.block_display_content", false],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_https_fetch.html
+++ b/dom/workers/test/serviceworkers/test_https_fetch.html
@@ -46,17 +46,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
       ["dom.caches.enabled", true]
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
+++ b/dom/workers/test/serviceworkers/test_https_fetch_cloned_response.html
@@ -40,17 +40,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
       ["dom.caches.enabled", true]
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_https_origin_after_redirect_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
+++ b/dom/workers/test/serviceworkers/test_https_synth_fetch_from_cached_sw.html
@@ -53,17 +53,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
       ["dom.caches.enabled", true]
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_imagecache.html
+++ b/dom/workers/test/serviceworkers/test_imagecache.html
@@ -40,17 +40,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_imagecache_max_age.html
+++ b/dom/workers/test/serviceworkers/test_imagecache_max_age.html
@@ -58,15 +58,14 @@
 
   SimpleTest.requestFlakyTimeout("This test needs to simulate the passing of time");
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ["dom.serviceWorkers.interception.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_importscript.html
+++ b/dom/workers/test/serviceworkers/test_importscript.html
@@ -58,16 +58,15 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html
+++ b/dom/workers/test/serviceworkers/test_importscript_mixedcontent.html
@@ -37,17 +37,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
       ["security.mixed_content.block_active_content", false],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_install_event.html
+++ b/dom/workers/test/serviceworkers/test_install_event.html
@@ -129,17 +129,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -196,17 +196,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.caches.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
deleted file mode 100644
--- a/dom/workers/test/serviceworkers/test_interception_featuredetect.html
+++ /dev/null
@@ -1,96 +0,0 @@
-
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1173389 - Test fetch interception feature detection.</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-  function register() {
-    // Randomness required to prevent reusing existing registration.
-    return navigator.serviceWorker.register("interception_featuredetect.js",
-                                            { scope: "featuredetect/" + Math.random() });
-  }
-
-  function registerWithPrefDisabled() {
-    return new Promise(function(resolve, reject) {
-      SpecialPowers.pushPrefEnv(
-        {"set": [["dom.serviceWorkers.interception.enabled", false]]},
-        function() {
-          register().then(function(v) {
-              ok(false, "Registration should fail when interception is disabled.");
-              reject();
-            }, function(e) {
-              ok(true, "Registration should fail when interception is disabled.");
-              resolve();
-            });
-        });
-    });
-  }
-
-  function registerWithPrefEnabled() {
-    return new Promise(function(resolve, reject) {
-      SpecialPowers.pushPrefEnv(
-        {"set": [["dom.serviceWorkers.interception.enabled", true]]},
-        function() {
-          register().then(function(v) {
-              ok(true, "Registration should succeed when interception is enabled.");
-              resolve();
-            }, function(e) {
-              ok(false, "Registration should succeed when interception is enabled.");
-              reject()
-            });
-        });
-    });
-  }
-
-  function unregister() {
-    return navigator.serviceWorker.getRegistrations().then(function(regs) {
-      var unregs = [];
-      regs.forEach(function(reg) {
-        if (reg.scope.indexOf("featuredetect") > -1) {
-          unregs.push(reg.unregister());
-        }
-      })
-
-      return Promise.all(unregs);
-    });
-  }
-
-  function runTest() {
-    Promise.resolve()
-      .then(registerWithPrefDisabled)
-      .then(registerWithPrefEnabled)
-      .then(registerWithPrefDisabled)
-      .then(unregister)
-      // put more tests here.
-      .then(function() {
-        SimpleTest.finish();
-      }).catch(function(e) {
-        ok(false, "Some test failed with error " + e);
-        SimpleTest.finish();
-      });
-  }
-
-  SimpleTest.waitForExplicitFinish();
-  SpecialPowers.pushPrefEnv({"set": [
-    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", false],
-    ["dom.serviceWorkers.enabled", true],
-    ["dom.serviceWorkers.testing.enabled", true]
-  ]}, runTest);
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/workers/test/serviceworkers/test_match_all.html
+++ b/dom/workers/test/serviceworkers/test_match_all.html
@@ -65,17 +65,16 @@
         ok(true, "Didn't crash on resolving matchAll promises while worker shuts down.");
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_match_all_advanced.html
+++ b/dom/workers/test/serviceworkers/test_match_all_advanced.html
@@ -85,17 +85,16 @@
         SimpleTest.finish();
       });
 
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_match_all_client_id.html
+++ b/dom/workers/test/serviceworkers/test_match_all_client_id.html
@@ -76,16 +76,15 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_match_all_client_properties.html
+++ b/dom/workers/test/serviceworkers/test_match_all_client_properties.html
@@ -83,16 +83,15 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_navigator.html
+++ b/dom/workers/test/serviceworkers/test_navigator.html
@@ -23,17 +23,16 @@
     ok(navigator.serviceWorker.ready instanceof Promise, "navigator.serviceWorker.ready should be a Promise.");
     ok(navigator.serviceWorker.controller === null, "There should be no controller worker for an uncontrolled document.");
   }
 
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true]
   ]}, function() {
     checkEnabled();
     SimpleTest.finish();
   });
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
+++ b/dom/workers/test/serviceworkers/test_not_intercept_plugin.html
@@ -63,18 +63,16 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.requestcontext.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_notificationclick.html
+++ b/dom/workers/test/serviceworkers/test_notificationclick.html
@@ -50,14 +50,13 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.webnotifications.workers.enabled", true],
     ["dom.webnotifications.serviceworker.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
     ["notification.prompt.testing", true],
   ]}, runTest);
 </script>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_opaque_intercept.html
+++ b/dom/workers/test/serviceworkers/test_opaque_intercept.html
@@ -72,16 +72,14 @@
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ['dom.serviceWorkers.interception.enabled', true],
-    ["dom.serviceWorkers.interception.opaque.enabled", true],
     ["dom.caches.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
+++ b/dom/workers/test/serviceworkers/test_origin_after_redirect_to_https_cached.html
@@ -43,16 +43,15 @@
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
-      ['dom.serviceWorkers.interception.enabled', true],
       ["dom.caches.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_post_message.html
+++ b/dom/workers/test/serviceworkers/test_post_message.html
@@ -64,17 +64,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_post_message_advanced.html
+++ b/dom/workers/test/serviceworkers/test_post_message_advanced.html
@@ -93,17 +93,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_post_message_source.html
+++ b/dom/workers/test/serviceworkers/test_post_message_source.html
@@ -52,17 +52,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_request_context.js
+++ b/dom/workers/test/serviceworkers/test_request_context.js
@@ -65,13 +65,12 @@ onload = function() {
     ["beacon.enabled", true],
     ["browser.send_pings", true],
     ["browser.send_pings.max_per_link", -1],
     ["dom.caches.enabled", true],
     ["dom.image.picture.enabled", true],
     ["dom.image.srcset.enabled", true],
     ["dom.requestcontext.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 };
--- a/dom/workers/test/serviceworkers/test_sandbox_intercept.html
+++ b/dom/workers/test/serviceworkers/test_sandbox_intercept.html
@@ -35,17 +35,16 @@
       }
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_sanitize.html
+++ b/dom/workers/test/serviceworkers/test_sanitize.html
@@ -70,17 +70,16 @@
   function registerSW() {
     return testFrame("sanitize/register.html");
   }
 
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, function() {
     start();
   });
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_sanitize_domain.html
+++ b/dom/workers/test/serviceworkers/test_sanitize_domain.html
@@ -73,17 +73,16 @@
               return testFrame("http://prefixexample.com/tests/dom/workers/test/serviceworkers/sanitize/register.html");
             });
   }
 
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, function() {
     start();
   });
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/test_scopes.html
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -106,17 +106,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_service_worker_allowed.html
+++ b/dom/workers/test/serviceworkers/test_service_worker_allowed.html
@@ -62,14 +62,13 @@
     });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
+++ b/dom/workers/test/serviceworkers/test_serviceworker_interfaces.html
@@ -99,17 +99,16 @@
     // if service workers are disabled by default we want to force on both
     // service workers and "dom.caches.enabled".  But if service workers are
     // enabled by default, we do not want to mess with the "dom.caches.enabled"
     // value, since that would defeat the purpose of the test.  Use a subframe
     // to decide whether service workers are enabled by default, so we don't
     // force creation of our own Navigator object before our prefs are set.
     var prefs = [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ];
 
     var subframe = document.createElement("iframe");
     document.body.appendChild(subframe);
     if (!("serviceWorker" in subframe.contentWindow.navigator)) {
 	prefs.push(["dom.caches.enabled", true]);
--- a/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
+++ b/dom/workers/test/serviceworkers/test_serviceworker_not_sharedworker.html
@@ -51,17 +51,16 @@
       sw.postMessage({msg: "whoareyou"});
     };
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true]
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_skip_waiting.html
+++ b/dom/workers/test/serviceworkers/test_skip_waiting.html
@@ -81,16 +81,15 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_strict_mode_warning.html
+++ b/dom/workers/test/serviceworkers/test_strict_mode_warning.html
@@ -27,17 +27,16 @@
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   onload = function() {
     SpecialPowers.pushPrefEnv({"set": [
       ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-      ["dom.serviceWorkers.interception.enabled", true],
       ["dom.serviceWorkers.enabled", true],
       ["dom.serviceWorkers.testing.enabled", true],
     ]}, runTest);
   };
 </script>
 </pre>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_third_party_iframes.html
+++ b/dom/workers/test/serviceworkers/test_third_party_iframes.html
@@ -139,17 +139,16 @@ const COOKIE_BEHAVIOR_REJECTFOREIGN = 1;
 const COOKIE_BEHAVIOR_REJECT        = 2;
 const COOKIE_BEHAVIOR_LIMITFOREIGN  = 3;
 
 let steps = [() => {
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["browser.dom.window.dump.enabled", true],
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_ACCEPT]
   ]}, next);
 }, () => {
   testShouldIntercept(next);
 }, () => {
   SpecialPowers.pushPrefEnv({"set": [
     ["network.cookie.cookieBehavior", COOKIE_BEHAVIOR_REJECTFOREIGN]
--- a/dom/workers/test/serviceworkers/test_unregister.html
+++ b/dom/workers/test/serviceworkers/test_unregister.html
@@ -123,17 +123,16 @@
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
+++ b/dom/workers/test/serviceworkers/test_unresolved_fetch_interception.html
@@ -82,14 +82,13 @@
   }
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.idle_timeout", 0],
     ["dom.serviceWorkers.idle_extended_timeout", 299999],
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
-    ["dom.serviceWorkers.interception.enabled", true]
     ]}, runTest);
   SimpleTest.waitForExplicitFinish();
 </script>
 </body>
 </html>
--- a/dom/workers/test/serviceworkers/test_workerUnregister.html
+++ b/dom/workers/test/serviceworkers/test_workerUnregister.html
@@ -67,17 +67,16 @@
     }).then(function() {
       SimpleTest.finish();
     });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_workerUpdate.html
+++ b/dom/workers/test/serviceworkers/test_workerUpdate.html
@@ -47,17 +47,16 @@
     }).then(function() {
       SimpleTest.finish();
     });
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true]
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
+++ b/dom/workers/test/serviceworkers/test_workerupdatefoundevent.html
@@ -70,17 +70,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/workers/test/serviceworkers/test_xslt.html
+++ b/dom/workers/test/serviceworkers/test_xslt.html
@@ -113,17 +113,16 @@
       .catch(function(e) {
         ok(false, "Some test failed with error " + e);
       }).then(SimpleTest.finish);
   }
 
   SimpleTest.waitForExplicitFinish();
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.exemptFromPerDomainMax", true],
-    ["dom.serviceWorkers.interception.enabled", true],
     ["dom.serviceWorkers.enabled", true],
     ["dom.serviceWorkers.testing.enabled", true],
   ]}, runTest);
 </script>
 </pre>
 </body>
 </html>
 
--- a/editor/composer/moz.build
+++ b/editor/composer/moz.build
@@ -22,28 +22,16 @@ UNIFIED_SOURCES += [
     'nsComposerRegistration.cpp',
     'nsComposeTxtSrvFilter.cpp',
     'nsEditingSession.cpp',
     'nsEditorSpellCheck.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 RESOURCE_FILES += [
-    'res/accessiblecaret.png',
-    'res/accessiblecaret@1.5x.png',
-    'res/accessiblecaret@2.25x.png',
-    'res/accessiblecaret@2x.png',
-    'res/accessiblecaret_tilt_left.png',
-    'res/accessiblecaret_tilt_left@1.5x.png',
-    'res/accessiblecaret_tilt_left@2.25x.png',
-    'res/accessiblecaret_tilt_left@2x.png',
-    'res/accessiblecaret_tilt_right.png',
-    'res/accessiblecaret_tilt_right@1.5x.png',
-    'res/accessiblecaret_tilt_right@2.25x.png',
-    'res/accessiblecaret_tilt_right@2x.png',
     'res/EditorOverride.css',
     'res/grabber.gif',
     'res/table-add-column-after-active.gif',
     'res/table-add-column-after-hover.gif',
     'res/table-add-column-after.gif',
     'res/table-add-column-before-active.gif',
     'res/table-add-column-before-hover.gif',
     'res/table-add-column-before.gif',
deleted file mode 100644
--- a/extensions/pref/autoconfig/src/Makefile.in
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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/.
-
-AUTOCFG_JS_EXPORTS = \
-		$(srcdir)/prefcalls.js \
-		$(NULL)
--- a/extensions/pref/autoconfig/src/moz.build
+++ b/extensions/pref/autoconfig/src/moz.build
@@ -10,8 +10,12 @@ UNIFIED_SOURCES += [
     'nsJSConfigTriggers.cpp',
     'nsReadConfig.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
+
+FINAL_TARGET_FILES.defaults.autoconfig += [
+    'prefcalls.js',
+]
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -157,17 +157,17 @@ if os_bsd or os_linux:
         ]
     if CONFIG['MOZ_ENABLE_QT']:
         SOURCES += [
             '!moc_message_pump_qt.cc',
             'src/base/message_pump_qt.cc',
         ]
 
 ost = CONFIG['OS_TEST']
-if '86' not in ost and 'arm' not in ost and 'mips' not in ost:
+if '86' not in ost and 'arm' not in ost and 'aarch64' != ost and 'mips' not in ost:
     SOURCES += [
         'src/base/atomicops_internals_mutex.cc',
     ]
 
 CXXFLAGS += CONFIG['TK_CFLAGS']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/ipc/chromium/src/base/atomicops.h
+++ b/ipc/chromium/src/base/atomicops.h
@@ -133,17 +133,19 @@ Atomic64 Release_Load(volatile const Ato
 
 // Include our platform specific implementation.
 #if defined(OS_WIN) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_msvc.h"
 #elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_macosx.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY)
 #include "base/atomicops_internals_x86_gcc.h"
-#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY)
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARMEL)
 #include "base/atomicops_internals_arm_gcc.h"
+#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM64)
+#include "base/atomicops_internals_arm64_gcc.h"
 #elif defined(COMPILER_GCC) && defined(ARCH_CPU_MIPS)
 #include "base/atomicops_internals_mips_gcc.h"
 #else
 #include "base/atomicops_internals_mutex.h"
 #endif
 
 #endif  // BASE_ATOMICOPS_H_
new file mode 100644
--- /dev/null
+++ b/ipc/chromium/src/base/atomicops_internals_arm64_gcc.h
@@ -0,0 +1,360 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file is an internal atomic implementation, use base/atomicops.h instead.
+
+// TODO(rmcilroy): Investigate whether we can use __sync__ intrinsics instead of
+//                 the hand coded assembly without introducing perf regressions.
+// TODO(rmcilroy): Investigate whether we can use acquire / release versions of
+//                 exclusive load / store assembly instructions and do away with
+//                 the barriers.
+
+#ifndef BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
+#define BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
+
+#if defined(OS_QNX)
+#include <sys/cpuinline.h>
+#endif
+
+namespace base {
+namespace subtle {
+
+inline void MemoryBarrier() {
+  __asm__ __volatile__ (  // NOLINT
+    "dmb ish                                  \n\t"  // Data memory barrier.
+    ::: "memory"
+  );  // NOLINT
+}
+
+
+inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
+                                         Atomic32 old_value,
+                                         Atomic32 new_value) {
+  Atomic32 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[prev], %[ptr]                 \n\t"  // Load the previous value.
+    "cmp %w[prev], %w[old_value]           \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    "1:                                    \n\t"
+    "clrex                                 \n\t"  // In case we didn't swap.
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
+                                         Atomic32 new_value) {
+  Atomic32 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[result], %[ptr]               \n\t"  // Load the previous value.
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [new_value]"r" (new_value)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr,
+                                          Atomic32 increment) {
+  Atomic32 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                       \n\t"
+    "ldxr %w[result], %[ptr]                  \n\t"  // Load the previous value.
+    "add %w[result], %w[result], %w[increment]\n\t"
+    "stxr %w[temp], %w[result], %[ptr]        \n\t"  // Try to store the result.
+    "cbnz %w[temp], 0b                        \n\t"  // Retry on failure.
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [increment]"r" (increment)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr,
+                                        Atomic32 increment) {
+  MemoryBarrier();
+  Atomic32 result = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+
+  return result;
+}
+
+inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[prev], %[ptr]                 \n\t"  // Load the previous value.
+    "cmp %w[prev], %w[old_value]           \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    "dmb ish                               \n\t"  // Data memory barrier.
+    "1:                                    \n\t"
+    // If the compare failed the 'dmb' is unnecessary, but we still need a
+    // 'clrex'.
+    "clrex                                 \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
+                                       Atomic32 old_value,
+                                       Atomic32 new_value) {
+  Atomic32 prev;
+  int32_t temp;
+
+  MemoryBarrier();
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %w[prev], %[ptr]                 \n\t"  // Load the previous value.
+    "cmp %w[prev], %w[old_value]           \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %w[new_value], %[ptr]  \n\t"  // Try to store the new value.
+    "cbnz %w[temp], 0b                     \n\t"  // Retry if it did not work.
+    "1:                                    \n\t"
+    // If the compare failed the we still need a 'clrex'.
+    "clrex                                 \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
+  return *ptr;
+}
+
+inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
+  Atomic32 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic32 Release_Load(volatile const Atomic32* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+// 64-bit versions of the operations.
+// See the 32-bit versions for comments.
+
+inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
+                                         Atomic64 old_value,
+                                         Atomic64 new_value) {
+  Atomic64 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[prev], %[ptr]                  \n\t"
+    "cmp %[prev], %[old_value]             \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    "1:                                    \n\t"
+    "clrex                                 \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
+                                         Atomic64 new_value) {
+  Atomic64 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[result], %[ptr]                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [new_value]"r" (new_value)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr,
+                                          Atomic64 increment) {
+  Atomic64 result;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                     \n\t"
+    "ldxr %[result], %[ptr]                 \n\t"
+    "add %[result], %[result], %[increment] \n\t"
+    "stxr %w[temp], %[result], %[ptr]       \n\t"
+    "cbnz %w[temp], 0b                      \n\t"
+    : [result]"=&r" (result),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [increment]"r" (increment)
+    : "memory"
+  );  // NOLINT
+
+  return result;
+}
+
+inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr,
+                                        Atomic64 increment) {
+  MemoryBarrier();
+  Atomic64 result = NoBarrier_AtomicIncrement(ptr, increment);
+  MemoryBarrier();
+
+  return result;
+}
+
+inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 prev;
+  int32_t temp;
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[prev], %[ptr]                  \n\t"
+    "cmp %[prev], %[old_value]             \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    "dmb ish                               \n\t"
+    "1:                                    \n\t"
+    "clrex                                 \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
+                                       Atomic64 old_value,
+                                       Atomic64 new_value) {
+  Atomic64 prev;
+  int32_t temp;
+
+  MemoryBarrier();
+
+  __asm__ __volatile__ (  // NOLINT
+    "0:                                    \n\t"
+    "ldxr %[prev], %[ptr]                  \n\t"
+    "cmp %[prev], %[old_value]             \n\t"
+    "bne 1f                                \n\t"
+    "stxr %w[temp], %[new_value], %[ptr]   \n\t"
+    "cbnz %w[temp], 0b                     \n\t"
+    "1:                                    \n\t"
+    "clrex                                 \n\t"
+    : [prev]"=&r" (prev),
+      [temp]"=&r" (temp),
+      [ptr]"+Q" (*ptr)
+    : [old_value]"r" (old_value),
+      [new_value]"r" (new_value)
+    : "memory", "cc"
+  );  // NOLINT
+
+  return prev;
+}
+
+inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+}
+
+inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) {
+  *ptr = value;
+  MemoryBarrier();
+}
+
+inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
+  MemoryBarrier();
+  *ptr = value;
+}
+
+inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
+  return *ptr;
+}
+
+inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
+  Atomic64 value = *ptr;
+  MemoryBarrier();
+  return value;
+}
+
+inline Atomic64 Release_Load(volatile const Atomic64* ptr) {
+  MemoryBarrier();
+  return *ptr;
+}
+
+}  // namespace base::subtle
+}  // namespace base
+
+#endif  // BASE_ATOMICOPS_INTERNALS_ARM64_GCC_H_
--- a/ipc/chromium/src/build/build_config.h
+++ b/ipc/chromium/src/build/build_config.h
@@ -104,17 +104,18 @@
 #define ARCH_CPU_64_BITS 1
 #elif defined(__s390__)
 #define ARCH_CPU_S390 1
 #define ARCH_CPU_32_BITS 1
 #elif defined(__alpha__)
 #define ARCH_CPU_ALPHA 1
 #define ARCH_CPU_64_BITS 1
 #elif defined(__aarch64__)
-#define ARCH_CPU_AARCH64 1
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
 #define ARCH_CPU_64_BITS 1
 #else
 #error Please add support for your architecture in build/build_config.h
 #endif
 
 // Type detection for wchar_t.
 #if defined(OS_WIN)
 #define WCHAR_T_IS_UTF16
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -473,17 +473,16 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeBinaryOperator(f, expected, ExprType::I32);
       case Expr::I64Add:
       case Expr::I64Sub:
       case Expr::I64Mul:
       case Expr::I64DivS:
       case Expr::I64DivU:
       case Expr::I64RemS:
       case Expr::I64RemU:
-        return f.fail("NYI: i64");
       case Expr::I64And:
       case Expr::I64Or:
       case Expr::I64Xor:
       case Expr::I64Shl:
       case Expr::I64ShrS:
       case Expr::I64ShrU:
         return DecodeBinaryOperator(f, expected, ExprType::I64);
       case Expr::F32Add:
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -1407,43 +1407,58 @@ EmitLoadGlobal(FunctionCompiler& f, Expr
     MOZ_ASSERT_IF(type != ExprType::Void, global.type == type);
     return true;
 }
 
 static bool EmitExpr(FunctionCompiler&, ExprType, MDefinition**, LabelVector* = nullptr);
 static bool EmitExprStmt(FunctionCompiler&, MDefinition**, LabelVector* = nullptr);
 
 static bool
-EmitLoadStoreAddress(FunctionCompiler& f, uint32_t* offset, uint32_t* align, MDefinition** base)
+EmitLoadStoreAddress(FunctionCompiler& f, Scalar::Type viewType, uint32_t* offset,
+                     uint32_t* align, MDefinition** base)
 {
     *offset = f.readVarU32();
     MOZ_ASSERT(*offset == 0, "Non-zero offsets not supported yet");
 
     *align = f.readVarU32();
 
-    return EmitExpr(f, ExprType::I32, base);
+    if (!EmitExpr(f, ExprType::I32, base))
+        return false;
+
+    // TODO Remove this (and the viewType param) after implementing unaligned
+    // loads/stores.
+    if (f.mg().isAsmJS())
+        return true;
+
+    int32_t maskVal = ~(Scalar::byteSize(viewType) - 1);
+    if (maskVal == -1)
+        return true;
+
+    MDefinition* mask = f.constant(Int32Value(maskVal), MIRType_Int32);
+    *base = f.bitwise<MBitAnd>(*base, mask, MIRType_Int32);
+    return true;
 }
 
 static bool
-EmitLoad(FunctionCompiler& f, Scalar::Type scalarType, MDefinition** def)
+EmitLoad(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
-    *def = f.loadHeap(scalarType, ptr);
+    *def = f.loadHeap(viewType, ptr);
     return true;
 }
 
 static bool
 EmitStore(FunctionCompiler& f, Scalar::Type viewType, MDefinition** def)
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
 
     MDefinition* rhs = nullptr;
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
         if (!EmitExpr(f, ExprType::I32, &rhs))
@@ -1466,17 +1481,17 @@ EmitStore(FunctionCompiler& f, Scalar::T
 }
 
 static bool
 EmitStoreWithCoercion(FunctionCompiler& f, Scalar::Type rhsType, Scalar::Type viewType,
                       MDefinition **def)
 {
     uint32_t offset, align;
     MDefinition* ptr;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &ptr))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &ptr))
         return false;
 
     MDefinition* rhs = nullptr;
     MDefinition* coerced = nullptr;
     if (rhsType == Scalar::Float32 && viewType == Scalar::Float64) {
         if (!EmitExpr(f, ExprType::F32, &rhs))
             return false;
         coerced = f.unary<MToDouble>(rhs);
@@ -1539,31 +1554,31 @@ EmitMathMinMax(FunctionCompiler& f, Expr
 
 static bool
 EmitAtomicsLoad(FunctionCompiler& f, MDefinition** def)
 {
     Scalar::Type viewType = Scalar::Type(f.readU8());
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     *def = f.atomicLoadHeap(viewType, index);
     return true;
 }
 
 static bool
 EmitAtomicsStore(FunctionCompiler& f, MDefinition** def)
 {
     Scalar::Type viewType = Scalar::Type(f.readU8());
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
     if (!EmitExpr(f, ExprType::I32, &value))
         return false;
     f.atomicStoreHeap(viewType, index, value);
     *def = value;
     return true;
@@ -1572,34 +1587,34 @@ EmitAtomicsStore(FunctionCompiler& f, MD
 static bool
 EmitAtomicsBinOp(FunctionCompiler& f, MDefinition** def)
 {
     Scalar::Type viewType = Scalar::Type(f.readU8());
     js::jit::AtomicOp op = js::jit::AtomicOp(f.readU8());
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
     if (!EmitExpr(f, ExprType::I32, &value))
         return false;
     *def = f.atomicBinopHeap(op, viewType, index, value);
     return true;
 }
 
 static bool
 EmitAtomicsCompareExchange(FunctionCompiler& f, MDefinition** def)
 {
     Scalar::Type viewType = Scalar::Type(f.readU8());
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* oldValue;
     if (!EmitExpr(f, ExprType::I32, &oldValue))
         return false;
     MDefinition* newValue;
     if (!EmitExpr(f, ExprType::I32, &newValue))
         return false;
@@ -1609,17 +1624,17 @@ EmitAtomicsCompareExchange(FunctionCompi
 
 static bool
 EmitAtomicsExchange(FunctionCompiler& f, MDefinition** def)
 {
     Scalar::Type viewType = Scalar::Type(f.readU8());
 
     uint32_t offset, align;
     MDefinition* index;
-    if (!EmitLoadStoreAddress(f, &offset, &align, &index))
+    if (!EmitLoadStoreAddress(f, viewType, &offset, &align, &index))
         return false;
 
     MDefinition* value;
     if (!EmitExpr(f, ExprType::I32, &value))
         return false;
     *def = f.atomicExchangeHeap(viewType, index, value);
     return true;
 }
@@ -2179,17 +2194,18 @@ EmitDivOrMod(FunctionCompiler& f, ExprTy
            ? f.div(lhs, rhs, ToMIRType(type), isUnsigned)
            : f.mod(lhs, rhs, ToMIRType(type), isUnsigned);
     return true;
 }
 
 static bool
 EmitDivOrMod(FunctionCompiler& f, ExprType type, bool isDiv, MDefinition** def)
 {
-    MOZ_ASSERT(type != ExprType::I32, "int div or mod must precise signedness");
+    MOZ_ASSERT(type != ExprType::I32 && type != ExprType::I64,
+               "int div or mod must indicate signedness");
     return EmitDivOrMod(f, type, isDiv, false, def);
 }
 
 static bool
 EmitComparison(FunctionCompiler& f, Expr expr, MDefinition** def)
 {
     MDefinition *lhs, *rhs;
     MCompare::CompareType compareType;
@@ -2892,16 +2908,28 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64Xor:
         return EmitBitwise<MBitXor>(f, ExprType::I64, def);
       case Expr::I64Shl:
         return EmitBitwise<MLsh>(f, ExprType::I64, def);
       case Expr::I64ShrS:
         return EmitBitwise<MRsh>(f, ExprType::I64, def);
       case Expr::I64ShrU:
         return EmitBitwise<MUrsh>(f, ExprType::I64, def);
+      case Expr::I64Add:
+        return EmitAddOrSub(f, ExprType::I64, IsAdd(true), def);
+      case Expr::I64Sub:
+        return EmitAddOrSub(f, ExprType::I64, IsAdd(false), def);
+      case Expr::I64Mul:
+        return EmitMultiply(f, ExprType::I64, def);
+      case Expr::I64DivS:
+      case Expr::I64DivU:
+        return EmitDivOrMod(f, ExprType::I64, IsDiv(true), IsUnsigned(op == Expr::I64DivU), def);
+      case Expr::I64RemS:
+      case Expr::I64RemU:
+        return EmitDivOrMod(f, ExprType::I64, IsDiv(false), IsUnsigned(op == Expr::I64RemU), def);
       // F32
       case Expr::F32Const:
         return EmitLiteral(f, ExprType::F32, def);
       case Expr::F32Add:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(true), def);
       case Expr::F32Sub:
         return EmitAddOrSub(f, ExprType::F32, IsAdd(false), def);
       case Expr::F32Mul:
@@ -3061,23 +3089,16 @@ EmitExpr(FunctionCompiler& f, ExprType t
       case Expr::I64LoadMem:
       case Expr::I64StoreMem8:
       case Expr::I64StoreMem16:
       case Expr::I64StoreMem32:
       case Expr::I64StoreMem:
       case Expr::I64Clz:
       case Expr::I64Ctz:
       case Expr::I64Popcnt:
-      case Expr::I64Add:
-      case Expr::I64Sub:
-      case Expr::I64Mul:
-      case Expr::I64DivS:
-      case Expr::I64DivU:
-      case Expr::I64RemS:
-      case Expr::I64RemU:
         MOZ_CRASH("NYI");
       case Expr::Unreachable:
         break;
       case Expr::Limit:
         MOZ_CRASH("Limit");
     }
 
     MOZ_CRASH("unexpected wasm opcode");
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -795,20 +795,23 @@ Module::setProfilingEnabled(JSContext* c
         for (const CallThunk& callThunk : module_->callThunks)
             EnableProfilingThunk(*this, callThunk, enabled);
 
         for (const CodeRange& codeRange : module_->codeRanges)
             EnableProfilingEpilogue(*this, codeRange, enabled);
     }
 
     // Update the function-pointer tables to point to profiling prologues.
-    for (FuncPtrTable& funcPtrTable : funcPtrTables_) {
-        auto array = reinterpret_cast<void**>(globalData() + funcPtrTable.globalDataOffset);
-        for (size_t i = 0; i < funcPtrTable.numElems; i++) {
+    for (FuncPtrTable& table : funcPtrTables_) {
+        auto array = reinterpret_cast<void**>(globalData() + table.globalDataOffset);
+        for (size_t i = 0; i < table.numElems; i++) {
             const CodeRange* codeRange = lookupCodeRange(array[i]);
+            // Don't update entries for the BadIndirectCall exit.
+            if (codeRange->isErrorExit())
+                continue;
             void* from = code() + codeRange->funcNonProfilingEntry();
             void* to = code() + codeRange->funcProfilingEntry();
             if (!enabled)
                 Swap(from, to);
             MOZ_ASSERT(array[i] == from);
             array[i] = to;
         }
     }
@@ -1052,18 +1055,19 @@ Module::staticallyLink(ExclusiveContext*
                                                PatchedImmPtr((void*)-1));
         }
     }
 
     for (const StaticLinkData::FuncPtrTable& table : linkData.funcPtrTables) {
         auto array = reinterpret_cast<void**>(globalData() + table.globalDataOffset);
         for (size_t i = 0; i < table.elemOffsets.length(); i++) {
             uint8_t* elem = code() + table.elemOffsets[i];
-            if (profilingEnabled_)
-                elem = code() + lookupCodeRange(elem)->funcProfilingEntry();
+            const CodeRange* codeRange = lookupCodeRange(elem);
+            if (profilingEnabled_ && !codeRange->isErrorExit())
+                elem = code() + codeRange->funcProfilingEntry();
             array[i] = elem;
         }
     }
 
     // CodeRangeVector, CallSiteVector and the code technically have all the
     // necessary info to do all the updates necessary in setProfilingEnabled.
     // However, to simplify the finding of function-pointer table sizes and
     // global-data offsets, save just that information here.
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -244,16 +244,19 @@ class CodeRange
     // profiling prologues/epilogues.
 
     bool isFunction() const {
         return kind() == Function;
     }
     bool isImportExit() const {
         return kind() == ImportJitExit || kind() == ImportInterpExit;
     }
+    bool isErrorExit() const {
+        return kind() == ErrorExit;
+    }
     uint32_t funcProfilingEntry() const {
         MOZ_ASSERT(isFunction());
         return begin();
     }
     uint32_t funcNonProfilingEntry() const {
         MOZ_ASSERT(isFunction());
         return begin_ + u.func.beginToEntry_;
     }
--- a/js/src/jit-test/tests/wasm/basic-integer.js
+++ b/js/src/jit-test/tests/wasm/basic-integer.js
@@ -10,16 +10,26 @@ function testUnary(type, opcode, op, exp
 
 function testBinary(type, opcode, lhs, rhs, expect) {
   if (type === 'i64') {
     // i64 cannot be imported/exported, so we use a wrapper function.
     assertEq(wasmEvalText(`(module
                             (func (param i64) (param i64) (result i64) (i64.${opcode} (get_local 0) (get_local 1)))
                             (func (result i32) (i64.eq (call 0 (i64.const ${lhs}) (i64.const ${rhs})) (i64.const ${expect})))
                             (export "" 1))`)(), 1);
+    // The same, but now the RHS is a constant.
+    assertEq(wasmEvalText(`(module
+                            (func (param i64) (result i64) (i64.${opcode} (get_local 0) (i64.const ${rhs})))
+                            (func (result i32) (i64.eq (call 0 (i64.const ${lhs})) (i64.const ${expect})))
+                            (export "" 1))`)(), 1);
+    // LHS and RHS are constants.
+    assertEq(wasmEvalText(`(module
+                            (func (result i64) (i64.${opcode} (i64.const ${lhs}) (i64.const ${rhs})))
+                            (func (result i32) (i64.eq (call 0) (i64.const ${expect})))
+                            (export "" 1))`)(), 1);
   } else {
     assertEq(wasmEvalText('(module (func (param ' + type + ') (param ' + type + ') (result ' + type + ') (' + type + '.' + opcode + ' (get_local 0) (get_local 1))) (export "" 0))')(lhs, rhs), expect);
   }
 }
 
 function testComparison(type, opcode, lhs, rhs, expect) {
   if (type === 'i64') {
     // i64 cannot be imported/exported, so we use a wrapper function.
@@ -68,25 +78,48 @@ testComparison('i32', 'gt_s', 40, 40, 0)
 testComparison('i32', 'gt_u', 40, 40, 0);
 testComparison('i32', 'ge_s', 40, 40, 1);
 testComparison('i32', 'ge_u', 40, 40, 1);
 
 //testUnary('i64', 'clz', 40, 58); // TODO: NYI
 //testUnary('i64', 'ctz', 40, 0); // TODO: NYI
 //testUnary('i64', 'popcnt', 40, 0); // TODO: NYI
 
-//testBinary('i64', 'add', 40, 2, 42); // TODO: NYI
-//testBinary('i64', 'sub', 40, 2, 38); // TODO: NYI
-//testBinary('i64', 'mul', 40, 2, 80); // TODO: NYI
-//testBinary('i64', 'div_s', -40, 2, -20); // TODO: NYI
-//testBinary('i64', 'div_u', -40, 2, 2147483628); // TODO: NYI
-//testBinary('i64', 'rem_s', 40, -3, 1); // TODO: NYI
-//testBinary('i64', 'rem_u', 40, -3, 40); // TODO: NYI
+if (getBuildConfiguration().x64) {
+    testBinary('i64', 'add', 40, 2, 42);
+    testBinary('i64', 'add', "0x1234567887654321", -1, "0x1234567887654320");
+    testBinary('i64', 'add', "0xffffffffffffffff", 1, 0);
+    testBinary('i64', 'sub', 40, 2, 38);
+    testBinary('i64', 'sub', "0x1234567887654321", "0x123456789", "0x12345677641fdb98");
+    testBinary('i64', 'sub', 3, 5, -2);
+    testBinary('i64', 'mul', 40, 2, 80);
+    testBinary('i64', 'mul', -1, 2, -2);
+    testBinary('i64', 'mul', 0x123456, "0x9876543210", "0xad77d2c5f941160");
+    testBinary('i64', 'div_s', -40, 2, -20);
+    testBinary('i64', 'div_s', "0x1234567887654321", 2, "0x91a2b3c43b2a190");
+    testBinary('i64', 'div_s', "0x1234567887654321", "0x1000000000", "0x1234567");
+    testBinary('i64', 'div_u', -40, 2, "0x7fffffffffffffec");
+    testBinary('i64', 'div_u', "0x1234567887654321", 9, "0x205d0b80f0b4059");
+    testBinary('i64', 'rem_s', 40, -3, 1);
+    testBinary('i64', 'rem_s', "0x1234567887654321", "0x1000000000", "0x887654321");
+    testBinary('i64', 'rem_s', "0x7fffffffffffffff", -1, 0);
+    testBinary('i64', 'rem_s', "0x8000000000000001", 1000, -807);
+    testBinary('i64', 'rem_s', "0x8000000000000000", -1, 0);
+    testBinary('i64', 'rem_u', 40, -3, 40);
+    testBinary('i64', 'rem_u', "0x1234567887654321", "0x1000000000", "0x887654321");
+    testBinary('i64', 'rem_u', "0x8000000000000000", -1, "0x8000000000000000");
+    testBinary('i64', 'rem_u', "0x8ff00ff00ff00ff0", "0x100000001", "0x80000001");
 
-if (getBuildConfiguration().x64) {
+    // These should trap, but for now we match the i32 version.
+    testBinary('i64', 'div_s', 10, 0, 0);
+    testBinary('i64', 'div_s', "0x8000000000000000", -1, "0x8000000000000000");
+    testBinary('i64', 'div_u', 0, 0, 0);
+    testBinary('i64', 'rem_s', 10, 0, 0);
+    testBinary('i64', 'rem_u', 10, 0, 0);
+
     testBinary('i64', 'and', 42, 6, 2);
     testBinary('i64', 'or', 42, 6, 46);
     testBinary('i64', 'xor', 42, 2, 40);
     testBinary('i64', 'and', "0x8765432112345678", "0xffff0000ffff0000", "0x8765000012340000");
     testBinary('i64', 'or', "0x8765432112345678", "0xffff0000ffff0000", "0xffff4321ffff5678");
     testBinary('i64', 'xor', "0x8765432112345678", "0xffff0000ffff0000", "0x789a4321edcb5678");
     testBinary('i64', 'shl', 40, 2, 160);
     testBinary('i64', 'shr_s', -40, 2, -10);
--- a/js/src/jit-test/tests/wasm/basic-memory.js
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -69,17 +69,20 @@ function testStoreError(type, ext, base,
     '     (get_local 0)' +
     '     (get_local 1)' +
     '    )' +
     '  ) (export "" 0))'
   ), Error, errorMsg);
 }
 
 testLoad('i32', '', 0, 0, 0, 0x03020100);
+
+testLoad('i32', '', 1, 0, 0, 0x03020100);   // TODO: unaligned NYI
 //testLoad('i32', '', 1, 0, 0, 0x04030201); // TODO: unaligned NYI
+
 //testLoad('i32', '', 0, 1, 0, 0x01020304); // TODO: offsets NYI
 //testLoad('i32', '', 1, 1, 4, 0x02030405); // TODO: offsets NYI
 //testLoad('i64', '', 0, 0, 0, 0x0001020304050607); // TODO: i64 NYI
 //testLoad('i64', '', 1, 0, 0, 0x0102030405060708); // TODO: i64 NYI
 //testLoad('i64', '', 0, 1, 0, 0x0102030405060708); // TODO: i64 NYI
 //testLoad('i64', '', 1, 1, 4, 0x0203040506070809); // TODO: i64 NYI
 testLoad('f32', '', 0, 0, 0, 3.820471434542632e-37);
 //testLoad('f32', '', 1, 0, 0, 1.539989614439558e-36); // TODO: unaligned NYI
--- a/js/src/jit-test/tests/wasm/basic.js
+++ b/js/src/jit-test/tests/wasm/basic.js
@@ -320,36 +320,50 @@ var {v2i, i2i, i2v} = wasmEvalText(`(mod
     (func (param i32) (result i32) (call_indirect 0 (get_local 0)))
     (func (param i32) (param i32) (result i32) (call_indirect 1 (get_local 0) (get_local 1)))
     (func (param i32) (call_indirect 2 (get_local 0) (i32.const 0)))
     (export "v2i" 6)
     (export "i2i" 7)
     (export "i2v" 8)
 )`);
 
+const badIndirectCall = /wasm indirect call signature mismatch/;
+
 assertEq(v2i(0), 13);
 assertEq(v2i(1), 42);
-assertErrorMessage(() => v2i(2), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => v2i(3), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => v2i(4), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => v2i(5), Error, /wasm indirect call signature mismatch/);
+assertErrorMessage(() => v2i(2), Error, badIndirectCall);
+assertErrorMessage(() => v2i(3), Error, badIndirectCall);
+assertErrorMessage(() => v2i(4), Error, badIndirectCall);
+assertErrorMessage(() => v2i(5), Error, badIndirectCall);
 
-assertErrorMessage(() => i2i(0), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2i(1), Error, /wasm indirect call signature mismatch/);
+assertErrorMessage(() => i2i(0), Error, badIndirectCall);
+assertErrorMessage(() => i2i(1), Error, badIndirectCall);
 assertEq(i2i(2, 100), 101);
 assertEq(i2i(3, 100), 102);
 assertEq(i2i(4, 100), 103);
 assertEq(i2i(5, 100), 104);
 
-assertErrorMessage(() => i2v(0), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2v(1), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2v(2), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2v(3), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2v(4), Error, /wasm indirect call signature mismatch/);
-assertErrorMessage(() => i2v(5), Error, /wasm indirect call signature mismatch/);
+assertErrorMessage(() => i2v(0), Error, badIndirectCall);
+assertErrorMessage(() => i2v(1), Error, badIndirectCall);
+assertErrorMessage(() => i2v(2), Error, badIndirectCall);
+assertErrorMessage(() => i2v(3), Error, badIndirectCall);
+assertErrorMessage(() => i2v(4), Error, badIndirectCall);
+assertErrorMessage(() => i2v(5), Error, badIndirectCall);
+
+{
+    enableSPSProfiling();
+    wasmEvalText(`(
+        module
+        (func (result i32) (i32.const 0))
+        (func)
+        (table 1 0)
+        (export "" 0)
+    )`)();
+    disableSPSProfiling();
+}
 
 for (bad of [6, 7, 100, Math.pow(2,31)-1, Math.pow(2,31), Math.pow(2,31)+1, Math.pow(2,32)-2, Math.pow(2,32)-1]) {
     assertThrowsInstanceOf(() => v2i(bad), RangeError);
     assertThrowsInstanceOf(() => i2i(bad, 0), RangeError);
     assertThrowsInstanceOf(() => i2v(bad, 0), RangeError);
 }
 
 if (hasI64) {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -1550,27 +1550,39 @@ LIRGenerator::visitAdd(MAdd* ins)
         if (ins->fallible())
             assignSnapshot(lir, Bailout_OverflowInvalidate);
 
         lowerForALU(lir, ins, lhs, rhs);
         MaybeSetRecoversInput(ins, lir);
         return;
     }
 
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LAddI64* lir = new(alloc()) LAddI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
     if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForFPU(new(alloc()) LMathD(JSOP_ADD), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         ReorderCommutative(&lhs, &rhs, ins);
         lowerForFPU(new(alloc()) LMathF(JSOP_ADD), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_ADD, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_ADD, ins);
 }
 
 void
 LIRGenerator::visitSub(MSub* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
 
@@ -1583,25 +1595,37 @@ LIRGenerator::visitSub(MSub* ins)
         if (ins->fallible())
             assignSnapshot(lir, Bailout_Overflow);
 
         lowerForALU(lir, ins, lhs, rhs);
         MaybeSetRecoversInput(ins, lir);
         return;
     }
 
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LSubI64* lir = new(alloc()) LSubI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
     if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         lowerForFPU(new(alloc()) LMathD(JSOP_SUB), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         lowerForFPU(new(alloc()) LMathF(JSOP_SUB), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_SUB, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_SUB, ins);
 }
 
 void
 LIRGenerator::visitMul(MMul* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
     MOZ_ASSERT(lhs->type() == rhs->type());
@@ -1611,81 +1635,120 @@ LIRGenerator::visitMul(MMul* ins)
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1 and we don't have to worry about
         // overflow, we can optimize to an LNegI.
         if (!ins->fallible() && rhs->isConstant() && rhs->toConstant()->toInt32() == -1)
             defineReuseInput(new(alloc()) LNegI(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerMulI(ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        ReorderCommutative(&lhs, &rhs, ins);
+        LMulI64* lir = new(alloc()) LMulI64;
+        lowerForALUInt64(lir, ins, lhs, rhs);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // If our RHS is a constant -1.0, we can optimize to an LNegD.
         if (rhs->isConstant() && rhs->toConstant()->toDouble() == -1.0)
             defineReuseInput(new(alloc()) LNegD(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathD(JSOP_MUL), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         ReorderCommutative(&lhs, &rhs, ins);
 
         // We apply the same optimizations as for doubles
         if (rhs->isConstant() && rhs->toConstant()->toFloat32() == -1.0f)
             defineReuseInput(new(alloc()) LNegF(useRegisterAtStart(lhs)), ins, 0);
         else
             lowerForFPU(new(alloc()) LMathF(JSOP_MUL), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_MUL, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_MUL, ins);
 }
 
 void
 LIRGenerator::visitDiv(MDiv* ins)
 {
     MDefinition* lhs = ins->lhs();
     MDefinition* rhs = ins->rhs();
     MOZ_ASSERT(lhs->type() == rhs->type());
 
     if (ins->specialization() == MIRType_Int32) {
         MOZ_ASSERT(lhs->type() == MIRType_Int32);
         lowerDivI(ins);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(lhs->type() == MIRType_Int64);
+        lowerDivI64(ins);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(lhs->type() == MIRType_Double);
         lowerForFPU(new(alloc()) LMathD(JSOP_DIV), ins, lhs, rhs);
-    } else if (ins->specialization() == MIRType_Float32) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Float32) {
         MOZ_ASSERT(lhs->type() == MIRType_Float32);
         lowerForFPU(new(alloc()) LMathF(JSOP_DIV), ins, lhs, rhs);
-    } else {
-        lowerBinaryV(JSOP_DIV, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_DIV, ins);
 }
 
 void
 LIRGenerator::visitMod(MMod* ins)
 {
     MOZ_ASSERT(ins->lhs()->type() == ins->rhs()->type());
 
     if (ins->specialization() == MIRType_Int32) {
         MOZ_ASSERT(ins->type() == MIRType_Int32);
         MOZ_ASSERT(ins->lhs()->type() == MIRType_Int32);
         lowerModI(ins);
-    } else if (ins->specialization() == MIRType_Double) {
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Int64) {
+        MOZ_ASSERT(ins->type() == MIRType_Int64);
+        MOZ_ASSERT(ins->lhs()->type() == MIRType_Int64);
+        lowerModI64(ins);
+        return;
+    }
+
+    if (ins->specialization() == MIRType_Double) {
         MOZ_ASSERT(ins->type() == MIRType_Double);
         MOZ_ASSERT(ins->lhs()->type() == MIRType_Double);
         MOZ_ASSERT(ins->rhs()->type() == MIRType_Double);
 
         // Note: useRegisterAtStart is safe here, the temp is not a FP register.
         LModD* lir = new(alloc()) LModD(useRegisterAtStart(ins->lhs()), useRegisterAtStart(ins->rhs()),
                                         tempFixed(CallTempReg0));
         defineReturn(lir, ins);
-    } else {
-        lowerBinaryV(JSOP_MOD, ins);
+        return;
     }
+
+    lowerBinaryV(JSOP_MOD, ins);
 }
 
 void
 LIRGenerator::lowerBinaryV(JSOp op, MBinaryInstruction* ins)
 {
     MDefinition* lhs = ins->getOperand(0);
     MDefinition* rhs = ins->getOperand(1);
 
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -894,16 +894,19 @@ MConstant::printOpcode(GenericPrinter& o
         out.printf("null");
         break;
       case MIRType_Boolean:
         out.printf(toBoolean() ? "true" : "false");
         break;
       case MIRType_Int32:
         out.printf("0x%x", toInt32());
         break;
+      case MIRType_Int64:
+        out.printf("0x%" PRIx64, toInt64());
+        break;
       case MIRType_Double:
         out.printf("%.16g", toDouble());
         break;
       case MIRType_Float32:
       {
         float val = toFloat32();
         out.printf("%.16g", val);
         break;
@@ -2696,16 +2699,19 @@ MBinaryArithInstruction::constantDoubleR
 }
 
 MDefinition*
 MBinaryArithInstruction::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     MDefinition* lhs = getOperand(0);
     MDefinition* rhs = getOperand(1);
     if (MConstant* folded = EvaluateConstantOperands(alloc, this)) {
         if (isTruncated()) {
             if (!folded->block())
                 block()->insertBefore(this, folded);
             return MTruncateToInt32::New(alloc, folded);
         }
@@ -2908,16 +2914,19 @@ MAbs::trySpecializeFloat32(TempAllocator
 }
 
 MDefinition*
 MDiv::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
         return folded;
 
     if (MDefinition* folded = EvaluateExactReciprocal(alloc, this))
         return folded;
 
     return this;
 }
@@ -2970,16 +2979,19 @@ MDiv::fallible() const
 }
 
 MDefinition*
 MMod::foldsTo(TempAllocator& alloc)
 {
     if (specialization_ == MIRType_None)
         return this;
 
+    if (specialization_ == MIRType_Int64)
+        return this;
+
     if (MDefinition* folded = EvaluateConstantOperands(alloc, this))
         return folded;
 
     return this;
 }
 
 void
 MMod::analyzeEdgeCasesForward()
@@ -5430,17 +5442,17 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
                 key->ensureTrackedProperty(propertycx, NameToId(name));
 
             if (!key->unknownProperties()) {
                 HeapTypeSetKey property = key->property(NameToId(name));
                 if (property.maybeTypes()) {
                     TypeSet::TypeList types;
                     if (!property.maybeTypes()->enumerateTypes(&types))
                         break;
-                    if (types.length()) {
+                    if (types.length() == 1) {
                         // Note: the return value here is ignored.
                         observed->addType(types[0], GetJitContext()->temp->lifoAlloc());
                         break;
                     }
                 }
             }
 
             obj = obj->getProto();
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -6723,23 +6723,23 @@ class MMod : public MBinaryArithInstruct
 
     MDefinition* foldsTo(TempAllocator& alloc) override;
 
     double getIdentity() override {
         MOZ_CRASH("not used");
     }
 
     bool canBeNegativeDividend() const {
-        MOZ_ASSERT(specialization_ == MIRType_Int32);
+        MOZ_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Int64);
         MOZ_ASSERT(!unsigned_);
         return canBeNegativeDividend_;
     }
 
     bool canBeDivideByZero() const {
-        MOZ_ASSERT(specialization_ == MIRType_Int32);
+        MOZ_ASSERT(specialization_ == MIRType_Int32 || specialization_ == MIRType_Int64);
         return canBeDivideByZero_;
     }
 
     bool canBePowerOfTwoDivisor() const {
         MOZ_ASSERT(specialization_ == MIRType_Int32);
         return canBePowerOfTwoDivisor_;
     }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -740,16 +740,17 @@ class MacroAssembler : public MacroAssem
 
     inline void sub32(const Address& src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Register src, Register dest) PER_SHARED_ARCH;
     inline void sub32(Imm32 imm, Register dest) PER_SHARED_ARCH;
 
     inline void subPtr(Register src, Register dest) PER_ARCH;
     inline void subPtr(Register src, const Address& dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
     inline void subPtr(Imm32 imm, Register dest) PER_ARCH;
+    inline void subPtr(ImmWord imm, Register dest) DEFINED_ON(x64);
     inline void subPtr(const Address& addr, Register dest) DEFINED_ON(mips_shared, arm, arm64, x86, x64);
 
     inline void subDouble(FloatRegister src, FloatRegister dest) PER_SHARED_ARCH;
 
     inline void mul32(Register src1, Register src2, Register dest, Label* onOver, Label* onZero) DEFINED_ON(arm64);
 
     inline void mul64(Imm64 imm, const Register64& dest) PER_ARCH;
 
--- a/js/src/jit/arm/Lowering-arm.cpp
+++ b/js/src/jit/arm/Lowering-arm.cpp
@@ -334,16 +334,28 @@ LIRGeneratorARM::lowerModI(MMod* mod)
                                             tempFixed(r0), tempFixed(r2), tempFixed(r3),
                                             temp(LDefinition::GENERAL));
     if (mod->fallible())
         assignSnapshot(lir, Bailout_DoubleOutput);
     defineFixed(lir, mod, LAllocation(AnyRegister(r1)));
 }
 
 void
+LIRGeneratorARM::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM::visitPowHalf(MPowHalf* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
     LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
     defineReuseInput(lir, ins, 0);
 }
 
--- a/js/src/jit/arm/Lowering-arm.h
+++ b/js/src/jit/arm/Lowering-arm.h
@@ -70,16 +70,18 @@ class LIRGeneratorARM : public LIRGenera
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
     void lowerTruncateFToInt32(MTruncateToInt32* ins);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
--- a/js/src/jit/arm64/Lowering-arm64.cpp
+++ b/js/src/jit/arm64/Lowering-arm64.cpp
@@ -142,16 +142,28 @@ LIRGeneratorARM64::lowerMulI(MMul* mul, 
 
 void
 LIRGeneratorARM64::lowerModI(MMod* mod)
 {
     MOZ_CRASH("lowerModI");
 }
 
 void
+LIRGeneratorARM64::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorARM64::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorARM64::visitPowHalf(MPowHalf* ins)
 {
     MOZ_CRASH("visitPowHalf");
 }
 
 LTableSwitch*
 LIRGeneratorARM64::newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                        MTableSwitch* tableswitch)
--- a/js/src/jit/arm64/Lowering-arm64.h
+++ b/js/src/jit/arm64/Lowering-arm64.h
@@ -72,16 +72,18 @@ class LIRGeneratorARM64 : public LIRGene
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerTruncateDToInt32(MTruncateToInt32* ins);
     void lowerTruncateFToInt32(MTruncateToInt32* ins);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitchV* newLTableSwitchV(MTableSwitch* ins);
     LTableSwitch* newLTableSwitch(const LAllocation& in,
--- a/js/src/jit/mips-shared/Lowering-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.cpp
@@ -184,16 +184,28 @@ LIRGeneratorMIPSShared::lowerModI(MMod* 
                            temp(LDefinition::GENERAL));
 
     if (mod->fallible())
         assignSnapshot(lir, Bailout_DoubleOutput);
     define(lir, mod);
 }
 
 void
+LIRGeneratorMIPSShared::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorMIPSShared::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorMIPSShared::visitPowHalf(MPowHalf* ins)
 {
     MDefinition* input = ins->input();
     MOZ_ASSERT(input->type() == MIRType_Double);
     LPowHalfD* lir = new(alloc()) LPowHalfD(useRegisterAtStart(input));
     defineReuseInput(lir, ins, 0);
 }
 
--- a/js/src/jit/mips-shared/Lowering-mips-shared.h
+++ b/js/src/jit/mips-shared/Lowering-mips-shared.h
@@ -58,16 +58,18 @@ class LIRGeneratorMIPSShared : public LI
     {
         return lowerForFPU(ins, mir, lhs, rhs);
     }
 
     void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
                                  MDefinition* lhs, MDefinition* rhs);
     void lowerDivI(MDiv* div);
     void lowerModI(MMod* mod);
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
     void lowerMulI(MMul* mul, MDefinition* lhs, MDefinition* rhs);
     void lowerUDiv(MDiv* div);
     void lowerUMod(MMod* mod);
     void visitPowHalf(MPowHalf* ins);
     void visitAsmJSNeg(MAsmJSNeg* ins);
 
     LTableSwitch* newLTableSwitch(const LAllocation& in, const LDefinition& inputCopy,
                                   MTableSwitch* ins);
--- a/js/src/jit/none/Lowering-none.h
+++ b/js/src/jit/none/Lowering-none.h
@@ -56,16 +56,18 @@ class LIRGeneratorNone : public LIRGener
     }
 
     void lowerConstantDouble(double, MInstruction*) { MOZ_CRASH(); }
     void lowerConstantFloat32(float, MInstruction*) { MOZ_CRASH(); }
     void lowerTruncateDToInt32(MTruncateToInt32*) { MOZ_CRASH(); }
     void lowerTruncateFToInt32(MTruncateToInt32*) { MOZ_CRASH(); }
     void lowerDivI(MDiv*) { MOZ_CRASH(); }
     void lowerModI(MMod*) { MOZ_CRASH(); }
+    void lowerDivI64(MDiv*) { MOZ_CRASH(); }
+    void lowerModI64(MMod*) { MOZ_CRASH(); }
     void lowerMulI(MMul*, MDefinition*, MDefinition*) { MOZ_CRASH(); }
     void lowerUDiv(MDiv*) { MOZ_CRASH(); }
     void lowerUMod(MMod*) { MOZ_CRASH(); }
     void visitBox(MBox* box) { MOZ_CRASH(); }
     void visitUnbox(MUnbox* unbox) { MOZ_CRASH(); }
     void visitReturn(MReturn* ret) { MOZ_CRASH(); }
     void visitPowHalf(MPowHalf*) { MOZ_CRASH(); }
     void visitAsmJSNeg(MAsmJSNeg*) { MOZ_CRASH(); }
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -3413,16 +3413,22 @@ class LAddI : public LBinaryMath<0>
         recoversInput_ = true;
     }
 
     MAdd* mir() const {
         return mir_->toAdd();
     }
 };
 
+class LAddI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(AddI64)
+};
+
 // Subtracts two integers, returning an integer value.
 class LSubI : public LBinaryMath<0>
 {
     bool recoversInput_;
 
   public:
     LIR_HEADER(SubI)
 
@@ -3440,16 +3446,28 @@ class LSubI : public LBinaryMath<0>
     void setRecoversInput() {
         recoversInput_ = true;
     }
     MSub* mir() const {
         return mir_->toSub();
     }
 };
 
+class LSubI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(SubI64)
+};
+
+class LMulI64 : public LInstructionHelper<INT64_PIECES, 2 * INT64_PIECES, 0>
+{
+  public:
+    LIR_HEADER(MulI64)
+};
+
 // Performs an add, sub, mul, or div on two double values.
 class LMathD : public LBinaryMath<0>
 {
     JSOp jsop_;
 
   public:
     LIR_HEADER(MathD)
 
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -153,18 +153,21 @@
     _(MathFunctionD)                \
     _(MathFunctionF)                \
     _(NotI)                         \
     _(NotD)                         \
     _(NotF)                         \
     _(NotO)                         \
     _(NotV)                         \
     _(AddI)                         \
+    _(AddI64)                       \
     _(SubI)                         \
+    _(SubI64)                       \
     _(MulI)                         \
+    _(MulI64)                       \
     _(MathD)                        \
     _(MathF)                        \
     _(DivI)                         \
     _(DivPowTwoI)                   \
     _(ModI)                         \
     _(ModPowTwoI)                   \
     _(ModD)                         \
     _(BinaryV)                      \
--- a/js/src/jit/shared/Lowering-shared-inl.h
+++ b/js/src/jit/shared/Lowering-shared-inl.h
@@ -69,16 +69,45 @@ LIRGeneratorShared::defineFixed(LInstruc
 
     LDefinition def(type, LDefinition::FIXED);
     def.setOutput(output);
 
     define(lir, mir, def);
 }
 
 template <size_t Ops, size_t Temps> void
+LIRGeneratorShared::defineInt64Fixed(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
+                                     const LInt64Allocation& output)
+{
+    uint32_t vreg = getVirtualRegister();
+
+#if JS_BITS_PER_WORD == 64
+    LDefinition def(LDefinition::GENERAL, LDefinition::FIXED);
+    def.setOutput(output.value());
+    lir->setDef(0, def);
+    lir->getDef(0)->setVirtualRegister(vreg);
+#else
+    LDefinition def0(LDefinition::GENERAL, LDefinition::FIXED);
+    def0.setOutput(output.low());
+    lir->setDef(0, def0);
+    lir->getDef(0)->setVirtualRegister(vreg);
+
+    getVirtualRegister();
+    LDefinition def1(LDefinition::GENERAL, LDefinition::FIXED);
+    def1.setOutput(output.high());
+    lir->setDef(1, def1);
+    lir->getDef(1)->setVirtualRegister(vreg + 1);
+#endif
+
+    lir->setMir(mir);
+    mir->setVirtualRegister(vreg);
+    add(lir);
+}
+
+template <size_t Ops, size_t Temps> void
 LIRGeneratorShared::defineReuseInput(LInstructionHelper<1, Ops, Temps>* lir, MDefinition* mir, uint32_t operand)
 {
     // The input should be used at the start of the instruction, to avoid moves.
     MOZ_ASSERT(lir->getOperand(operand)->toUse()->usedAtStart());
 
     LDefinition::Type type = LDefinition::TypeFrom(mir->type());
 
     LDefinition def(type, LDefinition::MUST_REUSE_INPUT);
--- a/js/src/jit/shared/Lowering-shared.h
+++ b/js/src/jit/shared/Lowering-shared.h
@@ -142,16 +142,20 @@ class LIRGeneratorShared : public MDefin
     inline void defineBox(LInstructionHelper<BOX_PIECES, Ops, Temps>* lir, MDefinition* mir,
                           LDefinition::Policy policy = LDefinition::REGISTER);
 
     template <size_t Ops, size_t Temps>
     inline void defineInt64(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
                             LDefinition::Policy policy = LDefinition::REGISTER);
 
     template <size_t Ops, size_t Temps>
+    inline void defineInt64Fixed(LInstructionHelper<INT64_PIECES, Ops, Temps>* lir, MDefinition* mir,
+                                 const LInt64Allocation& output);
+
+    template <size_t Ops, size_t Temps>
     inline void defineSinCos(LInstructionHelper<2, Ops, Temps> *lir, MDefinition *mir,
                              LDefinition::Policy policy = LDefinition::REGISTER);
 
     inline void defineSharedStubReturn(LInstruction* lir, MDefinition* mir);
     inline void defineReturn(LInstruction* lir, MDefinition* mir);
 
     template <size_t X>
     inline void define(details::LInstructionFixedDefsTempsHelper<1, X>* lir, MDefinition* mir,
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -612,20 +612,50 @@ class Assembler : public AssemblerX86Sha
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
 
     void imulq(Register src, Register dest) {
         masm.imulq_rr(src.encoding(), dest.encoding());
     }
+    void imulq(const Operand& src, Register dest) {
+        switch (src.kind()) {
+          case Operand::REG:
+            masm.imulq_rr(src.reg(), dest.encoding());
+            break;
+          case Operand::MEM_REG_DISP:
+            masm.imulq_mr(src.disp(), src.base(), dest.encoding());
+            break;
+          case Operand::MEM_ADDRESS32:
+            MOZ_CRASH("NYI");
+            break;
+          default:
+            MOZ_CRASH("unexpected operand kind");
+        }
+    }
+
+    void cqo() {
+        masm.cqo();
+    }
+    void idivq(Register divisor) {
+        masm.idivq_r(divisor.encoding());
+    }
+    void udivq(Register divisor) {
+        masm.divq_r(divisor.encoding());
+    }
+
     void vcvtsi2sdq(Register src, FloatRegister dest) {
         masm.vcvtsi2sdq_rr(src.encoding(), dest.encoding());
     }
 
+    void negq(Register reg) {
+        masm.negq_r(reg.encoding());
+    }
+
     void mov(ImmWord word, Register dest) {
         // Use xor for setting registers to zero, as it is specially optimized
         // for this purpose on modern hardware. Note that it does clobber FLAGS
         // though. Use xorl instead of xorq since they are functionally
         // equivalent (32-bit instructions zero-extend their results to 64 bits)
         // and xorl has a smaller encoding.
         if (word.value == 0)
             xorl(dest, dest);
--- a/js/src/jit/x64/BaseAssembler-x64.h
+++ b/js/src/jit/x64/BaseAssembler-x64.h
@@ -288,16 +288,40 @@ class BaseAssemblerX64 : public BaseAsse
     }
 
     void imulq_rr(RegisterID src, RegisterID dst)
     {
         spew("imulq      %s, %s", GPReg64Name(src), GPReg64Name(dst));
         m_formatter.twoByteOp64(OP2_IMUL_GvEv, src, dst);
     }
 
+    void imulq_mr(int32_t offset, RegisterID base, RegisterID dst)
+    {
+        spew("imulq      " MEM_ob ", %s", ADDR_ob(offset, base), GPReg64Name(dst));
+        m_formatter.twoByteOp64(OP2_IMUL_GvEv, offset, base, dst);
+    }
+
+    void cqo()
+    {
+        spew("cqo        ");
+        m_formatter.oneByteOp64(OP_CDQ);
+    }
+
+    void idivq_r(RegisterID divisor)
+    {
+        spew("idivq      %s", GPReg64Name(divisor));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, divisor, GROUP3_OP_IDIV);
+    }
+
+    void divq_r(RegisterID divisor)
+    {
+        spew("divq       %s", GPReg64Name(divisor));
+        m_formatter.oneByteOp64(OP_GROUP3_Ev, divisor, GROUP3_OP_DIV);
+    }
+
     // Comparisons:
 
     void cmpq_rr(RegisterID rhs, RegisterID lhs)
     {
         spew("cmpq       %s, %s", GPReg64Name(rhs), GPReg64Name(lhs));
         m_formatter.oneByteOp64(OP_CMP_GvEv, rhs, lhs);
     }
 
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 "jit/x64/CodeGenerator-x64.h"
 
+#include "mozilla/MathAlgorithms.h"
+
 #include "jit/IonCaches.h"
 #include "jit/MIR.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/MacroAssembler-inl.h"
 #include "jit/shared/CodeGenerator-shared-inl.h"
 
@@ -281,17 +283,17 @@ CodeGeneratorX64::visitBitOpI64(LBitOpI6
 
 void
 CodeGeneratorX64::visitShiftI64(LShiftI64* lir)
 {
     Register lhs = ToRegister(lir->getOperand(0));
     const LAllocation* rhs = lir->getOperand(1);
 
     if (rhs->isConstant()) {
-        int32_t shift = ToInt32(rhs) & 0x3F;
+        int32_t shift = int32_t(ToInt64(rhs) & 0x3F);
         switch (lir->bitop()) {
           case JSOP_LSH:
             if (shift)
                 masm.shlq(Imm32(shift), lhs);
             break;
           case JSOP_RSH:
             if (shift)
                 masm.sarq(Imm32(shift), lhs);
@@ -317,16 +319,170 @@ CodeGeneratorX64::visitShiftI64(LShiftI6
             break;
           default:
             MOZ_CRASH("Unexpected shift op");
         }
     }
 }
 
 void
+CodeGeneratorX64::visitAddI64(LAddI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant())
+        masm.addPtr(ImmWord(ToInt64(rhs)), lhs);
+    else
+        masm.addq(ToOperand(rhs), lhs);
+}
+
+void
+CodeGeneratorX64::visitSubI64(LSubI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant())
+        masm.subPtr(ImmWord(ToInt64(rhs)), lhs);
+    else
+        masm.subq(ToOperand(rhs), lhs);
+}
+
+void
+CodeGeneratorX64::visitMulI64(LMulI64* lir)
+{
+    Register lhs = ToRegister(lir->getOperand(0));
+    const LAllocation* rhs = lir->getOperand(1);
+
+    MOZ_ASSERT(ToRegister(lir->getDef(0)) == lhs);
+
+    if (rhs->isConstant()) {
+        int64_t constant = ToInt64(rhs);
+        switch (constant) {
+          case -1:
+            masm.negq(lhs);
+            return;
+          case 0:
+            masm.xorl(lhs, lhs);
+            return;
+          case 1:
+            // nop
+            return;
+          case 2:
+            masm.addq(lhs, lhs);
+            return;
+          default:
+            if (constant > 0) {
+                // Use shift if constant is power of 2.
+                int32_t shift = mozilla::FloorLog2(constant);
+                if (int64_t(1 << shift) == constant) {
+                    masm.shlq(Imm32(shift), lhs);
+                    return;
+                }
+            }
+            masm.mul64(Imm64(constant), Register64(lhs));
+        }
+    } else {
+        masm.imulq(ToOperand(rhs), lhs);
+    }
+}
+
+void
+CodeGeneratorX64::visitDivOrModI64(LDivOrModI64* lir)
+{
+    Register lhs = ToRegister(lir->lhs());
+    Register rhs = ToRegister(lir->rhs());
+    Register output = ToRegister(lir->output());
+
+    MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
+    MOZ_ASSERT(rhs != rdx);
+    MOZ_ASSERT_IF(output == rax, ToRegister(lir->remainder()) == rdx);
+    MOZ_ASSERT_IF(output == rdx, ToRegister(lir->remainder()) == rax);
+
+    Label done;
+
+    // Put the lhs in rax.
+    if (lhs != rax)
+        masm.mov(lhs, rax);
+
+    // Handle divide by zero. For now match asm.js and return 0, but
+    // eventually this should trap.
+    if (lir->canBeDivideByZero()) {
+        Label nonZero;
+        masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+        masm.xorl(output, output);
+        masm.jump(&done);
+        masm.bind(&nonZero);
+    }
+
+    // Handle an integer overflow exception from INT64_MIN / -1. Eventually
+    // signed integer division should trap, instead of returning the
+    // LHS (INT64_MIN).
+    if (lir->canBeNegativeOverflow()) {
+        Label notmin;
+        masm.branchPtr(Assembler::NotEqual, lhs, ImmWord(INT64_MIN), &notmin);
+        masm.branchPtr(Assembler::NotEqual, rhs, ImmWord(-1), &notmin);
+        if (lir->mir()->isMod()) {
+            masm.xorl(output, output);
+        } else {
+            if (lhs != output)
+                masm.mov(lhs, output);
+        }
+        masm.jump(&done);
+        masm.bind(&notmin);
+    }
+
+    // Sign extend the lhs into rdx to make rdx:rax.
+    masm.cqo();
+    masm.idivq(rhs);
+
+    masm.bind(&done);
+}
+
+void
+CodeGeneratorX64::visitUDivOrMod64(LUDivOrMod64* lir)
+{
+    Register lhs = ToRegister(lir->lhs());
+    Register rhs = ToRegister(lir->rhs());
+    Register output = ToRegister(lir->output());
+
+    MOZ_ASSERT_IF(lhs != rhs, rhs != rax);
+    MOZ_ASSERT(rhs != rdx);
+    MOZ_ASSERT_IF(output == rax, ToRegister(lir->remainder()) == rdx);
+    MOZ_ASSERT_IF(output == rdx, ToRegister(lir->remainder()) == rax);
+
+    // Put the lhs in rax.
+    if (lhs != rax)
+        masm.mov(lhs, rax);
+
+    Label done;
+
+    // Prevent divide by zero. For now match asm.js and return 0, but
+    // eventually this should trap.
+    if (lir->canBeDivideByZero()) {
+        Label nonZero;
+        masm.branchTestPtr(Assembler::NonZero, rhs, rhs, &nonZero);
+        masm.xorl(output, output);
+        masm.jump(&done);
+        masm.bind(&nonZero);
+    }
+
+    // Zero extend the lhs into rdx to make (rdx:rax).
+    masm.xorl(rdx, rdx);
+    masm.udivq(rhs);
+
+    masm.bind(&done);
+}
+
+void
 CodeGeneratorX64::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
 }
 
 void
 CodeGeneratorX64::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32* lir)
 {
--- a/js/src/jit/x64/CodeGenerator-x64.h
+++ b/js/src/jit/x64/CodeGenerator-x64.h
@@ -41,16 +41,21 @@ class CodeGeneratorX64 : public CodeGene
     void visitCompareB(LCompareB* lir);
     void visitCompareBAndBranch(LCompareBAndBranch* lir);
     void visitCompareBitwise(LCompareBitwise* lir);
     void visitCompareBitwiseAndBranch(LCompareBitwiseAndBranch* lir);
     void visitCompare64(LCompare64* lir);
     void visitCompare64AndBranch(LCompare64AndBranch* lir);
     void visitBitOpI64(LBitOpI64* lir);
     void visitShiftI64(LShiftI64* lir);
+    void visitAddI64(LAddI64* lir);
+    void visitSubI64(LSubI64* lir);
+    void visitMulI64(LMulI64* lir);
+    void visitDivOrModI64(LDivOrModI64* lir);
+    void visitUDivOrMod64(LUDivOrMod64* lir);
     void visitTruncateDToInt32(LTruncateDToInt32* ins);
     void visitTruncateFToInt32(LTruncateFToInt32* ins);
     void visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic* ins);
     void visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic* ins);
     void visitAsmJSCall(LAsmJSCall* ins);
     void visitAsmJSLoadHeap(LAsmJSLoadHeap* ins);
     void visitAsmJSStoreHeap(LAsmJSStoreHeap* ins);
     void visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins);
--- a/js/src/jit/x64/LIR-x64.h
+++ b/js/src/jit/x64/LIR-x64.h
@@ -94,12 +94,77 @@ class LAsmJSLoadFuncPtr : public LInstru
     const LAllocation* index() {
         return getOperand(0);
     }
     const LDefinition* temp() {
         return getTemp(0);
     }
 };
 
+class LDivOrModI64 : public LBinaryMath<1>
+{
+  public:
+    LIR_HEADER(DivOrModI64)
+
+    LDivOrModI64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
+        setOperand(0, lhs);
+        setOperand(1, rhs);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* remainder() {
+        return getTemp(0);
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+    bool canBeNegativeOverflow() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeNegativeDividend();
+        return mir_->toDiv()->canBeNegativeOverflow();
+    }
+};
+
+// This class performs a simple x86 'div', yielding either a quotient or
+// remainder depending on whether this instruction is defined to output
+// rax (quotient) or rdx (remainder).
+class LUDivOrMod64 : public LBinaryMath<1>
+{
+  public:
+    LIR_HEADER(UDivOrMod64);
+
+    LUDivOrMod64(const LAllocation& lhs, const LAllocation& rhs, const LDefinition& temp) {
+        setOperand(0, lhs);
+        setOperand(1, rhs);
+        setTemp(0, temp);
+    }
+
+    const LDefinition* remainder() {
+        return getTemp(0);
+    }
+
+    const char* extraName() const {
+        return mir()->isTruncated() ? "Truncated" : nullptr;
+    }
+
+    MBinaryArithInstruction* mir() const {
+        MOZ_ASSERT(mir_->isDiv() || mir_->isMod());
+        return static_cast<MBinaryArithInstruction*>(mir_);
+    }
+
+    bool canBeDivideByZero() const {
+        if (mir_->isMod())
+            return mir_->toMod()->canBeDivideByZero();
+        return mir_->toDiv()->canBeDivideByZero();
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_x64_LIR_x64_h */
--- a/js/src/jit/x64/LOpcodes-x64.h
+++ b/js/src/jit/x64/LOpcodes-x64.h
@@ -6,14 +6,16 @@
 
 #ifndef jit_x64_LOpcodes_x64_h
 #define jit_x64_LOpcodes_x64_h
 
 #include "jit/shared/LOpcodes-shared.h"
 
 #define LIR_CPU_OPCODE_LIST(_)      \
     _(DivOrModConstantI)            \
+    _(DivOrModI64)                  \
+    _(UDivOrMod64)                  \
     _(SimdValueInt32x4)             \
     _(SimdValueFloat32x4)           \
     _(UDivOrMod)                    \
     _(UDivOrModConstant)
 
 #endif /* jit_x64_LOpcodes_x64_h */
--- a/js/src/jit/x64/Lowering-x64.cpp
+++ b/js/src/jit/x64/Lowering-x64.cpp
@@ -336,8 +336,52 @@ LIRGeneratorX64::visitStoreTypedArrayEle
 void
 LIRGeneratorX64::visitRandom(MRandom* ins)
 {
     LRandom *lir = new(alloc()) LRandom(temp(),
                                         temp(),
                                         temp());
     defineFixed(lir, ins, LFloatReg(ReturnDoubleReg));
 }
+
+void
+LIRGeneratorX64::lowerDivI64(MDiv* div)
+{
+    if (div->isUnsigned()) {
+        lowerUDiv64(div);
+        return;
+    }
+
+    LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(div->lhs()), useRegister(div->rhs()),
+                                                  tempFixed(rdx));
+    defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
+}
+
+void
+LIRGeneratorX64::lowerModI64(MMod* mod)
+{
+    if (mod->isUnsigned()) {
+        lowerUMod64(mod);
+        return;
+    }
+
+    LDivOrModI64* lir = new(alloc()) LDivOrModI64(useRegister(mod->lhs()), useRegister(mod->rhs()),
+                                                  tempFixed(rax));
+    defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
+}
+
+void
+LIRGeneratorX64::lowerUDiv64(MDiv* div)
+{
+    LUDivOrMod64* lir = new(alloc()) LUDivOrMod64(useRegister(div->lhs()),
+                                                  useRegister(div->rhs()),
+                                                  tempFixed(rdx));
+    defineInt64Fixed(lir, div, LInt64Allocation(LAllocation(AnyRegister(rax))));
+}
+
+void
+LIRGeneratorX64::lowerUMod64(MMod* mod)
+{
+    LUDivOrMod64* lir = new(alloc()) LUDivOrMod64(useRegister(mod->lhs()),
+                                                  useRegister(mod->rhs()),
+                                                  tempFixed(rax));
+    defineInt64Fixed(lir, mod, LInt64Allocation(LAllocation(AnyRegister(rdx))));
+}
--- a/js/src/jit/x64/Lowering-x64.h
+++ b/js/src/jit/x64/Lowering-x64.h
@@ -31,16 +31,21 @@ class LIRGeneratorX64 : public LIRGenera
     LAllocation useByteOpRegister(MDefinition* mir);
     LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir);
     LDefinition tempByteOpRegister();
 
     LDefinition tempToUnbox();
 
     bool needTempForPostBarrier() { return false; }
 
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
+    void lowerUDiv64(MDiv* div);
+    void lowerUMod64(MMod* mod);
+
   public:
     void visitBox(MBox* box);
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
--- a/js/src/jit/x64/MacroAssembler-x64-inl.h
+++ b/js/src/jit/x64/MacroAssembler-x64-inl.h
@@ -185,16 +185,29 @@ MacroAssembler::subPtr(Register src, con
 
 void
 MacroAssembler::subPtr(Imm32 imm, Register dest)
 {
     subq(imm, dest);
 }
 
 void
+MacroAssembler::subPtr(ImmWord imm, Register dest)
+{
+    ScratchRegisterScope scratch(*this);
+    MOZ_ASSERT(dest != scratch);
+    if ((intptr_t)imm.value <= INT32_MAX && (intptr_t)imm.value >= INT32_MIN) {
+        subq(Imm32((int32_t)imm.value), dest);
+    } else {
+        mov(imm, scratch);
+        subq(scratch, dest);
+    }
+}
+
+void
 MacroAssembler::subPtr(const Address& addr, Register dest)
 {
     subq(Operand(addr), dest);
 }
 
 void
 MacroAssembler::mul64(Imm64 imm, const Register64& dest)
 {
--- a/js/src/jit/x86/Lowering-x86.cpp
+++ b/js/src/jit/x86/Lowering-x86.cpp
@@ -416,16 +416,28 @@ LIRGeneratorX86::visitAsmJSAtomicBinopHe
 
 void
 LIRGeneratorX86::visitAsmJSLoadFuncPtr(MAsmJSLoadFuncPtr* ins)
 {
     define(new(alloc()) LAsmJSLoadFuncPtr(useRegisterAtStart(ins->index())), ins);
 }
 
 void
+LIRGeneratorX86::lowerDivI64(MDiv* div)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+LIRGeneratorX86::lowerModI64(MMod* mod)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 LIRGeneratorX86::visitSubstr(MSubstr* ins)
 {
     // Due to lack of registers on x86, we reuse the string register as
     // temporary. As a result we only need two temporary registers and take a
     // bugos temporary as fifth argument.
     LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()),
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
--- a/js/src/jit/x86/Lowering-x86.h
+++ b/js/src/jit/x86/Lowering-x86.h
@@ -37,16 +37,19 @@ class LIRGeneratorX86 : public LIRGenera
         return LDefinition::BogusTemp();
     }
 
     bool needTempForPostBarrier() { return true; }
 
     void lowerUntypedPhiInput(MPhi* phi, uint32_t inputPosition, LBlock* block, size_t lirIndex);
     void defineUntypedPhi(MPhi* phi, size_t lirIndex);
 
+    void lowerDivI64(MDiv* div);
+    void lowerModI64(MMod* mod);
+
   public:
     void visitBox(MBox* box);
     void visitUnbox(MUnbox* unbox);
     void visitReturn(MReturn* ret);
     void visitCompareExchangeTypedArrayElement(MCompareExchangeTypedArrayElement* ins);
     void visitAtomicExchangeTypedArrayElement(MAtomicExchangeTypedArrayElement* ins);
     void visitAtomicTypedArrayElementBinop(MAtomicTypedArrayElementBinop* ins);
     void visitAsmJSUnsignedToDouble(MAsmJSUnsignedToDouble* ins);
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -66,16 +66,18 @@ std::ostream& operator<<(std::ostream& a
 
 /*static*/ bool
 AccessibleCaretManager::sSelectionBarEnabled = false;
 /*static*/ bool
 AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = false;
 /*static*/ bool
 AccessibleCaretManager::sCaretsExtendedVisibility = false;
 /*static*/ bool
+AccessibleCaretManager::sCaretsAlwaysTilt = false;
+/*static*/ bool
 AccessibleCaretManager::sCaretsScriptUpdates = false;
 /*static*/ bool
 AccessibleCaretManager::sHapticFeedback = false;
 
 AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell)
   : mPresShell(aPresShell)
 {
   if (!mPresShell) {
@@ -90,16 +92,18 @@ AccessibleCaretManager::AccessibleCaretM
   static bool addedPrefs = false;
   if (!addedPrefs) {
     Preferences::AddBoolVarCache(&sSelectionBarEnabled,
                                  "layout.accessiblecaret.bar.enabled");
     Preferences::AddBoolVarCache(&sCaretShownWhenLongTappingOnEmptyContent,
       "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content");
     Preferences::AddBoolVarCache(&sCaretsExtendedVisibility,
                                  "layout.accessiblecaret.extendedvisibility");
+    Preferences::AddBoolVarCache(&sCaretsAlwaysTilt,
+                                 "layout.accessiblecaret.always_tilt");
     Preferences::AddBoolVarCache(&sCaretsScriptUpdates,
       "layout.accessiblecaret.allow_script_change_updates");
     Preferences::AddBoolVarCache(&sHapticFeedback,
                                  "layout.accessiblecaret.hapticfeedback");
     addedPrefs = true;
   }
 }
 
@@ -389,26 +393,30 @@ AccessibleCaretManager::UpdateCaretsForS
     if (IsTerminated()) {
       return;
     }
   }
 
   if (aHint == UpdateCaretsHint::Default) {
     // Only check for tilt carets with default update hint. Otherwise we might
     // override the appearance set by the caller.
-    UpdateCaretsForTilt();
+    if (sCaretsAlwaysTilt) {
+      UpdateCaretsForAlwaysTilt(startFrame, endFrame);
+    } else {
+      UpdateCaretsForOverlappingTilt();
+    }
   }
 
   if (!mActiveCaret) {
     DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
   }
 }
 
 void
-AccessibleCaretManager::UpdateCaretsForTilt()
+AccessibleCaretManager::UpdateCaretsForOverlappingTilt()
 {
   if (mFirstCaret->IsVisuallyVisible() && mSecondCaret->IsVisuallyVisible()) {
     if (mFirstCaret->Intersects(*mSecondCaret)) {
       if (mFirstCaret->LogicalPosition().x <=
           mSecondCaret->LogicalPosition().x) {
         mFirstCaret->SetAppearance(Appearance::Left);
         mSecondCaret->SetAppearance(Appearance::Right);
       } else {
@@ -418,16 +426,32 @@ AccessibleCaretManager::UpdateCaretsForT
     } else {
       mFirstCaret->SetAppearance(Appearance::Normal);
       mSecondCaret->SetAppearance(Appearance::Normal);
     }
   }
 }
 
 void
+AccessibleCaretManager::UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                                  nsIFrame* aEndFrame)
+{
+  if (mFirstCaret->IsVisuallyVisible()) {
+    auto startFrameWritingMode = aStartFrame->GetWritingMode();
+    mFirstCaret->SetAppearance(startFrameWritingMode.IsBidiLTR() ?
+                               Appearance::Left : Appearance::Right);
+  }
+  if (mSecondCaret->IsVisuallyVisible()) {
+    auto endFrameWritingMode = aEndFrame->GetWritingMode();
+    mSecondCaret->SetAppearance(endFrameWritingMode.IsBidiLTR() ?
+                                Appearance::Right : Appearance::Left);
+  }
+}
+
+void
 AccessibleCaretManager::ProvideHapticFeedback()
 {
   if (sHapticFeedback) {
     nsCOMPtr<nsIHapticFeedback> haptic =
       do_GetService("@mozilla.org/widget/hapticfeedback;1");
     haptic->PerformSimpleAction(haptic->LongPress);
   }
 }
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -199,17 +199,21 @@ protected:
   // Get caret mode based on current selection.
   virtual CaretMode GetCaretMode() const;
 
   // @return true if aStartFrame comes before aEndFrame.
   virtual bool CompareTreePosition(nsIFrame* aStartFrame,
                                    nsIFrame* aEndFrame) const;
 
   // Check if the two carets is overlapping to become tilt.
-  virtual void UpdateCaretsForTilt();
+  virtual void UpdateCaretsForOverlappingTilt();
+
+  // Make the two carets always tilt.
+  virtual void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                         nsIFrame* aEndFrame);
 
   // Check whether AccessibleCaret is displayable in cursor mode or not.
   // @param aOutFrame returns frame of the cursor if it's displayable.
   // @param aOutOffset returns frame offset as well.
   virtual bool IsCaretDisplayableInCursorMode(nsIFrame** aOutFrame = nullptr,
                                               int32_t* aOutOffset = nullptr) const;
 
   virtual bool HasNonEmptyTextContent(nsINode* aNode) const;
@@ -272,16 +276,20 @@ protected:
   // which is based on the emptiness of the content, into something more
   // heuristic. See UpdateCaretsForCursorMode() for the details.
   static bool sCaretShownWhenLongTappingOnEmptyContent;
 
   // Android specific visibility extensions correct compatibility issues
   // with ActionBar visibility during page scroll.
   static bool sCaretsExtendedVisibility;
 
+  // Preference to make carets always tilt in selection mode. By default, the
+  // carets become tilt only when they are overlapping.
+  static bool sCaretsAlwaysTilt;
+
   // By default, javascript content selection changes closes AccessibleCarets and
   // UI interactions. Optionally, we can try to maintain the active UI, keeping
   // carets and ActionBar available.
   static bool sCaretsScriptUpdates;
 
   // AccessibleCaret pref for haptic feedback behaviour on longPress.
   static bool sHapticFeedback;
 };
--- a/layout/base/gtest/TestAccessibleCaretManager.cpp
+++ b/layout/base/gtest/TestAccessibleCaretManager.cpp
@@ -56,16 +56,17 @@ public:
   class MockAccessibleCaretManager : public AccessibleCaretManager
   {
   public:
     using CaretMode = AccessibleCaretManager::CaretMode;
     using AccessibleCaretManager::UpdateCarets;
     using AccessibleCaretManager::HideCarets;
     using AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent;
     using AccessibleCaretManager::sCaretsExtendedVisibility;
+    using AccessibleCaretManager::sCaretsAlwaysTilt;
 
     MockAccessibleCaretManager()
       : AccessibleCaretManager(nullptr)
     {
       mFirstCaret = MakeUnique<MockAccessibleCaret>();
       mSecondCaret = MakeUnique<MockAccessibleCaret>();
     }
 
@@ -86,17 +87,28 @@ public:
     }
 
     virtual bool IsCaretDisplayableInCursorMode(
       nsIFrame** aOutFrame = nullptr, int32_t* aOutOffset = nullptr) const override
     {
       return true;
     }
 
-    virtual void UpdateCaretsForTilt() override {}
+    virtual void UpdateCaretsForOverlappingTilt() override {}
+
+    virtual void UpdateCaretsForAlwaysTilt(nsIFrame* aStartFrame,
+                                           nsIFrame* aEndFrame)
+    {
+      if (mFirstCaret->IsVisuallyVisible()) {
+        mFirstCaret->SetAppearance(Appearance::Left);
+      }
+      if (mSecondCaret->IsVisuallyVisible()) {
+        mSecondCaret->SetAppearance(Appearance::Right);
+      }
+    }
 
     virtual bool IsTerminated() const override { return false; }
 
     MOCK_CONST_METHOD0(GetCaretMode, CaretMode());
     MOCK_CONST_METHOD1(DispatchCaretStateChangedEvent,
                        void(CaretChangedReason aReason));
     MOCK_CONST_METHOD1(HasNonEmptyTextContent, bool(nsINode* aNode));
 
@@ -406,17 +418,17 @@ TEST_F(AccessibleCaretManagerTester, Tes
 
   mManager.OnScrollEnd();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester,
-       TestScrollInSelectionModeWithExtendedVisibility)
+       TestScrollInSelectionModeWithExtendedVisibilityAndAlwaysTilt)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Selection));
 
   MockFunction<void(std::string aCheckPointName)> check;
   {
     InSequence dummy;
 
@@ -454,53 +466,57 @@ TEST_F(AccessibleCaretManagerTester,
     EXPECT_CALL(check, Call("reflow2"));
 
     // After the scroll ended, both carets are visible.
     EXPECT_CALL(mManager, DispatchCaretStateChangedEvent(
                   CaretChangedReason::Updateposition));
     EXPECT_CALL(check, Call("scrollend2"));
   }
 
-  AutoRestore<bool> savePref(
+  // Simulate Firefox Android preferences.
+  AutoRestore<bool> saveCaretsExtendedVisibility(
     MockAccessibleCaretManager::sCaretsExtendedVisibility);
   MockAccessibleCaretManager::sCaretsExtendedVisibility = true;
+  AutoRestore<bool> saveCaretsAlwaysTilt(
+    MockAccessibleCaretManager::sCaretsAlwaysTilt);
+  MockAccessibleCaretManager::sCaretsAlwaysTilt = true;
 
   mManager.UpdateCarets();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+  EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
   check.Call("updatecarets");
 
   mManager.OnScrollStart();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
   check.Call("scrollstart1");
 
   mManager.OnReflow();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
   check.Call("reflow1");
 
   mManager.OnScrollEnd();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
   check.Call("scrollend1");
 
   mManager.OnScrollStart();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
   check.Call("scrollstart2");
 
   mManager.OnReflow();
   EXPECT_EQ(FirstCaretAppearance(), Appearance::NormalNotShown);
   EXPECT_EQ(SecondCaretAppearance(), Appearance::NormalNotShown);
   check.Call("reflow2");
 
   mManager.OnScrollEnd();
-  EXPECT_EQ(FirstCaretAppearance(), Appearance::Normal);
-  EXPECT_EQ(SecondCaretAppearance(), Appearance::Normal);
+  EXPECT_EQ(FirstCaretAppearance(), Appearance::Left);
+  EXPECT_EQ(SecondCaretAppearance(), Appearance::Right);
   check.Call("scrollend2");
 }
 
 TEST_F(AccessibleCaretManagerTester, TestScrollInCursorModeWhenLogicallyVisible)
 {
   EXPECT_CALL(mManager, GetCaretMode())
     .WillRepeatedly(Return(CaretMode::Cursor));
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4382,31 +4382,31 @@ void ScrollFrameHelper::CurPosAttributeC
 }
 
 /* ============= Scroll events ========== */
 
 ScrollFrameHelper::ScrollEvent::ScrollEvent(ScrollFrameHelper* aHelper)
   : mHelper(aHelper)
 {
   mDriver = mHelper->mOuter->PresContext()->RefreshDriver();
-  mDriver->AddRefreshObserver(this, Flush_Style);
+  mDriver->AddRefreshObserver(this, Flush_Layout);
 }
 
 ScrollFrameHelper::ScrollEvent::~ScrollEvent()
 {
   if (mDriver) {
-    mDriver->RemoveRefreshObserver(this, Flush_Style);
+    mDriver->RemoveRefreshObserver(this, Flush_Layout);
     mDriver = nullptr;
   }
 }
 
 void
 ScrollFrameHelper::ScrollEvent::WillRefresh(mozilla::TimeStamp aTime)
 {
-  mDriver->RemoveRefreshObserver(this, Flush_Style);
+  mDriver->RemoveRefreshObserver(this, Flush_Layout);
   mDriver = nullptr;
   mHelper->FireScrollEvent();
 }
 
 void
 ScrollFrameHelper::FireScrollEvent()
 {
   MOZ_ASSERT(mScrollEvent);
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -96,16 +96,39 @@ public:
 
   void PostScrollEvent();
   void FireScrollEvent();
   void PostScrolledAreaEvent();
   void FireScrolledAreaEvent();
 
   bool IsSmoothScrollingEnabled();
 
+  /**
+   * This class handles the dispatching of scroll events to content.
+   *
+   * nsRefreshDriver maintains three lists of refresh observers, one for each
+   * flush type: Flush_Style, Flush_Layout, and Flush_Display.
+   *
+   * During a tick, it runs through each list of observers, in order, and runs
+   * them. To iterate over each list, it uses an EndLimitedIterator, which is
+   * designed to iterate only over elements present when the iterator was
+   * created, not elements added afterwards. This means that, for a given flush
+   * type, a refresh observer added during the execution of another refresh
+   * observer of that flush type, will not run until the next tick.
+   *
+   * During main-thread animation-driven scrolling, ScrollEvents are *posted*
+   * by AsyncScroll::WillRefresh(). AsyncScroll registers itself as a Flush_Style
+   * refresh observer.
+   *
+   * Posting a scroll event, as of bug 1250550, registers a Flush_Layout
+   * refresh observer, which *fires* the event when run. This allows the event
+   * to be fired to content in the same refresh driver tick as it is posted.
+   * This is an important invariant to maintain to reduce scroll event latency
+   * for main-thread scrolling.
+   */
   class ScrollEvent : public nsARefreshObserver {
   public:
     NS_INLINE_DECL_REFCOUNTING(ScrollEvent, override)
     explicit ScrollEvent(ScrollFrameHelper *helper);
     void WillRefresh(mozilla::TimeStamp aTime) override;
   protected:
     virtual ~ScrollEvent();
   private:
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -845,32 +845,35 @@ public:
   
   nsPoint GetPositionIgnoringScrolling();
 
   typedef AutoTArray<nsIContent*, 2> ContentArray;
   static void DestroyContentArray(ContentArray* aArray);
 
 #define NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, dtor)             \
   static const mozilla::FramePropertyDescriptor<type>* prop() {           \
-    static MOZ_CONSTEXPR auto descriptor =                                \
+    /* Use of MOZ_CONSTEXPR caused startup crashes with MSVC2015u1 PGO. */\
+    static const auto descriptor =                                        \
       mozilla::FramePropertyDescriptor<type>::NewWithDestructor<dtor>();  \
     return &descriptor;                                                   \
   }
 
 // Don't use this unless you really know what you're doing!
 #define NS_DECLARE_FRAME_PROPERTY_WITH_FRAME_IN_DTOR(prop, type, dtor)    \
   static const mozilla::FramePropertyDescriptor<type>* prop() {           \
-    static MOZ_CONSTEXPR auto descriptor = mozilla::                      \
+    /* Use of MOZ_CONSTEXPR caused startup crashes with MSVC2015u1 PGO. */\
+    static const auto descriptor = mozilla::                              \
       FramePropertyDescriptor<type>::NewWithDestructorWithFrame<dtor>();  \
     return &descriptor;                                                   \
   }
 
 #define NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(prop, type)                \
   static const mozilla::FramePropertyDescriptor<type>* prop() {           \
-    static MOZ_CONSTEXPR auto descriptor =                                \
+    /* Use of MOZ_CONSTEXPR caused startup crashes with MSVC2015u1 PGO. */\
+    static const auto descriptor =                                        \
       mozilla::FramePropertyDescriptor<type>::NewWithoutDestructor();     \
     return &descriptor;                                                   \
   }
 
 #define NS_DECLARE_FRAME_PROPERTY_DELETABLE(prop, type) \
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(prop, type, DeleteValue)
 
 #define NS_DECLARE_FRAME_PROPERTY_RELEASABLE(prop, type) \
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -770,18 +770,20 @@ PropertySupportsVariant(nsCSSProperty aP
         break;
 
       case eCSSProperty_border_top_left_radius:
       case eCSSProperty_border_top_right_radius:
       case eCSSProperty_border_bottom_left_radius:
       case eCSSProperty_border_bottom_right_radius:
       case eCSSProperty_background_position:
       case eCSSProperty_background_size:
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
       case eCSSProperty_mask_position:
       case eCSSProperty_mask_size:
+#endif
       case eCSSProperty_grid_auto_columns:
       case eCSSProperty_grid_auto_rows:
       case eCSSProperty_grid_template_columns:
       case eCSSProperty_grid_template_rows:
       case eCSSProperty_object_position:
       case eCSSProperty_scroll_snap_coordinate:
       case eCSSProperty_scroll_snap_destination:
       case eCSSProperty_transform_origin:
--- a/layout/reftests/w3c-css/submitted/masking/reftest.list
+++ b/layout/reftests/w3c-css/submitted/masking/reftest.list
@@ -1,4 +1,4 @@
-== mask-composite-1a.html mask-composite-1-ref.html
-== mask-composite-1b.html mask-composite-1-ref.html
-fails-if(cocoaWidget) == mask-composite-2a.html mask-composite-2-ref.html # bug 1231643
-fails-if(cocoaWidget) == mask-composite-2b.html mask-composite-2-ref.html # bug 1231643
+fails == mask-composite-1a.html mask-composite-1-ref.html # bug 1251161
+fails == mask-composite-1b.html mask-composite-1-ref.html # bug 1251161
+fails fails-if(cocoaWidget) == mask-composite-2a.html mask-composite-2-ref.html # bug 1231643;  bug 1251161
+fails fails-if(cocoaWidget) == mask-composite-2b.html mask-composite-2-ref.html # bug 1231643; bug 1251161
--- a/layout/style/Declaration.cpp
+++ b/layout/style/Declaration.cpp
@@ -346,17 +346,21 @@ Declaration::GetImageLayerValue(
       if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
         if (repeat || position || clip || origin || size || attachment) {
           // Uneven length lists, so can't be serialized as shorthand.
           aValue.Truncate();
           return;
         }
       // This layer is an mask layer
       } else {
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
         MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
+#else
+        MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
+#endif
         if (repeat || position || clip || origin || size || composite || mode) {
           // Uneven length lists, so can't be serialized as shorthand.
           aValue.Truncate();
           return;
         }
       }
       break;
     }
@@ -365,17 +369,21 @@ Declaration::GetImageLayerValue(
     if (aTable == nsStyleImageLayers::kBackgroundLayerTable) {
       if (!repeat || !position || !clip || !origin || !size || !attachment) {
         // Uneven length lists, so can't be serialized as shorthand.
         aValue.Truncate();
         return;
       }
     // This layer is an mask layer
     } else {
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
       MOZ_ASSERT(aTable == nsStyleImageLayers::kMaskLayerTable);
+#else
+      MOZ_ASSERT_UNREACHABLE("Should never get here when mask-as-shorthand is disable");
+#endif
       if (!repeat || !position || !clip || !origin || !size ||
           !composite || !mode) {
         // Uneven length lists, so can't be serialized as shorthand.
         aValue.Truncate();
         return;
       }
     }
     aValue.Append(char16_t(','));
@@ -652,21 +660,23 @@ Declaration::GetValue(nsCSSProperty aPro
       }
       break;
     }
     case eCSSProperty_background: {
       GetImageLayerValue(data, aValue, aSerialization,
                          nsStyleImageLayers::kBackgroundLayerTable);
       break;
     }
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
     case eCSSProperty_mask: {
       GetImageLayerValue(data, aValue, aSerialization,
                          nsStyleImageLayers::kMaskLayerTable);
       break;
     }
+#endif
     case eCSSProperty_font: {
       // systemFont might not be present; other values are guaranteed to be
       // available based on the shorthand check at the beginning of the
       // function, as long as the prop is enabled
       const nsCSSValue *systemFont =
         data->ValueFor(eCSSProperty__x_system_font);
       const nsCSSValue *style =
         data->ValueFor(eCSSProperty_font_style);
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -3426,38 +3426,38 @@ StyleAnimationValue::ExtractComputedValu
         }
 
         case eCSSProperty_background_position: {
           const nsStyleImageLayers& layers =
             static_cast<const nsStyleBackground*>(styleStruct)->mImage;
           ExtractImageLayerPositionList(layers, aComputedValue);
           break;
         }
-
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
         case eCSSProperty_mask_position: {
           const nsStyleImageLayers& layers =
             static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
           ExtractImageLayerPositionList(layers, aComputedValue);
           break;
         }
-
+#endif
         case eCSSProperty_background_size: {
           const nsStyleImageLayers& layers =
             static_cast<const nsStyleBackground*>(styleStruct)->mImage;
           ExtractImageLayerSizePairList(layers, aComputedValue);
           break;
         }
-
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
         case eCSSProperty_mask_size: {
           const nsStyleImageLayers& layers =
             static_cast<const nsStyleSVGReset*>(styleStruct)->mMask;
           ExtractImageLayerSizePairList(layers, aComputedValue);
           break;
         }
-
+#endif
         case eCSSProperty_filter: {
           const nsStyleSVGReset *svgReset =
             static_cast<const nsStyleSVGReset*>(styleStruct);
           const nsTArray<nsStyleFilter>& filters = svgReset->mFilters;
           nsAutoPtr<nsCSSValueList> result;
           nsCSSValueList **resultTail = getter_Transfers(result);
           for (uint32_t i = 0; i < filters.Length(); ++i) {
             nsCSSValueList *item = new nsCSSValueList;
--- a/layout/style/jar.mn
+++ b/layout/style/jar.mn
@@ -14,10 +14,22 @@ toolkit.jar:
 *  res/forms.css    (forms.css)
    res/number-control.css    (number-control.css)
    res/arrow.gif        (arrow.gif)
    res/arrow-left.gif   (arrow-left.gif)
    res/arrow-right.gif  (arrow-right.gif)
    res/arrowd.gif       (arrowd.gif)
    res/arrowd-left.gif  (arrowd-left.gif)
    res/arrowd-right.gif (arrowd-right.gif)
+   res/accessiblecaret-normal@1x.png         (res/accessiblecaret-normal@1x.png)
+   res/accessiblecaret-normal@1.5x.png       (res/accessiblecaret-normal@1.5x.png)
+   res/accessiblecaret-normal@2x.png         (res/accessiblecaret-normal@2x.png)
+   res/accessiblecaret-normal@2.25x.png      (res/accessiblecaret-normal@2.25x.png)
+   res/accessiblecaret-tilt-left@1x.png      (res/accessiblecaret-tilt-left@1x.png)
+   res/accessiblecaret-tilt-left@1.5x.png    (res/accessiblecaret-tilt-left@1.5x.png)
+   res/accessiblecaret-tilt-left@2x.png      (res/accessiblecaret-tilt-left@2x.png)
+   res/accessiblecaret-tilt-left@2.25x.png   (res/accessiblecaret-tilt-left@2.25x.png)
+   res/accessiblecaret-tilt-right@1x.png     (res/accessiblecaret-tilt-right@1x.png)
+   res/accessiblecaret-tilt-right@1.5x.png   (res/accessiblecaret-tilt-right@1.5x.png)
+   res/accessiblecaret-tilt-right@2x.png     (res/accessiblecaret-tilt-right@2x.png)
+   res/accessiblecaret-tilt-right@2.25x.png  (res/accessiblecaret-tilt-right@2.25x.png)
 
 % resource gre-resources %res/
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -11438,24 +11438,26 @@ CSSParserImpl::ParsePropertyByFunction(n
   case eCSSProperty_marker:
     return ParseMarker();
   case eCSSProperty_paint_order:
     return ParsePaintOrder();
   case eCSSProperty_clip_path:
     return ParseClipPath();
   case eCSSProperty_scroll_snap_type:
     return ParseScrollSnapType();
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
   case eCSSProperty_mask:
     return ParseImageLayers(nsStyleImageLayers::kMaskLayerTable);
   case eCSSProperty_mask_repeat:
     return ParseImageLayerRepeat(eCSSProperty_mask_repeat);
   case eCSSProperty_mask_position:
     return ParseImageLayerPosition(eCSSProperty_mask_position);
   case eCSSProperty_mask_size:
     return ParseImageLayerSize(eCSSProperty_mask_size);
+#endif
   case eCSSProperty_all:
     return ParseAll();
   default:
     MOZ_ASSERT(false, "should not be called");
     return false;
   }
 }
 
--- a/layout/style/nsCSSPropAliasList.h
+++ b/layout/style/nsCSSPropAliasList.h
@@ -341,16 +341,17 @@ CSS_PROP_ALIAS(-webkit-box-pack,
                WebkitBoxPack,
                WEBKIT_PREFIX_PREF)
 
 CSS_PROP_ALIAS(-webkit-user-select,
                user_select,
                WebkitUserSelect,
                WEBKIT_PREFIX_PREF)
 
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
 CSS_PROP_ALIAS(-webkit-mask,
                mask,
                WebkitMask,
                WEBKIT_PREFIX_PREF)
 CSS_PROP_ALIAS(-webkit-mask-clip,
                mask_clip,
                WebkitMaskClip,
                WEBKIT_PREFIX_PREF)
@@ -373,10 +374,10 @@ CSS_PROP_ALIAS(-webkit-mask-position,
 CSS_PROP_ALIAS(-webkit-mask-repeat,
                mask_repeat,
                WebkitMaskRepeat,
                WEBKIT_PREFIX_PREF)
 CSS_PROP_ALIAS(-webkit-mask-size,
                mask_size,
                WebkitMaskSize,
                WEBKIT_PREFIX_PREF)
-
+#endif
 #undef WEBKIT_PREFIX_PREF
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -4070,16 +4070,17 @@ CSS_PROP_SVG(
     marker_start,
     MarkerStart,
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HUO,
     nullptr,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
 CSS_PROP_SHORTHAND(
     mask,
     mask,
     Mask,
     CSS_PROPERTY_PARSE_FUNCTION,
     "")
 CSS_PROP_SVGRESET(
     mask-clip,
@@ -4169,16 +4170,29 @@ CSS_PROP_SVGRESET(
         CSS_PROPERTY_VALUE_LIST_USES_COMMAS |
         CSS_PROPERTY_VALUE_NONNEGATIVE |
         CSS_PROPERTY_STORES_CALC,
     "",
     0,
     kImageLayerSizeKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_Custom)
+#else
+CSS_PROP_SVGRESET(
+    mask,
+    mask,
+    Mask,
+    CSS_PROPERTY_PARSE_VALUE |
+      CSS_PROPERTY_CREATES_STACKING_CONTEXT,
+    "",
+    VARIANT_HUO,
+    nullptr,
+    CSS_PROP_NO_OFFSET,
+    eStyleAnimType_None)
+#endif
 CSS_PROP_SVGRESET(
     mask-type,
     mask_type,
     MaskType,
     CSS_PROPERTY_PARSE_VALUE,
     "layout.css.masking.enabled",
     VARIANT_HK,
     kMaskTypeKTable,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -2925,29 +2925,29 @@ static const nsCSSProperty gMozTransform
   eCSSProperty_UNKNOWN
 };
 
 static const nsCSSProperty gScrollSnapTypeSubpropTable[] = {
   eCSSProperty_scroll_snap_type_x,
   eCSSProperty_scroll_snap_type_y,
   eCSSProperty_UNKNOWN
 };
-
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
 static const nsCSSProperty gMaskSubpropTable[] = {
   eCSSProperty_mask_image,
   eCSSProperty_mask_repeat,
   eCSSProperty_mask_position,
   eCSSProperty_mask_clip,
   eCSSProperty_mask_origin,
   eCSSProperty_mask_size,
   eCSSProperty_mask_composite,
   eCSSProperty_mask_mode,
   eCSSProperty_UNKNOWN
 };
-
+#endif
 // FIXME: mask-border tables should be added when we implement
 // mask-border properties.
 
 const nsCSSProperty *const
 nsCSSProps::kSubpropertyTable[eCSSProperty_COUNT - eCSSProperty_COUNT_no_shorthands] = {
 #define CSS_PROP_PUBLIC_OR_PRIVATE(publicname_, privatename_) privatename_
 // Need an extra level of macro nesting to force expansion of method_
 // params before they get pasted.
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -6033,16 +6033,17 @@ nsComputedDOMStyle::DoGetMask()
     val->SetURI(firstLayer.mSourceURI);
   } else {
     val->SetIdent(eCSSKeyword_none);
   }
 
   return val.forget();
 }
 
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetMaskClip()
 {
   return GetBackgroundList(&nsStyleImageLayers::Layer::mClip,
                            &nsStyleImageLayers::mClipCount,
                            StyleSVGReset()->mMask,
                            nsCSSProps::kImageLayerOriginKTable);
 }
@@ -6096,16 +6097,17 @@ nsComputedDOMStyle::DoGetMaskRepeat()
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetMaskSize()
 {
   const nsStyleImageLayers& layers = StyleSVGReset()->mMask;
   return DoGetImageLayerSize(layers);
 }
+#endif
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetMaskType()
 {
   RefPtr<nsROCSSPrimitiveValue> val = new nsROCSSPrimitiveValue;
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(StyleSVGReset()->mMaskType,
                                    nsCSSProps::kMaskTypeKTable));
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -299,25 +299,26 @@ private:
   already_AddRefed<CSSValue> DoGetBackgroundRepeat();
   already_AddRefed<CSSValue> DoGetBackgroundClip();
   already_AddRefed<CSSValue> DoGetBackgroundBlendMode();
   already_AddRefed<CSSValue> DoGetBackgroundOrigin();
   already_AddRefed<CSSValue> DoGetBackgroundSize();
 
   /* Mask properties */
   already_AddRefed<CSSValue> DoGetMask();
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
   already_AddRefed<CSSValue> DoGetMaskImage();
   already_AddRefed<CSSValue> DoGetMaskPosition();
   already_AddRefed<CSSValue> DoGetMaskRepeat();
   already_AddRefed<CSSValue> DoGetMaskClip();
   already_AddRefed<CSSValue> DoGetMaskOrigin();
   already_AddRefed<CSSValue> DoGetMaskSize();
   already_AddRefed<CSSValue> DoGetMaskMode();
   already_AddRefed<CSSValue> DoGetMaskComposite();
-
+#endif
   /* Padding properties */
   already_AddRefed<CSSValue> DoGetPaddingTop();
   already_AddRefed<CSSValue> DoGetPaddingBottom();
   already_AddRefed<CSSValue> DoGetPaddingLeft();
   already_AddRefed<CSSValue> DoGetPaddingRight();
 
   /* Table Properties */
   already_AddRefed<CSSValue> DoGetBorderCollapse();
--- a/layout/style/nsComputedDOMStylePropertyList.h
+++ b/layout/style/nsComputedDOMStylePropertyList.h
@@ -313,24 +313,26 @@ COMPUTED_STYLE_PROP(filter,             
 COMPUTED_STYLE_PROP(flood_color,                   FloodColor)
 COMPUTED_STYLE_PROP(flood_opacity,                 FloodOpacity)
 COMPUTED_STYLE_PROP(image_rendering,               ImageRendering)
 COMPUTED_STYLE_PROP(lighting_color,                LightingColor)
 COMPUTED_STYLE_PROP(marker_end,                    MarkerEnd)
 COMPUTED_STYLE_PROP(marker_mid,                    MarkerMid)
 COMPUTED_STYLE_PROP(marker_start,                  MarkerStart)
 COMPUTED_STYLE_PROP(mask,                          Mask)
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
 COMPUTED_STYLE_