merge mozilla-inbound to mozilla-central. r=merge a=merge
authorSebastian Hengst <archaeopteryx@coole-files.de>
Sun, 10 Sep 2017 23:16:37 +0200
changeset 429459 a5f163da8a9be5d2e86138c57d59be69723b5457
parent 429446 bf3d75d48631b9bc3da253ee9af55c8d03856598 (current diff)
parent 429458 697d93c85b773258b762224cd30d9bcb2cb6d8fd (diff)
child 429466 eb5929b8485194247a7c31138cca292a131bf38e
child 429500 6163805633e53ec795aa894c72315e4daf3be207
push id7761
push userjlund@mozilla.com
push dateFri, 15 Sep 2017 00:19:52 +0000
treeherdermozilla-beta@c38455951db4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge, merge
milestone57.0a1
first release with
nightly linux32
a5f163da8a9b / 57.0a1 / 20170910220126 / files
nightly linux64
a5f163da8a9b / 57.0a1 / 20170910220126 / files
nightly mac
a5f163da8a9b / 57.0a1 / 20170910220126 / files
nightly win32
a5f163da8a9b / 57.0a1 / 20170910220126 / files
nightly win64
a5f163da8a9b / 57.0a1 / 20170910220126 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central. r=merge a=merge MozReview-Commit-ID: 6kVzWGLcJwv
--- a/browser/components/extensions/ext-browser.js
+++ b/browser/components/extensions/ext-browser.js
@@ -509,17 +509,17 @@ class TabTracker extends TabTrackerBase 
     // event listener is registered. To make sure that the event listener is
     // notified, we dispatch `tabs.onRemoved` asynchronously.
     Services.tm.dispatchToMainThread(() => {
       this.emit("tab-removed", {nativeTab, tabId, windowId, isWindowClosing});
     });
   }
 
   getBrowserData(browser) {
-    if (browser.ownerGlobal.location.href === "about:addons") {
+    if (browser.ownerDocument.documentURI === "about:addons") {
       // When we're loaded into a <browser> inside about:addons, we need to go up
       // one more level.
       browser = browser.ownerGlobal.QueryInterface(Ci.nsIInterfaceRequestor)
                        .getInterface(Ci.nsIDocShell)
                        .chromeEventHandler;
     }
 
     let result = {
--- a/dom/base/test/browser.ini
+++ b/dom/base/test/browser.ini
@@ -7,16 +7,22 @@ support-files =
   file_audioLoopInIframe.html
   file_blocking_image.html
   file_bug902350.html
   file_bug902350_frame.html
   file_bug1011748_redirect.sjs
   file_bug1011748_OK.sjs
   file_bug1303838.html
   file_bug1303838_target.html
+  file_bug1303838_target_foo.html
+  file_bug1303838_target_bar.html
+  file_bug1303838_target_baz.html
+  file_bug1303838_target_ifoo.html
+  file_bug1303838_target_ibar.html
+  file_bug1303838_target_ibaz.html
   file_bug1303838_with_iframe.html
   file_messagemanager_unload.html
   file_pluginAudio.html
   file_use_counter_outer.html
   file_use_counter_svg_getElementById.svg
   file_use_counter_svg_currentScale.svg
   file_use_counter_svg_fill_pattern_definition.svg
   file_use_counter_svg_fill_pattern.svg
--- a/dom/base/test/file_bug1303838.html
+++ b/dom/base/test/file_bug1303838.html
@@ -3,20 +3,20 @@
 <!--
 Tests for tab switching on link clicks.
 -->
 <head>
   <meta charset="utf-8">
   <title>Tests for tab switching on link clicks.</title>
 </head>
 <body>
-  <a id="link-1" target="testTab" href="data:text/html;charset=utf-8,foo">Link 1</a><br>
-  <a id="link-2" target="testTab" href="data:text/html;charset=utf-8,bar">Link 2</a><br>
-  <a id="link-3" target="testTab" href="data:text/html;charset=utf-8,baz">Link 3</a><br>
+  <a id="link-1" target="testTab" href="file_bug1303838_target_foo.html">Link 1</a><br>
+  <a id="link-2" target="testTab" href="file_bug1303838_target_bar.html">Link 2</a><br>
+  <a id="link-3" target="testTab" href="file_bug1303838_target_baz.html">Link 3</a><br>
   <a id="link-4" target="testTab" href="file_bug1303838_target.html">Link 4</a><br>
   <a id="anchor-link-1" target="testTab" href="file_bug1303838_target.html#foo">Anchor Link 1</a><br>
   <a id="anchor-link-2" target="testTab" href="file_bug1303838_target.html#bar">Anchor Link 2</a><br>
   <a id="anchor-link-3" target="testTab" href="file_bug1303838_target.html#baz">Anchor Link 3</a><br>
-  <a id="frame-link-1" target="testFrame" href="data:text/html;charset=utf-8,ifoo">Frame Link 1</a><br>
-  <a id="frame-link-2" target="testFrame" href="data:text/html;charset=utf-8,ibar">Frame Link 2</a><br>
-  <a id="frame-link-3" target="testFrame" href="data:text/html;charset=utf-8,ibaz">Frame Link 3</a><br>
+  <a id="frame-link-1" target="testFrame" href="file_bug1303838_target_ifoo.html">Frame Link 1</a><br>
+  <a id="frame-link-2" target="testFrame" href="file_bug1303838_target_ibar.html">Frame Link 2</a><br>
+  <a id="frame-link-3" target="testFrame" href="file_bug1303838_target_ibaz.html">Frame Link 3</a><br>
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_bar.html
@@ -0,0 +1,1 @@
+<html><body>bar</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_baz.html
@@ -0,0 +1,1 @@
+<html><body>baz</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_foo.html
@@ -0,0 +1,1 @@
+<html><body>foo</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_ibar.html
@@ -0,0 +1,1 @@
+<html><body>ibar</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_ibaz.html
@@ -0,0 +1,1 @@
+<html><body>ibaz</body></html>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/file_bug1303838_target_ifoo.html
@@ -0,0 +1,1 @@
+<html><body>ifoo</body></html>
--- a/dom/base/test/test_data_uri.html
+++ b/dom/base/test/test_data_uri.html
@@ -10,18 +10,20 @@
       src: url(data:font/opentype;base64,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);
     }
   </style>
 
 <script>
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.setBoolPref("security.data_uri.unique_opaque_origin", true);
+SpecialPowers.setBoolPref("security.data_uri.block_toplevel_data_uri_navigations", false);
 SimpleTest.registerCleanupFunction(() => {
   SpecialPowers.clearUserPref("security.data_uri.unique_opaque_origin");
+  SpecialPowers.clearUserPref("security.data_uri.block_toplevel_data_uri_navigations");
 });
 
 function imgListener(img) {
   return new Promise((resolve, reject) => {
     img.addEventListener("load", () => resolve());
     img.addEventListener("error", () => reject());
   });
 }
--- a/dom/base/test/test_x-frame-options.html
+++ b/dom/base/test/test_x-frame-options.html
@@ -170,15 +170,19 @@ var testFrameNotLoadedInDataURI = functi
     SimpleTest.finish();
   };
   win.location.href = "data:text/html,"+html;
 };
 
 SimpleTest.waitForExplicitFinish();
 
 // load the test harness
-document.getElementById("harness").src = "file_x-frame-options_main.html";
+SpecialPowers.pushPrefEnv({
+  "set": [["security.data_uri.block_toplevel_data_uri_navigations", false],]
+}, function() {
+  document.getElementById("harness").src = "file_x-frame-options_main.html";
+});
 
 </script>
 </pre>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/dom/file/tests/file_ipc_messagemanager_blob.html
@@ -0,0 +1,1 @@
+<!DOCTYPE HTML><html><body></body></html>
--- a/dom/file/tests/mochitest.ini
+++ b/dom/file/tests/mochitest.ini
@@ -13,15 +13,16 @@ support-files =
   !/dom/html/test/form_submit_server.sjs
   !/dom/xhr/tests/file_XHRSendData.sjs
 
 [test_blob_fragment_and_query.html]
 [test_blobconstructor.html]
 [test_blobURL_expiring.html]
 [test_file_from_blob.html]
 [test_ipc_messagemanager_blob.html]
+support-files = file_ipc_messagemanager_blob.html
 [test_nonascii_blob_url.html]
 [test_file_negative_date.html]
 [test_fileapi.html]
 [test_fileapi_slice.html]
 skip-if = (toolkit == 'android') # Android: Bug 775227
 [test_mozfiledataurl.html]
 skip-if = toolkit == 'android' #TIMED_OUT
--- a/dom/file/tests/test_ipc_messagemanager_blob.html
+++ b/dom/file/tests/test_ipc_messagemanager_blob.html
@@ -9,18 +9,17 @@
 </head>
 <body>
 
   <script type="application/javascript">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
-    const childFrameURL =
-      "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+    const childFrameURL = "file_ipc_messagemanager_blob.html";
 
     function childFrameScript() {
       "use strict";
 
       addMessageListener("test:ipcClonedMessage", function(message) {
         if (!(message.json instanceof Components.interfaces.nsIDOMBlob)) {
           sendAsyncMessage(message.name, message.json);
           return;
--- a/dom/html/test/test_fullscreen-api-race.html
+++ b/dom/html/test/test_fullscreen-api-race.html
@@ -32,17 +32,18 @@ SimpleTest.waitForExplicitFinish();
 SimpleTest.requestFlakyTimeout(
   "Need to wait for potential fullscreen transition");
 addLoadEvent(function () {
   SpecialPowers.pushPrefEnv({
     "set": [
       ["full-screen-api.unprefix.enabled", true],
       ["full-screen-api.allow-trusted-requests-only", false],
       // Use legacy data: URI behavior to run test.
-      ["security.data_uri.unique_opaque_origin", false]
+      ["security.data_uri.unique_opaque_origin", false],
+      ["security.data_uri.block_toplevel_data_uri_navigations", false],
     ]
   }, next);
 });
 
 const OPEN_WINDOW_FUNCS = [
   function openNewTab() {
     return window.open("about:blank");
   },
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/file_cpow_cookies.html
@@ -0,0 +1,1 @@
+<!DOCTYPE HTML><html><body></body></html>
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -7,16 +7,17 @@ support-files =
 skip-if = e10s
 [test_blob_sliced_from_parent_process.html]
 # This test is only supposed to run in the main process.
 skip-if = e10s
 [test_bug1086684.html]
 # This test is only supposed to run in the main process
 skip-if = e10s || toolkit == 'android'
 [test_cpow_cookies.html]
+support-files = file_cpow_cookies.html
 [test_child_docshell.html]
 skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf
 [test_CrashService_crash.html]
 skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows'))
 [test_temporaryfile_stream.html]
 skip-if = !e10s
 support-files =
   blob_verify.sjs
--- a/dom/ipc/tests/test_cpow_cookies.html
+++ b/dom/ipc/tests/test_cpow_cookies.html
@@ -9,18 +9,17 @@
 </head>
 <body>
 
   <script type="application/javascript">
     "use strict";
 
     SimpleTest.waitForExplicitFinish();
 
-    const childFrameURL =
-      "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+    const childFrameURL = "file_cpow_cookies.html";
 
     function childFrameScript() {
       "use strict";
 
       const Ci = Components.interfaces;
 
       function test1(message) {
         // NB: This is a no-op because we're a data: document with a null
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -30,17 +30,18 @@ StaticRefPtr<nsIThreadPool> AsyncCubebTa
 
 GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
   : mIterationStart(0),
     mIterationEnd(0),
     mGraphImpl(aGraphImpl),
     mWaitState(WAITSTATE_RUNNING),
     mCurrentTimeStamp(TimeStamp::Now()),
     mPreviousDriver(nullptr),
-    mNextDriver(nullptr)
+    mNextDriver(nullptr),
+    mScheduled(false)
 { }
 
 void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver,
                                GraphTime aLastSwitchNextIterationStart,
                                GraphTime aLastSwitchNextIterationEnd)
 {
   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
   // We set mIterationEnd here, because the first thing a driver do when it
@@ -130,16 +131,22 @@ void GraphDriver::SetNextDriver(GraphDri
 }
 
 void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver)
 {
   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
   mPreviousDriver = aPreviousDriver;
 }
 
+bool GraphDriver::Scheduled()
+{
+  GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
+  return mScheduled;
+}
+
 ThreadedDriver::ThreadedDriver(MediaStreamGraphImpl* aGraphImpl)
   : GraphDriver(aGraphImpl)
 { }
 
 class MediaStreamGraphShutdownThreadRunnable : public Runnable {
 public:
   explicit MediaStreamGraphShutdownThreadRunnable(
     already_AddRefed<nsIThread> aThread)
@@ -176,26 +183,26 @@ public:
     , mDriver(aDriver)
   {
   }
   NS_IMETHOD Run() override
   {
     LOG(LogLevel::Debug,
         ("Starting a new system driver for graph %p", mDriver->mGraphImpl));
 
-    GraphDriver* previousDriver = nullptr;
+    RefPtr<GraphDriver> previousDriver;
     {
       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
       previousDriver = mDriver->PreviousDriver();
     }
     if (previousDriver) {
       LOG(LogLevel::Debug,
           ("%p releasing an AudioCallbackDriver(%p), for graph %p",
            mDriver.get(),
-           previousDriver,
+           previousDriver.get(),
            mDriver->GraphImpl()));
       MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
       RefPtr<AsyncCubebTask> releaseEvent =
         new AsyncCubebTask(previousDriver->AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
       releaseEvent->Dispatch();
 
       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
       mDriver->SetPreviousDriver(nullptr);
@@ -219,17 +226,18 @@ ThreadedDriver::Start()
   LOG(LogLevel::Debug,
       ("Starting thread for a SystemClockDriver  %p", mGraphImpl));
   Unused << NS_WARN_IF(mThread);
   if (!mThread) { // Ensure we haven't already started it
     nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
     // Note: mThread may be null during event->Run() if we pass to NewNamedThread!  See AudioInitTask
     nsresult rv = NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread));
     if (NS_SUCCEEDED(rv)) {
-      mThread->EventTarget()->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
+      rv = mThread->EventTarget()->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
+      mScheduled = NS_SUCCEEDED(rv);
     }
   }
 }
 
 void
 ThreadedDriver::Resume()
 {
   Start();
@@ -791,17 +799,18 @@ AudioCallbackDriver::Start()
     }
   }
 
   LOG(LogLevel::Debug,
       ("Starting new audio driver off main thread, "
        "to ensure it runs after previous shutdown."));
   RefPtr<AsyncCubebTask> initEvent =
     new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::INIT);
-  initEvent->Dispatch();
+  nsresult rv = initEvent->Dispatch();
+  mScheduled = NS_SUCCEEDED(rv);
 }
 
 bool
 AudioCallbackDriver::StartStream()
 {
   if (cubeb_stream_start(mAudioStream) != CUBEB_OK) {
     NS_WARNING("Could not start cubeb stream for MSG.");
     return false;
@@ -1064,23 +1073,35 @@ AudioCallbackDriver::DataCallback(const 
   }
   return aFrames;
 }
 
 void
 AudioCallbackDriver::StateCallback(cubeb_state aState)
 {
   LOG(LogLevel::Debug, ("AudioCallbackDriver State: %d", aState));
-  // If we don't have an audio stream here, this means that the stream
-  // initialization has failed. A fallback on a SystemCallDriver will happen at
-  // the callsite of `cubeb_stream_init`.
-  if (aState == CUBEB_STATE_ERROR && mAudioStream) {
+
+  if (aState == CUBEB_STATE_ERROR) {
+    if (!mAudioStream) {
+      // If we don't have an audio stream here, this means that the stream
+      // initialization has failed. A fallback on a SystemCallDriver will happen at
+      // the callsite of `cubeb_stream_init`.
+      return;
+    }
+
+    MonitorAutoLock lock(GraphImpl()->GetMonitor());
+
+    if (NextDriver() && NextDriver()->Scheduled()) {
+      // We are switching to another driver that has already been scheduled
+      // to be initialized and started. There's nothing for us to do here.
+      return;
+    }
+
     // Fall back to a driver using a normal thread. If needed,
     // the graph will try to re-open an audio stream later.
-    MonitorAutoLock lock(GraphImpl()->GetMonitor());
     SystemClockDriver* nextDriver = new SystemClockDriver(GraphImpl());
     SetNextDriver(nextDriver);
     RemoveCallback();
     nextDriver->MarkAsFallback();
     nextDriver->SetGraphTime(this, mIterationStart, mIterationEnd);
     // We're not using SwitchAtNextIteration here, because there
     // won't be a next iteration if we don't restart things manually:
     // the audio stream just signaled that it's in error state.
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -145,16 +145,19 @@ public:
 
   // Those are simply or setting the associated pointer, but assert that the
   // lock is held.
   GraphDriver* NextDriver();
   GraphDriver* PreviousDriver();
   void SetNextDriver(GraphDriver* aNextDriver);
   void SetPreviousDriver(GraphDriver* aPreviousDriver);
 
+  /* Return whether we have been scheduled to start. */
+  bool Scheduled();
+
   /**
    * If we are running a real time graph, get the current time stamp to schedule
    * video frames. This has to be reimplemented by real time drivers.
    */
   virtual TimeStamp GetCurrentTimeStamp() {
     return mCurrentTimeStamp;
   }
 
@@ -248,16 +251,19 @@ protected:
   // check whether we're changing driver (in Switching()), from the graph
   // thread.
   // This must be accessed using the {Set,Get}PreviousDriver methods.
   RefPtr<GraphDriver> mPreviousDriver;
   // This is non-null only when this driver is going to switch to an other
   // driver at the end of this iteration.
   // This must be accessed using the {Set,Get}NextDriver methods.
   RefPtr<GraphDriver> mNextDriver;
+  // This is initially false, but set to true as soon the driver has been
+  // scheduled to start through GraphDriver::Start().
+  bool mScheduled;
   virtual ~GraphDriver()
   { }
 };
 
 class MediaStreamGraphInitThreadRunnable;
 
 /**
  * This class is a driver that manages its own thread.
--- a/dom/plugins/test/mochitest/test_pluginstream_err.html
+++ b/dom/plugins/test/mochitest/test_pluginstream_err.html
@@ -8,17 +8,17 @@ Tests for plugin stream error conditions
 <head>
   <title>NPAPI Stream Error Tests</title>
   <script type="text/javascript" 
           src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="plugin-utils.js"></script>
   <link rel="stylesheet" type="text/css" 
         href="/tests/SimpleTest/test.css" />
 </head>
-<body onload="runNextTest()">
+<body onload="startTests()">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=517078">
   Mozilla Bug 517078</a> - Plugin Stream Error Tests
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
 <div id="test">
 <script class="testbody" type="text/javascript">
@@ -156,13 +156,19 @@ function continueTest() {
   gTestWindow.document.getElementById("test").appendChild(plugin);
 
   gTestWindow.document.getElementById("test")
                       .appendChild(document.createElement("br"));
 
   index++;
 }
 
+function startTests() {
+  SpecialPowers.pushPrefEnv({"set": [
+    ["security.data_uri.block_toplevel_data_uri_navigations", false],
+  ]}, runNextTest);
+}
+
 </script>
 </div>
 </body>
 </html>
 
--- a/dom/storage/StorageObserver.cpp
+++ b/dom/storage/StorageObserver.cpp
@@ -135,18 +135,19 @@ StorageObserver::RemoveSink(StorageObser
   mSinks.RemoveElement(aObs);
 }
 
 void
 StorageObserver::Notify(const char* aTopic,
                         const nsAString& aOriginAttributesPattern,
                         const nsACString& aOriginScope)
 {
-  for (uint32_t i = 0; i < mSinks.Length(); ++i) {
-    StorageObserverSink* sink = mSinks[i];
+  nsTObserverArray<StorageObserverSink*>::ForwardIterator iter(mSinks);
+  while (iter.HasMore()) {
+    StorageObserverSink* sink = iter.GetNext();
     sink->Observe(aTopic, aOriginAttributesPattern, aOriginScope);
   }
 }
 
 void
 StorageObserver::NoteBackgroundThread(nsIEventTarget* aBackgroundThread)
 {
   mBackgroundThread = aBackgroundThread;
--- a/dom/storage/StorageObserver.h
+++ b/dom/storage/StorageObserver.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_StorageObserver_h
 #define mozilla_dom_StorageObserver_h
 
 #include "nsIObserver.h"
 #include "nsITimer.h"
 #include "nsWeakReference.h"
-#include "nsTArray.h"
+#include "nsTObserverArray.h"
 #include "nsString.h"
 
 namespace mozilla {
 namespace dom {
 
 class StorageObserver;
 
 // Implementers are StorageManager and StorageDBParent to forward to
@@ -59,16 +59,16 @@ private:
 
   static void TestingPrefChanged(const char* aPrefName, void* aClosure);
 
   static StorageObserver* sSelf;
 
   nsCOMPtr<nsIEventTarget> mBackgroundThread;
 
   // Weak references
-  nsTArray<StorageObserverSink*> mSinks;
+  nsTObserverArray<StorageObserverSink*> mSinks;
   nsCOMPtr<nsITimer> mDBThreadStartDelayTimer;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_StorageObserver_h
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -641,18 +641,28 @@ public:
         }
         if (aFontEntry->mFamilyName.IsEmpty()) {
             aFontEntry->mFamilyName = Name();
         } else {
             MOZ_ASSERT(aFontEntry->mFamilyName.Equals(Name()));
         }
         aFontEntry->mSkipDefaultFeatureSpaceCheck = mSkipDefaultFeatureSpaceCheck;
         mAvailableFonts.AppendElement(aFontEntry);
-        mIsSimpleFamily = false; // CheckForSimpleFamily may set this later,
-                                 // but at this point we're not sure
+
+        // If we're adding a face to a family that has been marked as "simple",
+        // we need to ensure any null entries are removed, as well as clearing
+        // the flag (which may be set again later).
+        if (mIsSimpleFamily) {
+            for (size_t i = mAvailableFonts.Length() - 1; i-- > 0; ) {
+                if (!mAvailableFonts[i]) {
+                    mAvailableFonts.RemoveElementAt(i);
+                }
+            }
+            mIsSimpleFamily = false;
+        }
     }
 
     // note that the styles for this family have been added
     bool HasStyles() { return mHasStyles; }
     void SetHasStyles(bool aHasStyles) { mHasStyles = aHasStyles; }
 
     // choose a specific face to match a style using CSS font matching
     // rules (weight matching occurs here).  may return a face that doesn't
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5862,26 +5862,21 @@ pref("dom.timeout.max_consecutive_callba
 pref("dom.payments.request.enabled", false);
 pref("dom.payments.loglevel", "Warn");
 
 #ifdef FUZZING
 pref("fuzzing.enabled", false);
 #endif
 
 #if defined(XP_WIN)
-#if defined(NIGHTLY_BUILD)
 pref("layers.mlgpu.enabled", true);
 
 // Both this and the master "enabled" pref must be on to use Advanced Layers
 // on Windows 7.
 pref("layers.mlgpu.enable-on-windows7", true);
-#else
-pref("layers.mlgpu.enabled", false);
-pref("layers.mlgpu.enable-on-windows7", false);
-#endif
 #endif
 
 // Set advanced layers preferences here to have them show up in about:config or
 // to be overridable in reftest.list files. They should pretty much all be set
 // to a value of 2, and the conditional-pref code in gfxPrefs.h will convert
 // it to a boolean as appropriate. In particular, do NOT add ifdefs here to
 // turn these on and off, instead use the conditional-pref code in gfxPrefs.h
 // to do that.
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -26,17 +26,17 @@ function getConsole() {
   });
 }
 
 XPCOMUtils.defineLazyGetter(this, "console", getConsole);
 
 const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
 
 let nextId = 0;
-XPCOMUtils.defineLazyGetter(this, "uniqueProcessID", () => appinfo.uniqueProcessID);
+const uniqueProcessID = String(appinfo.uniqueProcessID);
 
 function getUniqueId() {
   return `${nextId++}-${uniqueProcessID}`;
 }
 
 async function promiseFileContents(path) {
   let res = await OS.File.read(path);
   return res.buffer;
@@ -484,21 +484,17 @@ function defineLazyGetter(object, prop, 
  *        The target message manager on which to send messages, or the
  *        <browser> element which owns it.
  */
 class MessageManagerProxy {
   constructor(target) {
     this.listeners = new DefaultMap(() => new Map());
 
     if (target instanceof Ci.nsIMessageSender) {
-      Object.defineProperty(this, "messageManager", {
-        value: target,
-        configurable: true,
-        writable: true,
-      });
+      this.messageManager = target;
     } else {
       this.addListeners(target);
     }
   }
 
   /**
    * Disposes of the proxy object, removes event listeners, and drops
    * all references to the underlying message manager.
@@ -506,19 +502,18 @@ class MessageManagerProxy {
    * Must be called before the last reference to the proxy is dropped,
    * unless the underlying message manager or <browser> is also being
    * destroyed.
    */
   dispose() {
     if (this.eventTarget) {
       this.removeListeners(this.eventTarget);
       this.eventTarget = null;
-    } else {
-      this.messageManager = null;
     }
+    this.messageManager = null;
   }
 
   /**
    * Returns true if the given target is the same as, or owns, the given
    * message manager.
    *
    * @param {nsIMessageSender|MessageManagerProxy|Element} target
    *        The message manager, MessageManagerProxy, or <browser>
@@ -536,19 +531,16 @@ class MessageManagerProxy {
   }
 
   /**
    * @property {nsIMessageSender|null} messageManager
    *        The message manager that is currently being proxied. This
    *        may change during the life of the proxy object, so should
    *        not be stored elsewhere.
    */
-  get messageManager() {
-    return this.eventTarget && this.eventTarget.messageManager;
-  }
 
   /**
    * Sends a message on the proxied message manager.
    *
    * @param {array} args
    *        Arguments to be passed verbatim to the underlying
    *        sendAsyncMessage method.
    * @returns {undefined}
@@ -617,35 +609,36 @@ class MessageManagerProxy {
    * Adds docShell swap listeners to the message manager owner.
    *
    * @param {Element} target
    *        The target element.
    */
   addListeners(target) {
     target.addEventListener("SwapDocShells", this);
 
+    this.eventTarget = target;
+    this.messageManager = target.messageManager;
+
     for (let {message, listener, listenWhenClosed} of this.iterListeners()) {
-      target.addMessageListener(message, listener, listenWhenClosed);
+      this.messageManager.addMessageListener(message, listener, listenWhenClosed);
     }
-
-    this.eventTarget = target;
   }
 
   /**
    * @private
    * Removes docShell swap listeners to the message manager owner.
    *
    * @param {Element} target
    *        The target element.
    */
   removeListeners(target) {
     target.removeEventListener("SwapDocShells", this);
 
     for (let {message, listener} of this.iterListeners()) {
-      target.removeMessageListener(message, listener);
+      this.messageManager.removeMessageListener(message, listener);
     }
   }
 
   handleEvent(event) {
     if (event.type == "SwapDocShells") {
       this.removeListeners(this.eventTarget);
       this.addListeners(event.detail);
     }
--- a/toolkit/components/extensions/MessageChannel.jsm
+++ b/toolkit/components/extensions/MessageChannel.jsm
@@ -100,23 +100,26 @@ this.EXPORTED_SYMBOLS = ["MessageChannel
 
 /* globals MessageChannel */
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const {
   MessageManagerProxy,
 } = ExtensionUtils;
 
+const {DEBUG} = AppConstants;
+
 /**
  * Handles the mapping and dispatching of messages to their registered
  * handlers. There is one broker per message manager and class of
  * messages. Each class of messages is mapped to one native message
  * name, e.g., "MessageChannel:Message", and is dispatched to handlers
  * based on an internal message name, e.g., "Extension:ExecuteScript".
  */
 class FilteringMessageManager {
@@ -213,55 +216,94 @@ class FilteringMessageManager {
   removeHandler(messageName, handler) {
     if (this.handlers.has(messageName)) {
       this.handlers.get(messageName).delete(handler);
     }
   }
 }
 
 /**
+ * A simplified subclass of FilteringMessageManager that only supports
+ * one handler per message, and does not support filtering.
+ */
+class ResponseManager extends FilteringMessageManager {
+  * getHandlers(messageName, sender, recipient) {
+    let handler = this.handlers.get(messageName);
+    if (handler) {
+      yield handler;
+    }
+  }
+
+  addHandler(messageName, handler) {
+    if (DEBUG && this.handlers.has(messageName)) {
+      throw new Error(`Handler already registered for response ID ${messageName}`);
+    }
+    this.handlers.set(messageName, handler);
+  }
+
+  /**
+   * Unregisters a handler for the given message.
+   *
+   * @param {string} messageName
+   *     The internal message name for which to unregister the handler.
+   * @param {object} handler
+   *     The handler object to unregister.
+   */
+  removeHandler(messageName, handler) {
+    if (DEBUG && this.handlers.get(messageName) !== handler) {
+      throw new Error(`Attempting to remove unexpected response handler for ${messageName}`);
+    }
+    this.handlers.delete(messageName);
+  }
+}
+
+/**
  * Manages mappings of message managers to their corresponding message
  * brokers. Brokers are lazily created for each message manager the
  * first time they are accessed. In the case of content frame message
  * managers, they are also automatically destroyed when the frame
  * unload event fires.
  */
 class FilteringMessageManagerMap extends Map {
   // Unfortunately, we can't use a WeakMap for this, because message
   // managers do not support preserved wrappers.
 
   /**
    * @param {string} messageName
    *     The native message name passed to `FilteringMessageManager` constructors.
    * @param {function} callback
    *     The message callback function passed to
    *     `FilteringMessageManager` constructors.
+   * @param {function} [constructor = FilteringMessageManager]
+   *     The constructor for the message manager class that we're
+   *     mapping to.
    */
-  constructor(messageName, callback) {
+  constructor(messageName, callback, constructor = FilteringMessageManager) {
     super();
 
     this.messageName = messageName;
     this.callback = callback;
+    this._constructor = constructor;
   }
 
   /**
    * Returns, and possibly creates, a message broker for the given
    * message manager.
    *
    * @param {nsIMessageListenerManager} target
    *     The message manager for which to return a broker.
    *
    * @returns {FilteringMessageManager}
    */
   get(target) {
     if (this.has(target)) {
       return super.get(target);
     }
 
-    let broker = new FilteringMessageManager(this.messageName, this.callback, target);
+    let broker = new this._constructor(this.messageName, this.callback, target);
     this.set(target, broker);
 
     if (target instanceof Ci.nsIDOMEventTarget) {
       let onUnload = event => {
         target.removeEventListener("unload", onUnload);
         this.delete(target);
       };
       target.addEventListener("unload", onUnload);
@@ -278,25 +320,51 @@ this.MessageChannel = {
   init() {
     Services.obs.addObserver(this, "message-manager-close");
     Services.obs.addObserver(this, "message-manager-disconnect");
 
     this.messageManagers = new FilteringMessageManagerMap(
       MESSAGE_MESSAGE, this._handleMessage.bind(this));
 
     this.responseManagers = new FilteringMessageManagerMap(
-      MESSAGE_RESPONSE, this._handleResponse.bind(this));
+      MESSAGE_RESPONSE, this._handleResponse.bind(this),
+      ResponseManager);
 
     /**
-     * Contains a list of pending responses, either waiting to be
-     * received or waiting to be sent. @see _addPendingResponse
+     * @property {Set<Deferred>} pendingResponses
+     * Contains a set of pending responses, either waiting to be
+     * received or waiting to be sent.
+     *
+     * The response object must be a deferred promise with the following
+     * properties:
+     *
+     *  promise:
+     *    The promise object which resolves or rejects when the response
+     *    is no longer pending.
+     *
+     *  reject:
+     *    A function which, when called, causes the `promise` object to be
+     *    rejected.
+     *
+     *  sender:
+     *    A sender object, as passed to `sendMessage.
+     *
+     *  messageManager:
+     *    The message manager the response will be sent or received on.
+     *
+     * When the promise resolves or rejects, it must be removed from the
+     * list.
+     *
+     * These values are used to clear pending responses when execution
+     * contexts are destroyed.
      */
     this.pendingResponses = new Set();
 
     /**
+     * @property {LimitedSet<string>} abortedResponses
      * Contains the message name of a limited number of aborted response
      * handlers, the responses for which will be ignored.
      */
     this.abortedResponses = new ExtensionUtils.LimitedSet(30);
   },
 
   RESULT_SUCCESS: 0,
   RESULT_DISCONNECTED: 1,
@@ -539,26 +607,27 @@ this.MessageChannel = {
     deferred.promise = new Promise((resolve, reject) => {
       deferred.resolve = resolve;
       deferred.reject = reject;
     });
     deferred.sender = recipient;
     deferred.messageManager = target;
     deferred.channelId = channelId;
 
-    this._addPendingResponse(deferred);
-
     // The channel ID is used as the message name when routing responses.
     // Add a message listener to the response broker, and remove it once
     // we've gotten (or canceled) a response.
     let broker = this.responseManagers.get(target);
     broker.addHandler(channelId, deferred);
 
+    this.pendingResponses.add(deferred);
+
     let cleanup = () => {
       broker.removeHandler(channelId, deferred);
+      this.pendingResponses.delete(deferred);
     };
     deferred.promise.then(cleanup, cleanup);
 
     try {
       target.sendAsyncMessage(MESSAGE_MESSAGE, message);
     } catch (e) {
       deferred.reject(e);
     }
@@ -645,16 +714,23 @@ this.MessageChannel = {
     let target = new MessageManagerProxy(data.target);
 
     let deferred = {
       sender: data.sender,
       messageManager: target,
       channelId: data.channelId,
       respondingSide: true,
     };
+
+    let cleanup = () => {
+      this.pendingResponses.delete(deferred);
+      target.dispose();
+    };
+    this.pendingResponses.add(deferred);
+
     deferred.promise = new Promise((resolve, reject) => {
       deferred.reject = reject;
 
       this._callHandlers(handlers, data).then(resolve, reject);
       data = null;
     }).then(
       value => {
         let response = {
@@ -694,23 +770,20 @@ this.MessageChannel = {
                            "columnNumber", "message", "stack", "result"]) {
             if (key in error) {
               response.error[key] = error[key];
             }
           }
         }
 
         target.sendAsyncMessage(MESSAGE_RESPONSE, response);
-      }).catch(e => {
+      }).then(cleanup, e => {
+        cleanup();
         Cu.reportError(e);
-      }).then(() => {
-        target.dispose();
       });
-
-    this._addPendingResponse(deferred);
   },
 
   /**
    * Handles message callbacks from the response brokers.
    *
    * Each handler object is a deferred object created by `sendMessage`, and
    * should be resolved or rejected based on the contents of the response.
    *
@@ -733,52 +806,16 @@ this.MessageChannel = {
     } else if (data.result === this.RESULT_SUCCESS) {
       handlers[0].resolve(data.value);
     } else {
       handlers[0].reject(data.error);
     }
   },
 
   /**
-   * Adds a pending response to the the `pendingResponses` list.
-   *
-   * The response object must be a deferred promise with the following
-   * properties:
-   *
-   *  promise:
-   *    The promise object which resolves or rejects when the response
-   *    is no longer pending.
-   *
-   *  reject:
-   *    A function which, when called, causes the `promise` object to be
-   *    rejected.
-   *
-   *  sender:
-   *    A sender object, as passed to `sendMessage.
-   *
-   *  messageManager:
-   *    The message manager the response will be sent or received on.
-   *
-   * When the promise resolves or rejects, it will be removed from the
-   * list.
-   *
-   * These values are used to clear pending responses when execution
-   * contexts are destroyed.
-   *
-   * @param {Deferred} deferred
-   */
-  _addPendingResponse(deferred) {
-    let cleanup = () => {
-      this.pendingResponses.delete(deferred);
-    };
-    this.pendingResponses.add(deferred);
-    deferred.promise.then(cleanup, cleanup);
-  },
-
-  /**
    * Aborts pending message response for the specific channel.
    *
    * @param {string} channelId
    *    A string for channelId of the response.
    * @param {object} reason
    *    An object describing the reason the response was aborted.
    *    Will be passed to the promise rejection handler of the aborted
    *    response.
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -41,47 +41,20 @@ function WebRequestEventManager(context,
       }
       if (filter.tabId != null && browserData.tabId != filter.tabId) {
         return;
       }
       if (filter.windowId != null && browserData.windowId != filter.windowId) {
         return;
       }
 
-      let data2 = {
-        requestId: data.requestId,
-        url: data.url,
-        originUrl: data.originUrl,
-        documentUrl: data.documentUrl,
-        method: data.method,
-        tabId: browserData.tabId,
-        type: data.type,
-        timeStamp: Date.now(),
-        frameId: data.windowId,
-        parentFrameId: data.parentWindowId,
-      };
+      let event = data.serialize(eventName);
+      event.tabId = browserData.tabId;
 
-      const maybeCached = ["onResponseStarted", "onBeforeRedirect", "onCompleted", "onErrorOccurred"];
-      if (maybeCached.includes(eventName)) {
-        data2.fromCache = !!data.fromCache;
-      }
-
-      if ("ip" in data) {
-        data2.ip = data.ip;
-      }
-
-      let optional = ["requestHeaders", "responseHeaders", "statusCode", "statusLine", "error", "redirectUrl",
-                      "requestBody", "scheme", "realm", "isProxy", "challenger", "proxyInfo"];
-      for (let opt of optional) {
-        if (opt in data) {
-          data2[opt] = data[opt];
-        }
-      }
-
-      return fire.sync(data2);
+      return fire.sync(event);
     };
 
     let filter2 = {};
     if (filter.urls) {
       let perms = new MatchPatternSet([...context.extension.whiteListedHosts.patterns,
                                        ...context.extension.optionalOrigins.patterns]);
 
       filter2.urls = new MatchPatternSet(filter.urls);
--- a/toolkit/modules/addons/WebRequest.jsm
+++ b/toolkit/modules/addons/WebRequest.jsm
@@ -178,16 +178,50 @@ class ResponseHeaderChanger extends Head
       if (name.toLowerCase() === "content-type") {
         value = this.channel._contentType || value;
       }
       yield [name, value];
     }
   }
 }
 
+const MAYBE_CACHED_EVENTS = new Set([
+  "onResponseStarted", "onBeforeRedirect", "onCompleted", "onErrorOccurred",
+]);
+
+const OPTIONAL_PROPERTIES = [
+  "requestHeaders", "responseHeaders", "statusCode", "statusLine", "error", "redirectUrl",
+  "requestBody", "scheme", "realm", "isProxy", "challenger", "proxyInfo", "ip",
+];
+
+function serializeRequestData(eventName) {
+  let data = {
+    requestId: this.requestId,
+    url: this.url,
+    originUrl: this.originUrl,
+    documentUrl: this.documentUrl,
+    method: this.method,
+    type: this.type,
+    timeStamp: Date.now(),
+    frameId: this.windowId,
+    parentFrameId: this.parentWindowId,
+  };
+
+  if (MAYBE_CACHED_EVENTS.has(eventName)) {
+    data.fromCache = !!this.fromCache;
+  }
+
+  for (let opt of OPTIONAL_PROPERTIES) {
+    if (typeof this[opt] !== "undefined") {
+      data[opt] = this[opt];
+    }
+  }
+  return data;
+}
+
 var HttpObserverManager;
 
 var nextFakeRequestId = 1;
 
 var ContentPolicyManager = {
   policyData: new Map(),
   policies: new Map(),
   idMap: new Map(),
@@ -208,17 +242,17 @@ var ContentPolicyManager = {
       let callback = this.policies.get(id);
       if (!callback) {
         // It's possible that this listener has been removed and the
         // child hasn't learned yet.
         continue;
       }
       let response = null;
       let listenerKind = "onStop";
-      let data = Object.assign({requestId, browser}, msg.data);
+      let data = Object.assign({requestId, browser, serialize: serializeRequestData}, msg.data);
       data.URI = data.url;
 
       delete data.ids;
       try {
         response = callback(data);
         if (response) {
           if (response.cancel) {
             listenerKind = "onError";
@@ -689,16 +723,18 @@ HttpObserverManager = {
       isSystemPrincipal: channel.isSystemLoad,
 
       windowId: channel.windowId,
       parentWindowId: channel.parentWindowId,
 
       ip: channel.remoteAddress,
 
       proxyInfo: channel.proxyInfo,
+
+      serialize: serializeRequestData,
     };
 
     // force the protocol to be ws again.
     if (data.type == "websocket" && data.url.startsWith("http")) {
       data.url = `ws${data.url.substring(4)}`;
     }
 
     return Object.assign(data, extraData);