merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 15 Apr 2016 11:41:41 +0200
changeset 331224 afd82f887093e5e9e4015115ca5795ec82a6f732
parent 331078 755a175b3cc735b575a9e3dd4fcfcd4c7a63695c (current diff)
parent 331223 8de93854b963a067cc672d99295b452058297be8 (diff)
child 331225 354cb3932e36994d7e772cedba237e9c3c60fe4c
child 331250 e4a7a71a1119d1689e84bbf9b230581cea32ecee
child 331315 8f7045b63b07c01309ab451ad120eacfdaa420d1
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
afd82f887093 / 48.0a1 / 20160415030231 / files
nightly linux64
afd82f887093 / 48.0a1 / 20160415030231 / files
nightly mac
afd82f887093 / 48.0a1 / 20160415030231 / files
nightly win32
afd82f887093 / 48.0a1 / 20160415030231 / files
nightly win64
afd82f887093 / 48.0a1 / 20160415030231 / 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
browser/base/content/browser.js
browser/config/mozconfigs/win32/l10n-mozconfig
build/autoconf/wrapper.m4
build/win32/mozconfig.vs2010-win64
build/win64/mozconfig.vs2010
layout/reftests/w3c-css/skip.list
--- a/accessible/base/EventTree.cpp
+++ b/accessible/base/EventTree.cpp
@@ -180,62 +180,69 @@ TreeMutation::Done()
 ////////////////////////////////////////////////////////////////////////////////
 // EventTree
 
 void
 EventTree::Process()
 {
   EventTree* node = mFirst;
   while (node) {
-    node->Process();
+    // Skip a node and its subtree if its container is not in the document.
+    if (node->mContainer->IsInDocument()) {
+      node->Process();
+    }
     node = node->mNext;
   }
 
+  MOZ_ASSERT(mContainer || mDependentEvents.IsEmpty(),
+             "No container, no events");
+  MOZ_ASSERT(!mContainer || !mContainer->IsDefunct(),
+             "Processing events for defunct container");
+
   // Fire mutation events.
-  if (mContainer) {
-    uint32_t eventsCount = mDependentEvents.Length();
-    for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
-      AccMutationEvent* mtEvent = mDependentEvents[jdx];
-      MOZ_ASSERT(mtEvent->mEventRule != AccEvent::eDoNotEmit,
-                 "The event shouldn't be presented in the tree");
+  uint32_t eventsCount = mDependentEvents.Length();
+  for (uint32_t jdx = 0; jdx < eventsCount; jdx++) {
+    AccMutationEvent* mtEvent = mDependentEvents[jdx];
+    MOZ_ASSERT(mtEvent->mEventRule != AccEvent::eDoNotEmit,
+               "The event shouldn't be presented in the tree");
+
+    nsEventShell::FireEvent(mtEvent);
+    if (mtEvent->mTextChangeEvent) {
+      nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
+    }
 
-      nsEventShell::FireEvent(mtEvent);
-      if (mtEvent->mTextChangeEvent) {
-        nsEventShell::FireEvent(mtEvent->mTextChangeEvent);
+    if (mtEvent->IsHide()) {
+      // Fire menupopup end event before a hide event if a menu goes away.
+
+      // XXX: We don't look into children of hidden subtree to find hiding
+      // menupopup (as we did prior bug 570275) because we don't do that when
+      // menu is showing (and that's impossible until bug 606924 is fixed).
+      // Nevertheless we should do this at least because layout coalesces
+      // the changes before our processing and we may miss some menupopup
+      // events. Now we just want to be consistent in content insertion/removal
+      // handling.
+      if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
+        nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
+                                mtEvent->mAccessible);
       }
 
-      if (mtEvent->IsHide()) {
-        // Fire menupopup end event before a hide event if a menu goes away.
-
-        // XXX: We don't look into children of hidden subtree to find hiding
-        // menupopup (as we did prior bug 570275) because we don't do that when
-        // menu is showing (and that's impossible until bug 606924 is fixed).
-        // Nevertheless we should do this at least because layout coalesces
-        // the changes before our processing and we may miss some menupopup
-        // events. Now we just want to be consistent in content insertion/removal
-        // handling.
-        if (mtEvent->mAccessible->ARIARole() == roles::MENUPOPUP) {
-          nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
-                                  mtEvent->mAccessible);
-        }
-
-        AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
-        if (hideEvent->NeedsShutdown()) {
-          mtEvent->GetDocAccessible()->ShutdownChildrenInSubtree(mtEvent->mAccessible);
-        }
+      AccHideEvent* hideEvent = downcast_accEvent(mtEvent);
+      if (hideEvent->NeedsShutdown()) {
+        mtEvent->GetDocAccessible()->ShutdownChildrenInSubtree(mtEvent->mAccessible);
       }
     }
+  }
 
-    // Fire reorder event at last.
-    if (mFireReorder) {
-      nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
-    }
+  // Fire reorder event at last.
+  if (mFireReorder) {
+    MOZ_ASSERT(mContainer);
+    nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_REORDER, mContainer);
+  }
 
-    mDependentEvents.Clear();
-  }
+  mDependentEvents.Clear();
 }
 
 EventTree*
 EventTree::FindOrInsert(Accessible* aContainer)
 {
   if (!mFirst) {
     return mFirst = new EventTree(aContainer, true);
   }
@@ -328,17 +335,17 @@ EventTree::FindOrInsert(Accessible* aCon
   } while ((node = node->mNext));
 
   MOZ_ASSERT(prevNode, "Nowhere to insert");
   MOZ_ASSERT(!prevNode->mNext, "Taken by another node");
 
   // If 'this' node contains the given container accessible, then
   //   do not emit a reorder event for the container
   //   if a dependent show event target contains the given container then do not
-  //      emit show / hide events (to be done)
+  //   emit show / hide events (see Process() method)
 
   return prevNode->mNext = new EventTree(aContainer, mDependentEvents.IsEmpty());
 }
 
 void
 EventTree::Clear()
 {
   mFirst = nullptr;
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -385,17 +385,19 @@ NotificationController::WillRefresh(mozi
   mDocument->ProcessInvalidationList();
 
   // We cannot rely on DOM tree to keep aria-owns relations updated. Make
   // a validation to remove dead links.
   mDocument->ValidateARIAOwned();
 
   // Process relocation list.
   for (uint32_t idx = 0; idx < mRelocations.Length(); idx++) {
-    mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
+    if (mRelocations[idx]->IsInDocument()) {
+      mDocument->DoARIAOwnsRelocation(mRelocations[idx]);
+    }
   }
   mRelocations.Clear();
 
   // If a generic notification occurs after this point then we may be allowed to
   // process it synchronously.  However we do not want to reenter if fireing
   // events causes script to run.
   mObservingState = eRefreshProcessing;
 
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -419,27 +419,28 @@ DocAccessible::Shutdown()
   if (!mPresShell) // already shutdown
     return;
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocDestroy))
     logging::DocDestroy("document shutdown", mDocumentNode, this);
 #endif
 
+  // Mark the document as shutdown before AT is notified about the document
+  // removal from its container (valid for root documents on ATK and due to
+  // some reason for MSAA, refer to bug 757392 for details).
+  mStateFlags |= eIsDefunct;
+
   if (mNotificationController) {
     mNotificationController->Shutdown();
     mNotificationController = nullptr;
   }
 
   RemoveEventListeners();
 
-  // Mark the document as shutdown before AT is notified about the document
-  // removal from its container (valid for root documents on ATK and due to
-  // some reason for MSAA, refer to bug 757392 for details).
-  mStateFlags |= eIsDefunct;
   nsCOMPtr<nsIDocument> kungFuDeathGripDoc = mDocumentNode;
   mDocumentNode = nullptr;
 
   if (mParent) {
     DocAccessible* parentDocument = mParent->Document();
     if (parentDocument)
       parentDocument->RemoveChildDocument(this);
 
--- a/accessible/generic/OuterDocAccessible.cpp
+++ b/accessible/generic/OuterDocAccessible.cpp
@@ -79,37 +79,39 @@ OuterDocAccessible::ChildAtPoint(int32_t
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public
 
 void
 OuterDocAccessible::Shutdown()
 {
-  // XXX: sometimes outerdoc accessible is shutdown because of layout style
-  // change however the presshell of underlying document isn't destroyed and
-  // the document doesn't get pagehide events. Schedule a document rebind
-  // to its parent document. Otherwise a document accessible may be lost if its
-  // outerdoc has being recreated (see bug 862863 for details).
-
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocDestroy))
     logging::OuterDocDestroy(this);
 #endif
 
   Accessible* child = mChildren.SafeElementAt(0, nullptr);
   if (child) {
 #ifdef A11Y_LOG
     if (logging::IsEnabled(logging::eDocDestroy)) {
       logging::DocDestroy("outerdoc's child document rebind is scheduled",
                           child->AsDoc()->DocumentNode());
     }
 #endif
     RemoveChild(child);
-    mDoc->BindChildDocument(child->AsDoc());
+
+    // XXX: sometimes outerdoc accessible is shutdown because of layout style
+    // change however the presshell of underlying document isn't destroyed and
+    // the document doesn't get pagehide events. Schedule a document rebind
+    // to its parent document. Otherwise a document accessible may be lost if
+    // its outerdoc has being recreated (see bug 862863 for details).
+    if (!mDoc->IsDefunct()) {
+      mDoc->BindChildDocument(child->AsDoc());
+    }
   }
 
   AccessibleWrap::Shutdown();
 }
 
 bool
 OuterDocAccessible::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
 {
--- a/accessible/tests/mochitest/events/test_coalescence.html
+++ b/accessible/tests/mochitest/events/test_coalescence.html
@@ -23,24 +23,21 @@
     ////////////////////////////////////////////////////////////////////////////
     // Invoker base classes
 
     const kRemoveElm = 1;
     const kHideElm = 2;
     const kAddElm = 3;
     const kShowElm = 4;
 
-    const kToDo = true;
-
     /**
      * Base class to test of mutation events coalescence.
      */
     function coalescenceBase(aChildAction, aParentAction,
-                             aPerformActionOnChildInTheFirstPlace,
-                             aIsChildsToDo)
+                             aPerformActionOnChildInTheFirstPlace)
     {
       // Invoker interface
 
       this.invoke = function coalescenceBase_invoke()
       {
         if (aPerformActionOnChildInTheFirstPlace) {
           this.invokeAction(this.childNode, aChildAction);
           this.invokeAction(this.parentNode, aParentAction);
@@ -58,23 +55,22 @@
         if (aPerformActionOnChildInTheFirstPlace)
           return childAction + " and then " + parentAction;
 
         return parentAction + " and then " + childAction;
       }
 
       this.finalCheck = function coalescenceBase_check()
       {
-        if (!aIsChildsToDo)
-          return;
-
-        var eventType = eventTypeToString(this.getEventType(aChildAction));
-        todo(false,
-             "Unexpected event " + eventType +
-             " for child in the test '" + this.getID() + "'");
+        if (this.getEventType(aChildAction) == EVENT_HIDE) {
+          testIsDefunct(this.child);
+        }
+        if (this.getEventType(aParentAction) == EVENT_HIDE) {
+          testIsDefunct(this.parent);
+        }
       }
 
       // Implementation details
 
       this.invokeAction = function coalescenceBase_invokeAction(aNode, aAction)
       {
         switch (aAction) {
           case kRemoveElm:
@@ -133,47 +129,39 @@
         var eventType = this.getEventType(aParentAction);
         this.eventSeq = [
           new invokerChecker(eventType, this.parentNode),
           new invokerChecker(EVENT_REORDER, this.hostNode)
         ];
 
         // unexpected events
         this.unexpectedEventSeq = [
+          new invokerChecker(this.getEventType(aChildAction), this.childNode),
           new invokerChecker(EVENT_REORDER, this.parentNode)
         ];
-
-        if (!aIsChildsToDo) {
-          var eventType = this.getEventType(aChildAction);
-          var checker = new invokerChecker(eventType, this.childNode);
-          this.unexpectedEventSeq.unshift(checker);
-        }
       }
     }
 
     /**
      * Remove or hide mutation events coalescence testing.
      */
     function removeOrHideCoalescenceBase(aChildID, aParentID,
                                          aChildAction, aParentAction,
-                                         aPerformActionOnChildInTheFirstPlace,
-                                         aIsChildsToDo)
+                                         aPerformActionOnChildInTheFirstPlace)
     {
       this.__proto__ = new coalescenceBase(aChildAction, aParentAction,
-                                           aPerformActionOnChildInTheFirstPlace,
-                                           aIsChildsToDo);
+                                           aPerformActionOnChildInTheFirstPlace);
 
       this.init = function removeOrHideCoalescenceBase_init()
       {
         this.childNode = getNode(aChildID);
         this.parentNode = getNode(aParentID);
+        this.child = getAccessible(this.childNode);
+        this.parent = getAccessible(this.parentNode);
         this.hostNode = this.parentNode.parentNode;
-
-        // ensure child accessible is created
-        getAccessible(this.childNode);
       }
 
       // Initalization
 
       this.init();
       this.initSequence();
     }
 
@@ -182,17 +170,17 @@
 
     /**
      * Remove child node and then its parent node from DOM tree.
      */
     function removeChildNParent(aChildID, aParentID)
     {
       this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
                                                        kRemoveElm, kRemoveElm,
-                                                       true, kToDo);
+                                                       true);
     }
 
     /**
      * Remove parent node and then its child node from DOM tree.
      */
     function removeParentNChild(aChildID, aParentID)
     {
       this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
@@ -230,33 +218,29 @@
                                                        true);
     }
 
     /**
      * Hide parent node and then remove its child node.
      */
     function hideParentNRemoveChild(aChildID, aParentID)
     {
-      // Because of async layout changes we handle remove child node change
-      // before than hide parent node change even we hide parent before we
-      // remove a child. Therefore mark this as todo until we have more smart
-      // events coalescence.
       this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
                                                        kRemoveElm, kHideElm,
-                                                       false, kToDo);
+                                                       false);
     }
 
     /**
      * Remove child node and then hide its parent node.
      */
     function removeChildNHideParent(aChildID, aParentID)
     {
       this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
                                                        kRemoveElm, kHideElm,
-                                                       true, kToDo);
+                                                       true);
     }
 
     /**
      * Remove parent node and then hide its child node.
      */
     function removeParentNHideChild(aChildID, aParentID)
     {
       this.__proto__ = new removeOrHideCoalescenceBase(aChildID, aParentID,
--- a/accessible/tests/mochitest/treeupdate/test_ariaowns.html
+++ b/accessible/tests/mochitest/treeupdate/test_ariaowns.html
@@ -475,45 +475,62 @@
     function removeNotARIAOwnedEl(aContainer, aChild)
     {
       this.eventSeq = [
         new invokerChecker(EVENT_REORDER, aContainer)
       ];
 
       this.invoke = function removeNotARIAOwnedEl_invoke()
       {
-        dumpTree(aContainer, "before");
         var tree = {
           SECTION: [
             { TEXT_LEAF: [ ] },
             { GROUPING: [ ] }
           ]
         };
         testAccessibleTree(aContainer, tree);
 
         getNode(aContainer).removeChild(getNode(aChild));
       }
 
       this.finalCheck = function removeNotARIAOwnedEl_finalCheck()
       {
-        dumpTree(aContainer, "after");
         var tree = {
           SECTION: [
             { GROUPING: [ ] }
           ]
         };
         testAccessibleTree(aContainer, tree);
       }
 
       this.getID = function removeNotARIAOwnedEl_getID()
       {
         return `remove not ARIA owned child`;
       }
     }
 
+    function setARIAOwnsOnElToRemove(aParent, aChild)
+    {
+      this.eventSeq = [
+        new invokerChecker(EVENT_HIDE, getAccessible(aParent))
+      ];
+
+      this.invoke = function setARIAOwnsOnElToRemove_invoke()
+      {
+        getNode(aChild).setAttribute("aria-owns", "no_id");
+        getNode(aParent).removeChild(getNode(aChild));
+        getNode(aParent).parentNode.removeChild(getNode(aParent));
+      }
+
+      this.getID = function setARIAOwnsOnElToRemove_getID()
+      {
+        return `set ARIA owns on an element, and then remove it, and then remove its parent`;
+      }
+    }
+
 
     ////////////////////////////////////////////////////////////////////////////
     // Test
     ////////////////////////////////////////////////////////////////////////////
 
     //gA11yEventDumpToConsole = true;
     //enableLogging("tree,eventTree,verbose"); // debug stuff
 
@@ -550,16 +567,18 @@
         [ "CHECKBUTTON", "RADIOBUTTON", "PUSHBUTTON" ]));
       gQueue.push(new rearrangeARIAOwns(
         "t5_container", "t5_radio t5_button t5_checkbox",
         [ "t5_radio", "t5_button" ],
         [ "RADIOBUTTON", "PUSHBUTTON", "CHECKBUTTON" ]));
 
       gQueue.push(new removeNotARIAOwnedEl("t6_container", "t6_span"));
 
+      gQueue.push(new setARIAOwnsOnElToRemove("t7_parent", "t7_child"));
+
       gQueue.invoke(); // SimpleTest.finish() will be called in the end
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
 
   </script>
 </head>
@@ -602,11 +621,17 @@
     <div role="checkbox" id="t5_checkbox"></div>
     <div role="radio" id="t5_radio"></div>
   </div>
 
   <div id="t6_container" aria-owns="t6_fake">
     <span id="t6_span">hey</span>
   </div>
   <div id="t6_fake" role="group"></div>
+
+  <div id="t7_container">
+    <div id="t7_parent">
+      <div id="t7_child"></div>
+    </div>
+  </div>
 </body>
 
 </html>
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -3,17 +3,16 @@ dnl Local autoconf macros used with mozi
 dnl The contents of this file are under the Public Domain.
 dnl
 
 builtin(include, build/autoconf/hotfixes.m4)dnl
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/hooks.m4)dnl
 builtin(include, build/autoconf/config.status.m4)dnl
 builtin(include, build/autoconf/toolchain.m4)dnl
-builtin(include, build/autoconf/wrapper.m4)dnl
 builtin(include, build/autoconf/nspr.m4)dnl
 builtin(include, build/autoconf/nspr-build.m4)dnl
 builtin(include, build/autoconf/nss.m4)dnl
 builtin(include, build/autoconf/pkg.m4)dnl
 builtin(include, build/autoconf/codeset.m4)dnl
 builtin(include, build/autoconf/altoptions.m4)dnl
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
--- a/b2g/config/mozconfigs/win32_gecko/debug
+++ b/b2g/config/mozconfigs/win32_gecko/debug
@@ -12,17 +12,17 @@ ac_add_options --enable-debug
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # B2G Options
 ac_add_options --enable-application=b2g
 ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
--- a/b2g/config/mozconfigs/win32_gecko/nightly
+++ b/b2g/config/mozconfigs/win32_gecko/nightly
@@ -12,17 +12,17 @@ ac_add_options --enable-signmar
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # B2G Options
 ac_add_options --enable-application=b2g
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 GAIADIR=$topsrcdir/gaia
 
--- a/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/linux64/releng.manifest
@@ -11,18 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"size": 89319524,
-"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1",
+"version": "rustc 1.8.0 (db2939409 2016-04-11)",
+"size": 123218320,
+"digest": "7afcd7b39c4d5277db6b28951602aff4c698102ba45d3d811b353ca7446074beceebf03a2a529e323af19d73db4acbe96ec2bdad44def2e218ed36f55e82cab2",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/b2g/graphene/config/horizon-mozconfigs/win32/debug
+++ b/b2g/graphene/config/horizon-mozconfigs/win32/debug
@@ -7,17 +7,17 @@ ac_add_options --enable-debug
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # graphene Options
 ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/b2g/graphene/config/horizon-mozconfigs/win32/nightly
+++ b/b2g/graphene/config/horizon-mozconfigs/win32/nightly
@@ -9,15 +9,15 @@ ac_add_options --enable-signmar
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # graphene Options
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/horizon-mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/win32/debug
+++ b/b2g/graphene/config/mozconfigs/win32/debug
@@ -7,17 +7,17 @@ ac_add_options --enable-debug
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # graphene Options
 ENABLE_MARIONETTE=1
 
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/grapheneconfig/mozconfigs/common.override"
--- a/b2g/graphene/config/mozconfigs/win32/nightly
+++ b/b2g/graphene/config/mozconfigs/win32/nightly
@@ -9,15 +9,15 @@ ac_add_options --enable-signmar
 ac_add_options --enable-js-diagnostics
 
 # Needed to enable breakpad in application.ini
 export MOZILLA_OFFICIAL=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2013-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2013
 fi
 
 # graphene Options
 export CXXFLAGS=-DMOZ_ENABLE_JS_DUMP
 
 . "$topsrcdir/b2g/graphene/config/mozconfigs/common.override"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2770,26 +2770,25 @@ var BrowserOnClick = {
     let errorReporter = Cc["@mozilla.org/securityreporter;1"]
                           .getService(Ci.nsISecurityReporter);
     errorReporter.reportTLSError(transportSecurityInfo,
                                  uri.host, uri.port);
   },
 
   onCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) {
     let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
+    let securityInfo;
 
     switch (elementId) {
       case "exceptionDialogButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_CLICK_ADD_EXCEPTION);
         }
 
-        let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
-                           .getService(Ci.nsISerializationHelper);
-        let securityInfo = serhelper.deserializeObject(securityInfoAsString);
+        securityInfo = getSecurityInfo(securityInfoAsString);
         let sslStatus = securityInfo.QueryInterface(Ci.nsISSLStatusProvider)
                                     .SSLStatus;
         let params = { exceptionAdded : false,
                        sslStatus : sslStatus };
 
         try {
           switch (Services.prefs.getIntPref("browser.ssl_override_behavior")) {
             case 2 : // Pre-fetch & pre-populate
@@ -2817,27 +2816,31 @@ var BrowserOnClick = {
         goBackFromErrorPage();
         break;
 
       case "advancedButton":
         if (isTopFrame) {
           secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_BAD_CERT_TOP_UNDERSTAND_RISKS);
         }
 
+        securityInfo = getSecurityInfo(securityInfoAsString);
         let errorInfo = getDetailedCertErrorInfo(location,
-                                                 securityInfoAsString);
-        browser.messageManager.sendAsyncMessage("CertErrorDetails",
-                                                { info: errorInfo });
+                                                 securityInfo);
+        browser.messageManager.sendAsyncMessage( "CertErrorDetails", {
+            code: securityInfo.errorCode,
+            info: errorInfo
+        });
         break;
 
       case "copyToClipboard":
         const gClipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]
                                     .getService(Ci.nsIClipboardHelper);
+        securityInfo = getSecurityInfo(securityInfoAsString);
         let detailedInfo = getDetailedCertErrorInfo(location,
-                                                    securityInfoAsString);
+                                                    securityInfo);
         gClipboardHelper.copyString(detailedInfo);
         break;
 
     }
   },
 
   onAboutBlocked: function (elementId, reason, isTopFrame, location) {
     // Depending on what page we are displaying here (malware/phishing/unwanted)
@@ -3089,34 +3092,41 @@ function BrowserReloadWithFlags(reloadFl
 
   gBrowser.selectedBrowser
           .messageManager
           .sendAsyncMessage("Browser:Reload",
                             { flags: reloadFlags,
                               handlingUserInput: windowUtils.isHandlingUserInput });
 }
 
-/**
- * Returns a string with detailed information about the certificate validation
- * failure from the specified URI that can be used to send a report.
- */
-function getDetailedCertErrorInfo(location, securityInfoAsString) {
+function getSecurityInfo(securityInfoAsString) {
   if (!securityInfoAsString)
-    return "";
-
-  let certErrorDetails = location;
+    return null;
 
   const serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
                        .getService(Ci.nsISerializationHelper);
   let securityInfo = serhelper.deserializeObject(securityInfoAsString);
   securityInfo.QueryInterface(Ci.nsITransportSecurityInfo);
 
+  return securityInfo;
+}
+
+/**
+ * Returns a string with detailed information about the certificate validation
+ * failure from the specified URI that can be used to send a report.
+ */
+function getDetailedCertErrorInfo(location, securityInfo) {
+  if (!securityInfo)
+    return "";
+
+  let certErrorDetails = location;
+  let code = securityInfo.errorCode;
   let errors = Cc["@mozilla.org/nss_errors_service;1"]
                   .getService(Ci.nsINSSErrorsService);
-  let code = securityInfo.errorCode;
+
   certErrorDetails += "\r\n\r\n" + errors.getErrorMessage(errors.getXPCOMFromNSSError(code));
 
   const sss = Cc["@mozilla.org/ssservice;1"]
                  .getService(Ci.nsISiteSecurityService);
   // SiteSecurityService uses different storage if the channel is
   // private. Thus we must give isSecureHost correct flags or we
   // might get incorrect results.
   let flags = PrivateBrowsingUtils.isWindowPrivate(window) ?
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -207,16 +207,19 @@ Cc["@mozilla.org/eventlistenerservice;1"
   .addSystemEventListener(global, "contextmenu", handleContentContextMenu, false);
 
 // Values for telemtery bins: see TLS_ERROR_REPORT_UI in Histograms.json
 const TLS_ERROR_REPORT_TELEMETRY_UI_SHOWN = 0;
 const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1;
 const TLS_ERROR_REPORT_TELEMETRY_SUCCESS  = 6;
 const TLS_ERROR_REPORT_TELEMETRY_FAILURE  = 7;
 
+const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
+
+const SEC_ERROR_UNKNOWN_ISSUER = SEC_ERROR_BASE + 13;
 
 var AboutNetAndCertErrorListener = {
   init: function(chromeGlobal) {
     addMessageListener("CertErrorDetails", this);
     chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);
     chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true);
     chromeGlobal.addEventListener('AboutNetErrorOverride', this, false, true);
   },
@@ -239,16 +242,23 @@ var AboutNetAndCertErrorListener = {
         this.onCertErrorDetails(msg);
         break;
     }
   },
 
   onCertErrorDetails(msg) {
     let div = content.document.getElementById("certificateErrorText");
     div.textContent = msg.data.info;
+
+    switch (msg.data.code) {
+      case SEC_ERROR_UNKNOWN_ISSUER:
+        let learnMoreLink = content.document.getElementById("learnMoreLink");
+        learnMoreLink.href = "https://support.mozilla.org/kb/troubleshoot-SEC_ERROR_UNKNOWN_ISSUER";
+        break;
+    }
   },
 
   handleEvent: function(aEvent) {
     if (!this.isAboutNetError && !this.isAboutCertError) {
       return;
     }
 
     switch (aEvent.type) {
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -120,17 +120,17 @@ Site.prototype = {
       this.node.removeAttribute("pinned");
       control.setAttribute("title", newTabString("pin"));
     }
   },
 
   _newTabString: function(str, substrArr) {
     let regExp = /%[0-9]\$S/g;
     let matches;
-    while (matches = regExp.exec(str)) {
+    while ((matches = regExp.exec(str))) {
       let match = matches[0];
       let index = match.charAt(1); // Get the digit in the regExp.
       str = str.replace(match, substrArr[index - 1]);
     }
     return str;
   },
 
   _getSuggestedTileExplanation: function() {
--- a/browser/base/content/test/general/browser_aboutCertError.js
+++ b/browser/base/content/test/general/browser_aboutCertError.js
@@ -2,16 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 // This is testing the aboutCertError page (Bug 1207107).
 
 const GOOD_PAGE = "https://example.com/";
 const BAD_CERT = "https://expired.example.com/";
+const UNKNOWN_ISSUER = "https://self-signed.example.com ";
 const BAD_STS_CERT = "https://badchain.include-subdomains.pinning.example.com:443";
 const {TabStateFlusher} = Cu.import("resource:///modules/sessionstore/TabStateFlusher.jsm", {});
 const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
 add_task(function* checkReturnToAboutHome() {
   info("Loading a bad cert page directly and making sure 'return to previous page' goes to about:home");
   let browser;
   let certErrorLoaded;
@@ -41,17 +42,17 @@ add_task(function* checkReturnToAboutHom
     returnButton.click();
   });
   yield pageshowPromise;
 
   is(browser.webNavigation.canGoBack, true, "webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, false, "!webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, "about:home", "Went back");
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkReturnToPreviousPage() {
   info("Loading a bad cert page and making sure 'return to previous page' goes back");
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
   let browser = gBrowser.selectedBrowser;
 
   info("Loading and waiting for the cert error");
@@ -76,17 +77,17 @@ add_task(function* checkReturnToPrevious
     returnButton.click();
   });
   yield pageshowPromise;
 
   is(browser.webNavigation.canGoBack, false, "!webNavigation.canGoBack");
   is(browser.webNavigation.canGoForward, true, "webNavigation.canGoForward");
   is(gBrowser.currentURI.spec, GOOD_PAGE, "Went back");
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkBadStsCert() {
   info("Loading a badStsCert and making sure exception button doesn't show up");
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, GOOD_PAGE);
   let browser = gBrowser.selectedBrowser;
 
   info("Loading and waiting for the cert error");
@@ -96,17 +97,17 @@ add_task(function* checkBadStsCert() {
 
   let exceptionButtonHidden = yield ContentTask.spawn(browser, null, function* () {
     let doc = content.document;
     let exceptionButton = doc.getElementById("exceptionDialogButton");
     return exceptionButton.hidden;
   });
   ok(exceptionButtonHidden, "Exception button is hidden");
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkAdvancedDetails() {
   info("Loading a bad cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_CERT);
@@ -156,17 +157,17 @@ add_task(function* checkAdvancedDetails(
      "Correct error message found");
   ok(message.text.includes("HTTP Strict Transport Security: false"),
      "Correct HSTS value found");
   ok(message.text.includes("HTTP Public Key Pinning: false"),
      "Correct HPKP value found");
   let certChain = getCertChain(message.securityInfoAsString);
   ok(message.text.includes(certChain), "Found certificate chain");
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 add_task(function* checkAdvancedDetailsForHSTS() {
   info("Loading a bad STS cert page and verifying the advanced details section");
   let browser;
   let certErrorLoaded;
   let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
     gBrowser.selectedTab = gBrowser.addTab(BAD_STS_CERT);
@@ -228,17 +229,39 @@ add_task(function* checkAdvancedDetailsF
      "Correct error message found");
   ok(message.text.includes("HTTP Strict Transport Security: false"),
      "Correct HSTS value found");
   ok(message.text.includes("HTTP Public Key Pinning: true"),
      "Correct HPKP value found");
   let certChain = getCertChain(message.securityInfoAsString);
   ok(message.text.includes(certChain), "Found certificate chain");
 
-  gBrowser.removeCurrentTab();
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
+
+add_task(function* checkUnknownIssuerLearnMoreLink() {
+  info("Loading a cert error for self-signed pages and checking the correct link is shown");
+  let browser;
+  let certErrorLoaded;
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
+    gBrowser.selectedTab = gBrowser.addTab(UNKNOWN_ISSUER);
+    browser = gBrowser.selectedBrowser;
+    certErrorLoaded = waitForCertErrorLoad(browser);
+  }, false);
+
+  info("Loading and waiting for the cert error");
+  yield certErrorLoaded;
+
+  let href = yield ContentTask.spawn(browser, null, function* () {
+    let learnMoreLink = content.document.getElementById("learnMoreLink");
+    return learnMoreLink.href;
+  });
+  is(href, "https://support.mozilla.org/kb/troubleshoot-SEC_ERROR_UNKNOWN_ISSUER");
+
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
 });
 
 function waitForCertErrorLoad(browser) {
   return new Promise(resolve => {
     info("Waiting for DOMContentLoaded event");
     browser.addEventListener("DOMContentLoaded", function load() {
       browser.removeEventListener("DOMContentLoaded", load, false, true);
       resolve();
--- a/browser/components/sessionstore/test/browser_466937.js
+++ b/browser/components/sessionstore/test/browser_466937.js
@@ -12,17 +12,17 @@ add_task(function test_prevent_file_stea
   // Add a tab with some file input fields.
   let tab = gBrowser.addTab(URL);
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
 
   // Generate a path to a 'secret' file.
   let file = Services.dirsvc.get("TmpD", Ci.nsIFile);
   file.append("466937_test.file");
-  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("666", 8));
+  file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0o666);
   let testPath = file.path;
 
   // Fill in form values.
   yield setInputValue(browser, {id: "reverse_thief", value: "/home/user/secret2"});
   yield setInputValue(browser, {id: "bystander", value: testPath});
 
   // Duplicate and check form values.
   let tab2 = gBrowser.duplicateTab(tab);
--- a/browser/config/mozconfigs/linux64/artifact
+++ b/browser/config/mozconfigs/linux64/artifact
@@ -1,8 +1,10 @@
 MOZ_AUTOMATION_BUILD_SYMBOLS=0
 MOZ_AUTOMATION_L10N_CHECK=0
 
 NO_CACHE=1
 
 . "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
 
 ac_add_options --enable-artifact-builds
+unset CC
+unset CXX
--- a/browser/config/mozconfigs/win32/l10n-mozconfig
+++ b/browser/config/mozconfigs/win32/l10n-mozconfig
@@ -9,12 +9,12 @@ ac_add_options --with-branding=browser/b
 export MOZILLA_OFFICIAL=1
 
 # Enable Telemetry
 export MOZ_TELEMETRY_REPORTING=1
 
 if test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2015-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2015
 fi
 
 . "$topsrcdir/build/mozconfig.common.override"
--- a/browser/config/tooltool-manifests/linux64/releng.manifest
+++ b/browser/config/tooltool-manifests/linux64/releng.manifest
@@ -11,18 +11,19 @@
 "size": 12072532,
 "digest": "3915f8ec396c56a8a92e6f9695b70f09ce9d1582359d1258e37e3fd43a143bc974410e4cfc27f500e095f34a8956206e0ebf799b7287f0f38def0d5e34ed71c9",
 "algorithm": "sha512",
 "filename": "gtk3.tar.xz",
 "setup": "setup.sh",
 "unpack": true
 },
 {
-"size": 89319524,
-"digest": "5383d843c9f28abf0a6d254e9d975d96972d2c86d627ca836fa8e272a5d53230603b387d7d1499c49df7f84b1bb946946e800a85c88d968bdbe81c755fcb02e1",
+"version": "rustc 1.8.0 (db2939409 2016-04-11)",
+"size": 123218320,
+"digest": "7afcd7b39c4d5277db6b28951602aff4c698102ba45d3d811b353ca7446074beceebf03a2a529e323af19d73db4acbe96ec2bdad44def2e218ed36f55e82cab2",
 "algorithm": "sha512",
 "filename": "rustc.tar.xz",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/win32/releng.manifest
+++ b/browser/config/tooltool-manifests/win32/releng.manifest
@@ -1,18 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 51746920,
-"digest": "9d886f27b50ad0479062674e815f3a99bd75dbcf2e3932090352f2866fc90cc8d8aea0e790a42baba3bee302f43d236cce7377d469bb665e5d5a3f5edf05d0c5",
+"version": "1.9.0-beta.1 (37a2869af 2016-04-12)",
+"size": 96983375,
+"digest": "180118e83948c24c3df2e78f02468d9011c8583c1df36d25481f74c302e9085f442133884adf431b8c05567ce1d5dfb05915cdc0bd6303d599452cad350c8de4",
 "algorithm": "sha512",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/browser/config/tooltool-manifests/win64/releng.manifest
+++ b/browser/config/tooltool-manifests/win64/releng.manifest
@@ -1,18 +1,19 @@
 [
 {
 "size": 266240,
 "digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
 "algorithm": "sha512",
 "filename": "mozmake.exe"
 },
 {
-"size": 80157273,
-"digest": "c4704dcc6774b9f3baaa9313192d26e36bfba2d4380d0518ee7cb89153d9adfe63f228f0ac29848f02948eb1ab7e6624ba71210f0121196d2b54ecebd640d1e6",
+"version": "rustc 1.8.0 (db2939409 2016-04-11)",
+"size": 85285866,
+"digest": "f3862036781df9f699a18a5449d51a4f8880e7d890500b314d1f16f394da77c2c496fa537a691d9d99f3248ec2067beaf20b4361babe0dd449d4f7b4f539acac",
 "algorithm": "sha512",
 "visibility": "public",
 "filename": "rustc.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
--- a/browser/confvars.sh
+++ b/browser/confvars.sh
@@ -22,17 +22,16 @@ if test "$OS_ARCH" = "WINNT"; then
       fi
     fi
   fi
 fi
 
 # Enable building ./signmar and running libmar signature tests
 MOZ_ENABLE_SIGNMAR=1
 
-MOZ_CHROME_FILE_FORMAT=omni
 MOZ_SAFE_BROWSING=1
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_CRYPTO=1
 MOZ_SERVICES_HEALTHREPORT=1
 MOZ_SERVICES_SYNC=1
 MOZ_SERVICES_CLOUDSYNC=1
 MOZ_APP_VERSION=$FIREFOX_VERSION
 MOZ_APP_VERSION_DISPLAY=$FIREFOX_VERSION_DISPLAY
--- a/browser/extensions/e10srollout/bootstrap.js
+++ b/browser/extensions/e10srollout/bootstrap.js
@@ -58,23 +58,21 @@ function defineCohort() {
   let userOptedIn = optedIn();
   let disqualified = (Services.appinfo.multiprocessBlockPolicy != 0);
   let testGroup = (getUserSample() < TEST_THRESHOLD[updateChannel]);
 
   if (userOptedOut) {
     setCohort("optedOut");
   } else if (userOptedIn) {
     setCohort("optedIn");
-  } else if (disqualified) {
-    setCohort("disqualified");
   } else if (testGroup) {
-    setCohort("test");
+    setCohort(disqualified ? "disqualified-test" : "test");
     Preferences.set(PREF_TOGGLE_E10S, true);
   } else {
-    setCohort("control");
+    setCohort(disqualified ? "disqualified-control" : "control");
     Preferences.reset(PREF_TOGGLE_E10S);
   }
 }
 
 function shutdown(data, reason) {
 }
 
 function uninstall() {
--- a/browser/modules/DirectoryLinksProvider.jsm
+++ b/browser/modules/DirectoryLinksProvider.jsm
@@ -638,18 +638,20 @@ var DirectoryLinksProvider = {
       this._clearCampaignTimeout();
       this._avoidInadjacentSites = false;
 
       // Only check base domain for images when using the default pref
       let checkBase = !this.__linksURLModified;
       let validityFilter = function(link) {
         // Make sure the link url is allowed and images too if they exist
         return this.isURLAllowed(link.url, ALLOWED_LINK_SCHEMES, false) &&
-               this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES, checkBase) &&
-               this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES, checkBase);
+               (!link.imageURI ||
+                this.isURLAllowed(link.imageURI, ALLOWED_IMAGE_SCHEMES, checkBase)) &&
+               (!link.enhancedImageURI ||
+                this.isURLAllowed(link.enhancedImageURI, ALLOWED_IMAGE_SCHEMES, checkBase));
       }.bind(this);
 
       rawLinks.suggested.filter(validityFilter).forEach((link, position) => {
         // Suggested sites must have an adgroup name.
         if (!link.adgroup_name) {
           return;
         }
 
--- a/browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm
+++ b/browser/tools/mozscreenshots/mozscreenshots/extension/Screenshot.jsm
@@ -35,17 +35,17 @@ let Screenshot = {
   _screenshotFunction: null,
 
   init(path, extensionPath, imagePrefix = "") {
     this._path = path;
 
     let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
     dir.initWithPath(this._path);
     if (!dir.exists()) {
-      dir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
+      dir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
     }
 
     this._extensionPath = extensionPath;
     this._imagePrefix = imagePrefix;
     switch (Services.appinfo.OS) {
       case "WINNT":
         this._screenshotFunction = this._screenshotWindows;
         break;
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -1,51 +1,22 @@
 dnl This Source Code Form is subject to the terms of the Mozilla Public
 dnl License, v. 2.0. If a copy of the MPL was not distributed with this
 dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 AC_DEFUN([MOZ_TOOL_VARIABLES],
 [
 GNU_AS=
 GNU_LD=
+
 GNU_CC=
 GNU_CXX=
-CC_VERSION='N/A'
-CXX_VERSION='N/A'
-cat <<EOF > conftest.c
-#if defined(_MSC_VER)
-#if defined(__clang__)
-COMPILER clang-cl _MSC_VER
-#else
-COMPILER msvc _MSC_FULL_VER
-#endif
-#elif defined(__clang__)
-COMPILER clang __clang_major__.__clang_minor__.__clang_patchlevel__
-#elif defined(__GNUC__)
-COMPILER gcc __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
-#endif
-EOF
-read dummy compiler CC_VERSION <<EOF
-$($CC -E $CPPFLAGS $CFLAGS conftest.c 2>/dev/null | grep COMPILER)
-EOF
-read dummy cxxcompiler CXX_VERSION <<EOF
-$($CXX -E $CPPFLAGS $CXXFLAGS conftest.c 2>/dev/null | grep COMPILER)
-EOF
-if test "$compiler" != "$cxxcompiler"; then
-    AC_MSG_ERROR([Your C and C++ compilers are different.  You need to use the same compiler.])
-fi
-if test "$CC_VERSION" != "$CXX_VERSION"; then
-    # This may not be strictly necessary, but if we want to drop it, we
-    # should make sure any version checks below apply to both the C and
-    # C++ compiler versions.
-    AC_MSG_ERROR([Your C and C++ compiler versions are different.  You need to use the same compiler version.])
-fi
-CC_VERSION=`echo "$CC_VERSION" | sed 's/ //g'`
-CXX_VERSION=`echo "$CXX_VERSION" | sed 's/ //g'`
-if test "$compiler" = "gcc"; then
+dnl moz.configure ensures that the compilers have the same version
+CXX_VERSION=$CC_VERSION
+if test "$CC_TYPE" = "gcc"; then
     GNU_CC=1
     GNU_CXX=1
     changequote(<<,>>)
     GCC_VERSION_FULL="$CXX_VERSION"
     GCC_VERSION=`echo "$GCC_VERSION_FULL" | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/<<$>>1/;'`
 
     GCC_MAJOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>1 }'`
     GCC_MINOR_VERSION=`echo ${GCC_VERSION} | $AWK -F\. '{ print <<$>>2 }'`
@@ -55,32 +26,32 @@ fi
 if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_AS=1
 fi
 rm -f conftest.out
 if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_LD=1
 fi
 
-if test "$compiler" = "msvc"; then
+if test "$CC_TYPE" = "msvc"; then
      MSVC_VERSION_FULL="$CXX_VERSION"
      CC_VERSION=`echo ${CC_VERSION} | cut -c 1-4`
      CXX_VERSION=`echo ${CXX_VERSION} | cut -c 1-4`
 fi
 
 CLANG_CC=
 CLANG_CXX=
 CLANG_CL=
-if test "$compiler" = "clang"; then
+if test "$CC_TYPE" = "clang"; then
     GNU_CC=1
     GNU_CXX=1
     CLANG_CC=1
     CLANG_CXX=1
 fi
-if test "$compiler" = "clang-cl"; then
+if test "$CC_TYPE" = "clang-cl"; then
     CLANG_CL=1
     # We force clang-cl to emulate Visual C++ 2013 in configure.in, but that
     # is based on the CLANG_CL variable defined here, so make sure that we're
     # getting the right version here manually.
     CC_VERSION=1800
     CXX_VERSION=1800
     MSVC_VERSION_FULL=180030723
     # Build on clang-cl with MSVC 2013 Update 3 with fallback emulation.
@@ -108,29 +79,16 @@ fi
 AC_DEFUN([MOZ_CROSS_COMPILER],
 [
 echo "cross compiling from $host to $target"
 
 _SAVE_CC="$CC"
 _SAVE_CFLAGS="$CFLAGS"
 _SAVE_LDFLAGS="$LDFLAGS"
 
-AC_MSG_CHECKING([for host c compiler])
-AC_CHECK_PROGS(HOST_CC, cc gcc clang cl, "")
-if test -z "$HOST_CC"; then
-    AC_MSG_ERROR([no acceptable c compiler found in \$PATH])
-fi
-AC_MSG_RESULT([$HOST_CC])
-AC_MSG_CHECKING([for host c++ compiler])
-AC_CHECK_PROGS(HOST_CXX, c++ g++ clang++ cl, "")
-if test -z "$HOST_CXX"; then
-    AC_MSG_ERROR([no acceptable c++ compiler found in \$PATH])
-fi
-AC_MSG_RESULT([$HOST_CXX])
-
 if test -z "$HOST_AR_FLAGS"; then
     HOST_AR_FLAGS="$AR_FLAGS"
 fi
 AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :)
 AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :)
 CC="$HOST_CC"
 CFLAGS="$HOST_CFLAGS"
 LDFLAGS="$HOST_LDFLAGS"
@@ -157,21 +115,17 @@ dnl absolute paths would. Trick AC_CHECK
 dnl adding / to PATH. This is temporary until this moves to moz.configure
 dnl (soon).
 _SAVE_PATH=$PATH
 case "${TOOLCHAIN_PREFIX}" in
 /*)
     PATH="/:$PATH"
     ;;
 esac
-AC_CHECK_PROGS(CC, "${TOOLCHAIN_PREFIX}gcc", :)
-unset ac_cv_prog_CC
 AC_PROG_CC
-AC_CHECK_PROGS(CXX, "${TOOLCHAIN_PREFIX}g++", :)
-unset ac_cv_prog_CXX
 AC_PROG_CXX
 
 AC_CHECK_PROGS(RANLIB, "${TOOLCHAIN_PREFIX}ranlib", :)
 AC_CHECK_PROGS(AR, "${TOOLCHAIN_PREFIX}ar", :)
 AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :)
 AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :)
 AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :)
 AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :)
@@ -225,71 +179,47 @@ if test "$GNU_CXX"; then
       MOZ_NEEDS_LIBATOMIC=1
     else
       MOZ_NEEDS_LIBATOMIC=
     fi
     AC_SUBST(MOZ_NEEDS_LIBATOMIC)
 fi
 
 if test -n "$CROSS_COMPILE"; then
-    dnl When cross compile, we have no variable telling us what the host compiler is. Figure it out.
-    cat > conftest.C <<EOF
-#if defined(__clang__)
-COMPILER CLANG __clang_major__.__clang_minor__.__clang_patchlevel__
-#elif defined(__GNUC__)
-COMPILER GCC __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
-#endif
-EOF
-read dummy host_compiler HOST_CC_VERSION <<EOF
-$($HOST_CC -E conftest.C 2>/dev/null | grep COMPILER)
-EOF
-read dummy host_cxxcompiler HOST_CXX_VERSION <<EOF
-$($HOST_CXX -E conftest.C 2>/dev/null | grep COMPILER)
-EOF
-    rm conftest.C
-    if test "$host_compiler" != "$host_cxxcompiler"; then
-        AC_MSG_ERROR([Your C and C++ host compilers are different.  You need to use the same compiler.])
-    fi
-    if test "$HOST_CC_VERSION" != "$HOST_CXX_VERSION"; then
-        # This may not be strictly necessary, but if we want to drop it,
-        # we should make sure any version checks below apply to both the
-        # C and C++ compiler versions.
-        AC_MSG_ERROR([Your C and C++ host compiler versions are different.  You need to use the same compiler version.])
+    dnl moz.configure ensures that the compilers have the same version
+    HOST_CXX_VERSION=$HOST_CC_VERSION
+    if test "$HOST_CC_TYPE" = "gcc" ; then
+	changequote(<<,>>)
+	HOST_GCC_VERSION_FULL="$HOST_CXX_VERSION"
+	HOST_GCC_VERSION=`echo "$HOST_GCC_VERSION_FULL" | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/<<$>>1/;'`
+
+	HOST_GCC_MAJOR_VERSION=`echo ${HOST_GCC_VERSION} | $AWK -F\. '{ print <<$>>1 }'`
+	HOST_GCC_MINOR_VERSION=`echo ${HOST_GCC_VERSION} | $AWK -F\. '{ print <<$>>2 }'`
+	changequote([,])
+
+	if test "$HOST_GCC_MAJOR_VERSION" -eq 4 -a "$HOST_GCC_MINOR_VERSION" -lt 8 ||
+	   test "$HOST_GCC_MAJOR_VERSION" -lt 4; then
+	    AC_MSG_ERROR([Only GCC 4.8 or newer supported for host compiler])
+	fi
     fi
-    if test -n "$host_compiler"; then
-        if test "$host_compiler" = "GCC" ; then
-            changequote(<<,>>)
-            HOST_GCC_VERSION_FULL="$HOST_CXX_VERSION"
-            HOST_GCC_VERSION=`echo "$HOST_GCC_VERSION_FULL" | $PERL -pe '(split(/\./))[0]>=4&&s/(^\d*\.\d*).*/<<$>>1/;'`
 
-            HOST_GCC_MAJOR_VERSION=`echo ${HOST_GCC_VERSION} | $AWK -F\. '{ print <<$>>1 }'`
-            HOST_GCC_MINOR_VERSION=`echo ${HOST_GCC_VERSION} | $AWK -F\. '{ print <<$>>2 }'`
-            changequote([,])
-
-            if test "$HOST_GCC_MAJOR_VERSION" -eq 4 -a "$HOST_GCC_MINOR_VERSION" -lt 8 ||
-               test "$HOST_GCC_MAJOR_VERSION" -lt 4; then
-                AC_MSG_ERROR([Only GCC 4.8 or newer supported for host compiler])
-            fi
-        fi
+    HOST_CXXFLAGS="$HOST_CXXFLAGS -std=gnu++11"
 
-        HOST_CXXFLAGS="$HOST_CXXFLAGS -std=gnu++11"
+    _SAVE_CXXFLAGS="$CXXFLAGS"
+    _SAVE_CPPFLAGS="$CPPFLAGS"
+    _SAVE_CXX="$CXX"
+    CXXFLAGS="$HOST_CXXFLAGS"
+    CPPFLAGS="$HOST_CPPFLAGS"
+    CXX="$HOST_CXX"
+    if test "$HOST_CC_TYPE" = clang; then
+	AC_TRY_COMPILE([], [#if !__cpp_static_assert
+			    #error ISO WG21 SG10 feature test macros unsupported
+			    #endif],,AC_MSG_ERROR([Only clang/llvm 3.4 or newer supported]))
+    fi
 
-        _SAVE_CXXFLAGS="$CXXFLAGS"
-        _SAVE_CPPFLAGS="$CPPFLAGS"
-        _SAVE_CXX="$CXX"
-        CXXFLAGS="$HOST_CXXFLAGS"
-        CPPFLAGS="$HOST_CPPFLAGS"
-        CXX="$HOST_CXX"
-        if test "$host_compiler" = CLANG; then
-            AC_TRY_COMPILE([], [#if !__cpp_static_assert
-                                #error ISO WG21 SG10 feature test macros unsupported
-                                #endif],,AC_MSG_ERROR([Only clang/llvm 3.4 or newer supported]))
-        fi
-
-        CXXFLAGS="$_SAVE_CXXFLAGS"
-        CPPFLAGS="$_SAVE_CPPFLAGS"
-        CXX="$_SAVE_CXX"
-    fi
+    CXXFLAGS="$_SAVE_CXXFLAGS"
+    CPPFLAGS="$_SAVE_CPPFLAGS"
+    CXX="$_SAVE_CXX"
 elif test "$GNU_CXX"; then
     HOST_CXXFLAGS="$HOST_CXXFLAGS $_ADDED_CXXFLAGS"
 fi
 AC_LANG_C
 ])
deleted file mode 100644
--- a/build/autoconf/wrapper.m4
+++ /dev/null
@@ -1,25 +0,0 @@
-dnl This Source Code Form is subject to the terms of the Mozilla Public
-dnl License, v. 2.0. If a copy of the MPL was not distributed with this
-dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-dnl =======================================================================
-dnl = Enable compiling with various compiler wrappers (distcc, ccache, etc)
-dnl =======================================================================
-AC_DEFUN([MOZ_CHECK_COMPILER_WRAPPER],
-[
-if test -n "$COMPILER_WRAPPER"; then
-    case "$CC" in
-    $COMPILER_WRAPPER\ *)
-        :
-        ;;
-    *)
-        CC="$COMPILER_WRAPPER $CC"
-        CXX="$COMPILER_WRAPPER $CXX"
-        _SUBDIR_CC="$CC"
-        _SUBDIR_CXX="$CXX"
-        ac_cv_prog_CC="$CC"
-        ac_cv_prog_CXX="$CXX"
-        ;;
-    esac
-fi
-])
--- a/build/build-clang/build-clang.py
+++ b/build/build-clang/build-clang.py
@@ -110,30 +110,31 @@ def mkdir_p(path):
     try:
         os.makedirs(path)
     except OSError as e:
         if e.errno != errno.EEXIST or not os.path.isdir(path):
             raise
 
 
 def install_libgcc(gcc_dir, clang_dir):
-        libgcc_dir = glob.glob(os.path.join(gcc_dir, "lib", "gcc",
-                                            "x86_64-*linux-gnu",
-                                            "[0-9]*"))[0]
-        clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
-                                     "x86_64-unknown-linux-gnu",
-                                     os.path.basename(libgcc_dir))
-        mkdir_p(clang_lib_dir)
-        copy_dir_contents(libgcc_dir, clang_lib_dir)
-        libgcc_dir = os.path.join(gcc_dir, "lib64")
-        clang_lib_dir = os.path.join(clang_dir, "lib")
-        copy_dir_contents(libgcc_dir, clang_lib_dir)
-        include_dir = os.path.join(gcc_dir, "include")
-        clang_include_dir = os.path.join(clang_dir, "include")
-        copy_dir_contents(include_dir, clang_include_dir)
+    out = subprocess.check_output([os.path.join(gcc_dir, "bin", "gcc"),
+                                   '-print-libgcc-file-name'])
+
+    libgcc_dir = os.path.dirname(out.rstrip())
+    clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
+                                 "x86_64-unknown-linux-gnu",
+                                 os.path.basename(libgcc_dir))
+    mkdir_p(clang_lib_dir)
+    copy_dir_contents(libgcc_dir, clang_lib_dir)
+    libgcc_dir = os.path.join(gcc_dir, "lib64")
+    clang_lib_dir = os.path.join(clang_dir, "lib")
+    copy_dir_contents(libgcc_dir, clang_lib_dir)
+    include_dir = os.path.join(gcc_dir, "include")
+    clang_include_dir = os.path.join(clang_dir, "include")
+    copy_dir_contents(include_dir, clang_include_dir)
 
 
 def svn_co(source_dir, url, directory, revision):
     run_in(source_dir, ["svn", "co", "-r", revision, url, directory])
 
 
 def svn_update(directory, revision):
     run_in(directory, ["svn", "update", "-r", revision])
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -249,94 +249,44 @@ def early_options():
             option.env
             for option in __sandbox__._options.itervalues()
             if option.env
         )
     return early_options
 
 early_options = early_options()
 
-# At the moment, moz.configure doesn't have complete knowledge of all the
-# supported options in mozconfig because of all that is still in old.configure.
-# But we don't know all the options that moz.configure knows about until all
-# moz.configure files are executed, so we keep a manual list here, that is
-# checked in old.configure (we'll assume it's the last moz.configure file
-# processed). This is tedious but necessary for now.
-@depends('--help')
-def wanted_mozconfig_variables(help):
-     return set([
-         'AUTOCONF',
-         'AWK',
-         'CCACHE',
-         'COMPILER_WRAPPER',
-         'DISABLE_EXPORT_JS',
-         'DISABLE_SHARED_JS',
-         'DOXYGEN',
-         'DSYMUTIL',
-         'EXTERNAL_SOURCE_DIR',
-         'GENISOIMAGE',
-         'GRADLE',
-         'GRADLE_FLAGS',
-         'GRADLE_MAVEN_REPOSITORY',
-         'JS_STANDALONE',
-         'L10NBASEDIR',
-         'MOZILLABUILD',
-         'MOZ_ARTIFACT_BUILDS',
-         'MOZ_BUILD_APP',
-         'MOZ_BUILD_MOBILE_ANDROID_WITH_GRADLE',
-         'MOZ_CALLGRIND',
-         'MOZ_DMD',
-         'MOZ_FMP4',
-         'MOZ_INSTRUMENT_EVENT_LOOP',
-         'MOZ_INSTRUMENTS',
-         'MOZ_JPROF',
-         'MOZ_PROFILING',
-         'MOZ_USE_SYSTRACE',
-         'MOZ_VTUNE',
-         'MOZTTDIR',
-         'PERL',
-         'RPMBUILD',
-         'TAR',
-         'TOOLCHAIN_PREFIX',
-         'UNZIP',
-         'USE_FC_FREETYPE',
-         'WITHOUT_X',
-         'XARGS',
-         'YASM',
-         'ZIP',
-     ])
-
-
-@depends(mozconfig, wanted_mozconfig_variables, '--help')
+@depends(mozconfig, '--help')
 # This gives access to the sandbox. Don't copy this blindly.
 @imports('__sandbox__')
-def mozconfig_options(mozconfig, wanted_mozconfig_variables, help):
+def mozconfig_options(mozconfig, help):
     if mozconfig['path']:
         helper = __sandbox__._helper
         log.info('Adding configure options from %s' % mozconfig['path'])
         for arg in mozconfig['configure_args']:
             log.info('  %s' % arg)
             # We could be using imply_option() here, but it has other
             # contraints that don't really apply to the command-line
             # emulation that mozconfig provides.
             helper.add(arg, origin='mozconfig', args=helper._args)
 
         def add(key, value):
-            # See comment above wanted_mozconfig_variables
-            if key in wanted_mozconfig_variables:
+            if key.isupper():
                 arg = '%s=%s' % (key, value)
                 log.info('  %s' % arg)
                 helper.add(arg, origin='mozconfig', args=helper._args)
 
         for key, value in mozconfig['env']['added'].iteritems():
             add(key, value)
         for key, (_, value) in mozconfig['env']['modified'].iteritems():
             add(key, value)
         for key, value in mozconfig['vars']['added'].iteritems():
-            add(key, value)
+            # mozconfig_loader adds _IS_SET variables that are irrelevant
+            if not key.endswith('_IS_SET'):
+                add(key, value)
         for key, (_, value) in mozconfig['vars']['modified'].iteritems():
             add(key, value)
 
 
 # Mozilla-Build
 # ==============================================================
 option(env='MOZILLABUILD', nargs=1,
        help='Path to Mozilla Build (Windows-only)')
@@ -722,16 +672,52 @@ set_define('NIGHTLY_BUILD', delayed_geta
 add_old_configure_assignment('NIGHTLY_BUILD',
                              delayed_getattr(milestone, 'is_nightly'))
 set_config('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 set_define('RELEASE_BUILD', delayed_getattr(milestone, 'is_release'))
 add_old_configure_assignment('RELEASE_BUILD',
                              delayed_getattr(milestone, 'is_release'))
 
 
+# Set the MOZ_CONFIGURE_OPTIONS variable with all the options that
+# were passed somehow (environment, command line, mozconfig)
+@depends(mozconfig_options)
+@imports(_from='mozbuild.shellutil', _import='quote')
+@imports('__sandbox__')
+def all_configure_options(_):
+    result = []
+    previous = None
+    for option in __sandbox__._options.itervalues():
+        # __sandbox__._options contains items for both option.name and
+        # option.env. But it's also an OrderedDict, meaning both are
+        # consecutive.
+        # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not
+        # interesting.
+        if option == previous or option.env in ('OLD_CONFIGURE', 'MOZCONFIG'):
+            continue
+        previous = option
+        value = __sandbox__._value_for(option)
+        # We only want options that were explicitly given on the command
+        # line, the environment, or mozconfig, and that differ from the
+        # defaults.
+        if (value.origin not in ('default', 'implied') and
+                value != option.default):
+            result.append(__sandbox__._raw_options[option])
+        # We however always include options that are sent to old configure
+        # because we don't know their actual defaults. (Keep the conditions
+        # separate for ease of understanding and ease of removal)
+        elif (option.help == 'Help missing for old configure options' and
+                option in __sandbox__._raw_options):
+            result.append(__sandbox__._raw_options[option])
+
+    return quote(*result)
+
+set_config('MOZ_CONFIGURE_OPTIONS', all_configure_options)
+
+
 # This is temporary until js/src/configure and configure are merged.
 # Use instead of option() in js/moz.configure and more generally, for
 # options that are shared between configure and js/src/configure.
 @template
 def js_option(*args, **kwargs):
     opt = option(*args, **kwargs)
 
     @depends(opt.option, build_project)
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/memory.configure
@@ -0,0 +1,97 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+
+option(env='MOZ_JEMALLOC4', help='Enable jemalloc 4')
+imply_option('--enable-jemalloc', depends_if('MOZ_JEMALLOC4')(lambda x: '4'))
+
+
+option('--enable-jemalloc', nargs='?', choices=('4',), env='MOZ_MEMORY',
+       help='Replace memory allocator with jemalloc')
+
+@depends('--enable-jemalloc', target, build_project, c_compiler)
+def jemalloc(value, target, build_project, c_compiler):
+    if value.origin != 'default':
+        return bool(value) or None
+
+    if build_project == 'js':
+        return True
+
+    if target.kernel == 'Darwin' and target.cpu == 'x86_64':
+        # Don't enable by default on 32-bits OSX. See bug 702250.
+        return True
+
+    if target.kernel == 'WINNT' and c_compiler.type in ('msvc', 'clang-cl'):
+        return True
+
+    if target.kernel == 'Linux':
+        return True
+
+@depends('--enable-jemalloc')
+def jemalloc4(jemalloc):
+    if len(jemalloc) and jemalloc[0] == '4':
+        return True
+
+
+set_config('MOZ_MEMORY', jemalloc)
+set_define('MOZ_MEMORY', jemalloc)
+add_old_configure_assignment('MOZ_MEMORY', jemalloc)
+
+set_config('MOZ_JEMALLOC4', jemalloc4)
+set_define('MOZ_JEMALLOC4', jemalloc4)
+add_old_configure_assignment('MOZ_JEMALLOC4', jemalloc4)
+
+
+# Because --enable-jemalloc doesn't use a default because of the dependency
+# on the target, we can't use a js_option for it to propagate to js/src
+# through the old-configure.
+@depends(jemalloc, jemalloc4)
+def jemalloc_for_old_configure(jemalloc, jemalloc4):
+    if jemalloc:
+        return '--enable-jemalloc=4' if jemalloc4 else '--enable-jemalloc'
+    return '--disable-jemalloc'
+
+add_old_configure_arg(jemalloc_for_old_configure)
+
+
+@depends(jemalloc, jemalloc4, target)
+def jemalloc_os_define(jemalloc, jemalloc4, target):
+    if jemalloc and not jemalloc4:
+        if target.kernel == 'WINNT':
+            return 'MOZ_MEMORY_WINDOWS'
+        if target.kernel == 'Linux':
+            return 'MOZ_MEMORY_LINUX'
+        if target.kernel == 'Darwin':
+            return 'MOZ_MEMORY_DARWIN'
+        if target.kernel in ('kFreeBSD', 'FreeBSD', 'NetBSD'):
+            return 'MOZ_MEMORY_BSD'
+        die('--enable-jemalloc is not supported on %s', target.kernel)
+
+set_define(jemalloc_os_define, '1')
+
+@depends(jemalloc, jemalloc4, target)
+def jemalloc_os_define_android(jemalloc, jemalloc4, target):
+    if jemalloc and not jemalloc4 and target.os == 'Android':
+        return 'MOZ_MEMORY_ANDROID'
+
+set_define(jemalloc_os_define_android, '1')
+
+
+option('--enable-replace-malloc',
+       help='Enable ability to dynamically replace the malloc implementation')
+
+@depends('--enable-replace-malloc', jemalloc, milestone, build_project)
+def replace_malloc(value, jemalloc, milestone, build_project):
+    # Enable on central for the debugging opportunities it adds.
+    if milestone.is_nightly and jemalloc and build_project != 'js':
+        return True
+    if value and not jemalloc:
+        die('--enable-replace-malloc requires --enable-jemalloc')
+    return bool(value) or None
+
+set_config('MOZ_REPLACE_MALLOC', replace_malloc)
+set_define('MOZ_REPLACE_MALLOC', replace_malloc)
+add_old_configure_assignment('MOZ_REPLACE_MALLOC', replace_malloc)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -53,36 +53,16 @@ def autoconf(mozconfig, autoconf):
     if not os.path.exists(autoconf):
         die('Could not find autoconf 2.13 at %s', autoconf)
 
     return autoconf
 
 set_config('AUTOCONF', autoconf)
 
 
-# See comment in mozconfig_options() from build/moz.configure/init.configure
-@template
-# This gives access to the sandbox. Don't copy this blindly.
-@imports('__sandbox__')
-def check_mozconfig_variables():
-    # This escapes the sandbox. Don't copy this. This is only here because it
-    # is a one off until old-configure is gone.
-    all_options = __sandbox__._options.itervalues()
-
-    @depends(early_options, wanted_mozconfig_variables)
-    def check_mozconfig_variables(early_options, wanted_mozconfig_variables):
-        for option in all_options:
-            if (option.env and option.env not in early_options and
-                    option.env not in wanted_mozconfig_variables):
-                die('You need to add `%s` to the `wanted_mozconfig_variables` '
-                    'list in build/moz.configure/init.configure.', option.env)
-
-check_mozconfig_variables()
-
-
 @depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell,
          old_configure_assignments, build_project)
 @imports(_from='__builtin__', _import='open')
 @imports(_from='__builtin__', _import='print')
 @imports('glob')
 @imports('itertools')
 @imports('subprocess')
 # Import getmtime without overwriting the sandbox os.path.
@@ -181,17 +161,16 @@ def old_configure_options(*options):
     '--enable-android-apz',
     '--enable-android-omx',
     '--enable-android-resource-constrained',
     '--enable-approximate-location',
     '--enable-b2g-bt',
     '--enable-b2g-camera',
     '--enable-b2g-ril',
     '--enable-bundled-fonts',
-    '--enable-chrome-format',
     '--enable-clang-plugin',
     '--enable-content-sandbox',
     '--enable-cookies',
     '--enable-cpp-rtti',
     '--enable-crashreporter',
     '--enable-ctypes',
     '--enable-dbm',
     '--enable-dbus',
@@ -214,17 +193,16 @@ def old_configure_options(*options):
     '--enable-gold',
     '--enable-gps-debug',
     '--enable-hardware-aec-ns',
     '--enable-icf',
     '--enable-install-strip',
     '--enable-ion',
     '--enable-ios-target',
     '--enable-ipdl-tests',
-    '--enable-jemalloc',
     '--enable-jitspew',
     '--enable-libjpeg-turbo',
     '--enable-libproxy',
     '--enable-llvm-hacks',
     '--enable-logrefcnt',
     '--enable-macos-target',
     '--enable-maintenance-service',
     '--enable-media-navigator',
@@ -249,17 +227,16 @@ def old_configure_options(*options):
     '--enable-posix-nspr-emulation',
     '--enable-pref-extensions',
     '--enable-printing',
     '--enable-pulseaudio',
     '--enable-raw',
     '--enable-readline',
     '--enable-reflow-perf',
     '--enable-release',
-    '--enable-replace-malloc',
     '--enable-require-all-d3dc-versions',
     '--enable-rust',
     '--enable-safe-browsing',
     '--enable-sandbox',
     '--enable-signmar',
     '--enable-simulator',
     '--enable-skia',
     '--enable-skia-gpu',
@@ -462,8 +439,30 @@ def post_old_configure(raw_config):
         set_old_configure_config(
             k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v)
 
     for k, v in dict(raw_config['defines']).iteritems():
         set_old_configure_define(k[1:-1], v[1:-1])
 
     set_old_configure_config('non_global_defines',
                              raw_config['non_global_defines'])
+
+
+# Assuming no other option is declared after this function, handle the
+# env options that were injected by mozconfig_options by creating dummy
+# Option instances and having the sandbox's CommandLineHelper handle
+# them. We only do so for options that haven't been declared so far,
+# which should be a proxy for the options that old-configure handles
+# and that we don't know anything about.
+@depends('--help')
+@imports('__sandbox__')
+@imports(_from='mozbuild.configure.options', _import='Option')
+def remaining_mozconfig_options(_):
+    helper = __sandbox__._helper
+    for arg in helper:
+        if helper._origins[arg] != 'mozconfig':
+            continue
+        name = arg.split('=', 1)[0]
+        if name.isupper() and name not in __sandbox__._options:
+            option = Option(env=name, nargs='*', help=name)
+            helper.handle(option)
+
+# Please do not add anything after remaining_mozconfig_options()
--- a/build/moz.configure/toolchain.configure
+++ b/build/moz.configure/toolchain.configure
@@ -67,21 +67,25 @@ def android_ndk_include(compile_env, bui
     if compile_env and build_project in ('mobile/android', 'js'):
         return 'android-ndk.configure'
 
 include(android_ndk_include)
 
 
 # Compiler wrappers
 # ==============================================================
-js_option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
-          help='Enable compiling with wrappers such as distcc and ccache')
+# Normally, we'd use js_option and automatically have those variables
+# propagated to js/src, but things are complicated by possible additional
+# wrappers in CC/CXX, and by other subconfigures that do not handle those
+# options and do need CC/CXX altered.
+option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1,
+       help='Enable compiling with wrappers such as distcc and ccache')
 
-js_option('--with-ccache', env='CCACHE', nargs='?',
-          help='Enable compiling with ccache')
+option('--with-ccache', env='CCACHE', nargs='?',
+       help='Enable compiling with ccache')
 
 @depends_if('--with-ccache')
 def ccache(value):
     if len(value):
         return value
     # If --with-ccache was given without an explicit value, we default to
     # 'ccache'.
     return 'ccache'
@@ -92,23 +96,32 @@ ccache = check_prog('CCACHE', progs=(), 
 def using_ccache(ccache):
     return True
 
 set_config('MOZ_USING_CCACHE', using_ccache)
 
 @depends('--with-compiler-wrapper', ccache)
 @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
 def compiler_wrapper(wrapper, ccache):
+    if wrapper:
+        raw_wrapper = wrapper[0]
+        wrapper = shell_split(raw_wrapper)
+        wrapper_program = find_program(wrapper[0])
+        if not wrapper_program:
+            die('Cannot find `%s` from the given compiler wrapper `%s`',
+                wrapper[0], raw_wrapper)
+        wrapper[0] = wrapper_program
+
     if ccache:
         if wrapper:
-            return tuple([ccache] + shell_split(wrapper[0]))
+            return tuple([ccache] + wrapper)
         else:
             return (ccache,)
     elif wrapper:
-        return tuple(shell_split(wrapper[0]))
+        return tuple(wrapper)
 
 add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper)
 
 @depends_if(compiler_wrapper)
 def using_compiler_wrapper(compiler_wrapper):
     return True
 
 set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper)
@@ -126,8 +139,285 @@ def toolchain_prefix(value, target, host
     # Special case x86-64 <-> x86 cross compiling until we have the right tests
     # in moz.configure land.
     if cross_compiling and not all(i.cpu in ('x86_64', 'x86')
                                    for i in (target, host)):
         return '%s-' % target.toolchain
 
 set_config('TOOLCHAIN_PREFIX', toolchain_prefix)
 add_old_configure_assignment('TOOLCHAIN_PREFIX', toolchain_prefix)
+
+
+# Compilers
+# ==============================================================
+@imports('os')
+@imports('subprocess')
+@imports(_from='mozbuild.configure.util', _import='LineIO')
+@imports(_from='mozbuild.shellutil', _import='quote')
+@imports(_from='tempfile', _import='mkstemp')
+@imports(_from='textwrap', _import='dedent')
+def check_compiler(compiler, language):
+    check = dedent('''\
+        #if defined(_MSC_VER)
+        #if defined(__clang__)
+        COMPILER clang-cl _MSC_VER
+        #else
+        COMPILER msvc _MSC_FULL_VER
+        #endif
+        #elif defined(__clang__)
+        COMPILER clang __clang_major__.__clang_minor__.__clang_patchlevel__
+        #elif defined(__GNUC__)
+        COMPILER gcc __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__
+        #endif
+    ''')
+
+    suffix = {
+        'C': '.c',
+        'C++': '.cpp',
+    }[language]
+
+    fd, path = mkstemp(prefix='conftest.', suffix=suffix)
+    try:
+        source = check.encode('ascii', 'replace')
+
+        log.debug('Creating `%s` with content:', path)
+        with LineIO(lambda l: log.debug('| %s', l)) as out:
+            out.write(source)
+
+        os.write(fd, source)
+        os.close(fd)
+
+        cmd = compiler + ['-E', path]
+        log.debug('Executing: `%s`', quote(*cmd))
+        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        stdout, stderr = proc.communicate()
+        retcode = proc.wait()
+        if retcode == 0:
+            for line in stdout.splitlines():
+                if line.startswith('COMPILER '):
+                    _, type, version = line.split(None, 2)
+                    version = version.replace(' ', '')
+                    return type, version
+            return
+
+        log.debug('The command returned non-zero exit status %d.', retcode)
+        for out, desc in ((stdout, 'output'), (stderr, 'error output')):
+            if out:
+                log.debug('Its %s was:', desc)
+                with LineIO(lambda l: log.debug('| %s', l)) as o:
+                    o.write(out)
+    finally:
+        os.remove(path)
+
+
+@template
+def default_c_compilers(host_or_target):
+    '''Template defining the set of default C compilers for the host and
+    target platforms.
+    `host_or_target` is either `host` or `target` (the @depends functions
+    from init.configure.
+    '''
+    assert host_or_target in (host, target)
+
+    @depends(host_or_target, host, toolchain_prefix)
+    def default_c_compilers(host_or_target, host, toolchain_prefix):
+        if host_or_target.kernel == 'WINNT':
+            return ('cl', 'clang-cl', 'gcc', 'clang')
+        if host_or_target.kernel == 'Darwin':
+            return ('clang',)
+        if host_or_target != host:  # cross compilation
+            return ('%sgcc' % toolchain_prefix, 'gcc', 'clang')
+        return ('gcc', 'clang')
+
+    return default_c_compilers
+
+
+@template
+def default_cxx_compilers(c_compiler):
+    '''Template defining the set of default C++ compilers for the host and
+    target platforms.
+    `c_compiler` is the @depends function returning a Compiler instance for
+    the desired platform.
+
+    Because the build system expects the C and C++ compilers to be from the
+    same compiler suite, we derive the default C++ compilers from the C
+    compiler that was found if none was provided.
+    '''
+
+    @depends(c_compiler)
+    def default_cxx_compilers(c_compiler):
+        dir = os.path.dirname(c_compiler.compiler)
+        file = os.path.basename(c_compiler.compiler)
+
+        if c_compiler.type == 'gcc':
+            return (os.path.join(dir, file.replace('gcc', 'g++')),)
+
+        if c_compiler.type == 'clang':
+            return (os.path.join(dir, file.replace('clang', 'clang++')),)
+
+        return (c_compiler.compiler,)
+
+    return default_cxx_compilers
+
+
+@template
+def compiler(language, host_or_target, c_compiler=None, other_compiler=None):
+    '''Template handling the generic base checks for the compiler for the
+    given `language` on the given platform (`host_or_target`).
+    `host_or_target` is either `host` or `target` (the @depends functions
+    from init.configure.
+    When the language in 'C++', `c_compiler` is the result of the `compiler`
+    template for the language 'C' for the same `host_or_target`.
+    When `host_or_target` is `host`, `other_compiler` is the result of the
+    `compiler` template for the same `language` for `target`.
+    '''
+    assert host_or_target in (host, target)
+    assert language in ('C', 'C++')
+    assert language == 'C' or c_compiler
+    assert host_or_target == target or other_compiler
+
+    host_or_target_str = {
+        host: 'host',
+        target: 'target',
+    }[host_or_target]
+
+    var = {
+        ('C', target): 'CC',
+        ('C++', target): 'CXX',
+        ('C', host): 'HOST_CC',
+        ('C++', host): 'HOST_CXX',
+    }[language, host_or_target]
+
+    default_compilers = {
+        'C': lambda: default_c_compilers(host_or_target),
+        'C++': lambda: default_cxx_compilers(c_compiler),
+    }[language]()
+
+    what='the %s %s compiler' % (host_or_target_str, language),
+
+    option(env=var, nargs=1, help='Path to %s' % what)
+
+    # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/
+    # HOST_CXX variables.
+    @depends_if(var)
+    @imports(_from='itertools', _import='takewhile')
+    @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split')
+    def provided_compiler(cmd):
+        # Historically, the compiler variables have contained more than the
+        # path to the compiler itself. So for backwards compatibility, try to
+        # find what is what in there, assuming the first dash-prefixed item is
+        # a compiler option, the item before that is the compiler, and anything
+        # before that is a compiler wrapper.
+        cmd = shell_split(cmd[0])
+
+        without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd))
+
+        return namespace(
+            wrapper=without_flags[:-1],
+            compiler=without_flags[-1],
+            flags=cmd[len(without_flags):],
+        )
+
+    # Derive the host C compiler from the target C compiler when no explicit
+    # compiler was given and we're not cross compiling.
+    if language == 'C' and host_or_target == host:
+        @depends(provided_compiler, other_compiler, cross_compiling)
+        def provided_compiler(value, other_compiler, cross_compiling):
+            if value:
+                return value
+            if not cross_compiling:
+                return other_compiler
+
+    # Normally, we'd use `var` instead of `_var`, but the interaction with
+    # old-configure complicates things, and for now, we a) can't take the plain
+    # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let
+    # old-configure AC_SUBST it (because it's autoconf doing it, not us)
+    compiler = check_prog('_%s' % var, what=what, progs=default_compilers,
+                          input=delayed_getattr(provided_compiler, 'compiler'))
+
+    @depends(compiler, provided_compiler, compiler_wrapper)
+    @checking('%s version' % what, lambda x: x.version if x else 'not found')
+    @imports(_from='mozbuild.shellutil', _import='quote')
+    def valid_compiler(compiler, provided_compiler, compiler_wrapper):
+        wrapper = list(compiler_wrapper or ())
+        if provided_compiler:
+            provided_wrapper = list(provided_compiler.wrapper)
+            # When doing a subconfigure, the compiler is set by old-configure
+            # and it contains the wrappers from --with-compiler-wrapper and
+            # --with-ccache.
+            if provided_wrapper[:len(wrapper)] == wrapper:
+                provided_wrapper = provided_wrapper[len(wrapper):]
+            wrapper.extend(provided_wrapper)
+            flags = provided_compiler.flags
+        else:
+            flags = []
+
+        # Ideally, we'd always use the absolute path, but unfortunately, on
+        # Windows, the compiler is very often in a directory containing spaces.
+        # Unfortunately, due to the way autoconf does its compiler tests with
+        # eval, that doesn't work out. So in that case, check that the
+        # compiler can still be found in $PATH, and use the file name instead
+        # of the full path.
+        if quote(compiler) != compiler:
+            full_path = os.path.abspath(compiler)
+            compiler = os.path.basename(compiler)
+            found_compiler = find_program(compiler)
+            if not found_compiler:
+                die('%s is not in your $PATH'
+                    % quote(os.path.dirname(full_path)))
+            if os.path.normcase(find_program(compiler)) != os.path.normcase(
+                    full_path):
+                die('Found `%s` before `%s` in your $PATH. '
+                    'Please reorder your $PATH.',
+                    quote(os.path.dirname(found_compiler)),
+                    quote(os.path.dirname(full_path)))
+
+        result = check_compiler(wrapper + [compiler] + flags, language)
+        if result:
+            type, version = result
+            return namespace(
+                wrapper=wrapper,
+                compiler=compiler,
+                flags=flags,
+                type=type,
+                version=version,
+            )
+        die('Failed to get the compiler version')
+
+    if language == 'C++':
+        @depends(valid_compiler, c_compiler)
+        def compiler_suite_consistency(compiler, c_compiler):
+            if compiler.type != c_compiler.type:
+                die('The %s C compiler is %s, while the %s C++ compiler is '
+                    '%s. Need to use the same compiler suite.',
+                    host_or_target_str, c_compiler.type,
+                    host_or_target_str, compiler.type)
+
+            if compiler.version != c_compiler.version:
+                die('The %s C compiler is version %s, while the %s C++ '
+                    'compiler is version %s. Need to use the same compiler '
+                    'version.',
+                    host_or_target_str, c_compiler.version,
+                    host_or_target_str, compiler.version)
+
+    # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper
+    # and the flags that were part of the user input for those variables to
+    # be provided.
+    add_old_configure_assignment(var, depends_if(valid_compiler)(
+        lambda x: list(x.wrapper) + [x.compiler] + list(x.flags)))
+
+    # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow
+    # old-configure to do some of its still existing checks.
+    if language == 'C':
+        add_old_configure_assignment(
+            '%s_TYPE' % var, delayed_getattr(valid_compiler, 'type'))
+        add_old_configure_assignment(
+            '%s_VERSION' % var, delayed_getattr(valid_compiler, 'version'))
+
+    return valid_compiler
+
+
+c_compiler = compiler('C', target)
+cxx_compiler = compiler('C++', target, c_compiler=c_compiler)
+host_c_compiler = compiler('C', host, other_compiler=c_compiler)
+host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler,
+                             other_compiler=cxx_compiler)
deleted file mode 100644
--- a/build/win32/mozconfig.vs2010-win64
+++ /dev/null
@@ -1,31 +0,0 @@
-
-if [ -d "/c/PROGRA~2/MICROS~2.0" ]; then
-  # /c/Program Files (x86)/Microsoft Visual Studio 10.0
-  _VSPATH="/c/PROGRA~2/MICROS~2.0"
-else
-  _VSPATH="/c/tools/msvs10"
-fi
-
-## SDK redist ##
-export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x86/Microsoft.VC100.CRT
-
-## includes: win8 sdk includes, msvc 10 std library, directx sdk for d3d9 ##
-export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/um:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include
-
-## libs: win8 sdk x86 (32-bit) libs, msvc 10 (32-bit) std library, msvc 10 atl libs, directx sdk (32-bit) for d3d9  ##
-export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
-export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x86:${_VSPATH}/vc/lib:${_VSPATH}/vc/atlmfc/lib:/c/tools/sdks/dx10/lib
-
-## paths: win8 sdk x86 (32-bit) tools, msvc 10 (32-bit) build toolchain, moz tools  ##
-export PATH="/c/Program Files (x86)/Windows Kits/8.0/bin/x86:${_VSPATH}/Common7/IDE:${_VSPATH}/VC/BIN:${_VSPATH}/Common7/Tools:${_VSPATH}/VC/VCPackages:/c/mozilla-build/moztools:${PATH}"
-
-## WindowsSDKDir ##
-export WINDOWSSDKDIR="/c/Program Files (x86)/Windows Kits/8.0/"
-
-. $topsrcdir/build/mozconfig.vs-common
-
-mk_export_correct_style LIB
-mk_export_correct_style LIBPATH
-mk_export_correct_style PATH
-mk_export_correct_style INCLUDE
-mk_export_correct_style WIN32_REDIST_DIR
deleted file mode 100644
--- a/build/win64/mozconfig.vs2010
+++ /dev/null
@@ -1,40 +0,0 @@
-
-if [ -d "/c/PROGRA~2/MICROS~2.0" ]; then
-  # /c/Program Files (x86)/Microsoft Visual Studio 10.0
-  _VSPATH="/c/PROGRA~2/MICROS~2.0"
-else
-  _VSPATH="/c/tools/msvs10"
-fi
-
-## SDK redist ##
-export WIN32_REDIST_DIR=${_VSPATH}/VC/redist/x64/Microsoft.VC100.CRT
-
-## includes: win8 sdk includes, msvc 10 std library, directx sdk for d3d9 ##
-export INCLUDE=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/shared:/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/include/um:${_VSPATH}/vc/include:${_VSPATH}/vc/atlmfc/include:/c/tools/sdks/dx10/include
-
-## libs: win8 sdk x64 (64-bit) libs, msvc 10 (64-bit) std library, msvc 10 atl libs, directx sdk (64-bit) for d3d9  ##
-export LIBPATH=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x64:${_VSPATH}/vc/lib/amd64:${_VSPATH}/vc/atlmfc/lib/amd64:/c/tools/sdks/dx10/lib/x64
-export LIB=/c/Program\ Files\ \(x86\)/Windows\ Kits/8.0/Lib/win8/um/x64:${_VSPATH}/vc/lib/amd64:${_VSPATH}/vc/atlmfc/lib/amd64:/c/tools/sdks/dx10/lib/x64
-
-## paths: win8 sdk x64 (64-bit) tools, msvc 10 (64-bit) build toolchain, moz tools  ##
-export PATH="/c/Program Files (x86)/Windows Kits/8.0/bin/x64:${_VSPATH}/Common7/IDE:${_VSPATH}/VC/BIN/amd64:${_VSPATH}/VC/BIN/x86_amd64:${_VSPATH}/VC/BIN:${_VSPATH}/Common7/Tools:${_VSPATH}/VC/VCPackages:${PATH}"
-
-## WindowsSDKDir ##
-export WINDOWSSDKDIR="/c/Program Files (x86)/Windows Kits/8.0/"
-
-# Use 32bit linker for PGO crash bug.
-# https://connect.microsoft.com/VisualStudio/feedback/details/686117/
-if [ -f /c/PROGRA~2/MICROS~2.0/VC/BIN/x86_amd64/link.exe ]; then
-  # /c/Program Files (x86)/Microsoft Visual Studio 10.0
-  export LD=c:/PROGRA~2/MICROS~2.0/VC/BIN/x86_amd64/link.exe
-else
-  export LD=c:/tools/msvs10/VC/BIN/x86_amd64/link.exe
-fi
-
-. $topsrcdir/build/mozconfig.vs-common
-
-mk_export_correct_style LIB
-mk_export_correct_style LIBPATH
-mk_export_correct_style PATH
-mk_export_correct_style INCLUDE
-mk_export_correct_style WIN32_REDIST_DIR
--- a/config/config.mk
+++ b/config/config.mk
@@ -222,17 +222,17 @@ OS_LDFLAGS += $(PROFILE_USE_LDFLAGS)
 ifeq (WINNT,$(OS_ARCH))
 AR_FLAGS += -LTCG
 endif
 endif # MOZ_PROFILE_USE
 endif # NO_PROFILE_GUIDED_OPTIMIZE
 
 MAKE_JARS_FLAGS = \
 	-t $(topsrcdir) \
-	-f $(MOZ_CHROME_FILE_FORMAT) \
+	-f $(MOZ_JAR_MAKER_FILE_FORMAT) \
 	$(NULL)
 
 ifdef USE_EXTENSION_MANIFEST
 MAKE_JARS_FLAGS += -e
 endif
 
 TAR_CREATE_FLAGS = -chf
 
--- a/config/tests/src-simple/Makefile.in
+++ b/config/tests/src-simple/Makefile.in
@@ -17,17 +17,17 @@ ACDEFINES += \
 	-DAB_CD=ab-X-stuff \
 	$(NULL)
 
 MY_MANIFEST = $(if $(USE_EXTENSION_MANIFEST), $(FINAL_TARGET)/chrome.manifest, $(FINAL_TARGET)/chrome/test.manifest)
 REF_MANIFEST = $(if $(USE_EXTENSION_MANIFEST),chrome.manifest,test.manifest)
 
 check-%::
 	if test -d $(FINAL_TARGET); then rm -rf $(FINAL_TARGET); fi;
-	$(MAKE) realchrome MOZ_CHROME_FILE_FORMAT=$*
+	$(MAKE) realchrome MOZ_JAR_MAKER_FILE_FORMAT=$*
 	@echo 'Comparing manifests...'
 	@if ! sort $(MY_MANIFEST) | diff --text -U 0 $(srcdir)/../$(REF_MANIFEST).$* - ; then \
 	  echo 'TEST-UNEXPECTED-FAIL | config/tests/$(REF_MANIFEST).$* | differing content in manifest!' ; \
 	  false; \
 	fi
 	@if [ $* = 'jar' ]; then \
 	  $(UNZIP) -d $(FINAL_TARGET)/chrome/test $(FINAL_TARGET)/chrome/test.jar; \
 	fi
--- a/devtools/shared/DevToolsUtils.js
+++ b/devtools/shared/DevToolsUtils.js
@@ -492,20 +492,43 @@ function mainThreadFetch(aURL, aOptions=
 
 /**
  * Opens a channel for given URL. Tries a bit harder than NetUtil.newChannel.
  *
  * @param {String} url - The URL to open a channel for.
  * @param {Object} options - The options object passed to @method fetch.
  * @return {nsIChannel} - The newly created channel. Throws on failure.
  */
-function newChannelForURL(url, { policy }) {
+function newChannelForURL(url, { policy, window }) {
+  var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
+  if (window) {
+    // Respect private browsing.
+    var req = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                    .getInterface(Ci.nsIWebNavigation)
+                    .QueryInterface(Ci.nsIDocumentLoader)
+                    .loadGroup;
+    if (req) {
+      var nc = req.notificationCallbacks;
+      if (nc) {
+        try {
+          var lc = nc.getInterface(Ci.nsILoadContext);
+          if (lc) {
+            if (lc.usePrivateBrowsing) {
+              securityFlags |= Ci.nsILoadInfo.SEC_FORCE_PRIVATE_BROWSING;
+            }
+          }
+        } catch(ex) {}
+      }
+    }
+  }
+
   let channelOptions = {
     contentPolicyType: policy,
     loadUsingSystemPrincipal: true,
+    securityFlags: securityFlags,
     uri: url
   };
 
   try {
     return NetUtil.newChannel(channelOptions);
   } catch (e) {
     // In the xpcshell tests, the script url is the absolute path of the test
     // file, which will make a malformed URI error be thrown. Add the file
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10683,22 +10683,34 @@ nsDocShell::DoURILoad(nsIURI* aURI,
   nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
   if (inherit) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
   if (isSandBoxed) {
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
+  if (mInPrivateBrowsing) {
+    securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
+  }
+
   nsCOMPtr<nsILoadInfo> loadInfo =
     (aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT) ?
       new LoadInfo(loadingWindow, triggeringPrincipal,
                    securityFlags) :
       new LoadInfo(loadingPrincipal, triggeringPrincipal, loadingNode,
                    securityFlags, aContentPolicyType);
+
+  // We have to do this in case our OriginAttributes are different from the
+  // OriginAttributes of the parent document. Or in case there isn't a
+  // parent document.
+  NeckoOriginAttributes neckoAttrs;
+  neckoAttrs.InheritFromDocShellToNecko(GetOriginAttributes());
+  loadInfo->SetOriginAttributes(neckoAttrs);
+
   if (!isSrcdoc) {
     rv = NS_NewChannelInternal(getter_AddRefs(channel),
                                aURI,
                                loadInfo,
                                nullptr,   // loadGroup
                                static_cast<nsIInterfaceRequestor*>(this),
                                loadFlags);
 
@@ -10712,16 +10724,17 @@ nsDocShell::DoURILoad(nsIURI* aURI,
         nsresult rv2 = mContentListener->OnStartURIOpen(aURI, &abort);
         if (NS_SUCCEEDED(rv2) && abort) {
           // Hey, they're handling the load for us!  How convenient!
           return NS_OK;
         }
       }
       return rv;
     }
+
     if (aBaseURI) {
         nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(channel);
         if (vsc) {
             vsc->SetBaseURI(aBaseURI);
         }
     }
   } else {
     nsAutoCString scheme;
--- a/dom/animation/AnimationEffectTiming.h
+++ b/dom/animation/AnimationEffectTiming.h
@@ -12,18 +12,20 @@
 #include "nsStringFwd.h"
 
 namespace mozilla {
 namespace dom {
 
 class AnimationEffectTiming : public AnimationEffectTimingReadOnly
 {
 public:
-  AnimationEffectTiming(const TimingParams& aTiming, KeyframeEffect* aEffect)
-    : AnimationEffectTimingReadOnly(aTiming)
+  AnimationEffectTiming(nsISupports* aParent,
+                        const TimingParams& aTiming,
+                        KeyframeEffect* aEffect)
+    : AnimationEffectTimingReadOnly(aParent, aTiming)
     , mEffect(aEffect) { }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Unlink() override { mEffect = nullptr; }
 
   void SetDelay(double aDelay);
   void SetEndDelay(double aEndDelay);
--- a/dom/animation/AnimationEffectTimingReadOnly.h
+++ b/dom/animation/AnimationEffectTimingReadOnly.h
@@ -18,18 +18,20 @@
 
 namespace mozilla {
 namespace dom {
 
 class AnimationEffectTimingReadOnly : public nsWrapperCache
 {
 public:
   AnimationEffectTimingReadOnly() = default;
-  explicit AnimationEffectTimingReadOnly(const TimingParams& aTiming)
-    : mTiming(aTiming) { }
+  AnimationEffectTimingReadOnly(nsISupports* aParent,
+                                const TimingParams& aTiming)
+    : mParent(aParent)
+    , mTiming(aTiming) { }
 
   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(AnimationEffectTimingReadOnly)
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(AnimationEffectTimingReadOnly)
 
 protected:
   virtual ~AnimationEffectTimingReadOnly() = default;
 
 public:
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -57,17 +57,18 @@ GetComputedTimingDictionary(const Comput
   }
 }
 
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly,
                                    AnimationEffectReadOnly,
                                    mTarget,
-                                   mAnimation)
+                                   mAnimation,
+                                   mTiming)
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(KeyframeEffectReadOnly,
                                                AnimationEffectReadOnly)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(KeyframeEffectReadOnly)
 NS_INTERFACE_MAP_END_INHERITING(AnimationEffectReadOnly)
 
@@ -75,28 +76,29 @@ NS_IMPL_ADDREF_INHERITED(KeyframeEffectR
 NS_IMPL_RELEASE_INHERITED(KeyframeEffectReadOnly, AnimationEffectReadOnly)
 
 KeyframeEffectReadOnly::KeyframeEffectReadOnly(
   nsIDocument* aDocument,
   Element* aTarget,
   CSSPseudoElementType aPseudoType,
   const TimingParams& aTiming)
   : KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
-                           new AnimationEffectTimingReadOnly(aTiming))
+                           new AnimationEffectTimingReadOnly(aDocument,
+                                                             aTiming))
 {
 }
 
 KeyframeEffectReadOnly::KeyframeEffectReadOnly(
   nsIDocument* aDocument,
   Element* aTarget,
   CSSPseudoElementType aPseudoType,
   AnimationEffectTimingReadOnly* aTiming)
   : AnimationEffectReadOnly(aDocument)
   , mTarget(aTarget)
-  , mTiming(*aTiming)
+  , mTiming(aTiming)
   , mPseudoType(aPseudoType)
   , mInEffectOnLastAnimationTimingUpdate(false)
 {
   MOZ_ASSERT(aTiming);
   MOZ_ASSERT(aTarget, "null animation target is not yet supported");
 }
 
 JSObject*
@@ -1338,17 +1340,17 @@ KeyframeEffectReadOnly::SetPerformanceWa
 //
 //---------------------------------------------------------------------
 
 KeyframeEffect::KeyframeEffect(nsIDocument* aDocument,
                                Element* aTarget,
                                CSSPseudoElementType aPseudoType,
                                const TimingParams& aTiming)
   : KeyframeEffectReadOnly(aDocument, aTarget, aPseudoType,
-                           new AnimationEffectTiming(aTiming, this))
+                           new AnimationEffectTiming(aDocument, aTiming, this))
 {
 }
 
 JSObject*
 KeyframeEffect::WrapObject(JSContext* aCx,
                            JS::Handle<JSObject*> aGivenProto)
 {
   return KeyframeEffectBinding::Wrap(aCx, this, aGivenProto);
@@ -1397,13 +1399,17 @@ void KeyframeEffect::NotifySpecifiedTimi
                        EffectCompositor::RestyleType::Layer,
                        mAnimation->CascadeLevel());
     }
   }
 }
 
 KeyframeEffect::~KeyframeEffect()
 {
-  mTiming->Unlink();
+  // mTiming is cycle collected, so we have to do null check first even though
+  // mTiming shouldn't be null during the lifetime of KeyframeEffect.
+  if (mTiming) {
+    mTiming->Unlink();
+  }
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -367,17 +367,17 @@ protected:
   // As a result, we need to make sure this gets called whenever anything
   // changes with regards to this effects's timing including changes to the
   // owning Animation's timing.
   void UpdateTargetRegistration();
 
   nsCOMPtr<Element> mTarget;
   RefPtr<Animation> mAnimation;
 
-  OwningNonNull<AnimationEffectTimingReadOnly> mTiming;
+  RefPtr<AnimationEffectTimingReadOnly> mTiming;
   CSSPseudoElementType mPseudoType;
 
   // The specified keyframes.
   nsTArray<Keyframe>          mFrames;
 
   // A set of per-property value arrays, derived from |mFrames|.
   nsTArray<AnimationProperty> mProperties;
 
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -2282,16 +2282,21 @@ this.DOMApplicationRegistry = {
 
     // Try to download a new manifest.
     function doRequest(oldManifest, headers) {
       headers = headers || [];
       let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                   .createInstance(Ci.nsIXMLHttpRequest);
       xhr.open("GET", aData.manifestURL, true);
       xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+      if (xhr.channel.loadInfo) {
+        xhr.channel.loadInfo.originAttributes = { appId: app.installerAppId,
+                                                  inIsolatedMozBrowser: app.installerIsBrowser
+                                                };
+      }
       headers.forEach(function(aHeader) {
         debug("Adding header: " + aHeader.name + ": " + aHeader.value);
         xhr.setRequestHeader(aHeader.name, aHeader.value);
       });
       xhr.responseType = "json";
       if (app.etag) {
         debug("adding manifest etag:" + app.etag);
         xhr.setRequestHeader("If-None-Match", app.etag);
@@ -2591,16 +2596,20 @@ this.DOMApplicationRegistry = {
       }
       return;
     }
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                 .createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", app.manifestURL, true);
     xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+    if (xhr.channel.loadInfo) {
+      xhr.channel.loadInfo.originAttributes = { appId: aData.appId,
+                                                inIsolatedMozBrowser: aData.isBrowser};
+    }
     xhr.channel.notificationCallbacks = AppsUtils.createLoadContext(aData.appId,
                                                                     aData.isBrowser);
     xhr.responseType = "json";
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         if (!AppsUtils.checkManifestContentType(app.installOrigin, app.origin,
                                                 xhr.getResponseHeader("content-type"))) {
@@ -2706,16 +2715,20 @@ this.DOMApplicationRegistry = {
       }
       return;
     }
 
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                 .createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", app.manifestURL, true);
     xhr.channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
+    if (xhr.channel.loadInfo) {
+      xhr.channel.loadInfo.originAttributes = { appId: aData.appId,
+                                                inIsolatedMozBrowser: aData.isBrowser};
+    }
     xhr.channel.notificationCallbacks = AppsUtils.createLoadContext(aData.appId,
                                                                     aData.isBrowser);
     xhr.responseType = "json";
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         if (!AppsUtils.checkManifestContentType(app.installOrigin, app.origin,
                                                 xhr.getResponseHeader("content-type"))) {
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -64,17 +64,17 @@
 #  undef SendMessage
 # endif
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
-static const int kMinTelemetryMessageSize = 8192;
+static const size_t kMinTelemetryMessageSize = 8192;
 
 nsFrameMessageManager::nsFrameMessageManager(mozilla::dom::ipc::MessageManagerCallback* aCallback,
                                              nsFrameMessageManager* aParentManager,
                                              /* mozilla::dom::ipc::MessageManagerFlags */ uint32_t aFlags)
  : mChrome(!!(aFlags & mozilla::dom::ipc::MM_CHROME)),
    mGlobal(!!(aFlags & mozilla::dom::ipc::MM_GLOBAL)),
    mIsProcessManager(!!(aFlags & mozilla::dom::ipc::MM_PROCESSMANAGER)),
    mIsBroadcaster(!!(aFlags & mozilla::dom::ipc::MM_BROADCASTER)),
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -760,17 +760,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_document_importNode_document.html]
 [test_domparser_null_char.html]
 [test_domparsing.html]
 [test_elementTraversal.html]
 [test_element_closest.html]
 [test_encodeToStringWithMaxLength.html]
 [test_fileapi.html]
 [test_fileapi_slice.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 775227
+skip-if = toolkit == 'android' #bug 775227
 [test_getElementById.html]
 [test_html_colors_quirks.html]
 [test_html_colors_standards.html]
 [test_html_in_xhr.html]
 [test_htmlcopyencoder.html]
 [test_htmlcopyencoder.xhtml]
 [test_ipc_messagemanager_blob.html]
 [test_meta_viewport0.html]
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -527,16 +527,20 @@ TraceProtoAndIfaceCache(JSTracer* trc, J
   protoAndIfaceCache->Trace(trc);
 }
 
 inline void
 DestroyProtoAndIfaceCache(JSObject* obj)
 {
   MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
 
+  if (!HasProtoAndIfaceCache(obj)) {
+    return;
+  }
+
   ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
 
   delete protoAndIfaceCache;
 }
 
 /**
  * Add constants to an object.
  */
--- a/dom/bindings/SimpleGlobalObject.cpp
+++ b/dom/bindings/SimpleGlobalObject.cpp
@@ -36,29 +36,16 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCA
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(SimpleGlobalObject)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(SimpleGlobalObject)
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SimpleGlobalObject)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
 NS_INTERFACE_MAP_END
 
-static bool
-SimpleGlobal_enumerate(JSContext *cx, JS::Handle<JSObject *> obj)
-{
-  return JS_EnumerateStandardClasses(cx, obj);
-}
-
-static bool
-SimpleGlobal_resolve(JSContext *cx, JS::Handle<JSObject *> obj,
-                    JS::Handle<jsid> id, bool *resolvedp)
-{
-  return JS_ResolveStandardClass(cx, obj, id, resolvedp);
-}
-
 static void
 SimpleGlobal_finalize(js::FreeOp *fop, JSObject *obj)
 {
   SimpleGlobalObject* globalObject =
     static_cast<SimpleGlobalObject*>(JS_GetPrivate(obj));
   NS_RELEASE(globalObject);
 }
 
@@ -70,19 +57,19 @@ SimpleGlobal_moved(JSObject *obj, const 
   globalObject->UpdateWrapper(obj, old);
 }
 
 static const js::ClassOps SimpleGlobalClassOps = {
     nullptr,
     nullptr,
     nullptr,
     nullptr,
-    SimpleGlobal_enumerate,
-    SimpleGlobal_resolve,
-    nullptr,
+    JS_EnumerateStandardClasses,
+    JS_ResolveStandardClass,
+    JS_MayResolveStandardClass,
     SimpleGlobal_finalize,
     nullptr,
     nullptr,
     nullptr,
     JS_GlobalObjectTraceHook,
 };
 
 static const js::ClassExtension SimpleGlobalClassExtension = {
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2116,16 +2116,24 @@ CanvasRenderingContext2D::CreatePattern(
       return nullptr;
     }
 
     // Special case for Canvas, which could be an Azure canvas!
     nsICanvasRenderingContextInternal *srcCanvas = canvas->GetContextAtIndex(0);
     if (srcCanvas) {
       // This might not be an Azure canvas!
       RefPtr<SourceSurface> srcSurf = srcCanvas->GetSurfaceSnapshot();
+      if (!srcSurf) {
+        JSContext* context = nsContentUtils::GetCurrentJSContext();
+        if (context) {
+          JS_ReportWarning(context, "CanvasRenderingContext2D.createPattern() failed to snapshot source canvas.");
+        }
+        aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+        return nullptr;
+      }
 
       RefPtr<CanvasPattern> pat =
         new CanvasPattern(this, srcSurf, repeatMode, htmlElement->NodePrincipal(), canvas->IsWriteOnly(), false);
 
       return pat.forget();
     }
   } else if (aSource.IsHTMLImageElement()) {
     HTMLImageElement* img = &aSource.GetAsHTMLImageElement();
@@ -2137,16 +2145,24 @@ CanvasRenderingContext2D::CreatePattern(
     htmlElement = img;
   } else if (aSource.IsHTMLVideoElement()) {
     htmlElement = &aSource.GetAsHTMLVideoElement();
   } else {
     // Special case for ImageBitmap
     ImageBitmap& imgBitmap = aSource.GetAsImageBitmap();
     EnsureTarget();
     RefPtr<SourceSurface> srcSurf = imgBitmap.PrepareForDrawTarget(mTarget);
+    if (!srcSurf) {
+      JSContext* context = nsContentUtils::GetCurrentJSContext();
+      if (context) {
+        JS_ReportWarning(context, "CanvasRenderingContext2D.createPattern() failed to prepare source ImageBitmap.");
+      }
+      aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+      return nullptr;
+    }
 
     // An ImageBitmap never taints others so we set principalForSecurityCheck to
     // nullptr and set CORSUsed to true for passing the security check in
     // CanvasUtils::DoDrawImageSecurityCheck().
     RefPtr<CanvasPattern> pat =
       new CanvasPattern(this, srcSurf, repeatMode, nullptr, false, true);
 
     return pat.forget();
@@ -5106,61 +5122,16 @@ CanvasRenderingContext2D::AsyncDrawXULEl
     DocumentRendererParent *docrender =
       static_cast<DocumentRendererParent *>(pdocrender);
 
     docrender->SetCanvasContext(this, mThebes);
   }
 #endif
 }
 
-void
-CanvasRenderingContext2D::DrawWidgetAsOnScreen(nsGlobalWindow& aWindow,
-                                               mozilla::ErrorResult& aError)
-{
-  EnsureTarget();
-
-  // This is an internal API.
-  if (!nsContentUtils::IsCallerChrome()) {
-    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
-    return;
-  }
-
-  RefPtr<nsPresContext> presContext;
-  nsIDocShell* docshell = aWindow.GetDocShell();
-  if (docshell) {
-    docshell->GetPresContext(getter_AddRefs(presContext));
-  }
-  if (!presContext) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  nsIWidget* widget = presContext->GetRootWidget();
-  if (!widget) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-  RefPtr<SourceSurface> snapshot = widget->SnapshotWidgetOnScreen();
-  if (!snapshot) {
-    aError.Throw(NS_ERROR_FAILURE);
-    return;
-  }
-
-  gfx::Rect sourceRect(gfx::Point(0, 0), gfx::Size(snapshot->GetSize()));
-  mTarget->DrawSurface(snapshot, sourceRect, sourceRect,
-                       DrawSurfaceOptions(gfx::Filter::POINT),
-                       DrawOptions(GlobalAlpha(), CompositionOp::OP_OVER,
-                                   AntialiasMode::NONE));
-  mTarget->Flush();
-
-  RedrawUser(gfxRect(0, 0,
-                     std::min(mWidth, snapshot->GetSize().width),
-                     std::min(mHeight, snapshot->GetSize().height)));
-}
-
 //
 // device pixel getting/setting
 //
 
 already_AddRefed<ImageData>
 CanvasRenderingContext2D::GetImageData(JSContext* aCx, double aSx,
                                        double aSy, double aSw,
                                        double aSh, ErrorResult& aError)
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -404,18 +404,16 @@ public:
       CurrentState().imageSmoothingEnabled = aImageSmoothingEnabled;
     }
   }
 
   void DrawWindow(nsGlobalWindow& aWindow, double aX, double aY,
 		  double aW, double aH,
                   const nsAString& aBgColor, uint32_t aFlags,
                   mozilla::ErrorResult& aError);
-  void DrawWidgetAsOnScreen(nsGlobalWindow& aWindow,
-			    mozilla::ErrorResult& aError);
   void AsyncDrawXULElement(nsXULElement& aElem, double aX, double aY, double aW,
                            double aH, const nsAString& aBgColor, uint32_t aFlags,
                            mozilla::ErrorResult& aError);
 
   enum RenderingMode {
     SoftwareBackendMode,
     OpenGLBackendMode,
     DefaultBackendMode
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -565,50 +565,49 @@ BaseCaps(const WebGLContextOptions& opti
 }
 
 ////////////////////////////////////////
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithEGL(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                 WebGLContext* webgl)
 {
-    RefPtr<GLContext> gl;
-#ifndef XP_MACOSX // Mac doesn't have GLContextProviderEGL.
-    gfx::IntSize dummySize(16, 16);
-    gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
+    const gfx::IntSize dummySize(16, 16);
+    RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
                                                                      flags);
+
+    if (gl && gl->IsANGLE()) {
+        gl = nullptr;
+    }
+
     if (!gl) {
         webgl->GenerateWarning("Error during EGL OpenGL init.");
         return nullptr;
     }
 
-    if (gl->IsANGLE())
-        return nullptr;
-#endif // XP_MACOSX
     return gl.forget();
 }
 
 static already_AddRefed<GLContext>
 CreateGLWithANGLE(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                   WebGLContext* webgl)
 {
-    RefPtr<GLContext> gl;
+    const gfx::IntSize dummySize(16, 16);
+    RefPtr<GLContext> gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps,
+                                                                     flags);
 
-#ifdef XP_WIN
-    gfx::IntSize dummySize(16, 16);
-    gl = gl::GLContextProviderEGL::CreateOffscreen(dummySize, caps, flags);
+    if (gl && !gl->IsANGLE()) {
+        gl = nullptr;
+    }
+
     if (!gl) {
         webgl->GenerateWarning("Error during ANGLE OpenGL init.");
         return nullptr;
     }
 
-    if (!gl->IsANGLE())
-        return nullptr;
-#endif
-
     return gl.forget();
 }
 
 static already_AddRefed<gl::GLContext>
 CreateGLWithDefault(const gl::SurfaceCaps& caps, gl::CreateContextFlags flags,
                     WebGLContext* webgl)
 {
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
@@ -616,41 +615,42 @@ CreateGLWithDefault(const gl::SurfaceCap
     if (!(flags & CreateContextFlags::FORCE_ENABLE_HARDWARE) &&
         IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_OPENGL))
     {
         webgl->GenerateWarning("Refused to create native OpenGL context because of"
                                " blacklisting.");
         return nullptr;
     }
 
-    gfx::IntSize dummySize(16, 16);
+    const gfx::IntSize dummySize(16, 16);
     RefPtr<GLContext> gl = gl::GLContextProvider::CreateOffscreen(dummySize, caps, flags);
+
+    if (gl && gl->IsANGLE()) {
+        gl = nullptr;
+    }
+
     if (!gl) {
         webgl->GenerateWarning("Error during native OpenGL init.");
         return nullptr;
     }
 
-    if (gl->IsANGLE())
-        return nullptr;
-
     return gl.forget();
 }
 
 ////////////////////////////////////////
 
 bool
 WebGLContext::CreateAndInitGLWith(FnCreateGL_T fnCreateGL,
                                   const gl::SurfaceCaps& baseCaps,
                                   gl::CreateContextFlags flags)
 {
-    MOZ_ASSERT(!gl);
-
     std::queue<gl::SurfaceCaps> fallbackCaps;
     PopulateCapFallbackQueue(baseCaps, &fallbackCaps);
 
+    MOZ_RELEASE_ASSERT(!gl);
     gl = nullptr;
     while (!fallbackCaps.empty()) {
         gl::SurfaceCaps& caps = fallbackCaps.front();
 
         gl = fnCreateGL(caps, flags, this);
         if (gl)
             break;
 
@@ -665,52 +665,39 @@ WebGLContext::CreateAndInitGLWith(FnCrea
     }
 
     return true;
 }
 
 bool
 WebGLContext::CreateAndInitGL(bool forceEnabled)
 {
-    bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
-    bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
+    const bool useEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
 
-    if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
-        disableANGLE = true;
+    bool useANGLE = false;
+#ifdef XP_WIN
+    const bool disableANGLE = (gfxPrefs::WebGLDisableANGLE() ||
+                               PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"));
+    useANGLE = !disableANGLE;
+#endif
 
     gl::CreateContextFlags flags = gl::CreateContextFlags::NONE;
     if (forceEnabled) flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
     if (!IsWebGL2())  flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
     if (IsWebGL2())   flags |= gl::CreateContextFlags::PREFER_ES3;
 
     const gl::SurfaceCaps baseCaps = BaseCaps(mOptions, this);
 
-    MOZ_ASSERT(!gl);
-
-    if (preferEGL) {
-        if (CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags))
-            return true;
-    }
-
-    MOZ_ASSERT(!gl);
+    if (useEGL)
+        return CreateAndInitGLWith(CreateGLWithEGL, baseCaps, flags);
 
-    if (!disableANGLE) {
-        if (CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags))
-            return true;
-    }
-
-    MOZ_ASSERT(!gl);
+    if (useANGLE)
+        return CreateAndInitGLWith(CreateGLWithANGLE, baseCaps, flags);
 
-    if (CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags))
-        return true;
-
-    MOZ_ASSERT(!gl);
-    gl = nullptr;
-
-    return false;
+    return CreateAndInitGLWith(CreateGLWithDefault, baseCaps, flags);
 }
 
 // Fallback for resizes:
 bool
 WebGLContext::ResizeBackbuffer(uint32_t requestedWidth,
                                uint32_t requestedHeight)
 {
     uint32_t width = requestedWidth;
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1302,32 +1302,50 @@ nsresult HTMLMediaElement::LoadResource(
   if (GetCORSMode() == CORS_USE_CREDENTIALS) {
     securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
   }
 
   MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
   nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
+  nsDocShell* docShellPtr;
+  if (docShell) {
+    docShellPtr = nsDocShell::Cast(docShell);
+    bool privateBrowsing;
+    docShellPtr->GetUsePrivateBrowsing(&privateBrowsing);
+    if (privateBrowsing) {
+      securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
+    }
+  }
+
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel;
   nsresult rv = NS_NewChannel(getter_AddRefs(channel),
                               mLoadingSrc,
                               static_cast<Element*>(this),
                               securityFlags,
                               contentPolicyType,
                               loadGroup,
                               nullptr,   // aCallbacks
                               nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
                               nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
                               nsIChannel::LOAD_CLASSIFY_URI |
                               nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
 
   NS_ENSURE_SUCCESS(rv,rv);
 
+  // This is a workaround and it will be fix in bug 1264230.
+  nsCOMPtr<nsILoadInfo> loadInfo = channel->GetLoadInfo();
+  if (loadInfo) {
+    NeckoOriginAttributes originAttrs;
+    NS_GetOriginAttributes(channel, originAttrs);
+    loadInfo->SetOriginAttributes(originAttrs);
+  }
+
   // The listener holds a strong reference to us.  This creates a
   // reference cycle, once we've set mChannel, which is manually broken
   // in the listener's OnStartRequest method after it is finished with
   // the element. The cycle will also be broken if we get a shutdown
   // notification before OnStartRequest fires.  Necko guarantees that
   // OnStartRequest will eventually fire if we don't shut down first.
   RefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
 
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/bug1110030_helper.js
@@ -0,0 +1,267 @@
+// ***********************************
+// * Global variables
+// ***********************************
+const kIsWin = navigator.platform.indexOf("Win") == 0;
+
+// Bit value for the keyboard events
+const kKeyDown  = 0x01;
+const kKeyPress = 0x02;
+const kKeyUp    = 0x04;
+
+// Pair the event name to its bit value
+const kEventCode = {
+  'keydown'   : kKeyDown,
+  'keypress'  : kKeyPress,
+  'keyup'     : kKeyUp
+};
+
+// Holding the current test case's infomation:
+var gCurrentTest;
+
+// The current used input method of this test
+var gInputMethod;
+
+// ***********************************
+// * Utilities
+// ***********************************
+function addKeyEventListeners(eventTarget, handler)
+{
+  Object.keys(kEventCode).forEach(function(type) {
+    eventTarget.addEventListener(type, handler);
+  });
+}
+
+function eventToCode(type)
+{
+  return kEventCode[type];
+}
+
+// To test key events that will be generated by input method here,
+// we need to convert alphabets to native key code.
+// (Our input method for testing will handle alphabets)
+// On the other hand, to test key events that will not be generated by IME,
+// we use 0-9 for such case in our testing.
+function guessNativeKeyCode(key)
+{
+  let nativeCodeName = (kIsWin)? 'WIN_VK_' : 'MAC_VK_ANSI_';
+  if (/^[A-Z]$/.test(key)) {
+    nativeCodeName += key;
+  } else if (/^[a-z]$/.test(key)) {
+    nativeCodeName += key.toUpperCase();
+  } else if (/^[0-9]$/.test(key)) {
+    nativeCodeName += key.toString();
+  } else {
+    return 0;
+  }
+
+  return eval(nativeCodeName);
+}
+
+// ***********************************
+// * Frame loader and frame scripts
+// ***********************************
+function frameScript()
+{
+  function handler(e) {
+    sendAsyncMessage("forwardevent", { type: e.type, key: e.key });
+  }
+  function notifyFinish(e) {
+    if (e.type != 'keyup') return;
+    sendAsyncMessage("finish");
+  }
+  let input = content.document.getElementById('test-input');
+  input.addEventListener('keydown', handler);
+  input.addEventListener('keypress', handler);
+  input.addEventListener('keyup', handler);
+  input.addEventListener('keyup', notifyFinish);
+}
+
+function loadTestFrame(goNext) {
+  let iframe = document.createElement('iframe');
+  iframe.src = 'file_test_empty_app.html';
+  iframe.setAttribute('mozbrowser', true);
+
+  iframe.addEventListener("mozbrowserloadend", function onloadend() {
+    iframe.removeEventListener("mozbrowserloadend", onloadend);
+    iframe.focus();
+    var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+    mm.addMessageListener("forwardevent", function(msg) {
+      inputtextEventReceiver(msg.json);
+    });
+    mm.addMessageListener("finish", function(msg) {
+      if(goNext) {
+        goNext();
+      }
+    });
+    mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
+    return;
+  });
+
+  document.body.appendChild(iframe);
+}
+
+// ***********************************
+// * Event firer and listeners
+// ***********************************
+function fireEvent(callback)
+{
+  let key = gCurrentTest.key;
+  synthesizeNativeKey(KEYBOARD_LAYOUT_EN_US, guessNativeKeyCode(key), {},
+                      key, key, (callback) ? callback : null);
+}
+
+function hardwareEventReceiver(evt)
+{
+  if (!gCurrentTest) {
+    return;
+  }
+  gCurrentTest.hardwareinput.receivedEvents |= eventToCode(evt.type);
+  gCurrentTest.hardwareinput.receivedKeys += evt.key;
+}
+
+function inputtextEventReceiver(evt)
+{
+  if (!gCurrentTest) {
+    return;
+  }
+  gCurrentTest.inputtext.receivedEvents |= eventToCode(evt.type);
+  gCurrentTest.inputtext.receivedKeys += evt.key;
+}
+
+// ***********************************
+// * Event verifier
+// ***********************************
+function verifyResults(test)
+{
+  // Verify results received from inputcontent.hardwareinput
+  is(test.hardwareinput.receivedEvents,
+     test.hardwareinput.expectedEvents,
+     "received events from inputcontent.hardwareinput are wrong");
+
+  is(test.hardwareinput.receivedKeys,
+     test.hardwareinput.expectedKeys,
+     "received keys from inputcontent.hardwareinput are wrong");
+
+  // Verify results received from actual input text
+  is(test.inputtext.receivedEvents,
+     test.inputtext.expectedEvents,
+     "received events from input text are wrong");
+
+  is(test.inputtext.receivedKeys,
+     test.inputtext.expectedKeys,
+     "received keys from input text are wrong");
+}
+
+function areEventsSame(test)
+{
+  return (test.hardwareinput.receivedEvents ==
+          test.hardwareinput.expectedEvents) &&
+         (test.inputtext.receivedEvents ==
+          test.inputtext.expectedEvents);
+}
+
+// ***********************************
+// * Input Method
+// ***********************************
+// The method input used in this test
+// only handles alphabets
+function InputMethod(inputContext)
+{
+  this._inputContext = inputContext;
+  this.init();
+}
+
+InputMethod.prototype = {
+  init: function im_init() {
+    this._setKepMap();
+  },
+
+  handler: function im_handler(evt) {
+    // Ignore the key if the event is defaultPrevented
+    if (evt.defaultPrevented) {
+      return;
+    }
+
+    // Finish if there is no _inputContext
+    if (!this._inputContext) {
+      return;
+    }
+
+    // Generate the keyDict for inputcontext.keydown/keyup
+    let keyDict = this._generateKeyDict(evt);
+
+    // Ignore the key if IME doesn't want to handle it
+    if (!keyDict) {
+      return;
+    }
+
+    // Call preventDefault if the key will be handled.
+    evt.preventDefault();
+
+    // Call inputcontext.keydown/keyup
+    this._inputContext[evt.type](keyDict);
+  },
+
+  mapKey: function im_keymapping(key) {
+    if (!this._mappingTable) {
+      return;
+    }
+    return this._mappingTable[key];
+  },
+
+  _setKepMap: function im_setKeyMap() {
+    // A table to map characters:
+    // {
+    //   'A': 'B'
+    //   'a': 'b'
+    //   'B': 'C'
+    //   'b': 'c'
+    //   ..
+    //   ..
+    //   'Z': 'A',
+    //   'z': 'a',
+    // }
+    this._mappingTable = {};
+
+    let rotation = 1;
+
+    for (let i = 0 ; i < 26 ; i++) {
+      // Convert 'A' to 'B', 'B' to 'C', ..., 'Z' to 'A'
+      this._mappingTable[String.fromCharCode(i + 'A'.charCodeAt(0))] =
+        String.fromCharCode((i+rotation)%26 + 'A'.charCodeAt(0));
+
+      // Convert 'a' to 'b', 'b' to 'c', ..., 'z' to 'a'
+      this._mappingTable[String.fromCharCode(i + 'a'.charCodeAt(0))] =
+        String.fromCharCode((i+rotation)%26 + 'a'.charCodeAt(0));
+    }
+  },
+
+  _generateKeyDict: function im_generateKeyDict(evt) {
+
+    let mappedKey = this.mapKey(evt.key);
+
+    if (!mappedKey) {
+      return;
+    }
+
+    let keyDict = {
+      key: mappedKey,
+      code: this._guessCodeFromKey(mappedKey),
+      repeat: evt.repeat,
+    };
+
+    return keyDict;
+  },
+
+  _guessCodeFromKey: function im_guessCodeFromKey(key) {
+    if (/^[A-Z]$/.test(key)) {
+      return "Key" + key;
+    } else if (/^[a-z]$/.test(key)) {
+      return "Key" + key.toUpperCase();
+    } else if (/^[0-9]$/.test(key)) {
+      return "Digit" + key.toString();
+    } else {
+      return 0;
+    }
+  },
+};
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/file_test_empty_app.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<input id="test-input" type="text" value=""/>
+<script type="application/javascript;version=1.7">
+  let input = document.getElementById('test-input');
+  input.focus();
+</script>
+</body>
+</html>
--- a/dom/inputmethod/mochitest/mochitest.ini
+++ b/dom/inputmethod/mochitest/mochitest.ini
@@ -18,16 +18,21 @@ support-files =
 [test_bug978918.html]
 [test_bug1026997.html]
 [test_bug1043828.html]
 [test_bug1059163.html]
 [test_bug1066515.html]
 [test_bug1175399.html]
 [test_bug1137557.html]
 [test_focus_blur_manage_events.html]
+[test_forward_hardware_key_to_ime.html]
+skip-if = buildapp != 'mulet'
+support-files =
+  bug1110030_helper.js
+  file_test_empty_app.html
 [test_input_registry_events.html]
 [test_sendkey_cancel.html]
 [test_setSupportsSwitching.html]
 [test_simple_manage_events.html]
 [test_sync_edit.html]
 [test_two_inputs.html]
 [test_two_selects.html]
 [test_unload.html]
new file mode 100644
--- /dev/null
+++ b/dom/inputmethod/mochitest/test_forward_hardware_key_to_ime.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1110030
+-->
+<head>
+  <title>Forwarding Hardware Key to InputMethod</title>
+  <script type="application/javascript;version=1.7" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.7" src="inputmethod_common.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/NativeKeyCodes.js"></script>
+  <script type="text/javascript" src="bug1110030_helper.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1110030">Mozilla Bug 1110030</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="application/javascript;version=1.7">
+// The input context.
+var gContext = null;
+
+// The test cases.
+var gTests;
+
+inputmethod_setup(function() {
+  setInputContext();
+});
+
+function setInputContext() {
+  let im = navigator.mozInputMethod;
+
+  im.oninputcontextchange = function() {
+    ok(true, 'inputcontextchange event was fired.');
+    im.oninputcontextchange = null;
+
+    gContext = im.inputcontext;
+    if (!gContext || !gContext.hardwareinput) {
+      ok(false, 'Should have a non-null inputcontext.hardwareinput');
+      inputmethod_cleanup();
+      return;
+    }
+
+    prepareTest();
+  };
+
+  // Set current page as an input method.
+  SpecialPowers.wrap(im).setActive(true);
+
+  // verifyResultsAndMoveNext will be called after input#text-input
+  // receives all expected key events and it will verify results
+  // and start next test.
+  loadTestFrame(verifyResultsAndMoveNext);
+}
+
+function prepareTest()
+{
+  // Set the used input method of this test
+  gInputMethod = new InputMethod(gContext);
+
+  // Add listenr to hardwareinput
+  addKeyEventListeners(gContext.hardwareinput, function (evt) {
+    hardwareEventReceiver(evt);
+    gInputMethod.handler(evt);
+  });
+
+  // Set the test cases
+  gTests = [
+    // Case 1: IME handle the key input
+    {
+      key: 'z',
+      hardwareinput: {
+        expectedEvents: kKeyDown | kKeyUp,
+        receivedEvents: 0,
+        expectedKeys: 'zz', // One for keydown, the other for keyup
+        receivedKeys: '',
+      },
+      inputtext: {
+        expectedEvents: kKeyDown | kKeyPress | kKeyUp,
+        receivedEvents: 0,
+        expectedKeys: gInputMethod.mapKey('z') +  // for keydown
+                      gInputMethod.mapKey('z') +  // for keypress
+                      gInputMethod.mapKey('z'),   // for keyup
+        receivedKeys: '',
+      }
+    },
+    // case 2: IME doesn't handle the key input
+    {
+      key: '7',
+      hardwareinput: {
+        expectedEvents: kKeyDown | kKeyUp,
+        receivedEvents: 0,
+        expectedKeys: '77', // One for keydown, the other for keyup
+        receivedKeys: '',
+      },
+      inputtext: {
+        expectedEvents: kKeyDown | kKeyPress | kKeyUp,
+        receivedEvents: 0,
+        expectedKeys: '777', // keydown, keypress, keyup all will receive key
+        receivedKeys: '',
+      }
+    },
+    // case 3: IME is disable
+    // This case is same as
+    // dom/events/test/test_dom_before_after_keyboard_event*.html
+  ];
+
+  startTesting();
+}
+
+function startTesting()
+{
+  if (gTests.length <= 0) {
+    finish();
+    return;
+  }
+
+  gCurrentTest = gTests.shift();
+
+  fireEvent();
+}
+
+function verifyResultsAndMoveNext()
+{
+  verifyResults(gCurrentTest);
+  startTesting();
+}
+
+function finish()
+{
+  inputmethod_cleanup();
+}
+
+function errorHandler(msg)
+{
+  // Clear the test cases
+  if (gTests) {
+    gTests = [];
+  }
+
+  ok(false, msg);
+
+  inputmethod_cleanup();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5652,24 +5652,27 @@ ContentParent::RecvBeginDriverCrashGuard
     guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
     break;
   case gfx::CrashGuardType::D3D9Video:
     guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this);
     break;
   case gfx::CrashGuardType::GLContext:
     guard = MakeUnique<gfx::GLContextCrashGuard>(this);
     break;
+  case gfx::CrashGuardType::D3D11Video:
+    guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this);
+    break;
   default:
     MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
     return false;
   }
 
   if (guard->Crashed()) {
-  *aOutCrashed = true;
-  return true;
+    *aOutCrashed = true;
+    return true;
   }
 
   *aOutCrashed = false;
   mDriverCrashGuard = Move(guard);
   return true;
 }
 
 bool
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -158,18 +158,18 @@ DataContainerEventWarning=Use of DataCon
 # LOCALIZATION NOTE: Do not translate "window.controllers"
 Window_ControllersWarning=window.controllers is deprecated. Do not use it for UA detection.
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
 # LOCALIZATION NOTE: Do not translate "IndexedDB".
 IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
 # LOCALIZATION NOTE: Do not translate Will-change, %1$S,%2$S are numbers.
 IgnoringWillChangeOverBudgetWarning=Will-change memory consumption is too high. Budget limit is the document surface area multiplied by %1$S (%2$S px). Occurrences of will-change over the budget will be ignored.
-# LOCALIZATION NOTE: Do not translate "ServiceWorker".
-HittingMaxWorkersPerDomain=A ServiceWorker could not be started immediately because other documents in the same origin are already using the maximum number of workers. The ServiceWorker is now queued and will be started after some of the other workers have completed.
+# LOCALIZATION NOTE: Do not translate "Worker".
+HittingMaxWorkersPerDomain2=A Worker could not be started immediately because other documents in the same origin are already using the maximum number of workers. The Worker is now queued and will be started after some of the other workers have completed.
 # LOCALIZATION NOTE: Do not translate "setVelocity", "PannerNode", "AudioListener", "speedOfSound" and "dopplerFactor"
 PannerNodeDopplerWarning=Use of setVelocity on the PannerNode and AudioListener, and speedOfSound and dopplerFactor on the AudioListener are deprecated and those members will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/AudioListener#Deprecated_features
 # LOCALIZATION NOTE: Do not translate "Application Cache API", "AppCache" and "ServiceWorker".
 AppCacheWarning=The Application Cache API (AppCache) is deprecated and will be removed at a future date.  Please consider using ServiceWorker for offline support.
 # LOCALIZATION NOTE: Do not translate "Worker".
 EmptyWorkerSourceWarning=Attempting to create a Worker from an empty source. This is probably unintentional.
 WebrtcDeprecatedPrefixWarning=WebRTC interfaces with the "moz" prefix (mozRTCPeerConnection, mozRTCSessionDescription, mozRTCIceCandidate) have been deprecated.
 NavigatorGetUserMediaWarning=navigator.mozGetUserMedia has been replaced by navigator.mediaDevices.getUserMedia
--- a/dom/manifest/ManifestProcessor.jsm
+++ b/dom/manifest/ManifestProcessor.jsm
@@ -27,16 +27,18 @@ const {
 Cu.importGlobalProperties(['URL']);
 const displayModes = new Set(['fullscreen', 'standalone', 'minimal-ui',
   'browser'
 ]);
 const orientationTypes = new Set(['any', 'natural', 'landscape', 'portrait',
   'portrait-primary', 'portrait-secondary', 'landscape-primary',
   'landscape-secondary'
 ]);
+const textDirections = new Set(['ltr', 'rtl', 'auto']);
+
 Cu.import('resource://gre/modules/Console.jsm');
 Cu.import("resource://gre/modules/Services.jsm");
 // ValueExtractor is used by the various processors to get values
 // from the manifest and to report errors.
 Cu.import('resource://gre/modules/ValueExtractor.jsm');
 // ImageObjectProcessor is used to process things like icons and images
 Cu.import('resource://gre/modules/ImageObjectProcessor.jsm');
 
@@ -45,16 +47,19 @@ this.ManifestProcessor = { // jshint ign
     return 'browser';
   },
   get displayModes() {
     return displayModes;
   },
   get orientationTypes() {
     return orientationTypes;
   },
+  get textDirections() {
+    return textDirections;
+  },
   // process() method processes JSON text into a clean manifest
   // that conforms with the W3C specification. Takes an object
   // expecting the following dictionary items:
   //  * jsonText: the JSON string to be processed.
   //  * manifestURL: the URL of the manifest, to resolve URLs.
   //  * docURL: the URL of the owner doc, for security checks
   process({
     jsonText,
@@ -74,31 +79,47 @@ this.ManifestProcessor = { // jshint ign
     } catch (e) {}
     if (typeof rawManifest !== 'object' || rawManifest === null) {
       console.warn(domBundle.GetStringFromName('ManifestShouldBeObject'));
       rawManifest = {};
     }
     const extractor = new ValueExtractor(console, domBundle);
     const imgObjProcessor = new ImageObjectProcessor(console, extractor);
     const processedManifest = {
+      'dir': processDirMember.call(this),
       'lang': processLangMember(),
       'start_url': processStartURLMember(),
       'display': processDisplayMember.call(this),
       'orientation': processOrientationMember.call(this),
       'name': processNameMember(),
       'icons': imgObjProcessor.process(
         rawManifest, manifestURL, 'icons'
       ),
       'short_name': processShortNameMember(),
       'theme_color': processThemeColorMember(),
       'background_color': processBackgroundColorMember(),
     };
     processedManifest.scope = processScopeMember();
     return processedManifest;
 
+    function processDirMember() {
+      const spec = {
+        objectName: 'manifest',
+        object: rawManifest,
+        property: 'dir',
+        expectedType: 'string',
+        trim: true,
+      };
+      const value = extractor.extractValue(spec);
+      if (this.textDirections.has(value)) {
+        return value;
+      }
+      return 'auto';
+    }
+
     function processNameMember() {
       const spec = {
         objectName: 'manifest',
         object: rawManifest,
         property: 'name',
         expectedType: 'string',
         trim: true
       };
--- a/dom/manifest/test/mochitest.ini
+++ b/dom/manifest/test/mochitest.ini
@@ -4,16 +4,17 @@ support-files =
 	resource.sjs
 	manifestLoader.html
 [test_ImageObjectProcessor_background_color.html]
 [test_ImageObjectProcessor_density.html]
 [test_ImageObjectProcessor_sizes.html]
 [test_ImageObjectProcessor_src.html]
 [test_ImageObjectProcessor_type.html]
 [test_ManifestProcessor_background_color.html]
+[test_ManifestProcessor_dir.html]
 [test_ManifestProcessor_display.html]
 [test_ManifestProcessor_icons.html]
 [test_ManifestProcessor_JSON.html]
 [test_ManifestProcessor_lang.html]
 [test_ManifestProcessor_name_and_short_name.html]
 [test_ManifestProcessor_orientation.html]
 [test_ManifestProcessor_scope.html]
 [test_ManifestProcessor_start_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/manifest/test/test_ManifestProcessor_dir.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1258899
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1258899</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script src="common.js"></script>
+  <script>
+/**
+ * dir member
+ * https://w3c.github.io/manifest/#dir-member
+ **/
+'use strict';
+//Type checks
+typeTests.forEach((type) => {
+  var expected = `Expect non - string dir to default to "auto".`;
+  data.jsonText = JSON.stringify({
+    dir: type
+  });
+  var result = processor.process(data);
+  is(result.dir, 'auto', expected);
+});
+
+/*Test valid values*/
+var validDirs = ['ltr', 'rtl', 'auto']
+validDirs.forEach((dir) => {
+  var expected = `Expect dir value to be ${dir}.`;
+  data.jsonText = JSON.stringify({dir});
+  var result = processor.process(data);
+  is(result.dir, dir, expected);
+});
+
+//trim tests
+validDirs.forEach((dir) => {
+  var expected = `Expect trimmed dir to be returned.`;
+  var expandeddir =  seperators + lineTerminators + dir + lineTerminators + seperators;
+  data.jsonText = JSON.stringify({
+    dir: expandeddir
+  });
+  var result = processor.process(data);
+  is(result.dir, dir, expected);
+});
+
+//Unknown/Invalid directions
+var invalidDirs = ['LTR', 'RtL', `fooo${whiteSpace}rtl`, '', 'bar baz, some value', 'ltr rtl auto', 'AuTo'];
+invalidDirs.forEach((dir) => {
+  var expected = `Expect default dir "auto" to be returned: '${dir}'`;
+  data.jsonText = JSON.stringify({dir});
+  var result = processor.process(data);
+  is(result.dir, 'auto', expected);
+});
+  </script>
+</head>
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -232,19 +232,16 @@ MediaDecoderStateMachine::MediaDecoderSt
   mAudioCaptured(false),
   mAudioCompleted(false, "MediaDecoderStateMachine::mAudioCompleted"),
   mVideoCompleted(false, "MediaDecoderStateMachine::mVideoCompleted"),
   mNotifyMetadataBeforeFirstFrame(false),
   mDispatchedEventToDecode(false),
   mQuickBuffering(false),
   mMinimizePreroll(false),
   mDecodeThreadWaiting(false),
-  mDropAudioUntilNextDiscontinuity(false),
-  mDropVideoUntilNextDiscontinuity(false),
-  mCurrentTimeBeforeSeek(0),
   mDecodingFirstFrame(true),
   mSentLoadedMetadataEvent(false),
   mSentFirstFrameLoadedEvent(false),
   mSentPlaybackEndedEvent(false),
   mOutputStreamManager(new OutputStreamManager()),
   mResource(aDecoder->GetResource()),
   mAudioOffloading(false),
   mSilentDataDuration(0),
@@ -551,46 +548,22 @@ MediaDecoderStateMachine::NeedToDecodeAu
              IsAudioDecoding(), mMinimizePreroll, HaveEnoughDecodedAudio());
 
   return IsAudioDecoding() &&
          mState != DECODER_STATE_SEEKING &&
          ((IsDecodingFirstFrame() && AudioQueue().GetSize() == 0) ||
           (!mMinimizePreroll && !HaveEnoughDecodedAudio()));
 }
 
-bool
-MediaDecoderStateMachine::IsAudioSeekComplete()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
-    mCurrentSeek.Exists(), mDropAudioUntilNextDiscontinuity, AudioQueue().IsFinished(), AudioQueue().GetSize());
-  return
-    !HasAudio() ||
-    (mCurrentSeek.Exists() &&
-     !mDropAudioUntilNextDiscontinuity &&
-     (AudioQueue().IsFinished() || AudioQueue().GetSize() > 0));
-}
-
-bool
-MediaDecoderStateMachine::IsVideoSeekComplete()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
-    mCurrentSeek.Exists(), mDropVideoUntilNextDiscontinuity, VideoQueue().IsFinished(), VideoQueue().GetSize());
-  return
-    !HasVideo() ||
-    (mCurrentSeek.Exists() &&
-     !mDropVideoUntilNextDiscontinuity &&
-     (VideoQueue().IsFinished() || VideoQueue().GetSize() > 0));
-}
-
 void
 MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample)
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
+
   RefPtr<MediaData> audio(aAudioSample);
   MOZ_ASSERT(audio);
   mAudioDataRequest.Complete();
 
   // audio->GetEndTime() is not always mono-increasing in chained ogg.
   mDecodedAudioEndTime = std::max(audio->GetEndTime(), mDecodedAudioEndTime);
 
   SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
@@ -613,53 +586,16 @@ MediaDecoderStateMachine::OnAudioDecoded
         return;
       }
       if (mIsAudioPrerolling && DonePrerollingAudio()) {
         StopPrerollingAudio();
       }
       return;
     }
 
-    case DECODER_STATE_SEEKING: {
-      if (!mCurrentSeek.Exists()) {
-        // We've received a sample from a previous decode. Discard it.
-        return;
-      }
-      if (audio->mDiscontinuity) {
-        mDropAudioUntilNextDiscontinuity = false;
-      }
-      if (!mDropAudioUntilNextDiscontinuity) {
-        // We must be after the discontinuity; we're receiving samples
-        // at or after the seek target.
-        if (mCurrentSeek.mTarget.IsFast() &&
-            mCurrentSeek.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
-            audio->mTime < mCurrentTimeBeforeSeek) {
-          // We are doing a fastSeek, but we ended up *before* the previous
-          // playback position. This is surprising UX, so switch to an accurate
-          // seek and decode to the seek target. This is not conformant to the
-          // spec, fastSeek should always be fast, but until we get the time to
-          // change all Readers to seek to the keyframe after the currentTime
-          // in this case, we'll just decode forward. Bug 1026330.
-          mCurrentSeek.mTarget.SetType(SeekTarget::Accurate);
-        }
-        if (mCurrentSeek.mTarget.IsFast()) {
-          // Non-precise seek; we can stop the seek at the first sample.
-          Push(audio, MediaData::AUDIO_DATA);
-        } else {
-          // We're doing an accurate seek. We must discard
-          // MediaData up to the one containing exact seek target.
-          if (NS_FAILED(DropAudioUpToSeekTarget(audio))) {
-            DecodeError();
-            return;
-          }
-        }
-      }
-      CheckIfSeekComplete();
-      return;
-    }
     default: {
       // Ignore other cases.
       return;
     }
   }
 }
 
 void
@@ -728,16 +664,18 @@ MediaDecoderStateMachine::OnVideoPopped(
   MaybeStartBuffering();
 }
 
 void
 MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
                                        MediaDecoderReader::NotDecodedReason aReason)
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
+
   SAMPLE_LOG("OnNotDecoded (aType=%u, aReason=%u)", aType, aReason);
   bool isAudio = aType == MediaData::AUDIO_DATA;
   MOZ_ASSERT_IF(!isAudio, aType == MediaData::VIDEO_DATA);
 
   if (isAudio) {
     mAudioDataRequest.Complete();
   } else {
     mVideoDataRequest.Complete();
@@ -795,25 +733,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
       EnsureVideoDecodeTaskQueued();
     }
     return;
   }
 
   // This is an EOS. Finish off the queue, and then handle things based on our
   // state.
   MOZ_ASSERT(aReason == MediaDecoderReader::END_OF_STREAM);
-  if (!isAudio && mState == DECODER_STATE_SEEKING &&
-      mCurrentSeek.Exists() && mFirstVideoFrameAfterSeek) {
-    // Null sample. Hit end of stream. If we have decoded a frame,
-    // insert it into the queue so that we have something to display.
-    // We make sure to do this before invoking VideoQueue().Finish()
-    // below.
-    Push(mFirstVideoFrameAfterSeek, MediaData::VIDEO_DATA);
-    mFirstVideoFrameAfterSeek = nullptr;
-  }
   if (isAudio) {
     AudioQueue().Finish();
     StopPrerollingAudio();
   } else {
     VideoQueue().Finish();
     StopPrerollingVideo();
   }
   switch (mState) {
@@ -825,31 +754,16 @@ MediaDecoderStateMachine::OnNotDecoded(M
       CheckIfDecodeComplete();
 
       // Schedule next cycle to see if we can leave buffering state.
       if (mState == DECODER_STATE_BUFFERING) {
         ScheduleStateMachine();
       }
       return;
     }
-    case DECODER_STATE_SEEKING: {
-      if (!mCurrentSeek.Exists()) {
-        // We've received a sample from a previous decode. Discard it.
-        return;
-      }
-
-      if (isAudio) {
-        mDropAudioUntilNextDiscontinuity = false;
-      } else {
-        mDropVideoUntilNextDiscontinuity = false;
-      }
-
-      CheckIfSeekComplete();
-      return;
-    }
     default: {
       return;
     }
   }
 }
 
 bool
 MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
@@ -871,16 +785,18 @@ MediaDecoderStateMachine::MaybeFinishDec
   return true;
 }
 
 void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
+
   RefPtr<MediaData> video(aVideoSample);
   MOZ_ASSERT(video);
   mVideoDataRequest.Complete();
 
   // Handle abnormal or negative timestamps.
   mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, video->GetEndTime());
 
   SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
@@ -923,96 +839,23 @@ MediaDecoderStateMachine::OnVideoDecoded
           std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), mAmpleAudioThresholdUsecs);
         mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
                                               mAmpleAudioThresholdUsecs);
         DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
                     mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
       }
       return;
     }
-    case DECODER_STATE_SEEKING: {
-      if (!mCurrentSeek.Exists()) {
-        // We've received a sample from a previous decode. Discard it.
-        return;
-      }
-      if (mDropVideoUntilNextDiscontinuity) {
-        if (video->mDiscontinuity) {
-          mDropVideoUntilNextDiscontinuity = false;
-        }
-      }
-      if (!mDropVideoUntilNextDiscontinuity) {
-        // We must be after the discontinuity; we're receiving samples
-        // at or after the seek target.
-        if (mCurrentSeek.mTarget.IsFast() &&
-            mCurrentSeek.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
-            video->mTime < mCurrentTimeBeforeSeek) {
-          // We are doing a fastSeek, but we ended up *before* the previous
-          // playback position. This is surprising UX, so switch to an accurate
-          // seek and decode to the seek target. This is not conformant to the
-          // spec, fastSeek should always be fast, but until we get the time to
-          // change all Readers to seek to the keyframe after the currentTime
-          // in this case, we'll just decode forward. Bug 1026330.
-          mCurrentSeek.mTarget.SetType(SeekTarget::Accurate);
-        }
-        if (mCurrentSeek.mTarget.IsFast()) {
-          // Non-precise seek. We can stop the seek at the first sample.
-          Push(video, MediaData::VIDEO_DATA);
-        } else {
-          // We're doing an accurate seek. We still need to discard
-          // MediaData up to the one containing exact seek target.
-          if (NS_FAILED(DropVideoUpToSeekTarget(video))) {
-            DecodeError();
-            return;
-          }
-        }
-      }
-      CheckIfSeekComplete();
-      return;
-    }
     default: {
       // Ignore other cases.
       return;
     }
   }
 }
 
-void
-MediaDecoderStateMachine::CheckIfSeekComplete()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
-
-  const bool videoSeekComplete = IsVideoSeekComplete();
-  if (HasVideo() && !videoSeekComplete) {
-    // We haven't reached the target. Ensure we have requested another sample.
-    if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
-      DECODER_WARN("Failed to request video during seek");
-      DecodeError();
-    }
-  }
-
-  const bool audioSeekComplete = IsAudioSeekComplete();
-  if (HasAudio() && !audioSeekComplete) {
-    // We haven't reached the target. Ensure we have requested another sample.
-    if (NS_FAILED(EnsureAudioDecodeTaskQueued())) {
-      DECODER_WARN("Failed to request audio during seek");
-      DecodeError();
-    }
-  }
-
-  SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
-             audioSeekComplete, videoSeekComplete);
-
-  if (audioSeekComplete && videoSeekComplete) {
-    NS_ASSERTION(AudioQueue().GetSize() <= 1, "Should decode at most one sample");
-    NS_ASSERTION(VideoQueue().GetSize() <= 1, "Should decode at most one sample");
-    SeekCompleted();
-  }
-}
-
 bool
 MediaDecoderStateMachine::IsAudioDecoding()
 {
   MOZ_ASSERT(OnTaskQueue());
   return HasAudio() && !AudioQueue().IsFinished();
 }
 
 bool
@@ -1281,35 +1124,42 @@ MediaDecoderStateMachine::SetDormant(boo
   mPendingDormant.reset();
 
   DECODER_LOG("SetDormant=%d", aDormant);
 
   if (aDormant) {
     if (mState == DECODER_STATE_SEEKING) {
       if (mQueuedSeek.Exists()) {
         // Keep latest seek target
-      } else if (mCurrentSeek.Exists()) {
-        mQueuedSeek = Move(mCurrentSeek);
+      } else if (mSeekTask && mSeekTask->Exists()) {
+        mQueuedSeek = Move(mSeekTask->GetSeekJob());
+        mSeekTaskRequest.DisconnectIfExists();
       } else {
         mQueuedSeek.mTarget = SeekTarget(mCurrentPosition,
                                          SeekTarget::Accurate,
                                          MediaDecoderEventVisibility::Suppressed);
         // XXXbholley - Nobody is listening to this promise. Do we need to pass it
         // back to MediaDecoder when we come out of dormant?
         RefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
       }
     } else {
       mQueuedSeek.mTarget = SeekTarget(mCurrentPosition,
                                        SeekTarget::Accurate,
                                        MediaDecoderEventVisibility::Suppressed);
       // XXXbholley - Nobody is listening to this promise. Do we need to pass it
       // back to MediaDecoder when we come out of dormant?
       RefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
     }
-    mCurrentSeek.RejectIfExists(__func__);
+
+    // Discard the current seek task.
+    if (mSeekTask) {
+      mSeekTask->Discard();
+      mSeekTask = nullptr;
+    }
+
     SetState(DECODER_STATE_DORMANT);
     if (IsPlaying()) {
       StopPlayback();
     }
 
     Reset();
 
     // Note that we do not wait for the decode task queue to go idle before
@@ -1336,17 +1186,20 @@ MediaDecoderStateMachine::Shutdown()
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
   ScheduleStateMachine();
   SetState(DECODER_STATE_SHUTDOWN);
 
   mBufferedUpdateRequest.DisconnectIfExists();
 
   mQueuedSeek.RejectIfExists(__func__);
-  mCurrentSeek.RejectIfExists(__func__);
+  if (mSeekTask) {
+    mSeekTask->Discard();
+    mSeekTask = nullptr;
+  }
 
 #ifdef MOZ_EME
   mCDMProxyPromise.DisconnectIfExists();
 #endif
 
   if (IsPlaying()) {
     StopPlayback();
   }
@@ -1525,17 +1378,17 @@ MediaDecoderStateMachine::Seek(SeekTarge
   mQueuedSeek.RejectIfExists(__func__);
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
   SetState(DECODER_STATE_SEEKING);
 
   SeekJob seekJob;
   seekJob.mTarget = aTarget;
   InitiateSeek(Move(seekJob));
-  return mCurrentSeek.mPromise.Ensure(__func__);
+  return mSeekTask->GetSeekJob().mPromise.Ensure(__func__);
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InvokeSeek(SeekTarget aTarget)
 {
   return InvokeAsync(OwnerThread(), this, __func__,
                      &MediaDecoderStateMachine::Seek, aTarget);
 }
@@ -1609,60 +1462,120 @@ MediaDecoderStateMachine::DispatchDecode
   }
 }
 
 void
 MediaDecoderStateMachine::InitiateSeek(SeekJob aSeekJob)
 {
   MOZ_ASSERT(OnTaskQueue());
 
-  mCurrentSeek.RejectIfExists(__func__);
-  mCurrentSeek = Move(aSeekJob);
-
-  // Bound the seek time to be inside the media range.
-  int64_t end = Duration().ToMicroseconds();
-  NS_ASSERTION(end != -1, "Should know end time by now");
-  int64_t seekTime = mCurrentSeek.mTarget.GetTime().ToMicroseconds();
-  seekTime = std::min(seekTime, end);
-  seekTime = std::max(int64_t(0), seekTime);
-  NS_ASSERTION(seekTime >= 0 && seekTime <= end,
-               "Can only seek in range [0,duration]");
-  mCurrentSeek.mTarget.SetTime(media::TimeUnit::FromMicroseconds(seekTime));
-
-  mDropAudioUntilNextDiscontinuity = HasAudio();
-  mDropVideoUntilNextDiscontinuity = HasVideo();
-  mCurrentTimeBeforeSeek = GetMediaTime();
+  // Discard the existing seek task.
+  if (mSeekTask) {
+    mSeekTask->Discard();
+  }
+
+  mSeekTaskRequest.DisconnectIfExists();
+
+  // Create a new SeekTask instance for the incoming seek task.
+  mSeekTask = SeekTask::CreateSeekTask(mDecoderID, OwnerThread(), mReader.get(),
+                                       mReaderWrapper.get(), Move(aSeekJob),
+                                       mInfo, Duration(), GetMediaTime());
 
   // Stop playback now to ensure that while we're outside the monitor
   // dispatching SeekingStarted, playback doesn't advance and mess with
   // mCurrentPosition that we've setting to seekTime here.
   StopPlayback();
-  UpdatePlaybackPositionInternal(mCurrentSeek.mTarget.GetTime().ToMicroseconds());
-
-  mOnSeekingStart.Notify(mCurrentSeek.mTarget.mEventVisibility);
+  UpdatePlaybackPositionInternal(mSeekTask->GetSeekJob().mTarget.GetTime().ToMicroseconds());
+
+  mOnSeekingStart.Notify(mSeekTask->GetSeekJob().mTarget.mEventVisibility);
 
   // Reset our state machine and decoding pipeline before seeking.
-  Reset();
+  if (mSeekTask->NeedToResetMDSM()) { Reset(); }
 
   // Do the seek.
-  RefPtr<MediaDecoderStateMachine> self = this;
-  mSeekRequest.Begin(
-    mReaderWrapper->Seek(mCurrentSeek.mTarget, Duration())
-    ->Then(OwnerThread(), __func__,
-           [self] (media::TimeUnit) -> void {
-             self->mSeekRequest.Complete();
-             // We must decode the first samples of active streams, so we can determine
-             // the new stream time. So dispatch tasks to do that.
-             self->EnsureAudioDecodeTaskQueued();
-             self->EnsureVideoDecodeTaskQueued();
-           }, [self] (nsresult aResult) -> void {
-             self->mSeekRequest.Complete();
-             MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
-             self->DecodeError();
-           }));
+  mSeekTaskRequest.Begin(mSeekTask->Seek(Duration())
+    ->Then(OwnerThread(), __func__, this,
+           &MediaDecoderStateMachine::OnSeekTaskResolved,
+           &MediaDecoderStateMachine::OnSeekTaskRejected));
+}
+
+void
+MediaDecoderStateMachine::OnSeekTaskResolved(SeekTaskResolveValue aValue)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
+
+  mSeekTaskRequest.Complete();
+
+  if (aValue.mSeekedAudioData) {
+    Push(aValue.mSeekedAudioData.get(), MediaData::AUDIO_DATA);
+    mDecodedAudioEndTime =
+      std::max(aValue.mSeekedAudioData->GetEndTime(), mDecodedAudioEndTime);
+  }
+
+  if (aValue.mSeekedVideoData) {
+    Push(aValue.mSeekedVideoData.get(), MediaData::VIDEO_DATA);
+    mDecodedVideoEndTime =
+      std::max(aValue.mSeekedVideoData->GetEndTime(), mDecodedVideoEndTime);
+  }
+
+  if (aValue.mIsAudioQueueFinished) {
+    AudioQueue().Finish();
+    StopPrerollingAudio();
+  }
+
+  if (aValue.mIsVideoQueueFinished) {
+    VideoQueue().Finish();
+    StopPrerollingVideo();
+  }
+
+  if (aValue.mNeedToStopPrerollingAudio) {
+    StopPrerollingAudio();
+  }
+
+  if (aValue.mNeedToStopPrerollingVideo) {
+    StopPrerollingVideo();
+  }
+
+  SeekCompleted();
+
+  mSeekTask->Discard();
+  mSeekTask = nullptr;
+}
+
+void
+MediaDecoderStateMachine::OnSeekTaskRejected(SeekTaskRejectValue aValue)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
+
+  mSeekTaskRequest.Complete();
+
+  if (aValue.mIsAudioQueueFinished) {
+    AudioQueue().Finish();
+    StopPrerollingAudio();
+  }
+
+  if (aValue.mIsVideoQueueFinished) {
+    VideoQueue().Finish();
+    StopPrerollingVideo();
+  }
+
+  if (aValue.mNeedToStopPrerollingAudio) {
+    StopPrerollingAudio();
+  }
+
+  if (aValue.mNeedToStopPrerollingVideo) {
+    StopPrerollingVideo();
+  }
+
+  DecodeError();
+
+  mSeekTask->Discard();
+  mSeekTask = nullptr;
 }
 
 nsresult
 MediaDecoderStateMachine::DispatchAudioDecodeTaskIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (IsShutdown()) {
@@ -1675,39 +1588,40 @@ MediaDecoderStateMachine::DispatchAudioD
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::EnsureAudioDecodeTaskQueued()
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
               IsAudioDecoding(), AudioRequestStatus());
 
   if (mState != DECODER_STATE_DECODING &&
-      mState != DECODER_STATE_BUFFERING &&
-      mState != DECODER_STATE_SEEKING) {
+      mState != DECODER_STATE_BUFFERING) {
     return NS_OK;
   }
 
   if (!IsAudioDecoding() || mAudioDataRequest.Exists() ||
-      mAudioWaitRequest.Exists() || mSeekRequest.Exists()) {
+      mAudioWaitRequest.Exists()) {
     return NS_OK;
   }
 
   RequestAudioData();
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::RequestAudioData()
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
   mAudioDataRequest.Begin(
     mReaderWrapper->RequestAudioData()
     ->Then(OwnerThread(), __func__, this,
            &MediaDecoderStateMachine::OnAudioDecoded,
@@ -1729,50 +1643,50 @@ MediaDecoderStateMachine::DispatchVideoD
 
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachine::EnsureVideoDecodeTaskQueued()
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
              IsVideoDecoding(), VideoRequestStatus());
 
   if (mState != DECODER_STATE_DECODING &&
-      mState != DECODER_STATE_BUFFERING &&
-      mState != DECODER_STATE_SEEKING) {
+      mState != DECODER_STATE_BUFFERING) {
     return NS_OK;
   }
 
   if (!IsVideoDecoding() || mVideoDataRequest.Exists() ||
-      mVideoWaitRequest.Exists() || mSeekRequest.Exists()) {
+      mVideoWaitRequest.Exists()) {
     return NS_OK;
   }
 
   RequestVideoData();
   return NS_OK;
 }
 
 void
 MediaDecoderStateMachine::RequestVideoData()
 {
   MOZ_ASSERT(OnTaskQueue());
+  MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
 
   // Time the video decode, so that if it's slow, we can increase our low
   // audio threshold to reduce the chance of an audio underrun while we're
   // waiting for a video decode to complete.
   TimeStamp videoDecodeStartTime = TimeStamp::Now();
 
   bool skipToNextKeyFrame = mSentFirstFrameLoadedEvent &&
     NeedToSkipToNextKeyframe();
 
-  media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(
-    mState == DECODER_STATE_SEEKING ? 0 : GetMediaTime());
+  media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(GetMediaTime());
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
              currentTime.ToMicroseconds());
 
   RefPtr<MediaDecoderStateMachine> self = this;
   mVideoDataRequest.Begin(
     mReaderWrapper->RequestVideoData(skipToNextKeyFrame, currentTime)
@@ -2051,17 +1965,17 @@ MediaDecoderStateMachine::FinishDecodeFi
 }
 
 void
 MediaDecoderStateMachine::SeekCompleted()
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
 
-  int64_t seekTime = mCurrentSeek.mTarget.GetTime().ToMicroseconds();
+  int64_t seekTime = mSeekTask->GetSeekJob().mTarget.GetTime().ToMicroseconds();
   int64_t newCurrentTime = seekTime;
 
   // Setup timestamp state.
   RefPtr<MediaData> video = VideoQueue().PeekFront();
   if (seekTime == Duration().ToMicroseconds()) {
     newCurrentTime = seekTime;
   } else if (HasAudio()) {
     MediaData* audio = AudioQueue().PeekFront();
@@ -2096,17 +2010,17 @@ MediaDecoderStateMachine::SeekCompleted(
     nextState = DECODER_STATE_COMPLETED;
   } else {
     DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime);
     nextState = DECODER_STATE_DECODING;
   }
 
   // We want to resolve the seek request prior finishing the first frame
   // to ensure that the seeked event is fired prior loadeded.
-  mCurrentSeek.Resolve(nextState == DECODER_STATE_COMPLETED, __func__);
+  mSeekTask->GetSeekJob().Resolve(nextState == DECODER_STATE_COMPLETED, __func__);
 
   if (mDecodingFirstFrame) {
     // We were resuming from dormant, or initiated a seek early.
     // We can fire loadeddata now.
     FinishDecodeFirstFrame();
   }
 
   if (nextState == DECODER_STATE_DECODING) {
@@ -2346,26 +2260,23 @@ MediaDecoderStateMachine::Reset()
   StopMediaSink();
 
   mDecodedVideoEndTime = 0;
   mDecodedAudioEndTime = 0;
   mAudioCompleted = false;
   mVideoCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
-  mFirstVideoFrameAfterSeek = nullptr;
-  mDropAudioUntilNextDiscontinuity = true;
-  mDropVideoUntilNextDiscontinuity = true;
 
   mMetadataRequest.DisconnectIfExists();
   mAudioDataRequest.DisconnectIfExists();
   mAudioWaitRequest.DisconnectIfExists();
   mVideoDataRequest.DisconnectIfExists();
   mVideoWaitRequest.DisconnectIfExists();
-  mSeekRequest.DisconnectIfExists();
+  mSeekTaskRequest.DisconnectIfExists();
 
   mPlaybackOffset = 0;
 
   nsCOMPtr<nsIRunnable> resetTask =
     NS_NewRunnableMethod(mReader, &MediaDecoderReader::ResetDecode);
   DecodeTaskQueue()->Dispatch(resetTask.forget());
 }
 
@@ -2414,132 +2325,16 @@ MediaDecoderStateMachine::UpdatePlayback
   // Otherwise, MediaDecoder::AddOutputStream could kick in when we are outside
   // the monitor and get a staled value from GetCurrentTimeUs() which hits the
   // assertion in GetClock().
 
   int64_t delay = std::max<int64_t>(1, AUDIO_DURATION_USECS / mPlaybackRate);
   ScheduleStateMachineIn(delay);
 }
 
-nsresult
-MediaDecoderStateMachine::DropVideoUpToSeekTarget(MediaData* aSample)
-{
-  MOZ_ASSERT(OnTaskQueue());
-  RefPtr<VideoData> video(aSample->As<VideoData>());
-  MOZ_ASSERT(video);
-  DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
-              video->mTime, video->GetEndTime());
-  MOZ_ASSERT(mCurrentSeek.Exists());
-  const int64_t target = mCurrentSeek.mTarget.GetTime().ToMicroseconds();
-
-  // If the frame end time is less than the seek target, we won't want
-  // to display this frame after the seek, so discard it.
-  if (target >= video->GetEndTime()) {
-    DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
-                video->mTime, video->GetEndTime(), target);
-    mFirstVideoFrameAfterSeek = video;
-  } else {
-    if (target >= video->mTime && video->GetEndTime() >= target) {
-      // The seek target lies inside this frame's time slice. Adjust the frame's
-      // start time to match the seek target. We do this by replacing the
-      // first frame with a shallow copy which has the new timestamp.
-      RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video, target);
-      video = temp;
-    }
-    mFirstVideoFrameAfterSeek = nullptr;
-
-    DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
-                video->mTime, video->GetEndTime(), target);
-
-    MOZ_ASSERT(VideoQueue().GetSize() == 0, "Should be the 1st sample after seeking");
-    Push(video, MediaData::VIDEO_DATA);
-  }
-
-  return NS_OK;
-}
-
-nsresult
-MediaDecoderStateMachine::DropAudioUpToSeekTarget(MediaData* aSample)
-{
-  MOZ_ASSERT(OnTaskQueue());
-  RefPtr<AudioData> audio(aSample->As<AudioData>());
-  MOZ_ASSERT(audio &&
-             mCurrentSeek.Exists() &&
-             mCurrentSeek.mTarget.IsAccurate());
-
-  CheckedInt64 sampleDuration =
-    FramesToUsecs(audio->mFrames, mInfo.mAudio.mRate);
-  if (!sampleDuration.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-
-  if (audio->mTime + sampleDuration.value() <= mCurrentSeek.mTarget.GetTime().ToMicroseconds()) {
-    // Our seek target lies after the frames in this AudioData. Don't
-    // push it onto the audio queue, and keep decoding forwards.
-    return NS_OK;
-  }
-
-  if (audio->mTime > mCurrentSeek.mTarget.GetTime().ToMicroseconds()) {
-    // The seek target doesn't lie in the audio block just after the last
-    // audio frames we've seen which were before the seek target. This
-    // could have been the first audio data we've seen after seek, i.e. the
-    // seek terminated after the seek target in the audio stream. Just
-    // abort the audio decode-to-target, the state machine will play
-    // silence to cover the gap. Typically this happens in poorly muxed
-    // files.
-    DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
-    Push(audio, MediaData::AUDIO_DATA);
-    return NS_OK;
-  }
-
-  // The seek target lies somewhere in this AudioData's frames, strip off
-  // any frames which lie before the seek target, so we'll begin playback
-  // exactly at the seek target.
-  NS_ASSERTION(mCurrentSeek.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
-               "Target must at or be after data start.");
-  NS_ASSERTION(mCurrentSeek.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
-               "Data must end after target.");
-
-  CheckedInt64 framesToPrune =
-    UsecsToFrames(mCurrentSeek.mTarget.GetTime().ToMicroseconds() - audio->mTime, mInfo.mAudio.mRate);
-  if (!framesToPrune.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-  if (framesToPrune.value() > audio->mFrames) {
-    // We've messed up somehow. Don't try to trim frames, the |frames|
-    // variable below will overflow.
-    DECODER_WARN("Can't prune more frames that we have!");
-    return NS_ERROR_FAILURE;
-  }
-  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
-  uint32_t channels = audio->mChannels;
-  AlignedAudioBuffer audioData(frames * channels);
-  if (!audioData) {
-    return NS_ERROR_OUT_OF_MEMORY;
-  }
-  memcpy(audioData.get(),
-         audio->mAudioData.get() + (framesToPrune.value() * channels),
-         frames * channels * sizeof(AudioDataValue));
-  CheckedInt64 duration = FramesToUsecs(frames, mInfo.mAudio.mRate);
-  if (!duration.isValid()) {
-    return NS_ERROR_FAILURE;
-  }
-  RefPtr<AudioData> data(new AudioData(audio->mOffset,
-                                       mCurrentSeek.mTarget.GetTime().ToMicroseconds(),
-                                       duration.value(),
-                                       frames,
-                                       Move(audioData),
-                                       channels,
-                                       audio->mRate));
-  MOZ_ASSERT(AudioQueue().GetSize() == 0, "Should be the 1st sample after seeking");
-  Push(data, MediaData::AUDIO_DATA);
-
-  return NS_OK;
-}
-
 void MediaDecoderStateMachine::UpdateNextFrameStatus()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   MediaDecoderOwner::NextFrameStatus status;
   const char* statusString;
   if (mState <= DECODER_STATE_WAIT_FOR_CDM || IsDecodingFirstFrame()) {
     status = MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -90,16 +90,17 @@ hardware (via AudioStream).
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
 #include "MediaDecoderOwner.h"
 #include "MediaEventSource.h"
 #include "MediaMetadataManager.h"
 #include "MediaStatistics.h"
 #include "MediaTimer.h"
 #include "ImageContainer.h"
+#include "SeekTask.h"
 
 namespace mozilla {
 
 namespace media {
 class MediaSink;
 }
 
 class AudioSegment;
@@ -438,19 +439,16 @@ protected:
 
   // Return the current time, either the audio clock if available (if the media
   // has audio, and the playback is possible), or a clock for the video.
   // Called on the state machine thread.
   // If aTimeStamp is non-null, set *aTimeStamp to the TimeStamp corresponding
   // to the returned stream time.
   int64_t GetClock(TimeStamp* aTimeStamp = nullptr) const;
 
-  nsresult DropAudioUpToSeekTarget(MediaData* aSample);
-  nsresult DropVideoUpToSeekTarget(MediaData* aSample);
-
   void SetStartTime(int64_t aStartTimeUsecs);
 
   // Update only the state machine's current playback position (and duration,
   // if unknown).  Does not update the playback position on the decoder or
   // media element -- use UpdatePlaybackPosition for that.  Called on the state
   // machine thread, caller must hold the decoder lock.
   void UpdatePlaybackPositionInternal(int64_t aTime);
 
@@ -509,63 +507,16 @@ protected:
 
   // Dispatches a LoadedMetadataEvent.
   // This is threadsafe and can be called on any thread.
   // The decoder monitor must be held.
   void EnqueueLoadedMetadataEvent();
 
   void EnqueueFirstFrameLoadedEvent();
 
-  struct SeekJob {
-    SeekJob() {}
-
-    SeekJob(SeekJob&& aOther) : mTarget(aOther.mTarget)
-    {
-      aOther.mTarget.Reset();
-      mPromise = Move(aOther.mPromise);
-    }
-
-    SeekJob& operator=(SeekJob&& aOther)
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!Exists());
-      mTarget = aOther.mTarget;
-      aOther.mTarget.Reset();
-      mPromise = Move(aOther.mPromise);
-      return *this;
-    }
-
-    bool Exists()
-    {
-      MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
-      return mTarget.IsValid();
-    }
-
-    void Resolve(bool aAtEnd, const char* aCallSite)
-    {
-      mTarget.Reset();
-      MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
-      mPromise.Resolve(val, aCallSite);
-    }
-
-    void RejectIfExists(const char* aCallSite)
-    {
-      mTarget.Reset();
-      mPromise.RejectIfExists(true, aCallSite);
-    }
-
-    ~SeekJob()
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
-      MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
-    }
-
-    SeekTarget mTarget;
-    MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
-  };
-
   // Clears any previous seeking state and initiates a new see on the decoder.
   // The decoder monitor must be held.
   void InitiateSeek(SeekJob aSeekJob);
 
   nsresult DispatchAudioDecodeTaskIfNeeded();
 
   // Ensures a task to decode audio has been dispatched to the decode task queue.
   // If a task to decode has already been dispatched, this does nothing,
@@ -621,24 +572,16 @@ protected:
   // If so will trigger firing loadeddata event.
   // If there are any queued seek, will change state to DECODER_STATE_SEEKING
   // and return true.
   bool MaybeFinishDecodeFirstFrame();
   // Return true if we are currently decoding the first frames.
   bool IsDecodingFirstFrame();
   void FinishDecodeFirstFrame();
 
-  // Seeks to mSeekTarget. Called on the decode thread. The decoder monitor
-  // must be held with exactly one lock count.
-  void DecodeSeek();
-
-  void CheckIfSeekComplete();
-  bool IsAudioSeekComplete();
-  bool IsVideoSeekComplete();
-
   // Completes the seek operation, moves onto the next appropriate state.
   void SeekCompleted();
 
   // Queries our state to see whether the decode has finished for all streams.
   // If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
   // to run.
   // The decoder monitor must be held.
   void CheckIfDecodeComplete();
@@ -739,18 +682,22 @@ private:
     MOZ_ASSERT(OnTaskQueue());
     return mPlayState == MediaDecoder::PLAY_STATE_PLAYING ||
            mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING;
   }
 
   // Queued seek - moves to mCurrentSeek when DecodeFirstFrame completes.
   SeekJob mQueuedSeek;
 
-  // The position that we're currently seeking to.
-  SeekJob mCurrentSeek;
+  // mSeekTask is responsible for executing the current seek request.
+  RefPtr<media::SeekTask> mSeekTask;
+  MozPromiseRequestHolder<media::SeekTask::SeekTaskPromise> mSeekTaskRequest;
+
+  void OnSeekTaskResolved(media::SeekTaskResolveValue aValue);
+  void OnSeekTaskRejected(media::SeekTaskRejectValue aValue);
 
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
 
   // The reader, don't call its methods with the decoder monitor held.
@@ -860,22 +807,16 @@ private:
   {
     MOZ_ASSERT(OnTaskQueue());
     if (mIsVideoPrerolling) {
       mIsVideoPrerolling = false;
       ScheduleStateMachine();
     }
   }
 
-  // This temporarily stores the first frame we decode after we seek.
-  // This is so that if we hit end of stream while we're decoding to reach
-  // the seek target, we will still have a frame that we can display as the
-  // last frame in the media.
-  RefPtr<MediaData> mFirstVideoFrameAfterSeek;
-
   // When we start decoding (either for the first time, or after a pause)
   // we may be low on decoded data. We don't want our "low data" logic to
   // kick in and decide that we're low on decoded data because the download
   // can't keep up with the decode, and cause us to pause playback. So we
   // have a "preroll" stage, where we ignore the results of our "low data"
   // logic during the first few frames of our decode. This occurs during
   // playback. The flags below are true when the corresponding stream is
   // being "prerolled".
@@ -971,31 +912,16 @@ private:
   // memory and CPU overhead.
   bool mMinimizePreroll;
 
   // True if the decode thread has gone filled its buffers and is now
   // waiting to be awakened before it continues decoding. Synchronized
   // by the decoder monitor.
   bool mDecodeThreadWaiting;
 
-  // These two flags are true when we need to drop decoded samples that
-  // we receive up to the next discontinuity. We do this when we seek;
-  // the first sample in each stream after the seek is marked as being
-  // a "discontinuity".
-  bool mDropAudioUntilNextDiscontinuity;
-  bool mDropVideoUntilNextDiscontinuity;
-
-  // Track the current seek promise made by the reader.
-  MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
-
-  // We record the playback position before we seek in order to
-  // determine where the seek terminated relative to the playback position
-  // we were at before the seek.
-  int64_t mCurrentTimeBeforeSeek;
-
   // Track our request for metadata from the reader.
   MozPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
 
   // Stores presentation info required for playback. The decoder monitor
   // must be held when accessing this.
   MediaInfo mInfo;
 
   nsAutoPtr<MetadataTags> mMetadataTags;
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
 #include "nsPrintfCString.h"
 #include "nsSize.h"
 #include "Layers.h"
 #include "MediaData.h"
 #include "MediaInfo.h"
 #include "MediaFormatReader.h"
 #include "MediaResource.h"
new file mode 100644
--- /dev/null
+++ b/dom/media/SeekJob.cpp
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "SeekJob.h"
+
+namespace mozilla {
+
+SeekJob::SeekJob()
+{
+}
+
+SeekJob::SeekJob(SeekJob&& aOther) : mTarget(aOther.mTarget)
+{
+  aOther.mTarget.Reset();
+  mPromise = Move(aOther.mPromise);
+}
+
+SeekJob::~SeekJob()
+{
+  MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
+  MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
+}
+
+SeekJob& SeekJob::operator=(SeekJob&& aOther)
+{
+  MOZ_DIAGNOSTIC_ASSERT(!Exists());
+  mTarget = aOther.mTarget;
+  aOther.mTarget.Reset();
+  mPromise = Move(aOther.mPromise);
+  return *this;
+}
+
+bool SeekJob::Exists()
+{
+  MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
+  return mTarget.IsValid();
+}
+
+void SeekJob::Resolve(bool aAtEnd, const char* aCallSite)
+{
+  MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
+  mPromise.Resolve(val, aCallSite);
+  mTarget.Reset();
+}
+
+void SeekJob::RejectIfExists(const char* aCallSite)
+{
+  mTarget.Reset();
+  mPromise.RejectIfExists(true, aCallSite);
+}
+
+} // namespace mozilla
copy from dom/media/MediaDecoderStateMachine.h
copy to dom/media/SeekJob.h
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/SeekJob.h
@@ -1,1157 +1,38 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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/. */
-/*
-
-Each media element for a media file has one thread called the "audio thread".
-
-The audio thread  writes the decoded audio data to the audio
-hardware. This is done in a separate thread to ensure that the
-audio hardware gets a constant stream of data without
-interruption due to decoding or display. At some point
-AudioStream will be refactored to have a callback interface
-where it asks for data and this thread will no longer be
-needed.
-
-The element/state machine also has a TaskQueue which runs in a
-SharedThreadPool that is shared with all other elements/decoders. The state
-machine dispatches tasks to this to call into the MediaDecoderReader to
-request decoded audio or video data. The Reader will callback with decoded
-sampled when it has them available, and the state machine places the decoded
-samples into its queues for the consuming threads to pull from.
-
-The MediaDecoderReader can choose to decode asynchronously, or synchronously
-and return requested samples synchronously inside it's Request*Data()
-functions via callback. Asynchronous decoding is preferred, and should be
-used for any new readers.
-
-Synchronisation of state between the thread is done via a monitor owned
-by MediaDecoder.
-
-The lifetime of the audio thread is controlled by the state machine when
-it runs on the shared state machine thread. When playback needs to occur
-the audio thread is created and an event dispatched to run it. The audio
-thread exits when audio playback is completed or no longer required.
-
-A/V synchronisation is handled by the state machine. It examines the audio
-playback time and compares this to the next frame in the queue of video
-frames. If it is time to play the video frame it is then displayed, otherwise
-it schedules the state machine to run again at the time of the next frame.
-
-Frame skipping is done in the following ways:
 
-  1) The state machine will skip all frames in the video queue whose
-     display time is less than the current audio time. This ensures
-     the correct frame for the current time is always displayed.
-
-  2) The decode tasks will stop decoding interframes and read to the
-     next keyframe if it determines that decoding the remaining
-     interframes will cause playback issues. It detects this by:
-       a) If the amount of audio data in the audio queue drops
-          below a threshold whereby audio may start to skip.
-       b) If the video queue drops below a threshold where it
-          will be decoding video data that won't be displayed due
-          to the decode thread dropping the frame immediately.
-     TODO: In future we should only do this when the Reader is decoding
-           synchronously.
-
-When hardware accelerated graphics is not available, YCbCr conversion
-is done on the decode task queue when video frames are decoded.
+#ifndef SEEK_JOB_H
+#define SEEK_JOB_H
 
-The decode task queue pushes decoded audio and videos frames into two
-separate queues - one for audio and one for video. These are kept
-separate to make it easy to constantly feed audio data to the audio
-hardware while allowing frame skipping of video data. These queues are
-threadsafe, and neither the decode, audio, or state machine should
-be able to monopolize them, and cause starvation of the other threads.
-
-Both queues are bounded by a maximum size. When this size is reached
-the decode tasks will no longer request video or audio depending on the
-queue that has reached the threshold. If both queues are full, no more
-decode tasks will be dispatched to the decode task queue, so other
-decoders will have an opportunity to run.
-
-During playback the audio thread will be idle (via a Wait() on the
-monitor) if the audio queue is empty. Otherwise it constantly pops
-audio data off the queue and plays it with a blocking write to the audio
-hardware (via AudioStream).
-
-*/
-#if !defined(MediaDecoderStateMachine_h__)
-#define MediaDecoderStateMachine_h__
-
-#include "mozilla/Attributes.h"
-#include "mozilla/ReentrantMonitor.h"
-#include "mozilla/StateMirroring.h"
-
-#include "nsThreadUtils.h"
+#include "mozilla/MozPromise.h"
 #include "MediaDecoder.h"
 #include "MediaDecoderReader.h"
-#include "MediaDecoderOwner.h"
-#include "MediaEventSource.h"
-#include "MediaMetadataManager.h"
-#include "MediaStatistics.h"
-#include "MediaTimer.h"
-#include "ImageContainer.h"
+#include "SeekTarget.h"
 
 namespace mozilla {
 
-namespace media {
-class MediaSink;
-}
-
-class AudioSegment;
-class DecodedStream;
-class MediaDecoderReaderWrapper;
-class OutputStreamManager;
-class TaskQueue;
-
-extern LazyLogModule gMediaDecoderLog;
-extern LazyLogModule gMediaSampleLog;
-
-enum class MediaEventType : int8_t {
-  PlaybackStarted,
-  PlaybackStopped,
-  PlaybackEnded,
-  DecodeError,
-  Invalidate
-};
-
-/*
-  The state machine class. This manages the decoding and seeking in the
-  MediaDecoderReader on the decode task queue, and A/V sync on the shared
-  state machine thread, and controls the audio "push" thread.
-
-  All internal state is synchronised via the decoder monitor. State changes
-  are propagated by scheduling the state machine to run another cycle on the
-  shared state machine thread.
-
-  See MediaDecoder.h for more details.
-*/
-class MediaDecoderStateMachine
-{
-  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderStateMachine)
-public:
-  typedef MediaDecoderReader::AudioDataPromise AudioDataPromise;
-  typedef MediaDecoderReader::VideoDataPromise VideoDataPromise;
-  typedef MediaDecoderOwner::NextFrameStatus NextFrameStatus;
-  typedef mozilla::layers::ImageContainer::FrameID FrameID;
-  MediaDecoderStateMachine(MediaDecoder* aDecoder,
-                           MediaDecoderReader* aReader,
-                           bool aRealTime = false);
-
-  nsresult Init(MediaDecoder* aDecoder);
-
-  // Enumeration for the valid decoding states
-  enum State {
-    DECODER_STATE_DECODING_METADATA,
-    DECODER_STATE_WAIT_FOR_CDM,
-    DECODER_STATE_DORMANT,
-    DECODER_STATE_DECODING,
-    DECODER_STATE_SEEKING,
-    DECODER_STATE_BUFFERING,
-    DECODER_STATE_COMPLETED,
-    DECODER_STATE_SHUTDOWN,
-    DECODER_STATE_ERROR
-  };
-
-  void AddOutputStream(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
-  // Remove an output stream added with AddOutputStream.
-  void RemoveOutputStream(MediaStream* aStream);
-
-  // Seeks to the decoder to aTarget asynchronously.
-  RefPtr<MediaDecoder::SeekPromise> InvokeSeek(SeekTarget aTarget);
-
-  // Set/Unset dormant state.
-  void DispatchSetDormant(bool aDormant);
-
-  RefPtr<ShutdownPromise> BeginShutdown();
-
-  // Notifies the state machine that should minimize the number of samples
-  // decoded we preroll, until playback starts. The first time playback starts
-  // the state machine is free to return to prerolling normally. Note
-  // "prerolling" in this context refers to when we decode and buffer decoded
-  // samples in advance of when they're needed for playback.
-  void DispatchMinimizePrerollUntilPlaybackStarts()
-  {
-    RefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
-    {
-      MOZ_ASSERT(self->OnTaskQueue());
-      self->mMinimizePreroll = true;
-
-      // Make sure that this arrives before playback starts, otherwise this won't
-      // have the intended effect.
-      MOZ_DIAGNOSTIC_ASSERT(self->mPlayState == MediaDecoder::PLAY_STATE_LOADING);
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
-  // Set the media fragment end time. aEndTime is in microseconds.
-  void DispatchSetFragmentEndTime(int64_t aEndTime)
-  {
-    RefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self, aEndTime] () {
-      self->mFragmentEndTime = aEndTime;
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
-  void DispatchAudioOffloading(bool aAudioOffloading)
-  {
-    RefPtr<MediaDecoderStateMachine> self = this;
-    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([=] () {
-      if (self->mAudioOffloading != aAudioOffloading) {
-        self->mAudioOffloading = aAudioOffloading;
-        self->ScheduleStateMachine();
-      }
-    });
-    OwnerThread()->Dispatch(r.forget());
-  }
-
-  // Drop reference to mResource. Only called during shutdown dance.
-  void BreakCycles() {
-    MOZ_ASSERT(NS_IsMainThread());
-    mReader->BreakCycles();
-    mResource = nullptr;
-  }
-
-  TimedMetadataEventSource& TimedMetadataEvent() {
-    return mMetadataManager.TimedMetadataEvent();
-  }
-
-  MediaEventSource<void>& OnMediaNotSeekable() {
-    return mReader->OnMediaNotSeekable();
-  }
-
-  MediaEventSourceExc<nsAutoPtr<MediaInfo>,
-                      nsAutoPtr<MetadataTags>,
-                      MediaDecoderEventVisibility>&
-  MetadataLoadedEvent() { return mMetadataLoadedEvent; }
-
-  MediaEventSourceExc<nsAutoPtr<MediaInfo>,
-                      MediaDecoderEventVisibility>&
-  FirstFrameLoadedEvent() { return mFirstFrameLoadedEvent; }
-
-  MediaEventSource<MediaEventType>&
-  OnPlaybackEvent() { return mOnPlaybackEvent; }
-
-  MediaEventSource<MediaDecoderEventVisibility>&
-  OnSeekingStart() { return mOnSeekingStart; }
-
-  // Immutable after construction - may be called on any thread.
-  bool IsRealTime() const { return mRealTime; }
-
-  size_t SizeOfVideoQueue() {
-    return mReader->SizeOfVideoQueueInBytes();
-  }
-
-  size_t SizeOfAudioQueue() {
-    return mReader->SizeOfAudioQueueInBytes();
-  }
-
-private:
-  // Functions used by assertions to ensure we're calling things
-  // on the appropriate threads.
-  bool OnTaskQueue() const;
-
-  // Initialization that needs to happen on the task queue. This is the first
-  // task that gets run on the task queue, and is dispatched from the MDSM
-  // constructor immediately after the task queue is created.
-  void InitializationTask(MediaDecoder* aDecoder);
-
-  void SetDormant(bool aDormant);
-
-  void SetAudioCaptured(bool aCaptured);
-
-  void ReadMetadata();
-
-  RefPtr<MediaDecoder::SeekPromise> Seek(SeekTarget aTarget);
-
-  RefPtr<ShutdownPromise> Shutdown();
-
-  RefPtr<ShutdownPromise> FinishShutdown();
-
-  // Update the playback position. This can result in a timeupdate event
-  // and an invalidate of the frame being dispatched asynchronously if
-  // there is no such event currently queued.
-  // Only called on the decoder thread. Must be called with
-  // the decode monitor held.
-  void UpdatePlaybackPosition(int64_t aTime);
-
-  // Causes the state machine to switch to buffering state, and to
-  // immediately stop playback and buffer downloaded data. Called on
-  // the state machine thread.
-  void StartBuffering();
-
-  bool CanPlayThrough();
-
-  MediaStatistics GetStatistics();
-
-  // This is called on the state machine thread and audio thread.
-  // The decoder monitor must be obtained before calling this.
-  bool HasAudio() const {
-    MOZ_ASSERT(OnTaskQueue());
-    return mInfo.HasAudio();
-  }
-
-  // This is called on the state machine thread and audio thread.
-  // The decoder monitor must be obtained before calling this.
-  bool HasVideo() const {
-    MOZ_ASSERT(OnTaskQueue());
-    return mInfo.HasVideo();
-  }
-
-  // Should be called by main thread.
-  bool HaveNextFrameData();
-
-  // Must be called with the decode monitor held.
-  bool IsBuffering() const {
-    MOZ_ASSERT(OnTaskQueue());
-    return mState == DECODER_STATE_BUFFERING;
-  }
-
-  // Must be called with the decode monitor held.
-  bool IsSeeking() const {
-    MOZ_ASSERT(OnTaskQueue());
-    return mState == DECODER_STATE_SEEKING;
-  }
-
-  // Returns the state machine task queue.
-  TaskQueue* OwnerThread() const { return mTaskQueue; }
-
-  // Schedules the shared state machine thread to run the state machine.
-  void ScheduleStateMachine();
-
-  // Invokes ScheduleStateMachine to run in |aMicroseconds| microseconds,
-  // unless it's already scheduled to run earlier, in which case the
-  // request is discarded.
-  void ScheduleStateMachineIn(int64_t aMicroseconds);
-
-  void OnDelayedSchedule()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    mDelayedScheduler.CompleteRequest();
-    ScheduleStateMachine();
-  }
-
-  void NotReached() { MOZ_DIAGNOSTIC_ASSERT(false); }
-
-  // Discard audio/video data that are already played by MSG.
-  void DiscardStreamData();
-  bool HaveEnoughDecodedAudio();
-  bool HaveEnoughDecodedVideo();
-
-  // Returns true if the state machine has shutdown or is in the process of
-  // shutting down. The decoder monitor must be held while calling this.
-  bool IsShutdown();
-
-  // Returns true if we're currently playing. The decoder monitor must
-  // be held.
-  bool IsPlaying() const;
+struct SeekJob {
+  SeekJob();
 
-  // TODO: Those callback function may receive demuxed-only data.
-  // Need to figure out a suitable API name for this case.
-  void OnAudioDecoded(MediaData* aAudioSample);
-  void OnVideoDecoded(MediaData* aVideoSample, TimeStamp aDecodeStartTime);
-  void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
-  void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    OnNotDecoded(MediaData::AUDIO_DATA, aReason);
-  }
-  void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    OnNotDecoded(MediaData::VIDEO_DATA, aReason);
-  }
-
-  // Resets all state related to decoding and playback, emptying all buffers
-  // and aborting all pending operations on the decode task queue.
-  void Reset();
-
-protected:
-  virtual ~MediaDecoderStateMachine();
-
-  void SetState(State aState);
-
-  void BufferedRangeUpdated();
-
-  // Inserts MediaData* samples into their respective MediaQueues.
-  // aSample must not be null.
-
-  void Push(MediaData* aSample, MediaData::Type aSampleType);
-
-  void OnAudioPopped(const RefPtr<MediaData>& aSample);
-  void OnVideoPopped(const RefPtr<MediaData>& aSample);
-
-  void CheckIsAudible(const MediaData* aSample);
-  void VolumeChanged();
-  void LogicalPlaybackRateChanged();
-  void PreservesPitchChanged();
-
-  MediaQueue<MediaData>& AudioQueue() { return mAudioQueue; }
-  MediaQueue<MediaData>& VideoQueue() { return mVideoQueue; }
-
-  // True if our buffers of decoded audio are not full, and we should
-  // decode more.
-  bool NeedToDecodeAudio();
-
-  // True if our buffers of decoded video are not full, and we should
-  // decode more.
-  bool NeedToDecodeVideo();
-
-  // Returns true if we've got less than aAudioUsecs microseconds of decoded
-  // and playable data. The decoder monitor must be held.
-  //
-  // May not be invoked when mReader->UseBufferingHeuristics() is false.
-  bool HasLowDecodedData(int64_t aAudioUsecs);
-
-  bool OutOfDecodedAudio();
-
-  bool OutOfDecodedVideo()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return IsVideoDecoding() && !VideoQueue().IsFinished() && VideoQueue().GetSize() <= 1;
-  }
-
-
-  // Returns true if we're running low on data which is not yet decoded.
-  // The decoder monitor must be held.
-  bool HasLowUndecodedData();
-
-  // Returns true if we have less than aUsecs of undecoded data available.
-  bool HasLowUndecodedData(int64_t aUsecs);
-
-  // Returns true when there's decoded audio waiting to play.
-  // The decoder monitor must be held.
-  bool HasFutureAudio();
-
-  // Returns true if we recently exited "quick buffering" mode.
-  bool JustExitedQuickBuffering();
-
-  // Recomputes mNextFrameStatus, possibly dispatching notifications to interested
-  // parties.
-  void UpdateNextFrameStatus();
-
-  // Return the current time, either the audio clock if available (if the media
-  // has audio, and the playback is possible), or a clock for the video.
-  // Called on the state machine thread.
-  // If aTimeStamp is non-null, set *aTimeStamp to the TimeStamp corresponding
-  // to the returned stream time.
-  int64_t GetClock(TimeStamp* aTimeStamp = nullptr) const;
-
-  nsresult DropAudioUpToSeekTarget(MediaData* aSample);
-  nsresult DropVideoUpToSeekTarget(MediaData* aSample);
-
-  void SetStartTime(int64_t aStartTimeUsecs);
-
-  // Update only the state machine's current playback position (and duration,
-  // if unknown).  Does not update the playback position on the decoder or
-  // media element -- use UpdatePlaybackPosition for that.  Called on the state
-  // machine thread, caller must hold the decoder lock.
-  void UpdatePlaybackPositionInternal(int64_t aTime);
-
-  // Update playback position and trigger next update by default time period.
-  // Called on the state machine thread.
-  void UpdatePlaybackPositionPeriodically();
-
-  media::MediaSink* CreateAudioSink();
-
-  // Always create mediasink which contains an AudioSink or StreamSink inside.
-  already_AddRefed<media::MediaSink> CreateMediaSink(bool aAudioCaptured);
-
-  // Stops the media sink and shut it down.
-  // The decoder monitor must be held with exactly one lock count.
-  // Called on the state machine thread.
-  void StopMediaSink();
-
-  // Create and start the media sink.
-  // The decoder monitor must be held with exactly one lock count.
-  // Called on the state machine thread.
-  void StartMediaSink();
-
-  // Notification method invoked when mPlayState changes.
-  void PlayStateChanged();
-
-  // Notification method invoked when mLogicallySeeking changes.
-  void LogicallySeekingChanged();
-
-  // Sets internal state which causes playback of media to pause.
-  // The decoder monitor must be held.
-  void StopPlayback();
+  SeekJob(SeekJob&& aOther);
 
-  // If the conditions are right, sets internal state which causes playback
-  // of media to begin or resume.
-  // Must be called with the decode monitor held.
-  void MaybeStartPlayback();
-
-  // Check to see if we don't have enough data to play up to the next frame.
-  // If we don't, switch to buffering mode.
-  void MaybeStartBuffering();
-
-  // Moves the decoder into decoding state. Called on the state machine
-  // thread. The decoder monitor must be held.
-  void StartDecoding();
-
-  // Moves the decoder into the shutdown state, and dispatches an error
-  // event to the media element. This begins shutting down the decoder.
-  // The decoder monitor must be held. This is only called on the
-  // decode thread.
-  void DecodeError();
-
-  // Dispatches a task to the decode task queue to begin decoding metadata.
-  // This is threadsafe and can be called on any thread.
-  // The decoder monitor must be held.
-  nsresult EnqueueDecodeMetadataTask();
-
-  // Dispatches a LoadedMetadataEvent.
-  // This is threadsafe and can be called on any thread.
-  // The decoder monitor must be held.
-  void EnqueueLoadedMetadataEvent();
-
-  void EnqueueFirstFrameLoadedEvent();
-
-  struct SeekJob {
-    SeekJob() {}
-
-    SeekJob(SeekJob&& aOther) : mTarget(aOther.mTarget)
-    {
-      aOther.mTarget.Reset();
-      mPromise = Move(aOther.mPromise);
-    }
-
-    SeekJob& operator=(SeekJob&& aOther)
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!Exists());
-      mTarget = aOther.mTarget;
-      aOther.mTarget.Reset();
-      mPromise = Move(aOther.mPromise);
-      return *this;
-    }
-
-    bool Exists()
-    {
-      MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
-      return mTarget.IsValid();
-    }
-
-    void Resolve(bool aAtEnd, const char* aCallSite)
-    {
-      mTarget.Reset();
-      MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
-      mPromise.Resolve(val, aCallSite);
-    }
+  SeekJob& operator=(SeekJob&& aOther);
 
-    void RejectIfExists(const char* aCallSite)
-    {
-      mTarget.Reset();
-      mPromise.RejectIfExists(true, aCallSite);
-    }
-
-    ~SeekJob()
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
-      MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
-    }
-
-    SeekTarget mTarget;
-    MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
-  };
-
-  // Clears any previous seeking state and initiates a new see on the decoder.
-  // The decoder monitor must be held.
-  void InitiateSeek(SeekJob aSeekJob);
-
-  nsresult DispatchAudioDecodeTaskIfNeeded();
-
-  // Ensures a task to decode audio has been dispatched to the decode task queue.
-  // If a task to decode has already been dispatched, this does nothing,
-  // otherwise this dispatches a task to do the decode.
-  // This is called on the state machine or decode threads.
-  // The decoder monitor must be held.
-  nsresult EnsureAudioDecodeTaskQueued();
-  // Start a task to decode audio.
-  // The decoder monitor must be held.
-  void RequestAudioData();
-
-  nsresult DispatchVideoDecodeTaskIfNeeded();
-
-  // Ensures a task to decode video has been dispatched to the decode task queue.
-  // If a task to decode has already been dispatched, this does nothing,
-  // otherwise this dispatches a task to do the decode.
-  // The decoder monitor must be held.
-  nsresult EnsureVideoDecodeTaskQueued();
-  // Start a task to decode video.
-  // The decoder monitor must be held.
-  void RequestVideoData();
-
-  // Re-evaluates the state and determines whether we need to dispatch
-  // events to run the decode, or if not whether we should set the reader
-  // to idle mode. This is threadsafe, and can be called from any thread.
-  // The decoder monitor must be held.
-  void DispatchDecodeTasksIfNeeded();
-
-  // Returns the "media time". This is the absolute time which the media
-  // playback has reached. i.e. this returns values in the range
-  // [mStartTime, mEndTime], and mStartTime will not be 0 if the media does
-  // not start at 0. Note this is different than the "current playback position",
-  // which is in the range [0,duration].
-  int64_t GetMediaTime() const {
-    MOZ_ASSERT(OnTaskQueue());
-    return mCurrentPosition;
-  }
-
-  // Returns an upper bound on the number of microseconds of audio that is
-  // decoded and playable. This is the sum of the number of usecs of audio which
-  // is decoded and in the reader's audio queue, and the usecs of unplayed audio
-  // which has been pushed to the audio hardware for playback. Note that after
-  // calling this, the audio hardware may play some of the audio pushed to
-  // hardware, so this can only be used as a upper bound. The decoder monitor
-  // must be held when calling this. Called on the decode thread.
-  int64_t GetDecodedAudioDuration();
-
-  // Promise callbacks for metadata reading.
-  void OnMetadataRead(MetadataHolder* aMetadata);
-  void OnMetadataNotRead(ReadMetadataFailureReason aReason);
+  bool Exists();
 
-  // Checks whether we're finished decoding first audio and/or video packets.
-  // If so will trigger firing loadeddata event.
-  // If there are any queued seek, will change state to DECODER_STATE_SEEKING
-  // and return true.
-  bool MaybeFinishDecodeFirstFrame();
-  // Return true if we are currently decoding the first frames.
-  bool IsDecodingFirstFrame();
-  void FinishDecodeFirstFrame();
-
-  // Seeks to mSeekTarget. Called on the decode thread. The decoder monitor
-  // must be held with exactly one lock count.
-  void DecodeSeek();
-
-  void CheckIfSeekComplete();
-  bool IsAudioSeekComplete();
-  bool IsVideoSeekComplete();
-
-  // Completes the seek operation, moves onto the next appropriate state.
-  void SeekCompleted();
-
-  // Queries our state to see whether the decode has finished for all streams.
-  // If so, we move into DECODER_STATE_COMPLETED and schedule the state machine
-  // to run.
-  // The decoder monitor must be held.
-  void CheckIfDecodeComplete();
-
-  // Performs one "cycle" of the state machine. Polls the state, and may send
-  // a video frame to be displayed, and generally manages the decode. Called
-  // periodically via timer to ensure the video stays in sync.
-  nsresult RunStateMachine();
-
-  bool IsStateMachineScheduled() const;
-
-  // Returns true if we're not playing and the decode thread has filled its
-  // decode buffers and is waiting. We can shut the decode thread down in this
-  // case as it may not be needed again.
-  bool IsPausedAndDecoderWaiting();
-
-  // These return true if the respective stream's decode has not yet reached
-  // the end of stream.
-  bool IsAudioDecoding();
-  bool IsVideoDecoding();
-
-private:
-  // Resolved by the MediaSink to signal that all audio/video outstanding
-  // work is complete and identify which part(a/v) of the sink is shutting down.
-  void OnMediaSinkAudioComplete();
-  void OnMediaSinkVideoComplete();
-
-  // Rejected by the MediaSink to signal errors for audio/video.
-  void OnMediaSinkAudioError();
-  void OnMediaSinkVideoError();
-
-  // Return true if the video decoder's decode speed can not catch up the
-  // play time.
-  bool NeedToSkipToNextKeyframe();
-
-  void* const mDecoderID;
-  const RefPtr<FrameStatistics> mFrameStats;
-  const RefPtr<VideoFrameContainer> mVideoFrameContainer;
-  const dom::AudioChannel mAudioChannel;
-
-  // Task queue for running the state machine.
-  RefPtr<TaskQueue> mTaskQueue;
-
-  // State-watching manager.
-  WatchManager<MediaDecoderStateMachine> mWatchManager;
-
-  // True is we are decoding a realtime stream, like a camera stream.
-  const bool mRealTime;
-
-  // True if we've dispatched a task to run the state machine but the task has
-  // yet to run.
-  bool mDispatchedStateMachine;
-
-  // Used to dispatch another round schedule with specific target time.
-  DelayedScheduler mDelayedScheduler;
-
-  // Queue of audio frames. This queue is threadsafe, and is accessed from
-  // the audio, decoder, state machine, and main threads.
-  MediaQueue<MediaData> mAudioQueue;
-  // Queue of video frames. This queue is threadsafe, and is accessed from
-  // the decoder, state machine, and main threads.
-  MediaQueue<MediaData> mVideoQueue;
-
-  // The decoder monitor must be obtained before modifying this state.
-  // Accessed on state machine, audio, main, and AV thread.
-  Watchable<State> mState;
-
-  // The task queue in which we run decode tasks. This is referred to as
-  // the "decode thread", though in practise tasks can run on a different
-  // thread every time they're called.
-  TaskQueue* DecodeTaskQueue() const { return mReader->OwnerThread(); }
-
-  // Time that buffering started. Used for buffering timeout and only
-  // accessed on the state machine thread. This is null while we're not
-  // buffering.
-  TimeStamp mBufferingStart;
-
-  media::TimeUnit Duration() const { MOZ_ASSERT(OnTaskQueue()); return mDuration.Ref().ref(); }
-
-  // Recomputes the canonical duration from various sources.
-  void RecomputeDuration();
-
-
-  // FrameID which increments every time a frame is pushed to our queue.
-  FrameID mCurrentFrameID;
-
-  // The highest timestamp that our position has reached. Monotonically
-  // increasing.
-  Watchable<media::TimeUnit> mObservedDuration;
-
-  // Returns true if we're logically playing, that is, if the Play() has
-  // been called and Pause() has not or we have not yet reached the end
-  // of media. This is irrespective of the seeking state; if the owner
-  // calls Play() and then Seek(), we still count as logically playing.
-  // The decoder monitor must be held.
-  bool IsLogicallyPlaying()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return mPlayState == MediaDecoder::PLAY_STATE_PLAYING ||
-           mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING;
-  }
-
-  // Queued seek - moves to mCurrentSeek when DecodeFirstFrame completes.
-  SeekJob mQueuedSeek;
-
-  // The position that we're currently seeking to.
-  SeekJob mCurrentSeek;
+  void Resolve(bool aAtEnd, const char* aCallSite);
 
-  // Media Fragment end time in microseconds. Access controlled by decoder monitor.
-  int64_t mFragmentEndTime;
-
-  // The media sink resource.  Used on the state machine thread.
-  RefPtr<media::MediaSink> mMediaSink;
-
-  // The reader, don't call its methods with the decoder monitor held.
-  // This is created in the state machine's constructor.
-  const RefPtr<MediaDecoderReader> mReader;
-
-  const RefPtr<MediaDecoderReaderWrapper> mReaderWrapper;
-
-  // The end time of the last audio frame that's been pushed onto the media sink
-  // in microseconds. This will approximately be the end time
-  // of the audio stream, unless another frame is pushed to the hardware.
-  int64_t AudioEndTime() const;
-
-  // The end time of the last rendered video frame that's been sent to
-  // compositor.
-  int64_t VideoEndTime() const;
-
-  // The end time of the last decoded audio frame. This signifies the end of
-  // decoded audio data. Used to check if we are low in decoded data.
-  int64_t mDecodedAudioEndTime;
-
-  // The end time of the last decoded video frame. Used to check if we are low
-  // on decoded video data.
-  int64_t mDecodedVideoEndTime;
-
-  // Playback rate. 1.0 : normal speed, 0.5 : two times slower.
-  double mPlaybackRate;
-
-  // Time at which we started decoding. Synchronised via decoder monitor.
-  TimeStamp mDecodeStartTime;
-
-  // The maximum number of second we spend buffering when we are short on
-  // unbuffered data.
-  uint32_t mBufferingWait;
-  int64_t  mLowDataThresholdUsecs;
-
-  // If we've got more than this number of decoded video frames waiting in
-  // the video queue, we will not decode any more video frames until some have
-  // been consumed by the play state machine thread.
-  // Must hold monitor.
-  uint32_t GetAmpleVideoFrames() const;
-
-  // Low audio threshold. If we've decoded less than this much audio we
-  // consider our audio decode "behind", and we may skip video decoding
-  // in order to allow our audio decoding to catch up. We favour audio
-  // decoding over video. We increase this threshold if we're slow to
-  // decode video frames, in order to reduce the chance of audio underruns.
-  // Note that we don't ever reset this threshold, it only ever grows as
-  // we detect that the decode can't keep up with rendering.
-  int64_t mLowAudioThresholdUsecs;
-
-  // Our "ample" audio threshold. Once we've this much audio decoded, we
-  // pause decoding. If we increase mLowAudioThresholdUsecs, we'll also
-  // increase this too appropriately (we don't want mLowAudioThresholdUsecs
-  // to be greater than ampleAudioThreshold, else we'd stop decoding!).
-  // Note that we don't ever reset this threshold, it only ever grows as
-  // we detect that the decode can't keep up with rendering.
-  int64_t mAmpleAudioThresholdUsecs;
-
-  // If we're quick buffering, we'll remain in buffering mode while we have less than
-  // QUICK_BUFFERING_LOW_DATA_USECS of decoded data available.
-  int64_t mQuickBufferingLowDataThresholdUsecs;
-
-  // At the start of decoding we want to "preroll" the decode until we've
-  // got a few frames decoded before we consider whether decode is falling
-  // behind. Otherwise our "we're falling behind" logic will trigger
-  // unneccessarily if we start playing as soon as the first sample is
-  // decoded. These two fields store how many video frames and audio
-  // samples we must consume before are considered to be finished prerolling.
-  uint32_t AudioPrerollUsecs() const
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs / 2;
-  }
-
-  uint32_t VideoPrerollFrames() const
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return IsRealTime() ? 0 : GetAmpleVideoFrames() / 2;
-  }
-
-  bool DonePrerollingAudio()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return !IsAudioDecoding() ||
-        GetDecodedAudioDuration() >= AudioPrerollUsecs() * mPlaybackRate;
-  }
-
-  bool DonePrerollingVideo()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return !IsVideoDecoding() ||
-        static_cast<uint32_t>(VideoQueue().GetSize()) >=
-            VideoPrerollFrames() * mPlaybackRate + 1;
-  }
-
-  void StopPrerollingAudio()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mIsAudioPrerolling) {
-      mIsAudioPrerolling = false;
-      ScheduleStateMachine();
-    }
-  }
-
-  void StopPrerollingVideo()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mIsVideoPrerolling) {
-      mIsVideoPrerolling = false;
-      ScheduleStateMachine();
-    }
-  }
-
-  // This temporarily stores the first frame we decode after we seek.
-  // This is so that if we hit end of stream while we're decoding to reach
-  // the seek target, we will still have a frame that we can display as the
-  // last frame in the media.
-  RefPtr<MediaData> mFirstVideoFrameAfterSeek;
-
-  // When we start decoding (either for the first time, or after a pause)
-  // we may be low on decoded data. We don't want our "low data" logic to
-  // kick in and decide that we're low on decoded data because the download
-  // can't keep up with the decode, and cause us to pause playback. So we
-  // have a "preroll" stage, where we ignore the results of our "low data"
-  // logic during the first few frames of our decode. This occurs during
-  // playback. The flags below are true when the corresponding stream is
-  // being "prerolled".
-  bool mIsAudioPrerolling;
-  bool mIsVideoPrerolling;
+  void RejectIfExists(const char* aCallSite);
 
-  // Only one of a given pair of ({Audio,Video}DataPromise, WaitForDataPromise)
-  // should exist at any given moment.
-
-  MozPromiseRequestHolder<MediaDecoderReader::AudioDataPromise> mAudioDataRequest;
-  MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
-  const char* AudioRequestStatus()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mAudioDataRequest.Exists()) {
-      MOZ_DIAGNOSTIC_ASSERT(!mAudioWaitRequest.Exists());
-      return "pending";
-    } else if (mAudioWaitRequest.Exists()) {
-      return "waiting";
-    }
-    return "idle";
-  }
-
-  MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
-  MozPromiseRequestHolder<MediaDecoderReader::VideoDataPromise> mVideoDataRequest;
-  const char* VideoRequestStatus()
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    if (mVideoDataRequest.Exists()) {
-      MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
-      return "pending";
-    } else if (mVideoWaitRequest.Exists()) {
-      return "waiting";
-    }
-    return "idle";
-  }
-
-  MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise>& WaitRequestRef(MediaData::Type aType)
-  {
-    MOZ_ASSERT(OnTaskQueue());
-    return aType == MediaData::AUDIO_DATA ? mAudioWaitRequest : mVideoWaitRequest;
-  }
-
-  // True if we shouldn't play our audio (but still write it to any capturing
-  // streams). When this is true, the audio thread will never start again after
-  // it has stopped.
-  bool mAudioCaptured;
-
-  // True if the audio playback thread has finished. It is finished
-  // when either all the audio frames have completed playing, or we've moved
-  // into shutdown state, and the threads are to be
-  // destroyed. Written by the audio playback thread and read and written by
-  // the state machine thread. Synchronised via decoder monitor.
-  // When data is being sent to a MediaStream, this is true when all data has
-  // been written to the MediaStream.
-  Watchable<bool> mAudioCompleted;
-
-  // True if all video frames are already rendered.
-  Watchable<bool> mVideoCompleted;
-
-  // Set if MDSM receives dormant request during reading metadata.
-  Maybe<bool> mPendingDormant;
-
-  // Flag whether we notify metadata before decoding the first frame or after.
-  //
-  // Note that the odd semantics here are designed to replicate the current
-  // behavior where we notify the decoder each time we come out of dormant, but
-  // send suppressed event visibility for those cases. This code can probably be
-  // simplified.
-  bool mNotifyMetadataBeforeFirstFrame;
-
-  // True if we've dispatched an event to the decode task queue to call
-  // DecodeThreadRun(). We use this flag to prevent us from dispatching
-  // unneccessary runnables, since the decode thread runs in a loop.
-  bool mDispatchedEventToDecode;
-
-  // If this is true while we're in buffering mode, we can exit early,
-  // as it's likely we may be able to playback. This happens when we enter
-  // buffering mode soon after the decode starts, because the decode-ahead
-  // ran fast enough to exhaust all data while the download is starting up.
-  // Synchronised via decoder monitor.
-  bool mQuickBuffering;
-
-  // True if we should not decode/preroll unnecessary samples, unless we're
-  // played. "Prerolling" in this context refers to when we decode and
-  // buffer decoded samples in advance of when they're needed for playback.
-  // This flag is set for preload=metadata media, and means we won't
-  // decode more than the first video frame and first block of audio samples
-  // for that media when we startup, or after a seek. When Play() is called,
-  // we reset this flag, as we assume the user is playing the media, so
-  // prerolling is appropriate then. This flag is used to reduce the overhead
-  // of prerolling samples for media elements that may not play, both
-  // memory and CPU overhead.
-  bool mMinimizePreroll;
-
-  // True if the decode thread has gone filled its buffers and is now
-  // waiting to be awakened before it continues decoding. Synchronized
-  // by the decoder monitor.
-  bool mDecodeThreadWaiting;
-
-  // These two flags are true when we need to drop decoded samples that
-  // we receive up to the next discontinuity. We do this when we seek;
-  // the first sample in each stream after the seek is marked as being
-  // a "discontinuity".
-  bool mDropAudioUntilNextDiscontinuity;
-  bool mDropVideoUntilNextDiscontinuity;
-
-  // Track the current seek promise made by the reader.
-  MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
-
-  // We record the playback position before we seek in order to
-  // determine where the seek terminated relative to the playback position
-  // we were at before the seek.
-  int64_t mCurrentTimeBeforeSeek;
-
-  // Track our request for metadata from the reader.
-  MozPromiseRequestHolder<MediaDecoderReader::MetadataPromise> mMetadataRequest;
-
-  // Stores presentation info required for playback. The decoder monitor
-  // must be held when accessing this.
-  MediaInfo mInfo;
-
-  nsAutoPtr<MetadataTags> mMetadataTags;
-
-  mozilla::MediaMetadataManager mMetadataManager;
-
-  // Track our request to update the buffered ranges
-  MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest;
-
-  // True if we need to call FinishDecodeFirstFrame() upon frame decoding
-  // successeeding.
-  bool mDecodingFirstFrame;
+  ~SeekJob();
 
-  // True if we are back from DECODER_STATE_DORMANT state and
-  // LoadedMetadataEvent was already sent.
-  bool mSentLoadedMetadataEvent;
-  // True if we are back from DECODER_STATE_DORMANT state and
-  // FirstFrameLoadedEvent was already sent, then we can skip
-  // SetStartTime because the mStartTime already set before. Also we don't need
-  // to decode any audio/video since the MediaDecoder will trigger a seek
-  // operation soon.
-  bool mSentFirstFrameLoadedEvent;
-
-  bool mSentPlaybackEndedEvent;
-
-  // Data about MediaStreams that are being fed by the decoder.
-  const RefPtr<OutputStreamManager> mOutputStreamManager;
-
-  // Media data resource from the decoder.
-  RefPtr<MediaResource> mResource;
-
-  // Track the complete & error for audio/video separately
-  MozPromiseRequestHolder<GenericPromise> mMediaSinkAudioPromise;
-  MozPromiseRequestHolder<GenericPromise> mMediaSinkVideoPromise;
-
-  MediaEventListener mAudioQueueListener;
-  MediaEventListener mVideoQueueListener;
-
-  MediaEventProducerExc<nsAutoPtr<MediaInfo>,
-                        nsAutoPtr<MetadataTags>,
-                        MediaDecoderEventVisibility> mMetadataLoadedEvent;
-  MediaEventProducerExc<nsAutoPtr<MediaInfo>,
-                        MediaDecoderEventVisibility> mFirstFrameLoadedEvent;
-
-  MediaEventProducer<MediaEventType> mOnPlaybackEvent;
-  MediaEventProducer<MediaDecoderEventVisibility> mOnSeekingStart;
-
-  // True if audio is offloading.
-  // Playback will not start when audio is offloading.
-  bool mAudioOffloading;
-
-  // Duration of the continuous silent data.
-  uint32_t mSilentDataDuration;
-
-#ifdef MOZ_EME
-  void OnCDMProxyReady(RefPtr<CDMProxy> aProxy);
-  void OnCDMProxyNotReady();
-  RefPtr<CDMProxy> mCDMProxy;
-  MozPromiseRequestHolder<MediaDecoder::CDMProxyPromise> mCDMProxyPromise;
-#endif
-
-private:
-  // The buffered range. Mirrored from the decoder thread.
-  Mirror<media::TimeIntervals> mBuffered;
-
-  // The duration according to the demuxer's current estimate, mirrored from the main thread.
-  Mirror<media::NullableTimeUnit> mEstimatedDuration;
-
-  // The duration explicitly set by JS, mirrored from the main thread.
-  Mirror<Maybe<double>> mExplicitDuration;
-
-  // The current play state and next play state, mirrored from the main thread.
-  Mirror<MediaDecoder::PlayState> mPlayState;
-  Mirror<MediaDecoder::PlayState> mNextPlayState;
-  Mirror<bool> mLogicallySeeking;
-
-  // Volume of playback. 0.0 = muted. 1.0 = full volume.
-  Mirror<double> mVolume;
-
-  // TODO: The separation between mPlaybackRate and mLogicalPlaybackRate is a
-  // kludge to preserve existing fragile logic while converting this setup to
-  // state-mirroring. Some hero should clean this up.
-  Mirror<double> mLogicalPlaybackRate;
-
-  // Pitch preservation for the playback rate.
-  Mirror<bool> mPreservesPitch;
-
-  // True if the media is same-origin with the element. Data can only be
-  // passed to MediaStreams when this is true.
-  Mirror<bool> mSameOriginMedia;
-
-  // An identifier for the principal of the media. Used to track when
-  // main-thread induced principal changes get reflected on MSG thread.
-  Mirror<PrincipalHandle> mMediaPrincipalHandle;
-
-  // Estimate of the current playback rate (bytes/second).
-  Mirror<double> mPlaybackBytesPerSecond;
-
-  // True if mPlaybackBytesPerSecond is a reliable estimate.
-  Mirror<bool> mPlaybackRateReliable;
-
-  // Current decoding position in the stream.
-  Mirror<int64_t> mDecoderPosition;
-
-  // True if the media is seekable (i.e. supports random access).
-  Mirror<bool> mMediaSeekable;
-
-  // True if the media is seekable only in buffered ranges.
-  Mirror<bool> mMediaSeekableOnlyInBufferedRanges;
-
-  // Duration of the media. This is guaranteed to be non-null after we finish
-  // decoding the first frame.
-  Canonical<media::NullableTimeUnit> mDuration;
-
-  // Whether we're currently in or transitioning to shutdown state.
-  Canonical<bool> mIsShutdown;
-
-  // The status of our next frame. Mirrored on the main thread and used to
-  // compute ready state.
-  Canonical<NextFrameStatus> mNextFrameStatus;
-
-  // The time of the current frame in microseconds, corresponding to the "current
-  // playback position" in HTML5. This is referenced from 0, which is the initial
-  // playback position.
-  Canonical<int64_t> mCurrentPosition;
-
-  // Current playback position in the stream in bytes.
-  Canonical<int64_t> mPlaybackOffset;
-
-  // Used to distiguish whether the audio is producing sound.
-  Canonical<bool> mIsAudioDataAudible;
-
-public:
-  AbstractCanonical<media::TimeIntervals>* CanonicalBuffered() {
-    return mReader->CanonicalBuffered();
-  }
-  AbstractCanonical<media::NullableTimeUnit>* CanonicalDuration() {
-    return &mDuration;
-  }
-  AbstractCanonical<bool>* CanonicalIsShutdown() {
-    return &mIsShutdown;
-  }
-  AbstractCanonical<NextFrameStatus>* CanonicalNextFrameStatus() {
-    return &mNextFrameStatus;
-  }
-  AbstractCanonical<int64_t>* CanonicalCurrentPosition() {
-    return &mCurrentPosition;
-  }
-  AbstractCanonical<int64_t>* CanonicalPlaybackOffset() {
-    return &mPlaybackOffset;
-  }
-  AbstractCanonical<bool>* CanonicalIsAudioDataAudible() {
-    return &mIsAudioDataAudible;
-  }
+  SeekTarget mTarget;
+  MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
 };
 
 } // namespace mozilla
 
-#endif
+#endif /* SEEK_JOB_H */
new file mode 100644
--- /dev/null
+++ b/dom/media/SeekTask.cpp
@@ -0,0 +1,729 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "SeekTask.h"
+#include "MediaDecoderReaderWrapper.h"
+#include "mozilla/AbstractThread.h"
+#include "mozilla/Assertions.h"
+#include "nsPrintfCString.h"
+
+namespace mozilla {
+
+extern LazyLogModule gMediaDecoderLog;
+extern LazyLogModule gMediaSampleLog;
+
+// avoid redefined macro in unified build
+#undef LOG
+#undef DECODER_LOG
+#undef VERBOSE_LOG
+
+#define LOG(m, l, x, ...) \
+  MOZ_LOG(m, l, ("[SeekTask] Decoder=%p " x, mDecoderID, ##__VA_ARGS__))
+#define DECODER_LOG(x, ...) \
+  LOG(gMediaDecoderLog, LogLevel::Debug, x, ##__VA_ARGS__)
+#define VERBOSE_LOG(x, ...) \
+  LOG(gMediaDecoderLog, LogLevel::Verbose, x, ##__VA_ARGS__)
+#define SAMPLE_LOG(x, ...) \
+  LOG(gMediaSampleLog, LogLevel::Debug, x, ##__VA_ARGS__)
+
+// Somehow MSVC doesn't correctly delete the comma before ##__VA_ARGS__
+// when __VA_ARGS__ expands to nothing. This is a workaround for it.
+#define DECODER_WARN_HELPER(a, b) NS_WARNING b
+#define DECODER_WARN(x, ...) \
+  DECODER_WARN_HELPER(0, (nsPrintfCString("Decoder=%p " x, mDecoderID, ##__VA_ARGS__).get()))
+
+namespace media {
+
+/*static*/ already_AddRefed<SeekTask>
+SeekTask::CreateSeekTask(const void* aDecoderID,
+                         AbstractThread* aThread,
+                         MediaDecoderReader* aReader,
+                         MediaDecoderReaderWrapper* aReaderWrapper,
+                         SeekJob&& aSeekJob,
+                         const MediaInfo& aInfo,
+                         const media::TimeUnit& aDuration,
+                         int64_t aCurrentMediaTime)
+{
+  RefPtr<SeekTask> task(new SeekTask(aDecoderID, aThread, aReader,
+                                     aReaderWrapper, Move(aSeekJob), aInfo,
+                                     aDuration, aCurrentMediaTime));
+
+  return task.forget();
+}
+
+SeekTask::SeekTask(const void* aDecoderID,
+                   AbstractThread* aThread,
+                   MediaDecoderReader* aReader,
+                   MediaDecoderReaderWrapper* aReaderWrapper,
+                   SeekJob&& aSeekJob,
+                   const MediaInfo& aInfo,
+                   const media::TimeUnit& aDuration,
+                   int64_t aCurrentMediaTime)
+  : mDecoderID(aDecoderID)
+  , mOwnerThread(aThread)
+  , mReader(aReader)
+  , mReaderWrapper(aReaderWrapper)
+  , mSeekJob(Move(aSeekJob))
+  , mCurrentTimeBeforeSeek(aCurrentMediaTime)
+  , mAudioRate(aInfo.mAudio.mRate)
+  , mHasAudio(aInfo.HasAudio())
+  , mHasVideo(aInfo.HasVideo())
+  , mDropAudioUntilNextDiscontinuity(false)
+  , mDropVideoUntilNextDiscontinuity(false)
+  , mIsDiscarded(false)
+  , mIsAudioQueueFinished(false)
+  , mIsVideoQueueFinished(false)
+  , mNeedToStopPrerollingAudio(false)
+  , mNeedToStopPrerollingVideo(false)
+{
+  // Bound the seek time to be inside the media range.
+  int64_t end = aDuration.ToMicroseconds();
+  NS_ASSERTION(end != -1, "Should know end time by now");
+  int64_t seekTime = mSeekJob.mTarget.GetTime().ToMicroseconds();
+  seekTime = std::min(seekTime, end);
+  seekTime = std::max(int64_t(0), seekTime);
+  NS_ASSERTION(seekTime >= 0 && seekTime <= end,
+               "Can only seek in range [0,duration]");
+  mSeekJob.mTarget.SetTime(media::TimeUnit::FromMicroseconds(seekTime));
+
+  mDropAudioUntilNextDiscontinuity = HasAudio();
+  mDropVideoUntilNextDiscontinuity = HasVideo();
+}
+
+SeekTask::~SeekTask()
+{
+  MOZ_ASSERT(mIsDiscarded);
+}
+
+void
+SeekTask::Resolve(const char* aCallSite)
+{
+  SeekTaskResolveValue val;
+  val.mSeekedAudioData = mSeekedAudioData;
+  val.mSeekedVideoData = mSeekedVideoData;
+  val.mIsAudioQueueFinished = mIsAudioQueueFinished;
+  val.mIsVideoQueueFinished = mIsVideoQueueFinished;
+  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
+  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
+
+  mSeekTaskPromise.Resolve(val, aCallSite);
+}
+
+void
+SeekTask::RejectIfExist(const char* aCallSite)
+{
+  SeekTaskRejectValue val;
+  val.mIsAudioQueueFinished = mIsAudioQueueFinished;
+  val.mIsVideoQueueFinished = mIsVideoQueueFinished;
+  val.mNeedToStopPrerollingAudio = mNeedToStopPrerollingAudio;
+  val.mNeedToStopPrerollingVideo = mNeedToStopPrerollingVideo;
+
+  mSeekTaskPromise.RejectIfExists(val, aCallSite);
+}
+
+void
+SeekTask::AssertOwnerThread() const
+{
+  MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
+}
+
+bool
+SeekTask::HasAudio() const
+{
+  return mHasAudio;
+}
+
+bool
+SeekTask::HasVideo() const
+{
+  return mHasVideo;
+}
+
+TaskQueue*
+SeekTask::DecodeTaskQueue() const
+{
+  return mReader->OwnerThread();
+}
+
+AbstractThread*
+SeekTask::OwnerThread() const
+{
+  return mOwnerThread;
+}
+
+void
+SeekTask::Discard()
+{
+  // Disconnect MediaDecoder.
+  mSeekJob.RejectIfExists(__func__);
+
+  // Disconnect MDSM.
+  RejectIfExist(__func__);
+
+  // Disconnect MediaDecoderReader.
+  mSeekRequest.DisconnectIfExists();
+  mAudioDataRequest.DisconnectIfExists();
+  mVideoDataRequest.DisconnectIfExists();
+  mAudioWaitRequest.DisconnectIfExists();
+  mVideoWaitRequest.DisconnectIfExists();
+
+  mIsDiscarded = true;
+}
+
+bool
+SeekTask::NeedToResetMDSM() const
+{
+  return true;
+}
+
+SeekJob&
+SeekTask::GetSeekJob()
+{
+  return mSeekJob;
+}
+
+bool
+SeekTask::Exists()
+{
+  return mSeekJob.Exists();
+}
+
+RefPtr<SeekTask::SeekTaskPromise>
+SeekTask::Seek(const media::TimeUnit& aDuration)
+{
+  AssertOwnerThread();
+
+  // Do the seek.
+  mSeekRequest.Begin(mReaderWrapper->Seek(mSeekJob.mTarget, aDuration)
+    ->Then(OwnerThread(), __func__, this,
+           &SeekTask::OnSeekResolved, &SeekTask::OnSeekRejected));
+
+  return mSeekTaskPromise.Ensure(__func__);
+}
+
+bool
+SeekTask::IsVideoDecoding() const
+{
+  AssertOwnerThread();
+  return HasVideo() && !mIsVideoQueueFinished;
+}
+
+bool
+SeekTask::IsAudioDecoding() const
+{
+  AssertOwnerThread();
+  return HasAudio() && !mIsAudioQueueFinished;
+}
+
+nsresult
+SeekTask::EnsureAudioDecodeTaskQueued()
+{
+  AssertOwnerThread();
+
+  SAMPLE_LOG("EnsureAudioDecodeTaskQueued isDecoding=%d status=%s",
+              IsAudioDecoding(), AudioRequestStatus());
+
+  if (!IsAudioDecoding() ||
+      mAudioDataRequest.Exists() ||
+      mAudioWaitRequest.Exists() ||
+      mSeekRequest.Exists()) {
+    return NS_OK;
+  }
+
+  RequestAudioData();
+  return NS_OK;
+}
+
+nsresult
+SeekTask::EnsureVideoDecodeTaskQueued()
+{
+  AssertOwnerThread();
+
+  SAMPLE_LOG("EnsureVideoDecodeTaskQueued isDecoding=%d status=%s",
+             IsVideoDecoding(), VideoRequestStatus());
+
+  if (!IsVideoDecoding() ||
+      mVideoDataRequest.Exists() ||
+      mVideoWaitRequest.Exists() ||
+      mSeekRequest.Exists()) {
+    return NS_OK;
+  }
+
+  RequestVideoData();
+  return NS_OK;
+}
+
+const char*
+SeekTask::AudioRequestStatus()
+{
+  AssertOwnerThread();
+  if (mAudioDataRequest.Exists()) {
+    MOZ_DIAGNOSTIC_ASSERT(!mAudioWaitRequest.Exists());
+    return "pending";
+  } else if (mAudioWaitRequest.Exists()) {
+    return "waiting";
+  }
+  return "idle";
+}
+
+const char*
+SeekTask::VideoRequestStatus()
+{
+  AssertOwnerThread();
+  if (mVideoDataRequest.Exists()) {
+    MOZ_DIAGNOSTIC_ASSERT(!mVideoWaitRequest.Exists());
+    return "pending";
+  } else if (mVideoWaitRequest.Exists()) {
+    return "waiting";
+  }
+  return "idle";
+}
+
+void
+SeekTask::RequestAudioData()
+{
+  AssertOwnerThread();
+
+  SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
+             !!mSeekedAudioData, mReader->SizeOfAudioQueueInFrames());
+
+  mAudioDataRequest.Begin(mReaderWrapper->RequestAudioData()
+    ->Then(OwnerThread(), __func__, this,
+           &SeekTask::OnAudioDecoded, &SeekTask::OnAudioNotDecoded));
+}
+
+void
+SeekTask::RequestVideoData()
+{
+  AssertOwnerThread();
+  //These two variables are not used in the SEEKING state.
+  const bool skipToNextKeyFrame = false;
+  const media::TimeUnit currentTime = media::TimeUnit::FromMicroseconds(0);
+
+  SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
+               !!mSeekedVideoData, mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
+               currentTime.ToMicroseconds());
+
+  mVideoDataRequest.Begin(
+    mReaderWrapper->RequestVideoData(skipToNextKeyFrame, currentTime)
+    ->Then(OwnerThread(), __func__, this,
+           &SeekTask::OnVideoDecoded, &SeekTask::OnVideoNotDecoded));
+}
+
+nsresult
+SeekTask::DropAudioUpToSeekTarget(MediaData* aSample)
+{
+  AssertOwnerThread();
+  RefPtr<AudioData> audio(aSample->As<AudioData>());
+  MOZ_ASSERT(audio && mSeekJob.Exists() && mSeekJob.mTarget.IsAccurate());
+
+  CheckedInt64 sampleDuration = FramesToUsecs(audio->mFrames, mAudioRate);
+  if (!sampleDuration.isValid()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (audio->mTime + sampleDuration.value() <= mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+    // Our seek target lies after the frames in this AudioData. Don't
+    // push it onto the audio queue, and keep decoding forwards.
+    return NS_OK;
+  }
+
+  if (audio->mTime > mSeekJob.mTarget.GetTime().ToMicroseconds()) {
+    // The seek target doesn't lie in the audio block just after the last
+    // audio frames we've seen which were before the seek target. This
+    // could have been the first audio data we've seen after seek, i.e. the
+    // seek terminated after the seek target in the audio stream. Just
+    // abort the audio decode-to-target, the state machine will play
+    // silence to cover the gap. Typically this happens in poorly muxed
+    // files.
+    DECODER_WARN("Audio not synced after seek, maybe a poorly muxed file?");
+    mSeekedAudioData = audio;
+    return NS_OK;
+  }
+
+  // The seek target lies somewhere in this AudioData's frames, strip off
+  // any frames which lie before the seek target, so we'll begin playback
+  // exactly at the seek target.
+  NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() >= audio->mTime,
+               "Target must at or be after data start.");
+  NS_ASSERTION(mSeekJob.mTarget.GetTime().ToMicroseconds() < audio->mTime + sampleDuration.value(),
+               "Data must end after target.");
+
+  CheckedInt64 framesToPrune =
+    UsecsToFrames(mSeekJob.mTarget.GetTime().ToMicroseconds() - audio->mTime, mAudioRate);
+  if (!framesToPrune.isValid()) {
+    return NS_ERROR_FAILURE;
+  }
+  if (framesToPrune.value() > audio->mFrames) {
+    // We've messed up somehow. Don't try to trim frames, the |frames|
+    // variable below will overflow.
+    DECODER_WARN("Can't prune more frames that we have!");
+    return NS_ERROR_FAILURE;
+  }
+  uint32_t frames = audio->mFrames - static_cast<uint32_t>(framesToPrune.value());
+  uint32_t channels = audio->mChannels;
+  AlignedAudioBuffer audioData(frames * channels);
+  if (!audioData) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  memcpy(audioData.get(),
+         audio->mAudioData.get() + (framesToPrune.value() * channels),
+         frames * channels * sizeof(AudioDataValue));
+  CheckedInt64 duration = FramesToUsecs(frames, mAudioRate);
+  if (!duration.isValid()) {
+    return NS_ERROR_FAILURE;
+  }
+  RefPtr<AudioData> data(new AudioData(audio->mOffset,
+                                       mSeekJob.mTarget.GetTime().ToMicroseconds(),
+                                       duration.value(),
+                                       frames,
+                                       Move(audioData),
+                                       channels,
+                                       audio->mRate));
+  MOZ_ASSERT(!mSeekedAudioData, "Should be the 1st sample after seeking");
+  mSeekedAudioData = data;
+
+  return NS_OK;
+}
+
+nsresult
+SeekTask::DropVideoUpToSeekTarget(MediaData* aSample)
+{
+  AssertOwnerThread();
+  RefPtr<VideoData> video(aSample->As<VideoData>());
+  MOZ_ASSERT(video);
+  DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld]",
+              video->mTime, video->GetEndTime());
+  MOZ_ASSERT(mSeekJob.Exists());
+  const int64_t target = mSeekJob.mTarget.GetTime().ToMicroseconds();
+
+  // If the frame end time is less than the seek target, we won't want
+  // to display this frame after the seek, so discard it.
+  if (target >= video->GetEndTime()) {
+    DECODER_LOG("DropVideoUpToSeekTarget() pop video frame [%lld, %lld] target=%lld",
+                video->mTime, video->GetEndTime(), target);
+    mFirstVideoFrameAfterSeek = video;
+  } else {
+    if (target >= video->mTime && video->GetEndTime() >= target) {
+      // The seek target lies inside this frame's time slice. Adjust the frame's
+      // start time to match the seek target. We do this by replacing the
+      // first frame with a shallow copy which has the new timestamp.
+      RefPtr<VideoData> temp = VideoData::ShallowCopyUpdateTimestamp(video.get(), target);
+      video = temp;
+    }
+    mFirstVideoFrameAfterSeek = nullptr;
+
+    DECODER_LOG("DropVideoUpToSeekTarget() found video frame [%lld, %lld] containing target=%lld",
+                video->mTime, video->GetEndTime(), target);
+
+    MOZ_ASSERT(!mSeekedVideoData, "Should be the 1st sample after seeking");
+    mSeekedVideoData = video;
+  }
+
+  return NS_OK;
+}
+
+bool
+SeekTask::IsAudioSeekComplete()
+{
+  AssertOwnerThread();
+
+  SAMPLE_LOG("IsAudioSeekComplete() curTarVal=%d mAudDis=%d aqFin=%d aqSz=%d",
+      mSeekJob.Exists(), mDropAudioUntilNextDiscontinuity, mIsAudioQueueFinished, !!mSeekedAudioData);
+  return
+    !HasAudio() ||
+    (Exists() && !mDropAudioUntilNextDiscontinuity &&
+     (mIsAudioQueueFinished || mSeekedAudioData));
+}
+
+bool
+SeekTask::IsVideoSeekComplete()
+{
+  AssertOwnerThread();
+
+  SAMPLE_LOG("IsVideoSeekComplete() curTarVal=%d mVidDis=%d vqFin=%d vqSz=%d",
+      mSeekJob.Exists(), mDropVideoUntilNextDiscontinuity, mIsVideoQueueFinished, !!mSeekedVideoData);
+
+  return
+    !HasVideo() ||
+    (Exists() && !mDropVideoUntilNextDiscontinuity &&
+     (mIsVideoQueueFinished || mSeekedVideoData));
+}
+
+void
+SeekTask::CheckIfSeekComplete()
+{
+  AssertOwnerThread();
+
+  const bool videoSeekComplete = IsVideoSeekComplete();
+  if (HasVideo() && !videoSeekComplete) {
+    // We haven't reached the target. Ensure we have requested another sample.
+    if (NS_FAILED(EnsureVideoDecodeTaskQueued())) {
+      DECODER_WARN("Failed to request video during seek");
+      RejectIfExist(__func__);
+    }
+  }
+
+  const bool audioSeekComplete = IsAudioSeekComplete();
+  if (HasAudio() && !audioSeekComplete) {
+    // We haven't reached the target. Ensure we have requested another sample.
+    if (NS_FAILED(EnsureAudioDecodeTaskQueued())) {
+      DECODER_WARN("Failed to request audio during seek");
+      RejectIfExist(__func__);
+    }
+  }
+
+  SAMPLE_LOG("CheckIfSeekComplete() audioSeekComplete=%d videoSeekComplete=%d",
+             audioSeekComplete, videoSeekComplete);
+
+  if (audioSeekComplete && videoSeekComplete) {
+    Resolve(__func__); // Call to MDSM::SeekCompleted();
+  }
+}
+
+void
+SeekTask::OnSeekResolved(media::TimeUnit)
+{
+  AssertOwnerThread();
+  mSeekRequest.Complete();
+  // We must decode the first samples of active streams, so we can determine
+  // the new stream time. So dispatch tasks to do that.
+  EnsureAudioDecodeTaskQueued();
+  EnsureVideoDecodeTaskQueued();
+}
+
+void
+SeekTask::OnSeekRejected(nsresult aResult)
+{
+  AssertOwnerThread();
+  mSeekRequest.Complete();
+  MOZ_ASSERT(NS_FAILED(aResult), "Cancels should also disconnect mSeekRequest");
+  RejectIfExist(__func__);
+}
+
+void
+SeekTask::OnAudioDecoded(MediaData* aAudioSample)
+{
+  AssertOwnerThread();
+  RefPtr<MediaData> audio(aAudioSample);
+  MOZ_ASSERT(audio);
+  mAudioDataRequest.Complete();
+
+  // The MDSM::mDecodedAudioEndTime will be updated once the whole SeekTask is
+  // resolved.
+
+  SAMPLE_LOG("OnAudioDecoded [%lld,%lld] disc=%d",
+             (audio ? audio->mTime : -1),
+             (audio ? audio->GetEndTime() : -1),
+             (audio ? audio->mDiscontinuity : 0));
+
+  if (!Exists()) {
+    // We've received a sample from a previous decode. Discard it.
+    return;
+  }
+
+  if (audio->mDiscontinuity) {
+    mDropAudioUntilNextDiscontinuity = false;
+  }
+
+  if (!mDropAudioUntilNextDiscontinuity) {
+    // We must be after the discontinuity; we're receiving samples
+    // at or after the seek target.
+    if (mSeekJob.mTarget.IsFast() &&
+        mSeekJob.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
+        audio->mTime < mCurrentTimeBeforeSeek) {
+      // We are doing a fastSeek, but we ended up *before* the previous
+      // playback position. This is surprising UX, so switch to an accurate
+      // seek and decode to the seek target. This is not conformant to the
+      // spec, fastSeek should always be fast, but until we get the time to
+      // change all Readers to seek to the keyframe after the currentTime
+      // in this case, we'll just decode forward. Bug 1026330.
+      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
+    }
+    if (mSeekJob.mTarget.IsFast()) {
+      // Non-precise seek; we can stop the seek at the first sample.
+      mSeekedAudioData = audio;
+    } else {
+      // We're doing an accurate seek. We must discard
+      // MediaData up to the one containing exact seek target.
+      if (NS_FAILED(DropAudioUpToSeekTarget(audio.get()))) {
+        RejectIfExist(__func__);
+        return;
+      }
+    }
+  }
+  CheckIfSeekComplete();
+}
+
+void
+SeekTask::OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+{
+  AssertOwnerThread();
+  SAMPLE_LOG("OnAduioNotDecoded (aReason=%u)", aReason);
+  mAudioDataRequest.Complete();
+
+  if (aReason == MediaDecoderReader::DECODE_ERROR) {
+    // If this is a decode error, delegate to the generic error path.
+    RejectIfExist(__func__);
+    return;
+  }
+
+  // If the decoder is waiting for data, we tell it to call us back when the
+  // data arrives.
+  if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
+    MOZ_ASSERT(mReader->IsWaitForDataSupported(),
+               "Readers that send WAITING_FOR_DATA need to implement WaitForData");
+    RefPtr<SeekTask> self = this;
+    mAudioWaitRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                                        &MediaDecoderReader::WaitForData,
+                                        MediaData::AUDIO_DATA)
+      ->Then(OwnerThread(), __func__,
+             [self] (MediaData::Type aType) -> void {
+               self->mAudioWaitRequest.Complete();
+               self->EnsureAudioDecodeTaskQueued();
+             },
+             [self] (WaitForDataRejectValue aRejection) -> void {
+               self->mAudioWaitRequest.Complete();
+             }));
+
+    // We are out of data to decode and will enter buffering mode soon.
+    // We want to play the frames we have already decoded, so we stop pre-rolling
+    // and ensure that loadeddata is fired as required.
+    mNeedToStopPrerollingAudio = true;
+    return;
+  }
+
+  if (aReason == MediaDecoderReader::CANCELED) {
+    EnsureAudioDecodeTaskQueued();
+    return;
+  }
+
+  if (aReason == MediaDecoderReader::END_OF_STREAM) {
+    mIsAudioQueueFinished = true;
+    mDropAudioUntilNextDiscontinuity = false; // To make IsAudioSeekComplete() return TRUE.
+    CheckIfSeekComplete();
+  }
+}
+
+void
+SeekTask::OnVideoDecoded(MediaData* aVideoSample)
+{
+  AssertOwnerThread();
+  RefPtr<MediaData> video(aVideoSample);
+  MOZ_ASSERT(video);
+  mVideoDataRequest.Complete();
+
+  // The MDSM::mDecodedVideoEndTime will be updated once the whole SeekTask is
+  // resolved.
+
+  SAMPLE_LOG("OnVideoDecoded [%lld,%lld] disc=%d",
+             (video ? video->mTime : -1),
+             (video ? video->GetEndTime() : -1),
+             (video ? video->mDiscontinuity : 0));
+
+  if (!Exists()) {
+    // We've received a sample from a previous decode. Discard it.
+    return;
+  }
+
+  if (mDropVideoUntilNextDiscontinuity) {
+    if (video->mDiscontinuity) {
+      mDropVideoUntilNextDiscontinuity = false;
+    }
+  }
+
+  if (!mDropVideoUntilNextDiscontinuity) {
+    // We must be after the discontinuity; we're receiving samples
+    // at or after the seek target.
+    if (mSeekJob.mTarget.IsFast() &&
+        mSeekJob.mTarget.GetTime().ToMicroseconds() > mCurrentTimeBeforeSeek &&
+        video->mTime < mCurrentTimeBeforeSeek) {
+      // We are doing a fastSeek, but we ended up *before* the previous
+      // playback position. This is surprising UX, so switch to an accurate
+      // seek and decode to the seek target. This is not conformant to the
+      // spec, fastSeek should always be fast, but until we get the time to
+      // change all Readers to seek to the keyframe after the currentTime
+      // in this case, we'll just decode forward. Bug 1026330.
+      mSeekJob.mTarget.SetType(SeekTarget::Accurate);
+    }
+    if (mSeekJob.mTarget.IsFast()) {
+      // Non-precise seek. We can stop the seek at the first sample.
+      mSeekedVideoData = video;
+    } else {
+      // We're doing an accurate seek. We still need to discard
+      // MediaData up to the one containing exact seek target.
+      if (NS_FAILED(DropVideoUpToSeekTarget(video.get()))) {
+        RejectIfExist(__func__);
+        return;
+      }
+    }
+  }
+  CheckIfSeekComplete();
+}
+
+void
+SeekTask::OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
+{
+  AssertOwnerThread();
+  SAMPLE_LOG("OnVideoNotDecoded (aReason=%u)", aReason);
+  mVideoDataRequest.Complete();
+
+  if (aReason == MediaDecoderReader::DECODE_ERROR) {
+    // If this is a decode error, delegate to the generic error path.
+    RejectIfExist(__func__);
+    return;
+  }
+
+  // If the decoder is waiting for data, we tell it to call us back when the
+  // data arrives.
+  if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
+    MOZ_ASSERT(mReader->IsWaitForDataSupported(),
+               "Readers that send WAITING_FOR_DATA need to implement WaitForData");
+    RefPtr<SeekTask> self = this;
+    mVideoWaitRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                                        &MediaDecoderReader::WaitForData,
+                                        MediaData::VIDEO_DATA)
+      ->Then(OwnerThread(), __func__,
+             [self] (MediaData::Type aType) -> void {
+               self->mVideoWaitRequest.Complete();
+               self->EnsureVideoDecodeTaskQueued();
+             },
+             [self] (WaitForDataRejectValue aRejection) -> void {
+               self->mVideoWaitRequest.Complete();
+             }));
+
+    // We are out of data to decode and will enter buffering mode soon.
+    // We want to play the frames we have already decoded, so we stop pre-rolling
+    // and ensure that loadeddata is fired as required.
+    mNeedToStopPrerollingVideo = true;
+    return;
+  }
+
+  if (aReason == MediaDecoderReader::CANCELED) {
+    EnsureVideoDecodeTaskQueued();
+    return;
+  }
+
+  if (aReason == MediaDecoderReader::END_OF_STREAM) {
+
+    if (Exists() && mFirstVideoFrameAfterSeek) {
+      // Null sample. Hit end of stream. If we have decoded a frame,
+      // insert it into the queue so that we have something to display.
+      // We make sure to do this before invoking VideoQueue().Finish()
+      // below.
+      mSeekedVideoData = mFirstVideoFrameAfterSeek;
+      mFirstVideoFrameAfterSeek = nullptr;
+    }
+
+    mIsVideoQueueFinished = true;
+    mDropVideoUntilNextDiscontinuity = false; // To make IsVideoSeekComplete() return TRUE.
+    CheckIfSeekComplete();
+  }
+}
+
+} // namespace media
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/SeekTask.h
@@ -0,0 +1,184 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SEEK_TASK_H
+#define SEEK_TASK_H
+
+#include "mozilla/MozPromise.h"
+#include "MediaDecoderReader.h"
+#include "SeekJob.h"
+
+namespace mozilla {
+
+class AbstractThread;
+class MediaData;
+class MediaDecoderReaderWrapper;
+
+namespace media {
+
+struct SeekTaskResolveValue
+{
+  RefPtr<MediaData> mSeekedAudioData;
+  RefPtr<MediaData> mSeekedVideoData;
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+  bool mNeedToStopPrerollingAudio;
+  bool mNeedToStopPrerollingVideo;
+};
+
+struct SeekTaskRejectValue
+{
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+  bool mNeedToStopPrerollingAudio;
+  bool mNeedToStopPrerollingVideo;
+};
+
+class SeekTask {
+
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SeekTask)
+
+public:
+  static const bool IsExclusive = true;
+
+  using SeekTaskPromise =
+    MozPromise<SeekTaskResolveValue, SeekTaskRejectValue, IsExclusive>;
+
+  static already_AddRefed<SeekTask>
+  CreateSeekTask(const void* aDecoderID,
+                 AbstractThread* aThread,
+                 MediaDecoderReader* aReader,
+                 MediaDecoderReaderWrapper* aReaderWrapper,
+                 SeekJob&& aSeekJob,
+                 const MediaInfo& aInfo,
+                 const media::TimeUnit& aDuration,
+                 int64_t aCurrentMediaTime);
+
+  virtual void Discard();
+
+  virtual RefPtr<SeekTaskPromise> Seek(const media::TimeUnit& aDuration);
+
+  virtual bool NeedToResetMDSM() const;
+
+  SeekJob& GetSeekJob();
+
+  bool Exists();
+
+protected:
+  SeekTask(const void* aDecoderID,
+           AbstractThread* aThread,
+           MediaDecoderReader* aReader,
+           MediaDecoderReaderWrapper* aReaderWrapper,
+           SeekJob&& aSeekJob,
+           const MediaInfo& aInfo,
+           const media::TimeUnit& aDuration,
+           int64_t aCurrentMediaTime);
+
+  virtual ~SeekTask();
+
+  void Resolve(const char* aCallSite);
+
+  void RejectIfExist(const char* aCallSite);
+
+  void AssertOwnerThread() const;
+
+  bool HasAudio() const;
+
+  bool HasVideo() const;
+
+  TaskQueue* DecodeTaskQueue() const;
+
+  AbstractThread* OwnerThread() const;
+
+  bool IsVideoDecoding() const;
+
+  bool IsAudioDecoding() const;
+
+  nsresult EnsureVideoDecodeTaskQueued();
+
+  nsresult EnsureAudioDecodeTaskQueued();
+
+  const char* AudioRequestStatus();
+
+  const char* VideoRequestStatus();
+
+  void RequestVideoData();
+
+  void RequestAudioData();
+
+  nsresult DropAudioUpToSeekTarget(MediaData* aSample);
+
+  nsresult DropVideoUpToSeekTarget(MediaData* aSample);
+
+  bool IsAudioSeekComplete();
+
+  bool IsVideoSeekComplete();
+
+  void CheckIfSeekComplete();
+
+  virtual void OnSeekResolved(media::TimeUnit);
+
+  virtual void OnSeekRejected(nsresult aResult);
+
+  virtual void OnAudioDecoded(MediaData* aAudioSample);
+
+  virtual void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+
+  virtual void OnVideoDecoded(MediaData* aVideoSample);
+
+  virtual void OnVideoNotDecoded(MediaDecoderReader::NotDecodedReason aReason);
+
+  /*
+   * Data shared with MDSM.
+   */
+  const void* mDecoderID; // For logging.
+  const RefPtr<AbstractThread> mOwnerThread;
+  const RefPtr<MediaDecoderReader> mReader;
+  const RefPtr<MediaDecoderReaderWrapper> mReaderWrapper;
+
+  /*
+   * Internal state.
+   */
+  SeekJob mSeekJob;
+  MozPromiseHolder<SeekTaskPromise> mSeekTaskPromise;
+  int64_t mCurrentTimeBeforeSeek;
+  const uint32_t mAudioRate;  // Audio sample rate.
+  const bool mHasAudio;
+  const bool mHasVideo;
+  bool mDropAudioUntilNextDiscontinuity;
+  bool mDropVideoUntilNextDiscontinuity;
+  bool mIsDiscarded;
+
+  // This temporarily stores the first frame we decode after we seek.
+  // This is so that if we hit end of stream while we're decoding to reach
+  // the seek target, we will still have a frame that we can display as the
+  // last frame in the media.
+  RefPtr<MediaData> mFirstVideoFrameAfterSeek;
+
+  /*
+   * Track the current seek promise made by the reader.
+   */
+  MozPromiseRequestHolder<MediaDecoderReader::SeekPromise> mSeekRequest;
+  MozPromiseRequestHolder<MediaDecoderReader::AudioDataPromise> mAudioDataRequest;
+  MozPromiseRequestHolder<MediaDecoderReader::VideoDataPromise> mVideoDataRequest;
+  MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mAudioWaitRequest;
+  MozPromiseRequestHolder<MediaDecoderReader::WaitForDataPromise> mVideoWaitRequest;
+
+  /*
+   * Information which are going to be returned to MDSM.
+   */
+  RefPtr<MediaData> mSeekedAudioData;
+  RefPtr<MediaData> mSeekedVideoData;
+  bool mIsAudioQueueFinished;
+  bool mIsVideoQueueFinished;
+  bool mNeedToStopPrerollingAudio;
+  bool mNeedToStopPrerollingVideo;
+};
+
+} // namespace media
+} // namespace mozilla
+
+#endif /* SEEK_TASK_H */
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -129,17 +129,19 @@ EXPORTS += [
     'MediaTrackList.h',
     'MP3Decoder.h',
     'MP3Demuxer.h',
     'MP3FrameParser.h',
     'nsIDocumentActivity.h',
     'PrincipalChangeObserver.h',
     'QueueObject.h',
     'RtspMediaResource.h',
+    'SeekJob.h',
     'SeekTarget.h',
+    'SeekTask.h',
     'SelfRef.h',
     'SharedBuffer.h',
     'StreamBuffer.h',
     'ThreadPoolCOMListener.h',
     'TimeUnits.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
@@ -233,16 +235,18 @@ UNIFIED_SOURCES += [
     'MediaTimer.cpp',
     'MediaTrack.cpp',
     'MediaTrackList.cpp',
     'MP3Decoder.cpp',
     'MP3Demuxer.cpp',
     'MP3FrameParser.cpp',
     'QueueObject.cpp',
     'RtspMediaResource.cpp',
+    'SeekJob.cpp',
+    'SeekTask.cpp',
     'StreamBuffer.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
     'TrackUnionStream.cpp',
     'VideoFrameContainer.cpp',
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -10,17 +10,16 @@ support-files =
   !/dom/tests/mochitest/geolocation/network_geolocation.sjs
 
 [browser_test_toolbars_visibility.js]
 support-files =
   test_new_window_from_content_child.html
 [browser_bug1008941_dismissGeolocationHanger.js]
 skip-if = buildapp == 'mulet'
 [browser_test__content.js]
-skip-if = e10s
 [browser_ConsoleAPITests.js]
 skip-if = e10s
 [browser_ConsoleAPI_originAttributes.js]
 [browser_ConsoleStorageAPITests.js]
 skip-if = e10s
 [browser_ConsoleStoragePBTest_perwindowpb.js]
 skip-if = e10s
 [browser_autofocus_background.js]
@@ -30,21 +29,20 @@ skip-if= buildapp == 'mulet'
 skip-if = e10s
 [browser_bug396843.js]
 skip-if = e10s
 [browser_focus_steal_from_chrome.js]
 skip-if = e10s
 [browser_focus_steal_from_chrome_during_mousedown.js]
 skip-if = e10s
 [browser_frame_elements.js]
-skip-if = e10s
 [browser_localStorage_privatestorageevent.js]
 skip-if = e10s
 [browser_test_new_window_from_content.js]
-skip-if = (toolkit == 'android' || buildapp == 'b2g' || buildapp == 'mulet') || e10s
+skip-if = (toolkit == 'android' || buildapp == 'b2g' || buildapp == 'mulet') || (os == "linux" && debug) # see bug 1261495 for Linux debug time outs
 support-files =
   test_new_window_from_content_child.html
 [browser_webapps_permissions.js]
 # TODO: Re-enable permissions tests on Mac, bug 795334
 skip-if = buildapp != "b2g" || e10s
 support-files =
   test-webapp.webapp
   test-webapp-reinstall.webapp
--- a/dom/tests/mochitest/geolocation/mochitest.ini
+++ b/dom/tests/mochitest/geolocation/mochitest.ini
@@ -32,22 +32,22 @@ skip-if = buildapp == 'b2g' || toolkit =
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWatchConcurrent.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWatchSerial.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_manyWindows.html]
 skip-if = buildapp == 'b2g'
 [test_mozsettings.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #mozSettings is undefined
+skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_mozsettingsWatch.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #mozSettings is undefined
+skip-if = buildapp == 'b2g' || toolkit == 'android'
 [test_optional_api_params.html]
 skip-if = buildapp == 'b2g'
 [test_shutdown.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_timeoutCurrent.html]
 [test_timerRestartWatch.html]
-skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT
+skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_windowClose.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_worseAccuracyDoesNotBlockCallback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -227,27 +227,16 @@ interface CanvasRenderingContext2D {
   void drawWindow(Window window, double x, double y, double w, double h,
                   DOMString bgColor, optional unsigned long flags = 0);
   [Throws, ChromeOnly]
   void asyncDrawXULElement(XULElement elem, double x, double y, double w,
                            double h, DOMString bgColor,
                            optional unsigned long flags = 0);
 
   /**
-   * Render the root widget of a window into the canvas. Unlike drawWindow,
-   * this uses the operating system to snapshot the widget on-screen, rather
-   * than reading from our own compositor.
-   *
-   * Currently, this is only supported on Windows, and only on widgets that
-   * use OMTC, and only from within the chrome process.
-   */
-  [Throws, ChromeOnly]
-  void drawWidgetAsOnScreen(Window window);
-
-  /**
    * This causes a context that is currently using a hardware-accelerated
    * backend to fallback to a software one. All state should be preserved.
    */
   [ChromeOnly]
   void demote();
 };
 CanvasRenderingContext2D implements CanvasDrawingStyles;
 CanvasRenderingContext2D implements CanvasPathMethods;
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1456,27 +1456,22 @@ RuntimeService::RegisterWorker(WorkerPri
 
     queued = gMaxWorkersPerDomain &&
              domainInfo->ActiveWorkerCount() >= gMaxWorkersPerDomain &&
              !domain.IsEmpty() &&
              !exemptFromPerDomainMax;
 
     if (queued) {
       domainInfo->mQueuedWorkers.AppendElement(aWorkerPrivate);
+
+      // Worker spawn gets queued due to hitting max workers per domain
+      // limit so let's log a warning.
+      WorkerPrivate::ReportErrorToConsole("HittingMaxWorkersPerDomain2");
+
       if (isServiceWorker || isSharedWorker) {
-        AssertIsOnMainThread();
-        // ServiceWorker spawn gets queued due to hitting max workers per domain
-        // limit so let's log a warning.
-        // Note: aWorkerPrivate->GetDocument() call might result nullptr due to
-        // no window so the message warning will show up in the browser console.
-        nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
-                                        NS_LITERAL_CSTRING("DOM"),
-                                        aWorkerPrivate->GetDocument(),
-                                        nsContentUtils::eDOM_PROPERTIES,
-                                        "HittingMaxWorkersPerDomain");
         Telemetry::Accumulate(isSharedWorker ? Telemetry::SHARED_WORKER_SPAWN_GETS_QUEUED
                                              : Telemetry::SERVICE_WORKER_SPAWN_GETS_QUEUED, 1);
       }
     }
     else if (parent) {
       domainInfo->mChildWorkerCount++;
     }
     else if (isServiceWorker) {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -934,16 +934,72 @@ public:
 private:
   virtual bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     return aWorkerPrivate->ThawInternal();
   }
 };
 
+class ReportErrorToConsoleRunnable final : public WorkerRunnable
+{
+  const char* mMessage;
+
+public:
+  // aWorkerPrivate is the worker thread we're on (or the main thread, if null)
+  static void
+  Report(WorkerPrivate* aWorkerPrivate, const char* aMessage)
+  {
+    if (aWorkerPrivate) {
+      aWorkerPrivate->AssertIsOnWorkerThread();
+    } else {
+      AssertIsOnMainThread();
+    }
+
+    // Now fire a runnable to do the same on the parent's thread if we can.
+    if (aWorkerPrivate) {
+      RefPtr<ReportErrorToConsoleRunnable> runnable =
+        new ReportErrorToConsoleRunnable(aWorkerPrivate, aMessage);
+      runnable->Dispatch();
+      return;
+    }
+
+    // Log a warning to the console.
+    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
+                                    NS_LITERAL_CSTRING("DOM"),
+                                    nullptr,
+                                    nsContentUtils::eDOM_PROPERTIES,
+                                    aMessage);
+  }
+
+private:
+  ReportErrorToConsoleRunnable(WorkerPrivate* aWorkerPrivate, const char* aMessage)
+  : WorkerRunnable(aWorkerPrivate, ParentThreadUnchangedBusyCount),
+    mMessage(aMessage)
+  { }
+
+  virtual void
+  PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
+  {
+    aWorkerPrivate->AssertIsOnWorkerThread();
+
+    // Dispatch may fail if the worker was canceled, no need to report that as
+    // an error, so don't call base class PostDispatch.
+  }
+
+  virtual bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    WorkerPrivate* parent = aWorkerPrivate->GetParent();
+    MOZ_ASSERT_IF(!parent, NS_IsMainThread());
+    Report(parent, mMessage);
+    return true;
+  }
+};
+
 class ReportErrorRunnable final : public WorkerRunnable
 {
   nsString mMessage;
   nsString mFilename;
   nsString mLine;
   uint32_t mLineNumber;
   uint32_t mColumnNumber;
   uint32_t mFlags;
@@ -5844,16 +5900,28 @@ WorkerPrivate::ReportError(JSContext* aC
   ReportErrorRunnable::ReportError(aCx, this, fireAtScope, nullptr, message,
                                    filename, line, lineNumber,
                                    columnNumber, flags, errorNumber, exnType,
                                    mutedError, 0);
 
   mErrorHandlerRecursionCount--;
 }
 
+// static
+void
+WorkerPrivate::ReportErrorToConsole(const char* aMessage)
+{
+  WorkerPrivate* wp = nullptr;
+  if (!NS_IsMainThread()) {
+    wp = GetCurrentThreadWorkerPrivate();
+  }
+
+  ReportErrorToConsoleRunnable::Report(wp, aMessage);
+}
+
 int32_t
 WorkerPrivate::SetTimeout(JSContext* aCx,
                           dom::Function* aHandler,
                           const nsAString& aStringHandler,
                           int32_t aTimeout,
                           const Sequence<JS::Value>& aArguments,
                           bool aIsInterval,
                           ErrorResult& aRv)
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -1124,16 +1124,19 @@ public:
                         const nsAString& aMessage);
 
   bool
   NotifyInternal(JSContext* aCx, Status aStatus);
 
   void
   ReportError(JSContext* aCx, const char* aMessage, JSErrorReport* aReport);
 
+  static void
+  ReportErrorToConsole(const char* aMessage);
+
   int32_t
   SetTimeout(JSContext* aCx,
              Function* aHandler,
              const nsAString& aStringHandler,
              int32_t aTimeout,
              const Sequence<JS::Value>& aArguments,
              bool aIsInterval,
              ErrorResult& aRv);
--- a/dom/xbl/nsXBLPrototypeResources.cpp
+++ b/dom/xbl/nsXBLPrototypeResources.cpp
@@ -149,17 +149,17 @@ nsXBLPrototypeResources::GatherRuleProce
   nsTArray<RefPtr<CSSStyleSheet>> sheets(mStyleSheetList.Length());
   for (StyleSheetHandle sheet : mStyleSheetList) {
     MOZ_ASSERT(sheet->IsGecko(),
                "GatherRuleProcessor must only be called for "
                "nsXBLPrototypeResources objects with Gecko-flavored style "
                "backends");
     sheets.AppendElement(sheet->AsGecko());
   }
-  mRuleProcessor = new nsCSSRuleProcessor(sheets,
+  mRuleProcessor = new nsCSSRuleProcessor(Move(sheets),
                                           SheetType::Doc,
                                           nullptr,
                                           mRuleProcessor);
 }
 
 void
 nsXBLPrototypeResources::AppendStyleSheet(StyleSheetHandle aSheet)
 {
--- a/embedding/ios/confvars.sh
+++ b/embedding/ios/confvars.sh
@@ -1,16 +1,15 @@
 #! /bin/sh
 # 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/.
 
 MOZ_APP_NAME=geckoembed
 MOZ_APP_DISPLAYNAME=GeckoEmbed
 MOZ_UPDATER=
-MOZ_CHROME_FILE_FORMAT=omni
 MOZ_APP_VERSION=$MOZILLA_VERSION
 MOZ_PLACES=1
 MOZ_EXTENSIONS_DEFAULT=" gio"
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_CRYPTO=1
 MOZ_SERVICES_SYNC=1
 MOZ_SERVICES_HEALTHREPORT=1
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -94,16 +94,21 @@ ReleaseTemporarySurface(void* aPixels, v
   }
 }
 
 static SkBitmap
 GetBitmapForSurface(SourceSurface* aSurface)
 {
   SkBitmap bitmap;
 
+  if (!aSurface) {
+    gfxDebug() << "Creating empty Skia bitmap from null SourceSurface";
+    return bitmap;
+  }
+
   if (aSurface->GetType() == SurfaceType::SKIA) {
     bitmap = static_cast<SourceSurfaceSkia*>(aSurface)->GetBitmap();
     return bitmap;
   }
 
   DataSourceSurface* surf = aSurface->GetDataSurface().take();
   if (!surf) {
     gfxDevCrash(LogReason::SourceSurfaceIncompatible) << "Non-Skia SourceSurfaces need to be DataSourceSurfaces";
--- a/gfx/gl/GLBlitHelper.cpp
+++ b/gfx/gl/GLBlitHelper.cpp
@@ -63,16 +63,19 @@ GLBlitHelper::GLBlitHelper(GLContext* gl
     , mTexHeight(0)
     , mCurYScale(1.0f)
     , mCurCbCrScale(1.0f)
 {
 }
 
 GLBlitHelper::~GLBlitHelper()
 {
+    if (!mGL->MakeCurrent())
+        return;
+
     DeleteTexBlitProgram();
 
     GLuint tex[] = {
         mSrcTexY,
         mSrcTexCb,
         mSrcTexCr,
         mSrcTexEGL,
     };
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -2351,22 +2351,23 @@ GLContext::ClearSafely()
 }
 
 void
 GLContext::MarkDestroyed()
 {
     if (IsDestroyed())
         return;
 
+    // Null these before they're naturally nulled after dtor, as we want GLContext to
+    // still be alive in *their* dtors.
+    mScreen = nullptr;
+    mBlitHelper = nullptr;
+    mReadTexImageHelper = nullptr;
+
     if (MakeCurrent()) {
-        DestroyScreenBuffer();
-
-        mBlitHelper = nullptr;
-        mReadTexImageHelper = nullptr;
-
         mTexGarbageBin->GLContextTeardown();
     } else {
         NS_WARNING("MakeCurrent() failed during MarkDestroyed! Skipping GL object teardown.");
     }
 
     mSymbols.Zero();
 }
 
@@ -2600,18 +2601,16 @@ GLContext::CreateScreenBufferImpl(const 
     UniquePtr<GLScreenBuffer> newScreen = GLScreenBuffer::Create(this, size, caps);
     if (!newScreen)
         return false;
 
     if (!newScreen->Resize(size)) {
         return false;
     }
 
-    DestroyScreenBuffer();
-
     // This will rebind to 0 (Screen) if needed when
     // it falls out of scope.
     ScopedBindFramebuffer autoFB(this);
 
     mScreen = Move(newScreen);
 
     return true;
 }
@@ -2621,22 +2620,16 @@ GLContext::ResizeScreenBuffer(const IntS
 {
     if (!IsOffscreenSizeAllowed(size))
         return false;
 
     return mScreen->Resize(size);
 }
 
 void
-GLContext::DestroyScreenBuffer()
-{
-    mScreen = nullptr;
-}
-
-void
 GLContext::ForceDirtyScreen()
 {
     ScopedBindFramebuffer autoFB(0);
 
     BeforeGLDrawCall();
     // no-op; just pretend we did something
     AfterGLDrawCall();
 }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -3454,18 +3454,16 @@ public:
                               const GLuint texture,
                               GLuint* drawFB,
                               GLuint* readFB);
 
 protected:
     friend class GLScreenBuffer;
     UniquePtr<GLScreenBuffer> mScreen;
 
-    void DestroyScreenBuffer();
-
     SharedSurface* mLockedSurface;
 
 public:
     void LockSurface(SharedSurface* surf) {
         MOZ_ASSERT(!mLockedSurface);
         mLockedSurface = surf;
     }
 
--- a/gfx/gl/GLReadTexImageHelper.cpp
+++ b/gfx/gl/GLReadTexImageHelper.cpp
@@ -27,16 +27,19 @@ GLReadTexImageHelper::GLReadTexImageHelp
     mPrograms[0] = 0;
     mPrograms[1] = 0;
     mPrograms[2] = 0;
     mPrograms[3] = 0;
 }
 
 GLReadTexImageHelper::~GLReadTexImageHelper()
 {
+    if (!mGL->MakeCurrent())
+        return;
+
     mGL->fDeleteProgram(mPrograms[0]);
     mGL->fDeleteProgram(mPrograms[1]);
     mGL->fDeleteProgram(mPrograms[2]);
     mGL->fDeleteProgram(mPrograms[3]);
 }
 
 static const GLchar
 readTextureImageVS[] =
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -112,18 +112,20 @@ SharedSurface_ANGLEShareHandle::SharedSu
 {
 }
 
 
 SharedSurface_ANGLEShareHandle::~SharedSurface_ANGLEShareHandle()
 {
     mEGL->fDestroySurface(Display(), mPBuffer);
 
+    if (!mGL->MakeCurrent())
+        return;
+
     if (mFence) {
-        mGL->MakeCurrent();
         mGL->fDeleteFences(1, &mFence);
     }
 }
 
 void
 SharedSurface_ANGLEShareHandle::LockProdImpl()
 {
     GLContextEGL::Cast(mGL)->SetEGLSurfaceOverride(mPBuffer);
--- a/gfx/gl/SharedSurfaceD3D11Interop.cpp
+++ b/gfx/gl/SharedSurfaceD3D11Interop.cpp
@@ -295,22 +295,25 @@ SharedSurface_D3D11Interop::SharedSurfac
     , mKeyedMutex(keyedMutex)
     , mLockedForGL(false)
 { }
 
 SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
 {
     MOZ_ASSERT(!mLockedForGL);
 
-    mGL->fDeleteRenderbuffers(1, &mProdRB);
-
     if (!mDXGL->UnregisterObject(mObjectWGL)) {
         NS_WARNING("Failed to release a DXGL object, possibly leaking it.");
     }
 
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteRenderbuffers(1, &mProdRB);
+
     // mDXGL is closed when it runs out of refs.
 }
 
 void
 SharedSurface_D3D11Interop::LockProdImpl()
 { }
 
 void
--- a/gfx/gl/SharedSurfaceEGL.cpp
+++ b/gfx/gl/SharedSurfaceEGL.cpp
@@ -83,32 +83,34 @@ SharedSurface_EGLImage::SharedSurface_EG
     , mConsTex(0)
     , mSync(0)
 {}
 
 SharedSurface_EGLImage::~SharedSurface_EGLImage()
 {
     mEGL->fDestroyImage(Display(), mImage);
 
-    mGL->MakeCurrent();
-    mGL->fDeleteTextures(1, &mProdTex);
-    mProdTex = 0;
+    if (mSync) {
+        // We can't call this unless we have the ext, but we will always have
+        // the ext if we have something to destroy.
+        mEGL->fDestroySync(Display(), mSync);
+        mSync = 0;
+    }
 
     if (mConsTex) {
         MOZ_ASSERT(mGarbageBin);
         mGarbageBin->Trash(mConsTex);
         mConsTex = 0;
     }
 
-    if (mSync) {
-        // We can't call this unless we have the ext, but we will always have
-        // the ext if we have something to destroy.
-        mEGL->fDestroySync(Display(), mSync);
-        mSync = 0;
-    }
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteTextures(1, &mProdTex);
+    mProdTex = 0;
 }
 
 layers::TextureFlags
 SharedSurface_EGLImage::GetTextureFlags() const
 {
     return layers::TextureFlags::DEALLOCATE_CLIENT;
 }
 
--- a/gfx/gl/SharedSurfaceGLX.cpp
+++ b/gfx/gl/SharedSurfaceGLX.cpp
@@ -72,21 +72,21 @@ void
 SharedSurface_GLXDrawable::UnlockProdImpl()
 {
     GLContextGLX::Cast(mGL)->RestoreDrawable();
 }
 
 bool
 SharedSurface_GLXDrawable::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
-  if (!mXlibSurface)
-      return false;
+    if (!mXlibSurface)
+        return false;
 
-   *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
-   return true;
+    *out_descriptor = layers::SurfaceDescriptorX11(mXlibSurface, mInSameProcess);
+    return true;
 }
 
 bool
 SharedSurface_GLXDrawable::ReadbackBySharedHandle(gfx::DataSourceSurface* out_surface)
 {
     MOZ_ASSERT(out_surface);
     RefPtr<gfx::DataSourceSurface> dataSurf =
         new gfx::DataSourceSurfaceCairo(mXlibSurface->CairoSurface());
--- a/gfx/gl/SharedSurfaceGralloc.cpp
+++ b/gfx/gl/SharedSurfaceGralloc.cpp
@@ -130,30 +130,30 @@ SharedSurface_Gralloc::SharedSurface_Gra
     , mEGL(egl)
     , mSync(0)
     , mAllocator(allocator)
     , mTextureClient(textureClient)
     , mProdTex(prodTex)
 {
 }
 
-
 bool
 SharedSurface_Gralloc::HasExtensions(GLLibraryEGL* egl, GLContext* gl)
 {
     return egl->HasKHRImageBase() &&
            gl->IsExtensionSupported(GLContext::OES_EGL_image);
 }
 
 SharedSurface_Gralloc::~SharedSurface_Gralloc()
 {
-
     DEBUG_PRINT("[SharedSurface_Gralloc %p] destroyed\n", this);
 
-    mGL->MakeCurrent();
+    if (!mGL->MakeCurrent())
+        return;
+
     mGL->fDeleteTextures(1, &mProdTex);
 
     if (mSync) {
         MOZ_ALWAYS_TRUE( mEGL->fDestroySync(mEGL->Display(), mSync) );
         mSync = 0;
     }
 }
 
--- a/gfx/gl/SharedSurfaceIO.cpp
+++ b/gfx/gl/SharedSurfaceIO.cpp
@@ -160,21 +160,20 @@ SharedSurface_IOSurface::SharedSurface_I
     gl->MakeCurrent();
     mProdTex = 0;
     gl->fGenTextures(1, &mProdTex);
     BackTextureWithIOSurf(gl, mProdTex, mIOSurf);
 }
 
 SharedSurface_IOSurface::~SharedSurface_IOSurface()
 {
-    if (mProdTex) {
-        DebugOnly<bool> success = mGL->MakeCurrent();
-        MOZ_ASSERT(success);
-        mGL->fDeleteTextures(1, &mProdTex);
-    }
+    if (!mGL->MakeCurrent())
+        return;
+
+    mGL->fDeleteTextures(1, &mProdTex);
 }
 
 bool
 SharedSurface_IOSurface::ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor)
 {
     bool isOpaque = !mHasAlpha;
     *out_descriptor = layers::SurfaceDescriptorMacIOSurface(mIOSurf->GetIOSurfaceID(),
                                                             mIOSurf->GetContentsScaleFactor(),
--- a/gfx/gl/TextureGarbageBin.cpp
+++ b/gfx/gl/TextureGarbageBin.cpp
@@ -31,14 +31,15 @@ TextureGarbageBin::Trash(GLuint tex)
 
 void
 TextureGarbageBin::EmptyGarbage()
 {
     MutexAutoLock lock(mMutex);
     if (!mGL)
         return;
 
+    MOZ_RELEASE_ASSERT(mGL->IsCurrent());
     while (!mGarbageTextures.empty()) {
         GLuint tex = mGarbageTextures.top();
         mGarbageTextures.pop();
         mGL->fDeleteTextures(1, &tex);
     }
 }
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -123,16 +123,20 @@ struct EffectChain;
 class Image;
 class ImageHostOverlay;
 class Layer;
 class TextureSource;
 class DataTextureSource;
 class CompositingRenderTarget;
 class CompositorBridgeParent;
 class LayerManagerComposite;
+class CompositorOGL;
+class CompositorD3D9;
+class CompositorD3D11;
+class BasicCompositor;
 
 enum SurfaceInitMode
 {
   INIT_MODE_NONE,
   INIT_MODE_CLEAR
 };
 
 /**
@@ -420,16 +424,21 @@ public:
                        uint32_t aFlashCounter = DIAGNOSTIC_FLASH_COUNTER_MAX);
 
 #ifdef MOZ_DUMP_PAINTING
   virtual const char* Name() const = 0;
 #endif // MOZ_DUMP_PAINTING
 
   virtual LayersBackend GetBackendType() const = 0;
 
+  virtual CompositorOGL* AsCompositorOGL() { return nullptr; }
+  virtual CompositorD3D9* AsCompositorD3D9() { return nullptr; }
+  virtual CompositorD3D11* AsCompositorD3D11() { return nullptr; }
+  virtual BasicCompositor* AsBasicCompositor() { return nullptr; }
+
   /**
    * Each Compositor has a unique ID.
    * This ID is used to keep references to each Compositor in a map accessed
    * from the compositor thread only, so that async compositables can find
    * the right compositor parent and schedule compositing even if the compositor
    * changed.
    */
   uint32_t GetCompositorID() const
--- a/gfx/layers/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -418,16 +418,22 @@ public:
   {
     if (GetScrollbarDirection() == Layer::VERTICAL) {
       return mLayer->GetVisibleRegion().GetBounds().height;
     } else {
       return mLayer->GetVisibleRegion().GetBounds().width;
     }
   }
 
+  bool IsScrollbarContainer() const
+  {
+    MOZ_ASSERT(IsValid());
+    return mLayer->IsScrollbarContainer();
+  }
+
   // Expose an opaque pointer to the layer. Mostly used for printf
   // purposes. This is not intended to be a general-purpose accessor
   // for the underlying layer.
   const void* GetLayer() const
   {
     MOZ_ASSERT(IsValid());
 
     return (void*)mLayer;
--- a/gfx/layers/LayerScope.cpp
+++ b/gfx/layers/LayerScope.cpp
@@ -1018,17 +1018,17 @@ SenderHelper::SendLayer(LayerComposite* 
         case Layer::TYPE_IMAGE:
         case Layer::TYPE_CANVAS:
         case Layer::TYPE_PAINTED: {
             // Get CompositableHost and Compositor
             CompositableHost* compHost = aLayer->GetCompositableHost();
             Compositor* comp = compHost->GetCompositor();
             // Send EffectChain only for CompositorOGL
             if (LayersBackend::LAYERS_OPENGL == comp->GetBackendType()) {
-                CompositorOGL* compOGL = static_cast<CompositorOGL*>(comp);
+                CompositorOGL* compOGL = comp->AsCompositorOGL();
                 EffectChain effect;
                 // Generate primary effect (lock and gen)
                 AutoLockCompositableHost lock(compHost);
                 aLayer->GenEffectChain(effect);
 
                 LayerScope::DrawBegin();
                 LayerScope::DrawEnd(compOGL->gl(), effect, aWidth, aHeight);
             }
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -1958,16 +1958,19 @@ Layer::PrintInfo(std::stringstream& aStr
     aStream << " [extend3DContext]";
   }
   if (Combines3DTransformWithAncestors()) {
     aStream << " [combines3DTransformWithAncestors]";
   }
   if (Is3DContextLeaf()) {
     aStream << " [is3DContextLeaf]";
   }
+  if (IsScrollbarContainer()) {
+    aStream << " [scrollbar]";
+  }
   if (GetScrollbarDirection() == VERTICAL) {
     aStream << nsPrintfCString(" [vscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
   }
   if (GetScrollbarDirection() == HORIZONTAL) {
     aStream << nsPrintfCString(" [hscrollbar=%lld]", GetScrollbarTargetContainerId()).get();
   }
   if (GetIsFixedPosition()) {
     LayerPoint anchor = GetFixedPositionAnchor();
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -94,16 +94,21 @@ RotatedBuffer::DrawBufferQuadrant(gfx::D
   if (!fillRect.IntersectRect(mBufferRect, quadrantRect))
     return;
 
   gfx::Point quadrantTranslation(quadrantRect.x, quadrantRect.y);
 
   MOZ_ASSERT(aSource != BUFFER_BOTH);
   RefPtr<SourceSurface> snapshot = GetSourceSurface(aSource);
 
+  if (!snapshot) {
+    gfxCriticalError() << "Invalid snapshot in RotatedBuffer::DrawBufferQuadrant";
+    return;
+  }
+
   // direct2d is much slower when using OP_SOURCE so use OP_OVER and
   // (maybe) a clear instead. Normally we need to draw in a single operation
   // (to avoid flickering) but direct2d is ok since it defers rendering.
   // We should try abstract this logic in a helper when we have other use
   // cases.
   if ((aTarget->GetBackendType() == BackendType::DIRECT2D ||
        aTarget->GetBackendType() == BackendType::DIRECT2D1_1) &&
       aOperator == CompositionOp::OP_SOURCE) {
@@ -270,17 +275,17 @@ RotatedContentBuffer::BorrowDrawTargetFo
     return nullptr;
   }
 
   MOZ_ASSERT(!mLoanedDrawTarget, "draw target has been borrowed and not returned");
   if (aSource == BUFFER_BOTH && HaveBufferOnWhite()) {
     if (!EnsureBufferOnWhite()) {
       return nullptr;
     }
-    MOZ_ASSERT(mDTBuffer && mDTBufferOnWhite);
+    MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid() && mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
     mLoanedDrawTarget = Factory::CreateDualDrawTarget(mDTBuffer, mDTBufferOnWhite);
   } else if (aSource == BUFFER_WHITE) {
     if (!EnsureBufferOnWhite()) {
       return nullptr;
     }
     mLoanedDrawTarget = mDTBufferOnWhite;
   } else {
     // BUFFER_BLACK, or BUFFER_BOTH with a single buffer.
@@ -313,22 +318,22 @@ BorrowDrawTarget::ReturnDrawTarget(gfx::
     mLoanedDrawTarget = nullptr;
   }
   aReturned = nullptr;
 }
 
 gfxContentType
 RotatedContentBuffer::BufferContentType()
 {
-  if (mBufferProvider || mDTBuffer) {
-    SurfaceFormat format;
+  if (mBufferProvider || (mDTBuffer && mDTBuffer->IsValid())) {
+    SurfaceFormat format = SurfaceFormat::B8G8R8A8;
 
     if (mBufferProvider) {
       format = mBufferProvider->GetFormat();
-    } else if (mDTBuffer) {
+    } else if (mDTBuffer && mDTBuffer->IsValid()) {
       format = mDTBuffer->GetFormat();
     }
 
     return ContentForFormat(format);
   }
   return gfxContentType::SENTINEL;
 }
 
@@ -339,23 +344,23 @@ RotatedContentBuffer::BufferSizeOkFor(co
           (SizedToVisibleBounds != mBufferSizePolicy &&
            aSize < mBufferRect.Size()));
 }
 
 bool
 RotatedContentBuffer::EnsureBuffer()
 {
   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
-  if (!mDTBuffer) {
+  if (!mDTBuffer || !mDTBuffer->IsValid()) {
     if (mBufferProvider) {
       mDTBuffer = mBufferProvider->BorrowDrawTarget();
     }
   }
 
-  NS_WARN_IF_FALSE(mDTBuffer, "no buffer");
+  NS_WARN_IF_FALSE(mDTBuffer && mDTBuffer->IsValid(), "no buffer");
   return !!mDTBuffer;
 }
 
 bool
 RotatedContentBuffer::EnsureBufferOnWhite()
 {
   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   if (!mDTBufferOnWhite) {
@@ -367,23 +372,23 @@ RotatedContentBuffer::EnsureBufferOnWhit
 
   NS_WARN_IF_FALSE(mDTBufferOnWhite, "no buffer");
   return !!mDTBufferOnWhite;
 }
 
 bool
 RotatedContentBuffer::HaveBuffer() const
 {
-  return mDTBuffer || mBufferProvider;
+  return mBufferProvider || (mDTBuffer && mDTBuffer->IsValid());
 }
 
 bool
 RotatedContentBuffer::HaveBufferOnWhite() const
 {
-  return mDTBufferOnWhite || mBufferProviderOnWhite;
+  return mBufferProviderOnWhite || (mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
 }
 
 static void
 WrapRotationAxis(int32_t* aRotationPoint, int32_t aSize)
 {
   if (*aRotationPoint < 0) {
     *aRotationPoint += aSize;
   } else if (*aRotationPoint >= aSize) {
@@ -586,23 +591,23 @@ RotatedContentBuffer::BeginPaint(Painted
           (newRotation != IntPoint(0,0) && !canHaveRotation)) {
         // The stuff we need to redraw will wrap around an edge of the
         // buffer (and the caller doesn't know how to support that), so
         // move the pixels we can keep into a position that lets us
         // redraw in just one quadrant.
         if (mBufferRotation == IntPoint(0,0)) {
           IntRect srcRect(IntPoint(0, 0), mBufferRect.Size());
           IntPoint dest = mBufferRect.TopLeft() - destBufferRect.TopLeft();
-          MOZ_ASSERT(mDTBuffer);
+          MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid());
           mDTBuffer->CopyRect(srcRect, dest);
           if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
             if (!EnsureBufferOnWhite()) {
               return result;
             }
-            MOZ_ASSERT(mDTBufferOnWhite);
+            MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
             mDTBufferOnWhite->CopyRect(srcRect, dest);
           }
           result.mDidSelfCopy = true;
           mDidSelfCopy = true;
           // Don't set destBuffer; we special-case self-copies, and
           // just did the necessary work above.
           mBufferRect = destBufferRect;
         } else {
@@ -620,17 +625,17 @@ RotatedContentBuffer::BeginPaint(Painted
                            size.height, stride,
                            newRotation.x * bytesPerPixel, newRotation.y);
             mDTBuffer->ReleaseBits(data);
 
             if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
               if (!EnsureBufferOnWhite()) {
                 return result;
               }
-              MOZ_ASSERT(mDTBufferOnWhite);
+              MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid());
               mDTBufferOnWhite->LockBits(&data, &size, &stride, &format);
               uint8_t bytesPerPixel = BytesPerPixel(format);
               BufferUnrotate(data,
                              size.width * bytesPerPixel,
                              size.height, stride,
                              newRotation.x * bytesPerPixel, newRotation.y);
               mDTBufferOnWhite->ReleaseBits(data);
             }
@@ -687,25 +692,25 @@ RotatedContentBuffer::BeginPaint(Painted
     if (!isClear && (mode != SurfaceMode::SURFACE_COMPONENT_ALPHA || HaveBufferOnWhite())) {
       // Copy the bits
       IntPoint offset = -destBufferRect.TopLeft();
       Matrix mat = Matrix::Translation(offset.x, offset.y);
       destDTBuffer->SetTransform(mat);
       if (!EnsureBuffer()) {
         return result;
       }
-       MOZ_ASSERT(mDTBuffer, "Have we got a Thebes buffer for some reason?");
+      MOZ_ASSERT(mDTBuffer && mDTBuffer->IsValid(), "Have we got a Thebes buffer for some reason?");
       DrawBufferWithRotation(destDTBuffer, BUFFER_BLACK, 1.0, CompositionOp::OP_SOURCE);
       destDTBuffer->SetTransform(Matrix());
 
       if (mode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
         if (!destDTBufferOnWhite || !EnsureBufferOnWhite()) {
           return result;
         }
-        MOZ_ASSERT(mDTBufferOnWhite, "Have we got a Thebes buffer for some reason?");
+        MOZ_ASSERT(mDTBufferOnWhite && mDTBufferOnWhite->IsValid(), "Have we got a Thebes buffer for some reason?");
         destDTBufferOnWhite->SetTransform(mat);
         DrawBufferWithRotation(destDTBufferOnWhite, BUFFER_WHITE, 1.0, CompositionOp::OP_SOURCE);
         destDTBufferOnWhite->SetTransform(Matrix());
       }
     }
 
     mDTBuffer = destDTBuffer.forget();
     mDTBufferOnWhite = destDTBufferOnWhite.forget();
@@ -748,17 +753,18 @@ RotatedContentBuffer::BorrowDrawTargetFo
   if (result->GetBackendType() == BackendType::DIRECT2D ||
       result->GetBackendType() == BackendType::DIRECT2D1_1) {
     // Simplify the draw region to avoid hitting expensive drawing paths
     // for complex regions.
     drawPtr->SimplifyOutwardByArea(100 * 100);
   }
 
   if (aPaintState.mMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
-    if (!mDTBuffer || !mDTBufferOnWhite) {
+    if (!mDTBuffer || !mDTBuffer->IsValid() ||
+        !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
       // This can happen in release builds if allocating one of the two buffers
       // failed. This in turn can happen if unreasonably large textures are
       // requested.
       return nullptr;
     }
     for (auto iter = drawPtr->RectIter(); !iter.Done(); iter.Next()) {
       const IntRect& rect = iter.Get();
       mDTBuffer->FillRect(Rect(rect.x, rect.y, rect.width, rect.height),
@@ -775,21 +781,28 @@ RotatedContentBuffer::BorrowDrawTargetFo
   }
 
   return result;
 }
 
 already_AddRefed<SourceSurface>
 RotatedContentBuffer::GetSourceSurface(ContextSource aSource) const
 {
-  MOZ_ASSERT(mDTBuffer);
+  if (!mDTBuffer || !mDTBuffer->IsValid()) {
+    gfxCriticalNote << "Invalid buffer in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBuffer);
+    return nullptr;
+  }
+
   if (aSource == BUFFER_BLACK) {
     return mDTBuffer->Snapshot();
   } else {
-    MOZ_ASSERT(mDTBufferOnWhite);
+    if (!mDTBufferOnWhite || !mDTBufferOnWhite->IsValid()) {
+    gfxCriticalNote << "Invalid buffer on white in RotatedContentBuffer::GetSourceSurface " << gfx::hexa(mDTBufferOnWhite);
+      return nullptr;
+    }
     MOZ_ASSERT(aSource == BUFFER_WHITE);
     return mDTBufferOnWhite->Snapshot();
   }
 }
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/RotatedBuffer.h
+++ b/gfx/layers/RotatedBuffer.h
@@ -334,29 +334,29 @@ public:
               const gfx::Matrix* aMaskTransform);
 
 protected:
   // new texture client versions
   void SetBufferProvider(TextureClient* aClient)
   {
     // Only this buffer provider can give us a buffer.  If we
     // already have one, something has gone wrong.
-    MOZ_ASSERT(!aClient || !mDTBuffer);
+    MOZ_ASSERT(!aClient || !mDTBuffer || !mDTBuffer->IsValid());
 
     mBufferProvider = aClient;
     if (!mBufferProvider) {
       mDTBuffer = nullptr;
     }
   }
 
   void SetBufferProviderOnWhite(TextureClient* aClient)
   {
     // Only this buffer provider can give us a buffer.  If we
     // already have one, something has gone wrong.
-    MOZ_ASSERT(!aClient || !mDTBufferOnWhite);
+    MOZ_ASSERT(!aClient || !mDTBufferOnWhite || !mDTBufferOnWhite->IsValid());
 
     mBufferProviderOnWhite = aClient;
     if (!mBufferProviderOnWhite) {
       mDTBufferOnWhite = nullptr;
     }
   }
 
   /**
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -362,17 +362,18 @@ APZCTreeManager::PrepareNodeForLayer(con
     AttachNodeToTree(node, aParent, aNextSibling);
     node->SetHitTestData(
         GetEventRegions(aLayer),
         aLayer.GetTransformTyped(),
         aLayer.GetClipRect() ? Some(ParentLayerIntRegion(*aLayer.GetClipRect())) : Nothing(),
         GetEventRegionsOverride(aParent, aLayer));
     node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                            aLayer.GetScrollbarDirection(),
-                           aLayer.GetScrollbarSize());
+                           aLayer.GetScrollbarSize(),
+                           aLayer.IsScrollbarContainer());
     return node;
   }
 
   AsyncPanZoomController* apzc = nullptr;
   // If we get here, aLayer is a scrollable layer and somebody
   // has registered a GeckoContentController for it, so we need to ensure
   // it has an APZC instance to manage its scrolling.
 
@@ -541,17 +542,18 @@ APZCTreeManager::PrepareNodeForLayer(con
         GetEventRegions(aLayer),
         aLayer.GetTransformTyped(),
         Some(clipRegion),
         GetEventRegionsOverride(aParent, aLayer));
   }
 
   node->SetScrollbarData(aLayer.GetScrollbarTargetContainerId(),
                          aLayer.GetScrollbarDirection(),
-                         aLayer.GetScrollbarSize());
+                         aLayer.GetScrollbarSize(),
+                         aLayer.IsScrollbarContainer());
   return node;
 }
 
 HitTestingTreeNode*
 APZCTreeManager::UpdateHitTestingTree(TreeBuildingState& aState,
                                       const LayerMetricsWrapper& aLayer,
                                       uint64_t aLayersId,
                                       const gfx::Matrix4x4& aAncestorTransform,
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -23,16 +23,17 @@ HitTestingTreeNode::HitTestingTreeNode(A
                                        bool aIsPrimaryHolder,
                                        uint64_t aLayersId)
   : mApzc(aApzc)
   , mIsPrimaryApzcHolder(aIsPrimaryHolder)
   , mLayersId(aLayersId)
   , mScrollViewId(FrameMetrics::NULL_SCROLL_ID)
   , mScrollDir(Layer::NONE)
   , mScrollSize(0)
+  , mIsScrollbarContainer(false)
   , mOverride(EventRegionsOverride::NoOverride)
 {
 if (mIsPrimaryApzcHolder) {
     MOZ_ASSERT(mApzc);
   }
   MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
 }
 
@@ -87,21 +88,25 @@ HitTestingTreeNode::SetLastChild(HitTest
       // but it's better than nothing.
       MOZ_ASSERT(aChild->GetApzc() != parent);
       aChild->SetApzcParent(parent);
     }
   }
 }
 
 void
-HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize)
+HitTestingTreeNode::SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+                                     Layer::ScrollDirection aDir,
+                                     int32_t aScrollSize,
+                                     bool aIsScrollContainer)
 {
   mScrollViewId = aScrollViewId;
   mScrollDir = aDir;
   mScrollSize = aScrollSize;;
+  mIsScrollbarContainer = aIsScrollContainer;
 }
 
 bool
 HitTestingTreeNode::MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const
 {
   return ((mScrollDir == Layer::HORIZONTAL &&
            aDragMetrics.mDirection == AsyncDragMetrics::HORIZONTAL) ||
           (mScrollDir == Layer::VERTICAL &&
@@ -113,17 +118,17 @@ int32_t
 HitTestingTreeNode::GetScrollSize() const
 {
   return mScrollSize;
 }
 
 bool
 HitTestingTreeNode::IsScrollbarNode() const
 {
-  return (mScrollDir != Layer::NONE);
+  return mIsScrollbarContainer;
 }
 
 void
 HitTestingTreeNode::SetPrevSibling(HitTestingTreeNode* aSibling)
 {
   mPrevSibling = aSibling;
   if (aSibling) {
     aSibling->mParent = mParent;
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -87,17 +87,20 @@ public:
   void SetHitTestData(const EventRegions& aRegions,
                       const CSSTransformMatrix& aTransform,
                       const Maybe<ParentLayerIntRegion>& aClipRegion,
                       const EventRegionsOverride& aOverride);
   bool IsOutsideClip(const ParentLayerPoint& aPoint) const;
 
   /* Scrollbar info */
 
-  void SetScrollbarData(FrameMetrics::ViewID aScrollViewId, Layer::ScrollDirection aDir, int32_t aScrollSize);
+  void SetScrollbarData(FrameMetrics::ViewID aScrollViewId,
+                        Layer::ScrollDirection aDir,
+                        int32_t aScrollSize,
+                        bool aIsScrollContainer);
   bool MatchesScrollDragMetrics(const AsyncDragMetrics& aDragMetrics) const;
   int32_t GetScrollSize() const;
   bool IsScrollbarNode() const;
 
   /* Convert aPoint into the LayerPixel space for the layer corresponding to
    * this node. */
   Maybe<LayerPoint> Untransform(const ParentLayerPoint& aPoint) const;
   /* Assuming aPoint is inside the clip region for this node, check which of the
@@ -119,16 +122,17 @@ private:
   RefPtr<AsyncPanZoomController> mApzc;
   bool mIsPrimaryApzcHolder;
 
   uint64_t mLayersId;
 
   FrameMetrics::ViewID mScrollViewId;
   Layer::ScrollDirection mScrollDir;
   int32_t mScrollSize;
+  bool mIsScrollbarContainer;
 
   /* Let {L,M} be the {layer, scrollable metrics} pair that this node
    * corresponds to in the layer tree. mEventRegions contains the event regions
    * from L, in the case where event-regions are enabled. If event-regions are
    * disabled, it will contain the visible region of L, which we use as an
    * approximation to the hit region for the purposes of obscuring other layers.
    * This value is in L's LayerPixels.
    */
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -620,11 +620,19 @@ BasicCompositor::EndFrameForExternalComp
 {
   MOZ_ASSERT(!mTarget);
   MOZ_ASSERT(!mDrawTarget);
   MOZ_ASSERT(!mRenderTarget);
 
   mDidExternalComposition = true;
 }
 
+BasicCompositor*
+AssertBasicCompositor(Compositor* aCompositor)
+{
+  BasicCompositor* compositor = aCompositor ? aCompositor->AsBasicCompositor()
+                                            : nullptr;
+  MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+  return compositor;
+}
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/basic/BasicCompositor.h
+++ b/gfx/layers/basic/BasicCompositor.h
@@ -43,16 +43,19 @@ class BasicCompositor : public Composito
 {
 public:
   explicit BasicCompositor(CompositorBridgeParent* aParent, nsIWidget *aWidget);
 
 protected:
   virtual ~BasicCompositor();
 
 public:
+
+  virtual BasicCompositor* AsBasicCompositor() override { return this; }
+
   virtual bool Initialize() override;
 
   virtual void Destroy() override {}
 
   virtual void DetachWidget() override;
 
   virtual TextureFactoryIdentifier GetTextureFactoryIdentifier() override;
 
@@ -139,12 +142,14 @@ private:
 
   LayoutDeviceIntRect mInvalidRect;
   LayoutDeviceIntRegion mInvalidRegion;
   bool mDidExternalComposition;
 
   uint32_t mMaxTextureSize;
 };
 
+BasicCompositor* AssertBasicCompositor(Compositor* aCompositor);
+
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_BASICCOMPOSITOR_H */
--- a/gfx/layers/basic/GrallocTextureHostBasic.cpp
+++ b/gfx/layers/basic/GrallocTextureHostBasic.cpp
@@ -175,17 +175,21 @@ GrallocTextureHostBasic::ClearTextureSou
     mMappedBuffer = nullptr;
     graphicBuffer->unlock();
   }
 }
 
 void
 GrallocTextureHostBasic::SetCompositor(Compositor* aCompositor)
 {
-  BasicCompositor* compositor = static_cast<BasicCompositor*>(aCompositor);
+  BasicCompositor* compositor = AssertBasicCompositor(aCompositor);
+  if (!compositor) {
+    return;
+  }
+
   mCompositor = compositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(compositor);
   }
 }
 
 gfx::SurfaceFormat
 GrallocTextureHostBasic::GetFormat() const {
--- a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -57,17 +57,17 @@ MacIOSurfaceTextureSourceBasic::GetSurfa
     mSourceSurface = CreateSourceSurfaceFromMacIOSurface(mSurface);
   }
   return mSourceSurface;
 }
 
 void
 MacIOSurfaceTextureSourceBasic::SetCompositor(Compositor* aCompositor)
 {
-  mCompositor = static_cast<BasicCompositor*>(aCompositor);
+  mCompositor = AssertBasicCompositor(aCompositor);
 }
 
 bool
 MacIOSurfaceTextureHostBasic::Lock()
 {
   if (!mCompositor) {
     return false;
   }
@@ -76,17 +76,21 @@ MacIOSurfaceTextureHostBasic::Lock()
     mTextureSource = new MacIOSurfaceTextureSourceBasic(mCompositor, mSurface);
   }
   return true;
 }
 
 void
 MacIOSurfaceTextureHostBasic::SetCompositor(Compositor* aCompositor)
 {
-  BasicCompositor* compositor = static_cast<BasicCompositor*>(aCompositor);
+  BasicCompositor* compositor = AssertBasicCompositor(aCompositor);
+  if (!compositor) {
+    mTextureSource = nullptr;
+    return;
+  }
   mCompositor = compositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(compositor);
   }
 }
 
 gfx::SurfaceFormat
 MacIOSurfaceTextureHostBasic::GetFormat() const {
--- a/gfx/layers/basic/X11TextureSourceBasic.cpp
+++ b/gfx/layers/basic/X11TextureSourceBasic.cpp
@@ -40,19 +40,17 @@ X11TextureSourceBasic::GetSurface(DrawTa
                                                     GetSize(), GetFormat());
   }
   return mSourceSurface;
 }
 
 void
 X11TextureSourceBasic::SetCompositor(Compositor* aCompositor)
 {
-  MOZ_ASSERT(aCompositor->GetBackendType() == LayersBackend::LAYERS_BASIC);
-  BasicCompositor* compositor = static_cast<BasicCompositor*>(aCompositor);
-  mCompositor = compositor;
+  mCompositor = AssertBasicCompositor(aCompositor);
 }
 
 SurfaceFormat
 X11TextureSourceBasic::ContentTypeToSurfaceFormat(gfxContentType aType)
 {
   switch (aType) {
     case gfxContentType::COLOR:
       return SurfaceFormat::B8G8R8X8;
--- a/gfx/layers/client/ClientPaintedLayer.cpp
+++ b/gfx/layers/client/ClientPaintedLayer.cpp
@@ -155,23 +155,17 @@ ClientLayerManager::CreatePaintedLayer()
 {
   return CreatePaintedLayerWithHint(NONE);
 }
 
 already_AddRefed<PaintedLayer>
 ClientLayerManager::CreatePaintedLayerWithHint(PaintedLayerCreationHint aHint)
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
-  if (gfxPrefs::LayersTilesEnabled()
-#ifndef MOZ_X11
-      && (AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_OPENGL ||
-          AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D9 ||
-          AsShadowForwarder()->GetCompositorBackendType() == LayersBackend::LAYERS_D3D11)
-#endif
-  ) {
+  if (gfxPrefs::LayersTilesEnabled()) {
     RefPtr<ClientTiledPaintedLayer> layer = new ClientTiledPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   } else {
     RefPtr<ClientPaintedLayer> layer = new ClientPaintedLayer(this, aHint);
     CREATE_SHADOW(Painted);
     return layer.forget();
   }
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -1060,28 +1060,28 @@ LayerManagerComposite::RenderToPresentat
     surface = GLContextProviderEGL::CreateEGLSurface(window);
     if (!surface) {
       return;
     }
 
     AndroidBridge::Bridge()->SetPresentationSurface(surface);
   }
 
-  CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get());
+  CompositorOGL* compositor = mCompositor->AsCompositorOGL();
   GLContext* gl = compositor->gl();
   GLContextEGL* egl = GLContextEGL::Cast(gl);
 
   if (!egl) {
     return;
   }
 
   const IntSize windowSize = AndroidBridge::Bridge()->GetNativeWindowSize(window);
 
 #elif defined(MOZ_WIDGET_GONK)
-  CompositorOGL* compositor = static_cast<CompositorOGL*>(mCompositor.get());
+  CompositorOGL* compositor = mCompositor->AsCompositorOGL();
   nsScreenGonk* screen = static_cast<nsWindow*>(mCompositor->GetWidget())->GetScreen();
   if (!screen->IsPrimaryScreen()) {
     // Only primary screen support mirroring
     return;
   }
 
   nsWindow* mirrorScreenWidget = screen->GetMirroringWidget();
   if (!mirrorScreenWidget) {
--- a/gfx/layers/composite/X11TextureHost.cpp
+++ b/gfx/layers/composite/X11TextureHost.cpp
@@ -36,24 +36,22 @@ X11TextureHost::Lock()
   if (!mCompositor) {
     return false;
   }
 
   if (!mTextureSource) {
     switch (mCompositor->GetBackendType()) {
       case LayersBackend::LAYERS_BASIC:
         mTextureSource =
-          new X11TextureSourceBasic(static_cast<BasicCompositor*>(mCompositor.get()),
-                                    mSurface);
+          new X11TextureSourceBasic(mCompositor->AsBasicCompositor(), mSurface);
         break;
 #ifdef GL_PROVIDER_GLX
       case LayersBackend::LAYERS_OPENGL:
         mTextureSource =
-          new X11TextureSourceOGL(static_cast<CompositorOGL*>(mCompositor.get()),
-                                  mSurface);
+          new X11TextureSourceOGL(mCompositor->AsCompositorOGL(), mSurface);
         break;
 #endif
       default:
         return false;
     }
   }
 
   return true;
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -40,16 +40,18 @@ struct PixelShaderConstants
 struct DeviceAttachmentsD3D11;
 
 class CompositorD3D11 : public Compositor
 {
 public:
   CompositorD3D11(CompositorBridgeParent* aParent, nsIWidget* aWidget);
   ~CompositorD3D11();
 
+  virtual CompositorD3D11* AsCompositorD3D11() override { return this; }
+
   virtual bool Initialize() override;
   virtual void Destroy() override {}
   virtual void DetachWidget() override { mWidget = nullptr; }
 
   virtual TextureFactoryIdentifier
     GetTextureFactoryIdentifier() override;
 
   virtual already_AddRefed<DataTextureSource>
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -633,32 +633,34 @@ DXGITextureHostD3D11::GetDevice()
     return nullptr;
   }
 
   RefPtr<ID3D11Device> device;
   gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device);
   return device;
 }
 
-static bool AssertD3D11Compositor(Compositor* aCompositor)
+static CompositorD3D11* AssertD3D11Compositor(Compositor* aCompositor)
 {
-  bool ok = aCompositor && aCompositor->GetBackendType() == LayersBackend::LAYERS_D3D11;
-  MOZ_ASSERT(ok);
-  return ok;
+  CompositorD3D11* compositor = aCompositor ? aCompositor->AsCompositorD3D11()
+                                            : nullptr;
+  MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+  return compositor;
 }
 
 void
 DXGITextureHostD3D11::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D11Compositor(aCompositor)) {
+  CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+  if (!d3dCompositor) {
     mCompositor = nullptr;
     mTextureSource = nullptr;
     return;
   }
-  mCompositor = static_cast<CompositorD3D11*>(aCompositor);
+  mCompositor = d3dCompositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(aCompositor);
   }
 }
 
 bool
 DXGITextureHostD3D11::Lock()
 {
@@ -758,24 +760,24 @@ DXGIYCbCrTextureHostD3D11::GetDevice()
   RefPtr<ID3D11Device> device;
   gfxWindowsPlatform::GetPlatform()->GetD3D11Device(&device);
   return device;
 }
 
 void
 DXGIYCbCrTextureHostD3D11::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D11Compositor(aCompositor)) {
-    mCompositor = nullptr;
+  mCompositor = AssertD3D11Compositor(aCompositor);
+  if (!mCompositor) {
     mTextureSources[0] = nullptr;
     mTextureSources[1] = nullptr;
     mTextureSources[2] = nullptr;
     return;
   }
-  mCompositor = static_cast<CompositorD3D11*>(aCompositor);
+
   if (mTextureSources[0]) {
     mTextureSources[0]->SetCompositor(aCompositor);
   }
 }
 
 bool
 DXGIYCbCrTextureHostD3D11::Lock()
 {
@@ -991,20 +993,21 @@ DataTextureSourceD3D11::GetTileRect()
 {
   IntRect rect = GetTileRect(mCurrentTile);
   return IntRect(rect.x, rect.y, rect.width, rect.height);
 }
 
 void
 DataTextureSourceD3D11::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D11Compositor(aCompositor)) {
+  CompositorD3D11* d3dCompositor = AssertD3D11Compositor(aCompositor);
+  if (!d3dCompositor) {
     return;
   }
-  mCompositor = static_cast<CompositorD3D11*>(aCompositor);
+  mCompositor = d3dCompositor;
   if (mNextSibling) {
     mNextSibling->SetCompositor(aCompositor);
   }
 }
 
 CompositingRenderTargetD3D11::CompositingRenderTargetD3D11(ID3D11Texture2D* aTexture,
                                                            const gfx::IntPoint& aOrigin,
                                                            DXGI_FORMAT aFormatOverride)
--- a/gfx/layers/d3d9/CompositorD3D9.h
+++ b/gfx/layers/d3d9/CompositorD3D9.h
@@ -19,16 +19,18 @@ namespace mozilla {
 namespace layers {
 
 class CompositorD3D9 : public Compositor
 {
 public:
   CompositorD3D9(CompositorBridgeParent* aParent, nsIWidget *aWidget);
   ~CompositorD3D9();
 
+  virtual CompositorD3D9* AsCompositorD3D9() override { return this; }
+
   virtual bool Initialize() override;
   virtual void Destroy() override {}
   virtual void DetachWidget() override { mWidget = nullptr; }
 
   virtual TextureFactoryIdentifier
     GetTextureFactoryIdentifier() override;
 
   virtual bool CanUseCanvasLayerForSize(const gfx::IntSize &aSize) override;
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -488,31 +488,32 @@ DataTextureSourceD3D9::Update(gfx::DataS
         return false;
       }
     }
   }
 
   return true;
 }
 
-static bool AssertD3D9Compositor(Compositor* aCompositor)
+static CompositorD3D9* AssertD3D9Compositor(Compositor* aCompositor)
 {
-  bool ok = aCompositor && aCompositor->GetBackendType() == LayersBackend::LAYERS_D3D9;
-  MOZ_ASSERT(ok);
-  return ok;
+  CompositorD3D9* compositor = aCompositor ? aCompositor->AsCompositorD3D9()
+                                           : nullptr;
+  MOZ_DIAGNOSTIC_ASSERT(!!compositor);
+  return compositor;
 }
 
 void
 DataTextureSourceD3D9::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D9Compositor(aCompositor)) {
+  CompositorD3D9* d3dCompositor = AssertD3D9Compositor(aCompositor);
+  if (!d3dCompositor) {
     Reset();
     return;
   }
-  CompositorD3D9* d3dCompositor = static_cast<CompositorD3D9*>(aCompositor);
   if (mCompositor && mCompositor != d3dCompositor) {
     Reset();
   }
   mCompositor = d3dCompositor;
 }
 
 void
 DataTextureSourceD3D9::Reset()
@@ -915,22 +916,21 @@ TextureHostD3D9::GetDevice()
     return nullptr;
   }
   return mCompositor ? mCompositor->device() : nullptr;
 }
 
 void
 TextureHostD3D9::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D9Compositor(aCompositor)) {
-    mCompositor = nullptr;
+  mCompositor = AssertD3D9Compositor(aCompositor);
+  if (!mCompositor) {
     mTextureSource = nullptr;
     return;
   }
-  mCompositor = static_cast<CompositorD3D9*>(aCompositor);
   if (mTextureSource) {
     mTextureSource->SetCompositor(aCompositor);
   }
 }
 
 bool
 TextureHostD3D9::BindTextureSource(CompositableTextureSourceRef& aTexture)
 {
@@ -1041,22 +1041,20 @@ DXGITextureHostD3D9::Unlock()
 {
   MOZ_ASSERT(mIsLocked);
   mIsLocked = false;
 }
 
 void
 DXGITextureHostD3D9::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D9Compositor(aCompositor)) {
-    mCompositor = nullptr;
+  mCompositor = AssertD3D9Compositor(aCompositor);
+  if (!mCompositor) {
     mTextureSource = nullptr;
-    return;
   }
-  mCompositor = static_cast<CompositorD3D9*>(aCompositor);
 }
 
 void
 DXGITextureHostD3D9::DeallocateDeviceData()
 {
   mTextureSource = nullptr;
 }
 
@@ -1080,24 +1078,22 @@ DXGIYCbCrTextureHostD3D9::GetDevice()
     return nullptr;
   }
   return mCompositor ? mCompositor->device() : nullptr;
 }
 
 void
 DXGIYCbCrTextureHostD3D9::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertD3D9Compositor(aCompositor)) {
-    mCompositor = nullptr;
+  mCompositor = AssertD3D9Compositor(aCompositor);
+  if (!mCompositor) {
     mTextureSources[0] = nullptr;
     mTextureSources[1] = nullptr;
     mTextureSources[2] = nullptr;
-    return;
   }
-  mCompositor = static_cast<CompositorD3D9*>(aCompositor);
 }
 
 bool
 DXGIYCbCrTextureHostD3D9::Lock()
 {
   if (!GetDevice()) {
     NS_WARNING("trying to lock a TextureHost without a D3D device");
     return false;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -824,32 +824,16 @@ CompositorBridgeParent::RecvMakeSnapshot
     // of error to the child process and let it deal with it...
     return false;
   }
   ForceComposeToTarget(target, &aRect);
   return true;
 }
 
 bool
-CompositorBridgeParent::RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot)
-{
-  if (!mCompositor || !mCompositor->GetWidget()) {
-    return false;
-  }
-
-  RefPtr<DrawTarget> target = GetDrawTargetForDescriptor(aInSnapshot, gfx::BackendType::CAIRO);
-  MOZ_ASSERT(target);
-  if (!target) {
-    return false;
-  }
-  mCompositor->GetWidget()->CaptureWidgetOnScreen(target);
-  return true;
-}
-
-bool
 CompositorBridgeParent::RecvFlushRendering()
 {
   if (mCompositorScheduler->NeedsComposite())
   {
     CancelCurrentCompositeTask();
     ForceComposeToTarget(nullptr);
   }
   return true;
@@ -1934,18 +1918,16 @@ public:
   virtual bool RecvResume() override { return true; }
   virtual bool RecvNotifyHidden(const uint64_t& id) override;
   virtual bool RecvNotifyVisible(const uint64_t& id) override;
   virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
   virtual bool RecvAdoptChild(const uint64_t& child) override { return false; }
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const gfx::IntRect& aRect) override
   { return true; }
-  virtual bool RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot) override
-  { return true; }
   virtual bool RecvFlushRendering() override { return true; }
   virtual bool RecvForcePresent() override { return true; }
   virtual bool RecvNotifyRegionInvalidated(const nsIntRegion& aRegion) override { return true; }
   virtual bool RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex) override { return true; }
   virtual bool RecvStopFrameTimeRecording(const uint32_t& aStartIndex, InfallibleTArray<float>* intervals) override  { return true; }
 
   virtual bool RecvClearApproximatelyVisibleRegions(const uint64_t& aLayersId,
                                                     const uint32_t& aPresShellId) override
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -231,17 +231,16 @@ public:
   virtual bool RecvPause() override;
   virtual bool RecvResume() override;
   virtual bool RecvNotifyHidden(const uint64_t& id) override { return true; }
   virtual bool RecvNotifyVisible(const uint64_t& id) override { return true; }
   virtual bool RecvNotifyChildCreated(const uint64_t& child) override;
   virtual bool RecvAdoptChild(const uint64_t& child) override;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 const gfx::IntRect& aRect) override;
-  virtual bool RecvMakeWidgetSnapshot(const SurfaceDescriptor& aInSnapshot) override;
   virtual bool RecvFlushRendering() override;
   virtual bool RecvForcePresent() override;
 
   virtual bool RecvAcknowledgeCompositorUpdate(const uint64_t& aLayersId) override {
     MOZ_ASSERT_UNREACHABLE("This message is only sent cross-process");
     return true;
   }
 
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -356,16 +356,19 @@ LayerTransactionParent::RecvUpdate(Infal
       if (common.isStickyPosition()) {
         layer->SetStickyPositionData(common.stickyScrollContainerId(),
                                      common.stickyScrollRangeOuter(),
                                      common.stickyScrollRangeInner());
       }
       layer->SetScrollbarData(common.scrollbarTargetContainerId(),
         static_cast<Layer::ScrollDirection>(common.scrollbarDirection()),
         common.scrollbarThumbRatio());
+      if (common.isScrollbarContainer()) {
+        layer->SetIsScrollbarContainer();
+      }
       layer->SetMixBlendMode((gfx::CompositionOp)common.mixBlendMode());
       layer->SetForceIsolatedGroup(common.forceIsolatedGroup());
       if (PLayerParent* maskLayer = common.maskLayerParent()) {
         layer->SetMaskLayer(cast(maskLayer)->AsLayer());
       } else {
         layer->SetMaskLayer(nullptr);
       }
       layer->SetAnimations(common.animations());
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -225,16 +225,17 @@ struct CommonLayerAttributes {
   bool isClipFixed;
   bool isStickyPosition;
   uint64_t stickyScrollContainerId;
   LayerRect stickyScrollRangeOuter;
   LayerRect stickyScrollRangeInner;
   uint64_t scrollbarTargetContainerId;
   uint32_t scrollbarDirection;
   float scrollbarThumbRatio;
+  bool isScrollbarContainer;
   int8_t mixBlendMode;
   bool forceIsolatedGroup;
   nullable PLayer maskLayer;
   PLayer[] ancestorMaskLayers;
   // Animated colors will only honored for ColorLayers.
   Animation[] animations;
   nsIntRegion invalidRegion;
   ScrollMetadata[] scrollMetadata;
--- a/gfx/layers/ipc/PCompositorBridge.ipdl
+++ b/gfx/layers/ipc/PCompositorBridge.ipdl
@@ -133,24 +133,16 @@ parent:
   // render target at the time this message is received.  If the size
   // or format of |inSnapshot| doesn't match our render target,
   // results are undefined.
   //
   // NB: this message will result in animations, transforms, effects,
   // and so forth being interpolated.  That's what we want to happen.
   sync MakeSnapshot(SurfaceDescriptor inSnapshot, IntRect dirtyRect);
 
-  // Same as Makesnapshot(), except the snapshot is read from the underlying
-  // operating system desktop rather than the compositor's backbuffer. This
-  // is intended for testing whether hardware acceleration works.
-  //
-  // This call is part of IPDL, even though it simply wraps an nsIWidget
-  // call, to make sure it does not occur in the middle of a composite.
-  sync MakeWidgetSnapshot(SurfaceDescriptor inSnapshot);
-
   // Make sure any pending composites are started immediately and
   // block until they are completed.
   sync FlushRendering();
 
   // Force an additional frame presentation to be executed. This is used to
   // work around a windows presentation bug (See Bug 1232042)
   async ForcePresent();
 
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -829,16 +829,17 @@ ShadowLayerForwarder::EndTransaction(Inf
       common.stickyScrollContainerId() = 0;
       common.stickyScrollRangeOuter() = LayerRect();
       common.stickyScrollRangeInner() = LayerRect();
 #endif
     }
     common.scrollbarTargetContainerId() = mutant->GetScrollbarTargetContainerId();
     common.scrollbarDirection() = mutant->GetScrollbarDirection();
     common.scrollbarThumbRatio() = mutant->GetScrollbarThumbRatio();
+    common.isScrollbarContainer() = mutant->IsScrollbarContainer();
     common.mixBlendMode() = (int8_t)mutant->GetMixBlendMode();
     common.forceIsolatedGroup() = mutant->GetForceIsolatedGroup();
     if (Layer* maskLayer = mutant->GetMaskLayer()) {
       common.maskLayerChild() = Shadow(maskLayer->AsShadowableLayer());
     } else {
       common.maskLayerChild() = nullptr;
     }
     common.maskLayerParent() = nullptr;
--- a/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
+++ b/gfx/layers/opengl/CompositingRenderTargetOGL.cpp
@@ -76,17 +76,17 @@ CompositingRenderTargetOGL::BindRenderTa
   }
 }
 
 #ifdef MOZ_DUMP_PAINTING
 already_AddRefed<DataSourceSurface>
 CompositingRenderTargetOGL::Dump(Compositor* aCompositor)
 {
   MOZ_ASSERT(mInitParams.mStatus == InitParams::INITIALIZED);
-  CompositorOGL* compositorOGL = static_cast<CompositorOGL*>(aCompositor);
+  CompositorOGL* compositorOGL = aCompositor->AsCompositorOGL();
   return ReadBackSurface(mGL, mTextureHandle, true, compositorOGL->GetFBOFormat());
 }
 #endif
 
 void
 CompositingRenderTargetOGL::InitializeImpl()
 {
   MOZ_ASSERT(mInitParams.mStatus == InitParams::READY);
--- a/gfx/layers/opengl/CompositorOGL.h
+++ b/gfx/layers/opengl/CompositorOGL.h
@@ -196,16 +196,18 @@ public:
   explicit CompositorOGL(CompositorBridgeParent* aParent,
                          nsIWidget *aWidget, int aSurfaceWidth = -1, int aSurfaceHeight = -1,
                          bool aUseExternalSurfaceSize = false);
 
 protected:
   virtual ~CompositorOGL();
 
 public:
+  virtual CompositorOGL* AsCompositorOGL() override { return this; }
+
   virtual already_AddRefed<DataTextureSource>
   CreateDataTextureSource(TextureFlags aFlags = TextureFlags::NO_FLAGS) override;
 
   virtual bool Initialize() override;
 
   virtual void Destroy() override;
 
   virtual void DetachWidget() override { mWidget = nullptr; }
--- a/gfx/layers/opengl/GLManager.cpp
+++ b/gfx/layers/opengl/GLManager.cpp
@@ -57,16 +57,15 @@ public:
 private:
   RefPtr<CompositorOGL> mImpl;
 };
 
 /* static */ GLManager*
 GLManager::CreateGLManager(LayerManagerComposite* aManager)
 {
   if (aManager && aManager->GetCompositor()->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
-    return new GLManagerCompositor(static_cast<CompositorOGL*>(
-      aManager->GetCompositor()));
+    return new GLManagerCompositor(aManager->GetCompositor()->AsCompositorOGL());
   }
   return nullptr;
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -125,18 +125,17 @@ GrallocTextureHostOGL::GrallocTextureHos
 GrallocTextureHostOGL::~GrallocTextureHostOGL()
 {
   DestroyEGLImage();
 }
 
 void
 GrallocTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
-  MOZ_ASSERT(aCompositor);
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
+  mCompositor = AssertGLCompositor(aCompositor);
   if (mGLTextureSource) {
     mGLTextureSource->SetCompositor(mCompositor);
   }
 
   if (mCompositor && aCompositor != mCompositor) {
     DestroyEGLImage();
   }
 }
--- a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -63,23 +63,23 @@ MacIOSurfaceTextureHostOGL::Lock()
     }
   }
   return true;
 }
 
 void
 MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     mTextureSource = nullptr;
     mCompositor = nullptr;
     return;
   }
 
-  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   if (mCompositor != glCompositor) {
     // Cannot share GL texture identifiers across compositors.
     mTextureSource = nullptr;
   }
   mCompositor = glCompositor;
 }
 
 gfx::SurfaceFormat
@@ -150,23 +150,18 @@ MacIOSurfaceTextureSourceOGL::BindTextur
   gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
   mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
   ApplyFilterToBoundTexture(gl, aFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
 }
 
 void
 MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
-    mCompositor = nullptr;
-    return;
-  }
-
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
-  if (mNextSibling) {
+  mCompositor = AssertGLCompositor(aCompositor);
+  if (mCompositor && mNextSibling) {
     mNextSibling->SetCompositor(aCompositor);
   }
 }
 
 gl::GLContext*
 MacIOSurfaceTextureSourceOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
--- a/gfx/layers/opengl/TextureHostOGL.cpp
+++ b/gfx/layers/opengl/TextureHostOGL.cpp
@@ -226,31 +226,32 @@ TextureImageTextureSourceOGL::CopyTo(con
     aDest->AsSourceOGL()->AsTextureImageTextureSource();
   MOZ_ASSERT(dest, "Incompatible destination type!");
 
   mCompositor->BlitTextureImageHelper()->BlitTextureImage(mTexImage, aSourceRect,
                                                   dest->mTexImage, aDestRect);
   dest->mTexImage->MarkValid();
 }
 
-bool AssertGLCompositor(Compositor* aCompositor)
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor)
 {
-  bool ok = aCompositor && aCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL;
-  MOZ_ASSERT(ok);
-  return ok;
+  CompositorOGL* compositor = aCompositor ? aCompositor->AsCompositorOGL()
+                                          : nullptr;
+  MOZ_ASSERT(!!compositor);
+  return compositor;
 }
 
 void
 TextureImageTextureSourceOGL::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     DeallocateDeviceData();
     return;
   }
-  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   if (mCompositor != glCompositor) {
     DeallocateDeviceData();
     mCompositor = glCompositor;
   }
 }
 
 gfx::IntSize
 TextureImageTextureSourceOGL::GetSize() const
@@ -345,25 +346,26 @@ GLTextureSource::BindTexture(GLenum aTex
   gl->fActiveTexture(aTextureUnit);
   gl->fBindTexture(mTextureTarget, mTextureHandle);
   ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget);
 }
 
 void
 GLTextureSource::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     return;
   }
 
-  if (mCompositor && mCompositor != aCompositor) {
+  if (mCompositor && mCompositor != glCompositor) {
     gfxCriticalError() << "GLTextureSource does not support changing compositors";
   }
+  mCompositor = glCompositor;
 
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
   if (mNextSibling) {
     mNextSibling->SetCompositor(aCompositor);
   }
 }
 
 bool
 GLTextureSource::IsValid() const
 {
@@ -416,25 +418,26 @@ SurfaceTextureSource::BindTexture(GLenum
   mSurfTex->UpdateTexImage();
 
   ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget);
 }
 
 void
 SurfaceTextureSource::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     DeallocateDeviceData();
     return;
   }
-  if (mCompositor != aCompositor) {
+  if (mCompositor != glCompositor) {
     DeallocateDeviceData();
   }
 
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
+  mCompositor = glCompositor;
 }
 
 bool
 SurfaceTextureSource::IsValid() const
 {
   return !!gl();
 }
 
@@ -512,21 +515,21 @@ SurfaceTextureHost::Unlock()
 {
   MOZ_ASSERT(mSurfTex);
   mSurfTex->Detach();
 }
 
 void
 SurfaceTextureHost::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     DeallocateDeviceData();
     return;
   }
-  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   mCompositor = glCompositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(glCompositor);
   }
 }
 
 gfx::SurfaceFormat
 SurfaceTextureHost::GetFormat() const
@@ -586,21 +589,17 @@ EGLImageTextureSource::BindTexture(GLenu
   gl->fEGLImageTargetTexture2D(mTextureTarget, mImage);
 
   ApplyFilterToBoundTexture(gl, aFilter, mTextureTarget);
 }
 
 void
 EGLImageTextureSource::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
-    mCompositor = nullptr;
-    return;
-  }
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
+  mCompositor = AssertGLCompositor(aCompositor);
 }
 
 bool
 EGLImageTextureSource::IsValid() const
 {
   return !!gl();
 }
 
@@ -681,22 +680,22 @@ EGLImageTextureHost::Lock()
 void
 EGLImageTextureHost::Unlock()
 {
 }
 
 void
 EGLImageTextureHost::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     mCompositor = nullptr;
     mTextureSource = nullptr;
     return;
   }
-  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   mCompositor = glCompositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(glCompositor);
   }
 }
 
 gfx::SurfaceFormat
 EGLImageTextureHost::GetFormat() const
@@ -759,22 +758,22 @@ GLTextureHost::Lock()
                                          false /* owned by the client */);
   }
 
   return true;
 }
 void
 GLTextureHost::SetCompositor(Compositor* aCompositor)
 {
-  if (!AssertGLCompositor(aCompositor)) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (!glCompositor) {
     mCompositor = nullptr;
     mTextureSource = nullptr;
     return;
   }
-  CompositorOGL* glCompositor = static_cast<CompositorOGL*>(aCompositor);
   mCompositor = glCompositor;
   if (mTextureSource) {
     mTextureSource->SetCompositor(glCompositor);
   }
 }
 
 gfx::SurfaceFormat
 GLTextureHost::GetFormat() const
--- a/gfx/layers/opengl/TextureHostOGL.h
+++ b/gfx/layers/opengl/TextureHostOGL.h
@@ -516,14 +516,14 @@ protected:
   const EGLImage mImage;
   const EGLSync mSync;
   const gfx::IntSize mSize;
   const bool mHasAlpha;
   RefPtr<CompositorOGL> mCompositor;
   RefPtr<EGLImageTextureSource> mTextureSource;
 };
 
-bool AssertGLCompositor(Compositor* aCompositor);
+CompositorOGL* AssertGLCompositor(Compositor* aCompositor);
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_TEXTUREOGL_H */
--- a/gfx/layers/opengl/X11TextureSourceOGL.cpp
+++ b/gfx/layers/opengl/X11TextureSourceOGL.cpp
@@ -71,22 +71,24 @@ SurfaceFormat
 X11TextureSourceOGL::GetFormat() const {
   gfxContentType type = mSurface->GetContentType();
   return X11TextureSourceOGL::ContentTypeToSurfaceFormat(type);
 }
 
 void
 X11TextureSourceOGL::SetCompositor(Compositor* aCompositor)
 {
-  MOZ_ASSERT(!aCompositor || aCompositor->GetBackendType() == LayersBackend::LAYERS_OPENGL);
-  if (mCompositor == aCompositor) {
+  CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+  if (mCompositor == glCompositor) {
     return;
   }
   DeallocateDeviceData();
-  mCompositor = static_cast<CompositorOGL*>(aCompositor);
+  if (glCompositor) {
+    mCompositor = glCompositor;
+  }
 }
 
 gl::GLContext*
 X11TextureSourceOGL::gl() const
 {
   return mCompositor ? mCompositor->gl() : nullptr;
 }
 
--- a/image/imgLoader.cpp
+++ b/image/imgLoader.cpp
@@ -712,17 +712,18 @@ NewImageChannel(nsIChannel** aResult,
                 nsIURI* aInitialDocumentURI,
                 nsIURI* aReferringURI,
                 ReferrerPolicy aReferrerPolicy,
                 nsILoadGroup* aLoadGroup,
                 const nsCString& aAcceptHeader,
                 nsLoadFlags aLoadFlags,
                 nsContentPolicyType aPolicyType,
                 nsIPrincipal* aLoadingPrincipal,
-                nsISupports* aRequestingContext)
+                nsISupports* aRequestingContext,
+                bool aRespectPrivacy)
 {
   MOZ_ASSERT(aResult);
 
   nsresult rv;
   nsCOMPtr<nsIHttpChannel> newHttpChannel;
 
   nsCOMPtr<nsIInterfaceRequestor> callbacks;
 
@@ -759,16 +760,20 @@ NewImageChannel(nsIChannel** aResult,
     triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
   }
   nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aRequestingContext);
   nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
   if (inherit) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
 
+  if (aRespectPrivacy) {
+    securityFlags |= nsILoadInfo::SEC_FORCE_PRIVATE_BROWSING;
+  }
+
   // Note we are calling NS_NewChannelWithTriggeringPrincipal() here with a
   // node and a principal. This is for things like background images that are
   // specified by user stylesheets, where the document is being styled, but
   // the principal is that of the user stylesheet.
   if (requestingNode) {
     rv = NS_NewChannelWithTriggeringPrincipal(aResult,
                                               aURI,
                                               requestingNode,
@@ -835,16 +840,31 @@ NewImageChannel(nsIChannel** aResult,
 
   nsCOMPtr<nsILoadGroup> loadGroup = do_CreateInstance(NS_LOADGROUP_CONTRACTID);
   nsCOMPtr<nsILoadGroupChild> childLoadGroup = do_QueryInterface(loadGroup);
   if (childLoadGroup) {
     childLoadGroup->SetParentLoadGroup(aLoadGroup);
   }
   (*aResult)->SetLoadGroup(loadGroup);
 
+  // This is a workaround and a real fix in bug 1264231.
+  if (callbacks) {
+    nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(callbacks);
+    if (loadContext) {
+      nsCOMPtr<nsILoadInfo> loadInfo;
+      rv = (*aResult)->GetLoadInfo(getter_AddRefs(loadInfo));
+      NS_ENSURE_SUCCESS(rv, rv);
+      DocShellOriginAttributes originAttrs;
+      loadContext->GetOriginAttributes(originAttrs);
+      NeckoOriginAttributes neckoOriginAttrs;
+      neckoOriginAttrs.InheritFromDocShellToNecko(originAttrs);
+      loadInfo->SetOriginAttributes(neckoOriginAttrs);
+    }
+  }
+
   return NS_OK;
 }
 
 static uint32_t
 SecondsFromPRTime(PRTime prTime)
 {
   return uint32_t(int64_t(prTime) / int64_t(PR_USEC_PER_SEC));
 }
@@ -1616,17 +1636,18 @@ imgLoader::ValidateRequestWithNewChannel
                          aInitialDocumentURI,
                          aReferrerURI,
                          aReferrerPolicy,
                          aLoadGroup,
                          mAcceptHeader,
                          aLoadFlags,
                          aLoadPolicyType,
                          aLoadingPrincipal,
-                         aCX);
+                         aCX,
+                         mRespectPrivacy);
     if (NS_FAILED(rv)) {
       return false;
     }
 
     RefPtr<imgRequestProxy> req;
     rv = CreateNewProxyForRequest(request, aLoadGroup, aObserver,
                                   aLoadFlags, getter_AddRefs(req));
     if (NS_FAILED(rv)) {
@@ -2151,17 +2172,18 @@ imgLoader::LoadImage(nsIURI* aURI,
                          aInitialDocumentURI,
                          aReferrerURI,
                          aReferrerPolicy,
                          aLoadGroup,
                          mAcceptHeader,
                          requestFlags,
                          aContentPolicyType,
                          aLoadingPrincipal,
-                         aContext);
+                         aContext,
+                         mRespectPrivacy);
     if (NS_FAILED(rv)) {
       return NS_ERROR_FAILURE;
     }
 
     MOZ_ASSERT(NS_UsePrivateBrowsing(newChannel) == mRespectPrivacy);
 
     NewRequestAndEntry(forcePrincipalCheck, this, key,
                        getter_AddRefs(request),
--- a/image/test/unit/test_private_channel.js
+++ b/image/test/unit/test_private_channel.js
@@ -50,17 +50,22 @@ NotificationCallbacks.prototype = {
   },
   originAttributes: {}
 };
 
 var gImgPath = 'http://localhost:' + server.identity.primaryPort + '/image.png';
 
 function setup_chan(path, isPrivate, callback) {
   var uri = NetUtil.newURI(gImgPath);
-  var chan =  NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true});
+  var securityFlags = Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL;
+  if (isPrivate) {
+    securityFlags |= Ci.nsILoadInfo.SEC_FORCE_PRIVATE_BROWSING;
+  }
+  var chan =  NetUtil.newChannel({uri: uri, loadUsingSystemPrincipal: true,
+                                  securityFlags: securityFlags});
   chan.notificationCallbacks = new NotificationCallbacks(isPrivate);
   var channelListener = new ChannelListener();
   chan.asyncOpen2(channelListener);
 
   var listener = new ImageListener(null, callback);
   var outlistener = {};
   var loader = isPrivate ? gPrivateLoader : gPublicLoader;
   var outer = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
--- a/ipc/chromium/src/base/buffer.cc
+++ b/ipc/chromium/src/base/buffer.cc
@@ -46,17 +46,17 @@ Buffer::clear()
   mSize = 0;
   mReserved = 0;
 }
 
 void
 Buffer::try_realloc(size_t newlength)
 {
   char* buffer = (char*)realloc(mBuffer, newlength);
-  if (buffer) {
+  if (buffer || !newlength) {
     mBuffer = buffer;
     mReserved = newlength;
     return;
   }
 
   // If we're growing the buffer, crash. If we're shrinking, then we continue to
   // use the old (larger) buffer.
   MOZ_RELEASE_ASSERT(newlength <= mReserved);
@@ -102,16 +102,18 @@ Buffer::reserve(size_t size)
   if (mReserved < size) {
     try_realloc(size);
   }
 }
 
 char*
 Buffer::trade_bytes(size_t count)
 {
+  MOZ_RELEASE_ASSERT(count);
+
   char* result = mBuffer;
   mSize = mReserved = mSize - count;
   mBuffer = mReserved ? (char*)malloc(mReserved) : nullptr;
   MOZ_RELEASE_ASSERT(!mReserved || mBuffer);
   if (mSize) {
     memcpy(mBuffer, result + count, mSize);
   }
 
--- a/ipc/chromium/src/base/pickle.h
+++ b/ipc/chromium/src/base/pickle.h
@@ -64,16 +64,21 @@ class Pickle {
   Pickle& operator=(const Pickle& other);
 
   Pickle& operator=(Pickle&& other);
 
   // Returns the size of the Pickle's data.
   int size() const { return static_cast<int>(header_size_ +
                                              header_->payload_size); }
 
+  // Return the full size of the memory allocated for this Pickle's data.
+  uint32_t capacity() const {
+    return capacity_;
+  }
+
   // Returns the data for this Pickle.
   const void* data() const { return header_; }
 
   // Methods for reading the payload of the Pickle.  To read from the start of
   // the Pickle, initialize *iter to NULL.  If successful, these methods return
   // true.  Otherwise, false is returned to indicate that the result could not
   // be extracted.
   MOZ_WARN_UNUSED_RESULT bool ReadBool(void** iter, bool* result) const;
@@ -234,20 +239,16 @@ class Pickle {
     // We must have a valid header_.
     return payload() + payload_size();
   }
   const char* end_of_payload() const {
     // This object may be invalid.
     return header_ ? payload() + payload_size() : nullptr;
   }
 
-  uint32_t capacity() const {
-    return capacity_;
-  }
-
   // Resizes the buffer for use when writing the specified amount of data. The
   // location that the data should be written at is returned, or NULL if there
   // was an error. Call EndWrite with the returned offset and the given length
   // to pad out for the next write.
   char* BeginWrite(uint32_t length, uint32_t alignment);
 
   // Completes the write operation by padding the data with NULL bytes until it
   // is padded. Should be paired with BeginWrite, but it does not necessarily
--- a/ipc/chromium/src/chrome/common/ipc_channel_win.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_win.cc
@@ -430,17 +430,21 @@ bool Channel::ChannelImpl::ProcessIncomi
           listener_->OnMessageReceived(mozilla::Move(m));
         }
         p = message_tail;
       } else {
         // Last message is partial.
         break;
       }
     }
-    input_overflow_buf_.assign(p, end - p);
+    if (p != input_overflow_buf_.data()) {
+      // Don't assign unless we have to since this will throw away any memory we
+      // might have reserved.
+      input_overflow_buf_.assign(p, end - p);
+    }
 
     bytes_read = 0;  // Get more data.
   }
 
   return true;
 }
 
 bool Channel::ChannelImpl::ProcessOutgoingMessages(
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -114,17 +114,17 @@ struct RunnableMethodTraits<mozilla::ipc
             DebugAbort(__FILE__, __LINE__, #_cond,## __VA_ARGS__);  \
     } while (0)
 
 static MessageChannel* gParentProcessBlocker;
 
 namespace mozilla {
 namespace ipc {
 
-static const int kMinTelemetryMessageSize = 8192;
+static const uint32_t kMinTelemetryMessageSize = 8192;
 
 const int32_t MessageChannel::kNoTimeout = INT32_MIN;
 
 // static
 bool MessageChannel::sIsPumpingMessages = false;
 
 enum Direction
 {
@@ -752,18 +752,19 @@ MessageChannel::Echo(Message* aMsg)
 
     mLink->EchoMessage(msg.forget());
     return true;
 }
 
 bool
 MessageChannel::Send(Message* aMsg)
 {
-    if (aMsg->size() >= kMinTelemetryMessageSize) {
-        Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size());
+    if (aMsg->capacity() >= kMinTelemetryMessageSize) {
+        Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
+                              nsCString(aMsg->name()), aMsg->capacity());
     }
 
     CxxStackFrame frame(*this, OUT_MESSAGE, aMsg);
 
     nsAutoPtr<Message> msg(aMsg);
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
     if (MSG_ROUTING_NONE == msg->routing_id()) {
@@ -983,24 +984,23 @@ MessageChannel::OnMessageReceivedFromLin
             // If we compressed away the previous message, we'll re-use
             // its pending task.
             mWorkerLoop->PostTask(FROM_HERE, new DequeueTask(mDequeueOneTask));
         }
     }
 }
 
 void
-MessageChannel::PeekMessages(msgid_t aMsgId, mozilla::function<bool(const Message& aMsg)> aInvoke)
+MessageChannel::PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvoke)
 {
     MonitorAutoLock lock(*mMonitor);
 
     for (MessageQueue::iterator it = mPending.begin(); it != mPending.end(); it++) {
         Message &msg = *it;
-
-        if (msg.type() == aMsgId && !aInvoke(msg)) {
+        if (!aInvoke(msg)) {
             break;
         }
     }
 }
 
 void
 MessageChannel::ProcessPendingRequests(AutoEnterTransaction& aTransaction)
 {
@@ -1052,18 +1052,19 @@ MessageChannel::ProcessPendingRequests(A
             ProcessPendingRequest(*it);
         }
     }
 }
 
 bool
 MessageChannel::Send(Message* aMsg, Message* aReply)
 {
-    if (aMsg->size() >= kMinTelemetryMessageSize) {
-        Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE, nsCString(aMsg->name()), aMsg->size());
+    if (aMsg->capacity() >= kMinTelemetryMessageSize) {
+        Telemetry::Accumulate(Telemetry::IPC_MESSAGE_SIZE,
+                              nsCString(aMsg->name()), aMsg->capacity());
     }
 
     nsAutoPtr<Message> msg(aMsg);
 
     // Sanity checks.
     AssertWorkerThread();
     mMonitor->AssertNotCurrentThreadOwns();
 
--- a/ipc/glue/MessageChannel.h
+++ b/ipc/glue/MessageChannel.h
@@ -105,20 +105,20 @@ class MessageChannel : HasResultCodes
 
     void CloseWithTimeout();
 
     void SetAbortOnError(bool abort)
     {
         mAbortOnError = abort;
     }
 
-    // Call aInvoke for each pending message of type aId until it returns false.
+    // Call aInvoke for each pending message until it returns false.
     // XXX: You must get permission from an IPC peer to use this function
     //      since it requires custom deserialization and re-orders events.
-    void PeekMessages(Message::msgid_t aId, mozilla::function<bool(const Message& aMsg)> aInvoke);
+    void PeekMessages(mozilla::function<bool(const Message& aMsg)> aInvoke);
 
     // Misc. behavioral traits consumers can request for this channel
     enum ChannelFlags {
       REQUIRE_DEFAULT                         = 0,
       // Windows: if this channel operates on the UI thread, indicates
       // WindowsMessageLoop code should enable deferred native message
       // handling to prevent deadlocks. Should only be used for protocols
       // that manage child processes which might create native UI, like
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -6,22 +6,16 @@ import os, re, sys
 from copy import deepcopy
 from collections import OrderedDict
 
 import ipdl.ast
 import ipdl.builtin
 from ipdl.cxx.ast import *
 from ipdl.type import Actor, ActorType, ProcessGraph, TypeVisitor, builtinHeaderIncludes
 
-# FIXME/cjones: the chromium Message logging code doesn't work on
-# gcc/POSIX, because it wprintf()s across the chromium/mozilla
-# boundary. one side builds with -fshort-wchar, the other doesn't.
-# this code will remain off until the chromium base lib is replaced
-EMIT_LOGGING_CODE = ('win32' == sys.platform)
-
 ##-----------------------------------------------------------------------------
 ## "Public" interface to lowering
 ##
 class LowerToCxx:
     def lower(self, tu):
         '''returns |[ header: File ], [ cpp : File ]| representing the
 lowered form of |tu|'''
         # annotate the AST with IPDL/C++ IR-type stuff used later
--- a/js/public/GCHashTable.h
+++ b/js/public/GCHashTable.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GCHashTable_h
 #define GCHashTable_h
 
 #include "js/GCPolicyAPI.h"
 #include "js/HashTable.h"
 #include "js/RootingAPI.h"
+#include "js/SweepingAPI.h"
 #include "js/TracingAPI.h"
 
 namespace js {
 
 // Define a reasonable default GC policy for GC-aware Maps.
 template <typename Key, typename Value>
 struct DefaultMapSweepPolicy {
     static bool needsSweep(Key* key, Value* value) {
@@ -130,47 +131,54 @@ class GCRekeyableHashMap : public GCHash
 };
 
 template <typename Outer, typename... Args>
 class GCHashMapOperations
 {
     using Map = GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
-    using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
-    using Enum = typename Map::Enum;
 
     const Map& map() const { return static_cast<const Outer*>(this)->get(); }
 
   public:
+    using AddPtr = typename Map::AddPtr;
+
     bool initialized() const                   { return map().initialized(); }
     Ptr lookup(const Lookup& l) const          { return map().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
     Range all() const                          { return map().all(); }
     bool empty() const                         { return map().empty(); }
     uint32_t count() const                     { return map().count(); }
     size_t capacity() const                    { return map().capacity(); }
     bool has(const Lookup& l) const            { return map().lookup(l).found(); }
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return map().sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + map().sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 template <typename Outer, typename... Args>
 class MutableGCHashMapOperations
   : public GCHashMapOperations<Outer, Args...>
 {
     using Map = GCHashMap<Args...>;
     using Lookup = typename Map::Lookup;
     using Ptr = typename Map::Ptr;
-    using AddPtr = typename Map::AddPtr;
     using Range = typename Map::Range;
-    using Enum = typename Map::Enum;
 
     Map& map() { return static_cast<Outer*>(this)->get(); }
 
   public:
+    using AddPtr = typename Map::AddPtr;
+    struct Enum : public Map::Enum { explicit Enum(Outer& o) : Map::Enum(o.map()) {} };
+
     bool init(uint32_t len = 16) { return map().init(len); }
     void clear()                 { map().clear(); }
     void finish()                { map().finish(); }
     void remove(Ptr p)           { map().remove(p); }
 
     template<typename KeyInput, typename ValueInput>
     bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
         return map().add(p, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
@@ -209,16 +217,21 @@ class MutableHandleBase<GCHashMap<A,B,C,
   : public MutableGCHashMapOperations<JS::MutableHandle<GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
 {};
 
 template <typename A, typename B, typename C, typename D, typename E>
 class HandleBase<GCHashMap<A,B,C,D,E>>
   : public GCHashMapOperations<JS::Handle<GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
 {};
 
+template <typename A, typename B, typename C, typename D, typename E>
+class WeakCacheBase<GCHashMap<A,B,C,D,E>>
+  : public MutableGCHashMapOperations<JS::WeakCache<GCHashMap<A,B,C,D,E>>, A,B,C,D,E>
+{};
+
 // A GCHashSet is a HashSet with an additional trace method that knows
 // be traced to be kept alive will generally want to use this GCHashSet
 // specializeation in lieu of HashSet.
 //
 // Most types of GC pointers can be traced with no extra infrastructure. For
 // structs and non-gc-pointer members, ensure that there is a specialization of
 // GCPolicy<T> with an appropriate trace method available to handle the custom
 // type. Generic helpers can be found in js/public/TracingAPI.h.
@@ -268,47 +281,56 @@ class GCHashSet : public HashSet<T, Hash
 };
 
 template <typename Outer, typename... Args>
 class GCHashSetOperations
 {
     using Set = GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
     using Ptr = typename Set::Ptr;
-    using AddPtr = typename Set::AddPtr;
     using Range = typename Set::Range;
-    using Enum = typename Set::Enum;
 
-    const Set& set() const { return static_cast<const Outer*>(this)->extract(); }
+    const Set& set() const { return static_cast<const Outer*>(this)->get(); }
 
   public:
+    using AddPtr = typename Set::AddPtr;
+    using Entry = typename Set::Entry;
+
     bool initialized() const                   { return set().initialized(); }
     Ptr lookup(const Lookup& l) const          { return set().lookup(l); }
     AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); }
     Range all() const                          { return set().all(); }
     bool empty() const                         { return set().empty(); }
     uint32_t count() const                     { return set().count(); }
     size_t capacity() const                    { return set().capacity(); }
     bool has(const Lookup& l) const            { return set().lookup(l).found(); }
+    size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return set().sizeOfExcludingThis(mallocSizeOf);
+    }
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this) + set().sizeOfExcludingThis(mallocSizeOf);
+    }
 };
 
 template <typename Outer, typename... Args>
 class MutableGCHashSetOperations
   : public GCHashSetOperations<Outer, Args...>
 {
     using Set = GCHashSet<Args...>;
     using Lookup = typename Set::Lookup;
     using Ptr = typename Set::Ptr;
-    using AddPtr = typename Set::AddPtr;
     using Range = typename Set::Range;
-    using Enum = typename Set::Enum;
 
-    Set& set() { return static_cast<Outer*>(this)->extract(); }
+    Set& set() { return static_cast<Outer*>(this)->get(); }
 
   public:
+    using AddPtr = typename Set::AddPtr;
+    using Entry = typename Set::Entry;
+    struct Enum : public Set::Enum { explicit Enum(Outer& o) : Set::Enum(o.set()) {} };
+
     bool init(uint32_t len = 16) { return set().init(len); }
     void clear()                 { set().clear(); }
     void finish()                { set().finish(); }
     void remove(Ptr p)           { set().remove(p); }
     void remove(const Lookup& l) { set().remove(l); }
 
     template<typename TInput>
     bool add(AddPtr& p, TInput&& t) {
@@ -335,44 +357,31 @@ class MutableGCHashSetOperations
         return set().putNew(l, mozilla::Forward<TInput>(t));
     }
 };
 
 template <typename T, typename HP, typename AP>
 class RootedBase<GCHashSet<T, HP, AP>>
   : public MutableGCHashSetOperations<JS::Rooted<GCHashSet<T, HP, AP>>, T, HP, AP>
 {
-    using Set = GCHashSet<T, HP, AP>;
-
-    friend class GCHashSetOperations<JS::Rooted<Set>, T, HP, AP>;
-    const Set& extract() const { return *static_cast<const JS::Rooted<Set>*>(this)->address(); }
-
-    friend class MutableGCHashSetOperations<JS::Rooted<Set>, T, HP, AP>;
-    Set& extract() { return *static_cast<JS::Rooted<Set>*>(this)->address(); }
 };
 
 template <typename T, typename HP, typename AP>
 class MutableHandleBase<GCHashSet<T, HP, AP>>
   : public MutableGCHashSetOperations<JS::MutableHandle<GCHashSet<T, HP, AP>>, T, HP, AP>
 {
-    using Set = GCHashSet<T, HP, AP>;
-
-    friend class GCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP>;
-    const Set& extract() const {
-        return *static_cast<const JS::MutableHandle<Set>*>(this)->address();
-    }
-
-    friend class MutableGCHashSetOperations<JS::MutableHandle<Set>, T, HP, AP>;
-    Set& extract() { return *static_cast<JS::MutableHandle<Set>*>(this)->address(); }
 };
 
 template <typename T, typename HP, typename AP>
 class HandleBase<GCHashSet<T, HP, AP>>
   : public GCHashSetOperations<JS::Handle<GCHashSet<T, HP, AP>>, T, HP, AP>
 {
-    using Set = GCHashSet<T, HP, AP>;
-    friend class GCHashSetOperations<JS::Handle<Set>, T, HP, AP>;
-    const Set& extract() const { return *static_cast<const JS::Handle<Set>*>(this)->address(); }
+};
+
+template <typename T, typename HP, typename AP>
+class WeakCacheBase<GCHashSet<T, HP, AP>>
+  : public MutableGCHashSetOperations<JS::WeakCache<GCHashSet<T, HP, AP>>, T, HP, AP>
+{
 };
 
 } /* namespace js */
 
 #endif /* GCHashTable_h */
--- a/js/public/GCPolicyAPI.h
+++ b/js/public/GCPolicyAPI.h
@@ -64,16 +64,20 @@ struct StructGCPolicy
     static T initial() {
         return T();
     }
 
     static void trace(JSTracer* trc, T* tp, const char* name) {
         tp->trace(trc);
     }
 
+    static void sweep(T* tp) {
+        return tp->sweep();
+    }
+
     static bool needsSweep(T* tp) {
         return tp->needsSweep();
     }
 };
 
 // The default GC policy attempts to defer to methods on the underlying type.
 // Most C++ structures that contain a default constructor, a trace function and
 // a sweep function will work out of the box with Rooted, Handle, GCVector,
@@ -93,19 +97,16 @@ template <> struct GCPolicy<uint64_t> : 
 template <typename T>
 struct GCPointerPolicy
 {
     static T initial() { return nullptr; }
     static void trace(JSTracer* trc, T* vp, const char* name) {
         if (*vp)
             js::UnsafeTraceManuallyBarrieredEdge(trc, vp, name);
     }
-    static bool needsSweep(T* vp) {
-        return js::gc::EdgeNeedsSweep(vp);
-    }
 };
 template <> struct GCPolicy<JS::Symbol*> : public GCPointerPolicy<JS::Symbol*> {};
 template <> struct GCPolicy<JSAtom*> : public GCPointerPolicy<JSAtom*> {};
 template <> struct GCPolicy<JSFunction*> : public GCPointerPolicy<JSFunction*> {};
 template <> struct GCPolicy<JSObject*> : public GCPointerPolicy<JSObject*> {};
 template <> struct GCPolicy<JSScript*> : public GCPointerPolicy<JSScript*> {};
 template <> struct GCPolicy<JSString*> : public GCPointerPolicy<JSString*> {};
 
new file mode 100644
--- /dev/null
+++ b/js/public/SweepingAPI.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+#ifndef js_SweepingAPI_h
+#define js_SweepingAPI_h
+
+#include "js/HeapAPI.h"
+
+namespace js {
+template <typename T>
+class WeakCacheBase {};
+} // namespace js
+
+namespace JS {
+template <typename T> class WeakCache;
+
+namespace shadow {
+JS_PUBLIC_API(void)
+RegisterWeakCache(JS::Zone* zone, JS::WeakCache<void*>* cachep);
+} // namespace shadow
+
+// A WeakCache stores the given Sweepable container and links itself into a
+// list of such caches that are swept during each GC.
+template <typename T>
+class WeakCache : public js::WeakCacheBase<T>,
+                  private mozilla::LinkedListElement<WeakCache<T>>
+{
+    friend class mozilla::LinkedListElement<WeakCache<T>>;
+    friend class mozilla::LinkedList<WeakCache<T>>;
+
+    WeakCache(const WeakCache&) = delete;
+
+    using SweepFn = void (*)(T*);
+    SweepFn sweeper;
+    T cache;
+
+  public:
+    template <typename U>
+    WeakCache(Zone* zone, U&& initial)
+      : cache(mozilla::Forward<U>(initial))
+    {
+        sweeper = js::GCPolicy<T>::sweep;
+        shadow::RegisterWeakCache(zone, reinterpret_cast<WeakCache<void*>*>(this));
+    }
+    WeakCache(WeakCache&& other)
+      : sweeper(other.sweeper),
+        cache(mozilla::Forward<T>(other.cache))
+    {
+    }
+
+    const T& get() const { return cache; }
+    T& get() { return cache; }
+
+    void sweep() { sweeper(&cache); }
+};
+
+} // namespace JS
+
+#endif // js_SweepingAPI_h
--- a/js/public/TracingAPI.h
+++ b/js/public/TracingAPI.h
@@ -342,15 +342,16 @@ namespace js {
 //
 // This method does not check if |*edgep| is non-null before tracing through
 // it, so callers must check any nullable pointer before calling this method.
 template <typename T>
 extern JS_PUBLIC_API(void)
 UnsafeTraceManuallyBarrieredEdge(JSTracer* trc, T* edgep, const char* name);
 
 namespace gc {
+// Return true if the given edge is not live and is about to be swept.
 template <typename T>
 extern JS_PUBLIC_API(bool)
 EdgeNeedsSweep(JS::Heap<T>* edgep);
 } // namespace gc
 } // namespace js
 
 #endif /* js_TracingAPI_h */
--- a/js/public/TrackedOptimizationInfo.h
+++ b/js/public/TrackedOptimizationInfo.h
@@ -11,16 +11,17 @@
 
 namespace JS {
 
 #define TRACKED_STRATEGY_LIST(_)                        \
     _(GetProp_ArgumentsLength)                          \
     _(GetProp_ArgumentsCallee)                          \
     _(GetProp_InferredConstant)                         \
     _(GetProp_Constant)                                 \
+    _(GetProp_NotDefined)                               \
     _(GetProp_StaticName)                               \
     _(GetProp_SimdGetter)                               \
     _(GetProp_TypedObject)                              \
     _(GetProp_DefiniteSlot)                             \
     _(GetProp_Unboxed)                                  \
     _(GetProp_CommonGetter)                             \
     _(GetProp_InlineAccess)                             \
     _(GetProp_Innerize)                                 \
@@ -75,16 +76,17 @@ namespace JS {
     _(UnknownProperties)                                                \
     _(Singleton)                                                        \
     _(NotSingleton)                                                     \
     _(NotFixedSlot)                                                     \
     _(InconsistentFixedSlot)                                            \
     _(NotObject)                                                        \
     _(NotStruct)                                                        \
     _(NotUnboxed)                                                       \
+    _(NotUndefined)                                                     \
     _(UnboxedConvertedToNative)                                         \
     _(StructNoField)                                                    \
     _(InconsistentFieldType)                                            \
     _(InconsistentFieldOffset)                                          \
     _(NeedsTypeBarrier)                                                 \
     _(InDictionaryMode)                                                 \
     _(NoProtoFound)                                                     \
     _(MultiProtoPaths)                                                  \
--- a/js/src/aclocal.m4
+++ b/js/src/aclocal.m4
@@ -3,17 +3,16 @@ dnl Local autoconf macros used with mozi
 dnl The contents of this file are under the Public Domain.
 dnl
 
 builtin(include, ../../build/autoconf/hotfixes.m4)dnl
 builtin(include, ../../build/autoconf/acwinpaths.m4)dnl
 builtin(include, ../../build/autoconf/hooks.m4)dnl
 builtin(include, ../../build/autoconf/config.status.m4)dnl
 builtin(include, ../../build/autoconf/toolchain.m4)dnl
-builtin(include, ../../build/autoconf/wrapper.m4)dnl
 builtin(include, ../../build/autoconf/pkg.m4)dnl
 builtin(include, ../../build/autoconf/nspr.m4)dnl
 builtin(include, ../../build/autoconf/nspr-build.m4)dnl
 builtin(include, ../../build/autoconf/codeset.m4)dnl
 builtin(include, ../../build/autoconf/altoptions.m4)dnl
 builtin(include, ../../build/autoconf/mozprog.m4)dnl
 builtin(include, ../../build/autoconf/mozheader.m4)dnl
 builtin(include, ../../build/autoconf/mozcommonheader.m4)dnl
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -1022,8 +1022,65 @@ function ArrayConcat(arg1) {
 }
 
 function ArrayStaticConcat(arr, arg1) {
     if (arguments.length < 1)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.concat');
     var args = callFunction(std_Array_slice, arguments, 1);
     return callFunction(std_Function_apply, ArrayConcat, arr, args);
 }
+
+function ArrayStaticJoin(arr, separator) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.join');
+    return callFunction(std_Array_join, arr, separator);
+}
+
+function ArrayStaticReverse(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.reverse');
+    return callFunction(std_Array_reverse, arr);
+}
+
+function ArrayStaticSort(arr, comparefn) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.sort');
+    return callFunction(std_Array_sort, arr, comparefn);
+}
+
+function ArrayStaticPush(arr, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.push');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_push, arr, args);
+}
+
+function ArrayStaticPop(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.pop');
+    return callFunction(std_Array_pop, arr);
+}
+
+function ArrayStaticShift(arr) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.shift');
+    return callFunction(std_Array_shift, arr);
+}
+
+function ArrayStaticUnshift(arr, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.unshift');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_unshift, arr, args);
+}
+
+function ArrayStaticSplice(arr, start, deleteCount) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.splice');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_Array_splice, arr, args);
+}
+
+function ArrayStaticSlice(arr, start, end) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.slice');
+    return callFunction(std_Array_slice, arr, start, end);
+}
--- a/js/src/builtin/String.js
+++ b/js/src/builtin/String.js
@@ -717,17 +717,21 @@ function String_static_raw(callSite, ...
  * Mozilla proprietary.
  * Spec: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String#String_generic_methods
  */
 function String_static_localeCompare(str1, str2) {
     if (arguments.length < 1)
         ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "String.localeCompare");
     var locales = arguments.length > 2 ? arguments[2] : undefined;
     var options = arguments.length > 3 ? arguments[3] : undefined;
+#if EXPOSE_INTL_API
     return callFunction(String_localeCompare, str1, str2, locales, options);
+#else
+    return callFunction(std_String_localeCompare, str1, str2, locales, options);
+#endif
 }
 
 // ES6 draft 2014-04-27 B.2.3.3
 function String_big() {
     RequireObjectCoercible(this);
     return "<big>" + ToString(this) + "</big>";
 }
 
@@ -819,8 +823,113 @@ function String_fontsize(size) {
 }
 
 // ES6 draft 2014-04-27 B.2.3.10
 function String_link(url) {
     RequireObjectCoercible(this);
     var S = ToString(this);
     return '<a href="' + EscapeAttributeValue(url) + '">' + S + "</a>";
 }
+
+function String_static_toLowerCase(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLowerCase');
+    return callFunction(std_String_toLowerCase, string);
+}
+
+function String_static_toUpperCase(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toUpperCase');
+    return callFunction(std_String_toUpperCase, string);
+}
+
+function String_static_charAt(string, pos) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.charAt');
+    return callFunction(std_String_charAt, string, pos);
+}
+
+function String_static_charCodeAt(string, pos) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.charCodeAt');
+    return callFunction(std_String_charCodeAt, string, pos);
+}
+
+function String_static_includes(string, searchString) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.includes');
+    var position = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_includes, string, searchString, position);
+}
+
+function String_static_indexOf(string, searchString) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.indexOf');
+    var position = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_indexOf, string, searchString, position);
+}
+
+function String_static_lastIndexOf(string, searchString) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.lastIndexOf');
+    var position = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_lastIndexOf, string, searchString, position);
+}
+
+function String_static_startsWith(string, searchString) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.startsWith');
+    var position = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_startsWith, string, searchString, position);
+}
+
+function String_static_endsWith(string, searchString) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.endsWith');
+    var endPosition = arguments.length > 2 ? arguments[2] : undefined;
+    return callFunction(std_String_endsWith, string, searchString, endPosition);
+}
+
+function String_static_trim(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trim');
+    return callFunction(std_String_trim, string);
+}
+
+function String_static_trimLeft(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimLeft');
+    return callFunction(std_String_trimLeft, string);
+}
+
+function String_static_trimRight(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.trimRight');
+    return callFunction(std_String_trimRight, string);
+}
+
+function String_static_toLocaleLowerCase(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleLowerCase');
+    return callFunction(std_String_toLocaleLowerCase, string);
+}
+
+function String_static_toLocaleUpperCase(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.toLocaleUpperCase');
+    return callFunction(std_String_toLocaleUpperCase, string);
+}
+
+#if EXPOSE_INTL_API
+function String_static_normalize(string) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.normalize');
+    var form = arguments.length > 1 ? arguments[1] : undefined;
+    return callFunction(std_String_normalize, string, form);
+}
+#endif
+
+function String_static_concat(string, arg1) {
+    if (arguments.length < 1)
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'String.concat');
+    var args = callFunction(std_Array_slice, arguments, 1);
+    return callFunction(std_Function_apply, std_String_concat, string, args);
+}
--- a/js/src/devtools/automation/winbuildenv.sh
+++ b/js/src/devtools/automation/winbuildenv.sh
@@ -21,20 +21,20 @@ export OLD_LIBPATH=$(IFS=';'; for d in $
 
 # The various browser/config/mozconfigs/win32/* files use these checks to pick
 # the compiler.
 if $USE_64BIT; then
   . $topsrcdir/build/win64/mozconfig.vs2015
 elif test "$PROCESSOR_ARCHITECTURE" = "AMD64" -o "$PROCESSOR_ARCHITEW6432" = "AMD64"; then
   . $topsrcdir/build/win32/mozconfig.vs2015-win64
 else
-  . $topsrcdir/build/win32/mozconfig.vs2010
+  . $topsrcdir/build/win32/mozconfig.vs2015
 fi
 
 # PATH also needs to point to mozmake.exe, which can come from either
 # newer mozilla-build or tooltool.
 if ! which mozmake 2>/dev/null; then
     export PATH="$PATH:$SOURCE/.."
     if ! which mozmake 2>/dev/null; then
-	TT_SERVER=${TT_SERVER:-https://api.pub.build.mozilla.org/tooltool/}
-	( cd $SOURCE/..; ./scripts/scripts/tooltool/tooltool_wrapper.sh $SOURCE/browser/config/tooltool-manifests/${platform:-win32}/releng.manifest $TT_SERVER setup.sh c:/mozilla-build/python27/python.exe C:/mozilla-build/tooltool.py )
+  TT_SERVER=${TT_SERVER:-https://api.pub.build.mozilla.org/tooltool/}
+  ( cd $SOURCE/..; ./scripts/scripts/tooltool/tooltool_wrapper.sh $SOURCE/browser/config/tooltool-manifests/${platform:-win32}/releng.manifest $TT_SERVER setup.sh c:/mozilla-build/python27/python.exe C:/mozilla-build/tooltool.py )
     fi
 fi
new file mode 100755
--- /dev/null
+++ b/js/src/devtools/release/release-notes
@@ -0,0 +1,195 @@
+#!/usr/bin/perl
+
+# How to use:
+#
+# Step 1: run release-notes diff old-jsapi.h new-jsapi.h > diff.txt
+#
+# Step 2: edit diff.txt
+#  - when a function has been renamed, get the - and + lines adjacent and mark the - line with [renamed] at the end
+#  - when a function has been replaced, do the same (replacements behave differently)
+#  - for anything that isn't a simple addition, deletion, rename, or replace, tag with [other]
+#    (things tagged [other] will be put in a separate section for manual fixup)
+#
+# Step 3: run release-notes < diff.txt > changes.txt
+#  - this will group changes into sections and annotate them with bug numbers
+#  - the bugs chosen are just the bug that last touched each line, and are unlikely to be entirely accurate
+#
+# Step 4: run release-notes mdn < changes.txt > final.txt
+#  - this will add an MDN link to every list item, first checking whether such a link is valid
+#
+# Step 5: paste into the MDN page, eg https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Releases/45
+
+# Upcoming: basing everything off of jsapi.h is probably not going to work for
+# much longer, given that more stuff is moving into js/public. Scan
+# js/public/*.h too and record where everything comes from (to automate header
+# changes in the notes)?
+#
+# This is only looking at C style APIs. Dump out all methods too?
+#
+# The enbuggification should be split out into a separate phase because it is
+# wrong a fair amount of the time (whitespace changes, parameter changes,
+# etc.), and should have a way of running repeatedly so you can incrementally
+# fix stuff up.
+#
+# It would be very nice to have an example program that links against mozjs,
+# tested in CI, so we can diff that for release notes.
+
+use strict;
+use warnings;
+
+if (@ARGV && $ARGV[0] eq 'diff') {
+    my ($orig_file, $new_file) = @ARGV[1..2];
+    my $orig_api = grab_api($orig_file);
+    my $new_api = grab_api($new_file);
+    diff_apis($orig_api, $new_api);
+    exit 0;
+}
+
+my $path = "/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_Reference";
+my $url_prefix = "https://developer.mozilla.org$path";
+
+if (@ARGV && $ARGV[0] eq 'mdn') {
+    shift(@ARGV);
+    while(<>) {
+        if (/<li>([\w:]+)/) {
+            print STDERR "Checking $1...\n";
+            system("wget", "-q", "$url_prefix/$1");
+            if ($? == 0) {
+                s!<li>([\w:]+)!<li><a href="$path/$1">$1</a>!;
+            }
+        }
+        print;
+    }
+    exit 0;
+}
+
+sub grab_api {
+    my ($file) = @_;
+    open(my $fh, "<", $file) or die "open $file: $!";
+    my $grabbing;
+    my @api;
+    while(<$fh>) {
+        if ($grabbing && /^(\w+)/) {
+            push @api, $1;
+        }
+        $grabbing = /JS_PUBLIC_API/;
+    }
+    return \@api;
+}
+
+sub diff_apis {
+    my ($old, $new) = @_;
+    my %old;
+    @old{@$old} = ();
+    my %new;
+    @new{@$new} = ();
+
+    open(my $ofh, ">", "/tmp/r-c.diff.1");
+    print $ofh "$_\n" foreach (@$old);
+    close $ofh;
+    open(my $nfh, ">", "/tmp/r-c.diff.2");
+    print $nfh "$_\n" foreach (@$new);
+    close $nfh;
+    open(my $diff, "diff -u /tmp/r-c.diff.1 /tmp/r-c.diff.2 |");
+    while(<$diff>) {
+        if (/^-(\w+)/) {
+            next if exists $new{$1}; # Still exists, so skip it
+        } elsif (/^\+(\w+)/) {
+            next if exists $old{$1}; # It was already there, skip it
+        }
+        print;
+    }
+}
+
+my @added;
+my @renamed;
+my @replaced;
+my @deleted;
+my @other;
+
+my %N;
+
+my $renaming;
+my $replacing;
+while (<>) {
+    my $name;
+    if (/^[ +-](\w+)/) {
+        $name = $1;
+        $N{$name} = $name =~ /^JS_/ ? $name : "JS::$name";
+    }
+
+    if (/^-/)