Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Wed, 11 Nov 2015 16:42:05 -0800
changeset 308343 e86bdbfed16469a76af03f0682e215ec33b2ee74
parent 308318 8bf46ba0297dfecfdbae08edc1166d8a10bbadf8 (current diff)
parent 308274 1cdabbfe799a4e4a16a49d2d24d7f1e989389f8a (diff)
child 308382 a8ed7dd831d1969a5a1a8636e63bd93d6aeaf94a
push id7465
push usercku@mozilla.com
push dateThu, 12 Nov 2015 09:05:57 +0000
reviewersmerge
milestone45.0a1
Merge inbound to central, a=merge
ipc/chromium/Makefile.in
ipc/glue/Makefile.in
toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in
toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -80,17 +80,18 @@ MediaSourceDecoder::GetSeekable()
 
   media::TimeIntervals seekable;
   double duration = mMediaSource->Duration();
   if (IsNaN(duration)) {
     // Return empty range.
   } else if (duration > 0 && mozilla::IsInfinite(duration)) {
     media::TimeIntervals buffered = GetBuffered();
     if (buffered.Length()) {
-      seekable += media::TimeInterval(buffered.GetStart(), buffered.GetEnd());
+      seekable +=
+        media::TimeInterval(media::TimeUnit::FromSeconds(0), buffered.GetEnd());
     }
   } else {
     seekable += media::TimeInterval(media::TimeUnit::FromSeconds(0),
                                     media::TimeUnit::FromSeconds(duration));
   }
   MSE_DEBUG("ranges=%s", DumpTimeRanges(seekable).get());
   return seekable;
 }
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -522,18 +522,17 @@ already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
                                  ErrorResult& aRv)
 {
   aRealData.ComputeLengthAndData();
   aImagData.ComputeLengthAndData();
 
   if (aRealData.Length() != aImagData.Length() ||
-      aRealData.Length() == 0 ||
-      aRealData.Length() > 4096) {
+      aRealData.Length() == 0) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
   RefPtr<PeriodicWave> periodicWave =
     new PeriodicWave(this, aRealData.Data(), aImagData.Data(),
                      aImagData.Length(), aRv);
   if (aRv.Failed()) {
--- a/dom/media/webaudio/PeriodicWave.cpp
+++ b/dom/media/webaudio/PeriodicWave.cpp
@@ -22,17 +22,16 @@ PeriodicWave::PeriodicWave(AudioContext*
                            const uint32_t aLength,
                            ErrorResult& aRv)
   : mContext(aContext)
 {
   MOZ_ASSERT(aContext);
 
   // Caller should have checked this and thrown.
   MOZ_ASSERT(aLength > 0);
-  MOZ_ASSERT(aLength <= 4096);
   mLength = aLength;
 
   // Copy coefficient data. The two arrays share an allocation.
   mCoefficients = new ThreadSharedFloatArrayBufferList(2);
   float* buffer = static_cast<float*>(malloc(aLength*sizeof(float)*2));
   if (buffer == nullptr) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -26,86 +26,92 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "PeriodicWave.h"
 #include <algorithm>
 #include <cmath>
 #include "mozilla/FFTBlock.h"
 
-const unsigned PeriodicWaveSize = 4096; // This must be a power of two.
-const unsigned NumberOfRanges = 36; // There should be 3 * log2(PeriodicWaveSize) 1/3 octave ranges.
+const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two.
+const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two.
 const float CentsPerRange = 1200 / 3; // 1/3 Octave.
 
 using namespace mozilla;
 using mozilla::dom::OscillatorType;
 
 namespace WebCore {
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::create(float sampleRate,
                      const float* real,
                      const float* imag,
                      size_t numberOfComponents)
 {
-    bool isGood = real && imag && numberOfComponents > 0 &&
-         numberOfComponents <= PeriodicWaveSize;
+    bool isGood = real && imag && numberOfComponents > 0;
     MOZ_ASSERT(isGood);
     if (isGood) {
         RefPtr<PeriodicWave> periodicWave =
-            new PeriodicWave(sampleRate);
+            new PeriodicWave(sampleRate, numberOfComponents);
         periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
         return periodicWave.forget();
     }
     return nullptr;
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSine(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Sine);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSquare(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Square);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSawtooth(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Sawtooth);
     return periodicWave.forget();
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createTriangle(float sampleRate)
 {
     RefPtr<PeriodicWave> periodicWave =
-        new PeriodicWave(sampleRate);
+        new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Triangle);
     return periodicWave.forget();
 }
 
-PeriodicWave::PeriodicWave(float sampleRate)
+PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents)
     : m_sampleRate(sampleRate)
-    , m_periodicWaveSize(PeriodicWaveSize)
-    , m_numberOfRanges(NumberOfRanges)
     , m_centsPerRange(CentsPerRange)
 {
     float nyquist = 0.5 * m_sampleRate;
+
+    if (numberOfComponents <= MinPeriodicWaveSize) {
+        m_periodicWaveSize = MinPeriodicWaveSize;
+    } else {
+        unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
+        m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
+    }
+
+    m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f));
     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
     m_rateScale = m_periodicWaveSize / m_sampleRate;
 }
 
 size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t amount = aMallocSizeOf(this);
 
--- a/dom/media/webaudio/blink/PeriodicWave.h
+++ b/dom/media/webaudio/blink/PeriodicWave.h
@@ -71,17 +71,17 @@ public:
     float rateScale() const { return m_rateScale; }
 
     unsigned periodicWaveSize() const { return m_periodicWaveSize; }
     float sampleRate() const { return m_sampleRate; }
 
     size_t sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
-    explicit PeriodicWave(float sampleRate);
+    explicit PeriodicWave(float sampleRate, size_t numberOfComponents);
     ~PeriodicWave() {}
 
     void generateBasicWaveform(mozilla::dom::OscillatorType);
 
     float m_sampleRate;
     unsigned m_periodicWaveSize;
     unsigned m_numberOfRanges;
     float m_centsPerRange;
--- a/dom/media/webaudio/test/test_periodicWave.html
+++ b/dom/media/webaudio/test/test_periodicWave.html
@@ -32,19 +32,19 @@ addLoadEvent(function() {
   var ac = new AudioContext();
   ac.createPeriodicWave(new Float32Array(4096), new Float32Array(4096));
   expectException(function() {
     ac.createPeriodicWave(new Float32Array(512), imag);
   }, DOMException.NOT_SUPPORTED_ERR);
   expectException(function() {
     ac.createPeriodicWave(new Float32Array(0), new Float32Array(0));
   }, DOMException.NOT_SUPPORTED_ERR);
-  expectException(function() {
+  expectNoException(function() {
     ac.createPeriodicWave(new Float32Array(4097), new Float32Array(4097));
-  }, DOMException.NOT_SUPPORTED_ERR);
+  });
 
   runTest();
 });
 
 var gTest = {
   createGraph: function(context) {
     var merger = context.createChannelMerger();
 
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -131,17 +131,18 @@ public:
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) override { return NS_OK; };
   void AppendToSegment(AudioSegment& aSegment, TrackTicks aSamples);
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
                           StreamTime aDesiredTime) override
   {
-    NS_WARN_IF_FALSE(aDesiredTime <= aSource->GetEndOfAppendedData(aId),
+    NS_WARN_IF_FALSE(!aSource->FindTrack(aId) ||
+                     aDesiredTime <= aSource->GetEndOfAppendedData(aId),
                      "MediaEngineDefaultAudioSource data underrun");
   }
 
   virtual bool IsFake() override {
     return true;
   }
 
   virtual const dom::MediaSourceEnum GetMediaSource() override {
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -3,16 +3,19 @@ skip-if = e10s # Bug ?????? - most of th
 support-files =
   browser_frame_elements.html
   page_privatestorageevent.html
   position.html
   test-console-api.html
   test_bug1004814.html
   worker_bug1004814.js
 
+[browser_test_toolbars_visibility.js]
+support-files =
+  test_new_window_from_content_child.html
 [browser_bug1008941_dismissGeolocationHanger.js]
 skip-if = buildapp == 'mulet'
 [browser_test__content.js]
 [browser_ConsoleAPITests.js]
 [browser_ConsoleStorageAPITests.js]
 [browser_ConsoleStoragePBTest_perwindowpb.js]
 [browser_autofocus_background.js]
 skip-if= buildapp == 'mulet'
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_test_toolbars_visibility.js
@@ -0,0 +1,187 @@
+// Tests that toolbars have proper visibility when opening a new window
+// in either content or chrome context.
+
+const CONTENT_PAGE = "http://www.example.com/browser/dom/tests/browser/test_new_window_from_content_child.html";
+
+/**
+ * This function retrieves the visibility state of the toolbars of a
+ * window within the content context.
+ *
+ * @param aBrowser (<xul:browser>)
+ *        The browser to query for toolbar visibility states
+ * @returns Promise
+ *        A promise that resolves when the toolbar state is retrieved
+ *        within the content context, which value is an object that holds
+ *        the visibility state of the toolbars
+ */
+function getToolbarsFromBrowserContent(aBrowser) {
+  return ContentTask.spawn(aBrowser, {}, function*() {
+    return {
+      toolbar: content.toolbar.visible,
+      menubar: content.menubar.visible,
+      personalbar: content.personalbar.visible,
+      statusbar: content.statusbar.visible,
+      locationbar: content.locationbar.visible,
+    };
+  });
+}
+
+/**
+ * This function retrieves the visibility state of the toolbars of a
+ * window within the chrome context.
+ *
+ * @param win
+ *        the chrome privileged window
+ * @returns object
+ *        an object that holds the visibility state of the toolbars
+ */
+function getToolbarsFromWindowChrome(win) {
+  return {
+    toolbar: win.toolbar.visible,
+    menubar: win.menubar.visible,
+    personalbar: win.personalbar.visible,
+    statusbar: win.statusbar.visible,
+    locationbar: win.locationbar.visible,
+  }
+}
+
+/**
+ * Tests toolbar visibility when opening a window with default parameters.
+ *
+ * @param toolbars
+ *        the visibility state of the toolbar elements
+ */
+function testDefaultToolbars(toolbars) {
+  ok(toolbars.locationbar,
+     "locationbar should be visible on default window.open()");
+  ok(toolbars.menubar,
+     "menubar be visible on default window.open()");
+  ok(toolbars.personalbar,
+     "personalbar should be visible on default window.open()");
+  ok(toolbars.statusbar,
+     "statusbar should be visible on default window.open()");
+  ok(toolbars.toolbar,
+     "toolbar should be visible on default window.open()");
+}
+
+/**
+ * Tests toolbar visibility when opening a window with non default parameters
+ * on the content context.
+ *
+ * Ensure that locationbar can't be hidden in the content context, see bug#337344.
+ *
+ * @param toolbars
+ *        the visibility state of the toolbar elements
+ */
+function testNonDefaultContentToolbars(toolbars) {
+  // Locationbar should always be visible on content context
+  ok(toolbars.locationbar,
+     "locationbar should be visible even with location=no");
+  ok(!toolbars.menubar,
+     "menubar shouldn't be visible when menubar=no");
+  ok(!toolbars.personalbar,
+     "personalbar shouldn't be visible when personalbar=no");
+  // statusbar will report visible=true even when it's hidden because of bug#55820
+  todo(!toolbars.statusbar,
+       "statusbar shouldn't be visible when status=no");
+  ok(!toolbars.toolbar,
+     "toolbar shouldn't be visible when toolbar=no");
+}
+
+/**
+ * Tests toolbar visibility when opening a window with non default parameters
+ * on the chrome context.
+ *
+ * @param toolbars
+ *        the visibility state of the toolbar elements
+ */
+function testNonDefaultChromeToolbars(toolbars) {
+  // None of the toolbars should be visible if hidden with chrome privileges
+  ok(!toolbars.locationbar,
+     "locationbar should be visible on default window.open()");
+  ok(!toolbars.menubar,
+     "menubar be visible on default window.open()");
+  ok(!toolbars.personalbar,
+     "personalbar should be visible on default window.open()");
+  ok(!toolbars.statusbar,
+     "statusbar should be visible on default window.open()");
+  ok(!toolbars.toolbar,
+     "toolbar should be visible on default window.open()");
+}
+
+/**
+ * Ensure that toolbars of a window opened in the content context have the
+ * correct visibility.
+ *
+ * A window opened with default parameters should have all toolbars visible.
+ *
+ * A window opened with "location=no, personalbar=no, toolbar=no, scrollbars=no,
+ * menubar=no, status=no", should only have location visible.
+ */
+add_task(function*() {
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: CONTENT_PAGE,
+  }, function*(browser) {
+    // First, call the default window.open() which will open a new tab
+    let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser);
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#winOpenDefault", {}, browser);
+    let tab = yield newTabPromise;
+
+    // Check that all toolbars are visible
+    let toolbars = yield getToolbarsFromBrowserContent(gBrowser.selectedBrowser);
+    testDefaultToolbars(toolbars);
+
+    // Cleanup
+    yield BrowserTestUtils.removeTab(tab);
+
+    // Now let's open a window with toolbars hidden
+    let winPromise = BrowserTestUtils.waitForNewWindow();
+    yield BrowserTestUtils.synthesizeMouseAtCenter("#winOpenNonDefault", {}, browser);
+    let popupWindow = yield winPromise;
+
+    let popupBrowser = popupWindow.gBrowser.selectedBrowser;
+    yield BrowserTestUtils.browserLoaded(popupBrowser);
+
+    // Test toolbars visibility
+    let popupToolbars = yield getToolbarsFromBrowserContent(popupBrowser);
+    testNonDefaultContentToolbars(popupToolbars);
+
+    // Cleanup
+    yield BrowserTestUtils.closeWindow(popupWindow);
+  });
+});
+
+/**
+ * Ensure that toolbars of a window opened in the chrome context have the
+ * correct visibility.
+ *
+ * A window opened with default parameters should have all toolbars visible.
+ *
+ * A window opened with "location=no, personalbar=no, toolbar=no, scrollbars=no,
+ * menubar=no, status=no", should not have any toolbar visible.
+ */
+add_task(function* () {
+  // First open a default window from this chrome context
+  let defaultWindowPromise = BrowserTestUtils.waitForNewWindow();
+  window.open("about:robots", "_blank");
+  let defaultWindow = yield defaultWindowPromise;
+
+  // Check that all toolbars are visible
+  let toolbars = getToolbarsFromWindowChrome(defaultWindow);
+  testDefaultToolbars(toolbars);
+
+  // Now lets open a window with toolbars hidden from this chrome context
+  let features = "location=no, personalbar=no, toolbar=no, scrollbars=no, menubar=no, status=no";
+  let popupWindowPromise = BrowserTestUtils.waitForNewWindow();
+  window.open("about:robots", "_blank", features);
+  let popupWindow = yield popupWindowPromise;
+
+  // Test none of the tooolbars are visible
+  let hiddenToolbars = getToolbarsFromWindowChrome(popupWindow);
+  testNonDefaultChromeToolbars(hiddenToolbars);
+
+  // Cleanup
+  yield BrowserTestUtils.closeWindow(defaultWindow);
+  yield BrowserTestUtils.closeWindow(popupWindow);
+});
--- a/dom/tests/browser/test_new_window_from_content_child.html
+++ b/dom/tests/browser/test_new_window_from_content_child.html
@@ -1,19 +1,19 @@
 <!DOCTYPE html>
 <head>
   <meta charset="utf-8">
   <title>Test popup window opening behaviour</title>
 </head>
 <body>
   <p><a id="winOpenDefault" href="#" onclick="return openWindow();">Open a new window via window.open with default features.</a></p>
-  <p><a id="winOpenNonDefault" href="#" onclick="return openWindow('resizable=no, toolbar=no, scrollbars=no, menubar=no, status=no, directories=no, height=100, width=500');">Open a new window via window.open with non-default features.</a></p>
+  <p><a id="winOpenNonDefault" href="#" onclick="return openWindow('resizable=no, location=no, personalbar=no, toolbar=no, scrollbars=no, menubar=no, status=no, directories=no, height=100, width=500');">Open a new window via window.open with non-default features.</a></p>
   <p><a id="winOpenDialog" href="#" onclick="return openWindow('dialog=yes');">Open a new window via window.open with dialog=1.</a></p>
   <p><a id="targetBlank" href="about:robots" target="_blank">Open a new window via target="_blank".</a></p>
 </body>
 </html>
 
 <script>
 function openWindow(aFeatures="") {
   window.open("about:robots", "_blank", aFeatures);
   return false;
 }
-</script>
\ No newline at end of file
+</script>
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1521,17 +1521,17 @@ ServiceWorkerManager::Register(nsIDOMWin
 
   nsCString cleanedScope;
   rv = aScopeURI->GetSpecIgnoringRef(cleanedScope);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return NS_ERROR_FAILURE;
   }
 
   nsAutoCString spec;
-  rv = aScriptURI->GetSpec(spec);
+  rv = aScriptURI->GetSpecIgnoringRef(spec);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   nsCOMPtr<nsIGlobalObject> sgo = do_QueryInterface(window);
   ErrorResult result;
   RefPtr<Promise> promise = Promise::Create(sgo, result);
   if (result.Failed()) {
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -129,17 +129,22 @@ private:
 /// we will still send a printf.
 
 // The range is due to the values set in Histograms.json
 enum class LogReason : int {
   MustBeMoreThanThis = -1,
   // Start.  Do not insert, always add at end.  If you remove items,
   // make sure the other items retain their values.
   D3D11InvalidCallDeviceRemoved = 0,
-  D3D11InvalidCall = 1,
+  D3D11InvalidCall,
+  D3DLockTimeout,
+  D3D10FinalizeFrame,
+  D3D11FinalizeFrame,
+  D3D10SyncLock,
+  D3D11SyncLock,
   // End
   MustBeLessThanThis = 101,
 };
 
 struct BasicLogger
 {
   // For efficiency, this method exists and copies the logic of the
   // OutputMessage below.  If making any changes here, also make it
@@ -560,17 +565,17 @@ typedef Log<LOG_CRITICAL, CriticalLogger
 #define gfxWarningOnce static gfxWarning GFX_LOGGING_GLUE(sOnceAtLine,__LINE__) = gfxWarning
 
 // In the debug build, this is equivalent to the default gfxCriticalError.
 // In the non-debug build, on nightly and dev edition, it will MOZ_CRASH.
 // On beta and release versions, it will telemetry count, but proceed.
 //
 // You should create a (new) enum in the LogReason and use it for the reason
 // parameter to ensure uniqueness.
-#define gfxDevCrash(reason) gfxCriticalError(int(LogOptions::AutoPrefix) | int(LogOptions::AssertOnCall) | int(LogOptions::CrashAction), (reason))
+#define gfxDevCrash(reason) gfxCriticalError(int(gfx::LogOptions::AutoPrefix) | int(gfx::LogOptions::AssertOnCall) | int(gfx::LogOptions::CrashAction), (reason))
 
 // See nsDebug.h and the NS_WARN_IF macro
 
 #ifdef __cplusplus
  // For now, have MOZ2D_ERROR_IF available in debug and non-debug builds
 inline bool MOZ2D_error_if_impl(bool aCondition, const char* aExpr,
                                 const char* aFile, int32_t aLine)
 {
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -645,22 +645,21 @@ CairoImage::GetTextureClient(Compositabl
                                                            surface->GetSize(),
                                                            BackendSelector::Content,
                                                            TextureFlags::DEFAULT);
   }
   if (!textureClient) {
     return nullptr;
   }
 
-  if (!textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+  TextureClientAutoLock autoLock(textureClient, OpenMode::OPEN_WRITE_ONLY);
+  if (!autoLock.Succeeded()) {
     return nullptr;
   }
 
-  TextureClientAutoUnlock autoUnlock(textureClient);
-
   textureClient->UpdateFromSurface(surface);
 
   textureClient->SyncWithObject(forwarder->GetSyncObject());
 
   mTextureClients.Put(forwarder->GetSerial(), textureClient);
   return textureClient;
 }
 
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -93,32 +93,30 @@ CanvasClient2D::Update(gfx::IntSize aSiz
       NS_WARNING("Failed to allocate the TextureClient");
       return;
     }
     MOZ_ASSERT(mBuffer->CanExposeDrawTarget());
 
     bufferCreated = true;
   }
 
-  if (!mBuffer->Lock(OpenMode::OPEN_WRITE_ONLY)) {
-    mBuffer = nullptr;
-    return;
-  }
-
   bool updated = false;
   {
-    // Restrict drawTarget to a scope so that terminates before Unlock.
-    RefPtr<DrawTarget> target =
-      mBuffer->BorrowDrawTarget();
+    TextureClientAutoLock autoLock(mBuffer, OpenMode::OPEN_WRITE_ONLY);
+    if (!autoLock.Succeeded()) {
+      mBuffer = nullptr;
+      return;
+    }
+
+    RefPtr<DrawTarget> target = mBuffer->BorrowDrawTarget();
     if (target) {
       aLayer->UpdateTarget(target);
       updated = true;
     }
   }
-  mBuffer->Unlock();
 
   if (bufferCreated && !AddTextureClient(mBuffer)) {
     mBuffer = nullptr;
     return;
   }
 
   if (updated) {
     nsAutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
@@ -281,17 +279,19 @@ TexClientFromReadback(SharedSurface* src
       MOZ_CRASH("Bad `read{Format,Type}`.");
     }
 
     MOZ_ASSERT(texClient);
     if (!texClient)
         return nullptr;
 
     // With a texClient, we can lock for writing.
-    MOZ_ALWAYS_TRUE( texClient->Lock(OpenMode::OPEN_WRITE) );
+    TextureClientAutoLock autoLock(texClient, OpenMode::OPEN_WRITE);
+    DebugOnly<bool> succeeded = autoLock.Succeeded();
+    MOZ_ASSERT(succeeded, "texture should have locked");
 
     uint8_t* lockedBytes = texClient->GetLockedData();
 
     // ReadPixels from the current FB into lockedBits.
     auto width = src->mSize.width;
     auto height = src->mSize.height;
 
     {
@@ -313,18 +313,16 @@ TexClientFromReadback(SharedSurface* src
       uint8_t* itr = lockedBytes;
       for (size_t i = 0; i < pixels; i++) {
         SwapRB_R8G8B8A8(itr);
         itr += 4;
       }
 
       texClient->RemoveFlags(TextureFlags::RB_SWAPPED);
     }
-
-    texClient->Unlock();
   }
 
   return texClient.forget();
 }
 
 ////////////////////////////////////////
 
 static already_AddRefed<SharedSurfaceTextureClient>
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -9,16 +9,17 @@
 #include "gfxPlatform.h"                // for gfxPlatform
 #include "gfxEnv.h"                     // for gfxEnv
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "gfxPoint.h"                   // for IntSize, gfxPoint
 #include "gfxTeeSurface.h"              // for gfxTeeSurface
 #include "gfxUtils.h"                   // for gfxUtils
 #include "ipc/ShadowLayers.h"           // for ShadowLayerForwarder
 #include "mozilla/ArrayUtils.h"         // for ArrayLength
+#include "mozilla/Maybe.h"
 #include "mozilla/gfx/2D.h"             // for DrawTarget, Factory
 #include "mozilla/gfx/BasePoint.h"      // for BasePoint
 #include "mozilla/gfx/BaseSize.h"       // for BaseSize
 #include "mozilla/gfx/Rect.h"           // for Rect
 #include "mozilla/gfx/Types.h"
 #include "mozilla/layers/CompositorChild.h" // for CompositorChild
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/LayersMessages.h"  // for ThebesBufferData
@@ -565,43 +566,40 @@ ContentClientDoubleBuffered::FinalizeFra
   // nothing to sync at all, there is nothing to do and we can go home early.
   updateRegion.Sub(updateRegion, aRegionToDraw);
   if (updateRegion.IsEmpty()) {
     return;
   }
 
   // We need to ensure that we lock these two buffers in the same
   // order as the compositor to prevent deadlocks.
-  if (!mFrontClient->Lock(OpenMode::OPEN_READ_ONLY)) {
-    return;
-  }
-  if (mFrontClientOnWhite &&
-      !mFrontClientOnWhite->Lock(OpenMode::OPEN_READ_ONLY)) {
-    mFrontClient->Unlock();
+  TextureClientAutoLock frontLock(mFrontClient, OpenMode::OPEN_READ_ONLY);
+  if (!frontLock.Succeeded()) {
     return;
   }
-  {
-    // Restrict the DrawTargets and frontBuffer to a scope to make
-    // sure there is no more external references to the DrawTargets
-    // when we Unlock the TextureClients.
-    RefPtr<SourceSurface> surf = mFrontClient->BorrowDrawTarget()->Snapshot();
-    RefPtr<SourceSurface> surfOnWhite = mFrontClientOnWhite
-      ? mFrontClientOnWhite->BorrowDrawTarget()->Snapshot()
-      : nullptr;
-    SourceRotatedBuffer frontBuffer(surf,
-                                    surfOnWhite,
-                                    mFrontBufferRect,
-                                    mFrontBufferRotation);
-    UpdateDestinationFrom(frontBuffer, updateRegion);
+  Maybe<TextureClientAutoLock> frontOnWhiteLock;
+  if (mFrontClientOnWhite) {
+    frontOnWhiteLock.emplace(mFrontClientOnWhite, OpenMode::OPEN_READ_ONLY);
+    if (!frontOnWhiteLock->Succeeded()) {
+      return;
+    }
   }
 
-  mFrontClient->Unlock();
-  if (mFrontClientOnWhite) {
-    mFrontClientOnWhite->Unlock();
-  }
+  // Restrict the DrawTargets and frontBuffer to a scope to make
+  // sure there is no more external references to the DrawTargets
+  // when we Unlock the TextureClients.
+  RefPtr<SourceSurface> surf = mFrontClient->BorrowDrawTarget()->Snapshot();
+  RefPtr<SourceSurface> surfOnWhite = mFrontClientOnWhite
+    ? mFrontClientOnWhite->BorrowDrawTarget()->Snapshot()
+    : nullptr;
+  SourceRotatedBuffer frontBuffer(surf,
+                                  surfOnWhite,
+                                  mFrontBufferRect,
+                                  mFrontBufferRotation);
+  UpdateDestinationFrom(frontBuffer, updateRegion);
 }
 
 void
 ContentClientDoubleBuffered::EnsureBackBufferIfFrontBuffer()
 {
   if (!mTextureClient && mFrontClient) {
     CreateBackBuffer(mFrontBufferRect);
 
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -180,27 +180,30 @@ ImageClientSingle::UpdateImage(ImageCont
         const PlanarYCbCrData* data = ycbcr->GetData();
         if (!data) {
           return false;
         }
         texture = TextureClient::CreateForYCbCr(GetForwarder(),
           data->mYSize, data->mCbCrSize, data->mStereoMode,
           TextureFlags::DEFAULT | mTextureFlags
         );
-        if (!texture || !texture->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+        if (!texture) {
           return false;
         }
+
+        TextureClientAutoLock autoLock(texture, OpenMode::OPEN_WRITE_ONLY);
+        if (!autoLock.Succeeded()) {
+          return false;
+        }
+
         bool status = texture->AsTextureClientYCbCr()->UpdateYCbCr(*data);
         MOZ_ASSERT(status);
-
-        texture->Unlock();
         if (!status) {
           return false;
         }
-
       } else if (image->GetFormat() == ImageFormat::SURFACE_TEXTURE ||
                  image->GetFormat() == ImageFormat::EGLIMAGE) {
         gfx::IntSize size = image->GetSize();
 
         if (image->GetFormat() == ImageFormat::EGLIMAGE) {
           EGLImageImage* typedImage = static_cast<EGLImageImage*>(image);
           texture = new EGLImageTextureClient(GetForwarder(),
                                               mTextureFlags,
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -7,16 +7,17 @@
 #define MOZILLA_GFX_TEXTURECLIENT_H
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint8_t, uint64_t
 #include "GLTextureImage.h"             // for TextureImage
 #include "ImageTypes.h"                 // for StereoMode
 #include "mozilla/Assertions.h"         // for MOZ_ASSERT, etc
 #include "mozilla/Attributes.h"         // for override
+#include "mozilla/DebugOnly.h"
 #include "mozilla/RefPtr.h"             // for RefPtr, RefCounted
 #include "mozilla/gfx/2D.h"             // for DrawTarget
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/FenceUtils.h"  // for FenceHandle
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
@@ -715,27 +716,49 @@ public:
 
   virtual bool HasInternalBuffer() const override { return true; }
 
 protected:
   uint8_t* mBuffer;
   size_t mBufSize;
 };
 
-struct TextureClientAutoUnlock
+// Automatically lock and unlock a texture. Since texture locking is fallible,
+// Succeeded() must be checked on the guard object before proceeding.
+class MOZ_RAII TextureClientAutoLock
 {
-  TextureClient* mTexture;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
+
+public:
+  TextureClientAutoLock(TextureClient* aTexture, OpenMode aMode
+                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+   : mTexture(aTexture),
+     mSucceeded(false)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
-  explicit TextureClientAutoUnlock(TextureClient* aTexture)
-  : mTexture(aTexture) {}
+    mSucceeded = mTexture->Lock(aMode);
+    mChecked = false;
+  }
+  ~TextureClientAutoLock() {
+    MOZ_ASSERT(mChecked);
+    if (mSucceeded) {
+      mTexture->Unlock();
+    }
+  }
 
-  ~TextureClientAutoUnlock()
-  {
-    mTexture->Unlock();
+  bool Succeeded() {
+    mChecked = true;
+    return mSucceeded;
   }
+
+private:
+  TextureClient* mTexture;
+  DebugOnly<bool> mChecked;
+  bool mSucceeded;
 };
 
 class KeepAlive
 {
 public:
   virtual ~KeepAlive() {}
 };
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -596,30 +596,29 @@ TileClient::Flip()
   mInvalidBack = invalidFront;
 }
 
 static bool
 CopyFrontToBack(TextureClient* aFront,
                 TextureClient* aBack,
                 const gfx::IntRect& aRectToCopy)
 {
-  if (!aFront->Lock(OpenMode::OPEN_READ)) {
+  TextureClientAutoLock frontLock(aFront, OpenMode::OPEN_READ);
+  if (!frontLock.Succeeded()) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's front buffer";
     return false;
   }
 
   if (!aBack->Lock(OpenMode::OPEN_READ_WRITE)) {
     gfxCriticalError() << "[Tiling:Client] Failed to lock the tile's back buffer";
     return false;
   }
 
   gfx::IntPoint rectToCopyTopLeft = aRectToCopy.TopLeft();
   aFront->CopyToTextureClient(aBack, &aRectToCopy, &rectToCopyTopLeft);
-
-  aFront->Unlock();
   return true;
 }
 
 void
 TileClient::ValidateBackBufferFromFront(const nsIntRegion& aDirtyRegion,
                                         nsIntRegion& aAddPaintedRegion)
 {
   if (mBackBuffer && mFrontBuffer) {
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -127,17 +127,17 @@ static bool LockD3DTexture(T* aTexture)
 {
   MOZ_ASSERT(aTexture);
   RefPtr<IDXGIKeyedMutex> mutex;
   aTexture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
   // Textures created by the DXVA decoders don't have a mutex for synchronization
   if (mutex) {
     HRESULT hr = mutex->AcquireSync(0, 10000);
     if (hr == WAIT_TIMEOUT) {
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::D3DLockTimeout) << "D3D lock mutex timeout";
     }
 
     if (FAILED(hr)) {
       NS_WARNING("Failed to lock the texture");
       return false;
     }
   }
   return true;
@@ -1240,65 +1240,69 @@ SyncObjectD3D11::FinalizeFrame()
     
     if (FAILED(hr) || !mD3D10Texture) {
       gfxCriticalError() << "Failed to D3D10 OpenSharedResource for frame finalization: " << hexa(hr);
 
       if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
         return;
       }
 
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::D3D10FinalizeFrame) << "Without device reset: " << hexa(hr);
     }
 
     // test QI
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
     if (FAILED(hr) || !mutex) {
+      // Leave both the critical error and MOZ_CRASH for now; the critical error lets
+      // us "save" the hr value.  We will probably eventuall replace this with gfxDevCrash.
       gfxCriticalError() << "Failed to get KeyedMutex: " << hexa(hr);
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: Cannot get D3D10 KeyedMutex");
     }
   }
 
   if (!mD3D11Texture && mD3D11SyncedTextures.size()) {
     ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
 
     hr = device->OpenSharedResource(mHandle, __uuidof(ID3D11Texture2D), (void**)(ID3D11Texture2D**)getter_AddRefs(mD3D11Texture));
     
     if (FAILED(hr) || !mD3D11Texture) {
       gfxCriticalError() << "Failed to D3D11 OpenSharedResource for frame finalization: " << hexa(hr);
 
       if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
         return;
       }
 
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::D3D11FinalizeFrame) << "Without device reset: " << hexa(hr);
     }
 
     // test QI
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
 
     if (FAILED(hr) || !mutex) {
+      // Leave both the critical error and MOZ_CRASH for now; the critical error lets
+      // us "save" the hr value.  We will probably eventuall replace this with gfxDevCrash.
       gfxCriticalError() << "Failed to get KeyedMutex: " << hexa(hr);
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: Cannot get D3D11 KeyedMutex");
     }
   }
 
   if (mD3D10SyncedTextures.size()) {
     RefPtr<IDXGIKeyedMutex> mutex;
     hr = mD3D10Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
     hr = mutex->AcquireSync(0, 20000);
 
     if (hr == WAIT_TIMEOUT) {
       if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
         gfxWarning() << "AcquireSync timed out because of device reset.";
         return;
       }
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::D3D10SyncLock) << "Timeout on the D3D10 sync lock";
     }
 
     D3D10_BOX box;
     box.front = box.top = box.left = 0;
     box.back = box.bottom = box.right = 1;
 
     ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
@@ -1316,30 +1320,30 @@ SyncObjectD3D11::FinalizeFrame()
     hr = mD3D11Texture->QueryInterface((IDXGIKeyedMutex**)getter_AddRefs(mutex));
     hr = mutex->AcquireSync(0, 20000);
 
     if (hr == WAIT_TIMEOUT) {
       if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
         gfxWarning() << "AcquireSync timed out because of device reset.";
         return;
       }
-      MOZ_CRASH();
+      gfxDevCrash(LogReason::D3D11SyncLock) << "Timeout on the D3D11 sync lock";
     }
 
     D3D11_BOX box;
     box.front = box.top = box.left = 0;
     box.back = box.bottom = box.right = 1;
 
     ID3D11Device* dev = gfxWindowsPlatform::GetPlatform()->GetD3D11ContentDevice();
 
     if (!dev) {
       if (gfxWindowsPlatform::GetPlatform()->DidRenderingDeviceReset()) {
         return;
       }
-      MOZ_CRASH();
+      MOZ_CRASH("GFX: Invalid D3D11 content device");
     }
 
     RefPtr<ID3D11DeviceContext> ctx;
     dev->GetImmediateContext(getter_AddRefs(ctx));
 
     for (auto iter = mD3D11SyncedTextures.begin(); iter != mD3D11SyncedTextures.end(); iter++) {
       ctx->CopySubresourceRegion(mD3D11Texture, 0, 0, 0, 0, *iter, 0, &box);
     }
--- a/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
+++ b/gfx/layers/ipc/SharedPlanarYCbCrImage.cpp
@@ -85,21 +85,23 @@ SharedPlanarYCbCrImage::SetData(const Pl
   // allocate it. This code path is slower than the one used when Allocate has
   // been called since it will trigger a full copy.
   PlanarYCbCrData data = aData;
   if (!mTextureClient && !Allocate(data)) {
     return false;
   }
 
   MOZ_ASSERT(mTextureClient->AsTextureClientYCbCr());
-  if (!mTextureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
+
+  TextureClientAutoLock autoLock(mTextureClient, OpenMode::OPEN_WRITE_ONLY);
+  if (!autoLock.Succeeded()) {
     MOZ_ASSERT(false, "Failed to lock the texture.");
     return false;
   }
-  TextureClientAutoUnlock unlock(mTextureClient);
+
   if (!mTextureClient->AsTextureClientYCbCr()->UpdateYCbCr(aData)) {
     MOZ_ASSERT(false, "Failed to copy YCbCr data into the TextureClient");
     return false;
   }
   mTextureClient->MarkImmutable();
   return true;
 }
 
--- a/gfx/thebes/gfx2DGlue.h
+++ b/gfx/thebes/gfx2DGlue.h
@@ -46,21 +46,31 @@ inline gfxMatrix ThebesMatrix(const Matr
                    aMatrix._22, aMatrix._31, aMatrix._32);
 }
 
 inline Point ToPoint(const gfxPoint &aPoint)
 {
   return Point(Float(aPoint.x), Float(aPoint.y));
 }
 
+inline Size ToSize(const gfxSize &aSize)
+{
+  return Size(Float(aSize.width), Float(aSize.height));
+}
+
 inline gfxPoint ThebesPoint(const Point &aPoint)
 {
   return gfxPoint(aPoint.x, aPoint.y);
 }
 
+inline gfxSize ThebesSize(const Size &aSize)
+{
+  return gfxSize(aSize.width, aSize.height);
+}
+
 inline gfxRect ThebesRect(const Rect &aRect)
 {
   return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
 inline gfxRect ThebesRect(const RectDouble &aRect)
 {
   return gfxRect(aRect.x, aRect.y, aRect.width, aRect.height);
--- a/gfx/thebes/gfxFcPlatformFontList.cpp
+++ b/gfx/thebes/gfxFcPlatformFontList.cpp
@@ -842,41 +842,54 @@ gfxFontconfigFontFamily::FindStyleVariat
 {
     if (mHasStyles) {
         return;
     }
 
     // add font entries for each of the faces
     uint32_t numFonts = mFontPatterns.Length();
     NS_ASSERTION(numFonts, "font family containing no faces!!");
+    uint32_t numRegularFaces = 0;
     for (uint32_t i = 0; i < numFonts; i++) {
         FcPattern* face = mFontPatterns[i];
 
         // figure out the psname/fullname and choose which to use as the facename
         nsAutoString psname, fullname;
         GetFaceNames(face, mName, psname, fullname);
         const nsAutoString& faceName = !psname.IsEmpty() ? psname : fullname;
 
         gfxFontconfigFontEntry *fontEntry =
             new gfxFontconfigFontEntry(faceName, face);
         AddFontEntry(fontEntry);
 
+        if (fontEntry->IsUpright() &&
+            fontEntry->Weight() == NS_FONT_WEIGHT_NORMAL &&
+            fontEntry->Stretch() == NS_FONT_STRETCH_NORMAL) {
+            numRegularFaces++;
+        }
+
         if (LOG_FONTLIST_ENABLED()) {
             LOG_FONTLIST(("(fontlist) added (%s) to family (%s)"
                  " with style: %s weight: %d stretch: %d"
                  " psname: %s fullname: %s",
                  NS_ConvertUTF16toUTF8(fontEntry->Name()).get(),
                  NS_ConvertUTF16toUTF8(Name()).get(),
                  (fontEntry->IsItalic()) ?
                   "italic" : (fontEntry->IsOblique() ? "oblique" : "normal"),
                  fontEntry->Weight(), fontEntry->Stretch(),
                  NS_ConvertUTF16toUTF8(psname).get(),
                  NS_ConvertUTF16toUTF8(fullname).get()));
         }
     }
+
+    // somewhat arbitrary, but define a family with two or more regular
+    // faces as a family for which intra-family fallback should be used
+    if (numRegularFaces > 1) {
+        mCheckForFallbackFaces = true;
+    }
     mFaceNamesInitialized = true;
     mFontPatterns.Clear();
     SetHasStyles(true);
 }
 
 void
 gfxFontconfigFontFamily::AddFontPattern(FcPattern* aFontPattern)
 {
--- a/gfx/thebes/gfxFontEntry.h
+++ b/gfx/thebes/gfxFontEntry.h
@@ -658,17 +658,18 @@ public:
         mName(aName),
         mOtherFamilyNamesInitialized(false),
         mHasOtherFamilyNames(false),
         mFaceNamesInitialized(false),
         mHasStyles(false),
         mIsSimpleFamily(false),
         mIsBadUnderlineFamily(false),
         mFamilyCharacterMapInitialized(false),
-        mSkipDefaultFeatureSpaceCheck(false)
+        mSkipDefaultFeatureSpaceCheck(false),
+        mCheckForFallbackFaces(false)
         { }
 
     const nsString& Name() { return mName; }
 
     virtual void LocalizedName(nsAString& aLocalizedName);
     virtual bool HasOtherFamilyNames();
     
     nsTArray<RefPtr<gfxFontEntry> >& GetFontList() { return mAvailableFonts; }
@@ -762,16 +763,17 @@ public:
     void SetBadUnderlineFamily() {
         mIsBadUnderlineFamily = true;
         if (mHasStyles) {
             SetBadUnderlineFonts();
         }
     }
 
     bool IsBadUnderlineFamily() const { return mIsBadUnderlineFamily; }
+    bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
 
     // sort available fonts to put preferred (standard) faces towards the end
     void SortAvailableFonts();
 
     // check whether the family fits into the simple 4-face model,
     // so we can use simplified style-matching;
     // if so set the mIsSimpleFamily flag (defaults to False before we've checked)
     void CheckForSimpleFamily();
@@ -817,16 +819,17 @@ protected:
     bool mOtherFamilyNamesInitialized : 1;
     bool mHasOtherFamilyNames : 1;
     bool mFaceNamesInitialized : 1;
     bool mHasStyles : 1;
     bool mIsSimpleFamily : 1;
     bool mIsBadUnderlineFamily : 1;
     bool mFamilyCharacterMapInitialized : 1;
     bool mSkipDefaultFeatureSpaceCheck : 1;
+    bool mCheckForFallbackFaces : 1;  // check other faces for character
 
     enum {
         // for "simple" families, the faces are stored in mAvailableFonts
         // with fixed positions:
         kRegularFaceIndex    = 0,
         kBoldFaceIndex       = 1,
         kItalicFaceIndex     = 2,
         kBoldItalicFaceIndex = 3,
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -1643,16 +1643,22 @@ gfxFontGroup::AddFamilyToFontList(gfxFon
         if (!HasFont(fe)) {
             FamilyFace ff(aFamily, fe, needsBold);
             if (fe->mIsUserFontContainer) {
                 ff.CheckState(mSkipDrawing);
             }
             mFonts.AppendElement(ff);
         }
     }
+    // for a family marked as "check fallback faces", only mark the last
+    // entry so that fallbacks for a family are only checked once
+    if (aFamily->CheckForFallbackFaces() &&
+        !fontEntryList.IsEmpty() && !mFonts.IsEmpty()) {
+        mFonts.LastElement().SetCheckForFallbackFaces();
+    }
 }
 
 bool
 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
 {
     uint32_t count = mFonts.Length();
     for (uint32_t i = 0; i < count; ++i) {
         if (mFonts[i].FontEntry() == aFontEntry) {
@@ -2550,16 +2556,33 @@ gfxFontGroup::FindNonItalicFaceForChar(g
     if (!fe->HasCharacter(aCh)) {
         return nullptr;
     }
 
     RefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
     return font.forget();
 }
 
+already_AddRefed<gfxFont>
+gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
+                                      int32_t aRunScript)
+{
+    GlobalFontMatch data(aCh, aRunScript, &mStyle);
+    aFamily->SearchAllFontsForChar(&data);
+    gfxFontEntry* fe = data.mBestMatch;
+    if (!fe) {
+        return nullptr;
+    }
+
+    bool needsBold = mStyle.weight >= 600 && !fe->IsBold() &&
+                     mStyle.allowSyntheticWeight;
+    RefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
+    return font.forget();
+}
+
 gfxFloat
 gfxFontGroup::GetUnderlineOffset()
 {
     if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
         // if the fontlist contains a bad underline font, make the underline
         // offset the min of the first valid font and bad font underline offsets
         uint32_t len = mFonts.Length();
         for (uint32_t i = 0; i < len; i++) {
@@ -2614,20 +2637,27 @@ gfxFontGroup::FindFontForChar(uint32_t a
     if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
         RefPtr<gfxFont> firstFont = GetFontAt(0, aCh);
         if (firstFont) {
             if (firstFont->HasCharacter(aCh)) {
                 *aMatchType = gfxTextRange::kFontGroup;
                 return firstFont.forget();
             }
 
-            // If italic, test the regular face to see if it supports character.
-            // Only do this for platform fonts, not userfonts.
-            if (mStyle.style != NS_FONT_STYLE_NORMAL &&
-                !firstFont->GetFontEntry()->IsUserFont()) {
+            if (mFonts[0].CheckForFallbackFaces()) {
+                RefPtr<gfxFont> font =
+                    FindFallbackFaceForChar(mFonts[0].Family(), aCh, aRunScript);
+                if (font) {
+                    *aMatchType = gfxTextRange::kFontGroup;
+                    return font.forget();
+                }
+            } else if (mStyle.style != NS_FONT_STYLE_NORMAL &&
+                       !firstFont->GetFontEntry()->IsUserFont()) {
+                // If italic, test the regular face to see if it supports
+                // character. Only do this for platform fonts, not userfonts.
                 RefPtr<gfxFont> font =
                     FindNonItalicFaceForChar(mFonts[0].Family(), aCh);
                 if (font) {
                     *aMatchType = gfxTextRange::kFontGroup;
                     return font.forget();
                 }
             }
         }
@@ -2717,27 +2747,40 @@ gfxFontGroup::FindFontForChar(uint32_t a
             // build the font via GetFontAt
             font = GetFontAt(i, aCh);
             if (font) {
                 *aMatchType = gfxTextRange::kFontGroup;
                 return font.forget();
             }
         }
 
-        // If italic, test the regular face to see if it supports the character.
-        // Only do this for platform fonts, not userfonts.
-        fe = ff.FontEntry();
-        if (mStyle.style != NS_FONT_STYLE_NORMAL &&
-            !fe->mIsUserFontContainer &&
-            !fe->IsUserFont()) {
-            font = FindNonItalicFaceForChar(ff.Family(), aCh);
+        // check other family faces if needed
+        if (ff.CheckForFallbackFaces()) {
+            NS_ASSERTION(i == 0 ? true :
+                         !mFonts[i-1].CheckForFallbackFaces() ||
+                         !mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
+                         "should only do fallback once per font family");
+            font = FindFallbackFaceForChar(ff.Family(), aCh, aRunScript);
             if (font) {
                 *aMatchType = gfxTextRange::kFontGroup;
                 return font.forget();
             }
+        } else {
+            // If italic, test the regular face to see if it supports the
+            // character. Only do this for platform fonts, not userfonts.
+            fe = ff.FontEntry();
+            if (mStyle.style != NS_FONT_STYLE_NORMAL &&
+                !fe->mIsUserFontContainer &&
+                !fe->IsUserFont()) {
+                font = FindNonItalicFaceForChar(ff.Family(), aCh);
+                if (font) {
+                    *aMatchType = gfxTextRange::kFontGroup;
+                    return font.forget();
+                }
+            }
         }
     }
 
     if (fontListLength == 0) {
         RefPtr<gfxFont> defaultFont = GetDefaultFont();
         if (defaultFont->HasCharacter(aCh)) {
             *aMatchType = gfxTextRange::kFontGroup;
             return defaultFont.forget();
--- a/gfx/thebes/gfxTextRun.h
+++ b/gfx/thebes/gfxTextRun.h
@@ -877,50 +877,52 @@ protected:
     void ComputeRanges(nsTArray<gfxTextRange>& mRanges,
                        const T *aString, uint32_t aLength,
                        int32_t aRunScript, uint16_t aOrientation);
 
     class FamilyFace {
     public:
         FamilyFace() : mFamily(nullptr), mFontEntry(nullptr),
                        mNeedsBold(false), mFontCreated(false),
-                       mLoading(false), mInvalid(false)
+                       mLoading(false), mInvalid(false),
+                       mCheckForFallbackFaces(false)
         { }
 
         FamilyFace(gfxFontFamily* aFamily, gfxFont* aFont)
             : mFamily(aFamily), mNeedsBold(false), mFontCreated(true),
-              mLoading(false), mInvalid(false)
+              mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
         {
             NS_ASSERTION(aFont, "font pointer must not be null");
             NS_ASSERTION(!aFamily ||
                          aFamily->ContainsFace(aFont->GetFontEntry()),
                          "font is not a member of the given family");
             mFont = aFont;
             NS_ADDREF(aFont);
         }
 
         FamilyFace(gfxFontFamily* aFamily, gfxFontEntry* aFontEntry,
                    bool aNeedsBold)
             : mFamily(aFamily), mNeedsBold(aNeedsBold), mFontCreated(false),
-              mLoading(false), mInvalid(false)
+              mLoading(false), mInvalid(false), mCheckForFallbackFaces(false)
         {
             NS_ASSERTION(aFontEntry, "font entry pointer must not be null");
             NS_ASSERTION(!aFamily ||
                          aFamily->ContainsFace(aFontEntry),
                          "font is not a member of the given family");
             mFontEntry = aFontEntry;
             NS_ADDREF(aFontEntry);
         }
 
         FamilyFace(const FamilyFace& aOtherFamilyFace)
             : mFamily(aOtherFamilyFace.mFamily),
               mNeedsBold(aOtherFamilyFace.mNeedsBold),
               mFontCreated(aOtherFamilyFace.mFontCreated),
               mLoading(aOtherFamilyFace.mLoading),
-              mInvalid(aOtherFamilyFace.mInvalid)
+              mInvalid(aOtherFamilyFace.mInvalid),
+              mCheckForFallbackFaces(aOtherFamilyFace.mCheckForFallbackFaces)
         {
             if (mFontCreated) {
                 mFont = aOtherFamilyFace.mFont;
                 NS_ADDREF(mFont);
             } else {
                 mFontEntry = aOtherFamilyFace.mFontEntry;
                 NS_IF_ADDREF(mFontEntry);
             }
@@ -973,16 +975,18 @@ protected:
         bool IsUserFontContainer() const {
             return FontEntry()->mIsUserFontContainer;
         }
         bool IsLoading() const { return mLoading; }
         bool IsInvalid() const { return mInvalid; }
         void CheckState(bool& aSkipDrawing);
         void SetLoading(bool aIsLoading) { mLoading = aIsLoading; }
         void SetInvalid() { mInvalid = true; }
+        bool CheckForFallbackFaces() const { return mCheckForFallbackFaces; }
+        void SetCheckForFallbackFaces() { mCheckForFallbackFaces = true; }
 
         void SetFont(gfxFont* aFont)
         {
             NS_ASSERTION(aFont, "font pointer must not be null");
             NS_ADDREF(aFont);
             if (mFontCreated) {
                 NS_RELEASE(mFont);
             } else {
@@ -1001,16 +1005,17 @@ protected:
         union {
             gfxFont*            mFont;
             gfxFontEntry*       mFontEntry;
         };
         bool                    mNeedsBold   : 1;
         bool                    mFontCreated : 1;
         bool                    mLoading     : 1;
         bool                    mInvalid     : 1;
+        bool                    mCheckForFallbackFaces : 1;
     };
 
     // List of font families, either named or generic.
     // Generic names map to system pref fonts based on language.
     mozilla::FontFamilyList mFamilyList;
 
     // Fontlist containing a font entry for each family found. gfxFont objects
     // are created as needed and userfont loads are initiated when needed.
@@ -1097,17 +1102,23 @@ protected:
 
     // Helper for font-matching:
     // When matching the italic case, allow use of the regular face
     // if it supports a character but the italic one doesn't.
     // Return null if regular face doesn't support aCh
     already_AddRefed<gfxFont>
     FindNonItalicFaceForChar(gfxFontFamily* aFamily, uint32_t aCh);
 
-    // helper methods for looking up fonts
+    // search all faces in a family for a fallback in cases where it's unclear
+    // whether the family might have a font for a given character
+    already_AddRefed<gfxFont>
+    FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh,
+                            int32_t aRunScript);
+
+   // helper methods for looking up fonts
 
     // lookup and add a font with a given name (i.e. *not* a generic!)
     void AddPlatformFont(const nsAString& aName,
                          nsTArray<gfxFontFamily*>& aFamilyList);
 
     // do style selection and add entries to list
     void AddFamilyToFontList(gfxFontFamily* aFamily);
 
deleted file mode 100644
--- a/ipc/chromium/Makefile.in
+++ /dev/null
@@ -1,40 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-OS_CXXFLAGS := $(filter-out -fshort-wchar,$(OS_CXXFLAGS))
-
-ACDEFINES =
-
-ifndef MOZ_NATIVE_LIBEVENT # {
-vpath %.c \
-  $(srcdir)/src/third_party/libevent \
-  $(NULL)
-endif # }
-
-vpath %.cc \
-  $(srcdir)/src/base \
-  $(srcdir)/src/chrome/common \
-  $(NULL)
-
-vpath %.mm \
-  $(srcdir)/src/base \
-  $(srcdir)/src/chrome/common \
-  $(NULL)
-
-OS_CXXFLAGS += $(TK_CFLAGS)
-
-include $(topsrcdir)/config/rules.mk
-
-ifdef MOZ_NATIVE_LIBEVENT # {
-
-export-preqs = \
-  $(call mkdir_deps,$(CURDIR)/third_party/libevent) \
-  $(NULL)
-
-export:: $(DIST)/third_party/libevent/event.h
-
-$(DIST)/third_party/libevent/event.h:: $(export-preqs)
-	echo '#include <event.h>' > $(CURDIR)/third_party/libevent/event.h
-
-endif # }
--- a/ipc/chromium/moz.build
+++ b/ipc/chromium/moz.build
@@ -162,11 +162,13 @@ if os_bsd or os_linux:
         ]
 
 ost = CONFIG['OS_TEST']
 if '86' not in ost and 'arm' not in ost and 'mips' not in ost:
     SOURCES += [
         'src/base/atomicops_internals_mutex.cc',
     ]
 
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/ipc/chromium/src/base/message_pump_libevent.cc
+++ b/ipc/chromium/src/base/message_pump_libevent.cc
@@ -10,17 +10,17 @@
 #include <unistd.h>
 #endif
 
 #include "eintr_wrapper.h"
 #include "base/logging.h"
 #include "base/scoped_nsautorelease_pool.h"
 #include "base/time.h"
 #include "nsDependentSubstring.h"
-#include "third_party/libevent/event.h"
+#include "event.h"
 #include "mozilla/UniquePtr.h"
 
 // This macro checks that the _EVENT_SIZEOF_* constants defined in
 // ipc/chromiume/src/third_party/<platform>/event2/event-config.h are correct.
 #define CHECK_EVENT_SIZEOF(TYPE, type) \
     static_assert(_EVENT_SIZEOF_##TYPE == sizeof(type), \
     "bad _EVENT_SIZEOF_"#TYPE);
 
deleted file mode 100644
--- a/ipc/glue/Makefile.in
+++ /dev/null
@@ -1,1 +0,0 @@
-%/SharedMemoryBasic_mach.cpp: ;
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -118,17 +118,17 @@ TraceProcessGlobalRoot(JSTracer* trc, T*
 void
 TraceGenericPointerRoot(JSTracer* trc, gc::Cell** thingp, const char* name);
 
 // Trace a non-root edge that uses the base GC thing type, instead of a more
 // specific type.
 void
 TraceManuallyBarrieredGenericPointerEdge(JSTracer* trc, gc::Cell** thingp, const char* name);
 
-// Depricated. Please use one of the strongly typed variants above.
+// Deprecated. Please use one of the strongly typed variants above.
 void
 TraceChildren(JSTracer* trc, void* thing, JS::TraceKind kind);
 
 namespace gc {
 
 // Trace through a shape or group iteratively during cycle collection to avoid
 // deep or infinite recursion.
 void
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -808,17 +808,17 @@ IonBuilder::build()
         return false;
 
 #ifdef JS_JITSPEW
     if (info().isAnalysis()) {
         JitSpew(JitSpew_IonScripts, "Analyzing script %s:%" PRIuSIZE " (%p) %s",
                 script()->filename(), script()->lineno(), (void*)script(),
                 AnalysisModeString(info().analysisMode()));
     } else {
-        JitSpew(JitSpew_IonScripts, "%sompiling script %s:%" PRIuSIZE " (%p) (warmup-counter=%" PRIuSIZE ", level=%s)",
+        JitSpew(JitSpew_IonScripts, "%sompiling script %s:%" PRIuSIZE " (%p) (warmup-counter=%" PRIu32 ", level=%s)",
                 (script()->hasIonScript() ? "Rec" : "C"),
                 script()->filename(), script()->lineno(), (void*)script(),
                 script()->getWarmUpCount(), OptimizationLevelString(optimizationInfo().level()));
     }
 #endif
 
     initParameters();
     initLocals();
--- a/js/src/jit/JitSpewer.h
+++ b/js/src/jit/JitSpewer.h
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef jit_JitSpewer_h
 #define jit_JitSpewer_h
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/IntegerPrintfMacros.h"
 
 #include <stdarg.h>
 
 #include "jit/C1Spewer.h"
 #include "jit/JSONSpewer.h"
 #include "js/RootingAPI.h"
 
 namespace js {
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -71,17 +71,17 @@ struct ArgumentsData
     const HeapValue* begin() const { return args; }
     HeapValue* end() { return args + numArgs; }
     const HeapValue* end() const { return args + numArgs; }
 };
 
 // Maximum supported value of arguments.length. This bounds the maximum
 // number of arguments that can be supplied to Function.prototype.apply.
 // This value also bounds the number of elements parsed in an array
-// initialiser.
+// initializer.
 static const unsigned ARGS_LENGTH_MAX = 500 * 1000;
 
 /*
  * ArgumentsObject instances represent |arguments| objects created to store
  * function arguments when a function is called.  It's expensive to create such
  * objects if they're never used, so they're only created when they are
  * potentially used.
  *
--- a/js/src/vm/PIC.h
+++ b/js/src/vm/PIC.h
@@ -161,22 +161,22 @@ struct ForOfPIC
      *      To ensure that Array.prototype has not been modified.
      *
      *  ArrayIterator.prototype (arrayIteratorProto_)
      *  ArrayIterator.prototype's shape (arrayIteratorProtoShape_)
      *      To ensure that an ArrayIterator.prototype has not been modified.
      *
      *  Array.prototype's slot number for @@iterator (arrayProtoIteratorSlot_)
      *  Array.prototype's canonical value for @@iterator (canonicalIteratorFunc_)
-     *      To quickly retreive and ensure that the iterator constructor
+     *      To quickly retrieve and ensure that the iterator constructor
      *      stored in the slot has not changed.
      *
      *  ArrayIterator.prototype's slot number for 'next' (arrayIteratorProtoNextSlot_)
      *  ArrayIterator.prototype's canonical value for 'next' (canonicalNextFunc_)
-     *      To quickly retreive and ensure that the 'next' method for ArrayIterator
+     *      To quickly retrieve and ensure that the 'next' method for ArrayIterator
      *      objects has not changed.
      */
     class Chain : public BaseChain
     {
       private:
         // Pointer to canonical Array.prototype and ArrayIterator.prototype
         HeapPtrNativeObject arrayProto_;
         HeapPtrNativeObject arrayIteratorProto_;
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -612,17 +612,17 @@ InvokeInterruptCallback(JSContext* cx)
     return false;
 }
 
 void
 JSRuntime::resetJitStackLimit()
 {
     // Note that, for now, we use the untrusted limit for ion. This is fine,
     // because it's the most conservative limit, and if we hit it, we'll bail
-    // out of ion into the interpeter, which will do a proper recursion check.
+    // out of ion into the interpreter, which will do a proper recursion check.
 #ifdef JS_SIMULATOR
     jitStackLimit_ = jit::Simulator::StackLimit();
 #else
     jitStackLimit_ = mainThread.nativeStackLimit[StackForUntrustedScript];
 #endif
     jitStackLimitNoInterrupt_ = jitStackLimit_;
 }
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1027,17 +1027,17 @@ struct JSRuntime : public JS::shadow::Ru
      * duty (in debug builds) to verify that it matches the cx being used.
      */
     JSContext*         activeContext;
 #endif
 
     /* Garbage collector state, used by jsgc.c. */
     js::gc::GCRuntime   gc;
 
-    /* Garbage collector state has been sucessfully initialized. */
+    /* Garbage collector state has been successfully initialized. */
     bool                gcInitialized;
 
     int gcZeal() { return gc.zeal(); }
 
     void lockGC() {
         assertCanLock(js::GCLock);
         gc.lockGC();
     }
@@ -1097,17 +1097,17 @@ struct JSRuntime : public JS::shadow::Ru
     }
     void enableProfilerSampling() {
         suppressProfilerSampling = false;
     }
 
     /* Had an out-of-memory error which did not populate an exception. */
     bool                hadOutOfMemory;
 
-    /* We are curently deleting an object due to an initialization failure. */
+    /* We are currently deleting an object due to an initialization failure. */
     mozilla::DebugOnly<bool> handlingInitFailure;
 
     /* A context has been created on this runtime. */
     bool                haveCreatedContext;
 
     /*
      * Allow relazifying functions in compartments that are active. This is
      * only used by the relazifyFunctions() testing function.
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -680,31 +680,35 @@ TraceLoggerThreadState::init()
     }
 
     if (ContainsFlag(env, "IonCompiler")) {
         enabledTextIds[TraceLogger_IonCompilation] = true;
         enabledTextIds[TraceLogger_IonLinking] = true;
         enabledTextIds[TraceLogger_FoldTests] = true;
         enabledTextIds[TraceLogger_SplitCriticalEdges] = true;
         enabledTextIds[TraceLogger_RenumberBlocks] = true;
+        enabledTextIds[TraceLogger_ScalarReplacement] = true;
         enabledTextIds[TraceLogger_DominatorTree] = true;
         enabledTextIds[TraceLogger_PhiAnalysis] = true;
-        enabledTextIds[TraceLogger_ScalarReplacement] = true;
+        enabledTextIds[TraceLogger_MakeLoopsContiguous] = true;
         enabledTextIds[TraceLogger_ApplyTypes] = true;
         enabledTextIds[TraceLogger_EagerSimdUnbox] = true;
         enabledTextIds[TraceLogger_AliasAnalysis] = true;
         enabledTextIds[TraceLogger_GVN] = true;
         enabledTextIds[TraceLogger_LICM] = true;
+        enabledTextIds[TraceLogger_Sincos] = true;
         enabledTextIds[TraceLogger_RangeAnalysis] = true;
         enabledTextIds[TraceLogger_LoopUnrolling] = true;
         enabledTextIds[TraceLogger_EffectiveAddressAnalysis] = true;
         enabledTextIds[TraceLogger_AlignmentMaskAnalysis] = true;
         enabledTextIds[TraceLogger_EliminateDeadCode] = true;
+        enabledTextIds[TraceLogger_ReorderInstructions] = true;
         enabledTextIds[TraceLogger_EdgeCaseAnalysis] = true;
         enabledTextIds[TraceLogger_EliminateRedundantChecks] = true;
+        enabledTextIds[TraceLogger_AddKeepAliveInstructions] = true;
         enabledTextIds[TraceLogger_GenerateLIR] = true;
         enabledTextIds[TraceLogger_RegisterAllocation] = true;
         enabledTextIds[TraceLogger_GenerateCode] = true;
         enabledTextIds[TraceLogger_Scripts] = true;
     }
 
     enabledTextIds[TraceLogger_Interpreter] = enabledTextIds[TraceLogger_Engine];
     enabledTextIds[TraceLogger_Baseline] = enabledTextIds[TraceLogger_Engine];
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -30,39 +30,39 @@ class PerThreadData;
 
 namespace jit {
     class CompileRuntime;
 } // namespace jit
 
 /*
  * Tracelogging overview.
  *
- * Tracelogging makes it possible to trace the occurence of a single event and/or
+ * Tracelogging makes it possible to trace the occurrence of a single event and/or
  * the start and stop of an event. This is implemented to give an as low overhead as
  * possible so it doesn't interfere with running.
  *
  *
  * Logging something is done in 3 stages.
  * 1) Get the tracelogger of the current thread.
  *     - TraceLoggerForMainThread(JSRuntime*)
  *     - TraceLoggerForCurrentThread(); // Should NOT be used for the mainthread.
  *
  * 2) Optionally create a TraceLoggerEvent for the text that needs to get logged. This
  *    step takes some time, so try to do this beforehand, outside the hot
- *    path and don't do unnecessary repetitions, since it will criple
+ *    path and don't do unnecessary repetitions, since it will cripple
  *    performance.
  *     - TraceLoggerEvent event(logger, "foo");
  *
  *    There are also some predefined events. They are located in
  *    TraceLoggerTextId. They don't require to create an TraceLoggerEvent and
  *    can also be used as an argument to these functions.
- * 3) Log the occurence of a single event:
+ * 3) Log the occurrence of a single event:
  *    - TraceLogTimestamp(logger, TraceLoggerTextId);
  *      Note: it is temporarily not supported to provide an TraceLoggerEvent as
- *            argument to log the occurence of a single event.
+ *            argument to log the occurrence of a single event.
  *
  *    or log the start and stop of an event:
  *    - TraceLogStartEvent(logger, TraceLoggerTextId);
  *    - TraceLogStartEvent(logger, TraceLoggerEvent);
  *    - TraceLogStopEvent(logger, TraceLoggerTextId);
  *    - TraceLogStopEvent(logger, TraceLoggerEvent);
  *
  *    or the start/stop of an event with a RAII class:
--- a/js/src/vm/TraceLoggingGraph.h
+++ b/js/src/vm/TraceLoggingGraph.h
@@ -23,17 +23,17 @@
  *  - events: Name of the file containing a flat list of log events saved
  *            in binary format.
  *            (64bit: Time Stamp Counter, 32bit index to dict)
  *  - tree:   Name of the file containing the events with duration. The content
  *            is already in a tree data structure. This is also saved in a
  *            binary file.
  *  - treeFormat: The format used to encode the tree. By default "64,64,31,1,32".
  *                There are currently no other formats to save the tree.
- *     - 64,64,31,1,31 signifies how many bytes are used for the different
+ *     - 64,64,31,1,32 signifies how many bytes are used for the different
  *       parts of the tree.
  *       => 64 bits: Time Stamp Counter of start of event.
  *       => 64 bits: Time Stamp Counter of end of event.
  *       => 31 bits: Index to dict file containing the log text.
  *       =>  1 bit:  Boolean signifying if this entry has children.
  *                   When true, the child can be found just right after this entry.
  *       => 32 bits: Containing the ID of the next event on the same depth
  *                   or 0 if there isn't an event on the same depth anymore.
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -1826,17 +1826,17 @@ const Class TypedArrayObject::classes[Sc
 // Thus we need one class with cached prototype per kind of typed array, with a
 // delegated ClassSpec.
 #define IMPL_TYPED_ARRAY_PROTO_CLASS(typedArray, i) \
 { \
     /*
      * Actually ({}).toString.call(Uint8Array.prototype) should throw, because
      * Uint8Array.prototype lacks the the typed array internal slots.  (Same as
      * with %TypedArray%.prototype.)  It's not clear this is desirable (see
-     * above), but it's what we've always done, so keep doing it til we
+     * above), but it's what we've always done, so keep doing it till we
      * implement @@toStringTag or ES6 changes.
      */ \
     #typedArray "Prototype", \
     JSCLASS_HAS_CACHED_PROTO(JSProto_##typedArray), \
     nullptr, /* addProperty */ \
     nullptr, /* delProperty */ \
     nullptr, /* getProperty */ \
     nullptr, /* setProperty */ \
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1056,17 +1056,17 @@ public:
     mAppUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
     mContainerReferenceFrame =
       const_cast<nsIFrame*>(aContainerItem ? aContainerItem->ReferenceFrameForChildren() :
                                              mBuilder->FindReferenceFrameFor(mContainerFrame));
     bool isAtRoot = !aContainerItem || (aContainerItem->Frame() == mBuilder->RootReferenceFrame());
     MOZ_ASSERT_IF(isAtRoot, mContainerReferenceFrame == mBuilder->RootReferenceFrame());
     mContainerAnimatedGeometryRoot = isAtRoot
       ? mContainerReferenceFrame
-      : nsLayoutUtils::GetAnimatedGeometryRootFor(aContainerItem, aBuilder);
+      : aContainerItem->AnimatedGeometryRoot();
     MOZ_ASSERT(!mBuilder->IsPaintingToWindow() ||
       nsLayoutUtils::IsAncestorFrameCrossDoc(mBuilder->RootReferenceFrame(),
                                              mContainerAnimatedGeometryRoot));
     NS_ASSERTION(!aContainerItem || !aContainerItem->ShouldFixToViewport(mBuilder),
                  "Container items never return true for ShouldFixToViewport");
     mContainerFixedPosFrame =
         FindFixedPosFrameForLayerData(mContainerAnimatedGeometryRoot, false);
     // When AllowResidualTranslation is false, display items will be drawn
@@ -3711,18 +3711,17 @@ ContainerState::ChooseAnimatedGeometryRo
     // Don't use an item that won't be part of any PaintedLayers to pick the
     // active scrolled root.
     if (layerState == LAYER_ACTIVE_FORCE) {
       continue;
     }
 
     // Try using the actual active scrolled root of the backmost item, as that
     // should result in the least invalidation when scrolling.
-    *aAnimatedGeometryRoot =
-      nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
+    *aAnimatedGeometryRoot = item->AnimatedGeometryRoot();
     return true;
   }
   return false;
 }
 
 nsIntRegion
 ContainerState::ComputeOpaqueRect(nsDisplayItem* aItem,
                                   const nsIFrame* aAnimatedGeometryRoot,
@@ -3887,18 +3886,17 @@ ContainerState::ProcessDisplayItems(nsDi
     if (layerState == LAYER_INACTIVE &&
         nsDisplayItem::ForceActiveLayers()) {
       layerState = LAYER_ACTIVE;
     }
 
     bool forceInactive;
     const nsIFrame* animatedGeometryRoot;
     const nsIFrame* animatedGeometryRootForScrollMetadata = nullptr;
-    const nsIFrame* realAnimatedGeometryRootOfItem =
-      nsLayoutUtils::GetAnimatedGeometryRootFor(item, mBuilder);
+    const nsIFrame* realAnimatedGeometryRootOfItem = item->AnimatedGeometryRoot();
     if (mFlattenToSingleLayer) {
       forceInactive = true;
       animatedGeometryRoot = lastAnimatedGeometryRoot;
     } else {
       forceInactive = false;
       if (mManager->IsWidgetLayerManager()) {
         animatedGeometryRoot = realAnimatedGeometryRootOfItem;
         // Unlike GetAnimatedGeometryRootFor(), GetAnimatedGeometryRootForFrame() does not
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1991,21 +1991,27 @@ void nsDisplayList::SortByCSSOrder(nsDis
 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
                          SortLEQ aCmp, void* aClosure) {
   ::Sort(this, Count(), aCmp, aClosure);
 }
 
 nsDisplayItem::nsDisplayItem(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame)
   : mFrame(aFrame)
   , mClip(aBuilder->ClipState().GetCurrentCombinedClip(aBuilder))
+  , mAnimatedGeometryRoot(nullptr)
 #ifdef MOZ_DUMP_PAINTING
   , mPainted(false)
 #endif
 {
   mReferenceFrame = aBuilder->FindReferenceFrameFor(aFrame, &mToReferenceFrame);
+  // This can return the wrong result if the item override ShouldFixToViewport(),
+  // the item needs to set it again in its constructor.
+  mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootForInit(this, aBuilder);
+  MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(aBuilder->RootReferenceFrame(),
+                                                      mAnimatedGeometryRoot), "Bad");
   NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
                !aBuilder->IsForPainting(), "dirty rect not set");
   // The dirty rect is for mCurrentFrame, so we have to use
   // mCurrentOffsetToReferenceFrame
   mVisibleRect = aBuilder->GetDirtyRect() +
       aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 }
 
@@ -2136,16 +2142,19 @@ nsDisplayBackgroundImage::nsDisplayBackg
   : nsDisplayImageContainer(aBuilder, aFrame)
   , mBackgroundStyle(aBackgroundStyle)
   , mLayer(aLayer)
 {
   MOZ_COUNT_CTOR(nsDisplayBackgroundImage);
 
   mBounds = GetBoundsInternal(aBuilder);
   mDestArea = GetDestAreaInternal(aBuilder);
+  if (ShouldFixToViewport(aBuilder)) {
+    mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
+  }
 }
 
 nsRect
 nsDisplayBackgroundImage::GetDestAreaInternal(nsDisplayListBuilder* aBuilder)
 {
   if (!mBackgroundStyle) {
     return nsRect();
   }
@@ -3781,18 +3790,17 @@ RequiredLayerStateForChildren(nsDisplayL
                               LayerManager* aManager,
                               const ContainerLayerParameters& aParameters,
                               const nsDisplayList& aList,
                               nsIFrame* aExpectedAnimatedGeometryRootForChildren)
 {
   LayerState result = LAYER_INACTIVE;
   for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
     if (result == LAYER_INACTIVE &&
-        nsLayoutUtils::GetAnimatedGeometryRootFor(i, aBuilder) !=
-          aExpectedAnimatedGeometryRootForChildren) {
+        i->AnimatedGeometryRoot() != aExpectedAnimatedGeometryRootForChildren) {
       result = LAYER_ACTIVE;
     }
 
     LayerState state = i->GetLayerState(aBuilder, aManager, aParameters);
     if ((state == LAYER_ACTIVE || state == LAYER_ACTIVE_FORCE) &&
         state > result) {
       result = state;
     }
@@ -4056,18 +4064,17 @@ nsDisplayOpacity::GetLayerState(nsDispla
   if (mForEventsOnly) {
     MOZ_ASSERT(mOpacity == 0);
     return LAYER_INACTIVE;
   }
 
   if (NeedsActiveLayer(aBuilder))
     return LAYER_ACTIVE;
 
-  return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList,
-    nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder));
+  return RequiredLayerStateForChildren(aBuilder, aManager, aParameters, mList, AnimatedGeometryRoot());
 }
 
 bool
 nsDisplayOpacity::ComputeVisibility(nsDisplayListBuilder* aBuilder,
                                     nsRegion* aVisibleRegion) {
   // Our children are translucent so we should not allow them to subtract
   // area from aVisibleRegion. We do need to find out what is visible under
   // our children in the temporary compositing buffer, because if our children
@@ -4789,16 +4796,17 @@ nsDisplayTransform::SetReferenceFrameToA
 {
   if (mFrame == aBuilder->RootReferenceFrame()) {
     return;
   }
   nsIFrame *outerFrame = nsLayoutUtils::GetCrossDocParentFrame(mFrame);
   mReferenceFrame =
     aBuilder->FindReferenceFrameFor(outerFrame);
   mToReferenceFrame = mFrame->GetOffsetToCrossDoc(mReferenceFrame);
+  mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootForFrame(aBuilder, outerFrame);
   mVisibleRect = aBuilder->GetDirtyRect() + mToReferenceFrame;
 }
 
 void
 nsDisplayTransform::Init(nsDisplayListBuilder* aBuilder)
 {
   mHasBounds = false;
   mStoredList.SetClip(aBuilder, DisplayItemClip::NoClip());
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1226,16 +1226,17 @@ public:
   /**
    * This constructor is only used in rare cases when we need to construct
    * temporary items.
    */
   explicit nsDisplayItem(nsIFrame* aFrame)
     : mFrame(aFrame)
     , mClip(nullptr)
     , mReferenceFrame(nullptr)
+    , mAnimatedGeometryRoot(nullptr)
 #ifdef MOZ_DUMP_PAINTING
     , mPainted(false)
 #endif
   {
   }
   virtual ~nsDisplayItem() {}
 
   void* operator new(size_t aSize,
@@ -1687,16 +1688,21 @@ public:
    */
   const nsIFrame* ReferenceFrame() const { return mReferenceFrame; }
 
   /**
    * Returns the reference frame for display item children of this item.
    */
   virtual const nsIFrame* ReferenceFrameForChildren() const { return mReferenceFrame; }
 
+  nsIFrame* AnimatedGeometryRoot() const {
+    MOZ_ASSERT(mAnimatedGeometryRoot, "Must have cached AGR before accessing it!");
+    return mAnimatedGeometryRoot;
+  }
+
   /**
    * Checks if this display item (or any children) contains content that might
    * be rendered with component alpha (e.g. subpixel antialiasing). Returns the
    * bounds of the area that needs component alpha, or an empty rect if nothing
    * in the item does.
    */
   virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) { return nsRect(); }
 
@@ -1746,16 +1752,17 @@ protected:
   friend class nsDisplayList;
 
   nsDisplayItem() { mAbove = nullptr; }
 
   nsIFrame* mFrame;
   const DisplayItemClip* mClip;
   // Result of FindReferenceFrameFor(mFrame), if mFrame is non-null
   const nsIFrame* mReferenceFrame;
+  nsIFrame* mAnimatedGeometryRoot;
   // Result of ToReferenceFrame(mFrame), if mFrame is non-null
   nsPoint   mToReferenceFrame;
   // This is the rectangle that needs to be painted.
   // Display item construction sets this to the dirty rect.
   // nsDisplayList::ComputeVisibility sets this to the visible region
   // of the item by intersecting the current visible region with the bounds
   // of the item. Paint implementations can use this to limit their drawing.
   // Guaranteed to be contained in GetBounds().
--- a/layout/base/nsLayoutDebugger.cpp
+++ b/layout/base/nsLayoutDebugger.cpp
@@ -157,42 +157,41 @@ PrintDisplayItemTo(nsDisplayListBuilder*
     if (content->GetClasses()) {
       content->GetClasses()->ToString(tmp);
       contentData.AppendLiteral(" class:");
       contentData.Append(tmp);
     }
   }
   bool snap;
   nsRect rect = aItem->GetBounds(aBuilder, &snap);
-  nsRect layerRect = rect -
-    nsLayoutUtils::GetAnimatedGeometryRootFor(aItem, aBuilder)->
-      GetOffsetToCrossDoc(aItem->ReferenceFrame());
+  nsRect layerRect = rect - aItem->AnimatedGeometryRoot()->GetOffsetToCrossDoc(aItem->ReferenceFrame());
   nscolor color;
   nsRect vis = aItem->GetVisibleRect();
   nsRect component = aItem->GetComponentAlphaBounds(aBuilder);
   nsDisplayList* list = aItem->GetChildren();
   const DisplayItemClip& clip = aItem->GetClip();
   nsRegion opaque = aItem->GetOpaqueRegion(aBuilder, &snap);
 #ifdef MOZ_DUMP_PAINTING
   if (aDumpHtml && aItem->Painted()) {
     nsCString string(aItem->Name());
     string.Append('-');
     string.AppendInt((uint64_t)aItem);
     aStream << nsPrintfCString("<a href=\"javascript:ViewImage('%s')\">", string.BeginReading());
   }
 #endif
-  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s",
+  aStream << nsPrintfCString("%s p=0x%p f=0x%p(%s) %sbounds(%d,%d,%d,%d) layerBounds(%d,%d,%d,%d) visible(%d,%d,%d,%d) componentAlpha(%d,%d,%d,%d) clip(%s) %s ref=0x%p agr=0x%p",
           aItem->Name(), aItem, (void*)f, NS_ConvertUTF16toUTF8(contentData).get(),
           (aItem->ZIndex() ? nsPrintfCString("z=%d ", aItem->ZIndex()).get() : ""),
           rect.x, rect.y, rect.width, rect.height,
           layerRect.x, layerRect.y, layerRect.width, layerRect.height,
           vis.x, vis.y, vis.width, vis.height,
           component.x, component.y, component.width, component.height,
           clip.ToString().get(),
-          aItem->IsUniform(aBuilder, &color) ? " uniform" : "");
+          aItem->IsUniform(aBuilder, &color) ? " uniform" : "",
+          aItem->ReferenceFrame(), aItem->AnimatedGeometryRoot());
 
   nsRegionRectIterator iter(opaque);
   for (const nsRect* r = iter.Next(); r; r = iter.Next()) {
     aStream << nsPrintfCString(" (opaque %d,%d,%d,%d)", r->x, r->y, r->width, r->height);
   }
 
   if (aItem->ShouldFixToViewport(aBuilder)) {
     aStream << " fixed";
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1062,22 +1062,16 @@ GetDisplayPortFromMarginsData(nsIContent
 
       float left = std::min(margins.left, float(budget));
       float right = std::min(margins.right, budget - left);
       screenRect.x -= left;
       screenRect.width += left + right;
     }
   }
 
-  // Inflate the rectangle by 1 so that we always push to the next tile
-  // boundary. This is desirable to stop from having a rectangle with a
-  // moving origin occasionally being smaller when it coincidentally lines
-  // up to tile boundaries.
-  screenRect.Inflate(1);
-
   ScreenPoint scrollPosScreen = LayoutDevicePoint::FromAppUnits(scrollPos, auPerDevPixel)
                               * res;
 
   // Round-out the display port to the nearest alignment (tiles)
   screenRect += scrollPosScreen;
   float x = alignment.width * floor(screenRect.x / alignment.width);
   float y = alignment.height * floor(screenRect.y / alignment.height);
   float w = alignment.width * ceil(screenRect.width / alignment.width + 1);
@@ -1956,16 +1950,35 @@ nsLayoutUtils::GetAnimatedGeometryRootFo
     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(f);
     if (parent) {
       return GetAnimatedGeometryRootForFrame(aBuilder, parent);
     }
   }
   return GetAnimatedGeometryRootForFrame(aBuilder, f);
 }
 
+nsIFrame*
+nsLayoutUtils::GetAnimatedGeometryRootForInit(nsDisplayItem* aItem,
+                                              nsDisplayListBuilder* aBuilder)
+{
+  nsIFrame* f = aItem->Frame();
+  if (aItem->ShouldFixToViewport(aBuilder)) {
+    // Make its active scrolled root be the active scrolled root of
+    // the enclosing viewport, since it shouldn't be scrolled by scrolled
+    // frames in its document. InvalidateFixedBackgroundFramesFromList in
+    // nsGfxScrollFrame will not repaint this item when scrolling occurs.
+    nsIFrame* viewportFrame =
+      nsLayoutUtils::GetClosestFrameOfType(f, nsGkAtoms::viewportFrame, aBuilder->RootReferenceFrame());
+    if (viewportFrame) {
+      return GetAnimatedGeometryRootForFrame(aBuilder, viewportFrame);
+    }
+  }
+  return GetAnimatedGeometryRootForFrame(aBuilder, f);
+}
+
 // static
 nsIScrollableFrame*
 nsLayoutUtils::GetNearestScrollableFrameForDirection(nsIFrame* aFrame,
                                                      Direction aDirection)
 {
   NS_ASSERTION(aFrame, "GetNearestScrollableFrameForDirection expects a non-null frame");
   for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
     nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -558,16 +558,23 @@ public:
      * do not do any special processing for background attachment fixed items,
      * instead treating them like any other frame.
      */
     AGR_IGNORE_BACKGROUND_ATTACHMENT_FIXED = 0x01
   };
   static nsIFrame* GetAnimatedGeometryRootFor(nsDisplayItem* aItem,
                                               nsDisplayListBuilder* aBuilder,
                                               uint32_t aFlags = 0);
+  /**
+   * Version of GetAnimatedGeometryRootFor that can be called in nsDisplayItem
+   * constructor, and only from there. Not valid for transform items, but they
+   * set their AGR in their constructor.
+   */
+  static nsIFrame* GetAnimatedGeometryRootForInit(nsDisplayItem* aItem,
+                                                  nsDisplayListBuilder* aBuilder);
 
   /**
    * Finds the nearest ancestor frame to aFrame that is considered to have (or
    * will have) "animated geometry". This could be aFrame.
    */
   static nsIFrame* GetAnimatedGeometryRootForFrame(nsDisplayListBuilder* aBuilder,
                                                    nsIFrame* aFrame);
 
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -234,17 +234,21 @@ public:
 #endif
 };
 
 class nsDisplayCanvasBackgroundImage : public nsDisplayBackgroundImage {
 public:
   nsDisplayCanvasBackgroundImage(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
                                  uint32_t aLayer, const nsStyleBackground* aBg)
     : nsDisplayBackgroundImage(aBuilder, aFrame, aLayer, aBg)
-  {}
+  {
+    if (ShouldFixToViewport(aBuilder)) {
+      mAnimatedGeometryRoot = nsLayoutUtils::GetAnimatedGeometryRootFor(this, aBuilder);
+    }
+  }
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsRenderingContext* aCtx) override;
 
   virtual void NotifyRenderingChanged() override
   {
     mFrame->Properties().Delete(nsIFrame::CachedBackgroundImage());
     mFrame->Properties().Delete(nsIFrame::CachedBackgroundImageDT());
   }
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -4747,35 +4747,41 @@ ScrollFrameHelper::FinishReflowForScroll
   SetCoordAttribute(aContent, nsGkAtoms::maxpos, aMaxXY - aMinXY);
   SetCoordAttribute(aContent, nsGkAtoms::pageincrement, aPageIncrement);
   SetCoordAttribute(aContent, nsGkAtoms::increment, aIncrement);
 }
 
 bool
 ScrollFrameHelper::ReflowFinished()
 {
+  mPostedReflowCallback = false;
+
+  if (NS_SUBTREE_DIRTY(mOuter)) {
+    // We will get another call after the next reflow and scrolling
+    // later is less janky.
+    return false;
+  }
+
   nsAutoScriptBlocker scriptBlocker;
-  mPostedReflowCallback = false;
-
   ScrollToRestoredPosition();
 
   // Clamp current scroll position to new bounds. Normally this won't
   // do anything.
   nsPoint currentScrollPos = GetScrollPosition();
   ScrollToImpl(currentScrollPos, nsRect(currentScrollPos, nsSize(0, 0)));
   if (!mAsyncScroll && !mAsyncSmoothMSDScroll) {
     // We need to have mDestination track the current scroll position,
     // in case it falls outside the new reflow area. mDestination is used
     // by ScrollBy as its starting position.
     mDestination = GetScrollPosition();
   }
 
-  if (NS_SUBTREE_DIRTY(mOuter) || !mUpdateScrollbarAttributes)
+  if (!mUpdateScrollbarAttributes) {
     return false;
-
+  }
   mUpdateScrollbarAttributes = false;
 
   // Update scrollbar attributes.
   nsPresContext* presContext = mOuter->PresContext();
 
   if (mMayHaveDirtyFixedChildren) {
     mMayHaveDirtyFixedChildren = false;
     nsIFrame* parentFrame = mOuter->GetParent();
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -97,17 +97,19 @@ MP4Metadata::MP4Metadata(Stream* aSource
   , mSource(aSource)
 {
   mPrivate->mMetadataExtractor =
     new MPEG4Extractor(new DataSourceAdapter(mSource));
   mPrivate->mCanSeek =
     mPrivate->mMetadataExtractor->flags() & MediaExtractor::CAN_SEEK;
   sp<MetaData> metaData = mPrivate->mMetadataExtractor->getMetaData();
 
-  UpdateCrypto(metaData.get());
+  if (metaData.get()) {
+    UpdateCrypto(metaData.get());
+  }
 }
 
 MP4Metadata::~MP4Metadata()
 {
 }
 
 #ifdef MOZ_RUST_MP4PARSE
 #include "mp4parse.h"
@@ -312,16 +314,19 @@ MP4Metadata::ReadTrackIndex(FallibleTArr
 }
 
 int32_t
 MP4Metadata::GetTrackNumber(mozilla::TrackID aTrackID)
 {
   size_t numTracks = mPrivate->mMetadataExtractor->countTracks();
   for (size_t i = 0; i < numTracks; i++) {
     sp<MetaData> metaData = mPrivate->mMetadataExtractor->getTrackMetaData(i);
+    if (!metaData.get()) {
+      continue;
+    }
     int32_t value;
     if (metaData->findInt32(kKeyTrackID, &value) && value == aTrackID) {
       return i;
     }
   }
   return -1;
 }
 
--- a/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MetaData.h
@@ -243,17 +243,17 @@ private:
             void *ext_data;
             float reservoir;
         } u;
 
         bool usesReservoir() const {
             return mSize <= sizeof(u.reservoir);
         }
 
-        void allocateStorage(size_t size);
+        bool allocateStorage(size_t size);
         void freeStorage();
 
         void *storage() {
             return usesReservoir() ? &u.reservoir : u.ext_data;
         }
 
         const void *storage() const {
             return usesReservoir() ? &u.reservoir : u.ext_data;
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -415,17 +415,17 @@ MPEG4Extractor::~MPEG4Extractor() {
 
 uint32_t MPEG4Extractor::flags() const {
     return CAN_PAUSE | CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK;
 }
 
 sp<MetaData> MPEG4Extractor::getMetaData() {
     status_t err;
     if ((err = readMetaData()) != OK) {
-        return new MetaData;
+        return NULL;
     }
 
     return mFileMetaData;
 }
 
 size_t MPEG4Extractor::countTracks() {
     status_t err;
     if ((err = readMetaData()) != OK) {
@@ -509,24 +509,25 @@ status_t MPEG4Extractor::readMetaData() 
     }
 
     CHECK_NE(err, (status_t)NO_INIT);
 
     // copy pssh data into file metadata
     uint64_t psshsize = 0;
     for (size_t i = 0; i < mPssh.Length(); i++) {
         psshsize += 20 + mPssh[i].datalen;
-    }
-    if (psshsize > kMAX_ALLOCATION) {
-        return ERROR_MALFORMED;
+        if (mPssh[i].datalen > kMAX_ALLOCATION - 20 ||
+            psshsize > kMAX_ALLOCATION) {
+            return ERROR_MALFORMED;
+        }
     }
     if (psshsize) {
         char *buf = (char*)malloc(psshsize);
         if (!buf) {
-            return ERROR_MALFORMED;
+            return -ENOMEM;
         }
         char *ptr = buf;
         for (size_t i = 0; i < mPssh.Length(); i++) {
             memcpy(ptr, mPssh[i].uuid, 20); // uuid + length
             memcpy(ptr + 20, mPssh[i].data, mPssh[i].datalen);
             ptr += (20 + mPssh[i].datalen);
         }
         mFileMetaData->setData(kKeyPssh, 'pssh', buf, psshsize);
@@ -680,17 +681,20 @@ status_t MPEG4Extractor::parseDrmSINF(of
             SINF *sinf = mFirstSINF;
             while (sinf && (sinf->IPMPDescriptorID != id)) {
                 sinf = sinf->next;
             }
             if (sinf == NULL) {
                 return ERROR_MALFORMED;
             }
             sinf->len = dataLen - 3;
-            sinf->IPMPData = new char[sinf->len];
+            sinf->IPMPData = new (fallible) char[sinf->len];
+            if (!sinf->IPMPData) {
+                return -ENOMEM;
+            }
 
             if (mDataSource->readAt(data_offset + 2, sinf->IPMPData, sinf->len) < sinf->len) {
                 return ERROR_IO;
             }
             data_offset += sinf->len;
 
             size -= (dataLen + numOfBytes + 1);
         }
@@ -1141,17 +1145,20 @@ status_t MPEG4Extractor::parseChunk(off6
             }
 
             if (mDataSource->readAt(data_offset + 4, &pssh.uuid, 16) < 16) {
                 return ERROR_IO;
             }
 
             // Copy the contents of the box (including header) verbatim.
             pssh.datalen = chunk_data_size + 8;
-            pssh.data = new uint8_t[pssh.datalen];
+            pssh.data = new (fallible) uint8_t[pssh.datalen];
+            if (!pssh.data) {
+                return -ENOMEM;
+            }
             if (mDataSource->readAt(data_offset - 8, pssh.data, pssh.datalen) < pssh.datalen) {
                 return ERROR_IO;
             }
 
             mPssh.AppendElement(pssh);
 
             *offset += chunk_size;
             break;
@@ -1757,17 +1764,20 @@ status_t MPEG4Extractor::parseChunk(off6
 
         case FOURCC('a', 'v', 'c', 'C'):
         {
             if (chunk_data_size < 7) {
               ALOGE("short avcC chunk (%d bytes)", chunk_data_size);
               return ERROR_MALFORMED;
             }
 
-            sp<ABuffer> buffer = new ABuffer(chunk_data_size);
+            sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size);
+            if (!buffer.get()) {
+                return -ENOMEM;
+            }
 
             if (mDataSource->readAt(
                         data_offset, buffer->data(), chunk_data_size) < chunk_data_size) {
                 return ERROR_IO;
             }
 
             if (!mLastTrack) {
               return ERROR_MALFORMED;
@@ -1991,17 +2001,20 @@ status_t MPEG4Extractor::parseChunk(off6
                     kKeyTextFormatData, &type, &data, &size)) {
                 size = 0;
             }
 
             // Make sure (size + chunk_size) isn't going to overflow.
             if (size >= kMAX_ALLOCATION - chunk_size) {
                 return ERROR_MALFORMED;
             }
-            uint8_t *buffer = new uint8_t[size + chunk_size];
+            uint8_t *buffer = new (fallible) uint8_t[size + chunk_size];
+            if (!buffer) {
+                return -ENOMEM;
+            }
 
             if (size > 0) {
                 memcpy(buffer, data, size);
             }
 
             if ((size_t)(mDataSource->readAt(*offset, buffer + size, chunk_size))
                     < chunk_size) {
                 delete[] buffer;
@@ -2019,22 +2032,28 @@ status_t MPEG4Extractor::parseChunk(off6
             break;
         }
 
         case FOURCC('c', 'o', 'v', 'r'):
         {
             if (mFileMetaData != NULL) {
                 ALOGV("chunk_data_size = %lld and data_offset = %lld",
                         chunk_data_size, data_offset);
-                sp<ABuffer> buffer = new ABuffer(chunk_data_size + 1);
+                const int kSkipBytesOfDataBox = 16;
+                if (chunk_data_size <= kSkipBytesOfDataBox) {
+                  return ERROR_MALFORMED;
+                }
+                sp<ABuffer> buffer = new (fallible) ABuffer(chunk_data_size + 1);
+                if (!buffer.get()) {
+                    return -ENOMEM;
+                }
                 if (mDataSource->readAt(
                     data_offset, buffer->data(), chunk_data_size) != (ssize_t)chunk_data_size) {
                     return ERROR_IO;
                 }
-                const int kSkipBytesOfDataBox = 16;
                 mFileMetaData->setData(
                     kKeyAlbumArt, MetaData::TYPE_NONE,
                     buffer->data() + kSkipBytesOfDataBox, chunk_data_size - kSkipBytesOfDataBox);
             }
 
             *offset += chunk_size;
             break;
         }
--- a/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MetaData.cpp
@@ -215,73 +215,84 @@ bool MetaData::findData(uint32_t key, ui
     const typed_data &item = mItems.valueAt(i);
 
     item.getData(type, data, size);
 
     return true;
 }
 
 MetaData::typed_data::typed_data()
-    : mType(0),
+    : mType(TYPE_NONE),
       mSize(0) {
 }
 
 MetaData::typed_data::~typed_data() {
     clear();
 }
 
 MetaData::typed_data::typed_data(const typed_data &from)
     : mType(from.mType),
       mSize(0) {
-    allocateStorage(from.mSize);
-    memcpy(storage(), from.storage(), mSize);
+    if (allocateStorage(from.mSize)) {
+        memcpy(storage(), from.storage(), mSize);
+    }
 }
 
 MetaData::typed_data &MetaData::typed_data::operator=(
         const MetaData::typed_data &from) {
     if (this != &from) {
         clear();
-        mType = from.mType;
-        allocateStorage(from.mSize);
-        memcpy(storage(), from.storage(), mSize);
+        if (allocateStorage(from.mSize)) {
+            mType = from.mType;
+            memcpy(storage(), from.storage(), mSize);
+        }
     }
 
     return *this;
 }
 
 void MetaData::typed_data::clear() {
     freeStorage();
 
-    mType = 0;
+    mType = TYPE_NONE;
 }
 
 void MetaData::typed_data::setData(
         uint32_t type, const void *data, size_t size) {
     clear();
 
-    mType = type;
-    allocateStorage(size);
-    memcpy(storage(), data, size);
+    if (allocateStorage(size)) {
+        mType = type;
+        memcpy(storage(), data, size);
+    }
 }
 
 void MetaData::typed_data::getData(
         uint32_t *type, const void **data, size_t *size) const {
     *type = mType;
     *size = mSize;
     *data = storage();
 }
 
-void MetaData::typed_data::allocateStorage(size_t size) {
+bool MetaData::typed_data::allocateStorage(size_t size) {
+    // Update mSize now, as it is needed by usesReservoir() below.
+    // (mSize will be reset if the allocation fails further below.)
     mSize = size;
 
     if (usesReservoir()) {
-        return;
+        return true;
     }
 
     u.ext_data = malloc(mSize);
+    if (!u.ext_data) {
+      mType = TYPE_NONE;
+      mSize = 0;
+      return false;
+    }
+    return true;
 }
 
 void MetaData::typed_data::freeStorage() {
     if (!usesReservoir()) {
         if (u.ext_data) {
             free(u.ext_data);
             u.ext_data = NULL;
         }
--- a/media/libstagefright/gtest/TestParser.cpp
+++ b/media/libstagefright/gtest/TestParser.cpp
@@ -169,17 +169,18 @@ static const TestFileData testFiles[] = 
   { "test_case_1181213.mp4", 0,   0,   0, 0 },
   { "test_case_1181215.mp4", 0,   0,   0, 0 },
   { "test_case_1181220.mp4", 0,   0,   0, 0 },
   { "test_case_1181223.mp4", 0,   0,   0, 0 },
   { "test_case_1181719.mp4", 0,   0,   0, 0 },
   { "test_case_1185230.mp4", 1, 320, 240, 1 },
   { "test_case_1187067.mp4", 1, 160,  90, 0 },
   { "test_case_1200326.mp4", 0,   0,   0, 0 },
-  { "test_case_1204580.mp4", 1, 320, 180, 0 }
+  { "test_case_1204580.mp4", 1, 320, 180, 0 },
+  { "test_case_1216748.mp4", 0,   0,   0, 0 }
 };
 
 TEST(stagefright_MPEG4Metadata, test_case_mp4)
 {
   for (size_t test = 0; test < ArrayLength(testFiles); ++test) {
     nsTArray<uint8_t> buffer = ReadTestFile(testFiles[test].mFilename);
     ASSERT_FALSE(buffer.IsEmpty());
     RefPtr<Stream> stream = new TestStream(buffer.Elements(), buffer.Length());
--- a/media/libstagefright/gtest/moz.build
+++ b/media/libstagefright/gtest/moz.build
@@ -17,16 +17,17 @@ TEST_HARNESS_FILES.gtest += [
     'test_case_1181215.mp4',
     'test_case_1181220.mp4',
     'test_case_1181223.mp4',
     'test_case_1181719.mp4',
     'test_case_1185230.mp4',
     'test_case_1187067.mp4',
     'test_case_1200326.mp4',
     'test_case_1204580.mp4',
+    'test_case_1216748.mp4',
 ]
 
 if CONFIG['MOZ_RUST']:
     UNIFIED_SOURCES += ['TestMP4Rust.cpp',]
     TEST_HARNESS_FILES.gtest += [
         '../../../dom/media/test/street.mp4',
     ]
     LOCAL_INCLUDES += [
new file mode 100755
index 0000000000000000000000000000000000000000..7072f53bec645e38947b1b2322c4f0704d9d31d5
GIT binary patch
literal 296
zc${NkV30^FsVvAXFfn3aU|<B%Kx_zPH{|B$mjUUV+_H=m1{A=(m4U%z4+A3u2q6F?
zsw#B80|Q>b1jH>RMTyx!IwCp0tO#VnSqs}15Js2=v=<#P;$TDVWDR3rV1{s2Qd3Hb
efwTzJe5ekvnH*3tj<llGR5&df|2{V*u>=6D*&+`B
--- a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.c
@@ -68,43 +68,44 @@ int nr_stun_client_ctx_create(char *labe
     if(!(ctx->label=r_strdup(label)))
       ABORT(R_NO_MEMORY);
 
     ctx->sock=sock;
 
     nr_socket_getaddr(sock,&ctx->my_addr);
     nr_transport_addr_copy(&ctx->peer_addr,peer);
 
-    if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) {
-        if (RTO != 0)
-            ctx->rto_ms = RTO;
-        else
-            ctx->rto_ms = 100;
+    if (RTO != 0) {
+      ctx->rto_ms = RTO;
+    } else if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_RETRANSMIT_TIMEOUT, &ctx->rto_ms)) {
+      ctx->rto_ms = 100;
     }
 
     if (NR_reg_get_double(NR_STUN_REG_PREF_CLNT_RETRANSMIT_BACKOFF, &ctx->retransmission_backoff_factor))
-        ctx->retransmission_backoff_factor = 2.0;
+      ctx->retransmission_backoff_factor = 2.0;
 
     if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_MAXIMUM_TRANSMITS, &ctx->maximum_transmits))
-        ctx->maximum_transmits = 7;
+      ctx->maximum_transmits = 7;
 
-    if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->final_retransmit_backoff_ms))
-        ctx->final_retransmit_backoff_ms = 16 * ctx->rto_ms;
+    if (NR_reg_get_uint4(NR_STUN_REG_PREF_CLNT_FINAL_RETRANSMIT_BACKOFF, &ctx->maximum_transmits_timeout_ms))
+      ctx->maximum_transmits_timeout_ms = 16 * ctx->rto_ms;
 
-     ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD;
-     if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) ||
-         !allow_loopback) {
-       ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK;
-     }
+    ctx->mapped_addr_check_mask = NR_STUN_TRANSPORT_ADDR_CHECK_WILDCARD;
+    if (NR_reg_get_char(NR_STUN_REG_PREF_ALLOW_LOOPBACK_ADDRS, &allow_loopback) ||
+        !allow_loopback) {
+      ctx->mapped_addr_check_mask |= NR_STUN_TRANSPORT_ADDR_CHECK_LOOPBACK;
+    }
 
-    /* If we are doing TCP, compute the maximum timeout as if
-       we retransmitted and then set the maximum number of
-       transmits to 1 and the timeout to maximum timeout*/
     if (ctx->my_addr.protocol == IPPROTO_TCP) {
-      ctx->timeout_ms = ctx->final_retransmit_backoff_ms;
+      /* Because TCP is reliable there is only one final timeout value.
+       * We store the timeout value for TCP in here, because timeout_ms gets
+       * reset to 0 in client_reset() which gets called from client_start() */
+      ctx->maximum_transmits_timeout_ms = ctx->rto_ms *
+                        pow(ctx->retransmission_backoff_factor,
+                            ctx->maximum_transmits);
       ctx->maximum_transmits = 1;
     }
 
     *ctxp=ctx;
 
     _status=0;
   abort:
     if(_status){
@@ -388,28 +389,35 @@ static int nr_stun_client_send_request(n
 
     ctx->request_ct++;
 
     if (NR_STUN_GET_TYPE_CLASS(ctx->request->header.type) == NR_CLASS_INDICATION) {
         /* no need to set the timer because indications don't receive a
          * response */
     }
     else {
-        if (ctx->request_ct < ctx->maximum_transmits) {
-            ctx->timeout_ms *= ctx->retransmission_backoff_factor;
-            ctx->timeout_ms += ctx->rto_ms;
+        if (ctx->request_ct >= ctx->maximum_transmits) {
+          /* Reliable transport only get here once. Unreliable get here for
+           * their final timeout. */
+          ctx->timeout_ms += ctx->maximum_transmits_timeout_ms;
+        }
+        else if (ctx->timeout_ms) {
+          /* exponential backoff */
+          ctx->timeout_ms *= ctx->retransmission_backoff_factor;
         }
         else {
-            ctx->timeout_ms += ctx->final_retransmit_backoff_ms;
+          /* initial timeout unreliable transports */
+          ctx->timeout_ms = ctx->rto_ms;
         }
 
         r_log(NR_LOG_STUN,LOG_DEBUG,"STUN-CLIENT(%s): Next timer will fire in %u ms",ctx->label, ctx->timeout_ms);
 
         gettimeofday(&ctx->timer_set, 0);
 
+        assert(ctx->timeout_ms);
         NR_ASYNC_TIMER_SET(ctx->timeout_ms, nr_stun_client_timer_expired_cb, ctx, &ctx->timer_handle);
     }
 
     _status=0;
   abort:
     if (_status) {
       nr_stun_client_failed(ctx);
     }
--- a/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h
+++ b/media/mtransport/third_party/nICEr/src/stun/stun_client_ctx.h
@@ -165,17 +165,17 @@ struct nr_stun_client_ctx_ {
   nr_stun_client_results results;
   char *nonce;
   char *realm;
   void *timer_handle;
   int request_ct;
   UINT4 rto_ms;    /* retransmission time out */
   double retransmission_backoff_factor;
   UINT4 maximum_transmits;
-  UINT4 final_retransmit_backoff_ms;
+  UINT4 maximum_transmits_timeout_ms;
   UINT4 mapped_addr_check_mask;  /* What checks to run on mapped addresses */
   int timeout_ms;
   struct timeval timer_set;
   int retry_ct;
   NR_async_cb finished_cb;
   void *cb_arg;
   nr_stun_message *request;
   nr_stun_message *response;
--- a/mobile/android/base/gfx/DisplayPortCalculator.java
+++ b/mobile/android/base/gfx/DisplayPortCalculator.java
@@ -17,19 +17,16 @@ import android.util.Log;
 
 import java.util.HashMap;
 import java.util.Map;
 
 final class DisplayPortCalculator {
     private static final String LOGTAG = "GeckoDisplayPort";
     private static final PointF ZERO_VELOCITY = new PointF(0, 0);
 
-    // Keep this in sync with the TILEDLAYERBUFFER_TILE_SIZE defined in gfx/layers/TiledLayerBuffer.h
-    private static final int TILE_SIZE = 256;
-
     private static final String PREF_DISPLAYPORT_STRATEGY = "gfx.displayport.strategy";
     private static final String PREF_DISPLAYPORT_FM_MULTIPLIER = "gfx.displayport.strategy_fm.multiplier";
     private static final String PREF_DISPLAYPORT_FM_DANGER_X = "gfx.displayport.strategy_fm.danger_x";
     private static final String PREF_DISPLAYPORT_FM_DANGER_Y = "gfx.displayport.strategy_fm.danger_y";
     private static final String PREF_DISPLAYPORT_VB_MULTIPLIER = "gfx.displayport.strategy_vb.multiplier";
     private static final String PREF_DISPLAYPORT_VB_VELOCITY_THRESHOLD = "gfx.displayport.strategy_vb.threshold";
     private static final String PREF_DISPLAYPORT_VB_REVERSE_BUFFER = "gfx.displayport.strategy_vb.reverse_buffer";
     private static final String PREF_DISPLAYPORT_VB_DANGER_X_BASE = "gfx.displayport.strategy_vb.danger_x_base";
@@ -168,30 +165,29 @@ final class DisplayPortCalculator {
         float dangerZoneX = metrics.getWidth() * dangerZoneXMultiplier;
         float dangerZoneY = metrics.getHeight() * dangerZoneYMultiplier;
         rect = RectUtils.expand(rect, dangerZoneX, dangerZoneY);
         // clamp to page bounds
         return clampToPageBounds(rect, metrics);
     }
 
     /**
-     * Expand the given margins such that when they are applied on the viewport, the resulting rect
-     * does not have any partial tiles, except when it is clipped by the page bounds. This assumes
-     * the tiles are TILE_SIZE by TILE_SIZE and start at the origin, such that there will always be
-     * a tile at (0,0)-(TILE_SIZE,TILE_SIZE)).
+     * Calculate the display port by expanding the viewport by the specified
+     * margins, then clamping to the page size.
      */
-    private static DisplayPortMetrics getTileAlignedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
+    private static DisplayPortMetrics getPageClampedDisplayPortMetrics(RectF margins, float zoom, ImmutableViewportMetrics metrics) {
         float left = metrics.viewportRectLeft - margins.left;
         float top = metrics.viewportRectTop - margins.top;
         float right = metrics.viewportRectRight() + margins.right;
         float bottom = metrics.viewportRectBottom() + margins.bottom;
-        left = (float) Math.max(metrics.pageRectLeft, TILE_SIZE * Math.floor(left / TILE_SIZE));
-        top = (float) Math.max(metrics.pageRectTop, TILE_SIZE * Math.floor(top / TILE_SIZE));
-        right = (float) Math.min(metrics.pageRectRight, TILE_SIZE * Math.ceil(right / TILE_SIZE));
-        bottom = (float) Math.min(metrics.pageRectBottom, TILE_SIZE * Math.ceil(bottom / TILE_SIZE));
+        left = Math.max(metrics.pageRectLeft, left);
+        top = Math.max(metrics.pageRectTop, top);
+        right = Math.min(metrics.pageRectRight, right);
+        bottom = Math.min(metrics.pageRectBottom, bottom);
+
         return new DisplayPortMetrics(left, top, right, bottom, zoom);
     }
 
     /**
      * Adjust the given margins so if they are applied on the viewport in the metrics, the resulting rect
      * does not exceed the page bounds. This code will maintain the total margin amount for a given axis;
      * it assumes that margins.left + metrics.getWidth() + margins.right is less than or equal to
      * metrics.getPageWidth(); and the same for the y axis.
@@ -306,17 +302,17 @@ final class DisplayPortCalculator {
             // is split).
             RectF margins = new RectF();
             margins.left = horizontalBuffer / 2.0f;
             margins.right = horizontalBuffer - margins.left;
             margins.top = verticalBuffer / 2.0f;
             margins.bottom = verticalBuffer - margins.top;
             margins = shiftMarginsForPageBounds(margins, metrics);
 
-            return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+            return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
         }
 
         @Override
         public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
             // Increase the size of the viewport based on the danger zone multiplier (and clamp to page
             // boundaries), and intersect it with the current displayport to determine whether we're
             // close to checkerboarding.
             RectF adjustedViewport = expandByDangerZone(metrics.getViewport(), DANGER_ZONE_X_MULTIPLIER, DANGER_ZONE_Y_MULTIPLIER, metrics);
@@ -417,17 +413,17 @@ final class DisplayPortCalculator {
             float horizontalBuffer = displayPortWidth - metrics.getWidth();
             float verticalBuffer = displayPortHeight - metrics.getHeight();
 
             // split the buffer amounts into margins based on velocity, and shift it to
             // take into account the page bounds
             RectF margins = velocityBiasedMargins(horizontalBuffer, verticalBuffer, velocity);
             margins = shiftMarginsForPageBounds(margins, metrics);
 
-            return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+            return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
         }
 
         @Override
         public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
             // calculate the danger zone amounts based on the prefs
             float dangerZoneX = metrics.getWidth() * (DANGER_ZONE_BASE_X_MULTIPLIER + (velocity.x * DANGER_ZONE_INCR_X_MULTIPLIER));
             float dangerZoneY = metrics.getHeight() * (DANGER_ZONE_BASE_Y_MULTIPLIER + (velocity.y * DANGER_ZONE_INCR_Y_MULTIPLIER));
             // clamp it such that when added to the viewport, they don't exceed page size.
@@ -684,17 +680,17 @@ final class DisplayPortCalculator {
         public DisplayPortMetrics calculate(ImmutableViewportMetrics metrics, PointF velocity) {
             float width = metrics.getWidth();
             float height = metrics.getHeight();
             mPixelArea = (int)(width * height);
 
             if (velocity.length() < VELOCITY_THRESHOLD) {
                 // if we're going slow, expand the displayport to 9x viewport size
                 RectF margins = new RectF(width, height, width, height);
-                return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+                return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
             }
 
             // figure out how far we expect to be
             float minDx = velocity.x * mMinFramesToDraw;
             float minDy = velocity.y * mMinFramesToDraw;
             float maxDx = velocity.x * mMaxFramesToDraw;
             float maxDy = velocity.y * mMaxFramesToDraw;
 
@@ -709,17 +705,17 @@ final class DisplayPortCalculator {
 
             // and finally generate the displayport. the min/max stuff takes care of
             // negative velocities as well as positive.
             RectF margins = new RectF(
                 -Math.min(minDx, maxDx),
                 -Math.min(minDy, maxDy),
                 Math.max(minDx, maxDx),
                 Math.max(minDy, maxDy));
-            return getTileAlignedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
+            return getPageClampedDisplayPortMetrics(margins, metrics.zoomFactor, metrics);
         }
 
         @Override
         public boolean aboutToCheckerboard(ImmutableViewportMetrics metrics, PointF velocity, DisplayPortMetrics displayPort) {
             // the code below is the same as in calculate() but is awkward to refactor since it has multiple outputs.
             // refer to the comments in calculate() to understand what this is doing.
             float minDx = velocity.x * mMinFramesToDraw;
             float minDy = velocity.y * mMinFramesToDraw;
--- a/testing/marionette/client/marionette/tests/unit/test_emulator.py
+++ b/testing/marionette/client/marionette/tests/unit/test_emulator.py
@@ -7,40 +7,37 @@ from unittest import skip
 from marionette.marionette_test import MarionetteTestCase, skip_if_desktop, skip_unless_protocol
 from marionette_driver.errors import MarionetteException, JavascriptException
 
 
 class TestEmulatorContent(MarionetteTestCase):
     @skip_if_desktop
     def test_emulator_cmd(self):
         self.marionette.set_script_timeout(10000)
-        expected = ["<build>",
-                    "OK"]
-        result = self.marionette.execute_async_script("""
-        runEmulatorCmd("avd name", marionetteScriptFinished)
-        """);
-        self.assertEqual(result, expected)
+        expected = ["<build>", "OK"]
+        res = self.marionette.execute_async_script(
+            "runEmulatorCmd('avd name', marionetteScriptFinished)");
+        self.assertEqual(res, expected)
 
     @skip_if_desktop
     def test_emulator_shell(self):
         self.marionette.set_script_timeout(10000)
         expected = ["Hello World!"]
-        result = self.marionette.execute_async_script("""
-        runEmulatorShell(["echo", "Hello World!"], marionetteScriptFinished)
-        """);
-        self.assertEqual(result, expected)
+        res = self.marionette.execute_async_script(
+            "runEmulatorShell(['echo', 'Hello World!'], marionetteScriptFinished)")
+        self.assertEqual(res, expected)
 
     @skip_if_desktop
     def test_emulator_order(self):
         self.marionette.set_script_timeout(10000)
-        self.assertRaises(MarionetteException,
-                          self.marionette.execute_async_script,
-        """runEmulatorCmd("gsm status", function(result) {});
-           marionetteScriptFinished(true);
-        """);
+        with self.assertRaises(MarionetteException):
+            self.marionette.execute_async_script("""
+               runEmulatorCmd("gsm status", function(res) {});
+               marionetteScriptFinished(true);
+               """)
 
 
 class TestEmulatorChrome(TestEmulatorContent):
     def setUp(self):
         super(TestEmulatorChrome, self).setUp()
         self.marionette.set_context("chrome")
 
 
@@ -127,11 +124,29 @@ class TestEmulatorCallbacks(MarionetteTe
             self.assertEqual("shell response", res)
 
     @skip_unless_protocol(lambda level: level >= 3)
     def test_emulator_result_error_chrome(self):
         with self.marionette.using_context("chrome"):
             with self.assertRaisesRegexp(JavascriptException, "TypeError"):
                 self.marionette.execute_async_script("runEmulatorCmd()")
 
+    def test_multiple_callbacks(self):
+        res = self.marionette.execute_async_script("""
+            runEmulatorCmd("what");
+            runEmulatorCmd("ho");
+            marionetteScriptFinished("Frobisher");
+            """)
+        self.assertEqual("Frobisher", res)
+
+    # This should work, but requires work on emulator callbacks:
+    """
+    def test_multiple_nested_callbacks(self):
+        res = self.marionette.execute_async_script('''
+            runEmulatorCmd("what", function(res) {
+              runEmulatorCmd("ho", marionetteScriptFinished);
+            });''')
+        self.assertEqual("cmd response", res)
+    """
+
 
 def escape(word):
     return "'%s'" % word
--- a/testing/marionette/driver/marionette_driver/marionette.py
+++ b/testing/marionette/driver/marionette_driver/marionette.py
@@ -666,16 +666,37 @@ class Marionette(object):
         finally:
             s.close()
 
     def wait_for_port(self, timeout=60):
         return transport.wait_for_port(self.host, self.port, timeout=timeout)
 
     @do_crash_check
     def _send_message(self, name, params=None, key=None):
+        """Send a blocking message to the server.
+
+        Marionette provides an asynchronous, non-blocking interface and
+        this attempts to paper over this by providing a synchronous API
+        to the user.
+
+        In particular, the Python client can be instructed to carry out
+        a sequence of instructions on the connected emulator.  For this
+        reason, if ``execute_script``, ``execute_js_script``, or
+        ``execute_async_script`` is called, it will loop until all
+        commands requested from the server have been exhausted, and we
+        receive our expected response.
+
+        :param name: Requested command key.
+        :param params: Optional dictionary of key/value arguments.
+        :param key: Optional key to extract from response.
+
+        :returns: Full response from the server, or if `key` is given,
+            the value of said key in the response.
+        """
+
         if not self.session_id and name != "newSession":
             raise errors.MarionetteException("Please start a session")
 
         try:
             if self.protocol < 3:
                 data = {"name": name}
                 if params:
                     data["parameters"] = params
@@ -687,23 +708,26 @@ class Marionette(object):
 
         except IOError:
             if self.instance and not hasattr(self.instance, 'detached'):
                 # If we've launched the binary we've connected to, wait
                 # for it to shut down.
                 returncode = self.instance.runner.wait(timeout=self.DEFAULT_STARTUP_TIMEOUT)
                 raise IOError("process died with returncode %s" % returncode)
             raise
+
         except socket.timeout:
             self.session = None
             self.window = None
             self.client.close()
             raise errors.TimeoutException("Connection timed out")
 
-        if isinstance(msg, transport.Command):
+        # support execution of commands on the client,
+        # loop until we receive our expected response
+        while isinstance(msg, transport.Command):
             if msg.name == "runEmulatorCmd":
                 self.emulator_callback_id = msg.params.get("id")
                 msg = self._emulator_cmd(msg.params["emulator_cmd"])
             elif msg.name == "runEmulatorShell":
                 self.emulator_callback_id = msg.params.get("id")
                 msg = self._emulator_shell(msg.params["emulator_shell"])
             else:
                 raise IOError("Unknown command: %s" % msg)
--- a/testing/marionette/proxy.js
+++ b/testing/marionette/proxy.js
@@ -109,17 +109,17 @@ ContentSender.prototype.send = function(
 
   this.curId = uuidgen.generateUUID().toString();
 
   let proxy = new Promise((resolve, reject) => {
     let removeListeners = (n, fn) => {
       let rmFn = msg => {
         if (this.curId !== msg.json.command_id) {
           logger.warn("Skipping out-of-sync response from listener: " +
-              `Expected response to \`${name}' with ID ${this.curId}, ` +
+              `Expected response to ${name} with ID ${this.curId}, ` +
               "but got: " + msg.name + msg.json.toSource());
           return;
         }
 
         this.removeListeners();
         modal.removeHandler(handleDialog);
 
         fn(msg);
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/serviceworkerobject-scripturl.https.html
@@ -16,12 +16,11 @@ function url_test(name, url) {
                         'Returned ServiceWorker object should have scriptURL');
           service_worker_unregister_and_done(t, scope);
         })
       .catch(unreached_rejection(t));
   }, 'Verify the scriptURL property: ' + name);
 }
 
 url_test('relative', 'resources/empty-worker.js');
-url_test('with-fragment', 'resources/empty-worker.js#ref');
 url_test('absolute', (new URL('./resources/empty-worker.js', window.location)).href);
 
 </script>
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in
+++ /dev/null
@@ -1,1 +0,0 @@
-%/dump_syms.mm: ;
deleted file mode 100644
--- a/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in
+++ /dev/null
@@ -1,1 +0,0 @@
-%/dump_syms_tool.mm: ;
--- a/xpcom/idl-parser/xpidl/header.py
+++ b/xpcom/idl-parser/xpidl/header.py
@@ -106,16 +106,29 @@ def paramlistAsNative(m, empty='void'):
     if not m.notxpcom and m.realtype.name != 'void':
         l.append(paramAsNative(xpidl.Param(paramtype='out',
                                            type=None,
                                            name='_retval',
                                            attlist=[],
                                            location=None,
                                            realtype=m.realtype)))
 
+    # Set any optional out params to default to nullptr. Skip if we just added
+    # extra non-optional args to l.
+    if len(l) == len(m.params):
+        paramIter = len(m.params) - 1
+        while (paramIter >= 0 and m.params[paramIter].optional and
+                m.params[paramIter].paramtype == "out"):
+            t = m.params[paramIter].type
+            # Strings can't be optional, so this shouldn't happen, but let's make sure:
+            if t == "AString" or t == "ACString" or t == "DOMString" or t == "AUTF8String":
+                break
+            l[paramIter] += " = nullptr"
+            paramIter -= 1
+
     if len(l) == 0:
         return empty
 
     return ", ".join(l)
 
 
 def paramAsNative(p):
     return "%s%s" % (p.nativeType(),