Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 10 Apr 2017 16:32:45 -0700
changeset 560035 b5b5dbed1c409d96aa6b97f2036cd66312fc45ad
parent 560016 d7b81b700d7571cd4c5486a1152477fe99e0643d (current diff)
parent 559940 51e823f4025f4b96474803f2438c65d920b0b182 (diff)
child 560036 98914dc95cbd594427b6087590246053ad622688
child 560037 27a53350b193d02cb5fe58028472723d9926e8ad
child 560038 10f8809a1c6787021181bbff95a44ca976290c11
child 560040 d4734caba4c45b7ece4bf529e107e549759c9b7e
child 560042 0aec3a761e19217915dc8880e1280bf349899df2
child 560049 436cc77c9da22137371dbab5c1a2a3fedab2de55
child 560051 023755a77722359a7ed6e9e46220988047a5dcdb
child 560056 391aec52650da5f89ccac1da2460bef73c3ae243
child 560074 eb799af77ee1dbb277cf8f07946ab5d021cc0d74
child 560076 cc8c5d6599132f615317b1e27f5ee3f1259f5ea1
child 560077 a641cd9d71890bf5684bd28d927599cc54eee804
child 560081 34deed76a7932f1735501c81b270a6944fddfe24
child 560103 2cd8cabccff401af22a8311ef99fabdf4e108bf1
child 560104 4b18d8d05a81b12fada7995ff9d3fd79221aa0c5
child 560118 2913d2b1485b3cc434bf760da61902f9101ffd6b
child 560122 28d1d7a6f21a005baeaeef65c84df246d4b62a15
child 560127 2cb5bc0838961905c4aebf8dfce98dced46a96b3
child 560131 3ddc8ff8cc515a56d35b320985220b2d38038a5b
child 560133 5d9a5eb6ab02d86434b39fceea8d258ed60daf8d
child 560136 358a0d19d6069ed195309a9a79953c1ace62ad47
child 560138 77240f429825d88b6b1683cea70895f26195a8b9
child 560160 67f97fa23dda57c7b8c295e95a992d48cbe25384
child 560168 93123c26a530e4137dc359f4747adf2283f5c2eb
child 560169 32510f252e4c98667d58736136a56d605716f7ef
child 560171 73716f35eb6a0a06a9236176d63fd4cd77d8520e
child 560248 4e4bd12cc11cab7e9227bd240424311d4acf596a
child 560280 c1e635307599cc1a5f5a7d6f0928d7c50b915dc4
child 560331 5f2a699a9652f75c1934d319677a4e42c3c82d4b
child 560333 edb8934ad24399d2f08320fa99551d878f2e809e
child 560338 ab0f4c707a5247daf8fdf6a9666a5b753e71d457
child 560386 a0f2293a78d67aad4d5791f7b78e9f6e5d160f1b
child 560408 67e6e8e5f64c3a4baccbb925628d66cc1d947c1e
child 560461 d95fb608b44b1b168b3d7c652d4508f3b901d0b9
child 560475 a65635897035b2646c56ec950160d7ea1bc1ae2c
child 560584 bd13754e7f9ded6e19cdcf1ae4b0742eb1f9f9ac
child 560859 9833dbc443fe394926f5604b74e8d9af00cb6a28
child 560871 6ee4d7e0b93e4f75126a355393ff92cea687a560
child 560872 b05ee1233c7249972c289f4fea3ae6b95c53c927
child 560935 b281263b261a6996a884a1affb0ca4676bb3423f
child 561000 f9bcc709ac59a8ade9d49da2fdf49daaef4fb14e
child 561072 f5dceb91c91608cbe9e9f1653034e15c300c1016
child 561086 1ca9f5c9e8bc0c0b7fa775e131caa82ab754512d
child 561104 5553deecaff2e65df98234258fbd9ac66b5f6575
child 561112 7cdd7672a2b07d891836373a417bfe722b05ece5
child 561283 1354161605cf89a5425f6c6f3afae895ee3548f8
child 561331 2dfeeac72c21ed43178aa083b853fabeb07b835e
child 561354 65e871e3bb678a7ebbcaac0cb1c6c70fda6de4ed
child 561359 d0bfe98f41f5dcd47442ed59d6828de95bcce5ed
child 561672 4a33a57718a00f17243ad78e5ef4cb536a0290fc
child 561717 5a9c7038e1c00fa3abcf6829cac586aa3673a950
child 561749 3ca0577fab3da7c98d2945f545d8ea073d285708
child 561959 b702447ae655ac86b89cc1b062065b05ea0f5afc
child 563448 1bda60c62fb861c86cac1d5eb43887771fa1b6ea
child 563453 eda853138d4ee1c0f049139320620dfcf8edf26b
push id53301
push userbmo:mcmanus@ducksong.com
push dateTue, 11 Apr 2017 01:47:46 +0000
reviewersmerge
milestone55.0a1
Merge inbound to central, a=merge
layout/style/ServoStyleSet.cpp
media/libcubeb/temp-patch-debug-drift.patch
toolkit/components/telemetry/histogram-whitelists.json
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -19,16 +19,21 @@ var gBrowserThumbnails = {
   _sslDiskCacheEnabled: null,
 
   /**
    * Map of capture() timeouts assigned to their browsers.
    */
   _timeouts: null,
 
   /**
+   * Top site URLs refresh timer.
+   */
+  _topSiteURLsRefreshTimer: null,
+
+  /**
    * List of tab events we want to listen for.
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
     PageThumbs.addExpirationFilter(this);
     gBrowser.addTabsProgressListener(this);
     Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
@@ -42,16 +47,20 @@ var gBrowserThumbnails = {
 
     this._timeouts = new WeakMap();
   },
 
   uninit: function Thumbnails_uninit() {
     PageThumbs.removeExpirationFilter(this);
     gBrowser.removeTabsProgressListener(this);
     Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
+    if (this._topSiteURLsRefreshTimer) {
+      this._topSiteURLsRefreshTimer.cancel();
+      this._topSiteURLsRefreshTimer = null;
+    }
 
     this._tabEvents.forEach(function(aEvent) {
       gBrowser.tabContainer.removeEventListener(aEvent, this);
     }, this);
   },
 
   handleEvent: function Thumbnails_handleEvent(aEvent) {
     switch (aEvent.type) {
@@ -70,16 +79,32 @@ var gBrowserThumbnails = {
     }
   },
 
   observe: function Thumbnails_observe() {
     this._sslDiskCacheEnabled =
       Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
   },
 
+  clearTopSiteURLCache: function Thumbnails_clearTopSiteURLCache() {
+    if (this._topSiteURLsRefreshTimer) {
+      this._topSiteURLsRefreshTimer.cancel();
+      this._topSiteURLsRefreshTimer = null;
+    }
+    // Delete the defined property
+    delete this._topSiteURLs;
+    XPCOMUtils.defineLazyGetter(this, "_topSiteURLs",
+                                Thumbnails_getTopSiteURLs);
+  },
+
+  notify: function Thumbnails_notify(timer) {
+    gBrowserThumbnails._topSiteURLsRefreshTimer = null;
+    gBrowserThumbnails.clearTopSiteURLCache();
+  },
+
   filterForThumbnailExpiration:
   function Thumbnails_filterForThumbnailExpiration(aCallback) {
     aCallback(this._topSiteURLs);
   },
 
   /**
    * State change progress listener for all tabs.
    */
@@ -120,24 +145,35 @@ var gBrowserThumbnails = {
     // Capture only if it's the currently selected tab.
     if (aBrowser != gBrowser.selectedBrowser) {
       aCallback(false);
       return;
     }
     PageThumbs.shouldStoreThumbnail(aBrowser, aCallback);
   },
 
-  get _topSiteURLs() {
-    return NewTabUtils.links.getLinks().reduce((urls, link) => {
-      if (link)
-        urls.push(link.url);
-      return urls;
-    }, []);
-  },
-
   _clearTimeout: function Thumbnails_clearTimeout(aBrowser) {
     if (this._timeouts.has(aBrowser)) {
       aBrowser.removeEventListener("scroll", this);
       clearTimeout(this._timeouts.get(aBrowser));
       this._timeouts.delete(aBrowser);
     }
   }
 };
+
+function Thumbnails_getTopSiteURLs() {
+  // The _topSiteURLs getter can be expensive to run, but its return value can
+  // change frequently on new profiles, so as a compromise we cache its return
+  // value as a lazy getter for 1 minute every time it's called.
+  gBrowserThumbnails._topSiteURLsRefreshTimer =
+    Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+  gBrowserThumbnails._topSiteURLsRefreshTimer.initWithCallback(gBrowserThumbnails,
+                                                               60 * 1000,
+                                                               Ci.nsITimer.TYPE_ONE_SHOT);
+  return NewTabUtils.links.getLinks().reduce((urls, link) => {
+    if (link)
+      urls.push(link.url);
+    return urls;
+  }, []);
+}
+
+XPCOMUtils.defineLazyGetter(gBrowserThumbnails, "_topSiteURLs",
+                            Thumbnails_getTopSiteURLs);
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -158,17 +158,16 @@ PluginContent.prototype = {
   },
 
   onPageHide(event) {
     // Ignore events that aren't from the main document.
     if (!this.content || event.target != this.content.document) {
       return;
     }
 
-    this._finishRecordingFlashPluginTelemetry();
     this.clearPluginCaches();
     this.haveShownNotification = false;
   },
 
   getPluginUI(plugin, anonid) {
     return plugin.ownerDocument.
            getAnonymousElementByAttribute(plugin, "anonid", anonid);
   },
@@ -543,20 +542,16 @@ PluginContent.prototype = {
         shouldShowNotification = true;
         let pluginRect = plugin.getBoundingClientRect();
         if (pluginRect.width <= 5 && pluginRect.height <= 5) {
           Services.telemetry.getHistogramById("PLUGIN_TINY_CONTENT").add(1);
         }
         break;
     }
 
-    if (this._getPluginInfo(plugin).mimetype === FLASH_MIME_TYPE) {
-      this._recordFlashPluginTelemetry(eventType, plugin);
-    }
-
     // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
     let overlay = this.getPluginUI(plugin, "main");
     if (eventType != "PluginCrashed") {
       if (overlay != null) {
         this.setVisibility(plugin, overlay,
                            this.shouldShowOverlay(plugin, overlay));
         let resizeListener = () => {
           this.setVisibility(plugin, overlay,
@@ -578,59 +573,16 @@ PluginContent.prototype = {
       }, true);
     }
 
     if (shouldShowNotification) {
       this._showClickToPlayNotification(plugin, false);
     }
   },
 
-  _recordFlashPluginTelemetry(eventType, plugin) {
-    if (!Services.telemetry.canRecordExtended) {
-      return;
-    }
-
-    if (!this.flashPluginStats) {
-      this.flashPluginStats = {
-        instancesCount: 0,
-        plugins: new WeakSet()
-      };
-    }
-
-    if (!this.flashPluginStats.plugins.has(plugin)) {
-      // Reporting plugin instance and its dimensions only once.
-      this.flashPluginStats.plugins.add(plugin);
-
-      this.flashPluginStats.instancesCount++;
-
-      let pluginRect = plugin.getBoundingClientRect();
-      Services.telemetry.getHistogramById("FLASH_PLUGIN_WIDTH")
-                       .add(pluginRect.width);
-      Services.telemetry.getHistogramById("FLASH_PLUGIN_HEIGHT")
-                       .add(pluginRect.height);
-      Services.telemetry.getHistogramById("FLASH_PLUGIN_AREA")
-                       .add(pluginRect.width * pluginRect.height);
-
-      let state = this._getPluginInfo(plugin).fallbackType;
-      if (state === null) {
-        state = Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED;
-      }
-      Services.telemetry.getHistogramById("FLASH_PLUGIN_STATES")
-                       .add(state);
-    }
-  },
-
-  _finishRecordingFlashPluginTelemetry() {
-    if (this.flashPluginStats) {
-      Services.telemetry.getHistogramById("FLASH_PLUGIN_INSTANCES_ON_PAGE")
-                        .add(this.flashPluginStats.instancesCount);
-    delete this.flashPluginStats;
-    }
-  },
-
   isKnownPlugin(objLoadingContent) {
     return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
             Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
   },
 
   canActivatePlugin(objLoadingContent) {
     // if this isn't a known plugin, we can't activate it
     // (this also guards pluginHost.getPermissionStringForType against
--- a/caps/ContentPrincipal.cpp
+++ b/caps/ContentPrincipal.cpp
@@ -122,39 +122,16 @@ ContentPrincipal::Init(nsIURI *aCodebase
 }
 
 nsresult
 ContentPrincipal::GetScriptLocation(nsACString &aStr)
 {
   return mCodebase->GetSpec(aStr);
 }
 
-static nsresult
-AssignFullSpecToOriginNoSuffix(nsIURI* aURI, nsACString& aOriginNoSuffix)
-{
-  nsresult rv = aURI->GetAsciiSpec(aOriginNoSuffix);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // The origin, when taken from the spec, should not contain the ref part of
-  // the URL.
-
-  int32_t pos = aOriginNoSuffix.FindChar('?');
-  int32_t hashPos = aOriginNoSuffix.FindChar('#');
-
-  if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
-    pos = hashPos;
-  }
-
-  if (pos != kNotFound) {
-    aOriginNoSuffix.Truncate(pos);
-  }
-
-  return NS_OK;
-}
-
 /* static */ nsresult
 ContentPrincipal::GenerateOriginNoSuffixFromURI(nsIURI* aURI,
                                                 nsACString& aOriginNoSuffix)
 {
   if (!aURI) {
     return NS_ERROR_FAILURE;
   }
 
@@ -178,17 +155,17 @@ ContentPrincipal::GenerateOriginNoSuffix
 
   nsresult rv;
 // NB: This is only compiled for Thunderbird/Suite.
 #if IS_ORIGIN_IS_FULL_SPEC_DEFINED
   bool fullSpec = false;
   rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC, &fullSpec);
   NS_ENSURE_SUCCESS(rv, rv);
   if (fullSpec) {
-    return AssignFullSpecToOriginNoSuffix(origin, aOriginNoSuffix);
+    return origin->GetAsciiSpec(aOriginNoSuffix);
   }
 #endif
 
   // We want the invariant that prinA.origin == prinB.origin i.f.f.
   // prinA.equals(prinB). However, this requires that we impose certain constraints
   // on the behavior and origin semantics of principals, and in particular, forbid
   // creating origin strings for principals whose equality constraints are not
   // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs
@@ -253,18 +230,34 @@ ContentPrincipal::GenerateOriginNoSuffix
   if (!hostPort.IsEmpty()) {
     rv = origin->GetScheme(aOriginNoSuffix);
     NS_ENSURE_SUCCESS(rv, rv);
     aOriginNoSuffix.AppendLiteral("://");
     aOriginNoSuffix.Append(hostPort);
     return NS_OK;
   }
 
-  // Use the full spec.
-  return AssignFullSpecToOriginNoSuffix(origin, aOriginNoSuffix);
+  rv = aURI->GetAsciiSpec(aOriginNoSuffix);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // The origin, when taken from the spec, should not contain the ref part of
+  // the URL.
+
+  int32_t pos = aOriginNoSuffix.FindChar('?');
+  int32_t hashPos = aOriginNoSuffix.FindChar('#');
+
+  if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
+    pos = hashPos;
+  }
+
+  if (pos != kNotFound) {
+    aOriginNoSuffix.Truncate(pos);
+  }
+
+  return NS_OK;
 }
 
 bool
 ContentPrincipal::SubsumesInternal(nsIPrincipal* aOther,
                                    BasePrincipal::DocumentDomainConsideration aConsideration)
 {
   MOZ_ASSERT(aOther);
 
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -58,16 +58,21 @@
 #include "nsIObserver.h"
 #include "nsIRequest.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIWebSocketChannel.h"
 #include "nsIWebSocketListener.h"
 #include "nsProxyRelease.h"
 #include "nsWeakReference.h"
 
+#define OPEN_EVENT_STRING NS_LITERAL_STRING("open")
+#define MESSAGE_EVENT_STRING NS_LITERAL_STRING("message")
+#define ERROR_EVENT_STRING NS_LITERAL_STRING("error")
+#define CLOSE_EVENT_STRING NS_LITERAL_STRING("close")
+
 using namespace mozilla::net;
 using namespace mozilla::dom::workers;
 
 namespace mozilla {
 namespace dom {
 
 class WebSocketImpl final : public nsIInterfaceRequestor
                           , public nsIWebSocketListener
@@ -776,17 +781,17 @@ WebSocketImpl::OnStart(nsISupports* aCon
                             mWebSocket->mEstablishedProtocol,
                             mWebSocket->mEstablishedExtensions);
 
   // Let's keep the object alive because the webSocket can be CCed in the
   // onopen callback.
   RefPtr<WebSocket> webSocket = mWebSocket;
 
   // Call 'onopen'
-  rv = webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
+  rv = webSocket->CreateAndDispatchSimpleEvent(OPEN_EVENT_STRING);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch the open event");
   }
 
   webSocket->UpdateMustKeepAlive();
   return NS_OK;
 }
 
@@ -1876,17 +1881,17 @@ WebSocketImpl::DispatchConnectionCloseEv
 
   // Let's keep the object alive because the webSocket can be CCed in the
   // onerror or in the onclose callback.
   RefPtr<WebSocket> webSocket = mWebSocket;
 
   // Call 'onerror' if needed
   if (mFailed) {
     nsresult rv =
-      webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
+      webSocket->CreateAndDispatchSimpleEvent(ERROR_EVENT_STRING);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch the error event");
     }
   }
 
   nsresult rv = webSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,
                                                        mCloseEventCode,
                                                        mCloseEventReason);
@@ -1988,17 +1993,17 @@ WebSocket::CreateAndDispatchMessageEvent
                                              mImpl->mInnerWindowID,
                                              aData, messageType);
 
   // create an event that uses the MessageEvent interface,
   // which does not bubble, is not cancelable, and has no default action
 
   RefPtr<MessageEvent> event = new MessageEvent(this, nullptr, nullptr);
 
-  event->InitMessageEvent(nullptr, NS_LITERAL_STRING("message"), false, false,
+  event->InitMessageEvent(nullptr, MESSAGE_EVENT_STRING, false, false,
                           jsData, mImpl->mUTF16Origin, EmptyString(), nullptr,
                           Sequence<OwningNonNull<MessagePort>>());
   event->SetTrusted(true);
 
   return DispatchDOMEvent(nullptr, static_cast<Event*>(event), nullptr,
                           nullptr);
 }
 
@@ -2025,17 +2030,17 @@ WebSocket::CreateAndDispatchCloseEvent(b
   CloseEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mWasClean = aWasClean;
   init.mCode = aCode;
   init.mReason = aReason;
 
   RefPtr<CloseEvent> event =
-    CloseEvent::Constructor(this, NS_LITERAL_STRING("close"), init);
+    CloseEvent::Constructor(this, CLOSE_EVENT_STRING, init);
   event->SetTrusted(true);
 
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
 nsresult
 WebSocketImpl::ParseURL(const nsAString& aURL)
 {
@@ -2145,31 +2150,31 @@ WebSocket::UpdateMustKeepAlive()
   bool shouldKeepAlive = false;
   uint16_t readyState = ReadyState();
 
   if (mListenerManager) {
     switch (readyState)
     {
       case CONNECTING:
       {
-        if (mListenerManager->HasListenersFor(nsGkAtoms::onopen) ||
-            mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
-            mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
-            mListenerManager->HasListenersFor(nsGkAtoms::onclose)) {
+        if (mListenerManager->HasListenersFor(OPEN_EVENT_STRING) ||
+            mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
+            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
+            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING)) {
           shouldKeepAlive = true;
         }
       }
       break;
 
       case OPEN:
       case CLOSING:
       {
-        if (mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
-            mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
-            mListenerManager->HasListenersFor(nsGkAtoms::onclose) ||
+        if (mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
+            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
+            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING) ||
             mOutgoingBufferedAmount != 0) {
           shouldKeepAlive = true;
         }
       }
       break;
 
       case CLOSED:
       {
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -1956,17 +1956,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
 
   // We own only the items in mDOMMediaQueryLists that have listeners;
   // this reference is managed by their AddListener and RemoveListener
   // methods.
   for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
        l != &tmp->mDOMMediaQueryLists; ) {
     PRCList *next = PR_NEXT_LINK(l);
     MediaQueryList *mql = static_cast<MediaQueryList*>(l);
-    mql->RemoveAllListeners();
+    mql->Disconnect();
     l = next;
   }
 
   tmp->mInUnlinkOrDeletion = false;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 nsresult
 nsDocument::Init()
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -189,16 +189,20 @@ const kEventConstructors = {
                                              },
   MediaKeyMessageEvent:                      { create: function (aName, aProps) {
                                                          return new MediaKeyMessageEvent(aName, {
                                                            messageType: "license-request",
                                                            message: new ArrayBuffer(0)
                                                          });
                                                        },
                                              },
+  MediaQueryListEvent:                       { create: function (aName, aProps) {
+                                                         return new MediaQueryListEvent(aName, aProps);
+                                                       },
+                                             },
   MediaStreamEvent:                          { create: function (aName, aProps) {
                                                          return new MediaStreamEvent(aName, aProps);
                                                        },
                                              },
   MediaStreamTrackEvent:                     {
                                                // Difficult to test required arguments.
                                              },
   MessageEvent:                              { create: function (aName, aProps) {
--- a/dom/tests/mochitest/general/test_interfaces.js
+++ b/dom/tests/mochitest/general/test_interfaces.js
@@ -633,16 +633,18 @@ var interfaceNamesInGlobalScope =
     "MediaKeyMessageEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaKeyStatusMap",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "MediaQueryListEvent",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaRecorder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStreamAudioDestinationNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/MediaQueryList.webidl
+++ b/dom/webidl/MediaQueryList.webidl
@@ -1,20 +1,24 @@
 /* -*- 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/.
  *
  * The origin of this IDL file is
- * http://dev.w3.org/csswg/cssom-view/#the-mediaquerylist-interface
+ * https://drafts.csswg.org/cssom-view/#mediaquerylist
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface MediaQueryList {
+interface MediaQueryList : EventTarget {
   readonly attribute DOMString media;
   readonly attribute boolean matches;
-  void addListener(MediaQueryListListener listener);
-  void removeListener(MediaQueryListListener listener);
+
+  [Throws]
+  void addListener(EventListener? listener);
+
+  [Throws]
+  void removeListener(EventListener? listener);
+
+           attribute EventHandler onchange;
 };
-
-callback MediaQueryListListener = void (MediaQueryList list);
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaQueryListEvent.webidl
@@ -0,0 +1,18 @@
+/* -*- 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/.
+ *
+ * https://drafts.csswg.org/cssom-view/#mediaquerylistevent
+ */
+
+[Constructor(DOMString type, optional MediaQueryListEventInit eventInitDict)]
+interface MediaQueryListEvent : Event {
+  readonly attribute DOMString media;
+  readonly attribute boolean matches;
+};
+
+dictionary MediaQueryListEventInit : EventInit {
+  DOMString media = "";
+  boolean matches = false;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -1072,16 +1072,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'FontFaceSetLoadEvent.webidl',
     'GamepadAxisMoveEvent.webidl',
     'GamepadButtonEvent.webidl',
     'GamepadEvent.webidl',
     'GroupedHistoryEvent.webidl',
     'HashChangeEvent.webidl',
     'HiddenPluginEvent.webidl',
     'ImageCaptureErrorEvent.webidl',
+    'MediaQueryListEvent.webidl',
     'MediaStreamEvent.webidl',
     'MediaStreamTrackEvent.webidl',
     'OfflineAudioCompletionEvent.webidl',
     'PageTransitionEvent.webidl',
     'PerformanceEntryEvent.webidl',
     'PluginCrashedEvent.webidl',
     'PopStateEvent.webidl',
     'PopupBlockedEvent.webidl',
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -1220,46 +1220,55 @@ function GetOption(options, property, ty
         return value;
     }
 
     // Step 3.
     return fallback;
 }
 
 /**
- * Extracts a property value from the provided options object, converts it to a
- * Number value, checks whether it is in the allowed range, and fills in a
- * fallback value if necessary.
+ * The abstract operation DefaultNumberOption converts value to a Number value,
+ * checks whether it is in the allowed range, and fills in a fallback value if
+ * necessary.
  *
- * Spec: ECMAScript Internationalization API Specification, 9.2.10.
+ * Spec: ECMAScript Internationalization API Specification, 9.2.11.
  */
-function GetNumberOption(options, property, minimum, maximum, fallback) {
-    assert(typeof minimum === "number" && (minimum | 0) === minimum, "GetNumberOption");
-    assert(typeof maximum === "number" && (maximum | 0) === maximum, "GetNumberOption");
-    assert(typeof fallback === "number" && (fallback | 0) === fallback, "GetNumberOption");
-    assert(minimum <= fallback && fallback <= maximum, "GetNumberOption");
+function DefaultNumberOption(value, minimum, maximum, fallback) {
+    assert(typeof minimum === "number" && (minimum | 0) === minimum, "DefaultNumberOption");
+    assert(typeof maximum === "number" && (maximum | 0) === maximum, "DefaultNumberOption");
+    assert(typeof fallback === "number" && (fallback | 0) === fallback, "DefaultNumberOption");
+    assert(minimum <= fallback && fallback <= maximum, "DefaultNumberOption");
 
     // Step 1.
-    var value = options[property];
-
-    // Step 2.
     if (value !== undefined) {
         value = ToNumber(value);
         if (Number_isNaN(value) || value < minimum || value > maximum)
             ThrowRangeError(JSMSG_INVALID_DIGITS_VALUE, value);
 
         // Apply bitwise-or to convert -0 to +0 per ES2017, 5.2 and to ensure
         // the result is an int32 value.
         return std_Math_floor(value) | 0;
     }
 
-    // Step 3.
+    // Step 2.
     return fallback;
 }
 
+/**
+ * Extracts a property value from the provided options object, converts it to a
+ * Number value, checks whether it is in the allowed range, and fills in a
+ * fallback value if necessary.
+ *
+ * Spec: ECMAScript Internationalization API Specification, 9.2.12.
+ */
+function GetNumberOption(options, property, minimum, maximum, fallback) {
+    // Steps 1-3.
+    return DefaultNumberOption(options[property], minimum, maximum, fallback);
+}
+
 
 /********** Property access for Intl objects **********/
 
 
 // Symbols in the self-hosting compartment can't be cloned, use a separate
 // object to hold the actual symbol value.
 // TODO: Can we add support to clone symbols?
 var intlFallbackSymbolHolder = { value: undefined };
@@ -1934,35 +1943,35 @@ function SetNumberFormatDigitOptions(laz
     // We skip Step 1 because we set the properties on a lazyData object.
 
     // Step 2-3.
     assert(IsObject(options), "SetNumberFormatDigitOptions");
     assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
     assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
     assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
 
-    // Steps 4-6.
+    // Steps 4-8.
     const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
     const mnfd = GetNumberOption(options, "minimumFractionDigits", 0, 20, mnfdDefault);
     const mxfdActualDefault = std_Math_max(mnfd, mxfdDefault);
     const mxfd = GetNumberOption(options, "maximumFractionDigits", mnfd, 20, mxfdActualDefault);
 
-    // Steps 7-8.
+    // Steps 9-10.
     let mnsd = options.minimumSignificantDigits;
     let mxsd = options.maximumSignificantDigits;
 
-    // Steps 9-11.
+    // Steps 11-13.
     lazyData.minimumIntegerDigits = mnid;
     lazyData.minimumFractionDigits = mnfd;
     lazyData.maximumFractionDigits = mxfd;
 
-    // Step 12.
+    // Step 14.
     if (mnsd !== undefined || mxsd !== undefined) {
-        mnsd = GetNumberOption(options, "minimumSignificantDigits", 1, 21, 1);
-        mxsd = GetNumberOption(options, "maximumSignificantDigits", mnsd, 21, 21);
+        mnsd = DefaultNumberOption(mnsd, 1, 21, 1);
+        mxsd = DefaultNumberOption(mxsd, mnsd, 21, 21);
         lazyData.minimumSignificantDigits = mnsd;
         lazyData.maximumSignificantDigits = mxsd;
     }
 }
 
 
 /**
  * Initializes an object as a NumberFormat.
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/cacheir/add-function-prototype.js
@@ -0,0 +1,55 @@
+function checkPrototype(fun, proto, resolvesPrototype) {
+    var desc = Object.getOwnPropertyDescriptor(fun, "prototype");
+    assertEq(desc.value, proto);
+    assertEq(desc.configurable, !resolvesPrototype);
+    assertEq(desc.enumerable, !resolvesPrototype);
+    assertEq(desc.writable, true);
+}
+function addPrototype(fun, proto, resolvesPrototype) {
+    fun.prototype = proto;
+    checkPrototype(fun, proto, resolvesPrototype);
+}
+function test() {
+    for (var i=0; i<50; i++) {
+        addPrototype(function() {}, i, true);
+        addPrototype(function*() {}, i, true);
+        addPrototype(function async() {}, i, true);
+        // Builtins, arrow functions, bound functions don't have a default
+        // prototype property.
+        addPrototype(Math.abs, i, false);
+        addPrototype(Array.prototype.map, i, false);
+        addPrototype(() => 1, i, false);
+        addPrototype((function() {}).bind(null), i, false);
+    }
+
+    // Now test this with a different IC for each function type.
+    for (var i=0; i<50; i++) {
+        var f = function() {};
+        f.prototype = i;
+        checkPrototype(f, i, true);
+
+        f = function*() {};
+        f.prototype = i;
+        checkPrototype(f, i, true);
+
+        f = function async() {};
+        f.prototype = i;
+        checkPrototype(f, i, true);
+
+        Math.sin.prototype = i;
+        checkPrototype(Math.sin, i, false);
+
+        Array.prototype.filter.prototype = i;
+        checkPrototype(Array.prototype.filter, i, false);
+
+        f = () => 1;
+        f.prototype = i;
+        checkPrototype(f, i, false);
+
+        f = (function() {}).bind(null);
+        f.prototype = i;
+        checkPrototype(f, i, false);
+    }
+
+}
+test();
--- a/js/src/jit/CacheIR.cpp
+++ b/js/src/jit/CacheIR.cpp
@@ -3087,18 +3087,36 @@ SetPropIRGenerator::tryAttachAddSlotStub
         !propShape->hasSlot() ||
         !propShape->hasDefaultSetter() ||
         !propShape->writable())
     {
         return false;
     }
 
     // Watch out for resolve hooks.
-    if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj))
-        return false;
+    if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj)) {
+        // The JSFunction resolve hook defines a (non-configurable and
+        // non-enumerable) |prototype| property on certain functions. Scripts
+        // often assign a custom |prototype| object and we want to optimize
+        // this |prototype| set and eliminate the default object allocation.
+        //
+        // We check group->maybeInterpretedFunction() here and guard on the
+        // group. The group is unique for a particular function so this ensures
+        // we don't add the default prototype property to functions that don't
+        // have it.
+        if (!obj->is<JSFunction>() ||
+            !JSID_IS_ATOM(id, cx_->names().prototype) ||
+            !oldGroup->maybeInterpretedFunction() ||
+            !obj->as<JSFunction>().needsPrototypeProperty())
+        {
+            return false;
+        }
+        MOZ_ASSERT(!propShape->configurable());
+        MOZ_ASSERT(!propShape->enumerable());
+    }
 
     // Also watch out for addProperty hooks. Ignore the Array addProperty hook,
     // because it doesn't do anything for non-index properties.
     DebugOnly<uint32_t> index;
     MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
     if (!obj->is<ArrayObject>() && obj->getClass()->getAddProperty())
         return false;
 
@@ -3110,19 +3128,24 @@ SetPropIRGenerator::tryAttachAddSlotStub
 
         // If prototype defines this property in a non-plain way, don't optimize.
         Shape* protoShape = proto->as<NativeObject>().lookup(cx_, id);
         if (protoShape && !protoShape->hasDefaultSetter())
             return false;
 
         // Otherwise, if there's no such property, watch out for a resolve hook
         // that would need to be invoked and thus prevent inlining of property
-        // addition.
-        if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto))
+        // addition. Allow the JSFunction resolve hook as it only defines plain
+        // data properties and we don't need to invoke it for objects on the
+        // proto chain.
+        if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
+            !proto->is<JSFunction>())
+        {
             return false;
+        }
     }
 
     // Don't attach if we are adding a property to an object which the new
     // script properties analysis hasn't been performed for yet, as there
     // may be a shape change required here afterwards.
     if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
         *isTemporarilyUnoptimizable_ = true;
         return false;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -449,16 +449,43 @@ ResolveInterpretedFunctionPrototype(JSCo
 
     // Per ES5 15.3.5.2 a user-defined function's .prototype property is
     // initially non-configurable, non-enumerable, and writable.
     RootedValue protoVal(cx, ObjectValue(*proto));
     return DefineProperty(cx, fun, id, protoVal, nullptr, nullptr,
                           JSPROP_PERMANENT | JSPROP_RESOLVING);
 }
 
+bool
+JSFunction::needsPrototypeProperty()
+{
+    /*
+     * Built-in functions do not have a .prototype property per ECMA-262,
+     * or (Object.prototype, Function.prototype, etc.) have that property
+     * created eagerly.
+     *
+     * ES5 15.3.4.5: bound functions don't have a prototype property. The
+     * isBuiltin() test covers this case because bound functions are native
+     * (and thus built-in) functions by definition/construction.
+     *
+     * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors.
+     * Generators are not constructors, but they have a .prototype property anyway,
+     * according to errata to ES6. See bug 1191486.
+     *
+     * Thus all of the following don't get a .prototype property:
+     * - Methods (that are not class-constructors or generators)
+     * - Arrow functions
+     * - Function.prototype
+     */
+    if (isBuiltin())
+        return IsWrappedAsyncGenerator(this);
+
+    return isConstructor() || isStarGenerator() || isLegacyGenerator() || isAsync();
+}
+
 static bool
 fun_mayResolve(const JSAtomState& names, jsid id, JSObject*)
 {
     if (!JSID_IS_ATOM(id))
         return false;
 
     JSAtom* atom = JSID_TO_ATOM(id);
     return atom == names.prototype || atom == names.length || atom == names.name;
@@ -468,42 +495,18 @@ static bool
 fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
 {
     if (!JSID_IS_ATOM(id))
         return true;
 
     RootedFunction fun(cx, &obj->as<JSFunction>());
 
     if (JSID_IS_ATOM(id, cx->names().prototype)) {
-        /*
-         * Built-in functions do not have a .prototype property per ECMA-262,
-         * or (Object.prototype, Function.prototype, etc.) have that property
-         * created eagerly.
-         *
-         * ES5 15.3.4.5: bound functions don't have a prototype property. The
-         * isBuiltin() test covers this case because bound functions are native
-         * (and thus built-in) functions by definition/construction.
-         *
-         * ES6 9.2.8 MakeConstructor defines the .prototype property on constructors.
-         * Generators are not constructors, but they have a .prototype property anyway,
-         * according to errata to ES6. See bug 1191486.
-         *
-         * Thus all of the following don't get a .prototype property:
-         * - Methods (that are not class-constructors or generators)
-         * - Arrow functions
-         * - Function.prototype
-         */
-        if (!IsWrappedAsyncGenerator(fun)) {
-            if (fun->isBuiltin())
-                return true;
-            if (!fun->isConstructor()) {
-                if (!fun->isStarGenerator() && !fun->isLegacyGenerator() && !fun->isAsync())
-                    return true;
-            }
-        }
+        if (!fun->needsPrototypeProperty())
+            return true;
 
         if (!ResolveInterpretedFunctionPrototype(cx, fun, id))
             return false;
 
         *resolvedp = true;
         return true;
     }
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -232,16 +232,17 @@ class JSFunction : public js::NativeObje
         return isLambda() && displayAtom() && !hasCompileTimeName() && !hasGuessedAtom();
     }
 
     bool hasLexicalThis() const {
         return isArrow() || nonLazyScript()->isGeneratorExp();
     }
 
     bool isBuiltinFunctionConstructor();
+    bool needsPrototypeProperty();
 
     /* Returns the strictness of this function, which must be interpreted. */
     bool strict() const {
         MOZ_ASSERT(isInterpreted());
         return isInterpretedLazy() ? lazyScript()->strict() : nonLazyScript()->strict();
     }
 
     void setFlags(uint16_t flags) {
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -863,12 +863,15 @@ skip-if(!xulRuntime.shell) script test26
 skip script test262/language/statements/async-function/early-errors-no-async-generator-n.js
 
 # Need to be rewritten to follow the change in https://github.com/tc39/proposal-async-iteration/pull/92
 skip script test262/language/statements/async-generator/yield-star-async-next.js
 skip script test262/language/statements/async-generator/yield-star-async-return.js
 skip script test262/language/statements/async-generator/yield-star-async-throw.js
 
 # https://github.com/tc39/test262/pull/947
+skip script test262/intl402/NumberFormat/11.1.1_32.js
+
+# https://github.com/tc39/test262/pull/947
 skip script test262/intl402/DateTimeFormat/prototype/formatToParts/length.js
 
 # https://github.com/tc39/test262/pull/947
 skip script test262/intl402/PluralRules/this-not-ignored.js
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -10988,18 +10988,20 @@ PresShell::AddSizeOfIncludingThis(Malloc
     *aPresShellSize += mCaret->SizeOfIncludingThis(aMallocSizeOf);
   }
   *aPresShellSize += mApproximatelyVisibleFrames.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += mFramesToDirty.ShallowSizeOfExcludingThis(aMallocSizeOf);
   *aPresShellSize += aArenaObjectsSize->mOther;
 
   if (nsStyleSet* styleSet = StyleSet()->GetAsGecko()) {
     *aStyleSetsSize += styleSet->SizeOfIncludingThis(aMallocSizeOf);
+  } else if (ServoStyleSet* styleSet = StyleSet()->GetAsServo()) {
+    *aStyleSetsSize += styleSet->SizeOfIncludingThis(aMallocSizeOf);
   } else {
-    NS_WARNING("ServoStyleSets do not support memory measurements yet");
+    MOZ_CRASH();
   }
 
   *aTextRunsSize += SizeOfTextRuns(aMallocSizeOf);
 
   *aPresContextSize += mPresContext->SizeOfIncludingThis(aMallocSizeOf);
 }
 
 size_t
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2089,43 +2089,23 @@ nsPresContext::MediaFeatureValuesChanged
   // (in HTML5 terms), although we also want to notify them on certain
   // flushes.  (We're already running off an event.)
   //
   // Note that we do this after the new style from media queries in
   // style sheets has been computed.
 
   if (!PR_CLIST_IS_EMPTY(mDocument->MediaQueryLists())) {
     // We build a list of all the notifications we're going to send
-    // before we send any of them.  (The spec says the notifications
-    // should be a queued task, so any removals that happen during the
-    // notifications shouldn't affect what gets notified.)  Furthermore,
-    // we hold strong pointers to everything we're going to make
-    // notification calls to, since each notification involves calling
-    // arbitrary script that might otherwise destroy these objects, or,
-    // for that matter, |this|.
-    //
-    // Note that we intentionally send the notifications to media query
-    // list in the order they were created and, for each list, to the
-    // listeners in the order added.
-    nsTArray<MediaQueryList::HandleChangeData> notifyList;
+    // before we send any of them.
     for (PRCList *l = PR_LIST_HEAD(mDocument->MediaQueryLists());
          l != mDocument->MediaQueryLists(); l = PR_NEXT_LINK(l)) {
+      nsAutoMicroTask mt;
       MediaQueryList *mql = static_cast<MediaQueryList*>(l);
-      mql->MediumFeaturesChanged(notifyList);
+      mql->MaybeNotify();
     }
-
-    if (!notifyList.IsEmpty()) {
-      for (uint32_t i = 0, i_end = notifyList.Length(); i != i_end; ++i) {
-        nsAutoMicroTask mt;
-        MediaQueryList::HandleChangeData &d = notifyList[i];
-        d.callback->Call(*d.mql);
-      }
-    }
-
-    // NOTE:  When |notifyList| goes out of scope, our destructor could run.
   }
 }
 
 void
 nsPresContext::PostMediaFeatureValuesChangedEvent()
 {
   // FIXME: We should probably replace this event with use of
   // nsRefreshDriver::AddStyleFlushObserver (except the pres shell would
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -9971,25 +9971,21 @@ nsFrame::BoxReflow(nsBoxLayoutState&    
 
     nsSize parentSize(aWidth, aHeight);
     if (parentSize.height != NS_INTRINSICSIZE)
       parentSize.height += margin.TopBottom();
     if (parentSize.width != NS_INTRINSICSIZE)
       parentSize.width += margin.LeftRight();
 
     nsIFrame *parentFrame = GetParent();
-    nsFrameState savedState = parentFrame->GetStateBits();
     WritingMode parentWM = parentFrame->GetWritingMode();
     ReflowInput
       parentReflowInput(aPresContext, parentFrame, aRenderingContext,
                         LogicalSize(parentWM, parentSize),
                         ReflowInput::DUMMY_PARENT_REFLOW_STATE);
-    const nsFrameState bitsToLeaveUntouched = NS_FRAME_HAS_PROPERTIES;
-    parentFrame->RemoveStateBits(~bitsToLeaveUntouched);
-    parentFrame->AddStateBits(savedState & ~bitsToLeaveUntouched);
 
     // This may not do very much useful, but it's probably worth trying.
     if (parentSize.width != NS_INTRINSICSIZE)
       parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
     if (parentSize.height != NS_INTRINSICSIZE)
       parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
     parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
     // XXX use box methods
--- a/layout/style/CSSStyleSheet.cpp
+++ b/layout/style/CSSStyleSheet.cpp
@@ -187,19 +187,21 @@ CSSStyleSheet::SizeOfIncludingThis(Mallo
     if (s->Inner()->mSheets.LastElement() == s) {
       n += s->Inner()->SizeOfIncludingThis(aMallocSizeOf);
     }
 
     // Measurement of the following members may be added later if DMD finds it
     // is worthwhile:
     // - s->mRuleCollection
     // - s->mRuleProcessors
+    // - s->mStyleSets
     //
     // The following members are not measured:
     // - s->mOwnerRule, because it's non-owning
+    // - s->mScopeElement, because it's non-owning
 
     s = s->mNext ? s->mNext->AsGecko() : nullptr;
   }
   return n;
 }
 
 CSSStyleSheetInner::CSSStyleSheetInner(CSSStyleSheetInner& aCopy,
                                        CSSStyleSheet* aPrimarySheet)
--- a/layout/style/MediaQueryList.cpp
+++ b/layout/style/MediaQueryList.cpp
@@ -2,69 +2,71 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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/. */
 
 /* implements DOM interface for querying and observing media queries */
 
 #include "mozilla/dom/MediaQueryList.h"
+#include "mozilla/dom/MediaQueryListEvent.h"
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/EventTargetBinding.h"
 #include "nsPresContext.h"
 #include "nsMediaList.h"
 #include "nsCSSParser.h"
 #include "nsIDocument.h"
 
+#define ONCHANGE_STRING NS_LITERAL_STRING("change")
+
 namespace mozilla {
 namespace dom {
 
 MediaQueryList::MediaQueryList(nsIDocument *aDocument,
                                const nsAString &aMediaQueryList)
-  : mDocument(aDocument),
-    mMediaList(new nsMediaList),
-    mMatchesValid(false)
+  : mDocument(aDocument)
+  , mMediaList(new nsMediaList)
+  , mMatchesValid(false)
+  , mIsKeptAlive(false)
 {
   PR_INIT_CLIST(this);
 
   nsCSSParser parser;
   parser.ParseMediaList(aMediaQueryList, nullptr, 0, mMediaList);
 }
 
 MediaQueryList::~MediaQueryList()
 {
   if (mDocument) {
     PR_REMOVE_LINK(this);
   }
 }
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaQueryList)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaQueryList)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaQueryList,
+                                                  DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCallbacks)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaQueryList)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaQueryList,
+                                                DOMEventTargetHelper)
   if (tmp->mDocument) {
     PR_REMOVE_LINK(tmp);
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
   }
-  tmp->RemoveAllListeners();
+  tmp->Disconnect();
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(MediaQueryList)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MediaQueryList)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
-NS_INTERFACE_MAP_BEGIN(MediaQueryList)
-  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-  NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(MediaQueryList)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaQueryList)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaQueryList)
+NS_IMPL_ADDREF_INHERITED(MediaQueryList, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(MediaQueryList, DOMEventTargetHelper)
 
 void
 MediaQueryList::GetMedia(nsAString &aMedia)
 {
   mMediaList->GetText(aMedia);
 }
 
 bool
@@ -75,67 +77,134 @@ MediaQueryList::Matches()
                "when listeners present, must keep mMatches current");
     RecomputeMatches();
   }
 
   return mMatches;
 }
 
 void
-MediaQueryList::AddListener(MediaQueryListListener& aListener)
+MediaQueryList::AddListener(EventListener* aListener, ErrorResult& aRv)
 {
-  if (!HasListeners()) {
-    // When we have listeners, the pres context owns a reference to
-    // this.  This is a cyclic reference that can only be broken by
-    // cycle collection.
-    NS_ADDREF_THIS();
+  if (!aListener) {
+    return;
   }
 
+  AddEventListenerOptionsOrBoolean options;
+  options.SetAsBoolean() = false;
+
+  AddEventListener(ONCHANGE_STRING, aListener, options, false, aRv);
+}
+
+void
+MediaQueryList::AddEventListener(const nsAString& aType,
+                                 EventListener* aCallback,
+                                 const AddEventListenerOptionsOrBoolean& aOptions,
+                                 const dom::Nullable<bool>& aWantsUntrusted,
+                                 ErrorResult& aRv)
+{
   if (!mMatchesValid) {
     MOZ_ASSERT(!HasListeners(),
                "when listeners present, must keep mMatches current");
     RecomputeMatches();
   }
 
-  for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
-    if (aListener == *mCallbacks[i]) {
-      // Already registered
-      return;
-    }
+  DOMEventTargetHelper::AddEventListener(aType, aCallback, aOptions,
+                                         aWantsUntrusted, aRv);
+
+  if (aRv.Failed()) {
+    return;
   }
 
-  if (!mCallbacks.AppendElement(&aListener, fallible)) {
-    if (!HasListeners()) {
-      // Append failed; undo the AddRef above.
-      NS_RELEASE_THIS();
-    }
+  UpdateMustKeepAlive();
+}
+
+void
+MediaQueryList::RemoveListener(EventListener* aListener, ErrorResult& aRv)
+{
+  if (!aListener) {
+    return;
   }
+
+  EventListenerOptionsOrBoolean options;
+  options.SetAsBoolean() = false;
+
+  RemoveEventListener(ONCHANGE_STRING, aListener, options, aRv);
 }
 
 void
-MediaQueryList::RemoveListener(MediaQueryListListener& aListener)
+MediaQueryList::RemoveEventListener(const nsAString& aType,
+                                    EventListener* aCallback,
+                                    const EventListenerOptionsOrBoolean& aOptions,
+                                    ErrorResult& aRv)
+{
+  DOMEventTargetHelper::RemoveEventListener(aType, aCallback, aOptions, aRv);
+
+  if (aRv.Failed()) {
+    return;
+  }
+
+  UpdateMustKeepAlive();
+}
+
+EventHandlerNonNull*
+MediaQueryList::GetOnchange()
+{
+  if (NS_IsMainThread()) {
+    return GetEventHandler(nsGkAtoms::onchange, EmptyString());
+  }
+  return GetEventHandler(nullptr, ONCHANGE_STRING);
+}
+
+void
+MediaQueryList::SetOnchange(EventHandlerNonNull* aCallback)
 {
-  for (uint32_t i = 0; i < mCallbacks.Length(); ++i) {
-    if (aListener == *mCallbacks[i]) {
-      mCallbacks.RemoveElementAt(i);
-      if (!HasListeners()) {
-        // See NS_ADDREF_THIS() in AddListener.
-        NS_RELEASE_THIS();
-      }
-      break;
-    }
+  if (NS_IsMainThread()) {
+    SetEventHandler(nsGkAtoms::onchange, EmptyString(), aCallback);
+  } else {
+    SetEventHandler(nullptr, ONCHANGE_STRING, aCallback);
+  }
+
+  UpdateMustKeepAlive();
+}
+
+void
+MediaQueryList::UpdateMustKeepAlive()
+{
+  bool toKeepAlive = HasListeners();
+  if (toKeepAlive == mIsKeptAlive) {
+    return;
+  }
+
+  // When we have listeners, the pres context owns a reference to
+  // this.  This is a cyclic reference that can only be broken by
+  // cycle collection.
+
+  mIsKeptAlive = toKeepAlive;
+
+  if (toKeepAlive) {
+    NS_ADDREF_THIS();
+  } else {
+    NS_RELEASE_THIS();
   }
 }
 
-void
-MediaQueryList::RemoveAllListeners()
+bool
+MediaQueryList::HasListeners()
 {
-  bool hadListeners = HasListeners();
-  mCallbacks.Clear();
-  if (hadListeners) {
+  return HasListenersFor(ONCHANGE_STRING);
+}
+
+void
+MediaQueryList::Disconnect()
+{
+  DisconnectFromOwner();
+
+  if (mIsKeptAlive) {
+    mIsKeptAlive = false;
     // See NS_ADDREF_THIS() in AddListener.
     NS_RELEASE_THIS();
   }
 }
 
 void
 MediaQueryList::RecomputeMatches()
 {
@@ -164,43 +233,53 @@ MediaQueryList::RecomputeMatches()
     // XXXbz What's the right behavior here?  Spec doesn't say.
     return;
   }
 
   mMatches = mMediaList->Matches(presContext, nullptr);
   mMatchesValid = true;
 }
 
-void
-MediaQueryList::MediumFeaturesChanged(
-    nsTArray<HandleChangeData>& aListenersToNotify)
-{
-  mMatchesValid = false;
-
-  if (HasListeners()) {
-    bool oldMatches = mMatches;
-    RecomputeMatches();
-    if (mMatches != oldMatches) {
-      for (uint32_t i = 0, i_end = mCallbacks.Length(); i != i_end; ++i) {
-        HandleChangeData *d = aListenersToNotify.AppendElement(fallible);
-        if (d) {
-          d->mql = this;
-          d->callback = mCallbacks[i];
-        }
-      }
-    }
-  }
-}
-
 nsISupports*
 MediaQueryList::GetParentObject() const
 {
   return mDocument;
 }
 
 JSObject*
 MediaQueryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return MediaQueryListBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+MediaQueryList::MaybeNotify()
+{
+  mMatchesValid = false;
+
+  if (!HasListeners()) {
+    return;
+  }
+
+  bool oldMatches = mMatches;
+  RecomputeMatches();
+
+  // No need to notify the change.
+  if (mMatches == oldMatches) {
+    return;
+  }
+
+  MediaQueryListEventInit init;
+  init.mBubbles = false;
+  init.mCancelable = false;
+  init.mMatches = mMatches;
+  mMediaList->GetText(init.mMedia);
+
+  RefPtr<MediaQueryListEvent> event =
+    MediaQueryListEvent::Constructor(this, ONCHANGE_STRING, init);
+  event->SetTrusted(true);
+
+  bool dummy;
+  DispatchEvent(event, &dummy);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/layout/style/MediaQueryList.h
+++ b/layout/style/MediaQueryList.h
@@ -11,65 +11,77 @@
 
 #include "nsISupports.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "prclist.h"
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
+#include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/MediaQueryListBinding.h"
 
 class nsIDocument;
 class nsMediaList;
 
 namespace mozilla {
 namespace dom {
 
-class MediaQueryList final : public nsISupports,
-                             public nsWrapperCache,
+class MediaQueryList final : public DOMEventTargetHelper,
                              public PRCList
 {
 public:
   // The caller who constructs is responsible for calling Evaluate
   // before calling any other methods.
   MediaQueryList(nsIDocument *aDocument,
                  const nsAString &aMediaQueryList);
 private:
   ~MediaQueryList();
 
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaQueryList)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaQueryList, DOMEventTargetHelper)
 
   nsISupports* GetParentObject() const;
 
-  struct HandleChangeData {
-    RefPtr<MediaQueryList> mql;
-    RefPtr<mozilla::dom::MediaQueryListListener> callback;
-  };
-
-  // Appends listeners that need notification to aListenersToNotify
-  void MediumFeaturesChanged(nsTArray<HandleChangeData>& aListenersToNotify);
-
-  bool HasListeners() const { return !mCallbacks.IsEmpty(); }
-
-  void RemoveAllListeners();
+  void MaybeNotify();
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL methods
   void GetMedia(nsAString& aMedia);
   bool Matches();
-  void AddListener(mozilla::dom::MediaQueryListListener& aListener);
-  void RemoveListener(mozilla::dom::MediaQueryListListener& aListener);
+  void AddListener(EventListener* aListener, ErrorResult& aRv);
+  void RemoveListener(EventListener* aListener, ErrorResult& aRv);
+
+  EventHandlerNonNull* GetOnchange();
+  void SetOnchange(EventHandlerNonNull* aCallback);
+
+  using nsIDOMEventTarget::AddEventListener;
+  using nsIDOMEventTarget::RemoveEventListener;
+
+  virtual void AddEventListener(const nsAString& aType,
+                                EventListener* aCallback,
+                                const AddEventListenerOptionsOrBoolean& aOptions,
+                                const Nullable<bool>& aWantsUntrusted,
+                                ErrorResult& aRv) override;
+  virtual void RemoveEventListener(const nsAString& aType,
+                                   EventListener* aCallback,
+                                   const EventListenerOptionsOrBoolean& aOptions,
+                                   ErrorResult& aRv) override;
+
+  bool HasListeners();
+
+  void Disconnect();
 
 private:
   void RecomputeMatches();
 
+  void UpdateMustKeepAlive();
+
   // We only need a pointer to the document to support lazy
   // reevaluation following dynamic changes.  However, this lazy
   // reevaluation is perhaps somewhat important, since some usage
   // patterns may involve the creation of large numbers of
   // MediaQueryList objects which almost immediately become garbage
   // (after a single call to the .matches getter).
   //
   // This pointer does make us a little more dependent on cycle
@@ -79,15 +91,15 @@ private:
   // after cycle collection unlinking.  Having a non-null mDocument
   // is equivalent to being in that document's mDOMMediaQueryLists
   // linked list.
   nsCOMPtr<nsIDocument> mDocument;
 
   RefPtr<nsMediaList> mMediaList;
   bool mMatches;
   bool mMatchesValid;
-  nsTArray<RefPtr<mozilla::dom::MediaQueryListListener>> mCallbacks;
+  bool mIsKeptAlive;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* !defined(mozilla_dom_MediaQueryList_h) */
--- a/layout/style/ServoStyleRule.cpp
+++ b/layout/style/ServoStyleRule.cpp
@@ -166,18 +166,24 @@ ServoStyleRule::Clone() const
   // anything, so this method should never be called.
   MOZ_ASSERT_UNREACHABLE("Shouldn't be cloning ServoStyleRule");
   return nullptr;
 }
 
 size_t
 ServoStyleRule::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
 {
-  // TODO Implement this!
-  return aMallocSizeOf(this);
+  size_t n = aMallocSizeOf(this);
+
+  // Measurement of the following members may be added later if DMD finds it is
+  // worthwhile:
+  // - mRawRule
+  // - mDecls
+
+  return n;
 }
 
 #ifdef DEBUG
 void
 ServoStyleRule::List(FILE* out, int32_t aIndent) const
 {
   nsAutoCString str;
   for (int32_t i = 0; i < aIndent; i++) {
--- a/layout/style/ServoStyleSet.cpp
+++ b/layout/style/ServoStyleSet.cpp
@@ -99,16 +99,33 @@ void
 ServoStyleSet::Shutdown()
 {
   // Make sure we drop our cached style contexts before the presshell arena
   // starts going away.
   ClearNonInheritingStyleContexts();
   mRawSet = nullptr;
 }
 
+size_t
+ServoStyleSet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+
+  // Measurement of the following members may be added later if DMD finds it is
+  // worthwhile:
+  // - mRawSet
+  // - mSheets
+  // - mNonInheritingStyleContexts
+  //
+  // The following members are not measured:
+  // - mPresContext, because it a non-owning pointer
+
+  return n;
+}
+
 bool
 ServoStyleSet::GetAuthorStyleDisabled() const
 {
   return false;
 }
 
 nsresult
 ServoStyleSet::SetAuthorStyleDisabled(bool aStyleDisabled)
--- a/layout/style/ServoStyleSet.h
+++ b/layout/style/ServoStyleSet.h
@@ -87,16 +87,18 @@ public:
 
   ServoStyleSet();
   ~ServoStyleSet();
 
   void Init(nsPresContext* aPresContext);
   void BeginShutdown();
   void Shutdown();
 
+  size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
+
   bool GetAuthorStyleDisabled() const;
   nsresult SetAuthorStyleDisabled(bool aStyleDisabled);
 
   void BeginUpdate();
   nsresult EndUpdate();
 
   already_AddRefed<nsStyleContext>
   ResolveStyleFor(dom::Element* aElement,
--- a/layout/style/ServoStyleSheet.cpp
+++ b/layout/style/ServoStyleSheet.cpp
@@ -25,16 +25,26 @@ namespace mozilla {
 
 ServoStyleSheetInner::ServoStyleSheetInner(CORSMode aCORSMode,
                                            ReferrerPolicy aReferrerPolicy,
                                            const SRIMetadata& aIntegrity)
   : StyleSheetInfo(aCORSMode, aReferrerPolicy, aIntegrity)
 {
 }
 
+size_t
+ServoStyleSheetInner::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = aMallocSizeOf(this);
+
+  // XXX: need to measure mSheet
+
+  return n;
+}
+
 ServoStyleSheet::ServoStyleSheet(css::SheetParsingMode aParsingMode,
                                  CORSMode aCORSMode,
                                  net::ReferrerPolicy aReferrerPolicy,
                                  const dom::SRIMetadata& aIntegrity)
   : StyleSheet(StyleBackendType::Servo, aParsingMode)
 {
   mInner = new ServoStyleSheetInner(aCORSMode, aReferrerPolicy, aIntegrity);
   mInner->AddSheet(this);
@@ -228,9 +238,30 @@ ServoStyleSheet::InsertRuleIntoGroupInte
                                              css::GroupRule* aGroup,
                                              uint32_t aIndex)
 {
   auto rules = static_cast<ServoCSSRuleList*>(aGroup->CssRules());
   MOZ_ASSERT(rules->GetParentRule() == aGroup);
   return rules->InsertRule(aRule, aIndex);
 }
 
+size_t
+ServoStyleSheet::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
+{
+  size_t n = StyleSheet::SizeOfIncludingThis(aMallocSizeOf);
+  const ServoStyleSheet* s = this;
+  while (s) {
+    // See the comment in CSSStyleSheet::SizeOfIncludingThis() for an
+    // explanation of this.
+    if (s->Inner()->mSheets.LastElement() == s) {
+      n += s->Inner()->SizeOfIncludingThis(aMallocSizeOf);
+    }
+
+    // Measurement of the following members may be added later if DMD finds it
+    // is worthwhile:
+    // - s->mRuleList
+
+    s = s->mNext ? s->mNext->AsServo() : nullptr;
+  }
+  return n;
+}
+
 } // namespace mozilla
--- a/layout/style/ServoStyleSheet.h
+++ b/layout/style/ServoStyleSheet.h
@@ -28,16 +28,18 @@ class Loader;
 //
 
 struct ServoStyleSheetInner : public StyleSheetInfo
 {
   ServoStyleSheetInner(CORSMode aCORSMode,
                        ReferrerPolicy aReferrerPolicy,
                        const dom::SRIMetadata& aIntegrity);
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
+
   RefPtr<const RawServoStyleSheet> mSheet;
   // XXX StyleSheetInfo already has mSheetURI, mBaseURI, and mPrincipal.
   // Can we somehow replace them with URLExtraData directly? The issue
   // is currently URLExtraData is immutable, but URIs in StyleSheetInfo
   // seems to be mutable, so we probably cannot set them altogether.
   // Also, this is mostly a duplicate reference of the same url data
   // inside RawServoStyleSheet. We may want to just use that instead.
   RefPtr<URLExtraData> mURLData;
@@ -118,16 +120,18 @@ protected:
                               uint32_t aIndex, ErrorResult& aRv);
   void DeleteRuleInternal(uint32_t aIndex, ErrorResult& aRv);
   nsresult InsertRuleIntoGroupInternal(const nsAString& aRule,
                                        css::GroupRule* aGroup,
                                        uint32_t aIndex);
 
   void EnabledStateChangedInternal() {}
 
+  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
+
 private:
   ServoStyleSheet(const ServoStyleSheet& aCopy,
                   ServoStyleSheet* aParentToUse,
                   css::ImportRule* aOwnerRuleToUse,
                   nsIDocument* aDocumentToUse,
                   nsINode* aOwningNodeToUse);
 
   void DropRuleList();
--- a/layout/style/test/test_media_query_list.html
+++ b/layout/style/test/test_media_query_list.html
@@ -36,24 +36,28 @@ function run() {
   iframe.style.height = h + "px";
   subroot.offsetWidth; // flush layout
 
   function setup_mql(str) {
     var obj = {
       str: str,
       mql: subwin.matchMedia(str),
       notifyCount: 0,
-      listener: function(mql) {
-                  is(mql, obj.mql,
+      listener: function(event) {
+                  ok(event instanceof MediaQueryListEvent,
                      "correct argument to listener: " + obj.str);
+                  is(event.media, obj.mql.media,
+                     "correct media in the event: " + obj.str);
+                  is(event.target, obj.mql,
+                     "correct target in the event: " + obj.str);
                   ++obj.notifyCount;
                   // Test the last match result only on odd
                   // notifications.
                   if (obj.notifyCount & 1) {
-                    obj.lastOddMatchResult = mql.matches;
+                    obj.lastOddMatchResult = event.target.matches;
                   }
                 }
     }
     obj.mql.addListener(obj.listener);
     return obj;
   }
 
   function finish_mql(obj) {
@@ -146,23 +150,23 @@ function run() {
   finish_mql(w_min_10em);
   finish_mql(w_max_9em);
   finish_mql(w_max_10em);
 
   // Additional tests of listener mutation.
   (function() {
     var received = [];
     var received_mql = [];
-    function listener1(mql) {
+    function listener1(event) {
       received.push(1);
-      received_mql.push(mql);
+      received_mql.push(event.target);
     }
-    function listener2(mql) {
+    function listener2(event) {
       received.push(2);
-      received_mql.push(mql);
+      received_mql.push(event.target);
     }
 
     iframe.style.width = "200px";
     subroot.offsetWidth; // flush layout
 
     var mql = subwin.matchMedia("(min-width: 150px)");
     mql.addListener(listener1);
     mql.addListener(listener1);
@@ -219,78 +223,52 @@ function run() {
        "notification of lists in order created");
     is(received_mql[1], mql,
        "notification of lists in order created");
     is(received_mql[2], mql2,
        "notification of lists in order created");
     received = [];
     received_mql = [];
 
-    function removing_listener(mql) {
+    function removing_listener(event) {
       received.push(3);
-      received_mql.push(mql);
-      mql.removeListener(listener2);
+      received_mql.push(event.target);
+      event.target.removeListener(listener2);
       mql2.removeListener(listener1);
     }
 
     mql.addListener(removing_listener);
     mql.removeListener(listener2);
     mql.addListener(listener2); // after removing_listener (3)
 
     iframe.style.width = "100px";
     subroot.offsetWidth; // flush layout
 
-    // mql(1, 3, 2) mql2(1)
-    is(JSON.stringify(received), "[1,3,2,1]",
+    // mql(1, 3)
+    is(JSON.stringify(received), "[1,3]",
        "listeners still notified after removed if change was before");
     is(received_mql[0], mql,
        "notification order (removal tests)");
     is(received_mql[1], mql,
        "notification order (removal tests)");
-    is(received_mql[2], mql,
-       "notification order (removal tests)");
-    is(received_mql[3], mql2,
-       "notification order (removal tests)");
     received = [];
     received_mql = [];
 
     iframe.style.width = "200px";
     subroot.offsetWidth; // flush layout
 
     // mql(1, 3)
     is(JSON.stringify(received), "[1,3]",
        "listeners not notified for changes after their removal");
     is(received_mql[0], mql,
        "notification order (removal tests)");
     is(received_mql[1], mql,
        "notification order (removal tests)");
   })();
 
-  /* Bug 716751: null-dereference crash */
-  (function() {
-    iframe.style.width = "200px";
-    subroot.offsetWidth; // flush layout
-
-    var mql = subwin.matchMedia("(min-width: 150px)");
-    SimpleTest.doesThrow(function() {
-      mql.addListener(null);
-    }, "expected an exception");
-
-    iframe.style.width = "100px";
-    subroot.offsetWidth; // flush layout
-    // With the bug, we crash here.  No need for test assertions.
-
-    SimpleTest.doesThrow(function() {
-      mql.removeListener(null);
-    }, "expected an exception");
-    SimpleTest.doesThrow(function() {
-      mql.removeListener(null);
-    }, "expected an exception");
-  })();
-
   /* Bug 753777: test that things work in a freshly-created iframe */
   (function() {
     var iframe = document.createElement("iframe");
     document.body.appendChild(iframe);
 
     is(iframe.contentWindow.matchMedia("(min-width: 1px)").matches, true,
        "(min-width: 1px) should match in newly-created iframe");
     is(iframe.contentWindow.matchMedia("(max-width: 1px)").matches, false,
@@ -298,17 +276,17 @@ function run() {
 
     document.body.removeChild(iframe);
   })();
 
   /* Bug 716751: listeners lost due to GC */
   var gc_received = [];
   (function() {
     var received = [];
-    var listener1 = function(mql) {
+    var listener1 = function(event) {
       gc_received.push(1);
     }
 
     iframe.style.width = "200px";
     subroot.offsetWidth; // flush layout
 
     var mql = subwin.matchMedia("(min-width: 150px)");
     mql.addListener(listener1);
@@ -332,17 +310,17 @@ function run() {
 
     is(JSON.stringify(gc_received), "[1,1]", "GC test: after notification 2");
 
     bug1270626();
   }
 
   /* Bug 1270626: listeners that throw exceptions */
   function bug1270626() {
-    var throwingListener = function(mql) {
+    var throwingListener = function(event) {
       throw "error";
     }
 
     iframe.style.width = "200px";
     subroot.offsetWidth; // flush layout
 
     var mql = subwin.matchMedia("(min-width: 150px)");
     mql.addListener(throwingListener);
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -2302,29 +2302,27 @@ static int
 audiounit_setup_stream(cubeb_stream * stm)
 {
   stm->mutex.assert_current_thread_owns();
 
   int r = 0;
 
   AudioDeviceID in_dev = stm->input_device;
   AudioDeviceID out_dev = stm->output_device;
-  if (has_input(stm) && has_output(stm) &&
-      !getenv("CUBEB_AUDIOUNIT_DISABLE_AGGREGATE_DEVICE")) {
+  if (has_input(stm) && has_output(stm)) {
     r = audiounit_create_aggregate_device(stm);
     if (r != CUBEB_OK) {
       stm->aggregate_device_id = 0;
       LOG("(%p) Create aggregate devices failed.", stm);
       // !!!NOTE: It is not necessary to return here. If it does not
       // return it will fallback to the old implementation. The intention
       // is to investigate how often it fails. I plan to remove
       // it after a couple of weeks.
       return r;
     } else {
-      LOG("(%p) Using aggregate device", stm);
       in_dev = out_dev = stm->aggregate_device_id;
     }
   }
 
   if (has_input(stm)) {
     r = audiounit_create_unit(&stm->input_unit,
                               INPUT,
                               in_dev);
--- a/media/libcubeb/src/cubeb_resampler.cpp
+++ b/media/libcubeb/src/cubeb_resampler.cpp
@@ -13,17 +13,16 @@
 #include <cassert>
 #include <cstring>
 #include <cstddef>
 #include <cstdio>
 #include "cubeb_resampler.h"
 #include "cubeb-speex-resampler.h"
 #include "cubeb_resampler_internal.h"
 #include "cubeb_utils.h"
-#include "cubeb_log.h"
 
 int
 to_speex_quality(cubeb_resampler_quality q)
 {
   switch(q) {
   case CUBEB_RESAMPLER_QUALITY_VOIP:
     return SPEEX_RESAMPLER_QUALITY_VOIP;
   case CUBEB_RESAMPLER_QUALITY_DEFAULT:
@@ -71,19 +70,16 @@ long passthrough_resampler<T>::fill(void
   long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
                           output_buffer, output_frames);
 
   if (input_buffer) {
     internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
     *input_frames_count = output_frames;
   }
 
-  ALOGV("passthrough: after callback, internal input buffer length: %zu",
-        internal_input_buffer.length());
-
   return rv;
 }
 
 template<typename T, typename InputProcessor, typename OutputProcessor>
 cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
   ::cubeb_resampler_speex(InputProcessor * input_processor,
                           OutputProcessor * output_processor,
                           cubeb_stream * s,
@@ -235,26 +231,16 @@ cubeb_resampler_speex<T, InputProcessor,
   } else {
     resampled_input = nullptr;
   }
 
   got = data_callback(stream, user_ptr,
                       resampled_input, out_unprocessed,
                       output_frames_before_processing);
 
-  size_t input_processor_buffer_sizes[2];
-  size_t output_processor_buffer_sizes[2];
-  input_processor->internal_buffer_sizes(input_processor_buffer_sizes);
-  output_processor->internal_buffer_sizes(output_processor_buffer_sizes);
-  ALOGV("duplex resampler: after callback, resampling buffer state:"
-        "input_processor(input: %zu, output: %zu) "
-        "output_processor(input: %zu, output: %zu) ",
-        input_processor_buffer_sizes[0], input_processor_buffer_sizes[1],
-        output_processor_buffer_sizes[0], output_processor_buffer_sizes[1]);
-
   if (got < 0) {
     return got;
   }
 
   output_processor->written(got);
 
   /* Process the output. If not enough frames have been returned from the
    * callback, drain the processors. */
--- a/media/libcubeb/src/cubeb_resampler_internal.h
+++ b/media/libcubeb/src/cubeb_resampler_internal.h
@@ -174,22 +174,16 @@ public:
   }
 
   /** Destructor, deallocate the resampler */
   virtual ~cubeb_resampler_speex_one_way()
   {
     speex_resampler_destroy(speex_resampler);
   }
 
-  void internal_buffer_sizes(size_t buf_sizes[2])
-  {
-    buf_sizes[0] = resampling_in_buffer.length();
-    buf_sizes[1] = resampling_out_buffer.length();
-  }
-
   /** Sometimes, it is necessary to add latency on one way of a two-way
    * resampler so that the stream are synchronized. This must be called only on
    * a fresh resampler, otherwise, silent samples will be inserted in the
    * stream.
    * @param frames the number of frames of latency to add. */
   void add_latency(size_t frames)
   {
     additional_latency += frames;
@@ -367,21 +361,16 @@ public:
   }
   /* Add some latency to the delay line.
    * @param frames the number of frames of latency to add. */
   void add_latency(size_t frames)
   {
     length += frames;
     delay_input_buffer.push_silence(frames_to_samples(frames));
   }
-  void internal_buffer_sizes(size_t buf_sizes[2])
-  {
-    buf_sizes[0] = delay_input_buffer.length();
-    buf_sizes[1] = delay_output_buffer.length();
-  }
   /** Push some frames into the delay line.
    * @parameter buffer the frames to push.
    * @parameter frame_count the number of frames in #buffer. */
   void input(T * buffer, uint32_t frame_count)
   {
     delay_input_buffer.push(buffer, frames_to_samples(frame_count));
   }
   /** Pop some frames from the internal buffer, into a internal output buffer.
deleted file mode 100644
--- a/media/libcubeb/temp-patch-debug-drift.patch
+++ /dev/null
@@ -1,150 +0,0 @@
-diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp
---- a/media/libcubeb/src/cubeb_audiounit.cpp
-+++ b/media/libcubeb/src/cubeb_audiounit.cpp
-@@ -2302,27 +2302,29 @@ static int
- audiounit_setup_stream(cubeb_stream * stm)
- {
-   stm->mutex.assert_current_thread_owns();
- 
-   int r = 0;
- 
-   AudioDeviceID in_dev = stm->input_device;
-   AudioDeviceID out_dev = stm->output_device;
--  if (has_input(stm) && has_output(stm)) {
-+  if (has_input(stm) && has_output(stm) &&
-+      !getenv("CUBEB_AUDIOUNIT_DISABLE_AGGREGATE_DEVICE")) {
-     r = audiounit_create_aggregate_device(stm);
-     if (r != CUBEB_OK) {
-       stm->aggregate_device_id = 0;
-       LOG("(%p) Create aggregate devices failed.", stm);
-       // !!!NOTE: It is not necessary to return here. If it does not
-       // return it will fallback to the old implementation. The intention
-       // is to investigate how often it fails. I plan to remove
-       // it after a couple of weeks.
-       return r;
-     } else {
-+      LOG("(%p) Using aggregate device", stm);
-       in_dev = out_dev = stm->aggregate_device_id;
-     }
-   }
- 
-   if (has_input(stm)) {
-     r = audiounit_create_unit(&stm->input_unit,
-                               INPUT,
-                               in_dev);
-diff --git a/media/libcubeb/src/cubeb_resampler.cpp b/media/libcubeb/src/cubeb_resampler.cpp
---- a/media/libcubeb/src/cubeb_resampler.cpp
-+++ b/media/libcubeb/src/cubeb_resampler.cpp
-@@ -13,16 +13,17 @@
- #include <cassert>
- #include <cstring>
- #include <cstddef>
- #include <cstdio>
- #include "cubeb_resampler.h"
- #include "cubeb-speex-resampler.h"
- #include "cubeb_resampler_internal.h"
- #include "cubeb_utils.h"
-+#include "cubeb_log.h"
- 
- int
- to_speex_quality(cubeb_resampler_quality q)
- {
-   switch(q) {
-   case CUBEB_RESAMPLER_QUALITY_VOIP:
-     return SPEEX_RESAMPLER_QUALITY_VOIP;
-   case CUBEB_RESAMPLER_QUALITY_DEFAULT:
-@@ -70,16 +71,19 @@ long passthrough_resampler<T>::fill(void
-   long rv = data_callback(stream, user_ptr, internal_input_buffer.data(),
-                           output_buffer, output_frames);
- 
-   if (input_buffer) {
-     internal_input_buffer.pop(nullptr, frames_to_samples(output_frames));
-     *input_frames_count = output_frames;
-   }
- 
-+  ALOGV("passthrough: after callback, internal input buffer length: %zu",
-+        internal_input_buffer.length());
-+
-   return rv;
- }
- 
- template<typename T, typename InputProcessor, typename OutputProcessor>
- cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
-   ::cubeb_resampler_speex(InputProcessor * input_processor,
-                           OutputProcessor * output_processor,
-                           cubeb_stream * s,
-@@ -231,16 +235,26 @@ cubeb_resampler_speex<T, InputProcessor,
-   } else {
-     resampled_input = nullptr;
-   }
- 
-   got = data_callback(stream, user_ptr,
-                       resampled_input, out_unprocessed,
-                       output_frames_before_processing);
- 
-+  size_t input_processor_buffer_sizes[2];
-+  size_t output_processor_buffer_sizes[2];
-+  input_processor->internal_buffer_sizes(input_processor_buffer_sizes);
-+  output_processor->internal_buffer_sizes(output_processor_buffer_sizes);
-+  ALOGV("duplex resampler: after callback, resampling buffer state:"
-+        "input_processor(input: %zu, output: %zu) "
-+        "output_processor(input: %zu, output: %zu) ",
-+        input_processor_buffer_sizes[0], input_processor_buffer_sizes[1],
-+        output_processor_buffer_sizes[0], output_processor_buffer_sizes[1]);
-+
-   if (got < 0) {
-     return got;
-   }
- 
-   output_processor->written(got);
- 
-   /* Process the output. If not enough frames have been returned from the
-    * callback, drain the processors. */
-diff --git a/media/libcubeb/src/cubeb_resampler_internal.h b/media/libcubeb/src/cubeb_resampler_internal.h
---- a/media/libcubeb/src/cubeb_resampler_internal.h
-+++ b/media/libcubeb/src/cubeb_resampler_internal.h
-@@ -174,16 +174,22 @@ public:
-   }
- 
-   /** Destructor, deallocate the resampler */
-   virtual ~cubeb_resampler_speex_one_way()
-   {
-     speex_resampler_destroy(speex_resampler);
-   }
- 
-+  void internal_buffer_sizes(size_t buf_sizes[2])
-+  {
-+    buf_sizes[0] = resampling_in_buffer.length();
-+    buf_sizes[1] = resampling_out_buffer.length();
-+  }
-+
-   /** Sometimes, it is necessary to add latency on one way of a two-way
-    * resampler so that the stream are synchronized. This must be called only on
-    * a fresh resampler, otherwise, silent samples will be inserted in the
-    * stream.
-    * @param frames the number of frames of latency to add. */
-   void add_latency(size_t frames)
-   {
-     additional_latency += frames;
-@@ -361,16 +367,21 @@ public:
-   }
-   /* Add some latency to the delay line.
-    * @param frames the number of frames of latency to add. */
-   void add_latency(size_t frames)
-   {
-     length += frames;
-     delay_input_buffer.push_silence(frames_to_samples(frames));
-   }
-+  void internal_buffer_sizes(size_t buf_sizes[2])
-+  {
-+    buf_sizes[0] = delay_input_buffer.length();
-+    buf_sizes[1] = delay_output_buffer.length();
-+  }
-   /** Push some frames into the delay line.
-    * @parameter buffer the frames to push.
-    * @parameter frame_count the number of frames in #buffer. */
-   void input(T * buffer, uint32_t frame_count)
-   {
-     delay_input_buffer.push(buffer, frames_to_samples(frame_count));
-   }
-   /** Pop some frames from the internal buffer, into a internal output buffer.
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -61,11 +61,8 @@ if [ -n "$rev" ]; then
     version=$version-dirty
     echo "WARNING: updating from a dirty git repository."
   fi
   sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\{40\}\(-dirty\)\{0,1\} .\{1,100\}/$version ($date)/" README_MOZILLA
   rm README_MOZILLA.bak
 else
   echo "Remember to update README_MOZILLA with the version details."
 fi
-
-echo "Applying a patch on top of $rev"
-patch -p3 < temp-patch-debug-drift.patch
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -103,16 +103,20 @@ pref("network.http.keep-alive.timeout", 
 pref("network.http.max-connections", 20);
 pref("network.http.max-persistent-connections-per-server", 6);
 pref("network.http.max-persistent-connections-per-proxy", 20);
 
 // spdy
 pref("network.http.spdy.push-allowance", 32768);
 pref("network.http.spdy.default-hpack-buffer", 4096); // 4k
 
+// Racing the cache with the network should be disabled to prevent accidental
+// data usage.
+pref("network.http.rcwn.enabled", false);
+
 // See bug 545869 for details on why these are set the way they are
 pref("network.buffer.cache.count", 24);
 pref("network.buffer.cache.size",  16384);
 
 // predictive actions
 pref("network.predictor.enabled", true);
 pref("network.predictor.max-db-size", 2097152); // bytes
 pref("network.predictor.preserve", 50); // percentage of predictor data to keep when cleaning up
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -4496,16 +4496,19 @@ Tab.prototype = {
       }
     }
 
     // Update the page actions URI for helper apps.
     if (BrowserApp.selectedTab == this) {
       ExternalApps.updatePageActionUri(fixedURI);
     }
 
+    // Strip reader mode URI and also make it exposable if needed
+    fixedURI = this._stripAboutReaderURL(fixedURI);
+
     let message = {
       type: "Content:LocationChange",
       tabID: this.id,
       uri: truncate(fixedURI.spec, MAX_URI_LENGTH),
       userRequested: this.userRequested || "",
       baseDomain: baseDomain,
       contentType: (contentType ? contentType : ""),
       sameDocument: sameDocument,
@@ -4524,18 +4527,25 @@ Tab.prototype = {
       // XXX This code assumes that this is the earliest hook we have at which
       // browser.contentDocument is changed to the new document we're loading
       this.contentDocumentIsDisplayed = false;
       this.hasTouchListener = false;
       Services.obs.notifyObservers(this.browser, "Session:NotifyLocationChange", null);
     }
   },
 
-  _stripAboutReaderURL: function (url) {
-    return ReaderMode.getOriginalUrl(url) || url;
+  _stripAboutReaderURL: function (originalURI) {
+    try {
+      let strippedURL = ReaderMode.getOriginalUrl(originalURI.spec);
+      if(strippedURL){
+        // Continue to create exposable uri if original uri is stripped.
+        originalURI = URIFixup.createExposableURI(Services.io.newURI(strippedURL));
+      }
+    } catch (ex) { }
+    return originalURI;
   },
 
   // Properties used to cache security state used to update the UI
   _state: null,
   _hostChanged: false, // onLocationChange will flip this bit
 
   onSecurityChange: function(aWebProgress, aRequest, aState) {
     // Don't need to do anything if the data we use to update the UI hasn't changed
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1635,16 +1635,24 @@ pref("network.http.enforce-framing.soft"
 // array - behavior as it used to be. If it is true: empty headers coming from
 // the network will exist in header array as empty string. Call SetHeader with
 // an empty value will still delete the header.(Bug 6699259)
 pref("network.http.keep_empty_response_headers_as_empty_string", true);
 
 // Max size, in bytes, for received HTTP response header.
 pref("network.http.max_response_header_size", 393216);
 
+// If we should attempt to race the cache and network
+pref("network.http.rcwn.enabled", false);
+pref("network.http.rcwn.cache_queue_normal_threshold", 50);
+pref("network.http.rcwn.cache_queue_priority_threshold", 10);
+// We might attempt to race the cache with the network only if a resource
+// is smaller than this size.
+pref("network.http.rcwn.small_resource_size_kb", 256);
+
 // The ratio of the transaction count for the focused window and the count of
 // all available active connections.
 pref("network.http.focused_window_transaction_ratio", "0.9");
 
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
--- a/netwerk/cache2/CacheFileContextEvictor.cpp
+++ b/netwerk/cache2/CacheFileContextEvictor.cpp
@@ -608,18 +608,21 @@ CacheFileContextEvictor::EvictEntries()
       // We doom any active handle in CacheFileIOManager::EvictByContext(), so
       // this must be a new one. Skip it.
       LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since we "
            "found an active handle. [handle=%p]", handle.get()));
       continue;
     }
 
     CacheIndex::EntryStatus status;
-    bool pinned;
-    rv = CacheIndex::HasEntry(hash, &status, &pinned);
+    bool pinned = false;
+    auto callback = [&pinned](const CacheIndexEntry * aEntry) {
+      pinned = aEntry->IsPinned();
+    };
+    rv = CacheIndex::HasEntry(hash, &status, callback);
     // This must never fail, since eviction (this code) happens only when the index
     // is up-to-date and thus the informatin is known.
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     if (pinned != mEntries[0]->mPinned) {
       LOG(("CacheFileContextEvictor::EvictEntries() - Skipping entry since pinning "
            "doesn't match [evicting pinned=%d, entry pinned=%d]",
            mEntries[0]->mPinned, pinned));
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -1139,48 +1139,46 @@ CacheIndex::RemoveAll()
     file->Remove(false);
   }
 
   return NS_OK;
 }
 
 // static
 nsresult
-CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval, bool *_pinned)
+CacheIndex::HasEntry(const nsACString &aKey, EntryStatus *_retval,
+                     const std::function<void(const CacheIndexEntry*)> &aCB)
 {
   LOG(("CacheIndex::HasEntry() [key=%s]", PromiseFlatCString(aKey).get()));
 
   SHA1Sum sum;
   SHA1Sum::Hash hash;
   sum.update(aKey.BeginReading(), aKey.Length());
   sum.finish(hash);
 
-  return HasEntry(hash, _retval, _pinned);
+  return HasEntry(hash, _retval, aCB);
 }
 
 // static
 nsresult
-CacheIndex::HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval, bool *_pinned)
+CacheIndex::HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval,
+                     const std::function<void(const CacheIndexEntry*)> &aCB)
 {
   StaticMutexAutoLock lock(sLock);
 
   RefPtr<CacheIndex> index = gInstance;
 
   if (!index) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (!index->IsIndexUsable()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-  if (_pinned) {
-    *_pinned = false;
-  }
-
   const CacheIndexEntry *entry = nullptr;
 
   switch (index->mState) {
     case READING:
     case WRITING:
       entry = index->mPendingUpdates.GetEntry(hash);
       MOZ_FALLTHROUGH;
     case BUILDING:
@@ -1205,18 +1203,18 @@ CacheIndex::HasEntry(const SHA1Sum::Hash
     if (entry->IsRemoved()) {
       if (entry->IsFresh()) {
         *_retval = DOES_NOT_EXIST;
       } else {
         *_retval = DO_NOT_KNOW;
       }
     } else {
       *_retval = EXISTS;
-      if (_pinned && entry->IsPinned()) {
-        *_pinned = true;
+      if (aCB) {
+        aCB(entry);
       }
     }
   }
 
   LOG(("CacheIndex::HasEntry() - result is %u", *_retval));
   return NS_OK;
 }
 
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -722,21 +722,22 @@ public:
   enum EntryStatus {
     EXISTS         = 0,
     DOES_NOT_EXIST = 1,
     DO_NOT_KNOW    = 2
   };
 
   // Returns status of the entry in index for the given key. It can be called
   // on any thread.
-  // If _pinned is non-null, it's filled with pinning status of the entry.
+  // If the optional aCB callback is given, the it will be called with a
+  // CacheIndexEntry only if _retval is EXISTS when the method returns.
   static nsresult HasEntry(const nsACString &aKey, EntryStatus *_retval,
-                           bool *_pinned = nullptr);
+                           const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
   static nsresult HasEntry(const SHA1Sum::Hash &hash, EntryStatus *_retval,
-                           bool *_pinned = nullptr);
+                           const std::function<void(const CacheIndexEntry*)> &aCB = nullptr);
 
   // Returns a hash of the least important entry that should be evicted if the
   // cache size is over limit and also returns a total number of all entries in
   // the index minus the number of forced valid entries and unpinned entries
   // that we encounter when searching (see below)
   static nsresult GetEntryForEviction(bool aIgnoreEmptyEntries, SHA1Sum::Hash *aHash, uint32_t *aCnt);
 
   // Checks if a cache entry is currently forced valid. Used to prevent an entry
--- a/netwerk/cache2/CacheStorage.cpp
+++ b/netwerk/cache2/CacheStorage.cpp
@@ -166,16 +166,43 @@ NS_IMETHODIMP CacheStorage::Exists(nsIUR
   nsAutoCString asciiSpec;
   rv = noRefURI->GetAsciiSpec(asciiSpec);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return CacheStorageService::Self()->CheckStorageEntry(
     this, asciiSpec, aIdExtension, aResult);
 }
 
+nsresult
+CacheStorage::GetCacheIndexEntryAttrs(nsIURI *aURI,
+                                      const nsACString &aIdExtension,
+                                      bool *aHasAltData,
+                                      uint32_t *aSizeInKB)
+{
+  NS_ENSURE_ARG(aURI);
+  NS_ENSURE_ARG(aHasAltData);
+  NS_ENSURE_ARG(aSizeInKB);
+  if (!CacheStorageService::Self()) {
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  nsresult rv;
+
+  nsCOMPtr<nsIURI> noRefURI;
+  rv = aURI->CloneIgnoringRef(getter_AddRefs(noRefURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoCString asciiSpec;
+  rv = noRefURI->GetAsciiSpec(asciiSpec);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return CacheStorageService::Self()->GetCacheIndexEntryAttrs(
+    this, asciiSpec, aIdExtension, aHasAltData, aSizeInKB);
+}
+
 NS_IMETHODIMP CacheStorage::AsyncDoomURI(nsIURI *aURI, const nsACString & aIdExtension,
                                          nsICacheEntryDoomCallback* aCallback)
 {
   if (!CacheStorageService::Self())
     return NS_ERROR_NOT_INITIALIZED;
 
   nsresult rv;
 
--- a/netwerk/cache2/CacheStorage.h
+++ b/netwerk/cache2/CacheStorage.h
@@ -68,14 +68,17 @@ protected:
   bool mPinning : 1;
 
 public:
   nsILoadContextInfo* LoadInfo() const { return mLoadContextInfo; }
   bool WriteToDisk() const { return mWriteToDisk && !mLoadContextInfo->IsPrivate(); }
   bool LookupAppCache() const { return mLookupAppCache; }
   bool SkipSizeCheck() const { return mSkipSizeCheck; }
   bool Pinning() const { return mPinning; }
+  virtual nsresult GetCacheIndexEntryAttrs(
+          nsIURI *aURI, const nsACString &aIdExtension,
+          bool *aHasAltData, uint32_t *aSizeInKB) override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif
--- a/netwerk/cache2/CacheStorageService.cpp
+++ b/netwerk/cache2/CacheStorageService.cpp
@@ -1630,16 +1630,58 @@ CacheStorageService::CheckStorageEntry(C
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   *aResult = status == CacheIndex::EXISTS;
   LOG(("  %sfound in index", *aResult ? "" : "not "));
   return NS_OK;
 }
 
+nsresult
+CacheStorageService::GetCacheIndexEntryAttrs(CacheStorage const* aStorage,
+                                             const nsACString &aURI,
+                                             const nsACString &aIdExtension,
+                                             bool *aHasAltData,
+                                             uint32_t *aFileSizeKb)
+{
+  nsresult rv;
+
+  nsAutoCString contextKey;
+  CacheFileUtils::AppendKeyPrefix(aStorage->LoadInfo(), contextKey);
+
+  LOG(("CacheStorageService::GetCacheIndexEntryAttrs [uri=%s, eid=%s, contextKey=%s]",
+    aURI.BeginReading(), aIdExtension.BeginReading(), contextKey.get()));
+
+  nsAutoCString fileKey;
+  rv = CacheEntry::HashingKey(contextKey, aIdExtension, aURI, fileKey);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  *aHasAltData = false;
+  *aFileSizeKb = 0;
+  auto closure = [&aHasAltData, &aFileSizeKb](const CacheIndexEntry *entry) {
+    *aHasAltData = entry->GetHasAltData();
+    *aFileSizeKb = entry->GetFileSize();
+  };
+
+  CacheIndex::EntryStatus status;
+  rv = CacheIndex::HasEntry(fileKey, &status, closure);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  if (status != CacheIndex::EXISTS) {
+    return NS_ERROR_CACHE_KEY_NOT_FOUND;
+  }
+
+  return NS_OK;
+}
+
+
 namespace {
 
 class CacheEntryDoomByKeyCallback : public CacheFileIOListener
                                   , public nsIRunnable
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIRUNNABLE
--- a/netwerk/cache2/CacheStorageService.h
+++ b/netwerk/cache2/CacheStorageService.h
@@ -106,16 +106,22 @@ public:
                              int64_t aDataSize, int32_t aFetchCount,
                              uint32_t aLastModifiedTime, uint32_t aExpirationTime,
                              bool aPinned, nsILoadContextInfo* aInfo) = 0;
   };
 
   // Invokes OnEntryInfo for the given aEntry, synchronously.
   static void GetCacheEntryInfo(CacheEntry* aEntry, EntryInfoCallback *aVisitor);
 
+  nsresult GetCacheIndexEntryAttrs(CacheStorage const* aStorage,
+                                   const nsACString &aURI,
+                                   const nsACString &aIdExtension,
+                                   bool *aHasAltData,
+                                   uint32_t *aFileSizeKb);
+
   static uint32_t CacheQueueSize(bool highPriority);
 
   // Memory reporting
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
 private:
--- a/netwerk/cache2/nsICacheStorage.idl
+++ b/netwerk/cache2/nsICacheStorage.idl
@@ -126,9 +126,17 @@ interface nsICacheStorage : nsISupports
   void asyncEvictStorage(in nsICacheEntryDoomCallback aCallback);
 
   /**
    * Visits the storage and its entries.
    * NOTE: Disk storage also visits memory storage.
    */
   void asyncVisitStorage(in nsICacheStorageVisitor aVisitor,
                          in boolean aVisitEntries);
+
+  %{C++
+    virtual nsresult GetCacheIndexEntryAttrs(
+          nsIURI *aURI, const nsACString &aIdExtension,
+          bool *aHasAltData, uint32_t *aSizeInKB) {
+      return NS_ERROR_NOT_IMPLEMENTED;
+    }
+  %}
 };
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -114,16 +114,20 @@
 
 namespace mozilla { namespace net {
 
 namespace {
 
 // Monotonically increasing ID for generating unique cache entries per
 // intercepted channel.
 static uint64_t gNumIntercepted = 0;
+static bool sRCWNEnabled = false;
+static uint32_t sRCWNQueueSizeNormal = 50;
+static uint32_t sRCWNQueueSizePriority = 10;
+static uint32_t sRCWNSmallResourceSizeKB = 256;
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 #define RECOVER_FROM_CACHE_FILE_ERROR(result) \
         ((result) == NS_ERROR_FILE_NOT_FOUND || \
@@ -2043,17 +2047,19 @@ nsHttpChannel::ProcessResponse()
         // for Strict-Transport-Security.
     } else {
         // Given a successful connection, process any STS or PKP data that's
         // relevant.
         DebugOnly<nsresult> rv = ProcessSecurityHeaders();
         MOZ_ASSERT(NS_SUCCEEDED(rv), "ProcessSTSHeader failed, continuing load.");
     }
 
-    MOZ_ASSERT(!mCachedContentIsValid);
+    MOZ_ASSERT(!mCachedContentIsValid || mRacingNetAndCache,
+               "We should not be hitting the network if we have valid cached "
+               "content unless we are racing the network and cache");
 
     ProcessSSLInformation();
 
     // notify "http-on-examine-response" observers
     gHttpHandler->OnExamineResponse(this);
 
     return ContinueProcessResponse1();
 }
@@ -3602,16 +3608,29 @@ nsHttpChannel::OpenCacheEntry(bool isHtt
             DebugOnly<bool> exists;
             MOZ_ASSERT(NS_SUCCEEDED(cacheStorage->Exists(openURI, extension, &exists)) && exists,
                        "The entry must exist in the cache after we create it here");
         }
 
         mCacheOpenWithPriority = cacheEntryOpenFlags & nsICacheStorage::OPEN_PRIORITY;
         mCacheQueueSizeWhenOpen = CacheStorageService::CacheQueueSize(mCacheOpenWithPriority);
 
+        bool hasAltData = false;
+        uint32_t sizeInKb = 0;
+        rv = cacheStorage->GetCacheIndexEntryAttrs(openURI, extension,
+                                                   &hasAltData, &sizeInKb);
+
+        // We will attempt to race the network vs the cache if we've found this
+        // entry in the cache index, and it has appropriate
+        // attributes (doesn't have alt-data, and has a small size)
+        if (sRCWNEnabled && mInterceptCache != INTERCEPTED &&
+            NS_SUCCEEDED(rv) && !hasAltData && sizeInKb < sRCWNSmallResourceSizeKB) {
+            MaybeRaceNetworkWithCache();
+        }
+
         if (!mCacheOpenDelay) {
             rv = cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, this);
         } else {
             // We pass `this` explicitly as a parameter due to the raw pointer
             // to refcounted object in lambda analysis.
             mCacheOpenFunc = [openURI, extension, cacheEntryOpenFlags, cacheStorage] (nsHttpChannel* self) -> void {
                 cacheStorage->AsyncOpenURI(openURI, extension, cacheEntryOpenFlags, self);
             };
@@ -5146,17 +5165,17 @@ nsHttpChannel::FinalizeCacheEntry()
 nsresult
 nsHttpChannel::InstallCacheListener(int64_t offset)
 {
     nsresult rv;
 
     LOG(("Preparing to write data into the cache [uri=%s]\n", mSpec.get()));
 
     MOZ_ASSERT(mCacheEntry);
-    MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial);
+    MOZ_ASSERT(mCacheEntryIsWriteOnly || mCachedContentIsPartial || mRacingNetAndCache);
     MOZ_ASSERT(mListener);
 
     nsAutoCString contentEncoding, contentType;
     Unused << mResponseHead->GetHeader(nsHttp::Content_Encoding, contentEncoding);
     mResponseHead->ContentType(contentType);
     // If the content is compressible and the server has not compressed it,
     // mark the cache entry for compression.
     if (contentEncoding.IsEmpty() &&
@@ -5802,16 +5821,25 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     MOZ_ASSERT(NS_IsMainThread());
 
     if (!gHttpHandler->Active()) {
         LOG(("  after HTTP shutdown..."));
         ReleaseListeners();
         return NS_ERROR_NOT_AVAILABLE;
     }
 
+    static bool sRCWNInited = false;
+    if (!sRCWNInited) {
+        sRCWNInited = true;
+        Preferences::AddBoolVarCache(&sRCWNEnabled, "network.http.rcwn.enabled");
+        Preferences::AddUintVarCache(&sRCWNQueueSizeNormal, "network.http.rcwn.cache_queue_normal_threshold");
+        Preferences::AddUintVarCache(&sRCWNQueueSizePriority, "network.http.rcwn.cache_queue_priority_threshold");
+        Preferences::AddUintVarCache(&sRCWNSmallResourceSizeKB, "network.http.rcwn.small_resource_size_kb");
+    }
+
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv)) {
         ReleaseListeners();
         return rv;
     }
 
     if (mInterceptCache != INTERCEPTED && ShouldIntercept()) {
         mInterceptCache = MAYBE_INTERCEPT;
@@ -7160,33 +7188,34 @@ nsHttpChannel::OnDataAvailable(nsIReques
 {
     PROFILER_LABEL("nsHttpChannel", "OnDataAvailable",
         js::ProfileEntry::Category::NETWORK);
 
     LOG(("nsHttpChannel::OnDataAvailable [this=%p request=%p offset=%" PRIu64
          " count=%" PRIu32 "]\n",
         this, request, offset, count));
 
-    LOG(("OnDataAvailable %p requestFromCache: %d mFirstResponseSource: %d\n", this, request == mCachePump, mFirstResponseSource));
+    LOG(("  requestFromCache: %d mFirstResponseSource: %d\n",
+        request == mCachePump, mFirstResponseSource));
 
     // don't send out OnDataAvailable notifications if we've been canceled.
     if (mCanceled)
         return mStatus;
 
+    if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
+        (request == mTransactionPump && mTransactionReplaced)) {
+        uint32_t n;
+        return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
+    }
+
     MOZ_ASSERT(mResponseHead, "No response head in ODA!!");
 
     MOZ_ASSERT(!(mCachedContentIsPartial && (request == mTransactionPump)),
                "transaction pump not suspended");
 
-    if (mAuthRetryPending || WRONG_RACING_RESPONSE_SOURCE(request) ||
-        (request == mTransactionPump && mTransactionReplaced)) {
-        uint32_t n;
-        return input->ReadSegments(NS_DiscardSegment, nullptr, count, &n);
-    }
-
     mIsReadingFromCache = (request == mCachePump);
 
     if (mListener) {
         //
         // synthesize transport progress event.  we do this here since we want
         // to delay OnProgress events until we start streaming data.  this is
         // crucially important since it impacts the lock icon (see bug 240053).
         //
@@ -8712,17 +8741,19 @@ nsHttpChannel::Test_triggerDelayedOpenCa
 }
 
 nsresult
 nsHttpChannel::TriggerNetwork(int32_t aTimeout)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
     // If a network request has already gone out, there is no point in
     // doing this again.
+    LOG(("nsHttpChannel::TriggerNetwork [this=%p]\n", this));
     if (mNetworkTriggered) {
+        LOG(("  network already triggered. Returning.\n"));
         return NS_OK;
     }
 
     if (!aTimeout) {
         mNetworkTriggered = true;
         if (!mOnCacheAvailableCalled) {
             // If the network was triggered before onCacheEntryAvailable was
             // called, we are either racing network and cache, or the load is
@@ -8735,28 +8766,53 @@ nsHttpChannel::TriggerNetwork(int32_t aT
         }
 
         // If we are waiting for a proxy request, that means we can't trigger
         // the next step just yet. We need for mConnectionInfo to be non-null
         // before we call TryHSTSPriming. OnProxyAvailable will trigger
         // BeginConnect, and Connect will call TryHSTSPriming even if it's
         // for the cache callbacks.
         if (mProxyRequest) {
+            LOG(("  proxy request in progress. Delaying network trigger.\n"));
             mWaitingForProxy = true;
             return NS_OK;
         }
 
+        LOG(("  triggering network\n"));
         return TryHSTSPriming();
     }
 
+    LOG(("  setting timer to trigger network: %d ms\n", aTimeout));
     mNetworkTriggerTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
     mNetworkTriggerTimer->InitWithCallback(this, aTimeout, nsITimer::TYPE_ONE_SHOT);
     return NS_OK;
 }
 
+nsresult
+nsHttpChannel::MaybeRaceNetworkWithCache()
+{
+    // Don't trigger the network if the load flags say so.
+    if (mLoadFlags & (LOAD_ONLY_FROM_CACHE | LOAD_NO_NETWORK_IO)) {
+        return NS_OK;
+    }
+
+
+    uint32_t threshold = mCacheOpenWithPriority ? sRCWNQueueSizePriority
+                                                : sRCWNQueueSizeNormal;
+    // No need to trigger to trigger the racing, since most likely the cache
+    // will be faster.
+    if (mCacheQueueSizeWhenOpen < threshold) {
+        return NS_OK;
+    }
+
+    MOZ_ASSERT(sRCWNEnabled, "The pref must be truned on.");
+    LOG(("nsHttpChannel::MaybeRaceNetworkWithCache [this=%p]\n", this));
+    return TriggerNetwork(0);
+}
+
 NS_IMETHODIMP
 nsHttpChannel::Test_triggerNetwork(int32_t aTimeout)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Must be called on the main thread");
     return TriggerNetwork(aTimeout);
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -655,16 +655,20 @@ private:
 
     // We need to remember which is the source of the response we are using.
     enum {
         RESPONSE_PENDING,           // response is pending
         RESPONSE_FROM_CACHE,        // response coming from cache. no network.
         RESPONSE_FROM_NETWORK,      // response coming from the network
     } mFirstResponseSource = RESPONSE_PENDING;
 
+    // Determines if it's possible and advisable to race the network request
+    // with the cache fetch, and proceeds to do so.
+    nsresult MaybeRaceNetworkWithCache();
+
     nsresult TriggerNetwork(int32_t aTimeout);
     void CancelNetworkRequest(nsresult aStatus);
     // Timer used to delay the network request, or to trigger the network
     // request if retrieving the cache entry takes too long.
     nsCOMPtr<nsITimer> mNetworkTriggerTimer;
     // Is true if the network request has been triggered.
     bool mNetworkTriggered = false;
     bool mWaitingForProxy = false;
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -192,26 +192,20 @@ function* createFileInHome() {
   }
 }
 
 // Test if the content process can create a temp file, should pass
 function* createTempFile() {
   let browser = gBrowser.selectedBrowser;
   let path = fileInTempDir().path;
   let fileCreated = yield ContentTask.spawn(browser, path, createFile);
-  if (!fileCreated && isWin()) {
-    // TODO: fix 1329294 and enable this test for Windows.
-    // Not using todo() because this only fails on automation.
-    info("ignoring failure to write to content temp due to 1329294\n");
-  } else {
-    ok(fileCreated == true, "creating a file in content temp is permitted");
-    // now delete the file
-    let fileDeleted = yield ContentTask.spawn(browser, path, deleteFile);
-    ok(fileDeleted == true, "deleting a file in content temp is permitted");
-  }
+  ok(fileCreated == true, "creating a file in content temp is permitted");
+  // now delete the file
+  let fileDeleted = yield ContentTask.spawn(browser, path, deleteFile);
+  ok(fileDeleted == true, "deleting a file in content temp is permitted");
 }
 
 // Test reading files and dirs from web and file content processes.
 function* testFileAccess() {
   // for tests that run in a web content process
   let webBrowser = gBrowser.selectedBrowser;
 
   // For now, we'll only test file access from the file content process if
--- a/testing/mozharness/configs/merge_day/beta_to_release.py
+++ b/testing/mozharness/configs/merge_day/beta_to_release.py
@@ -16,18 +16,18 @@ config = {
         "ac_add_options --with-branding=mobile/android/branding/beta",
         "ac_add_options --with-branding=mobile/android/branding/official")
         for d in ["mobile/android/config/mozconfigs/android-api-15/",
                   "mobile/android/config/mozconfigs/android-x86/"]
         for f in ["debug", "nightly", "l10n-nightly", "l10n-release", "release"]
     ] + [
         # File, from, to
         ("{}/{}".format(d, f),
-        "ac_add_options --with-l10n-base=../../mozilla-aurora",
-        "ac_add_options --with-l10n-base=../../mozilla-beta")
+        "ac_add_options --with-l10n-base=../../mozilla-beta",
+        "ac_add_options --with-l10n-base=../../mozilla-release")
         for d in ["mobile/android/config/mozconfigs/android-api-15/",
                   "mobile/android/config/mozconfigs/android-x86/"]
         for f in ["l10n-nightly", "l10n-release"]
     ] + [
         # File, from, to
         ("browser/confvars.sh",
          "ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-beta,firefox-mozilla-release",
          "ACCEPTED_MAR_CHANNEL_IDS=firefox-mozilla-release"),
--- a/testing/mozharness/scripts/web_platform_tests.py
+++ b/testing/mozharness/scripts/web_platform_tests.py
@@ -142,17 +142,18 @@ class WebPlatformTest(TestingMixin, Merc
         cmd += ["--log-raw=-",
                 "--log-raw=%s" % os.path.join(dirs["abs_blob_upload_dir"],
                                               "wpt_raw.log"),
                 "--log-errorsummary=%s" % os.path.join(dirs["abs_blob_upload_dir"],
                                                        "wpt_errorsummary.log"),
                 "--binary=%s" % self.binary_path,
                 "--symbols-path=%s" % self.query_symbols_url(),
                 "--stackwalk-binary=%s" % self.query_minidump_stackwalk(),
-                "--stackfix-dir=%s" % os.path.join(dirs["abs_test_install_dir"], "bin")]
+                "--stackfix-dir=%s" % os.path.join(dirs["abs_test_install_dir"], "bin"),
+                "--run-by-dir=3"]
 
         for test_type in c.get("test_type", []):
             cmd.append("--test-type=%s" % test_type)
 
         if not c["e10s"]:
             cmd.append("--disable-e10s")
 
         for opt in ["total_chunks", "this_chunk"]:
--- a/testing/web-platform/harness/wptrunner/testloader.py
+++ b/testing/web-platform/harness/wptrunner/testloader.py
@@ -622,17 +622,16 @@ class PathGroupedSource(TestSource):
         if not self.current_queue or self.current_queue.empty():
             try:
                 data = self.test_queue.get(block=True, timeout=1)
                 self.current_queue = Queue()
                 for item in data:
                     self.current_queue.put(item)
             except Empty:
                 return None
-
         return self.current_queue
 
     def requeue_test(self, test):
         self.current_queue.put(test)
 
     def __exit__(self, *args, **kwargs):
         if self.current_queue:
             self.current_queue.close()
--- a/testing/web-platform/harness/wptrunner/testrunner.py
+++ b/testing/web-platform/harness/wptrunner/testrunner.py
@@ -506,19 +506,20 @@ class TestRunnerManager(threading.Thread
         while test is None:
             if test_queue is None:
                 test_queue = self.test_source.get_queue()
                 if test_queue is None:
                     self.logger.info("No more tests")
                     return None, None
             try:
                 # Need to block here just to allow for contention with other processes
-                test = test_queue.get(block=True, timeout=1)
+                test = test_queue.get(block=True, timeout=2)
             except Empty:
-                pass
+                if test_queue.empty():
+                    test_queue = None
         return test, test_queue
 
     def run_test(self):
         assert isinstance(self.state, RunnerManagerState.running)
         assert self.state.test is not None
 
         if self.browser.update_settings(self.state.test):
             self.logger.info("Restarting browser for new test environment")
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/dom/events/EventListener-invoke-legacy.html.ini
@@ -0,0 +1,4 @@
+[EventListener-invoke-legacy.html]
+  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1351409
+
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/resume-timer-on-history-back.html.ini
@@ -0,0 +1,4 @@
+[resume-timer-on-history-back.html]
+  type: testharness
+  disabled:
+    if (os == "win"): https://bugzilla.mozilla.org/show_bug.cgi?id=1321179
--- a/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini
+++ b/testing/web-platform/meta/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html.ini
@@ -1,6 +1,5 @@
 [scroll-restoration-fragment-scrolling-cross-origin.html]
   type: testharness
-  expected: ERROR
   [Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation]
-    expected: TIMEOUT
+    expected: FAIL
 
--- a/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
+++ b/testing/web-platform/tests/html/browsers/browsing-the-web/history-traversal/persisted-user-state-restoration/scroll-restoration-fragment-scrolling-cross-origin.html
@@ -42,26 +42,25 @@
         // navigate to a new page from a different origin
         iframe.src = iframe.src.replace("http://", "http://www.").replace("page-with-fragment.html#fragment", "blank1.html");
       }, function() {
         // going back causes the iframe to traverse back
         history.back();
       }, function() {
         // coming back from history, scrollRestoration should be set to manual and respected
         assert_equals(iframe.contentWindow.location.href, baseURL + '/resources/page-with-fragment.html#fragment', 'should be back on page-with-fragment page');
-        iframe.contentWindow.requestAnimationFrame(function() {
+        iframe.contentWindow.requestAnimationFrame(t.step_func_done(function() {
           assert_equals(iframe.contentWindow.history.scrollRestoration, 'manual', 'navigating back should retain scrollRestoration value');
           assert_equals(iframe.contentWindow.scrollX, 0, 'should not scroll to fragment');
           assert_equals(iframe.contentWindow.scrollY, 0, 'should not scroll to fragment');
-          t.done();
-        });
+        }));
       }
     ];
 
     var stepCount = 0;
     var next = t.step_func(function() {
       steps[stepCount++]();
     });
 
     iframe.onload = next;
     next();
   }, 'Manual scroll restoration should take precedent over scrolling to fragment in cross origin navigation');
-</script>
\ No newline at end of file
+</script>
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -3811,52 +3811,16 @@
   },
   "PLUGIN_SHUTDOWN_MS": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 5000,
     "n_buckets": 20,
     "description": "Time spent shutting down plugins (ms)"
   },
-  "FLASH_PLUGIN_STATES": {
-    "expires_in_version": "50",
-    "kind": "enumerated",
-    "n_values": 20,
-    "description": "A flash object's initialization state"
-  },
-  "FLASH_PLUGIN_AREA": {
-    "expires_in_version": "50",
-    "kind": "exponential",
-    "low": 256,
-    "high": 16777216,
-    "n_buckets": 50,
-    "description": "Flash object area (width * height)"
-  },
-  "FLASH_PLUGIN_WIDTH": {
-    "expires_in_version": "50",
-    "kind": "linear",
-    "low": 1,
-    "high": 2000,
-    "n_buckets": 50,
-    "description": "Flash object width"
-  },
-  "FLASH_PLUGIN_HEIGHT": {
-    "expires_in_version": "50",
-    "kind": "linear",
-    "low": 1,
-    "high": 2000,
-    "n_buckets": 50,
-    "description": "Flash object height"
-  },
-  "FLASH_PLUGIN_INSTANCES_ON_PAGE": {
-    "expires_in_version": "50",
-    "kind": "enumerated",
-    "n_values": 30,
-    "description": "Flash object instances count on page"
-  },
   "MOZ_SQLITE_OPEN_MS": {
     "expires_in_version": "default",
     "kind": "exponential",
     "high": 3000,
     "n_buckets": 10,
     "description": "Time spent on SQLite open() (ms)"
   },
   "MOZ_SQLITE_OPEN_MAIN_THREAD_MS": {
--- a/toolkit/components/telemetry/histogram-whitelists.json
+++ b/toolkit/components/telemetry/histogram-whitelists.json
@@ -242,21 +242,16 @@
     "FENNEC_SYNC_NUMBER_OF_SYNCS_COMPLETED",
     "FENNEC_SYNC_NUMBER_OF_SYNCS_FAILED",
     "FENNEC_SYNC_NUMBER_OF_SYNCS_FAILED_BACKOFF",
     "FENNEC_SYNC_NUMBER_OF_SYNCS_STARTED",
     "FENNEC_TABQUEUE_QUEUESIZE",
     "FENNEC_TOPSITES_LOADER_TIME_MS",
     "FENNEC_WAS_KILLED",
     "FETCH_IS_MAINTHREAD",
-    "FLASH_PLUGIN_AREA",
-    "FLASH_PLUGIN_HEIGHT",
-    "FLASH_PLUGIN_INSTANCES_ON_PAGE",
-    "FLASH_PLUGIN_STATES",
-    "FLASH_PLUGIN_WIDTH",
     "FONTLIST_INITFACENAMELISTS",
     "FONTLIST_INITOTHERFAMILYNAMES",
     "FONT_CACHE_HIT",
     "FORCED_DEVICE_RESET_REASON",
     "FX_BOOKMARKS_TOOLBAR_INIT_MS",
     "FX_BROWSER_FULLSCREEN_USED",
     "FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE",
     "FX_GESTURE_INSTALL_SNAPSHOT_OF_PAGE",
@@ -952,21 +947,16 @@
     "FENNEC_SYNC_NUMBER_OF_SYNCS_FAILED",
     "FENNEC_SYNC_NUMBER_OF_SYNCS_FAILED_BACKOFF",
     "FENNEC_SYNC_NUMBER_OF_SYNCS_STARTED",
     "FENNEC_TABQUEUE_QUEUESIZE",
     "FENNEC_TOPSITES_LOADER_TIME_MS",
     "FENNEC_WAS_KILLED",
     "FETCH_IS_MAINTHREAD",
     "FIND_PLUGINS",
-    "FLASH_PLUGIN_AREA",
-    "FLASH_PLUGIN_HEIGHT",
-    "FLASH_PLUGIN_INSTANCES_ON_PAGE",
-    "FLASH_PLUGIN_STATES",
-    "FLASH_PLUGIN_WIDTH",
     "FONTLIST_INITFACENAMELISTS",
     "FONTLIST_INITOTHERFAMILYNAMES",
     "FONT_CACHE_HIT",
     "FORCED_DEVICE_RESET_REASON",
     "FORGET_SKIPPABLE_MAX",
     "FX_BOOKMARKS_TOOLBAR_INIT_MS",
     "FX_BROWSER_FULLSCREEN_USED",
     "FX_GESTURE_COMPRESS_SNAPSHOT_OF_PAGE",
--- a/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_bug818225.js
@@ -18,16 +18,17 @@ function* runTests() {
   is(path, expectedPath, "Thumbnail file has correct path");
 
   yield testIfExists(path, true, "Thumbnail file exists");
 
 }
 
 function createThumbnail(aURL) {
   addTab(aURL, function() {
+    gBrowserThumbnails.clearTopSiteURLCache();
     whenFileExists(aURL, function() {
       gBrowser.removeTab(gBrowser.selectedTab);
       next();
     });
   });
 }
 
 function testIfExists(aPath, aExpected, aMessage) {
--- a/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
+++ b/toolkit/components/thumbnails/test/browser_thumbnails_storage.js
@@ -98,15 +98,16 @@ function promiseClearHistory(aUseRange) 
     s.range = null;
     s.ignoreTimespan = true;
   });
 }
 
 function promiseCreateThumbnail() {
   return new Promise(resolve => {
     addTab(URL, function() {
+      gBrowserThumbnails.clearTopSiteURLCache();
       whenFileExists(URL, function() {
         gBrowser.removeTab(gBrowser.selectedTab);
         resolve();
       });
     });
   });
 }
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -741,17 +741,24 @@ GetContentProcessTempBaseDirKey()
 //
 // Sets mContentTempDir so that it refers to the appropriate temp dir.
 // If the sandbox is enabled, NS_APP_CONTENT_PROCESS_TEMP_DIR, otherwise
 // NS_OS_TEMP_DIR is used.
 //
 nsresult
 nsXREDirProvider::LoadContentProcessTempDir()
 {
-  mContentTempDir = GetContentProcessSandboxTempDir();
+  // The parent is responsible for creating the sandbox temp dir.
+  if (XRE_IsParentProcess()) {
+    mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
+    mContentTempDir = mContentProcessSandboxTempDir;
+  } else {
+    mContentTempDir = GetContentProcessSandboxTempDir();
+  }
+
   if (mContentTempDir) {
     return NS_OK;
   } else {
     return NS_GetSpecialDirectory(NS_OS_TEMP_DIR,
                                   getter_AddRefs(mContentTempDir));
   }
 }
 
@@ -1197,20 +1204,21 @@ nsXREDirProvider::DoStartup()
 
       mozilla::Telemetry::Accumulate(mozilla::Telemetry::NUMBER_OF_PROFILES,
                                      count);
     }
 
     obsSvc->NotifyObservers(nullptr, "profile-initial-state", nullptr);
 
 #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
-    // The parent is responsible for creating the sandbox temp dir
-    if (XRE_IsParentProcess()) {
-      mContentProcessSandboxTempDir = CreateContentProcessSandboxTempDir();
-      mContentTempDir = mContentProcessSandboxTempDir;
+    // Makes sure the content temp dir has been loaded if it hasn't been
+    // already. In the parent this ensures it has been created before we attempt
+    // to start any content processes.
+    if (!mContentTempDir) {
+      mozilla::Unused << NS_WARN_IF(NS_FAILED(LoadContentProcessTempDir()));
     }
 #endif
   }
   return NS_OK;
 }
 
 void
 nsXREDirProvider::DoShutdown()
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -21,16 +21,17 @@
 #include "prtime.h"
 #include "prthread.h"
 #include <dlfcn.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include "nsCRT.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Services.h"
+#include "mozilla/ClearOnShutdown.h"
 
 #include "gfxXlibSurface.h"
 #include "gfxContext.h"
 #include "nsImageToPixbuf.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsISelection.h"
@@ -146,23 +147,30 @@ nsDragService::~nsDragService()
     MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::~nsDragService"));
     if (mTaskSource)
         g_source_remove(mTaskSource);
 
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(nsDragService, nsBaseDragService, nsIObserver)
 
-/* static */ nsDragService*
+mozilla::StaticRefPtr<nsDragService> sDragServiceInstance;
+/* static */ already_AddRefed<nsDragService>
 nsDragService::GetInstance()
 {
-    static const nsIID iid = NS_DRAGSERVICE_CID;
-    nsCOMPtr<nsIDragService> dragService = do_GetService(iid);
-    return static_cast<nsDragService*>(dragService.get());
-    // We rely on XPCOM keeping a reference to the service.
+  if (gfxPlatform::IsHeadless()) {
+    return nullptr;
+  }
+  if (!sDragServiceInstance) {
+    sDragServiceInstance = new nsDragService();
+    ClearOnShutdown(&sDragServiceInstance);
+  }
+
+  RefPtr<nsDragService> service = sDragServiceInstance.get();
+  return service.forget();
 }
 
 // nsIObserver
 
 NS_IMETHODIMP
 nsDragService::Observe(nsISupports *aSubject, const char *aTopic,
                        const char16_t *aData)
 {
--- a/widget/gtk/nsDragService.h
+++ b/widget/gtk/nsDragService.h
@@ -80,17 +80,17 @@ public:
     NS_IMETHOD IsDataFlavorSupported (const char *aDataFlavor,
                                       bool *_retval) override;
 
      NS_IMETHOD UpdateDragEffect() override;
 
     // Methods called from nsWindow to handle responding to GTK drag
     // destination signals
 
-    static nsDragService* GetInstance();
+    static already_AddRefed<nsDragService> GetInstance();
 
     void TargetDataReceived          (GtkWidget         *aWidget,
                                       GdkDragContext    *aContext,
                                       gint               aX,
                                       gint               aY,
                                       GtkSelectionData  *aSelection_data,
                                       guint              aInfo,
                                       guint32            aTime);
--- a/widget/gtk/nsWidgetFactory.cpp
+++ b/widget/gtk/nsWidgetFactory.cpp
@@ -69,17 +69,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsChildWindow)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTransferable)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBidiKeyboard)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTMLFormatConverter)
 #ifdef MOZ_X11
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIdleServiceGTK, nsIdleServiceGTK::GetInstance)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsClipboardHelper)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsClipboard, Init)
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsDragService)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDragService, nsDragService::GetInstance)
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSound)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(ScreenManager, ScreenManager::GetAddRefedSingleton)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsImageToPixbuf)
 
 
 // from nsWindow.cpp
 extern bool gDisableNativeTheme;
--- a/widget/gtk/nsWindow.cpp
+++ b/widget/gtk/nsWindow.cpp
@@ -730,17 +730,17 @@ nsWindow::Destroy()
     if (rollupListener) {
         nsCOMPtr<nsIWidget> rollupWidget = rollupListener->GetRollupWidget();
         if (static_cast<nsIWidget *>(this) == rollupWidget) {
             rollupListener->Rollup(0, false, nullptr, nullptr);
         }
     }
 
     // dragService will be null after shutdown of the service manager.
-    nsDragService *dragService = nsDragService::GetInstance();
+    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
     if (dragService && this == dragService->GetMostRecentDestWindow()) {
         dragService->ScheduleLeaveEvent();
     }
 
     NativeShow(false);
 
     if (mIMContext) {
         mIMContext->OnDestroyWindow(this);
@@ -3392,17 +3392,18 @@ nsWindow::OnDragDataReceivedEvent(GtkWid
                                   gint aY,
                                   GtkSelectionData  *aSelectionData,
                                   guint aInfo,
                                   guint aTime,
                                   gpointer aData)
 {
     LOGDRAG(("nsWindow::OnDragDataReceived(%p)\n", (void*)this));
 
-    nsDragService::GetInstance()->
+    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+    dragService->
         TargetDataReceived(aWidget, aDragContext, aX, aY,
                            aSelectionData, aInfo, aTime);
 }
 
 #if GTK_CHECK_VERSION(3,4,0)
 gboolean
 nsWindow::OnTouchEvent(GdkEventTouch* aEvent)
 {
@@ -5906,32 +5907,33 @@ drag_motion_event_cb(GtkWidget *aWidget,
     if (!innerMostWindow) {
         innerMostWindow = window;
     }
 
     LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
 
     LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
 
-    return nsDragService::GetInstance()->
+    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+    return dragService->
         ScheduleMotionEvent(innerMostWindow, aDragContext,
                             point, aTime);
 }
 
 static void
 drag_leave_event_cb(GtkWidget *aWidget,
                     GdkDragContext *aDragContext,
                     guint aTime,
                     gpointer aData)
 {
     RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
     if (!window)
         return;
 
-    nsDragService *dragService = nsDragService::GetInstance();
+    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
 
     nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
     if (!mostRecentDragWindow) {
         // This can happen when the target will not accept a drop.  A GTK drag
         // source sends the leave message to the destination before the
         // drag-failed signal on the source widget, but the leave message goes
         // via the X server, and so doesn't get processed at least until the
         // event loop runs again.
@@ -5978,17 +5980,18 @@ drag_drop_event_cb(GtkWidget *aWidget,
     if (!innerMostWindow) {
         innerMostWindow = window;
     }
 
     LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
 
     LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
 
-    return nsDragService::GetInstance()->
+    RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+    return dragService->
         ScheduleDropEvent(innerMostWindow, aDragContext,
                           point, aTime);
 }
 
 static void
 drag_data_received_event_cb(GtkWidget *aWidget,
                             GdkDragContext *aDragContext,
                             gint aX,
--- a/widget/headless/HeadlessWidget.cpp
+++ b/widget/headless/HeadlessWidget.cpp
@@ -121,15 +121,17 @@ HeadlessWidget::DispatchEvent(WidgetGUIE
 #ifdef DEBUG
   debug_DumpEvent(stdout, aEvent->mWidget, aEvent, "HeadlessWidget", 0);
 #endif
 
   aStatus = nsEventStatus_eIgnore;
 
   if (mAttachedWidgetListener) {
     aStatus = mAttachedWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
+  } else if (mWidgetListener) {
+    aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents);
   }
 
   return NS_OK;
 }
 
 } // namespace widget
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/widget/headless/tests/headless_button.html
@@ -0,0 +1,6 @@
+<html>
+<head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"></head>
+<body>
+<button id="btn">button</button>
+</body>
+</html>
--- a/widget/headless/tests/test_headless.js
+++ b/widget/headless/tests/test_headless.js
@@ -7,16 +7,18 @@ Cu.import("resource://testing-common/htt
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const server = new HttpServer();
 server.registerDirectory("/", do_get_cwd());
 server.start(-1);
 const ROOT = `http://localhost:${server.identity.primaryPort}`;
 const BASE = `${ROOT}/`;
 const HEADLESS_URL = `${BASE}/headless.html`;
+const HEADLESS_BUTTON_URL = `${BASE}/headless_button.html`;
+do_register_cleanup(() => { server.stop(() => {})});
 
 function loadContentWindow(webNavigation, uri) {
   return new Promise((resolve, reject) => {
     webNavigation.loadURI(uri, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null);
     let docShell = webNavigation.QueryInterface(Ci.nsIInterfaceRequestor)
                   .getInterface(Ci.nsIDocShell);
     let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
                       .getInterface(Ci.nsIWebProgress);
@@ -83,12 +85,57 @@ add_task(function* test_snapshot() {
     if (imageData[i + 2] === 255) {
       found = true;
       break;
     }
   }
   ok(found, "Found blue text on page.");
 
   webNavigation.close();
-  yield new Promise((resolve) => {
-    server.stop(resolve);
-  });
+});
+
+// Ensure keydown events are triggered on the windowless browser.
+add_task(function* test_keydown() {
+  let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
+  let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
+  let contentWindow = yield loadContentWindow(webNavigation, HEADLESS_URL);
+
+  let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+  let keydown = new Promise((resolve) => {
+    contentWindow.addEventListener("keydown", () => {
+      resolve();
+    }, { once: true });
+  })
+  utils.sendKeyEvent("keydown", 65, 65, 0);
+
+  yield keydown;
+  ok(true, "Send keydown didn't crash");
+
+  webNavigation.close();
 });
+
+// Test dragging the mouse on a button to ensure the creation of the drag
+// service doesn't crash in headless.
+add_task(function* test_mouse_drag() {
+  let windowlessBrowser = Services.appShell.createWindowlessBrowser(false);
+  let webNavigation = windowlessBrowser.QueryInterface(Ci.nsIWebNavigation);
+  let contentWindow = yield loadContentWindow(webNavigation, HEADLESS_BUTTON_URL);
+  contentWindow.resizeTo(400, 400);
+
+  let target = contentWindow.document.getElementById('btn');
+  let rect = target.getBoundingClientRect();
+  let left = rect.left;
+  let top = rect.top;
+
+  let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+  utils.sendMouseEvent("mousedown", left, top, 0, 1, 0, false, 0, 0);
+  utils.sendMouseEvent("mousemove", left, top, 0, 1, 0, false, 0, 0);
+  // Wait for a turn of the event loop since the synthetic mouse event
+  // that creates the drag service is processed during the refresh driver.
+  yield new Promise((r) => {do_execute_soon(r)});
+  utils.sendMouseEvent("mouseup", left, top, 0, 1, 0, false, 0, 0);
+
+  ok(true, "Send mouse event didn't crash");
+
+  webNavigation.close();
+});
--- a/widget/headless/tests/xpcshell.ini
+++ b/widget/headless/tests/xpcshell.ini
@@ -1,4 +1,6 @@
 [test_headless.js]
 skip-if = os != "linux"
 headless = true
-support-files = headless.html
+support-files =
+  headless.html
+  headless_button.html