Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 20 Jan 2015 22:16:53 -0500
changeset 251916 5dfb49c9f1fcd387c356fbdfdc6b75e264008a0b
parent 251915 df6b421df37f0cf62d1496253f2110cf83e99e19 (current diff)
parent 251887 540077a308669a42c0d8fe7dbd43d4cc36c9a5ff (diff)
child 251917 a2651cba867321f9750c28b0f80770f698fc29ec
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
testing/release/README.txt
testing/release/common/check_updates.sh
testing/release/common/download_builds.sh
testing/release/common/download_mars.sh
testing/release/common/unpack.sh
testing/release/l10n/verify_l10n.sh
testing/release/updates/moz18-firefox-linux-major.cfg
testing/release/updates/moz18-firefox-linux.cfg
testing/release/updates/moz18-firefox-mac-major.cfg
testing/release/updates/moz18-firefox-mac.cfg
testing/release/updates/moz18-firefox-win32-major.cfg
testing/release/updates/moz18-firefox-win32.cfg
testing/release/updates/moz18-thunderbird-linux.cfg
testing/release/updates/moz18-thunderbird-mac.cfg
testing/release/updates/moz18-thunderbird-win32.cfg
testing/release/updates/moz180-firefox-linux-major.cfg
testing/release/updates/moz180-firefox-linux-partners-major.cfg
testing/release/updates/moz180-firefox-linux-yahoo.cfg
testing/release/updates/moz180-firefox-linux.cfg
testing/release/updates/moz180-firefox-mac-major.cfg
testing/release/updates/moz180-firefox-mac-partners-major.cfg
testing/release/updates/moz180-firefox-mac-yahoo.cfg
testing/release/updates/moz180-firefox-mac.cfg
testing/release/updates/moz180-firefox-win32-google.cfg
testing/release/updates/moz180-firefox-win32-major.cfg
testing/release/updates/moz180-firefox-win32-partners-major.cfg
testing/release/updates/moz180-firefox-win32-yahoo.cfg
testing/release/updates/moz180-firefox-win32.cfg
testing/release/updates/moz180-thunderbird-linux-major.cfg
testing/release/updates/moz180-thunderbird-linux.cfg
testing/release/updates/moz180-thunderbird-mac-major.cfg
testing/release/updates/moz180-thunderbird-mac.cfg
testing/release/updates/moz180-thunderbird-win32-major.cfg
testing/release/updates/moz180-thunderbird-win32.cfg
testing/release/updates/moz19-firefox-linux.cfg
testing/release/updates/moz19-firefox-mac.cfg
testing/release/updates/moz19-firefox-win32.cfg
testing/release/updates/verify.sh
--- a/accessible/base/AccIterator.cpp
+++ b/accessible/base/AccIterator.cpp
@@ -327,16 +327,74 @@ Accessible*
 IDRefsIterator::Next()
 {
   nsIContent* nextElm = NextElem();
   return nextElm ? mDoc->GetAccessible(nextElm) : nullptr;
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
+// ARIAOwnedByIterator
+////////////////////////////////////////////////////////////////////////////////
+
+ARIAOwnedByIterator::ARIAOwnedByIterator(const Accessible* aDependent) :
+  RelatedAccIterator(aDependent->Document(), aDependent->GetContent(),
+                     nsGkAtoms::aria_owns), mDependent(aDependent)
+{
+}
+
+Accessible*
+ARIAOwnedByIterator::Next()
+{
+  Accessible* owner = RelatedAccIterator::Next();
+  Accessible* cur = owner;
+  while (cur) {
+    if (cur == mDependent)
+      return Next(); // owner cannot be a child of dependent.
+
+    if (cur->IsDoc())
+      break; // don't cross document boundaries
+
+    cur = cur->Parent();
+  }
+
+  return owner;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// ARIAOwnsIterator
+////////////////////////////////////////////////////////////////////////////////
+
+ARIAOwnsIterator::ARIAOwnsIterator(const Accessible* aOwner) :
+  mIter(aOwner->Document(), aOwner->GetContent(), nsGkAtoms::aria_owns),
+  mOwner(aOwner)
+{
+}
+
+Accessible*
+ARIAOwnsIterator::Next()
+{
+  Accessible* child = mIter.Next();
+  const Accessible* cur = mOwner;
+  while (cur) {
+    if (cur == child)
+      return Next(); // cannot own its own parent
+
+    if (cur->IsDoc())
+      break; // don't cross document boundaries
+
+    cur = cur->Parent();
+  }
+
+  return child;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
 // SingleAccIterator
 ////////////////////////////////////////////////////////////////////////////////
 
 Accessible*
 SingleAccIterator::Next()
 {
   nsRefPtr<Accessible> nextAcc;
   mAcc.swap(nextAcc);
--- a/accessible/base/AccIterator.h
+++ b/accessible/base/AccIterator.h
@@ -242,16 +242,58 @@ private:
   IDRefsIterator operator = (const IDRefsIterator&);
 
   nsString mIDs;
   nsIContent* mContent;
   DocAccessible* mDoc;
   nsAString::index_type mCurrIdx;
 };
 
+
+/**
+ * Iterates over related accessible referred by aria-owns.
+ */
+class ARIAOwnedByIterator MOZ_FINAL : public RelatedAccIterator
+{
+public:
+  explicit ARIAOwnedByIterator(const Accessible* aDependent);
+  virtual ~ARIAOwnedByIterator() { }
+
+  virtual Accessible* Next() MOZ_OVERRIDE;
+
+private:
+  ARIAOwnedByIterator() = delete;
+  ARIAOwnedByIterator(const ARIAOwnedByIterator&) = delete;
+  ARIAOwnedByIterator& operator = (const ARIAOwnedByIterator&) = delete;
+
+  const Accessible* mDependent;
+};
+
+
+/**
+ * Iterates over related accessible referred by aria-owns.
+ */
+class ARIAOwnsIterator MOZ_FINAL : public AccIterable
+{
+public:
+  explicit ARIAOwnsIterator(const Accessible* aOwner);
+  virtual ~ARIAOwnsIterator() { }
+
+  virtual Accessible* Next() MOZ_OVERRIDE;
+
+private:
+  ARIAOwnsIterator() = delete;
+  ARIAOwnsIterator(const ARIAOwnsIterator&) = delete;
+  ARIAOwnsIterator& operator = (const ARIAOwnsIterator&) = delete;
+
+  IDRefsIterator mIter;
+  const Accessible* mOwner;
+};
+
+
 /**
  * Iterator that points to a single accessible returning it on the first call
  * to Next().
  */
 class SingleAccIterator : public AccIterable
 {
 public:
   explicit SingleAccIterator(Accessible* aTarget): mAcc(aTarget) { }
--- a/accessible/base/FocusManager.cpp
+++ b/accessible/base/FocusManager.cpp
@@ -319,19 +319,17 @@ FocusManager::ProcessFocusEvent(AccEvent
           continue;
         }
       }
 
       // If no required context role then check aria-owns relation.
       if (!tryOwnsParent)
         break;
 
-      RelatedAccIterator iter(child->Document(), child->GetContent(),
-                              nsGkAtoms::aria_owns);
-      parent = iter.Next();
+      parent = ARIAOwnedByIterator(child).Next();
       tryOwnsParent = false;
     }
 
     if (ARIAMenubar != mActiveARIAMenubar) {
       // Leaving ARIA menu. Fire menu_end event on current menubar.
       if (mActiveARIAMenubar) {
         nsRefPtr<AccEvent> menuEndEvent =
           new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -1276,17 +1276,17 @@ Accessible::Value(nsString& aValue)
     return;
   }
 
   // Value of combobox is a text of current or selected item.
   if (mRoleMapEntry->Is(nsGkAtoms::combobox)) {
     Accessible* option = CurrentItem();
     if (!option) {
       Accessible* listbox = nullptr;
-      IDRefsIterator iter(mDoc, mContent, nsGkAtoms::aria_owns);
+      ARIAOwnsIterator iter(this);
       while ((listbox = iter.Next()) && !listbox->IsListControl());
 
       if (!listbox) {
         uint32_t childCount = ChildCount();
         for (uint32_t idx = 0; idx < childCount; idx++) {
           Accessible* child = mChildren.ElementAt(idx);
           if (child->IsListControl())
             listbox = child;
@@ -1554,18 +1554,17 @@ Accessible::RelationByType(RelationType 
           mContent->IsXUL())
         rel.AppendIter(new IDRefsIterator(mDoc, mContent,
                                           nsGkAtoms::control));
 
       return rel;
     }
 
     case RelationType::NODE_CHILD_OF: {
-      Relation rel(new RelatedAccIterator(Document(), mContent,
-                                          nsGkAtoms::aria_owns));
+      Relation rel(new ARIAOwnedByIterator(this));
 
       // This is an ARIA tree or treegrid that doesn't use owns, so we need to
       // get the parent the hard way.
       if (mRoleMapEntry && (mRoleMapEntry->role == roles::OUTLINEITEM ||
                             mRoleMapEntry->role == roles::LISTITEM ||
                             mRoleMapEntry->role == roles::ROW)) {
         rel.AppendTarget(GetGroupInfo()->ConceptualParent());
       }
@@ -1585,17 +1584,17 @@ Accessible::RelationByType(RelationType 
             rel.AppendTarget(Parent());
         }
       }
 
       return rel;
     }
 
     case RelationType::NODE_PARENT_OF: {
-      Relation rel(new IDRefsIterator(mDoc, mContent, nsGkAtoms::aria_owns));
+      Relation rel(new ARIAOwnsIterator(this));
 
       // ARIA tree or treegrid can do the hierarchy by @aria-level, ARIA trees
       // also can be organized by groups.
       if (mRoleMapEntry &&
           (mRoleMapEntry->role == roles::OUTLINEITEM ||
            mRoleMapEntry->role == roles::LISTITEM ||
            mRoleMapEntry->role == roles::ROW ||
            mRoleMapEntry->role == roles::OUTLINE ||
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -69,16 +69,25 @@
       testRelation("descr2", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("descr3", RELATION_DESCRIPTION_FOR, "checkbox5");
       testRelation("checkbox5", RELATION_DESCRIBED_BY, ["descr2", "descr3"]);
 
       // aria_owns, multiple relations
       testRelation("treeitem1", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem2", RELATION_NODE_CHILD_OF, "tree");
 
+      // aria-owns, bad relations
+      testRelation("ariaowns_container", RELATION_NODE_CHILD_OF, null);
+      testRelation("ariaowns_self", RELATION_NODE_CHILD_OF, null);
+      testRelation("ariaowns_uncle", RELATION_NODE_CHILD_OF, "ariaowns_self");
+
+      testRelation("ariaowns_container", RELATION_NODE_PARENT_OF, null);
+      testRelation("ariaowns_self", RELATION_NODE_PARENT_OF, "ariaowns_uncle");
+      testRelation("ariaowns_uncle", RELATION_NODE_PARENT_OF, null);
+
       // 'node child of' relation for outlineitem role
       testRelation("treeitem3", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem4", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem5", RELATION_NODE_CHILD_OF, "treeitem4");
       testRelation("treeitem6", RELATION_NODE_CHILD_OF, "tree");
       testRelation("treeitem7", RELATION_NODE_CHILD_OF, "treeitem6");
       testRelation("tree2_ti1", RELATION_NODE_CHILD_OF, "tree2");
       testRelation("tree2_ti1a", RELATION_NODE_CHILD_OF, "tree2_ti1");
@@ -305,16 +314,22 @@
     <div role="treeitem" id="treeitem4" aria-level="1">Green</div>
     <div role="treeitem" id="treeitem5" aria-level="2">Light green</div>
     <div role="treeitem" id="treeitem6" aria-level="1">Green2</div>
     <div role="group">
       <div role="treeitem" id="treeitem7">Super light green</div>
     </div>
   </div>
 
+  <div id="ariaowns_container">
+    <div id="ariaowns_self"
+         aria-owns="aria_ownscontainer ariaowns_self ariaowns_uncle"></div>
+  </div>
+  <div id="ariaowns_uncle"></div>
+
   <div aria-owns="simplegrid-ownrow" role="grid" id="simplegrid">
     <div role="row" id="simplegrid-row1" aria-level="1">
       <div role="gridcell">cell 1,1</div>
       <div role="gridcell">cell 1,2</div>
     </div>
     <div role="row" id="simplegrid-row2" aria-level="1">
       <div role="gridcell">cell 2,1</div>
       <div role="gridcell">cell 2,2</div>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -124,17 +124,17 @@
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="2e43efe1b30d0b98574d293059556aebd2f46454"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
   <default remote="sprd-aosp" revision="sprdb2g_gonk4.4" sync-j="4"/>
   <!-- Stock Android things -->
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
-  <project name="device/sprd" path="device/sprd" revision="56f0085d1105fd6519d5d3773f91d48b2ad66d5c"/>
+  <project name="device/sprd" path="device/sprd" revision="8491e6338aa1e12699a2064895b02fb015f91b89"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="6c6f012cea17fb8b3263605737816cf6663432f1"/>
   <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
   <project name="u-boot" path="u-boot" revision="5167e5eec5cb6b3147839da158637e6d953a4e4f"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "579e01ad4d6e4177a8f636305ac877835d99f134", 
+        "git_revision": "5e98dc164b17fd6decb48a9eaddef0e55b82e249", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "91945bbce2f909915fe3edf033215baa4c80e3cd", 
+    "revision": "d60b9f55a77aa72606acb397b3770adc1bcf5110", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,21 +12,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -54,16 +54,19 @@ MOZ_TIME_MANAGER=1
 MOZ_PAY=1
 MOZ_TOOLKIT_SEARCH=
 MOZ_PLACES=
 MOZ_B2G=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_NUWA_PROCESS=1
 MOZ_B2G_LOADER=1
+# Warnings-as-errors cannot be enabled on Lollipop until bug 1119980 is fixed.
+if test "$PLATFORM_SDK_VERSION" -lt 21; then
 MOZ_ENABLE_WARNINGS_AS_ERRORS=1
 fi
+fi
 
 MOZ_JSDOWNLOADS=1
 
 MOZ_BUNDLED_FONTS=1
 
 export JS_GC_SMALL_CHUNK_SIZE=1
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1419896968000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1421170069000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i71" id="youtube@2youtube.com">
@@ -861,16 +861,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i286" id="{58bd07eb-0ee0-4df0-8121-dc9b693373df}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i816" id="noOpus@outlook.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i540" id="/^(ffxtlbr@mixidj\.com|{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}|{67097627-fd8e-4f6b-af4b-ecb65e50112e}|{f6f0f973-a4a3-48cf-9a7a-b7a69c30d71a}|{a3d0e35f-f1da-4ccb-ae77-e9d27777e68d}|{1122b43d-30ee-403f-9bfa-3cc99b0caddd})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1007,22 +1013,16 @@
               </prefs>
     </emItem>
       <emItem  blockID="i55" id="youtube@youtube7.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i497" id="{872b5b88-9db5-4310-bdd0-ac189557e5f5}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
-                    </versionRange>
-                    <prefs>
-              </prefs>
-    </emItem>
       <emItem  blockID="i76" id="crossriderapp3924@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -1107,16 +1107,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i674" id="crossriderapp12555@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i812" id="{1e4ea5fc-09e5-4f45-a43b-c048304899fc}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i710" id="{e0352044-1439-48ba-99b6-b05ed1a4d2de}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
@@ -1155,17 +1161,17 @@
               </prefs>
     </emItem>
       <emItem  blockID="i754" id="{bb7b7a60-f574-47c2-8a0b-4c56f2da9802}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+      <emItem  blockID="i497" id="{872b5b88-9db5-4310-bdd0-ac189557e5f5}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
@@ -1344,16 +1350,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i115" id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i820" id="{aab02ab1-33cf-4dfa-8a9f-f4e60e976d27}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i525" id="/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1482,16 +1494,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i726" id="{d87d56b2-1379-49f4-b081-af2850c79d8e}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i505" id="extacylife@a.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1818,22 +1836,20 @@
               </prefs>
     </emItem>
       <emItem  blockID="i42" id="{D19CA586-DD6C-4a0a-96F8-14644F340D60}">
                         <versionRange  minVersion="0.1" maxVersion="14.4.0" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i760" id="toolbar11093@freshy.com">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
-                    </versionRange>
-                    <prefs>
-                  <pref>browser.startup.homepage</pref>
-                  <pref>browser.search.defaultenginename</pref>
+      <emItem  blockID="i818" id="contentarget@maildrop.cc">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i142" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
                         <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
                     </versionRange>
                                 <versionRange  minVersion="4.2" maxVersion="4.2" severity="3">
                     </versionRange>
                     <prefs>
@@ -2058,16 +2074,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i91" id="crossriderapp4926@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="0.81.43" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i814" id="liiros@facebook.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i67" id="youtube2@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i520" id="/^({7316e43a-3ebd-4bb4-95c1-9caf6756c97f}|{0cc09160-108c-4759-bab1-5c12c216e005}|{ef03e721-f564-4333-a331-d4062cee6f2b}|{465fcfbb-47a4-4866-a5d5-d12f9a77da00}|{7557724b-30a9-42a4-98eb-77fcb0fd1be3}|{b7c7d4b0-7a84-4b73-a7ef-48ef59a52c3b})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -2163,16 +2185,24 @@
               </prefs>
     </emItem>
       <emItem  blockID="i312" id="extension21804@extension21804.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i760" id="toolbar11093@freshy.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+                  <pref>browser.startup.homepage</pref>
+                  <pref>browser.search.defaultenginename</pref>
+              </prefs>
+    </emItem>
       <emItem  blockID="i324" id="/^((34qEOefiyYtRJT@IM5Munavn\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\.com)|(E3wI2n@PEHTuuNVu\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\.com))$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i756" id="{5eeb83d0-96ea-4249-942c-beead6847053}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -139,29 +139,21 @@ pref("app.update.cert.maxErrors", 5);
 // when the |app.update.cert.checkAttributes| preference is set to false. Also,
 // the |app.update.url.override| preference should ONLY be used for testing.
 // IMPORTANT! metro.js should also be updated for updates to certs.X.issuerName
 // IMPORTANT! media.gmp-manager.certs.* prefs should also be updated if these
 // are updated.
 
 // Non-release builds (Nightly, Aurora, etc.) have been switched over to aus4.mozilla.org.
 // This condition protects us against accidentally using it for release builds.
-#ifndef RELEASE_BUILD
 pref("app.update.certs.1.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
 pref("app.update.certs.1.commonName", "aus4.mozilla.org");
 
 pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
 pref("app.update.certs.2.commonName", "aus4.mozilla.org");
-#else
-pref("app.update.certs.1.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
-pref("app.update.certs.1.commonName", "aus3.mozilla.org");
-
-pref("app.update.certs.2.issuerName", "CN=DigiCert Secure Server CA,O=DigiCert Inc,C=US");
-pref("app.update.certs.2.commonName", "aus3.mozilla.org");
-#endif
 #endif
 
 // Whether or not app updates are enabled
 pref("app.update.enabled", true);
 
 // This preference turns on app.update.mode and allows automatic download and
 // install to take place. We use a separate boolean toggle for this to make
 // the UI easier to construct.
@@ -189,21 +181,17 @@ pref("app.update.badge", true);
 pref("app.update.badge", false);
 #endif
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
-#ifndef RELEASE_BUILD
 pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
-#else
-pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
-#endif
 // app.update.url.manual is in branding section
 // app.update.url.details is in branding section
 
 // User-settable override to app.update.url for testing purposes.
 //pref("app.update.url.override", "");
 
 // app.update.interval is in branding section
 // app.update.promptWaitTime is in branding section
--- a/browser/base/content/searchSuggestionUI.js
+++ b/browser/base/content/searchSuggestionUI.js
@@ -52,16 +52,18 @@ function SearchSuggestionUIController(in
   this.input.addEventListener("keypress", this);
   this.input.addEventListener("input", this);
   this.input.addEventListener("focus", this);
   this.input.addEventListener("blur", this);
   window.addEventListener("ContentSearchService", this);
 
   this._stickyInputValue = "";
   this._hideSuggestions();
+
+  this._ignoreInputEvent = false;
 }
 
 SearchSuggestionUIController.prototype = {
 
   // The timeout (ms) of the remote suggestions.  Corresponds to
   // SearchSuggestionController.remoteTimeout.  Uses
   // SearchSuggestionController's default timeout if falsey.
   remoteTimeout: undefined,
@@ -138,16 +140,20 @@ SearchSuggestionUIController.prototype =
     this._sendMsg("AddFormHistoryEntry", this.input.value);
   },
 
   handleEvent: function (event) {
     this["_on" + event.type[0].toUpperCase() + event.type.substr(1)](event);
   },
 
   _onInput: function () {
+    if (this._ignoreInputEvent) {
+      this._ignoreInputEvent = false;
+      return;
+    }
     if (this.input.value) {
       this._getSuggestions();
     }
     else {
       this._stickyInputValue = "";
       this._hideSuggestions();
     }
     this.selectAndUpdateInput(-1);
@@ -226,16 +232,30 @@ SearchSuggestionUIController.prototype =
   _onMousemove: function (event) {
     this.selectedIndex = this._indexOfTableRowOrDescendent(event.target);
   },
 
   _onMousedown: function (event) {
     let idx = this._indexOfTableRowOrDescendent(event.target);
     let suggestion = this.suggestionAtIndex(idx);
     this._stickyInputValue = suggestion;
+
+    // Commit composition string forcibly, because setting input value does not
+    // work if input has composition string (see bug 1115616 and bug 632744).
+    try {
+      let imeEditor = this.input.editor.QueryInterface(Components.interfaces.nsIEditorIMESupport);
+      if (imeEditor.composing) {
+        // Ignore input event for compisition end to avoid getting suggestion
+        // again.
+        this._ignoreInputEvent = true;
+        imeEditor.forceCompositionEnd();
+        this._ignoreInputEvent = false;
+      }
+    } catch(e) { }
+
     this.input.value = suggestion;
     this.input.setAttribute("selection-index", idx);
     this.input.setAttribute("selection-kind", "mouse");
     this._hideSuggestions();
     if (this.onClick) {
       this.onClick.call(null);
     }
   },
--- a/browser/base/content/test/general/browser_searchSuggestionUI.js
+++ b/browser/base/content/test/general/browser_searchSuggestionUI.js
@@ -182,16 +182,56 @@ add_task(function* formHistory() {
 
   // Type an X again.  The form history entry should still be gone.
   state = yield msg("key", { key: "x", waitForSuggestions: true });
   checkState(state, "x", ["xfoo", "xbar"], -1);
 
   yield msg("reset");
 });
 
+add_task(function* composition() {
+  yield setUp();
+
+  let state = yield msg("startComposition", { data: "" });
+  checkState(state, "", [], -1);
+  state = yield msg("updateComposition", { data: "x" });
+  checkState(state, "", [], -1);
+  state = yield msg("changeComposition", { data: "x", waitForSuggestions: true });
+  checkState(state, "x", ["xfoo", "xbar"], -1);
+
+  // Mouse over the first suggestion.
+  state = yield msg("mousemove", 0);
+  checkState(state, "x", ["xfoo", "xbar"], 0);
+
+  // Mouse over the second suggestion.
+  state = yield msg("mousemove", 1);
+  checkState(state, "x", ["xfoo", "xbar"], 1);
+
+  // Click the second suggestion.  This should make it sticky.  To make sure it
+  // sticks, trigger suggestions again and cycle through them by pressing Down
+  // until nothing is selected again.
+  state = yield msg("mousedown", 1);
+
+  checkState(state, "xbar", [], -1);
+
+  state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
+  checkState(state, "xbar", ["xbarfoo", "xbarbar"], -1);
+
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xbarfoo", ["xbarfoo", "xbarbar"], 0);
+
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xbarbar", ["xbarfoo", "xbarbar"], 1);
+
+  state = yield msg("key", "VK_DOWN");
+  checkState(state, "xbar", ["xbarfoo", "xbarbar"], -1);
+
+  yield msg("reset");
+});
+
 
 let gDidInitialSetUp = false;
 
 function setUp() {
   return Task.spawn(function* () {
     if (!gDidInitialSetUp) {
       yield promiseNewEngine(TEST_ENGINE_BASENAME);
       yield promiseTab();
--- a/browser/base/content/test/general/searchSuggestionUI.js
+++ b/browser/base/content/test/general/searchSuggestionUI.js
@@ -22,16 +22,43 @@ let messageHandlers = {
 
   key: function (arg) {
     let keyName = typeof(arg) == "string" ? arg : arg.key;
     content.synthesizeKey(keyName, {});
     let wait = arg.waitForSuggestions ? waitForSuggestions : cb => cb();
     wait(ack);
   },
 
+  startComposition: function (arg) {
+    let data = typeof(arg) == "string" ? arg : arg.data;
+    content.synthesizeComposition({ type: "compositionstart", data: data });
+    ack();
+  },
+
+  updateComposition: function (arg) {
+    let data = typeof(arg) == "string" ? arg : arg.data;
+    content.synthesizeComposition({ type: "compositionupdate", data: data });
+    ack();
+  },
+
+  changeComposition: function (arg) {
+    let data = typeof(arg) == "string" ? arg : arg.data;
+    content.synthesizeCompositionChange({
+      composition: {
+        string: data,
+        clauses: [
+          { length: data.length, attr: content.COMPOSITION_ATTR_RAWINPUT }
+        ]
+      },
+      caret: { start: data.length, length: 0 }
+    });
+    let wait = arg.waitForSuggestions ? waitForSuggestions : cb => cb();
+    wait(ack);
+  },
+
   focus: function () {
     gController.input.focus();
     ack();
   },
 
   blur: function () {
     gController.input.blur();
     ack();
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -474,21 +474,17 @@ pref("app.update.metro.enabled", true);
 // If set to true, the Update Service will present no UI for any event.
 pref("app.update.silent", true);
 
 // If set to true, the Update Service will apply updates in the background
 // when it finishes downloading them.
 pref("app.update.staging.enabled", true);
 
 // Update service URL:
-#ifndef RELEASE_BUILD
 pref("app.update.url", "https://aus4.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
-#else
-pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
-#endif
 
 // Show the Update Checking/Ready UI when the user was idle for x seconds
 pref("app.update.idletime", 60);
 
 // Whether or not we show a dialog box informing the user that the update was
 // successfully applied. This is off in Firefox by default since we show a
 // upgrade start page instead! Other apps may wish to show this UI, and supply
 // a whatsNewURL field in their brand.properties that contains a link to a page
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -167,17 +167,17 @@ AC_CHECK_PROGS(CC, "${target_alias}-gcc"
 unset ac_cv_prog_CC
 AC_PROG_CC
 AC_CHECK_PROGS(CXX, "${target_alias}-g++" "${target}-g++", :)
 unset ac_cv_prog_CXX
 AC_PROG_CXX
 
 AC_CHECK_PROGS(RANLIB, "${target_alias}-ranlib" "${target}-ranlib", :)
 AC_CHECK_PROGS(AR, "${target_alias}-ar" "${target}-ar", :)
-MOZ_PATH_PROGS(AS, "${target_alias}-as" "${target}-as", :)
+AC_CHECK_PROGS(AS, "${target_alias}-as" "${target}-as", :)
 AC_CHECK_PROGS(LD, "${target_alias}-ld" "${target}-ld", :)
 AC_CHECK_PROGS(STRIP, "${target_alias}-strip" "${target}-strip", :)
 AC_CHECK_PROGS(WINDRES, "${target_alias}-windres" "${target}-windres", :)
 AC_CHECK_PROGS(OTOOL, "${target_alias}-otool" "${target}-otool", :)
 AC_DEFINE(CROSS_COMPILE)
 CROSS_COMPILE=1
 
 dnl If we cross compile for ppc on Mac OS X x86, cross_compiling will
--- a/configure.in
+++ b/configure.in
@@ -528,17 +528,17 @@ See https://developer.mozilla.org/en/Win
           if test -z "MAKEPRI" ; then
               AC_MSG_ERROR([makepri.exe is required for generating metro browser install components. It should be in the Win8 SDK.])
           fi
           AC_SUBST(MAKEPRI)
         fi
 
         dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
         dnl not something else like "magnetic tape manipulation utility".
-        MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'`
+        MSMT_TOOL=`${MT-mt} 2>&1|grep 'Microsoft (R) Manifest Tool'`
         if test -z "$MSMT_TOOL"; then
           AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.])
         fi
 
         changequote(,)
         _MSMT_VER_FILTER='s|.*[^!-~]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p'
         changequote([,])
         MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"`
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1798,16 +1798,22 @@ Element::SetSMILOverrideStyleRule(css::S
 }
 
 bool
 Element::IsLabelable() const
 {
   return false;
 }
 
+bool
+Element::IsInteractiveHTMLContent() const
+{
+  return false;
+}
+
 css::StyleRule*
 Element::GetInlineStyleRule()
 {
   return nullptr;
 }
 
 nsresult
 Element::SetInlineStyleRule(css::StyleRule* aStyleRule,
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -280,16 +280,21 @@ public:
   virtual nsICSSDeclaration* GetSMILOverrideStyle();
 
   /**
    * Returns if the element is labelable as per HTML specification.
    */
   virtual bool IsLabelable() const;
 
   /**
+   * Returns if the element is interactive content as per HTML specification.
+   */
+  virtual bool IsInteractiveHTMLContent() const;
+
+  /**
    * Is the attribute named stored in the mapped attributes?
    *
    * // XXXbz we use this method in HasAttributeDependentStyle, so svg
    *    returns true here even though it stores nothing in the mapped
    *    attributes.
    */
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
 
--- a/dom/base/MultipartFileImpl.cpp
+++ b/dom/base/MultipartFileImpl.cpp
@@ -230,16 +230,46 @@ MultipartFileImpl::GetMozFullPathInterna
   if (!blobImpl) {
     FileImplBase::GetMozFullPathInternal(aFilename, aRv);
     return;
   }
 
   blobImpl->GetMozFullPathInternal(aFilename, aRv);
 }
 
+nsresult
+MultipartFileImpl::SetMutable(bool aMutable)
+{
+  nsresult rv;
+
+  // This looks a little sketchy since FileImpl objects are supposed to be
+  // threadsafe. However, we try to enforce that all FileImpl objects must be
+  // set to immutable *before* being passed to another thread, so this should
+  // be safe.
+  if (!aMutable && !mImmutable && !mBlobImpls.IsEmpty()) {
+    for (uint32_t index = 0, count = mBlobImpls.Length();
+         index < count;
+         index++) {
+      rv = mBlobImpls[index]->SetMutable(aMutable);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+    }
+  }
+
+  rv = FileImplBase::SetMutable(aMutable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT_IF(!aMutable, mImmutable);
+
+  return NS_OK;
+}
+
 void
 MultipartFileImpl::InitializeChromeFile(File& aBlob,
                                         const ChromeFilePropertyBag& aBag,
                                         ErrorResult& aRv)
 {
   NS_ASSERTION(!mImmutable, "Something went wrong ...");
 
   if (mImmutable) {
--- a/dom/base/MultipartFileImpl.h
+++ b/dom/base/MultipartFileImpl.h
@@ -96,16 +96,19 @@ public:
   virtual const nsTArray<nsRefPtr<FileImpl>>* GetSubBlobImpls() const MOZ_OVERRIDE
   {
     return &mBlobImpls;
   }
 
   virtual void GetMozFullPathInternal(nsAString& aFullPath,
                                       ErrorResult& aRv) MOZ_OVERRIDE;
 
+  virtual nsresult
+  SetMutable(bool aMutable) MOZ_OVERRIDE;
+
   void SetName(const nsAString& aName)
   {
     mName = aName;
   }
 
   void SetFromNsIFile(bool aValue)
   {
     mIsFromNsIFile = aValue;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -5247,16 +5247,17 @@ already_AddRefed<AnonymousContent>
 nsIDocument::InsertAnonymousContent(Element& aElement, ErrorResult& aRv)
 {
   nsIPresShell* shell = GetShell();
   if (!shell || !shell->GetCanvasFrame()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
+  nsAutoScriptBlocker scriptBlocker;
   nsCOMPtr<Element> container = shell->GetCanvasFrame()
                                      ->GetCustomContentContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
 
   // Clone the node to avoid returning a direct reference
@@ -5271,29 +5272,32 @@ nsIDocument::InsertAnonymousContent(Elem
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
   nsRefPtr<AnonymousContent> anonymousContent =
     new AnonymousContent(clonedElement->AsElement());
   mAnonymousContents.AppendElement(anonymousContent);
 
+  shell->GetCanvasFrame()->ShowCustomContentContainer();
+
   return anonymousContent.forget();
 }
 
 void
 nsIDocument::RemoveAnonymousContent(AnonymousContent& aContent,
                                     ErrorResult& aRv)
 {
   nsIPresShell* shell = GetShell();
   if (!shell || !shell->GetCanvasFrame()) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
+  nsAutoScriptBlocker scriptBlocker;
   nsCOMPtr<Element> container = shell->GetCanvasFrame()
                                      ->GetCustomContentContainer();
   if (!container) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return;
   }
 
   // Iterate over mAnonymousContents to find and remove the given node.
@@ -5309,16 +5313,19 @@ nsIDocument::RemoveAnonymousContent(Anon
       container->RemoveChild(*node, aRv);
       if (aRv.Failed()) {
         return;
       }
 
       break;
     }
   }
+  if (mAnonymousContents.IsEmpty()) {
+    shell->GetCanvasFrame()->HideCustomContentContainer();
+  }
 }
 
 //
 // nsIDOMDocument interface
 //
 DocumentType*
 nsIDocument::GetDoctype() const
 {
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -1472,17 +1472,17 @@ nsXMLHttpRequest::CreateReadystatechange
 
   return NS_OK;
 }
 
 void
 nsXMLHttpRequest::DispatchProgressEvent(DOMEventTargetHelper* aTarget,
                                         const nsAString& aType,
                                         bool aLengthComputable,
-                                        uint64_t aLoaded, uint64_t aTotal)
+                                        int64_t aLoaded, int64_t aTotal)
 {
   NS_ASSERTION(aTarget, "null target");
   NS_ASSERTION(!aType.IsEmpty(), "missing event type");
 
   if (NS_FAILED(CheckInnerWindowCorrectness()) ||
       (!AllowUploadProgress() && aTarget == mUpload)) {
     return;
   }
@@ -1492,17 +1492,17 @@ nsXMLHttpRequest::DispatchProgressEvent(
                          aType.EqualsLiteral(TIMEOUT_STR) ||
                          aType.EqualsLiteral(ABORT_STR);
 
   ProgressEventInit init;
   init.mBubbles = false;
   init.mCancelable = false;
   init.mLengthComputable = aLengthComputable;
   init.mLoaded = aLoaded;
-  init.mTotal = (aTotal == UINT64_MAX) ? 0 : aTotal;
+  init.mTotal = (aTotal == -1) ? 0 : aTotal;
 
   nsRefPtr<ProgressEvent> event =
     ProgressEvent::Constructor(aTarget, aType, init);
   event->SetTrusted(true);
 
   aTarget->DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 
   if (dispatchLoadend) {
@@ -2776,20 +2776,25 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
   if ((aVariant || !aBody.IsNull()) && httpChannel &&
       !method.LowerCaseEqualsLiteral("get") &&
       !method.LowerCaseEqualsLiteral("head")) {
 
     nsAutoCString charset;
     nsAutoCString defaultContentType;
     nsCOMPtr<nsIInputStream> postDataStream;
 
+    uint64_t size_u64;
     rv = GetRequestBody(aVariant, aBody, getter_AddRefs(postDataStream),
-                        &mUploadTotal, defaultContentType, charset);
+                        &size_u64, defaultContentType, charset);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // make sure it fits within js MAX_SAFE_INTEGER
+    mUploadTotal =
+      net::InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
+
     if (postDataStream) {
       // If no content type header was set by the client, we set it to
       // application/xml.
       nsAutoCString contentType;
       if (NS_FAILED(httpChannel->
                       GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
                                        contentType)) ||
           contentType.IsEmpty()) {
@@ -3583,28 +3588,28 @@ nsXMLHttpRequest::MaybeDispatchProgressE
       mArrayBufferBuilder.reset();
     }
   }
 
   mProgressSinceLastProgressEvent = false;
 }
 
 NS_IMETHODIMP
-nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, uint64_t aProgress, uint64_t aProgressMax)
+nsXMLHttpRequest::OnProgress(nsIRequest *aRequest, nsISupports *aContext, int64_t aProgress, int64_t aProgressMax)
 {
   // We're uploading if our state is XML_HTTP_REQUEST_OPENED or
   // XML_HTTP_REQUEST_SENT
   bool upload = !!((XML_HTTP_REQUEST_OPENED | XML_HTTP_REQUEST_SENT) & mState);
   // When uploading, OnProgress reports also headers in aProgress and aProgressMax.
   // So, try to remove the headers, if possible.
-  bool lengthComputable = (aProgressMax != UINT64_MAX);
+  bool lengthComputable = (aProgressMax != -1);
   if (upload) {
-    uint64_t loaded = aProgress;
+    int64_t loaded = aProgress;
     if (lengthComputable) {
-      uint64_t headerSize = aProgressMax - mUploadTotal;
+      int64_t headerSize = aProgressMax - mUploadTotal;
       loaded -= headerSize;
     }
     mUploadLengthComputable = lengthComputable;
     mUploadTransferred = loaded;
     mProgressSinceLastProgressEvent = true;
 
     MaybeDispatchProgressEvents(false);
   } else {
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -544,17 +544,17 @@ public:
                     JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
 
   // This creates a trusted readystatechange event, which is not cancelable and
   // doesn't bubble.
   nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent);
   void DispatchProgressEvent(mozilla::DOMEventTargetHelper* aTarget,
                              const nsAString& aType,
                              bool aLengthComputable,
-                             uint64_t aLoaded, uint64_t aTotal);
+                             int64_t aLoaded, int64_t aTotal);
 
   // Dispatch the "progress" event on the XHR or XHR.upload object if we've
   // received data since the last "progress" event. Also dispatches
   // "uploadprogress" as needed.
   void MaybeDispatchProgressEvents(bool aFinalProgress);
 
   // This is called by the factory constructor.
   nsresult Init();
@@ -719,18 +719,18 @@ protected:
   nsIRequestObserver* mRequestObserver;
 
   nsCOMPtr<nsIURI> mBaseURI;
   nsCOMPtr<nsILoadGroup> mLoadGroup;
 
   uint32_t mState;
 
   nsRefPtr<nsXMLHttpRequestUpload> mUpload;
-  uint64_t mUploadTransferred;
-  uint64_t mUploadTotal;
+  int64_t mUploadTransferred;
+  int64_t mUploadTotal;
   bool mUploadLengthComputable;
   bool mUploadComplete;
   bool mProgressSinceLastProgressEvent;
 
   // Timeout support
   PRTime mRequestSentTime;
   uint32_t mTimeoutMilliseconds;
   nsCOMPtr<nsITimer> mTimeoutTimer;
@@ -739,28 +739,28 @@ protected:
 
   bool mErrorLoad;
   bool mWaitingForOnStopRequest;
   bool mProgressTimerIsActive;
   bool mIsHtml;
   bool mWarnAboutMultipartHtml;
   bool mWarnAboutSyncHtml;
   bool mLoadLengthComputable;
-  uint64_t mLoadTotal; // 0 if not known.
+  int64_t mLoadTotal; // 0 if not known.
   // Amount of script-exposed (i.e. after undoing gzip compresion) data
   // received.
   uint64_t mDataAvailable;
   // Number of HTTP message body bytes received so far. This quantity is
   // in the same units as Content-Length and mLoadTotal, and hence counts
   // compressed bytes when the channel has gzip Content-Encoding. If the
   // channel does not have Content-Encoding, this will be the same as
   // mDataReceived except between the OnProgress that changes mLoadTransferred
   // and the corresponding OnDataAvailable (which changes mDataReceived).
   // Ordering of OnProgress and OnDataAvailable is undefined.
-  uint64_t mLoadTransferred;
+  int64_t mLoadTransferred;
   nsCOMPtr<nsITimer> mProgressNotifier;
   void HandleProgressTimerCallback();
 
   bool mIsSystem;
   bool mIsAnon;
 
   /**
    * Close the XMLHttpRequest's channels and dispatch appropriate progress
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -317,31 +317,23 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
 },
 
 'DeviceStorage': {
     'nativeType': 'nsDOMDeviceStorage',
     'headerFile': 'DeviceStorage.h',
 },
 
-'Document': [
-{
+'Document': {
     'nativeType': 'nsIDocument',
     'binaryNames': {
         'documentURI': 'documentURIFromJS',
         'URL': 'documentURIFromJS'
     }
 },
-# Note: we still need the worker descriptor here because
-# XMLHttpRequest.send() uses it.
-{
-    'nativeType': 'JSObject',
-    'workers': True,
-    'skipGen': True
-}],
 
 'DOMException': {
     'binaryNames': {
         'message': 'messageMoz',
     },
 },
 
 'DOMMatrixReadOnly': {
@@ -426,25 +418,19 @@ DOMInterfaces = {
     'implicitJSContext': [ 'readAsArrayBuffer' ],
 },
 
 'FileReaderSync': {
     'workers': True,
     'wrapperCache': False,
 },
 
-'FormData': [
-{
+'FormData': {
     'nativeType': 'nsFormData'
 },
-{
-    'workers': True,
-    'skipGen': True,
-    'nativeType': 'JSObject'
-}],
 
 'Geolocation': {
     'headerFile': 'nsGeolocation.h'
 },
 
 'History': {
     'headerFile': 'nsHistory.h',
     'nativeType': 'nsHistory'
@@ -617,23 +603,21 @@ DOMInterfaces = {
 },
 {
     'workers': True,
 }],
 
 'InstallPhaseEvent': {
     'headerFile': 'ServiceWorkerEvents.h',
     'nativeType': 'mozilla::dom::workers::InstallPhaseEvent',
-    'workers': True
 },
 
 'InstallEvent': {
     'headerFile': 'ServiceWorkerEvents.h',
     'nativeType': 'mozilla::dom::workers::InstallEvent',
-    'workers': True
 },
 
 'KeyEvent': {
     'concrete': False
 },
 
 'LocalMediaStream': {
     'headerFile': 'DOMMediaStream.h',
@@ -644,33 +628,20 @@ DOMInterfaces = {
     'nativeType': 'nsLocation',
 },
 
 'MediaList': {
     'nativeType': 'nsMediaList',
     'headerFile': 'nsIMediaList.h',
 },
 
-'MediaSource': [{
-},
-{
-    'nativeType': 'JSObject',
-    'workers': True,
-    'skipGen': True
-}],
-
-'MediaStream': [{
+'MediaStream': {
     'headerFile': 'DOMMediaStream.h',
     'nativeType': 'mozilla::DOMMediaStream'
 },
-{
-    'nativeType': 'JSObject',
-    'workers': True,
-    'skipGen': True
-}],
 
 'MediaStreamAudioDestinationNode': {
     'binaryNames': { 'stream': 'DOMStream' }
 },
 
 'MediaStreamList': {
     'headerFile': 'MediaStreamList.h',
 },
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6827,17 +6827,39 @@ class CGMethodCall(CGThing):
 
         def getPerSignatureCall(signature, argConversionStartsAt=0):
             return CGPerSignatureCall(signature[0], signature[1],
                                       nativeMethodName, static, descriptor,
                                       method,
                                       argConversionStartsAt=argConversionStartsAt,
                                       isConstructor=isConstructor)
 
-        signatures = method.signatures()
+        def filteredSignatures(signatures, descriptor):
+            def typeExposedInWorkers(type):
+                return (not type.isGeckoInterface() or
+                        type.inner.isExternal() or
+                        type.inner.isExposedInAnyWorker())
+            if descriptor.workers:
+                # Filter out the signatures that should not be exposed in a
+                # worker.  The IDL parser enforces the return value being
+                # exposed correctly, but we have to check the argument types.
+                assert all(typeExposedInWorkers(sig[0]) for sig in signatures)
+                signatures = filter(
+                    lambda sig: all(typeExposedInWorkers(arg.type)
+                                    for arg in sig[1]),
+                    signatures)
+                if len(signatures) == 0:
+                    raise TypeError("%s.%s has a worker binding with no "
+                                    "signatures that take arguments exposed in "
+                                    "workers." %
+                                    (descriptor.interface.identifier.name,
+                                     method.identifier.name))
+            return signatures
+
+        signatures = filteredSignatures(method.signatures(), descriptor)
         if len(signatures) == 1:
             # Special case: we can just do a per-signature method call
             # here for our one signature and not worry about switching
             # on anything.
             signature = signatures[0]
             self.cgRoot = CGList([getPerSignatureCall(signature)])
             requiredArgs = requiredArgCount(signature)
 
@@ -6854,24 +6876,27 @@ class CGMethodCall(CGThing):
             return
 
         # Need to find the right overload
         maxArgCount = method.maxArgCount
         allowedArgCounts = method.allowedArgCounts
 
         argCountCases = []
         for argCountIdx, argCount in enumerate(allowedArgCounts):
-            possibleSignatures = method.signaturesForArgCount(argCount)
+            possibleSignatures = filteredSignatures(
+                method.signaturesForArgCount(argCount),
+                descriptor)
 
             # Try to optimize away cases when the next argCount in the list
             # will have the same code as us; if it does, we can fall through to
             # that case.
             if argCountIdx+1 < len(allowedArgCounts):
-                nextPossibleSignatures = \
-                    method.signaturesForArgCount(allowedArgCounts[argCountIdx+1])
+                nextPossibleSignatures = filteredSignatures(
+                    method.signaturesForArgCount(allowedArgCounts[argCountIdx+1]),
+                    descriptor)
             else:
                 nextPossibleSignatures = None
             if possibleSignatures == nextPossibleSignatures:
                 # Same set of signatures means we better have the same
                 # distinguishing index.  So we can in fact just fall through to
                 # the next case here.
                 assert (len(possibleSignatures) == 1 or
                         (method.distinguishingIndexForArgCount(argCount) ==
--- a/dom/bluetooth/BluetoothInterface.cpp
+++ b/dom/bluetooth/BluetoothInterface.cpp
@@ -1,15 +1,18 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=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/. */
 
 #include "BluetoothInterface.h"
+#if ANDROID_VERSION >= 17
+#include <cutils/properties.h>
+#endif
 #ifdef MOZ_B2G_BT_BLUEDROID
 #include "BluetoothHALInterface.h"
 #endif
 #ifdef MOZ_B2G_BT_DAEMON
 #include "BluetoothDaemonInterface.h"
 #endif
 
 BEGIN_BLUETOOTH_NAMESPACE
@@ -86,30 +89,70 @@ BluetoothNotificationHandler::~Bluetooth
 { }
 
 // Interface
 //
 
 BluetoothInterface*
 BluetoothInterface::GetInstance()
 {
+#if ANDROID_VERSION >= 17
+  /* We pick a default backend from the available ones. The branches
+   * are ordered by preference.
+   */
+#ifdef MOZ_B2G_BT_DAEMON
+  static const char sDefaultBackend[] = "bluetoothd";
+#else
+#ifdef MOZ_B2G_BT_BLUEDROID
+  static const char sDefaultBackend[] = "bluedroid";
+#else
+  static const char const * sDefaultBackend = nullptr;
+#endif
+#endif
+
   /* Here's where we decide which implementation to use. Currently
    * there is only Bluedroid and the Bluetooth daemon, but others are
    * possible. Having multiple interfaces built-in and selecting the
-   * correct one at runtime could also be an option.
+   * correct one at runtime is also an option.
    */
+
+  char value[PROPERTY_VALUE_MAX];
+  int len;
+
+  len = property_get("ro.moz.bluetooth.backend", value, sDefaultBackend);
+  if (len < 0) {
+    BT_WARNING("No Bluetooth backend available.");
+    return nullptr;
+  }
+
+  const nsDependentCString backend(value, len);
+
 #ifdef MOZ_B2G_BT_BLUEDROID
-  return BluetoothHALInterface::GetInstance();
+  if (backend.LowerCaseEqualsLiteral("bluedroid")) {
+    return BluetoothHALInterface::GetInstance();
+  } else
+#endif
+#ifdef MOZ_B2G_BT_DAEMON
+  if (backend.LowerCaseEqualsLiteral("bluetoothd")) {
+    return BluetoothDaemonInterface::GetInstance();
+  } else
+#endif
+  {
+    BT_WARNING("Bluetooth backend '%s' is unknown or not available.",
+               backend.get());
+  }
+  return nullptr;
+
 #else
-#ifdef MOZ_B2G_BT_DAEMON
-  return BluetoothDaemonInterface::GetInstance();
-#else
+  /* Anything that's not Android 4.2 or later uses BlueZ instead. The
+   * code should actually never reach this point.
+   */
+  BT_WARNING("No Bluetooth backend available for your system.");
   return nullptr;
 #endif
-#endif
 }
 
 BluetoothInterface::BluetoothInterface()
 { }
 
 BluetoothInterface::~BluetoothInterface()
 { }
 
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -11,17 +11,16 @@
 #include "mozilla/Attributes.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextComposition.h"
 #include "mozilla/TextEvents.h"
 #include "mozilla/dom/HTMLFormElement.h"
-#include "mozilla/dom/TabParent.h"
 
 #include "HTMLInputElement.h"
 #include "IMEContentObserver.h"
 
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
@@ -384,37 +383,16 @@ IMEStateManager::OnChangeFocusInternal(n
   if (NS_WARN_IF(!widget)) {
     PR_LOG(sISMLog, PR_LOG_ERROR,
       ("ISM:   IMEStateManager::OnChangeFocusInternal(), FAILED due to "
        "no widget to manage its IME state"));
     return NS_OK;
   }
 
   IMEState newState = GetNewIMEState(aPresContext, aContent);
-
-  // In e10s, remote content may have IME focus.  The main process (i.e. this process)
-  // would attempt to set state to DISABLED if, for example, the user clicks
-  // some other remote content.  The content process would later re-ENABLE IME, meaning
-  // that all state-changes were unnecessary.
-  // Here we filter the common case where the main process knows that the remote
-  // process controls IME focus.  The DISABLED->re-ENABLED progression can
-  // still happen since remote content may be concurrently communicating its claim
-  // on focus to the main process... but this cannot cause bugs like missed keypresses.
-  // (It just means a lot of needless IPC.)
-  if ((newState.mEnabled == IMEState::DISABLED) && TabParent::GetIMETabParent()) {
-    PR_LOG(sISMLog, PR_LOG_DEBUG,
-      ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
-       "Parent process cancels to set DISABLED state because the content process "
-       "has IME focus and has already sets IME state"));  
-    MOZ_ASSERT(XRE_IsParentProcess(),
-      "TabParent::GetIMETabParent() should never return non-null value "
-      "in the content process");
-    return NS_OK;
-  }
-
   if (!focusActuallyChanging) {
     // actual focus isn't changing, but if IME enabled state is changing,
     // we should do it.
     InputContext context = widget->GetInputContext();
     if (context.mIMEState.mEnabled == newState.mEnabled) {
       PR_LOG(sISMLog, PR_LOG_DEBUG,
         ("ISM:   IMEStateManager::OnChangeFocusInternal(), "
          "neither focus nor IME state is changing"));
--- a/dom/fetch/Request.cpp
+++ b/dom/fetch/Request.cpp
@@ -146,33 +146,45 @@ Request::Constructor(const GlobalObject&
   if (mode != RequestMode::EndGuard_) {
     request->SetMode(mode);
   }
 
   if (credentials != RequestCredentials::EndGuard_) {
     request->SetCredentialsMode(credentials);
   }
 
+  // Request constructor step 14.
   if (aInit.mMethod.WasPassed()) {
-    nsCString method = aInit.mMethod.Value();
-    ToLowerCase(method);
+    nsAutoCString method(aInit.mMethod.Value());
+    nsAutoCString upperCaseMethod = method;
+    ToUpperCase(upperCaseMethod);
 
-    if (!method.EqualsASCII("options") &&
-        !method.EqualsASCII("get") &&
-        !method.EqualsASCII("head") &&
-        !method.EqualsASCII("post") &&
-        !method.EqualsASCII("put") &&
-        !method.EqualsASCII("delete")) {
+    // Step 14.1. Disallow forbidden methods, and anything that is not a HTTP
+    // token, since HTTP states that Method may be any of the defined values or
+    // a token (extension method).
+    if (upperCaseMethod.EqualsLiteral("CONNECT") ||
+        upperCaseMethod.EqualsLiteral("TRACE") ||
+        upperCaseMethod.EqualsLiteral("TRACK") ||
+        !NS_IsValidHTTPToken(method)) {
       NS_ConvertUTF8toUTF16 label(method);
       aRv.ThrowTypeError(MSG_INVALID_REQUEST_METHOD, &label);
       return nullptr;
     }
 
-    ToUpperCase(method);
-    request->SetMethod(method);
+    // Step 14.2
+    if (upperCaseMethod.EqualsLiteral("DELETE") ||
+        upperCaseMethod.EqualsLiteral("GET") ||
+        upperCaseMethod.EqualsLiteral("HEAD") ||
+        upperCaseMethod.EqualsLiteral("POST") ||
+        upperCaseMethod.EqualsLiteral("PUT") ||
+        upperCaseMethod.EqualsLiteral("OPTIONS")) {
+      request->SetMethod(upperCaseMethod);
+    } else {
+      request->SetMethod(method);
+    }
   }
 
   nsRefPtr<InternalHeaders> requestHeaders = request->Headers();
 
   nsRefPtr<InternalHeaders> headers;
   if (aInit.mHeaders.WasPassed()) {
     nsRefPtr<Headers> h = Headers::Constructor(aGlobal, aInit.mHeaders.Value(), aRv);
     if (aRv.Failed()) {
--- a/dom/html/HTMLAnchorElement.h
+++ b/dom/html/HTMLAnchorElement.h
@@ -37,16 +37,22 @@ public:
 
   // CC
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLAnchorElement,
                                            nsGenericHTMLElement)
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
   virtual bool Draggable() const MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLAnchorElement
   NS_DECL_NSIDOMHTMLANCHORELEMENT
 
   // DOM memory reporter participant
   NS_DECL_SIZEOF_EXCLUDING_THIS
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
--- a/dom/html/HTMLAudioElement.cpp
+++ b/dom/html/HTMLAudioElement.cpp
@@ -35,16 +35,22 @@ HTMLAudioElement::HTMLAudioElement(alrea
   : HTMLMediaElement(aNodeInfo)
 {
 }
 
 HTMLAudioElement::~HTMLAudioElement()
 {
 }
 
+bool
+HTMLAudioElement::IsInteractiveHTMLContent() const
+{
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
+}
+
 already_AddRefed<HTMLAudioElement>
 HTMLAudioElement::Audio(const GlobalObject& aGlobal,
                         const Optional<nsAString>& aSrc,
                         ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
   nsIDocument* doc;
   if (!win || !(doc = win->GetExtantDoc())) {
--- a/dom/html/HTMLAudioElement.h
+++ b/dom/html/HTMLAudioElement.h
@@ -18,16 +18,19 @@ namespace dom {
 
 class HTMLAudioElement MOZ_FINAL : public HTMLMediaElement
 {
 public:
   typedef mozilla::dom::NodeInfo NodeInfo;
 
   explicit HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo);
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
+
   // nsIDOMHTMLMediaElement
   using HTMLMediaElement::GetPaused;
 
   virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
   virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
   virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
 
--- a/dom/html/HTMLButtonElement.h
+++ b/dom/html/HTMLButtonElement.h
@@ -31,16 +31,22 @@ public:
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLButtonElement, button)
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLButtonElement
   NS_DECL_NSIDOMHTMLBUTTONELEMENT
 
   // overriden nsIFormControl methods
   NS_IMETHOD_(uint32_t) GetType() const MOZ_OVERRIDE { return mType; }
   NS_IMETHOD Reset() MOZ_OVERRIDE;
   NS_IMETHOD SubmitNamesValues(nsFormSubmission* aFormSubmission) MOZ_OVERRIDE;
   NS_IMETHOD SaveState() MOZ_OVERRIDE;
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -21,16 +21,22 @@ public:
   explicit HTMLIFrameElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                              FromParser aFromParser = NOT_FROM_PARSER);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLIFrameElement, iframe)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLIFrameElement
   NS_DECL_NSIDOMHTMLIFRAMEELEMENT
 
   // nsIContent
   virtual bool ParseAttribute(int32_t aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult) MOZ_OVERRIDE;
--- a/dom/html/HTMLImageElement.cpp
+++ b/dom/html/HTMLImageElement.cpp
@@ -144,16 +144,22 @@ NS_IMPL_URI_ATTR(HTMLImageElement, LongD
 NS_IMPL_STRING_ATTR(HTMLImageElement, Sizes, sizes)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Lowsrc, lowsrc)
 NS_IMPL_URI_ATTR(HTMLImageElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLImageElement, Srcset, srcset)
 NS_IMPL_STRING_ATTR(HTMLImageElement, UseMap, usemap)
 NS_IMPL_INT_ATTR(HTMLImageElement, Vspace, vspace)
 
 bool
+HTMLImageElement::IsInteractiveHTMLContent() const
+{
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
+}
+
+bool
 HTMLImageElement::IsSrcsetEnabled()
 {
   return Preferences::GetBool(kPrefSrcsetEnabled, false);
 }
 
 nsresult
 HTMLImageElement::GetCurrentSrc(nsAString& aValue)
 {
--- a/dom/html/HTMLImageElement.h
+++ b/dom/html/HTMLImageElement.h
@@ -41,16 +41,19 @@ public:
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLImageElement,
                                            nsGenericHTMLElement)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual bool Draggable() const MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
+
   // nsIDOMHTMLImageElement
   NS_DECL_NSIDOMHTMLIMAGEELEMENT
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLImageElement, img)
 
   // override from nsImageLoadingContent
   CORSMode GetCORSMode() MOZ_OVERRIDE;
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -3206,16 +3206,22 @@ HTMLInputElement::Focus(ErrorResult& aEr
         break;
       }
     }
   }
 
   return;
 }
 
+bool
+HTMLInputElement::IsInteractiveHTMLContent() const
+{
+  return mType != NS_FORM_INPUT_HIDDEN;
+}
+
 NS_IMETHODIMP
 HTMLInputElement::Select()
 {
   if (mType == NS_FORM_INPUT_NUMBER) {
     nsNumberControlFrame* numberControlFrame =
       do_QueryFrame(GetPrimaryFrame());
     if (numberControlFrame) {
       return numberControlFrame->HandleSelectCall();
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -114,16 +114,19 @@ public:
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
   using nsGenericHTMLElement::Focus;
   virtual void Blur(ErrorResult& aError) MOZ_OVERRIDE;
   virtual void Focus(ErrorResult& aError) MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
+
   // nsIDOMHTMLInputElement
   NS_DECL_NSIDOMHTMLINPUTELEMENT
 
   // nsIPhonetic
   NS_DECL_NSIPHONETIC
 
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) MOZ_OVERRIDE
--- a/dom/html/HTMLLabelElement.cpp
+++ b/dom/html/HTMLLabelElement.cpp
@@ -78,29 +78,24 @@ HTMLLabelElement::Focus(ErrorResult& aEr
   if (fm) {
     nsCOMPtr<nsIDOMElement> elem = do_QueryObject(GetLabeledElement());
     if (elem)
       fm->SetFocus(elem, 0);
   }
 }
 
 static bool
-EventTargetIn(WidgetEvent* aEvent, nsIContent* aChild, nsIContent* aStop)
+InInteractiveHTMLContent(nsIContent* aContent, nsIContent* aStop)
 {
-  nsCOMPtr<nsIContent> c = do_QueryInterface(aEvent->target);
-  nsIContent *content = c;
-  while (content) {
-    if (content == aChild) {
+  nsIContent* content = aContent;
+  while (content && content != aStop) {
+    if (content->IsElement() &&
+        content->AsElement()->IsInteractiveHTMLContent()) {
       return true;
     }
-
-    if (content == aStop) {
-      break;
-    }
-
     content = content->GetParent();
   }
   return false;
 }
 
 nsresult
 HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
 {
@@ -110,20 +105,25 @@ HTMLLabelElement::PostHandleEvent(EventC
        aVisitor.mEvent->message != NS_MOUSE_BUTTON_DOWN) ||
       aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
       !aVisitor.mPresContext ||
       // Don't handle the event if it's already been handled by another label
       aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
     return NS_OK;
   }
 
+  nsCOMPtr<nsIContent> target = do_QueryInterface(aVisitor.mEvent->target);
+  if (InInteractiveHTMLContent(target, this)) {
+    return NS_OK;
+  }
+
   // Strong ref because event dispatch is going to happen.
   nsRefPtr<Element> content = GetLabeledElement();
 
-  if (content && !EventTargetIn(aVisitor.mEvent, content, this)) {
+  if (content) {
     mHandlingEvent = true;
     switch (aVisitor.mEvent->message) {
       case NS_MOUSE_BUTTON_DOWN:
         if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
           // We reset the mouse-down point on every event because there is
           // no guarantee we will reach the NS_MOUSE_CLICK code below.
           LayoutDeviceIntPoint* curPoint =
             new LayoutDeviceIntPoint(mouseEvent->refPoint);
--- a/dom/html/HTMLLabelElement.h
+++ b/dom/html/HTMLLabelElement.h
@@ -27,16 +27,22 @@ public:
   {
   }
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLLabelElement, label)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLLabelElement
   NS_DECL_NSIDOMHTMLLABELELEMENT
 
   using nsGenericHTMLFormElement::GetForm;
   void GetHtmlFor(nsString& aHtmlFor)
   {
     GetHTMLAttr(nsGkAtoms::_for, aHtmlFor);
   }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -429,16 +429,17 @@ NS_IMETHODIMP HTMLMediaElement::MediaLoa
 NS_IMPL_ADDREF_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
 NS_IMPL_RELEASE_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement, nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcAttrStream)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourcePointer)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoadBlockedDoc)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSourceLoadCandidate)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioChannelAgent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
   for (uint32_t i = 0; i < tmp->mOutputStreams.Length(); ++i) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOutputStreams[i].mStream);
@@ -2882,16 +2883,35 @@ private:
 };
 
 void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream)
 {
   NS_ASSERTION(!mSrcStream && !mSrcStreamListener, "Should have been ended already");
 
   mSrcStream = aStream;
 
+  nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
+  if (!window) {
+    return;
+  }
+
+  // Now that we have access to |mSrcStream| we can pipe it to our shadow
+  // version |mPlaybackStream|. If two media elements are playing the
+  // same realtime DOMMediaStream, this allows them to pause playback
+  // independently of each other.
+  mPlaybackStream = DOMMediaStream::CreateTrackUnionStream(window);
+  mPlaybackStreamInputPort = mPlaybackStream->GetStream()->AsProcessedStream()->
+    AllocateInputPort(mSrcStream->GetStream(), MediaInputPort::FLAG_BLOCK_OUTPUT);
+
+  nsRefPtr<nsIPrincipal> principal = GetCurrentPrincipal();
+  mPlaybackStream->CombineWithPrincipal(principal);
+
+  // Let |mSrcStream| decide when the stream has finished.
+  GetSrcMediaStream()->AsProcessedStream()->SetAutofinish(true);
+
   nsRefPtr<MediaStream> stream = mSrcStream->GetStream();
   if (stream) {
     stream->SetAudioChannelType(mAudioChannel);
   }
 
   // XXX if we ever support capturing the output of a media element which is
   // playing a stream, we'll need to add a CombineWithPrincipal call here.
   mSrcStreamListener = new StreamListener(this);
@@ -2928,16 +2948,18 @@ void HTMLMediaElement::SetupSrcMediaStre
 void HTMLMediaElement::EndSrcMediaStreamPlayback()
 {
   MediaStream* stream = GetSrcMediaStream();
   if (stream) {
     stream->RemoveListener(mSrcStreamListener);
   }
   mSrcStream->DisconnectTrackListListeners(AudioTracks(), VideoTracks());
 
+  mPlaybackStreamInputPort->Destroy();
+
   // Kill its reference to this element
   mSrcStreamListener->Forget();
   mSrcStreamListener = nullptr;
   if (stream) {
     stream->RemoveAudioOutput(this);
   }
   VideoFrameContainer* container = GetVideoFrameContainer();
   if (container) {
@@ -2948,16 +2970,18 @@ void HTMLMediaElement::EndSrcMediaStream
   }
   if (mPaused && stream) {
     stream->ChangeExplicitBlockerCount(-1);
   }
   if (mPausedForInactiveDocumentOrChannel && stream) {
     stream->ChangeExplicitBlockerCount(-1);
   }
   mSrcStream = nullptr;
+  mPlaybackStreamInputPort = nullptr;
+  mPlaybackStream = nullptr;
 }
 
 void HTMLMediaElement::ProcessMediaFragmentURI()
 {
   nsMediaFragmentURIParser parser(mLoadingSrc);
 
   if (mDecoder && parser.HasEndTime()) {
     mFragmentEnd = parser.GetEndTime();
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -333,18 +333,18 @@ public:
    * be fired if we've not fired a timeupdate event (for any reason) in the
    * last 250ms, as required by the spec when the current time is periodically
    * increasing during playback.
    */
   virtual void FireTimeUpdate(bool aPeriodic) MOZ_FINAL MOZ_OVERRIDE;
 
   MediaStream* GetSrcMediaStream() const
   {
-    NS_ASSERTION(mSrcStream, "Don't call this when not playing a stream");
-    return mSrcStream->GetStream();
+    NS_ASSERTION(mPlaybackStream, "Don't call this when not playing a stream");
+    return mPlaybackStream->GetStream();
   }
 
   // WebIDL
 
   MediaError* GetError() const
   {
     return mError;
   }
@@ -992,16 +992,24 @@ protected:
   // set in the src attribute.
   nsRefPtr<DOMMediaStream> mSrcAttrStream;
 
   // Holds a reference to the DOM wrapper for the MediaStream that we're
   // actually playing.
   // At most one of mDecoder and mSrcStream can be non-null.
   nsRefPtr<DOMMediaStream> mSrcStream;
 
+  // Holds a reference to a MediaInputPort connecting mSrcStream to mPlaybackStream.
+  nsRefPtr<MediaInputPort> mPlaybackStreamInputPort;
+
+  // Holds a reference to a stream with mSrcStream as input but intended for
+  // playback. Used so we don't block playback of other video elements
+  // playing the same mSrcStream.
+  nsRefPtr<DOMMediaStream> mPlaybackStream;
+
   // Holds references to the DOM wrappers for the MediaStreams that we're
   // writing to.
   struct OutputMediaStream {
     nsRefPtr<DOMMediaStream> mStream;
     bool mFinishWhenEnded;
   };
   nsTArray<OutputMediaStream> mOutputStreams;
 
--- a/dom/html/HTMLObjectElement.cpp
+++ b/dom/html/HTMLObjectElement.cpp
@@ -41,16 +41,22 @@ HTMLObjectElement::HTMLObjectElement(alr
 
 HTMLObjectElement::~HTMLObjectElement()
 {
   UnregisterActivityObserver();
   DestroyImageLoadingContent();
 }
 
 bool
+HTMLObjectElement::IsInteractiveHTMLContent() const
+{
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::usemap);
+}
+
+bool
 HTMLObjectElement::IsDoneAddingChildren()
 {
   return mIsDoneAddingChildren;
 }
 
 void
 HTMLObjectElement::DoneAddingChildren(bool aHaveNotified)
 {
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -25,16 +25,19 @@ public:
   explicit HTMLObjectElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                              FromParser aFromParser = NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
+
   // nsIDOMHTMLObjectElement
   NS_DECL_NSIDOMHTMLOBJECTELEMENT
 
   virtual nsresult BindToTree(nsIDocument *aDocument, nsIContent *aParent,
                               nsIContent *aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
--- a/dom/html/HTMLSelectElement.h
+++ b/dom/html/HTMLSelectElement.h
@@ -142,16 +142,22 @@ public:
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLSelectElement, select)
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLSelectElement
   NS_DECL_NSIDOMHTMLSELECTELEMENT
 
   // WebIdl HTMLSelectElement
   bool Autofocus() const
   {
     return GetBoolAttr(nsGkAtoms::autofocus);
   }
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -48,16 +48,22 @@ public:
   explicit HTMLTextAreaElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
                                FromParser aFromParser = NOT_FROM_PARSER);
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual int32_t TabIndexDefault() MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE
+  {
+    return true;
+  }
+
   // nsIDOMHTMLTextAreaElement
   NS_DECL_NSIDOMHTMLTEXTAREAELEMENT
 
   // nsIDOMNSEditableElement
   NS_IMETHOD GetEditor(nsIEditor** aEditor) MOZ_OVERRIDE
   {
     return nsGenericHTMLElement::GetEditor(aEditor);
   }
--- a/dom/html/HTMLVideoElement.cpp
+++ b/dom/html/HTMLVideoElement.cpp
@@ -121,16 +121,22 @@ nsresult HTMLVideoElement::SetAcceptHead
       "application/ogg;q=0.7,"
       "audio/*;q=0.6,*/*;q=0.5");
 
   return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                     value,
                                     false);
 }
 
+bool
+HTMLVideoElement::IsInteractiveHTMLContent() const
+{
+  return HasAttr(kNameSpaceID_None, nsGkAtoms::controls);
+}
+
 uint32_t HTMLVideoElement::MozParsedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetParsedFrames() : 0;
 }
--- a/dom/html/HTMLVideoElement.h
+++ b/dom/html/HTMLVideoElement.h
@@ -44,16 +44,19 @@ public:
   virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // Set size with the current video frame's height and width.
   // If there is no video frame, returns NS_ERROR_FAILURE.
   nsresult GetVideoSize(nsIntSize* size);
 
   virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
+  // Element
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
+
   // WebIDL
 
   uint32_t Width() const
   {
     return GetIntAttr(nsGkAtoms::width, 0);
   }
 
   void SetWidth(uint32_t aValue, ErrorResult& aRv)
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -1786,16 +1786,25 @@ nsGenericHTMLElement::GetContextMenu(nsI
 
 bool
 nsGenericHTMLElement::IsLabelable() const
 {
   return Tag() == nsGkAtoms::progress ||
          Tag() == nsGkAtoms::meter;
 }
 
+bool
+nsGenericHTMLElement::IsInteractiveHTMLContent() const
+{
+  return Tag() == nsGkAtoms::details ||
+         Tag() == nsGkAtoms::embed ||
+         Tag() == nsGkAtoms::keygen ||
+         HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex);
+}
+
 already_AddRefed<UndoManager>
 nsGenericHTMLElement::GetUndoManager()
 {
   nsDOMSlots* slots = GetExistingDOMSlots();
   if (slots && slots->mUndoManager) {
     nsRefPtr<UndoManager> undoManager = slots->mUndoManager;
     return undoManager.forget();
   } else {
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -899,16 +899,17 @@ public:
   }
 
   bool IsHidden() const
   {
     return HasAttr(kNameSpaceID_None, nsGkAtoms::hidden);
   }
 
   virtual bool IsLabelable() const MOZ_OVERRIDE;
+  virtual bool IsInteractiveHTMLContent() const MOZ_OVERRIDE;
 
   static bool TouchEventsEnabled(JSContext* /* unused */, JSObject* /* unused */);
 
   static inline bool
   CanHaveName(nsIAtom* aTag)
   {
     return aTag == nsGkAtoms::img ||
            aTag == nsGkAtoms::form ||
--- a/dom/html/test/forms/mochitest.ini
+++ b/dom/html/test/forms/mochitest.ini
@@ -59,16 +59,17 @@ skip-if = (toolkit == 'gonk' && debug) #
 skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_input_sanitization.html]
 [test_input_textarea_set_value_no_scroll.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_input_typing_sanitization.html]
 skip-if = buildapp == 'mulet'
 [test_input_untrusted_key_events.html]
 [test_input_url.html]
+[test_interactive_content_in_label.html]
 [test_label_control_attribute.html]
 [test_label_input_controls.html]
 [test_max_attribute.html]
 skip-if = e10s
 [test_maxlength_attribute.html]
 [test_meter_element.html]
 [test_meter_pseudo-classes.html]
 [test_min_attribute.html]
@@ -76,16 +77,17 @@ skip-if = e10s
 [test_mozistextfield.html]
 [test_novalidate_attribute.html]
 [test_option_disabled.html]
 [test_option_index_attribute.html]
 [test_option_text.html]
 [test_output_element.html]
 [test_pattern_attribute.html]
 [test_progress_element.html]
+[test_radio_in_label.html]
 [test_radio_radionodelist.html]
 [test_required_attribute.html]
 skip-if = e10s
 [test_restore_form_elements.html]
 [test_save_restore_radio_groups.html]
 [test_select_selectedOptions.html]
 [test_select_validation.html]
 [test_set_range_text.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_interactive_content_in_label.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=229925
+-->
+<head>
+  <title>Test for Bug 229925</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.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=229925">Mozilla Bug 229925</a>
+<p id="display"></p>
+<form action="#">
+  <label>
+    <span id="text">label</span>
+    <input type="button" id="target" value="target">
+
+    <a id="yes1" href="#">a</a>
+    <audio id="yes2" controls></audio>
+    <button id="yes3">button</button>
+    <details id="yes4">details</details>
+    <embed id="yes5">embed</embed>
+    <iframe id="yes6" src="data:text/plain," style="width: 16px; height: 16px;"></iframe>
+    <img id="yes7" src="data:image/png," usemap="#map">
+    <input id="yes8" type="text" size="4">
+    <keygen id="yes9">
+    <label id="yes10">label</label>
+    <object id="yes11" usemap="#map">object</object>
+    <select id="yes12"><option>select</option></select>
+    <textarea id="yes13" cols="1" rows="1"></textarea>
+    <video id="yes14" controls></video>
+    <span id="yes15" tabindex="1">tabindex</span>
+
+    <audio id="no1"></audio>
+    <img id="no2" src="data:image/png,">
+    <input id="no3" type="hidden">
+    <object id="no4">object</object>
+    <video id="no5"></video>
+  </label>
+</form>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 229925 **/
+
+var target = document.getElementById("target");
+
+var yes_nodes = [
+  document.getElementById("yes1"),
+  document.getElementById("yes2"),
+  document.getElementById("yes3"),
+  document.getElementById("yes4"),
+  document.getElementById("yes5"),
+  document.getElementById("yes6"),
+  document.getElementById("yes7"),
+  document.getElementById("yes8"),
+  document.getElementById("yes9"),
+  document.getElementById("yes10"),
+  document.getElementById("yes11"),
+  document.getElementById("yes12"),
+  document.getElementById("yes13"),
+  document.getElementById("yes14"),
+  document.getElementById("yes15"),
+];
+
+var no_nodes = [
+  document.getElementById("text"),
+  document.getElementById("no1"),
+  document.getElementById("no2"),
+  document.getElementById("no3"),
+  document.getElementById("no4"),
+  document.getElementById("no5"),
+];
+
+var target_clicked = false;
+target.addEventListener("click", function() {
+  target_clicked = true;
+});
+
+var node;
+for (node of yes_nodes) {
+  target_clicked = false;
+  node.click();
+  is(target_clicked, false, "mouse click on interactive content " + node.nodeName + " shouldn't dispatch event to label target");
+}
+
+for (node of no_nodes) {
+  target_clicked = false;
+  node.click();
+  is(target_clicked, true, "mouse click on non interactive content " + node.nodeName + " should dispatch event to label target");
+}
+
+</script>
+</pre>
+</body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/dom/html/test/forms/test_radio_in_label.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=229925
+-->
+<head>
+  <title>Test for Bug 229925</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.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=229925">Mozilla Bug 229925</a>
+<p id="display"></p>
+<form>
+  <label>
+    <span id="s1">LABEL</span>
+    <input type="radio" name="rdo" value="1" id="r1" onmousedown="document.body.appendChild(document.createTextNode('down'));">
+    <input type="radio" name="rdo" value="2" id="r2" checked="checked">
+  </label>
+</form>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 229925 **/
+var r1 = document.getElementById("r1");
+var r2 = document.getElementById("r2");
+var s1 = document.getElementById("s1");
+
+r1.click();
+ok(r1.checked,
+   "The first radio input element should be checked by clicking the element");
+r2.click();
+ok(r2.checked,
+   "The second radio input element should be checked by clicking the element");
+s1.click();
+ok(r1.checked,
+   "The first radio input element should be checked by clicking other element");
+
+r1.focus();
+synthesizeKey("VK_LEFT", {});
+ok(r2.checked,
+   "The second radio input element should be checked by key");
+synthesizeKey("VK_LEFT", {});
+ok(r1.checked,
+   "The first radio input element should be checked by key");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -119,18 +119,22 @@ skip-if = (buildapp == 'b2g' && toolkit 
 # This test can only run in the main process.
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s
 [test_blob_simple.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_blob_worker_crash.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_blob_worker_xhr_post.html]
 skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
+[test_blob_worker_xhr_post_multifile.html]
+skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
 [test_blob_worker_xhr_read.html]
 skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
+[test_blob_worker_xhr_read_slice.html]
+skip-if = ((buildapp == 'b2g' && toolkit != 'gonk') || (e10s && toolkit == 'windows')) # Bug 931116
 [test_blocked_order.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_bug937006.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_clear.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_complex_keyPaths.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
copy from dom/indexedDB/test/test_blob_worker_xhr_post.html
copy to dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
--- a/dom/indexedDB/test/test_blob_worker_xhr_post.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_post_multifile.html
@@ -5,16 +5,22 @@
 <html>
 <head>
   <title>Indexed Database Property Test</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
+  /**
+   * Create a composite/multi-file Blob on the worker, then post it as an XHR
+   * payload and ensure that we don't hang/generate an assertion/etc. but
+   * instead generate the expected 404.  This test is basically the same as
+   * test_blob_worker_xhr_post.html except for the composite Blob.
+   */
   function testSteps()
   {
     const BLOB_DATA = ["fun ", "times ", "all ", "around!"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
 
@@ -57,46 +63,40 @@
     event = yield undefined;
 
     blob = event.target.result;
 
     ok(blob instanceof Blob, "Got a blob");
     is(blob.size, BLOB_SIZE, "Correct size");
     is(blob.type, BLOB_TYPE, "Correct type");
 
-    let slice = blob.slice(0, BLOB_DATA[0].length, BLOB_TYPE);
-
-    ok(slice instanceof Blob, "Slice returned a blob");
-    is(slice.size, BLOB_DATA[0].length, "Correct size for slice");
-    is(slice.type, BLOB_TYPE, "Correct type for slice");
-
-    info("Sending slice to a worker");
-
     function workerScript() {
       onmessage = function(event) {
         var blob = event.data;
+        var compositeBlob = new Blob(["preceding string. ", blob],
+                                     { type: "text/plain" });
         var xhr = new XMLHttpRequest();
         // We just want to make sure the error case doesn't fire; it's fine for
         // us to just want a 404.
         xhr.open('POST', 'http://mochi.test:8888/does-not-exist', true);
         xhr.onload = function() {
           postMessage({ status: xhr.status });
         };
         xhr.onerror = function() {
           postMessage({ status: 'error' });
         }
-        xhr.send(blob);
+        xhr.send(compositeBlob);
       }
     }
 
     let workerScriptUrl =
       URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
 
     let xhrWorker = new Worker(workerScriptUrl);
-    xhrWorker.postMessage(slice);
+    xhrWorker.postMessage(blob);
     xhrWorker.onmessage = grabEventAndContinueHandler;
     event = yield undefined;
 
     is(event.data.status, 404, "XHR generated the expected 404");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
copy from dom/indexedDB/test/test_blob_worker_xhr_read.html
copy to dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
--- a/dom/indexedDB/test/test_blob_worker_xhr_read.html
+++ b/dom/indexedDB/test/test_blob_worker_xhr_read_slice.html
@@ -7,18 +7,19 @@
   <title>Indexed Database Blob Read From Worker</title>
 
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script type="text/javascript;version=1.7">
   /**
    * Create an IndexedDB-backed Blob, send it to the worker, try and read the
-   * contents of the Blob from the worker using an XHR.  Ideally, we don't
-   * deadlock the main thread.
+   * *SLICED* contents of the Blob from the worker using an XHR.  This is
+   * (as of the time of writing this) basically the same as
+   * test_blob_worker_xhr_read.html but with slicing added.
    */
   function testSteps()
   {
     const BLOB_DATA = ["Green"];
     const BLOB_TYPE = "text/plain";
     const BLOB_SIZE = BLOB_DATA.join("").length;
 
     info("Setting up");
@@ -67,17 +68,18 @@
     is(blob.size, BLOB_SIZE, "Correct size");
     is(blob.type, BLOB_TYPE, "Correct type");
 
     info("Sending blob to a worker");
 
     function workerScript() {
       onmessage = function(event) {
         var blob = event.data;
-        var blobUrl = URL.createObjectURL(blob);
+        var slicedBlob = blob.slice(0, 3, "text/plain");
+        var blobUrl = URL.createObjectURL(slicedBlob);
         var xhr = new XMLHttpRequest();
         xhr.open('GET', blobUrl, true);
         xhr.responseType = 'text';
         xhr.onload = function() {
           postMessage({ data: xhr.response });
           URL.revokeObjectURL(blobUrl);
         };
         xhr.onerror = function() {
@@ -91,17 +93,17 @@
     let workerScriptUrl =
       URL.createObjectURL(new Blob(["(", workerScript.toSource(), ")()"]));
 
     let xhrWorker = new Worker(workerScriptUrl);
     xhrWorker.postMessage(blob);
     xhrWorker.onmessage = grabEventAndContinueHandler;
     event = yield undefined;
 
-    is(event.data.data, "Green", "XHR returned expected payload.");
+    is(event.data.data, "Gre", "XHR returned expected sliced payload.");
     xhrWorker.terminate();
 
     URL.revokeObjectURL(workerScriptUrl);
 
     finishTest();
     yield undefined;
   }
   </script>
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1918,17 +1918,21 @@ public:
               ErrorResult& aRv) MOZ_OVERRIDE;
 
   virtual nsresult
   GetInternalStream(nsIInputStream** aStream) MOZ_OVERRIDE;
 
   virtual int64_t
   GetFileId() MOZ_OVERRIDE;
 
-  virtual int64_t GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
+  virtual int64_t
+  GetLastModified(ErrorResult& aRv) MOZ_OVERRIDE;
+
+  virtual nsresult
+  SetMutable(bool aMutable) MOZ_OVERRIDE;
 
   virtual BlobChild*
   GetBlobChild() MOZ_OVERRIDE;
 
   virtual BlobParent*
   GetBlobParent() MOZ_OVERRIDE;
 
 protected:
@@ -2000,24 +2004,38 @@ public:
   }
 
   uint64_t
   Start() const
   {
     return mStart;
   }
 
+  void
+  EnsureActorWasCreated()
+  {
+    MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
+                  mActorWasCreated);
+
+    if (!mActorWasCreated) {
+      EnsureActorWasCreatedInternal();
+    }
+  }
+
   NS_DECL_ISUPPORTS_INHERITED
 
   virtual BlobChild*
   GetBlobChild() MOZ_OVERRIDE;
 
 private:
   ~RemoteBlobSliceImpl()
   { }
+
+  void
+  EnsureActorWasCreatedInternal();
 };
 
 /*******************************************************************************
  * BlobParent::RemoteBlobImpl Declaration
  ******************************************************************************/
 
 class BlobParent::RemoteBlobImpl MOZ_FINAL
   : public FileImpl
@@ -2385,16 +2403,36 @@ RemoteBlobImpl::GetLastModified(ErrorRes
 {
   if (IsDateUnknown()) {
     return 0;
   }
 
   return mLastModificationDate;
 }
 
+nsresult
+BlobChild::
+RemoteBlobImpl::SetMutable(bool aMutable)
+{
+  if (!aMutable && IsSlice()) {
+    // Make sure that slices are backed by a real actor now while we are still
+    // on the correct thread.
+    AsSlice()->EnsureActorWasCreated();
+  }
+
+  nsresult rv = FileImplBase::SetMutable(aMutable);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_ASSERT_IF(!aMutable, mImmutable);
+
+  return NS_OK;
+}
+
 BlobChild*
 BlobChild::
 RemoteBlobImpl::GetBlobChild()
 {
   return mActor;
 }
 
 BlobParent*
@@ -2567,31 +2605,22 @@ RemoteBlobSliceImpl::RemoteBlobSliceImpl
     MOZ_ASSERT(parentSize >= aStart + aLength);
   }
 #endif
 
   // Account for the offset of the parent slice, if any.
   mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart;
 }
 
-NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
-                             BlobChild::RemoteBlobImpl)
-
-BlobChild*
+void
 BlobChild::
-RemoteBlobSliceImpl::GetBlobChild()
+RemoteBlobSliceImpl::EnsureActorWasCreatedInternal()
 {
-  MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
-                mActorWasCreated);
-
-  if (mActorWasCreated) {
-    return RemoteBlobImpl::GetBlobChild();
-  }
-
   MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+  MOZ_ASSERT(!mActorWasCreated);
 
   mActorWasCreated = true;
 
   BlobChild* baseActor = mParent->GetActor();
   MOZ_ASSERT(baseActor);
   MOZ_ASSERT(baseActor->HasManager());
 
   nsID id;
@@ -2606,18 +2635,28 @@ RemoteBlobSliceImpl::GetBlobChild()
                                 mContentType /* contentType */));
 
   if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
     mActor = SendSliceConstructor(contentManager, this, params);
   } else {
     mActor =
       SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
   }
-
-  return mActor;
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
+                             BlobChild::RemoteBlobImpl)
+
+BlobChild*
+BlobChild::
+RemoteBlobSliceImpl::GetBlobChild()
+{
+  EnsureActorWasCreated();
+
+  return RemoteBlobImpl::GetBlobChild();
 }
 
 /*******************************************************************************
  * BlobParent::RemoteBlobImpl
  ******************************************************************************/
 
 BlobParent::
 RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, FileImpl* aBlobImpl)
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -277,23 +277,23 @@ parent:
     EnableDisableCommands(nsString action,
                           nsCString[] enabledCommands,
                           nsCString[] disabledCommands);
 
     prio(urgent) sync GetInputContext() returns (int32_t IMEEnabled,
                                                  int32_t IMEOpen,
                                                  intptr_t NativeIMEContext);
 
-    prio(urgent) sync SetInputContext(int32_t IMEEnabled,
-                                      int32_t IMEOpen,
-                                      nsString type,
-                                      nsString inputmode,
-                                      nsString actionHint,
-                                      int32_t cause,
-                                      int32_t focusChange);
+    prio(urgent) async SetInputContext(int32_t IMEEnabled,
+                                       int32_t IMEOpen,
+                                       nsString type,
+                                       nsString inputmode,
+                                       nsString actionHint,
+                                       int32_t cause,
+                                       int32_t focusChange);
 
     sync IsParentWindowMainWidgetVisible() returns (bool visible);
 
     /**
      * Gets the DPI of the screen corresponding to this browser.
      */
     sync GetDPI() returns (float value);
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -2023,44 +2023,31 @@ bool
 TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
                                const int32_t& aIMEOpen,
                                const nsString& aType,
                                const nsString& aInputmode,
                                const nsString& aActionHint,
                                const int32_t& aCause,
                                const int32_t& aFocusChange)
 {
-  nsCOMPtr<nsIWidget> widget = GetWidget();
-  if (!widget || !AllowContentIME()) {
-    return true;
-  }
-
-  InputContext oldContext = widget->GetInputContext();
-
-  // Ignore if current widget IME setting is not DISABLED and didn't come
-  // from remote content.  Chrome content may have taken over.
-  if (oldContext.mIMEState.mEnabled != IMEState::DISABLED &&
-      oldContext.IsOriginMainProcess()) {
-    return true;
-  }
-
   // mIMETabParent (which is actually static) tracks which if any TabParent has IMEFocus
   // When the input mode is set to anything but IMEState::DISABLED,
   // mIMETabParent should be set to this
   mIMETabParent =
     aIMEEnabled != static_cast<int32_t>(IMEState::DISABLED) ? this : nullptr;
+  nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget || !AllowContentIME())
+    return true;
 
   InputContext context;
   context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
   context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
   context.mHTMLInputType.Assign(aType);
   context.mHTMLInputInputmode.Assign(aInputmode);
   context.mActionHint.Assign(aActionHint);
-  context.mOrigin = InputContext::ORIGIN_CONTENT;
-
   InputContextAction action(
     static_cast<InputContextAction::Cause>(aCause),
     static_cast<InputContextAction::FocusChange>(aFocusChange));
   widget->SetInputContext(context, action);
 
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   if (!observerService)
     return true;
--- a/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
+++ b/dom/media/fmp4/gonk/GonkAudioDecoderManager.cpp
@@ -158,22 +158,29 @@ GonkAudioDecoderManager::Output(int64_t 
         return NS_ERROR_NOT_AVAILABLE;
       } else if (rv != NS_OK || data == nullptr) {
         return NS_ERROR_UNEXPECTED;
       }
       aOutData = data;
       return NS_OK;
     }
     case android::INFO_FORMAT_CHANGED:
-    case android::INFO_OUTPUT_BUFFERS_CHANGED:
     {
       // If the format changed, update our cached info.
       GADM_LOG("Decoder format changed");
       return Output(aStreamOffset, aOutData);
     }
+    case android::INFO_OUTPUT_BUFFERS_CHANGED:
+    {
+      GADM_LOG("Info Output Buffers Changed");
+      if (mDecoder->UpdateOutputBuffers()) {
+        return Output(aStreamOffset, aOutData);
+      }
+      return NS_ERROR_FAILURE;
+    }
     case -EAGAIN:
     {
       return NS_ERROR_NOT_AVAILABLE;
     }
     case android::ERROR_END_OF_STREAM:
     {
       GADM_LOG("Got EOS frame!");
       nsRefPtr<AudioData> data;
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -504,16 +504,17 @@ skip-if = (toolkit == 'android' && proce
 [test_streams_autoplay.html]
 [test_streams_element_capture.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_streams_element_capture_createObjectURL.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_streams_element_capture_playback.html]
 [test_streams_element_capture_reset.html]
 [test_streams_gc.html]
+[test_streams_individual_pause.html]
 [test_streams_srcObject.html]
 [test_streams_tracks.html]
 [test_texttrack.html]
 [test_texttrackcue.html]
 [test_texttracklist.html]
 [test_texttrackregion.html]
 [test_timeupdate_small_files.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_streams_individual_pause.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for bug 1073406. Pausing a video element should not pause another playing the same stream.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<video id="video1" autoplay></video>
+<video id="video2" autoplay></video>
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var getVideoImagePixelData = function(v) {
+  var canvas = document.createElement("canvas");
+  var ctx = canvas.getContext("2d");
+  ctx.drawImage(v, 0, 0);
+  var imgData = ctx.getImageData(canvas.width/2, canvas.height/2, 1, 1).data;
+  return "r" + imgData[0] +
+         "g" + imgData[1] +
+         "b" + imgData[2] +
+         "a" + imgData[3];
+}
+
+navigator.mozGetUserMedia({video: true, fake: true}, function(stream) {
+  var stream = stream;
+  var video1 = document.getElementById('video1');
+  var video2 = document.getElementById('video2');
+
+  var src = URL.createObjectURL(stream);
+  video1.src = src;
+  video2.src = src;
+
+  video1.onplaying = () => video1.pause();
+
+  var v1PausedImageData;
+  var v2PausedImageData;
+
+  video1.onpause = function() {
+    v1PausedImageData = getVideoImagePixelData(video1);
+    v2PausedImageData = getVideoImagePixelData(video2);
+    v2TimesToTest = 3;
+    video2.ontimeupdate = function() {
+      if (getVideoImagePixelData(video2) === v2PausedImageData) {
+        // Wait until video2 has progressed it's video.
+        // If it doesn't, we'll time out and fail.
+        info("video2 has not progressed. Waiting.");
+        return;
+      }
+
+      if (--v2TimesToTest > 0) {
+        // Wait for a while to be sure video1 would have gotten a frame
+        // if it is playing.
+        info("video2 progressed OK");
+        return;
+      }
+
+      video2.ontimeupdate = null;
+      ok(true, "video2 is playing");
+      isnot(video1.currentTime, video2.currentTime,
+            "v1 and v2 should not be at the same currentTime");
+      is(v1PausedImageData, getVideoImagePixelData(video1),
+         "video1 video frame should not have updated since video1 paused");
+      SimpleTest.finish();
+    };
+  };
+}, function(error) {
+  ok(false, "getUserMedia should not fail, got " + error.name);
+  SimpleTest.finish();
+});
+</script>
+</body>
+</html>
--- a/dom/plugins/base/nsPluginStreamListenerPeer.cpp
+++ b/dom/plugins/base/nsPluginStreamListenerPeer.cpp
@@ -567,18 +567,18 @@ nsPluginStreamListenerPeer::OnStartReque
     return rv;
   }
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginStreamListenerPeer::OnProgress(nsIRequest *request,
                                                      nsISupports* aContext,
-                                                     uint64_t aProgress,
-                                                     uint64_t aProgressMax)
+                                                     int64_t aProgress,
+                                                     int64_t aProgressMax)
 {
   nsresult rv = NS_OK;
   return rv;
 }
 
 NS_IMETHODIMP nsPluginStreamListenerPeer::OnStatus(nsIRequest *request,
                                                    nsISupports* aContext,
                                                    nsresult aStatus,
--- a/dom/plugins/test/mochitest/cocoa_focus.html
+++ b/dom/plugins/test/mochitest/cocoa_focus.html
@@ -12,16 +12,17 @@
 
     function ok(aValue, aMessage) {
       window.opener.SimpleTest.ok(aValue, aMessage);
     }
 
     function runTests() {
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       var utils = SpecialPowers.DOMWindowUtils;
+      var scale = utils.screenPixelsPerCSSPixel;
 
       var plugin1 = document.getElementById("plugin1"); // What we're testing.
       var plugin2 = document.getElementById("plugin2"); // Dummy.
 
       var plugin1Bounds = plugin1.getBoundingClientRect();
       var plugin2Bounds = plugin2.getBoundingClientRect();
 
       var plugin1X = (window.mozInnerScreenX + plugin1Bounds.left + 10);
@@ -50,18 +51,18 @@
       try {
         plugin1.getFocusState();
       } catch (e) {
         initialStateUnknown = true;
       }
       is(initialStateUnknown, true, "Initial state should be unknown, assumed false.");
 
       // Give the plugin focus (the window is already focused).
-      utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
-      utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
+      utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1);
+      utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1);
       expectedEventCount++;
 
       is(plugin1.getFocusState(), true, "(1) Plugin should have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Make sure window activation state changes don't spontaneously
       // change plugin focus.
 
@@ -73,29 +74,29 @@
 
       // Focus the window.
       window.focus();
 
       is(plugin1.getFocusState(), true, "(3) Plugin should still have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Take focus from the plugin.
-      utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseDown, 0, plugin2);
-      utils.sendNativeMouseEvent(plugin2X, plugin2Y, NSLeftMouseUp, 0, plugin2);
+      utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseDown, 0, plugin2);
+      utils.sendNativeMouseEvent(plugin2X * scale, plugin2Y * scale, NSLeftMouseUp, 0, plugin2);
       expectedEventCount++;
 
       is(plugin1.getFocusState(), false, "(4) Plugin should not have focus.");
       is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
 
       // Make sure window activation causes the plugin to be informed of focus
       // changes that took place while the window was inactive.
 
       // Give the plugin focus (the window is already focused).
-      utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseDown, 0, plugin1);
-      utils.sendNativeMouseEvent(plugin1X, plugin1Y, NSLeftMouseUp, 0, plugin1);
+      utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseDown, 0, plugin1);
+      utils.sendNativeMouseEvent(plugin1X * scale, plugin1Y * scale, NSLeftMouseUp, 0, plugin1);
       expectedEventCount++;
 
       // Blur the window.
       window.blur();
 
       // Take focus from the plugin while the window is blurred.
       plugin2.focus();
 
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -127,30 +127,33 @@ static nsTArray<CommandChain*> gCommandC
 const CommandFunc NetworkUtils::sWifiEnableChain[] = {
   NetworkUtils::clearWifiTetherParms,
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::startAccessPointDriver,
   NetworkUtils::setAccessPoint,
   NetworkUtils::startSoftAP,
   NetworkUtils::setInterfaceUp,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::enableNat,
   NetworkUtils::wifiTetheringSuccess
 };
 
 const CommandFunc NetworkUtils::sWifiDisableChain[] = {
   NetworkUtils::clearWifiTetherParms,
   NetworkUtils::stopSoftAP,
   NetworkUtils::stopAccessPointDriver,
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::untetherInterface,
+  NetworkUtils::removeInterfaceFromLocalNetwork,
   NetworkUtils::preTetherInterfaceList,
   NetworkUtils::postTetherInterfaceList,
   NetworkUtils::disableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::stopTethering,
   NetworkUtils::wifiTetheringSuccess
 };
 
@@ -168,16 +171,18 @@ const CommandFunc NetworkUtils::sWifiRet
 
   // sWifiEnableChain:
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::startAccessPointDriver,
   NetworkUtils::setAccessPoint,
   NetworkUtils::startSoftAP,
   NetworkUtils::setInterfaceUp,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::enableNat,
   NetworkUtils::wifiTetheringSuccess
 };
 
@@ -186,24 +191,27 @@ const CommandFunc NetworkUtils::sWifiOpe
   NetworkUtils::wifiOperationModeSuccess
 };
 
 const CommandFunc NetworkUtils::sUSBEnableChain[] = {
   NetworkUtils::setInterfaceUp,
   NetworkUtils::enableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::usbTetheringSuccess
 };
 
 const CommandFunc NetworkUtils::sUSBDisableChain[] = {
   NetworkUtils::untetherInterface,
+  NetworkUtils::removeInterfaceFromLocalNetwork,
   NetworkUtils::preTetherInterfaceList,
   NetworkUtils::postTetherInterfaceList,
   NetworkUtils::disableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::stopTethering,
   NetworkUtils::usbTetheringSuccess
 };
 
@@ -729,16 +737,58 @@ void NetworkUtils::tetherInterface(Comma
                                    NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname));
 
   doCommand(command, aChain, aCallback);
 }
 
+void NetworkUtils::addInterfaceToLocalNetwork(CommandChain* aChain,
+                                              CommandCallback aCallback,
+                                              NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add local %s",
+           GET_CHAR(mInternalIfname));
+
+  doCommand(command, aChain, aCallback);
+}
+
+void NetworkUtils::addRouteToLocalNetwork(CommandChain* aChain,
+                                          CommandCallback aCallback,
+                                          NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  uint32_t prefix = atoi(GET_CHAR(mPrefix));
+  uint32_t ip = inet_addr(GET_CHAR(mIp));
+  char* networkAddr = getNetworkAddr(ip, prefix);
+
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network route add local %s %s/%s",
+           GET_CHAR(mInternalIfname), networkAddr, GET_CHAR(mPrefix));
+
+  doCommand(command, aChain, aCallback);
+}
+
 void NetworkUtils::preTetherInterfaceList(CommandChain* aChain,
                                           CommandCallback aCallback,
                                           NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   if (SDK_VERSION >= 16) {
     snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list");
   } else {
@@ -842,22 +892,48 @@ void NetworkUtils::untetherInterface(Com
                                      NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname));
 
   doCommand(command, aChain, aCallback);
 }
 
+void NetworkUtils::removeInterfaceFromLocalNetwork(CommandChain* aChain,
+                                                   CommandCallback aCallback,
+                                                   NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network interface remove local %s",
+           GET_CHAR(mIfname));
+
+  doCommand(command, aChain, aCallback);
+}
+
 void NetworkUtils::setDnsForwarders(CommandChain* aChain,
                                     CommandCallback aCallback,
                                     NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
-  snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s", GET_CHAR(mDns1), GET_CHAR(mDns2));
+
+  if (SDK_VERSION >= 20) {
+    snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %d %s %s",
+             GET_FIELD(mNetId), GET_CHAR(mDns1), GET_CHAR(mDns2));
+  } else {
+    snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s",
+             GET_CHAR(mDns1), GET_CHAR(mDns2));
+  }
 
   doCommand(command, aChain, aCallback);
 }
 
 void NetworkUtils::enableNat(CommandChain* aChain,
                              CommandCallback aCallback,
                              NetworkResultOptions& aResult)
 {
@@ -2118,16 +2194,25 @@ CommandResult NetworkUtils::setWifiTethe
   if (strcmp(interfaceProperties.dns2, "")) {
     int type = getIpType(interfaceProperties.dns2);
     if (type != AF_INET6) {
       aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
     }
   }
   dumpParams(aOptions, "WIFI");
 
+  if (SDK_VERSION >= 20) {
+    NetIdManager::NetIdInfo netIdInfo;
+    if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) {
+      ERROR("No such interface: %s", GET_CHAR(mExternalIfname));
+      return -1;
+    }
+    aOptions.mNetId = netIdInfo.mNetId;
+  }
+
   if (enable) {
     NU_DBG("Starting Wifi Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sWifiEnableChain, wifiTetheringFail);
   } else {
     NU_DBG("Stopping Wifi Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sWifiDisableChain, wifiTetheringFail);
@@ -2150,16 +2235,25 @@ CommandResult NetworkUtils::setUSBTether
   if (strcmp(interfaceProperties.dns2, "")) {
     int type = getIpType(interfaceProperties.dns2);
     if (type != AF_INET6) {
       aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
     }
   }
   dumpParams(aOptions, "USB");
 
+  if (SDK_VERSION >= 20) {
+    NetIdManager::NetIdInfo netIdInfo;
+    if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) {
+      ERROR("No such interface: %s", GET_CHAR(mExternalIfname));
+      return -1;
+    }
+    aOptions.mNetId = netIdInfo.mNetId;
+  }
+
   if (enable) {
     NU_DBG("Starting USB Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sUSBEnableChain, usbTetheringFail);
   } else {
     NU_DBG("Stopping USB Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sUSBDisableChain, usbTetheringFail);
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -351,23 +351,26 @@ private:
   static void clearWifiTetherParms(PARAMS);
   static void enableAlarm(PARAMS);
   static void disableAlarm(PARAMS);
   static void setQuota(PARAMS);
   static void removeQuota(PARAMS);
   static void setAlarm(PARAMS);
   static void setInterfaceUp(PARAMS);
   static void tetherInterface(PARAMS);
+  static void addInterfaceToLocalNetwork(PARAMS);
+  static void addRouteToLocalNetwork(PARAMS);
   static void preTetherInterfaceList(PARAMS);
   static void postTetherInterfaceList(PARAMS);
   static void setIpForwardingEnabled(PARAMS);
   static void tetheringStatus(PARAMS);
   static void stopTethering(PARAMS);
   static void startTethering(PARAMS);
   static void untetherInterface(PARAMS);
+  static void removeInterfaceFromLocalNetwork(PARAMS);
   static void setDnsForwarders(PARAMS);
   static void enableNat(PARAMS);
   static void disableNat(PARAMS);
   static void setDefaultInterface(PARAMS);
   static void setInterfaceDns(PARAMS);
   static void wifiTetheringSuccess(PARAMS);
   static void usbTetheringSuccess(PARAMS);
   static void networkInterfaceAlarmSuccess(PARAMS);
--- a/dom/webidl/DataStore.webidl
+++ b/dom/webidl/DataStore.webidl
@@ -52,17 +52,17 @@ interface DataStore : EventTarget {
   [Throws]
   Promise<unsigned long> getLength();
 
   [NewObject, Throws]
   DataStoreCursor sync(optional DOMString revisionId = "");
 };
 
 partial interface DataStore {
-  [ChromeOnly, Throws]
+  [ChromeOnly, Throws, Exposed=Window]
   void setDataStoreImpl(DataStoreImpl store);
 };
 
 // TODO Bug 957086 - The constructor and the setDataStoreCursorImpl(...) will be
 //                   removed once the DataStore API is fully rewritten in C++,
 //                   which currently plays a role of C++ proxy directing to the
 //                   JS codes implemented by the DataStoreCursorImpl WebIDL.
 
@@ -77,17 +77,17 @@ interface DataStoreCursor {
   [Throws]
   Promise<DataStoreTask> next();
 
   [Throws]
   void close();
 };
 
 partial interface DataStoreCursor {
-  [ChromeOnly]
+  [ChromeOnly, Exposed=Window]
   void setDataStoreCursorImpl(DataStoreCursorImpl cursor);
 };
 
 enum DataStoreOperation {
   "add",
   "update",
   "remove",
   "clear",
--- a/dom/workers/DataStore.cpp
+++ b/dom/workers/DataStore.cpp
@@ -711,22 +711,16 @@ WorkerDataStore::Sync(JSContext* aCx,
                                    aRevisionId,
                                    aRv);
   runnable->Dispatch(aCx);
 
   return workerCursor.forget();
 }
 
 void
-WorkerDataStore::SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv)
-{
-  NS_NOTREACHED("We don't use this for the WorkerDataStore!");
-}
-
-void
 WorkerDataStore::SetBackingDataStore(
   const nsMainThreadPtrHandle<DataStore>& aBackingStore)
 {
   mBackingStore = aBackingStore;
 }
 
 void
 WorkerDataStore::SetDataStoreChangeEventProxy(
--- a/dom/workers/DataStore.h
+++ b/dom/workers/DataStore.h
@@ -80,19 +80,16 @@ public:
   already_AddRefed<Promise> GetLength(JSContext* aCx, ErrorResult& aRv);
 
   already_AddRefed<WorkerDataStoreCursor> Sync(JSContext* aCx,
                                                const nsAString& aRevisionId,
                                                ErrorResult& aRv);
 
   IMPL_EVENT_HANDLER(change)
 
-  // We don't use this for the WorkerDataStore.
-  void SetDataStoreImpl(DataStoreImpl& aStore, ErrorResult& aRv);
-
   void SetBackingDataStore(
     const nsMainThreadPtrHandle<DataStore>& aBackingStore);
 
   void SetDataStoreChangeEventProxy(DataStoreChangeEventProxy* aEventProxy);
 
 protected:
   virtual ~WorkerDataStore() {}
 
--- a/dom/workers/DataStoreCursor.cpp
+++ b/dom/workers/DataStoreCursor.cpp
@@ -183,21 +183,15 @@ WorkerDataStoreCursor::Close(JSContext* 
   workerPrivate->AssertIsOnWorkerThread();
 
   nsRefPtr<DataStoreCursorCloseRunnable> runnable =
     new DataStoreCursorCloseRunnable(workerPrivate, mBackingCursor, aRv);
   runnable->Dispatch(aCx);
 }
 
 void
-WorkerDataStoreCursor::SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor)
-{
-  NS_NOTREACHED("We don't use this for the WorkerDataStoreCursor!");
-}
-
-void
 WorkerDataStoreCursor::SetBackingDataStoreCursor(
   const nsMainThreadPtrHandle<DataStoreCursor>& aBackingCursor)
 {
   mBackingCursor = aBackingCursor;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/DataStoreCursor.h
+++ b/dom/workers/DataStoreCursor.h
@@ -40,19 +40,16 @@ public:
   // WebIDL (public APIs)
 
   already_AddRefed<WorkerDataStore> GetStore(JSContext *aCx, ErrorResult& aRv);
 
   already_AddRefed<Promise> Next(JSContext *aCx, ErrorResult& aRv);
 
   void Close(JSContext *aCx, ErrorResult& aRv);
 
-  // We don't use this for the WorkerDataStore.
-  void SetDataStoreCursorImpl(DataStoreCursorImpl& aCursor);
-
   void SetBackingDataStoreCursor(
     const nsMainThreadPtrHandle<DataStoreCursor>& aBackingCursor);
 
 protected:
   virtual ~WorkerDataStoreCursor() {}
 
 private:
   nsMainThreadPtrHandle<DataStoreCursor> mBackingCursor;
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -33,17 +33,17 @@ protected:
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallPhaseEvent, Event)
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
   {
-    return mozilla::dom::InstallPhaseEventBinding_workers::Wrap(aCx, this);
+    return mozilla::dom::InstallPhaseEventBinding::Wrap(aCx, this);
   }
 
   static already_AddRefed<InstallPhaseEvent>
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
               const EventInit& aOptions)
   {
     nsRefPtr<InstallPhaseEvent> e = new InstallPhaseEvent(aOwner);
@@ -85,17 +85,17 @@ protected:
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(InstallEvent, InstallPhaseEvent)
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObjectInternal(JSContext* aCx) MOZ_OVERRIDE
   {
-    return mozilla::dom::InstallEventBinding_workers::Wrap(aCx, this);
+    return mozilla::dom::InstallEventBinding::Wrap(aCx, this);
   }
 
   static already_AddRefed<InstallEvent>
   Constructor(mozilla::dom::EventTarget* aOwner,
               const nsAString& aType,
               const InstallEventInit& aOptions)
   {
     nsRefPtr<InstallEvent> e = new InstallEvent(aOwner);
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -76,16 +76,20 @@ public:
   CreateURLRunnable(WorkerPrivate* aWorkerPrivate, FileImpl* aBlobImpl,
                     const mozilla::dom::objectURLOptions& aOptions,
                     nsString& aURL)
   : WorkerMainThreadRunnable(aWorkerPrivate),
     mBlobImpl(aBlobImpl),
     mURL(aURL)
   {
     MOZ_ASSERT(aBlobImpl);
+
+    DebugOnly<bool> isMutable;
+    MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+    MOZ_ASSERT(!isMutable);
   }
 
   bool
   MainThreadRun()
   {
     using namespace mozilla::ipc;
 
     AssertIsOnMainThread();
@@ -109,16 +113,20 @@ public:
             MOZ_ASSERT(newBlobImplHolder);
 
             mBlobImpl = newBlobImplHolder;
           }
         }
       }
     }
 
+    DebugOnly<bool> isMutable;
+    MOZ_ASSERT(NS_SUCCEEDED(mBlobImpl->GetMutable(&isMutable)));
+    MOZ_ASSERT(!isMutable);
+
     nsCOMPtr<nsIPrincipal> principal;
     nsIDocument* doc = nullptr;
 
     nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
     if (window) {
       doc = window->GetExtantDoc();
       if (!doc) {
         SetDOMStringToNull(mURL);
@@ -867,38 +875,33 @@ URL::SetHash(const nsAString& aHash, Err
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 // static
 void
-URL::CreateObjectURL(const GlobalObject& aGlobal, JSObject* aBlob,
-                     const mozilla::dom::objectURLOptions& aOptions,
-                     nsString& aResult, mozilla::ErrorResult& aRv)
-{
-  SetDOMStringToNull(aResult);
-
-  NS_NAMED_LITERAL_STRING(argStr, "Argument 1 of URL.createObjectURL");
-  NS_NAMED_LITERAL_STRING(blobStr, "MediaStream");
-  aRv.ThrowTypeError(MSG_DOES_NOT_IMPLEMENT_INTERFACE, &argStr, &blobStr);
-}
-
-// static
-void
 URL::CreateObjectURL(const GlobalObject& aGlobal, File& aBlob,
                      const mozilla::dom::objectURLOptions& aOptions,
                      nsString& aResult, mozilla::ErrorResult& aRv)
 {
   JSContext* cx = aGlobal.Context();
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
 
+  nsRefPtr<FileImpl> blobImpl = aBlob.Impl();
+  MOZ_ASSERT(blobImpl);
+
+  aRv = blobImpl->SetMutable(false);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
   nsRefPtr<CreateURLRunnable> runnable =
-    new CreateURLRunnable(workerPrivate, aBlob.Impl(), aOptions, aResult);
+    new CreateURLRunnable(workerPrivate, blobImpl, aOptions, aResult);
 
   if (!runnable->Dispatch(cx)) {
     JS_ReportPendingException(cx);
   }
 }
 
 // static
 void
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -54,21 +54,16 @@ public:
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               URL& aBase, ErrorResult& aRv);
   static already_AddRefed<URL>
   Constructor(const GlobalObject& aGlobal, const nsAString& aUrl,
               const nsAString& aBase, ErrorResult& aRv);
 
   static void
   CreateObjectURL(const GlobalObject& aGlobal,
-                  JSObject* aArg, const objectURLOptions& aOptions,
-                  nsString& aResult, ErrorResult& aRv);
-
-  static void
-  CreateObjectURL(const GlobalObject& aGlobal,
                   File& aArg, const objectURLOptions& aOptions,
                   nsString& aResult, ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl);
 
   void GetHref(nsString& aHref, ErrorResult& aRv) const;
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -56,16 +56,17 @@
 #include "mozilla/dom/WorkerBinding.h"
 #include "mozilla/dom/indexedDB/IDBFactory.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/Preferences.h"
+#include "MultipartFileImpl.h"
 #include "nsAlgorithm.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsDOMJSUtils.h"
 #include "nsHostObjectProtocolHandler.h"
 #include "nsJSEnvironment.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
@@ -302,16 +303,94 @@ LogErrorToConsole(const nsAString& aMess
   __android_log_print(ANDROID_LOG_INFO, "Gecko", kErrorString, msg.get(),
                       filename.get(), aLineNumber);
 #endif
 
   fprintf(stderr, kErrorString, msg.get(), filename.get(), aLineNumber);
   fflush(stderr);
 }
 
+// Recursive!
+already_AddRefed<FileImpl>
+EnsureBlobForBackgroundManager(FileImpl* aBlobImpl,
+                               PBackgroundChild* aManager = nullptr)
+{
+  MOZ_ASSERT(aBlobImpl);
+
+  if (!aManager) {
+    aManager = BackgroundChild::GetForCurrentThread();
+    MOZ_ASSERT(aManager);
+  }
+
+  nsRefPtr<FileImpl> blobImpl = aBlobImpl;
+
+  const nsTArray<nsRefPtr<FileImpl>>* subBlobImpls =
+    aBlobImpl->GetSubBlobImpls();
+
+  if (!subBlobImpls) {
+    if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
+      // Always make sure we have a blob from an actor we can use on this
+      // thread.
+      BlobChild* blobChild = BlobChild::GetOrCreate(aManager, blobImpl);
+      MOZ_ASSERT(blobChild);
+
+      blobImpl = blobChild->GetBlobImpl();
+      MOZ_ASSERT(blobImpl);
+
+      DebugOnly<bool> isMutable;
+      MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+      MOZ_ASSERT(!isMutable);
+    } else {
+      MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
+    }
+
+    return blobImpl.forget();
+  }
+
+  const uint32_t subBlobCount = subBlobImpls->Length();
+  MOZ_ASSERT(subBlobCount);
+
+  nsTArray<nsRefPtr<FileImpl>> newSubBlobImpls;
+  newSubBlobImpls.SetLength(subBlobCount);
+
+  bool newBlobImplNeeded = false;
+
+  for (uint32_t index = 0; index < subBlobCount; index++) {
+    const nsRefPtr<FileImpl>& subBlobImpl = subBlobImpls->ElementAt(index);
+    MOZ_ASSERT(subBlobImpl);
+
+    nsRefPtr<FileImpl>& newSubBlobImpl = newSubBlobImpls[index];
+
+    newSubBlobImpl = EnsureBlobForBackgroundManager(subBlobImpl, aManager);
+    MOZ_ASSERT(newSubBlobImpl);
+
+    if (subBlobImpl != newSubBlobImpl) {
+      newBlobImplNeeded = true;
+    }
+  }
+
+  if (newBlobImplNeeded) {
+    nsString contentType;
+    blobImpl->GetType(contentType);
+
+    if (blobImpl->IsFile()) {
+      nsString name;
+      blobImpl->GetName(name);
+
+      blobImpl = new MultipartFileImpl(newSubBlobImpls, name, contentType);
+    } else {
+      blobImpl = new MultipartFileImpl(newSubBlobImpls, contentType);
+    }
+
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(blobImpl->SetMutable(false)));
+  }
+
+  return blobImpl.forget();
+}
+
 void
 ReadBlobOrFile(JSContext* aCx,
                JSStructuredCloneReader* aReader,
                bool aIsMainThread,
                JS::MutableHandle<JSObject*> aBlobOrFile)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aReader);
@@ -322,32 +401,18 @@ ReadBlobOrFile(JSContext* aCx,
     FileImpl* rawBlobImpl;
     MOZ_ALWAYS_TRUE(JS_ReadBytes(aReader, &rawBlobImpl, sizeof(rawBlobImpl)));
 
     MOZ_ASSERT(rawBlobImpl);
 
     blobImpl = rawBlobImpl;
   }
 
-  DebugOnly<bool> isMutable;
-  MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
-  MOZ_ASSERT(!isMutable);
-
-  if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(blobImpl)) {
-    PBackgroundChild* backgroundManager =
-      BackgroundChild::GetForCurrentThread();
-    MOZ_ASSERT(backgroundManager);
-
-    // Always make sure we have a blob from an actor we can use on this thread.
-    BlobChild* blobChild = BlobChild::GetOrCreate(backgroundManager, blobImpl);
-    MOZ_ASSERT(blobChild);
-
-    blobImpl = blobChild->GetBlobImpl();
-    MOZ_ASSERT(blobImpl);
-  }
+  blobImpl = EnsureBlobForBackgroundManager(blobImpl);
+  MOZ_ASSERT(blobImpl);
 
   nsCOMPtr<nsISupports> parent;
   if (aIsMainThread) {
     AssertIsOnMainThread();
 
     nsCOMPtr<nsIScriptGlobalObject> scriptGlobal =
       nsJSUtils::GetStaticScriptGlobal(JS::CurrentGlobalOrNull(aCx));
     parent = do_QueryInterface(scriptGlobal);
@@ -371,34 +436,20 @@ WriteBlobOrFile(JSContext* aCx,
                 JSStructuredCloneWriter* aWriter,
                 FileImpl* aBlobOrFileImpl,
                 nsTArray<nsCOMPtr<nsISupports>>& aClonedObjects)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aWriter);
   MOZ_ASSERT(aBlobOrFileImpl);
 
-  nsRefPtr<FileImpl> newBlobOrFileImpl;
-  if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryObject(aBlobOrFileImpl)) {
-    PBackgroundChild* backgroundManager =
-      BackgroundChild::GetForCurrentThread();
-    MOZ_ASSERT(backgroundManager);
-
-    // Always make sure we have a blob from an actor we can use on this thread.
-    BlobChild* blobChild =
-      BlobChild::GetOrCreate(backgroundManager, aBlobOrFileImpl);
-    MOZ_ASSERT(blobChild);
-
-    newBlobOrFileImpl = blobChild->GetBlobImpl();
-    MOZ_ASSERT(newBlobOrFileImpl);
-
-    aBlobOrFileImpl = newBlobOrFileImpl;
-  }
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(aBlobOrFileImpl->SetMutable(false)));
+  nsRefPtr<FileImpl> blobImpl = EnsureBlobForBackgroundManager(aBlobOrFileImpl);
+  MOZ_ASSERT(blobImpl);
+
+  aBlobOrFileImpl = blobImpl;
 
   if (NS_WARN_IF(!JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0)) ||
       NS_WARN_IF(!JS_WriteBytes(aWriter,
                                 &aBlobOrFileImpl,
                                 sizeof(aBlobOrFileImpl)))) {
     return false;
   }
 
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -2173,16 +2173,24 @@ XMLHttpRequest::Send(File& aBody, ErrorR
   }
 
   JS::Rooted<JS::Value> value(cx);
   if (!GetOrCreateDOMReflector(cx, &aBody, &value)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
+  nsRefPtr<FileImpl> blobImpl = aBody.Impl();
+  MOZ_ASSERT(blobImpl);
+
+  aRv = blobImpl->SetMutable(false);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
+
   const JSStructuredCloneCallbacks* callbacks =
     mWorkerPrivate->IsChromeWorker() ?
     ChromeWorkerStructuredCloneCallbacks(false) :
     WorkerStructuredCloneCallbacks(false);
 
   nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
 
   JSAutoStructuredCloneBuffer buffer;
--- a/dom/workers/test/fetch/worker_test_request.js
+++ b/dom/workers/test/fetch/worker_test_request.js
@@ -73,27 +73,43 @@ function testBug1109574() {
   is(r1.bodyUsed, false, "Initial value of bodyUsed should be false");
   var r2 = new Request(r1);
   is(r1.bodyUsed, false, "Request with null body should not have bodyUsed set");
   // This should succeed.
   var r3 = new Request(r1);
 }
 
 function testMethod() {
-  var allowed = ["delete", "get", "head", "options", "post", "put"];
+  // These get normalized.
+  var allowed = ["delete", "get", "head", "options", "post", "put" ];
   for (var i = 0; i < allowed.length; ++i) {
     try {
       var r = new Request("", { method: allowed[i] });
       ok(true, "Method " + allowed[i] + " should be allowed");
+      is(r.method, allowed[i].toUpperCase(),
+         "Standard HTTP method " + allowed[i] + " should be normalized");
     } catch(e) {
       ok(false, "Method " + allowed[i] + " should be allowed");
     }
   }
 
-  var forbidden = ["aardvark", "connect", "trace", "track"];
+  var allowed = [ "pAtCh", "foo" ];
+  for (var i = 0; i < allowed.length; ++i) {
+    try {
+      var r = new Request("", { method: allowed[i] });
+      ok(true, "Method " + allowed[i] + " should be allowed");
+      is(r.method, allowed[i],
+         "Non-standard but valid HTTP method " + allowed[i] +
+         " should not be normalized");
+    } catch(e) {
+      ok(false, "Method " + allowed[i] + " should be allowed");
+    }
+  }
+
+  var forbidden = ["connect", "trace", "track", "<invalid token??"];
   for (var i = 0; i < forbidden.length; ++i) {
     try {
       var r = new Request("", { method: forbidden[i] });
       ok(false, "Method " + forbidden[i] + " should be forbidden");
     } catch(e) {
       ok(true, "Method " + forbidden[i] + " should be forbidden");
     }
   }
--- a/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/nsWebBrowserPersist.cpp
@@ -905,41 +905,41 @@ nsWebBrowserPersist::OnDataAvailable(
 }
 
 
 //*****************************************************************************
 // nsWebBrowserPersist::nsIProgressEventSink
 //*****************************************************************************
 
 /* void onProgress (in nsIRequest request, in nsISupports ctxt,
-    in unsigned long long aProgress, in unsigned long long aProgressMax); */
+    in long long aProgress, in long long aProgressMax); */
 NS_IMETHODIMP nsWebBrowserPersist::OnProgress(
-    nsIRequest *request, nsISupports *ctxt, uint64_t aProgress,
-    uint64_t aProgressMax)
+    nsIRequest *request, nsISupports *ctxt, int64_t aProgress,
+    int64_t aProgressMax)
 {
     if (!mProgressListener)
     {
         return NS_OK;
     }
 
     // Store the progress of this request
     nsCOMPtr<nsISupports> keyPtr = do_QueryInterface(request);
     OutputData *data = mOutputMap.Get(keyPtr);
     if (data)
     {
-        data->mSelfProgress = int64_t(aProgress);
-        data->mSelfProgressMax = int64_t(aProgressMax);
+        data->mSelfProgress = aProgress;
+        data->mSelfProgressMax = aProgressMax;
     }
     else
     {
         UploadData *upData = mUploadList.Get(keyPtr);
         if (upData)
         {
-            upData->mSelfProgress = int64_t(aProgress);
-            upData->mSelfProgressMax = int64_t(aProgressMax);
+            upData->mSelfProgress = aProgress;
+            upData->mSelfProgressMax = aProgressMax;
         }
     }
 
     // Notify listener of total progress
     CalcTotalProgress();
     if (mProgressListener2)
     {
       mProgressListener2->OnProgressChange64(nullptr, request, aProgress,
--- a/gfx/2d/DataSurfaceHelpers.cpp
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -40,29 +40,29 @@ bool
 SurfaceContainsPoint(SourceSurface* aSurface, const IntPoint& aPoint)
 {
   IntSize size = aSurface->GetSize();
   return aPoint.x >= 0 && aPoint.x < size.width &&
          aPoint.y >= 0 && aPoint.y < size.height;
 }
 
 void
-ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride)
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride)
 {
-  uint32_t* pixel = reinterpret_cast<uint32_t*>(aData);
+  int height = aSize.height, width = aSize.width * 4;
 
-  for (int row = 0; row < aSize.height; ++row) {
-    for (int column = 0; column < aSize.width; ++column) {
+  for (int row = 0; row < height; ++row) {
+    for (int column = 0; column < width; column += 4) {
 #ifdef IS_BIG_ENDIAN
-      pixel[column] |= 0x000000FF;
+      aData[column] = 0xFF;
 #else
-      pixel[column] |= 0xFF000000;
+      aData[column + 3] = 0xFF;
 #endif
     }
-    pixel += (aStride/4);
+    aData += aStride;
   }
 }
 
 void
 CopySurfaceDataToPackedArray(uint8_t* aSrc, uint8_t* aDst, IntSize aSrcSize,
                              int32_t aSrcStride, int32_t aBytesPerPixel)
 {
   MOZ_ASSERT(aBytesPerPixel > 0,
--- a/gfx/2d/DataSurfaceHelpers.h
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -7,17 +7,17 @@
 #define _MOZILLA_GFX_DATASURFACEHELPERS_H
 
 #include "2D.h"
 
 namespace mozilla {
 namespace gfx {
 
 void
-ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, int32_t aStride);
+ConvertBGRXToBGRA(uint8_t* aData, const IntSize &aSize, const int32_t aStride);
 
 /**
  * Copy the pixel data from aSrc and pack it into aDst. aSrcSize, aSrcStride
  * and aBytesPerPixel give the size, stride and bytes per pixel for aSrc's
  * surface. Callers are responsible for making sure that aDst is big enough to
  * contain |aSrcSize.width * aSrcSize.height * aBytesPerPixel| bytes.
  */
 void
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -1103,17 +1103,29 @@ DrawTargetCG::StrokeRect(const Rect &aRe
     CGContextAddRect(cg, RectToCGRect(aRect));
     CGContextReplacePathWithStrokedPath(cg);
     CGRect extents = CGContextGetPathBoundingBox(cg);
     //XXX: should we use EO clip here?
     CGContextClip(cg);
     DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
-    CGContextStrokeRect(cg, RectToCGRect(aRect));
+    // We'd like to use CGContextStrokeRect(cg, RectToCGRect(aRect));
+    // Unfortunately, newer versions of OS X no longer start at the top-left
+    // corner and stroke clockwise as older OS X versions and all the other
+    // Moz2D backends do. (Newer versions start at the top right-hand corner
+    // and stroke counter-clockwise.) For consistency we draw the rect by hand.
+    CGRect rect = RectToCGRect(aRect);
+    CGContextBeginPath(cg);
+    CGContextMoveToPoint(cg, CGRectGetMinX(rect), CGRectGetMinY(rect));
+    CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMinY(rect));
+    CGContextAddLineToPoint(cg, CGRectGetMaxX(rect), CGRectGetMaxY(rect));
+    CGContextAddLineToPoint(cg, CGRectGetMinX(rect), CGRectGetMaxY(rect));
+    CGContextClosePath(cg);
+    CGContextStrokePath(cg);
   }
 
   fixer.Fix(mCg);
   CGContextRestoreGState(mCg);
 }
 
 
 void
--- a/gfx/2d/convolver.cpp
+++ b/gfx/2d/convolver.cpp
@@ -148,16 +148,18 @@ class CircularRowBuffer {
   // The y coordinate of the |next_row_|. This is incremented each time a
   // new row is appended and does not wrap.
   int next_row_coordinate_;
 
   // Buffer used by GetRowAddresses().
   std::vector<unsigned char*> row_addresses_;
 };
 
+}  // namespace
+
 // Convolves horizontally along a single row. The row data is given in
 // |src_data| and continues for the [begin, end) of the filter.
 template<bool has_alpha>
 void ConvolveHorizontally(const unsigned char* src_data,
                           int begin, int end,
                           const ConvolutionFilter1D& filter,
                           unsigned char* out_row) {
   // Loop over each pixel on this row in the output image.
@@ -262,17 +264,70 @@ void ConvolveVertically(const Convolutio
         out_row[byte_offset + A_OFFSET_IDX] = alpha;
     } else {
       // No alpha channel, the image is opaque.
       out_row[byte_offset + A_OFFSET_IDX] = 0xff;
     }
   }
 }
 
-}  // namespace
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+                        int filter_length,
+                        unsigned char* const* source_data_rows,
+                        int width, unsigned char* out_row,
+                        bool has_alpha, bool use_sse2) {
+  int processed = 0;
+
+#if defined(USE_SSE2)
+  // If the binary was not built with SSE2 support, we had to fallback to C version.
+  int simd_width = width & ~3;
+  if (use_sse2 && simd_width) {
+    ConvolveVertically_SSE2(filter_values, filter_length,
+                            source_data_rows, 0, simd_width,
+                            out_row, has_alpha);
+    processed = simd_width;
+  }
+#endif
+    
+  if (width > processed) {
+    if (has_alpha) {
+      ConvolveVertically<true>(filter_values, filter_length, source_data_rows,
+                               processed, width, out_row);
+    } else {
+      ConvolveVertically<false>(filter_values, filter_length, source_data_rows,
+                                processed, width, out_row);
+    }
+  }
+}
+
+void ConvolveHorizontally(const unsigned char* src_data,
+                          const ConvolutionFilter1D& filter,
+                          unsigned char* out_row,
+                          bool has_alpha, bool use_sse2) {
+  int width = filter.num_values();
+  int processed = 0;
+#if defined(USE_SSE2)
+  int simd_width = width & ~3;
+  if (use_sse2 && simd_width) {
+    // SIMD implementation works with 4 pixels at a time.
+    // Therefore we process as much as we can using SSE and then use
+    // C implementation for leftovers
+    ConvolveHorizontally_SSE2(src_data, 0, simd_width, filter, out_row);
+    processed = simd_width;
+  }
+#endif
+
+  if (width > processed) {
+    if (has_alpha) {
+      ConvolveHorizontally<true>(src_data, processed, width, filter, out_row);
+    } else {
+      ConvolveHorizontally<false>(src_data, processed, width, filter, out_row);
+    }
+  }
+}
 
 // ConvolutionFilter1D ---------------------------------------------------------
 
 ConvolutionFilter1D::ConvolutionFilter1D()
     : max_filter_(0) {
 }
 
 ConvolutionFilter1D::~ConvolutionFilter1D() {
@@ -457,30 +512,15 @@ void BGRAConvolve2D(const unsigned char*
     unsigned char* const* rows_to_convolve =
         row_buffer.GetRowAddresses(&first_row_in_circular_buffer);
 
     // Now compute the start of the subset of those rows that the filter
     // needs.
     unsigned char* const* first_row_for_filter =
         &rows_to_convolve[filter_offset - first_row_in_circular_buffer];
 
-    int processed = 0;
-#if defined(USE_SSE2)
-    int simd_width = pixel_width & ~3;
-    if (use_sse2 && simd_width) {
-        ConvolveVertically_SSE2(filter_values, filter_length, first_row_for_filter,
-                                0, simd_width, cur_output_row, source_has_alpha);
-        processed = simd_width;
-    }
-#endif
-    if (source_has_alpha) {
-      ConvolveVertically<true>(filter_values, filter_length,
-                               first_row_for_filter,
-                               processed, pixel_width, cur_output_row);
-    } else {
-      ConvolveVertically<false>(filter_values, filter_length,
-                               first_row_for_filter,
-                               processed, pixel_width, cur_output_row);
-    }
+    ConvolveVertically(filter_values, filter_length,
+                       first_row_for_filter, pixel_width,
+                       cur_output_row, source_has_alpha, use_sse2);
   }
 }
 
 }  // namespace skia
--- a/gfx/2d/convolver.h
+++ b/gfx/2d/convolver.h
@@ -181,11 +181,22 @@ class ConvolutionFilter1D {
 void BGRAConvolve2D(const unsigned char* source_data,
                     int source_byte_row_stride,
                     bool source_has_alpha,
                     const ConvolutionFilter1D& xfilter,
                     const ConvolutionFilter1D& yfilter,
                     int output_byte_row_stride,
                     unsigned char* output);
 
+void ConvolveHorizontally(const unsigned char* src_data,
+                          const ConvolutionFilter1D& filter,
+                          unsigned char* out_row,
+                          bool has_alpha, bool use_sse2);
+
+void ConvolveVertically(const ConvolutionFilter1D::Fixed* filter_values,
+                        int filter_length,
+                        unsigned char* const* source_data_rows,
+                        int pixel_width, unsigned char* out_row,
+                        bool has_alpha, bool use_sse2);
+
 }  // namespace skia
 
 #endif  // SKIA_EXT_CONVOLVER_H_
--- a/gfx/2d/image_operations.cpp
+++ b/gfx/2d/image_operations.cpp
@@ -39,206 +39,49 @@
 #include "convolver.h"
 #include "skia/SkColorPriv.h"
 #include "skia/SkBitmap.h"
 #include "skia/SkRect.h"
 #include "skia/SkFontHost.h"
 
 namespace skia {
 
-namespace {
-
-// Returns the ceiling/floor as an integer.
-inline int CeilInt(float val) {
-  return static_cast<int>(ceil(val));
-}
-inline int FloorInt(float val) {
-  return static_cast<int>(floor(val));
-}
-
-// Filter function computation -------------------------------------------------
-
-// Evaluates the box filter, which goes from -0.5 to +0.5.
-float EvalBox(float x) {
-  return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
-}
-
-// Evaluates the Lanczos filter of the given filter size window for the given
-// position.
-//
-// |filter_size| is the width of the filter (the "window"), outside of which
-// the value of the function is 0. Inside of the window, the value is the
-// normalized sinc function:
-//   lanczos(x) = sinc(x) * sinc(x / filter_size);
-// where
-//   sinc(x) = sin(pi*x) / (pi*x);
-float EvalLanczos(int filter_size, float x) {
-  if (x <= -filter_size || x >= filter_size)
-    return 0.0f;  // Outside of the window.
-  if (x > -std::numeric_limits<float>::epsilon() &&
-      x < std::numeric_limits<float>::epsilon())
-    return 1.0f;  // Special case the discontinuity at the origin.
-  float xpi = x * static_cast<float>(M_PI);
-  return (sin(xpi) / xpi) *  // sinc(x)
-          sin(xpi / filter_size) / (xpi / filter_size);  // sinc(x/filter_size)
-}
-
-// Evaluates the Hamming filter of the given filter size window for the given
-// position.
-//
-// The filter covers [-filter_size, +filter_size]. Outside of this window
-// the value of the function is 0. Inside of the window, the value is sinus
-// cardinal multiplied by a recentered Hamming function. The traditional
-// Hamming formula for a window of size N and n ranging in [0, N-1] is:
-//   hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
-// In our case we want the function centered for x == 0 and at its minimum
-// on both ends of the window (x == +/- filter_size), hence the adjusted
-// formula:
-//   hamming(x) = (0.54 -
-//                 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
-//              = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
-//              = 0.54 + 0.46 * cos(pi * x / filter_size)
-float EvalHamming(int filter_size, float x) {
-  if (x <= -filter_size || x >= filter_size)
-    return 0.0f;  // Outside of the window.
-  if (x > -std::numeric_limits<float>::epsilon() &&
-      x < std::numeric_limits<float>::epsilon())
-    return 1.0f;  // Special case the sinc discontinuity at the origin.
-  const float xpi = x * static_cast<float>(M_PI);
-
-  return ((sin(xpi) / xpi) *  // sinc(x)
-          (0.54f + 0.46f * cos(xpi / filter_size)));  // hamming(x)
-}
-
-// ResizeFilter ----------------------------------------------------------------
-
-// Encapsulates computation and storage of the filters required for one complete
-// resize operation.
-class ResizeFilter {
- public:
-  ResizeFilter(ImageOperations::ResizeMethod method,
-               int src_full_width, int src_full_height,
-               int dest_width, int dest_height,
-               const SkIRect& dest_subset);
-
-  // Returns the filled filter values.
-  const ConvolutionFilter1D& x_filter() { return x_filter_; }
-  const ConvolutionFilter1D& y_filter() { return y_filter_; }
-
- private:
-  // Returns the number of pixels that the filer spans, in filter space (the
-  // destination image).
-  float GetFilterSupport(float scale) {
-    switch (method_) {
-      case ImageOperations::RESIZE_BOX:
-        // The box filter just scales with the image scaling.
-        return 0.5f;  // Only want one side of the filter = /2.
-      case ImageOperations::RESIZE_HAMMING1:
-        // The Hamming filter takes as much space in the source image in
-        // each direction as the size of the window = 1 for Hamming1.
-        return 1.0f;
-      case ImageOperations::RESIZE_LANCZOS2:
-        // The Lanczos filter takes as much space in the source image in
-        // each direction as the size of the window = 2 for Lanczos2.
-        return 2.0f;
-      case ImageOperations::RESIZE_LANCZOS3:
-        // The Lanczos filter takes as much space in the source image in
-        // each direction as the size of the window = 3 for Lanczos3.
-        return 3.0f;
-      default:
-        return 1.0f;
-    }
-  }
-
-  // Computes one set of filters either horizontally or vertically. The caller
-  // will specify the "min" and "max" rather than the bottom/top and
-  // right/bottom so that the same code can be re-used in each dimension.
-  //
-  // |src_depend_lo| and |src_depend_size| gives the range for the source
-  // depend rectangle (horizontally or vertically at the caller's discretion
-  // -- see above for what this means).
-  //
-  // Likewise, the range of destination values to compute and the scale factor
-  // for the transform is also specified.
-  void ComputeFilters(int src_size,
-                      int dest_subset_lo, int dest_subset_size,
-                      float scale, ConvolutionFilter1D* output);
-
-  // Computes the filter value given the coordinate in filter space.
-  inline float ComputeFilter(float pos) {
-    switch (method_) {
-      case ImageOperations::RESIZE_BOX:
-        return EvalBox(pos);
-      case ImageOperations::RESIZE_HAMMING1:
-        return EvalHamming(1, pos);
-      case ImageOperations::RESIZE_LANCZOS2:
-        return EvalLanczos(2, pos);
-      case ImageOperations::RESIZE_LANCZOS3:
-        return EvalLanczos(3, pos);
-      default:
-        return 0;
-    }
-  }
-
-  ImageOperations::ResizeMethod method_;
-
-  // Subset of scaled destination bitmap to compute.
-  SkIRect out_bounds_;
-
-  ConvolutionFilter1D x_filter_;
-  ConvolutionFilter1D y_filter_;
-
-  DISALLOW_COPY_AND_ASSIGN(ResizeFilter);
-};
-
-ResizeFilter::ResizeFilter(ImageOperations::ResizeMethod method,
-                           int src_full_width, int src_full_height,
-                           int dest_width, int dest_height,
-                           const SkIRect& dest_subset)
-    : method_(method),
-      out_bounds_(dest_subset) {
-  // method_ will only ever refer to an "algorithm method".
-  SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
-           (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
-
-  float scale_x = static_cast<float>(dest_width) /
-                  static_cast<float>(src_full_width);
-  float scale_y = static_cast<float>(dest_height) /
-                  static_cast<float>(src_full_height);
-
-  ComputeFilters(src_full_width, dest_subset.fLeft, dest_subset.width(),
-                 scale_x, &x_filter_);
-  ComputeFilters(src_full_height, dest_subset.fTop, dest_subset.height(),
-                 scale_y, &y_filter_);
-}
+namespace resize {
 
 // TODO(egouriou): Take advantage of periods in the convolution.
 // Practical resizing filters are periodic outside of the border area.
 // For Lanczos, a scaling by a (reduced) factor of p/q (q pixels in the
 // source become p pixels in the destination) will have a period of p.
 // A nice consequence is a period of 1 when downscaling by an integral
 // factor. Downscaling from typical display resolutions is also bound
 // to produce interesting periods as those are chosen to have multiple
 // small factors.
 // Small periods reduce computational load and improve cache usage if
 // the coefficients can be shared. For periods of 1 we can consider
 // loading the factors only once outside the borders.
-void ResizeFilter::ComputeFilters(int src_size,
-                                  int dest_subset_lo, int dest_subset_size,
-                                  float scale, ConvolutionFilter1D* output) {
+void ComputeFilters(ImageOperations::ResizeMethod method,
+                    int src_size, int dst_size,
+                    int dest_subset_lo, int dest_subset_size,
+                    ConvolutionFilter1D* output) {
+  // method_ will only ever refer to an "algorithm method".
+  SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
+           (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
+
+  float scale = static_cast<float>(dst_size) / static_cast<float>(src_size);
+ 
   int dest_subset_hi = dest_subset_lo + dest_subset_size;  // [lo, hi)
 
   // When we're doing a magnification, the scale will be larger than one. This
   // means the destination pixels are much smaller than the source pixels, and
   // that the range covered by the filter won't necessarily cover any source
   // pixel boundaries. Therefore, we use these clamped values (max of 1) for
   // some computations.
   float clamped_scale = std::min(1.0f, scale);
 
-  float src_support = GetFilterSupport(clamped_scale) / clamped_scale;
+  float src_support = GetFilterSupport(method, clamped_scale) / clamped_scale;
 
   // Speed up the divisions below by turning them into multiplies.
   float inv_scale = 1.0f / scale;
 
   StackVector<float, 64> filter_values;
   StackVector<int16_t, 64> fixed_filter_values;
 
   // Loop over all pixels in the output range. We will generate one set of
@@ -276,17 +119,17 @@ void ResizeFilter::ComputeFilters(int sr
       // is at (2.5, 2.5).
       float src_filter_dist =
            ((static_cast<float>(cur_filter_pixel) + 0.5f) - src_pixel);
 
       // Since the filter really exists in dest space, map it there.
       float dest_filter_dist = src_filter_dist * clamped_scale;
 
       // Compute the filter value at that location.
-      float filter_value = ComputeFilter(dest_filter_dist);
+      float filter_value = ComputeFilter(method, dest_filter_dist);
       filter_values->push_back(filter_value);
 
       filter_sum += filter_value;
     }
 
     // The filter must be normalized so that we don't affect the brightness of
     // the image. Convert to normalized fixed point.
     int16_t fixed_sum = 0;
@@ -307,16 +150,18 @@ void ResizeFilter::ComputeFilters(int sr
     // Now it's ready to go.
     output->AddFilter(src_begin, &fixed_filter_values[0],
                       static_cast<int>(fixed_filter_values->size()));
   }
 
   output->PaddingForSIMD(8);
 }
 
+}
+
 ImageOperations::ResizeMethod ResizeMethodToAlgorithmMethod(
     ImageOperations::ResizeMethod method) {
   // Convert any "Quality Method" into an "Algorithm Method"
   if (method >= ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD &&
       method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD) {
     return method;
   }
   // The call to ImageOperationsGtv::Resize() above took care of
@@ -336,18 +181,16 @@ ImageOperations::ResizeMethod ResizeMeth
       // an acceptable trade-off between quality and speed.
     case ImageOperations::RESIZE_BETTER:
       return ImageOperations::RESIZE_HAMMING1;
     default:
       return ImageOperations::RESIZE_LANCZOS3;
   }
 }
 
-}  // namespace
-
 // Resize ----------------------------------------------------------------------
 
 // static
 SkBitmap ImageOperations::Resize(const SkBitmap& source,
                                  ResizeMethod method,
                                  int dest_width, int dest_height,
                                  const SkIRect& dest_subset,
                                  void* dest_pixels /* = nullptr */) {
@@ -491,18 +334,21 @@ SkBitmap ImageOperations::ResizeBasic(co
   // Check that we deal with an "algorithm methods" from this point onward.
   SkASSERT((ImageOperations::RESIZE_FIRST_ALGORITHM_METHOD <= method) &&
            (method <= ImageOperations::RESIZE_LAST_ALGORITHM_METHOD));
 
   SkAutoLockPixels locker(source);
   if (!source.readyToDraw())
       return SkBitmap();
 
-  ResizeFilter filter(method, source.width(), source.height(),
-                      dest_width, dest_height, dest_subset);
+  ConvolutionFilter1D x_filter;
+  ConvolutionFilter1D y_filter;
+
+  resize::ComputeFilters(method, source.width(), dest_width, dest_subset.fLeft, dest_subset.width(), &x_filter);
+  resize::ComputeFilters(method, source.height(), dest_height, dest_subset.fTop, dest_subset.height(), &y_filter);
 
   // Get a source bitmap encompassing this touched area. We construct the
   // offsets and row strides such that it looks like a new bitmap, while
   // referring to the old data.
   const uint8_t* source_subset =
       reinterpret_cast<const uint8_t*>(source.getPixels());
 
   // Convolve into the result.
@@ -517,17 +363,17 @@ SkBitmap ImageOperations::ResizeBasic(co
   } else {
     result.allocPixels(info);
   }
 
   if (!result.readyToDraw())
     return SkBitmap();
 
   BGRAConvolve2D(source_subset, static_cast<int>(source.rowBytes()),
-                 !source.isOpaque(), filter.x_filter(), filter.y_filter(),
+                 !source.isOpaque(), x_filter, y_filter,
                  static_cast<int>(result.rowBytes()),
                  static_cast<unsigned char*>(result.getPixels()));
 
   // Preserve the "opaque" flag for use as an optimization later.
   result.setAlphaType(source.alphaType());
 
   return result;
 }
--- a/gfx/2d/image_operations.h
+++ b/gfx/2d/image_operations.h
@@ -26,16 +26,18 @@
 // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 // SUCH DAMAGE.
 
 #ifndef SKIA_EXT_IMAGE_OPERATIONS_H_
 #define SKIA_EXT_IMAGE_OPERATIONS_H_
 
 #include "skia/SkTypes.h"
 #include "Types.h"
+#include "convolver.h"
+#include "skia/SkRect.h"
 
 class SkBitmap;
 struct SkIRect;
 
 namespace skia {
 
 class ImageOperations {
  public:
@@ -147,11 +149,137 @@ class ImageOperations {
                               void* dest_pixels = nullptr);
 
   // Subpixel renderer.
   static SkBitmap ResizeSubpixel(const SkBitmap& source,
                                  int dest_width, int dest_height,
                                  const SkIRect& dest_subset);
 };
 
+// Returns the ceiling/floor as an integer.
+inline int CeilInt(float val) {
+  return static_cast<int>(ceil(val));
+}
+inline int FloorInt(float val) {
+  return static_cast<int>(floor(val));
+}
+
+// Filter function computation -------------------------------------------------
+
+// Evaluates the box filter, which goes from -0.5 to +0.5.
+inline float EvalBox(float x) {
+  return (x >= -0.5f && x < 0.5f) ? 1.0f : 0.0f;
+}
+
+// Evaluates the Lanczos filter of the given filter size window for the given
+// position.
+//
+// |filter_size| is the width of the filter (the "window"), outside of which
+// the value of the function is 0. Inside of the window, the value is the
+// normalized sinc function:
+//   lanczos(x) = sinc(x) * sinc(x / filter_size);
+// where
+//   sinc(x) = sin(pi*x) / (pi*x);
+inline float EvalLanczos(int filter_size, float x) {
+  if (x <= -filter_size || x >= filter_size)
+    return 0.0f;  // Outside of the window.
+  if (x > -std::numeric_limits<float>::epsilon() &&
+      x < std::numeric_limits<float>::epsilon())
+    return 1.0f;  // Special case the discontinuity at the origin.
+  float xpi = x * static_cast<float>(M_PI);
+  return (sin(xpi) / xpi) *  // sinc(x)
+          sin(xpi / filter_size) / (xpi / filter_size);  // sinc(x/filter_size)
+}
+
+// Evaluates the Hamming filter of the given filter size window for the given
+// position.
+//
+// The filter covers [-filter_size, +filter_size]. Outside of this window
+// the value of the function is 0. Inside of the window, the value is sinus
+// cardinal multiplied by a recentered Hamming function. The traditional
+// Hamming formula for a window of size N and n ranging in [0, N-1] is:
+//   hamming(n) = 0.54 - 0.46 * cos(2 * pi * n / (N-1)))
+// In our case we want the function centered for x == 0 and at its minimum
+// on both ends of the window (x == +/- filter_size), hence the adjusted
+// formula:
+//   hamming(x) = (0.54 -
+//                 0.46 * cos(2 * pi * (x - filter_size)/ (2 * filter_size)))
+//              = 0.54 - 0.46 * cos(pi * x / filter_size - pi)
+//              = 0.54 + 0.46 * cos(pi * x / filter_size)
+inline float EvalHamming(int filter_size, float x) {
+  if (x <= -filter_size || x >= filter_size)
+    return 0.0f;  // Outside of the window.
+  if (x > -std::numeric_limits<float>::epsilon() &&
+      x < std::numeric_limits<float>::epsilon())
+    return 1.0f;  // Special case the sinc discontinuity at the origin.
+  const float xpi = x * static_cast<float>(M_PI);
+
+  return ((sin(xpi) / xpi) *  // sinc(x)
+          (0.54f + 0.46f * cos(xpi / filter_size)));  // hamming(x)
+}
+
+// ResizeFilter ----------------------------------------------------------------
+
+// Encapsulates computation and storage of the filters required for one complete
+// resize operation.
+
+namespace resize {
+
+  // Returns the number of pixels that the filer spans, in filter space (the
+  // destination image).
+  inline float GetFilterSupport(ImageOperations::ResizeMethod method,
+                                float scale) {
+    switch (method) {
+      case ImageOperations::RESIZE_BOX:
+        // The box filter just scales with the image scaling.
+        return 0.5f;  // Only want one side of the filter = /2.
+      case ImageOperations::RESIZE_HAMMING1:
+        // The Hamming filter takes as much space in the source image in
+        // each direction as the size of the window = 1 for Hamming1.
+        return 1.0f;
+      case ImageOperations::RESIZE_LANCZOS2:
+        // The Lanczos filter takes as much space in the source image in
+        // each direction as the size of the window = 2 for Lanczos2.
+        return 2.0f;
+      case ImageOperations::RESIZE_LANCZOS3:
+        // The Lanczos filter takes as much space in the source image in
+        // each direction as the size of the window = 3 for Lanczos3.
+        return 3.0f;
+      default:
+        return 1.0f;
+    }
+  }
+
+  // Computes one set of filters either horizontally or vertically. The caller
+  // will specify the "min" and "max" rather than the bottom/top and
+  // right/bottom so that the same code can be re-used in each dimension.
+  //
+  // |src_depend_lo| and |src_depend_size| gives the range for the source
+  // depend rectangle (horizontally or vertically at the caller's discretion
+  // -- see above for what this means).
+  //
+  // Likewise, the range of destination values to compute and the scale factor
+  // for the transform is also specified.
+  void ComputeFilters(ImageOperations::ResizeMethod method,
+                      int src_size, int dst_size,
+                      int dest_subset_lo, int dest_subset_size,
+                      ConvolutionFilter1D* output);
+
+  // Computes the filter value given the coordinate in filter space.
+  inline float ComputeFilter(ImageOperations::ResizeMethod method, float pos) {
+    switch (method) {
+      case ImageOperations::RESIZE_BOX:
+        return EvalBox(pos);
+      case ImageOperations::RESIZE_HAMMING1:
+        return EvalHamming(1, pos);
+      case ImageOperations::RESIZE_LANCZOS2:
+        return EvalLanczos(2, pos);
+      case ImageOperations::RESIZE_LANCZOS3:
+        return EvalLanczos(3, pos);
+      default:
+        return 0;
+    }
+  }
+}
+
 }  // namespace skia
 
 #endif  // SKIA_EXT_IMAGE_OPERATIONS_H_
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -287,16 +287,20 @@ APZCTreeManager::AttachNodeToTree(HitTes
     aNode->MakeRoot();
   }
 }
 
 static EventRegions
 GetEventRegions(const LayerMetricsWrapper& aLayer)
 {
   if (gfxPrefs::LayoutEventRegionsEnabled()) {
+    if (aLayer.IsScrollInfoLayer()) {
+      return EventRegions(nsIntRegion(ParentLayerIntRect::ToUntyped(
+        RoundedToInt(aLayer.Metrics().mCompositionBounds))));
+    }
     return aLayer.GetEventRegions();
   }
   return EventRegions(aLayer.GetVisibleRegion());
 }
 
 already_AddRefed<HitTestingTreeNode>
 APZCTreeManager::RecycleOrCreateNode(TreeBuildingState& aState,
                                      AsyncPanZoomController* aApzc)
@@ -324,19 +328,16 @@ APZCTreeManager::PrepareNodeForLayer(con
                                      HitTestingTreeNode* aParent,
                                      HitTestingTreeNode* aNextSibling,
                                      TreeBuildingState& aState)
 {
   bool needsApzc = true;
   if (!aMetrics.IsScrollable()) {
     needsApzc = false;
   }
-  if (gfxPrefs::LayoutEventRegionsEnabled() && aLayer.IsScrollInfoLayer()) {
-    needsApzc = false;
-  }
 
   const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
   if (!(state && state->mController.get())) {
     needsApzc = false;
   }
 
   nsRefPtr<HitTestingTreeNode> node = nullptr;
   if (!needsApzc) {
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -38,17 +38,17 @@ class TextureClientX11 : public TextureC
   virtual bool IsLocked() const MOZ_OVERRIDE { return mLocked; }
 
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags flags) MOZ_OVERRIDE;
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
-  virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
+  virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
   virtual TemporaryRef<TextureClient>
   CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
                 TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
 
  private:
--- a/gfx/layers/basic/X11TextureSourceBasic.h
+++ b/gfx/layers/basic/X11TextureSourceBasic.h
@@ -28,17 +28,17 @@ public:
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE;
 
   virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) MOZ_OVERRIDE;
 
   virtual void DeallocateDeviceData() MOZ_OVERRIDE { }
 
-  virtual void SetCompositor(Compositor* aCompositor);
+  virtual void SetCompositor(Compositor* aCompositor) MOZ_OVERRIDE;
 
   static gfx::SurfaceFormat ContentTypeToSurfaceFormat(gfxContentType aType);
 
 protected:
   RefPtr<BasicCompositor> mCompositor;
   RefPtr<gfxXlibSurface> mSurface;
   RefPtr<gfx::SourceSurface> mSourceSurface;
 };
--- a/gfx/layers/composite/X11TextureHost.h
+++ b/gfx/layers/composite/X11TextureHost.h
@@ -36,17 +36,17 @@ public:
   }
 
   virtual TemporaryRef<gfx::DataSourceSurface> GetAsSurface() MOZ_OVERRIDE
   {
     return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
   }
 
 #ifdef MOZ_LAYERS_HAVE_LOG
-  virtual const char* Name() { return "X11TextureHost"; }
+  virtual const char* Name() MOZ_OVERRIDE { return "X11TextureHost"; }
 #endif
 
 protected:
   RefPtr<Compositor> mCompositor;
   RefPtr<TextureSource> mTextureSource;
   RefPtr<gfxXlibSurface> mSurface;
 };
 
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -91,16 +91,17 @@ CompositorChild::Create(Transport* aTran
   // We release this ref in ActorDestroy().
   sCompositor = child.forget().take();
 
   int32_t width;
   int32_t height;
   sCompositor->SendGetTileSize(&width, &height);
   gfxPlatform::GetPlatform()->SetTileSize(width, height);
 
+  // We release this ref in ActorDestroy().
   return sCompositor;
 }
 
 /*static*/ CompositorChild*
 CompositorChild::Get()
 {
   // This is only expected to be used in child processes.
   MOZ_ASSERT(XRE_GetProcessType() != GeckoProcessType_Default);
@@ -175,23 +176,24 @@ CompositorChild::ActorDestroy(ActorDestr
   // Due to poor lifetime management of gralloc (and possibly shmems) we will
   // crash at some point in the future when we get destroyed due to abnormal
   // shutdown. Its better just to crash here. On desktop though, we have a chance
   // of recovering.
   if (aWhy == AbnormalShutdown) {
     NS_RUNTIMEABORT("ActorDestroy by IPC channel failure at CompositorChild");
   }
 #endif
-
+  if (sCompositor) {
+    sCompositor->Release();
+    sCompositor = nullptr;
+  }
   // We don't want to release the ref to sCompositor here, during
   // cleanup, because that will cause it to be deleted while it's
   // still being used.  So defer the deletion to after it's not in
   // use.
-  sCompositor = nullptr;
-
   MessageLoop::current()->PostTask(
     FROM_HERE,
     NewRunnableMethod(this, &CompositorChild::Release));
 }
 
 bool
 CompositorChild::RecvSharedCompositorFrameMetrics(
     const mozilla::ipc::SharedMemoryBasic::Handle& metrics,
--- a/gfx/tests/gtest/TestCompositor.cpp
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -28,21 +28,21 @@ using namespace mozilla::gl;
 
 class MockWidget : public nsBaseWidget
 {
 public:
   MockWidget() {}
 
   NS_DECL_ISUPPORTS_INHERITED
 
-  NS_IMETHOD              GetClientBounds(nsIntRect &aRect) {
+  NS_IMETHOD              GetClientBounds(nsIntRect &aRect) MOZ_OVERRIDE {
     aRect = nsIntRect(0, 0, gCompWidth, gCompHeight);
     return NS_OK;
   }
-  NS_IMETHOD              GetBounds(nsIntRect &aRect) { return GetClientBounds(aRect); }
+  NS_IMETHOD              GetBounds(nsIntRect &aRect) MOZ_OVERRIDE { return GetClientBounds(aRect); }
 
   void* GetNativeData(uint32_t aDataType) MOZ_OVERRIDE {
     if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
       mozilla::gl::SurfaceCaps caps = mozilla::gl::SurfaceCaps::ForRGB();
       caps.preserve = false;
       caps.bpp16 = false;
       nsRefPtr<GLContext> context = GLContextProvider::CreateOffscreen(
         gfxIntSize(gCompWidth, gCompHeight), caps);
@@ -50,40 +50,40 @@ public:
     }
     return nullptr;
   }
 
   NS_IMETHOD              Create(nsIWidget *aParent,
                                  nsNativeWidget aNativeParent,
                                  const nsIntRect &aRect,
                                  nsDeviceContext *aContext,
-                                 nsWidgetInitData *aInitData = nullptr) { return NS_OK; }
-  NS_IMETHOD              Show(bool aState) { return NS_OK; }
-  virtual bool            IsVisible() const { return true; }
+                                 nsWidgetInitData *aInitData = nullptr) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              Show(bool aState) MOZ_OVERRIDE { return NS_OK; }
+  virtual bool            IsVisible() const MOZ_OVERRIDE { return true; }
   NS_IMETHOD              ConstrainPosition(bool aAllowSlop,
-                                            int32_t *aX, int32_t *aY) { return NS_OK; }
-  NS_IMETHOD              Move(double aX, double aY) { return NS_OK; }
-  NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+                                            int32_t *aX, int32_t *aY) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              Move(double aX, double aY) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              Resize(double aWidth, double aHeight, bool aRepaint) MOZ_OVERRIDE { return NS_OK; }
   NS_IMETHOD              Resize(double aX, double aY,
-                                 double aWidth, double aHeight, bool aRepaint) { return NS_OK; }
+                                 double aWidth, double aHeight, bool aRepaint) MOZ_OVERRIDE { return NS_OK; }
 
-  NS_IMETHOD              Enable(bool aState) { return NS_OK; }
-  virtual bool            IsEnabled() const { return true; }
-  NS_IMETHOD              SetFocus(bool aRaise) { return NS_OK; }
-  virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations) { return NS_OK; }
-  NS_IMETHOD              Invalidate(const nsIntRect &aRect) { return NS_OK; }
-  NS_IMETHOD              SetTitle(const nsAString& title) { return NS_OK; }
-  virtual nsIntPoint      WidgetToScreenOffset() { return nsIntPoint(0, 0); }
+  NS_IMETHOD              Enable(bool aState) MOZ_OVERRIDE { return NS_OK; }
+  virtual bool            IsEnabled() const MOZ_OVERRIDE { return true; }
+  NS_IMETHOD              SetFocus(bool aRaise) MOZ_OVERRIDE { return NS_OK; }
+  virtual nsresult        ConfigureChildren(const nsTArray<Configuration>& aConfigurations) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              Invalidate(const nsIntRect &aRect) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              SetTitle(const nsAString& title) MOZ_OVERRIDE { return NS_OK; }
+  virtual nsIntPoint      WidgetToScreenOffset() MOZ_OVERRIDE { return nsIntPoint(0, 0); }
   NS_IMETHOD              DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
-                                        nsEventStatus& aStatus) { return NS_OK; }
-  NS_IMETHOD              CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) { return NS_OK; }
+                                        nsEventStatus& aStatus) MOZ_OVERRIDE { return NS_OK; }
+  NS_IMETHOD              CaptureRollupEvents(nsIRollupListener * aListener, bool aDoCapture) MOZ_OVERRIDE { return NS_OK; }
   NS_IMETHOD_(void)       SetInputContext(const InputContext& aContext,
-                                          const InputContextAction& aAction) {}
-  NS_IMETHOD_(InputContext) GetInputContext() { abort(); }
-  NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) { return NS_OK; }
+                                          const InputContextAction& aAction) MOZ_OVERRIDE {}
+  NS_IMETHOD_(InputContext) GetInputContext() MOZ_OVERRIDE { abort(); }
+  NS_IMETHOD              ReparentNativeWidget(nsIWidget* aNewParent) MOZ_OVERRIDE { return NS_OK; }
 private:
   ~MockWidget() {}
 };
 
 NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
 
 struct LayerManagerData {
   RefPtr<MockWidget> mWidget;
--- a/gfx/thebes/SoftwareVsyncSource.cpp
+++ b/gfx/thebes/SoftwareVsyncSource.cpp
@@ -17,46 +17,56 @@ SoftwareVsyncSource::~SoftwareVsyncSourc
 {
   MOZ_ASSERT(NS_IsMainThread());
   // Ensure we disable vsync on the main thread here
   mGlobalDisplay->DisableVsync();
   mGlobalDisplay = nullptr;
 }
 
 SoftwareDisplay::SoftwareDisplay()
-  : mCurrentTaskMonitor("SoftwareVsyncCurrentTaskMonitor")
+  : mVsyncEnabled(false)
+  , mCurrentTaskMonitor("SoftwareVsyncCurrentTaskMonitor")
 {
   // Mimic 60 fps
   MOZ_ASSERT(NS_IsMainThread());
   const double rate = 1000 / 60.0;
   mVsyncRate = mozilla::TimeDuration::FromMilliseconds(rate);
   mVsyncThread = new base::Thread("SoftwareVsyncThread");
-  EnableVsync();
 }
 
 void
 SoftwareDisplay::EnableVsync()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
-  mVsyncEnabled = true;
-  MOZ_ASSERT(!mVsyncThread->IsRunning());
-  MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread");
-  mCurrentVsyncTask = NewRunnableMethod(this,
-      &SoftwareDisplay::NotifyVsync,
-      mozilla::TimeStamp::Now());
-  mVsyncThread->message_loop()->PostTask(FROM_HERE, mCurrentVsyncTask);
+  if (IsVsyncEnabled()) {
+    return;
+  }
+
+  { // scope lock
+    mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
+    mVsyncEnabled = true;
+    MOZ_ASSERT(!mVsyncThread->IsRunning());
+    MOZ_RELEASE_ASSERT(mVsyncThread->Start(), "Could not start software vsync thread");
+    mCurrentVsyncTask = NewRunnableMethod(this,
+        &SoftwareDisplay::NotifyVsync,
+        mozilla::TimeStamp::Now());
+    mVsyncThread->message_loop()->PostTask(FROM_HERE, mCurrentVsyncTask);
+  }
 }
 
 void
 SoftwareDisplay::DisableVsync()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  if (!IsVsyncEnabled()) {
+    return;
+  }
+
   MOZ_ASSERT(mVsyncThread->IsRunning());
-  { // Scope lock
+  { // scope lock
     mozilla::MonitorAutoLock lock(mCurrentTaskMonitor);
     mVsyncEnabled = false;
     if (mCurrentVsyncTask) {
       mCurrentVsyncTask->Cancel();
       mCurrentVsyncTask = nullptr;
     }
   }
   mVsyncThread->Stop();
--- a/gfx/thebes/VsyncSource.cpp
+++ b/gfx/thebes/VsyncSource.cpp
@@ -36,16 +36,17 @@ VsyncSource::GetRefreshTimerVsyncDispatc
 {
   MOZ_ASSERT(XRE_IsParentProcess());
   // See also AddCompositorVsyncDispatcher().
   return GetGlobalDisplay().GetRefreshTimerVsyncDispatcher();
 }
 
 VsyncSource::Display::Display()
   : mDispatcherLock("display dispatcher lock")
+  , mRefreshTimerNeedsVsync(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mRefreshTimerVsyncDispatcher = new RefreshTimerVsyncDispatcher();
 }
 
 VsyncSource::Display::~Display()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -67,29 +68,67 @@ VsyncSource::Display::NotifyVsync(TimeSt
   mRefreshTimerVsyncDispatcher->NotifyVsync(aVsyncTimestamp);
 }
 
 void
 VsyncSource::Display::AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCompositorVsyncDispatcher);
-  MutexAutoLock lock(mDispatcherLock);
-  if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
-    mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
+  { // scope lock
+    MutexAutoLock lock(mDispatcherLock);
+    if (!mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
+      mCompositorVsyncDispatchers.AppendElement(aCompositorVsyncDispatcher);
+    }
   }
+  UpdateVsyncStatus();
 }
 
 void
 VsyncSource::Display::RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCompositorVsyncDispatcher);
-  MutexAutoLock lock(mDispatcherLock);
-  mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
+  { // Scope lock
+    MutexAutoLock lock(mDispatcherLock);
+    if (mCompositorVsyncDispatchers.Contains(aCompositorVsyncDispatcher)) {
+      mCompositorVsyncDispatchers.RemoveElement(aCompositorVsyncDispatcher);
+    }
+  }
+  UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::NotifyRefreshTimerVsyncStatus(bool aEnable)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mRefreshTimerNeedsVsync = aEnable;
+  UpdateVsyncStatus();
+}
+
+void
+VsyncSource::Display::UpdateVsyncStatus()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // WARNING: This function SHOULD NOT BE CALLED WHILE HOLDING LOCKS
+  // NotifyVsync grabs a lock to dispatch vsync events
+  // When disabling vsync, we wait for the underlying thread to stop on some platforms
+  // We can deadlock if we wait for the underlying vsync thread to stop
+  // while the vsync thread is in NotifyVsync.
+  bool enableVsync = false;
+  { // scope lock
+    MutexAutoLock lock(mDispatcherLock);
+    enableVsync = !mCompositorVsyncDispatchers.IsEmpty() || mRefreshTimerNeedsVsync;
+  }
+
+  if (enableVsync) {
+    EnableVsync();
+  } else {
+    DisableVsync();
+  }
 }
 
 nsRefPtr<RefreshTimerVsyncDispatcher>
 VsyncSource::Display::GetRefreshTimerVsyncDispatcher()
 {
   return mRefreshTimerVsyncDispatcher;
 }
 
--- a/gfx/thebes/VsyncSource.h
+++ b/gfx/thebes/VsyncSource.h
@@ -44,24 +44,28 @@ public:
       // All platforms should normalize to the vsync that just occured.
       // Large parts of Gecko assume TimeStamps should not be in the future such as animations
       virtual void NotifyVsync(TimeStamp aVsyncTimestamp);
 
       nsRefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
 
       void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
       void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
+      void NotifyRefreshTimerVsyncStatus(bool aEnable);
 
       // These should all only be called on the main thread
       virtual void EnableVsync() = 0;
       virtual void DisableVsync() = 0;
       virtual bool IsVsyncEnabled() = 0;
 
     private:
+      void UpdateVsyncStatus();
+
       Mutex mDispatcherLock;
+      bool mRefreshTimerNeedsVsync;
       nsTArray<nsRefPtr<CompositorVsyncDispatcher>> mCompositorVsyncDispatchers;
       nsRefPtr<RefreshTimerVsyncDispatcher> mRefreshTimerVsyncDispatcher;
   };
 
   void AddCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
   void RemoveCompositorVsyncDispatcher(CompositorVsyncDispatcher* aCompositorVsyncDispatcher);
 
   nsRefPtr<RefreshTimerVsyncDispatcher> GetRefreshTimerVsyncDispatcher();
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxMatrix.h"
 #include "gfx3DMatrix.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/Tools.h"
@@ -568,19 +568,29 @@ gfx3DMatrix::Inverse() const
 
   temp /= det;
   return temp;
 }
 
 gfxPoint
 gfx3DMatrix::Transform(const gfxPoint& point) const
 {
-  Point3D vec3d(point.x, point.y, 0);
-  vec3d = Transform3D(vec3d);
-  return gfxPoint(vec3d.x, vec3d.y);
+  // Note: we don't use Transform3D here because passing point.x/y via
+  // a Point3D would lose precision and cause bugs, e.g. bug 1091709.
+  gfxFloat px = point.x;
+  gfxFloat py = point.y;
+
+  gfxFloat x = px * _11 + py * _21 + _41;
+  gfxFloat y = px * _12 + py * _22 + _42;
+  gfxFloat w = px * _14 + py * _24 + _44;
+
+  x /= w;
+  y /= w;
+
+  return gfxPoint(x, y);
 }
 
 Point3D
 gfx3DMatrix::Transform3D(const Point3D& point) const
 {
   gfxFloat x = point.x * _11 + point.y * _21 + point.z * _31 + _41;
   gfxFloat y = point.x * _12 + point.y * _22 + point.z * _32 + _42;
   gfxFloat z = point.x * _13 + point.y * _23 + point.z * _33 + _43;
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -437,33 +437,38 @@ public:
     return mGlobalDisplay;
   }
 
   class GonkDisplay MOZ_FINAL : public VsyncSource::Display
   {
   public:
     GonkDisplay() : mVsyncEnabled(false)
     {
-      EnableVsync();
     }
 
     ~GonkDisplay()
     {
       DisableVsync();
     }
 
     virtual void EnableVsync() MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
+      if (IsVsyncEnabled()) {
+        return;
+      }
       mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(true);
     }
 
     virtual void DisableVsync() MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
+      if (!IsVsyncEnabled()) {
+        return;
+      }
       mVsyncEnabled = HwcComposer2D::GetInstance()->EnableVsync(false);
     }
 
     virtual bool IsVsyncEnabled() MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
       return mVsyncEnabled;
     }
@@ -479,19 +484,22 @@ private:
   GonkDisplay mGlobalDisplay;
 }; // GonkVsyncSource
 #endif
 
 already_AddRefed<mozilla::gfx::VsyncSource>
 gfxAndroidPlatform::CreateHardwareVsyncSource()
 {
 #ifdef MOZ_WIDGET_GONK
-    nsRefPtr<VsyncSource> vsyncSource = new GonkVsyncSource();
-    if (!vsyncSource->GetGlobalDisplay().IsVsyncEnabled()) {
+    nsRefPtr<GonkVsyncSource> vsyncSource = new GonkVsyncSource();
+    VsyncSource::Display& display = vsyncSource->GetGlobalDisplay();
+    display.EnableVsync();
+    if (!display.IsVsyncEnabled()) {
         NS_WARNING("Error enabling gonk vsync. Falling back to software vsync\n");
         return gfxPlatform::CreateHardwareVsyncSource();
     }
+    display.DisableVsync();
     return vsyncSource.forget();
 #else
     NS_WARNING("Hardware vsync not supported on android yet");
     return nullptr;
 #endif
 }
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -292,34 +292,16 @@ gfxContext::Rectangle(const gfxRect& rec
 
   mPathBuilder->MoveTo(rec.TopLeft());
   mPathBuilder->LineTo(rec.TopRight());
   mPathBuilder->LineTo(rec.BottomRight());
   mPathBuilder->LineTo(rec.BottomLeft());
   mPathBuilder->Close();
 }
 
-void
-gfxContext::DrawSurface(gfxASurface *surface, const gfxSize& size)
-{
-  // Lifetime needs to be limited here since we may wrap surface's data.
-  RefPtr<SourceSurface> surf =
-    gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(mDT, surface);
-
-  if (!surf) {
-    return;
-  }
-
-  Rect rect(0, 0, Float(size.width), Float(size.height));
-  rect.Intersect(Rect(0, 0, Float(surf->GetSize().width), Float(surf->GetSize().height)));
-
-  // XXX - Should fix pixel snapping.
-  mDT->DrawSurface(surf, rect, rect);
-}
-
 // transform stuff
 void
 gfxContext::Multiply(const gfxMatrix& matrix)
 {
   ChangeTransform(ToMatrix(matrix) * mTransform);
 }
 
 void
@@ -607,37 +589,16 @@ gfxContext::Clip()
     EnsurePath();
     mDT->PushClip(mPath);
     AzureState::PushedClip clip = { mPath, Rect(), mTransform };
     CurrentState().pushedClips.AppendElement(clip);
   }
 }
 
 void
-gfxContext::ResetClip()
-{
-  for (int i = mStateStack.Length() - 1; i >= 0; i--) {
-    for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
-      mDT->PopClip();
-    }
-
-    if (mStateStack[i].clipWasReset) {
-      break;
-    }
-  }
-  CurrentState().pushedClips.Clear();
-  CurrentState().clipWasReset = true;
-}
-
-void
-gfxContext::UpdateSurfaceClip()
-{
-}
-
-void
 gfxContext::PopClip()
 {
   MOZ_ASSERT(CurrentState().pushedClips.Length() > 0);
 
   CurrentState().pushedClips.RemoveElementAt(CurrentState().pushedClips.Length() - 1);
   mDT->PopClip();
 }
 
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -317,26 +317,16 @@ public:
      * Shorthand for creating a pattern and calling the pattern-taking
      * variant of Mask.
      */
     void Mask(gfxASurface *surface, const gfxPoint& offset = gfxPoint(0.0, 0.0));
 
     void Mask(mozilla::gfx::SourceSurface *surface, const mozilla::gfx::Point& offset = mozilla::gfx::Point());
 
     /**
-     ** Shortcuts
-     **/
-
-    /**
-     * Creates a new path with a rectangle from 0,0 to size.w,size.h
-     * and calls cairo_fill.
-     */
-    void DrawSurface(gfxASurface *surface, const gfxSize& size);
-
-    /**
      ** Line Properties
      **/
 
     void SetDash(gfxFloat *dashes, int ndash, gfxFloat offset);
     // Return true if dashing is set, false if it's not enabled or the
     // context is in an error state.  |offset| can be nullptr to mean
     // "don't care".
     bool CurrentDash(FallibleTArray<gfxFloat>& dashes, gfxFloat* offset) const;
@@ -435,38 +425,26 @@ public:
 
     /**
      * Clips all further drawing to the current path.
      * This does not consume the current path.
      */
     void Clip();
 
     /**
-     * Undoes any clipping. Further drawings will only be restricted by the
-     * surface dimensions.
-     */
-    void ResetClip();
-
-    /**
      * Helper functions that will create a rect path and call Clip().
      * Any current path will be destroyed by these functions!
      */
     void Clip(const Rect& rect);
     void Clip(const gfxRect& rect); // will clip to a rect
     void Clip(Path* aPath);
 
     void PopClip();
 
     /**
-     * This will ensure that the surface actually has its clip set.
-     * Useful if you are doing native drawing.
-     */
-    void UpdateSurfaceClip();
-
-    /**
      * This will return the current bounds of the clip region in user
      * space.
      */
     gfxRect GetClipExtents();
 
     /**
      * Returns true if the given rectangle is fully contained in the current clip. 
      * This is conservative; it may return false even when the given rectangle is 
--- a/gfx/thebes/gfxPlatformGtk.h
+++ b/gfx/thebes/gfxPlatformGtk.h
@@ -27,57 +27,58 @@ public:
     static gfxPlatformGtk *GetPlatform() {
         return (gfxPlatformGtk*) gfxPlatform::GetPlatform();
     }
 
     virtual already_AddRefed<gfxASurface>
       CreateOffscreenSurface(const IntSize& size,
                              gfxContentType contentType) MOZ_OVERRIDE;
 
-    mozilla::TemporaryRef<mozilla::gfx::ScaledFont>
-      GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont);
+    virtual mozilla::TemporaryRef<mozilla::gfx::ScaledFont>
+      GetScaledFontForFont(mozilla::gfx::DrawTarget* aTarget, gfxFont *aFont) MOZ_OVERRIDE;
 
-    nsresult GetFontList(nsIAtom *aLangGroup,
-                         const nsACString& aGenericFamily,
-                         nsTArray<nsString>& aListOfFonts);
+    virtual nsresult GetFontList(nsIAtom *aLangGroup,
+                                 const nsACString& aGenericFamily,
+                                 nsTArray<nsString>& aListOfFonts) MOZ_OVERRIDE;
 
-    nsresult UpdateFontList();
-
-    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+    virtual nsresult UpdateFontList() MOZ_OVERRIDE;
 
-    gfxFontGroup *CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
-                                  const gfxFontStyle *aStyle,
-                                  gfxUserFontSet *aUserFontSet);
+    virtual nsresult GetStandardFamilyName(const nsAString& aFontName,
+                                           nsAString& aFamilyName) MOZ_OVERRIDE;
+
+    virtual gfxFontGroup* CreateFontGroup(const mozilla::FontFamilyList& aFontFamilyList,
+                                          const gfxFontStyle *aStyle,
+                                          gfxUserFontSet *aUserFontSet) MOZ_OVERRIDE;
 
     /**
      * Look up a local platform font using the full font face name (needed to
      * support @font-face src local() )
      */
     virtual gfxFontEntry* LookupLocalFont(const nsAString& aFontName,
                                           uint16_t aWeight,
                                           int16_t aStretch,
-                                          bool aItalic);
+                                          bool aItalic) MOZ_OVERRIDE;
 
     /**
      * Activate a platform font (needed to support @font-face src url() )
      *
      */
     virtual gfxFontEntry* MakePlatformFont(const nsAString& aFontName,
                                            uint16_t aWeight,
                                            int16_t aStretch,
                                            bool aItalic,
                                            const uint8_t* aFontData,
-                                           uint32_t aLength);
+                                           uint32_t aLength) MOZ_OVERRIDE;
 
     /**
      * Check whether format is supported on a platform or not (if unclear,
      * returns true).
      */
     virtual bool IsFontFormatSupported(nsIURI *aFontURI,
-                                         uint32_t aFormatFlags);
+                                         uint32_t aFormatFlags) MOZ_OVERRIDE;
 
 #if (MOZ_WIDGET_GTK == 2)
     static void SetGdkDrawable(cairo_surface_t *target,
                                GdkDrawable *drawable);
     static GdkDrawable *GetGdkDrawable(cairo_surface_t *target);
 #endif
 
     static int32_t GetDPI();
@@ -99,24 +100,25 @@ public:
         // since GTK2 theme rendering still requires xlib surfaces per se.
 #if (MOZ_WIDGET_GTK == 3)
         return gfxPrefs::UseImageOffscreenSurfaces();
 #else
         return false;
 #endif
     }
 
-    virtual gfxImageFormat GetOffscreenFormat();
+    virtual gfxImageFormat GetOffscreenFormat() MOZ_OVERRIDE;
 
-    virtual int GetScreenDepth() const;
+    virtual int GetScreenDepth() const MOZ_OVERRIDE;
 
 protected:
     static gfxFontconfigUtils *sFontconfigUtils;
 
 private:
-    virtual void GetPlatformCMSOutputProfile(void *&mem, size_t &size);
+    virtual void GetPlatformCMSOutputProfile(void *&mem,
+                                             size_t &size) MOZ_OVERRIDE;
 
 #ifdef MOZ_X11
     static bool sUseXRender;
 #endif
 };
 
 #endif /* GFX_PLATFORM_GTK_H */
--- a/gfx/thebes/gfxPlatformMac.cpp
+++ b/gfx/thebes/gfxPlatformMac.cpp
@@ -431,28 +431,31 @@ public:
   {
     return mGlobalDisplay;
   }
 
   class OSXDisplay MOZ_FINAL : public VsyncSource::Display
   {
   public:
     OSXDisplay()
+      : mDisplayLink(nullptr)
     {
-      EnableVsync();
     }
 
     ~OSXDisplay()
     {
       DisableVsync();
     }
 
     virtual void EnableVsync() MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
+      if (IsVsyncEnabled()) {
+        return;
+      }
 
       // Create a display link capable of being used with all active displays
       // TODO: See if we need to create an active DisplayLink for each monitor in multi-monitor
       // situations. According to the docs, it is compatible with all displays running on the computer
       // But if we have different monitors at different display rates, we may hit issues.
       if (CVDisplayLinkCreateWithActiveCGDisplays(&mDisplayLink) != kCVReturnSuccess) {
         NS_WARNING("Could not create a display link, returning");
         return;
@@ -468,16 +471,19 @@ public:
         NS_WARNING("Could not activate the display link");
         mDisplayLink = nullptr;
       }
     }
 
     virtual void DisableVsync() MOZ_OVERRIDE
     {
       MOZ_ASSERT(NS_IsMainThread());
+      if (!IsVsyncEnabled()) {
+        return;
+      }
 
       // Release the display link
       if (mDisplayLink) {
         CVDisplayLinkRelease(mDisplayLink);
         mDisplayLink = nullptr;
       }
     }
 
--- a/gfx/thebes/gfxXlibSurface.h
+++ b/gfx/thebes/gfxXlibSurface.h
@@ -46,20 +46,21 @@ public:
                        Drawable relatedDrawable = None);
     static already_AddRefed<gfxXlibSurface>
     Create(Screen* screen, XRenderPictFormat *format, const gfxIntSize& size,
            Drawable relatedDrawable = None);
 
     virtual ~gfxXlibSurface();
 
     virtual already_AddRefed<gfxASurface>
-    CreateSimilarSurface(gfxContentType aType, const gfxIntSize& aSize);
+    CreateSimilarSurface(gfxContentType aType,
+                         const gfxIntSize& aSize) MOZ_OVERRIDE;
     virtual void Finish() MOZ_OVERRIDE;
 
-    virtual const gfxIntSize GetSize() const;
+    virtual const gfxIntSize GetSize() const MOZ_OVERRIDE;
 
     Display* XDisplay() { return mDisplay; }
     Screen* XScreen();
     Drawable XDrawable() { return mDrawable; }
     XRenderPictFormat* XRenderFormat();
 
     static int DepthOfVisual(const Screen* screen, const Visual* visual);
     static Visual* FindVisual(Screen* screen, gfxImageFormat format);
@@ -75,17 +76,17 @@ public:
     // on those created by a Create() factory method.
     Drawable ReleasePixmap();
 
     // Find a visual and colormap pair suitable for rendering to this surface.
     bool GetColormapAndVisual(Colormap* colormap, Visual **visual);
 
     // This surface is a wrapper around X pixmaps, which are stored in the X
     // server, not the main application.
-    virtual gfxMemoryLocation GetMemoryLocation() const;
+    virtual gfxMemoryLocation GetMemoryLocation() const MOZ_OVERRIDE;
 
 #if defined(GL_PROVIDER_GLX)
     GLXPixmap GetGLXPixmap();
 #endif
 
     // Return true if cairo will take its slow path when this surface is used
     // in a pattern with EXTEND_PAD.  As a workaround for XRender's RepeatPad
     // not being implemented correctly on old X servers, cairo avoids XRender
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -134,16 +134,30 @@ nsJPEGDecoder::~nsJPEGDecoder()
 }
 
 Telemetry::ID
 nsJPEGDecoder::SpeedHistogram()
 {
   return Telemetry::IMAGE_DECODE_SPEED_JPEG;
 }
 
+nsresult
+nsJPEGDecoder::SetTargetSize(const nsIntSize& aSize)
+{
+  // Make sure the size is reasonable.
+  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a downscaler that we'll filter our output through.
+  mDownscaler.emplace(aSize);
+
+  return NS_OK;
+}
+
 void
 nsJPEGDecoder::InitInternal()
 {
   mCMSMode = gfxPlatform::GetCMSMode();
   if ((mDecodeFlags & DECODER_NO_COLORSPACE_CONVERSION) != 0) {
     mCMSMode = eCMSMode_Off;
   }
 
@@ -389,16 +403,27 @@ nsJPEGDecoder::WriteInternal(const char*
     if (!mImageData) {
       mState = JPEG_ERROR;
       PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
       PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
              ("} (could not initialize image frame)"));
       return;
     }
 
+    if (mDownscaler) {
+      nsresult rv = mDownscaler->BeginFrame(GetSize(),
+                                            mImageData,
+                                            /* aHasAlpha = */ false);
+      if (NS_FAILED(rv)) {
+        mState = JPEG_ERROR;
+        return;
+      }
+    }
+
+
     PR_LOG(GetJPEGDecoderAccountingLog(), PR_LOG_DEBUG,
            ("        JPEGDecoderAccounting: nsJPEGDecoder::"
             "Write -- created image frame with %ux%u pixels",
             mInfo.output_width, mInfo.output_height));
 
     mState = JPEG_START_DECOMPRESS;
   }
 
@@ -507,16 +532,17 @@ nsJPEGDecoder::WriteInternal(const char*
             return; // I/O suspension
           }
 
           if (jpeg_input_complete(&mInfo) &&
               (mInfo.input_scan_number == mInfo.output_scan_number))
             break;
 
           mInfo.output_scanline = 0;
+          mDownscaler->ResetForNextProgressivePass();
         }
       }
 
       mState = JPEG_DONE;
     }
   }
 
   case JPEG_DONE: {
@@ -586,26 +612,35 @@ nsJPEGDecoder::NotifyDone()
 void
 nsJPEGDecoder::OutputScanlines(bool* suspend)
 {
   *suspend = false;
 
   const uint32_t top = mInfo.output_scanline;
 
   while ((mInfo.output_scanline < mInfo.output_height)) {
-      // Use the Cairo image buffer as scanline buffer
-      uint32_t* imageRow = ((uint32_t*)mImageData) +
-                           (mInfo.output_scanline * mInfo.output_width);
+      uint32_t* imageRow = nullptr;
+      if (mDownscaler) {
+        imageRow = reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer());
+      } else {
+        imageRow = reinterpret_cast<uint32_t*>(mImageData) +
+                   (mInfo.output_scanline * mInfo.output_width);
+      }
+
+      MOZ_ASSERT(imageRow, "Should have a row buffer here");
 
       if (mInfo.out_color_space == MOZ_JCS_EXT_NATIVE_ENDIAN_XRGB) {
         // Special case: scanline will be directly converted into packed ARGB
         if (jpeg_read_scanlines(&mInfo, (JSAMPARRAY)&imageRow, 1) != 1) {
           *suspend = true; // suspend
           break;
         }
+        if (mDownscaler) {
+          mDownscaler->CommitRow();
+        }
         continue; // all done for this row!
       }
 
       JSAMPROW sampleRow = (JSAMPROW)imageRow;
       if (mInfo.output_components == 3) {
         // Put the pixels at end of row to enable in-place expansion
         sampleRow += mInfo.output_width;
       }
@@ -671,23 +706,32 @@ nsJPEGDecoder::OutputScanlines(bool* sus
 
       // copy remaining pixel(s)
       while (idx--) {
         // 32-bit read of final pixel will exceed buffer, so read bytes
         *imageRow++ = gfxPackedPixel(0xFF, sampleRow[0], sampleRow[1],
                                      sampleRow[2]);
         sampleRow += 3;
       }
+
+      if (mDownscaler) {
+        mDownscaler->CommitRow();
+      }
   }
 
   if (top != mInfo.output_scanline) {
-      nsIntRect r(0, top, mInfo.output_width, mInfo.output_scanline-top);
-      PostInvalidation(r);
+    PostInvalidation(nsIntRect(0, top,
+                               mInfo.output_width,
+                               mInfo.output_scanline - top),
+                     mDownscaler ? Some(mDownscaler->TakeInvalidRect())
+                                 : Nothing());
   }
 
+  MOZ_ASSERT(!mDownscaler || !mDownscaler->HasInvalidation(),
+             "Didn't send downscaler's invalidation");
 }
 
 
 // Override the standard error method in the IJG JPEG decoder code.
 METHODDEF(void)
 my_error_exit (j_common_ptr cinfo)
 {
   decoder_error_mgr* err = (decoder_error_mgr*) cinfo->err;
--- a/image/decoders/nsJPEGDecoder.h
+++ b/image/decoders/nsJPEGDecoder.h
@@ -10,16 +10,17 @@
 #include "RasterImage.h"
 // On Windows systems, RasterImage.h brings in 'windows.h', which defines INT32.
 // But the jpeg decoder has its own definition of INT32. To avoid build issues,
 // we need to undefine the version from 'windows.h'.
 #undef INT32
 
 #include "Decoder.h"
 
+#include "Downscaler.h"
 #include "nsAutoPtr.h"
 
 #include "nsIInputStream.h"
 #include "nsIPipe.h"
 #include "qcms.h"
 
 extern "C" {
 #include "jpeglib.h"
@@ -50,27 +51,31 @@ class RasterImage;
 struct Orientation;
 
 class nsJPEGDecoder : public Decoder
 {
 public:
   nsJPEGDecoder(RasterImage* aImage, Decoder::DecodeStyle aDecodeStyle);
   virtual ~nsJPEGDecoder();
 
+  virtual nsresult SetTargetSize(const nsIntSize& aSize) MOZ_OVERRIDE;
+
   virtual void InitInternal() MOZ_OVERRIDE;
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) MOZ_OVERRIDE;
   virtual void FinishInternal() MOZ_OVERRIDE;
 
   virtual Telemetry::ID SpeedHistogram() MOZ_OVERRIDE;
   void NotifyDone();
 
 protected:
   Orientation ReadOrientationFromEXIF();
   void OutputScanlines(bool* suspend);
 
+  Maybe<Downscaler> mDownscaler;
+
 public:
   struct jpeg_decompress_struct mInfo;
   struct jpeg_source_mgr mSourceMgr;
   decoder_error_mgr mErr;
   jstate mState;
 
   uint32_t mBytesToSkip;
 
new file mode 100644
--- /dev/null
+++ b/image/src/Downscaler.cpp
@@ -0,0 +1,219 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Downscaler.h"
+
+#include <algorithm>
+#include <ctime>
+#include "gfxPrefs.h"
+#include "image_operations.h"
+#include "convolver.h"
+#include "skia/SkTypes.h"
+
+using std::max;
+using std::swap;
+
+namespace mozilla {
+namespace image {
+
+Downscaler::Downscaler(const nsIntSize& aTargetSize)
+  : mTargetSize(aTargetSize)
+  , mOutputBuffer(nullptr)
+  , mXFilter(MakeUnique<skia::ConvolutionFilter1D>())
+  , mYFilter(MakeUnique<skia::ConvolutionFilter1D>())
+  , mWindowCapacity(0)
+  , mHasAlpha(true)
+{
+  MOZ_ASSERT(gfxPrefs::ImageDownscaleDuringDecodeEnabled(),
+             "Downscaling even though downscale-during-decode is disabled?");
+  MOZ_ASSERT(mTargetSize.width > 0 && mTargetSize.height > 0,
+             "Invalid target size");
+}
+
+Downscaler::~Downscaler()
+{
+  ReleaseWindow();
+}
+
+void
+Downscaler::ReleaseWindow()
+{
+  if (!mWindow) {
+    return;
+  }
+
+  for (int32_t i = 0; i < mWindowCapacity; ++i) {
+    delete[] mWindow[i];
+  }
+
+  mWindow = nullptr;
+  mWindowCapacity = 0;
+}
+
+nsresult
+Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
+                       uint8_t* aOutputBuffer,
+                       bool aHasAlpha)
+{
+  MOZ_ASSERT(aOutputBuffer);
+  MOZ_ASSERT(mTargetSize != aOriginalSize,
+             "Created a downscaler, but not downscaling?");
+  MOZ_ASSERT(mTargetSize.width <= aOriginalSize.width,
+             "Created a downscaler, but width is larger");
+  MOZ_ASSERT(mTargetSize.height <= aOriginalSize.height,
+             "Created a downscaler, but height is larger");
+  MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0,
+             "Invalid original size");
+
+  mOriginalSize = aOriginalSize;
+  mOutputBuffer = aOutputBuffer;
+  mHasAlpha = aHasAlpha;
+
+  ResetForNextProgressivePass();
+  ReleaseWindow();
+
+  auto resizeMethod = skia::ImageOperations::RESIZE_LANCZOS3;
+
+  skia::resize::ComputeFilters(resizeMethod, mOriginalSize.width,
+                               mTargetSize.width, 0,
+                               mTargetSize.width, mXFilter.get());
+
+  skia::resize::ComputeFilters(resizeMethod, mOriginalSize.height,
+                               mTargetSize.height, 0,
+                               mTargetSize.height, mYFilter.get());
+
+  // Allocate the buffer, which contains scanlines of the original image.
+  mRowBuffer = MakeUnique<uint8_t[]>(mOriginalSize.width * sizeof(uint32_t));
+  if (MOZ_UNLIKELY(!mRowBuffer)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // Allocate the window, which contains horizontally downscaled scanlines. (We
+  // can store scanlines which are already downscale because our downscaling
+  // filter is separable.)
+  mWindowCapacity = mYFilter->max_filter();
+  mWindow = MakeUnique<uint8_t*[]>(mWindowCapacity);
+  if (MOZ_UNLIKELY(!mWindow)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  bool anyAllocationFailed = false;
+  const int rowSize = mTargetSize.width * sizeof(uint32_t);
+  for (int32_t i = 0; i < mWindowCapacity; ++i) {
+    mWindow[i] = new uint8_t[rowSize];
+    anyAllocationFailed = anyAllocationFailed || mWindow[i] == nullptr;
+  }
+
+  if (MOZ_UNLIKELY(anyAllocationFailed)) {
+    // We intentionally iterate through the entire array even if an allocation
+    // fails, to ensure that all the pointers in it are either valid or nullptr.
+    // That in turn ensures that ReleaseWindow() can clean up correctly.
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  return NS_OK;
+}
+
+void
+Downscaler::ResetForNextProgressivePass()
+{
+  mPrevInvalidatedLine = 0;
+  mCurrentOutLine = 0;
+  mCurrentInLine = 0;
+  mLinesInBuffer = 0;
+}
+
+void
+Downscaler::CommitRow()
+{
+  MOZ_ASSERT(mOutputBuffer, "Should have a current frame");
+  MOZ_ASSERT(mCurrentInLine < mOriginalSize.height, "Past end of input");
+  MOZ_ASSERT(mCurrentOutLine < mTargetSize.height, "Past end of output");
+
+  int32_t filterOffset = 0;
+  int32_t filterLength = 0;
+  mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
+
+  int32_t inLineToRead = filterOffset + mLinesInBuffer;
+  MOZ_ASSERT(mCurrentInLine <= inLineToRead, "Reading past end of input");
+  if (mCurrentInLine == inLineToRead) {
+    skia::ConvolveHorizontally(mRowBuffer.get(), *mXFilter,
+                               mWindow[mLinesInBuffer++], mHasAlpha,
+                               /* use_sse2 = */ true);
+  }
+
+  while (mLinesInBuffer == filterLength &&
+         mCurrentOutLine < mTargetSize.height) {
+    DownscaleInputLine();
+    mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
+  }
+
+  mCurrentInLine += 1;
+}
+
+bool
+Downscaler::HasInvalidation() const
+{
+  return mCurrentOutLine > mPrevInvalidatedLine;
+}
+
+nsIntRect
+Downscaler::TakeInvalidRect()
+{
+  if (MOZ_UNLIKELY(!HasInvalidation())) {
+    return nsIntRect();
+  }
+
+  nsIntRect invalidRect(0, mPrevInvalidatedLine,
+                        mTargetSize.width,
+                        mCurrentOutLine - mPrevInvalidatedLine);
+  mPrevInvalidatedLine = mCurrentOutLine;
+  return invalidRect;
+}
+
+void
+Downscaler::DownscaleInputLine()
+{
+  typedef skia::ConvolutionFilter1D::Fixed FilterValue;
+
+  MOZ_ASSERT(mOutputBuffer);
+  MOZ_ASSERT(mCurrentOutLine < mTargetSize.height, "Writing past end of output");
+
+  int32_t filterOffset = 0;
+  int32_t filterLength = 0;
+  auto filterValues =
+    mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
+
+  uint8_t* outputLine =
+    &mOutputBuffer[mCurrentOutLine * mTargetSize.width * sizeof(uint32_t)];
+  skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
+                           filterLength, mWindow.get(), mXFilter->num_values(),
+                           outputLine, mHasAlpha, /* use_sse2 = */ true);
+
+  mCurrentOutLine += 1;
+
+  if (mCurrentOutLine == mTargetSize.height) {
+    // We're done.
+    return;
+  }
+
+  int32_t newFilterOffset = 0;
+  int32_t newFilterLength = 0;
+  mYFilter->FilterForValue(mCurrentOutLine, &newFilterOffset, &newFilterLength);
+
+  int diff = newFilterOffset - filterOffset;
+  MOZ_ASSERT(diff >= 0, "Moving backwards in the filter?");
+
+  // Shift the buffer. We're just moving pointers here, so this is cheap.
+  mLinesInBuffer -= diff;
+  mLinesInBuffer = max(mLinesInBuffer, 0);
+  for (int32_t i = 0; i < mLinesInBuffer; ++i) {
+    swap(mWindow[i], mWindow[filterLength - mLinesInBuffer + i]);
+  }
+}
+
+} // namespace image
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/image/src/Downscaler.h
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Downscaler is a high-quality, streaming image downscaler based upon Skia's
+ * scaling implementation.
+ */
+
+#ifndef MOZILLA_IMAGELIB_DOWNSCALER_H_
+#define MOZILLA_IMAGELIB_DOWNSCALER_H_
+
+#include "mozilla/UniquePtr.h"
+#include "nsRect.h"
+
+#ifdef MOZ_ENABLE_SKIA
+
+namespace skia {
+  class ConvolutionFilter1D;
+} // namespace skia
+
+namespace mozilla {
+namespace image {
+
+/**
+ * Downscaler is a high-quality, streaming image downscaler based upon Skia's
+ * scaling implementation.
+ *
+ * Decoders can construct a Downscaler once they know their target size, then
+ * call BeginFrame() for each frame they decode. They should write a decoded row
+ * into the buffer returned by RowBuffer(), and then call CommitRow() to signal
+ * that they have finished.
+ *
+
+ * Because invalidations need to be computed in terms of the scaled version of
+ * the image, Downscaler also tracks them. Decoders can call HasInvalidation()
+ * and TakeInvalidRect() instead of tracking invalidations themselves.
+ */
+class Downscaler
+{
+public:
+  /// Constructs a new Downscaler which to scale to size @aTargetSize.
+  explicit Downscaler(const nsIntSize& aTargetSize);
+  ~Downscaler();
+
+  const nsIntSize& OriginalSize() const { return mOriginalSize; }
+  const nsIntSize& TargetSize() const { return mTargetSize; }
+
+  /**
+   * Begins a new frame and reinitializes the Downscaler.
+   *
+   * @param aOriginalSize The original size of this frame, before scaling.
+   * @param aOutputBuffer The buffer to which the Downscaler should write its
+   *                      output; this is the same buffer where the Decoder
+   *                      would write its output when not downscaling during
+   *                      decode.
+   * @param aHasAlpha Whether or not this frame has an alpha channel.
+   *                  Performance is a little better if it doesn't have one.
+   */
+  nsresult BeginFrame(const nsIntSize& aOriginalSize,
+                      uint8_t* aOutputBuffer,
+                      bool aHasAlpha);
+
+  /// Retrieves the buffer into which the Decoder should write each row.
+  uint8_t* RowBuffer() { return mRowBuffer.get(); }
+
+  /// Signals that the decoder has finished writing a row into the row buffer.
+  void CommitRow();
+
+  /// Returns true if there is a non-empty invalid rect available.
+  bool HasInvalidation() const;
+
+  /// Takes the Downscaler's current invalid rect and resets it.
+  nsIntRect TakeInvalidRect();
+
+  /**
+   * Resets the Downscaler's position in the image, for a new progressive pass
+   * over the same frame. Because the same data structures can be reused, this
+   * is more efficient than calling BeginFrame.
+   */
+  void ResetForNextProgressivePass();
+
+private:
+  void DownscaleInputLine();
+  void ReleaseWindow();
+
+  nsIntSize mOriginalSize;
+  nsIntSize mTargetSize;
+
+  uint8_t* mOutputBuffer;
+
+  UniquePtr<uint8_t[]> mRowBuffer;
+  UniquePtr<uint8_t*[]> mWindow;
+
+  UniquePtr<skia::ConvolutionFilter1D> mXFilter;
+  UniquePtr<skia::ConvolutionFilter1D> mYFilter;
+
+  int32_t mWindowCapacity;
+
+  int32_t mLinesInBuffer;
+  int32_t mPrevInvalidatedLine;
+  int32_t mCurrentOutLine;
+  int32_t mCurrentInLine;
+
+  bool mHasAlpha;
+};
+
+} // namespace image
+} // namespace mozilla
+
+
+#else
+
+
+/**
+ * Downscaler requires Skia to work, so we provide a dummy implementation if
+ * Skia is disabled that asserts if constructed.
+ */
+
+namespace mozilla {
+namespace image {
+
+class Downscaler
+{
+public:
+  explicit Downscaler(const nsIntSize&)
+  {
+    MOZ_RELEASE_ASSERT("Skia is not enabled");
+  }
+
+  const nsIntSize& OriginalSize() const { return nsIntSize(); }
+  const nsIntSize& TargetSize() const { return nsIntSize(); }
+  uint8_t* Buffer() { return nullptr; }
+
+  nsresult BeginFrame(const nsIntSize&, uint8_t*, bool)
+  {
+    return NS_ERROR_FAILURE;
+  }
+
+  void CommitRow() { }
+  bool HasInvalidation() const { return false; }
+  nsIntRect TakeInvalidRect() { return nsIntRect(); }
+  void ResetForNextProgressivePass() { }
+};
+
+
+} // namespace image
+} // namespace mozilla
+
+#endif
+
+#endif // MOZILLA_IMAGELIB_DOWNSCALER_H_
--- a/image/src/ImageFactory.cpp
+++ b/image/src/ImageFactory.cpp
@@ -29,18 +29,19 @@ namespace image {
 
 /*static*/ void
 ImageFactory::Initialize()
 { }
 
 static bool
 ShouldDownscaleDuringDecode(const nsCString& aMimeType)
 {
-  // Not enabled for anything yet.
-  return false;
+  return aMimeType.EqualsLiteral(IMAGE_JPEG) ||
+         aMimeType.EqualsLiteral(IMAGE_JPG) ||
+         aMimeType.EqualsLiteral(IMAGE_PJPEG);
 }
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1410,16 +1410,27 @@ RasterImage::CreateDecoder(const Maybe<n
 
   return decoder.forget();
 }
 
 void
 RasterImage::WantDecodedFrames(const nsIntSize& aSize, uint32_t aFlags,
                                bool aShouldSyncNotify)
 {
+  if (mDownscaleDuringDecode) {
+    // We're about to decode again, which may mean that some of the previous
+    // sizes we've decoded at aren't useful anymore. We can allow them to
+    // expire from the cache by unlocking them here. When the decode finishes,
+    // it will send an invalidation that will cause all instances of this image
+    // to redraw. If this image is locked, any surfaces that are still useful
+    // will become locked again when LookupFrame touches them, and the remainder
+    // will eventually expire.
+    SurfaceCache::UnlockSurfaces(ImageKey(this));
+  }
+
   if (aShouldSyncNotify) {
     // We can sync notify, which means we can also sync decode.
     if (aFlags & FLAG_SYNC_DECODE) {
       Decode(DecodeStrategy::SYNC_IF_POSSIBLE, Some(aSize), aFlags);
       return;
     }
 
     // Here we are explicitly trading off flashing for responsiveness in the
--- a/image/src/SurfaceCache.cpp
+++ b/image/src/SurfaceCache.cpp
@@ -527,17 +527,19 @@ public:
     DrawableFrameRef ref = surface->DrawableRef();
     if (!ref) {
       // The surface was released by the operating system. Remove the cache
       // entry as well.
       Remove(surface);
       return DrawableFrameRef();
     }
 
-    if (!surface->IsLocked()) {
+    if (cache->IsLocked()) {
+      LockSurface(surface);
+    } else {
       mExpirationTracker.MarkUsed(surface);
     }
 
     return ref;
   }
 
   DrawableFrameRef LookupBestMatch(const ImageKey         aImageKey,
                                    const SurfaceKey&      aSurfaceKey,
@@ -566,17 +568,19 @@ public:
         break;
       }
 
       // The surface was released by the operating system. Remove the cache
       // entry as well.
       Remove(surface);
     }
 
-    if (!surface->IsLocked()) {
+    if (cache->IsLocked()) {
+      LockSurface(surface);
+    } else {
       mExpirationTracker.MarkUsed(surface);
     }
 
     return ref;
   }
 
   void RemoveSurface(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
@@ -602,32 +606,47 @@ public:
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       cache = new ImageSurfaceCache;
       mImageCaches.Put(aImageKey, cache);
     }
 
     cache->SetLocked(true);
 
-    // Try to lock all the surfaces the per-image cache is holding.
-    cache->ForEach(DoLockSurface, this);
+    // We don't relock this image's existing surfaces right away; instead, the
+    // image should arrange for Lookup() to touch them if they are still useful.
   }
 
   void UnlockImage(const ImageKey aImageKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
-    if (!cache)
-      return;  // Already unlocked and removed.
+    if (!cache || !cache->IsLocked()) {
+      return;  // Already unlocked.
+    }
 
     cache->SetLocked(false);
 
     // Unlock all the surfaces the per-image cache is holding.
     cache->ForEach(DoUnlockSurface, this);
   }
 
+  void UnlockSurfaces(const ImageKey aImageKey)
+  {
+    nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
+    if (!cache || !cache->IsLocked()) {
+      return;  // Already unlocked.
+    }
+
+    // (Note that we *don't* unlock the per-image cache here; that's the
+    // difference between this and UnlockImage.)
+
+    // Unlock all the surfaces the per-image cache is holding.
+    cache->ForEach(DoUnlockSurface, this);
+  }
+
   void RemoveImage(const ImageKey aImageKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache)
       return;  // No cached surfaces for this image, so nothing to do.
 
     // Discard all of the cached surfaces for this image.
     // XXX(seth): This is O(n^2) since for each item in the cache we are
@@ -673,43 +692,38 @@ public:
 
     // Discard surfaces until we've reduced our cost to our target cost.
     while (mAvailableCost < targetCost) {
       MOZ_ASSERT(!mCosts.IsEmpty(), "Removed everything and still not done");
       Remove(mCosts.LastElement().GetSurface());
     }
   }
 
+  void LockSurface(CachedSurface* aSurface)
+  {
+    if (aSurface->GetLifetime() == Lifetime::Transient ||
+        aSurface->IsLocked()) {
+      return;
+    }
+
+    StopTracking(aSurface);
+
+    // Lock the surface. This can fail.
+    aSurface->SetLocked(true);
+    StartTracking(aSurface);
+  }
+
   static PLDHashOperator DoStopTracking(const SurfaceKey&,
                                         CachedSurface*    aSurface,
                                         void*             aCache)
   {
     static_cast<SurfaceCacheImpl*>(aCache)->StopTracking(aSurface);
     return PL_DHASH_NEXT;
   }
 
-  static PLDHashOperator DoLockSurface(const SurfaceKey&,
-                                       CachedSurface*    aSurface,
-                                       void*             aCache)
-  {
-    if (aSurface->GetLifetime() == Lifetime::Transient ||
-        aSurface->IsLocked()) {
-      return PL_DHASH_NEXT;
-    }
-
-    auto cache = static_cast<SurfaceCacheImpl*>(aCache);
-    cache->StopTracking(aSurface);
-
-    // Lock the surface. This can fail.
-    aSurface->SetLocked(true);
-    cache->StartTracking(aSurface);
-
-    return PL_DHASH_NEXT;
-  }
-
   static PLDHashOperator DoUnlockSurface(const SurfaceKey&,
                                          CachedSurface*    aSurface,
                                          void*             aCache)
   {
     if (aSurface->GetLifetime() == Lifetime::Transient ||
         !aSurface->IsLocked()) {
       return PL_DHASH_NEXT;
     }
@@ -995,16 +1009,25 @@ SurfaceCache::UnlockImage(Image* aImageK
 {
   if (sInstance) {
     MutexAutoLock lock(sInstance->GetMutex());
     return sInstance->UnlockImage(aImageKey);
   }
 }
 
 /* static */ void
+SurfaceCache::UnlockSurfaces(const ImageKey aImageKey)
+{
+  if (sInstance) {
+    MutexAutoLock lock(sInstance->GetMutex());
+    return sInstance->UnlockSurfaces(aImageKey);
+  }
+}
+
+/* static */ void
 SurfaceCache::RemoveSurface(const ImageKey    aImageKey,
                             const SurfaceKey& aSurfaceKey)
 {
   if (sInstance) {
     MutexAutoLock lock(sInstance->GetMutex());
     sInstance->RemoveSurface(aImageKey, aSurfaceKey);
   }
 }
--- a/image/src/SurfaceCache.h
+++ b/image/src/SurfaceCache.h
@@ -144,19 +144,16 @@ MOZ_END_ENUM_CLASS(InsertOutcome)
  * currently visible to expire) or because it's not possible to rematerialize
  * the surface. SurfaceCache supports this through the use of image locking and
  * surface lifetimes; see the comments for Insert() and LockImage() for more
  * details.
  *
  * Any image which stores surfaces in the SurfaceCache *must* ensure that it
  * calls RemoveImage() before it is destroyed. See the comments for
  * RemoveImage() for more details.
- *
- * SurfaceCache is not thread-safe; it should only be accessed from the main
- * thread.
  */
 struct SurfaceCache
 {
   typedef gfx::IntSize IntSize;
 
   /**
    * Initialize static data. Called during imagelib module initialization.
    */
@@ -166,16 +163,19 @@ struct SurfaceCache
    * Release static data. Called during imagelib module shutdown.
    */
   static void Shutdown();
 
   /**
    * Look up the imgFrame containing a surface in the cache and returns a
    * drawable reference to that imgFrame.
    *
+   * If the image associated with the surface is locked, then the surface will
+   * be locked before it is returned.
+   *
    * If the imgFrame was found in the cache, but had stored its surface in a
    * volatile buffer which was discarded by the OS, then it is automatically
    * removed from the cache and an empty DrawableFrameRef is returned. Note that
    * this will never happen to persistent surfaces associated with a locked
    * image; the cache keeps a strong reference to such surfaces internally.
    *
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested surface.
@@ -195,16 +195,19 @@ struct SurfaceCache
 
   /**
    * Looks up the best matching surface in the cache and returns a drawable
    * reference to the imgFrame containing it.
    *
    * Returned surfaces may vary from the requested surface only in terms of
    * size, unless @aAlternateFlags is specified.
    *
+   * If the image associated with the surface is locked, then the surface will
+   * be locked before it is returned.
+   *
    * @param aImageKey    Key data identifying which image the surface belongs to.
    * @param aSurfaceKey  Key data which identifies the ideal surface to return.
    * @param aAlternateFlags If not Nothing(), a different set of flags than the
    *                        ones specified in @aSurfaceKey which are also
    *                        acceptable to the caller. This is much more
    *                        efficient than calling LookupBestMatch() twice.
    *
    * @return a DrawableFrameRef to the imgFrame wrapping a surface similar to
@@ -216,22 +219,30 @@ struct SurfaceCache
                                             = Nothing());
 
   /**
    * Insert a surface into the cache. If a surface with the same ImageKey and
    * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
-   * surfaces can ordinarily also expire from the cache at any time, but if the
-   * image they're associated with is locked, then these surfaces will never
-   * expire. This means that surfaces which cannot be rematerialized should be
-   * inserted with a persistent lifetime *after* the image is locked with
-   * LockImage(); if you use the other order, the surfaces might expire before
-   * LockImage() gets called.
+   * surfaces, on the other hand, will never expire as long as they remain
+   * locked, but if they become unlocked, can expire just like transient
+   * surfaces. When it is first inserted, a persistent surface is locked if its
+   * associated image is locked. When that image is later unlocked, the surface
+   * becomes unlocked too. To become locked again at that point, two things must
+   * happen: the image must become locked again (via LockImage()), and the
+   * surface must be touched again (via one of the Lookup() functions).
+   *
+   * All of this means that a very particular procedure has to be followed for
+   * surfaces which cannot be rematerialized. First, they must be inserted
+   * with a persistent lifetime *after* the image is locked with LockImage(); if
+   * you use the other order, the surfaces might expire before LockImage() gets
+   * called or before the surface is touched again by Lookup(). Second, the
+   * image they are associated with must never be unlocked.
    *
    * If a surface cannot be rematerialized, it may be important to know whether
    * it was inserted into the cache successfully. Insert() returns FAILURE if it
    * failed to insert the surface, which could happen because of capacity
    * reasons, or because it was already freed by the OS. If you aren't inserting
    * a surface with persistent lifetime, or if the surface isn't associated with
    * a locked image, checking for SUCCESS or FAILURE is useless: the surface
    * might expire immediately after being inserted, even though Insert()
@@ -271,18 +282,27 @@ struct SurfaceCache
    * @param aSize  The dimensions of a surface in pixels.
    *
    * @return false if the surface cache can't hold a surface of that size.
    */
   static bool CanHold(const IntSize& aSize);
   static bool CanHold(size_t aSize);
 
   /**
-   * Locks an image, preventing any of that image's surfaces from expiring
-   * unless they have a transient lifetime.
+   * Locks an image. Any of the image's persistent surfaces which are either
+   * inserted or accessed while the image is locked will not expire.
+   *
+   * Locking an image does not automatically lock that image's existing
+   * surfaces. A call to LockImage() guarantees that persistent surfaces which
+   * are inserted afterward will not expire before the next call to
+   * UnlockImage() or UnlockSurfaces() for that image. Surfaces that are
+   * accessed via Lookup() or LookupBestMatch() after a LockImage() call will
+   * also not expire until the next UnlockImage() or UnlockSurfaces() call for
+   * that image. Any other surfaces owned by the image may expire at any time,
+   * whether they are persistent or transient.
    *
    * Regardless of locking, any of an image's surfaces may be removed using
    * RemoveSurface(), and all of an image's surfaces are removed by
    * RemoveImage(), whether the image is locked or not.
    *
    * It's safe to call LockImage() on an image that's already locked; this has
    * no effect.
    *
@@ -297,21 +317,42 @@ struct SurfaceCache
   static void LockImage(const ImageKey aImageKey);
 
   /**
    * Unlocks an image, allowing any of its surfaces to expire at any time.
    *
    * It's OK to call UnlockImage() on an image that's already unlocked; this has
    * no effect.
    *
-   * @param aImageKey    The image to lock.
+   * @param aImageKey    The image to unlock.
    */
   static void UnlockImage(const ImageKey aImageKey);
 
   /**
+   * Unlocks the existing surfaces of an image, allowing them to expire at any
+   * time.
+   *
+   * This does not unlock the image itself, so accessing the surfaces via
+   * Lookup() or LookupBestMatch() will lock them again, and prevent them from
+   * expiring.
+   *
+   * This is intended to be used in situations where it's no longer clear that
+   * all of the persistent surfaces owned by an image are needed. Calling
+   * UnlockSurfaces() and then taking some action that will cause Lookup() to
+   * touch any surfaces that are still useful will permit the remaining surfaces
+   * to expire from the cache.
+   *
+   * If the image is unlocked, this has no effect.
+   *
+   * @param aImageKey    The image which should have its existing surfaces
+   *                     unlocked.
+   */
+  static void UnlockSurfaces(const ImageKey aImageKey);
+
+  /**
    * Removes a surface from the cache, if it's present. If it's not present,
    * RemoveSurface() has no effect.
    *
    * Use this function to remove individual surfaces that have become invalid.
    * Prefer RemoveImage() or DiscardAll() when they're applicable, as they have
    * much better performance than calling this function repeatedly.
    *
    * @param aImageKey    Key data identifying which image the surface belongs to.
--- a/image/src/imgLoader.cpp
+++ b/image/src/imgLoader.cpp
@@ -455,18 +455,18 @@ NS_IMPL_ISUPPORTS(imgMemoryReporter, nsI
 NS_IMPL_ISUPPORTS(nsProgressNotificationProxy,
                   nsIProgressEventSink,
                   nsIChannelEventSink,
                   nsIInterfaceRequestor)
 
 NS_IMETHODIMP
 nsProgressNotificationProxy::OnProgress(nsIRequest* request,
                                         nsISupports* ctxt,
-                                        uint64_t progress,
-                                        uint64_t progressMax)
+                                        int64_t progress,
+                                        int64_t progressMax)
 {
   nsCOMPtr<nsILoadGroup> loadGroup;
   request->GetLoadGroup(getter_AddRefs(loadGroup));
 
   nsCOMPtr<nsIProgressEventSink> target;
   NS_QueryNotificationCallbacks(mOriginalCallbacks,
                                 loadGroup,
                                 NS_GET_IID(nsIProgressEventSink),
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -14,16 +14,17 @@ EXPORTS += [
     'Orientation.h',
     'SurfaceCache.h',
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
+    'Downscaler.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageFactory.cpp',
     'ImageMetadata.cpp',
     'ImageOps.cpp',
     'ImageWrapper.cpp',
@@ -53,16 +54,18 @@ FAIL_ON_WARNINGS = True
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     # Because SVGDocumentWrapper.cpp includes "mozilla/dom/SVGSVGElement.h"
     '/dom/base',
     '/dom/svg',
+    # Access to Skia headers for Downscaler
+    '/gfx/2d',
     # We need to instantiate the decoders
     '/image/decoders',
     # Because VectorImage.cpp includes nsSVGUtils.h and nsSVGEffects.h
     '/layout/svg',
     # For URI-related functionality
     '/netwerk/base/src',
 ]
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -420,17 +420,17 @@ case "$target" in
         if test "$_CC_MAJOR_VERSION" = "18"; then
             _CC_SUITE=12
         else
             AC_MSG_ERROR([This version ($CC_VERSION) of the MSVC compiler is unsupported. See https://developer.mozilla.org/en/Windows_Build_Prerequisites.])
         fi
 
         dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
         dnl not something else like "magnetic tape manipulation utility".
-        MSMT_TOOL=`mt 2>&1|grep 'Microsoft (R) Manifest Tool'`
+        MSMT_TOOL=`${MT-mt} 2>&1|grep 'Microsoft (R) Manifest Tool'`
         if test -z "$MSMT_TOOL"; then
           AC_MSG_ERROR([Microsoft (R) Manifest Tool must be in your \$PATH.])
         fi
 
         changequote(,)
         _MSMT_VER_FILTER='s|.*[^!-~]\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*|\1|p'
         changequote([,])
         MSMANIFEST_TOOL_VERSION=`echo ${MSMT_TOOL}|sed -ne "$_MSMT_VER_FILTER"`
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -596,16 +596,19 @@ class FullParseHandler
     void setListFlag(ParseNode *pn, unsigned flag) {
         MOZ_ASSERT(pn->isArity(PN_LIST));
         pn->pn_xflags |= flag;
     }
     ParseNode *setInParens(ParseNode *pn) {
         pn->setInParens(true);
         return pn;
     }
+    ParseNode *setLikelyIIFE(ParseNode *pn) {
+        return setInParens(pn);
+    }
     void setPrologue(ParseNode *pn) {
         pn->pn_prologue = true;
     }
 
     bool isConstant(ParseNode *pn) {
         return pn->isConstant();
     }
     PropertyName *isName(ParseNode *pn) {
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -506,16 +506,17 @@ class ParseNode
 
     bool isAssignment() const {
         ParseNodeKind kind = getKind();
         return PNK_ASSIGNMENT_START <= kind && kind <= PNK_ASSIGNMENT_LAST;
     }
 
     /* Boolean attributes. */
     bool isInParens() const                { return pn_parens; }
+    bool isLikelyIIFE() const              { return isInParens(); }
     void setInParens(bool enabled)         { pn_parens = enabled; }
     bool isUsed() const                    { return pn_used; }
     void setUsed(bool enabled)             { pn_used = enabled; }
     bool isDefn() const                    { return pn_defn; }
     void setDefn(bool enabled)             { pn_defn = enabled; }
 
     static const unsigned NumDefinitionFlagBits = 10;
     static const unsigned NumListFlagBits = 10;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2150,25 +2150,28 @@ Parser<ParseHandler>::templateLiteral()
     } while (tt == TOK_TEMPLATE_HEAD);
     return nodeList;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
 Parser<ParseHandler>::functionDef(HandlePropertyName funName,
                                   FunctionType type, FunctionSyntaxKind kind,
-                                  GeneratorKind generatorKind)
+                                  GeneratorKind generatorKind, InvokedPrediction invoked)
 {
     MOZ_ASSERT_IF(kind == Statement, funName);
 
     /* Make a TOK_FUNCTION node. */
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         return null();
 
+    if (invoked)
+        pn = handler.setLikelyIIFE(pn);
+
     bool bodyProcessed;
     if (!checkFunctionDefinition(funName, &pn, kind, &bodyProcessed))
         return null();
 
     if (bodyProcessed)
         return pn;
 
     RootedObject proto(context);
@@ -2318,16 +2321,23 @@ Parser<FullParseHandler>::functionArgsAn
 
     // Create box for fun->object early to protect against last-ditch GC.
     FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
     if (!funbox)
         return false;
 
     // Try a syntax parse for this inner function.
     do {
+        // If we're assuming this function is an IIFE, always perform a full
+        // parse to avoid the overhead of a lazy syntax-only parse. Although
+        // the prediction may be incorrect, IIFEs are common enough that it
+        // pays off for lots of code.
+        if (pn->isLikelyIIFE() && !funbox->isGenerator())
+            break;
+
         Parser<SyntaxParseHandler> *parser = handler.syntaxParser;
         if (!parser)
             break;
 
         {
             // Move the syntax parser to the current position in the stream.
             TokenStream::Position position(keepAtoms);
             tokenStream.tell(&position);
@@ -2632,17 +2642,17 @@ Parser<ParseHandler>::functionStmt()
         !report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
         return null();
 
     return functionDef(name, Normal, Statement, generatorKind);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::functionExpr()
+Parser<ParseHandler>::functionExpr(InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
 
     GeneratorKind generatorKind = NotGenerator;
     TokenKind tt;
     if (!tokenStream.getToken(&tt))
         return null();
 
@@ -2658,17 +2668,17 @@ Parser<ParseHandler>::functionExpr()
     } else if (tt == TOK_YIELD) {
         if (!checkYieldNameValidity())
             return null();
         name = tokenStream.currentName();
     } else {
         tokenStream.ungetToken();
     }
 
-    return functionDef(name, Normal, Expression, generatorKind);
+    return functionDef(name, Normal, Expression, generatorKind, invoked);
 }
 
 /*
  * Return true if this node, known to be an unparenthesized string literal,
  * could be the string of a directive in a Directive Prologue. Directive
  * strings never contain escape sequences or line continuations.
  * isEscapeFreeStringLiteral, below, checks whether the node itself could be
  * a directive.
@@ -4400,20 +4410,20 @@ SyntaxParseHandler::Node
 Parser<SyntaxParseHandler>::exportDeclaration()
 {
     JS_ALWAYS_FALSE(abortIfSyntaxParser());
     return SyntaxParseHandler::NodeFailure;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::expressionStatement()
+Parser<ParseHandler>::expressionStatement(InvokedPrediction invoked)
 {
     tokenStream.ungetToken();
-    Node pnexpr = expr();
+    Node pnexpr = expr(invoked);
     if (!pnexpr)
         return null();
     if (!MatchOrInsertSemicolon(tokenStream))
         return null();
     return handler.newExprStatement(pnexpr, pos().end);
 }
 
 template <typename ParseHandler>
@@ -5881,26 +5891,29 @@ Parser<ParseHandler>::statement(bool can
         TokenKind next;
         if (!tokenStream.peekToken(&next))
             return null();
         if (next == TOK_COLON)
             return labeledStatement();
         return expressionStatement();
       }
 
+      case TOK_NEW:
+        return expressionStatement(PredictInvoked);
+
       default:
         return expressionStatement();
     }
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::expr()
-{
-    Node pn = assignExpr();
+Parser<ParseHandler>::expr(InvokedPrediction invoked)
+{
+    Node pn = assignExpr(invoked);
     if (!pn)
         return null();
 
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_COMMA))
         return null();
     if (matched) {
         Node seq = handler.newList(PNK_COMMA, pn);
@@ -6012,33 +6025,33 @@ Precedence(ParseNodeKind pnk) {
 
     MOZ_ASSERT(pnk >= PNK_BINOP_FIRST);
     MOZ_ASSERT(pnk <= PNK_BINOP_LAST);
     return PrecedenceTable[pnk - PNK_BINOP_FIRST];
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::orExpr1()
+Parser<ParseHandler>::orExpr1(InvokedPrediction invoked)
 {
     // Shift-reduce parser for the left-associative binary operator part of
     // the JS syntax.
 
     // Conceptually there's just one stack, a stack of pairs (lhs, op).
     // It's implemented using two separate arrays, though.
     Node nodeStack[PRECEDENCE_CLASSES];
     ParseNodeKind kindStack[PRECEDENCE_CLASSES];
     int depth = 0;
 
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
 
     Node pn;
     for (;;) {
-        pn = unaryExpr();
+        pn = unaryExpr(invoked);
         if (!pn)
             return pn;
 
         // If a binary operator follows, consume it and compute the
         // corresponding operator.
         TokenKind tok;
         if (!tokenStream.getToken(&tok))
             return null();
@@ -6078,19 +6091,19 @@ Parser<ParseHandler>::orExpr1()
 
     MOZ_ASSERT(depth == 0);
     pc->parsingForInit = oldParsingForInit;
     return pn;
 }
 
 template <typename ParseHandler>
 MOZ_ALWAYS_INLINE typename ParseHandler::Node
-Parser<ParseHandler>::condExpr1()
-{
-    Node condition = orExpr1();
+Parser<ParseHandler>::condExpr1(InvokedPrediction invoked)
+{
+    Node condition = orExpr1(invoked);
     if (!condition || !tokenStream.isCurrentTokenType(TOK_HOOK))
         return condition;
 
     /*
      * Always accept the 'in' operator in the middle clause of a ternary,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
@@ -6179,17 +6192,17 @@ Parser<SyntaxParseHandler>::checkAndMark
     {
         return abortIfSyntaxParser();
     }
     return checkStrictAssignment(pn);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::assignExpr()
+Parser<ParseHandler>::assignExpr(InvokedPrediction invoked)
 {
     JS_CHECK_RECURSION(context, return null());
 
     // It's very common at this point to have a "detectably simple" expression,
     // i.e. a name/number/string token followed by one of the following tokens
     // that obviously isn't part of an expression: , ; : ) ] }
     //
     // (In Parsemark this happens 81.4% of the time;  in code with large
@@ -6231,17 +6244,17 @@ Parser<ParseHandler>::assignExpr()
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
 
-    Node lhs = condExpr1();
+    Node lhs = condExpr1(invoked);
     if (!lhs)
         return null();
 
     ParseNodeKind kind;
     JSOp op;
     switch (tokenStream.currentToken().type) {
       case TOK_ASSIGN:       kind = PNK_ASSIGN;       op = JSOP_NOP;    break;
       case TOK_ADDASSIGN:    kind = PNK_ADDASSIGN;    op = JSOP_ADD;    break;
@@ -6337,17 +6350,17 @@ Parser<ParseHandler>::unaryOpExpr(ParseN
     Node kid = unaryExpr();
     if (!kid)
         return null();
     return handler.newUnary(kind, op, begin, kid);
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::unaryExpr()
+Parser<ParseHandler>::unaryExpr(InvokedPrediction invoked)
 {
     Node pn, pn2;
 
     JS_CHECK_RECURSION(context, return null());
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
@@ -6395,17 +6408,17 @@ Parser<ParseHandler>::unaryExpr()
                 return null();
             pc->sc->setBindingsAccessedDynamically();
         }
 
         return handler.newDelete(begin, expr);
       }
 
       default:
-        pn = memberExpr(tt, true);
+        pn = memberExpr(tt, /* allowCallSyntax = */ true, invoked);
         if (!pn)
             return null();
 
         /* Don't look across a newline boundary for a postfix incop. */
         if (!tokenStream.peekTokenSameLine(&tt, TokenStream::Operand))
             return null();
         if (tt == TOK_INC || tt == TOK_DEC) {
             tokenStream.consumeKnownToken(tt);
@@ -7498,50 +7511,50 @@ Parser<ParseHandler>::argumentList(Node 
         return false;
     }
     handler.setEndPosition(listNode, pos().end);
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
+Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
 
     Node lhs;
 
     JS_CHECK_RECURSION(context, return null());
 
     /* Check for new expression first. */
     if (tt == TOK_NEW) {
         lhs = handler.newList(PNK_NEW, null(), JSOP_NEW);
         if (!lhs)
             return null();
 
         if (!tokenStream.getToken(&tt, TokenStream::Operand))
             return null();
-        Node ctorExpr = memberExpr(tt, false);
+        Node ctorExpr = memberExpr(tt, false, PredictInvoked);
         if (!ctorExpr)
             return null();
 
         handler.addList(lhs, ctorExpr);
 
         bool matched;
         if (!tokenStream.matchToken(&matched, TOK_LP))
             return null();
         if (matched) {
             bool isSpread = false;
             if (!argumentList(lhs, &isSpread))
                 return null();
             if (isSpread)
                 handler.setOp(lhs, JSOP_SPREADNEW);
         }
     } else {
-        lhs = primaryExpr(tt);
+        lhs = primaryExpr(tt, invoked);
         if (!lhs)
             return null();
     }
 
     while (true) {
         if (!tokenStream.getToken(&tt))
             return null();
         if (tt == TOK_EOF)
@@ -8124,24 +8137,24 @@ Parser<ParseHandler>::methodDefinition(N
         return false;
     if (!handler.addMethodDefinition(literal, propname, fn, op))
         return false;
     return true;
 }
 
 template <typename ParseHandler>
 typename ParseHandler::Node
-Parser<ParseHandler>::primaryExpr(TokenKind tt)
+Parser<ParseHandler>::primaryExpr(TokenKind tt, InvokedPrediction invoked)
 {
     MOZ_ASSERT(tokenStream.isCurrentTokenType(tt));
     JS_CHECK_RECURSION(context, return null());
 
     switch (tt) {
       case TOK_FUNCTION:
-        return functionExpr();
+        return functionExpr(invoked);
 
       case TOK_LB:
         return arrayInitializer();
 
       case TOK_LC:
         return objectLiteral();
 
       case TOK_LET:
@@ -8265,17 +8278,17 @@ Parser<ParseHandler>::parenExprOrGenerat
 
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    Node pn = expr();
+    Node pn = expr(PredictInvoked);
     pc->parsingForInit = oldParsingForInit;
 
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     if (!tokenStream.matchToken(&matched, TOK_FOR))
         return null();
@@ -8343,17 +8356,17 @@ Parser<ParseHandler>::exprInParens()
 
     /*
      * Always accept the 'in' operator in a parenthesized expression,
      * where it's unambiguous, even if we might be parsing the init of a
      * for statement.
      */
     bool oldParsingForInit = pc->parsingForInit;
     pc->parsingForInit = false;
-    Node pn = expr();
+    Node pn = expr(PredictInvoked);
     pc->parsingForInit = oldParsingForInit;
 
     if (!pn)
         return null();
 
 #if JS_HAS_GENERATOR_EXPRS
     bool matched;
     if (!tokenStream.matchToken(&matched, TOK_FOR))
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -514,16 +514,19 @@ class Parser : private JS::AutoGCRooter,
 
     virtual bool strictMode() { return pc->sc->strict; }
 
     const ReadOnlyCompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
+    enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
+
+  private:
     /*
      * JS parsers, from lowest to highest precedence.
      *
      * Each parser must be called during the dynamic scope of a ParseContext
      * object, pointed to by this->pc.
      *
      * Each returns a parse node tree or null on error.
      *
@@ -531,17 +534,17 @@ class Parser : private JS::AutoGCRooter,
      * pointing to the token one past the end of the parsed fragment.  For a
      * number of the parsers this is convenient and avoids a lot of
      * unnecessary ungetting and regetting of tokens.
      *
      * Some parsers have two versions:  an always-inlined version (with an 'i'
      * suffix) and a never-inlined version (with an 'n' suffix).
      */
     Node functionStmt();
-    Node functionExpr();
+    Node functionExpr(InvokedPrediction invoked = PredictUninvoked);
     Node statements();
 
     Node blockStatement();
     Node ifStatement();
     Node doWhileStatement();
     Node whileStatement();
     Node forStatement();
     Node switchStatement();
@@ -553,43 +556,44 @@ class Parser : private JS::AutoGCRooter,
     Node throwStatement();
     Node tryStatement();
     Node debuggerStatement();
 
     Node lexicalDeclaration(bool isConst);
     Node letStatement();
     Node importDeclaration();
     Node exportDeclaration();
-    Node expressionStatement();
+    Node expressionStatement(InvokedPrediction invoked = PredictUninvoked);
     Node variables(ParseNodeKind kind, bool *psimple = nullptr,
                    StaticBlockObject *blockObj = nullptr,
                    VarContext varContext = HoistVars);
-    Node expr();
-    Node assignExpr();
+    Node expr(InvokedPrediction invoked = PredictUninvoked);
+    Node assignExpr(InvokedPrediction invoked = PredictUninvoked);
     Node assignExprWithoutYield(unsigned err);
     Node yieldExpression();
-    Node condExpr1();
-    Node orExpr1();
-    Node unaryExpr();
-    Node memberExpr(TokenKind tt, bool allowCallSyntax);
-    Node primaryExpr(TokenKind tt);
+    Node condExpr1(InvokedPrediction invoked = PredictUninvoked);
+    Node orExpr1(InvokedPrediction invoked = PredictUninvoked);
+    Node unaryExpr(InvokedPrediction invoked = PredictUninvoked);
+    Node memberExpr(TokenKind tt, bool allowCallSyntax,
+                    InvokedPrediction invoked = PredictUninvoked);
+    Node primaryExpr(TokenKind tt, InvokedPrediction invoked = PredictUninvoked);
     Node parenExprOrGeneratorComprehension();
     Node exprInParens();
 
     bool methodDefinition(Node literal, Node propname, FunctionType type, FunctionSyntaxKind kind,
                           GeneratorKind generatorKind, JSOp Op);
 
     /*
      * Additional JS parsers.
      */
     bool functionArguments(FunctionSyntaxKind kind, FunctionType type, Node *list, Node funcpn,
                            bool *hasRest);
 
     Node functionDef(HandlePropertyName name, FunctionType type, FunctionSyntaxKind kind,
-                     GeneratorKind generatorKind);
+                     GeneratorKind generatorKind, InvokedPrediction invoked = PredictUninvoked);
     bool functionArgsAndBody(Node pn, HandleFunction fun,
                              FunctionType type, FunctionSyntaxKind kind,
                              GeneratorKind generatorKind,
                              Directives inheritedDirectives, Directives *newDirectives);
 
     Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
 
     Node condition();
--- a/js/src/frontend/SyntaxParseHandler.h
+++ b/js/src/frontend/SyntaxParseHandler.h
@@ -226,16 +226,19 @@ class SyntaxParseHandler
     void setBlockId(Node pn, unsigned blockid) {}
     void setFlag(Node pn, unsigned flag) {}
     void setListFlag(Node pn, unsigned flag) {}
     Node setInParens(Node pn) {
         // String literals enclosed by parentheses are ignored during
         // strict mode parsing.
         return (pn == NodeString) ? NodeGeneric : pn;
     }
+    Node setLikelyIIFE(Node pn) {
+        return pn; // Remain in syntax-parse mode.
+    }
     void setPrologue(Node pn) {}
 
     bool isConstant(Node pn) { return false; }
     PropertyName *isName(Node pn) {
         return (pn == NodeName) ? lastAtom->asPropertyName() : nullptr;
     }
     PropertyName *isGetProp(Node pn) {
         return (pn == NodeGetProp) ? lastAtom->asPropertyName() : nullptr;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -2,16 +2,18 @@
  * 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 gc_GCRuntime_h
 #define gc_GCRuntime_h
 
+#include "mozilla/Atomics.h"
+
 #include "jsgc.h"
 
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
 #include "gc/Statistics.h"
 #include "gc/StoreBuffer.h"
 #include "gc/Tracer.h"
 
@@ -478,17 +480,19 @@ class GCRuntime
     bool isIncrementalGc() { return isIncremental; }
     bool isFullGc() { return isFull; }
 
     bool shouldCleanUpEverything() { return cleanUpEverything; }
 
     bool areGrayBitsValid() { return grayBitsValid; }
     void setGrayBitsInvalid() { grayBitsValid = false; }
 
-    bool isGcNeeded() { return minorGCRequested || majorGCRequested; }
+    bool minorGCRequested() const { return minorGCTriggerReason != JS::gcreason::NO_REASON; }
+    bool majorGCRequested() const { return majorGCTriggerReason != JS::gcreason::NO_REASON; }
+    bool isGcNeeded() { return minorGCRequested() || majorGCRequested(); }
 
     double computeHeapGrowthFactor(size_t lastBytes);
     size_t computeTriggerBytes(double growthFactor, size_t lastBytes);
 
     JSGCMode gcMode() const { return mode; }
     void setGCMode(JSGCMode m) {
         mode = m;
         marker.setGCMode(mode);
@@ -524,17 +528,16 @@ class GCRuntime
     static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
     // Free certain LifoAlloc blocks from the background sweep thread.
     void freeUnusedLifoBlocksAfterSweeping(LifoAlloc *lifo);
     void freeAllLifoBlocksAfterSweeping(LifoAlloc *lifo);
 
     // Public here for ReleaseArenaLists and FinalizeTypedArenas.
     void releaseArena(ArenaHeader *aheader, const AutoLockGC &lock);
-    void decommitArena(ArenaHeader *aheader, AutoLockGC &lock);
 
     void releaseHeldRelocatedArenas();
 
   private:
     void minorGCImpl(JS::gcreason::Reason reason, Nursery::TypeObjectList *pretenureTypes);
 
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
@@ -694,20 +697,18 @@ class GCRuntime
     bool cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
     bool grayBitsValid;
 
-    volatile uintptr_t majorGCRequested;
-    JS::gcreason::Reason majorGCTriggerReason;
+    mozilla::Atomic<JS::gcreason::Reason, mozilla::Relaxed> majorGCTriggerReason;
 
-    bool minorGCRequested;
     JS::gcreason::Reason minorGCTriggerReason;
 
     /* Incremented at the start of every major GC. */
     uint64_t majorGCNumber;
 
     /* The major GC number at which to release observed type information. */
     uint64_t jitReleaseNumber;
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1736,22 +1736,16 @@ GCMarker::processMarkStackTop(SliceBudge
     }
 
     processMarkStackOther(tag, addr);
     return;
 
   scan_value_array:
     MOZ_ASSERT(vp <= end);
     while (vp != end) {
-        budget.step();
-        if (budget.isOverBudget()) {
-            pushValueArray(obj, vp, end);
-            return;
-        }
-
         const Value &v = *vp++;
         if (v.isString()) {
             markAndScanString(obj, v.toString());
         } else if (v.isObject()) {
             JSObject *obj2 = &v.toObject();
             if (markObject(obj, obj2)) {
                 pushValueArray(obj, vp, end);
                 obj = obj2;
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -1072,16 +1072,35 @@ function rsin_object(i) {
     var o = { valueOf: function() { return t; } };
     var x = Math.sin(o);
     t = 777;
     if (uceFault_sin_object(i) || uceFault_sin_object(i))
         assertEq(x, Math.sin(i));
     return i;
 }
 
+var uceFault_log_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_log_number'));
+function rlog_number(i) {
+    var x = Math.log(i);
+    if (uceFault_log_number(i) || uceFault_log_number(i))
+        assertEq(x, Math.log(99) /* log(99) */);
+    return i;
+}
+
+var uceFault_log_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_log_object'));
+function rlog_object(i) {
+    var t = i;
+    var o = { valueOf: function() { return t; } };
+    var x = Math.log(o); /* Evaluated with t == i, not t == 1000 */
+    t = 1000;
+    if (uceFault_log_object(i) || uceFault_log_object(i))
+        assertEq(x, Math.log(99) /* log(99) */);
+    return i;
+}
+
 for (i = 0; i < 100; i++) {
     rbitnot_number(i);
     rbitnot_object(i);
     rbitand_number(i);
     rbitand_object(i);
     rbitor_number(i);
     rbitor_object(i);
     rbitxor_number(i);
@@ -1175,16 +1194,18 @@ for (i = 0; i < 100; i++) {
     rtodouble_value(i);
     rtodouble_number(i);
     rtofloat32_number(i);
     rtofloat32_object(i);
     rhypot_number(i);
     rhypot_object(i);
     rsin_number(i);
     rsin_object(i);
+    rlog_number(i);
+    rlog_object(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
 
 function alignedAlloc($size, $alignment) {
     var $1 = $size + 4 | 0;
     var $2 = $alignment - 1 | 0;
--- a/js/src/jit/BaselineFrame.h
+++ b/js/src/jit/BaselineFrame.h
@@ -327,23 +327,27 @@ class BaselineFrame
     void setDebugModeOSRInfo(BaselineDebugModeOSRInfo *info) {
         flags_ |= HAS_DEBUG_MODE_OSR_INFO;
         debugModeOSRInfo_ = info;
     }
 
     void deleteDebugModeOSRInfo();
 
     // See the HAS_OVERRIDE_PC comment.
+    bool hasOverridePc() const {
+        return flags_ & HAS_OVERRIDE_PC;
+    }
+
     jsbytecode *overridePc() const {
-        MOZ_ASSERT(flags_ & HAS_OVERRIDE_PC);
+        MOZ_ASSERT(hasOverridePc());
         return script()->offsetToPC(overrideOffset_);
     }
 
     jsbytecode *maybeOverridePc() const {
-        if (flags_ & HAS_OVERRIDE_PC)
+        if (hasOverridePc())
             return overridePc();
         return nullptr;
     }
 
     void setOverridePc(jsbytecode *pc) {
         flags_ |= HAS_OVERRIDE_PC;
         overrideOffset_ = script()->pcToOffset(pc);
     }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -717,19 +717,24 @@ CodeGenerator::visitFunctionDispatch(LFu
     } else {
         casesWithFallback = mir->numCases() + 1;
         lastLabel = skipTrivialBlocks(mir->getFallback())->lir()->label();
     }
 
     // Compare function pointers, except for the last case.
     for (size_t i = 0; i < casesWithFallback - 1; i++) {
         MOZ_ASSERT(i < mir->numCases());
-        JSFunction *func = mir->getCase(i);
         LBlock *target = skipTrivialBlocks(mir->getCaseBlock(i))->lir();
-        masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
+        if (types::TypeObject *funcType = mir->getCaseTypeObject(i)) {
+            masm.branchPtr(Assembler::Equal, Address(input, JSObject::offsetOfType()),
+                           ImmGCPtr(funcType), target->label());
+        } else {
+            JSFunction *func = mir->getCase(i);
+            masm.branchPtr(Assembler::Equal, input, ImmGCPtr(func), target->label());
+        }
     }
 
     // Jump to the last case.
     masm.jump(lastLabel);
 }
 
 void
 CodeGenerator::visitTypeObjectDispatch(LTypeObjectDispatch *lir)
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -266,66 +266,62 @@ IonBuilder::getSingleCallTarget(types::T
     if (!obj || !obj->is<JSFunction>())
         return nullptr;
 
     return &obj->as<JSFunction>();
 }
 
 bool
 IonBuilder::getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
-                               ObjectVector &targets, uint32_t maxTargets, bool *gotLambda)
+                               ObjectVector &targets, uint32_t maxTargets)
 {
     MOZ_ASSERT(targets.empty());
-    MOZ_ASSERT(gotLambda);
-    *gotLambda = false;
 
     if (!calleeTypes)
         return true;
 
     if (calleeTypes->baseFlags() != 0)
         return true;
 
     unsigned objCount = calleeTypes->getObjectCount();
 
     if (objCount == 0 || objCount > maxTargets)
         return true;
 
     if (!targets.reserve(objCount))
         return false;
-    for(unsigned i = 0; i < objCount; i++) {
+    for (unsigned i = 0; i < objCount; i++) {
         JSObject *obj = calleeTypes->getSingleObject(i);
-        if (!obj) {
+        if (obj) {
+            MOZ_ASSERT(obj->hasSingletonType());
+        } else {
             types::TypeObject *typeObj = calleeTypes->getTypeObject(i);
             if (!typeObj)
                 continue;
 
             obj = typeObj->maybeInterpretedFunction();
             if (!obj) {
                 targets.clear();
                 return true;
             }
 
-            *gotLambda = true;
+            MOZ_ASSERT(!obj->hasSingletonType());
         }
 
         // Don't optimize if the callee is not callable or constructable per
         // the manner it is being invoked, so that CallKnown does not have to
         // handle these cases (they will always throw).
         if (constructing ? !obj->isConstructor() : !obj->isCallable()) {
             targets.clear();
             return true;
         }
 
         targets.infallibleAppend(obj);
     }
 
-    // For now, only inline "singleton" lambda calls
-    if (*gotLambda && targets.length() > 1)
-        targets.clear();
-
     return true;
 }
 
 IonBuilder::InliningDecision
 IonBuilder::DontInline(JSScript *targetScript, const char *reason)
 {
     if (targetScript) {
         JitSpew(JitSpew_Inlining, "Cannot inline %s:%u: %s",
@@ -4828,17 +4824,17 @@ IonBuilder::inlineSingleCall(CallInfo &c
 
     if (!inlineScriptedCall(callInfo, target))
         return InliningStatus_Error;
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
 IonBuilder::inlineCallsite(const ObjectVector &targets, ObjectVector &originals,
-                           bool lambda, CallInfo &callInfo)
+                           CallInfo &callInfo)
 {
     if (targets.empty())
         return InliningStatus_NotInlined;
 
     // Is the function provided by an MGetPropertyCache?
     // If so, the cache may be movable to a fallback path, with a dispatch
     // instruction guarding on the incoming TypeObject.
     WrapMGetPropertyCache propCache(getInlineableGetPropertyCache(callInfo));
@@ -4863,17 +4859,17 @@ IonBuilder::inlineCallsite(const ObjectV
         // Inlining will elminate uses of the original callee, but it needs to
         // be preserved in phis if we bail out.  Mark the old callee definition as
         // implicitly used to ensure this happens.
         callInfo.fun()->setImplicitlyUsedUnchecked();
 
         // If the callee is not going to be a lambda (which may vary across
         // different invocations), then the callee definition can be replaced by a
         // constant.
-        if (!lambda) {
+        if (target->hasSingletonType()) {
             // Replace the function with an MConstant.
             MConstant *constFun = constant(ObjectValue(*target));
             callInfo.setFun(constFun);
         }
 
         return inlineSingleCall(callInfo, target);
     }
 
@@ -5105,22 +5101,29 @@ IonBuilder::inlineCalls(CallInfo &callIn
             choiceSet[i] = false;
             continue;
         }
 
         MBasicBlock *inlineBlock = newBlock(dispatchBlock, pc);
         if (!inlineBlock)
             return false;
 
-        // Create a function MConstant to use in the entry ResumePoint.
-        MConstant *funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
+        // Create a function MConstant to use in the entry ResumePoint. If we
+        // can't use a constant, add a no-op MPolyInlineGuard, to prevent
+        // hoisting scope chain gets above the dispatch instruction.
+        MInstruction *funcDef;
+        if (target->hasSingletonType())
+            funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
+        else
+            funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
+
         funcDef->setImplicitlyUsedUnchecked();
         dispatchBlock->add(funcDef);
 
-        // Use the MConstant in the inline resume point and on stack.
+        // Use the inlined callee in the inline resume point and on stack.
         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
         inlineBlock->entryResumePoint()->replaceOperand(funIndex, funcDef);
         inlineBlock->rewriteSlot(funIndex, funcDef);
 
         // Create a new CallInfo to track modified state within the inline block.
         CallInfo inlineInfo(alloc(), callInfo.constructing());
         if (!inlineInfo.init(callInfo))
             return false;
@@ -5155,17 +5158,18 @@ IonBuilder::inlineCalls(CallInfo &callIn
         // inlineSingleCall() changed |current| to the inline return block.
         MBasicBlock *inlineReturnBlock = current;
         setCurrent(dispatchBlock);
 
         // Connect the inline path to the returnBlock.
         //
         // Note that guarding is on the original function pointer even
         // if there is a clone, since cloning occurs at the callsite.
-        dispatch->addCase(original, inlineBlock);
+        types::TypeObject *funType = original->hasSingletonType() ? nullptr : original->type();
+        dispatch->addCase(original, funType, inlineBlock);
 
         MDefinition *retVal = inlineReturnBlock->peek(-1);
         retPhi->addInput(retVal);
         inlineReturnBlock->end(MGoto::New(alloc(), returnBlock));
         if (!returnBlock->addPredecessorWithoutPhis(inlineReturnBlock))
             return false;
     }
 
@@ -5206,18 +5210,21 @@ IonBuilder::inlineCalls(CallInfo &callIn
             if (dispatch->numCases() + 1 == originals.length()) {
                 for (uint32_t i = 0; i < originals.length(); i++) {
                     if (choiceSet[i])
                         continue;
 
                     MOZ_ASSERT(!remaining);
 
                     if (targets[i]->is<JSFunction>()) {
-                        remaining = &targets[i]->as<JSFunction>();
-                        clonedAtCallsite = targets[i] != originals[i];
+                        JSFunction *target = &targets[i]->as<JSFunction>();
+                        if (target->hasSingletonType()) {
+                            remaining = target;
+                            clonedAtCallsite = target != originals[i];
+                        }
                     }
                     break;
                 }
             }
 
             if (!inlineGenericFallback(remaining, callInfo, dispatchBlock, clonedAtCallsite))
                 return false;
             dispatch->addFallback(current);
@@ -5636,23 +5643,19 @@ IonBuilder::jsop_call(uint32_t argc, boo
             observed->addType(types::Type::DoubleType(), alloc_->lifoAlloc());
         }
     }
 
     int calleeDepth = -((int)argc + 2);
 
     // Acquire known call target if existent.
     ObjectVector originals(alloc());
-    bool gotLambda = false;
     types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet();
-    if (calleeTypes) {
-        if (!getPolyCallTargets(calleeTypes, constructing, originals, 4, &gotLambda))
-            return false;
-    }
-    MOZ_ASSERT_IF(gotLambda, originals.length() <= 1);
+    if (calleeTypes && !getPolyCallTargets(calleeTypes, constructing, originals, 4))
+        return false;
 
     // If any call targets need to be cloned, look for existing clones to use.
     // Keep track of the originals as we need to case on them for poly inline.
     bool hasClones = false;
     ObjectVector targets(alloc());
     if (!targets.reserve(originals.length()))
         return false;
     for (uint32_t i = 0; i < originals.length(); i++) {
@@ -5671,17 +5674,17 @@ IonBuilder::jsop_call(uint32_t argc, boo
         targets.infallibleAppend(obj);
     }
 
     CallInfo callInfo(alloc(), constructing);
     if (!callInfo.init(current, argc))
         return false;
 
     // Try inlining
-    InliningStatus status = inlineCallsite(targets, originals, gotLambda, callInfo);
+    InliningStatus status = inlineCallsite(targets, originals, callInfo);
     if (status == InliningStatus_Inlined)
         return true;
     if (status == InliningStatus_Error)
         return false;
 
     // No inline, just make the call.
     JSFunction *target = nullptr;
     if (targets.length() == 1 && targets[0]->is<JSFunction>())
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -233,17 +233,17 @@ class IonBuilder
     bool inspectOpcode(JSOp op);
     uint32_t readIndex(jsbytecode *pc);
     JSAtom *readAtom(jsbytecode *pc);
     bool abort(const char *message, ...);
     void spew(const char *message);
 
     JSFunction *getSingleCallTarget(types::TemporaryTypeSet *calleeTypes);
     bool getPolyCallTargets(types::TemporaryTypeSet *calleeTypes, bool constructing,
-                            ObjectVector &targets, uint32_t maxTargets, bool *gotLambda);
+                            ObjectVector &targets, uint32_t maxTargets);
 
     void popCfgStack();
     DeferredEdge *filterDeadDeferredEdges(DeferredEdge *edge);
     bool processDeferredContinues(CFGState &state);
     ControlStatus processControlEnd();
     ControlStatus processCfgStack();
     ControlStatus processCfgEntry(CFGState &state);
     ControlStatus processIfEnd(CFGState &state);
@@ -812,17 +812,17 @@ class IonBuilder
     InliningStatus inlineNativeCall(CallInfo &callInfo, JSFunction *target);
     InliningStatus inlineNativeGetter(CallInfo &callInfo, JSFunction *target);
     InliningStatus inlineNonFunctionCall(CallInfo &callInfo, JSObject *target);
     bool inlineScriptedCall(CallInfo &callInfo, JSFunction *target);
     InliningStatus inlineSingleCall(CallInfo &callInfo, JSObject *target);
 
     // Call functions
     InliningStatus inlineCallsite(const ObjectVector &targets, ObjectVector &originals,
-                                  bool lambda, CallInfo &callInfo);
+                                  CallInfo &callInfo);
     bool inlineCalls(CallInfo &callInfo, const ObjectVector &targets, ObjectVector &originals,
                      BoolVector &choiceSet, MGetPropertyCache *maybeCache);
 
     // Inlining helpers.
     bool inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                bool clonedAtCallsite);
     bool inlineTypeObjectFallback(CallInfo &callInfo, MBasicBlock *dispatchBlock,
                                   MTypeObjectDispatch *dispatch, MGetPropertyCache *cache,
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -155,16 +155,22 @@ class JitFrameIterator
         return type_ == JitFrame_IonJS;
     }
     bool isBailoutJS() const {
         return type_ == JitFrame_Bailout;
     }
     bool isBaselineStub() const {
         return type_ == JitFrame_BaselineStub;
     }
+    bool isBaselineStubMaybeUnwound() const {
+        return type_ == JitFrame_BaselineStub || type_ == JitFrame_Unwound_BaselineStub;
+    }
+    bool isRectifierMaybeUnwound() const {
+        return type_ == JitFrame_Rectifier || type_ == JitFrame_Unwound_Rectifier;
+    }
     bool isBareExit() const;
     template <typename T> bool isExitFrameLayout() const;
 
     bool isEntry() const {
         return type_ == JitFrame_Entry;
     }
     bool isFunctionFrame() const;
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -1471,73 +1471,87 @@ void UpdateJitActivationsForMinorGC(PerT
     }
 }
 
 void
 GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
 {
     JitSpew(JitSpew_IonSnapshots, "Recover PC & Script from the last frame.");
 
+    // Recover the return address so that we can look it up in the
+    // PcScriptCache, as script/pc computation is expensive.
     JSRuntime *rt = cx->runtime();
-
-    // Recover the return address.
     JitActivationIterator iter(rt);
     JitFrameIterator it(iter);
-
-    // If the previous frame is a rectifier frame (maybe unwound),
-    // skip past it.
-    if (it.prevType() == JitFrame_Rectifier || it.prevType() == JitFrame_Unwound_Rectifier) {
-        ++it;
-        MOZ_ASSERT(it.prevType() == JitFrame_BaselineStub ||
-                   it.prevType() == JitFrame_BaselineJS ||
-                   it.prevType() == JitFrame_IonJS);
-    }
-
-    // If the previous frame is a stub frame, skip the exit frame so that
-    // returnAddress below gets the return address into the BaselineJS
-    // frame.
-    if (it.prevType() == JitFrame_BaselineStub || it.prevType() == JitFrame_Unwound_BaselineStub) {
+    uint8_t *retAddr;
+    if (it.type() == JitFrame_Exit) {
         ++it;
-        MOZ_ASSERT(it.prevType() == JitFrame_BaselineJS);
+
+        // Skip rectifier frames.
+        if (it.isRectifierMaybeUnwound()) {
+            ++it;
+            MOZ_ASSERT(it.isBaselineStub() || it.isBaselineJS() || it.isIonJS());
+        }
+
+        // Skip Baseline stub frames.
+        if (it.isBaselineStubMaybeUnwound()) {
+            ++it;
+            MOZ_ASSERT(it.isBaselineJS());
+        }
+
+        MOZ_ASSERT(it.isBaselineJS() || it.isIonJS());
+
+        // Don't use the return address if the BaselineFrame has an override pc.
+        // The override pc is cheap to get, so we won't benefit from the cache,
+        // and the override pc could change without the return address changing.
+        // Moreover, sometimes when an override pc is present during exception
+        // handling, the return address is set to nullptr as a sanity check,
+        // since we do not return to the frame that threw the exception.
+        if (!it.isBaselineJS() || !it.baselineFrame()->hasOverridePc()) {
+            retAddr = it.returnAddressToFp();
+            MOZ_ASSERT(retAddr);
+        } else {
+            retAddr = nullptr;
+        }
+    } else {
+        MOZ_ASSERT(it.isBailoutJS());
+        retAddr = it.returnAddress();
     }
 
-    uint8_t *retAddr = it.returnAddress();
-    uint32_t hash = PcScriptCache::Hash(retAddr);
-    MOZ_ASSERT(retAddr != nullptr);
-
-    // Lazily initialize the cache. The allocation may safely fail and will not GC.
-    if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) {
-        rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
-        if (rt->ionPcScriptCache)
-            rt->ionPcScriptCache->clear(rt->gc.gcNumber());
+    uint32_t hash;
+    if (retAddr) {
+        hash = PcScriptCache::Hash(retAddr);
+
+        // Lazily initialize the cache. The allocation may safely fail and will not GC.
+        if (MOZ_UNLIKELY(rt->ionPcScriptCache == nullptr)) {
+            rt->ionPcScriptCache = (PcScriptCache *)js_malloc(sizeof(struct PcScriptCache));
+            if (rt->ionPcScriptCache)
+                rt->ionPcScriptCache->clear(rt->gc.gcNumber());
+        }
+
+        if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes))
+            return;
     }
 
-    // Attempt to lookup address in cache.
-    if (rt->ionPcScriptCache && rt->ionPcScriptCache->get(rt, hash, retAddr, scriptRes, pcRes))
-        return;
-
     // Lookup failed: undertake expensive process to recover the innermost inlined frame.
-    if (!it.isBailoutJS())
-        ++it; // Skip exit frame.
     jsbytecode *pc = nullptr;
-
     if (it.isIonJS() || it.isBailoutJS()) {
         InlineFrameIterator ifi(cx, &it);
         *scriptRes = ifi.script();
         pc = ifi.pc();
     } else {
         MOZ_ASSERT(it.isBaselineJS());
         it.baselineScriptAndPc(scriptRes, &pc);
     }
 
     if (pcRes)
         *pcRes = pc;
 
     // Add entry to cache.
-    if (rt->ionPcScriptCache)
+    if (retAddr && rt->ionPcScriptCache)
         rt->ionPcScriptCache->add(hash, retAddr, pc, *scriptRes);
 }
 
 void
 OsiIndex::fixUpOffset(MacroAssembler &masm)
 {
     callPointDisplacement_ = masm.actualOffset(callPointDisplacement_);
 }
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3155,16 +3155,23 @@ LIRGenerator::visitGuardString(MGuardStr
 {
     // The type policy does all the work, so at this point the input
     // is guaranteed to be a string.
     MOZ_ASSERT(ins->input()->type() == MIRType_String);
     redefine(ins, ins->input());
 }
 
 void
+LIRGenerator::visitPolyInlineGuard(MPolyInlineGuard *ins)
+{
+    MOZ_ASSERT(ins->input()->type() == MIRType_Object);
+    redefine(ins, ins->input());
+}
+
+void
 LIRGenerator::visitGuardShapePolymorphic(MGuardShapePolymorphic *ins)
 {
     MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
     MOZ_ASSERT(ins->type() == MIRType_Object);
 
     LGuardShapePolymorphic *guard =
         new(alloc()) LGuardShapePolymorphic(useRegister(ins->obj()), temp());
     assignSnapshot(guard, Bailout_ShapeGuard);
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -215,16 +215,17 @@ class LIRGenerator : public LIRGenerator
     void visitSetPropertyPolymorphic(MSetPropertyPolymorphic *ins);
     void visitGetElementCache(MGetElementCache *ins);
     void visitBindNameCache(MBindNameCache *ins);
     void visitGuardObjectIdentity(MGuardObjectIdentity *ins);
     void visitGuardClass(MGuardClass *ins);
     void visitGuardObject(MGuardObject *ins);
     void visitGuardString(MGuardString *ins);
     void visitGuardShapePolymorphic(MGuardShapePolymorphic *ins);
+    void visitPolyInlineGuard(MPolyInlineGuard *ins);
     void visitAssertRange(MAssertRange *ins);
     void visitCallGetProperty(MCallGetProperty *ins);
     void visitDeleteProperty(MDeleteProperty *ins);
     void visitDeleteElement(MDeleteElement *ins);
     void visitGetNameCache(MGetNameCache *ins);
     void visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins);
     void visitCallsiteCloneCache(MCallsiteCloneCache *ins);
     void visitCallGetElement(MCallGetElement *ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -1137,16 +1137,28 @@ MStringLength::foldsTo(TempAllocator &al
         Value value = string()->constantValue();
         JSAtom *atom = &value.toString()->asAtom();
         return MConstant::New(alloc, Int32Value(atom->length()));
     }
 
     return this;
 }
 
+MDefinition *
+MConcat::foldsTo(TempAllocator &alloc)
+{
+    if (lhs()->isConstantValue() && lhs()->constantValue().toString()->empty())
+        return rhs();
+
+    if (rhs()->isConstantValue() && rhs()->constantValue().toString()->empty())
+        return lhs();
+
+    return this;
+}
+
 static bool
 EnsureFloatInputOrConvert(MUnaryInstruction *owner, TempAllocator &alloc)
 {
     MDefinition *input = owner->input();
     if (!input->canProduceFloat32()) {
         if (input->type() == MIRType_Float32)
             ConvertDefinitionToDouble<0>(alloc, input, owner);
         return false;
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4115,16 +4115,38 @@ class MGuardString
         return new(alloc) MGuardString(ins);
     }
 
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::None();
     }
 };
 
+class MPolyInlineGuard
+  : public MUnaryInstruction,
+    public SingleObjectPolicy::Data
+{
+    explicit MPolyInlineGuard(MDefinition *ins)
+      : MUnaryInstruction(ins)
+    {
+        setGuard();
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(PolyInlineGuard)
+
+    static MPolyInlineGuard *New(TempAllocator &alloc, MDefinition *ins) {
+        return new(alloc) MPolyInlineGuard(ins);
+    }
+    AliasSet getAliasSet() const MOZ_OVERRIDE {
+        return AliasSet::None();
+    }
+};
+
 class MAssertRange
   : public MUnaryInstruction,
     public NoTypePolicy::Data
 {
     // This is the range checked by the assertion. Don't confuse this with the
     // range_ member or the range() accessor. Since MAssertRange doesn't return
     // a value, it doesn't use those.
     const Range *assertedRange_;
@@ -5689,16 +5711,17 @@ class MMathFunction
         return function_ == Floor || function_ == Ceil || function_ == Round;
     }
     void trySpecializeFloat32(TempAllocator &alloc) MOZ_OVERRIDE;
     void computeRange(TempAllocator &alloc) MOZ_OVERRIDE;
     bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
     bool canRecoverOnBailout() const MOZ_OVERRIDE {
         switch(function_) {
           case Sin:
+          case Log:
           case Round:
             return true;
           default:
             return false;
         }
     }
 
     ALLOW_CLONE(MMathFunction)
@@ -6096,16 +6119,17 @@ class MConcat
     }
 
   public:
     INSTRUCTION_HEADER(Concat)
     static MConcat *New(TempAllocator &alloc, MDefinition *left, MDefinition *right) {
         return new(alloc) MConcat(left, right);
     }
 
+    MDefinition *foldsTo(TempAllocator &alloc) MOZ_OVERRIDE;
     bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::None();
     }
 
     bool writeRecoverData(CompactBufferWriter &writer) const MOZ_OVERRIDE;
@@ -9379,20 +9403,24 @@ class MSetPropertyPolymorphic
 
 class MDispatchInstruction
   : public MControlInstruction,
     public SingleObjectPolicy::Data
 {
     // Map from JSFunction* -> MBasicBlock.
     struct Entry {
         JSFunction *func;
+        // If |func| has a singleton type, |funcType| is null. Otherwise,
+        // |funcType| holds the TypeObject for |func|, and dispatch guards
+        // on the type instead of directly on the function.
+        types::TypeObject *funcType;
         MBasicBlock *block;
 
-        Entry(JSFunction *func, MBasicBlock *block)
-          : func(func), block(block)
+        Entry(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block)
+          : func(func), funcType(funcType), block(block)
         { }
     };
     Vector<Entry, 4, JitAllocPolicy> map_;
 
     // An optional fallback path that uses MCall.
     MBasicBlock *fallback_;
     MUse operand_;
 
@@ -9450,25 +9478,28 @@ class MDispatchInstruction
     MBasicBlock *getSuccessor(size_t i) const MOZ_FINAL MOZ_OVERRIDE {
         MOZ_ASSERT(i < numSuccessors());
         if (i == map_.length())
             return fallback_;
         return map_[i].block;
     }
 
   public:
-    void addCase(JSFunction *func, MBasicBlock *block) {
-        map_.append(Entry(func, block));
+    void addCase(JSFunction *func, types::TypeObject *funcType, MBasicBlock *block) {
+        map_.append(Entry(func, funcType, block));
     }
     uint32_t numCases() const {
         return map_.length();
     }
     JSFunction *getCase(uint32_t i) const {
         return map_[i].func;
     }
+    types::TypeObject *getCaseTypeObject(uint32_t i) const {
+        return map_[i].funcType;
+    }
     MBasicBlock *getCaseBlock(uint32_t i) const {
         return map_[i].block;
     }
 
     bool hasFallback() const {
         return bool(fallback_);
     }
     void addFallback(MBasicBlock *block) {
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -97,16 +97,17 @@ namespace jit {
     _(StringSplit)                                                          \
     _(Substr)                                                               \
     _(Return)                                                               \
     _(Throw)                                                                \
     _(Box)                                                                  \
     _(Unbox)                                                                \
     _(GuardObject)                                                          \
     _(GuardString)                                                          \
+    _(PolyInlineGuard)                                                      \
     _(AssertRange)                                                          \
     _(ToDouble)                                                             \
     _(ToFloat32)                                                            \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(ToObjectOrNull)                                                       \
     _(NewArray)                                                             \
--- a/js/src/jit/PcScriptCache.h
+++ b/js/src/jit/PcScriptCache.h
@@ -56,16 +56,19 @@ struct PcScriptCache
         *scriptRes = entries[hash].script;
         if (pcRes)
             *pcRes = entries[hash].pc;
 
         return true;
     }
 
     void add(uint32_t hash, uint8_t *addr, jsbytecode *pc, JSScript *script) {
+        MOZ_ASSERT(addr);
+        MOZ_ASSERT(pc);
+        MOZ_ASSERT(script);
         entries[hash].returnAddress = addr;
         entries[hash].pc = pc;
         entries[hash].script = script;
     }
 
     static uint32_t Hash(uint8_t *addr) {
         uint32_t key = (uint32_t)((uintptr_t)addr);
         return ((key >> 3) * 2654435761u) % Length;
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -905,16 +905,17 @@ bool
 MMathFunction::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     switch (function_) {
       case Round:
         writer.writeUnsigned(uint32_t(RInstruction::Recover_Round));
         return true;
       case Sin:
+      case Log:
         writer.writeUnsigned(uint32_t(RInstruction::Recover_MathFunction));
         writer.writeByte(function_);
         return true;
       default:
         MOZ_CRASH("Unknown math function.");
     }
 }
 
@@ -932,16 +933,26 @@ RMathFunction::recover(JSContext *cx, Sn
         RootedValue result(cx);
 
         if (!js::math_sin_handle(cx, arg, &result))
             return false;
 
         iter.storeInstructionResult(result);
         return true;
       }
+      case MMathFunction::Log: {
+        RootedValue arg(cx, iter.read());
+        RootedValue result(cx);
+
+        if (!js::math_log_handle(cx, arg, &result))
+            return false;
+
+        iter.storeInstructionResult(result);
+        return true;
+      }
       default:
         MOZ_CRASH("Unknown math function.");
     }
 }
 
 bool
 MStringSplit::writeRecoverData(CompactBufferWriter &writer) const
 {
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -2712,19 +2712,24 @@ Simulator::decodeType01(SimInstruction *
                 break;
               case 3: { // BLX
                 uint32_t old_pc = get_pc();
                 set_pc(get_register(rm));
                 set_register(lr, old_pc + SimInstruction::kInstrSize);
                 break;
               }
               case 7: { // BKPT
-                ArmDebugger dbg(this);
-                printf("Simulator hit BKPT.\n");
-                dbg.debug();
+                fprintf(stderr, "Simulator hit BKPT.\n");
+                if (getenv("ARM_SIM_DEBUGGER")) {
+                    ArmDebugger dbg(this);
+                    dbg.debug();
+                } else {
+                    fprintf(stderr, "Use ARM_SIM_DEBUGGER=1 to enter the builtin debugger.\n");
+                    MOZ_CRASH("ARM simulator breakpoint");
+                }
                 break;
               }
               default:
                 MOZ_CRASH();
             }
         } else if (instr->bits(22, 21) == 3) {
             int rm = instr->rmValue();
             int rd = instr->rdValue();
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1059,31 +1059,16 @@ void
 GCRuntime::releaseArena(ArenaHeader *aheader, const AutoLockGC &lock)
 {
     aheader->zone->usage.removeGCArena();
     if (isBackgroundSweeping())
         aheader->zone->threshold.updateForRemovedArena(tunables);
     return aheader->chunk()->releaseArena(rt, aheader, lock);
 }
 
-void
-GCRuntime::decommitArena(ArenaHeader *aheader, AutoLockGC &lock)
-{
-    aheader->zone->usage.removeGCArena();
-    if (isBackgroundSweeping())
-        aheader->zone->threshold.updateForRemovedArena(tunables);
-
-    bool ok;
-    {
-        AutoUnlockGC unlock(lock);
-        ok = MarkPagesUnused(aheader, ArenaSize);
-    }
-    return aheader->chunk()->releaseArena(rt, aheader, lock, Chunk::ArenaDecommitState(ok));
-}
-
 GCRuntime::GCRuntime(JSRuntime *rt) :
     rt(rt),
     systemZone(nullptr),
     nursery(rt),
     storeBuffer(rt, nursery),
     stats(rt),
     marker(rt),
     usage(nullptr),
@@ -1093,19 +1078,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     verifyPostData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(PRMJ_Now()),
     mode(JSGC_MODE_INCREMENTAL),
     decommitThreshold(32 * 1024 * 1024),
     cleanUpEverything(false),
     grayBitsValid(false),
-    majorGCRequested(0),
     majorGCTriggerReason(JS::gcreason::NO_REASON),
-    minorGCRequested(false),
     minorGCTriggerReason(JS::gcreason::NO_REASON),
     majorGCNumber(0),
     jitReleaseNumber(0),
     number(0),
     startNumber(0),
     isFull(false),
 #ifdef DEBUG
     disableStrictProxyCheckingCount(0),
@@ -2734,26 +2717,16 @@ ReleaseArenaList(JSRuntime *rt, ArenaHea
 {
     ArenaHeader *next;
     for (; aheader; aheader = next) {
         next = aheader->next;
         rt->gc.releaseArena(aheader, lock);
     }
 }
 
-void
-DecommitArenaList(JSRuntime *rt, ArenaHeader *aheader, AutoLockGC &lock)
-{
-    ArenaHeader *next;
-    for (; aheader; aheader = next) {
-        next = aheader->next;
-        rt->gc.decommitArena(aheader, lock);
-    }
-}
-
 ArenaLists::~ArenaLists()
 {
     AutoLockGC lock(runtime_);
 
     for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
         /*
          * We can only call this during the shutdown after the last GC when
          * the background finalization is disabled.
@@ -2788,17 +2761,17 @@ ArenaLists::forceFinalizeNow(FreeOp *fop
 {
     MOZ_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
 
     ArenaHeader *arenas = arenaLists[thingKind].head();
     if (!arenas)
         return;
     arenaLists[thingKind].clear();
 
-    const size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
+    size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
     SortedArenaList finalizedSorted(thingsPerArena);
 
     SliceBudget budget;
     FinalizeArenas(fop, &arenas, finalizedSorted, thingKind, budget, keepArenas);
     MOZ_ASSERT(!arenas);
 
     if (empty) {
         MOZ_ASSERT(keepArenas == KEEP_ARENAS);
@@ -2857,17 +2830,17 @@ ArenaLists::queueForBackgroundSweep(Free
 ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead, ArenaHeader **empty)
 {
     MOZ_ASSERT(listHead);
     MOZ_ASSERT(empty);
 
     AllocKind thingKind = listHead->getAllocKind();
     Zone *zone = listHead->zone;
 
-    const size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
+    size_t thingsPerArena = Arena::thingsPerArena(Arena::thingSize(thingKind));
     SortedArenaList finalizedSorted(thingsPerArena);
 
     SliceBudget budget;
     FinalizeArenas(fop, &listHead, finalizedSorted, thingKind, budget, KEEP_ARENAS);
     MOZ_ASSERT(!listHead);
 
     finalizedSorted.extractEmpty(empty);
 
@@ -3139,32 +3112,30 @@ void
 js::MarkCompartmentActive(InterpreterFrame *fp)
 {
     fp->script()->compartment()->zone()->active = true;
 }
 
 void
 GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
 {
-    if (majorGCRequested)
+    if (majorGCRequested())
         return;
 
-    majorGCRequested = true;
     majorGCTriggerReason = reason;
     rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
 }
 
 void
 GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
-    if (minorGCRequested)
+    if (minorGCRequested())
         return;
 
-    minorGCRequested = true;
     minorGCTriggerReason = reason;
     rt->requestInterrupt(JSRuntime::RequestInterruptUrgent);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /*
@@ -3189,17 +3160,17 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone 
     size_t usedBytes = zone->usage.gcBytes();
     size_t thresholdBytes = zone->threshold.gcTriggerBytes();
     size_t igcThresholdBytes = thresholdBytes * tunables.zoneAllocThresholdFactor();
 
     if (usedBytes >= thresholdBytes) {
         // The threshold has been surpassed, immediately trigger a GC,
         // which will be done non-incrementally.
         triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
-    } else if (usedBytes >= igcThresholdBytes && interFrameGC) {
+    } else if (usedBytes >= igcThresholdBytes) {
         // Reduce the delay to the start of the next incremental slice.
         if (zone->gcDelayBytes < ArenaSize)
             zone->gcDelayBytes = 0;
         else
             zone->gcDelayBytes -= ArenaSize;
 
         if (!zone->gcDelayBytes) {
             // Start or continue an in progress incremental GC. We do this
@@ -3397,17 +3368,17 @@ GCRuntime::sweepBackgroundThings(ZoneLis
                 ArenaHeader *arenas = zone->arenas.arenaListsToSweep[kind];
                 if (arenas)
                     ArenaLists::backgroundFinalize(&fop, arenas, &emptyArenas);
             }
         }
     }
 
     AutoLockGC lock(rt);
-    DecommitArenaList(rt, emptyArenas, lock);
+    ReleaseArenaList(rt, emptyArenas, lock);
     while (!zones.isEmpty())
         zones.removeFront();
 }
 
 void
 GCRuntime::assertBackgroundSweepingFinished()
 {
 #ifdef DEBUG
@@ -6054,17 +6025,17 @@ GCRuntime::gcCycle(bool incremental, Sli
     /*
      * Marking can trigger many incidental post barriers, some of them for
      * objects which are not going to be live after the GC.
      */
     AutoDisableStoreBuffer adsb(this);
 
     AutoTraceSession session(rt, MajorCollecting);
 
-    majorGCRequested = false;
+    majorGCTriggerReason = JS::gcreason::NO_REASON;
     interFrameGC = true;
 
     number++;
     if (!isIncrementalGCInProgress())
         majorGCNumber++;
 
     // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
@@ -6436,17 +6407,17 @@ GCRuntime::onOutOfMallocMemory(const Aut
     // might let the OS scrape together enough pages to satisfy the failing
     // malloc request.
     decommitAllWithoutUnlocking(lock);
 }
 
 void
 GCRuntime::minorGCImpl(JS::gcreason::Reason reason, Nursery::TypeObjectList *pretenureTypes)
 {
-    minorGCRequested = false;
+    minorGCTriggerReason = JS::gcreason::NO_REASON;
     TraceLoggerThread *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger_MinorGC);
     nursery.collect(rt, reason, pretenureTypes);
     MOZ_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 }
 
 // Alternate to the runtime-taking form that allows marking type objects as
 // needing pretenuring.
@@ -6485,24 +6456,24 @@ GCRuntime::enableGenerationalGC()
     }
 }
 
 bool
 GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */)
 {
     // This method returns whether a major GC was performed.
 
-    if (minorGCRequested) {
+    if (minorGCRequested()) {
         if (cx)
             minorGC(cx, minorGCTriggerReason);
         else
             minorGC(minorGCTriggerReason);
     }
 
-    if (majorGCRequested) {
+    if (majorGCRequested()) {
         if (!isIncrementalGCInProgress())
             startGC(GC_NORMAL, majorGCTriggerReason);
         else
             gcSlice(majorGCTriggerReason);
         return true;
     }
 
     return false;
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -314,19 +314,16 @@ js::math_ceil(JSContext *cx, unsigned ar
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
-    double x;
-    if (!ToNumber(cx, args[0], &x))
-        return false;
     return math_ceil_handle(cx, args[0], args.rval());
 }
 
 bool
 js::math_clz32(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -510,67 +507,68 @@ js::math_fround(JSContext *cx, unsigned 
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
-    float f;
-    if (!RoundFloat32(cx, args[0], &f))
-        return false;
-
-    args.rval().setDouble(static_cast<double>(f));
-    return true;
+    return RoundFloat32(cx, args[0], args.rval());
 }
 
 #if defined(SOLARIS) && defined(__GNUC__)
 #define LOG_IF_OUT_OF_RANGE(x) if (x < 0) return GenericNaN();
 #else
 #define LOG_IF_OUT_OF_RANGE(x)
 #endif
 
 double
 js::math_log_impl(MathCache *cache, double x)
 {
     LOG_IF_OUT_OF_RANGE(x);
-    return cache->lookup(log, x, MathCache::Log);
+    return cache->lookup(math_log_uncached, x, MathCache::Log);
 }
 
 double
 js::math_log_uncached(double x)
 {
     LOG_IF_OUT_OF_RANGE(x);
     return log(x);
 }
 
 #undef LOG_IF_OUT_OF_RANGE
 
 bool
+js::math_log_handle(JSContext *cx, HandleValue val, MutableHandleValue res)
+{
+    double in;
+    if (!ToNumber(cx, val, &in))
+        return false;
+
+    MathCache *mathCache = cx->runtime()->getMathCache(cx);
+    if (!mathCache)
+        return false;
+
+    double out = math_log_impl(mathCache, in);
+    res.setNumber(out);
+    return true;
+}
+
+bool
 js::math_log(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         args.rval().setNaN();
         return true;
     }
 
-    double x;
-    if (!ToNumber(cx, args[0], &x))
-        return false;
-
-    MathCache *mathCache = cx->runtime()->getMathCache(cx);
-    if (!mathCache)
-        return false;
-
-    double z = math_log_impl(mathCache, x);
-    args.rval().setNumber(z);
-    return true;
+    return math_log_handle(cx, args[0], args.rval());
 }
 
 double
 js::math_max_impl(double x, double y)
 {
     // Math.max(num, NaN) => NaN, Math.max(-0, +0) => +0
     if (x > y || IsNaN(x) || (x == y && IsNegative(y)))
         return x;
--- a/js/src/jsmath.h
+++ b/js/src/jsmath.h
@@ -166,16 +166,19 @@ math_log(JSContext *cx, unsigned argc, j
 
 extern double
 math_log_impl(MathCache *cache, double x);
 
 extern double
 math_log_uncached(double x);
 
 extern bool
+math_log_handle(JSContext *cx, HandleValue val, MutableHandleValue res);
+
+extern bool
 math_sin(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern double
 math_sin_impl(MathCache *cache, double x);
 
 extern double
 math_sin_uncached(double x);
 
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1399,17 +1399,18 @@ class JSScript : public js::gc::TenuredC
         return baselineOrIonRaw;
     }
     static size_t offsetOfBaselineOrIonSkipArgCheck() {
         return offsetof(JSScript, baselineOrIonSkipArgCheck);
     }
 
     bool isRelazifiable() const {
         return (selfHosted() || lazyScript) &&
-               !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() && !doNotRelazify_;
+               !isGenerator() && !hasBaselineScript() && !hasAnyIonScript() &&
+               !hasScriptCounts() && !doNotRelazify_;
     }
     void setLazyScript(js::LazyScript *lazy) {
         lazyScript = lazy;
     }
     js::LazyScript *maybeLazyScript() {
         return lazyScript;
     }
 
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -638,16 +638,21 @@ public:
     // When AllowResidualTranslation is false, display items will be drawn
     // scaled with a translation by integer pixels, so we know how the snapping
     // will work.
     mSnappingEnabled = aManager->IsSnappingEffectiveTransforms() &&
       !mParameters.AllowResidualTranslation();
     CollectOldLayers();
   }
 
+  ~ContainerState()
+  {
+    MOZ_ASSERT(mHoistedItems.IsEmpty());
+  }
+
   /**
    * This is the method that actually walks a display list and builds
    * the child layers.
    */
   void ProcessDisplayItems(nsDisplayList* aList);
   /**
    * This finalizes all the open PaintedLayers by popping every element off
    * mPaintedLayerDataStack, then sets the children of the container layer
@@ -714,16 +719,21 @@ public:
    * is in the coordinate space of the container reference frame.
    * aLayerContentsVisibleRect, if non-null, is in the layer's own
    * coordinate system.
    */
   void SetOuterVisibleRegionForLayer(Layer* aLayer,
                                      const nsIntRegion& aOuterVisibleRegion,
                                      const nsIntRect* aLayerContentsVisibleRect = nullptr) const;
 
+  void AddHoistedItem(nsDisplayItem* aItem)
+  {
+    mHoistedItems.AppendToTop(aItem);
+  }
+
 protected:
   friend class PaintedLayerData;
 
   /**
    * Grab the next recyclable PaintedLayer, or create one if there are no
    * more recyclable PaintedLayers. Does any necessary invalidation of
    * a recycled PaintedLayer, and sets up the transform on the PaintedLayer
    * to account for scrolling.
@@ -920,16 +930,21 @@ protected:
   AutoLayersArray                  mNewChildLayers;
   nsTArray<nsRefPtr<PaintedLayer> > mRecycledPaintedLayers;
   nsDataHashtable<nsPtrHashKey<Layer>, nsRefPtr<ImageLayer> >
     mRecycledMaskImageLayers;
   uint32_t                         mNextFreeRecycledPaintedLayer;
   nscoord                          mAppUnitsPerDevPixel;
   bool                             mSnappingEnabled;
   bool                             mFlattenToSingleLayer;
+  /**
+   * In some cases we need to hoist nsDisplayScrollInfoLayer items out from a
+   * nested inactive container. This holds the items hoisted up from children.
+   */
+  nsDisplayList                    mHoistedItems;
 };
 
 class PaintedDisplayItemLayerUserData : public LayerUserData
 {
 public:
   PaintedDisplayItemLayerUserData() :
     mMaskClipCount(0),
     mForcedBackgroundColor(NS_RGBA(0,0,0,0)),
@@ -1088,24 +1103,25 @@ FrameLayerBuilder::Shutdown()
   if (gMaskLayerImageCache) {
     delete gMaskLayerImageCache;
     gMaskLayerImageCache = nullptr;
   }
 }
 
 void
 FrameLayerBuilder::Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
-                        PaintedLayerData* aLayerData)
+                        PaintedLayerData* aLayerData, ContainerState* aContainingContainerState)
 {
   mDisplayListBuilder = aBuilder;
   mRootPresContext = aBuilder->RootReferenceFrame()->PresContext()->GetRootPresContext();
   if (mRootPresContext) {
     mInitialDOMGeneration = mRootPresContext->GetDOMGeneration();
   }
   mContainingPaintedLayer = aLayerData;
+  mContainingContainerState = aContainingContainerState;
   aManager->SetUserData(&gLayerManagerLayerBuilder, this);
 }
 
 void
 FrameLayerBuilder::FlashPaint(gfxContext *aContext)
 {
   float r = float(rand()) / RAND_MAX;
   float g = float(rand()) / RAND_MAX;
@@ -2210,17 +2226,21 @@ ContainerState::PopPaintedLayerData()
     SetOuterVisibleRegionForLayer(layer, data->mVisibleRegion);
   }
 
   nsIntRect layerBounds = data->mBounds;
   layerBounds.MoveBy(-GetTranslationForPaintedLayer(data->mLayer));
   layer->SetLayerBounds(layerBounds);
 
 #ifdef MOZ_DUMP_PAINTING
-  layer->AddExtraDumpInfo(nsCString(data->mLog));
+  if (PaintedLayerData* containingPld = mLayerBuilder->GetContainingPaintedLayerData()) {
+    containingPld->mLayer->AddExtraDumpInfo(nsCString(data->mLog));
+  } else {
+    layer->AddExtraDumpInfo(nsCString(data->mLog));
+  }
 #endif
 
   nsIntRegion transparentRegion;
   transparentRegion.Sub(data->mVisibleRegion, data->mOpaqueRegion);
   bool isOpaque = transparentRegion.IsEmpty();
   // For translucent PaintedLayers, try to find an opaque background
   // color that covers the entire area beneath it so we can pull that
   // color into this layer to make it opaque.
@@ -2858,31 +2878,44 @@ ContainerState::ProcessDisplayItems(nsDi
     nsDisplayList* itemSameCoordinateSystemChildren
       = item->GetSameCoordinateSystemChildren();
     if (item->ShouldFlattenAway(mBuilder)) {
       aList->AppendToBottom(itemSameCoordinateSystemChildren);
       item->~nsDisplayItem();
       continue;
     }
 
+    nsDisplayItem::Type itemType = item->GetType();
+    if (itemType == nsDisplayItem::TYPE_SCROLL_INFO_LAYER &&
+        mLayerBuilder->GetContainingContainerState()) {
+      // We have encountered a scrollable area inside a nested (inactive)
+      // layer manager, so we need to hoist the item out into the parent; that
+      // way we will still generate a scrollinfo layer for it and the APZ can
+      // drive main-thread sync scrolling.
+      // Note: |item| is removed from aList and will be attached into the parent
+      // list, so we don't delete it here.
+      static_cast<nsDisplayScrollInfoLayer*>(item)->MarkHoisted();
+      mLayerBuilder->GetContainingContainerState()->AddHoistedItem(item);
+      continue;
+    }
+
     savedItems.AppendToTop(item);
 
     NS_ASSERTION(mAppUnitsPerDevPixel == AppUnitsPerDevPixel(item),
       "items in a container layer should all have the same app units per dev pixel");
 
     if (mBuilder->NeedToForceTransparentSurfaceForItem(item)) {
       aList->SetNeedsTransparentSurface();
     }
 
     nsIntRect itemVisibleRect =
       ScaleToOutsidePixels(item->GetVisibleRect(), false);
     bool snap;
     nsRect itemContent = item->GetBounds(mBuilder, &snap);
     nsIntRect itemDrawRect = ScaleToOutsidePixels(itemContent, snap);
-    nsDisplayItem::Type itemType = item->GetType();
     bool prerenderedTransform = itemType == nsDisplayItem::TYPE_TRANSFORM &&
         static_cast<nsDisplayTransform*>(item)->ShouldPrerender(mBuilder);
     nsIntRect clipRect;
     const DisplayItemClip& itemClip = item->GetClip();
     if (itemClip.HasClip()) {
       itemContent.IntersectRect(itemContent, itemClip.GetClipRect());
       clipRect = ScaleToNearestPixels(itemClip.GetClipRect());
       if (!prerenderedTransform) {
@@ -3123,23 +3156,28 @@ ContainerState::ProcessDisplayItems(nsDi
             itemClip, aList,
             &paintedLayerData->mHideAllLayersBelow,
             &paintedLayerData->mOpaqueForAnimatedGeometryRootParent);
         paintedLayerData->Accumulate(this, item, opaquePixels,
             itemVisibleRect, itemDrawRect, itemClip);
       }
     }
 
+    // Finish the hoisting process by taking the items from the child and adding
+    // them to the list here.
+    aList->AppendToBottom(&mHoistedItems);
+
     if (itemSameCoordinateSystemChildren &&
         itemSameCoordinateSystemChildren->NeedsTransparentSurface()) {
       aList->SetNeedsTransparentSurface();
     }
   }
 
   aList->AppendToTop(&savedItems);
+  MOZ_ASSERT(mHoistedItems.IsEmpty());
 }
 
 void
 ContainerState::InvalidateForLayerChange(nsDisplayItem* aItem, PaintedLayer* aNewLayer)
 {
   NS_ASSERTION(aItem->GetPerFrameKey(),
                "Display items that render using Thebes must have a key");
   nsDisplayItemGeometry* oldGeometry = nullptr;
@@ -3274,17 +3312,17 @@ FrameLayerBuilder::ComputeGeometryChange
   aData->EndUpdate(geometry);
 }
 
 void
 FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData* aLayerData,
                                         nsDisplayItem* aItem,
                                         const DisplayItemClip& aClip,
                                         const nsIntRect& aItemVisibleRect,
-                                        const ContainerState& aContainerState,
+                                        ContainerState& aContainerState,
                                         LayerState aLayerState,
                                         const nsPoint& aTopLeft)
 {
   PaintedLayer* layer = aLayerData->mLayer;
   PaintedDisplayItemLayerUserData* paintedData =
     static_cast<PaintedDisplayItemLayerUserData*>
       (layer->GetUserData(&gPaintedDisplayItemLayerUserData));
   nsRefPtr<BasicLayerManager> tempManager;
@@ -3318,18 +3356,19 @@ FrameLayerBuilder::AddPaintedDisplayItem
 
   PaintedLayerItemsEntry* entry = mPaintedLayerItems.PutEntry(layer);
   if (entry) {
     entry->mContainerLayerFrame = aContainerState.GetContainerFrame();
     if (entry->mContainerLayerGeneration == 0) {
       entry->mContainerLayerGeneration = mContainerLayerGeneration;
     }
     if (tempManager) {
+      FLB_LOG_PAINTED_LAYER_DECISION(aLayerData, "Creating nested FLB for item %p\n", aItem);
       FrameLayerBuilder* layerBuilder = new FrameLayerBuilder();
-      layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData);
+      layerBuilder->Init(mDisplayListBuilder, tempManager, aLayerData, &aContainerState);
 
       tempManager->BeginTransaction();
       if (mRetainingManager) {
         layerBuilder->DidBeginRetainedLayerTransaction(tempManager);
       }
 
       UniquePtr<LayerProperties> props(LayerProperties::CloneFrom(tempManager->GetRoot()));
       nsRefPtr<Layer> tmpLayer =
--- a/layout/base/FrameLayerBuilder.h
+++ b/layout/base/FrameLayerBuilder.h
@@ -172,17 +172,18 @@ public:
   ~FrameLayerBuilder()
   {
     MOZ_COUNT_DTOR(FrameLayerBuilder);
   }
 
   static void Shutdown();
 
   void Init(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
-            PaintedLayerData* aLayerData = nullptr);
+            PaintedLayerData* aLayerData = nullptr,
+            ContainerState* aContainingContainerState = nullptr);
 
   /**
    * Call this to notify that we have just started a transaction on the
    * retained layer manager aManager.
    */
   void DidBeginRetainedLayerTransaction(LayerManager* aManager);
 
   /**
@@ -301,17 +302,17 @@ public:
    * for the container layer this ThebesItem belongs to.
    * aItem must have an underlying frame.
    * @param aTopLeft offset from active scrolled root to reference frame
    */
   void AddPaintedDisplayItem(PaintedLayerData* aLayer,
                             nsDisplayItem* aItem,
                             const DisplayItemClip& aClip,
                             const nsIntRect& aItemVisibleRect,
-                            const ContainerState& aContainerState,
+                            ContainerState& aContainerState,
                             LayerState aLayerState,
                             const nsPoint& aTopLeft);
 
   /**
    * Gets the frame property descriptor for the given manager, or for the current
    * widget layer manager if nullptr is passed.
    */
   static const FramePropertyDescriptor* GetDescriptorForManager(LayerManager* aManager);
@@ -631,16 +632,21 @@ public:
     return mContainingPaintedLayer;
   }
 
   bool IsBuildingRetainedLayers()
   {
     return !mContainingPaintedLayer && mRetainingManager;
   }
 
+  ContainerState* GetContainingContainerState()
+  {
+    return mContainingContainerState;
+  }
+
   /**
    * Attempt to build the most compressed layer tree possible, even if it means
    * throwing away existing retained buffers.
    */
   void SetLayerTreeCompressionMode() { mInLayerTreeCompressionMode = true; }
   bool CheckInLayerTreeCompressionMode();
 
   void ComputeGeometryChangeForItem(DisplayItemData* aData);
@@ -684,17 +690,19 @@ protected:
    * clipping data) to be rendered in the layer.
    */
   nsTHashtable<PaintedLayerItemsEntry> mPaintedLayerItems;
 
   /**
    * When building layers for an inactive layer, this is where the
    * inactive layer will be placed.
    */
-  PaintedLayerData*                    mContainingPaintedLayer;
+  PaintedLayerData*                   mContainingPaintedLayer;
+
+  ContainerState*                     mContainingContainerState;
 
   /**
    * Saved generation counter so we can detect DOM changes.
    */
   uint32_t                            mInitialDOMGeneration;
   /**
    * Set to true if we have detected and reported DOM modification during
    * the current paint.
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -9291,19 +9291,19 @@ nsCSSFrameConstructor::RecreateFramesFor
 
     if (frame->GetStateBits() & NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT) {
       // Recreate the frames for the entire nsIAnonymousContentCreator tree
       // since |frame| or one of its descendants may need an nsStyleContext
       // that associates it to a CSS pseudo-element, and only the
       // nsIAnonymousContentCreator that created this content knows how to make
       // that happen.
       nsIAnonymousContentCreator* acc = nullptr;
-      nsIFrame* ancestor = frame->GetParent();
+      nsIFrame* ancestor = nsLayoutUtils::GetParentOrPlaceholderFor(frame);
       while (!(acc = do_QueryFrame(ancestor))) {
-        ancestor = ancestor->GetParent();
+        ancestor = nsLayoutUtils::GetParentOrPlaceholderFor(ancestor);
       }
       NS_ASSERTION(acc, "Where is the nsIAnonymousContentCreator? We may fail "
                         "to recreate its content correctly");
       // nsSVGUseFrame is special, and we know this is unnecessary for it.
       if (ancestor->GetType() != nsGkAtoms::svgUseFrame) {
         NS_ASSERTION(aContent->IsInNativeAnonymousSubtree(),
                      "Why is NS_FRAME_ANONYMOUSCONTENTCREATOR_CONTENT set?");
         return RecreateFramesForContent(ancestor->GetContent(), aAsyncInsert,
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2154,17 +2154,19 @@ nsDisplayBackgroundImage::AppendBackgrou
   if (!isThemed) {
     bgSC = GetBackgroundStyleContext(aFrame);
     if (bgSC) {
       bg = bgSC->StyleBackground();
     }
   }
 
   bool drawBackgroundColor = false;
-  nscolor color;
+  // Dummy initialisation to keep Valgrind/Memcheck happy.
+  // See bug 1122375 comment 1.
+  nscolor color = NS_RGBA(0,0,0,0);
   if (!nsCSSRendering::IsCanvasFrame(aFrame) && bg) {
     bool drawBackgroundImage;
     color =
       nsCSSRendering::DetermineBackgroundColor(presContext, bgSC, aFrame,
                                                drawBackgroundImage, drawBackgroundColor);
   }
 
   const nsStyleBorder* borderStyle = aFrame->StyleBorder();
@@ -4595,16 +4597,17 @@ nsDisplayScrollLayer::WriteDebugInfo(std
           << " scrolledFrame " << mScrolledFrame << ")";
 }
 
 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aScrolledFrame,
   nsIFrame* aScrollFrame)
   : nsDisplayScrollLayer(aBuilder, aScrollFrame, aScrolledFrame, aScrollFrame)
+  , mHoisted(false)
 {
 #ifdef NS_BUILD_REFCNT_LOGGING
   MOZ_COUNT_CTOR(nsDisplayScrollInfoLayer);
 #endif
 }
 
 nsDisplayScrollInfoLayer::~nsDisplayScrollInfoLayer()
 {
@@ -4612,21 +4615,43 @@ nsDisplayScrollInfoLayer::~nsDisplayScro
 }
 
 nsRect
 nsDisplayScrollInfoLayer::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap)
 {
   return nsDisplayWrapList::GetBounds(aBuilder, aSnap);
 }
 
+already_AddRefed<Layer>
+nsDisplayScrollInfoLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
+                                     LayerManager* aManager,
+                                     const ContainerLayerParameters& aContainerParameters)
+{
+  // Only build scrollinfo layers if event-regions are disabled, so that the
+  // compositor knows where the inactive scrollframes are. When event-regions
+  // are enabled, the dispatch-to-content regions generally provide this
+  // information to the APZ code. However, in some cases, there might be
+  // content that cannot be layerized, and so needs to scroll synchronously.
+  // To handle those cases (which are indicated by setting mHoisted to true), we
+  // still want to generate scrollinfo layers.
+  if (gfxPrefs::LayoutEventRegionsEnabled() && !mHoisted) {
+    return nullptr;
+  }
+  return nsDisplayScrollLayer::BuildLayer(aBuilder, aManager, aContainerParameters);
+}
+
 LayerState
 nsDisplayScrollInfoLayer::GetLayerState(nsDisplayListBuilder* aBuilder,
                                         LayerManager* aManager,
                                         const ContainerLayerParameters& aParameters)
 {
+  // See comment in BuildLayer
+  if (gfxPrefs::LayoutEventRegionsEnabled() && !mHoisted) {
+    return LAYER_NONE;
+  }
   return LAYER_ACTIVE_EMPTY;
 }
 
 bool
 nsDisplayScrollInfoLayer::TryMerge(nsDisplayListBuilder* aBuilder,
                                    nsDisplayItem* aItem)
 {
   return false;
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -3205,25 +3205,34 @@ public:
   nsDisplayScrollInfoLayer(nsDisplayListBuilder* aBuilder,
                            nsIFrame* aScrolledFrame, nsIFrame* aScrollFrame);
   NS_DISPLAY_DECL_NAME("ScrollInfoLayer", TYPE_SCROLL_INFO_LAYER)
 
   virtual ~nsDisplayScrollInfoLayer();
 
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) MOZ_OVERRIDE;
 
+  virtual already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
+                                             LayerManager* aManager,
+                                             const ContainerLayerParameters& aContainerParameters) MOZ_OVERRIDE;
+
   virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters) MOZ_OVERRIDE;
   virtual bool ShouldBuildLayerEvenIfInvisible(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
   { return true; }
   virtual bool TryMerge(nsDisplayListBuilder* aBuilder,
                           nsDisplayItem* aItem) MOZ_OVERRIDE;
 
   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
+
+  void MarkHoisted() { mHoisted = true; }
+
+private:
+  bool mHoisted;
 };
 
 /**
  * nsDisplayZoom is used for subdocuments that have a different full zoom than
  * their parent documents. This item creates a container layer.
  */
 class nsDisplayZoom : public nsDisplaySubDocument {
 public:
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -59,16 +59,20 @@
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "nsIIPCBackgroundChildCreateCallback.h"
 #include "mozilla/layout/VsyncChild.h"
 #include "VsyncSource.h"
 #include "mozilla/VsyncDispatcher.h"
 #include "nsThreadUtils.h"
 #include "mozilla/unused.h"
 
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
+
 using namespace mozilla;
 using namespace mozilla::widget;
 using namespace mozilla::ipc;
 using namespace mozilla::layout;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo *gLog = nullptr;
 #define LOG(...) PR_LOG(gLog, PR_LOG_NOTICE, (__VA_ARGS__))
@@ -776,56 +780,76 @@ static RefreshDriverTimer* sRegularRateT
 static InactiveRefreshDriverTimer* sThrottledRateTimer;
 
 #ifdef XP_WIN
 static int32_t sHighPrecisionTimerRequests = 0;
 // a bare pointer to avoid introducing a static constructor
 static nsITimer *sDisableHighPrecisionTimersTimer = nullptr;
 #endif
 
-static RefreshDriverTimer*
-CreateVsyncRefreshTimer()
+static void
+CreateContentVsyncRefreshTimer(void*)
 {
-  // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
-  // ready.
-  gfxPrefs::GetSingleton();
-
-  if (!gfxPrefs::VsyncAlignedRefreshDriver()) {
-    return nullptr;
-  }
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!XRE_IsParentProcess());
 
-  if (XRE_IsParentProcess()) {
-    // Make sure all vsync systems are ready.
-    gfxPlatform::GetPlatform();
-    // In parent process, we don't need to use ipc. We can create the
-    // VsyncRefreshDriverTimer directly.
-    return new VsyncRefreshDriverTimer();
-  }
-
-  // For ChildProcess case.
-  // Create the PVsync actor for vsync-base refresh timer.
+  // Create the PVsync actor child for vsync-base refresh timer.
   // PBackgroundChild is created asynchronously. If PBackgroundChild is still
   // unavailable, setup VsyncChildCreateCallback callback to handle the async
   // connect. We will still use software timer before PVsync ready, and change
   // to use hw timer when the connection is done. Please check
   // VsyncChildCreateCallback::CreateVsyncActor() and
   // nsRefreshDriver::PVsyncActorCreated().
   PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
   if (backgroundChild) {
     // If we already have PBackgroundChild, create the
     // child VsyncRefreshDriverTimer here.
     VsyncChildCreateCallback::CreateVsyncActor(backgroundChild);
-    return sRegularRateTimer;
+    return;
   }
   // Setup VsyncChildCreateCallback callback
   nsRefPtr<nsIIPCBackgroundChildCreateCallback> callback = new VsyncChildCreateCallback();
   if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
     MOZ_CRASH("PVsync actor create failed!");
   }
-  return nullptr;
+}
+
+static void
+CreateVsyncRefreshTimer()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Sometimes, gfxPrefs is not initialized here. Make sure the gfxPrefs is
+  // ready.
+  gfxPrefs::GetSingleton();
+
+  if (!gfxPrefs::VsyncAlignedRefreshDriver()) {
+    return;
+  }
+
+  if (XRE_IsParentProcess()) {
+    // Make sure all vsync systems are ready.
+    gfxPlatform::GetPlatform();
+    // In parent process, we don't need to use ipc. We can create the
+    // VsyncRefreshDriverTimer directly.
+    sRegularRateTimer = new VsyncRefreshDriverTimer();
+    return;
+  }
+
+#ifdef MOZ_NUWA_PROCESS
+  // NUWA process will just use software timer. Use NuwaAddFinalConstructor()
+  // to register a callback to create the vsync-base refresh timer after a
+  // process is created.
+  if (IsNuwaProcess()) {
+    NuwaAddFinalConstructor(&CreateContentVsyncRefreshTimer, nullptr);
+    return;
+  }
+#endif
+  // If this process is not created by NUWA, just create the vsync timer here.
+  CreateContentVsyncRefreshTimer(nullptr);
 }
 
 static uint32_t
 GetFirstFrameDelay(imgIRequest* req)
 {
   nsCOMPtr<imgIContainer> container;
   if (NS_FAILED(req->GetImage(getter_AddRefs(container))) || !container) {
     return 0;
@@ -931,18 +955,18 @@ nsRefreshDriver::ChooseTimer() const
                                                            DEFAULT_INACTIVE_TIMER_DISABLE_SECONDS * 1000.0);
     return sThrottledRateTimer;
   }
 
   if (!sRegularRateTimer) {
     bool isDefault = true;
     double rate = GetRegularTimerInterval(&isDefault);
 
-    // Try to use vsync-base refresh timer first.
-    sRegularRateTimer = CreateVsyncRefreshTimer();
+    // Try to use vsync-base refresh timer first for sRegularRateTimer.
+    CreateVsyncRefreshTimer();
 
 #ifdef XP_WIN
     if (!sRegularRateTimer && PreciseRefreshDriverTimerWindowsDwmVsync::IsSupported()) {
       sRegularRateTimer = new PreciseRefreshDriverTimerWindowsDwmVsync(rate, isDefault);
     }
 #endif
     if (!sRegularRateTimer) {
       sRegularRateTimer = new PreciseRefreshDriverTimer(rate);
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -48,16 +48,34 @@ NS_NewCanvasFrame(nsIPresShell* aPresShe
 
 NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
 
 NS_QUERYFRAME_HEAD(nsCanvasFrame)
   NS_QUERYFRAME_ENTRY(nsCanvasFrame)
   NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
+void
+nsCanvasFrame::ShowCustomContentContainer()
+{
+  if (mCustomContentContainer) {
+    mCustomContentContainer->UnsetAttr(kNameSpaceID_None, nsGkAtoms::hidden, true);
+  }
+}
+
+void
+nsCanvasFrame::HideCustomContentContainer()
+{
+  if (mCustomContentContainer) {
+    mCustomContentContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::hidden,
+                                     NS_LITERAL_STRING("true"),
+                                     true);
+  }
+}
+
 nsresult
 nsCanvasFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements)
 {
   if (!mContent) {
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = mContent->OwnerDoc();
@@ -121,16 +139,21 @@ nsCanvasFrame::CreateAnonymousContent(ns
 
   // Append all existing AnonymousContent nodes stored at document level if any.
   size_t len = doc->GetAnonymousContents().Length();
   for (size_t i = 0; i < len; ++i) {
     nsCOMPtr<Element> node = doc->GetAnonymousContents()[i]->GetContentNode();
     mCustomContentContainer->AppendChildTo(node->AsContent(), true);
   }
 
+  // Only create a frame for mCustomContentContainer if it has some children.
+  if (len == 0) {
+    HideCustomContentContainer();
+  }
+
   return NS_OK;
 }
 
 void
 nsCanvasFrame::AppendAnonymousContentTo(nsTArray<nsIContent*>& aElements, uint32_t aFilter)
 {
   if (mTouchCaretElement) {
     aElements.AppendElement(mTouchCaretElement);
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -99,16 +99,28 @@ public:
     return mSelectionCaretsEndElement;
   }
 
   mozilla::dom::Element* GetCustomContentContainer() const
   {
     return mCustomContentContainer;
   }
 
+  /**
+   * Unhide the CustomContentContainer. This call only has an effect if
+   * mCustomContentContainer is non-null.
+   */
+  void ShowCustomContentContainer();
+
+  /**
+   * Hide the CustomContentContainer. This call only has an effect if
+   * mCustomContentContainer is non-null.
+   */
+  void HideCustomContentContainer();
+
   /** SetHasFocus tells the CanvasFrame to draw with focus ring
    *  @param aHasFocus true to show focus ring, false to hide it
    */
   NS_IMETHOD SetHasFocus(bool aHasFocus);
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -327,26 +327,28 @@ fuzzy-if(cocoaWidget&&layersGPUAccelerat
 == text-layout-07.svg text-layout-07-ref.svg
 == text-layout-08.svg text-layout-08-ref.svg
 == text-scale-01.svg text-scale-01-ref.svg
 HTTP(..) == text-scale-02.svg text-scale-02-ref.svg
 HTTP(..) == text-scale-03.svg text-scale-03-ref.svg
 == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
 == stroke-dasharray-01.svg stroke-dasharray-01-ref.svg
 == stroke-dasharray-02.svg pass.svg
+== stroke-dasharray-03.svg pass.svg
 == stroke-dasharray-and-pathLength-01.svg pass.svg
 == stroke-dasharray-and-text-01.svg stroke-dasharray-and-text-01-ref.svg
 == stroke-dashoffset-01.svg pass.svg
 == stroke-linecap-round-w-zero-length-segs-01.svg pass.svg
 == stroke-linecap-round-w-zero-length-segs-02.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-01.svg pass.svg
 == stroke-linecap-square-w-zero-length-segs-02.svg pass.svg
 == textPath-01.svg textPath-01-ref.svg
 == textPath-02.svg pass.svg
 == textPath-03.svg pass.svg
+== textPath-04.svg pass.svg
 == text-style-01a.svg text-style-01-ref.svg
 == text-style-01b.svg text-style-01-ref.svg
 == text-style-01c.svg text-style-01-ref.svg
 == text-style-01d.svg text-style-01-ref.svg
 == text-style-01e.svg text-style-01-ref.svg
 == text-white-space-01.svg text-white-space-01-ref.svg
 == thin-stroke-01.svg pass.svg
 == zero-stroke-01.svg pass.svg
copy from layout/reftests/svg/stroke-dasharray-02.svg
copy to layout/reftests/svg/stroke-dasharray-03.svg
--- a/layout/reftests/svg/stroke-dasharray-02.svg
+++ b/layout/reftests/svg/stroke-dasharray-03.svg
@@ -1,33 +1,20 @@
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <svg xmlns="http://www.w3.org/2000/svg">
 
-  <title>Test the start point and direction of dashing on circle and ellipse</title>
+  <title>Test the start point and direction of dashing on rect</title>
 
-  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=944704 -->
+  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=1122578 -->
 
   <rect width="100%" height="100%" fill="lime"/>
 
-  <!-- Test circle element dashes cover two red circles -->
-  <circle cx="100" cy="62" r="8" fill="red"/>
-  <circle cx="66" cy="98" r="8" fill="red"/>
-  <circle cx="50" cy="50" r="50" fill="none" stroke="lime" stroke-width="30" stroke-dasharray="25 25 25 100000"/>
-
-  <!-- Sanity test to check that two circles cover circle element dashes (i.e. that the previous check didn't pass because the stroke was solid) -->
-  <circle cx="200" cy="50" r="50" fill="none" stroke="red" stroke-width="10" stroke-dasharray="10 40 10 100000"/>
-  <circle cx="250" cy="56" r="11" fill="lime"/>
-  <circle cx="223" cy="96" r="11" fill="lime"/>
+  <rect x="20" y="20"  width="100" height="60" fill="none" stroke="red" stroke-width="10" stroke-dasharray="120,10000"/>
+  <path d="M20,20 h100 v20" fill="none" stroke="lime" stroke-width="10"/>
 
-  <!-- Test ellipse element dashes cover two red circles -->
-  <circle cx="95" cy="211" r="8" fill="red"/>
-  <circle cx="47" cy="225" r="8" fill="red"/>
-  <ellipse cx="50" cy="200" rx="50" ry="25" fill="none" stroke="lime" stroke-width="25" stroke-dasharray="25 25 25 100000"/>
-
-  <!-- Sanity test to check that two circles cover ellipse element dashes (i.e. that the previous check didn't pass because the stroke was solid) -->
-  <ellipse cx="200" cy="200" rx="50" ry="25" fill="none" stroke="red" stroke-width="10" stroke-dasharray="10 40 10 100000"/>
-  <circle cx="250" cy="205" r="11" fill="lime"/>
-  <circle cx="206" cy="225" r="11" fill="lime"/>
+  <rect x="20" y="100" width="100" height="60" fill="none" stroke="red" stroke-width="10" stroke-dasharray="120,10000" rx="10" ry="10"/>
+  <path d="M30,100 h80 a10,10 0 0 1 10,10 v25" fill="none" stroke="lime" stroke-width="14"/>
 
 </svg>
+
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/textPath-04.svg
@@ -0,0 +1,28 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <title>Test effect on display:none on path</title>
+
+  <defs>
+<!--
+    <path id="p" display="none" d="M100,100 h200"/>
+-->
+    <path id="p" d="M100,100 h200"/>
+  </defs>
+
+  <rect width="100%" height="100%" fill="lime"/>
+
+  <!-- check if something displays, its displayed in the right place -->
+  <text font-size="50" fill="red" transform="translate(0,100)">
+      <textPath xlink:href="#p">abc</textPath>
+  </text>
+  <text x="100" y="200" font-size="50" stroke-width="4" fill="lime" stroke="lime">abc</text>
+
+  <!-- check something displays -->
+  <text x="200" y="200" font-size="50" fill="red">abc</text>
+  <text font-size="50" stroke-width="4" stroke="lime" fill="lime" transform="translate(100,100)">
+      <textPath xlink:href="#p">abc</textPath>
+  </text>
+</svg>
--- a/layout/style/nsStyleContext.cpp
+++ b/layout/style/nsStyleContext.cpp
@@ -790,17 +790,20 @@ nsStyleContext::CalcStyleDifference(nsSt
     }
 
     // NB: Calling Peek on |this|, not |thisVis| (see above).
     if (!change && PeekStyleBorder()) {
       const nsStyleBorder *thisVisBorder = thisVis->StyleBorder();
       const nsStyleBorder *otherVisBorder = otherVis->StyleBorder();
       NS_FOR_CSS_SIDES(side) {
         bool thisFG, otherFG;
-        nscolor thisColor, otherColor;
+        // Dummy initialisations to keep Valgrind/Memcheck happy.
+        // See bug 1122375 comment 4.
+        nscolor thisColor = NS_RGBA(0, 0, 0, 0);
+        nscolor otherColor = NS_RGBA(0, 0, 0, 0);
         thisVisBorder->GetBorderColor(side, thisColor, thisFG);
         otherVisBorder->GetBorderColor(side, otherColor, otherFG);
         if (thisFG != otherFG || (!thisFG && thisColor != otherColor)) {
           change = true;
           break;
         }
       }
     }
@@ -830,17 +833,20 @@ nsStyleContext::CalcStyleDifference(nsSt
         change = true;
       }
     }
 
     // NB: Calling Peek on |this|, not |thisVis| (see above).
     if (!change && PeekStyleTextReset()) {
       const nsStyleTextReset *thisVisTextReset = thisVis->StyleTextReset();
       const nsStyleTextReset *otherVisTextReset = otherVis->StyleTextReset();
-      nscolor thisVisDecColor, otherVisDecColor;
+      // Dummy initialisations to keep Valgrind/Memcheck happy.
+      // See bug 1122375 comment 4.
+      nscolor thisVisDecColor = NS_RGBA(0, 0, 0, 0);
+      nscolor otherVisDecColor = NS_RGBA(0, 0, 0, 0);
       bool thisVisDecColorIsFG, otherVisDecColorIsFG;
       thisVisTextReset->GetDecorationColor(thisVisDecColor,
                                            thisVisDecColorIsFG);
       otherVisTextReset->GetDecorationColor(otherVisDecColor,
                                             otherVisDecColorIsFG);
       if (thisVisDecColorIsFG != otherVisDecColorIsFG ||
           (!thisVisDecColorIsFG && thisVisDecColor != otherVisDecColor)) {
         change = true;
--- a/layout/svg/SVGTextFrame.cpp
+++ b/layout/svg/SVGTextFrame.cpp
@@ -4811,18 +4811,18 @@ SVGTextFrame::AdjustPositionsForClusters
         mPositions[charIndex + 1].mStartOfChunk = true;
       }
     }
 
     it.Next();
   }
 }
 
-nsIFrame*
-SVGTextFrame::GetTextPathPathFrame(nsIFrame* aTextPathFrame)
+SVGPathElement*
+SVGTextFrame::GetTextPathPathElement(nsIFrame* aTextPathFrame)
 {
   nsSVGTextPathProperty *property = static_cast<nsSVGTextPathProperty*>
     (aTextPathFrame->Properties().Get(nsSVGEffects::HrefProperty()));
 
   if (!property) {
     nsIContent* content = aTextPathFrame->GetContent();
     dom::SVGTextPathElement* tp = static_cast<dom::SVGTextPathElement*>(content);
     nsAutoString href;
@@ -4837,31 +4837,29 @@ SVGTextFrame::GetTextPathPathFrame(nsIFr
                                               content->GetCurrentDoc(), base);
 
     property = nsSVGEffects::GetTextPathProperty(targetURI, aTextPathFrame,
                                                  nsSVGEffects::HrefProperty());
     if (!property)
       return nullptr;
   }
 
-  return property->GetReferencedFrame(nsGkAtoms::svgPathGeometryFrame, nullptr);
+  Element* element = property->GetReferencedElement();
+  return (element && element->IsSVG(nsGkAtoms::path)) ?
+    static_cast<SVGPathElement*>(element) : nullptr;
 }
 
 TemporaryRef<Path>
 SVGTextFrame::GetTextPath(nsIFrame* aTextPathFrame)
 {
-  nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
-
-  if (!pathFrame) {
+  SVGPathElement* element = GetTextPathPathElement(aTextPathFrame);
+  if (!element) {
     return nullptr;
   }
 
-  nsSVGPathGeometryElement *element =
-    static_cast<nsSVGPathGeometryElement*>(pathFrame->GetContent());
-
   RefPtr<Path> path = element->GetOrBuildPathForMeasuring();
   if (!path) {
     return nullptr;
   }
 
   gfxMatrix matrix = element->PrependLocalTransformsTo(gfxMatrix());
   if (!matrix.IsIdentity()) {
     RefPtr<PathBuilder> builder =
@@ -4870,22 +4868,21 @@ SVGTextFrame::GetTextPath(nsIFrame* aTex
   }
 
   return path.forget();
 }
 
 gfxFloat
 SVGTextFrame::GetOffsetScale(nsIFrame* aTextPathFrame)
 {
-  nsIFrame *pathFrame = GetTextPathPathFrame(aTextPathFrame);
-  if (!pathFrame)
+  SVGPathElement* pathElement = GetTextPathPathElement(aTextPathFrame);
+  if (!pathElement)
     return 1.0;
 
-  return static_cast<dom::SVGPathElement*>(pathFrame->GetContent())->
-    GetPathLengthScale(dom::SVGPathElement::eForTextPath);
+  return pathElement->GetPathLengthScale(dom::SVGPathElement::eForTextPath);
 }
 
 gfxFloat
 SVGTextFrame::GetStartOffset(nsIFrame* aTextPathFrame)
 {
   dom::SVGTextPathElement *tp =
     static_cast<dom::SVGTextPathElement*>(aTextPathFrame->GetContent());
   nsSVGLength2 *length =
--- a/layout/svg/SVGTextFrame.h
+++ b/layout/svg/SVGTextFrame.h
@@ -29,16 +29,17 @@ class CharIterator;
 class nsISVGPoint;
 class TextFrameIterator;
 class TextNodeCorrespondenceRecorder;
 struct TextRenderedRun;
 class TextRenderedRunIterator;
 
 namespace dom {
 class SVGIRect;
+class SVGPathElement;
 }
 
 /**
  * Information about the positioning for a single character in an SVG <text>
  * element.
  *
  * During SVG text layout, we use infinity values to represent positions and
  * rotations that are not explicitly specified with x/y/rotate attributes.
@@ -590,17 +591,18 @@ private:
    * the text frames.
    *
    * @param aShouldPaintSVGGlyphs (out) Whether SVG glyphs in the text
    *   should be painted.
    */
   bool ShouldRenderAsPath(nsTextFrame* aFrame, bool& aShouldPaintSVGGlyphs);
 
   // Methods to get information for a <textPath> frame.
-  nsIFrame* GetTextPathPathFrame(nsIFrame* aTextPathFrame);
+  mozilla::dom::SVGPathElement*
+  GetTextPathPathElement(nsIFrame* aTextPathFrame);
   mozilla::TemporaryRef<Path> GetTextPath(nsIFrame* aTextPathFrame);
   gfxFloat GetOffsetScale(nsIFrame* aTextPathFrame);
   gfxFloat GetStartOffset(nsIFrame* aTextPathFrame);
 
   DrawMode SetupContextPaint(const DrawTarget* aDrawTarget,
                              const gfxMatrix& aContextMatrix,
                              nsIFrame* aFrame,
                              gfxTextContextPaint* aOuterContextPaint,
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -480,18 +480,17 @@ nsJARChannel::NotifyError(nsresult aErro
 }
 
 void
 nsJARChannel::FireOnProgress(uint64_t aProgress)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mProgressSink);
 
-  mProgressSink->OnProgress(this, nullptr, aProgress,
-                            uint64_t(mContentLength));
+  mProgressSink->OnProgress(this, nullptr, aProgress, mContentLength);
 }
 
 nsresult
 nsJARChannel::SetRemoteNSPRFileDesc(PRFileDesc *fd)
 {
     PROsfd osfd = dup(PR_FileDesc2NativeHandle(fd));
     if (osfd == -1) {
         return NS_ERROR_FAILURE;
--- a/netwerk/base/public/nsIProgressEventSink.idl
+++ b/netwerk/base/public/nsIProgressEventSink.idl
@@ -22,39 +22,39 @@ interface nsIRequest;
  * The channel will begin passing notifications to the progress event sink
  * after its asyncOpen method has been called.  Notifications will cease once
  * the channel calls its listener's onStopRequest method or once the channel
  * is canceled (via nsIRequest::cancel).
  *
  * NOTE: This interface is actually not specific to channels and may be used
  * with other implementations of nsIRequest.
  */
-[scriptable, uuid(D974C99E-4148-4df9-8D98-DE834A2F6462)]
+[scriptable, uuid(87d55fba-cb7e-4f38-84c1-5c6c2b2a55e9)]
 interface nsIProgressEventSink : nsISupports
 {
     /**
      * Called to notify the event sink that progress has occurred for the
      * given request.
      *
      * @param aRequest
      *        the request being observed (may QI to nsIChannel).
      * @param aContext
      *        if aRequest is a channel, then this parameter is the listener
      *        context passed to nsIChannel::asyncOpen.
      * @param aProgress
      *        numeric value in the range 0 to aProgressMax indicating the
      *        number of bytes transfered thus far.
      * @param aProgressMax
      *        numeric value indicating maximum number of bytes that will be
-     *        transfered (or 0xFFFFFFFFFFFFFFFF if total is unknown).
+     *        transfered (or -1 if total is unknown).
      */
     void onProgress(in nsIRequest aRequest,
                     in nsISupports aContext,
-                    in unsigned long long aProgress,
-                    in unsigned long long aProgressMax);
+                    in long long aProgress,
+                    in long long aProgressMax);
 
     /**
      * Called to notify the event sink with a status message for the given
      * request.
      *
      * @param aRequest
      *        the request being observed (may QI to nsIChannel).
      * @param aContext
--- a/netwerk/base/public/nsITransport.idl
+++ b/netwerk/base/public/nsITransport.idl
@@ -19,17 +19,17 @@ interface nsIEventTarget;
  * inherent data transfer implied by this interface (i.e., data is being
  * transfered in some fashion via the streams exposed by this interface).
  *
  * A transport can have an event sink associated with it.  The event sink 
  * receives transport-specific events as the transfer is occuring.  For a
  * socket transport, these events can include status about the connection.
  * See nsISocketTransport for more info about socket transport specifics.
  */
-[scriptable, uuid(d8786c64-eb49-4a0b-b42c-0936a745fbe8)]
+[scriptable, uuid(2a8c6334-a5e6-4ec3-9865-1256541446fb)]
 interface nsITransport : nsISupports
 {
     /**
      * Open flags.
      */
     const unsigned long OPEN_BLOCKING   = 1<<0;
     const unsigned long OPEN_UNBUFFERED = 1<<1;
 
@@ -149,15 +149,15 @@ interface nsITransportEventSink : nsISup
      *        the transport status (resolvable to a string using
      *        nsIErrorService). See nsISocketTransport for socket specific
      *        status codes and more comments.
      * @param aProgress
      *        the amount of data either read or written depending on the value
      *        of the status code.  this value is relative to aProgressMax.
      * @param aProgressMax
      *        the maximum amount of data that will be read or written.  if
-     *        unknown, 0xFFFFFFFF will be passed.
+     *        unknown, -1 will be passed.
      */
     void onTransportStatus(in nsITransport aTransport,
                            in nsresult aStatus,
-                           in unsigned long long aProgress,
-                           in unsigned long long aProgressMax);
+                           in long long aProgress,
+                           in long long aProgressMax);
 };
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -2764,9 +2764,33 @@ NS_IsSrcdocChannel(nsIChannel *aChannel)
 bool NS_IsReasonableHTTPHeaderValue(const nsACString& aValue);
 
 /**
  * Return true if the given string is a valid HTTP token per RFC 2616 section
  * 2.2.
  */
 bool NS_IsValidHTTPToken(const nsACString& aToken);
 
+namespace mozilla {
+namespace net {
+
+const static uint64_t kJS_MAX_SAFE_UINTEGER = +9007199254740991ULL;
+const static  int64_t kJS_MIN_SAFE_INTEGER  = -9007199254740991LL;
+const static  int64_t kJS_MAX_SAFE_INTEGER  = +9007199254740991LL;
+
+// Make sure a 64bit value can be captured by JS MAX_SAFE_INTEGER
+inline bool
+InScriptableRange(int64_t val)
+{
+    return (val <= kJS_MAX_SAFE_INTEGER) && (val >= kJS_MIN_SAFE_INTEGER);
+}
+
+// Make sure a 64bit value can be captured by JS MAX_SAFE_INTEGER
+inline bool
+InScriptableRange(uint64_t val)
+{
+    return val <= kJS_MAX_SAFE_UINTEGER;
+}
+
+} // namespace mozilla
+} // namespace mozilla::net
+
 #endif // !nsNetUtil_h__
--- a/netwerk/base/src/Dashboard.cpp
+++ b/netwerk/base/src/Dashboard.cpp
@@ -163,17 +163,17 @@ public:
 
     nsString mStatus;
 };
 
 NS_IMPL_ISUPPORTS(ConnectionData, nsITransportEventSink, nsITimerCallback)
 
 NS_IMETHODIMP
 ConnectionData::OnTransportStatus(nsITransport *aTransport, nsresult aStatus,
-                                  uint64_t aProgress, uint64_t aProgressMax)
+                                  int64_t aProgress, int64_t aProgressMax)
 {
     if (aStatus == NS_NET_STATUS_CONNECTED_TO) {
         StopTimer();
     }
 
     GetErrorString(aStatus, mStatus);
     nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethodWithArg<nsRefPtr<ConnectionData> >
--- a/netwerk/base/src/nsBaseChannel.cpp
+++ b/netwerk/base/src/nsBaseChannel.cpp
@@ -648,17 +648,17 @@ nsBaseChannel::AsyncOpen(nsIStreamListen
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsBaseChannel::nsITransportEventSink
 
 NS_IMETHODIMP
 nsBaseChannel::OnTransportStatus(nsITransport *transport, nsresult status,
-                                 uint64_t progress, uint64_t progressMax)
+                                 int64_t progress, int64_t progressMax)
 {
   // In some cases, we may wish to suppress transport-layer status events.
 
   if (!mPump || NS_FAILED(mStatus)) {
     return NS_OK;
   }
 
   SUSPEND_PUMP_FOR_SCOPE();
@@ -791,29 +791,29 @@ nsBaseChannel::OnDataAvailable(nsIReques
                                nsIInputStream *stream, uint64_t offset,
                                uint32_t count)
 {
   SUSPEND_PUMP_FOR_SCOPE();
 
   nsresult rv = mListener->OnDataAvailable(this, mListenerContext, stream,
                                            offset, count);
   if (mSynthProgressEvents && NS_SUCCEEDED(rv)) {
-    uint64_t prog = offset + count;
+    int64_t prog = offset + count;
     if (NS_IsMainThread()) {
       OnTransportStatus(nullptr, NS_NET_STATUS_READING, prog, mContentLength);
     } else {
       class OnTransportStatusAsyncEvent : public nsRunnable
       {
         nsRefPtr<nsBaseChannel> mChannel;
-        uint64_t mProgress;
-        uint64_t mContentLength;
+        int64_t mProgress;
+        int64_t mContentLength;
       public:
         OnTransportStatusAsyncEvent(nsBaseChannel* aChannel,
-                                    uint64_t aProgress,
-                                    uint64_t aContentLength)
+                                    int64_t aProgress,
+                                    int64_t aContentLength)
           : mChannel(aChannel),
             mProgress(aProgress),
             mContentLength(aContentLength)
         { }
 
         NS_IMETHOD Run() MOZ_OVERRIDE
         {
           return mChannel->OnTransportStatus(nullptr, NS_NET_STATUS_READING,
--- a/netwerk/base/src/nsIncrementalDownload.cpp
+++ b/netwerk/base/src/nsIncrementalDownload.cpp
@@ -200,18 +200,18 @@ nsIncrementalDownload::FlushChunk()
 
 void
 nsIncrementalDownload::UpdateProgress()
 {
   mLastProgressUpdate = PR_Now();
 
   if (mProgressSink)
     mProgressSink->OnProgress(this, mObserverContext,
-                              uint64_t(int64_t(mCurrentSize) + mChunkLen),
-                              uint64_t(int64_t(mTotalSize)));
+                              mCurrentSize + mChunkLen,
+                              mTotalSize);
 }
 
 nsresult
 nsIncrementalDownload::CallOnStartRequest()
 {
   if (!mObserver || mDidOnStartRequest)
     return NS_OK;
 
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -978,18 +978,19 @@ nsSocketTransport::SendStatus(nsresult s
         case NS_NET_STATUS_RECEIVING_FROM:
             progress = mInput.ByteCount();
             break;
         default:
             progress = 0;
             break;
         }
     }
-    if (sink)
-        sink->OnTransportStatus(this, status, progress, UINT64_MAX);
+    if (sink) {
+        sink->OnTransportStatus(this, status, progress, -1);
+    }
 }
 
 nsresult
 nsSocketTransport::ResolveHost()
 {
     SOCKET_LOG(("nsSocketTransport::ResolveHost [this=%p %s:%d%s]\n",
                 this, SocketHost().get(), SocketPort(),
                 mConnectionFlags & nsSocketTransport::BYPASS_CACHE ?
--- a/netwerk/base/src/nsStreamTransportService.cpp
+++ b/netwerk/base/src/nsStreamTransportService.cpp
@@ -54,18 +54,18 @@ private:
     }
 
     nsCOMPtr<nsIAsyncInputStream>   mPipeIn;
 
     // while the copy is active, these members may only be accessed from the
     // nsIInputStream implementation.
     nsCOMPtr<nsITransportEventSink> mEventSink;
     nsCOMPtr<nsIInputStream>        mSource;
-    uint64_t                        mOffset;
-    uint64_t                        mLimit;
+    int64_t                         mOffset;
+    int64_t                         mLimit;
     bool                            mCloseWhenDone;
     bool                            mFirstTime;
 
     // this variable serves as a lock to prevent the state of the transport
     // from being modified once the copy is in progress.
     bool                            mInProgress;
 };
 
@@ -169,31 +169,34 @@ nsInputStreamTransport::Available(uint64
 
 NS_IMETHODIMP
 nsInputStreamTransport::Read(char *buf, uint32_t count, uint32_t *result)
 {
     if (mFirstTime) {
         mFirstTime = false;
         if (mOffset != 0) {
             // read from current position if offset equal to max
-            if (mOffset != UINT64_MAX) {
+            if (mOffset != -1) {
                 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
                 if (seekable)
                     seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
             }
             // reset offset to zero so we can use it to enforce limit
             mOffset = 0;
         }
     }
 
     // limit amount read
-    uint64_t max = mLimit - mOffset;
-    if (max == 0) {
-        *result = 0;
-        return NS_OK;
+    uint64_t max = count;
+    if (mLimit != -1) {
+        max = mLimit - mOffset;
+        if (max == 0) {
+            *result = 0;
+            return NS_OK;
+        }
     }
 
     if (count > max)
         count = static_cast<uint32_t>(max);
 
     nsresult rv = mSource->Read(buf, count, result);
 
     if (NS_SUCCEEDED(rv)) {
@@ -231,18 +234,18 @@ class nsOutputStreamTransport : public n
                               , public nsIOutputStream
 {
 public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSITRANSPORT
     NS_DECL_NSIOUTPUTSTREAM
 
     nsOutputStreamTransport(nsIOutputStream *sink,
-                            uint64_t offset,
-                            uint64_t limit,
+                            int64_t offset,
+                            int64_t limit,
                             bool closeWhenDone)
         : mSink(sink)
         , mOffset(offset)
         , mLimit(limit)
         , mCloseWhenDone(closeWhenDone)
         , mFirstTime(true)
         , mInProgress(false)
     {
@@ -254,18 +257,18 @@ private:
     }
 
     nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
  
     // while the copy is active, these members may only be accessed from the
     // nsIOutputStream implementation.
     nsCOMPtr<nsITransportEventSink> mEventSink;
     nsCOMPtr<nsIOutputStream>       mSink;
-    uint64_t                        mOffset;
-    uint64_t                        mLimit;
+    int64_t                         mOffset;
+    int64_t                         mLimit;
     bool                            mCloseWhenDone;
     bool                            mFirstTime;
 
     // this variable serves as a lock to prevent the state of the transport
     // from being modified once the copy is in progress.
     bool                            mInProgress;
 };
 
@@ -369,31 +372,34 @@ nsOutputStreamTransport::Flush()
 
 NS_IMETHODIMP
 nsOutputStreamTransport::Write(const char *buf, uint32_t count, uint32_t *result)
 {
     if (mFirstTime) {
         mFirstTime = false;
         if (mOffset != 0) {
             // write to current position if offset equal to max
-            if (mOffset != UINT64_MAX) {
+            if (mOffset != -1) {
                 nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
                 if (seekable)
                     seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
             }
             // reset offset to zero so we can use it to enforce limit
             mOffset = 0;
         }
     }
 
     // limit amount written
-    uint64_t max = mLimit - mOffset;
-    if (max == 0) {
-        *result = 0;
-        return NS_OK;
+    uint64_t max = count;
+    if (mLimit != -1) {
+        max = mLimit - mOffset;
+        if (max == 0) {
+            *result = 0;
+            return NS_OK;
+        }
     }
 
     if (count > max)
         count = static_cast<uint32_t>(max);
 
     nsresult rv = mSink->Write(buf, count, result);
 
     if (NS_SUCCEEDED(rv)) {
--- a/netwerk/base/src/nsTransportUtils.cpp
+++ b/netwerk/base/src/nsTransportUtils.cpp
@@ -51,18 +51,18 @@ public:
 };
 
 class nsTransportStatusEvent : public nsRunnable
 {
 public:
     nsTransportStatusEvent(nsTransportEventSinkProxy *proxy,
                            nsITransport *transport,
                            nsresult status,
-                           uint64_t progress,
-                           uint64_t progressMax)
+                           int64_t progress,
+                           int64_t progressMax)
         : mProxy(proxy)
         , mTransport(transport)
         , mStatus(status)
         , mProgress(progress)
         , mProgressMax(progressMax)
     {}
 
     ~nsTransportStatusEvent() {}
@@ -82,27 +82,27 @@ public:
         return NS_OK;
     }
 
     nsRefPtr<nsTransportEventSinkProxy> mProxy;
 
     // parameters to OnTransportStatus
     nsCOMPtr<nsITransport> mTransport;
     nsresult               mStatus;
-    uint64_t               mProgress;
-    uint64_t               mProgressMax;
+    int64_t                mProgress;
+    int64_t                mProgressMax;
 };
 
 NS_IMPL_ISUPPORTS(nsTransportEventSinkProxy, nsITransportEventSink)
 
 NS_IMETHODIMP
 nsTransportEventSinkProxy::OnTransportStatus(nsITransport *transport,
                                              nsresult status,
-                                             uint64_t progress,
-                                             uint64_t progressMax)
+                                             int64_t progress,
+                                             int64_t progressMax)
 {
     nsresult rv = NS_OK;
     nsRefPtr<nsTransportStatusEvent> event;
     {
         MutexAutoLock lock(mLock);