merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 20 Nov 2014 11:38:15 +0100
changeset 216632 6ce1b906c690fd60abfbf4c7dea60d9e405f0ad8
parent 216631 cc4e62b85d77b09c619097fdb3554bce111d1c13 (current diff)
parent 216567 19b303e7e7f262f0a8ee925770981499b3632e5f (diff)
child 216633 7b7941988a83594e39548b9f855c774844b269ab
child 216651 7d17b594834f9fe5b538161241f9868c84a54529
child 216732 87e96cf43c685eb1d476ab3a42d29a315a68e75d
child 216763 66c4c215d9f7d2da77d3bc94490a73495fdc4efb
push id52105
push usercbook@mozilla.com
push dateThu, 20 Nov 2014 11:03:23 +0000
treeherdermozilla-inbound@7b7941988a83 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
6ce1b906c690 / 36.0a1 / 20141120030202 / files
nightly linux64
6ce1b906c690 / 36.0a1 / 20141120030202 / files
nightly mac
6ce1b906c690 / 36.0a1 / 20141120030202 / files
nightly win32
6ce1b906c690 / 36.0a1 / 20141120030202 / files
nightly win64
6ce1b906c690 / 36.0a1 / 20141120030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
--- a/browser/devtools/framework/test/browser_devtools_api.js
+++ b/browser/devtools/framework/test/browser_devtools_api.js
@@ -71,17 +71,20 @@ function runTests1(aTab) {
     is(toolbox.target, target, "toolbox target is correct");
     is(toolbox._host.hostTab, gBrowser.selectedTab, "toolbox host is correct");
 
     ok(events["init"], "init event fired");
     ok(events["ready"], "ready event fired");
 
     gDevTools.unregisterTool(toolId1);
 
-    runTests2();
+    // Wait for unregisterTool to select the next tool before calling runTests2,
+    // otherwise we will receive the wrong select event when waiting for
+    // unregisterTool to select the next tool in continueTests below.
+    toolbox.once("select", runTests2);
   });
 }
 
 // Test scenario 2: the tool definition build method returns panel instance.
 function runTests2() {
   let toolDefinition = {
     id: toolId2,
     isTargetSupported: function() true,
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -546,69 +546,16 @@ ConvertActorsToBlobs(IDBDatabase* aDatab
 
       file->mFile.swap(blob);
       file->mFileInfo.swap(fileInfo);
     }
   }
 }
 
 void
-DispatchSuccessEvent(ResultHelper* aResultHelper,
-                     nsIDOMEvent* aEvent = nullptr)
-{
-  MOZ_ASSERT(aResultHelper);
-
-  PROFILER_LABEL("IndexedDB",
-                 "DispatchSuccessEvent",
-                 js::ProfileEntry::Category::STORAGE);
-
-  nsRefPtr<IDBRequest> request = aResultHelper->Request();
-  MOZ_ASSERT(request);
-  request->AssertIsOnOwningThread();
-
-  nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
-
-  nsCOMPtr<nsIDOMEvent> successEvent;
-  if (!aEvent) {
-    successEvent = CreateGenericEvent(request,
-                                      nsDependentString(kSuccessEventType),
-                                      eDoesNotBubble,
-                                      eNotCancelable);
-    if (NS_WARN_IF(!successEvent)) {
-      return;
-    }
-
-    aEvent = successEvent;
-  }
-
-  request->SetResultCallback(aResultHelper);
-
-  MOZ_ASSERT(aEvent);
-  MOZ_ASSERT_IF(transaction, transaction->IsOpen());
-
-  bool dummy;
-  nsresult rv = request->DispatchEvent(aEvent, &dummy);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return;
-  }
-
-  MOZ_ASSERT_IF(transaction,
-                transaction->IsOpen() || transaction->IsAborted());
-
-  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
-  MOZ_ASSERT(internalEvent);
-
-  if (transaction &&
-      transaction->IsOpen() &&
-      internalEvent->mFlags.mExceptionHasBeenRisen) {
-    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
-  }
-}
-
-void
 DispatchErrorEvent(IDBRequest* aRequest,
                    nsresult aErrorCode,
                    IDBTransaction* aTransaction = nullptr,
                    nsIDOMEvent* aEvent = nullptr)
 {
   MOZ_ASSERT(aRequest);
   aRequest->AssertIsOnOwningThread();
   MOZ_ASSERT(NS_FAILED(aErrorCode));
@@ -657,16 +604,74 @@ DispatchErrorEvent(IDBRequest* aRequest,
     if (internalEvent->mFlags.mExceptionHasBeenRisen) {
       transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
     } else if (doDefault) {
       transaction->Abort(request);
     }
   }
 }
 
+void
+DispatchSuccessEvent(ResultHelper* aResultHelper,
+                     nsIDOMEvent* aEvent = nullptr)
+{
+  MOZ_ASSERT(aResultHelper);
+
+  PROFILER_LABEL("IndexedDB",
+                 "DispatchSuccessEvent",
+                 js::ProfileEntry::Category::STORAGE);
+
+  nsRefPtr<IDBRequest> request = aResultHelper->Request();
+  MOZ_ASSERT(request);
+  request->AssertIsOnOwningThread();
+
+  nsRefPtr<IDBTransaction> transaction = aResultHelper->Transaction();
+
+  if (transaction && transaction->IsAborted()) {
+    DispatchErrorEvent(request, transaction->AbortCode(), transaction);
+    return;
+  }
+
+  nsCOMPtr<nsIDOMEvent> successEvent;
+  if (!aEvent) {
+    successEvent = CreateGenericEvent(request,
+                                      nsDependentString(kSuccessEventType),
+                                      eDoesNotBubble,
+                                      eNotCancelable);
+    if (NS_WARN_IF(!successEvent)) {
+      return;
+    }
+
+    aEvent = successEvent;
+  }
+
+  request->SetResultCallback(aResultHelper);
+
+  MOZ_ASSERT(aEvent);
+  MOZ_ASSERT_IF(transaction, transaction->IsOpen());
+
+  bool dummy;
+  nsresult rv = request->DispatchEvent(aEvent, &dummy);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return;
+  }
+
+  MOZ_ASSERT_IF(transaction,
+                transaction->IsOpen() || transaction->IsAborted());
+
+  WidgetEvent* internalEvent = aEvent->GetInternalNSEvent();
+  MOZ_ASSERT(internalEvent);
+
+  if (transaction &&
+      transaction->IsOpen() &&
+      internalEvent->mFlags.mExceptionHasBeenRisen) {
+    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
+  }
+}
+
 } // anonymous namespace
 
 /*******************************************************************************
  * Local class implementations
  ******************************************************************************/
 
 void
 PermissionRequestMainProcessHelper::OnPromptComplete(
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -184,16 +184,23 @@ public:
 
   bool
   IsAborted() const
   {
     AssertIsOnOwningThread();
     return NS_FAILED(mAbortCode);
   }
 
+  nsresult
+  AbortCode() const
+  {
+    AssertIsOnOwningThread();
+    return mAbortCode;
+  }
+
   void
   GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
 
   // 'Get' prefix is to avoid name collisions with the enum
   Mode
   GetMode() const
   {
     AssertIsOnOwningThread();
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -1664,16 +1664,17 @@ MediaCache::NoteSeek(MediaCacheStream* a
     // Any played block that is entirely after the start of the seeked-over
     // range must be converted.
     int32_t blockIndex =
       (aStream->mStreamOffset + BLOCK_SIZE - 1)/BLOCK_SIZE;
     int32_t endIndex =
       std::min<int64_t>((aOldOffset + BLOCK_SIZE - 1)/BLOCK_SIZE,
              aStream->mBlocks.Length());
     while (blockIndex < endIndex) {
+      MOZ_ASSERT(endIndex > 0);
       int32_t cacheBlockIndex = aStream->mBlocks[endIndex - 1];
       if (cacheBlockIndex >= 0) {
         BlockOwner* bo = GetBlockOwner(cacheBlockIndex, aStream);
         NS_ASSERTION(bo, "Stream doesn't own its blocks?");
         if (bo->mClass == PLAYED_BLOCK) {
           aStream->mPlayedBlocks.RemoveBlock(cacheBlockIndex);
           bo->mClass = READAHEAD_BLOCK;
           // Adding this as the first block is sure to be OK since
--- a/dom/media/mediasource/MediaSource.cpp
+++ b/dom/media/mediasource/MediaSource.cpp
@@ -413,24 +413,20 @@ MediaSource::QueueAsyncSimpleEvent(const
   nsCOMPtr<nsIRunnable> event = new AsyncEventRunner<MediaSource>(this, aName);
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaSource::DurationChange(double aOldDuration, double aNewDuration)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("MediaSource(%p)::DurationChange(aNewDuration=%f)", this, aNewDuration);
+  MSE_DEBUG("MediaSource(%p)::DurationChange(aOldDuration=%f, aNewDuration=%f)", this, aOldDuration, aNewDuration);
 
   if (aNewDuration < aOldDuration) {
-    ErrorResult rv;
-    mSourceBuffers->Remove(aNewDuration, aOldDuration, rv);
-    if (rv.Failed()) {
-      return;
-    }
+    mSourceBuffers->RangeRemoval(aNewDuration, aOldDuration);
   }
   // TODO: If partial audio frames/text cues exist, clamp duration based on mSourceBuffers.
 }
 
 void
 MediaSource::NotifyEvicted(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/mediasource/SourceBuffer.cpp
+++ b/dom/media/mediasource/SourceBuffer.cpp
@@ -183,20 +183,29 @@ SourceBuffer::Remove(double aStart, doub
     return;
   }
   if (IsNaN(mMediaSource->Duration()) ||
       aStart < 0 || aStart > mMediaSource->Duration() ||
       aEnd <= aStart || IsNaN(aEnd)) {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return;
   }
-  if (mUpdating || mMediaSource->ReadyState() != MediaSourceReadyState::Open) {
+  if (mUpdating) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
+  if (mMediaSource->ReadyState() == MediaSourceReadyState::Ended) {
+    mMediaSource->SetReadyState(MediaSourceReadyState::Open);
+  }
+  RangeRemoval(aStart, aEnd);
+}
+
+void
+SourceBuffer::RangeRemoval(double aStart, double aEnd)
+{
   StartUpdating();
   /// TODO: Run coded frame removal algorithm.
 
   // Run the final step of the coded frame removal algorithm asynchronously
   // to ensure the SourceBuffer's updating flag transition behaves as
   // required by the spec.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &SourceBuffer::StopUpdating);
   NS_DispatchToMainThread(event);
--- a/dom/media/mediasource/SourceBuffer.h
+++ b/dom/media/mediasource/SourceBuffer.h
@@ -103,16 +103,19 @@ public:
   void Ended();
 
   // Evict data in the source buffer in the given time range.
   void Evict(double aStart, double aEnd);
 
   double GetBufferedStart();
   double GetBufferedEnd();
 
+  // Runs the range removal algorithm as defined by the MSE spec.
+  void RangeRemoval(double aStart, double aEnd);
+
 #if defined(DEBUG)
   void Dump(const char* aPath);
 #endif
 
 private:
   ~SourceBuffer();
 
   friend class AsyncEventRunner<SourceBuffer>;
--- a/dom/media/mediasource/SourceBufferList.cpp
+++ b/dom/media/mediasource/SourceBufferList.cpp
@@ -103,25 +103,22 @@ SourceBufferList::AnyUpdating()
     if (mSourceBuffers[i]->Updating()) {
       return true;
     }
   }
   return false;
 }
 
 void
-SourceBufferList::Remove(double aStart, double aEnd, ErrorResult& aRv)
+SourceBufferList::RangeRemoval(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MSE_DEBUG("SourceBufferList(%p)::Remove(aStart=%f, aEnd=%f", this, aStart, aEnd);
+  MSE_DEBUG("SourceBufferList(%p)::RangeRemoval(aStart=%f, aEnd=%f", this, aStart, aEnd);
   for (uint32_t i = 0; i < mSourceBuffers.Length(); ++i) {
-    mSourceBuffers[i]->Remove(aStart, aEnd, aRv);
-    if (aRv.Failed()) {
-      return;
-    }
+    mSourceBuffers[i]->RangeRemoval(aStart, aEnd);
   }
 }
 
 void
 SourceBufferList::Evict(double aStart, double aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("SourceBufferList(%p)::Evict(aStart=%f, aEnd=%f)", this, aStart, aEnd);
--- a/dom/media/mediasource/SourceBufferList.h
+++ b/dom/media/mediasource/SourceBufferList.h
@@ -61,19 +61,18 @@ public:
   void Clear();
 
   // True if list has zero entries.
   bool IsEmpty();
 
   // Returns true if updating is true on any SourceBuffers in the list.
   bool AnyUpdating();
 
-  // Calls Remove(aStart, aEnd) on each SourceBuffer in the list.  Aborts on
-  // first error, with result returned in aRv.
-  void Remove(double aStart, double aEnd, ErrorResult& aRv);
+  // Runs the range removal steps from the MSE specification on each SourceBuffer.
+  void RangeRemoval(double aStart, double aEnd);
 
   // Mark all SourceBuffers input buffers as ended.
   void Ended();
 
   // Evicts data for the given time range from each SourceBuffer in the list.
   void Evict(double aStart, double aEnd);
 
   // Returns the highest end time of any of the Sourcebuffers.
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -4,16 +4,18 @@ support-files =
   mediasource.js
   seek.webm seek.webm^headers^
   seek_lowres.webm seek_lowres.webm^headers^
 
 [test_MediaSource.html]
 [test_MediaSource_disabled.html]
 [test_BufferedSeek.html]
 [test_BufferingWait.html]
+[test_EndOfStream.html]
+skip-if = (toolkit == 'android' || buildapp == 'mulet') #timeout android/mulet only bug 1101187
 [test_FrameSelection.html]
 [test_HaveMetadataUnbufferedSeek.html]
 [test_LoadedMetadataFired.html]
 [test_SeekableAfterEndOfStream.html]
 [test_SeekableAfterEndOfStreamSplit.html]
 [test_SeekableBeforeEndOfStream.html]
 [test_SeekableBeforeEndOfStreamSplit.html]
 [test_SplitAppendDelay.html]
--- a/dom/media/mediasource/test/test_BufferedSeek.html
+++ b/dom/media/mediasource/test/test_BufferedSeek.html
@@ -7,24 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       sb.addEventListener("updateend", function () {
-        ms.endOfStream()
+        updateCount++;
+        /* Ensure that we endOfStream on the first update event only as endOfStream can
+           raise more if the duration of the last buffered range and the intial duration
+           differ. See bug 1065207 */
+        if (updateCount == 1) {
+          ms.endOfStream();
+        };
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
       if (v.currentTime != target &&
           v.buffered.length &&
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_EndOfStream.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: endOfStream call after an appendBuffer</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+runWithMSE(function () {
+  var ms = new MediaSource();
+
+  var v = document.createElement("video");
+  v.src = URL.createObjectURL(ms);
+  document.body.appendChild(v);
+
+  ms.addEventListener("sourceopen", function () {
+    var sb = ms.addSourceBuffer("video/webm");
+
+    fetchWithXHR("seek.webm", function (arrayBuffer) {
+      sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 88966));
+      var count = 0;
+      sb.addEventListener("updateend", function () {
+        ++count;
+        if (count == 1) {
+          setTimeout(function() {
+                       var fail = false;
+                       try {
+                         ms.endOfStream();
+                       } catch (e) {
+                         fail = true;
+                       }
+                       ok(!fail, "MediaSource.endOfStream succeeded");
+                       SimpleTest.finish();
+                     }, 0);
+        }
+      });
+    });
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/mediasource/test/test_FrameSelection.html
+++ b/dom/media/mediasource/test/test_FrameSelection.html
@@ -7,16 +7,18 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       // Append entire file covering range [0, 4].
       sb.appendBuffer(new Uint8Array(arrayBuffer));
     });
@@ -29,23 +31,23 @@ runWithMSE(function (ms, v) {
     v.addEventListener("loadedmetadata", function () {
       is(v.currentTime, 0, "currentTime has correct initial value");
       is(v.videoWidth, 320, "videoWidth has correct initial value");
       is(v.videoHeight, 240, "videoHeight has correct initial value");
 
       fetchWithXHR("seek_lowres.webm", function (arrayBuffer) {
         // Append initialization segment.
         sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 438));
-        var first = true;
         sb.addEventListener("updateend", function () {
-          if (first) {
+          updateCount++;
+          if (updateCount == 1) {
             // Append media segment covering range [2, 4].
             sb.appendBuffer(new Uint8Array(arrayBuffer, 51003));
-            first = false;
-          } else {
+          }
+          else if (updateCount == 2) {
             ms.endOfStream();
             target = targets.shift();
             v.currentTime = target.currentTime;
           }
         });
       });
     });
 
--- a/dom/media/mediasource/test/test_MediaSource.html
+++ b/dom/media/mediasource/test/test_MediaSource.html
@@ -52,17 +52,22 @@ runWithMSE(function () {
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       is(sb.updating, true, "SourceBuffer.updating is expected value after appendBuffer");
     });
 
     sb.addEventListener("update", function () {
       is(sb.updating, false, "SourceBuffer.updating is expected value in update event");
       updateCount++;
-      ms.endOfStream();
+      /* Ensure that we endOfStream on the first update event only as endOfStream can
+         raise more if the duration of the last buffered range and the intial duration
+         differ. See bug 1065207 */
+      if (updateCount == 1) {
+        ms.endOfStream();
+      }
     });
 
     sb.addEventListener("updatestart", function () {
       updatestartCount++;
     });
 
     sb.addEventListener("updateend", function () {
       is(sb.updating, false, "SourceBuffer.updating is expected value in updateend event");
@@ -79,19 +84,20 @@ runWithMSE(function () {
   v.addEventListener("loadedmetadata", function () {
     loadedmetadataCount++;
   });
 
   v.addEventListener("ended", function () {
     // XXX: Duration should be exactly 4.0, see bug 1065207.
     ok(Math.abs(v.duration - 4) <= 0.002, "Video has correct duration");
     ok(Math.abs(v.currentTime - 4) <= 0.002, "Video has played to end");
-    is(updateCount, 1, "update event received");
-    is(updateendCount, 1, "updateend event received");
-    is(updatestartCount, 1, "updatestart event received");
+    // XXX: 2 update events can be received dueto duration differences, see bug 1065207.
+    ok(updateCount == 1 || updateCount == 2, "update event received");
+    ok(updateendCount == 1 || updateendCount == 2, "updateend event received");
+    ok(updatestartCount == 1 || updatestartCount == 2, "updatestart event received");
     is(loadedmetadataCount, 1, "loadedmetadata event received");
     v.parentNode.removeChild(v);
     SimpleTest.finish();
   });
 });
 
 </script>
 </pre>
--- a/dom/media/mediasource/test/test_SeekableAfterEndOfStream.html
+++ b/dom/media/mediasource/test/test_SeekableAfterEndOfStream.html
@@ -7,24 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer));
       sb.addEventListener("updateend", function () {
-        ms.endOfStream()
+        updateCount++;
+        /* Ensure that we endOfStream on the first update event only as endOfStream can
+           raise more if the duration of the last buffered range and the intial duration
+           differ. See bug 1065207 */
+        if (updateCount == 1) {
+          ms.endOfStream();
+        };
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
       ok(v.seekable.length, "Resource is seekable");
       ok(v.seekable.length &&
--- a/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
+++ b/dom/media/mediasource/test/test_SeekableAfterEndOfStreamSplit.html
@@ -19,17 +19,17 @@ runWithMSE(function (ms, v) {
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 25223));
       var updateCount = 0;
       sb.addEventListener("updateend", function () {
         updateCount++;
         if (updateCount == 1) {
           sb.appendBuffer(new Uint8Array(arrayBuffer, 25223));
         }
-        else {
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
     });
 
     var target = 2;
 
     v.addEventListener("loadedmetadata", function () {
--- a/dom/media/mediasource/test/test_SplitAppend.html
+++ b/dom/media/mediasource/test/test_SplitAppend.html
@@ -7,28 +7,30 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
-      var first = true;
       sb.addEventListener("updateend", function () {
-        if (first) {
+        updateCount++;
+        if (updateCount == 1) {
           sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
-          first = false;
-        } else {
+        }
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
--- a/dom/media/mediasource/test/test_SplitAppendDelay.html
+++ b/dom/media/mediasource/test/test_SplitAppendDelay.html
@@ -7,30 +7,32 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
+var updateCount = 0;
+
 runWithMSE(function (ms, v) {
   ms.addEventListener("sourceopen", function () {
     var sb = ms.addSourceBuffer("video/webm");
 
     fetchWithXHR("seek.webm", function (arrayBuffer) {
       sb.appendBuffer(new Uint8Array(arrayBuffer, 0, 318));
-      var first = true;
       sb.addEventListener("updateend", function () {
-        if (first) {
+        updateCount++;
+        if (updateCount == 1) {
           window.setTimeout(function () {
             sb.appendBuffer(new Uint8Array(arrayBuffer, 318));
-            first = false;
           }, 1000);
-        } else {
+        }
+        else if (updateCount == 2) {
           ms.endOfStream();
         }
       });
       v.play();
     });
   });
 
   v.addEventListener("ended", function () {
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -116,16 +116,25 @@ function PlayFragmented(test, elem, toke
   return new Promise(function(resolve, reject) {
     var ms = new MediaSource();
     elem.src = URL.createObjectURL(ms);
 
     var sb;
     var curFragment = 0;
 
     function addNextFragment() {
+      /* We can get another updateevent as a result of calling ms.endOfStream() if
+         the highest end time of our source buffers is different from that of the
+         media source duration. Due to bug 1065207 this can happen because of
+         inaccuracies in the frame duration calculations. Check if we are already
+         "ended" and ignore the update event */
+      if (ms.readyState == "ended") {
+        return;
+      }
+
       if (curFragment >= test.fragments.length) {
         Log(token, "addNextFragment() end of stream");
         ms.endOfStream();
         resolve();
         return;
       }
 
       var fragmentFile = test.fragments[curFragment++];
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginHelperQt.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PluginHelperQt.h"
+#include <QtCore/QCoreApplication>
+#include <QtCore/QEventLoop>
+
+static const int kMaxtimeToProcessEvents = 30;
+
+bool
+PluginHelperQt::AnswerProcessSomeEvents()
+{
+    QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/dom/plugins/ipc/PluginHelperQt.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef PluginHelperQt_h_
+#define PluginHelperQt_h_
+
+class PluginHelperQt
+{
+public:
+  static bool AnswerProcessSomeEvents();
+};
+
+#endif // PluginHelperQt_h_
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -1,19 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: sw=4 ts=4 et :
  * 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/. */
 
 #ifdef MOZ_WIDGET_QT
-// Must be included first to avoid conflicts.
-#include <QtCore/QCoreApplication>
-#include <QtCore/QEventLoop>
-#include "NestedLoopTimer.h"
+#include "PluginHelperQt.h"
 #endif
 
 #include "mozilla/plugins/PluginModuleParent.h"
 
 #include "base/process_util.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/ContentChild.h"
@@ -1641,23 +1638,21 @@ PluginModuleParent::ContentsScaleFactorC
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->ContentsScaleFactorChanged(aContentsScaleFactor);
 }
 #endif // #if defined(XP_MACOSX)
 
 #if defined(MOZ_WIDGET_QT)
-static const int kMaxtimeToProcessEvents = 30;
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
 {
     PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
-    QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
-
+    PluginHelperQt::AnswerProcessSomeEvents();
     PLUGIN_LOG_DEBUG(("... quitting mini nested loop"));
 
     return true;
 }
 
 #elif defined(XP_MACOSX)
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
--- a/dom/plugins/ipc/moz.build
+++ b/dom/plugins/ipc/moz.build
@@ -65,18 +65,19 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
     EXPORTS.mozilla.plugins += [
         'PluginInterposeOSX.h',
     ]
 
 if CONFIG['MOZ_ENABLE_QT']:
     GENERATED_SOURCES += [
         'moc_NestedLoopTimer.cpp',
     ]
-    UNIFIED_SOURCES += [
+    SOURCES += [
         'NestedLoopTimer.cpp',
+        'PluginHelperQt.cpp',
     ]
 
 UNIFIED_SOURCES += [
     'BrowserStreamChild.cpp',
     'BrowserStreamParent.cpp',
     'ChildAsyncCall.cpp',
     'ChildTimer.cpp',
     'PluginBackgroundDestroyer.cpp',
--- a/editor/libeditor/nsEditor.cpp
+++ b/editor/libeditor/nsEditor.cpp
@@ -1759,17 +1759,17 @@ public:
   {
   }
 
   NS_IMETHOD Run()
   {
     // Note that we don't need to check mDispatchInputEvent here.  We need
     // to check it only when the editor requests to dispatch the input event.
 
-    if (!mTarget->IsInDoc()) {
+    if (!mTarget->IsInComposedDoc()) {
       return NS_OK;
     }
 
     nsCOMPtr<nsIPresShell> ps = mEditor->GetPresShell();
     if (!ps) {
       return NS_OK;
     }
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -846,16 +846,26 @@ DrawTargetD2D1::factory()
   }
 
   RadialGradientEffectD2D1::Register(mFactory);
 
   return mFactory;
 }
 
 void
+DrawTargetD2D1::CleanupD2D()
+{
+  if (mFactory) {
+    RadialGradientEffectD2D1::Unregister(mFactory);
+    mFactory->Release();
+    mFactory = nullptr;
+  }
+}
+
+void
 DrawTargetD2D1::MarkChanged()
 {
   if (mSnapshot) {
     if (mSnapshot->hasOneRef()) {
       // Just destroy it, since no-one else knows about it.
       mSnapshot = nullptr;
     } else {
       mSnapshot->DrawTargetWillChange();
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -604,16 +604,21 @@ Factory::CreateDrawTargetForD3D11Texture
   return nullptr;
 }
 
 void
 Factory::SetDirect3D11Device(ID3D11Device *aDevice)
 {
   mD3D11Device = aDevice;
 
+  if (mD2D1Device) {
+    mD2D1Device->Release();
+    mD2D1Device = nullptr;
+  }
+
   RefPtr<ID2D1Factory1> factory = D2DFactory1();
 
   RefPtr<IDXGIDevice> device;
   aDevice->QueryInterface((IDXGIDevice**)byRef(device));
   factory->CreateDevice(device, &mD2D1Device);
 }
 
 ID3D11Device*
@@ -651,17 +656,22 @@ uint64_t
 Factory::GetD2DVRAMUsageSourceSurface()
 {
   return DrawTargetD2D::mVRAMUsageSS;
 }
 
 void
 Factory::D2DCleanup()
 {
+  if (mD2D1Device) {
+    mD2D1Device->Release();
+    mD2D1Device = nullptr;
+  }
   DrawTargetD2D::CleanupD2D();
+  DrawTargetD2D1::CleanupD2D();
 }
 
 #endif // XP_WIN
 
 #ifdef USE_SKIA_GPU
 TemporaryRef<DrawTarget>
 Factory::CreateDrawTargetSkiaWithGrContext(GrContext* aGrContext,
                                            const IntSize &aSize,
--- a/gfx/2d/RadialGradientEffectD2D1.cpp
+++ b/gfx/2d/RadialGradientEffectD2D1.cpp
@@ -279,16 +279,22 @@ RadialGradientEffectD2D1::Register(ID2D1
   HRESULT hr = aFactory->RegisterEffectFromString(CLSID_RadialGradientEffect, kXmlDescription, bindings, ARRAYSIZE(bindings), CreateEffect);
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to register radial gradient effect.";
   }
   return hr;
 }
 
+void
+RadialGradientEffectD2D1::Unregister(ID2D1Factory1 *aFactory)
+{
+  aFactory->UnregisterEffect(CLSID_RadialGradientEffect);
+}
+
 HRESULT __stdcall
 RadialGradientEffectD2D1::CreateEffect(IUnknown **aEffectImpl)
 {
   *aEffectImpl = static_cast<ID2D1EffectImpl*>(new RadialGradientEffectD2D1());
   (*aEffectImpl)->AddRef();
 
   return S_OK;
 }
--- a/gfx/2d/RadialGradientEffectD2D1.h
+++ b/gfx/2d/RadialGradientEffectD2D1.h
@@ -67,16 +67,17 @@ public:
 
   // ID2D1TransformNode
   IFACEMETHODIMP_(UINT32) GetInputCount() const { return 1; }
 
   // ID2D1DrawTransform
   IFACEMETHODIMP SetDrawInfo(ID2D1DrawInfo *pDrawInfo);
 
   static HRESULT Register(ID2D1Factory1* aFactory);
+  static void Unregister(ID2D1Factory1* aFactory);
   static HRESULT __stdcall CreateEffect(IUnknown** aEffectImpl);
 
   HRESULT SetStopCollection(IUnknown *aStopCollection);
   IUnknown *GetStopCollection() const { return mStopCollection; }
 
 private:
   TemporaryRef<ID2D1ResourceTexture> CreateGradientTexture();
 
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -84,27 +84,31 @@ if CONFIG['MOZ_ENABLE_SKIA']:
     ]
 
 # Are we targeting x86 or x64?  If so, build SSE2 files.
 if CONFIG['INTEL_ARCHITECTURE']:
     # VC2005 doesn't support _mm_castsi128_ps, so SSE2 is turned off
     if CONFIG['_MSC_VER'] != '1400':
         SOURCES += [
             'BlurSSE2.cpp',
-            'convolverSSE2.cpp',
             'FilterProcessingSSE2.cpp',
             'ImageScalingSSE2.cpp',
         ]
+        if CONFIG['MOZ_ENABLE_SKIA']:
+            SOURCES += [
+                'convolverSSE2.cpp',
+            ]
         DEFINES['USE_SSE2'] = True
         # The file uses SSE2 intrinsics, so it needs special compile flags on some
         # compilers.
         SOURCES['BlurSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
         SOURCES['FilterProcessingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
         SOURCES['ImageScalingSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
-        SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+        if CONFIG['MOZ_ENABLE_SKIA']:
+            SOURCES['convolverSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
 
 UNIFIED_SOURCES += [
     'Blur.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
     'DrawEventRecorder.cpp',
     'DrawTarget.cpp',
     'DrawTargetCairo.cpp',
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -397,29 +397,16 @@ APZCTreeManager::PrepareAPZCForLayer(con
       apzc->AddHitTestRegions(EventRegions(unobscured));
       APZCTM_LOG("Adding region %s to visible region of APZC %p\n", Stringify(unobscured).c_str(), apzc);
     }
   }
 
   return apzc;
 }
 
-static EventRegions
-EventRegionsFor(const LayerMetricsWrapper& aLayer)
-{
-  // This is a workaround for bug 1082594. We should be able to replace this
-  // with just a call to aLayer.GetEventRegions() once that bug is fixed.
-  if (aLayer.IsScrollInfoLayer()) {
-    EventRegions regions(ParentLayerIntRect::ToUntyped(RoundedIn(aLayer.Metrics().mCompositionBounds)));
-    regions.mDispatchToContentHitRegion = regions.mHitRegion;
-    return regions;
-  }
-  return aLayer.GetEventRegions();
-}
-
 AsyncPanZoomController*
 APZCTreeManager::UpdatePanZoomControllerTree(TreeBuildingState& aState,
                                              const LayerMetricsWrapper& aLayer,
                                              uint64_t aLayersId,
                                              const gfx::Matrix4x4& aAncestorTransform,
                                              AsyncPanZoomController* aParent,
                                              AsyncPanZoomController* aNextSibling,
                                              const nsIntRegion& aObscured)
@@ -492,17 +479,17 @@ APZCTreeManager::UpdatePanZoomController
     next = UpdatePanZoomControllerTree(aState, child, childLayersId,
                                        ancestorTransform, aParent, next,
                                        obscured);
 
     // Each layer obscures its previous siblings, so we augment the obscured
     // region as we loop backwards through the children.
     nsIntRegion childRegion;
     if (gfxPrefs::LayoutEventRegionsEnabled()) {
-      childRegion = EventRegionsFor(child).mHitRegion;
+      childRegion = child.GetEventRegions().mHitRegion;
     } else {
       childRegion = child.GetVisibleRegion();
     }
     childRegion.Transform(gfx::To3DMatrix(child.GetTransform()));
     if (child.GetClipRect()) {
       childRegion.AndWith(*child.GetClipRect());
     }
 
@@ -526,17 +513,17 @@ APZCTreeManager::UpdatePanZoomController
     // When we compute the unobscured regions below, we subtract off the
     // |obscured| region, but it would also be ok to do this before the above
     // loop. At that point |obscured| would only have the uncles' hit regions
     // and not the children. The reason this is ok is again because of the way
     // we do hit-testing (where the deepest APZC is used) it doesn't matter if
     // we count the children as obscuring the parent or not.
 
     EventRegions unobscured;
-    unobscured.Sub(EventRegionsFor(aLayer), obscured);
+    unobscured.Sub(aLayer.GetEventRegions(), obscured);
     APZCTM_LOG("Picking up unobscured hit region %s from layer %p\n", Stringify(unobscured).c_str(), aLayer.GetLayer());
 
     // Take the hit region of the |aLayer|'s subtree (which has already been
     // transformed into the coordinate space of |aLayer|) and...
     EventRegions subtreeEventRegions = aState.mEventRegions.LastElement();
     aState.mEventRegions.RemoveElementAt(aState.mEventRegions.Length() - 1);
     // ... combine it with the hit region for this layer, and then ...
     subtreeEventRegions.OrWith(unobscured);
--- a/gfx/thebes/gfxQtPlatform.cpp
+++ b/gfx/thebes/gfxQtPlatform.cpp
@@ -2,17 +2,16 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <QPixmap>
 #include <QWindow>
 #ifdef MOZ_X11
 #include <qpa/qplatformnativeinterface.h>
-#include <qpa/qplatformintegration.h>
 #endif
 #include <QGuiApplication>
 #include <QScreen>
 
 #include "gfxQtPlatform.h"
 
 #include "gfxFontconfigUtils.h"
 
--- a/ipc/chromium/src/base/message_pump_qt.cc
+++ b/ipc/chromium/src/base/message_pump_qt.cc
@@ -10,17 +10,16 @@
 
 #include "base/message_pump_qt.h"
 
 #include <fcntl.h>
 #include <limits>
 #include <math.h>
 
 #include "base/eintr_wrapper.h"
-#include "base/lazy_instance.h"
 #include "base/logging.h"
 #include "base/platform_thread.h"
 
 namespace {
 // Cached QEvent user type, registered for our event system
 static int sPokeEvent;
 }  // namespace
 
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -47,33 +47,45 @@ class ChunkPool
     Chunk *head_;
     size_t count_;
 
   public:
     ChunkPool() : head_(nullptr), count_(0) {}
 
     size_t count() const { return count_; }
 
-    /* Must be called with the GC lock taken. */
-    inline Chunk *get(JSRuntime *rt);
+    Chunk *head() { MOZ_ASSERT(head_); return head_; }
+    Chunk *pop();
+    void push(Chunk *chunk);
+    Chunk *remove(Chunk *chunk);
 
-    /* Must be called either during the GC or with the GC lock taken. */
-    inline void put(Chunk *chunk);
+#ifdef DEBUG
+    bool contains(Chunk *chunk) const;
+    bool verify() const;
+#endif
 
-    class Enum {
+    // Pool mutation does not invalidate an Iter unless the mutation
+    // is of the Chunk currently being visited by the Iter.
+    class Iter {
       public:
-        explicit Enum(ChunkPool &pool) : pool(pool), chunkp(&pool.head_) {}
-        bool empty() { return !*chunkp; }
-        Chunk *front();
-        inline void popFront();
-        inline void removeAndPopFront();
+        explicit Iter(ChunkPool &pool) : current_(pool.head_) {}
+        bool done() const { return !current_; }
+        void next();
+        Chunk *get() const { return current_; }
+        operator Chunk *() const { return get(); }
+        Chunk *operator->() const { return get(); }
       private:
-        ChunkPool &pool;
-        Chunk **chunkp;
+        Chunk *current_;
     };
+
+  private:
+    // ChunkPool controls external resources with interdependencies on the
+    // JSRuntime and related structs, so must not be copied.
+    ChunkPool(const ChunkPool &) MOZ_DELETE;
+    ChunkPool operator=(const ChunkPool &) MOZ_DELETE;
 };
 
 // Performs extra allocation off the main thread so that when memory is
 // required on the main thread it will already be available and waiting.
 class BackgroundAllocTask : public GCParallelTask
 {
     // Guarded by the GC lock.
     JSRuntime *runtime;
@@ -458,20 +470,21 @@ class GCRuntime
         mode = m;
         marker.setGCMode(mode);
     }
 
     inline void updateOnFreeArenaAlloc(const ChunkInfo &info);
     inline void updateOnArenaFree(const ChunkInfo &info);
 
     GCChunkSet::Range allChunks() { return chunkSet.all(); }
-    Chunk **getAvailableChunkList();
     void moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock);
     bool hasChunk(Chunk *chunk) { return chunkSet.has(chunk); }
+    ChunkPool &availableChunks(const AutoLockGC &lock) { return availableChunks_; }
     ChunkPool &emptyChunks(const AutoLockGC &lock) { return emptyChunks_; }
+    const ChunkPool &availableChunks(const AutoLockGC &lock) const { return availableChunks_; }
     const ChunkPool &emptyChunks(const AutoLockGC &lock) const { return emptyChunks_; }
 
 #ifdef JS_GC_ZEAL
     void startVerifyPreBarriers();
     bool endVerifyPreBarriers();
     void startVerifyPostBarriers();
     bool endVerifyPostBarriers();
     void finishVerifier();
@@ -624,18 +637,18 @@ class GCRuntime
 
     /*
      * Doubly-linked lists of chunks from user and system compartments. The GC
      * allocates its arenas from the corresponding list and when all arenas
      * in the list head are taken, then the chunk is removed from the list.
      * During the GC when all arenas in a chunk become free, that chunk is
      * removed from the list and scheduled for release.
      */
-    js::gc::Chunk *availableChunkListHead;
-    js::gc::ChunkPool emptyChunks_;
+    ChunkPool availableChunks_;
+    ChunkPool emptyChunks_;
 
     js::RootedValueMap rootsHash;
 
     size_t maxMallocBytes;
 
     /*
      * Number of the committed arenas in all GC chunks including empty chunks.
      */
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -741,17 +741,17 @@ struct ChunkTrailer
 
 static_assert(sizeof(ChunkTrailer) == 2 * sizeof(uintptr_t) + sizeof(uint64_t),
               "ChunkTrailer size is incorrect.");
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo
 {
     Chunk           *next;
-    Chunk           **prevp;
+    Chunk           *prev;
 
     /* Free arenas are linked together with aheader.next. */
     ArenaHeader     *freeArenasHead;
 
 #if JS_BITS_PER_WORD == 32
     /*
      * Calculating sizes and offsets is simpler if sizeof(ChunkInfo) is
      * architecture-independent.
@@ -940,49 +940,29 @@ struct Chunk
     bool unused() const {
         return info.numArenasFree == ArenasPerChunk;
     }
 
     bool hasAvailableArenas() const {
         return info.numArenasFree != 0;
     }
 
-    inline void addToAvailableList(JSRuntime *rt);
-    inline void insertToAvailableList(Chunk **insertPoint);
-    inline void removeFromAvailableList();
-
     ArenaHeader *allocateArena(JSRuntime *rt, JS::Zone *zone, AllocKind kind,
                                const AutoLockGC &lock);
 
     enum ArenaDecommitState { IsCommitted = false, IsDecommitted = true };
     void releaseArena(JSRuntime *rt, ArenaHeader *aheader, const AutoLockGC &lock,
                       ArenaDecommitState state = IsCommitted);
     void recycleArena(ArenaHeader *aheader, SortedArenaList &dest, AllocKind thingKind,
                       size_t thingsPerArena);
 
     static Chunk *allocate(JSRuntime *rt);
 
     void decommitAllArenas(JSRuntime *rt);
 
-    /*
-     * Assuming that the info.prevp points to the next field of the previous
-     * chunk in a doubly-linked list, get that chunk.
-     */
-    Chunk *getPrevious() {
-        MOZ_ASSERT(info.prevp);
-        return fromPointerToNext(info.prevp);
-    }
-
-    /* Get the chunk from a pointer to its info.next field. */
-    static Chunk *fromPointerToNext(Chunk **nextFieldPtr) {
-        uintptr_t addr = reinterpret_cast<uintptr_t>(nextFieldPtr);
-        MOZ_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next));
-        return reinterpret_cast<Chunk *>(addr - offsetof(Chunk, info.next));
-    }
-
   private:
     inline void init(JSRuntime *rt);
 
     /* Search for a decommitted arena to allocate. */
     unsigned findDecommittedArenaOffset();
     ArenaHeader* fetchNextDecommittedArena();
 
     void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1101576.js
@@ -0,0 +1,14 @@
+// Random chosen test: js/src/jit-test/tests/ion/bug928423.js
+o = {
+        a: 1,
+            b: 1
+}
+print(1);
+for (var x = 0; x < 2; x++) {
+    print(2);
+    o["a1".substr(0, 1)]
+    o["b1".substr(0, 1)]
+}
+print(3);
+// jsfunfuzz
+"a" + "b"
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -6053,16 +6053,18 @@ static const VMFunction SubstringKernelI
 
 bool CodeGenerator::visitSubstr(LSubstr *lir)
 {
     Register string = ToRegister(lir->string());
     Register begin = ToRegister(lir->begin());
     Register length = ToRegister(lir->length());
     Register output = ToRegister(lir->output());
     Register temp = ToRegister(lir->temp());
+    Register temp2 = ToRegister(lir->temp2());
+    Register temp3 = ToRegister(lir->temp3());
     Address stringFlags(string, JSString::offsetOfFlags());
 
     Label isLatin1, notInline, nonZero, isInlinedLatin1;
 
     // For every edge case use the C++ variant.
     // Note: we also use this upon allocation failure in newGCString and
     // newGCFatInlineString. To squeeze out even more performance those failures
     // can be handled by allocate in ool code and returning to jit code to fill
@@ -6097,31 +6099,33 @@ bool CodeGenerator::visitSubstr(LSubstr 
 
     masm.branchTest32(Assembler::NonZero, stringFlags, Imm32(JSString::LATIN1_CHARS_BIT),
                       &isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS),
                      Address(output, JSString::offsetOfFlags()));
         masm.computeEffectiveAddress(stringStorage, temp);
         BaseIndex chars(temp, begin, ScaleFromElemWidth(sizeof(char16_t)));
-        masm.computeEffectiveAddress(chars, begin);
+        masm.computeEffectiveAddress(chars, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char16_t), sizeof(char16_t));
+        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char16_t), sizeof(char16_t));
+        masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store16(Imm32(0), Address(temp, 0));
         masm.jump(done);
     }
     masm.bind(&isInlinedLatin1);
     {
         masm.store32(Imm32(JSString::INIT_FAT_INLINE_FLAGS | JSString::LATIN1_CHARS_BIT),
                      Address(output, JSString::offsetOfFlags()));
-        masm.computeEffectiveAddress(stringStorage, temp);
+        masm.computeEffectiveAddress(stringStorage, temp2);
         static_assert(sizeof(char) == 1, "begin index shouldn't need scaling");
-        masm.addPtr(temp, begin);
+        masm.addPtr(begin, temp2);
         masm.computeEffectiveAddress(outputStorage, temp);
-        CopyStringChars(masm, temp, begin, length, string, sizeof(char), sizeof(char));
+        CopyStringChars(masm, temp, temp2, length, temp3, sizeof(char), sizeof(char));
+        masm.load32(Address(output, JSString::offsetOfLength()), length);
         masm.store8(Imm32(0), Address(temp, 0));
         masm.jump(done);
     }
 
     // Handle other cases with a DependentString.
     masm.bind(&notInline);
     masm.newGCString(output, temp, slowPath);
     masm.store32(length, Address(output, JSString::offsetOfLength()));
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -3445,41 +3445,49 @@ class LStringSplit : public LCallInstruc
     const LAllocation *separator() {
         return getOperand(1);
     }
     const MStringSplit *mir() const {
         return mir_->toStringSplit();
     }
 };
 
-class LSubstr : public LInstructionHelper<1, 3, 1>
+class LSubstr : public LInstructionHelper<1, 3, 3>
 {
   public:
     LIR_HEADER(Substr)
 
     LSubstr(const LAllocation &string, const LAllocation &begin, const LAllocation &length,
-            const LDefinition &temp)
+            const LDefinition &temp, const LDefinition &temp2, const LDefinition &temp3)
     {
         setOperand(0, string);
         setOperand(1, begin);
         setOperand(2, length);
         setTemp(0, temp);
+        setTemp(1, temp2);
+        setTemp(2, temp3);
     }
     const LAllocation *string() {
         return getOperand(0);
     }
     const LAllocation *begin() {
         return getOperand(1);
     }
     const LAllocation *length() {
         return getOperand(2);
     }
     const LDefinition *temp() {
         return getTemp(0);
     }
+    const LDefinition *temp2() {
+        return getTemp(1);
+    }
+    const LDefinition *temp3() {
+        return getTemp(2);
+    }
     const MStringSplit *mir() const {
         return mir_->toStringSplit();
     }
 };
 
 // Convert a 32-bit integer to a double.
 class LInt32ToDouble : public LInstructionHelper<1, 1, 0>
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2179,20 +2179,25 @@ LIRGenerator::visitStringReplace(MString
                                                       useRegisterAtStart(ins->pattern()),
                                                       useRegisterOrConstantAtStart(ins->replacement()));
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitSubstr(MSubstr *ins)
 {
-    LSubstr *lir = new (alloc()) LSubstr(useFixed(ins->string(), CallTempReg1),
+    // The last temporary need to be a register that can handle 8bit moves, but
+    // there is no way to signal that to register allocator, except to give a
+    // fixed temporary that is able to do this.
+    LSubstr *lir = new (alloc()) LSubstr(useRegister(ins->string()),
                                          useRegister(ins->begin()),
                                          useRegister(ins->length()),
-                                         temp());
+                                         temp(),
+                                         temp(),
+                                         tempFixed(CallTempReg1));
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitLambda(MLambda *ins)
 {
     if (ins->info().singletonType || ins->info().useNewTypeForClone) {
         // If the function has a singleton type, this instruction will only be
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -29,16 +29,17 @@ UNIFIED_SOURCES += [
     'testException.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForOfIterator.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
     'testFuncCallback.cpp',
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
+    'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCMarking.cpp',
     'testGCOutOfMemory.cpp',
     'testGCStoreBufferRemoval.cpp',
     'testHashTable.cpp',
     'testHashTableInit.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testGCChunkPool.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+* vim: set ts=8 sts=4 et sw=4 tw=99:
+*/
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Move.h"
+
+#include "gc/GCRuntime.h"
+#include "gc/Heap.h"
+
+#include "jsapi-tests/tests.h"
+
+BEGIN_TEST(testGCChunkPool)
+{
+    const int N = 10;
+    js::gc::ChunkPool pool;
+
+    // Create.
+    for (int i = 0; i < N; ++i) {
+        js::gc::Chunk *chunk = js::gc::Chunk::allocate(rt);
+        CHECK(chunk);
+        pool.push(chunk);
+    }
+    MOZ_ASSERT(pool.verify());
+
+    // Iterate.
+    uint32_t i = 0;
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), ++i)
+        CHECK(iter.get());
+    CHECK(i == pool.count());
+    MOZ_ASSERT(pool.verify());
+
+    // Push/Pop.
+    for (int i = 0; i < N; ++i) {
+        js::gc::Chunk *chunkA = pool.pop();
+        js::gc::Chunk *chunkB = pool.pop();
+        js::gc::Chunk *chunkC = pool.pop();
+        pool.push(chunkA);
+        pool.push(chunkB);
+        pool.push(chunkC);
+    }
+    MOZ_ASSERT(pool.verify());
+
+    // Remove.
+    js::gc::Chunk *chunk = nullptr;
+    int offset = N / 2;
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done(); iter.next(), --offset) {
+        if (offset == 0) {
+            chunk = pool.remove(iter.get());
+            break;
+        }
+    }
+    CHECK(chunk);
+    MOZ_ASSERT(!pool.contains(chunk));
+    MOZ_ASSERT(pool.verify());
+    pool.push(chunk);
+
+    // Destruct.
+    js::AutoLockGC lock(rt);
+    for (js::gc::ChunkPool::Iter iter(pool); !iter.done();) {
+        js::gc::Chunk *chunk = iter.get();
+        iter.next();
+        pool.remove(chunk);
+        js::gc::UnmapPages(chunk, js::gc::ChunkSize);
+    }
+
+    return true;
+}
+END_TEST(testGCChunkPool)
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -658,106 +658,136 @@ AllocChunk(JSRuntime *rt)
 }
 
 static inline void
 FreeChunk(JSRuntime *rt, Chunk *p)
 {
     UnmapPages(static_cast<void *>(p), ChunkSize);
 }
 
-/* Must be called with the GC lock taken. */
-inline Chunk *
-ChunkPool::get(JSRuntime *rt)
-{
-    Chunk *chunk = head_;
-    if (!chunk) {
-        MOZ_ASSERT(!count_);
+Chunk *
+ChunkPool::pop()
+{
+    MOZ_ASSERT(bool(head_) == bool(count_));
+    if (!count_)
         return nullptr;
-    }
-
-    MOZ_ASSERT(count_);
-    head_ = chunk->info.next;
-    --count_;
-    return chunk;
-}
-
-/* Must be called either during the GC or with the GC lock taken. */
-inline void
-ChunkPool::put(Chunk *chunk)
-{
+    return remove(head_);
+}
+
+void
+ChunkPool::push(Chunk *chunk)
+{
+    MOZ_ASSERT(!chunk->info.next);
+    MOZ_ASSERT(!chunk->info.prev);
+
     chunk->info.age = 0;
     chunk->info.next = head_;
+    if (head_)
+        head_->info.prev = chunk;
     head_ = chunk;
-    count_++;
-}
-
-inline Chunk *
-ChunkPool::Enum::front()
-{
-    Chunk *chunk = *chunkp;
-    MOZ_ASSERT_IF(chunk, pool.count() != 0);
+    ++count_;
+
+    MOZ_ASSERT(verify());
+}
+
+Chunk *
+ChunkPool::remove(Chunk *chunk)
+{
+    MOZ_ASSERT(count_ > 0);
+    MOZ_ASSERT(contains(chunk));
+
+    if (head_ == chunk)
+        head_ = chunk->info.next;
+    if (chunk->info.prev)
+        chunk->info.prev->info.next = chunk->info.next;
+    if (chunk->info.next)
+        chunk->info.next->info.prev = chunk->info.prev;
+    chunk->info.next = chunk->info.prev = nullptr;
+    --count_;
+
+    MOZ_ASSERT(verify());
     return chunk;
 }
 
-inline void
-ChunkPool::Enum::popFront()
-{
-    MOZ_ASSERT(!empty());
-    chunkp = &front()->info.next;
-}
-
-inline void
-ChunkPool::Enum::removeAndPopFront()
-{
-    MOZ_ASSERT(!empty());
-    *chunkp = front()->info.next;
-    --pool.count_;
+#ifdef DEBUG
+bool
+ChunkPool::contains(Chunk *chunk) const
+{
+    verify();
+    for (Chunk *cursor = head_; cursor; cursor = cursor->info.next) {
+        if (cursor == chunk)
+            return true;
+    }
+    return false;
+}
+
+bool
+ChunkPool::verify() const
+{
+    MOZ_ASSERT(bool(head_) == bool(count_));
+    uint32_t count = 0;
+    for (Chunk *cursor = head_; cursor; cursor = cursor->info.next, ++count) {
+        MOZ_ASSERT_IF(cursor->info.prev, cursor->info.prev->info.next == cursor);
+        MOZ_ASSERT_IF(cursor->info.next, cursor->info.next->info.prev == cursor);
+    }
+    MOZ_ASSERT(count_ == count);
+    return true;
+}
+#endif
+
+void
+ChunkPool::Iter::next()
+{
+    MOZ_ASSERT(!done());
+    current_ = current_->info.next;
 }
 
 Chunk *
 GCRuntime::expireEmptyChunkPool(bool shrinkBuffers, const AutoLockGC &lock)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
-    for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); ) {
-        Chunk *chunk = e.front();
+    for (ChunkPool::Iter iter(emptyChunks(lock)); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+
         MOZ_ASSERT(chunk->unused());
         MOZ_ASSERT(!chunkSet.has(chunk));
         if (freeChunkCount >= tunables.maxEmptyChunkCount() ||
             (freeChunkCount >= tunables.minEmptyChunkCount() &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
-            e.removeAndPopFront();
+            emptyChunks(lock).remove(chunk);
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
-            e.popFront();
         }
     }
     MOZ_ASSERT(emptyChunks(lock).count() <= tunables.maxEmptyChunkCount());
     MOZ_ASSERT_IF(shrinkBuffers, emptyChunks(lock).count() <= tunables.minEmptyChunkCount());
     return freeList;
 }
 
 static void
 FreeChunkPool(JSRuntime *rt, ChunkPool &pool)
 {
-    for (ChunkPool::Enum e(pool); !e.empty();) {
-        Chunk *chunk = e.front();
-        e.removeAndPopFront();
+    for (ChunkPool::Iter iter(pool); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+        pool.remove(chunk);
         MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
         FreeChunk(rt, chunk);
     }
     MOZ_ASSERT(pool.count() == 0);
 }
 
 void
 GCRuntime::freeEmptyChunks(JSRuntime *rt, const AutoLockGC &lock)
@@ -782,17 +812,17 @@ Chunk::allocate(JSRuntime *rt)
     if (!chunk)
         return nullptr;
     chunk->init(rt);
     rt->gc.stats.count(gcstats::STAT_NEW_CHUNK);
     return chunk;
 }
 
 /* Must be called with the GC lock taken. */
-inline void
+void
 GCRuntime::releaseChunk(Chunk *chunk)
 {
     MOZ_ASSERT(chunk);
     prepareToFreeChunk(chunk->info);
     FreeChunk(rt, chunk);
 }
 
 inline void
@@ -835,64 +865,25 @@ Chunk::init(JSRuntime *rt)
     /*
      * Decommit the arenas. We do this after poisoning so that if the OS does
      * not have to recycle the pages, we still get the benefit of poisoning.
      */
     decommitAllArenas(rt);
 
     /* Initialize the chunk info. */
     info.age = 0;
+    info.next = nullptr;
+    info.prev = nullptr;
     info.trailer.storeBuffer = nullptr;
     info.trailer.location = ChunkLocationBitTenuredHeap;
     info.trailer.runtime = rt;
 
     /* The rest of info fields are initialized in pickChunk. */
 }
 
-inline Chunk **
-GCRuntime::getAvailableChunkList()
-{
-    return &availableChunkListHead;
-}
-
-inline void
-Chunk::addToAvailableList(JSRuntime *rt)
-{
-    insertToAvailableList(rt->gc.getAvailableChunkList());
-}
-
-inline void
-Chunk::insertToAvailableList(Chunk **insertPoint)
-{
-    MOZ_ASSERT(hasAvailableArenas());
-    MOZ_ASSERT(!info.prevp);
-    MOZ_ASSERT(!info.next);
-    info.prevp = insertPoint;
-    Chunk *insertBefore = *insertPoint;
-    if (insertBefore) {
-        MOZ_ASSERT(insertBefore->info.prevp == insertPoint);
-        insertBefore->info.prevp = &info.next;
-    }
-    info.next = insertBefore;
-    *insertPoint = this;
-}
-
-inline void
-Chunk::removeFromAvailableList()
-{
-    MOZ_ASSERT(info.prevp);
-    *info.prevp = info.next;
-    if (info.next) {
-        MOZ_ASSERT(info.next->info.prevp == &info.next);
-        info.next->info.prevp = info.prevp;
-    }
-    info.prevp = nullptr;
-    info.next = nullptr;
-}
-
 /*
  * Search for and return the next decommitted Arena. Our goal is to keep
  * lastDecommittedArenaOffset "close" to a free arena. We do this by setting
  * it to the most recently freed arena when we free, and forcing it to
  * the last alloc + 1 when we allocate.
  */
 uint32_t
 Chunk::findDecommittedArenaOffset()
@@ -950,17 +941,17 @@ Chunk::fetchNextFreeArena(JSRuntime *rt)
 ArenaHeader *
 Chunk::allocateArena(JSRuntime *rt, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
 {
     ArenaHeader *aheader = info.numArenasFreeCommitted > 0
                            ? fetchNextFreeArena(rt)
                            : fetchNextDecommittedArena();
     aheader->init(zone, thingKind);
     if (MOZ_UNLIKELY(!hasAvailableArenas()))
-        removeFromAvailableList();
+        rt->gc.availableChunks(lock).remove(this);
     return aheader;
 }
 
 inline void
 GCRuntime::updateOnArenaFree(const ChunkInfo &info)
 {
     ++numArenasFreeCommitted;
 }
@@ -1001,36 +992,36 @@ Chunk::releaseArena(JSRuntime *rt, Arena
     if (state == IsCommitted) {
         aheader->setAsNotAllocated();
         addArenaToFreeList(rt, aheader);
     } else {
         addArenaToDecommittedList(rt, aheader);
     }
 
     if (info.numArenasFree == 1) {
-        MOZ_ASSERT(!info.prevp);
+        MOZ_ASSERT(!info.prev);
         MOZ_ASSERT(!info.next);
-        addToAvailableList(rt);
+        rt->gc.availableChunks(lock).push(this);
     } else if (!unused()) {
-        MOZ_ASSERT(info.prevp);
+        MOZ_ASSERT(rt->gc.availableChunks(lock).contains(this));
     } else {
         MOZ_ASSERT(unused());
-        removeFromAvailableList();
+        rt->gc.availableChunks(lock).remove(this);
         decommitAllArenas(rt);
         rt->gc.moveChunkToFreePool(this, lock);
     }
 }
 
 void
 GCRuntime::moveChunkToFreePool(Chunk *chunk, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
-    emptyChunks(lock).put(chunk);
+    emptyChunks(lock).push(chunk);
 }
 
 inline bool
 GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
 {
     // To minimize memory waste, we do not want to run the background chunk
     // allocation if we already have some empty chunks or when the runtime has
     // a small heap size (and therefore likely has a small growth rate).
@@ -1074,22 +1065,20 @@ class js::gc::AutoMaybeStartBackgroundAl
             runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
 Chunk *
 GCRuntime::pickChunk(const AutoLockGC &lock,
                      AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
-    Chunk **listHeadp = getAvailableChunkList();
-    Chunk *chunk = *listHeadp;
-    if (chunk)
-        return chunk;
-
-    chunk = emptyChunks(lock).get(rt);
+    if (availableChunks(lock).count())
+        return availableChunks(lock).head();
+
+    Chunk *chunk = emptyChunks(lock).pop();
     if (!chunk) {
         chunk = Chunk::allocate(rt);
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
@@ -1106,19 +1095,17 @@ GCRuntime::pickChunk(const AutoLockGC &l
      */
     GCChunkSet::AddPtr p = chunkSet.lookupForAdd(chunk);
     MOZ_ASSERT(!p);
     if (!chunkSet.add(p, chunk)) {
         releaseChunk(chunk);
         return nullptr;
     }
 
-    chunk->info.prevp = nullptr;
-    chunk->info.next = nullptr;
-    chunk->addToAvailableList(rt);
+    availableChunks(lock).push(chunk);
 
     return chunk;
 }
 
 ArenaHeader *
 GCRuntime::allocateArena(Chunk *chunk, Zone *zone, AllocKind thingKind, const AutoLockGC &lock)
 {
     MOZ_ASSERT(chunk->hasAvailableArenas());
@@ -1163,17 +1150,16 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     systemZone(nullptr),
 #ifdef JSGC_GENERATIONAL
     nursery(rt),
     storeBuffer(rt, nursery),
 #endif
     stats(rt),
     marker(rt),
     usage(nullptr),
-    availableChunkListHead(nullptr),
     maxMallocBytes(0),
     numArenasFreeCommitted(0),
     verifyPreData(nullptr),
     verifyPostData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(0),
     mode(JSGC_MODE_INCREMENTAL),
@@ -1407,17 +1393,23 @@ GCRuntime::finish()
             for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
                 js_delete(comp.get());
             js_delete(zone.get());
         }
     }
 
     zones.clear();
 
-    availableChunkListHead = nullptr;
+    for (ChunkPool::Iter iter(availableChunks_); !iter.done();) {
+        Chunk *chunk = iter.get();
+        iter.next();
+        MOZ_ASSERT(chunkSet.has(chunk));
+        availableChunks_.remove(chunk);
+    }
+
     if (chunkSet.initialized()) {
         for (GCChunkSet::Range r(chunkSet.all()); !r.empty(); r.popFront())
             releaseChunk(r.front());
         chunkSet.clear();
     }
 
     FreeChunkPool(rt, emptyChunks_);
 
@@ -3402,42 +3394,44 @@ GCRuntime::maybePeriodicFullGC()
 }
 
 // Do all possible decommit immediately from the current thread without
 // releasing the GC lock or allocating any memory.
 void
 GCRuntime::decommitAllWithoutUnlocking(const AutoLockGC &lock)
 {
     MOZ_ASSERT(emptyChunks(lock).count() == 0);
-    for (Chunk *chunk = *getAvailableChunkList(); chunk; chunk = chunk->info.next) {
+    for (ChunkPool::Iter chunk(availableChunks(lock)); !chunk.done(); chunk.next()) {
         for (size_t i = 0; i < ArenasPerChunk; ++i) {
             if (chunk->decommittedArenas.get(i) || chunk->arenas[i].aheader.allocated())
                 continue;
 
             if (MarkPagesUnused(&chunk->arenas[i], ArenaSize)) {
                 chunk->info.numArenasFreeCommitted--;
                 chunk->decommittedArenas.set(i);
             }
         }
     }
+    MOZ_ASSERT(availableChunks(lock).verify());
 }
 
 void
 GCRuntime::decommitArenas(const AutoLockGC &lock)
 {
     // Verify that all entries in the empty chunks pool are decommitted.
-    for (ChunkPool::Enum e(emptyChunks(lock)); !e.empty(); e.popFront())
-        MOZ_ASSERT(e.front()->info.numArenasFreeCommitted == 0);
+    for (ChunkPool::Iter chunk(emptyChunks(lock)); !chunk.done(); chunk.next())
+        MOZ_ASSERT(!chunk->info.numArenasFreeCommitted);
 
     // Build a Vector of all current available Chunks. Since we release the
     // gc lock while doing the decommit syscall, it is dangerous to iterate
     // the available list directly, as concurrent operations can modify it.
     mozilla::Vector<Chunk *> toDecommit;
-    for (Chunk *chunk = availableChunkListHead; chunk; chunk = chunk->info.next) {
-        if (!toDecommit.append(chunk)) {
+    MOZ_ASSERT(availableChunks(lock).verify());
+    for (ChunkPool::Iter iter(availableChunks(lock)); !iter.done(); iter.next()) {
+        if (!toDecommit.append(iter.get())) {
             // The OOM handler does a full, immediate decommit, so there is
             // nothing more to do here in any case.
             return onOutOfMallocMemory(lock);
         }
     }
 
     // Start at the tail and stop before the first chunk: we allocate from the
     // head and don't want to thrash with the mutator.
@@ -3457,16 +3451,17 @@ GCRuntime::decommitArenas(const AutoLock
             chunk->releaseArena(rt, aheader, lock, Chunk::ArenaDecommitState(ok));
 
             // FIXME Bug 1095620: add cancellation support when this becomes
             // a ParallelTask.
             if (/* cancel_ || */ !ok)
                 return;
         }
     }
+    MOZ_ASSERT(availableChunks(lock).verify());
 }
 
 void
 GCRuntime::expireChunksAndArenas(bool shouldShrink, const AutoLockGC &lock)
 {
 #ifdef JSGC_FJGENERATIONAL
     rt->threadPool.pruneChunkCache();
 #endif
@@ -3649,17 +3644,17 @@ BackgroundAllocTask::run()
     while (!cancel_ && runtime->gc.wantBackgroundAllocation(lock)) {
         Chunk *chunk;
         {
             AutoUnlockGC unlock(runtime);
             chunk = Chunk::allocate(runtime);
             if (!chunk)
                 break;
         }
-        chunkPool_.put(chunk);
+        chunkPool_.push(chunk);
     }
 }
 
 void
 GCHelperState::startBackgroundSweep()
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -364,22 +364,27 @@ class DebugScript
      * When non-zero, compile script in single-step mode. The top bit is set and
      * cleared by setStepMode, as used by JSD. The lower bits are a count,
      * adjusted by changeStepModeCount, used by the Debugger object. Only
      * when the bit is clear and the count is zero may we compile the script
      * without single-step support.
      */
     uint32_t        stepMode;
 
-    /* Number of breakpoint sites at opcodes in the script. */
+    /*
+     * Number of breakpoint sites at opcodes in the script. This is the number
+     * of populated entries in DebugScript::breakpoints, below.
+     */
     uint32_t        numSites;
 
     /*
-     * Array with all breakpoints installed at opcodes in the script, indexed
-     * by the offset of the opcode into the script.
+     * Breakpoints set in our script. For speed and simplicity, this array is
+     * parallel to script->code(): the BreakpointSite for the opcode at
+     * script->code()[offset] is debugScript->breakpoints[offset]. Naturally,
+     * this array's true length is script->length().
      */
     BreakpointSite  *breakpoints[1];
 };
 
 typedef HashMap<JSScript *,
                 DebugScript *,
                 DefaultHasher<JSScript *>,
                 SystemAllocPolicy> DebugScriptMap;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -236,16 +236,17 @@ class Debugger::FrameRange
             entry = dbg->frames.lookup(frame);
             if (entry)
                 break;
             nextDebugger++;
         }
     }
 };
 
+
 /*** Breakpoints *********************************************************************************/
 
 BreakpointSite::BreakpointSite(JSScript *script, jsbytecode *pc)
   : script(script), pc(pc), enabledCount(0)
 {
     MOZ_ASSERT(!script->hasBreakpointsAt(pc));
     JS_INIT_CLIST(&breakpoints);
 }
@@ -338,16 +339,17 @@ Breakpoint::nextInDebugger()
 
 Breakpoint *
 Breakpoint::nextInSite()
 {
     JSCList *link = JS_NEXT_LINK(&siteLinks);
     return (link == &site->breakpoints) ? nullptr : fromSiteLinks(link);
 }
 
+
 /*** Debugger hook dispatch **********************************************************************/
 
 Debugger::Debugger(JSContext *cx, NativeObject *dbg)
   : object(dbg),
     uncaughtExceptionHook(nullptr),
     enabled(true),
     trackingAllocationSites(false),
     allocationSamplingProbability(1.0),
@@ -2963,18 +2965,21 @@ Debugger::addDebuggeeGlobal(JSContext *c
             return false;
         }
 
         debuggeeCompartment->setObjectMetadataCallback(SavedStacksMetadataCallback);
         setMetadataCallback = true;
     }
 
     /*
-     * Each debugger-debuggee relation must be stored in up to three places.
-     * JSCompartment::addDebuggee enables debug mode if needed.
+     * For global to become this js::Debugger's debuggee:
+     * - global must be in this->debuggees,
+     * - this js::Debugger must be in global->getDebuggers(), and
+     * - JSCompartment::isDebuggee()'s bit must be set.
+     * All three indications must be kept consistent.
      */
     AutoCompartment ac(cx, global);
     GlobalObject::DebuggerVector *v = GlobalObject::getOrCreateDebuggers(cx, global);
     if (!v || !v->append(this)) {
         js_ReportOutOfMemory(cx);
     } else {
         if (!debuggees.put(global)) {
             js_ReportOutOfMemory(cx);
@@ -5809,16 +5814,17 @@ static const JSPropertySpec DebuggerFram
 };
 
 static const JSFunctionSpec DebuggerFrame_methods[] = {
     JS_FN("eval", DebuggerFrame_eval, 1, 0),
     JS_FN("evalWithBindings", DebuggerFrame_evalWithBindings, 1, 0),
     JS_FS_END
 };
 
+
 /*** Debugger.Object *****************************************************************************/
 
 static void
 DebuggerObject_trace(JSTracer *trc, JSObject *obj)
 {
     /*
      * There is a barrier on private pointers, so the Unbarriered marking
      * is okay.
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -873,18 +873,17 @@ RestyleManager::ProcessRestyledFrames(ns
     aChangeList.ChangeAt(index, &changeData);
     if (changeData->mFrame) {
       propTable->Delete(changeData->mFrame, ChangeListProperty());
     }
 
 #ifdef DEBUG
     // reget frame from content since it may have been regenerated...
     if (changeData->mContent) {
-      if (!nsAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent) &&
-          !nsTransitionManager::ContentOrAncestorHasTransition(changeData->mContent)) {
+      if (!css::CommonAnimationManager::ContentOrAncestorHasAnimation(changeData->mContent)) {
         nsIFrame* frame = changeData->mContent->GetPrimaryFrame();
         if (frame) {
           DebugVerifyStyleTree(frame);
         }
       }
     } else if (!changeData->mFrame ||
                changeData->mFrame->GetType() != nsGkAtoms::viewportFrame) {
       NS_WARNING("Unable to test style tree integrity -- no content node "
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2885,16 +2885,22 @@ nsDisplayLayerEventRegions::AddFrame(nsD
   } else {
     mHitRegion.Or(mHitRegion, borderBox);
   }
   if (aBuilder->GetAncestorHasTouchEventHandler()) {
     mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, borderBox);
   }
 }
 
+void
+nsDisplayLayerEventRegions::AddInactiveScrollPort(const nsRect& aRect)
+{
+  mDispatchToContentHitRegion.Or(mDispatchToContentHitRegion, aRect);
+}
+
 nsDisplayCaret::nsDisplayCaret(nsDisplayListBuilder* aBuilder,
                                nsIFrame* aCaretFrame)
   : nsDisplayItem(aBuilder, aCaretFrame)
   , mCaret(aBuilder->GetCaret())
   , mBounds(aBuilder->GetCaretRect() + ToReferenceFrame())
 {
   MOZ_COUNT_CTOR(nsDisplayCaret);
 }
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -2578,16 +2578,21 @@ public:
   }
 
   NS_DISPLAY_DECL_NAME("LayerEventRegions", TYPE_LAYER_EVENT_REGIONS)
 
   // Indicate that aFrame's border-box contributes to the event regions for
   // this layer. aFrame must have the same reference frame as mFrame.
   void AddFrame(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame);
 
+  // Indicate that an inactive scrollframe's scrollport should be added to the
+  // dispatch-to-content region, to ensure that APZ lets content create a
+  // displayport.
+  void AddInactiveScrollPort(const nsRect& aRect);
+
   const nsRegion& HitRegion() { return mHitRegion; }
   const nsRegion& MaybeHitRegion() { return mMaybeHitRegion; }
   const nsRegion& DispatchToContentHitRegion() { return mDispatchToContentHitRegion; }
 
 private:
   // Relative to aFrame's reference frame.
   // These are the points that are definitely in the hit region.
   nsRegion mHitRegion;
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2947,16 +2947,27 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       nsLayoutUtils::WantSubAPZC() &&
       WantAsyncScroll() &&
       // If we are the root scroll frame for the display root then we don't need a scroll
       // info layer to make a ComputeFrameMetrics call for us as
       // nsDisplayList::PaintForFrame already calls ComputeFrameMetrics for us.
       (!mIsRoot || aBuilder->RootReferenceFrame()->PresContext() != mOuter->PresContext());
   }
 
+  if (aBuilder->IsPaintingToWindow() &&
+      !mShouldBuildScrollableLayer &&
+      shouldBuildLayer)
+  {
+    if (nsDisplayLayerEventRegions *eventRegions = aBuilder->GetLayerEventRegions()) {
+      // Make sure that APZ will dispatch events back to content so we can
+      // create a displayport for this frame.
+      eventRegions->AddInactiveScrollPort(mScrollPort + aBuilder->ToReferenceFrame(mOuter));
+    }
+  }
+
   mScrollParentID = aBuilder->GetCurrentScrollParentId();
 
   nsDisplayListCollection scrolledContent;
   {
     // Note that setting the current scroll parent id here means that positioned children
     // of this scroll info layer will pick up the scroll info layer as their scroll handoff
     // parent. This is intentional because that is what happens for positioned children
     // of scroll layers, and we want to maintain consistent behaviour between scroll layers
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1442,16 +1442,49 @@ nsDisplayImage::GetLayerState(nsDisplayL
   mImage->GetImageContainer(aManager, getter_AddRefs(container));
   if (!container) {
     return LAYER_NONE;
   }
 
   return LAYER_ACTIVE;
 }
 
+
+/* virtual */ nsRegion
+nsDisplayImage::GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+                                bool* aSnap)
+{
+  *aSnap = true;
+  bool animated;
+  if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated &&
+      mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
+    // OK, the entire region painted by the image is opaque. But what is that
+    // region? It's the image's "dest rect" (the rect where a full copy of
+    // the image is mapped), clipped to the container's content box (which is
+    // what GetBounds() returns). So, we grab those rects and intersect them.
+    const nsRect frameContentBox = GetBounds(aSnap);
+
+    // Note: To get the "dest rect", we have to provide the "constraint rect"
+    // (which is the content-box, with the effects of fragmentation undone).
+    nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
+    nsRect constraintRect(frameContentBox.TopLeft(),
+                          imageFrame->mComputedSize);
+    constraintRect.y -= imageFrame->GetContinuationOffset();
+
+    const nsRect destRect =
+      nsLayoutUtils::ComputeObjectDestRect(constraintRect,
+                                           imageFrame->mIntrinsicSize,
+                                           imageFrame->mIntrinsicRatio,
+                                           imageFrame->StylePosition());
+
+    return nsRegion(destRect.Intersect(frameContentBox));
+  }
+  return nsRegion();
+}
+
 already_AddRefed<Layer>
 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder,
                            LayerManager* aManager,
                            const ContainerLayerParameters& aParameters)
 {
   nsRefPtr<ImageContainer> container;
   nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container));
   NS_ENSURE_SUCCESS(rv, nullptr);
--- a/layout/generic/nsImageFrame.h
+++ b/layout/generic/nsImageFrame.h
@@ -394,30 +394,24 @@ public:
   nsRect GetBounds(bool* aSnap)
   {
     *aSnap = true;
 
     nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame);
     return imageFrame->GetInnerArea() + ToReferenceFrame();
   }
 
-  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
+                           bool* aSnap) MOZ_OVERRIDE
   {
     return GetBounds(aSnap);
   }
 
-  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder, bool* aSnap)
-  {
-    *aSnap = true;
-    bool animated;
-    if (mImage && mImage->GetAnimated(&animated) == NS_OK && !animated && mImage->FrameIsOpaque(imgIContainer::FRAME_CURRENT)) {
-      return nsRegion(GetBounds(aSnap));
-    }
-    return nsRegion();
-  }
+  virtual nsRegion GetOpaqueRegion(nsDisplayListBuilder* aBuilder,
+                                   bool* aSnap) MOZ_OVERRIDE;
 
   virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
                                              LayerManager* aManager,
                                              const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE;
 
   /**
    * Configure an ImageLayer for this display item.
    * Set the required filter and scaling transform.
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-1-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        padding: 4px;
+        width: 32px;
+        height: 32px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is kept from filling the container via 'object-fit'. This
+     is an interesting case because, by default, images fill their container,
+     which means we can often optimize away the background completely. BUT, if
+     "object-fit" prevents the image from filling its container, we can't
+     optimize away the background; it need to be painted in the uncovered area.
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        object-fit: none;
+        width: 40px;
+        height: 40px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-2-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .fakeBackground {
+        background: salmon;
+        height: 3in;
+        width: 32px;
+      }
+
+      img.test {
+        width: 32px;
+        height: 32px;
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <div class="fakeBackground"></div>
+    <img class="test" src="blue-32x32.png">
+    <div class="fakeBackground"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-fit-with-background-2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is kept from filling the container via 'object-fit' (and
+     the img element is fragmented). This is an interesting case because, by
+     default, images fill their container, which means we can often optimize
+     away the background completely. BUT, if "object-fit" prevents the image
+     from filling its container, we can't optimize away the background; it need
+     to be painted in the uncovered area.
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        object-fit: none;
+        width: 32px;
+        /* We make the height 6in larger than the image's intrinsic height,
+         * which gives us the following happy results:
+         *  (1) the <img> will split over several 3in tall reftest-print cards
+         *      (so, we get to test fragmentation).
+         *  (2) the image pixels end up on the second fragment (not the first),
+         *      so we get to test image-data painting on later fragments.
+         *  (3) the reference case can easily match us using a simple img
+         *      with 3in-tall divs before & after it.
+         */
+        height: calc(32px + 6in);
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-1-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        padding-top: 5px;
+        padding-left: 5px;
+        width: 27px;
+        height: 27px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is offset from the container via 'object-position'. This is
+     an interesting case because, by default, images fill their container,
+     which means we can often optimize away the background completely. BUT, if
+     "object-position" offsets the image from its container's content-box, we
+     can't optimize away the background; it need to be painted in the uncovered
+     area.
+-->
+<html>
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      .test {
+        background: salmon;
+        object-position: 5px 5px;
+        width: 32px;
+        height: 32px;
+        display: block;
+        margin-bottom: 2px;
+      }
+    </style>
+  </head>
+  <body>
+    <img    class="test"    src="blue-32x32.png">
+    <embed  class="test"    src="blue-32x32.png">
+    <object class="test"   data="blue-32x32.png"></object>
+    <video  class="test" poster="blue-32x32.png"></video>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-2-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        padding-left: 10px;
+        padding-top: 20px;
+        width: 22px;
+        height: calc(5in - 20px);
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/image/image-object-position-with-background-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!--
+     This testcase ensures that we paint the background around an opaque image,
+     when the image is offset from the container via 'object-position' (and
+     the img element is fragmented). This is an interesting case because, by
+     default, images fill their container, which means we can often optimize
+     away the background completely. BUT, if "object-position" offsets the
+     image from its container's content-box, we can't optimize away the
+     background; it need to be painted in the uncovered area.
+-->
+<html class="reftest-print">
+  <head>
+    <meta charset="utf-8">
+    <style type="text/css">
+      img.test {
+        background: salmon;
+        object-position: 10px 20px;
+        width: 32px;
+        height: 5in;
+        display: block; /* Required for fragmentation */
+      }
+    </style>
+  </head>
+  <body>
+    <img class="test" src="blue-32x32.png">
+  </body>
+</html>
--- a/layout/reftests/image/reftest.list
+++ b/layout/reftests/image/reftest.list
@@ -6,16 +6,22 @@ fuzzy-if(Android,8,30) == background-ima
 skip-if(B2G&&browserIsRemote) == image-zoom-1.html image-zoom-1-ref.html
 skip-if(B2G&&browserIsRemote) == image-zoom-2.html image-zoom-1-ref.html
 == invalid-url-image-1.html invalid-url-image-1-ref.html
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1a.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1b.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1c.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == sync-image-switch-1d.html sync-image-switch-1-ref.html # bug 855050 for WinXP
 
+# Tests for "object-fit" & "object-position"
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-1.html image-object-fit-with-background-1-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-fit-with-background-2.html image-object-fit-with-background-2-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-1.html image-object-position-with-background-1-ref.html
+test-pref(layout.css.object-fit-and-position.enabled,true) == image-object-position-with-background-2.html image-object-position-with-background-2-ref.html
+
 # Tests for image-orientation used with 'from-image' (note that all
 # image-orientation tests are fuzzy because the JPEG images do not perfectly
 # reproduce blocks of solid color, even at maximum quality):
 fuzzy(2,5) == image-orientation-from-image.html?none     image-orientation-ref.html?0
 fuzzy(1,1) == image-orientation-from-image.html?0        image-orientation-ref.html?0
 fuzzy(1,1) == image-orientation-from-image.html?90       image-orientation-ref.html?90
 fuzzy(1,1) == image-orientation-from-image.html?180      image-orientation-ref.html?180
 fuzzy(1,1) == image-orientation-from-image.html?270      image-orientation-ref.html?270
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -17,16 +17,17 @@
 #include "nsIFrame.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/LookAndFeel.h"
 #include "Layers.h"
 #include "FrameLayerBuilder.h"
 #include "nsDisplayList.h"
 #include "mozilla/MemoryReporting.h"
 #include "RestyleManager.h"
+#include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 
 
 using mozilla::layers::Layer;
 using mozilla::dom::AnimationPlayer;
 using mozilla::dom::Animation;
 
@@ -181,16 +182,60 @@ CommonAnimationManager::HasAttributeDepe
 }
 
 /* virtual */ bool
 CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext)
 {
   return false;
 }
 
+/* virtual */ void
+CommonAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
+{
+  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
+                    "pres context mismatch");
+  nsIStyleRule *rule =
+    GetAnimationRule(aData->mElement,
+                     nsCSSPseudoElements::ePseudo_NotPseudoElement);
+  if (rule) {
+    aData->mRuleWalker->Forward(rule);
+  }
+}
+
+/* virtual */ void
+CommonAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
+{
+  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
+                    "pres context mismatch");
+  if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
+      aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
+    return;
+  }
+
+  // FIXME: Do we really want to be the only thing keeping a
+  // pseudo-element alive?  I *think* the non-animation restyle should
+  // handle that, but should add a test.
+  nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
+  if (rule) {
+    aData->mRuleWalker->Forward(rule);
+  }
+}
+
+/* virtual */ void
+CommonAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
+{
+}
+
+#ifdef MOZ_XUL
+/* virtual */ void
+CommonAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
+{
+}
+#endif
+
 /* virtual */ size_t
 CommonAnimationManager::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mElementCollections
   //
   // The following members are not measured
@@ -252,16 +297,116 @@ CommonAnimationManager::ExtractComputedV
                         StyleAnimationValue::eUnit_Enumerated,
                       "unexpected unit");
     aComputedValue.SetIntValue(aComputedValue.GetIntValue(),
                                StyleAnimationValue::eUnit_Visibility);
   }
   return result;
 }
 
+AnimationPlayerCollection*
+CommonAnimationManager::GetAnimationPlayers(dom::Element *aElement,
+                                            nsCSSPseudoElements::Type aPseudoType,
+                                            bool aCreateIfNeeded)
+{
+  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
+    // Early return for the most common case.
+    return nullptr;
+  }
+
+  nsIAtom *propName;
+  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
+    propName = GetAnimationsAtom();
+  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
+    propName = GetAnimationsBeforeAtom();
+  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
+    propName = GetAnimationsAfterAtom();
+  } else {
+    NS_ASSERTION(!aCreateIfNeeded,
+                 "should never try to create transitions for pseudo "
+                 "other than :before or :after");
+    return nullptr;
+  }
+  AnimationPlayerCollection* collection =
+    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
+  if (!collection && aCreateIfNeeded) {
+    // FIXME: Consider arena-allocating?
+    collection =
+      new AnimationPlayerCollection(aElement, propName, this);
+    nsresult rv =
+      aElement->SetProperty(propName, collection,
+                            &AnimationPlayerCollection::PropertyDtor, false);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("SetProperty failed");
+      delete collection;
+      return nullptr;
+    }
+    if (propName == nsGkAtoms::animationsProperty ||
+        propName == nsGkAtoms::transitionsProperty) {
+      aElement->SetMayHaveAnimations();
+    }
+
+    AddElementCollection(collection);
+  }
+
+  return collection;
+}
+
+nsIStyleRule*
+CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
+                                         nsCSSPseudoElements::Type aPseudoType)
+{
+  NS_ABORT_IF_FALSE(
+    aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
+    aPseudoType == nsCSSPseudoElements::ePseudo_before ||
+    aPseudoType == nsCSSPseudoElements::ePseudo_after,
+    "forbidden pseudo type");
+
+  if (!mPresContext->IsDynamic()) {
+    // For print or print preview, ignore animations.
+    return nullptr;
+  }
+
+  AnimationPlayerCollection* collection =
+    GetAnimationPlayers(aElement, aPseudoType, false);
+  if (!collection) {
+    return nullptr;
+  }
+
+  RestyleManager* restyleManager = mPresContext->RestyleManager();
+  if (restyleManager->SkipAnimationRules()) {
+    // During the non-animation part of processing restyles, we don't
+    // add the animation rule.
+
+    if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
+      collection->PostRestyleForAnimation(mPresContext);
+    }
+
+    return nullptr;
+  }
+
+  // Animations should already be refreshed, but transitions may not be.
+  // Note that this is temporary, we would like both animations and transitions
+  // to both be refreshed by this point.
+  if (IsAnimationManager()) {
+    NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
+                     collection->mStyleRuleRefreshTime ==
+                       mPresContext->RefreshDriver()->MostRecentRefresh(),
+                     "should already have refreshed style rule");
+  } else {
+    // FIXME: Remove this assignment.  See bug 1061364.
+    collection->mNeedsRefreshes = true;
+    collection->EnsureStyleRuleFor(
+      mPresContext->RefreshDriver()->MostRecentRefresh(),
+      EnsureStyleRule_IsNotThrottled);
+  }
+
+  return collection->mStyleRule;
+}
+
 /* static */ const CommonAnimationManager::LayerAnimationRecord
   CommonAnimationManager::sLayerAnimationInfo[] =
     { { eCSSProperty_transform,
         nsDisplayItem::TYPE_TRANSFORM,
         nsChangeHint_UpdateTransformLayer },
       { eCSSProperty_opacity,
         nsDisplayItem::TYPE_OPACITY,
         nsChangeHint_UpdateOpacityLayer } };
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -48,45 +48,67 @@ public:
 
   // nsIStyleRuleProcessor (parts)
   virtual nsRestyleHint HasStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual nsRestyleHint HasStateDependentStyle(PseudoElementStateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual bool HasDocumentStateDependentStyle(StateRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual nsRestyleHint
     HasAttributeDependentStyle(AttributeRuleProcessorData* aData) MOZ_OVERRIDE;
   virtual bool MediumFeaturesChanged(nsPresContext* aPresContext) MOZ_OVERRIDE;
+  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
+  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
+  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
+#ifdef MOZ_XUL
+  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
+#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   /**
    * Notify the manager that the pres context is going away.
    */
   void Disconnect();
 
   // Tell the restyle tracker about all the styles that we're currently
   // animating, so that it can update the animation rule for these
   // elements.
   void AddStyleUpdatesTo(mozilla::RestyleTracker& aTracker);
 
-  virtual AnimationPlayerCollection*
+  AnimationPlayerCollection*
   GetAnimationPlayers(dom::Element *aElement,
                       nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) = 0;
+                      bool aCreateIfNeeded);
+
+  // Returns true if aContent or any of its ancestors has an animation
+  // or transition.
+  static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
+    do {
+      if (aContent->GetProperty(nsGkAtoms::animationsProperty) ||
+          aContent->GetProperty(nsGkAtoms::transitionsProperty)) {
+        return true;
+      }
+    } while ((aContent = aContent->GetParent()));
+
+    return false;
+  }
 
   // Notify this manager that one of its collections of animation players,
   // has been updated.
   void NotifyCollectionUpdated(AnimationPlayerCollection& aCollection);
 
   enum FlushFlags {
     Can_Throttle,
     Cannot_Throttle
   };
 
+  nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
+                                 nsCSSPseudoElements::Type aPseudoType);
+
   static bool ExtractComputedValueForTransition(
                   nsCSSProperty aProperty,
                   nsStyleContext* aStyleContext,
                   mozilla::StyleAnimationValue& aComputedValue);
 
   // For CSS properties that may be animated on a separate layer, represents
   // a record of the corresponding layer type and change hint.
   struct LayerAnimationRecord {
@@ -109,16 +131,24 @@ protected:
 
   void AddElementCollection(AnimationPlayerCollection* aCollection);
   void ElementCollectionRemoved() { CheckNeedsRefresh(); }
   void RemoveAllElementCollections();
 
   // Check to see if we should stop or start observing the refresh driver
   void CheckNeedsRefresh();
 
+  virtual nsIAtom* GetAnimationsAtom() = 0;
+  virtual nsIAtom* GetAnimationsBeforeAtom() = 0;
+  virtual nsIAtom* GetAnimationsAfterAtom() = 0;
+
+  virtual bool IsAnimationManager() {
+    return false;
+  }
+
   // When this returns a value other than nullptr, it also,
   // as a side-effect, notifies the ActiveLayerTracker.
   static AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent,
                              nsIAtom* aElementProperty,
                              nsCSSProperty aProperty);
 
   PRCList mElementCollections;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -6,17 +6,16 @@
 #include "nsAnimationManager.h"
 #include "nsTransitionManager.h"
 
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 
 #include "nsPresContext.h"
-#include "nsRuleProcessorData.h"
 #include "nsStyleSet.h"
 #include "nsStyleChangeList.h"
 #include "nsCSSRules.h"
 #include "RestyleManager.h"
 #include "nsLayoutUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include <math.h>
@@ -193,107 +192,16 @@ nsAnimationManager::QueueEvents(Animatio
   for (size_t playerIdx = aCollection->mPlayers.Length(); playerIdx-- != 0; ) {
     CSSAnimationPlayer* player =
       aCollection->mPlayers[playerIdx]->AsCSSAnimationPlayer();
     MOZ_ASSERT(player, "Expected a collection of CSS Animation players");
     player->QueueEvents(aEventsToDispatch);
   }
 }
 
-AnimationPlayerCollection*
-nsAnimationManager::GetAnimationPlayers(dom::Element *aElement,
-                                        nsCSSPseudoElements::Type aPseudoType,
-                                        bool aCreateIfNeeded)
-{
-  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
-    // Early return for the most common case.
-    return nullptr;
-  }
-
-  nsIAtom *propName;
-  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
-    propName = nsGkAtoms::animationsProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
-    propName = nsGkAtoms::animationsOfBeforeProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-    propName = nsGkAtoms::animationsOfAfterProperty;
-  } else {
-    NS_ASSERTION(!aCreateIfNeeded,
-                 "should never try to create transitions for pseudo "
-                 "other than :before or :after");
-    return nullptr;
-  }
-  AnimationPlayerCollection* collection =
-    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
-  if (!collection && aCreateIfNeeded) {
-    // FIXME: Consider arena-allocating?
-    collection =
-      new AnimationPlayerCollection(aElement, propName, this);
-    nsresult rv =
-      aElement->SetProperty(propName, collection,
-                            &AnimationPlayerCollection::PropertyDtor, false);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("SetProperty failed");
-      delete collection;
-      return nullptr;
-    }
-    if (propName == nsGkAtoms::animationsProperty) {
-      aElement->SetMayHaveAnimations();
-    }
-
-    AddElementCollection(collection);
-  }
-
-  return collection;
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(ElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  nsIStyleRule *rule =
-    GetAnimationRule(aData->mElement,
-                     nsCSSPseudoElements::ePseudo_NotPseudoElement);
-  if (rule) {
-    aData->mRuleWalker->Forward(rule);
-  }
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(PseudoElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  if (aData->mPseudoType != nsCSSPseudoElements::ePseudo_before &&
-      aData->mPseudoType != nsCSSPseudoElements::ePseudo_after) {
-    return;
-  }
-
-  // FIXME: Do we really want to be the only thing keeping a
-  // pseudo-element alive?  I *think* the non-animation restyle should
-  // handle that, but should add a test.
-  nsIStyleRule *rule = GetAnimationRule(aData->mElement, aData->mPseudoType);
-  if (rule) {
-    aData->mRuleWalker->Forward(rule);
-  }
-}
-
-/* virtual */ void
-nsAnimationManager::RulesMatching(AnonBoxRuleProcessorData* aData)
-{
-}
-
-#ifdef MOZ_XUL
-/* virtual */ void
-nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData)
-{
-}
-#endif
-
 /* virtual */ size_t
 nsAnimationManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 
   // Measurement of the following members may be added later if DMD finds it is
   // worthwhile:
   // - mPendingEvents
@@ -724,57 +632,16 @@ nsAnimationManager::BuildSegment(Infalli
   } else {
     tf = &aAnimation.GetTimingFunction();
   }
   segment.mTimingFunction.Init(*tf);
 
   return true;
 }
 
-nsIStyleRule*
-nsAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
-                                     nsCSSPseudoElements::Type aPseudoType)
-{
-  NS_ABORT_IF_FALSE(
-    aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
-    aPseudoType == nsCSSPseudoElements::ePseudo_before ||
-    aPseudoType == nsCSSPseudoElements::ePseudo_after,
-    "forbidden pseudo type");
-
-  if (!mPresContext->IsDynamic()) {
-    // For print or print preview, ignore animations.
-    return nullptr;
-  }
-
-  AnimationPlayerCollection* collection =
-    GetAnimationPlayers(aElement, aPseudoType, false);
-  if (!collection) {
-    return nullptr;
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->SkipAnimationRules()) {
-    // During the non-animation part of processing restyles, we don't
-    // add the animation rule.
-
-    if (collection->mStyleRule && restyleManager->PostAnimationRestyles()) {
-      collection->PostRestyleForAnimation(mPresContext);
-    }
-
-    return nullptr;
-  }
-
-  NS_WARN_IF_FALSE(!collection->mNeedsRefreshes ||
-                   collection->mStyleRuleRefreshTime ==
-                     mPresContext->RefreshDriver()->MostRecentRefresh(),
-                   "should already have refreshed style rule");
-
-  return collection->mStyleRule;
-}
-
 /* virtual */ void
 nsAnimationManager::WillRefresh(mozilla::TimeStamp aTime)
 {
   NS_ABORT_IF_FALSE(mPresContext,
                     "refresh driver should not notify additional observers "
                     "after pres context has been destroyed");
   if (!mPresContext->GetPresShell()) {
     // Someone might be keeping mPresContext alive past the point
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -156,40 +156,23 @@ public:
 
   static mozilla::AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent, nsCSSProperty aProperty)
   {
     return mozilla::css::CommonAnimationManager::GetAnimationsForCompositor(
       aContent, nsGkAtoms::animationsProperty, aProperty);
   }
 
-  // Returns true if aContent or any of its ancestors has an animation.
-  static bool ContentOrAncestorHasAnimation(nsIContent* aContent) {
-    do {
-      if (aContent->GetProperty(nsGkAtoms::animationsProperty)) {
-        return true;
-      }
-    } while ((aContent = aContent->GetParent()));
-
-    return false;
-  }
-
   void UpdateStyleAndEvents(mozilla::AnimationPlayerCollection* aEA,
                             mozilla::TimeStamp aRefreshTime,
                             mozilla::EnsureStyleRuleFlags aFlags);
   void QueueEvents(mozilla::AnimationPlayerCollection* aEA,
                    mozilla::EventArray &aEventsToDispatch);
 
   // nsIStyleRuleProcessor (parts)
-  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
-#ifdef MOZ_XUL
-  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
-#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf)
     const MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE;
 
@@ -218,22 +201,29 @@ public:
    */
   void DispatchEvents() {
     // Fast-path the common case: no events
     if (!mPendingEvents.IsEmpty()) {
       DoDispatchEvents();
     }
   }
 
-  virtual mozilla::AnimationPlayerCollection*
-  GetAnimationPlayers(mozilla::dom::Element *aElement,
-                      nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) MOZ_OVERRIDE;
-  nsIStyleRule* GetAnimationRule(mozilla::dom::Element* aElement,
-                                 nsCSSPseudoElements::Type aPseudoType);
+protected:
+  virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsProperty;
+  }
+  virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsOfBeforeProperty;
+  }
+  virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::animationsOfAfterProperty;
+  }
+  virtual bool IsAnimationManager() MOZ_OVERRIDE {
+    return true;
+  }
 
 private:
   void BuildAnimations(nsStyleContext* aStyleContext,
                        mozilla::dom::Element* aTarget,
                        mozilla::dom::AnimationTimeline* aTimeline,
                        mozilla::AnimationPlayerPtrArray& aAnimations);
   bool BuildSegment(InfallibleTArray<mozilla::AnimationPropertySegment>&
                       aSegments,
--- a/layout/style/nsStyleSet.cpp
+++ b/layout/style/nsStyleSet.cpp
@@ -1445,18 +1445,21 @@ nsStyleSet::RuleNodeWithReplacement(Elem
             }
           }
           break;
         }
         case eRestyle_CSSTransitions: {
           if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement ||
               aPseudoType == nsCSSPseudoElements::ePseudo_before ||
               aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-            PresContext()->TransitionManager()->
-              WalkTransitionRule(aElement, aPseudoType, &ruleWalker);
+            nsIStyleRule* rule = PresContext()->TransitionManager()->
+              GetAnimationRule(aElement, aPseudoType);
+            if (rule) {
+              ruleWalker.ForwardOnPossiblyCSSRule(rule);
+            }
           }
           break;
         }
         case eRestyle_SVGAttrAnimations: {
           MOZ_ASSERT(aReplacements & (eRestyle_ChangeAnimationPhase |
                                       eRestyle_ChangeAnimationPhaseDescendants),
                      "don't know how to do this level without phase change");
 
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -431,17 +431,17 @@ nsTransitionManager::ConsiderStartingTra
   // transition that the current value rounds to the final value.  In
   // this case, we'll end up with shouldAnimate as false (because
   // there's no value change), but we need to return early here rather
   // than cancel the running transition because shouldAnimate is false!
   MOZ_ASSERT(!oldPT || oldPT->Properties()[0].mSegments.Length() == 1,
              "Should have one animation property segment for a transition");
   if (haveCurrentTransition && haveValues &&
       oldPT->Properties()[0].mSegments[0].mToValue == endValue) {
-    // WalkTransitionRule already called RestyleForAnimation.
+    // GetAnimationRule already called RestyleForAnimation.
     return;
   }
 
   nsPresContext *presContext = aNewStyleContext->PresContext();
 
   if (!shouldAnimate) {
     if (haveCurrentTransition) {
       // We're in the middle of a transition, and just got a non-transition
@@ -455,17 +455,17 @@ nsTransitionManager::ConsiderStartingTra
       players.RemoveElementAt(currentIndex);
       aElementTransitions->UpdateAnimationGeneration(mPresContext);
 
       if (players.IsEmpty()) {
         aElementTransitions->Destroy();
         // |aElementTransitions| is now a dangling pointer!
         aElementTransitions = nullptr;
       }
-      // WalkTransitionRule already called RestyleForAnimation.
+      // GetAnimationRule already called RestyleForAnimation.
     }
     return;
   }
 
   const nsTimingFunction &tf = aTransition.GetTimingFunction();
   float delay = aTransition.GetDelay();
   float duration = aTransition.GetDuration();
   if (duration < 0.0) {
@@ -574,143 +574,20 @@ nsTransitionManager::ConsiderStartingTra
   }
   aElementTransitions->UpdateAnimationGeneration(mPresContext);
   aElementTransitions->PostRestyleForAnimation(presContext);
 
   *aStartedAny = true;
   aWhichStarted->AddProperty(aProperty);
 }
 
-AnimationPlayerCollection*
-nsTransitionManager::GetAnimationPlayers(
-  dom::Element *aElement,
-  nsCSSPseudoElements::Type aPseudoType,
-  bool aCreateIfNeeded)
-{
-  if (!aCreateIfNeeded && PR_CLIST_IS_EMPTY(&mElementCollections)) {
-    // Early return for the most common case.
-    return nullptr;
-  }
-
-  nsIAtom *propName;
-  if (aPseudoType == nsCSSPseudoElements::ePseudo_NotPseudoElement) {
-    propName = nsGkAtoms::transitionsProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_before) {
-    propName = nsGkAtoms::transitionsOfBeforeProperty;
-  } else if (aPseudoType == nsCSSPseudoElements::ePseudo_after) {
-    propName = nsGkAtoms::transitionsOfAfterProperty;
-  } else {
-    NS_ASSERTION(!aCreateIfNeeded,
-                 "should never try to create transitions for pseudo "
-                 "other than :before or :after");
-    return nullptr;
-  }
-  AnimationPlayerCollection* collection =
-    static_cast<AnimationPlayerCollection*>(aElement->GetProperty(propName));
-  if (!collection && aCreateIfNeeded) {
-    // FIXME: Consider arena-allocating?
-    collection = new AnimationPlayerCollection(aElement, propName, this);
-    nsresult rv =
-      aElement->SetProperty(propName, collection,
-                            &AnimationPlayerCollection::PropertyDtor, false);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("SetProperty failed");
-      delete collection;
-      return nullptr;
-    }
-    if (propName == nsGkAtoms::transitionsProperty) {
-      aElement->SetMayHaveAnimations();
-    }
-
-    AddElementCollection(collection);
-  }
-
-  return collection;
-}
-
 /*
  * nsIStyleRuleProcessor implementation
  */
 
-void
-nsTransitionManager::WalkTransitionRule(dom::Element* aElement,
-                                        nsCSSPseudoElements::Type aPseudoType,
-                                        nsRuleWalker* aRuleWalker)
-{
-  AnimationPlayerCollection* collection =
-    GetAnimationPlayers(aElement, aPseudoType, false);
-  if (!collection) {
-    return;
-  }
-
-  if (!mPresContext->IsDynamic()) {
-    // For print or print preview, ignore animations.
-    return;
-  }
-
-  RestyleManager* restyleManager = mPresContext->RestyleManager();
-  if (restyleManager->SkipAnimationRules()) {
-    // If we're processing a normal style change rather than one from
-    // animation, don't add the transition rule.  This allows us to
-    // compute the new style value rather than having the transition
-    // override it, so that we can start transitioning differently.
-
-    if (restyleManager->PostAnimationRestyles()) {
-      // We need to immediately restyle with animation
-      // after doing this.
-      collection->PostRestyleForAnimation(mPresContext);
-    }
-    return;
-  }
-
-  collection->mNeedsRefreshes = true;
-  collection->EnsureStyleRuleFor(
-    mPresContext->RefreshDriver()->MostRecentRefresh(),
-    EnsureStyleRule_IsNotThrottled);
-
-  if (collection->mStyleRule) {
-    aRuleWalker->Forward(collection->mStyleRule);
-  }
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(ElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-  WalkTransitionRule(aData->mElement,
-                     nsCSSPseudoElements::ePseudo_NotPseudoElement,
-                     aData->mRuleWalker);
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(PseudoElementRuleProcessorData* aData)
-{
-  NS_ABORT_IF_FALSE(aData->mPresContext == mPresContext,
-                    "pres context mismatch");
-
-  // Note:  If we're the only thing keeping a pseudo-element frame alive
-  // (per ProbePseudoStyleContext), we still want to keep it alive, so
-  // this is ok.
-  WalkTransitionRule(aData->mElement, aData->mPseudoType,
-                     aData->mRuleWalker);
-}
-
-/* virtual */ void
-nsTransitionManager::RulesMatching(AnonBoxRuleProcessorData* aData)
-{
-}
-
-#ifdef MOZ_XUL
-/* virtual */ void
-nsTransitionManager::RulesMatching(XULTreeRuleProcessorData* aData)
-{
-}
-#endif
-
 /* virtual */ size_t
 nsTransitionManager::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf);
 }
 
 /* virtual */ size_t
 nsTransitionManager::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -95,33 +95,16 @@ public:
     : mozilla::css::CommonAnimationManager(aPresContext)
     , mInAnimationOnlyStyleUpdate(false)
   {
   }
 
   typedef mozilla::AnimationPlayerCollection AnimationPlayerCollection;
 
   static AnimationPlayerCollection*
-  GetTransitions(nsIContent* aContent) {
-    return static_cast<AnimationPlayerCollection*>
-      (aContent->GetProperty(nsGkAtoms::transitionsProperty));
-  }
-
-  // Returns true if aContent or any of its ancestors has a transition.
-  static bool ContentOrAncestorHasTransition(nsIContent* aContent) {
-    do {
-      if (GetTransitions(aContent)) {
-        return true;
-      }
-    } while ((aContent = aContent->GetParent()));
-
-    return false;
-  }
-
-  static AnimationPlayerCollection*
   GetAnimationsForCompositor(nsIContent* aContent, nsCSSProperty aProperty)
   {
     return mozilla::css::CommonAnimationManager::GetAnimationsForCompositor(
       aContent, nsGkAtoms::transitionsProperty, aProperty);
   }
 
   /**
    * StyleContextChanged
@@ -143,40 +126,36 @@ public:
     StyleContextChanged(mozilla::dom::Element *aElement,
                         nsStyleContext *aOldStyleContext,
                         nsStyleContext *aNewStyleContext);
 
   void SetInAnimationOnlyStyleUpdate(bool aInAnimationOnlyUpdate) {
     mInAnimationOnlyStyleUpdate = aInAnimationOnlyUpdate;
   }
 
-  // nsIStyleRuleProcessor (parts)
-  virtual void RulesMatching(ElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(PseudoElementRuleProcessorData* aData) MOZ_OVERRIDE;
-  virtual void RulesMatching(AnonBoxRuleProcessorData* aData) MOZ_OVERRIDE;
-#ifdef MOZ_XUL
-  virtual void RulesMatching(XULTreeRuleProcessorData* aData) MOZ_OVERRIDE;
-#endif
   virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
   virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     MOZ_MUST_OVERRIDE MOZ_OVERRIDE;
 
   // nsARefreshObserver
   virtual void WillRefresh(mozilla::TimeStamp aTime) MOZ_OVERRIDE;
 
   void FlushTransitions(FlushFlags aFlags);
 
-  virtual AnimationPlayerCollection*
-  GetAnimationPlayers(mozilla::dom::Element *aElement,
-                      nsCSSPseudoElements::Type aPseudoType,
-                      bool aCreateIfNeeded) MOZ_OVERRIDE;
-  void WalkTransitionRule(mozilla::dom::Element* aElement,
-                          nsCSSPseudoElements::Type aPseudoType,
-                          nsRuleWalker* aRuleWalker);
+protected:
+  virtual nsIAtom* GetAnimationsAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsProperty;
+  }
+  virtual nsIAtom* GetAnimationsBeforeAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsOfBeforeProperty;
+  }
+  virtual nsIAtom* GetAnimationsAfterAtom() MOZ_OVERRIDE {
+    return nsGkAtoms::transitionsOfAfterProperty;
+  }
 
 private:
   void
   ConsiderStartingTransition(nsCSSProperty aProperty,
                              const mozilla::StyleTransition& aTransition,
                              mozilla::dom::Element* aElement,
                              AnimationPlayerCollection*& aElementTransitions,
                              nsStyleContext* aOldStyleContext,
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -459,16 +459,24 @@ status_t MPEG4Extractor::readMetaData() 
     if (mInitCheck != NO_INIT) {
         return mInitCheck;
     }
 
     off64_t offset = 0;
     status_t err;
     while (!mFirstTrack) {
         err = parseChunk(&offset, 0);
+        // The parseChunk function returns UNKNOWN_ERROR to skip
+        // some boxes we don't want to handle. Filter that error
+        // code but return others so e.g. I/O errors propagate.
+        if (err != OK && err != (status_t) UNKNOWN_ERROR) {
+          ALOGW("Error %d parsing chuck at offset %lld looking for first track",
+              err, (long long)offset);
+          break;
+        }
     }
 
     if (mInitCheck == OK) {
         if (mHasVideo) {
             mFileMetaData->setCString(
                     kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG4);
         } else {
             mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
--- a/media/mtransport/test/buffered_stun_socket_unittest.cpp
+++ b/media/mtransport/test/buffered_stun_socket_unittest.cpp
@@ -37,28 +37,16 @@ MtransportTestUtils *test_utils;
 static uint8_t kStunMessage[] = {
   0x00, 0x01, 0x00, 0x08, 0x21, 0x12, 0xa4, 0x42,
   0x9b, 0x90, 0xbe, 0x2c, 0xae, 0x1a, 0x0c, 0xa8,
   0xa0, 0xd6, 0x8b, 0x08, 0x80, 0x28, 0x00, 0x04,
   0xdb, 0x35, 0x5f, 0xaa
 };
 static size_t kStunMessageLen = sizeof(kStunMessage);
 
-class DummySocket;
-
-// Temporary whitelist for refcounted class dangerously exposing its destructor.
-// Filed bug 1028140 to address this class.
-namespace mozilla {
-template<>
-struct HasDangerousPublicDestructor<DummySocket>
-{
-  static const bool value = true;
-};
-}
-
 class DummySocket : public NrSocketBase {
  public:
   DummySocket()
       : writable_(UINT_MAX),
         write_buffer_(nullptr),
         readable_(UINT_MAX),
         read_buffer_(nullptr),
         cb_(nullptr),
@@ -202,16 +190,18 @@ class DummySocket : public NrSocketBase 
     }
 
     return self_;
   }
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DummySocket);
 
  private:
+  ~DummySocket() {}
+
   DISALLOW_COPY_ASSIGN(DummySocket);
 
   size_t writable_;  // Amount we allow someone to write.
   ScopedDeletePtr<DataBuffer> write_buffer_;
   size_t readable_;   // Amount we allow someone to read.
   ScopedDeletePtr<DataBuffer> read_buffer_;
 
   NR_async_cb cb_;
@@ -225,17 +215,17 @@ class BufferedStunSocketTest : public ::
       : dummy_(nullptr),
         test_socket_(nullptr) {}
 
   ~BufferedStunSocketTest() {
     nr_socket_destroy(&test_socket_);
   }
 
   void SetUp() {
-    ScopedDeletePtr<DummySocket> dummy(new DummySocket());
+    nsRefPtr<DummySocket> dummy(new DummySocket());
 
     int r = nr_socket_buffered_stun_create(
         dummy->get_nr_socket(),
         kStunMessageLen,
         &test_socket_);
     ASSERT_EQ(0, r);
     dummy_ = dummy.forget();  // Now owned by test_socket_.
 
@@ -246,17 +236,17 @@ class BufferedStunSocketTest : public ::
     r = nr_socket_connect(test_socket_,
                           &remote_addr_);
     ASSERT_EQ(0, r);
   }
 
   nr_socket *socket() { return test_socket_; }
 
  protected:
-  DummySocket *dummy_;
+  nsRefPtr<DummySocket> dummy_;
   nr_socket *test_socket_;
   nr_transport_addr remote_addr_;
 };
 
 
 TEST_F(BufferedStunSocketTest, TestCreate) {
 }
 
--- a/testing/config/mozharness/android_arm_config.py
+++ b/testing/config/mozharness/android_arm_config.py
@@ -1,40 +1,107 @@
 # 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/.
 
 config = {
     "suite_definitions": {
         "mochitest": {
             "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
+            "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
+                "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
+                "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
+                "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
+                "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
+                "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=16",
+                "--run-only-tests=android23.json"
+            ],
+        },
+        "mochitest-gl": {
+            "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
             "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
                 "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
                 "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
                 "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
                 "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
                 "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
-                "--quiet", "--log-raw=%(raw_log_file)s"
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=2",
+                "--test-manifest=gl.json"
+            ],
+        },
+        "robocop": {
+            "run_filename": "runtestsremote.py",
+            "testsdir": "mochitest",
+            "options": ["--autorun", "--close-when-done", "--dm_trans=sut",
+                "--console-level=INFO", "--app=%(app)s", "--remote-webserver=%(remote_webserver)s",
+                "--xre-path=%(xre_path)s", "--utility-path=%(utility_path)s",
+                "--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
+                "--http-port=%(http_port)s", "--ssl-port=%(ssl_port)s",
+                "--certificate-path=%(certs_path)s", "--symbols-path=%(symbols_path)s",
+                "--quiet", "--log-raw=%(raw_log_file)s",
+                "--total-chunks=4",
+                "--robocop-path=../..",
+                "--robocop-ids=fennec_ids.txt",
+                "--robocop=robocop.ini",
             ],
         },
         "reftest": {
             "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
             "options": [ "--app=%(app)s", "--ignore-window-size",
                 "--bootstrap",
                 "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
                 "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
                 "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
                 "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
                 "--symbols-path=%(symbols_path)s",
+                "--total-chunks=16",
+                "tests/layout/reftests/reftest.list",
+            ],
+        },
+        "crashtest": {
+            "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
+            "options": [ "--app=%(app)s", "--ignore-window-size",
+                "--bootstrap",
+                "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
+                "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
+                "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
+                "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
+                "--symbols-path=%(symbols_path)s",
+                "--total-chunks=2",
+                "tests/testing/crashtest/crashtests.list"
+            ],
+        },
+        "jsreftest": {
+            "run_filename": "remotereftest.py",
+            "testsdir": "reftest",
+            "options": [ "--app=%(app)s", "--ignore-window-size",
+                "--bootstrap",
+                "--remote-webserver=%(remote_webserver)s", "--xre-path=%(xre_path)s",
+                "--utility-path=%(utility_path)s", "--deviceIP=%(device_ip)s",
+                "--devicePort=%(device_port)s", "--http-port=%(http_port)s",
+                "--ssl-port=%(ssl_port)s", "--httpd-path", "reftest/components",
+                "--symbols-path=%(symbols_path)s",
+                "../jsreftest/tests/jstests.list",
+                "--total-chunks=6",
+                "--extra-profile-file=jsreftest/tests/user.js",
             ],
         },
         "xpcshell": {
             "run_filename": "remotexpcshelltests.py",
+            "testsdir": "xpcshell",
             "options": ["--deviceIP=%(device_ip)s", "--devicePort=%(device_port)s",
                 "--xre-path=%(xre_path)s", "--testing-modules-dir=%(modules_dir)s",
                 "--apk=%(installer_path)s", "--no-logfiles",
                 "--symbols-path=%(symbols_path)s",
                 "--manifest=tests/xpcshell.ini",
                 "--log-raw=%(raw_log_file)s",
+                "--total-chunks=3",
             ],
         },
     }, # end suite_definitions
 }
--- a/testing/marionette/client/marionette/tests/unit/test_findelement.py
+++ b/testing/marionette/client/marionette/tests/unit/test_findelement.py
@@ -1,16 +1,16 @@
 # 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/.
 
 from marionette_test import MarionetteTestCase
 from marionette import HTMLElement
 from by import By
-from errors import NoSuchElementException
+from errors import NoSuchElementException, InvalidSelectorException
 
 
 class TestElements(MarionetteTestCase):
     def test_id(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         el = self.marionette.execute_script("return window.document.getElementById('mozLink');")
         found_el = self.marionette.find_element(By.ID, "mozLink")
@@ -149,8 +149,13 @@ class TestElements(MarionetteTestCase):
         self.assertFalse(el.id in [found_el.id for found_el in found_els])
 
     def test_finding_active_element_returns_element(self):
         test_html = self.marionette.absolute_url("test.html")
         self.marionette.navigate(test_html)
         fbody = self.marionette.find_element(By.TAG_NAME, 'body')
         abody = self.marionette.get_active_element()
         self.assertEqual(fbody, abody)
+
+    def test_throws_error_when_trying_to_use_invalid_selector_type(self):
+        test_html = self.marionette.absolute_url("test.html")
+        self.marionette.navigate(test_html)
+        self.assertRaises(InvalidSelectorException, self.marionette.find_element, "Brie Search Type", "doesn't matter")
--- a/testing/marionette/marionette-elements.js
+++ b/testing/marionette/marionette-elements.js
@@ -288,17 +288,17 @@ ElementManager.prototype = {
    * @return nsIDOMElement or list of nsIDOMElements
    *        Returns the element(s) by calling the on_success function.
    */
   find: function EM_find(win, values, searchTimeout, on_success, on_error, all, command_id) {
     let startTime = values.time ? values.time : new Date().getTime();
     let startNode = (values.element != undefined) ?
                     this.getKnownElement(values.element, win) : win.document;
     if (this.elementStrategies.indexOf(values.using) < 0) {
-      throw new ElementException("No such strategy.", 17, null);
+      throw new ElementException("No such strategy.", 32, null);
     }
     let found = all ? this.findElements(values.using, values.value, win.document, startNode) :
                       this.findElement(values.using, values.value, win.document, startNode);
     let type = Object.prototype.toString.call(found);
     let isArrayLike = ((type == '[object Array]') || (type == '[object HTMLCollection]') || (type == '[object NodeList]'));
     if (found == null || (isArrayLike && found.length <= 0)) {
       if (!searchTimeout || new Date().getTime() - startTime > searchTimeout) {
         if (all) {
--- a/testing/mochitest/redirect.html
+++ b/testing/mochitest/redirect.html
@@ -12,18 +12,29 @@
         detail: {
           "data": aURL + location.search,
           "type": "loadURI"
         }
       });
       document.dispatchEvent(event);
     }
 
+    function redirectToHarness()
+    {
+      redirect("chrome://mochikit/content/harness.xul");
+    }
+
     function onLoad() {
-      redirect("chrome://mochikit/content/harness.xul");
+      // Wait for MozAfterPaint, since the listener in browser-test.js is not
+      // added until then.
+      window.addEventListener("MozAfterPaint", function testOnMozAfterPaint() {
+        window.removeEventListener("MozAfterPaint", testOnMozAfterPaint);
+        setTimeout(redirectToHarness, 0);
+      });
+
     }
   </script>
 </head>
 
 <body onload="onLoad();">
 redirecting...
 </body>
 </html>
--- a/testing/web-platform/meta/media-source/mediasource-remove.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-remove.html.ini
@@ -1,14 +1,11 @@
 [mediasource-remove.html]
   type: testharness
   expected: TIMEOUT
-  [Test remove transitioning readyState from \'ended\' to \'open\'.]
-    expected: FAIL
-
   [Test removing all appended data.]
     expected: FAIL
 
   [Test removing beginning of appended data.]
     expected: FAIL
 
   [Test removing the middle of appended data.]
     expected: FAIL
--- a/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
+++ b/toolkit/mozapps/extensions/internal/OpenH264Provider.jsm
@@ -22,16 +22,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/GMPInstallManager.jsm");
 
 const URI_EXTENSION_STRINGS    = "chrome://mozapps/locale/extensions/extensions.properties";
 const STRING_TYPE_NAME         = "type.%ID%.name";
 
 const SEC_IN_A_DAY              = 24 * 60 * 60;
 
 const EME_PREF_ENABLED         = "media.eme.enabled";
+const NS_GRE_BIN_DIR           = "GreBinD";
 const CLEARKEY_PLUGIN_ID       = "gmp-clearkey";
 const CLEARKEY_VERSION         = "0.1";
 
 const OPENH264_PLUGIN_ID       = "gmp-gmpopenh264";
 const OPENH264_PREF_BRANCH     = "media." + OPENH264_PLUGIN_ID + ".";
 const OPENH264_PREF_ENABLED    = "enabled";
 const OPENH264_PREF_VERSION    = "version";
 const OPENH264_PREF_LASTUPDATE = "lastUpdate";
@@ -278,17 +279,18 @@ let OpenH264Provider = {
         gmpService.addPluginDirectory(this.gmpPath);
       } catch (e if e.name == 'NS_ERROR_NOT_AVAILABLE') {
         this._log.warn("startup() - adding gmp directory failed with " + e.name + " - sandboxing not available?");
       }
     }
 
     if (Preferences.get(EME_PREF_ENABLED, false)) {
       try {
-        gmpService.addPluginDirectory(OS.Path.join(OS.Constants.Path.libDir,
+        let greBinDir = Services.dirsvc.get(NS_GRE_BIN_DIR, Ci.nsILocalFile);
+        gmpService.addPluginDirectory(OS.Path.join(greBinDir.path,
                                                    CLEARKEY_PLUGIN_ID,
                                                    CLEARKEY_VERSION));
       } catch (e) {
         this._log.warn("startup() - adding clearkey CDM failed", e);
       }
     }
 
     let telemetry = {};
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -79,16 +79,19 @@ if CONFIG['MOZ_ENABLE_GTK']:
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
     UNIFIED_SOURCES += [
         'android/nsAndroidHandlerApp.cpp',
         'android/nsExternalSharingAppService.cpp',
         'android/nsExternalURLHandlerService.cpp',
         'android/nsMIMEInfoAndroid.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt':
+    SOURCES += [
+        'unix/nsMIMEInfoQt.cpp',
+    ]
     UNIFIED_SOURCES += [
         'unix/nsGNOMERegistry.cpp',
         'unix/nsMIMEInfoUnix.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     UNIFIED_SOURCES += [
         'win/nsMIMEInfoWin.cpp',
     ]
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoQt.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WIDGET_QT
+#include <QDesktopServices>
+#include <QUrl>
+#include <QString>
+#include <QStringList>
+#endif
+
+#include "nsMIMEInfoQt.h"
+#include "nsIURI.h"
+#include "nsStringGlue.h"
+
+nsresult
+nsMIMEInfoQt::LoadUriInternal(nsIURI * aURI)
+{
+#ifdef MOZ_WIDGET_QT
+  nsAutoCString spec;
+  aURI->GetAsciiSpec(spec);
+  if (QDesktopServices::openUrl(QUrl(spec.get()))) {
+    return NS_OK;
+  }
+#endif
+
+  return NS_ERROR_FAILURE;
+}
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/unix/nsMIMEInfoQt.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsMIMEInfoQt_h_
+#define nsMIMEInfoQt_h_
+
+#include "nsCOMPtr.h"
+
+class nsIURI;
+
+class nsMIMEInfoQt
+{
+public:
+  static nsresult LoadUriInternal(nsIURI * aURI);
+};
+
+#endif // nsMIMEInfoQt_h_
--- a/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
+++ b/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp
@@ -1,47 +1,43 @@
 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifdef MOZ_WIDGET_QT
-#include <QDesktopServices>
-#include <QUrl>
-#include <QString>
 #if (MOZ_ENABLE_CONTENTACTION)
 #include <contentaction/contentaction.h>
 #include "nsContentHandlerApp.h"
 #endif
 #endif
 
 #include "nsMIMEInfoUnix.h"
 #include "nsGNOMERegistry.h"
 #include "nsIGIOService.h"
 #include "nsNetCID.h"
 #include "nsIIOService.h"
 #include "nsIGnomeVFSService.h"
 #include "nsAutoPtr.h"
 #ifdef MOZ_ENABLE_DBUS
 #include "nsDBusHandlerApp.h"
 #endif
+#ifdef MOZ_WIDGET_QT
+#include "nsMIMEInfoQt.h"
+#endif
 
 nsresult
 nsMIMEInfoUnix::LoadUriInternal(nsIURI * aURI)
 {
   nsresult rv = nsGNOMERegistry::LoadURL(aURI);
 
 #ifdef MOZ_WIDGET_QT
   if (NS_FAILED(rv)) {
-    nsAutoCString spec;
-    aURI->GetAsciiSpec(spec);
-    if (QDesktopServices::openUrl(QUrl(spec.get()))) {
-      rv = NS_OK;
-    }
+    rv = nsMIMEInfoQt::LoadUriInternal(aURI);
   }
 #endif
 
   return rv;
 }
 
 NS_IMETHODIMP
 nsMIMEInfoUnix::GetHasDefaultHandler(bool *_retval)
--- a/widget/qt/moz.build
+++ b/widget/qt/moz.build
@@ -37,17 +37,17 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/widget',
 ]
 
 if CONFIG['MOZ_X11']:
     LOCAL_INCLUDES += [
-        '../shared/x11',
+        '../x11',
     ]
 
 #DEFINES['DEBUG_WIDGETS'] = True
 
 if CONFIG['OS_ARCH'] == 'Linux':
     DEFINES['_BSD_SOURCE'] = True
 
 if CONFIG['OS_ARCH'] == 'SunOS' and not CONFIG['GNU_CC']:
--- a/widget/qt/nsPrintSettingsQt.cpp
+++ b/widget/qt/nsPrintSettingsQt.cpp
@@ -286,33 +286,27 @@ static const QPrinter::PageSize indexToQ
     QPrinter::B4, QPrinter::B6, QPrinter::B7, QPrinter::B8, QPrinter::B9,
     QPrinter::C5E, QPrinter::Comm10E, QPrinter::DLE, QPrinter::Folio,
     QPrinter::Ledger, QPrinter::Tabloid
 };
 
 NS_IMETHODIMP
 nsPrintSettingsQt::GetPaperName(char16_t** aPaperName)
 {
-    PR_STATIC_ASSERT(sizeof(indexToPaperName)/
-        sizeof(char*) == QPrinter::NPageSize);
-    PR_STATIC_ASSERT(sizeof(indexToQtPaperEnum)/
-        sizeof(QPrinter::PageSize) == QPrinter::NPageSize);
-
     QPrinter::PaperSize size = mQPrinter->paperSize();
     QString name(indexToPaperName[size]);
-    *aPaperName = ToNewUnicode(nsDependentString
-        ((const char16_t*)name.constData()));
+    *aPaperName = ToNewUnicode(nsDependentString((const char16_t*)name.constData()));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPrintSettingsQt::SetPaperName(const char16_t* aPaperName)
 {
     QString ref((QChar*)aPaperName, NS_strlen(aPaperName));
-    for (uint32_t i = 0; i < QPrinter::NPageSize; i++)
+    for (uint32_t i = 0; i < sizeof(indexToPaperName)/sizeof(char*); i++)
     {
         if (ref == QString(indexToPaperName[i])) {
             mQPrinter->setPageSize(indexToQtPaperEnum[i]);
             return NS_OK;
         }
     }
     return NS_ERROR_FAILURE;
 }