Merge m-c to graphics
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 09 Jan 2017 10:18:37 -0500
changeset 342040 b9def278aa383e84a539598eee0d59d419e0bdc3
parent 342039 5de7e7c04c4e190ec7b4713bced396e7bbd441a4 (current diff)
parent 328519 2977ca1224525680cbfb5c3ce3018818b6dfd8f2 (diff)
child 342041 eefd48f36d82304bd1a4bb8626b29ef5ee54bb80
push id31345
push userkwierso@gmail.com
push dateFri, 10 Feb 2017 20:35:09 +0000
treeherdermozilla-central@a288fe35e494 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone53.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 graphics MozReview-Commit-ID: 5ovnMmSOiZX
gfx/2d/2D.h
gfx/2d/ScaledFontDWrite.cpp
gfx/layers/ipc/CompositorBridgeChild.cpp
gfx/layers/ipc/CompositorBridgeParent.cpp
js/src/jit/BaselineCacheIRCompiler.cpp
layout/base/nsLayoutUtils.cpp
layout/generic/nsTextFrame.cpp
layout/painting/FrameLayerBuilder.cpp
layout/reftests/css-animations/reftest.list
media/webrtc/signaling/gtest/jsep_session_unittest.cpp
media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_utility_sndio.cc
media/webrtc/trunk/webrtc/modules/audio_device/sndio/audio_device_utility_sndio.h
modules/libpref/init/all.js
security/nss.symbols
taskcluster/ci/test/test-sets.yml
testing/tools/grabber/getpages.sh
testing/tools/proxyserver/proxyserver.py
testing/web-platform/meta/html/semantics/forms/the-input-element/radio-groupname-case.html.ini
tools/check-moz-style/checkmozstyle.py
tools/check-moz-style/diff_parser.py
tools/check-moz-style/modules/__init__.py
tools/check-moz-style/modules/cpplint.py
tools/check-moz-style/modules/diff_parser.py
tools/check-moz-style/modules/logging.py
tools/check-moz-style/modules/scm.py
tools/check-moz-style/run_tests.py
tools/check-moz-style/tests/test1.cpp
tools/check-moz-style/tests/test1.out
tools/check-moz-style/tests/test1.patch
tools/check-moz-style/tests/test2.cpp
tools/check-moz-style/tests/test2.out
tools/check-moz-style/tests/test2.patch
tools/check-moz-style/tests/test3.out
tools/check-moz-style/tests/test3.patch
tools/check-moz-style/tests/test4.cpp
tools/check-moz-style/tests/test4.out
tools/check-moz-style/tests/test4.patch
tools/check-moz-style/tests/test5.cpp
tools/check-moz-style/tests/test5.out
tools/check-moz-style/tests/test5.patch
widget/PuppetWidget.cpp
widget/cocoa/nsChildView.mm
widget/gtk/nsWindow.cpp
widget/nsBaseWidget.cpp
widget/windows/nsWindow.cpp
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -843,36 +843,35 @@ NotificationController::WillRefresh(mozi
     for (size_t i = 0; i < newDocCount; i++) {
       DocAccessible* childDoc = newChildDocs[i];
       if (childDoc->IsDefunct()) {
         continue;
       }
 
       Accessible* parent = childDoc->Parent();
       DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
+      MOZ_DIAGNOSTIC_ASSERT(parentIPCDoc);
       uint64_t id = reinterpret_cast<uintptr_t>(parent->UniqueID());
-      MOZ_ASSERT(id);
+      MOZ_DIAGNOSTIC_ASSERT(id);
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
       }
 
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
 
 #if defined(XP_WIN)
-      MOZ_ASSERT(parentIPCDoc);
       parentIPCDoc->ConstructChildDocInParentProcess(ipcDoc, id,
                                                      AccessibleWrap::GetChildIDFor(childDoc));
 #else
       nsCOMPtr<nsITabChild> tabChild =
         do_GetInterface(mDocument->DocumentNode()->GetDocShell());
       if (tabChild) {
-        MOZ_ASSERT(parentIPCDoc);
         static_cast<TabChild*>(tabChild.get())->
           SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id, 0, 0);
       }
 #endif
     }
   }
 
   mObservingState = eRefreshObserving;
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -434,39 +434,59 @@ DocAccessibleParent::Destroy()
   DocManager::NotifyOfRemoteDocShutdown(this);
   ProxyDestroyed(this);
   if (mParentDoc)
     mParentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
+xpcAccessibleGeneric*
+DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
+{
+  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
+  MOZ_ASSERT(doc);
+
+  return doc->GetXPCAccessible(aProxy);
+}
+
 bool
-DocAccessibleParent::CheckDocTree() const
+DocAccessibleParent::CheckDocTreeInternal() const
 {
   size_t childDocs = mChildDocs.Length();
   for (size_t i = 0; i < childDocs; i++) {
     if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
       return false;
 
-    if (!mChildDocs[i]->CheckDocTree()) {
+    if (!mChildDocs[i]->CheckDocTreeInternal()) {
       return false;
     }
   }
 
   return true;
 }
 
-xpcAccessibleGeneric*
-DocAccessibleParent::GetXPCAccessible(ProxyAccessible* aProxy)
+const DocAccessibleParent*
+DocAccessibleParent::CheckTopDoc() const
 {
-  xpcAccessibleDocument* doc = GetAccService()->GetXPCDocument(this);
-  MOZ_ASSERT(doc);
+  const DocAccessibleParent* doc = this;
+  while (doc->ParentDoc()) {
+    doc = doc->ParentDoc();
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(doc->mTopLevel);
+  MOZ_DIAGNOSTIC_ASSERT(DocManager::TopLevelRemoteDocs()->Contains(doc));
 
-  return doc->GetXPCAccessible(aProxy);
+  return doc;
+}
+
+bool
+DocAccessibleParent::CheckDocTree() const
+{
+  return CheckTopDoc()->CheckDocTreeInternal();
 }
 
 #if defined(XP_WIN)
 /**
  * @param aCOMProxy COM Proxy to the document in the content process.
  */
 void
 DocAccessibleParent::SetCOMProxy(const RefPtr<IAccessible>& aCOMProxy)
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -82,19 +82,22 @@ public:
 
     mParent = nullptr;
   }
 
   virtual mozilla::ipc::IPCResult RecvShutdown() override;
   void Destroy();
   virtual void ActorDestroy(ActorDestroyReason aWhy) override
   {
+    if (mShutdown) {
+      return;
+    }
+
     MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
-    if (!mShutdown)
-      Destroy();
+    Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
   DocAccessibleParent* ParentDoc() const { return mParentDoc; }
 
@@ -172,18 +175,20 @@ private:
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
+  xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
   MOZ_MUST_USE bool CheckDocTree() const;
-  xpcAccessibleGeneric* GetXPCAccessible(ProxyAccessible* aProxy);
+  MOZ_MUST_USE bool CheckDocTreeInternal() const;
+  const DocAccessibleParent* CheckTopDoc() const;
 
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -29,8 +29,11 @@ if CONFIG['MOZ_XUL']:
     DIRS += ['xul']
 
 TEST_DIRS += ['tests/mochitest']
 
 BROWSER_CHROME_MANIFESTS += [
   'tests/browser/browser.ini',
   'tests/browser/e10s/browser.ini'
 ]
+
+with Files("**"):
+    BUG_COMPONENT = ("Core", "Disability Access APIs")
\ No newline at end of file
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -535,8 +535,11 @@ EXTRA_JS_MODULES.commonjs.sdk.worker += 
 EXTRA_JS_MODULES.commonjs.sdk.zip += [
     'source/lib/sdk/zip/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.toolkit += [
     'source/lib/toolkit/loader.js',
     'source/lib/toolkit/require.js',
 ]
+
+with Files("**"):
+    BUG_COMPONENT = ("Add-on SDK", "General")
\ No newline at end of file
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1156,16 +1156,19 @@ pref("browser.newtabpage.introShown", fa
 pref("browser.newtabpage.enabled", true);
 
 // Toggles the enhanced content of 'about:newtab'. Shows sponsored tiles.
 sticky_pref("browser.newtabpage.enhanced", true);
 
 // enables Activity Stream inspired layout
 pref("browser.newtabpage.compact", false);
 
+// enables showing basic placeholders for missing thumbnails
+pref("browser.newtabpage.thumbnailPlaceholder", false);
+
 // number of rows of newtab grid
 pref("browser.newtabpage.rows", 3);
 
 // number of columns of newtab grid
 pref("browser.newtabpage.columns", 5);
 
 // directory tiles download URL
 pref("browser.newtabpage.directory.source", "https://tiles.services.mozilla.com/v3/links/fetch/%LOCALE%/%CHANNEL%");
@@ -1296,16 +1299,19 @@ pref("identity.fxaccounts.remote.webchan
 // discovery is enabled.
 pref("identity.fxaccounts.contextParam", "fx_desktop_v3");
 
 // The URL we take the user to when they opt to "manage" their Firefox Account.
 // Note that this will always need to be in the same TLD as the
 // "identity.fxaccounts.remote.signup.uri" pref.
 pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings?service=sync&context=fx_desktop_v3");
 
+// The URL of the FxA device manager page
+pref("identity.fxaccounts.settings.devices.uri", "https://accounts.firefox.com/settings/clients?service=sync&context=fx_desktop_v3");
+
 // The remote URL of the FxA Profile Server
 pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
 
 // The remote URL of the FxA OAuth Server
 pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
 
 // Whether we display profile images in the UI or not.
 pref("identity.fxaccounts.profile_image.enabled", true);
--- a/browser/base/content/aboutNetError.xhtml
+++ b/browser/base/content/aboutNetError.xhtml
@@ -303,17 +303,17 @@
             // If it looks like an error that is user config based
             if (getErrorCode() == "nssFailure2" && hasPrefStyleError && options && options.changedCertPrefs) {
               showPrefChangeContainer();
             }
           }
           if (getErrorCode() == "weakCryptoUsed" || getErrorCode() == "sslv3Used") {
             setupAdvancedButton(getErrorCode() == "weakCryptoUsed");
           }
-        }.bind(this), true, true);
+        }, true, true);
 
         var event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
         document.dispatchEvent(event);
 
         if (err == "inadequateSecurityError") {
           // Remove the "Try again" button for HTTP/2 inadequate security as it
           // is useless.
           document.getElementById("errorTryAgain").style.display = "none";
--- a/browser/base/content/abouthome/aboutHome.js
+++ b/browser/base/content/abouthome/aboutHome.js
@@ -287,17 +287,16 @@ function loadSnippets() {
       loadCompleted();
     };
     try {
       xhr.open("GET", updateURL, true);
       xhr.send(null);
     } catch (ex) {
       showSnippets();
       loadCompleted();
-      return;
     }
   } else {
     showSnippets();
     loadCompleted();
   }
 }
 
 /**
--- a/browser/base/content/browser-data-submission-info-bar.js
+++ b/browser/base/content/browser-data-submission-info-bar.js
@@ -99,17 +99,16 @@ var gDataNotificationInfoBar = {
   observe(subject, topic, data) {
     switch (topic) {
       case "datareporting:notify-data-policy:request":
         let request = subject.wrappedJSObject.object;
         try {
           this._displayDataPolicyInfoBar(request);
         } catch (ex) {
           request.onUserNotifyFailed(ex);
-          return;
         }
         break;
 
       case "datareporting:notify-data-policy:close":
         // If this observer fires, it means something else took care of
         // responding. Therefore, we don't need to do anything. So, we
         // act like we took action and clear state.
         this._actionTaken = true;
--- a/browser/base/content/browser-fullScreenAndPointerLock.js
+++ b/browser/base/content/browser-fullScreenAndPointerLock.js
@@ -3,56 +3,45 @@
  * 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/. */
 
 var PointerlockFsWarning = {
 
   _element: null,
   _origin: null,
 
-  init() {
-    this.Timeout.prototype = {
-      start() {
-        this.cancel();
-        this._id = setTimeout(() => this._handle(), this._delay);
-      },
-      cancel() {
-        if (this._id) {
-          clearTimeout(this._id);
-          this._id = 0;
-        }
-      },
-      _handle() {
-        this._id = 0;
-        this._func();
-      },
-      get delay() {
-        return this._delay;
-      }
-    };
-  },
-
-  /* eslint-disable object-shorthand */
-  /* The object-shorthand rule must be disabled for this constructor
-   * because the ES6 method syntax causes "this.Timeout is not a
-   * constructor" exception. Further, using the {ignoreConstructors: true}
-   * option causes "TypeError: Cannot read property 'charAt' of undefined"
-   * in eslint.
-   */
   /**
    * Timeout object for managing timeout request. If it is started when
    * the previous call hasn't finished, it would automatically cancelled
    * the previous one.
    */
-  Timeout: function(func, delay) {
-    this._id = 0;
-    this._func = func;
-    this._delay = delay;
+  Timeout: class {
+    constructor(func, delay) {
+      this._id = 0;
+      this._func = func;
+      this._delay = delay;
+    }
+    start() {
+      this.cancel();
+      this._id = setTimeout(() => this._handle(), this._delay);
+    }
+    cancel() {
+      if (this._id) {
+        clearTimeout(this._id);
+        this._id = 0;
+      }
+    }
+    _handle() {
+      this._id = 0;
+      this._func();
+    }
+    get delay() {
+      return this._delay;
+    }
   },
-  /* eslint-enable object-shorthand */
 
   showPointerLock(aOrigin) {
     if (!document.fullscreen) {
       let timeout = gPrefService.getIntPref("pointer-lock-api.warning.timeout");
       this.show(aOrigin, "pointerlock-warning", timeout, 0);
     }
   },
 
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -67,17 +67,17 @@ var gPluginHandler = {
           this.submitReport(msg.data.runID, msg.data.keyVals, msg.data.submitURLOptIn);
         }
         break;
       case "PluginContent:LinkClickCallback":
         switch (msg.data.name) {
           case "managePlugins":
           case "openHelpPage":
           case "openPluginUpdatePage":
-            this[msg.data.name].call(this, msg.data.pluginTag);
+            this[msg.data.name](msg.data.pluginTag);
             break;
         }
         break;
       default:
         Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
         break;
     }
   },
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1346,17 +1346,16 @@ var gBrowserInit = {
       placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
     }
 
     LightWeightThemeWebInstaller.init();
 
     if (Win7Features)
       Win7Features.onOpenWindow();
 
-    PointerlockFsWarning.init();
     FullScreen.init();
     PointerLock.init();
 
     // initialize the sync UI
     gSyncUI.init();
     gFxAccounts.init();
 
     if (AppConstants.MOZ_DATA_REPORTING)
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -168,17 +168,18 @@ var gGrid = {
     let site = document.createElementNS(HTML_NAMESPACE, "div");
     site.classList.add("newtab-site");
     site.setAttribute("draggable", "true");
 
     // Create the site's inner HTML code.
     site.innerHTML =
       '<span class="newtab-sponsored">' + newTabString("sponsored.button") + '</span>' +
       '<a class="newtab-link">' +
-      '  <span class="newtab-thumbnail"/>' +
+      '  <span class="newtab-thumbnail placeholder"/>' +
+      '  <span class="newtab-thumbnail thumbnail"/>' +
       '  <span class="newtab-thumbnail enhanced-content"/>' +
       '  <span class="newtab-title"/>' +
       '</a>' +
       '<input type="button" title="' + newTabString("pin") + '"' +
       '       class="newtab-control newtab-control-pin"/>' +
       '<input type="button" title="' + newTabString("block") + '"' +
       '       class="newtab-control newtab-control-block"/>' +
       '<span class="newtab-suggested"/>';
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -1,14 +1,17 @@
 #ifdef 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/. */
 #endif
 
+const THUMBNAIL_PLACEHOLDER_ENABLED =
+  Services.prefs.getBoolPref("browser.newtabpage.thumbnailPlaceholder");
+
 /**
  * This class represents a site that is contained in a cell and can be pinned,
  * moved around or deleted.
  */
 function Site(aNode, aLink) {
   this._node = aNode;
   this._node._newtabSite = this;
 
@@ -239,24 +242,36 @@ Site.prototype = {
   /**
    * Refreshes the thumbnail for the site.
    */
   refreshThumbnail: function Site_refreshThumbnail() {
     // Only enhance tiles if that feature is turned on
     let link = gAllPages.enhanced && DirectoryLinksProvider.getEnhancedLink(this.link) ||
                this.link;
 
-    let thumbnail = this._querySelector(".newtab-thumbnail");
+    let thumbnail = this._querySelector(".newtab-thumbnail.thumbnail");
     if (link.bgColor) {
       thumbnail.style.backgroundColor = link.bgColor;
     }
-
     let uri = link.imageURI || PageThumbs.getThumbnailURL(this.url);
     thumbnail.style.backgroundImage = 'url("' + uri + '")';
 
+    if (THUMBNAIL_PLACEHOLDER_ENABLED &&
+        link.type == "history" &&
+        link.baseDomain) {
+      let placeholder = this._querySelector(".newtab-thumbnail.placeholder");
+      let hue = 0;
+      for (let c of link.baseDomain) {
+        hue += c.charCodeAt(0);
+      }
+      hue %= 256;
+      placeholder.style.backgroundColor = "hsl(" + hue + ",50%,60%)";
+      placeholder.textContent = link.baseDomain.substr(0,1);
+    }
+
     if (link.enhancedImageURI) {
       let enhanced = this._querySelector(".enhanced-content");
       enhanced.style.backgroundImage = 'url("' + link.enhancedImageURI + '")';
 
       if (this.link.type != link.type) {
         this.node.setAttribute("type", "enhanced");
         this.enhancedId = link.directoryId;
       }
--- a/browser/base/content/pageinfo/permissions.js
+++ b/browser/base/content/pageinfo/permissions.js
@@ -168,17 +168,19 @@ function onRadioClick(aPartId) {
   var radioGroup = document.getElementById(aPartId + "RadioGroup");
   var id = radioGroup.selectedItem.id;
   var permission = id.split('#')[1];
   SitePermissions.set(gPermURI, aPartId, permission);
 }
 
 function setRadioState(aPartId, aValue) {
   var radio = document.getElementById(aPartId + "#" + aValue);
-  radio.radioGroup.selectedItem = radio;
+  if (radio) {
+    radio.radioGroup.selectedItem = radio;
+  }
 }
 
 function initIndexedDBRow() {
   let row = document.getElementById("perm-indexedDB-row");
   let extras = document.getElementById("perm-indexedDB-extras");
 
   row.appendChild(extras);
 
--- a/browser/base/content/test/general/browser_bookmark_popup.js
+++ b/browser/base/content/test/general/browser_bookmark_popup.js
@@ -91,17 +91,16 @@ add_task(function* panel_shown_for_once_
     shouldAutoClose: true,
     isBookmarkRemoved: false,
   });
 });
 
 add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() {
   todo(false, "bug 1250267, may need to add some tracking state to " +
               "browser-places.js for this.");
-  return;
 
   /*
   yield test_bookmarks_popup({
     isNewBookmark: true,
     *popupShowFn() {
       EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
       yield new Promise(resolve => setTimeout(resolve, 300));
       EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
--- a/browser/base/content/test/general/browser_bug356571.js
+++ b/browser/base/content/test/general/browser_bug356571.js
@@ -57,17 +57,17 @@ var gProgressListener = {
       ok(gBrowser.tabs.length == kURIs.length, "Correctly opened all expected tabs");
       finishTest();
     }
   }
 }
 
 function test() {
   todo(false, "temp. disabled");
-  return; /* FIXME */
+  /* FIXME */
   /*
   waitForExplicitFinish();
   // Wait for all tabs to finish loading
   gBrowser.addTabsProgressListener(gProgressListener);
   loadOneOrMoreURIs(kURIs.join("|"));
   */
 }
 
--- a/browser/base/content/test/general/browser_bug537013.js
+++ b/browser/base/content/test/general/browser_bug537013.js
@@ -122,14 +122,14 @@ function continueTests3() {
 
 // Test that findbar gets restored when a tab is moved to a new window.
 function checkNewWindow() {
   ok(!newWindow.gFindBar.hidden, "New window shows find bar!");
   // Disabled the following assertion due to intermittent failure on OSX 10.6 Debug.
   if (!HasFindClipboard) {
     is(newWindow.gFindBar._findField.value, texts[1],
        "New window find bar has correct find value!");
+    ok(!newWindow.gFindBar.getElement("find-next").disabled,
+       "New window findbar has disabled buttons!");
   }
-  ok(newWindow.gFindBar.getElement("find-next").disabled,
-     "New window findbar has disabled buttons!");
   newWindow.close();
   finish();
 }
--- a/browser/base/content/test/general/browser_contextmenu.js
+++ b/browser/base/content/test/general/browser_contextmenu.js
@@ -369,17 +369,16 @@ add_task(function* test_image_in_iframe(
           "context-viewframeinfo",     true], null]
   );
 });
 
 add_task(function* test_textarea() {
   // Disabled since this is seeing spell-check-enabled
   // instead of spell-add-dictionaries-main
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-textarea",
     ["context-undo",                false,
      "---",                         null,
      "context-cut",                 true,
      "context-copy",                true,
      "context-paste",               null,
@@ -393,17 +392,16 @@ add_task(function* test_textarea() {
       skipFocusChange: true,
     }
   );
   */
 });
 
 add_task(function* test_textarea_spellcheck() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-textarea",
     ["*chubbiness",         true, // spelling suggestion
      "spell-add-to-dictionary", true,
      "---",                 null,
      "context-undo",        false,
      "---",                 null,
@@ -433,17 +431,16 @@ add_task(function* test_textarea_spellch
 });
 
 add_task(function* test_plaintext2() {
   yield test_contextmenu("#test-text", plainTextItems);
 });
 
 add_task(function* test_undo_add_to_dictionary() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-textarea",
     ["spell-undo-add-to-dictionary", true,
      "---",                 null,
      "context-undo",        false,
      "---",                 null,
      "context-cut",         true,
@@ -467,17 +464,16 @@ add_task(function* test_undo_add_to_dict
       }
     }
   );
   */
 });
 
 add_task(function* test_contenteditable() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-contenteditable",
     ["spell-no-suggestions", false,
      "spell-add-to-dictionary", true,
      "---",                 null,
      "context-undo",        false,
      "---",                 null,
@@ -721,17 +717,16 @@ add_task(function* test_imagelink() {
      "context-setDesktopBackground", true,
      "context-viewimageinfo",        true
     ]
   );
 });
 
 add_task(function* test_select_input_text() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-select-input-text",
     ["context-undo",         false,
      "---",                  null,
      "context-cut",          true,
      "context-copy",         true,
      "context-paste",        null, // ignore clipboard state
@@ -754,17 +749,16 @@ add_task(function* test_select_input_tex
       }
     }
   );
   */
 });
 
 add_task(function* test_select_input_text_password() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-select-input-text-type-password",
     ["context-undo",        false,
      "---",                 null,
      "context-cut",         true,
      "context-copy",        true,
      "context-paste",       null, // ignore clipboard state
@@ -873,17 +867,16 @@ add_task(function* test_srcdoc() {
      "context-viewsource",   true,
      "context-viewinfo",     true
     ]
   );
 });
 
 add_task(function* test_input_spell_false() {
   todo(false, "spell checker tests are failing, bug 1246296");
-  return;
 
   /*
   yield test_contextmenu("#test-contenteditable-spellcheck-false",
     ["context-undo",        false,
      "---",                 null,
      "context-cut",         true,
      "context-copy",        true,
      "context-paste",       null, // ignore clipboard state
--- a/browser/base/content/test/newtab/browser_newtab_bug1145428.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug1145428.js
@@ -26,17 +26,17 @@ add_task(function* () {
 
   function getData(cellNum) {
     return performOnCell(cellNum, cell => {
       if (!cell.site)
         return null;
       let siteNode = cell.site.node;
       return {
         type: siteNode.getAttribute("type"),
-        thumbnail: siteNode.querySelector(".newtab-thumbnail").style.backgroundImage,
+        thumbnail: siteNode.querySelector(".newtab-thumbnail.thumbnail").style.backgroundImage,
         enhanced: siteNode.querySelector(".enhanced-content").style.backgroundImage,
         title: siteNode.querySelector(".newtab-title").textContent,
         suggested: siteNode.getAttribute("suggested"),
         url: siteNode.querySelector(".newtab-link").getAttribute("href"),
       };
     });
   }
 
--- a/browser/base/content/test/newtab/browser_newtab_bug1178586.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug1178586.js
@@ -24,17 +24,17 @@ add_task(function* () {
 
   function getData(cellNum) {
     return performOnCell(cellNum, cell => {
       if (!cell.site)
         return null;
       let siteNode = cell.site.node;
       return {
         type: siteNode.getAttribute("type"),
-        thumbnail: siteNode.querySelector(".newtab-thumbnail").style.backgroundImage,
+        thumbnail: siteNode.querySelector(".newtab-thumbnail.thumbnail").style.backgroundImage,
         enhanced: siteNode.querySelector(".enhanced-content").style.backgroundImage,
         title: siteNode.querySelector(".newtab-title").textContent,
         suggested: siteNode.getAttribute("suggested"),
         url: siteNode.querySelector(".newtab-link").getAttribute("href"),
       };
     });
   }
 
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -486,17 +486,16 @@ function whenSearchInitDone() {
       let eventName = "ContentSearchService";
       content.addEventListener(eventName, function onEvent(event) {
         if (event.detail.type == "State") {
           content.removeEventListener(eventName, onEvent);
           let resolver = function() {
             // Wait for the search controller to receive the event, then resolve.
             if (content.gSearch._contentSearchController.defaultEngine) {
               resolve();
-              return;
             }
           }
           content.setTimeout(resolver, 0);
         }
       });
     });
   });
 }
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -912,17 +912,16 @@ const CustomizableWidgets = [
       let doc = aEvent.target.ownerDocument;
       let container = doc.getElementById("PanelUI-feeds");
       let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true);
 
       // For no feeds or only a single one, don't show the panel.
       if (!gotView) {
         aEvent.preventDefault();
         aEvent.stopPropagation();
-        return;
       }
     },
     onCreated(node) {
       let win = node.ownerGlobal;
       let selectedBrowser = win.gBrowser.selectedBrowser;
       let feeds = selectedBrowser && selectedBrowser.feeds;
       if (!feeds || !feeds.length) {
         node.setAttribute("disabled", "true");
--- a/browser/components/downloads/content/downloads.js
+++ b/browser/components/downloads/content/downloads.js
@@ -715,17 +715,17 @@ XPCOMUtils.defineConstant(this, "Downloa
  */
 const DownloadsView = {
   //////////////////////////////////////////////////////////////////////////////
   //// Functions handling download items in the list
 
   /**
    * Maximum number of items shown by the list at any given time.
    */
-  kItemCountLimit: 3,
+  kItemCountLimit: 5,
 
   /**
    * Indicates whether we are still loading downloads data asynchronously.
    */
   loading: false,
 
   /**
    * Ordered array of all Download objects.  We need to keep this array because
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -382,21 +382,23 @@ extensions.registerSchemaAPI("tabs", "ad
         AllWindowEvents.addListener("TabOpen", openListener);
         return () => {
           AllWindowEvents.removeListener("TabMove", moveListener);
           AllWindowEvents.removeListener("TabOpen", openListener);
         };
       }).api(),
 
       onUpdated: new EventManager(context, "tabs.onUpdated", fire => {
+        const restricted = ["url", "favIconUrl", "title"];
+
         function sanitize(extension, changeInfo) {
           let result = {};
           let nonempty = false;
           for (let prop in changeInfo) {
-            if ((prop != "favIconUrl" && prop != "url") || extension.hasPermission("tabs")) {
+            if (extension.hasPermission("tabs") || !restricted.includes(prop)) {
               nonempty = true;
               result[prop] = changeInfo[prop];
             }
           }
           return [nonempty, result];
         }
 
         let fireForBrowser = (browser, changed) => {
@@ -418,24 +420,27 @@ extensions.registerSchemaAPI("tabs", "ad
               needed.push("favIconUrl");
             }
             if (changed.includes("muted")) {
               needed.push("mutedInfo");
             }
             if (changed.includes("soundplaying")) {
               needed.push("audible");
             }
+            if (changed.includes("label")) {
+              needed.push("title");
+            }
           } else if (event.type == "TabPinned") {
             needed.push("pinned");
           } else if (event.type == "TabUnpinned") {
             needed.push("pinned");
           }
 
           if (needed.length && !extension.hasPermission("tabs")) {
-            needed = needed.filter(attr => attr != "url" && attr != "favIconUrl");
+            needed = needed.filter(attr => !restricted.includes(attr));
           }
 
           if (needed.length) {
             let tab = TabManager.convert(extension, event.originalTarget);
 
             let changeInfo = {};
             for (let prop of needed) {
               changeInfo[prop] = tab[prop];
@@ -839,16 +844,21 @@ extensions.registerSchemaAPI("tabs", "ad
         if (details.matchAboutBlank) {
           options.match_about_blank = details.matchAboutBlank;
         }
         if (details.runAt !== null) {
           options.run_at = details.runAt;
         } else {
           options.run_at = "document_idle";
         }
+        if (details.cssOrigin !== null) {
+          options.css_origin = details.cssOrigin;
+        } else {
+          options.css_origin = "author";
+        }
 
         return tabListener.awaitTabReady(tab).then(() => {
           let browser = tab.linkedBrowser;
           let recipient = {
             innerWindowID: browser.innerWindowID,
           };
 
           return context.sendMessage(browser.messageManager, "Extension:Execute", {options}, {recipient});
--- a/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js
+++ b/browser/components/extensions/test/browser/browser_ext_popup_sendMessage.js
@@ -18,17 +18,17 @@ add_task(function* test_popup_sendMessag
       },
     },
 
     files: {
       "popup.html": scriptPage("popup.js"),
       "popup.js": async function() {
         browser.runtime.onMessage.addListener(async msg => {
           if (msg == "popup-ping") {
-            return Promise.resolve("popup-pong");
+            return "popup-pong";
           }
         });
 
         let response = await browser.runtime.sendMessage("background-ping");
         browser.test.sendMessage("background-ping-response", response);
       },
     },
 
--- a/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_insertCSS.js
@@ -25,16 +25,43 @@ add_task(function* testExecuteScript() {
         background: "rgb(42, 42, 42)",
         foreground: "rgb(0, 113, 4)",
         promise: () => {
           return browser.tabs.insertCSS({
             code: "* { background: rgb(42, 42, 42) }",
           });
         },
       },
+      {
+        background: "rgb(42, 42, 42)",
+        foreground: "rgb(0, 113, 4)",
+        promise: () => {
+          return browser.tabs.insertCSS({
+            code: "* { background: rgb(100, 100, 100) !important }",
+            cssOrigin: "author",
+          }).then(r => browser.tabs.insertCSS({
+            code: "* { background: rgb(42, 42, 42) !important }",
+            cssOrigin: "author",
+          }));
+        },
+      },
+      {
+        background: "rgb(100, 100, 100)",
+        foreground: "rgb(0, 113, 4)",
+        promise: () => {
+          // User has higher importance
+          return browser.tabs.insertCSS({
+            code: "* { background: rgb(100, 100, 100) !important }",
+            cssOrigin: "user",
+          }).then(r => browser.tabs.insertCSS({
+            code: "* { background: rgb(42, 42, 42) !important }",
+            cssOrigin: "author",
+          }));
+        },
+      },
     ];
 
     function checkCSS() {
       let computedStyle = window.getComputedStyle(document.body);
       return [computedStyle.backgroundColor, computedStyle.color];
     }
 
     try {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_onUpdated.js
@@ -89,17 +89,17 @@ add_task(function* () {
 
 function* do_test_update(background, withPermissions = true) {
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
 
   yield focusWindow(win1);
 
   let manifest = {};
   if (withPermissions) {
-    manifest.permissions = ["tabs"];
+    manifest.permissions = ["tabs", "http://mochi.test/"];
   }
   let extension = ExtensionTestUtils.loadExtension({manifest, background});
 
   yield Promise.all([
     yield extension.startup(),
     yield extension.awaitFinish("finish"),
   ]);
 
@@ -117,17 +117,16 @@ add_task(function* test_pinned() {
         browser.test.assertEq(tabId, tab.id, "Check tab id");
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("pinned" in changeInfo) {
           browser.test.assertTrue(changeInfo.pinned, "Check changeInfo.pinned");
           browser.tabs.onUpdated.removeListener(onUpdated);
           // Remove created tab.
           browser.tabs.remove(tabId);
           browser.test.notifyPass("finish");
-          return;
         }
       });
       browser.tabs.update(tab.id, {pinned: true});
     });
   });
 });
 
 add_task(function* test_unpinned() {
@@ -139,17 +138,16 @@ add_task(function* test_unpinned() {
         browser.test.assertEq(tabId, tab.id, "Check tab id");
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("pinned" in changeInfo) {
           browser.test.assertFalse(changeInfo.pinned, "Check changeInfo.pinned");
           browser.tabs.onUpdated.removeListener(onUpdated);
           // Remove created tab.
           browser.tabs.remove(tabId);
           browser.test.notifyPass("finish");
-          return;
         }
       });
       browser.tabs.update(tab.id, {pinned: false});
     });
   });
 });
 
 add_task(function* test_url() {
@@ -162,37 +160,65 @@ add_task(function* test_url() {
         browser.test.log("onUpdate: " + JSON.stringify(changeInfo));
         if ("url" in changeInfo) {
           browser.test.assertEq("about:blank", changeInfo.url,
                                 "Check changeInfo.url");
           browser.tabs.onUpdated.removeListener(onUpdated);
           // Remove created tab.
           browser.tabs.remove(tabId);
           browser.test.notifyPass("finish");
-          return;
         }
       });
       browser.tabs.update(tab.id, {url: "about:blank"});
     });
   });
 });
 
-add_task(function* test_without_tabs_permission() {
-  yield do_test_update(function background() {
-    browser.tabs.create({url: "about:blank"}, function(tab) {
-      browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
-        if (tabId == tab.id) {
-          browser.test.assertFalse("url" in changeInfo, "url should not be included without tabs permission");
-          browser.test.assertFalse("favIconUrl" in changeInfo, "favIconUrl should not be included without tabs permission");
+add_task(function* test_title() {
+  yield do_test_update(async function background() {
+    const url = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
+    const tab = await browser.tabs.create({url});
+
+    browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
+      browser.test.assertEq(tabId, tab.id, "Check tab id");
+      browser.test.log(`onUpdated: ${JSON.stringify(changeInfo)}`);
+      if ("title" in changeInfo && changeInfo.title === "New Message (1)") {
+        browser.test.log("changeInfo.title is correct");
+        browser.tabs.onUpdated.removeListener(onUpdated);
+        browser.tabs.remove(tabId);
+        browser.test.notifyPass("finish");
+      }
+    });
+
+    browser.tabs.executeScript(tab.id, {code: "document.title = 'New Message (1)'"});
+  });
+});
 
-          if (changeInfo.status == "complete") {
-            browser.tabs.onUpdated.removeListener(onUpdated);
-            browser.tabs.remove(tabId);
-            browser.test.notifyPass("finish");
-          }
+add_task(function* test_without_tabs_permission() {
+  yield do_test_update(async function background() {
+    const url = "http://mochi.test:8888/browser/browser/components/extensions/test/browser/context_tabs_onUpdated_page.html";
+    const tab = await browser.tabs.create({url});
+    let count = 0;
+
+    browser.tabs.onUpdated.addListener(function onUpdated(tabId, changeInfo) {
+      browser.test.assertEq(tabId, tab.id, "Check tab id");
+      browser.test.log(`onUpdated: ${JSON.stringify(changeInfo)}`);
+
+      browser.test.assertFalse("url" in changeInfo, "url should not be included without tabs permission");
+      browser.test.assertFalse("favIconUrl" in changeInfo, "favIconUrl should not be included without tabs permission");
+      browser.test.assertFalse("title" in changeInfo, "title should not be included without tabs permission");
+
+      if (changeInfo.status == "complete") {
+        count++;
+        if (count === 2) {
+          browser.test.log("Reload complete");
+          browser.tabs.onUpdated.removeListener(onUpdated);
+          browser.tabs.remove(tabId);
+          browser.test.notifyPass("finish");
         }
-      });
-      browser.tabs.reload(tab.id);
+      }
     });
+
+    browser.tabs.reload(tab.id);
   }, false /* withPermissions */);
 });
 
 add_task(forceGC);
--- a/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_removeCSS.js
@@ -42,16 +42,38 @@ add_task(function* testExecuteScript() {
         background: "transparent",
         foreground: "rgb(0, 0, 0)",
         promise: () => {
           return browser.tabs.removeCSS({
             file: "file2.css",
           });
         },
       },
+      // Insert CSS code.
+      {
+        background: "rgb(42, 42, 42)",
+        foreground: "rgb(0, 0, 0)",
+        promise: () => {
+          return browser.tabs.insertCSS({
+            code: "* { background: rgb(42, 42, 42) }",
+            cssOrigin: "user",
+          });
+        },
+      },
+      // Remove CSS code again.
+      {
+        background: "transparent",
+        foreground: "rgb(0, 0, 0)",
+        promise: () => {
+          return browser.tabs.removeCSS({
+            code: "* { background: rgb(42, 42, 42) }",
+            cssOrigin: "user",
+          });
+        },
+      },
     ];
 
     function checkCSS() {
       let computedStyle = window.getComputedStyle(document.body);
       return [computedStyle.backgroundColor, computedStyle.color];
     }
 
     try {
--- a/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_sendMessage.js
@@ -77,17 +77,17 @@ add_task(function* tabsSendMessageReply(
           if (msg == "respond-now") {
             respond(msg);
           } else if (msg == "respond-soon") {
             setTimeout(() => { respond(msg); }, 0);
             return true;
           } else if (msg == "respond-promise") {
             return Promise.resolve(msg);
           } else if (msg == "respond-never") {
-            return;
+            return undefined;
           } else if (msg == "respond-error") {
             return Promise.reject(new Error(msg));
           } else if (msg == "throw-error") {
             throw new Error(msg);
           }
         });
 
         browser.runtime.onMessage.addListener((msg, sender, respond) => {
--- a/browser/components/migration/ChromeProfileMigrator.js
+++ b/browser/components/migration/ChromeProfileMigrator.js
@@ -288,18 +288,18 @@ function GetBookmarksResource(aProfileFo
             parentGuid =
               yield MigrationUtils.createImportedBookmarksFolder("Chrome", parentGuid);
           }
           yield insertBookmarkItems(parentGuid, roots.other.children, errorGatherer);
         }
         if (gotErrors) {
           throw new Error("The migration included errors.");
         }
-      }.bind(this)).then(() => aCallback(true),
-                         () => aCallback(false));
+      }).then(() => aCallback(true),
+              () => aCallback(false));
     }
   };
 }
 
 function GetHistoryResource(aProfileFolder) {
   let historyFile = aProfileFolder.clone();
   historyFile.append("History");
   if (!historyFile.exists())
--- a/browser/components/migration/SafariProfileMigrator.js
+++ b/browser/components/migration/SafariProfileMigrator.js
@@ -323,17 +323,17 @@ SearchStrings.prototype = {
           if (recentSearchStrings && recentSearchStrings.length > 0) {
             let changes = recentSearchStrings.map((searchString) => (
               {op: "add",
                fieldname: "searchbar-history",
                value: searchString}));
             FormHistory.update(changes);
           }
         }
-      }.bind(this), aCallback));
+      }, aCallback));
   }
 };
 
 function SafariProfileMigrator() {
 }
 
 SafariProfileMigrator.prototype = Object.create(MigratorPrototype);
 
--- a/browser/components/newtab/tests/browser/browser_newtab_overrides.js
+++ b/browser/components/newtab/tests/browser/browser_newtab_overrides.js
@@ -130,10 +130,10 @@ add_task(function* override_blank_loads_
     yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
   }
 });
 
 function nextChangeNotificationPromise(aNewURL, testMessage) {
   return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) {  // jshint unused:false
       Assert.equal(aData, aNewURL, testMessage);
       return true;
-  }.bind(this));
+  });
 }
--- a/browser/components/newtab/tests/browser/browser_newtabwebchannel.js
+++ b/browser/components/newtab/tests/browser/browser_newtabwebchannel.js
@@ -86,28 +86,28 @@ add_task(function* webchannel_broadcast(
   let countingMessagePromise = new Promise(resolve => {
     let count = 0;
     NewTabWebChannel.on("foo", function test_message(name, msg) {
       count += 1;
       if (count === 2) {
         NewTabWebChannel.off("foo", test_message);
         resolve(msg.target);
       }
-    }.bind(this));
+    });
   });
 
   let countingReplyPromise = new Promise(resolve => {
     let count = 0;
     NewTabWebChannel.on("reply", function test_message(name, msg) {
       count += 1;
       if (count === 2) {
         NewTabWebChannel.off("reply", test_message);
         resolve(msg.target);
       }
-    }.bind(this));
+    });
   });
 
   let countingUnloadPromise = new Promise(resolve => {
     let count = 0;
     NewTabWebChannel.on("targetUnload", function test_message() {
       count += 1;
       if (count === 2) {
         NewTabWebChannel.off("targetUnload", test_message);
@@ -142,17 +142,17 @@ add_task(function* webchannel_broadcast(
  */
 add_task(function* webchannel_switch() {
   setup();
 
   function newMessagePromise() {
     return new Promise(resolve => {
       NewTabWebChannel.once("foo", function(name, msg) {
         resolve(msg.target);
-      }.bind(this));
+      });
     });
   }
 
   let replyCount = 0;
   function newReplyPromise() {
     return new Promise(resolve => {
       NewTabWebChannel.on("reply", function() {
         replyCount += 1;
--- a/browser/components/newtab/tests/browser/browser_remotenewtab_pageloads.js
+++ b/browser/components/newtab/tests/browser/browser_remotenewtab_pageloads.js
@@ -43,10 +43,10 @@ add_task(function* open_newtab() {
   });
   yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 function nextChangeNotificationPromise(aNewURL, testMessage) {
   return TestUtils.topicObserved("newtab-url-changed", function observer(aSubject, aData) {  // jshint unused:false
       Assert.equal(aData, aNewURL, testMessage);
       return true;
-  }.bind(this));
+  });
 }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -246,16 +246,19 @@ BrowserGlue.prototype = {
         }
         break;
       case "weave:service:ready":
         this._setSyncAutoconnectDelay();
         break;
       case "fxaccounts:onverified":
         this._showSyncStartedDoorhanger();
         break;
+      case "fxaccounts:device_connected":
+        this._onDeviceConnected(data);
+        break;
       case "fxaccounts:device_disconnected":
         this._onDeviceDisconnected();
         break;
       case "weave:engine:clients:display-uris":
         this._onDisplaySyncURIs(subject);
         break;
       case "session-save":
         this._setPrefToSaveSession(true);
@@ -403,16 +406,17 @@ BrowserGlue.prototype = {
     os.addObserver(this, "quit-application-requested", false);
     os.addObserver(this, "quit-application-granted", false);
     if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
       os.addObserver(this, "browser-lastwindow-close-requested", false);
       os.addObserver(this, "browser-lastwindow-close-granted", false);
     }
     os.addObserver(this, "weave:service:ready", false);
     os.addObserver(this, "fxaccounts:onverified", false);
+    os.addObserver(this, "fxaccounts:device_connected", false);
     os.addObserver(this, "fxaccounts:device_disconnected", false);
     os.addObserver(this, "weave:engine:clients:display-uris", false);
     os.addObserver(this, "session-save", false);
     os.addObserver(this, "places-init-complete", false);
     this._isPlacesInitObserver = true;
     os.addObserver(this, "places-database-locked", false);
     this._isPlacesLockedObserver = true;
     os.addObserver(this, "distribution-customization-complete", false);
@@ -455,16 +459,17 @@ BrowserGlue.prototype = {
     os.removeObserver(this, "quit-application-granted");
     os.removeObserver(this, "restart-in-safe-mode");
     if (OBSERVE_LASTWINDOW_CLOSE_TOPICS) {
       os.removeObserver(this, "browser-lastwindow-close-requested");
       os.removeObserver(this, "browser-lastwindow-close-granted");
     }
     os.removeObserver(this, "weave:service:ready");
     os.removeObserver(this, "fxaccounts:onverified");
+    os.removeObserver(this, "fxaccounts:device_connected");
     os.removeObserver(this, "fxaccounts:device_disconnected");
     os.removeObserver(this, "weave:engine:clients:display-uris");
     os.removeObserver(this, "session-save");
     if (this._bookmarksBackupIdleTime) {
       this._idleService.removeIdleObserver(this, this._bookmarksBackupIdleTime);
       delete this._bookmarksBackupIdleTime;
     }
     if (this._isPlacesInitObserver)
@@ -1156,17 +1161,17 @@ BrowserGlue.prototype = {
                           .add(shouldCheck);
         Services.telemetry.getHistogramById("BROWSER_SET_DEFAULT_DIALOG_PROMPT_RAWCOUNT")
                           .add(promptCount);
       } catch (ex) { /* Don't break the default prompt if telemetry is broken. */ }
 
       if (willPrompt) {
         Services.tm.mainThread.dispatch(function() {
           DefaultBrowserCheck.prompt(RecentWindow.getMostRecentBrowserWindow());
-        }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
+        }, Ci.nsIThread.DISPATCH_NORMAL);
       }
     }
 
     E10SAccessibilityCheck.onWindowsRestored();
   },
 
   _createExtraDefaultProfile() {
     if (!AppConstants.MOZ_DEV_EDITION) {
@@ -2325,16 +2330,42 @@ BrowserGlue.prototype = {
         imageURL = "chrome://branding/content/icon64.png";
       }
       AlertsService.showAlertNotification(imageURL, title, body, true, null, clickCallback);
     } catch (ex) {
       Cu.reportError("Error displaying tab(s) received by Sync: " + ex);
     }
   },
 
+  _onDeviceConnected(deviceName) {
+    let accountsBundle = Services.strings.createBundle(
+      "chrome://browser/locale/accounts.properties"
+    );
+    let title = accountsBundle.GetStringFromName("deviceConnectedTitle");
+    let body = accountsBundle.formatStringFromName("deviceConnectedBody", [deviceName], 1);
+    let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.settings.devices.uri");
+
+    function clickCallback(subject, topic, data) {
+      if (topic != "alertclickcallback")
+        return;
+      let win = RecentWindow.getMostRecentBrowserWindow({private: false});
+      if (!win) {
+        Services.appShell.hiddenDOMWindow.open(url);
+      } else {
+        win.gBrowser.addTab(url);
+      }
+    }
+
+    try {
+      AlertsService.showAlertNotification(null, title, body, true, url, clickCallback);
+    } catch (ex) {
+      Cu.reportError("Error notifying of a new Sync device: " + ex);
+    }
+  },
+
   _onDeviceDisconnected() {
     let bundle = Services.strings.createBundle("chrome://browser/locale/accounts.properties");
     let title = bundle.GetStringFromName("deviceDisconnectedNotification.title");
     let body = bundle.GetStringFromName("deviceDisconnectedNotification.body");
 
     let clickCallback = (subject, topic, data) => {
       if (topic != "alertclickcallback")
         return;
--- a/browser/components/places/content/sidebarUtils.js
+++ b/browser/components/places/content/sidebarUtils.js
@@ -37,17 +37,16 @@ var SidebarUtils = {
     var isContainer = tbo.view.isContainer(cell.row);
     var openInTabs = isContainer &&
                      (aEvent.button == 1 ||
                       (aEvent.button == 0 && modifKey)) &&
                      PlacesUtils.hasChildURIs(tbo.view.nodeForTreeIndex(cell.row));
 
     if (aEvent.button == 0 && isContainer && !openInTabs) {
       tbo.view.toggleOpenState(cell.row);
-      return;
     } else if (!mouseInGutter && openInTabs &&
             aEvent.originalTarget.localName == "treechildren") {
       tbo.view.selection.select(cell.row);
       PlacesUIUtils.openContainerNodeInTabs(aTree.selectedNode, aEvent, aTree);
     } else if (!mouseInGutter && !isContainer &&
              aEvent.originalTarget.localName == "treechildren") {
       // Clear all other selection since we're loading a link now. We must
       // do this *before* attempting to load the link since openURL uses
--- a/browser/components/places/tests/browser/browser_bookmarksProperties.js
+++ b/browser/components/places/tests/browser/browser_bookmarksProperties.js
@@ -116,17 +116,16 @@ gTests.push({
             is(tree.view.selection.count, 1,
                "We have selected a tag from the autocomplete popup");
             info("About to focus the autocomplete results tree");
             tree.focus();
             EventUtils.synthesizeKey("VK_RETURN", {}, self.window);
             break;
           default:
             ok(false, "unknown event: " + aEvent.type);
-            return;
         }
       }
     };
     tagsField.popup.addEventListener("popupshown", popupListener, true);
     tagsField.popup.addEventListener("popuphidden", popupListener, true);
 
     // Open tags autocomplete popup.
     info("About to focus the tagsField");
@@ -218,17 +217,16 @@ gTests.push({
             is(tree.view.selection.count, 1,
                "We have selected a tag from the autocomplete popup");
             info("About to focus the autocomplete results tree");
             tree.focus();
             EventUtils.synthesizeKey("VK_ESCAPE", {}, self.window);
             break;
           default:
             ok(false, "unknown event: " + aEvent.type);
-            return;
         }
       }
     };
     tagsField.popup.addEventListener("popupshown", popupListener, true);
     tagsField.popup.addEventListener("popuphidden", popupListener, true);
 
     // Open tags autocomplete popup.
     info("About to focus the tagsField");
--- a/browser/components/preferences/blocklists.js
+++ b/browser/components/preferences/blocklists.js
@@ -82,23 +82,16 @@ var gBlocklistManager = {
     let blocklistsText = document.getElementById("blocklistsText");
     while (blocklistsText.hasChildNodes()) {
       blocklistsText.removeChild(blocklistsText.firstChild);
     }
     blocklistsText.appendChild(document.createTextNode(params.introText));
 
     document.title = params.windowTitle;
 
-    let treecols = document.getElementsByTagName("treecols")[0];
-    treecols.addEventListener("click", event => {
-      if (event.target.nodeName != "treecol" || event.button != 0) {
-        return;
-      }
-    });
-
     this._loadBlockLists();
   },
 
   uninit() {},
 
   onListSelected() {
     for (let list of this._blockLists) {
       list.selected = false;
--- a/browser/components/preferences/in-content/main.js
+++ b/browser/components/preferences/in-content/main.js
@@ -192,17 +192,16 @@ var gMainPane = {
             return;
           }
 
           // Revert the checkbox in case we didn't quit
           revertCheckbox();
           return;
         case CONFIRM_RESTART_PROMPT_RESTART_LATER:
           createOrRemoveSpecialDevEditionFile();
-          return;
       }
     }
   },
 
   onGetStarted(aEvent) {
     if (AppConstants.MOZ_DEV_EDITION) {
       const Cc = Components.classes, Ci = Components.interfaces;
       let wm = Cc["@mozilla.org/appshell/window-mediator;1"]
--- a/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
+++ b/browser/components/preferences/in-content/tests/browser_advanced_siteData.js
@@ -28,16 +28,68 @@ const mockOfflineAppCacheHelper = {
     OfflineAppCacheHelper.clear = this.clear;
   },
 
   unregister() {
     OfflineAppCacheHelper.clear = this.originalClear;
   }
 };
 
+const mockSiteDataManager = {
+  sites: new Map([
+    [
+      "https://shopping.xyz.com/",
+      {
+        usage: 102400,
+        host: "shopping.xyz.com",
+        status: Ci.nsIPermissionManager.ALLOW_ACTION
+      }
+    ],
+    [
+      "https://music.bar.com/",
+      {
+        usage: 10240,
+        host: "music.bar.com",
+        status: Ci.nsIPermissionManager.ALLOW_ACTION
+      }
+    ],
+    [
+      "https://news.foo.com/",
+      {
+        usage: 1024,
+        host: "news.foo.com",
+        status: Ci.nsIPermissionManager.DENY_ACTION
+      }
+    ]
+  ]),
+
+  _originalGetSites: null,
+
+  getSites() {
+    let list = [];
+    this.sites.forEach((data, origin) => {
+      list.push({
+        usage: data.usage,
+        status: data.status,
+        uri: NetUtil.newURI(origin)
+      });
+    });
+    return Promise.resolve(list);
+  },
+
+  register() {
+    this._originalGetSites = SiteDataManager.getSites;
+    SiteDataManager.getSites = this.getSites.bind(this);
+  },
+
+  unregister() {
+    SiteDataManager.getSites = this._originalGetSites;
+  }
+};
+
 function addPersistentStoragePerm(origin) {
   let uri = NetUtil.newURI(origin);
   let principal = Services.scriptSecurityManager.createCodebasePrincipal(uri, {});
   Services.perms.addFromPrincipal(principal, "persistent-storage", Ci.nsIPermissionManager.ALLOW_ACTION);
 }
 
 function getPersistentStoragePermStatus(origin) {
   let uri = NetUtil.newURI(origin);
@@ -86,17 +138,17 @@ registerCleanupFunction(function() {
 });
 
 add_task(function* () {
   yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
   addPersistentStoragePerm(TEST_ORIGIN);
 
   yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_BASE_URL + "site_data_test.html");
   yield waitForEvent(gBrowser.selectedBrowser.contentWindow, "test-indexedDB-done");
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 
   yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
 
   // Test the initial states
   let cacheUsage = yield getCacheUsage();
   let quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
   let totalUsage = yield SiteDataManager.getTotalUsage();
   Assert.greater(cacheUsage, 0, "The cache usage should not be 0");
@@ -146,10 +198,109 @@ add_task(function* () {
   cacheUsage = yield getCacheUsage();
   quotaUsage = yield getQuotaUsage(TEST_ORIGIN);
   totalUsage = yield SiteDataManager.getTotalUsage();
   is(cacheUsage, 0, "The cahce usage should be removed");
   is(quotaUsage, 0, "The quota usage should be removed");
   is(totalUsage, 0, "The total usage should be removed");
   // Test accepting "Clear All Data" ends
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
+
+add_task(function* () {
+  yield SpecialPowers.pushPrefEnv({set: [["browser.storageManager.enabled", true]]});
+
+  mockSiteDataManager.register();
+  let updatePromise = promiseSitesUpdated();
+  yield openPreferencesViaOpenPreferencesAPI("advanced", "networkTab", { leaveOpen: true });
+  yield updatePromise;
+
+  // Open the siteDataSettings subdialog
+  let doc = gBrowser.selectedBrowser.contentDocument;
+  let settingsBtn = doc.getElementById("siteDataSettings");
+  let dialogOverlay = doc.getElementById("dialogOverlay");
+  let dialogPromise = promiseLoadSubDialog("chrome://browser/content/preferences/siteDataSettings.xul");
+  settingsBtn.doCommand();
+  yield dialogPromise;
+  is(dialogOverlay.style.visibility, "visible", "The dialog should be visible");
+
+  let dialogFrame = doc.getElementById("dialogFrame");
+  let frameDoc = dialogFrame.contentDocument;
+  let hostCol = frameDoc.getElementById("hostCol");
+  let usageCol = frameDoc.getElementById("usageCol");
+  let statusCol = frameDoc.getElementById("statusCol");
+  let sitesList = frameDoc.getElementById("sitesList");
+  let mockSites = mockSiteDataManager.sites;
+
+  // Test default sorting
+  assertSortByHost("ascending");
+
+  // Test sorting on the host column
+  hostCol.click();
+  assertSortByHost("descending");
+  hostCol.click();
+  assertSortByHost("ascending");
+
+  // Test sorting on the permission status column
+  statusCol.click();
+  assertSortByStatus("ascending");
+  statusCol.click();
+  assertSortByStatus("descending");
+
+  // Test sorting on the usage column
+  usageCol.click();
+  assertSortByUsage("ascending");
+  usageCol.click();
+  assertSortByUsage("descending");
+
+  mockSiteDataManager.unregister();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+
+  function assertSortByHost(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aOrigin = siteItems[i].getAttribute("data-origin");
+      let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+      let a = mockSites.get(aOrigin);
+      let b = mockSites.get(bOrigin);
+      let result = a.host.localeCompare(b.host);
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by host");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by host");
+      }
+    }
+  }
+
+  function assertSortByStatus(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aOrigin = siteItems[i].getAttribute("data-origin");
+      let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+      let a = mockSites.get(aOrigin);
+      let b = mockSites.get(bOrigin);
+      let result = a.status - b.status;
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by permission status");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by permission status");
+      }
+    }
+  }
+
+  function assertSortByUsage(order) {
+    let siteItems = sitesList.getElementsByTagName("richlistitem");
+    for (let i = 0; i < siteItems.length - 1; ++i) {
+      let aOrigin = siteItems[i].getAttribute("data-origin");
+      let bOrigin = siteItems[i + 1].getAttribute("data-origin");
+      let a = mockSites.get(aOrigin);
+      let b = mockSites.get(bOrigin);
+      let result = a.usage - b.usage;
+      if (order == "ascending") {
+        Assert.lessOrEqual(result, 0, "Should sort sites in the ascending order by usage");
+      } else {
+        Assert.greaterOrEqual(result, 0, "Should sort sites in the descending order by usage");
+      }
+    }
+  }
+});
+
--- a/browser/components/preferences/siteDataSettings.js
+++ b/browser/components/preferences/siteDataSettings.js
@@ -20,37 +20,78 @@ let gSiteDataSettings = {
   // - uri: uri of site; instance of nsIURI
   // - status: persistent-storage permission status
   // - usage: disk usage which site uses
   _sites: null,
 
   _list: null,
 
   init() {
+    function setEventListener(id, eventType, callback) {
+      document.getElementById(id)
+              .addEventListener(eventType, callback.bind(gSiteDataSettings));
+    }
+
     this._list = document.getElementById("sitesList");
     SiteDataManager.getSites().then(sites => {
       this._sites = sites;
-      this._sortSites(this._sites, "decending");
+      let sortCol = document.getElementById("hostCol");
+      this._sortSites(this._sites, sortCol);
       this._buildSitesList(this._sites);
     });
+
+    setEventListener("hostCol", "click", this.onClickTreeCol);
+    setEventListener("usageCol", "click", this.onClickTreeCol);
+    setEventListener("statusCol", "click", this.onClickTreeCol);
   },
 
   /**
-   * Sort sites by usages
-   *
    * @param sites {Array}
-   * @param order {String} indicate to sort in the "decending" or "ascending" order
+   * @param col {XULElement} the <treecol> being sorted on
    */
-  _sortSites(sites, order) {
-    sites.sort((a, b) => {
-      if (order === "ascending") {
-        return a.usage - b.usage;
-      }
-      return b.usage - a.usage;
+  _sortSites(sites, col) {
+    let isCurrentSortCol = col.getAttribute("data-isCurrentSortCol")
+    let sortDirection = col.getAttribute("data-last-sortDirection") || "ascending";
+    if (isCurrentSortCol) {
+      // Sort on the current column, flip the sorting direction
+      sortDirection = sortDirection === "ascending" ? "descending" : "ascending";
+    }
+
+    let sortFunc = null;
+    switch (col.id) {
+      case "hostCol":
+        sortFunc = (a, b) => {
+          let aHost = a.uri.host.toLowerCase();
+          let bHost = b.uri.host.toLowerCase();
+          return aHost.localeCompare(bHost);
+        }
+        break;
+
+      case "statusCol":
+        sortFunc = (a, b) => a.status - b.status;
+        break;
+
+      case "usageCol":
+        sortFunc = (a, b) => a.usage - b.usage;
+        break;
+    }
+    if (sortDirection === "descending") {
+      sites.sort((a, b) => sortFunc(b, a));
+    } else {
+      sites.sort(sortFunc);
+    }
+
+    let cols = this._list.querySelectorAll("treecol");
+    cols.forEach(c => {
+      c.removeAttribute("sortDirection");
+      c.removeAttribute("data-isCurrentSortCol");
     });
+    col.setAttribute("data-isCurrentSortCol", true);
+    col.setAttribute("sortDirection", sortDirection);
+    col.setAttribute("data-last-sortDirection", sortDirection);
   },
 
   _buildSitesList(sites) {
     // Clear old entries.
     while (this._list.childNodes.length > 1) {
       this._list.removeChild(this._list.lastChild);
     }
 
@@ -60,10 +101,15 @@ let gSiteDataSettings = {
       let size = DownloadUtils.convertByteUnits(data.usage);
       let item = document.createElement("richlistitem");
       item.setAttribute("data-origin", data.uri.spec);
       item.setAttribute("host", data.uri.host);
       item.setAttribute("status", prefStrBundle.getString(statusStrId));
       item.setAttribute("usage", prefStrBundle.getFormattedString("siteUsage", size));
       this._list.appendChild(item);
     }
+  },
+
+  onClickTreeCol(e) {
+    this._sortSites(this._sites, e.target);
+    this._buildSitesList(this._sites);
   }
 };
--- a/browser/components/preferences/siteDataSettings.xul
+++ b/browser/components/preferences/siteDataSettings.xul
@@ -23,16 +23,16 @@
                 src="chrome://browser/locale/preferences/preferences.properties"/>
 
   <vbox flex="1">
     <description>&settings.description;</description>
     <separator class="thin"/>
 
     <richlistbox id="sitesList" orient="vertical" flex="1">
       <listheader>
-        <treecol flex="4" width="50" label="&hostCol.label;"/>
-        <treecol flex="2" width="50" label="&statusCol.label;"/>
-        <treecol flex="1" width="50" label="&usageCol.label;"/>
+        <treecol flex="4" width="50" label="&hostCol.label;" id="hostCol"/>
+        <treecol flex="2" width="50" label="&statusCol.label;" id="statusCol"/>
+        <treecol flex="1" width="50" label="&usageCol.label;" id="usageCol"/>
       </listheader>
     </richlistbox>
   </vbox>
 
 </window>
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -150,17 +150,16 @@ add_task(function* testAltReturn() {
   });
 
   is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab");
   is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testAltReturn opened correct search page");
 });
 
 // Shift key has no effect for now, so skip it
 add_task(function* testShiftAltReturn() {
-  return;
   /*
   yield* prepareTest();
 
   let url = expectedURL(searchBar.value);
 
   let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url);
   EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true });
   yield newTabPromise;
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -1313,17 +1313,17 @@ this.UITour = {
         maybeNotifyHeartbeat("Heartbeat:Voted", { score: rating });
 
         // Append the score data to the engagement URL.
         userEngaged(new Map([
           ["type", "stars"],
           ["score", rating],
           ["flowid", aOptions.flowId]
         ]));
-      }.bind(this));
+      });
 
       // Add it to the container.
       ratingContainer.appendChild(ratingElement);
     }
 
     frag.appendChild(ratingContainer);
 
     // Make sure the stars are not pushed to the right by the spacer.
@@ -1946,17 +1946,16 @@ this.UITour = {
       log.error("startSubTour: No feature option specified");
       return;
     }
 
     if (aFeature == "readinglist") {
       ReaderParent.showReaderModeInfoPanel(browser);
     } else {
       log.error("startSubTour: Unknown feature option specified");
-      return;
     }
   },
 
   addNavBarWidget(aTarget, aMessageManager, aCallbackID) {
     if (aTarget.node) {
       log.error("addNavBarWidget: can't add a widget already present:", aTarget);
       return;
     }
@@ -2026,17 +2025,16 @@ this.UITour = {
         for (let engine of engines) {
           if (engine.identifier == aID) {
             Services.search.defaultEngine = engine;
             resolve();
             return;
           }
         }
         reject("selectSearchEngine could not find engine with given ID");
-        return;
       });
     });
   },
 
   notify(eventName, params) {
     let winEnum = Services.wm.getEnumerator("navigator:browser");
     while (winEnum.hasMoreElements()) {
       let window = winEnum.getNext();
--- a/browser/extensions/pocket/content/pocket-content-process.js
+++ b/browser/extensions/pocket/content/pocket-content-process.js
@@ -40,15 +40,13 @@ AboutPocketChildListener.prototype = {
   receiveMessage: function receiveMessage(message) {
     switch (message.name) {
       case "PocketShuttingDown":
         this.onShutdown();
         break;
       default:
         break;
     }
-
-    return;
   }
 };
 
 const listener = new AboutPocketChildListener();
 listener.onStartup();
--- a/browser/locales/en-US/chrome/browser/accounts.properties
+++ b/browser/locales/en-US/chrome/browser/accounts.properties
@@ -19,16 +19,21 @@ verifyDescription = Verify %S
 # These strings are shown in a desktop notification after the
 # user requests we resend a verification email.
 verificationSentTitle = Verification Sent
 # LOCALIZATION NOTE (verificationSentBody) - %S = Email address of user's Firefox Account
 verificationSentBody = A verification link has been sent to %S.
 verificationNotSentTitle = Unable to Send Verification
 verificationNotSentBody = We are unable to send a verification mail at this time, please try again later.
 
+# LOCALIZATION NOTE (deviceConnectedTitle, deviceConnectedBody)
+# These strings are used in a notification shown when a new device joins the Sync account.
+deviceConnectedTitle = Firefox Sync
+deviceConnectedBody = This computer is now syncing with %S.
+
 # LOCALIZATION NOTE (syncStartNotification.title, syncStartNotification.body)
 # These strings are used in a notification shown after Sync is connected.
 syncStartNotification.title = Sync enabled
 # %S is brandShortName
 syncStartNotification.body2 = %S will begin syncing momentarily.
 
 # LOCALIZATION NOTE (deviceDisconnectedNotification.title, deviceDisconnectedNotification.body)
 # These strings are used in a notification shown after Sync was disconnected remotely.
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -251,17 +251,16 @@ this.ContentSearch = {
       let params = {
         postData: submission.postData,
         inBackground: Services.prefs.getBoolPref("browser.tabs.loadInBackground"),
       };
       win.openUILinkIn(submission.uri.spec, where, params);
     }
     win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey,
                                               { selection: data.selection });
-    return;
   },
 
   getSuggestions: Task.async(function* (engineName, searchString, browser) {
     let engine = Services.search.getEngineByName(engineName);
     if (!engine) {
       throw new Error("Unknown engine name: " + engineName);
     }
 
--- a/browser/modules/LaterRun.jsm
+++ b/browser/modules/LaterRun.jsm
@@ -59,17 +59,16 @@ let LaterRun = {
       // We need to store seconds in order to fit within int prefs.
       Preferences.set(kProfileCreationTime, Math.floor(Date.now() / 1000));
     }
     this.sessionCount++;
 
     if (this.hoursSinceInstall > kSelfDestructHoursLimit ||
         this.sessionCount > kSelfDestructSessionLimit) {
       this.selfDestruct();
-      return;
     }
   },
 
   // The enabled, hoursSinceInstall and sessionCount properties mirror the
   // preferences system, and are here for convenience.
   get enabled() {
     return Preferences.get(kEnabledPref, false);
   },
--- a/browser/modules/SitePermissions.jsm
+++ b/browser/modules/SitePermissions.jsm
@@ -181,17 +181,17 @@ this.SitePermissions = {
         return gStringBundle.GetStringFromName("alwaysAsk");
       case this.ALLOW:
         return gStringBundle.GetStringFromName("allow");
       case this.SESSION:
         return gStringBundle.GetStringFromName("allowForSession");
       case this.BLOCK:
         return gStringBundle.GetStringFromName("block");
       default:
-        throw new Error("unknown permission state");
+        return null;
     }
   }
 };
 
 var gPermissionObject = {
   /* Holds permission ID => options pairs.
    *
    * Supported options:
--- a/browser/modules/SocialService.jsm
+++ b/browser/modules/SocialService.jsm
@@ -445,17 +445,17 @@ this.SocialService = {
     }.bind(this));
   },
 
   // Returns a single provider object with the specified origin.  The provider
   // must be "installed" (ie, in ActiveProviders)
   getProvider: function getProvider(origin, onDone) {
     schedule((function() {
       onDone(SocialServiceInternal.providers[origin] || null);
-    }).bind(this));
+    }));
   },
 
   // Returns an unordered array of installed providers
   getProviderList(onDone) {
     schedule(function() {
       onDone(SocialServiceInternal.providerArray);
     });
   },
--- a/browser/themes/linux/downloads/downloads.css
+++ b/browser/themes/linux/downloads/downloads.css
@@ -1,34 +1,21 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 %include ../../shared/downloads/downloads.inc.css
 
-/*** Panel and outer controls ***/
-
-@keyfocus@ #downloadsSummary:focus,
-@keyfocus@ .downloadsPanelFooterButton:focus {
-  outline: 1px -moz-dialogtext dotted;
-  outline-offset: -5px;
-}
-
 /*** List items and similar elements in the summary ***/
 
 :root {
-  --downloads-item-height: 6em;
+  --downloads-item-height: 5.5em;
   --downloads-item-font-size-factor: 0.9;
-  --downloads-item-target-margin-bottom: 7px;
-  --downloads-item-details-margin-top: 1px;
   --downloads-item-details-opacity: 0.6;
 }
 
-.downloadButton:focus > .button-box {
-  outline: 1px -moz-dialogtext dotted;
-}
-
-/*** Highlighted list items ***/
-
-@keyfocus@ @itemFocused@ {
+@keyfocus@ @itemFocused@,
+@keyfocus@ #downloadsSummary:focus,
+@keyfocus@ .downloadsPanelFooterButton:focus,
+.downloadButton:focus {
   outline: 1px -moz-dialogtext dotted;
   outline-offset: -1px;
 }
--- a/browser/themes/osx/downloads/downloads.css
+++ b/browser/themes/osx/downloads/downloads.css
@@ -14,20 +14,18 @@
 @keyfocus@ .downloadsPanelFooterButton:focus {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
 /*** List items and similar elements in the summary ***/
 
 :root {
-  --downloads-item-height: 7em;
+  --downloads-item-height: 6em;
   --downloads-item-font-size-factor: 0.95;
-  --downloads-item-target-margin-bottom: 6px;
-  --downloads-item-details-margin-top: 0;
   --downloads-item-details-opacity: 0.7;
 }
 
 .downloadButton:focus > .button-box {
   outline: 2px -moz-mac-focusring solid;
   outline-offset: -2px;
 }
 
--- a/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
+++ b/browser/themes/shared/downloads/allDownloadsViewOverlay.inc.css
@@ -16,45 +16,24 @@
 }
 
 /*** List items ***/
 
 #downloadsRichListBox > richlistitem.download {
   height: var(--downloads-item-height);
 }
 
-%ifdef XP_WIN
-@media (-moz-os-version: windows-xp) {
-%endif
-#downloadsRichListBox > richlistitem.download {
-  padding: 5px 8px;
-}
-%ifdef XP_WIN
-}
-%endif
-
 .downloadTypeIcon {
-  margin-top: 8px;
-  margin-inline-end: 12px;
-  margin-bottom: 8px;
-  margin-inline-start: 0;
+  margin: 8px 13px;
   width: 32px;
   height: 32px;
 }
 
-%ifdef XP_WIN
-@media not all and (-moz-os-version: windows-xp) {
-  .downloadTypeIcon {
-    margin-inline-start: 8px;
-  }
-}
-%endif
-
 .downloadBlockedBadge {
-  margin: 0 4px;
+  margin: 0 5px;
   background: url("chrome://browser/skin/downloads/download-blocked.svg") top right / 16px no-repeat;
 }
 
 .downloadBlockedBadge:-moz-locale-dir(rtl) {
   background-position-x: left;
 }
 
 @item@[verdict="PotentiallyUnwanted"] .downloadBlockedBadge {
@@ -65,34 +44,37 @@
   background-image: url("chrome://browser/skin/info.svg");
 }
 
 @item@ > toolbarseparator {
   display: none;
 }
 
 .downloadTarget {
-  margin-bottom: 3px;
+  margin: 0;
 }
 
 .downloadDetails {
   opacity: 0.7;
   font-size: 95%;
+  /* Use calc() to keep the height consistent with .downloadTarget, so that the
+     progress bar can be vertically centered. */
+  margin: 4px 0 calc(1em / 0.95 - 1em);
 }
 
 .downloadButton {
   -moz-appearance: none;
   -moz-box-align: center;
   background: transparent;
   min-width: 0;
   min-height: 0;
-  margin: 3px;
+  margin: 0;
   border: none;
-  padding: 5px;
   color: inherit;
+  padding: 0 18px;
 }
 
 .downloadButton > .button-box {
   -moz-appearance: none;
   padding: 2px !important;
   border-radius: 50%;
 }
 
--- a/browser/themes/shared/downloads/downloads.inc.css
+++ b/browser/themes/shared/downloads/downloads.inc.css
@@ -20,21 +20,19 @@
 
 #downloadsPanel > .panel-arrowcontainer > .panel-arrowcontent,
 #downloadsPanel-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
   padding: 0;
 }
 
 #downloadsListBox {
   background: transparent;
-  padding: 0;
   color: inherit;
   -moz-appearance: none;
   margin: 0;
-  border: none;
 }
 
 #emptyDownloads {
   padding: 16px 25px;
   margin: 0;
   /* The panel can be wider than this description after the blocked subview is
      shown, so center the text. */
   text-align: center;
@@ -145,63 +143,52 @@ toolbarseparator.downloadsDropmarkerSpli
 
 /* Override default icon size which is too small for this dropdown */
 .downloadsDropmarker > .button-box > .button-menu-dropmarker {
   width: 16px;
   height: 16px;
 }
 
 #downloadsSummary {
-  padding: 0 12px;
   -moz-user-focus: normal;
 }
 
 #downloadsSummary > .downloadTypeIcon {
   list-style-image: url("chrome://browser/skin/downloads/download-summary.svg");
 }
 
 #downloadsSummaryDescription {
   color: -moz-nativehyperlinktext;
 }
 
 /*** List items and similar elements in the summary ***/
 
 #downloadsSummary,
 richlistitem[type="download"] {
   height: var(--downloads-item-height);
-  padding-inline-end: 0;
-  color: inherit;
 }
 
 richlistitem[type="download"] {
-  margin: 0;
   border-bottom: 1px solid var(--panel-separator-color);
   background: transparent;
-  padding: 0;
+  color: inherit;
 }
 
 richlistitem[type="download"]:last-child {
   border-bottom: none;
 }
 
-richlistitem[type="download"] > .downloadMainArea {
-  padding: 8px;
-}
-
 .downloadTypeIcon {
-  margin-top: 8px;
-  margin-inline-end: 12px;
-  margin-bottom: 8px;
-  margin-inline-start: 0;
+  margin: 8px 13px;
   width: 32px;
   height: 32px;
 }
 
 .downloadBlockedBadge {
-  margin: 0 4px;
+  margin: 0 5px;
   background: url("chrome://browser/skin/downloads/download-blocked.svg") top right / 16px no-repeat;
 }
 
 .downloadBlockedBadge:-moz-locale-dir(rtl) {
   background-position-x: left;
 }
 
 @item@[verdict="PotentiallyUnwanted"] .downloadBlockedBadge {
@@ -221,47 +208,46 @@ richlistitem[type="download"] > .downloa
       which is a ch unit. Since this is the value that should control the
       panel width, we apply it to the outer container to constrain
       .downloadTarget and .downloadProgress.
 
    Finally, since we want .downloadTarget's font-size to be at 100% of the
    font-size of .downloadContainer's parent, we use calc to go from the
    smaller font-size back to the original font-size.
  */
-#downloadsSummaryDetails,
 .downloadContainer {
   font-size: calc(100% * var(--downloads-item-font-size-factor));
+  margin-inline-end: 13px;
 }
 
 #downloadsSummaryDescription,
 .downloadTarget {
-  margin-bottom: var(--downloads-item-target-margin-bottom);
-}
-
-.downloadTarget {
+  margin: 0;
   font-size: calc(100% / var(--downloads-item-font-size-factor));
 }
 
 #downloadsSummaryDetails,
 .downloadDetails {
-  margin-top: var(--downloads-item-details-margin-top);
   opacity: var(--downloads-item-details-opacity);
+  /* Use calc() to keep the height consistent with .downloadTarget, so that the
+     progress bar can be vertically centered. */
+  margin: 4px 0 calc(1em / var(--downloads-item-font-size-factor) - 1em);
 }
 
 @item@[verdict] > toolbarseparator {
   visibility: hidden;
 }
 
 .downloadButton {
   -moz-appearance: none;
   min-width: 58px;
   margin: 0;
   border: none;
   background: transparent;
-  padding: 8px;
+  padding: 0;
   color: inherit;
 }
 
 .downloadButton > .button-box > .button-icon {
   width: 16px;
   height: 16px;
   margin: 1px;
   filter: url("chrome://global/skin/filters.svg#fill");
--- a/browser/themes/shared/downloads/progressmeter.inc.css
+++ b/browser/themes/shared/downloads/progressmeter.inc.css
@@ -1,15 +1,13 @@
 /*** Common-styled progressmeter ***/
 .downloadProgress {
   height: 8px;
   border-radius: 1px;
-  margin-top: 2px;
-  margin-bottom: 2px;
-  margin-inline-start: 6px;
+  margin: 4px 0 0;
   margin-inline-end: 12px;
 
   /* for overriding rules in progressmeter.css */
   -moz-appearance: none;
   border-style: none;
   background-color: transparent;
   min-width: initial;
   min-height: initial;
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -110,20 +110,21 @@
 
 #identity-box[sharing] > #sharing-icon {
   display: -moz-box;
   animation-delay: -1.5s;
 }
 
 #identity-box[sharing] > #identity-icon,
 #sharing-icon {
-  animation: 3s linear pulse infinite;
+  animation: 3s linear identity-box-sharing-icon-pulse infinite;
 }
 
-@keyframes pulse {
+/* This should remain identical to tab-sharing-icon-pulse in tabs.inc.css */
+@keyframes identity-box-sharing-icon-pulse {
   0%, 16.66%, 83.33%, 100% {
     opacity: 0;
   }
   33.33%, 66.66% {
     opacity: 1;
   }
 }
 
--- a/browser/themes/shared/newtab/newTab.inc.css
+++ b/browser/themes/shared/newtab/newTab.inc.css
@@ -162,16 +162,26 @@ body:not(.compact) .newtab-site[dragged]
 body.compact .newtab-thumbnail {
   height: 100%;
   border-radius: calc(var(--cell-corner-radius) + 1px);
   outline: 1px solid hsla(0,0%,0%,.1);
   -moz-outline-radius: var(--cell-corner-radius);
   outline-offset: -1px;
 }
 
+.newtab-thumbnail.placeholder {
+  color: white;
+  font-size: 85px;
+  line-height: 200%;
+}
+
+body.compact .newtab-thumbnail.placeholder {
+  font-size: 45px;
+}
+
 .newtab-cell:not([ignorehover]) .newtab-site:hover .newtab-thumbnail.enhanced-content {
   opacity: 0;
 }
 
 .newtab-site[type=affiliate] .newtab-thumbnail,
 .newtab-site[type=enhanced] .newtab-thumbnail,
 .newtab-site[type=organic] .newtab-thumbnail,
 .newtab-site[type=sponsored] .newtab-thumbnail {
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -34,28 +34,16 @@
 %ifdef MOZ_WIDGET_GTK
     list-style-image: url(moz-icon://stock/gtk-dialog-info?size=dialog);
 %else
     list-style-image: url(chrome://global/skin/icons/information-32.png);
 %endif
   }
 }
 
-#notification-popup > .panel-arrowcontainer > .panel-arrowcontent {
-  /* In order to display the action buttons near the edge of the arrow panel we
-   * have to reset its default padding and specify the padding in the individual
-   * "popupnotification" elements instead. To keep the rounded borders of the
-   * panel, we also have to ensure the contents are clipped to the border box
-   * by hiding the overflow, and we have to override the "display" property so
-   * that the height of the contents is computed correctly in that case. */
-  padding: 0;
-  overflow: hidden;
-  display: block;
-}
-
 .notification-anchor-icon:not(.plugin-blocked):-moz-lwtheme,
 #blocked-permissions-container > .blocked-permission-icon:-moz-lwtheme {
   filter: url(chrome://global/skin/filters.svg#fill);
   fill: currentColor;
 }
 
 .notification-anchor-icon:not(.plugin-blocked):not(:hover) {
   opacity: .8;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -86,20 +86,21 @@
 }
 
 .tab-icon-image {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .tab-icon-image[sharing]:not([selected]),
 .tab-sharing-icon-overlay {
-  animation: 3s linear pulse infinite;
+  animation: 3s linear tab-sharing-icon-pulse infinite;
 }
 
-@keyframes pulse {
+/* This should remain identical to identity-box-sharing-icon-pulse in identity-block.inc.css */
+@keyframes tab-sharing-icon-pulse {
   0%, 16.66%, 83.33%, 100% {
     opacity: 0;
   }
   33.33%, 66.66% {
     opacity: 1;
   }
 }
 
--- a/browser/themes/windows/downloads/downloads.css
+++ b/browser/themes/windows/downloads/downloads.css
@@ -14,20 +14,18 @@
 
 @keyfocus@ #downloadsSummary:focus {
   outline-offset: -5px;
 }
 
 /*** List items and similar elements in the summary ***/
 
 :root {
-  --downloads-item-height: 7em;
+  --downloads-item-height: 5.5em;
   --downloads-item-font-size-factor: 0.9;
-  --downloads-item-target-margin-bottom: 6px;
-  --downloads-item-details-margin-top: 0;
   --downloads-item-details-opacity: 0.6;
 }
 
 .downloadButton > .button-box {
   border: 1px solid transparent;
 }
 
 @keyfocus@ .downloadButton:focus > .button-box {
--- a/build/build-clang/README
+++ b/build/build-clang/README
@@ -40,17 +40,17 @@ build-clang.py accepts a JSON config for
 * cc: Path to the bootsraping C Compiler.
 * cxx: Path to the bootsraping C++ Compiler.
 * ar: Path to the library archiver tool.
 * ranlib: Path to the ranlib tool.
 * ld: Path to the linker.
 * patches: Optional list of patches to apply per platform.  Supported platforms: macosx64, linux32, linux64.  The default is Release.
 * build_type: The type of build to make.  Supported types: Release, Debug, RelWithDebInfo or MinSizeRel.
 * build_libcxx: Whether to build with libcxx.  The default is false.
-* import_clang_tidy: Whether to import Mozilla checks into clang-tidy before building.  The default is false.
+* build_clang_tidy: Whether to build clang-tidy with the Mozilla checks imported.  The default is false.
 * osx_cross_compile: Whether to invoke CMake for OS X cross compile builds.
 * assertions: Whether to enable LLVM assertions.  The default is false.
 
 Environment Variables
 ---------------------
 
 The following environment variables are used for cross-compile builds targeting OS X on Linux.
 
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -52,17 +52,17 @@ def run_in(path, args):
 
 
 def patch(patch, srcdir):
     patch = os.path.realpath(patch)
     check_run(['patch', '-d', srcdir, '-p1', '-i', patch, '--fuzz=0',
                '-s'])
 
 
-def do_import_clang_tidy(source_dir):
+def import_clang_tidy(source_dir):
     clang_plugin_path = os.path.join(os.path.dirname(sys.argv[0]),
                                      '..', 'clang-plugin')
     clang_tidy_path = os.path.join(source_dir,
                                    'tools/clang/tools/extra/clang-tidy')
     sys.path.append(clang_plugin_path)
     from import_mozilla_checks import do_import
     do_import(clang_plugin_path, clang_tidy_path)
 
@@ -123,16 +123,26 @@ def copy_dir_contents(src, dest):
 def mkdir_p(path):
     try:
         os.makedirs(path)
     except OSError as e:
         if e.errno != errno.EEXIST or not os.path.isdir(path):
             raise
 
 
+def delete(path):
+    if os.path.isdir(path):
+        shutil.rmtree(path)
+    else:
+        try:
+            os.unlink(path)
+        except:
+            pass
+
+
 def install_libgcc(gcc_dir, clang_dir):
     out = subprocess.check_output([os.path.join(gcc_dir, "bin", "gcc"),
                                    '-print-libgcc-file-name'])
 
     libgcc_dir = os.path.dirname(out.rstrip())
     clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
                                  "x86_64-unknown-linux-gnu",
                                  os.path.basename(libgcc_dir))
@@ -200,16 +210,18 @@ def build_one_stage(cc, cxx, ld, ar, ran
     build_dir = stage_dir + "/build"
     inst_dir = stage_dir + "/clang"
 
     # If CMake has already been run, it may have been run with different
     # arguments, so we need to re-run it.  Make sure the cached copy of the
     # previous CMake run is cleared before running it again.
     if os.path.exists(build_dir + "/CMakeCache.txt"):
         os.remove(build_dir + "/CMakeCache.txt")
+    if os.path.exists(build_dir + "/CMakeFiles"):
+        shutil.rmtree(build_dir + "/CMakeFiles")
 
     # cmake doesn't deal well with backslashes in paths.
     def slashify_path(path):
         return path.replace('\\', '/')
 
     cmake_args = ["-GNinja",
                   "-DCMAKE_C_COMPILER=%s" % slashify_path(cc[0]),
                   "-DCMAKE_CXX_COMPILER=%s" % slashify_path(cxx[0]),
@@ -274,16 +286,82 @@ def get_tool(config, key):
             return f
 
     # Assume that we have the name of some program that should be on PATH.
     try:
         return which.which(f) if f else which.which(key)
     except which.WhichError:
         raise ValueError("%s not found on PATH" % f)
 
+
+# This function is intended to be called on the final build directory when
+# building clang-tidy.  Its job is to remove all of the files which won't
+# be used for clang-tidy to reduce the download size.  Currently when this
+# function finishes its job, it will leave final_dir with a layout like this:
+#
+# clang/
+#   bin/
+#     clang-tidy
+#   include/
+#     * (nothing will be deleted here)
+#   lib/
+#     clang/
+#       4.0.0/
+#         include/
+#           * (nothing will be deleted here)
+#   share/
+#     clang/
+#       clang-tidy-diff.py
+#       run-clang-tidy.py
+def prune_final_dir_for_clang_tidy(final_dir):
+    # Make sure we only have what we expect.
+    dirs = ("bin", "include", "lib", "libexec", "msbuild-bin", "share", "tools")
+    for f in glob.glob("%s/*" % final_dir):
+        if os.path.basename(f) not in dirs:
+            raise Exception("Found unknown file %s in the final directory" % f)
+        if not os.path.isdir(f):
+            raise Exception("Expected %s to be a directory" %f)
+
+    # In bin/, only keep clang-tidy.
+    re_clang_tidy = re.compile(r"^clang-tidy(\.exe)?$", re.I)
+    for f in glob.glob("%s/bin/*" % final_dir):
+        if re_clang_tidy.search(os.path.basename(f)) is None:
+            delete(f)
+
+    # Keep include/ intact.
+
+    # In lib/, only keep lib/clang/N.M.O/include.
+    re_ver_num = re.compile(r"^\d+\.\d+\.\d+$", re.I)
+    for f in glob.glob("%s/lib/*" % final_dir):
+        if os.path.basename(f) != "clang":
+            delete(f)
+    for f in glob.glob("%s/lib/clang/*" % final_dir):
+        if re_ver_num.search(os.path.basename(f)) is None:
+            delete(f)
+    for f in glob.glob("%s/lib/clang/*/*" % final_dir):
+        if os.path.basename(f) != "include":
+            delete(f)
+
+    # Completely remove libexec/, msbuilld-bin and tools, if it exists.
+    shutil.rmtree(os.path.join(final_dir, "libexec"))
+    for d in ("msbuild-bin", "tools"):
+        d = os.path.join(final_dir, d)
+        if os.path.exists(d):
+            shutil.rmtree(d)
+
+    # In share/, only keep share/clang/*tidy*
+    re_clang_tidy = re.compile(r"tidy", re.I)
+    for f in glob.glob("%s/share/*" % final_dir):
+        if os.path.basename(f) != "clang":
+            delete(f)
+    for f in glob.glob("%s/share/clang/*" % final_dir):
+        if re_clang_tidy.search(os.path.basename(f)) is None:
+            delete(f)
+
+
 if __name__ == "__main__":
     # The directories end up in the debug info, so the easy way of getting
     # a reproducible build is to run it in a know absolute directory.
     # We use a directory in /builds/slave because the mozilla infrastructure
     # cleans it up automatically.
     base_dir = "/builds/slave/moz-toolchain"
     if is_windows():
         # TODO: Because Windows taskcluster builds are run with distinct
@@ -351,21 +429,21 @@ if __name__ == "__main__":
         build_type = config["build_type"]
         if build_type not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
             raise ValueError("We only know how to do Release, Debug, RelWithDebInfo or MinSizeRel builds")
     build_libcxx = False
     if "build_libcxx" in config:
         build_libcxx = config["build_libcxx"]
         if build_libcxx not in (True, False):
             raise ValueError("Only boolean values are accepted for build_libcxx.")
-    import_clang_tidy = False
-    if "import_clang_tidy" in config:
-        import_clang_tidy = config["import_clang_tidy"]
-        if import_clang_tidy not in (True, False):
-            raise ValueError("Only boolean values are accepted for import_clang_tidy.")
+    build_clang_tidy = False
+    if "build_clang_tidy" in config:
+        build_clang_tidy = config["build_clang_tidy"]
+        if build_clang_tidy not in (True, False):
+            raise ValueError("Only boolean values are accepted for build_clang_tidy.")
     osx_cross_compile = False
     if "osx_cross_compile" in config:
         osx_cross_compile = config["osx_cross_compile"]
         if osx_cross_compile not in (True, False):
             raise ValueError("Only boolean values are accepted for osx_cross_compile.")
         if osx_cross_compile and not is_linux():
             raise ValueError("osx_cross_compile can only be used on Linux.")
     assertions = False
@@ -430,25 +508,22 @@ if __name__ == "__main__":
                 (source_dir + "/libcxx",
                  llvm_source_dir + "/projects/libcxx"),
                 (source_dir + "/libcxxabi",
                  llvm_source_dir + "/projects/libcxxabi")]
     for l in symlinks:
         # On Windows, we have to re-copy the whole directory every time.
         if not is_windows() and os.path.islink(l[1]):
             continue
-        if os.path.isdir(l[1]):
-            shutil.rmtree(l[1])
-        elif os.path.exists(l[1]):
-            os.unlink(l[1])
+        delete(l[1]);
         if os.path.exists(l[0]):
             symlink(l[0], l[1])
 
-    if import_clang_tidy:
-        do_import_clang_tidy(llvm_source_dir)
+    if build_clang_tidy:
+        import_clang_tidy(llvm_source_dir)
 
     if not os.path.exists(build_dir):
         os.makedirs(build_dir)
 
     stage1_dir = build_dir + '/stage1'
     stage1_inst_dir = stage1_dir + '/clang'
 
     final_stage_dir = stage1_dir
@@ -531,12 +606,17 @@ if __name__ == "__main__":
                 (cc_name, exe_ext)] + extra_cflags2,
             [stage2_inst_dir + "/bin/%s%s" %
                 (cxx_name, exe_ext)] + extra_cxxflags2,
             [ld] + extra_ldflags,
             ar, ranlib,
             llvm_source_dir, stage3_dir, build_libcxx, osx_cross_compile,
             build_type, assertions, python_path, gcc_dir)
 
+    package_name = "clang"
+    if build_clang_tidy:
+        prune_final_dir_for_clang_tidy(os.path.join(final_stage_dir, "clang"))
+        package_name = "clang-tidy"
+
     if is_darwin() or is_windows():
-        build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang")
+        build_tar_package("tar", package_name + ".tar.bz2", final_stage_dir, "clang")
     else:
-        build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang")
+        build_tar_package("tar", package_name + ".tar.xz", final_stage_dir, "clang")
--- a/build/build-clang/clang-tidy-linux64.json
+++ b/build/build-clang/clang-tidy-linux64.json
@@ -1,15 +1,15 @@
 {
     "llvm_revision": "290055",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "import_clang_tidy": true,
+    "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
     "gcc_dir": "/home/worker/workspace/build/src/gcc",
--- a/build/build-clang/clang-tidy-macosx64.json
+++ b/build/build-clang/clang-tidy-macosx64.json
@@ -1,15 +1,15 @@
 {
     "llvm_revision": "290055",
     "stages": "1",
     "build_libcxx": true,
     "build_type": "Release",
     "assertions": false,
-    "import_clang_tidy": true,
+    "build_clang_tidy": true,
     "osx_cross_compile": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/trunk",
     "python_path": "/usr/bin/python2.7",
--- a/build/build-clang/clang-tidy-win32.json
+++ b/build/build-clang/clang-tidy-win32.json
@@ -1,15 +1,15 @@
 {
     "llvm_revision": "290055",
     "stages": "1",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
-    "import_clang_tidy": true,
+    "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe"
--- a/build/build-clang/clang-tidy-win64.json
+++ b/build/build-clang/clang-tidy-win64.json
@@ -1,15 +1,15 @@
 {
     "llvm_revision": "290055",
     "stages": "1",
     "build_libcxx": false,
     "build_type": "Release",
     "assertions": false,
-    "import_clang_tidy": true,
+    "build_clang_tidy": true,
     "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
     "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
     "extra_repo": "https://llvm.org/svn/llvm-project/clang-tools-extra/trunk",
     "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
     "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
     "python_path": "c:/mozilla-build/python/python.exe",
     "cc": "cl.exe",
     "cxx": "cl.exe"
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -384,33 +384,35 @@ class RemoteAutomation(Automation):
             if timeout == None:
                 timeout = self.timeout
             status = 0
             top = self.procName
             slowLog = False
             while (top == self.procName):
                 # Get log updates on each interval, but if it is taking
                 # too long, only do it every 60 seconds
+                hasOutput = False
                 if (not slowLog) or (timer % 60 == 0):
                     startRead = datetime.datetime.now()
                     hasOutput = self.read_stdout()
                     if (datetime.datetime.now() - startRead) > datetime.timedelta(seconds=5):
                         slowLog = True
                     if hasOutput:
                         noOutputTimer = 0
                 time.sleep(interval)
                 timer += interval
                 noOutputTimer += interval
                 if (timer > timeout):
                     status = 1
                     break
                 if (noOutputTimeout and noOutputTimer > noOutputTimeout):
                     status = 2
                     break
-                top = self.dm.getTopActivity()
+                if not hasOutput:
+                    top = self.dm.getTopActivity()
             # Flush anything added to stdout during the sleep
             self.read_stdout()
             return status
 
         def kill(self, stagedShutdown = False):
             if self.utilityPath:
                 # Take a screenshot to capture the screen state just before
                 # the application is killed. There are on-device screenshot
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -73,16 +73,21 @@ automation/pretty-l10n-check: automation
 
 automation/upload: automation/installer
 automation/upload: automation/package
 automation/upload: automation/package-tests
 automation/upload: automation/buildsymbols
 automation/upload: automation/update-packaging
 automation/upload: automation/sdk
 
+# buildsymbols will modify our test binaries, which can interfere with
+# packaging them. A finer-grained dependency can help performance here
+# once bug 1329020 is fixed.
+automation/pretty-package-tests: automation/buildsymbols
+
 # automation/{pretty-}package should depend on build (which is implicit due to
 # the way client.mk invokes automation/build), but buildsymbols changes the
 # binaries/libs, and that's what we package/test.
 automation/pretty-package: automation/buildsymbols
 
 # The installer, sdk and packager all run stage-package, and may conflict
 # with each other.
 automation/installer: automation/package
--- a/chrome/moz.build
+++ b/chrome/moz.build
@@ -33,8 +33,11 @@ LOCAL_INCLUDES += [
     '/dom/base',
     '/netwerk/base',
     '/netwerk/protocol/res',
     '/xpcom/components'
 ]
 
 if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
     CXXFLAGS += CONFIG['TK_CFLAGS']
+
+with Files("**"):
+    BUG_COMPONENT = ("Toolkit", "Startup and Profile System")
--- a/devtools/.eslintrc.js
+++ b/devtools/.eslintrc.js
@@ -323,16 +323,18 @@ module.exports = {
     // Disallow the use of Boolean literals in conditional expressions.
     "no-unneeded-ternary": "error",
     // Disallow unreachable statements after a return, throw, continue, or break
     // statement.
     "no-unreachable": "error",
     // Disallow global and local variables that aren't used, but allow unused
     // function arguments.
     "no-unused-vars": ["error", {"vars": "all", "args": "none"}],
+    // Disallow flow control that escapes from "finally".
+    "no-unsafe-finally": "error",
     // Allow using variables before they are defined.
     "no-use-before-define": "off",
     // We use var-only-at-top-level instead of no-var as we allow top level
     // vars.
     "no-var": "off",
     // Allow using TODO/FIXME comments.
     "no-warning-comments": "off",
     // Disallow use of the with statement.
--- a/devtools/client/debugger/new/bundle.js
+++ b/devtools/client/debugger/new/bundle.js
@@ -35259,18 +35259,20 @@ var Debugger =
 	  },
 	
 	  render: function () {
 	    var _state = this.state;
 	    var focusedItem = _state.focusedItem;
 	    var sourceTree = _state.sourceTree;
 	    var parentMap = _state.parentMap;
 	
+        const isEmpty = sourceTree.contents.length === 0;
 	
 	    var tree = ManagedTree({
+          key: isEmpty ? "empty" : "full",
 	      getParent: item => {
 	        return parentMap.get(item);
 	      },
 	      getChildren: item => {
 	        if (nodeHasChildren(item)) {
 	          return item.contents;
 	        }
 	        return [];
@@ -37350,18 +37352,20 @@ var Debugger =
 	__webpack_require__(396);
 	
 	var ManagedTree = React.createClass({
 	  propTypes: Tree.propTypes,
 	
 	  displayName: "ManagedTree",
 	
 	  getInitialState() {
-	    return { expanded: new Set(),
-	      focusedItem: null };
+	    return {
+          expanded: new Set(),
+	      focusedItem: null
+        };
 	  },
 	
 	  setExpanded(item, isExpanded) {
 	    var expanded = this.state.expanded;
 	    var key = this.props.getKey(item);
 	    if (isExpanded) {
 	      expanded.add(key);
 	    } else {
@@ -58323,9 +58327,9 @@ var Debugger =
 	module.exports = {
 	  getStr,
 	  getFormatStr,
 	  setBundle
 	};
 
 /***/ }
 /******/ ]);
-//# sourceMappingURL=bundle.js.map
\ No newline at end of file
+//# sourceMappingURL=bundle.js.map
--- a/devtools/client/debugger/new/source-map-worker.js
+++ b/devtools/client/debugger/new/source-map-worker.js
@@ -84,17 +84,18 @@ var Debugger =
 	    var map = yield _getSourceMap(generatedSourceId);
 	    if (!map) {
 	      return location;
 	    }
 	
 	    var _map$generatedPositio = map.generatedPositionFor({
 	      source: originalSource.url,
 	      line: location.line,
-	      column: location.column == null ? 0 : location.column
+	      column: location.column == null ? 0 : location.column,
+          bias: SourceMapConsumer.LEAST_UPPER_BOUND
 	    });
 	
 	    var line = _map$generatedPositio.line;
 	    var column = _map$generatedPositio.column;
 	
 	
 	    return {
 	      sourceId: generatedSourceId,
@@ -5822,9 +5823,9 @@ var Debugger =
 	};
 	
 	exports.SourceNode = SourceNode;
 
 
 /***/ }
 
 /******/ });
-//# sourceMappingURL=source-map-worker.js.map
\ No newline at end of file
+//# sourceMappingURL=source-map-worker.js.map
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -7,17 +7,17 @@
 "use strict";
 
 const promise = require("promise");
 const CssLogic = require("devtools/shared/inspector/css-logic");
 const {ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 const {TextProperty} =
       require("devtools/client/inspector/rules/models/text-property");
 const {promiseWarn} = require("devtools/client/inspector/shared/utils");
-const {parseDeclarations} = require("devtools/shared/css/parsing-utils");
+const {parseNamedDeclarations} = require("devtools/shared/css/parsing-utils");
 const Services = require("Services");
 
 const STYLE_INSPECTOR_PROPERTIES = "devtools/shared/locales/styleinspector.properties";
 const {LocalizationHelper} = require("devtools/shared/l10n");
 const STYLE_INSPECTOR_L10N = new LocalizationHelper(STYLE_INSPECTOR_PROPERTIES);
 
 /**
  * Rule is responsible for the following:
@@ -237,18 +237,18 @@ Rule.prototype = {
       disabled.delete(this.style);
     }
 
     return modifications.apply().then(() => {
       let cssProps = {};
       // Note that even though StyleRuleActors normally provide parsed
       // declarations already, _applyPropertiesNoAuthored is only used when
       // connected to older backend that do not provide them. So parse here.
-      for (let cssProp of parseDeclarations(this.cssProperties.isKnown,
-                                            this.style.authoredText)) {
+      for (let cssProp of parseNamedDeclarations(this.cssProperties.isKnown,
+                                                 this.style.authoredText)) {
         cssProps[cssProp.name] = cssProp;
       }
 
       for (let textProp of this.textProps) {
         if (!textProp.enabled) {
           continue;
         }
         let cssProp = cssProps[textProp.name];
@@ -435,28 +435,25 @@ Rule.prototype = {
    */
   _getTextProperties: function () {
     let textProps = [];
     let store = this.elementStyle.store;
 
     // Starting with FF49, StyleRuleActors provide parsed declarations.
     let props = this.style.declarations;
     if (!props.length) {
-      props = parseDeclarations(this.cssProperties.isKnown,
-                                this.style.authoredText, true);
+      // If the authored text has an invalid property, it will show up
+      // as nameless.  Skip these as we don't currently have a good
+      // way to display them.
+      props = parseNamedDeclarations(this.cssProperties.isKnown,
+                                     this.style.authoredText, true);
     }
 
     for (let prop of props) {
       let name = prop.name;
-      // If the authored text has an invalid property, it will show up
-      // as nameless.  Skip these as we don't currently have a good
-      // way to display them.
-      if (!name) {
-        continue;
-      }
       // In an inherited rule, we only show inherited properties.
       // However, we must keep all properties in order for rule
       // rewriting to work properly.  So, compute the "invisible"
       // property here.
       let invisible = this.inherited && !this.cssProperties.isInherited(name);
       let value = store.userProperties.getProperty(this.style, name,
                                                    prop.value);
       let textProp = new TextProperty(this, name, value, prop.priority,
--- a/devtools/client/inspector/rules/views/rule-editor.js
+++ b/devtools/client/inspector/rules/views/rule-editor.js
@@ -12,17 +12,17 @@ const {InplaceEditor, editableField, edi
 const {TextPropertyEditor} =
       require("devtools/client/inspector/rules/views/text-property-editor");
 const {
   createChild,
   blurOnMultipleProperties,
   promiseWarn
 } = require("devtools/client/inspector/shared/utils");
 const {
-  parseDeclarations,
+  parseNamedDeclarations,
   parsePseudoClassesAndAttributes,
   SELECTOR_ATTRIBUTE,
   SELECTOR_ELEMENT,
   SELECTOR_PSEUDO_CLASS
 } = require("devtools/shared/css/parsing-utils");
 const promise = require("promise");
 const Services = require("Services");
 const EventEmitter = require("devtools/shared/event-emitter");
@@ -468,18 +468,17 @@ RuleEditor.prototype = {
     if (!value || !commit) {
       return;
     }
 
     // parseDeclarations allows for name-less declarations, but in the present
     // case, we're creating a new declaration, it doesn't make sense to accept
     // these entries
     this.multipleAddedProperties =
-      parseDeclarations(this.rule.cssProperties.isKnown, value, true)
-      .filter(d => d.name);
+      parseNamedDeclarations(this.rule.cssProperties.isKnown, value, true);
 
     // Blur the editor field now and deal with adding declarations later when
     // the field gets destroyed (see _newPropertyDestroy)
     this.editor.input.blur();
   },
 
   /**
    * Called when the new property editor is destroyed.
--- a/devtools/client/jsonview/components/json-panel.js
+++ b/devtools/client/jsonview/components/json-panel.js
@@ -54,18 +54,18 @@ define(function (require, exports, modul
       // XXX shortcut for focusing the Filter field (see Bug 1178771).
     },
 
     onFilter: function (object) {
       if (!this.props.searchFilter) {
         return true;
       }
 
-      let json = JSON.stringify(object).toLowerCase();
-      return json.indexOf(this.props.searchFilter.toLowerCase()) >= 0;
+      let json = object.name + JSON.stringify(object.value);
+      return json.toLowerCase().indexOf(this.props.searchFilter.toLowerCase()) >= 0;
     },
 
     getExpandedNodes: function (object, path = "", level = 0) {
       if (typeof object != "object") {
         return null;
       }
 
       if (level > AUTO_EXPAND_MAX_LEVEL) {
--- a/devtools/client/locales/en-US/responsive.properties
+++ b/devtools/client/locales/en-US/responsive.properties
@@ -13,16 +13,19 @@
 
 # LOCALIZATION NOTE (responsive.editDeviceList): option displayed in the device
 # selector
 responsive.editDeviceList=Edit list…
 
 # LOCALIZATION NOTE (responsive.exit): tooltip text of the exit button.
 responsive.exit=Close Responsive Design Mode
 
+# LOCALIZATION NOTE (responsive.rotate): tooltip text of the rotate button.
+responsive.rotate=Rotate viewport
+
 # LOCALIZATION NOTE (responsive.deviceListLoading): placeholder text for
 # device selector when it's still fetching devices
 responsive.deviceListLoading=Loading…
 
 # LOCALIZATION NOTE (responsive.deviceListError): placeholder text for
 # device selector when an error occurred
 responsive.deviceListError=No list available
 
--- a/devtools/client/netmonitor/components/request-list-content.js
+++ b/devtools/client/netmonitor/components/request-list-content.js
@@ -35,24 +35,26 @@ const RequestListContent = createClass({
       toggleDelay: REQUESTS_TOOLTIP_TOGGLE_DELAY,
       interactive: true
     });
 
     // Install event handler to hide the tooltip on scroll
     this.refs.contentEl.addEventListener("scroll", this.onScroll, true);
   },
 
-  componentWillUpdate() {
-    // Check if the list is scrolled to bottom, before UI update
-    this.shouldScrollBottom = this.isScrolledToBottom();
+  componentWillUpdate(nextProps) {
+    // Check if the list is scrolled to bottom before the UI update.
+    // The scroll is ever needed only if new rows are added to the list.
+    const delta = nextProps.displayedRequests.size - this.props.displayedRequests.size;
+    this.shouldScrollBottom = delta > 0 && this.isScrolledToBottom();
   },
 
   componentDidUpdate(prevProps) {
     // Update the CSS variables for waterfall scaling after props change
-    this.setScalingStyles();
+    this.setScalingStyles(prevProps);
 
     // Keep the list scrolled to bottom if a new row was added
     if (this.shouldScrollBottom) {
       let node = this.refs.contentEl;
       node.scrollTop = node.scrollHeight;
     }
   },
 
@@ -67,22 +69,20 @@ const RequestListContent = createClass({
    * Set the CSS variables for waterfall scaling. If React supported setting CSS
    * variables as part of the "style" property of a DOM element, we would use that.
    *
    * However, React doesn't support this, so we need to use a hack and update the
    * DOM element directly: https://github.com/facebook/react/issues/6411
    */
   setScalingStyles(prevProps) {
     const { scale } = this.props;
-    if (scale == this.currentScale) {
+    if (prevProps && prevProps.scale === scale) {
       return;
     }
 
-    this.currentScale = scale;
-
     const { style } = this.refs.contentEl;
     style.removeProperty("--timings-scale");
     style.removeProperty("--timings-rev-scale");
     style.setProperty("--timings-scale", scale);
     style.setProperty("--timings-rev-scale", 1 / scale);
   },
 
   isScrolledToBottom() {
--- a/devtools/client/netmonitor/components/request-list-item.js
+++ b/devtools/client/netmonitor/components/request-list-item.js
@@ -1,21 +1,59 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-const { createClass, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
+const { createClass, createFactory, PropTypes, DOM } = require("devtools/client/shared/vendor/react");
 const { div, span, img } = DOM;
 const { L10N } = require("../l10n");
 const { getFormattedSize } = require("../utils/format-utils");
 const { getAbbreviatedMimeType } = require("../request-utils");
 
 /**
+ * Compare two objects on a subset of their properties
+ */
+function propertiesEqual(props, item1, item2) {
+  return item1 === item2 || props.every(p => item1[p] === item2[p]);
+}
+
+/**
+ * Used by shouldComponentUpdate: compare two items, and compare only properties
+ * relevant for rendering the RequestListItem. Other properties (like request and
+ * response headers, cookies, bodies) are ignored. These are very useful for the
+ * sidebar details, but not here.
+ */
+const UPDATED_REQ_ITEM_PROPS = [
+  "mimeType",
+  "eventTimings",
+  "securityState",
+  "responseContentDataUri",
+  "status",
+  "statusText",
+  "fromCache",
+  "fromServiceWorker",
+  "method",
+  "url",
+  "remoteAddress",
+  "cause",
+  "contentSize",
+  "transferredSize",
+  "startedMillis",
+  "totalTime",
+];
+
+const UPDATED_REQ_PROPS = [
+  "index",
+  "isSelected",
+  "firstRequestStartedMillis"
+];
+
+/**
  * Render one row in the request list.
  */
 const RequestListItem = createClass({
   displayName: "RequestListItem",
 
   propTypes: {
     item: PropTypes.object.isRequired,
     index: PropTypes.number.isRequired,
@@ -28,20 +66,18 @@ const RequestListItem = createClass({
 
   componentDidMount() {
     if (this.props.isSelected) {
       this.refs.el.focus();
     }
   },
 
   shouldComponentUpdate(nextProps) {
-    return !relevantPropsEqual(this.props.item, nextProps.item)
-      || this.props.index !== nextProps.index
-      || this.props.isSelected !== nextProps.isSelected
-      || this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis;
+    return !propertiesEqual(UPDATED_REQ_ITEM_PROPS, this.props.item, nextProps.item) ||
+           !propertiesEqual(UPDATED_REQ_PROPS, this.props, nextProps);
   },
 
   componentDidUpdate(prevProps) {
     if (!prevProps.isSelected && this.props.isSelected) {
       this.refs.el.focus();
       if (this.props.onFocusedNodeChange) {
         this.props.onFocusedNodeChange();
       }
@@ -83,218 +119,292 @@ const RequestListItem = createClass({
       {
         ref: "el",
         className: classList.join(" "),
         "data-id": item.id,
         tabIndex: 0,
         onContextMenu,
         onMouseDown,
       },
-      StatusColumn(item),
-      MethodColumn(item),
-      FileColumn(item),
-      DomainColumn(item, onSecurityIconClick),
-      CauseColumn(item),
-      TypeColumn(item),
-      TransferredSizeColumn(item),
-      ContentSizeColumn(item),
-      WaterfallColumn(item, firstRequestStartedMillis)
+      StatusColumn({ item }),
+      MethodColumn({ item }),
+      FileColumn({ item }),
+      DomainColumn({ item, onSecurityIconClick }),
+      CauseColumn({ item }),
+      TypeColumn({ item }),
+      TransferredSizeColumn({ item }),
+      ContentSizeColumn({ item }),
+      WaterfallColumn({ item, firstRequestStartedMillis })
     );
   }
 });
 
-/**
- * Used by shouldComponentUpdate: compare two items, and compare only properties
- * relevant for rendering the RequestListItem. Other properties (like request and
- * response headers, cookies, bodies) are ignored. These are very useful for the
- * sidebar details, but not here.
- */
-const RELEVANT_ITEM_PROPS = [
+const UPDATED_STATUS_PROPS = [
   "status",
   "statusText",
   "fromCache",
   "fromServiceWorker",
-  "method",
-  "url",
-  "responseContentDataUri",
-  "remoteAddress",
-  "securityState",
-  "cause",
-  "mimeType",
-  "contentSize",
-  "transferredSize",
-  "startedMillis",
-  "totalTime",
-  "eventTimings",
 ];
 
-function relevantPropsEqual(item1, item2) {
-  return item1 === item2 || RELEVANT_ITEM_PROPS.every(p => item1[p] === item2[p]);
-}
+const StatusColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return !propertiesEqual(UPDATED_STATUS_PROPS, this.props.item, nextProps.item);
+  },
 
-function StatusColumn(item) {
-  const { status, statusText, fromCache, fromServiceWorker } = item;
+  render() {
+    const { status, statusText, fromCache, fromServiceWorker } = this.props.item;
 
-  let code, title;
+    let code, title;
 
-  if (status) {
-    if (fromCache) {
-      code = "cached";
-    } else if (fromServiceWorker) {
-      code = "service worker";
-    } else {
-      code = status;
-    }
+    if (status) {
+      if (fromCache) {
+        code = "cached";
+      } else if (fromServiceWorker) {
+        code = "service worker";
+      } else {
+        code = status;
+      }
 
-    if (statusText) {
-      title = `${status} ${statusText}`;
-      if (fromCache) {
-        title += " (cached)";
-      }
-      if (fromServiceWorker) {
-        title += " (service worker)";
+      if (statusText) {
+        title = `${status} ${statusText}`;
+        if (fromCache) {
+          title += " (cached)";
+        }
+        if (fromServiceWorker) {
+          title += " (service worker)";
+        }
       }
     }
-  }
 
-  return div({ className: "requests-menu-subitem requests-menu-status", title },
-    div({ className: "requests-menu-status-icon", "data-code": code }),
-    span({ className: "subitem-label requests-menu-status-code" }, status)
-  );
-}
+    return div({ className: "requests-menu-subitem requests-menu-status", title },
+      div({ className: "requests-menu-status-icon", "data-code": code }),
+      span({ className: "subitem-label requests-menu-status-code" }, status)
+    );
+  }
+}));
+
+const MethodColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return this.props.item.method !== nextProps.item.method;
+  },
 
-function MethodColumn(item) {
-  const { method } = item;
-  return div({ className: "requests-menu-subitem requests-menu-method-box" },
-    span({ className: "subitem-label requests-menu-method" }, method)
-  );
-}
+  render() {
+    const { method } = this.props.item;
+    return div({ className: "requests-menu-subitem requests-menu-method-box" },
+      span({ className: "subitem-label requests-menu-method" }, method)
+    );
+  }
+}));
 
-function FileColumn(item) {
-  const { urlDetails, responseContentDataUri } = item;
+const UPDATED_FILE_PROPS = [
+  "urlDetails",
+  "responseContentDataUri",
+];
 
-  return div({ className: "requests-menu-subitem requests-menu-icon-and-file" },
-    img({
-      className: "requests-menu-icon",
-      src: responseContentDataUri,
-      hidden: !responseContentDataUri,
-      "data-type": responseContentDataUri ? "thumbnail" : undefined
-    }),
-    div(
-      {
-        className: "subitem-label requests-menu-file",
-        title: urlDetails.unicodeUrl
-      },
-      urlDetails.baseNameWithQuery
-    )
-  );
-}
+const FileColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return !propertiesEqual(UPDATED_FILE_PROPS, this.props.item, nextProps.item);
+  },
+
+  render() {
+    const { urlDetails, responseContentDataUri } = this.props.item;
 
-function DomainColumn(item, onSecurityIconClick) {
-  const { urlDetails, remoteAddress, securityState } = item;
+    return div({ className: "requests-menu-subitem requests-menu-icon-and-file" },
+      img({
+        className: "requests-menu-icon",
+        src: responseContentDataUri,
+        hidden: !responseContentDataUri,
+        "data-type": responseContentDataUri ? "thumbnail" : undefined
+      }),
+      div(
+        {
+          className: "subitem-label requests-menu-file",
+          title: urlDetails.unicodeUrl
+        },
+        urlDetails.baseNameWithQuery
+      )
+    );
+  }
+}));
+
+const UPDATED_DOMAIN_PROPS = [
+  "urlDetails",
+  "remoteAddress",
+  "securityState",
+];
 
-  let iconClassList = [ "requests-security-state-icon" ];
-  let iconTitle;
-  if (urlDetails.isLocal) {
-    iconClassList.push("security-state-local");
-    iconTitle = L10N.getStr("netmonitor.security.state.secure");
-  } else if (securityState) {
-    iconClassList.push(`security-state-${securityState}`);
-    iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
-  }
+const DomainColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return !propertiesEqual(UPDATED_DOMAIN_PROPS, this.props.item, nextProps.item);
+  },
+
+  render() {
+    const { item, onSecurityIconClick } = this.props;
+    const { urlDetails, remoteAddress, securityState } = item;
 
-  let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : "");
+    let iconClassList = [ "requests-security-state-icon" ];
+    let iconTitle;
+    if (urlDetails.isLocal) {
+      iconClassList.push("security-state-local");
+      iconTitle = L10N.getStr("netmonitor.security.state.secure");
+    } else if (securityState) {
+      iconClassList.push(`security-state-${securityState}`);
+      iconTitle = L10N.getStr(`netmonitor.security.state.${securityState}`);
+    }
+
+    let title = urlDetails.host + (remoteAddress ? ` (${remoteAddress})` : "");
 
-  return div(
-    { className: "requests-menu-subitem requests-menu-security-and-domain" },
-    div({
-      className: iconClassList.join(" "),
-      title: iconTitle,
-      onClick: onSecurityIconClick,
-    }),
-    span({ className: "subitem-label requests-menu-domain", title }, urlDetails.host)
-  );
-}
+    return div(
+      { className: "requests-menu-subitem requests-menu-security-and-domain" },
+      div({
+        className: iconClassList.join(" "),
+        title: iconTitle,
+        onClick: onSecurityIconClick,
+      }),
+      span({ className: "subitem-label requests-menu-domain", title }, urlDetails.host)
+    );
+  }
+}));
 
-function CauseColumn(item) {
-  const { cause } = item;
+const CauseColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return this.props.item.cause !== nextProps.item.cause;
+  },
 
-  let causeType = "";
-  let causeUri = undefined;
-  let causeHasStack = false;
+  render() {
+    const { cause } = this.props.item;
+
+    let causeType = "";
+    let causeUri = undefined;
+    let causeHasStack = false;
 
-  if (cause) {
-    causeType = cause.type;
-    causeUri = cause.loadingDocumentUri;
-    causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
-  }
+    if (cause) {
+      causeType = cause.type;
+      causeUri = cause.loadingDocumentUri;
+      causeHasStack = cause.stacktrace && cause.stacktrace.length > 0;
+    }
 
-  return div(
-    { className: "requests-menu-subitem requests-menu-cause", title: causeUri },
-    span({ className: "requests-menu-cause-stack", hidden: !causeHasStack }, "JS"),
-    span({ className: "subitem-label" }, causeType)
-  );
-}
+    return div(
+      { className: "requests-menu-subitem requests-menu-cause", title: causeUri },
+      span({ className: "requests-menu-cause-stack", hidden: !causeHasStack }, "JS"),
+      span({ className: "subitem-label" }, causeType)
+    );
+  }
+}));
 
 const CONTENT_MIME_TYPE_ABBREVIATIONS = {
   "ecmascript": "js",
   "javascript": "js",
   "x-javascript": "js"
 };
 
-function TypeColumn(item) {
-  const { mimeType } = item;
-  let abbrevType;
-  if (mimeType) {
-    abbrevType = getAbbreviatedMimeType(mimeType);
-    abbrevType = CONTENT_MIME_TYPE_ABBREVIATIONS[abbrevType] || abbrevType;
+const TypeColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return this.props.item.mimeType !== nextProps.item.mimeType;
+  },
+
+  render() {
+    const { mimeType } = this.props.item;
+    let abbrevType;
+    if (mimeType) {
+      abbrevType = getAbbreviatedMimeType(mimeType);
+      abbrevType = CONTENT_MIME_TYPE_ABBREVIATIONS[abbrevType] || abbrevType;
+    }
+
+    return div(
+      { className: "requests-menu-subitem requests-menu-type", title: mimeType },
+      span({ className: "subitem-label" }, abbrevType)
+    );
   }
+}));
 
-  return div(
-    { className: "requests-menu-subitem requests-menu-type", title: mimeType },
-    span({ className: "subitem-label" }, abbrevType)
-  );
-}
+const UPDATED_TRANSFERRED_PROPS = [
+  "transferredSize",
+  "fromCache",
+  "fromServiceWorker",
+];
+
+const TransferredSizeColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return !propertiesEqual(UPDATED_TRANSFERRED_PROPS, this.props.item, nextProps.item);
+  },
+
+  render() {
+    const { transferredSize, fromCache, fromServiceWorker } = this.props.item;
 
-function TransferredSizeColumn(item) {
-  const { transferredSize, fromCache, fromServiceWorker } = item;
+    let text;
+    let className = "subitem-label";
+    if (fromCache) {
+      text = L10N.getStr("networkMenu.sizeCached");
+      className += " theme-comment";
+    } else if (fromServiceWorker) {
+      text = L10N.getStr("networkMenu.sizeServiceWorker");
+      className += " theme-comment";
+    } else if (typeof transferredSize == "number") {
+      text = getFormattedSize(transferredSize);
+    } else if (transferredSize === null) {
+      text = L10N.getStr("networkMenu.sizeUnavailable");
+    }
 
-  let text;
-  let className = "subitem-label";
-  if (fromCache) {
-    text = L10N.getStr("networkMenu.sizeCached");
-    className += " theme-comment";
-  } else if (fromServiceWorker) {
-    text = L10N.getStr("networkMenu.sizeServiceWorker");
-    className += " theme-comment";
-  } else if (typeof transferredSize == "number") {
-    text = getFormattedSize(transferredSize);
-  } else if (transferredSize === null) {
-    text = L10N.getStr("networkMenu.sizeUnavailable");
+    return div(
+      { className: "requests-menu-subitem requests-menu-transferred", title: text },
+      span({ className }, text)
+    );
   }
+}));
+
+const ContentSizeColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return this.props.item.contentSize !== nextProps.item.contentSize;
+  },
+
+  render() {
+    const { contentSize } = this.props.item;
+
+    let text;
+    if (typeof contentSize == "number") {
+      text = getFormattedSize(contentSize);
+    }
 
-  return div(
-    { className: "requests-menu-subitem requests-menu-transferred", title: text },
-    span({ className }, text)
-  );
-}
+    return div(
+      {
+        className: "requests-menu-subitem subitem-label requests-menu-size",
+        title: text
+      },
+      span({ className: "subitem-label" }, text)
+    );
+  }
+}));
 
-function ContentSizeColumn(item) {
-  const { contentSize } = item;
+const UPDATED_WATERFALL_PROPS = [
+  "eventTimings",
+  "totalTime",
+  "fromCache",
+  "fromServiceWorker",
+];
 
-  let text;
-  if (typeof contentSize == "number") {
-    text = getFormattedSize(contentSize);
-  }
+const WaterfallColumn = createFactory(createClass({
+  shouldComponentUpdate(nextProps) {
+    return this.props.firstRequestStartedMillis !== nextProps.firstRequestStartedMillis ||
+           !propertiesEqual(UPDATED_WATERFALL_PROPS, this.props.item, nextProps.item);
+  },
 
-  return div(
-    { className: "requests-menu-subitem subitem-label requests-menu-size", title: text },
-    span({ className: "subitem-label" }, text)
-  );
-}
+  render() {
+    const { item, firstRequestStartedMillis } = this.props;
+    const startedDeltaMillis = item.startedMillis - firstRequestStartedMillis;
+    const paddingInlineStart = `${startedDeltaMillis}px`;
+
+    return div({ className: "requests-menu-subitem requests-menu-waterfall" },
+      div(
+        { className: "requests-menu-timings", style: { paddingInlineStart } },
+        timingBoxes(item)
+      )
+    );
+  }
+}));
 
 // List of properties of the timing info we want to create boxes for
 const TIMING_KEYS = ["blocked", "dns", "connect", "send", "wait", "receive"];
 
 function timingBoxes(item) {
   const { eventTimings, totalTime, fromCache, fromServiceWorker } = item;
   let boxes = [];
 
@@ -326,21 +436,9 @@ function timingBoxes(item) {
       className: "requests-menu-timings-total",
       title: text
     }, text));
   }
 
   return boxes;
 }
 
-function WaterfallColumn(item, firstRequestStartedMillis) {
-  const startedDeltaMillis = item.startedMillis - firstRequestStartedMillis;
-  const paddingInlineStart = `${startedDeltaMillis}px`;
-
-  return div({ className: "requests-menu-subitem requests-menu-waterfall" },
-    div(
-      { className: "requests-menu-timings", style: { paddingInlineStart } },
-      timingBoxes(item)
-    )
-  );
-}
-
 module.exports = RequestListItem;
--- a/devtools/client/netmonitor/netmonitor-controller.js
+++ b/devtools/client/netmonitor/netmonitor-controller.js
@@ -313,17 +313,65 @@ var NetMonitorController = {
            (this.tabClient.traits.reconfigure || !this._target.isApp);
   },
 
   /**
    * Open a given source in Debugger
    */
   viewSourceInDebugger(sourceURL, sourceLine) {
     return this._toolbox.viewSourceInDebugger(sourceURL, sourceLine);
-  }
+  },
+
+  /**
+   * Start monitoring all incoming update events about network requests and wait until
+   * a complete info about all requests is received. (We wait for the timings info
+   * explicitly, because that's always the last piece of information that is received.)
+   *
+   * This method is designed to wait for network requests that are issued during a page
+   * load, when retrieving page resources (scripts, styles, images). It has certain
+   * assumptions that can make it unsuitable for other types of network communication:
+   * - it waits for at least one network request to start and finish before returning
+   * - it waits only for request that were issued after it was called. Requests that are
+   *   already in mid-flight will be ignored.
+   * - the request start and end times are overlapping. If a new request starts a moment
+   *   after the previous one was finished, the wait will be ended in the "interim"
+   *   period.
+   * @returns a promise that resolves when the wait is done.
+   * TODO: should be unified with whenDataAvailable in netmonitor-view.js
+   */
+  waitForAllRequestsFinished() {
+    return new Promise(resolve => {
+      // Key is the request id, value is a boolean - is request finished or not?
+      let requests = new Map();
+
+      function onRequest(_, id) {
+        requests.set(id, false);
+      }
+
+      function onTimings(_, id) {
+        requests.set(id, true);
+        maybeResolve();
+      }
+
+      function maybeResolve() {
+        // Have all the requests in the map finished yet?
+        if (![...requests.values()].every(finished => finished)) {
+          return;
+        }
+
+        // All requests are done - unsubscribe from events and resolve!
+        window.off(EVENTS.NETWORK_EVENT, onRequest);
+        window.off(EVENTS.RECEIVED_EVENT_TIMINGS, onTimings);
+        resolve();
+      }
+
+      window.on(EVENTS.NETWORK_EVENT, onRequest);
+      window.on(EVENTS.RECEIVED_EVENT_TIMINGS, onTimings);
+    });
+  },
 };
 
 /**
  * Functions handling target-related lifetime events.
  */
 function TargetEventsHandler() {
   this._onTabNavigated = this._onTabNavigated.bind(this);
   this._onTabDetached = this._onTabDetached.bind(this);
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/editor.js
@@ -0,0 +1,92 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const { createClass, DOM, PropTypes } = require("devtools/client/shared/vendor/react");
+const SourceEditor = require("devtools/client/sourceeditor/editor");
+
+const { div } = DOM;
+
+/**
+ * CodeMirror editor as a React component
+ */
+const Editor = createClass({
+  displayName: "Editor",
+
+  propTypes: {
+    open: PropTypes.bool,
+    text: PropTypes.string,
+  },
+
+  getDefaultProps() {
+    return {
+      open: true,
+      text: "",
+    };
+  },
+
+  componentDidMount() {
+    const { text } = this.props;
+
+    this.editor = new SourceEditor({
+      lineNumbers: true,
+      readOnly: true,
+      value: text,
+    });
+
+    this.deferEditor = this.editor.appendTo(this.refs.editorElement);
+  },
+
+  componentDidUpdate(prevProps) {
+    const { mode, open, text } = this.props;
+
+    if (!open) {
+      return;
+    }
+
+    if (prevProps.mode !== mode) {
+      this.deferEditor.then(() => {
+        this.editor.setMode(mode);
+      });
+    }
+
+    if (prevProps.text !== text) {
+      this.deferEditor.then(() => {
+        // FIXME: Workaround for browser_net_accessibility test to
+        // make sure editor node exists while setting editor text.
+        // deferEditor workround should be removed in bug 1308442
+        if (this.refs.editor) {
+          this.editor.setText(text);
+        }
+      });
+    }
+  },
+
+  componentWillUnmount() {
+    this.deferEditor.then(() => {
+      this.editor.destroy();
+      this.editor = null;
+    });
+    this.deferEditor = null;
+  },
+
+  render() {
+    const { open } = this.props;
+
+    return (
+      div({ className: "editor-container devtools-monospace" },
+        div({
+          ref: "editorElement",
+          className: "editor-mount devtools-monospace",
+          // Using visibility instead of display property to avoid breaking
+          // CodeMirror indentation
+          style: { visibility: open ? "visible" : "hidden" },
+        }),
+      )
+    );
+  }
+});
+
+module.exports = Editor;
--- a/devtools/client/netmonitor/shared/components/moz.build
+++ b/devtools/client/netmonitor/shared/components/moz.build
@@ -1,9 +1,11 @@
 # 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/.
 
 DevToolsModules(
+    'editor.js',
     'preview-panel.js',
+    'properties-view.js',
     'security-panel.js',
     'timings-panel.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/shared/components/properties-view.js
@@ -0,0 +1,162 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const {
+  createClass,
+  createFactory,
+  DOM,
+  PropTypes,
+} = require("devtools/client/shared/vendor/react");
+const { createFactories } = require("devtools/client/shared/components/reps/rep-utils");
+const { MODE } = require("devtools/client/shared/components/reps/constants");
+const { FILTER_SEARCH_DELAY } = require("../../constants");
+
+// Components
+const Editor = createFactory(require("devtools/client/netmonitor/shared/components/editor"));
+const SearchBox = createFactory(require("devtools/client/shared/components/search-box"));
+const TreeView = createFactory(require("devtools/client/shared/components/tree/tree-view"));
+const TreeRow = createFactory(require("devtools/client/shared/components/tree/tree-row"));
+const { Rep } = createFactories(require("devtools/client/shared/components/reps/rep"));
+
+const { div, tr, td } = DOM;
+
+/*
+ * Properties View component
+ * A scrollable tree view component which provides some useful features for
+ * representing object properties.
+ *
+ * Search filter - Set enableFilter to enable / disable SearchBox feature.
+ * Tree view - Default enabled.
+ * Source editor - Enable by specifying object level 1 property name to "editorText".
+ * Rep - Default enabled.
+ */
+const PropertiesView = createClass({
+  displayName: "PropertiesView",
+
+  propTypes: {
+    object: PropTypes.object,
+    enableInput: PropTypes.bool,
+    expandableStrings: PropTypes.bool,
+    filterPlaceHolder: PropTypes.string,
+    sectionNames: PropTypes.array,
+  },
+
+  getDefaultProps() {
+    return {
+      enableInput: true,
+      enableFilter: true,
+      expandableStrings: false,
+      filterPlaceHolder: "",
+    };
+  },
+
+  getInitialState() {
+    return {
+      filterText: "",
+    };
+  },
+
+  getRowClass(object, sectionNames) {
+    return sectionNames.includes(object.name) ? "tree-section" : "";
+  },
+
+  onFilter(object, whiteList) {
+    let { name, value } = object;
+    let filterText = this.state.filterText;
+
+    if (!filterText || whiteList.includes(name)) {
+      return true;
+    }
+
+    let jsonString = JSON.stringify({ [name]: value }).toLowerCase();
+    return jsonString.includes(filterText.toLowerCase());
+  },
+
+  renderRowWithEditor(props) {
+    const { level, name, value } = props.member;
+    // Display source editor when prop name specify to editorText
+    if (level === 1 && name === "editorText") {
+      return (
+        tr({},
+          td({ colSpan: 2 },
+            Editor({ text: value })
+          )
+        )
+      );
+    }
+
+    return TreeRow(props);
+  },
+
+  renderValueWithRep(props) {
+    // Hide rep summary for sections
+    if (props.member.level === 0) {
+      return null;
+    }
+
+    return Rep(Object.assign(props, {
+      // FIXME: A workaround for the issue in StringRep
+      // Force StringRep to crop the text everytime
+      member: Object.assign({}, props.member, { open: false }),
+      mode: MODE.TINY,
+      cropLimit: 60,
+    }));
+  },
+
+  updateFilterText(filterText) {
+    this.setState({
+      filterText,
+    });
+  },
+
+  render() {
+    const {
+      object,
+      decorator,
+      enableInput,
+      enableFilter,
+      expandableStrings,
+      filterPlaceHolder,
+      renderRow,
+      renderValue,
+      sectionNames,
+    } = this.props;
+
+    return (
+      div({ className: "properties-view" },
+        enableFilter && div({ className: "searchbox-section" },
+          SearchBox({
+            delay: FILTER_SEARCH_DELAY,
+            type: "filter",
+            onChange: this.updateFilterText,
+            placeholder: filterPlaceHolder,
+          }),
+        ),
+        div({ className: "tree-container" },
+          TreeView({
+            object,
+            columns: [{
+              id: "value",
+              width: "100%",
+            }],
+            decorator: decorator || {
+              getRowClass: (rowObject) => this.getRowClass(rowObject, sectionNames),
+            },
+            enableInput,
+            expandableStrings,
+            expandedNodes: new Set(sectionNames.map((sec) => "/" + sec)),
+            onFilter: (props) => this.onFilter(props, sectionNames),
+            renderRow: renderRow || this.renderRowWithEditor,
+            renderValue: renderValue || this.renderValueWithRep,
+          }),
+        ),
+      )
+    );
+  }
+
+});
+
+module.exports = PropertiesView;
--- a/devtools/client/responsive.html/components/viewport-toolbar.js
+++ b/devtools/client/responsive.html/components/viewport-toolbar.js
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { DOM: dom, createClass, createFactory, PropTypes, addons } =
   require("devtools/client/shared/vendor/react");
 
+const { getStr } = require("../utils/l10n");
 const Types = require("../types");
 const DeviceSelector = createFactory(require("./device-selector"));
 
 module.exports = createClass({
   displayName: "ViewportToolbar",
 
   propTypes: {
     devices: PropTypes.shape(Types.devices).isRequired,
@@ -43,13 +44,14 @@ module.exports = createClass({
         selectedDevice,
         onChangeViewportDevice,
         onResizeViewport,
         onUpdateDeviceModalOpen,
       }),
       dom.button({
         className: "viewport-rotate-button toolbar-button devtools-button",
         onClick: onRotateViewport,
+        title: getStr("responsive.rotate"),
       })
     );
   },
 
 });
--- a/devtools/client/shared/components/tree/tree-cell.js
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -5,42 +5,49 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Make this available to both AMD and CJS environments
 define(function (require, exports, module) {
   const React = require("devtools/client/shared/vendor/react");
 
   // Shortcuts
-  const { td, span } = React.DOM;
+  const { input, span, td } = React.DOM;
   const PropTypes = React.PropTypes;
 
   /**
    * This template represents a cell in TreeView row. It's rendered
    * using <td> element (the row is <tr> and the entire tree is <table>).
    */
   let TreeCell = React.createClass({
     displayName: "TreeCell",
 
     // See TreeView component for detailed property explanation.
     propTypes: {
       value: PropTypes.any,
       decorator: PropTypes.object,
       id: PropTypes.string.isRequired,
       member: PropTypes.object.isRequired,
-      renderValue: PropTypes.func.isRequired
+      renderValue: PropTypes.func.isRequired,
+      enableInput: PropTypes.bool,
+    },
+
+    getInitialState: function () {
+      return {
+        inputEnabled: false,
+      };
     },
 
     /**
      * Optimize cell rendering. Rerender cell content only if
      * the value or expanded state changes.
      */
-    shouldComponentUpdate: function (nextProps) {
+    shouldComponentUpdate: function (nextProps, nextState) {
       return (this.props.value != nextProps.value) ||
-        (this.props.member.open != nextProps.member.open);
+        (this.state !== nextState);
     },
 
     getCellClass: function (object, id) {
       let decorator = this.props.decorator;
       if (!decorator || !decorator.getCellClass) {
         return [];
       }
 
@@ -52,43 +59,70 @@ define(function (require, exports, modul
 
       if (typeof classNames == "string") {
         classNames = [classNames];
       }
 
       return classNames;
     },
 
+    updateInputEnabled: function (evt) {
+      this.setState(Object.assign({}, this.state, {
+        inputEnabled: evt.target.nodeName !== "input",
+      }));
+    },
+
     render: function () {
-      let member = this.props.member;
+      let {
+        member,
+        id,
+        value,
+        decorator,
+        renderValue,
+        enableInput,
+      } = this.props;
       let type = member.type || "";
-      let id = this.props.id;
-      let value = this.props.value;
-      let decorator = this.props.decorator;
 
       // Compute class name list for the <td> element.
       let classNames = this.getCellClass(member.object, id) || [];
       classNames.push("treeValueCell");
       classNames.push(type + "Cell");
 
       // Render value using a default render function or custom
       // provided function from props or a decorator.
-      let renderValue = this.props.renderValue || defaultRenderValue;
+      renderValue = renderValue || defaultRenderValue;
       if (decorator && decorator.renderValue) {
         renderValue = decorator.renderValue(member.object, id) || renderValue;
       }
 
       let props = Object.assign({}, this.props, {
         object: value,
       });
 
+      let cellElement;
+      if (enableInput && this.state.inputEnabled && type !== "object") {
+        classNames.push("inputEnabled");
+        cellElement = input({
+          autoFocus: true,
+          onBlur: this.updateInputEnabled,
+          readOnly: true,
+          value,
+        });
+      } else {
+        cellElement = span({
+          onClick: (type !== "object") ? this.updateInputEnabled : null,
+        },
+          renderValue(props)
+        );
+      }
+
       // Render me!
       return (
         td({ className: classNames.join(" ") },
-          span({}, renderValue(props))
+          cellElement
         )
       );
     }
   });
 
   // Default value rendering.
   let defaultRenderValue = props => {
     return (
--- a/devtools/client/shared/components/tree/tree-header.js
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -75,19 +75,19 @@ define(function (require, exports, modul
           classNames.push("treeHeaderCell");
         }
 
         cells.push(
           td({
             className: classNames.join(" "),
             style: cellStyle,
             key: col.id},
-            div({ className: visible ? "treeHeaderCellBox" : "" },
-              visible ? col.title : ""
-            )
+            visible ? div({ className: "treeHeaderCellBox"},
+              col.title
+            ) : null,
           )
         );
       });
 
       return (
         thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
           cells
         ))
--- a/devtools/client/shared/components/tree/tree-view.css
+++ b/devtools/client/shared/components/tree/tree-view.css
@@ -29,16 +29,35 @@
 }
 
 .treeTable .treeValueCell {
   padding: 2px 0;
   padding-inline-start: 5px;
   overflow: hidden;
 }
 
+.treeTable .treeValueCell.inputEnabled {
+  padding-top: 0;
+  padding-bottom: 0;
+}
+
+.treeTable .treeValueCell input {
+  width: 100%;
+  background: none;
+  border: none;
+  color: inherit;
+  margin-inline-end: 2px;
+}
+
+.treeTable .treeValueCell input:focus {
+  outline: none;
+  box-shadow: var(--theme-focus-box-shadow-textbox);
+  transition: all 0.2s ease-in-out;
+}
+
 .treeTable .treeLabel {
   cursor: default;
   overflow: hidden;
   padding-inline-start: 4px;
   white-space: nowrap;
   unicode-bidi: -moz-plaintext;
 }
 
--- a/devtools/client/shared/test/unit/test_parseDeclarations.js
+++ b/devtools/client/shared/test/unit/test_parseDeclarations.js
@@ -1,17 +1,21 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {require} = Components.utils.import("resource://devtools/shared/Loader.jsm", {});
-const {parseDeclarations, _parseCommentDeclarations} = require("devtools/shared/css/parsing-utils");
+const {
+  parseDeclarations,
+  _parseCommentDeclarations,
+  parseNamedDeclarations
+} = require("devtools/shared/css/parsing-utils");
 const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
 
 const TEST_DATA = [
   // Simple test
   {
     input: "p:v;",
     expected: [{name: "p", value: "v", priority: "", offsets: [0, 4]}]
   },
@@ -361,16 +365,17 @@ const TEST_DATA = [
     input: "stroke-dasharray: 1/*ThisIsAComment*/2;",
     expected: [{name: "stroke-dasharray", value: "1 2", priority: "", offsets: [0, 39]}]
   },
 ];
 
 function run_test() {
   run_basic_tests();
   run_comment_tests();
+  run_named_tests();
 }
 
 // Test parseDeclarations.
 function run_basic_tests() {
   for (let test of TEST_DATA) {
     do_print("Test input string " + test.input);
     let output;
     try {
@@ -411,16 +416,38 @@ function run_comment_tests() {
   for (let test of COMMENT_DATA) {
     do_print("Test input string " + test.input);
     let output = _parseCommentDeclarations(isCssPropertyKnown, test.input, 0,
                                            test.input.length + 4);
     deepEqual(output, test.expected);
   }
 }
 
+const NAMED_DATA = [
+  {
+    input: "position:absolute;top50px;height:50px;",
+    expected: [
+      {name: "position", value: "absolute", priority: "", terminator: "",
+       offsets: [0, 18], colonOffsets: [8, 9]},
+      {name: "height", value: "50px", priority: "", terminator: "",
+       offsets: [26, 38], colonOffsets: [32, 33]}
+    ],
+  },
+];
+
+// Test parseNamedDeclarations.
+function run_named_tests() {
+  for (let test of NAMED_DATA) {
+    do_print("Test input string " + test.input);
+    let output = parseNamedDeclarations(isCssPropertyKnown, test.input, true);
+    do_print(JSON.stringify(output));
+    deepEqual(output, test.expected);
+  }
+}
+
 function assertOutput(actual, expected) {
   if (actual.length === expected.length) {
     for (let i = 0; i < expected.length; i++) {
       do_check_true(!!actual[i]);
       do_print("Check that the output item has the expected name, " +
         "value and priority");
       do_check_eq(expected[i].name, actual[i].name);
       do_check_eq(expected[i].value, actual[i].value);
--- a/devtools/client/shared/test/unit/test_rewriteDeclarations.js
+++ b/devtools/client/shared/test/unit/test_rewriteDeclarations.js
@@ -468,16 +468,24 @@ const TEST_DATA = [
     expected: "\n  a:b;\n  e:f;",
   },
   {
     desc: "delete disabled property leaving other disabled property",
     input: "\n  a:b;\n  /* color:#f0c; background-color: seagreen; */\n  e:f;",
     instruction: {type: "remove", name: "color", index: 1},
     expected: "\n  a:b;\n   /* background-color: seagreen; */\n  e:f;",
   },
+
+  {
+    desc: "regression test for bug 1328016",
+    input: "position:absolute;top50px;height:50px;width:50px;",
+    instruction: {type: "set", name: "width", value: "60px", priority: "",
+                  index: 2},
+    expected: "position:absolute;top50px;height:50px;width:60px;",
+  },
 ];
 
 function rewriteDeclarations(inputString, instruction, defaultIndentation) {
   let rewriter = new RuleRewriter(isCssPropertyKnown, null, inputString);
   rewriter.defaultIndentation = defaultIndentation;
 
   switch (instruction.type) {
     case "rename":
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -1097,52 +1097,101 @@
   content: "";
 }
 
 /* Layout additional warning icon in tree value cell  */
 .security-info-value {
   display: flex;
 }
 
-.security-info-value .textbox-input {
+.treeTable .textbox-input {
   text-overflow: ellipsis;
   border: none;
   background: none;
   color: inherit;
   width: 100%;
   margin-inline-end: 2px;
 }
 
-.security-info-value .textbox-input:focus {
+.treeTable .textbox-input:focus {
   outline: 0;
   box-shadow: var(--theme-focus-box-shadow-textbox);
 }
 
 .treeTable .treeLabel {
   font-weight: 600;
 }
 
-/* Customize default tree table style to align with devtools theme  */
-.theme-light .treeTable .treeLabel,
-.theme-light .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
-  color: var(--theme-highlight-red);
+.properties-view {
+  /* FIXME: Minus 24px * 2 for toolbox height + panel height
+   * Give a fixed panel container height in order to force tree view scrollable */
+  height: calc(100vh - 48px);
+  display: flex;
+  flex-direction: column;
+}
+
+.properties-view .searchbox-section {
+  flex: 0 1 auto;
+}
+
+.properties-view .devtools-searchbox {
+  padding: 0;
+}
+
+.properties-view .devtools-searchbox input {
+  margin: 1px 3px;
+}
+
+.tree-container {
+  position: relative;
+  height: 100%;
 }
 
-.theme-dark .treeTable .treeLabel,
-.theme-dark .treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
-  color: var(--theme-highlight-purple);
+/* Make treeTable fill parent element and scrollable */
+.tree-container .treeTable {
+  position: absolute;
+  display: block;
+  overflow-y: auto;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+}
+
+.properties-view .devtools-searchbox,
+.tree-container .treeTable .tree-section {
+  width: 100%;
+  background-color: var(--theme-toolbar-background);
+}
+
+.tree-container .treeTable .tree-section > * {
+  vertical-align: middle;
 }
 
-.theme-firebug .treeTable .treeLabel {
-  color: var(--theme-body-color);
+.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel,
+.tree-container .treeTable .treeRow.tree-section > .treeLabelCell > .treeLabel:hover {
+  color: var(--theme-body-color-alt);
+}
+
+.tree-container .treeTable .treeValueCell {
+  /* FIXME: Make value cell can be reduced to shorter width */
+  max-width: 0;
+  padding-inline-end: 5px;
 }
 
-.treeTable .treeRow.hasChildren > .treeLabelCell > .treeLabel:hover {
-  cursor: default;
-  text-decoration: none;
+.tree-container .objectBox {
+  white-space: nowrap;
+}
+
+.editor-container,
+.editor-mount,
+.editor-mount iframe {
+  border: none;
+  width: 100%;
+  height: 100%;
 }
 
 /*
  * FIXME: normal html block element cannot fill outer XUL element
  * This workaround should be removed after netmonitor is migrated to react
  */
 #react-preview-tabpanel-hook,
 #react-security-tabpanel-hook,
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -4,17 +4,17 @@
 
 "use strict";
 
 const {Cc, Ci} = require("chrome");
 const promise = require("promise");
 const protocol = require("devtools/shared/protocol");
 const {LongStringActor} = require("devtools/server/actors/string");
 const {getDefinedGeometryProperties} = require("devtools/server/actors/highlighters/geometry-editor");
-const {parseDeclarations} = require("devtools/shared/css/parsing-utils");
+const {parseNamedDeclarations} = require("devtools/shared/css/parsing-utils");
 const {isCssPropertyKnown} = require("devtools/server/actors/css-properties");
 const {Task} = require("devtools/shared/task");
 const events = require("sdk/event/core");
 
 // This will also add the "stylesheet" actor type for protocol.js to recognize
 const {UPDATE_PRESERVING_RULES, UPDATE_GENERAL} = require("devtools/server/actors/stylesheets");
 const {pageStyleSpec, styleRuleSpec, ELEMENT_STYLE} = require("devtools/shared/specs/styles");
 
@@ -1090,19 +1090,19 @@ var StyleRuleActor = protocol.ActorClass
         form.keyText = this.rawRule.keyText || "";
         break;
     }
 
     // Parse the text into a list of declarations so the client doesn't have to
     // and so that we can safely determine if a declaration is valid rather than
     // have the client guess it.
     if (form.authoredText || form.cssText) {
-      let declarations = parseDeclarations(isCssPropertyKnown,
-                                           form.authoredText || form.cssText,
-                                           true);
+      let declarations = parseNamedDeclarations(isCssPropertyKnown,
+                                                form.authoredText || form.cssText,
+                                                true);
       form.declarations = declarations.map(decl => {
         decl.isValid = DOMUtils.cssPropertyIsValid(decl.name, decl.value);
         return decl;
       });
     }
 
     return form;
   },
--- a/devtools/shared/css/parsing-utils.js
+++ b/devtools/shared/css/parsing-utils.js
@@ -444,19 +444,29 @@ function parseDeclarationsInternal(isCss
  */
 function parseDeclarations(isCssPropertyKnown, inputString,
                            parseComments = false) {
   return parseDeclarationsInternal(isCssPropertyKnown, inputString,
                                    parseComments, false, false);
 }
 
 /**
+ * Like @see parseDeclarations, but removes properties that do not
+ * have a name.
+ */
+function parseNamedDeclarations(isCssPropertyKnown, inputString,
+                                parseComments = false) {
+  return parseDeclarations(isCssPropertyKnown, inputString, parseComments)
+         .filter(item => !!item.name);
+}
+
+/**
  * Return an object that can be used to rewrite declarations in some
  * source text.  The source text and parsing are handled in the same
- * way as @see parseDeclarations, with |parseComments| being true.
+ * way as @see parseNamedDeclarations, with |parseComments| being true.
  * Rewriting is done by calling one of the modification functions like
  * setPropertyEnabled.  The returned object has the same interface
  * as @see RuleModificationList.
  *
  * An example showing how to disable the 3rd property in a rule:
  *
  *    let rewriter = new RuleRewriter(isCssPropertyKnown, ruleActor,
  *                                    ruleActor.authoredText);
@@ -514,18 +524,18 @@ RuleRewriter.prototype = {
    *
    * @param {String} inputString the input to use
    */
   startInitialization: function (inputString) {
     this.inputString = inputString;
     // Whether there are any newlines in the input text.
     this.hasNewLine = /[\r\n]/.test(this.inputString);
     // The declarations.
-    this.declarations = parseDeclarations(this.isCssPropertyKnown, this.inputString,
-                                          true);
+    this.declarations = parseNamedDeclarations(this.isCssPropertyKnown, this.inputString,
+                                               true);
     this.decl = null;
     this.result = null;
   },
 
   /**
    * An internal function to complete initialization and set some
    * properties for further processing.
    *
@@ -1158,14 +1168,15 @@ function getAngleValueInDegrees(angleVal
 }
 
 exports.cssTokenizer = cssTokenizer;
 exports.cssTokenizerWithLineColumn = cssTokenizerWithLineColumn;
 exports.escapeCSSComment = escapeCSSComment;
 // unescapeCSSComment is exported for testing.
 exports._unescapeCSSComment = unescapeCSSComment;
 exports.parseDeclarations = parseDeclarations;
+exports.parseNamedDeclarations = parseNamedDeclarations;
 // parseCommentDeclarations is exported for testing.
 exports._parseCommentDeclarations = parseCommentDeclarations;
 exports.RuleRewriter = RuleRewriter;
 exports.parsePseudoClassesAndAttributes = parsePseudoClassesAndAttributes;
 exports.parseSingleValue = parseSingleValue;
 exports.getAngleValueInDegrees = getAngleValueInDegrees;
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -1395,16 +1395,23 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Fr
   nsIDocument* doc = tmp->OwnerDoc();
   doc->BindingManager()->RemovedFromDocument(tmp, doc,
                                              nsBindingManager::eDoNotRunDtor);
 
   // Unlink any DOM slots of interest.
   {
     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
     if (slots) {
+      if (tmp->IsElement()) {
+        Element* elem = tmp->AsElement();
+        for (auto iter = slots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
+          DOMIntersectionObserver* observer = iter.Key();
+          observer->UnlinkTarget(*elem);
+        }
+      }
       slots->Unlink(tmp->IsXULElement());
     }
   }
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -711,21 +711,30 @@ TimeoutManager::RescheduleTimeout(Timeou
   // And make sure delay is nonnegative; that might happen if the timer
   // thread is firing our timers somewhat early or if they're taking a long
   // time to run the callback.
   if (delay < TimeDuration(0)) {
     delay = TimeDuration(0);
   }
 
   if (!aTimeout->mTimer) {
-    NS_ASSERTION(mWindow.IsFrozen() || mWindow.IsSuspended(),
-                 "How'd our timer end up null if we're not frozen or "
-                 "suspended?");
-
-    aTimeout->mTimeRemaining = delay;
+    if (mWindow.IsFrozen()) {
+      // If we are frozen simply set timeout->mTimeRemaining to be the
+      // "time remaining" in the timeout (i.e., the interval itself).  This
+      // will be used to create a new mWhen time when the window is thawed.
+      // The end effect is that time does not appear to pass for frozen windows.
+      aTimeout->mTimeRemaining = delay;
+    } else if (mWindow.IsSuspended()) {
+    // Since we are not frozen we must set a precise mWhen target wakeup
+    // time.  Even if we are suspended we want to use this target time so
+    // that it appears time passes while suspended.
+      aTimeout->mWhen = currentNow + delay;
+    } else {
+      MOZ_ASSERT_UNREACHABLE("Window should be frozen or suspended.");
+    }
     return true;
   }
 
   aTimeout->mWhen = currentNow + delay;
 
   // Reschedule the OS timer. Don't bother returning any error codes if
   // this fails since the callers of this method don't care about them.
   nsresult rv = aTimeout->InitTimer(mWindow.EventTargetFor(TaskCategory::Timer),
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -9718,17 +9718,17 @@ nsContentUtils::AttemptLargeAllocationLo
 
   nsIDocShell* docShell = outer->GetDocShell();
   if (!docShell->GetIsOnlyToplevelInTabGroup()) {
     if (doc) {
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM"),
                                       doc,
                                       nsContentUtils::eDOM_PROPERTIES,
-                                      "LargeAllocationRelatedBrowsingContexts");
+                                      "LargeAllocationNotOnlyToplevelInTabGroup");
     }
     return false;
   }
 
   // Get the request method, and check if it is a GET request. If it is not GET,
   // then we cannot perform a large allocation load.
   nsAutoCString requestMethod;
   rv = aChannel->GetRequestMethod(requestMethod);
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -726,17 +726,17 @@ IsInsideRuby(nsINode* aNode)
 static bool
 IsSelectionInsideRuby(nsISelection* aSelection)
 {
   int32_t rangeCount;
   nsresult rv = aSelection->GetRangeCount(&rangeCount);
   if (NS_FAILED(rv)) {
     return false;
   }
-  for (auto i : MakeRange(rangeCount)) {
+  for (auto i : IntegerRange(rangeCount)) {
     nsCOMPtr<nsIDOMRange> range;
     aSelection->GetRangeAt(i, getter_AddRefs(range));
     nsCOMPtr<nsIDOMNode> node;
     range->GetCommonAncestorContainer(getter_AddRefs(node));
     nsCOMPtr<nsINode> n = do_QueryInterface(node);
     if (!IsInsideRuby(n)) {
       return false;
     }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -128,18 +128,17 @@ using namespace mozilla::dom;
     eDOMClassInfo_##_class##_id,
 #else
 #define NS_DEFINE_CLASSINFO_DATA_DEBUG(_class)                                \
   // nothing
 #endif
 
 #define NS_DEFINE_CLASSINFO_DATA_HELPER(_class, _helper, _flags,              \
                                         _chromeOnly, _allowXBL)               \
-  { #_class,                                                                  \
-    nullptr,                                                                  \
+  { nullptr,                                                                  \
     XPC_MAKE_CLASS_OPS(_flags),                                               \
     XPC_MAKE_CLASS(#_class, _flags,                                           \
                    &sClassInfoData[eDOMClassInfo_##_class##_id].mClassOps),   \
     _helper::doCreate,                                                        \
     nullptr,                                                                  \
     nullptr,                                                                  \
     nullptr,                                                                  \
     _flags,                                                                   \
@@ -672,17 +671,17 @@ nsDOMClassInfo::Init()
   int32_t i;
 
   for (i = 0; i < eDOMClassInfoIDCount; ++i) {
     if (i == eDOMClassInfo_DOMPrototype_id) {
       continue;
     }
 
     nsDOMClassInfoData& data = sClassInfoData[i];
-    nameSpaceManager->RegisterClassName(data.mName, i, data.mChromeOnly,
+    nameSpaceManager->RegisterClassName(data.mClass.name, i, data.mChromeOnly,
                                         data.mAllowXBL, &data.mNameUTF16);
   }
 
   for (i = 0; i < eDOMClassInfoIDCount; ++i) {
     RegisterClassProtos(i);
   }
 
   sIsInitialized = true;
@@ -770,17 +769,17 @@ nsDOMClassInfo::GetFlags(uint32_t *aFlag
   return NS_OK;
 }
 
 // nsIXPCScriptable
 
 NS_IMETHODIMP
 nsDOMClassInfo::GetClassName(char **aClassName)
 {
-  *aClassName = NS_strdup(mData->mName);
+  *aClassName = NS_strdup(mData->mClass.name);
 
   return NS_OK;
 }
 
 // virtual
 uint32_t
 nsDOMClassInfo::GetScriptableFlags()
 {
@@ -849,17 +848,17 @@ nsDOMClassInfo::Resolve(nsIXPConnectWrap
   if (id != sConstructor_id) {
     *resolvedp = false;
     return NS_OK;
   }
 
   JS::Rooted<JSObject*> global(cx, ::JS_GetGlobalForObject(cx, obj));
 
   JS::Rooted<JS::PropertyDescriptor> desc(cx);
-  if (!JS_GetPropertyDescriptor(cx, global, mData->mName, &desc)) {
+  if (!JS_GetPropertyDescriptor(cx, global, mData->mClass.name, &desc)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (desc.object() && !desc.hasGetterOrSetter() && desc.value().isObject()) {
     // If val is not an (non-null) object there either is no
     // constructor for this class, or someone messed with
     // window.classname, just fall through and let the JS engine
     // return the Object constructor.
@@ -952,17 +951,17 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
     if (iim) {
       nsCOMPtr<nsIInterfaceInfo> if_info;
       iim->GetInfoForIID(mData->mProtoChainInterface,
                          getter_AddRefs(if_info));
 
       if (if_info) {
         nsXPIDLCString name;
         if_info->GetName(getter_Copies(name));
-        NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), mData->mName) == 0,
+        NS_ASSERTION(nsCRT::strcmp(CutPrefix(name), mData->mClass.name) == 0,
                      "Class name and proto chain interface name mismatch!");
       }
     }
   }
 #endif
 
   // Make prototype delegation work correctly. Consider if a site sets
   // HTMLElement.prototype.foopy = function () { ... } Now, calling
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -26,19 +26,17 @@ struct nsDOMClassInfoData;
 
 typedef nsIClassInfo* (*nsDOMClassInfoConstructorFnc)
   (nsDOMClassInfoData* aData);
 
 typedef nsresult (*nsDOMConstructorFunc)(nsISupports** aNewObject);
 
 struct nsDOMClassInfoData
 {
-  // XXX: mName is the same as the name gettable from the callback. This
-  // redundancy should be removed eventually.
-  const char *mName;
+  // The ASCII name is available as mClass.name.
   const char16_t *mNameUTF16;
   const js::ClassOps mClassOps;
   const js::Class mClass;
   nsDOMClassInfoConstructorFnc mConstructorFptr;
 
   nsIClassInfo *mCachedClassInfo;
   const nsIID *mProtoChainInterface;
   const nsIID **mInterfaces;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -7913,50 +7913,36 @@ nsDocument::IsScriptEnabled()
   }
 
   return xpc::Scriptability::Get(globalObject->GetGlobalJSObject()).Allowed();
 }
 
 nsRadioGroupStruct*
 nsDocument::GetRadioGroupInternal(const nsAString& aName) const
 {
-#ifdef DEBUG
-  if (IsHTMLDocument()) {
-    nsAutoString lcName;
-    ToLowerCase(aName, lcName);
-    MOZ_ASSERT(aName == lcName);
-  }
-#endif
-
   nsRadioGroupStruct* radioGroup;
   if (!mRadioGroups.Get(aName, &radioGroup)) {
     return nullptr;
   }
 
   return radioGroup;
 }
 
 nsRadioGroupStruct*
 nsDocument::GetRadioGroup(const nsAString& aName) const
 {
   nsAutoString tmKey(aName);
-  if (IsHTMLDocument()) {
-    ToLowerCase(tmKey); //should case-insensitive.
-  }
 
   return GetRadioGroupInternal(tmKey);
 }
 
 nsRadioGroupStruct*
 nsDocument::GetOrCreateRadioGroup(const nsAString& aName)
 {
   nsAutoString tmKey(aName);
-  if (IsHTMLDocument()) {
-    ToLowerCase(tmKey); //should case-insensitive.
-  }
 
   if (nsRadioGroupStruct* radioGroup = GetRadioGroupInternal(tmKey)) {
     return radioGroup;
   }
 
   nsAutoPtr<nsRadioGroupStruct> newRadioGroup(new nsRadioGroupStruct());
   mRadioGroups.Put(tmKey, newRadioGroup);
 
@@ -9989,18 +9975,18 @@ nsDocument::FindImageMap(const nsAString
   }
 
   uint32_t i, n = mImageMaps->Length(true);
   nsString name;
   for (i = 0; i < n; ++i) {
     nsIContent* map = mImageMaps->Item(i);
     if (map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, mapName,
                          eCaseMatters) ||
-        (map->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name) &&
-         mapName.Equals(name, nsCaseInsensitiveStringComparator()))) {
+        map->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, mapName,
+                         eCaseMatters)) {
       return map->AsElement();
     }
   }
 
   return nullptr;
 }
 
 #define DEPRECATED_OPERATION(_op) #_op "Warning",
@@ -10800,17 +10786,17 @@ nsDocument::RestorePreviousFullScreenSta
     AskWindowToExitFullscreen(this);
     return;
   }
 
   // If fullscreen mode is updated the pointer should be unlocked
   UnlockPointer();
   // All documents listed in the array except the last one are going to
   // completely exit from the fullscreen state.
-  for (auto i : MakeRange(exitDocs.Length() - 1)) {
+  for (auto i : IntegerRange(exitDocs.Length() - 1)) {
     exitDocs[i]->CleanupFullscreenState();
   }
   // The last document will either rollback one fullscreen element, or
   // completely exit from the fullscreen state as well.
   nsIDocument* newFullscreenDoc;
   if (lastDoc->mFullScreenStack.Length() > 1) {
     lastDoc->FullScreenStackPop();
     newFullscreenDoc = lastDoc;
--- a/dom/base/nsNodeUtils.cpp
+++ b/dom/base/nsNodeUtils.cpp
@@ -292,26 +292,16 @@ nsNodeUtils::LastRelease(nsINode* aNode)
   nsINode::nsSlots* slots = aNode->GetExistingSlots();
   if (slots) {
     if (!slots->mMutationObservers.IsEmpty()) {
       NS_OBSERVER_ARRAY_NOTIFY_OBSERVERS(slots->mMutationObservers,
                                          nsIMutationObserver,
                                          NodeWillBeDestroyed, (aNode));
     }
 
-    if (aNode->IsElement()) {
-      Element* elem = aNode->AsElement();
-      FragmentOrElement::nsDOMSlots* domSlots =
-        static_cast<FragmentOrElement::nsDOMSlots*>(slots);
-      for (auto iter = domSlots->mRegisteredIntersectionObservers.Iter(); !iter.Done(); iter.Next()) {
-        DOMIntersectionObserver* observer = iter.Key();
-        observer->UnlinkTarget(*elem);
-      }
-    }
-
     delete slots;
     aNode->mSlots = nullptr;
   }
 
   // Kill properties first since that may run external code, so we want to
   // be in as complete state as possible at that time.
   if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {
     // Delete all properties before tearing down the document. Some of the
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -3295,17 +3295,28 @@ nsRange::ExcludeNonSelectableNodes(nsTAr
           // Store it in the result (strong ref) - do this before creating
           // a new range in |newRange| below so we don't drop the last ref
           // to the range created in the previous iteration.
           if (!added && !err.Failed()) {
             aOutRanges->AppendElement(range);
           }
 
           // Create a new range for the remainder.
-          rv = CreateRange(node, 0, endParent, endOffset,
+          nsINode* startParent = node;
+          int32_t startOffset = 0;
+          // Don't start *inside* a node with independent selection though
+          // (e.g. <input>).
+          if (content && content->HasIndependentSelection()) {
+            nsINode* parent = startParent->GetParent();
+            if (parent) {
+              startOffset = parent->IndexOf(startParent);
+              startParent = parent;
+            }
+          }
+          rv = CreateRange(startParent, startOffset, endParent, endOffset,
                            getter_AddRefs(newRange));
           if (NS_FAILED(rv) || newRange->Collapsed()) {
             newRange = nullptr;
           }
           range = newRange;
           break; // create a new iterator for the new range, if any
         }
       } else {
--- a/dom/base/test/chrome/test_bug884693.xul
+++ b/dom/base/test/chrome/test_bug884693.xul
@@ -50,18 +50,24 @@ https://bugzilla.mozilla.org/show_bug.cg
         };
 
         xhr.open("GET", `${SERVER_URL}?${status}&${statusText}&${body}`);
         xhr.send();
       });
     }
 
     SimpleTest.waitForExplicitFinish();
-    runTest(204, "No content", "", "", []).
-      then(() => { return runTest(204, "No content", INVALID_XML, "", []); }).
+    runTest(201, "Created", "", "", []).
+      then(() => { return runTest(201, "Created", INVALID_XML, INVALID_XML, []); }).
+      then(() => { return runTest(202, "Accepted", "", "", []); }).
+      then(() => { return runTest(202, "Accepted", INVALID_XML, INVALID_XML, []); }).
+      then(() => { return runTest(204, "No Content", "", "", []); }).
+      then(() => { return runTest(204, "No Content", INVALID_XML, "", []); }).
+      then(() => { return runTest(205, "Reset Content", "", "", []); }).
+      then(() => { return runTest(205, "Reset Content", INVALID_XML, "", []); }).
       then(() => { return runTest(304, "Not modified", "", "", []); }).
       then(() => { return runTest(304, "Not modified", INVALID_XML, "", []); }).
       then(() => { return runTest(200, "OK", "", "", ["no root element found"]); }).
       then(() => { return runTest(200, "OK", INVALID_XML, INVALID_XML, ["syntax error"]); }).
       then(SimpleTest.finish);
 
   ]]></script>
 </window>
--- a/dom/base/test/test_user_select.html
+++ b/dom/base/test/test_user_select.html
@@ -39,16 +39,19 @@ a { position:absolute; bottom: 0; right:
 <div class="non-selectable">x</div>
 <div class="non-selectable">x</div>
 bbbb</div>
 <div id="testG" style="white-space:pre">aaaa
 <div class="non-selectable">x</div>
 <div class="non-selectable">x</div>
 <div class="non-selectable">x</div>
 bbbb</div>
+<div id="testH" style="white-space:pre">aaaa
+<div class="non-selectable">x</div><input>
+bbbbbbb</div>
 
 <iframe id="testD" src="data:text/html,<body>aaaa<span style='-moz-user-select:none'>bbbb</span>cccc"></iframe>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 function test()
 {
@@ -281,16 +284,26 @@ function test()
   clear();
   e = document.getElementById('testG');
   synthesizeMouse(e, 1, 1, {});
   synthesizeMouse(e, 400, 180, { shiftKey: true });
   checkText("aaaa bbbb", e); // XXX this doesn't seem right - bug 1247799
   checkRanges([[0,0,-1,1],[2,0,-1,3],[4,0,-1,5],[6,0,6,5]], e);
   doneTest(e);
 
+  clear();
+  e = document.getElementById('testH');
+  synthesizeMouse(e, 1, 1, {});
+  synthesizeMouse(e, 30, 90, { shiftKey: true });
+  synthesizeMouse(e, 50, 90, { shiftKey: true });
+  synthesizeMouse(e, 70, 90, { shiftKey: true });
+  checkText("aaaa bbb", e);
+  checkRanges([[0,0,-1,1],[-1,2,3,4]], e);
+
+  doneTest(e);
   // ======================================================
   // ==================== Script tests ====================
   // ======================================================
 
   clear();
   e = document.getElementById('testD');
   clear(e.contentWindow);
   sel = e.contentWindow.getSelection();
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -38,16 +38,17 @@ skip-if = debug == false
 [test_interfaceToString.html]
 [test_exceptions_from_jsimplemented.html]
 tags = webrtc
 [test_lenientThis.html]
 [test_lookupGetter.html]
 [test_namedNoIndexed.html]
 [test_named_getter_enumerability.html]
 [test_Object.prototype_props.html]
+[test_proxy_expandos.html]
 [test_queryInterface.html]
 [test_returnUnion.html]
 skip-if = debug == false
 [test_usvstring.html]
 skip-if = debug == false
 [test_sequence_wrapping.html]
 subsuite = gpu
 [test_setWithNamedGetterNoNamedSetter.html]
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_proxy_expandos.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=965992
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 965992</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=965992">Mozilla Bug 965992</a>
+<p id="display"></p>
+<form id="theform"></form>
+<pre id="test">
+<script type="application/javascript">
+
+// Ensure we are in JIT code and attach IC stubs.
+const iterations = 50;
+
+function testFoo(obj, expected, kind) {
+  for (var i = 0; i < iterations; i++) {
+    is(obj.foo, expected, "Looking up an expando should work - " + kind);
+  }
+}
+
+function getPropTests(obj) {
+  // Start with a plain data property.
+  obj.foo = "bar";
+  testFoo(obj, "bar", "plain");
+
+  // Now change it to a scripted getter.
+  var count = 0;
+  Object.defineProperty(obj, "foo", {get:function() {
+    is(this, obj, "Getter should have the proxy as |this|");
+    is(arguments.length, 0, "Shouldn't pass arguments to getters");
+    count++;
+    return 123;
+  }, configurable: true});
+  testFoo(obj, 123, "scripted getter");
+  is(count, iterations, "Should have called the getter enough times");
+
+  // Now try a native getter.
+  Object.defineProperty(obj, "foo", {get: Object.prototype.valueOf, configurable: true});
+  testFoo(obj, obj, "native getter");
+}
+
+function getElemTests(obj) {
+  // Define two expando properties, then test inline caches for obj[prop]
+  // correctly guard on prop being the same.
+  var count = 0;
+  obj.elem1 = 1;
+  Object.defineProperty(obj, "elem2", {get: function() { count++; return 2; }});
+
+  for (var i = 0; i < iterations; i++) {
+    var prop = ((i & 1) == 0) ? "elem1" : "elem2";
+    is(obj[prop], (i & 1) + 1, "Should return correct property value");
+  }
+  is(count, iterations / 2, "Should have called the getter enough times");
+}
+
+var directExpando = document.getElementsByTagName("*");
+var indirectExpando = document.getElementById("theform");
+
+getPropTests(directExpando);
+getPropTests(indirectExpando);
+
+getElemTests(indirectExpando);
+getElemTests(directExpando);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/cache/ActorChild.cpp
+++ b/dom/cache/ActorChild.cpp
@@ -15,17 +15,17 @@ namespace cache {
 
 void
 ActorChild::SetWorkerHolder(CacheWorkerHolder* aWorkerHolder)
 {
   // Some of the Cache actors can have multiple DOM objects associated with
   // them.  In this case the workerHolder will be added multiple times.  This is
   // permitted, but the workerHolder should be the same each time.
   if (mWorkerHolder) {
-    MOZ_ASSERT(mWorkerHolder == aWorkerHolder);
+    MOZ_DIAGNOSTIC_ASSERT(mWorkerHolder == aWorkerHolder);
     return;
   }
 
   mWorkerHolder = aWorkerHolder;
   if (mWorkerHolder) {
     mWorkerHolder->AddActor(this);
   }
 }
@@ -53,14 +53,14 @@ ActorChild::WorkerHolderNotified() const
 }
 
 ActorChild::ActorChild()
 {
 }
 
 ActorChild::~ActorChild()
 {
-  MOZ_ASSERT(!mWorkerHolder);
+  MOZ_DIAGNOSTIC_ASSERT(!mWorkerHolder);
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/AutoUtils.cpp
+++ b/dom/cache/AutoUtils.cpp
@@ -61,29 +61,29 @@ namespace cache {
 
 AutoChildOpArgs::AutoChildOpArgs(TypeUtils* aTypeUtils,
                                  const CacheOpArgs& aOpArgs,
                                  uint32_t aEntryCount)
   : mTypeUtils(aTypeUtils)
   , mOpArgs(aOpArgs)
   , mSent(false)
 {
-  MOZ_ASSERT(mTypeUtils);
+  MOZ_DIAGNOSTIC_ASSERT(mTypeUtils);
   MOZ_RELEASE_ASSERT(aEntryCount != 0);
   // We are using AutoIPCStream objects to cleanup target IPCStream
   // structures embedded in our CacheOpArgs.  These IPCStream structs
   // must not move once we attach our AutoIPCStream to them.  Therefore,
   // its important that any arrays containing streams are pre-sized for
   // the number of entries we have in order to avoid realloc moving
   // things around on us.
   if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
     CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
     args.requestResponseList().SetCapacity(aEntryCount);
   } else {
-    MOZ_ASSERT(aEntryCount == 1);
+    MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
   }
 }
 
 AutoChildOpArgs::~AutoChildOpArgs()
 {
   CleanupAction action = mSent ? Forget : Delete;
 
   switch(mOpArgs.type()) {
@@ -140,47 +140,47 @@ AutoChildOpArgs::~AutoChildOpArgs()
 
   mStreamCleanupList.Clear();
 }
 
 void
 AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
                      SchemeAction aSchemeAction, ErrorResult& aRv)
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   switch(mOpArgs.type()) {
     case CacheOpArgs::TCacheMatchArgs:
     {
       CacheMatchArgs& args = mOpArgs.get_CacheMatchArgs();
       mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
                                  aSchemeAction, mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheMatchAllArgs:
     {
       CacheMatchAllArgs& args = mOpArgs.get_CacheMatchAllArgs();
-      MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
+      MOZ_DIAGNOSTIC_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
       args.requestOrVoid() = CacheRequest();
       mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
                                  aRequest, aBodyAction, aSchemeAction,
                                  mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheDeleteArgs:
     {
       CacheDeleteArgs& args = mOpArgs.get_CacheDeleteArgs();
       mTypeUtils->ToCacheRequest(args.request(), aRequest, aBodyAction,
                                  aSchemeAction, mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TCacheKeysArgs:
     {
       CacheKeysArgs& args = mOpArgs.get_CacheKeysArgs();
-      MOZ_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
+      MOZ_DIAGNOSTIC_ASSERT(args.requestOrVoid().type() == CacheRequestOrVoid::Tvoid_t);
       args.requestOrVoid() = CacheRequest();
       mTypeUtils->ToCacheRequest(args.requestOrVoid().get_CacheRequest(),
                                   aRequest, aBodyAction, aSchemeAction,
                                   mStreamCleanupList, aRv);
       break;
     }
     case CacheOpArgs::TStorageMatchArgs:
     {
@@ -195,17 +195,17 @@ AutoChildOpArgs::Add(InternalRequest* aR
 }
 
 namespace {
 
 bool
 MatchInPutList(InternalRequest* aRequest,
                const nsTArray<CacheRequestResponse>& aPutList)
 {
-  MOZ_ASSERT(aRequest);
+  MOZ_DIAGNOSTIC_ASSERT(aRequest);
 
   // This method implements the SW spec QueryCache algorithm against an
   // in memory array of Request/Response objects.  This essentially the
   // same algorithm that is implemented in DBSchema.cpp.  Unfortunately
   // we cannot unify them because when operating against the real database
   // we don't want to load all request/response objects into memory.
 
   // Note, we can skip the check for a invalid request method because
@@ -248,33 +248,33 @@ MatchInPutList(InternalRequest* aRequest
     // Assume the vary headers match until we find a conflict
     bool varyHeadersMatch = true;
 
     char* rawBuffer = varyHeaders.BeginWriting();
     char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
     for (; token;
          token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
       nsDependentCString header(token);
-      MOZ_ASSERT(!header.EqualsLiteral("*"),
-                 "We should have already caught this in "
-                 "TypeUtils::ToPCacheResponseWithoutBody()");
+      MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
+                            "We should have already caught this in "
+                            "TypeUtils::ToPCacheResponseWithoutBody()");
 
       ErrorResult headerRv;
       nsAutoCString value;
       requestHeaders->Get(header, value, headerRv);
       if (NS_WARN_IF(headerRv.Failed())) {
         headerRv.SuppressException();
-        MOZ_ASSERT(value.IsEmpty());
+        MOZ_DIAGNOSTIC_ASSERT(value.IsEmpty());
       }
 
       nsAutoCString cachedValue;
       cachedRequestHeaders->Get(header, cachedValue, headerRv);
       if (NS_WARN_IF(headerRv.Failed())) {
         headerRv.SuppressException();
-        MOZ_ASSERT(cachedValue.IsEmpty());
+        MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
       }
 
       if (value != cachedValue) {
         varyHeadersMatch = false;
         break;
       }
     }
 
@@ -289,17 +289,17 @@ MatchInPutList(InternalRequest* aRequest
 
 } // namespace
 
 void
 AutoChildOpArgs::Add(InternalRequest* aRequest, BodyAction aBodyAction,
                      SchemeAction aSchemeAction, Response& aResponse,
                      ErrorResult& aRv)
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   switch(mOpArgs.type()) {
     case CacheOpArgs::TCachePutAllArgs:
     {
       CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
 
       // Throw an error if a request/response pair would mask another
       // request/response pair in the same PutAll operation.  This is
@@ -345,17 +345,17 @@ AutoChildOpArgs::Add(InternalRequest* aR
     default:
       MOZ_CRASH("Cache args type cannot send a Request/Response pair!");
   }
 }
 
 const CacheOpArgs&
 AutoChildOpArgs::SendAsOpArgs()
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
   mSent = true;
   for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
     autoStream->TakeValue();
   }
   return mOpArgs;
 }
 
 // --------------------------------------------
@@ -363,32 +363,32 @@ AutoChildOpArgs::SendAsOpArgs()
 AutoParentOpResult::AutoParentOpResult(mozilla::ipc::PBackgroundParent* aManager,
                                        const CacheOpResult& aOpResult,
                                        uint32_t aEntryCount)
   : mManager(aManager)
   , mOpResult(aOpResult)
   , mStreamControl(nullptr)
   , mSent(false)
 {
-  MOZ_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
   MOZ_RELEASE_ASSERT(aEntryCount != 0);
   // We are using AutoIPCStream objects to cleanup target IPCStream
   // structures embedded in our CacheOpArgs.  These IPCStream structs
   // must not move once we attach our AutoIPCStream to them.  Therefore,
   // its important that any arrays containing streams are pre-sized for
   // the number of entries we have in order to avoid realloc moving
   // things around on us.
   if (mOpResult.type() == CacheOpResult::TCacheMatchAllResult) {
     CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
     result.responseList().SetCapacity(aEntryCount);
   } else if (mOpResult.type() == CacheOpResult::TCacheKeysResult) {
     CacheKeysResult& result = mOpResult.get_CacheKeysResult();
     result.requestList().SetCapacity(aEntryCount);
   } else {
-    MOZ_ASSERT(aEntryCount == 1);
+    MOZ_DIAGNOSTIC_ASSERT(aEntryCount == 1);
   }
 }
 
 AutoParentOpResult::~AutoParentOpResult()
 {
   CleanupAction action = mSent ? Forget : Delete;
 
   switch (mOpResult.type()) {
@@ -411,33 +411,33 @@ AutoParentOpResult::~AutoParentOpResult(
   }
 
   mStreamCleanupList.Clear();
 }
 
 void
 AutoParentOpResult::Add(CacheId aOpenedCacheId, Manager* aManager)
 {
-  MOZ_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
-  MOZ_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
+  MOZ_DIAGNOSTIC_ASSERT(mOpResult.type() == CacheOpResult::TStorageOpenResult);
+  MOZ_DIAGNOSTIC_ASSERT(mOpResult.get_StorageOpenResult().actorParent() == nullptr);
   mOpResult.get_StorageOpenResult().actorParent() =
     mManager->SendPCacheConstructor(new CacheParent(aManager, aOpenedCacheId));
 }
 
 void
 AutoParentOpResult::Add(const SavedResponse& aSavedResponse,
                         StreamList* aStreamList)
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   switch (mOpResult.type()) {
     case CacheOpResult::TCacheMatchResult:
     {
       CacheMatchResult& result = mOpResult.get_CacheMatchResult();
-      MOZ_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
+      MOZ_DIAGNOSTIC_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
       result.responseOrVoid() = aSavedResponse.mValue;
       SerializeResponseBody(aSavedResponse, aStreamList,
                             &result.responseOrVoid().get_CacheResponse());
       break;
     }
     case CacheOpResult::TCacheMatchAllResult:
     {
       CacheMatchAllResult& result = mOpResult.get_CacheMatchAllResult();
@@ -450,32 +450,32 @@ AutoParentOpResult::Add(const SavedRespo
       result.responseList().AppendElement(aSavedResponse.mValue);
       SerializeResponseBody(aSavedResponse, aStreamList,
                             &result.responseList().LastElement());
       break;
     }
     case CacheOpResult::TStorageMatchResult:
     {
       StorageMatchResult& result = mOpResult.get_StorageMatchResult();
-      MOZ_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
+      MOZ_DIAGNOSTIC_ASSERT(result.responseOrVoid().type() == CacheResponseOrVoid::Tvoid_t);
       result.responseOrVoid() = aSavedResponse.mValue;
       SerializeResponseBody(aSavedResponse, aStreamList,
                             &result.responseOrVoid().get_CacheResponse());
       break;
     }
     default:
       MOZ_CRASH("Cache result type cannot handle returning a Response!");
   }
 }
 
 void
 AutoParentOpResult::Add(const SavedRequest& aSavedRequest,
                         StreamList* aStreamList)
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   switch (mOpResult.type()) {
     case CacheOpResult::TCacheKeysResult:
     {
       CacheKeysResult& result = mOpResult.get_CacheKeysResult();
       // Ensure that we don't realloc the array since this can result
       // in our AutoIPCStream objects to reference the wrong memory
       // location.  This should never happen and is a UAF if it does.
@@ -498,51 +498,51 @@ AutoParentOpResult::Add(const SavedReque
     default:
       MOZ_CRASH("Cache result type cannot handle returning a Request!");
   }
 }
 
 const CacheOpResult&
 AutoParentOpResult::SendAsOpResult()
 {
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
   mSent = true;
   for (UniquePtr<AutoIPCStream>& autoStream : mStreamCleanupList) {
     autoStream->TakeValue();
   }
   return mOpResult;
 }
 
 void
 AutoParentOpResult::SerializeResponseBody(const SavedResponse& aSavedResponse,
                                           StreamList* aStreamList,
                                           CacheResponse* aResponseOut)
 {
-  MOZ_ASSERT(aResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aResponseOut);
 
   if (!aSavedResponse.mHasBodyId) {
     aResponseOut->body() = void_t();
     return;
   }
 
   aResponseOut->body() = CacheReadStream();
   SerializeReadStream(aSavedResponse.mBodyId, aStreamList,
                       &aResponseOut->body().get_CacheReadStream());
 }
 
 void
 AutoParentOpResult::SerializeReadStream(const nsID& aId, StreamList* aStreamList,
                                         CacheReadStream* aReadStreamOut)
 {
-  MOZ_ASSERT(aStreamList);
-  MOZ_ASSERT(aReadStreamOut);
-  MOZ_ASSERT(!mSent);
+  MOZ_DIAGNOSTIC_ASSERT(aStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(!mSent);
 
   nsCOMPtr<nsIInputStream> stream = aStreamList->Extract(aId);
-  MOZ_ASSERT(stream);
+  MOZ_DIAGNOSTIC_ASSERT(stream);
 
   if (!mStreamControl) {
     mStreamControl = static_cast<CacheStreamControlParent*>(
       mManager->SendPCacheStreamControlConstructor(new CacheStreamControlParent()));
 
     // If this failed, then the child process is gone.  Warn and allow actor
     // cleanup to proceed as normal.
     if (!mStreamControl) {
@@ -552,14 +552,14 @@ AutoParentOpResult::SerializeReadStream(
   }
 
   aStreamList->SetStreamControl(mStreamControl);
 
   RefPtr<ReadStream> readStream = ReadStream::Create(mStreamControl,
                                                      aId, stream);
   ErrorResult rv;
   readStream->Serialize(aReadStreamOut, mStreamCleanupList, rv);
-  MOZ_ASSERT(!rv.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!rv.Failed());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -117,18 +117,18 @@ public:
   FetchHandler(CacheWorkerHolder* aWorkerHolder, Cache* aCache,
                nsTArray<RefPtr<Request>>&& aRequestList, Promise* aPromise)
     : mWorkerHolder(aWorkerHolder)
     , mCache(aCache)
     , mRequestList(Move(aRequestList))
     , mPromise(aPromise)
   {
     MOZ_ASSERT_IF(!NS_IsMainThread(), mWorkerHolder);
-    MOZ_ASSERT(mCache);
-    MOZ_ASSERT(mPromise);
+    MOZ_DIAGNOSTIC_ASSERT(mCache);
+    MOZ_DIAGNOSTIC_ASSERT(mPromise);
   }
 
   virtual void
   ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override
   {
     NS_ASSERT_OWNINGTHREAD(FetchHandler);
 
     // Stop holding the worker alive when we leave this method.
@@ -193,17 +193,17 @@ public:
         // TODO: abort the fetch requests we have running (bug 1157434)
         mPromise->MaybeReject(errorResult);
         return;
       }
 
       responseList.AppendElement(Move(response));
     }
 
-    MOZ_ASSERT(mRequestList.Length() == responseList.Length());
+    MOZ_DIAGNOSTIC_ASSERT(mRequestList.Length() == responseList.Length());
 
     // Now store the unwrapped Response list in the Cache.
     ErrorResult result;
     RefPtr<Promise> put = mCache->PutAll(mRequestList, responseList, result);
     if (NS_WARN_IF(result.Failed())) {
       // TODO: abort the fetch requests we have running (bug 1157434)
       mPromise->MaybeReject(result);
       return;
@@ -252,18 +252,18 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 Cache::Cache(nsIGlobalObject* aGlobal, CacheChild* aActor)
   : mGlobal(aGlobal)
   , mActor(aActor)
 {
-  MOZ_ASSERT(mGlobal);
-  MOZ_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
   mActor->SetListener(this);
 }
 
 already_AddRefed<Promise>
 Cache::Match(const RequestOrUSVString& aRequest,
              const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   if (NS_WARN_IF(!mActor)) {
@@ -334,17 +334,17 @@ Cache::Add(JSContext* aContext, const Re
 
   CacheChild::AutoLock actorLock(mActor);
 
   if (!IsValidPutRequestMethod(aRequest, aRv)) {
     return nullptr;
   }
 
   GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
-  MOZ_ASSERT(!global.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
 
   nsTArray<RefPtr<Request>> requestList(1);
   RefPtr<Request> request = Request::Constructor(global, aRequest,
                                                    RequestInit(), aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
@@ -366,17 +366,17 @@ Cache::AddAll(JSContext* aContext,
   if (NS_WARN_IF(!mActor)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
   GlobalObject global(aContext, mGlobal->GetGlobalJSObject());
-  MOZ_ASSERT(!global.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
 
   nsTArray<RefPtr<Request>> requestList(aRequestList.Length());
   for (uint32_t i = 0; i < aRequestList.Length(); ++i) {
     RequestOrUSVString requestOrString;
 
     if (aRequestList[i].IsRequest()) {
       requestOrString.SetAsRequest() = aRequestList[i].GetAsRequest();
       if (NS_WARN_IF(!IsValidPutRequestMethod(requestOrString.GetAsRequest(),
@@ -536,18 +536,18 @@ JSObject*
 Cache::WrapObject(JSContext* aContext, JS::Handle<JSObject*> aGivenProto)
 {
   return CacheBinding::Wrap(aContext, this, aGivenProto);
 }
 
 void
 Cache::DestroyInternal(CacheChild* aActor)
 {
-  MOZ_ASSERT(mActor);
-  MOZ_ASSERT(mActor == aActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
   mActor->ClearListener();
   mActor = nullptr;
 }
 
 nsIGlobalObject*
 Cache::GetGlobalObject() const
 {
   return mGlobal;
@@ -560,50 +560,50 @@ Cache::AssertOwningThread() const
   NS_ASSERT_OWNINGTHREAD(Cache);
 }
 #endif
 
 PBackgroundChild*
 Cache::GetIPCManager()
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
-  MOZ_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
   return mActor->Manager();
 }
 
 Cache::~Cache()
 {
   NS_ASSERT_OWNINGTHREAD(Cache);
   if (mActor) {
     mActor->StartDestroyFromListener();
     // DestroyInternal() is called synchronously by StartDestroyFromListener().
     // So we should have already cleared the mActor.
-    MOZ_ASSERT(!mActor);
+    MOZ_DIAGNOSTIC_ASSERT(!mActor);
   }
 }
 
 already_AddRefed<Promise>
 Cache::ExecuteOp(AutoChildOpArgs& aOpArgs, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
 
   RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
   if (NS_WARN_IF(!promise)) {
     return nullptr;
   }
 
   mActor->ExecuteOp(mGlobal, promise, this, aOpArgs.SendAsOpArgs());
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 Cache::AddAll(const GlobalObject& aGlobal,
               nsTArray<RefPtr<Request>>&& aRequestList, ErrorResult& aRv)
 {
-  MOZ_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
 
   // If there is no work to do, then resolve immediately
   if (aRequestList.IsEmpty()) {
     RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
     if (NS_WARN_IF(!promise)) {
       return nullptr;
     }
 
@@ -648,17 +648,17 @@ Cache::AddAll(const GlobalObject& aGloba
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 Cache::PutAll(const nsTArray<RefPtr<Request>>& aRequestList,
               const nsTArray<RefPtr<Response>>& aResponseList,
               ErrorResult& aRv)
 {
-  MOZ_ASSERT(aRequestList.Length() == aResponseList.Length());
+  MOZ_DIAGNOSTIC_ASSERT(aRequestList.Length() == aResponseList.Length());
 
   if (NS_WARN_IF(!mActor)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   CacheChild::AutoLock actorLock(mActor);
 
--- a/dom/cache/CacheChild.cpp
+++ b/dom/cache/CacheChild.cpp
@@ -37,35 +37,35 @@ CacheChild::CacheChild()
 {
   MOZ_COUNT_CTOR(cache::CacheChild);
 }
 
 CacheChild::~CacheChild()
 {
   MOZ_COUNT_DTOR(cache::CacheChild);
   NS_ASSERT_OWNINGTHREAD(CacheChild);
-  MOZ_ASSERT(!mListener);
-  MOZ_ASSERT(!mNumChildActors);
-  MOZ_ASSERT(!mLocked);
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mNumChildActors);
+  MOZ_DIAGNOSTIC_ASSERT(!mLocked);
 }
 
 void
 CacheChild::SetListener(Cache* aListener)
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
-  MOZ_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
   mListener = aListener;
-  MOZ_ASSERT(mListener);
+  MOZ_DIAGNOSTIC_ASSERT(mListener);
 }
 
 void
 CacheChild::ClearListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
-  MOZ_ASSERT(mListener);
+  MOZ_DIAGNOSTIC_ASSERT(mListener);
   mListener = nullptr;
 }
 
 void
 CacheChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
                       nsISupports* aParent, const CacheOpArgs& aArgs)
 {
   mNumChildActors += 1;
@@ -76,17 +76,17 @@ CacheChild::ExecuteOp(nsIGlobalObject* a
 void
 CacheChild::StartDestroyFromListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
 
   // The listener should be held alive by any async operations, so if it
   // is going away then there must not be any child actors.  This in turn
   // ensures that StartDestroy() will not trigger the delayed path.
-  MOZ_ASSERT(!mNumChildActors);
+  MOZ_DIAGNOSTIC_ASSERT(!mNumChildActors);
 
   StartDestroy();
 }
 
 void
 CacheChild::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
@@ -108,31 +108,31 @@ CacheChild::StartDestroy()
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
   listener->DestroyInternal(this);
 
   // Cache listener should call ClearListener() in DestroyInternal()
-  MOZ_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
 
   // Start actor destruction from parent process
   Unused << SendTeardown();
 }
 
 void
 CacheChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
   RefPtr<Cache> listener = mListener;
   if (listener) {
     listener->DestroyInternal(this);
     // Cache listener should call ClearListener() in DestroyInternal()
-    MOZ_ASSERT(!mListener);
+    MOZ_DIAGNOSTIC_ASSERT(!mListener);
   }
 
   RemoveWorkerHolder();
 }
 
 PCacheOpChild*
 CacheChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
 {
@@ -162,24 +162,24 @@ CacheChild::MaybeFlushDelayedDestroy()
     StartDestroy();
   }
 }
 
 void
 CacheChild::Lock()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
-  MOZ_ASSERT(!mLocked);
+  MOZ_DIAGNOSTIC_ASSERT(!mLocked);
   mLocked = true;
 }
 
 void
 CacheChild::Unlock()
 {
   NS_ASSERT_OWNINGTHREAD(CacheChild);
-  MOZ_ASSERT(mLocked);
+  MOZ_DIAGNOSTIC_ASSERT(mLocked);
   mLocked = false;
   MaybeFlushDelayedDestroy();
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/CacheChild.h
+++ b/dom/cache/CacheChild.h
@@ -30,17 +30,17 @@ public:
   class MOZ_RAII AutoLock final
   {
     CacheChild* mActor;
 
   public:
     explicit AutoLock(CacheChild* aActor)
       : mActor(aActor)
     {
-      MOZ_ASSERT(mActor);
+      MOZ_DIAGNOSTIC_ASSERT(mActor);
       mActor->Lock();
     }
 
     ~AutoLock()
     {
       mActor->Unlock();
     }
   };
--- a/dom/cache/CacheOpChild.cpp
+++ b/dom/cache/CacheOpChild.cpp
@@ -65,28 +65,28 @@ AddWorkerHolderToStreamChild(const Cache
 
 CacheOpChild::CacheOpChild(CacheWorkerHolder* aWorkerHolder,
                            nsIGlobalObject* aGlobal,
                            nsISupports* aParent, Promise* aPromise)
   : mGlobal(aGlobal)
   , mParent(aParent)
   , mPromise(aPromise)
 {
-  MOZ_ASSERT(mGlobal);
-  MOZ_ASSERT(mParent);
-  MOZ_ASSERT(mPromise);
+  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
+  MOZ_DIAGNOSTIC_ASSERT(mParent);
+  MOZ_DIAGNOSTIC_ASSERT(mPromise);
 
   MOZ_ASSERT_IF(!NS_IsMainThread(), aWorkerHolder);
   SetWorkerHolder(aWorkerHolder);
 }
 
 CacheOpChild::~CacheOpChild()
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
-  MOZ_ASSERT(!mPromise);
+  MOZ_DIAGNOSTIC_ASSERT(!mPromise);
 }
 
 void
 CacheOpChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
 
   // If the actor was terminated for some unknown reason, then indicate the
@@ -101,17 +101,17 @@ CacheOpChild::ActorDestroy(ActorDestroyR
 
 mozilla::ipc::IPCResult
 CacheOpChild::Recv__delete__(const ErrorResult& aRv,
                              const CacheOpResult& aResult)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpChild);
 
   if (NS_WARN_IF(aRv.Failed())) {
-    MOZ_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
+    MOZ_DIAGNOSTIC_ASSERT(aResult.type() == CacheOpResult::Tvoid_t);
     // TODO: Remove this const_cast (bug 1152078).
     // It is safe for now since this ErrorResult is handed off to us by IPDL
     // and is thrown into the trash afterwards.
     mPromise->MaybeReject(const_cast<ErrorResult&>(aRv));
     mPromise = nullptr;
     return IPC_OK();
   }
 
--- a/dom/cache/CacheOpParent.cpp
+++ b/dom/cache/CacheOpParent.cpp
@@ -24,40 +24,40 @@ using mozilla::ipc::SendStreamParent;
 
 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager, CacheId aCacheId,
                              const CacheOpArgs& aOpArgs)
   : mIpcManager(aIpcManager)
   , mCacheId(aCacheId)
   , mNamespace(INVALID_NAMESPACE)
   , mOpArgs(aOpArgs)
 {
-  MOZ_ASSERT(mIpcManager);
+  MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
 }
 
 CacheOpParent::CacheOpParent(PBackgroundParent* aIpcManager,
                              Namespace aNamespace, const CacheOpArgs& aOpArgs)
   : mIpcManager(aIpcManager)
   , mCacheId(INVALID_CACHE_ID)
   , mNamespace(aNamespace)
   , mOpArgs(aOpArgs)
 {
-  MOZ_ASSERT(mIpcManager);
+  MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
 }
 
 CacheOpParent::~CacheOpParent()
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpParent);
 }
 
 void
 CacheOpParent::Execute(ManagerId* aManagerId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpParent);
-  MOZ_ASSERT(!mManager);
-  MOZ_ASSERT(!mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(!mManager);
+  MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
 
   RefPtr<cache::Manager> manager;
   nsresult rv = cache::Manager::GetOrCreate(aManagerId, getter_AddRefs(manager));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     ErrorResult result(rv);
     Unused << Send__delete__(this, result, void_t());
     result.SuppressException();
     return;
@@ -65,24 +65,24 @@ CacheOpParent::Execute(ManagerId* aManag
 
   Execute(manager);
 }
 
 void
 CacheOpParent::Execute(cache::Manager* aManager)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpParent);
-  MOZ_ASSERT(!mManager);
-  MOZ_ASSERT(!mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(!mManager);
+  MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
 
   mManager = aManager;
 
   // Handle put op
   if (mOpArgs.type() == CacheOpArgs::TCachePutAllArgs) {
-    MOZ_ASSERT(mCacheId != INVALID_CACHE_ID);
+    MOZ_DIAGNOSTIC_ASSERT(mCacheId != INVALID_CACHE_ID);
 
     const CachePutAllArgs& args = mOpArgs.get_CachePutAllArgs();
     const nsTArray<CacheRequestResponse>& list = args.requestResponseList();
 
     AutoTArray<nsCOMPtr<nsIInputStream>, 256> requestStreamList;
     AutoTArray<nsCOMPtr<nsIInputStream>, 256> responseStreamList;
 
     for (uint32_t i = 0; i < list.Length(); ++i) {
@@ -94,32 +94,32 @@ CacheOpParent::Execute(cache::Manager* a
 
     mManager->ExecutePutAll(this, mCacheId, args.requestResponseList(),
                             requestStreamList, responseStreamList);
     return;
   }
 
   // Handle all other cache ops
   if (mCacheId != INVALID_CACHE_ID) {
-    MOZ_ASSERT(mNamespace == INVALID_NAMESPACE);
+    MOZ_DIAGNOSTIC_ASSERT(mNamespace == INVALID_NAMESPACE);
     mManager->ExecuteCacheOp(this, mCacheId, mOpArgs);
     return;
   }
 
   // Handle all storage ops
-  MOZ_ASSERT(mNamespace != INVALID_NAMESPACE);
+  MOZ_DIAGNOSTIC_ASSERT(mNamespace != INVALID_NAMESPACE);
   mManager->ExecuteStorageOp(this, mNamespace, mOpArgs);
 }
 
 void
 CacheOpParent::WaitForVerification(PrincipalVerifier* aVerifier)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpParent);
-  MOZ_ASSERT(!mManager);
-  MOZ_ASSERT(!mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(!mManager);
+  MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
 
   mVerifier = aVerifier;
   mVerifier->AddListener(this);
 }
 
 void
 CacheOpParent::ActorDestroy(ActorDestroyReason aReason)
 {
@@ -159,18 +159,18 @@ CacheOpParent::OnPrincipalVerified(nsres
 void
 CacheOpParent::OnOpComplete(ErrorResult&& aRv, const CacheOpResult& aResult,
                             CacheId aOpenedCacheId,
                             const nsTArray<SavedResponse>& aSavedResponseList,
                             const nsTArray<SavedRequest>& aSavedRequestList,
                             StreamList* aStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheOpParent);
-  MOZ_ASSERT(mIpcManager);
-  MOZ_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mIpcManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
 
   // Never send an op-specific result if we have an error.  Instead, send
   // void_t() to ensure that we don't leak actors on the child side.
   if (NS_WARN_IF(aRv.Failed())) {
     Unused << Send__delete__(this, aRv, void_t());
     aRv.SuppressException(); // We serialiazed it, as best we could.
     return;
   }
--- a/dom/cache/CacheParent.cpp
+++ b/dom/cache/CacheParent.cpp
@@ -20,30 +20,30 @@ DeallocPCacheParent(PCacheParent* aActor
   delete aActor;
 }
 
 CacheParent::CacheParent(cache::Manager* aManager, CacheId aCacheId)
   : mManager(aManager)
   , mCacheId(aCacheId)
 {
   MOZ_COUNT_CTOR(cache::CacheParent);
-  MOZ_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
   mManager->AddRefCacheId(mCacheId);
 }
 
 CacheParent::~CacheParent()
 {
   MOZ_COUNT_DTOR(cache::CacheParent);
-  MOZ_ASSERT(!mManager);
+  MOZ_DIAGNOSTIC_ASSERT(!mManager);
 }
 
 void
 CacheParent::ActorDestroy(ActorDestroyReason aReason)
 {
-  MOZ_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
   mManager->ReleaseCacheId(mCacheId);
   mManager = nullptr;
 }
 
 PCacheOpParent*
 CacheParent::AllocPCacheOpParent(const CacheOpArgs& aOpArgs)
 {
   if (aOpArgs.type() != CacheOpArgs::TCacheMatchArgs &&
--- a/dom/cache/CacheStorage.cpp
+++ b/dom/cache/CacheStorage.cpp
@@ -136,18 +136,18 @@ IsTrusted(const PrincipalInfo& aPrincipa
 } // namespace
 
 // static
 already_AddRefed<CacheStorage>
 CacheStorage::CreateOnMainThread(Namespace aNamespace, nsIGlobalObject* aGlobal,
                                  nsIPrincipal* aPrincipal, bool aStorageDisabled,
                                  bool aForceTrustedOrigin, ErrorResult& aRv)
 {
-  MOZ_ASSERT(aGlobal);
-  MOZ_ASSERT(aPrincipal);
+  MOZ_DIAGNOSTIC_ASSERT(aGlobal);
+  MOZ_DIAGNOSTIC_ASSERT(aPrincipal);
   MOZ_ASSERT(NS_IsMainThread());
 
   if (aStorageDisabled) {
     NS_WARNING("CacheStorage has been disabled.");
     RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
     return ref.forget();
   }
 
@@ -173,18 +173,18 @@ CacheStorage::CreateOnMainThread(Namespa
   return ref.forget();
 }
 
 // static
 already_AddRefed<CacheStorage>
 CacheStorage::CreateOnWorker(Namespace aNamespace, nsIGlobalObject* aGlobal,
                              WorkerPrivate* aWorkerPrivate, ErrorResult& aRv)
 {
-  MOZ_ASSERT(aGlobal);
-  MOZ_ASSERT(aWorkerPrivate);
+  MOZ_DIAGNOSTIC_ASSERT(aGlobal);
+  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
 
   if (!aWorkerPrivate->IsStorageAllowed()) {
     NS_WARNING("CacheStorage is not allowed.");
     RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
     return ref.forget();
   }
 
@@ -233,27 +233,27 @@ CacheStorage::CreateOnWorker(Namespace a
   return ref.forget();
 }
 
 // static
 bool
 CacheStorage::DefineCaches(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
-             "Passed object is not a global object!");
+  MOZ_DIAGNOSTIC_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL,
+                                           "Passed object is not a global object!");
   js::AssertSameCompartment(aCx, aGlobal);
 
   if (NS_WARN_IF(!CacheStorageBinding::GetConstructorObject(aCx) ||
                  !CacheBinding::GetConstructorObject(aCx))) {
     return false;
   }
 
   nsIPrincipal* principal = nsContentUtils::ObjectPrincipal(aGlobal);
-  MOZ_ASSERT(principal);
+  MOZ_DIAGNOSTIC_ASSERT(principal);
 
   ErrorResult rv;
   RefPtr<CacheStorage> storage =
     CreateOnMainThread(DEFAULT_NAMESPACE, xpc::NativeGlobal(aGlobal), principal,
                        false, /* private browsing */
                        true,  /* force trusted */
                        rv);
   if (NS_WARN_IF(rv.MaybeSetPendingException(aCx))) {
@@ -273,17 +273,17 @@ CacheStorage::CacheStorage(Namespace aNa
                            CacheWorkerHolder* aWorkerHolder)
   : mNamespace(aNamespace)
   , mGlobal(aGlobal)
   , mPrincipalInfo(MakeUnique<PrincipalInfo>(aPrincipalInfo))
   , mWorkerHolder(aWorkerHolder)
   , mActor(nullptr)
   , mStatus(NS_OK)
 {
-  MOZ_ASSERT(mGlobal);
+  MOZ_DIAGNOSTIC_ASSERT(mGlobal);
 
   // If the PBackground actor is already initialized then we can
   // immediately use it
   PBackgroundChild* actor = BackgroundChild::GetForCurrentThread();
   if (actor) {
     ActorCreated(actor);
     return;
   }
@@ -297,17 +297,17 @@ CacheStorage::CacheStorage(Namespace aNa
   }
 }
 
 CacheStorage::CacheStorage(nsresult aFailureResult)
   : mNamespace(INVALID_NAMESPACE)
   , mActor(nullptr)
   , mStatus(aFailureResult)
 {
-  MOZ_ASSERT(NS_FAILED(mStatus));
+  MOZ_DIAGNOSTIC_ASSERT(NS_FAILED(mStatus));
 }
 
 already_AddRefed<Promise>
 CacheStorage::Match(const RequestOrUSVString& aRequest,
                     const CacheQueryOptions& aOptions, ErrorResult& aRv)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
 
@@ -496,17 +496,17 @@ CacheStorage::WrapObject(JSContext* aCon
 {
   return mozilla::dom::CacheStorageBinding::Wrap(aContext, this, aGivenProto);
 }
 
 void
 CacheStorage::ActorCreated(PBackgroundChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_ASSERT(aActor);
+  MOZ_DIAGNOSTIC_ASSERT(aActor);
 
   if (NS_WARN_IF(mWorkerHolder && mWorkerHolder->Notified())) {
     ActorFailed();
     return;
   }
 
   // WorkerHolder ownership is passed to the CacheStorageChild actor and any
   // actors it may create.  The WorkerHolder will keep the worker thread alive
@@ -517,45 +517,45 @@ CacheStorage::ActorCreated(PBackgroundCh
 
   if (NS_WARN_IF(!constructedActor)) {
     ActorFailed();
     return;
   }
 
   mWorkerHolder = nullptr;
 
-  MOZ_ASSERT(constructedActor == newActor);
+  MOZ_DIAGNOSTIC_ASSERT(constructedActor == newActor);
   mActor = newActor;
 
   MaybeRunPendingRequests();
-  MOZ_ASSERT(mPendingRequests.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mPendingRequests.IsEmpty());
 }
 
 void
 CacheStorage::ActorFailed()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_ASSERT(!NS_FAILED(mStatus));
+  MOZ_DIAGNOSTIC_ASSERT(!NS_FAILED(mStatus));
 
   mStatus = NS_ERROR_UNEXPECTED;
   mWorkerHolder = nullptr;
 
   for (uint32_t i = 0; i < mPendingRequests.Length(); ++i) {
     nsAutoPtr<Entry> entry(mPendingRequests[i].forget());
     entry->mPromise->MaybeReject(NS_ERROR_UNEXPECTED);
   }
   mPendingRequests.Clear();
 }
 
 void
 CacheStorage::DestroyInternal(CacheStorageChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
-  MOZ_ASSERT(mActor);
-  MOZ_ASSERT(mActor == aActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor);
+  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
   mActor->ClearListener();
   mActor = nullptr;
 
   // Note that we will never get an actor again in case another request is
   // made before this object is destructed.
   ActorFailed();
 }
 
@@ -584,17 +584,17 @@ CacheStorage::GetIPCManager()
 
 CacheStorage::~CacheStorage()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorage);
   if (mActor) {
     mActor->StartDestroyFromListener();
     // DestroyInternal() is called synchronously by StartDestroyFromListener().
     // So we should have already cleared the mActor.
-    MOZ_ASSERT(!mActor);
+    MOZ_DIAGNOSTIC_ASSERT(!mActor);
   }
 }
 
 void
 CacheStorage::MaybeRunPendingRequests()
 {
   if (!mActor) {
     return;
--- a/dom/cache/CacheStorageChild.cpp
+++ b/dom/cache/CacheStorageChild.cpp
@@ -24,33 +24,33 @@ DeallocPCacheStorageChild(PCacheStorageC
 
 CacheStorageChild::CacheStorageChild(CacheStorage* aListener,
                                      CacheWorkerHolder* aWorkerHolder)
   : mListener(aListener)
   , mNumChildActors(0)
   , mDelayedDestroy(false)
 {
   MOZ_COUNT_CTOR(cache::CacheStorageChild);
-  MOZ_ASSERT(mListener);
+  MOZ_DIAGNOSTIC_ASSERT(mListener);
 
   SetWorkerHolder(aWorkerHolder);
 }
 
 CacheStorageChild::~CacheStorageChild()
 {
   MOZ_COUNT_DTOR(cache::CacheStorageChild);
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  MOZ_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
 }
 
 void
 CacheStorageChild::ClearListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
-  MOZ_ASSERT(mListener);
+  MOZ_DIAGNOSTIC_ASSERT(mListener);
   mListener = nullptr;
 }
 
 void
 CacheStorageChild::ExecuteOp(nsIGlobalObject* aGlobal, Promise* aPromise,
                              nsISupports* aParent, const CacheOpArgs& aArgs)
 {
   mNumChildActors += 1;
@@ -61,17 +61,17 @@ CacheStorageChild::ExecuteOp(nsIGlobalOb
 void
 CacheStorageChild::StartDestroyFromListener()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
 
   // The listener should be held alive by any async operations, so if it
   // is going away then there must not be any child actors.  This in turn
   // ensures that StartDestroy() will not trigger the delayed path.
-  MOZ_ASSERT(!mNumChildActors);
+  MOZ_DIAGNOSTIC_ASSERT(!mNumChildActors);
 
   StartDestroy();
 }
 
 void
 CacheStorageChild::StartDestroy()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
@@ -93,31 +93,31 @@ CacheStorageChild::StartDestroy()
   // that by just ignoring the second StartDestroy() call.
   if (!listener) {
     return;
   }
 
   listener->DestroyInternal(this);
 
   // CacheStorage listener should call ClearListener() in DestroyInternal()
-  MOZ_ASSERT(!mListener);
+  MOZ_DIAGNOSTIC_ASSERT(!mListener);
 
   // Start actor destruction from parent process
   Unused << SendTeardown();
 }
 
 void
 CacheStorageChild::ActorDestroy(ActorDestroyReason aReason)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStorageChild);
   RefPtr<CacheStorage> listener = mListener;
   if (listener) {
     listener->DestroyInternal(this);
     // CacheStorage listener should call ClearListener() in DestroyInternal()
-    MOZ_ASSERT(!mListener);
+    MOZ_DIAGNOSTIC_ASSERT(!mListener);
   }
 
   RemoveWorkerHolder();
 }
 
 PCacheOpChild*
 CacheStorageChild::AllocPCacheOpChild(const CacheOpArgs& aOpArgs)
 {
@@ -131,17 +131,17 @@ CacheStorageChild::DeallocPCacheOpChild(
   delete aActor;
   NoteDeletedActor();
   return true;
 }
 
 void
 CacheStorageChild::NoteDeletedActor()
 {
-  MOZ_ASSERT(mNumChildActors);
+  MOZ_DIAGNOSTIC_ASSERT(mNumChildActors);
   mNumChildActors -= 1;
   if (!mNumChildActors && mDelayedDestroy) {
     StartDestroy();
   }
 }
 
 } // namespace cache
 } // namespace dom
--- a/dom/cache/CacheStorageParent.cpp
+++ b/dom/cache/CacheStorageParent.cpp
@@ -38,28 +38,28 @@ DeallocPCacheStorageParent(PCacheStorage
 
 CacheStorageParent::CacheStorageParent(PBackgroundParent* aManagingActor,
                                        Namespace aNamespace,
                                        const PrincipalInfo& aPrincipalInfo)
   : mNamespace(aNamespace)
   , mVerifiedStatus(NS_OK)
 {
   MOZ_COUNT_CTOR(cache::CacheStorageParent);
-  MOZ_ASSERT(aManagingActor);
+  MOZ_DIAGNOSTIC_ASSERT(aManagingActor);
 
   // Start the async principal verification process immediately.
   mVerifier = PrincipalVerifier::CreateAndDispatch(this, aManagingActor,
                                                    aPrincipalInfo);
-  MOZ_ASSERT(mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(mVerifier);
 }
 
 CacheStorageParent::~CacheStorageParent()
 {
   MOZ_COUNT_DTOR(cache::CacheStorageParent);
-  MOZ_ASSERT(!mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(!mVerifier);
 }
 
 void
 CacheStorageParent::ActorDestroy(ActorDestroyReason aReason)
 {
   if (mVerifier) {
     mVerifier->RemoveListener(this);
     mVerifier = nullptr;
@@ -90,29 +90,29 @@ CacheStorageParent::DeallocPCacheOpParen
 
 mozilla::ipc::IPCResult
 CacheStorageParent::RecvPCacheOpConstructor(PCacheOpParent* aActor,
                                             const CacheOpArgs& aOpArgs)
 {
   auto actor = static_cast<CacheOpParent*>(aActor);
 
   if (mVerifier) {
-    MOZ_ASSERT(!mManagerId);
+    MOZ_DIAGNOSTIC_ASSERT(!mManagerId);
     actor->WaitForVerification(mVerifier);
     return IPC_OK();
   }
 
   if (NS_WARN_IF(NS_FAILED(mVerifiedStatus))) {
     ErrorResult result(mVerifiedStatus);
     Unused << CacheOpParent::Send__delete__(actor, result, void_t());
     result.SuppressException();
     return IPC_OK();
   }
 
-  MOZ_ASSERT(mManagerId);
+  MOZ_DIAGNOSTIC_ASSERT(mManagerId);
   actor->Execute(mManagerId);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 CacheStorageParent::RecvTeardown()
 {
   if (!Send__delete__(this)) {
@@ -120,19 +120,19 @@ CacheStorageParent::RecvTeardown()
     NS_WARNING("CacheStorage failed to delete actor.");
   }
   return IPC_OK();
 }
 
 void
 CacheStorageParent::OnPrincipalVerified(nsresult aRv, ManagerId* aManagerId)
 {
-  MOZ_ASSERT(mVerifier);
-  MOZ_ASSERT(!mManagerId);
-  MOZ_ASSERT(NS_SUCCEEDED(mVerifiedStatus));
+  MOZ_DIAGNOSTIC_ASSERT(mVerifier);
+  MOZ_DIAGNOSTIC_ASSERT(!mManagerId);
+  MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mVerifiedStatus));
 
   if (NS_WARN_IF(NS_FAILED(aRv))) {
     mVerifiedStatus = aRv;
   }
 
   mManagerId = aManagerId;
   mVerifier->RemoveListener(this);
   mVerifier = nullptr;
--- a/dom/cache/CacheStreamControlChild.cpp
+++ b/dom/cache/CacheStreamControlChild.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheStreamControlChild.h"
 
-#include "mozilla/DebugOnly.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PFileDescriptorSetChild.h"
 #include "nsISupportsImpl.h"
@@ -84,28 +83,29 @@ CacheStreamControlChild::StartDestroy()
   // asked us to shutdown.  So simulate the CloseAll IPC message.
   RecvCloseAll();
 }
 
 void
 CacheStreamControlChild::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
   aReadStreamOut->controlParent() = nullptr;
   aReadStreamOut->controlChild() = this;
 }
 
 void
 CacheStreamControlChild::SerializeStream(CacheReadStream* aReadStreamOut,
                                          nsIInputStream* aStream,
                                          nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlChild);
-  MOZ_ASSERT(aReadStreamOut);
-  MOZ_ASSERT(aStream);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(aStream);
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
   autoStream->Serialize(aStream, Manager());
   aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
 CacheStreamControlChild::NoteClosedAfterForget(const nsID& aId)
 {
--- a/dom/cache/CacheStreamControlParent.cpp
+++ b/dom/cache/CacheStreamControlParent.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/CacheStreamControlParent.h"
 
-#include "mozilla/DebugOnly.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/cache/CacheTypes.h"
 #include "mozilla/dom/cache/ReadStream.h"
 #include "mozilla/dom/cache/StreamList.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/PFileDescriptorSetParent.h"
 #include "nsISupportsImpl.h"
@@ -35,35 +34,37 @@ DeallocPCacheStreamControlParent(PCacheS
 CacheStreamControlParent::CacheStreamControlParent()
 {
   MOZ_COUNT_CTOR(cache::CacheStreamControlParent);
 }
 
 CacheStreamControlParent::~CacheStreamControlParent()
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
-  MOZ_ASSERT(!mStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(!mStreamList);
   MOZ_COUNT_DTOR(cache::CacheStreamControlParent);
 }
 
 void
 CacheStreamControlParent::SerializeControl(CacheReadStream* aReadStreamOut)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
   aReadStreamOut->controlChild() = nullptr;
   aReadStreamOut->controlParent() = this;
 }
 
 void
 CacheStreamControlParent::SerializeStream(CacheReadStream* aReadStreamOut,
                                           nsIInputStream* aStream,
                                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
-  MOZ_ASSERT(aStream);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(aStream);
   UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream(aReadStreamOut->stream()));
   autoStream->Serialize(aStream, Manager());
   aStreamCleanupList.AppendElement(Move(autoStream));
 }
 
 void
 CacheStreamControlParent::NoteClosedAfterForget(const nsID& aId)
 {
@@ -88,26 +89,26 @@ CacheStreamControlParent::ActorDestroy(A
   mStreamList->NoteClosedAll();
   mStreamList = nullptr;
 }
 
 mozilla::ipc::IPCResult
 CacheStreamControlParent::RecvNoteClosed(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
-  MOZ_ASSERT(mStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(mStreamList);
   mStreamList->NoteClosed(aId);
   return IPC_OK();
 }
 
 void
 CacheStreamControlParent::SetStreamList(StreamList* aStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
-  MOZ_ASSERT(!mStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(!mStreamList);
   mStreamList = aStreamList;
 }
 
 void
 CacheStreamControlParent::Close(const nsID& aId)
 {
   NS_ASSERT_OWNINGTHREAD(CacheStreamControlParent);
   NotifyClose(aId);
--- a/dom/cache/CacheWorkerHolder.cpp
+++ b/dom/cache/CacheWorkerHolder.cpp
@@ -16,31 +16,31 @@ namespace cache {
 using mozilla::dom::workers::Terminating;
 using mozilla::dom::workers::Status;
 using mozilla::dom::workers::WorkerPrivate;
 
 // static
 already_AddRefed<CacheWorkerHolder>
 CacheWorkerHolder::Create(WorkerPrivate* aWorkerPrivate)
 {
-  MOZ_ASSERT(aWorkerPrivate);
+  MOZ_DIAGNOSTIC_ASSERT(aWorkerPrivate);
 
   RefPtr<CacheWorkerHolder> workerHolder = new CacheWorkerHolder();
   if (NS_WARN_IF(!workerHolder->HoldWorker(aWorkerPrivate, Terminating))) {
     return nullptr;
   }
 
   return workerHolder.forget();
 }
 
 void
 CacheWorkerHolder::AddActor(ActorChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
-  MOZ_ASSERT(aActor);
+  MOZ_DIAGNOSTIC_ASSERT(aActor);
   MOZ_ASSERT(!mActorList.Contains(aActor));
 
   mActorList.AppendElement(aActor);
 
   // Allow an actor to be added after we've entered the Notifying case.  We
   // can't stop the actor creation from racing with out destruction of the
   // other actors and we need to wait for this extra one to close as well.
   // Signal it should destroy itself right away.
@@ -48,21 +48,24 @@ CacheWorkerHolder::AddActor(ActorChild* 
     aActor->StartDestroy();
   }
 }
 
 void
 CacheWorkerHolder::RemoveActor(ActorChild* aActor)
 {
   NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
-  MOZ_ASSERT(aActor);
+  MOZ_DIAGNOSTIC_ASSERT(aActor);
 
-  DebugOnly<bool> removed = mActorList.RemoveElement(aActor);
+#if defined(RELEASE_OR_BETA)
+  mActorList.RemoveElement(aActor);
+#else
+  MOZ_DIAGNOSTIC_ASSERT(mActorList.RemoveElement(aActor));
+#endif
 
-  MOZ_ASSERT(removed);
   MOZ_ASSERT(!mActorList.Contains(aActor));
 }
 
 bool
 CacheWorkerHolder::Notified() const
 {
   return mNotified;
 }
@@ -78,28 +81,29 @@ CacheWorkerHolder::Notify(Status aStatus
     return true;
   }
 
   mNotified = true;
 
   // Start the asynchronous destruction of our actors.  These will call back
   // into RemoveActor() once the actor is destroyed.
   for (uint32_t i = 0; i < mActorList.Length(); ++i) {
+    MOZ_DIAGNOSTIC_ASSERT(mActorList[i]);
     mActorList[i]->StartDestroy();
   }
 
   return true;
 }
 
 CacheWorkerHolder::CacheWorkerHolder()
   : mNotified(false)
 {
 }
 
 CacheWorkerHolder::~CacheWorkerHolder()
 {
   NS_ASSERT_OWNINGTHREAD(CacheWorkerHolder);
-  MOZ_ASSERT(mActorList.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mActorList.IsEmpty());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/Connection.cpp
+++ b/dom/cache/Connection.cpp
@@ -17,17 +17,17 @@ using mozilla::dom::quota::QuotaObject;
 
 NS_IMPL_ISUPPORTS(cache::Connection, mozIStorageAsyncConnection,
                                      mozIStorageConnection);
 
 Connection::Connection(mozIStorageConnection* aBase)
   : mBase(aBase)
   , mClosed(false)
 {
-  MOZ_ASSERT(mBase);
+  MOZ_DIAGNOSTIC_ASSERT(mBase);
 }
 
 Connection::~Connection()
 {
   NS_ASSERT_OWNINGTHREAD(Connection);
   MOZ_ALWAYS_SUCCEEDS(Close());
 }
 
--- a/dom/cache/Context.cpp
+++ b/dom/cache/Context.cpp
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/Context.h"
 
 #include "mozilla/AutoRestore.h"
-#include "mozilla/DebugOnly.h"
 #include "mozilla/dom/cache/Action.h"
 #include "mozilla/dom/cache/FileUtils.h"
 #include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/cache/ManagerId.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozIStorageConnection.h"
 #include "nsIFile.h"
 #include "nsIPrincipal.h"
@@ -30,57 +29,56 @@ public:
   NullAction()
   {
   }
 
   virtual void
   RunOnTarget(Resolver* aResolver, const QuotaInfo&, Data*) override
   {
     // Resolve success immediately.  This Action does no actual work.
-    MOZ_ASSERT(aResolver);
+    MOZ_DIAGNOSTIC_ASSERT(aResolver);
     aResolver->Resolve(NS_OK);
   }
 };
 
 } // namespace
 
 namespace mozilla {
 namespace dom {
 namespace cache {
 
-using mozilla::DebugOnly;
 using mozilla::dom::quota::AssertIsOnIOThread;
 using mozilla::dom::quota::OpenDirectoryListener;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::PERSISTENCE_TYPE_DEFAULT;
 using mozilla::dom::quota::PersistenceType;
 
 class Context::Data final : public Action::Data
 {
 public:
   explicit Data(nsIThread* aTarget)
     : mTarget(aTarget)
   {
-    MOZ_ASSERT(mTarget);
+    MOZ_DIAGNOSTIC_ASSERT(mTarget);
   }
 
   virtual mozIStorageConnection*
   GetConnection() const override
   {
     MOZ_ASSERT(mTarget == NS_GetCurrentThread());
     return mConnection;
   }
 
   virtual void
   SetConnection(mozIStorageConnection* aConn) override
   {
     MOZ_ASSERT(mTarget == NS_GetCurrentThread());
-    MOZ_ASSERT(!mConnection);
+    MOZ_DIAGNOSTIC_ASSERT(!mConnection);
     mConnection = aConn;
-    MOZ_ASSERT(mConnection);
+    MOZ_DIAGNOSTIC_ASSERT(mConnection);
   }
 
 private:
   ~Data()
   {
     // We could proxy release our data here, but instead just assert.  The
     // Context code should guarantee that we are destroyed on the target
     // thread once the connection is initialized.  If we're not, then
@@ -114,42 +112,42 @@ public:
     , mData(aData)
     , mTarget(aTarget)
     , mInitAction(aInitAction)
     , mInitiatingThread(NS_GetCurrentThread())
     , mResult(NS_OK)
     , mState(STATE_INIT)
     , mCanceled(false)
   {
-    MOZ_ASSERT(mContext);
-    MOZ_ASSERT(mManager);
-    MOZ_ASSERT(mData);
-    MOZ_ASSERT(mTarget);
-    MOZ_ASSERT(mInitiatingThread);
-    MOZ_ASSERT(mInitAction);
+    MOZ_DIAGNOSTIC_ASSERT(mContext);
+    MOZ_DIAGNOSTIC_ASSERT(mManager);
+    MOZ_DIAGNOSTIC_ASSERT(mData);
+    MOZ_DIAGNOSTIC_ASSERT(mTarget);
+    MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
+    MOZ_DIAGNOSTIC_ASSERT(mInitAction);
   }
 
   nsresult Dispatch()
   {
     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-    MOZ_ASSERT(mState == STATE_INIT);
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
 
     mState = STATE_GET_INFO;
     nsresult rv = NS_DispatchToMainThread(this, nsIThread::DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mState = STATE_COMPLETE;
       Clear();
     }
     return rv;
   }
 
   void Cancel()
   {
     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-    MOZ_ASSERT(!mCanceled);
+    MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
     mCanceled = true;
     mInitAction->CancelOnInitiatingThread();
   }
 
   void OpenDirectory();
 
   // OpenDirectoryListener methods
   virtual void
@@ -165,17 +163,17 @@ private:
     SyncResolver()
       : mResolved(false)
       , mResult(NS_OK)
     { }
 
     virtual void
     Resolve(nsresult aRv) override
     {
-      MOZ_ASSERT(!mResolved);
+      MOZ_DIAGNOSTIC_ASSERT(!mResolved);
       mResolved = true;
       mResult = aRv;
     };
 
     bool Resolved() const { return mResolved; }
     nsresult Result() const { return mResult; }
 
   private:
@@ -184,19 +182,19 @@ private:
     bool mResolved;
     nsresult mResult;
 
     NS_INLINE_DECL_REFCOUNTING(Context::QuotaInitRunnable::SyncResolver, override)
   };
 
   ~QuotaInitRunnable()
   {
-    MOZ_ASSERT(mState == STATE_COMPLETE);
-    MOZ_ASSERT(!mContext);
-    MOZ_ASSERT(!mInitAction);
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
+    MOZ_DIAGNOSTIC_ASSERT(!mContext);
+    MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
   }
 
   enum State
   {
     STATE_INIT,
     STATE_GET_INFO,
     STATE_CREATE_QUOTA_MANAGER,
     STATE_OPEN_DIRECTORY,
@@ -205,30 +203,30 @@ private:
     STATE_RUN_ON_TARGET,
     STATE_RUNNING,
     STATE_COMPLETING,
     STATE_COMPLETE
   };
 
   void Complete(nsresult aResult)
   {
-    MOZ_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult));
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING || NS_FAILED(aResult));
 
-    MOZ_ASSERT(NS_SUCCEEDED(mResult));
+    MOZ_DIAGNOSTIC_ASSERT(NS_SUCCEEDED(mResult));
     mResult = aResult;
 
     mState = STATE_COMPLETING;
     MOZ_ALWAYS_SUCCEEDS(
       mInitiatingThread->Dispatch(this, nsIThread::DISPATCH_NORMAL));
   }
 
   void Clear()
   {
     NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-    MOZ_ASSERT(mContext);
+    MOZ_DIAGNOSTIC_ASSERT(mContext);
     mContext = nullptr;
     mManager = nullptr;
     mInitAction = nullptr;
   }
 
   RefPtr<Context> mContext;
   RefPtr<ThreadsafeHandle> mThreadsafeHandle;
   RefPtr<Manager> mManager;
@@ -246,19 +244,19 @@ public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
 };
 
 void
 Context::QuotaInitRunnable::OpenDirectory()
 {
   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-  MOZ_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER ||
-             mState == STATE_OPEN_DIRECTORY);
-  MOZ_ASSERT(QuotaManager::Get());
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CREATE_QUOTA_MANAGER ||
+                        mState == STATE_OPEN_DIRECTORY);
+  MOZ_DIAGNOSTIC_ASSERT(QuotaManager::Get());
 
   // QuotaManager::OpenDirectory() will hold a reference to us as
   // a listener.  We will then get DirectoryLockAcquired() on the owning
   // thread when it is safe to access our storage directory.
   mState = STATE_WAIT_FOR_DIRECTORY_LOCK;
   QuotaManager::Get()->OpenDirectory(PERSISTENCE_TYPE_DEFAULT,
                                      mQuotaInfo.mGroup,
                                      mQuotaInfo.mOrigin,
@@ -267,43 +265,43 @@ Context::QuotaInitRunnable::OpenDirector
                                      /* aExclusive */ false,
                                      this);
 }
 
 void
 Context::QuotaInitRunnable::DirectoryLockAcquired(DirectoryLock* aLock)
 {
   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-  MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
-  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
+  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
 
   mDirectoryLock = aLock;
 
   if (mCanceled) {
     Complete(NS_ERROR_ABORT);
     return;
   }
 
   QuotaManager* qm = QuotaManager::Get();
-  MOZ_ASSERT(qm);
+  MOZ_DIAGNOSTIC_ASSERT(qm);
 
   mState = STATE_ENSURE_ORIGIN_INITIALIZED;
   nsresult rv = qm->IOThread()->Dispatch(this, nsIThread::DISPATCH_NORMAL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Complete(rv);
     return;
   }
 }
 
 void
 Context::QuotaInitRunnable::DirectoryLockFailed()
 {
   NS_ASSERT_OWNINGTHREAD(QuotaInitRunnable);
-  MOZ_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
-  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_WAIT_FOR_DIRECTORY_LOCK);
+  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
 
   NS_WARNING("Failed to acquire a directory lock!");
 
   Complete(NS_ERROR_FAILURE);
 }
 
 NS_IMPL_ISUPPORTS(mozilla::dom::cache::Context::QuotaInitRunnable, nsIRunnable);
 
@@ -429,17 +427,17 @@ Context::QuotaInitRunnable::Run()
       AssertIsOnIOThread();
 
       if (mCanceled) {
         resolver->Resolve(NS_ERROR_ABORT);
         break;
       }
 
       QuotaManager* qm = QuotaManager::Get();
-      MOZ_ASSERT(qm);
+      MOZ_DIAGNOSTIC_ASSERT(qm);
       nsresult rv = qm->EnsureOriginIsInitialized(PERSISTENCE_TYPE_DEFAULT,
                                                   mQuotaInfo.mSuffix,
                                                   mQuotaInfo.mGroup,
                                                   mQuotaInfo.mOrigin,
                                                   mQuotaInfo.mIsApp,
                                                   getter_AddRefs(mQuotaInfo.mDir));
       if (NS_FAILED(rv)) {
         resolver->Resolve(rv);
@@ -457,17 +455,17 @@ Context::QuotaInitRunnable::Run()
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
 
       mState = STATE_RUNNING;
 
       // Execute the provided initialization Action.  The Action must Resolve()
       // before returning.
       mInitAction->RunOnTarget(resolver, mQuotaInfo, mData);
-      MOZ_ASSERT(resolver->Resolved());
+      MOZ_DIAGNOSTIC_ASSERT(resolver->Resolved());
 
       mData = nullptr;
 
       // If the database was opened, then we should always succeed when creating
       // the marker file.  If it wasn't opened successfully, then no need to
       // create a marker file anyway.
       if (NS_SUCCEEDED(resolver->Result())) {
         MOZ_ALWAYS_SUCCEEDS(CreateMarkerFile(mQuotaInfo));
@@ -518,28 +516,28 @@ public:
     , mTarget(aTarget)
     , mAction(aAction)
     , mQuotaInfo(aQuotaInfo)
     , mInitiatingThread(NS_GetCurrentThread())
     , mState(STATE_INIT)
     , mResult(NS_OK)
     , mExecutingRunOnTarget(false)
   {
-    MOZ_ASSERT(mContext);
+    MOZ_DIAGNOSTIC_ASSERT(mContext);
     // mData may be nullptr
-    MOZ_ASSERT(mTarget);
-    MOZ_ASSERT(mAction);
+    MOZ_DIAGNOSTIC_ASSERT(mTarget);
+    MOZ_DIAGNOSTIC_ASSERT(mAction);
     // mQuotaInfo.mDir may be nullptr if QuotaInitRunnable failed
-    MOZ_ASSERT(mInitiatingThread);
+    MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
   }
 
   nsresult Dispatch()
   {
     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
-    MOZ_ASSERT(mState == STATE_INIT);
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_INIT);
 
     mState = STATE_RUN_ON_TARGET;
     nsresult rv = mTarget->Dispatch(this, nsIEventTarget::DISPATCH_NORMAL);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mState = STATE_COMPLETE;
       Clear();
     }
     return rv;
@@ -557,17 +555,17 @@ public:
   {
     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
     mAction->CancelOnInitiatingThread();
   }
 
   virtual void Resolve(nsresult aRv) override
   {
     MOZ_ASSERT(mTarget == NS_GetCurrentThread());
-    MOZ_ASSERT(mState == STATE_RUNNING);
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_RUNNING);
 
     mResult = aRv;
 
     // We ultimately must complete on the initiating thread, but bounce through
     // the current thread again to ensure that we don't destroy objects and
     // state out from under the currently running action's stack.
     mState = STATE_RESOLVING;
 
@@ -583,26 +581,26 @@ public:
     // bounce to run on the target thread again.
     MOZ_ALWAYS_SUCCEEDS(
       mTarget->Dispatch(this, nsIThread::DISPATCH_NORMAL));
   }
 
 private:
   ~ActionRunnable()
   {
-    MOZ_ASSERT(mState == STATE_COMPLETE);
-    MOZ_ASSERT(!mContext);
-    MOZ_ASSERT(!mAction);
+    MOZ_DIAGNOSTIC_ASSERT(mState == STATE_COMPLETE);
+    MOZ_DIAGNOSTIC_ASSERT(!mContext);
+    MOZ_DIAGNOSTIC_ASSERT(!mAction);
   }
 
   void Clear()
   {
     NS_ASSERT_OWNINGTHREAD(ActionRunnable);
-    MOZ_ASSERT(mContext);
-    MOZ_ASSERT(mAction);
+    MOZ_DIAGNOSTIC_ASSERT(mContext);
+    MOZ_DIAGNOSTIC_ASSERT(mAction);
     mContext->RemoveActivity(this);
     mContext = nullptr;
     mAction = nullptr;
   }
 
   enum State
   {
     STATE_INIT,
@@ -668,17 +666,17 @@ NS_IMPL_ISUPPORTS(mozilla::dom::cache::C
 NS_IMETHODIMP
 Context::ActionRunnable::Run()
 {
   switch(mState) {
     // ----------------------
     case STATE_RUN_ON_TARGET:
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mTarget);
-      MOZ_ASSERT(!mExecutingRunOnTarget);
+      MOZ_DIAGNOSTIC_ASSERT(!mExecutingRunOnTarget);
 
       // Note that we are calling RunOnTarget().  This lets us detect
       // if Resolve() is called synchronously.
       AutoRestore<bool> executingRunOnTarget(mExecutingRunOnTarget);
       mExecutingRunOnTarget = true;
 
       mState = STATE_RUNNING;
       mAction->RunOnTarget(this, mQuotaInfo, mData);
@@ -815,26 +813,26 @@ Context::ThreadsafeHandle::InvalidateAnd
   // Cancel the Context through the weak reference.  This means we can
   // allow the Context to close by dropping the strong ref, but then
   // still cancel ongoing IO if necessary.
   if (mWeakRef) {
     mWeakRef->Invalidate();
   }
   // We should synchronously have AllowToCloseOnOwningThread called when
   // the Context is canceled.
-  MOZ_ASSERT(!mStrongRef);
+  MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
 }
 
 void
 Context::ThreadsafeHandle::ContextDestroyed(Context* aContext)
 {
   MOZ_ASSERT(mOwningThread == NS_GetCurrentThread());
-  MOZ_ASSERT(!mStrongRef);
-  MOZ_ASSERT(mWeakRef);
-  MOZ_ASSERT(mWeakRef == aContext);
+  MOZ_DIAGNOSTIC_ASSERT(!mStrongRef);
+  MOZ_DIAGNOSTIC_ASSERT(mWeakRef);
+  MOZ_DIAGNOSTIC_ASSERT(mWeakRef == aContext);
   mWeakRef = nullptr;
 }
 
 // static
 already_AddRefed<Context>
 Context::Create(Manager* aManager, nsIThread* aTarget,
                 Action* aInitAction, Context* aOldContext)
 {
@@ -846,49 +844,49 @@ Context::Create(Manager* aManager, nsITh
 Context::Context(Manager* aManager, nsIThread* aTarget, Action* aInitAction)
   : mManager(aManager)
   , mTarget(aTarget)
   , mData(new Data(aTarget))
   , mState(STATE_CONTEXT_PREINIT)
   , mOrphanedData(false)
   , mInitAction(aInitAction)
 {
-  MOZ_ASSERT(mManager);
-  MOZ_ASSERT(mTarget);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mTarget);
 }
 
 void
 Context::Dispatch(Action* aAction)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(aAction);
+  MOZ_DIAGNOSTIC_ASSERT(aAction);
 
-  MOZ_ASSERT(mState != STATE_CONTEXT_CANCELED);
+  MOZ_DIAGNOSTIC_ASSERT(mState != STATE_CONTEXT_CANCELED);
   if (mState == STATE_CONTEXT_CANCELED) {
     return;
   } else if (mState == STATE_CONTEXT_INIT ||
              mState == STATE_CONTEXT_PREINIT) {
     PendingAction* pending = mPendingActions.AppendElement();
     pending->mAction = aAction;
     return;
   }
 
-  MOZ_ASSERT(mState == STATE_CONTEXT_READY);
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_READY);
   DispatchAction(aAction);
 }
 
 void
 Context::CancelAll()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
 
   // In PREINIT state we have not dispatch the init action yet.  Just
   // forget it.
   if (mState == STATE_CONTEXT_PREINIT) {
-    MOZ_ASSERT(!mInitRunnable);
+    MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
     mInitAction = nullptr;
 
   // In INIT state we have dispatched the runnable, but not received the
   // async completion yet.  Cancel the runnable, but don't forget about it
   // until we get OnQuotaInit() callback.
   } else if (mState == STATE_CONTEXT_INIT) {
     mInitRunnable->Cancel();
   }
@@ -948,18 +946,18 @@ Context::CancelForCacheId(CacheId aCache
       activity->Cancel();
     }
   }
 }
 
 Context::~Context()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(mManager);
-  MOZ_ASSERT(!mData);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(!mData);
 
   if (mThreadsafeHandle) {
     mThreadsafeHandle->ContextDestroyed(this);
   }
 
   // Note, this may set the mOrphanedData flag.
   mManager->RemoveContext(this);
 
@@ -988,23 +986,23 @@ Context::Init(Context* aOldContext)
 void
 Context::Start()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
 
   // Previous context closing delayed our start, but then we were canceled.
   // In this case, just do nothing here.
   if (mState == STATE_CONTEXT_CANCELED) {
-    MOZ_ASSERT(!mInitRunnable);
-    MOZ_ASSERT(!mInitAction);
+    MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
+    MOZ_DIAGNOSTIC_ASSERT(!mInitAction);
     return;
   }
 
-  MOZ_ASSERT(mState == STATE_CONTEXT_PREINIT);
-  MOZ_ASSERT(!mInitRunnable);
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_PREINIT);
+  MOZ_DIAGNOSTIC_ASSERT(!mInitRunnable);
 
   mInitRunnable = new QuotaInitRunnable(this, mManager, mData, mTarget,
                                         mInitAction);
   mInitAction = nullptr;
 
   mState = STATE_CONTEXT_INIT;
 
   nsresult rv = mInitRunnable->Dispatch();
@@ -1038,24 +1036,24 @@ Context::DispatchAction(Action* aAction,
 }
 
 void
 Context::OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
                      already_AddRefed<DirectoryLock> aDirectoryLock)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
 
-  MOZ_ASSERT(mInitRunnable);
+  MOZ_DIAGNOSTIC_ASSERT(mInitRunnable);
   mInitRunnable = nullptr;
 
   mQuotaInfo = aQuotaInfo;
 
   // Always save the directory lock to ensure QuotaManager does not shutdown
   // before the Context has gone away.
-  MOZ_ASSERT(!mDirectoryLock);
+  MOZ_DIAGNOSTIC_ASSERT(!mDirectoryLock);
   mDirectoryLock = aDirectoryLock;
 
   // If we opening the context failed, but we were not explicitly canceled,
   // still treat the entire context as canceled.  We don't want to allow
   // new actions to be dispatched.  We also cannot leave the context in
   // the INIT state after failing to open.
   if (NS_FAILED(aRv)) {
     mState = STATE_CONTEXT_CANCELED;
@@ -1066,39 +1064,39 @@ Context::OnQuotaInit(nsresult aRv, const
       mPendingActions[i].mAction->CompleteOnInitiatingThread(aRv);
     }
     mPendingActions.Clear();
     mThreadsafeHandle->AllowToClose();
     // Context will destruct after return here and last ref is released.
     return;
   }
 
-  MOZ_ASSERT(mState == STATE_CONTEXT_INIT);
+  MOZ_DIAGNOSTIC_ASSERT(mState == STATE_CONTEXT_INIT);
   mState = STATE_CONTEXT_READY;
 
   for (uint32_t i = 0; i < mPendingActions.Length(); ++i) {
     DispatchAction(mPendingActions[i].mAction);
   }
   mPendingActions.Clear();
 }
 
 void
 Context::AddActivity(Activity* aActivity)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(aActivity);
+  MOZ_DIAGNOSTIC_ASSERT(aActivity);
   MOZ_ASSERT(!mActivityList.Contains(aActivity));
   mActivityList.AppendElement(aActivity);
 }
 
 void
 Context::RemoveActivity(Activity* aActivity)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(aActivity);
+  MOZ_DIAGNOSTIC_ASSERT(aActivity);
   MOZ_ALWAYS_TRUE(mActivityList.RemoveElement(aActivity));
   MOZ_ASSERT(!mActivityList.Contains(aActivity));
 }
 
 void
 Context::NoteOrphanedData()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
@@ -1116,36 +1114,36 @@ Context::CreateThreadsafeHandle()
   RefPtr<ThreadsafeHandle> ref = mThreadsafeHandle;
   return ref.forget();
 }
 
 void
 Context::SetNextContext(Context* aNextContext)
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(aNextContext);
-  MOZ_ASSERT(!mNextContext);
+  MOZ_DIAGNOSTIC_ASSERT(aNextContext);
+  MOZ_DIAGNOSTIC_ASSERT(!mNextContext);
   mNextContext = aNextContext;
 }
 
 void
 Context::DoomTargetData()
 {
   NS_ASSERT_OWNINGTHREAD(Context);
-  MOZ_ASSERT(mData);
+  MOZ_DIAGNOSTIC_ASSERT(mData);
 
   // We are about to drop our reference to the Data.  We need to ensure that
   // the ~Context() destructor does not run until contents of Data have been
   // released on the Target thread.
 
   // Dispatch a no-op Action.  This will hold the Context alive through a
   // roundtrip to the target thread and back to the owning thread.  The
   // ref to the Data object is cleared on the owning thread after creating
   // the ActionRunnable, but before dispatching it.
   RefPtr<Action> action = new NullAction();
   DispatchAction(action, true /* doomed data */);
 
-  MOZ_ASSERT(!mData);
+  MOZ_DIAGNOSTIC_ASSERT(!mData);
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/DBAction.cpp
+++ b/dom/cache/DBAction.cpp
@@ -35,18 +35,18 @@ DBAction::~DBAction()
 {
 }
 
 void
 DBAction::RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
                       Data* aOptionalData)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aResolver);
-  MOZ_ASSERT(aQuotaInfo.mDir);
+  MOZ_DIAGNOSTIC_ASSERT(aResolver);
+  MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
 
   if (IsCanceled()) {
     aResolver->Resolve(NS_ERROR_ABORT);
     return;
   }
 
   nsCOMPtr<nsIFile> dbDir;
   nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
@@ -70,17 +70,17 @@ DBAction::RunOnTarget(Resolver* aResolve
 
   // If there is no previous Action, then we must open one.
   if (!conn) {
     rv = OpenConnection(aQuotaInfo, dbDir, getter_AddRefs(conn));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aResolver->Resolve(rv);
       return;
     }
-    MOZ_ASSERT(conn);
+    MOZ_DIAGNOSTIC_ASSERT(conn);
 
     // Save this connection in the shared Data object so later Actions can
     // use it.  This avoids opening a new connection for every Action.
     if (aOptionalData) {
       // Since we know this connection will be around for as long as the
       // Cache is open, use our special wrapped connection class.  This
       // will let us perform certain operations once the Cache origin
       // is closed.
@@ -92,18 +92,18 @@ DBAction::RunOnTarget(Resolver* aResolve
   RunWithDBOnTarget(aResolver, aQuotaInfo, dbDir, conn);
 }
 
 nsresult
 DBAction::OpenConnection(const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                          mozIStorageConnection** aConnOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aDBDir);
-  MOZ_ASSERT(aConnOut);
+  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConnOut);
 
   nsCOMPtr<mozIStorageConnection> conn;
 
   bool exists;
   nsresult rv = aDBDir->Exists(&exists);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (!exists) {
@@ -213,19 +213,19 @@ SyncDBAction::~SyncDBAction()
 }
 
 void
 SyncDBAction::RunWithDBOnTarget(Resolver* aResolver,
                                 const QuotaInfo& aQuotaInfo, nsIFile* aDBDir,
                                 mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aResolver);
-  MOZ_ASSERT(aDBDir);
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aResolver);
+  MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsresult rv = RunSyncWithDBOnTarget(aQuotaInfo, aDBDir, aConn);
   aResolver->Resolve(rv);
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/cache/DBSchema.cpp
+++ b/dom/cache/DBSchema.cpp
@@ -401,17 +401,17 @@ private:
   nsCOMPtr<mozIStorageConnection> mConn;
   bool mForeignKeyCheckingDisabled;
 };
 
 nsresult
 CreateOrMigrateSchema(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   int32_t schemaVersion;
   nsresult rv = aConn->GetSchemaVersion(&schemaVersion);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (schemaVersion == kLatestSchemaVersion) {
     // We already have the correct schema version.  Validate it matches
     // our expected schema and then proceed.
@@ -491,17 +491,17 @@ CreateOrMigrateSchema(mozIStorageConnect
 
   return rv;
 }
 
 nsresult
 InitializeConnection(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // This function needs to perform per-connection initialization tasks that
   // need to happen regardless of the schema.
 
   nsPrintfCString pragmas(
     // Use a smaller page size to improve perf/footprint; default is too large
     "PRAGMA page_size = %u; "
     // Enable auto_vacuum; this must happen after page_size and before WAL
@@ -566,18 +566,18 @@ InitializeConnection(mozIStorageConnecti
 
   return NS_OK;
 }
 
 nsresult
 CreateCacheId(mozIStorageConnection* aConn, CacheId* aCacheIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aCacheIdOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
 
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "INSERT INTO caches DEFAULT VALUES;"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsCOMPtr<mozIStorageStatement> state;
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
@@ -596,17 +596,17 @@ CreateCacheId(mozIStorageConnection* aCo
   return rv;
 }
 
 nsresult
 DeleteCacheId(mozIStorageConnection* aConn, CacheId aCacheId,
               nsTArray<nsID>& aDeletedBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // Delete the bodies explicitly as we need to read out the body IDs
   // anyway.  These body IDs must be deleted one-by-one as content may
   // still be referencing them invidivually.
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryAll(aConn, aCacheId, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -634,35 +634,35 @@ DeleteCacheId(mozIStorageConnection* aCo
   return rv;
 }
 
 nsresult
 IsCacheOrphaned(mozIStorageConnection* aConn, CacheId aCacheId,
                 bool* aOrphanedOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aOrphanedOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aOrphanedOut);
 
   // err on the side of not deleting user data
   *aOrphanedOut = false;
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT COUNT(*) FROM storage WHERE cache_id=:cache_id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool hasMoreData = false;
   rv = state->ExecuteStep(&hasMoreData);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-  MOZ_ASSERT(hasMoreData);
+  MOZ_DIAGNOSTIC_ASSERT(hasMoreData);
 
   int32_t refCount;
   rv = state->GetInt32(0, &refCount);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   *aOrphanedOut = refCount == 0;
 
   return rv;
@@ -689,17 +689,17 @@ FindOrphanedCacheIds(mozIStorageConnecti
 
   return rv;
 }
 
 nsresult
 GetKnownBodyIds(mozIStorageConnection* aConn, nsTArray<nsID>& aBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT request_body_id, response_body_id FROM entries;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool hasMoreData = false;
@@ -727,19 +727,19 @@ GetKnownBodyIds(mozIStorageConnection* a
 nsresult
 CacheMatch(mozIStorageConnection* aConn, CacheId aCacheId,
            const CacheRequest& aRequest,
            const CacheQueryParams& aParams,
            bool* aFoundResponseOut,
            SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aFoundResponseOut);
-  MOZ_ASSERT(aSavedResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
 
   *aFoundResponseOut = false;
 
   AutoTArray<EntryId, 1> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches, 1);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (matches.IsEmpty()) {
@@ -757,17 +757,17 @@ CacheMatch(mozIStorageConnection* aConn,
 
 nsresult
 CacheMatchAll(mozIStorageConnection* aConn, CacheId aCacheId,
               const CacheRequestOrVoid& aRequestOrVoid,
               const CacheQueryParams& aParams,
               nsTArray<SavedResponse>& aSavedResponsesOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
   nsresult rv;
 
   AutoTArray<EntryId, 256> matches;
   if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     rv = QueryAll(aConn, aCacheId, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   } else {
     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
@@ -790,17 +790,17 @@ nsresult
 CachePut(mozIStorageConnection* aConn, CacheId aCacheId,
          const CacheRequest& aRequest,
          const nsID* aRequestBodyId,
          const CacheResponse& aResponse,
          const nsID* aResponseBodyId,
          nsTArray<nsID>& aDeletedBodyIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   CacheQueryParams params(false, false, false, false,
                            NS_LITERAL_STRING(""));
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, params, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   AutoTArray<IdCount, 16> deletedSecurityIdList;
@@ -822,18 +822,18 @@ CachePut(mozIStorageConnection* aConn, C
 
 nsresult
 CacheDelete(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const CacheQueryParams& aParams,
             nsTArray<nsID>& aDeletedBodyIdListOut, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aSuccessOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aSuccessOut);
 
   *aSuccessOut = false;
 
   AutoTArray<EntryId, 256> matches;
   nsresult rv = QueryCache(aConn, aCacheId, aRequest, aParams, matches);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   if (matches.IsEmpty()) {
@@ -855,17 +855,17 @@ CacheDelete(mozIStorageConnection* aConn
 
 nsresult
 CacheKeys(mozIStorageConnection* aConn, CacheId aCacheId,
           const CacheRequestOrVoid& aRequestOrVoid,
           const CacheQueryParams& aParams,
           nsTArray<SavedRequest>& aSavedRequestsOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
   nsresult rv;
 
   AutoTArray<EntryId, 256> matches;
   if (aRequestOrVoid.type() == CacheRequestOrVoid::Tvoid_t) {
     rv = QueryAll(aConn, aCacheId, matches);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   } else {
     rv = QueryCache(aConn, aCacheId, aRequestOrVoid, aParams, matches);
@@ -888,19 +888,19 @@ nsresult
 StorageMatch(mozIStorageConnection* aConn,
              Namespace aNamespace,
              const CacheRequest& aRequest,
              const CacheQueryParams& aParams,
              bool* aFoundResponseOut,
              SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aFoundResponseOut);
-  MOZ_ASSERT(aSavedResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aFoundResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
 
   *aFoundResponseOut = false;
 
   nsresult rv;
 
   // If we are given a cache to check, then simply find its cache ID
   // and perform the match.
   if (!aParams.cacheName().EqualsLiteral("")) {
@@ -956,19 +956,19 @@ StorageMatch(mozIStorageConnection* aCon
 }
 
 nsresult
 StorageGetCacheId(mozIStorageConnection* aConn, Namespace aNamespace,
                   const nsAString& aKey, bool* aFoundCacheOut,
                   CacheId* aCacheIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aFoundCacheOut);
-  MOZ_ASSERT(aCacheIdOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aFoundCacheOut);
+  MOZ_DIAGNOSTIC_ASSERT(aCacheIdOut);
 
   *aFoundCacheOut = false;
 
   // How we constrain the key column depends on the value of our key.  Use
   // a format string for the query and let CreateAndBindKeyStatement() fill
   // it in for us.
   const char* query = "SELECT cache_id FROM storage "
                       "WHERE namespace=:namespace AND %s "
@@ -997,17 +997,17 @@ StorageGetCacheId(mozIStorageConnection*
   return rv;
 }
 
 nsresult
 StoragePutCache(mozIStorageConnection* aConn, Namespace aNamespace,
                 const nsAString& aKey, CacheId aCacheId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "INSERT INTO storage (namespace, key, cache_id) "
                  "VALUES (:namespace, :key, :cache_id);"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1026,17 +1026,17 @@ StoragePutCache(mozIStorageConnection* a
   return rv;
 }
 
 nsresult
 StorageForgetCache(mozIStorageConnection* aConn, Namespace aNamespace,
                    const nsAString& aKey)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // How we constrain the key column depends on the value of our key.  Use
   // a format string for the query and let CreateAndBindKeyStatement() fill
   // it in for us.
   const char *query = "DELETE FROM storage WHERE namespace=:namespace AND %s;";
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = CreateAndBindKeyStatement(aConn, query, aKey,
@@ -1052,17 +1052,17 @@ StorageForgetCache(mozIStorageConnection
   return rv;
 }
 
 nsresult
 StorageGetKeys(mozIStorageConnection* aConn, Namespace aNamespace,
                nsTArray<nsString>& aKeysOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT key FROM storage WHERE namespace=:namespace ORDER BY rowid;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt32ByName(NS_LITERAL_CSTRING("namespace"), aNamespace);
@@ -1082,17 +1082,17 @@ StorageGetKeys(mozIStorageConnection* aC
 
 namespace {
 
 nsresult
 QueryAll(mozIStorageConnection* aConn, CacheId aCacheId,
          nsTArray<EntryId>& aEntryIdListOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT id FROM entries WHERE cache_id=:cache_id ORDER BY id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = state->BindInt64ByName(NS_LITERAL_CSTRING("cache_id"), aCacheId);
@@ -1112,18 +1112,18 @@ QueryAll(mozIStorageConnection* aConn, C
 nsresult
 QueryCache(mozIStorageConnection* aConn, CacheId aCacheId,
            const CacheRequest& aRequest,
            const CacheQueryParams& aParams,
            nsTArray<EntryId>& aEntryIdListOut,
            uint32_t aMaxResults)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aMaxResults > 0);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aMaxResults > 0);
 
   if (!aParams.ignoreMethod() &&
       !aRequest.method().LowerCaseEqualsLiteral("get"))
   {
     return NS_OK;
   }
 
   nsAutoCString query(
@@ -1217,17 +1217,17 @@ QueryCache(mozIStorageConnection* aConn,
 }
 
 nsresult
 MatchByVaryHeader(mozIStorageConnection* aConn,
                   const CacheRequest& aRequest,
                   EntryId entryId, bool* aSuccessOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   *aSuccessOut = false;
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT value FROM response_headers "
     "WHERE name='vary' AND entry_id=:entry_id;"
   ), getter_AddRefs(state));
@@ -1243,17 +1243,17 @@ MatchByVaryHeader(mozIStorageConnection*
     nsAutoCString value;
     rv = state->GetUTF8String(0, value);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     varyValues.AppendElement(value);
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Should not have called this function if this was not the case
-  MOZ_ASSERT(!varyValues.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(!varyValues.IsEmpty());
 
   state->Reset();
   rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT name, value FROM request_headers "
     "WHERE entry_id=:entry_id;"
   ), getter_AddRefs(state));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -1288,33 +1288,33 @@ MatchByVaryHeader(mozIStorageConnection*
     // Extract the header names inside the Vary header value.
     nsAutoCString varyValue(varyValues[i]);
     char* rawBuffer = varyValue.BeginWriting();
     char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
     bool bailOut = false;
     for (; token;
          token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
       nsDependentCString header(token);
-      MOZ_ASSERT(!header.EqualsLiteral("*"),
-                 "We should have already caught this in "
-                 "TypeUtils::ToPCacheResponseWithoutBody()");
+      MOZ_DIAGNOSTIC_ASSERT(!header.EqualsLiteral("*"),
+                            "We should have already caught this in "
+                            "TypeUtils::ToPCacheResponseWithoutBody()");
 
       ErrorResult errorResult;
       nsAutoCString queryValue;
       queryHeaders->Get(header, queryValue, errorResult);
       if (errorResult.Failed()) {
         errorResult.SuppressException();
-        MOZ_ASSERT(queryValue.IsEmpty());
+        MOZ_DIAGNOSTIC_ASSERT(queryValue.IsEmpty());
       }
 
       nsAutoCString cachedValue;
       cachedHeaders->Get(header, cachedValue, errorResult);
       if (errorResult.Failed()) {
         errorResult.SuppressException();
-        MOZ_ASSERT(cachedValue.IsEmpty());
+        MOZ_DIAGNOSTIC_ASSERT(cachedValue.IsEmpty());
       }
 
       if (queryValue != cachedValue) {
         varyHeadersMatch = false;
         bailOut = true;
         break;
       }
     }
@@ -1331,23 +1331,23 @@ MatchByVaryHeader(mozIStorageConnection*
 nsresult
 DeleteEntries(mozIStorageConnection* aConn,
               const nsTArray<EntryId>& aEntryIdList,
               nsTArray<nsID>& aDeletedBodyIdListOut,
               nsTArray<IdCount>& aDeletedSecurityIdListOut,
               uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   if (aEntryIdList.IsEmpty()) {
     return NS_OK;
   }
 
-  MOZ_ASSERT(aPos < aEntryIdList.Length());
+  MOZ_DIAGNOSTIC_ASSERT(aPos < aEntryIdList.Length());
 
   if (aLen < 0) {
     aLen = aEntryIdList.Length() - aPos;
   }
 
   // Sqlite limits the number of entries allowed for an IN clause,
   // so split up larger operations.
   if (aLen > kMaxEntriesPerStatement) {
@@ -1444,20 +1444,20 @@ DeleteEntries(mozIStorageConnection* aCo
 
   return rv;
 }
 
 nsresult
 InsertSecurityInfo(mozIStorageConnection* aConn, nsICryptoHash* aCrypto,
                    const nsACString& aData, int32_t *aIdOut)
 {
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aCrypto);
-  MOZ_ASSERT(aIdOut);
-  MOZ_ASSERT(!aData.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aCrypto);
+  MOZ_DIAGNOSTIC_ASSERT(aIdOut);
+  MOZ_DIAGNOSTIC_ASSERT(!aData.IsEmpty());
 
   // We want to use an index to find existing security blobs, but indexing
   // the full blob would be quite expensive.  Instead, we index a small
   // hash value.  Calculate this hash as the first 8 bytes of the SHA1 of
   // the full data.
   nsAutoCString hash;
   nsresult rv = HashCString(aCrypto, aData, hash);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -1561,17 +1561,17 @@ DeleteSecurityInfo(mozIStorageConnection
   bool hasMoreData = false;
   rv = state->ExecuteStep(&hasMoreData);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   int32_t refcount = -1;
   rv = state->GetInt32(0, &refcount);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
-  MOZ_ASSERT(refcount >= aCount);
+  MOZ_DIAGNOSTIC_ASSERT(refcount >= aCount);
 
   // Next, calculate the new refcount
   int32_t newCount = refcount - aCount;
 
   // If the last reference to this security blob was removed we can
   // just remove the entire row.
   if (newCount == 0) {
     rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
@@ -1623,17 +1623,17 @@ DeleteSecurityInfoList(mozIStorageConnec
 nsresult
 InsertEntry(mozIStorageConnection* aConn, CacheId aCacheId,
             const CacheRequest& aRequest,
             const nsID* aRequestBodyId,
             const CacheResponse& aResponse,
             const nsID* aResponseBodyId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   nsresult rv = NS_OK;
 
   nsCOMPtr<nsICryptoHash> crypto =
     do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   int32_t securityId = -1;
@@ -1792,17 +1792,17 @@ InsertEntry(mozIStorageConnection* aConn
   }
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsAutoCString serializedInfo;
   // We only allow content serviceworkers right now.
   if (aResponse.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
     const mozilla::ipc::PrincipalInfo& principalInfo =
       aResponse.principalInfo().get_PrincipalInfo();
-    MOZ_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
+    MOZ_DIAGNOSTIC_ASSERT(principalInfo.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
     const mozilla::ipc::ContentPrincipalInfo& cInfo =
       principalInfo.get_ContentPrincipalInfo();
 
     serializedInfo.Append(cInfo.spec());
 
     nsAutoCString suffix;
     cInfo.attrs().CreateSuffix(suffix);
     serializedInfo.Append(suffix);
@@ -1907,18 +1907,18 @@ InsertEntry(mozIStorageConnection* aConn
   return rv;
 }
 
 nsresult
 ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
              SavedResponse* aSavedResponseOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aSavedResponseOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aSavedResponseOut);
 
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "entries.response_type, "
       "entries.response_status, "
       "entries.response_status_text, "
       "entries.response_headers_guard, "
@@ -2035,18 +2035,18 @@ ReadResponse(mozIStorageConnection* aCon
   return rv;
 }
 
 nsresult
 ReadRequest(mozIStorageConnection* aConn, EntryId aEntryId,
             SavedRequest* aSavedRequestOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aSavedRequestOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aSavedRequestOut);
   nsCOMPtr<mozIStorageStatement> state;
   nsresult rv = aConn->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT "
       "request_method, "
       "request_url_no_query, "
       "request_url_query, "
       "request_url_fragment, "
       "request_referrer, "
@@ -2154,45 +2154,45 @@ ReadRequest(mozIStorageConnection* aConn
 }
 
 void
 AppendListParamsToQuery(nsACString& aQuery,
                         const nsTArray<EntryId>& aEntryIdList,
                         uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
+  MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
   for (int32_t i = aPos; i < aLen; ++i) {
     if (i == 0) {
       aQuery.AppendLiteral("?");
     } else {
       aQuery.AppendLiteral(",?");
     }
   }
 }
 
 nsresult
 BindListParamsToQuery(mozIStorageStatement* aState,
                       const nsTArray<EntryId>& aEntryIdList,
                       uint32_t aPos, int32_t aLen)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT((aPos + aLen) <= aEntryIdList.Length());
+  MOZ_DIAGNOSTIC_ASSERT((aPos + aLen) <= aEntryIdList.Length());
   for (int32_t i = aPos; i < aLen; ++i) {
     nsresult rv = aState->BindInt32ByIndex(i, aEntryIdList[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
 nsresult
 BindId(mozIStorageStatement* aState, const nsACString& aName, const nsID* aId)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aState);
+  MOZ_DIAGNOSTIC_ASSERT(aState);
   nsresult rv;
 
   if (!aId) {
     rv = aState->BindNullByName(aName);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
     return rv;
   }
 
@@ -2203,18 +2203,18 @@ BindId(mozIStorageStatement* aState, con
 
   return rv;
 }
 
 nsresult
 ExtractId(mozIStorageStatement* aState, uint32_t aPos, nsID* aIdOut)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aState);
-  MOZ_ASSERT(aIdOut);
+  MOZ_DIAGNOSTIC_ASSERT(aState);
+  MOZ_DIAGNOSTIC_ASSERT(aIdOut);
 
   nsAutoCString idString;
   nsresult rv = aState->GetUTF8String(aPos, idString);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool success = aIdOut->Parse(idString.get());
   if (NS_WARN_IF(!success)) { return NS_ERROR_UNEXPECTED; }
 
@@ -2222,19 +2222,19 @@ ExtractId(mozIStorageStatement* aState, 
 }
 
 nsresult
 CreateAndBindKeyStatement(mozIStorageConnection* aConn,
                           const char* aQueryFormat,
                           const nsAString& aKey,
                           mozIStorageStatement** aStateOut)
 {
-  MOZ_ASSERT(aConn);
-  MOZ_ASSERT(aQueryFormat);
-  MOZ_ASSERT(aStateOut);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aQueryFormat);
+  MOZ_DIAGNOSTIC_ASSERT(aStateOut);
 
   // The key is stored as a blob to avoid encoding issues.  An empty string
   // is mapped to NULL for blobs.  Normally we would just write the query
   // as "key IS :key" to do the proper NULL checking, but that prevents
   // sqlite from using the key index.  Therefore use "IS NULL" explicitly
   // if the key is empty, otherwise use "=:key" so that sqlite uses the
   // index.
   const char* constraint = nullptr;
@@ -2258,17 +2258,17 @@ CreateAndBindKeyStatement(mozIStorageCon
   state.forget(aStateOut);
 
   return rv;
 }
 
 nsresult
 HashCString(nsICryptoHash* aCrypto, const nsACString& aIn, nsACString& aOut)
 {
-  MOZ_ASSERT(aCrypto);
+  MOZ_DIAGNOSTIC_ASSERT(aCrypto);
 
   nsresult rv = aCrypto->Init(nsICryptoHash::SHA1);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aCrypto->Update(reinterpret_cast<const uint8_t*>(aIn.BeginReading()),
                        aIn.Length());
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -2522,64 +2522,66 @@ RewriteEntriesSchema(mozIStorageConnecti
 
   return rv;
 }
 
 nsresult
 Migrate(mozIStorageConnection* aConn)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   int32_t currentVersion = 0;
   nsresult rv = aConn->GetSchemaVersion(&currentVersion);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool rewriteSchema = false;
 
   while (currentVersion < kLatestSchemaVersion) {
     // Wiping old databases is handled in DBAction because it requires
     // making a whole new mozIStorageConnection.  Make sure we don't
     // accidentally get here for one of those old databases.
-    MOZ_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
+    MOZ_DIAGNOSTIC_ASSERT(currentVersion >= kFirstShippedSchemaVersion);
 
     for (uint32_t i = 0; i < sMigrationListLength; ++i) {
       if (sMigrationList[i].mFromVersion == currentVersion) {
         bool shouldRewrite = false;
         rv = sMigrationList[i].mFunc(aConn, shouldRewrite);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
         if (shouldRewrite) {
           rewriteSchema = true;
         }
         break;
       }
     }
 
-    DebugOnly<int32_t> lastVersion = currentVersion;
+#if !defined(RELEASE_OR_BETA)
+    int32_t lastVersion = currentVersion;
+#endif
     rv = aConn->GetSchemaVersion(&currentVersion);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    MOZ_ASSERT(currentVersion > lastVersion);
+    MOZ_DIAGNOSTIC_ASSERT(currentVersion > lastVersion);
   }
 
-  MOZ_ASSERT(currentVersion == kLatestSchemaVersion);
+  MOZ_DIAGNOSTIC_ASSERT(currentVersion == kLatestSchemaVersion);
 
   if (rewriteSchema) {
     // Now overwrite the master SQL for the entries table to remove the column
     // default value.  This is also necessary for our Validate() method to
     // pass on this database.
     rv = RewriteEntriesSchema(aConn);
   }
 
   return rv;
 }
 
 nsresult MigrateFrom15To16(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // Add the request_redirect column with a default value of "follow".  Note,
   // we only use a default value here because its required by ALTER TABLE and
   // we need to apply the default "follow" to existing records in the table.
   // We don't actually want to keep the default in the schema for future
   // INSERTs.
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE entries "
@@ -2594,17 +2596,17 @@ nsresult MigrateFrom15To16(mozIStorageCo
 
   return rv;
 }
 
 nsresult
 MigrateFrom16To17(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // This migration path removes the response_redirected and
   // response_redirected_url columns from the entries table.  sqlite doesn't
   // support removing a column from a table using ALTER TABLE, so we need to
   // create a new table without those columns, fill it up with the existing
   // data, and then drop the original table and rename the new one to the old
   // one.
 
@@ -2726,17 +2728,17 @@ MigrateFrom16To17(mozIStorageConnection*
 
   return rv;
 }
 
 nsresult
 MigrateFrom17To18(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // This migration is needed in order to remove "only-if-cached" RequestCache
   // values from the database.  This enum value was removed from the spec in
   // https://github.com/whatwg/fetch/issues/39 but we unfortunately happily
   // accepted this value in the Request constructor.
   //
   // There is no good value to upgrade this to, so we just stick to "default".
 
@@ -2753,17 +2755,17 @@ MigrateFrom17To18(mozIStorageConnection*
 
   return rv;
 }
 
 nsresult
 MigrateFrom18To19(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // This migration is needed in order to update the RequestMode values for
   // Request objects corresponding to a navigation content policy type to
   // "navigate".
 
   static_assert(int(nsIContentPolicy::TYPE_DOCUMENT) == 6 &&
                 int(nsIContentPolicy::TYPE_SUBDOCUMENT) == 7 &&
                 int(nsIContentPolicy::TYPE_INTERNAL_FRAME) == 28 &&
@@ -2781,17 +2783,17 @@ MigrateFrom18To19(mozIStorageConnection*
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   return rv;
 }
 
 nsresult MigrateFrom19To20(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // Add the request_referrer_policy column with a default value of
   // "no-referrer-when-downgrade".  Note, we only use a default value here
   // because its required by ALTER TABLE and we need to apply the default
   // "no-referrer-when-downgrade" to existing records in the table. We don't
   // actually want to keep the default in the schema for future INSERTs.
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE entries "
@@ -2805,17 +2807,17 @@ nsresult MigrateFrom19To20(mozIStorageCo
   aRewriteSchema = true;
 
   return rv;
 }
 
 nsresult MigrateFrom20To21(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // This migration creates response_url_list table to store response_url and
   // removes the response_url column from the entries table.
   // sqlite doesn't support removing a column from a table using ALTER TABLE,
   // so we need to create a new table without those columns, fill it up with the
   // existing data, and then drop the original table and rename the new one to
   // the old one.
 
@@ -2959,17 +2961,17 @@ nsresult MigrateFrom20To21(mozIStorageCo
   aRewriteSchema = true;
 
   return rv;
 }
 
 nsresult MigrateFrom21To22(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // Add the request_integrity column.
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE entries "
     "ADD COLUMN request_integrity TEXT NULL"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -2979,28 +2981,28 @@ nsresult MigrateFrom21To22(mozIStorageCo
   aRewriteSchema = true;
 
   return rv;
 }
 
 nsresult MigrateFrom22To23(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // The only change between 22 and 23 was a different snappy compression
   // format, but it's backwards-compatible.
   nsresult rv = aConn->SetSchemaVersion(23);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
   return rv;
 }
 nsresult MigrateFrom23To24(mozIStorageConnection* aConn, bool& aRewriteSchema)
 {
   MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(aConn);
+  MOZ_DIAGNOSTIC_ASSERT(aConn);
 
   // Add the request_url_fragment column.
   nsresult rv = aConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     "ALTER TABLE entries "
     "ADD COLUMN request_url_fragment TEXT NOT NULL DEFAULT ''"
   ));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
--- a/dom/cache/FileUtils.cpp
+++ b/dom/cache/FileUtils.cpp
@@ -38,17 +38,17 @@ BodyIdToFile(nsIFile* aBaseDir, const ns
              nsIFile** aBodyFileOut);
 
 } // namespace
 
 // static
 nsresult
 BodyCreateDir(nsIFile* aBaseDir)
 {
-  MOZ_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> aBodyDir;
   nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -60,17 +60,17 @@ BodyCreateDir(nsIFile* aBaseDir)
 
   return rv;
 }
 
 // static
 nsresult
 BodyDeleteDir(nsIFile* aBaseDir)
 {
-  MOZ_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> aBodyDir;
   nsresult rv = aBaseDir->Clone(getter_AddRefs(aBodyDir));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = aBodyDir->Append(NS_LITERAL_STRING("morgue"));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
@@ -83,24 +83,24 @@ BodyDeleteDir(nsIFile* aBaseDir)
 
   return rv;
 }
 
 // static
 nsresult
 BodyGetCacheDir(nsIFile* aBaseDir, const nsID& aId, nsIFile** aCacheDirOut)
 {
-  MOZ_ASSERT(aBaseDir);
-  MOZ_ASSERT(aCacheDirOut);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aCacheDirOut);
 
   *aCacheDirOut = nullptr;
 
   nsresult rv = aBaseDir->Clone(aCacheDirOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-  MOZ_ASSERT(*aCacheDirOut);
+  MOZ_DIAGNOSTIC_ASSERT(*aCacheDirOut);
 
   rv = (*aCacheDirOut)->Append(NS_LITERAL_STRING("morgue"));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   // Some file systems have poor performance when there are too many files
   // in a single directory.  Mitigate this issue by spreading the body
   // files out into sub-directories.  We use the last byte of the ID for
   // the name of the sub-directory.
@@ -121,22 +121,22 @@ BodyGetCacheDir(nsIFile* aBaseDir, const
 // static
 nsresult
 BodyStartWriteStream(const QuotaInfo& aQuotaInfo,
                      nsIFile* aBaseDir, nsIInputStream* aSource,
                      void* aClosure,
                      nsAsyncCopyCallbackFun aCallback, nsID* aIdOut,
                      nsISupports** aCopyContextOut)
 {
-  MOZ_ASSERT(aBaseDir);
-  MOZ_ASSERT(aSource);
-  MOZ_ASSERT(aClosure);
-  MOZ_ASSERT(aCallback);
-  MOZ_ASSERT(aIdOut);
-  MOZ_ASSERT(aCopyContextOut);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aSource);
+  MOZ_DIAGNOSTIC_ASSERT(aClosure);
+  MOZ_DIAGNOSTIC_ASSERT(aCallback);
+  MOZ_DIAGNOSTIC_ASSERT(aIdOut);
+  MOZ_DIAGNOSTIC_ASSERT(aCopyContextOut);
 
   nsresult rv;
   nsCOMPtr<nsIUUIDGenerator> idGen =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = idGen->GenerateUUIDInPlace(aIdOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -178,31 +178,31 @@ BodyStartWriteStream(const QuotaInfo& aQ
 
   return rv;
 }
 
 // static
 void
 BodyCancelWrite(nsIFile* aBaseDir, nsISupports* aCopyContext)
 {
-  MOZ_ASSERT(aBaseDir);
-  MOZ_ASSERT(aCopyContext);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aCopyContext);
 
   nsresult rv = NS_CancelAsyncCopy(aCopyContext, NS_ERROR_ABORT);
   Unused << NS_WARN_IF(NS_FAILED(rv));
 
   // The partially written file must be cleaned up after the async copy
   // makes its callback.
 }
 
 // static
 nsresult
 BodyFinalizeWrite(nsIFile* aBaseDir, const nsID& aId)
 {
-  MOZ_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_TMP, getter_AddRefs(tmpFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   nsCOMPtr<nsIFile> finalFile;
   rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL, getter_AddRefs(finalFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -217,18 +217,18 @@ BodyFinalizeWrite(nsIFile* aBaseDir, con
   return rv;
 }
 
 // static
 nsresult
 BodyOpen(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir, const nsID& aId,
          nsIInputStream** aStreamOut)
 {
-  MOZ_ASSERT(aBaseDir);
-  MOZ_ASSERT(aStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aStreamOut);
 
   nsCOMPtr<nsIFile> finalFile;
   nsresult rv = BodyIdToFile(aBaseDir, aId, BODY_FILE_FINAL,
                              getter_AddRefs(finalFile));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   bool exists;
   rv = finalFile->Exists(&exists);
@@ -287,24 +287,24 @@ BodyDeleteFiles(nsIFile* aBaseDir, const
 }
 
 namespace {
 
 nsresult
 BodyIdToFile(nsIFile* aBaseDir, const nsID& aId, BodyFileType aType,
              nsIFile** aBodyFileOut)
 {
-  MOZ_ASSERT(aBaseDir);
-  MOZ_ASSERT(aBodyFileOut);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aBodyFileOut);
 
   *aBodyFileOut = nullptr;
 
   nsresult rv = BodyGetCacheDir(aBaseDir, aId, aBodyFileOut);
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-  MOZ_ASSERT(*aBodyFileOut);
+  MOZ_DIAGNOSTIC_ASSERT(*aBodyFileOut);
 
   char idString[NSID_LENGTH];
   aId.ToProvidedString(idString);
 
   NS_ConvertASCIItoUTF16 fileName(idString);
 
   if (aType == BODY_FILE_FINAL) {
     fileName.AppendLiteral(".final");
@@ -318,17 +318,17 @@ BodyIdToFile(nsIFile* aBaseDir, const ns
   return rv;
 }
 
 } // namespace
 
 nsresult
 BodyDeleteOrphanedFiles(nsIFile* aBaseDir, nsTArray<nsID>& aKnownBodyIdList)
 {
-  MOZ_ASSERT(aBaseDir);
+  MOZ_DIAGNOSTIC_ASSERT(aBaseDir);
 
   // body files are stored in a directory structure like:
   //
   //  /morgue/01/{01fdddb2-884d-4c3d-95ba-0c8062f6c325}.final
   //  /morgue/02/{02fdddb2-884d-4c3d-95ba-0c8062f6c325}.tmp
 
   nsCOMPtr<nsIFile> dir;
   nsresult rv = aBaseDir->Clone(getter_AddRefs(dir));
@@ -416,17 +416,17 @@ BodyDeleteOrphanedFiles(nsIFile* aBaseDi
   return rv;
 }
 
 namespace {
 
 nsresult
 GetMarkerFileHandle(const QuotaInfo& aQuotaInfo, nsIFile** aFileOut)
 {
-  MOZ_ASSERT(aFileOut);
+  MOZ_DIAGNOSTIC_ASSERT(aFileOut);
 
   nsCOMPtr<nsIFile> marker;
   nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(marker));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
   rv = marker->Append(NS_LITERAL_STRING("cache"));
   if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
--- a/dom/cache/Manager.cpp
+++ b/dom/cache/Manager.cpp
@@ -112,18 +112,18 @@ public:
   explicit DeleteOrphanedBodyAction(const nsID& aBodyId)
   {
     mDeletedBodyIdList.AppendElement(aBodyId);
   }
 
   virtual void
   RunOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo, Data*) override
   {
-    MOZ_ASSERT(aResolver);
-    MOZ_ASSERT(aQuotaInfo.mDir);
+    MOZ_DIAGNOSTIC_ASSERT(aResolver);
+    MOZ_DIAGNOSTIC_ASSERT(aQuotaInfo.mDir);
 
     // Note that since DeleteOrphanedBodyAction isn't used while the context is
     // being initialized, we don't need to check for cancellation here.
 
     nsCOMPtr<nsIFile> dbDir;
     nsresult rv = aQuotaInfo.mDir->Clone(getter_AddRefs(dbDir));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       aResolver->Resolve(rv);
@@ -226,35 +226,35 @@ public:
 
     return nullptr;
   }
 
   static void
   Remove(Manager* aManager)
   {
     mozilla::ipc::AssertIsOnBackgroundThread();
-    MOZ_ASSERT(aManager);
-    MOZ_ASSERT(sFactory);
+    MOZ_DIAGNOSTIC_ASSERT(aManager);
+    MOZ_DIAGNOSTIC_ASSERT(sFactory);
 
     MOZ_ALWAYS_TRUE(sFactory->mManagerList.RemoveElement(aManager));
 
     // clean up the factory singleton if there are no more managers
     MaybeDestroyInstance();
   }
 
   static void
   Abort(const nsACString& aOrigin)
   {
     mozilla::ipc::AssertIsOnBackgroundThread();
 
     if (!sFactory) {
       return;
     }
 
-    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
+    MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
 
     {
       ManagerList::ForwardIterator iter(sFactory->mManagerList);
       while (iter.HasMore()) {
         RefPtr<Manager> manager = iter.GetNext();
         if (aOrigin.IsVoid() ||
             manager->mManagerId->QuotaOrigin() == aOrigin) {
           manager->Abort();
@@ -267,17 +267,17 @@ public:
   ShutdownAll()
   {
     mozilla::ipc::AssertIsOnBackgroundThread();
 
     if (!sFactory) {
       return;
     }
 
-    MOZ_ASSERT(!sFactory->mManagerList.IsEmpty());
+    MOZ_DIAGNOSTIC_ASSERT(!sFactory->mManagerList.IsEmpty());
 
     {
       // Note that we are synchronously calling shutdown code here.  If any
       // of the shutdown code synchronously decides to delete the Factory
       // we need to delay that delete until the end of this method.
       AutoRestore<bool> restore(sFactory->mInSyncShutdown);
       sFactory->mInSyncShutdown = true;
 
@@ -303,18 +303,18 @@ private:
     : mInSyncShutdown(false)
   {
     MOZ_COUNT_CTOR(cache::Manager::Factory);
   }
 
   ~Factory()
   {
     MOZ_COUNT_DTOR(cache::Manager::Factory);
-    MOZ_ASSERT(mManagerList.IsEmpty());
-    MOZ_ASSERT(!mInSyncShutdown);
+    MOZ_DIAGNOSTIC_ASSERT(mManagerList.IsEmpty());
+    MOZ_DIAGNOSTIC_ASSERT(!mInSyncShutdown);
   }
 
   static nsresult
   MaybeCreateInstance()
   {
     mozilla::ipc::AssertIsOnBackgroundThread();
 
     if (!sFactory) {
@@ -343,17 +343,17 @@ private:
 
     return NS_OK;
   }
 
   static void
   MaybeDestroyInstance()
   {
     mozilla::ipc::AssertIsOnBackgroundThread();
-    MOZ_ASSERT(sFactory);
+    MOZ_DIAGNOSTIC_ASSERT(sFactory);
 
     // If the factory is is still in use then we cannot delete yet.  This
     // could be due to managers still existing or because we are in the
     // middle of shutting down.  We need to be careful not to delete ourself
     // synchronously during shutdown.
     if (!sFactory->mManagerList.IsEmpty() || sFactory->mInSyncShutdown) {
       return;
     }
@@ -617,19 +617,19 @@ public:
     , mManager(aManager)
     , mListenerId(aListenerId)
     , mCacheId(aCacheId)
     , mList(aPutList.Length())
     , mExpectedAsyncCopyCompletions(1)
     , mAsyncResult(NS_OK)
     , mMutex("cache::Manager::CachePutAllAction")
   {
-    MOZ_ASSERT(!aPutList.IsEmpty());
-    MOZ_ASSERT(aPutList.Length() == aRequestStreamList.Length());
-    MOZ_ASSERT(aPutList.Length() == aResponseStreamList.Length());
+    MOZ_DIAGNOSTIC_ASSERT(!aPutList.IsEmpty());
+    MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aRequestStreamList.Length());
+    MOZ_DIAGNOSTIC_ASSERT(aPutList.Length() == aResponseStreamList.Length());
 
     for (uint32_t i = 0; i < aPutList.Length(); ++i) {
       Entry* entry = mList.AppendElement();
       entry->mRequest = aPutList[i].request();
       entry->mRequestStream = aRequestStreamList[i];
       entry->mResponse = aPutList[i].response();
       entry->mResponseStream = aResponseStreamList[i];
     }
@@ -637,31 +637,31 @@ public:
 
 private:
   ~CachePutAllAction() { }
 
   virtual void
   RunWithDBOnTarget(Resolver* aResolver, const QuotaInfo& aQuotaInfo,
                     nsIFile* aDBDir, mozIStorageConnection* aConn) override
   {
-    MOZ_ASSERT(aResolver);
-    MOZ_ASSERT(aDBDir);
-    MOZ_ASSERT(aConn);
-    MOZ_ASSERT(!mResolver);
-    MOZ_ASSERT(!mDBDir);
-    MOZ_ASSERT(!mConn);
+    MOZ_DIAGNOSTIC_ASSERT(aResolver);
+    MOZ_DIAGNOSTIC_ASSERT(aDBDir);
+    MOZ_DIAGNOSTIC_ASSERT(aConn);
+    MOZ_DIAGNOSTIC_ASSERT(!mResolver);
+    MOZ_DIAGNOSTIC_ASSERT(!mDBDir);
+    MOZ_DIAGNOSTIC_ASSERT(!mConn);
 
-    MOZ_ASSERT(!mTargetThread);
+    MOZ_DIAGNOSTIC_ASSERT(!mTargetThread);
     mTargetThread = NS_GetCurrentThread();
-    MOZ_ASSERT(mTargetThread);
+    MOZ_DIAGNOSTIC_ASSERT(mTargetThread);
 
     // We should be pre-initialized to expect one async completion.  This is
     // the "manual" completion we call at the end of this method in all
     // cases.
-    MOZ_ASSERT(mExpectedAsyncCopyCompletions == 1);
+    MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions == 1);
 
     mResolver = aResolver;
     mDBDir = aDBDir;
     mConn = aConn;
 
     // File bodies are streamed to disk via asynchronous copying.  Start
     // this copying now.  Each copy will eventually result in a call
     // to OnAsyncCopyComplete().
@@ -690,19 +690,19 @@ private:
 
   // Called once for each asynchronous file copy whether it succeeds or
   // fails.  If a file copy is canceled, it still calls this method with
   // an error code.
   void
   OnAsyncCopyComplete(nsresult aRv)
   {
     MOZ_ASSERT(mTargetThread == NS_GetCurrentThread());
-    MOZ_ASSERT(mConn);
-    MOZ_ASSERT(mResolver);
-    MOZ_ASSERT(mExpectedAsyncCopyCompletions > 0);
+    MOZ_DIAGNOSTIC_ASSERT(mConn);
+    MOZ_DIAGNOSTIC_ASSERT(mResolver);
+    MOZ_DIAGNOSTIC_ASSERT(mExpectedAsyncCopyCompletions > 0);
 
     // Explicitly check for cancellation here to catch a race condition.
     // Consider:
     //
     // 1) NS_AsyncCopy() executes on IO thread, but has not saved its
     //    copy context yet.
     // 2) CancelAllStreamCopying() occurs on PBackground thread
     // 3) Copy context from (1) is saved on IO thread.
@@ -837,30 +837,30 @@ private:
     ResponseStream
   };
 
   nsresult
   StartStreamCopy(const QuotaInfo& aQuotaInfo, Entry& aEntry,
                   StreamId aStreamId, uint32_t* aCopyCountOut)
   {
     MOZ_ASSERT(mTargetThread == NS_GetCurrentThread());
-    MOZ_ASSERT(aCopyCountOut);
+    MOZ_DIAGNOSTIC_ASSERT(aCopyCountOut);
 
     if (IsCanceled()) {
       return NS_ERROR_ABORT;
     }
 
     nsCOMPtr<nsIInputStream> source;
     nsID* bodyId;
 
     if (aStreamId == RequestStream) {
       source = aEntry.mRequestStream;
       bodyId = &aEntry.mRequestBodyId;
     } else {
-      MOZ_ASSERT(aStreamId == ResponseStream);
+      MOZ_DIAGNOSTIC_ASSERT(aStreamId == ResponseStream);
       source = aEntry.mResponseStream;
       bodyId = &aEntry.mResponseBodyId;
     }
 
     if (!source) {
       return NS_OK;
     }
 
@@ -893,17 +893,17 @@ private:
     }
     mCopyContextList.Clear();
   }
 
   static void
   AsyncCopyCompleteFunc(void* aClosure, nsresult aRv)
   {
     // May be on any thread, including STS event target.
-    MOZ_ASSERT(aClosure);
+    MOZ_DIAGNOSTIC_ASSERT(aClosure);
     // Weak ref as we are guaranteed to the action is alive until
     // CompleteOnInitiatingThread is called.
     CachePutAllAction* action = static_cast<CachePutAllAction*>(aClosure);
     action->CallOnAsyncCopyCompleteOnTargetThread(aRv);
   }
 
   void
   CallOnAsyncCopyCompleteOnTargetThread(nsresult aRv)
@@ -1448,23 +1448,23 @@ Manager::RemoveListener(Listener* aListe
                                   ListenerEntryListenerComparator()));
   MaybeAllowContextToClose();
 }
 
 void
 Manager::RemoveContext(Context* aContext)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(mContext);
-  MOZ_ASSERT(mContext == aContext);
+  MOZ_DIAGNOSTIC_ASSERT(mContext);
+  MOZ_DIAGNOSTIC_ASSERT(mContext == aContext);
 
   // Whether the Context destruction was triggered from the Manager going
   // idle or the underlying storage being invalidated, we should know we
   // are closing before the Context is destroyed.
-  MOZ_ASSERT(mState == Closing);
+  MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
 
   // Before forgetting the Context, check to see if we have any outstanding
   // cache or body objects waiting for deletion.  If so, note that we've
   // orphaned data so it will be cleaned up on the next open.
   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
     if (mCacheIdRefs[i].mOrphaned) {
       aContext->NoteOrphanedData();
       break;
@@ -1518,19 +1518,21 @@ Manager::AddRefCacheId(CacheId aCacheId)
 }
 
 void
 Manager::ReleaseCacheId(CacheId aCacheId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
     if (mCacheIdRefs[i].mCacheId == aCacheId) {
-      DebugOnly<uint32_t> oldRef = mCacheIdRefs[i].mCount;
+#if !defined(RELEASE_OR_BETA)
+      uint32_t oldRef = mCacheIdRefs[i].mCount;
+#endif
       mCacheIdRefs[i].mCount -= 1;
-      MOZ_ASSERT(mCacheIdRefs[i].mCount < oldRef);
+      MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount < oldRef);
       if (mCacheIdRefs[i].mCount == 0) {
         bool orphaned = mCacheIdRefs[i].mOrphaned;
         mCacheIdRefs.RemoveElementAt(i);
         RefPtr<Context> context = mContext;
         // If the context is already gone, then orphan flag should have been
         // set in RemoveContext().
         if (orphaned && context) {
           if (context->IsCanceled()) {
@@ -1567,19 +1569,21 @@ Manager::AddRefBodyId(const nsID& aBodyI
 }
 
 void
 Manager::ReleaseBodyId(const nsID& aBodyId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
     if (mBodyIdRefs[i].mBodyId == aBodyId) {
-      DebugOnly<uint32_t> oldRef = mBodyIdRefs[i].mCount;
+#if !defined(RELEASE_OR_BETA)
+      uint32_t oldRef = mBodyIdRefs[i].mCount;
+#endif
       mBodyIdRefs[i].mCount -= 1;
-      MOZ_ASSERT(mBodyIdRefs[i].mCount < oldRef);
+      MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount < oldRef);
       if (mBodyIdRefs[i].mCount < 1) {
         bool orphaned = mBodyIdRefs[i].mOrphaned;
         mBodyIdRefs.RemoveElementAt(i);
         RefPtr<Context> context = mContext;
         // If the context is already gone, then orphan flag should have been
         // set in RemoveContext().
         if (orphaned && context) {
           if (context->IsCanceled()) {
@@ -1603,43 +1607,43 @@ Manager::GetManagerId() const
   RefPtr<ManagerId> ref = mManagerId;
   return ref.forget();
 }
 
 void
 Manager::AddStreamList(StreamList* aStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(aStreamList);
   mStreamLists.AppendElement(aStreamList);
 }
 
 void
 Manager::RemoveStreamList(StreamList* aStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aStreamList);
+  MOZ_DIAGNOSTIC_ASSERT(aStreamList);
   mStreamLists.RemoveElement(aStreamList);
 }
 
 void
 Manager::ExecuteCacheOp(Listener* aListener, CacheId aCacheId,
                         const CacheOpArgs& aOpArgs)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
-  MOZ_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aOpArgs.type() != CacheOpArgs::TCachePutAllArgs);
 
   if (NS_WARN_IF(mState == Closing)) {
     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
     return;
   }
 
   RefPtr<Context> context = mContext;
-  MOZ_ASSERT(!context->IsCanceled());
+  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
 
   RefPtr<StreamList> streamList = new StreamList(this, context);
   ListenerId listenerId = SaveListener(aListener);
 
   RefPtr<Action> action;
   switch(aOpArgs.type()) {
     case CacheOpArgs::TCacheMatchArgs:
       action = new CacheMatchAction(this, listenerId, aCacheId,
@@ -1665,25 +1669,25 @@ Manager::ExecuteCacheOp(Listener* aListe
   context->Dispatch(action);
 }
 
 void
 Manager::ExecuteStorageOp(Listener* aListener, Namespace aNamespace,
                           const CacheOpArgs& aOpArgs)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
 
   if (NS_WARN_IF(mState == Closing)) {
     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), void_t());
     return;
   }
 
   RefPtr<Context> context = mContext;
-  MOZ_ASSERT(!context->IsCanceled());
+  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
 
   RefPtr<StreamList> streamList = new StreamList(this, context);
   ListenerId listenerId = SaveListener(aListener);
 
   RefPtr<Action> action;
   switch(aOpArgs.type()) {
     case CacheOpArgs::TStorageMatchArgs:
       action = new StorageMatchAction(this, listenerId, aNamespace,
@@ -1714,25 +1718,25 @@ Manager::ExecuteStorageOp(Listener* aLis
 
 void
 Manager::ExecutePutAll(Listener* aListener, CacheId aCacheId,
                        const nsTArray<CacheRequestResponse>& aPutList,
                        const nsTArray<nsCOMPtr<nsIInputStream>>& aRequestStreamList,
                        const nsTArray<nsCOMPtr<nsIInputStream>>& aResponseStreamList)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
 
   if (NS_WARN_IF(mState == Closing)) {
     aListener->OnOpComplete(ErrorResult(NS_ERROR_FAILURE), CachePutAllResult());
     return;
   }
 
   RefPtr<Context> context = mContext;
-  MOZ_ASSERT(!context->IsCanceled());
+  MOZ_DIAGNOSTIC_ASSERT(!context->IsCanceled());
 
   ListenerId listenerId = SaveListener(aListener);
 
   RefPtr<Action> action = new CachePutAllAction(this, listenerId, aCacheId,
                                                   aPutList, aRequestStreamList,
                                                   aResponseStreamList);
 
   context->Dispatch(action);
@@ -1740,25 +1744,25 @@ Manager::ExecutePutAll(Listener* aListen
 
 Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
   : mManagerId(aManagerId)
   , mIOThread(aIOThread)
   , mContext(nullptr)
   , mShuttingDown(false)
   , mState(Open)
 {
-  MOZ_ASSERT(mManagerId);
-  MOZ_ASSERT(mIOThread);
+  MOZ_DIAGNOSTIC_ASSERT(mManagerId);
+  MOZ_DIAGNOSTIC_ASSERT(mIOThread);
 }
 
 Manager::~Manager()
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(mState == Closing);
-  MOZ_ASSERT(!mContext);
+  MOZ_DIAGNOSTIC_ASSERT(mState == Closing);
+  MOZ_DIAGNOSTIC_ASSERT(!mContext);
 
   nsCOMPtr<nsIThread> ioThread;
   mIOThread.swap(ioThread);
 
   // Don't spin the event loop in the destructor waiting for the thread to
   // shutdown.  Defer this to the main thread, instead.
   MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioThread, &nsIThread::Shutdown)));
 }
@@ -1809,17 +1813,17 @@ Manager::Shutdown()
     return;
   }
 }
 
 void
 Manager::Abort()
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
-  MOZ_ASSERT(mContext);
+  MOZ_DIAGNOSTIC_ASSERT(mContext);
 
   // Note that we are closing to prevent any new requests from coming in and
   // creating a new Context.  We must ensure all Contexts and IO operations are
   // complete before origin clear proceeds.
   NoteClosing();
 
   // Cancel and only note that we are done after the context is cleaned up.
   RefPtr<Context> context = mContext;
@@ -1863,35 +1867,35 @@ Manager::GetListener(ListenerId aListene
 }
 
 bool
 Manager::SetCacheIdOrphanedIfRefed(CacheId aCacheId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mCacheIdRefs.Length(); ++i) {
     if (mCacheIdRefs[i].mCacheId == aCacheId) {
-      MOZ_ASSERT(mCacheIdRefs[i].mCount > 0);
-      MOZ_ASSERT(!mCacheIdRefs[i].mOrphaned);
+      MOZ_DIAGNOSTIC_ASSERT(mCacheIdRefs[i].mCount > 0);
+      MOZ_DIAGNOSTIC_ASSERT(!mCacheIdRefs[i].mOrphaned);
       mCacheIdRefs[i].mOrphaned = true;
       return true;
     }
   }
   return false;
 }
 
 // TODO: provide way to set body non-orphaned if its added back to a cache (bug 1110479)
 
 bool
 Manager::SetBodyIdOrphanedIfRefed(const nsID& aBodyId)
 {
   NS_ASSERT_OWNINGTHREAD(Manager);
   for (uint32_t i = 0; i < mBodyIdRefs.Length(); ++i) {
     if (mBodyIdRefs[i].mBodyId == aBodyId) {
-      MOZ_ASSERT(mBodyIdRefs[i].mCount > 0);
-      MOZ_ASSERT(!mBodyIdRefs[i].mOrphaned);
+      MOZ_DIAGNOSTIC_ASSERT(mBodyIdRefs[i].mCount > 0);
+      MOZ_DIAGNOSTIC_ASSERT(!mBodyIdRefs[i].mOrphaned);
       mBodyIdRefs[i].mOrphaned = true;
       return true;
     }
   }
   return false;
 }
 
 void
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -47,17 +47,17 @@ ManagerId::Principal() const
   nsCOMPtr<nsIPrincipal> ref = mPrincipal;
   return ref.forget();
 }
 
 ManagerId::ManagerId(nsIPrincipal* aPrincipal, const nsACString& aQuotaOrigin)
     : mPrincipal(aPrincipal)
     , mQuotaOrigin(aQuotaOrigin)
 {
-  MOZ_ASSERT(mPrincipal);
+  MOZ_DIAGNOSTIC_ASSERT(mPrincipal);
 }
 
 ManagerId::~ManagerId()
 {
   // If we're already on the main thread, then default destruction is fine
   if (NS_IsMainThread()) {
     return;
   }
--- a/dom/cache/PrincipalVerifier.cpp
+++ b/dom/cache/PrincipalVerifier.cpp
@@ -44,55 +44,55 @@ PrincipalVerifier::CreateAndDispatch(Lis
 
   return verifier.forget();
 }
 
 void
 PrincipalVerifier::AddListener(Listener* aListener)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
   MOZ_ASSERT(!mListenerList.Contains(aListener));
   mListenerList.AppendElement(aListener);
 }
 
 void
 PrincipalVerifier::RemoveListener(Listener* aListener)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
   MOZ_ALWAYS_TRUE(mListenerList.RemoveElement(aListener));
 }
 
 PrincipalVerifier::PrincipalVerifier(Listener* aListener,
                                      PBackgroundParent* aActor,
                                      const PrincipalInfo& aPrincipalInfo)
   : mActor(BackgroundParent::GetContentParent(aActor))
   , mPrincipalInfo(aPrincipalInfo)
   , mInitiatingThread(NS_GetCurrentThread())
   , mResult(NS_OK)
 {
   AssertIsOnBackgroundThread();
-  MOZ_ASSERT(mInitiatingThread);
-  MOZ_ASSERT(aListener);
+  MOZ_DIAGNOSTIC_ASSERT(mInitiatingThread);
+  MOZ_DIAGNOSTIC_ASSERT(aListener);
 
   mListenerList.AppendElement(aListener);
 }
 
 PrincipalVerifier::~PrincipalVerifier()
 {
   // Since the PrincipalVerifier is a Runnable that executes on multiple
   // threads, its a race to see which thread de-refs us last.  Therefore
   // we cannot guarantee which thread we destruct on.
 
-  MOZ_ASSERT(mListenerList.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty());
 
   // We should always be able to explicitly release the actor on the main
   // thread.
-  MOZ_ASSERT(!mActor);
+  MOZ_DIAGNOSTIC_ASSERT(!mActor);
 }
 
 NS_IMETHODIMP
 PrincipalVerifier::Run()
 {
   // Executed twice.  First, on the main thread and then back on the
   // originating thread.
 
@@ -185,17 +185,17 @@ PrincipalVerifier::CompleteOnInitiatingT
 {
   AssertIsOnBackgroundThread();
   ListenerList::ForwardIterator iter(mListenerList);
   while (iter.HasMore()) {
     iter.GetNext()->OnPrincipalVerified(mResult, mManagerId);
   }
 
   // The listener must clear its reference in OnPrincipalVerified()
-  MOZ_ASSERT(mListenerList.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mListenerList.IsEmpty());
 }
 
 void
 PrincipalVerifier::DispatchToInitiatingThread(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mResult = aRv;
--- a/dom/cache/QuotaClient.cpp
+++ b/dom/cache/QuotaClient.cpp
@@ -1,28 +1,26 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/cache/QuotaClient.h"
 
-#include "mozilla/DebugOnly.h"
 #include "mozilla/dom/cache/Manager.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/dom/quota/UsageInfo.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "nsIFile.h"
 #include "nsISimpleEnumerator.h"
 #include "nsThreadUtils.h"
 
 namespace {
 
-using mozilla::DebugOnly;
 using mozilla::dom::ContentParentId;
 using mozilla::dom::cache::Manager;
 using mozilla::dom::quota::Client;
 using mozilla::dom::quota::PersistenceType;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::quota::UsageInfo;
 using mozilla::ipc::AssertIsOnBackgroundThread;
 
@@ -50,17 +48,17 @@ GetBodyUsage(nsIFile* aDir, UsageInfo* a
       rv = GetBodyUsage(file, aUsageInfo);
       if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
       continue;
     }
 
     int64_t fileSize;
     rv = file->GetFileSize(&fileSize);
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-    MOZ_ASSERT(fileSize >= 0);
+    MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
     aUsageInfo->AppendToFileUsage(fileSize);
   }
 
   return NS_OK;
 }
 
 class CacheQuotaClient final : public Client
@@ -85,20 +83,20 @@ public:
     return GetUsageForOrigin(aPersistenceType, aGroup, aOrigin, aUsageInfo);
   }
 
   virtual nsresult
   GetUsageForOrigin(PersistenceType aPersistenceType, const nsACString& aGroup,
                     const nsACString& aOrigin,
                     UsageInfo* aUsageInfo) override
   {
-    MOZ_ASSERT(aUsageInfo);
+    MOZ_DIAGNOSTIC_ASSERT(aUsageInfo);
 
     QuotaManager* qm = QuotaManager::Get();
-    MOZ_ASSERT(qm);
+    MOZ_DIAGNOSTIC_ASSERT(qm);
 
     nsCOMPtr<nsIFile> dir;
     nsresult rv = qm->GetDirectoryForOrigin(aPersistenceType, aOrigin,
                                             getter_AddRefs(dir));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
 
     rv = dir->Append(NS_LITERAL_STRING(DOMCACHE_DIRECTORY_NAME));
     if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
@@ -143,17 +141,17 @@ public:
         continue;
       }
 
       if (leafName.EqualsLiteral("caches.sqlite") ||
           leafName.EqualsLiteral("caches.sqlite-wal")) {
         int64_t fileSize;
         rv = file->GetFileSize(&fileSize);
         if (NS_WARN_IF(NS_FAILED(rv))) { return rv; }
-        MOZ_ASSERT(fileSize >= 0);
+        MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
 
         aUsageInfo->AppendToDatabaseUsage(fileSize);
         continue;
       }
 
       NS_WARNING("Unknown Cache file found!");
     }
 
--- a/dom/cache/ReadStream.cpp
+++ b/dom/cache/ReadStream.cpp
@@ -190,53 +190,53 @@ ReadStream::Inner::Inner(StreamControl* 
                          nsIInputStream* aStream)
   : mControl(aControl)
   , mId(aId)
   , mStream(aStream)
   , mSnappyStream(new SnappyUncompressInputStream(aStream))
   , mOwningThread(NS_GetCurrentThread())
   , mState(Open)
 {
-  MOZ_ASSERT(mStream);
-  MOZ_ASSERT(mControl);
+  MOZ_DIAGNOSTIC_ASSERT(mStream);
+  MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->AddReadStream(this);
 }
 
 void
 ReadStream::Inner::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
                              nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                              ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
-  MOZ_ASSERT(aReadStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
   *aReadStreamOut = CacheReadStream();
   Serialize(&aReadStreamOut->get_CacheReadStream(), aStreamCleanupList, aRv);
 }
 
 void
 ReadStream::Inner::Serialize(CacheReadStream* aReadStreamOut,
                              nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                              ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
-  MOZ_ASSERT(aReadStreamOut);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut);
 
   if (mState != Open) {
     aRv.ThrowTypeError<MSG_CACHE_STREAM_CLOSED>();
     return;
   }
 
-  MOZ_ASSERT(mControl);
+  MOZ_DIAGNOSTIC_ASSERT(mControl);
 
   aReadStreamOut->id() = mId;
   mControl->SerializeControl(aReadStreamOut);
   mControl->SerializeStream(aReadStreamOut, mStream, aStreamCleanupList);
 
-  MOZ_ASSERT(aReadStreamOut->stream().type() ==
-             IPCStream::TInputStreamParamsWithFds);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStreamOut->stream().type() ==
+                        IPCStream::TInputStreamParamsWithFds);
 
   // We're passing ownership across the IPC barrier with the control, so
   // do not signal that the stream is closed here.
   Forget();
 }
 
 void
 ReadStream::Inner::CloseStream()
@@ -287,17 +287,17 @@ ReadStream::Inner::Available(uint64_t* a
 
   return rv;
 }
 
 nsresult
 ReadStream::Inner::Read(char* aBuf, uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
-  MOZ_ASSERT(aNumReadOut);
+  MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 
   nsresult rv = mSnappyStream->Read(aBuf, aCount, aNumReadOut);
 
   if ((NS_FAILED(rv) && rv != NS_BASE_STREAM_WOULD_BLOCK) ||
       *aNumReadOut == 0) {
     Close();
   }
 
@@ -306,17 +306,17 @@ ReadStream::Inner::Read(char* aBuf, uint
   return rv;
 }
 
 nsresult
 ReadStream::Inner::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                 uint32_t aCount, uint32_t* aNumReadOut)
 {
   // stream ops can happen on any thread
-  MOZ_ASSERT(aNumReadOut);
+  MOZ_DIAGNOSTIC_ASSERT(aNumReadOut);
 
   if (aCount) {
     mHasEverBeenRead = true;
   }
 
   nsresult rv = mSnappyStream->ReadSegments(aWriter, aClosure, aCount,
                                             aNumReadOut);
 
@@ -341,18 +341,18 @@ ReadStream::Inner::IsNonBlocking(bool* a
 {
   // stream ops can happen on any thread
   return mSnappyStream->IsNonBlocking(aNonBlockingOut);
 }
 
 ReadStream::Inner::~Inner()
 {
   // Any thread
-  MOZ_ASSERT(mState == Closed);
-  MOZ_ASSERT(!mControl);
+  MOZ_DIAGNOSTIC_ASSERT(mState == Closed);
+  MOZ_DIAGNOSTIC_ASSERT(!mControl);
 }
 
 void
 ReadStream::Inner::NoteClosed()
 {
   // Any thread
   if (mState == Closed) {
     return;
@@ -391,32 +391,32 @@ ReadStream::Inner::NoteClosedOnOwningThr
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
 
   // Mark closed and do nothing if we were already closed
   if (!mState.compareExchange(Open, Closed)) {
     return;
   }
 
-  MOZ_ASSERT(mControl);
+  MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->NoteClosed(this, mId);
   mControl = nullptr;
 }
 
 void
 ReadStream::Inner::ForgetOnOwningThread()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mOwningThread);
 
   // Mark closed and do nothing if we were already closed
   if (!mState.compareExchange(Open, Closed)) {
     return;
   }
 
-  MOZ_ASSERT(mControl);
+  MOZ_DIAGNOSTIC_ASSERT(mControl);
   mControl->ForgetReadStream(this);
   mControl = nullptr;
 }
 
 // ----------------------------------------------------------------------------
 
 NS_IMPL_ISUPPORTS(cache::ReadStream, nsIInputStream, ReadStream);
 
@@ -437,51 +437,51 @@ ReadStream::Create(const CacheReadStream
 {
   // The parameter may or may not be for a Cache created stream.  The way we
   // tell is by looking at the stream control actor.  If the actor exists,
   // then we know the Cache created it.
   if (!aReadStream.controlChild() && !aReadStream.controlParent()) {
     return nullptr;
   }
 
-  MOZ_ASSERT(aReadStream.stream().type() ==
-             IPCStream::TInputStreamParamsWithFds);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStream.stream().type() ==
+                        IPCStream::TInputStreamParamsWithFds);
 
   // Control is guaranteed to survive this method as ActorDestroy() cannot
   // run on this thread until we complete.
   StreamControl* control;
   if (aReadStream.controlChild()) {
     auto actor = static_cast<CacheStreamControlChild*>(aReadStream.controlChild());
     control = actor;
   } else {
     auto actor = static_cast<CacheStreamControlParent*>(aReadStream.controlParent());
     control = actor;
   }
-  MOZ_ASSERT(control);
+  MOZ_DIAGNOSTIC_ASSERT(control);
 
   nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(aReadStream.stream());
-  MOZ_ASSERT(stream);
+  MOZ_DIAGNOSTIC_ASSERT(stream);
 
   // Currently we expect all cache read streams to be blocking file streams.
-#ifdef DEBUG
+#if !defined(RELEASE_OR_BETA)
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(stream);
-  MOZ_ASSERT(!asyncStream);
+  MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
 #endif
 
   RefPtr<Inner> inner = new Inner(control, aReadStream.id(), stream);
   RefPtr<ReadStream> ref = new ReadStream(inner);
   return ref.forget();
 }
 
 // static
 already_AddRefed<ReadStream>
 ReadStream::Create(PCacheStreamControlParent* aControl, const nsID& aId,
                    nsIInputStream* aStream)
 {
-  MOZ_ASSERT(aControl);
+  MOZ_DIAGNOSTIC_ASSERT(aControl);
   auto actor = static_cast<CacheStreamControlParent*>(aControl);
   RefPtr<Inner> inner = new Inner(actor, aId, aStream);
   RefPtr<ReadStream> ref = new ReadStream(inner);
   return ref.forget();
 }
 
 void
 ReadStream::Serialize(CacheReadStreamOrVoid* aReadStreamOut,
@@ -497,17 +497,17 @@ ReadStream::Serialize(CacheReadStream* a
                       ErrorResult& aRv)
 {
   mInner->Serialize(aReadStreamOut, aStreamCleanupList, aRv);
 }
 
 ReadStream::ReadStream(ReadStream::Inner* aInner)
   : mInner(aInner)
 {
-  MOZ_ASSERT(mInner);
+  MOZ_DIAGNOSTIC_ASSERT(mInner);
 }
 
 ReadStream::~ReadStream()
 {
   // Explicitly close the inner stream so that it does not have to
   // deal with implicitly closing at destruction time.
   mInner->Close();
 }
--- a/dom/cache/StreamControl.cpp
+++ b/dom/cache/StreamControl.cpp
@@ -9,17 +9,17 @@
 namespace mozilla {
 namespace dom {
 namespace cache {
 
 void
 StreamControl::AddReadStream(ReadStream::Controllable* aReadStream)
 {
   AssertOwningThread();
-  MOZ_ASSERT(aReadStream);
+  MOZ_DIAGNOSTIC_ASSERT(aReadStream);
   MOZ_ASSERT(!mReadStreamList.Contains(aReadStream));
   mReadStreamList.AppendElement(aReadStream);
 }
 
 void
 StreamControl::ForgetReadStream(ReadStream::Controllable* aReadStream)
 {
   AssertOwningThread();
@@ -33,35 +33,37 @@ StreamControl::NoteClosed(ReadStream::Co
   AssertOwningThread();
   ForgetReadStream(aReadStream);
   NoteClosedAfterForget(aId);
 }
 
 StreamControl::~StreamControl()
 {
   // owning thread only, but can't call virtual AssertOwningThread in destructor
-  MOZ_ASSERT(mReadStreamList.IsEmpty());
+  MOZ_DIAGNOSTIC_ASSERT(mReadStreamList.IsEmpty());
 }
 
 void
 StreamControl::CloseReadStreams(const nsID& aId)
 {
   AssertOwningThread();
-  DebugOnly<uint32_t> closedCount = 0;
+#if !defined(RELEASE_OR_BETA)
+  uint32_t closedCount = 0;
+#endif
 
   ReadStreamList::ForwardIterator iter(mReadStreamList);
   while (iter.HasMore()) {
     RefPtr<ReadStream::Controllable> stream = iter.GetNext();
     if (stream->MatchId(aId)) {
       stream->CloseStream();
       closedCount += 1;
     }
   }
 
-  MOZ_ASSERT(closedCount > 0);
+  MOZ_DIAGNOSTIC_ASSERT(closedCount > 0);
 }
 
 void
 StreamControl::CloseAllReadStreams()
 {
   AssertOwningThread();
 
   ReadStreamList::ForwardIterator iter(mReadStreamList);
--- a/dom/cache/StreamList.cpp
+++ b/dom/cache/StreamList.cpp
@@ -17,70 +17,70 @@ namespace cache {
 
 StreamList::StreamList(Manager* aManager, Context* aContext)
   : mManager(aManager)
   , mContext(aContext)
   , mCacheId(INVALID_CACHE_ID)
   , mStreamControl(nullptr)
   , mActivated(false)
 {
-  MOZ_ASSERT(mManager);
+  MOZ_DIAGNOSTIC_ASSERT(mManager);
   mContext->AddActivity(this);
 }
 
 void
 StreamList::SetStreamControl(CacheStreamControlParent* aStreamControl)
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
-  MOZ_ASSERT(aStreamControl);
+  MOZ_DIAGNOSTIC_ASSERT(aStreamControl);
 
   // For cases where multiple streams are serialized for a single list
   // then the control will get passed multiple times.  This is ok, but
   // it should be the same control each time.
   if (mStreamControl) {
-    MOZ_ASSERT(aStreamControl == mStreamControl);
+    MOZ_DIAGNOSTIC_ASSERT(aStreamControl == mStreamControl);
     return;
   }
 
   mStreamControl = aStreamControl;
   mStreamControl->SetStreamList(this);
 }
 
 void
 StreamList::RemoveStreamControl(CacheStreamControlParent* aStreamControl)
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
-  MOZ_ASSERT(mStreamControl);
-  MOZ_ASSERT(mStreamControl == aStreamControl);
+  MOZ_DIAGNOSTIC_ASSERT(mStreamControl);
+  MOZ_DIAGNOSTIC_ASSERT(mStreamControl == aStreamControl);
   mStreamControl = nullptr;
 }
 
 void
 StreamList::Activate(CacheId aCacheId)
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
-  MOZ_ASSERT(!mActivated);
-  MOZ_ASSERT(mCacheId == INVALID_CACHE_ID);
+  MOZ_DIAGNOSTIC_ASSERT(!mActivated);
+  MOZ_DIAGNOSTIC_ASSERT(mCacheId == INVALID_CACHE_ID);
   mActivated = true;
   mCacheId = aCacheId;
   mManager->AddRefCacheId(mCacheId);
   mManager->AddStreamList(this);
 
   for (uint32_t i = 0; i < mList.Length(); ++i) {
     mManager->AddRefBodyId(mList[i].mId);
   }
 }
 
 void
 StreamList::Add(const nsID& aId, nsIInputStream* aStream)
 {
   // All streams should be added on IO thread before we set the stream
   // control on the owning IPC thread.
-  MOZ_ASSERT(!mStreamControl);
-  MOZ_ASSERT(aStream);
+  MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
+  MOZ_DIAGNOSTIC_ASSERT(aStream);
   Entry* entry = mList.AppendElement();
   entry->mId = aId;
   entry->mStream = aStream;
 }
 
 already_AddRefed<nsIInputStream>
 StreamList::Extract(const nsID& aId)
 {
@@ -154,17 +154,17 @@ StreamList::MatchesCacheId(CacheId aCach
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
   return aCacheId == mCacheId;
 }
 
 StreamList::~StreamList()
 {
   NS_ASSERT_OWNINGTHREAD(StreamList);
-  MOZ_ASSERT(!mStreamControl);
+  MOZ_DIAGNOSTIC_ASSERT(!mStreamControl);
   if (mActivated) {
     mManager->RemoveStreamList(this);
     for (uint32_t i = 0; i < mList.Length(); ++i) {
       mManager->ReleaseBodyId(mList[i].mId);
     }
     mManager->ReleaseCacheId(mCacheId);
   }
   mContext->RemoveActivity(this);
--- a/dom/cache/TypeUtils.cpp
+++ b/dom/cache/TypeUtils.cpp
@@ -60,17 +60,17 @@ HasVaryStar(mozilla::dom::InternalHeader
     }
   }
   return false;
 }
 
 void
 ToHeadersEntryList(nsTArray<HeadersEntry>& aOut, InternalHeaders* aHeaders)
 {
-  MOZ_ASSERT(aHeaders);
+  MOZ_DIAGNOSTIC_ASSERT(aHeaders);
 
   AutoTArray<InternalHeaders::Entry, 16> entryList;
   aHeaders->GetEntries(entryList);
 
   for (uint32_t i = 0; i < entryList.Length(); ++i) {
     InternalHeaders::Entry& entry = entryList[i];
     aOut.AppendElement(HeadersEntry(entry.mName, entry.mValue));
   }
@@ -116,17 +116,17 @@ TypeUtils::ToInternalRequest(const Ownin
 }
 
 void
 TypeUtils::ToCacheRequest(CacheRequest& aOut, InternalRequest* aIn,
                           BodyAction aBodyAction, SchemeAction aSchemeAction,
                           nsTArray<UniquePtr<AutoIPCStream>>& aStreamCleanupList,
                           ErrorResult& aRv)
 {
-  MOZ_ASSERT(aIn);
+  MOZ_DIAGNOSTIC_ASSERT(aIn);
   aIn->GetMethod(aOut.method());
   nsCString url(aIn->GetURLWithoutFragment());
   bool schemeValid;
   ProcessURL(url, &schemeValid, &aOut.urlWithoutQuery(), &aOut.urlQuery(), aRv);
   if (aRv.Failed()) {
     return;
   }
   if (!schemeValid) {
@@ -137,17 +137,17 @@ TypeUtils::ToCacheRequest(CacheRequest& 
       return;
     }
   }
   aOut.urlFragment() = aIn->GetFragment();
 
   aIn->GetReferrer(aOut.referrer());
   aOut.referrerPolicy() = aIn->ReferrerPolicy_();
   RefPtr<InternalHeaders> headers = aIn->Headers();
-  MOZ_ASSERT(headers);
+  MOZ_DIAGNOSTIC_ASSERT(headers);
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
   aOut.mode() = aIn->Mode();
   aOut.credentials() = aIn->GetCredentialsMode();
   aOut.contentPolicyType() = aIn->ContentPolicyType();
   aOut.requestCache() = aIn->GetCacheMode();
   aOut.requestRedirect() = aIn->GetRedirectMode();
 
@@ -174,26 +174,26 @@ TypeUtils::ToCacheResponseWithoutBody(Ca
 {
   aOut.type() = aIn.Type();
 
   aIn.GetUnfilteredURLList(aOut.urlList());
   AutoTArray<nsCString, 4> urlList;
   aIn.GetURLList(urlList);
 
   for (uint32_t i = 0; i < aOut.urlList().Length(); i++) {
-    MOZ_ASSERT(!aOut.urlList()[i].IsEmpty());
+    MOZ_DIAGNOSTIC_ASSERT(!aOut.urlList()[i].IsEmpty());
     // Pass all Response URL schemes through... The spec only requires we take
     // action on invalid schemes for Request objects.
     ProcessURL(aOut.urlList()[i], nullptr, nullptr, nullptr, aRv);
   }
 
   aOut.status() = aIn.GetUnfilteredStatus();
   aOut.statusText() = aIn.GetUnfilteredStatusText();
   RefPtr<InternalHeaders> headers = aIn.UnfilteredHeaders();
-  MOZ_ASSERT(headers);
+  MOZ_DIAGNOSTIC_ASSERT(headers);
   if (HasVaryStar(headers)) {
     aRv.ThrowTypeError<MSG_RESPONSE_HAS_VARY_STAR>();
     return;
   }
   ToHeadersEntryList(aOut.headers(), headers);
   aOut.headersGuard() = headers->Guard();
   aOut.channelInfo() = aIn.GetChannelInfo().AsIPCChannelInfo();
   if (aIn.GetPrincipalInfo()) {
@@ -262,19 +262,19 @@ TypeUtils::ToResponse(const CacheRespons
 
   RefPtr<InternalHeaders> internalHeaders =
     ToInternalHeaders(aIn.headers(), aIn.headersGuard());
   ErrorResult result;
 
   // Be careful to fill the headers before setting the guard in order to
   // correctly re-create the original headers.
   ir->Headers()->Fill(*internalHeaders, result);
-  MOZ_ASSERT(!result.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
   ir->Headers()->SetGuard(aIn.headersGuard(), result);
-  MOZ_ASSERT(!result.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
 
   ir->InitChannelInfo(aIn.channelInfo());
   if (aIn.principalInfo().type() == mozilla::ipc::OptionalPrincipalInfo::TPrincipalInfo) {
     UniquePtr<mozilla::ipc::PrincipalInfo> info(new mozilla::ipc::PrincipalInfo(aIn.principalInfo().get_PrincipalInfo()));
     ir->SetPrincipalInfo(Move(info));
   }
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
@@ -294,17 +294,17 @@ TypeUtils::ToResponse(const CacheRespons
       ir = ir->OpaqueResponse();
       break;
     case ResponseType::Opaqueredirect:
       ir = ir->OpaqueRedirectResponse();
       break;
     default:
       MOZ_CRASH("Unexpected ResponseType!");
   }
-  MOZ_ASSERT(ir);
+  MOZ_DIAGNOSTIC_ASSERT(ir);
 
   RefPtr<Response> ref = new Response(GetGlobalObject(), ir);
   return ref.forget();
 }
 already_AddRefed<InternalRequest>
 TypeUtils::ToInternalRequest(const CacheRequest& aIn)
 {
   nsAutoCString url(aIn.urlWithoutQuery());
@@ -323,20 +323,20 @@ TypeUtils::ToInternalRequest(const Cache
 
   RefPtr<InternalHeaders> internalHeaders =
     ToInternalHeaders(aIn.headers(), aIn.headersGuard());
   ErrorResult result;
 
   // Be careful to fill the headers before setting the guard in order to
   // correctly re-create the original headers.
   internalRequest->Headers()->Fill(*internalHeaders, result);
-  MOZ_ASSERT(!result.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
 
   internalRequest->Headers()->SetGuard(aIn.headersGuard(), result);
-  MOZ_ASSERT(!result.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!result.Failed());
 
   nsCOMPtr<nsIInputStream> stream = ReadStream::Create(aIn.body());
 
   internalRequest->SetBody(stream);
 
   return internalRequest.forget();
 }
 
@@ -405,17 +405,17 @@ TypeUtils::ProcessURL(nsACString& aUrl, 
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   if (!aUrlWithoutQueryOut) {
     return;
   }
 
-  MOZ_ASSERT(aUrlQueryOut);
+  MOZ_DIAGNOSTIC_ASSERT(aUrlQueryOut);
 
   if (queryLen < 0) {
     *aUrlWithoutQueryOut = aUrl;
     *aUrlQueryOut = EmptyCString();
     return;
   }
 
   // ParsePath gives us query position relative to the start of the path
@@ -424,17 +424,17 @@ TypeUtils::ProcessURL(nsACString& aUrl, 
   *aUrlWithoutQueryOut = Substring(aUrl, 0, queryPos - 1);
   *aUrlQueryOut = Substring(aUrl, queryPos - 1, queryLen + 1);
 }
 
 void
 TypeUtils::CheckAndSetBodyUsed(Request* aRequest, BodyAction aBodyAction,
                                ErrorResult& aRv)
 {
-  MOZ_ASSERT(aRequest);
+  MOZ_DIAGNOSTIC_ASSERT(aRequest);
 
   if (aBodyAction == IgnoreBody) {
     return;
   }
 
   if (aRequest->BodyUsed()) {
     aRv.ThrowTypeError<MSG_FETCH_BODY_CONSUMED_ERROR>();
     return;
@@ -456,17 +456,17 @@ TypeUtils::ToInternalRequest(const nsASt
   // Re-create a GlobalObject stack object so we can use webidl Constructors.
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   JSContext* cx = jsapi.cx();
   GlobalObject global(cx, GetGlobalObject()->GetGlobalJSObject());
-  MOZ_ASSERT(!global.Failed());
+  MOZ_DIAGNOSTIC_ASSERT(!global.Failed());
 
   RefPtr<Request> request = Request::Constructor(global, requestOrString,
                                                    RequestInit(), aRv);
   if (NS_WARN_IF(aRv.Failed())) { return nullptr; }
 
   return request->GetInternalRequest();
 }
 
--- a/dom/gamepad/cocoa/CocoaGamepad.cpp
+++ b/dom/gamepad/cocoa/CocoaGamepad.cpp
@@ -536,18 +536,19 @@ DarwinGamepadService::StartupInternal()
   // CFRunLoopRun() is a blocking message loop when it's called in
   // non-main thread so this thread cannot receive any other runnables
   // and nsITimer timeout events after it's called.
   CFRunLoopRun();
 }
 
 void DarwinGamepadService::Startup()
 {
-  Unused << NS_NewThread(getter_AddRefs(mMonitorThread),
-                         new DarwinGamepadServiceStartupRunnable(this));
+  Unused << NS_NewNamedThread("Gamepad",
+                              getter_AddRefs(mMonitorThread),
+                              new DarwinGamepadServiceStartupRunnable(this));
 }
 
 void DarwinGamepadService::Shutdown()
 {
   IOHIDManagerRef manager = (IOHIDManagerRef)mManager;
   CFRunLoopStop(mMonitorRunLoop);
   if (manager) {
     IOHIDManagerClose(manager, 0);
--- a/dom/gamepad/windows/WindowsGamepad.cpp
+++ b/dom/gamepad/windows/WindowsGamepad.cpp
@@ -1068,17 +1068,17 @@ void
 StartGamepadMonitoring()
 {
   AssertIsOnBackgroundThread();
 
   if (gMonitorThread || gService) {
     return;
   }
   sIsShutdown = false;
-  NS_NewThread(getter_AddRefs(gMonitorThread));
+  NS_NewNamedThread("Gamepad", getter_AddRefs(gMonitorThread));
   gMonitorThread->Dispatch(new StartWindowsGamepadServiceRunnable(),
                            NS_DISPATCH_NORMAL);
 }
 
 void
 StopGamepadMonitoring()
 {
   AssertIsOnBackgroundThread();
--- a/dom/html/HTMLFormElement.h
+++ b/dom/html/HTMLFormElement.h
@@ -553,17 +553,17 @@ public:
 protected:
 
   //
   // Data members
   //
   /** The list of controls (form.elements as well as stuff not in elements) */
   RefPtr<HTMLFormControlsCollection> mControls;
   /** The currently selected radio button of each group */
-  nsRefPtrHashtable<nsStringCaseInsensitiveHashKey, HTMLInputElement> mSelectedRadioButtons;
+  nsRefPtrHashtable<nsStringHashKey, HTMLInputElement> mSelectedRadioButtons;
   /** The number of required radio button of each group */
   nsDataHashtable<nsStringCaseInsensitiveHashKey,uint32_t> mRequiredRadioButtonCounts;
   /** The value missing state of each group */
   nsDataHashtable<nsStringCaseInsensitiveHashKey,bool> mValueMissingRadioGroups;
   /** Whether we are currently processing a submit event or not */
   bool mGeneratingSubmit;
   /** Whether we are currently processing a reset event or not */
   bool mGeneratingReset;
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -5715,16 +5715,21 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
   uint32_t
   SerialNumber() const
   {
     return mSerialNumber;
   }
 
+  nsCString GetThreadName() const
+  {
+    return nsPrintfCString("IndexedDB #%lu", mSerialNumber);
+  }
+
 private:
   ~ThreadRunnable() override;
 
   NS_DECL_NSIRUNNABLE
 };
 
 class ConnectionPool::TransactionInfo final
 {
@@ -12566,17 +12571,20 @@ ConnectionPool::ScheduleTransaction(Tran
     if (mIdleThreads.IsEmpty()) {
       bool created = false;
 
       if (mTotalThreadCount < kMaxConnectionThreadCount) {
         // This will set the thread up with the profiler.
         RefPtr<ThreadRunnable> runnable = new ThreadRunnable();
 
         nsCOMPtr<nsIThread> newThread;
-        if (NS_SUCCEEDED(NS_NewThread(getter_AddRefs(newThread), runnable))) {
+        nsresult rv =
+          NS_NewNamedThread(runnable->GetThreadName(),
+                            getter_AddRefs(newThread), runnable);
+        if (NS_SUCCEEDED(rv)) {
           MOZ_ASSERT(newThread);
 
           IDB_DEBUG_LOG(("ConnectionPool created thread %lu",
                          runnable->SerialNumber()));
 
           dbInfo->mThreadInfo.mThread.swap(newThread);
           dbInfo->mThreadInfo.mRunnable.swap(runnable);
 
@@ -13279,43 +13287,27 @@ ThreadRunnable::~ThreadRunnable()
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(ConnectionPool::ThreadRunnable, Runnable)
 
 nsresult
 ConnectionPool::
 ThreadRunnable::Run()
 {
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  char stackTopGuess;
-#endif // MOZ_ENABLE_PROFILER_SPS
-
   MOZ_ASSERT(!IsOnBackgroundThread());
   MOZ_ASSERT(mContinueRunning);
 
   if (!mFirstRun) {
     mContinueRunning = false;
     return NS_OK;
   }
 
   mFirstRun = false;
 
   {
-    // Scope for the thread name. Both PR_SetCurrentThreadName() and
-    // profiler_register_thread() copy the string so we don't need to keep it.
-    const nsPrintfCString threadName("IndexedDB #%lu", mSerialNumber);
-
-    PR_SetCurrentThreadName(threadName.get());
-
-#ifdef MOZ_ENABLE_PROFILER_SPS
-    profiler_register_thread(threadName.get(), &stackTopGuess);
-#endif // MOZ_ENABLE_PROFILER_SPS
-  }
-
-  {
     // Scope for the profiler label.
     PROFILER_LABEL("IndexedDB",
                    "ConnectionPool::ThreadRunnable::Run",
                    js::ProfileEntry::Category::STORAGE);
 
     nsIThread* currentThread = NS_GetCurrentThread();
     MOZ_ASSERT(currentThread);
 
@@ -13346,20 +13338,16 @@ ThreadRunnable::Run()
         MOZ_ALWAYS_TRUE(
           PR_Sleep(PR_MillisecondsToInterval(kDEBUGTransactionThreadSleepMS)) ==
             PR_SUCCESS);
       }
 #endif // DEBUG
     }
   }
 
-#ifdef MOZ_ENABLE_PROFILER_SPS
-  profiler_unregister_thread();
-#endif // MOZ_ENABLE_PROFILER_SPS
-
   return NS_OK;
 }
 
 ConnectionPool::
 ThreadInfo::ThreadInfo()
 {
   AssertIsOnBackgroundThread();
 
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -1,14 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
+#include "nsIRequest.idl"
 
 interface mozIDOMWindow;
 interface nsPIDOMWindowInner;
 interface mozIDOMWindowProxy;
 interface nsIArray;
 interface nsIDocument;
 interface nsIInterceptedChannel;
 interface nsIPrincipal;
@@ -93,17 +94,20 @@ interface nsIServiceWorkerManager : nsIS
   /**
    * Registers a ServiceWorker with script loaded from `aScriptURI` to act as
    * the ServiceWorker for aScope.  Requires a valid entry settings object on
    * the stack. This means you must call this from content code 'within'
    * a window.
    *
    * Returns a Promise.
    */
-  nsISupports register(in mozIDOMWindow aWindow, in nsIURI aScope, in nsIURI aScriptURI);
+  nsISupports register(in mozIDOMWindow aWindow,
+                       in nsIURI aScope,
+                       in nsIURI aScriptURI,
+                       in nsLoadFlags aLoadFlags);
 
   /**
    * Unregister an existing ServiceWorker registration for `aScope`.
    * It keeps aCallback alive until the operation is concluded.
    */
   void unregister(in nsIPrincipal aPrincipal,
                   in nsIServiceWorkerUnregisterCallback aCallback,
                   in DOMString aScope);
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -310,11 +310,11 @@ GenericImageNameGIF=image.gif
 # LOCALIZATION NOTE: Do not translate ".png"
 GenericImageNamePNG=image.png
 GenericFileName=file
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
 LargeAllocationSuccess=This page was loaded in a new process due to a Large-Allocation header.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name. Do not translate GET.
 LargeAllocationNonGetRequest=A Large-Allocation header was ignored due to the load being triggered by a non-GET request.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name. Do not translate `window.opener`.
-LargeAllocationRelatedBrowsingContexts=A Large-Allocation header was ignored due to the presence of windows which have a reference to this browsing context through the frame hierarchy or window.opener.
+LargeAllocationNotOnlyToplevelInTabGroup=A Large-Allocation header was ignored due to the presence of windows which have a reference to this browsing context through the frame hierarchy or window.opener.
 # LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
 LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
--- a/dom/media/FileBlockCache.cpp
+++ b/dom/media/FileBlockCache.cpp
@@ -17,19 +17,20 @@ nsresult FileBlockCache::Open(PRFileDesc
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ENSURE_TRUE(aFD != nullptr, NS_ERROR_FAILURE);
   {
     MonitorAutoLock mon(mFileMonitor);
     mFD = aFD;
   }
   {
     MonitorAutoLock mon(mDataMonitor);
-    nsresult res = NS_NewThread(getter_AddRefs(mThread),
-                                nullptr,
-                                SharedThreadPool::kStackSize);
+    nsresult res = NS_NewNamedThread("FileBlockCache",
+                                     getter_AddRefs(mThread),
+                                     nullptr,
+                                     SharedThreadPool::kStackSize);
     mIsOpen = NS_SUCCEEDED(res);
     return res;
   }
 }
 
 FileBlockCache::FileBlockCache()
   : mFileMonitor("MediaCache.Writer.IO.Monitor"),
     mFD(nullptr),
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -34,29 +34,16 @@ extern mozilla::LazyLogModule gMediaStre
 #else
 #define LIFECYCLE_LOG(...)
 #endif
 
 namespace mozilla {
 
 StaticRefPtr<nsIThreadPool> AsyncCubebTask::sThreadPool;
 
-struct AutoProfilerUnregisterThread
-{
-  // The empty ctor is used to silence a pre-4.8.0 GCC unused variable warning.
-  AutoProfilerUnregisterThread()
-  {
-  }
-
-  ~AutoProfilerUnregisterThread()
-  {
-    profiler_unregister_thread();
-  }
-};
-
 GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
   : mIterationStart(0),
     mIterationEnd(0),
     mGraphImpl(aGraphImpl),
     mWaitState(WAITSTATE_RUNNING),
     mCurrentTimeStamp(TimeStamp::Now()),
     mPreviousDriver(nullptr),
     mNextDriver(nullptr)
@@ -191,19 +178,17 @@ ThreadedDriver::~ThreadedDriver()
 class MediaStreamGraphInitThreadRunnable : public Runnable {
 public:
   explicit MediaStreamGraphInitThreadRunnable(ThreadedDriver* aDriver)
     : mDriver(aDriver)
   {
   }
   NS_IMETHOD Run() override
   {
-    char aLocal;
     STREAM_LOG(LogLevel::Debug, ("Starting system thread"));
-    profiler_register_thread("MediaStreamGraph", &aLocal);
     LIFECYCLE_LOG("Starting a new system driver for graph %p\n",
                   mDriver->mGraphImpl);
 
     GraphDriver* previousDriver = nullptr;
     {
       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
       previousDriver = mDriver->PreviousDriver();
     }
@@ -311,18 +296,16 @@ bool
 SystemClockDriver::IsFallback()
 {
   return mIsFallback;
 }
 
 void
 ThreadedDriver::RunThread()
 {
-  AutoProfilerUnregisterThread autoUnregister;
-
   bool stillProcessing = true;
   while (stillProcessing) {
     mIterationStart = IterationEnd();
     mIterationEnd += GetIntervalForIteration();
 
     GraphTime stateComputedTime = StateComputedTime();
     if (stateComputedTime < mIterationEnd) {
       STREAM_LOG(LogLevel::Warning, ("Media graph global underrun detected"));
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2509,38 +2509,17 @@ MediaDecoderStateMachine::HaveEnoughDeco
   auto ampleAudioUSecs = mAmpleAudioThresholdUsecs * mPlaybackRate;
   return AudioQueue().GetSize() > 0 &&
          GetDecodedAudioDuration() >= ampleAudioUSecs;
 }
 
 bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
 {
   MOZ_ASSERT(OnTaskQueue());
-
-  if (VideoQueue().GetSize() == 0) {
-    return false;
-  }
-
-  if (VideoQueue().GetSize() - 1 < GetAmpleVideoFrames() * mPlaybackRate) {
-    return false;
-  }
-
-  return true;
-}
-
-bool
-MediaDecoderStateMachine::NeedToDecodeVideo()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("NeedToDecodeVideo() isDec=%d minPrl=%d enufVid=%d",
-             IsVideoDecoding(), mMinimizePreroll, HaveEnoughDecodedVideo());
-  return IsVideoDecoding() &&
-         mState != DECODER_STATE_SEEKING &&
-         ((!mSentFirstFrameLoadedEvent && VideoQueue().GetSize() == 0) ||
-          (!mMinimizePreroll && !HaveEnoughDecodedVideo()));
+  return VideoQueue().GetSize() >= GetAmpleVideoFrames() * mPlaybackRate + 1;
 }
 
 bool
 MediaDecoderStateMachine::NeedToSkipToNextKeyframe()
 {
   MOZ_ASSERT(OnTaskQueue());
   // Don't skip when we're still decoding first frames.
   if (!mSentFirstFrameLoadedEvent) {
@@ -2589,29 +2568,16 @@ MediaDecoderStateMachine::NeedToSkipToNe
     DECODER_LOG("Skipping video decode to the next keyframe lowAudio=%d lowVideo=%d lowUndecoded=%d async=%d",
                 isLowOnDecodedAudio, isLowOnDecodedVideo, lowBuffered, mReader->IsAsync());
     return true;
   }
 
   return false;
 }
 
-bool
-MediaDecoderStateMachine::NeedToDecodeAudio()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("NeedToDecodeAudio() isDec=%d minPrl=%d enufAud=%d",
-             IsAudioDecoding(), mMinimizePreroll, HaveEnoughDecodedAudio());
-
-  return IsAudioDecoding() &&
-         mState != DECODER_STATE_SEEKING &&
-         ((!mSentFirstFrameLoadedEvent && AudioQueue().GetSize() == 0) ||
-          (!mMinimizePreroll && !HaveEnoughDecodedAudio()));
-}
-
 void
 MediaDecoderStateMachine::PushAudio(MediaData* aSample)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(aSample);
   AudioQueue().Push(aSample);
 }
 
@@ -2707,29 +2673,28 @@ nsresult MediaDecoderStateMachine::Init(
     auto s = new DecodeMetadataState(self);
     self->mStateObj.reset(s);
     s->Enter();
   }));
 
   return NS_OK;
 }
 
-void MediaDecoderStateMachine::StopPlayback()
+void
+MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("StopPlayback()");
 
   mOnPlaybackEvent.Notify(MediaEventType::PlaybackStopped);
 
   if (IsPlaying()) {
     mMediaSink->SetPlaying(false);
     MOZ_ASSERT(!IsPlaying());
   }
-
-  DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::MaybeStartPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   // Should try to start playback only after decoding first frames.
   MOZ_ASSERT(mSentFirstFrameLoadedEvent);
   MOZ_ASSERT(mState == DECODER_STATE_DECODING ||
@@ -2966,40 +2931,29 @@ void MediaDecoderStateMachine::StopMedia
 
 void
 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (mState != DECODER_STATE_DECODING &&
       mState != DECODER_STATE_DECODING_FIRSTFRAME &&
-      mState != DECODER_STATE_BUFFERING &&
-      mState != DECODER_STATE_SEEKING) {
+      mState != DECODER_STATE_BUFFERING) {
     return;
   }
 
-  // NeedToDecodeAudio() can go from false to true while we hold the
-  // monitor, but it can't go from true to false. This can happen because
-  // NeedToDecodeAudio() takes into account the amount of decoded audio
-  // that's been written to the AudioStream but not played yet. So if we
-  // were calling NeedToDecodeAudio() twice and we thread-context switch
-  // between the calls, audio can play, which can affect the return value
-  // of NeedToDecodeAudio() giving inconsistent results. So we cache the
-  // value returned by NeedToDecodeAudio(), and make decisions
-  // based on the cached value. If NeedToDecodeAudio() has
-  // returned false, and then subsequently returns true and we're not
-  // playing, it will probably be OK since we don't need to consume data
-  // anyway.
-
-  const bool needToDecodeAudio = NeedToDecodeAudio();
-  const bool needToDecodeVideo = NeedToDecodeVideo();
-
-  // If we're in completed state, we should not need to decode anything else.
-  MOZ_ASSERT(mState != DECODER_STATE_COMPLETED ||
-             (!needToDecodeAudio && !needToDecodeVideo));
+  const bool needToDecodeAudio =
+    IsAudioDecoding() &&
+    ((!mSentFirstFrameLoadedEvent && AudioQueue().GetSize() == 0) ||
+     (!mMinimizePreroll && !HaveEnoughDecodedAudio()));
+
+  const bool needToDecodeVideo =
+    IsVideoDecoding() &&
+    ((!mSentFirstFrameLoadedEvent && VideoQueue().GetSize() == 0) ||
+     (!mMinimizePreroll && !HaveEnoughDecodedVideo()));
 
   SAMPLE_LOG("DispatchDecodeTasksIfNeeded needAudio=%d audioStatus=%s needVideo=%d videoStatus=%s",
              needToDecodeAudio, AudioRequestStatus(),
              needToDecodeVideo, VideoRequestStatus());
 
   if (needToDecodeAudio) {
     EnsureAudioDecodeTaskQueued();
   }
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -336,24 +336,16 @@ protected:
 
   void VolumeChanged();
   void SetPlaybackRate(double aPlaybackRate);
   void PreservesPitchChanged();
 
   MediaQueue<MediaData>& AudioQueue() { return mAudioQueue; }
   MediaQueue<MediaData>& VideoQueue() { return mVideoQueue; }
 
-  // True if our buffers of decoded audio are not full, and we should
-  // decode more.
-  bool NeedToDecodeAudio();
-
-  // True if our buffers of decoded video are not full, and we should
-  // decode more.
-  bool NeedToDecodeVideo();
-
   // True if we are low in decoded audio/video data.
   // May not be invoked when mReader->UseBufferingHeuristics() is false.
   bool HasLowDecodedData();
 
   bool HasLowDecodedAudio();
 
   bool HasLowDecodedVideo();
 
--- a/dom/media/MediaRecorder.cpp
+++ b/dom/media/MediaRecorder.cpp
@@ -271,27 +271,24 @@ class MediaRecorder::Session: public nsI
 
     NS_IMETHOD Run() override
     {
       MOZ_ASSERT(NS_GetCurrentThread() == mSession->mReadThread);
 
       LOG(LogLevel::Debug, ("Session.ExtractRunnable shutdown = %d", mSession->mEncoder->IsShutdown()));
       if (!mSession->mEncoder->IsShutdown()) {
         mSession->Extract(false);
-        nsCOMPtr<nsIRunnable> event = new ExtractRunnable(mSession);
-        if (NS_FAILED(NS_DispatchToCurrentThread(event))) {
+        if (NS_FAILED(NS_DispatchToCurrentThread(this))) {
           NS_WARNING("Failed to dispatch ExtractRunnable to encoder thread");
         }
       } else {
         // Flush out remaining encoded data.
         mSession->Extract(true);
-        if (mSession->mIsRegisterProfiler)
-           profiler_unregister_thread();
         if (NS_FAILED(NS_DispatchToMainThread(
-                        new DestroyRunnable(mSession)))) {
+                        new DestroyRunnable(mSession.forget())))) {
           MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
         }
       }
       return NS_OK;
     }
 
   private:
     RefPtr<Session> mSession;
@@ -364,16 +361,19 @@ class MediaRecorder::Session: public nsI
   // Main thread task.
   // To delete RecordingSession object.
   class DestroyRunnable : public Runnable
   {
   public:
     explicit DestroyRunnable(Session* aSession)
       : mSession(aSession) {}
 
+    explicit DestroyRunnable(already_AddRefed<Session> aSession)
+      : mSession(aSession) {}
+
     NS_IMETHOD Run() override
     {
       LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)",
                          (int)mSession->mRefCnt, mSession->mStopIssued, mSession.get()));
       MOZ_ASSERT(NS_IsMainThread() && mSession);
       RefPtr<MediaRecorder> recorder = mSession->mRecorder;
       if (!recorder) {
         return NS_OK;
@@ -383,17 +383,17 @@ class MediaRecorder::Session: public nsI
       // We need to switch MediaRecorder to "Stop" state first to make sure
       // MediaRecorder is not associated with this Session anymore, then, it's
       // safe to delete this Session.
       // Also avoid to run if this session already call stop before
       if (!mSession->mStopIssued) {
         ErrorResult result;
         mSession->mStopIssued = true;
         recorder->Stop(result);
-        if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession)))) {
+        if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession.forget())))) {
           MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
         }
         return NS_OK;
       }
 
       // Dispatch stop event and clear MIME type.
       mSession->mMimeType = NS_LITERAL_STRING("");
       recorder->SetMimeType(mSession->mMimeType);
@@ -414,17 +414,16 @@ class MediaRecorder::Session: public nsI
   friend class TracksAvailableCallback;
 
 public:
   Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
     : mRecorder(aRecorder)
     , mTimeSlice(aTimeSlice)
     , mStopIssued(false)
     , mIsStartEventFired(false)
-    , mIsRegisterProfiler(false)
     , mNeedSessionEndTask(true)
     , mSelectedVideoTrackID(TRACK_NONE)
   {
     MOZ_ASSERT(NS_IsMainThread());
 
     uint32_t maxMem = Preferences::GetUint("media.recorder.max_memory",
                                            MAX_ALLOW_MEMORY_BUFFER);
     mEncodedBufferCache = new EncodedBufferCache(maxMem);
@@ -513,16 +512,20 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     mStopIssued = true;
     CleanupStreams();
     if (mNeedSessionEndTask) {
       LOG(LogLevel::Debug, ("Session.Stop mNeedSessionEndTask %p", this));
       // End the Session directly if there is no ExtractRunnable.
       DoSessionEndTask(NS_OK);
     }
+    // If we don't do this, the Session will be purged only when the navigator exit
+    // by the ShutdownObserver and the memory and number of threads will quickly
+    // grows with each couple stop/start.
+    nsContentUtils::UnregisterShutdownObserver(this);
   }
 
   nsresult Pause()
   {
     LOG(LogLevel::Debug, ("Session.Pause"));
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
@@ -590,34 +593,29 @@ private:
   {
     LOG(LogLevel::Debug, ("Session.~Session (%p)", this));
     CleanupStreams();
     if (mReadThread) {
       mReadThread->Shutdown();
       mReadThread = nullptr;
       // Inside the if() so that if we delete after xpcom-shutdown's Observe(), we
       // won't try to remove it after the observer service is shut down.
+      // Unregistering for safety in case Stop() was never called
       nsContentUtils::UnregisterShutdownObserver(this);
     }
   }
   // Pull encoded media data from MediaEncoder and put into EncodedBufferCache.
   // Destroy this session object in the end of this function.
   // If the bool aForceFlush is true, we will force to dispatch a
   // PushBlobRunnable to main thread.
   void Extract(bool aForceFlush)
   {
     MOZ_ASSERT(NS_GetCurrentThread() == mReadThread);
     LOG(LogLevel::Debug, ("Session.Extract %p", this));
 
-    if (!mIsRegisterProfiler) {
-      char aLocal;
-      profiler_register_thread("Media_Encoder", &aLocal);
-      mIsRegisterProfiler = true;
-    }
-
     PROFILER_LABEL("MediaRecorder", "Session Extract",
       js::ProfileEntry::Category::OTHER);
 
     // Pull encoded media data from MediaEncoder
     nsTArray<nsTArray<uint8_t> > encodedBuf;
     mEncoder->GetEncodedData(&encodedBuf, mMimeType);
 
     // Append pulled data into cache buffer.
@@ -828,16 +826,27 @@ private:
     }
     for (RefPtr<MediaInputPort>& inputPort : mInputPorts) {
       MOZ_ASSERT(inputPort);
       inputPort->Destroy();
     }
     mInputPorts.Clear();
 
     if (mTrackUnionStream) {
+      if (mEncoder) {
+        nsTArray<RefPtr<mozilla::dom::VideoStreamTrack>> videoTracks;
+        DOMMediaStream* domStream = mRecorder->Stream();
+        if (domStream) {
+          domStream->GetVideoTracks(videoTracks);
+          if (!videoTracks.IsEmpty()) {
+            videoTracks[0]->RemoveDirectListener(mEncoder->GetVideoSink());
+          }
+        }
+      }
+
       // Sometimes the MediaEncoder might be initialized fail and go to
       // |CleanupStreams|. So the mEncoder might be a nullptr in this case.
       if (mEncoder && mSelectedVideoTrackID != TRACK_NONE) {
         mTrackUnionStream->RemoveVideoOutput(mEncoder->GetVideoSink(), mSelectedVideoTrackID);
       }
       if (mEncoder) {
         mTrackUnionStream->RemoveListener(mEncoder.get());
       }
@@ -917,18 +926,16 @@ private:
   // handler. "mTimeSlice < 0" means Session object does not push encoded data to
   // onDataAvailable, instead, it passive wait the client side pull encoded data
   // by calling requestData API.
   const int32_t mTimeSlice;
   // Indicate this session's stop has been called.
   bool mStopIssued;
   // Indicate the session had fire start event. Encoding thread only.
   bool mIsStartEventFired;
-  // The register flag for "Media_Encoder" thread to profiler
-  bool mIsRegisterProfiler;
   // False if the InitEncoder called successfully, ensure the
   // ExtractRunnable/DestroyRunnable will end the session.
   // Main thread only.
   bool mNeedSessionEndTask;
   TrackID mSelectedVideoTrackID;
 };
 
 NS_IMPL_ISUPPORTS(MediaRecorder::Session, nsIObserver)
--- a/dom/media/android/AndroidMediaResourceServer.cpp
+++ b/dom/media/android/AndroidMediaResourceServer.cpp
@@ -370,17 +370,17 @@ ResourceSocketListener::OnSocketAccepted
 
   rv = aTrans->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(input));
   if (NS_FAILED(rv)) return rv;
 
   rv = aTrans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(output));
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIThread> thread;
-  rv = NS_NewThread(getter_AddRefs(thread));
+  rv = NS_NewNamedThread("ServeResource", getter_AddRefs(thread));
   if (NS_FAILED(rv)) return rv;
 
   nsCOMPtr<nsIRunnable> event = new ServeResourceEvent(input.get(), output.get(), mServer);
   return thread->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
 NS_IMETHODIMP
 ResourceSocketListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus)
--- a/dom/media/gtest/TestMP4Reader.cpp
+++ b/dom/media/gtest/TestMP4Reader.cpp
@@ -39,17 +39,17 @@ public:
     // This needs to be done before invoking GetBuffered. This is normally
     // done by MediaDecoderStateMachine.
     reader->DispatchSetStartTime(0);
   }
 
   void Init() {
     nsCOMPtr<nsIThread> thread;
     nsCOMPtr<nsIRunnable> r = NewRunnableMethod(this, &TestBinding::ReadMetadata);
-    nsresult rv = NS_NewThread(getter_AddRefs(thread), r);
+    nsresult rv = NS_NewNamedThread("ReadMetadata", getter_AddRefs(thread), r);
     EXPECT_EQ(NS_OK, rv);
     thread->Shutdown();
   }
 
 private:
   virtual ~TestBinding()
   {
     {
--- a/dom/media/gtest/TestMozPromise.cpp
+++ b/dom/media/gtest/TestMozPromise.cpp
@@ -107,16 +107,50 @@ TEST(MozPromise, BasicReject)
   RefPtr<TaskQueue> queue = atq.Queue();
   RunOnTaskQueue(queue, [queue] () -> void {
     TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
       DO_FAIL,
       [queue] (int aRejectValue) -> void { EXPECT_EQ(aRejectValue, 42.0); queue->BeginShutdown(); });
   });
 }
 
+TEST(MozPromise, BasicResolveOrRejectResolved)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndResolve(42, __func__)->Then(queue, __func__,
+      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
+      {
+        EXPECT_TRUE(aValue.IsResolve());
+        EXPECT_FALSE(aValue.IsReject());
+        EXPECT_FALSE(aValue.IsNothing());
+        EXPECT_EQ(aValue.ResolveValue(), 42);
+        queue->BeginShutdown();
+      });
+  });
+}
+
+TEST(MozPromise, BasicResolveOrRejectRejected)
+{
+  AutoTaskQueue atq;
+  RefPtr<TaskQueue> queue = atq.Queue();
+  RunOnTaskQueue(queue, [queue] () -> void {
+    TestPromise::CreateAndReject(42.0, __func__)->Then(queue, __func__,
+      [queue] (const TestPromise::ResolveOrRejectValue& aValue) -> void
+      {
+        EXPECT_TRUE(aValue.IsReject());
+        EXPECT_FALSE(aValue.IsResolve());
+        EXPECT_FALSE(aValue.IsNothing());
+        EXPECT_EQ(aValue.RejectValue(), 42.0);
+        queue->BeginShutdown();
+      });
+  });
+}
+
 TEST(MozPromise, AsyncResolve)
 {
   AutoTaskQueue atq;
   RefPtr<TaskQueue> queue = atq.Queue();
   RunOnTaskQueue(queue, [queue] () -> void {
     RefPtr<TestPromise::Private> p = new TestPromise::Private(__func__);
 
     // Kick off three racing tasks, and make sure we get the one that finishes earliest.
--- a/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
+++ b/dom/media/webaudio/blink/HRTFDatabaseLoader.cpp
@@ -23,16 +23,17 @@
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "HRTFDatabaseLoader.h"
 #include "HRTFDatabase.h"
+#include "GeckoProfiler.h"
 
 using namespace mozilla;
 
 namespace WebCore {
 
 // Singleton
 nsTHashtable<HRTFDatabaseLoader::LoaderByRateEntry>*
     HRTFDatabaseLoader::s_loaderMap = nullptr;
@@ -146,16 +147,17 @@ void HRTFDatabaseLoader::MainThreadRelea
         // on this (main) thread.
         delete this;
     }
 }
 
 // Asynchronously load the database in this thread.
 static void databaseLoaderEntry(void* threadData)
 {
+    AutoProfilerRegister registerThread("HRTFDatabaseLdr");
     PR_SetCurrentThreadName("HRTFDatabaseLdr");
 
     HRTFDatabaseLoader* loader = reinterpret_cast<HRTFDatabaseLoader*>(threadData);
     MOZ_ASSERT(loader);
     loader->load();
 }
 
 void HRTFDatabaseLoader::load()
--- a/dom/plugins/base/android/ANPAudio.cpp
+++ b/dom/plugins/base/android/ANPAudio.cpp
@@ -117,18 +117,16 @@ public:
   }
 
   ANPAudioTrack* mTrack;
 };
 
 NS_IMETHODIMP
 AudioRunnable::Run()
 {
-  PR_SetCurrentThreadName("Android Audio");
-
   JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
 
   mozilla::AutoLocalJNIFrame autoFrame(jenv, 2);
 
   jbyteArray bytearray = jenv->NewByteArray(mTrack->bufferSize);
   if (!bytearray) {
     LOG("AudioRunnable:: Run.  Could not create bytearray");
     return NS_ERROR_FAILURE;
@@ -316,17 +314,17 @@ anp_audio_start(ANPAudioTrack* s)
 
   s->isStopped = false;
   s->keepGoing = true;
 
   // AudioRunnable now owns the ANPAudioTrack
   RefPtr<AudioRunnable> runnable = new AudioRunnable(s);
 
   nsCOMPtr<nsIThread> thread;
-  NS_NewThread(getter_AddRefs(thread), runnable);
+  NS_NewNamedThread("Android Audio", getter_AddRefs(thread), runnable);
 }
 
 void
 anp_audio_pause(ANPAudioTrack* s)
 {
   if (s == nullptr || s->output_unit == nullptr) {
     return;
   }
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -4377,17 +4377,17 @@ QuotaManager::OpenDirectoryInternal(Null
       nsAutoPtr<nsTHashtable<nsCStringHashKey>>& origin = origins[clientType];
       if (!origin) {
         origin = new nsTHashtable<nsCStringHashKey>();
       }
       origin->PutEntry(originScope.GetOrigin());
     }
   }
 
-  for (uint32_t index : MakeRange(uint32_t(Client::TYPE_MAX))) {
+  for (uint32_t index : IntegerRange(uint32_t(Client::TYPE_MAX))) {
     if (origins[index]) {
       for (auto iter = origins[index]->Iter(); !iter.Done(); iter.Next()) {
         MOZ_ASSERT(mClients[index]);
 
         mClients[index]->AbortOperations(iter.Get()->GetKey());
       }
     }
   }
--- a/dom/storage/StorageDBThread.cpp
+++ b/dom/storage/StorageDBThread.cpp
@@ -22,16 +22,17 @@
 #include "mozIStorageValueArray.h"
 #include "mozIStorageFunction.h"
 #include "mozilla/BasePrincipal.h"
 #include "nsIObserverService.h"
 #include "nsVariant.h"
 #include "mozilla/IOInterposer.h"
 #include "mozilla/Services.h"
 #include "mozilla/Tokenizer.h"
+#include "GeckoProfiler.h"
 
 // How long we collect write oprerations
 // before they are flushed to the database
 // In milliseconds.
 #define FLUSHING_INTERVAL_MS 5000
 
 // Write Ahead Log's maximum size is 512KB
 #define MAX_WAL_SIZE_BYTES 512 * 1024
@@ -334,16 +335,17 @@ StorageDBThread::SetDefaultPriority()
   if (--mPriorityCounter <= 0) {
     PR_SetThreadPriority(mThread, PR_PRIORITY_LOW);
   }
 }
 
 void
 StorageDBThread::ThreadFunc(void* aArg)