Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 20 Jan 2015 22:12:46 -0500
changeset 242262 92dcd34c8aa12103e0173645f09d6fa0dacfa9fd
parent 242176 0bca66c907ce7a9a065e80fd64629f33fa5d476f (current diff)
parent 242261 c8b693e4b0de9477b0c33c9d852118de201ab48c (diff)
child 242263 540077a308669a42c0d8fe7dbd43d4cc36c9a5ff
push id7677
push userraliiev@mozilla.com
push dateMon, 23 Feb 2015 18:11:24 +0000
treeherdermozilla-aurora@f531d838c055 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
Merge inbound to m-c. a=merge
browser/app/profile/firefox.js
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/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/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/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/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/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)
 {
     /*
@@ -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);
 
         // try to coalesce events! ;-)
         if (mLastEvent && (mCoalesceAll || mLastEvent->mStatus == status)) {
--- a/netwerk/protocol/file/nsFileChannel.cpp
+++ b/netwerk/protocol/file/nsFileChannel.cpp
@@ -17,16 +17,19 @@
 #include "nsAutoPtr.h"
 #include "nsIContentPolicy.h"
 #include "nsContentUtils.h"
 
 #include "nsIFileURL.h"
 #include "nsIMIMEService.h"
 #include <algorithm>
 
+using namespace mozilla;
+using namespace mozilla::net;
+
 //-----------------------------------------------------------------------------
 
 class nsFileCopyEvent : public nsRunnable {
 public:
   nsFileCopyEvent(nsIOutputStream *dest, nsIInputStream *source, int64_t len)
     : mDest(dest)
     , mSource(source)
     , mLen(len)
@@ -448,18 +451,19 @@ nsFileChannel::SetUploadStream(nsIInputS
   if ((mUploadStream = stream)) {
     mUploadLength = contentLength;
     if (mUploadLength < 0) {
       // Make sure we know how much data we are uploading.
       uint64_t avail;
       nsresult rv = mUploadStream->Available(&avail);
       if (NS_FAILED(rv))
         return rv;
-      if (avail < INT64_MAX)
-        mUploadLength = avail;
+      // if this doesn't fit in the javascript MAX_SAFE_INTEGER
+      // pretend we don't know the size
+      mUploadLength = InScriptableRange(avail) ? avail : -1;
     }
   } else {
     mUploadLength = -1;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -75,17 +75,17 @@ NS_IMPL_ISUPPORTS_INHERITED(nsFtpState,
 nsFtpState::nsFtpState()
     : nsBaseContentStream(true)
     , mState(FTP_INIT)
     , mNextState(FTP_S_USER)
     , mKeepRunning(true)
     , mReceivedControlData(false)
     , mTryingCachedControl(false)
     , mRETRFailed(false)
-    , mFileSize(UINT64_MAX)
+    , mFileSize(kJS_MAX_SAFE_UINTEGER)
     , mServerType(FTP_GENERIC_TYPE)
     , mAction(GET)
     , mAnonymous(true)
     , mRetryPass(false)
     , mStorReplyReceived(false)
     , mInternalError(NS_OK)
     , mReconnectAndLoginAgain(false)
     , mCacheConnection(true)
@@ -2164,17 +2164,17 @@ nsFtpState::ConvertDirspecFromVMS(nsCStr
     }
     LOG(("FTP:(%x) ConvertDirspecFromVMS   to: \"%s\"\n", this, dirSpec.get()));
 }
 
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsFtpState::OnTransportStatus(nsITransport *transport, nsresult status,
-                              uint64_t progress, uint64_t progressMax)
+                              int64_t progress, int64_t progressMax)
 {
     // Mix signals from both the control and data connections.
 
     // Ignore data transfer events on the control connection.
     if (mControlConnection && transport == mControlConnection->Transport()) {
         switch (status) {
         case NS_NET_STATUS_RESOLVING_HOST:
         case NS_NET_STATUS_RESOLVED_HOST:
--- a/netwerk/protocol/http/Http2Push.cpp
+++ b/netwerk/protocol/http/Http2Push.cpp
@@ -276,17 +276,17 @@ Http2PushTransactionBuffer::Connection()
 void
 Http2PushTransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
 {
   *outCB = nullptr;
 }
 
 void
 Http2PushTransactionBuffer::OnTransportStatus(nsITransport* transport,
-                                              nsresult status, uint64_t progress)
+                                              nsresult status, int64_t progress)
 {
 }
 
 nsHttpConnectionInfo *
 Http2PushTransactionBuffer::ConnectionInfo()
 {
   if (!mPushStream) {
     return nullptr;
--- a/netwerk/protocol/http/Http2Session.cpp
+++ b/netwerk/protocol/http/Http2Session.cpp
@@ -2220,17 +2220,17 @@ Http2Session::RecvAltSvc(Http2Session *s
 
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
 void
 Http2Session::OnTransportStatus(nsITransport* aTransport,
-                                nsresult aStatus, uint64_t aProgress)
+                                nsresult aStatus, int64_t aProgress)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   switch (aStatus) {
     // These should appear only once, deliver to the first
     // transaction on the session.
   case NS_NET_STATUS_RESOLVING_HOST:
   case NS_NET_STATUS_RESOLVED_HOST:
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -526,17 +526,17 @@ HttpChannelChild::DoOnStatus(nsIRequest*
     nsAutoCString host;
     mURI->GetHost(host);
     mProgressSink->OnStatus(aRequest, nullptr, status,
                             NS_ConvertUTF8toUTF16(host).get());
   }
 }
 
 void
-HttpChannelChild::DoOnProgress(nsIRequest* aRequest, uint64_t progress, uint64_t progressMax)
+HttpChannelChild::DoOnProgress(nsIRequest* aRequest, int64_t progress, int64_t progressMax)
 {
   LOG(("HttpChannelChild::DoOnProgress [this=%p]\n", this));
   if (mCanceled)
     return;
 
   // cache the progress sink so we don't have to query for it each time.
   if (!mProgressSink)
     GetCallback(mProgressSink);
@@ -544,17 +544,18 @@ HttpChannelChild::DoOnProgress(nsIReques
   // block status/progress after Cancel or OnStopRequest has been called,
   // or if channel has LOAD_BACKGROUND set.
   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending &&
       !(mLoadFlags & LOAD_BACKGROUND))
   {
     // OnProgress
     //
     if (progress > 0) {
-      MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
+      MOZ_ASSERT((progressMax == -1) || (progress <= progressMax),
+                 "unexpected progress values");
       mProgressSink->OnProgress(aRequest, nullptr, progress, progressMax);
     }
   }
 }
 
 void
 HttpChannelChild::DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                     nsIInputStream* aStream,
@@ -687,62 +688,63 @@ HttpChannelChild::DoOnStopRequest(nsIReq
   if (mLoadGroup)
     mLoadGroup->RemoveRequest(this, nullptr, mStatus);
 }
 
 class ProgressEvent : public ChannelEvent
 {
  public:
   ProgressEvent(HttpChannelChild* child,
-                const uint64_t& progress,
-                const uint64_t& progressMax)
+                const int64_t& progress,
+                const int64_t& progressMax)
   : mChild(child)
   , mProgress(progress)
   , mProgressMax(progressMax) {}
 
   void Run() { mChild->OnProgress(mProgress, mProgressMax); }
  private:
   HttpChannelChild* mChild;
-  uint64_t mProgress, mProgressMax;
+  int64_t mProgress, mProgressMax;
 };
 
 bool
-HttpChannelChild::RecvOnProgress(const uint64_t& progress,
-                                 const uint64_t& progressMax)
+HttpChannelChild::RecvOnProgress(const int64_t& progress,
+                                 const int64_t& progressMax)
 {
   if (mEventQ->ShouldEnqueue())  {
     mEventQ->Enqueue(new ProgressEvent(this, progress, progressMax));
   } else {
     OnProgress(progress, progressMax);
   }
   return true;
 }
 
 void
-HttpChannelChild::OnProgress(const uint64_t& progress,
-                             const uint64_t& progressMax)
+HttpChannelChild::OnProgress(const int64_t& progress,
+                             const int64_t& progressMax)
 {
-  LOG(("HttpChannelChild::OnProgress [this=%p progress=%llu/%llu]\n",
+  LOG(("HttpChannelChild::OnProgress [this=%p progress=%lld/%lld]\n",
        this, progress, progressMax));
 
   if (mCanceled)
     return;
 
   // cache the progress sink so we don't have to query for it each time.
   if (!mProgressSink) {
     GetCallback(mProgressSink);
   }
 
   AutoEventEnqueuer ensureSerialDispatch(mEventQ);
 
   // Block socket status event after Cancel or OnStopRequest has been called.
   if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending)
   {
     if (progress > 0) {
-      MOZ_ASSERT(progress <= progressMax, "unexpected progress values");
+      MOZ_ASSERT((progressMax == -1) || (progress <= progressMax),
+                 "unexpected progress values");
       mProgressSink->OnProgress(this, nullptr, progress, progressMax);
     }
   }
 }
 
 class StatusEvent : public ChannelEvent
 {
  public:
@@ -1368,17 +1370,17 @@ InterceptStreamListener::OnStatus(nsIReq
                                   nsresult status, const char16_t* aStatusArg)
 {
   mOwner->DoOnStatus(mOwner, status);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptStreamListener::OnProgress(nsIRequest* aRequest, nsISupports* aContext,
-                                    uint64_t aProgress, uint64_t aProgressMax)
+                                    int64_t aProgress, int64_t aProgressMax)
 {
   mOwner->DoOnProgress(mOwner, aProgress, aProgressMax);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptStreamListener::OnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
                                          nsIInputStream* aInputStream, uint64_t aOffset,
@@ -1391,19 +1393,18 @@ InterceptStreamListener::OnDataAvailable
     nsCOMPtr<nsIURI> uri;
     mOwner->GetURI(getter_AddRefs(uri));
 
     nsAutoCString host;
     uri->GetHost(host);
 
     OnStatus(mOwner, aContext, NS_NET_STATUS_READING, NS_ConvertUTF8toUTF16(host).get());
 
-    uint64_t progressMax(uint64_t(mOwner->GetResponseHead()->ContentLength()));
-    uint64_t progress = aOffset + uint64_t(aCount);
-    OnProgress(mOwner, aContext, progress, progressMax);
+    int64_t progress = aOffset + aCount;
+    OnProgress(mOwner, aContext, progress, mOwner->GetResponseHead()->ContentLength());
   }
 
   mOwner->DoOnDataAvailable(mOwner, mContext, aInputStream, aOffset, aCount);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptStreamListener::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext, nsresult aStatusCode)
--- a/netwerk/protocol/http/HttpChannelChild.h
+++ b/netwerk/protocol/http/HttpChannelChild.h
@@ -121,17 +121,17 @@ protected:
   bool RecvOnTransportAndData(const nsresult& channelStatus,
                               const nsresult& status,
                               const uint64_t& progress,
                               const uint64_t& progressMax,
                               const nsCString& data,
                               const uint64_t& offset,
                               const uint32_t& count) MOZ_OVERRIDE;
   bool RecvOnStopRequest(const nsresult& statusCode, const ResourceTimingStruct& timing) MOZ_OVERRIDE;
-  bool RecvOnProgress(const uint64_t& progress, const uint64_t& progressMax) MOZ_OVERRIDE;
+  bool RecvOnProgress(const int64_t& progress, const int64_t& progressMax) MOZ_OVERRIDE;
   bool RecvOnStatus(const nsresult& status) MOZ_OVERRIDE;
   bool RecvFailedAsyncOpen(const nsresult& status) MOZ_OVERRIDE;
   bool RecvRedirect1Begin(const uint32_t& newChannel,
                           const URIParams& newURI,
                           const uint32_t& redirectFlags,
                           const nsHttpResponseHead& responseHead) MOZ_OVERRIDE;
   bool RecvRedirect3Complete() MOZ_OVERRIDE;
   bool RecvAssociateApplicationCache(const nsCString& groupID,
@@ -143,17 +143,17 @@ protected:
   bool GetAssociatedContentSecurity(nsIAssociatedContentSecurity** res = nullptr);
   virtual void DoNotifyListenerCleanup() MOZ_OVERRIDE;
 
 private:
   nsresult ContinueAsyncOpen();
 
   void DoOnStartRequest(nsIRequest* aRequest, nsISupports* aContext);
   void DoOnStatus(nsIRequest* aRequest, nsresult status);
-  void DoOnProgress(nsIRequest* aRequest, uint64_t progress, uint64_t progressMax);
+  void DoOnProgress(nsIRequest* aRequest, int64_t progress, int64_t progressMax);
   void DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext, nsIInputStream* aStream,
                          uint64_t offset, uint32_t count);
   void DoPreOnStopRequest(nsresult aStatus);
   void DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext);
 
   // Discard the prior interception and continue with the original network request.
   void ResetInterception();
 
@@ -207,17 +207,17 @@ private:
   void OnTransportAndData(const nsresult& channelStatus,
                           const nsresult& status,
                           const uint64_t progress,
                           const uint64_t& progressMax,
                           const nsCString& data,
                           const uint64_t& offset,
                           const uint32_t& count);
   void OnStopRequest(const nsresult& channelStatus, const ResourceTimingStruct& timing);
-  void OnProgress(const uint64_t& progress, const uint64_t& progressMax);
+  void OnProgress(const int64_t& progress, const int64_t& progressMax);
   void OnStatus(const nsresult& status);
   void FailedAsyncOpen(const nsresult& status);
   void HandleAsyncAbort();
   void Redirect1Begin(const uint32_t& newChannelId,
                       const URIParams& newUri,
                       const uint32_t& redirectFlags,
                       const nsHttpResponseHead& responseHead);
   void Redirect3Complete();
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -814,18 +814,18 @@ HttpChannelParent::OnDataAvailable(nsIRe
 
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIProgressEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnProgress(nsIRequest *aRequest,
                               nsISupports *aContext,
-                              uint64_t aProgress,
-                              uint64_t aProgressMax)
+                              int64_t aProgress,
+                              int64_t aProgressMax)
 {
   // OnStatus has always just set mStoredStatus. If it indicates this precedes
   // OnDataAvailable, store and ODA will send to child.
   if (mStoredStatus == NS_NET_STATUS_RECEIVING_FROM ||
       mStoredStatus == NS_NET_STATUS_READING)
   {
     mStoredProgress = aProgress;
     mStoredProgressMax = aProgressMax;
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -158,18 +158,18 @@ private:
   nsCOMPtr<nsIChannel> mRedirectChannel;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
 
   nsAutoPtr<class nsHttpChannel::OfflineCacheEntryAsForeignMarker> mOfflineForeignMarker;
 
   // state for combining OnStatus/OnProgress with OnDataAvailable
   // into one IPDL call to child.
   nsresult mStoredStatus;
-  uint64_t mStoredProgress;
-  uint64_t mStoredProgressMax;
+  int64_t mStoredProgress;
+  int64_t mStoredProgressMax;
 
   bool mSentRedirect1Begin          : 1;
   bool mSentRedirect1BeginFailed    : 1;
   bool mReceivedRedirect2Verify     : 1;
 
   nsRefPtr<OfflineObserver> mObserver;
 
   PBOverrideStatus mPBOverride;
--- a/netwerk/protocol/http/NullHttpTransaction.cpp
+++ b/netwerk/protocol/http/NullHttpTransaction.cpp
@@ -142,17 +142,17 @@ void
 NullHttpTransaction::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
 {
   nsCOMPtr<nsIInterfaceRequestor> copyCB(mCallbacks);
   *outCB = copyCB.forget().take();
 }
 
 void
 NullHttpTransaction::OnTransportStatus(nsITransport* transport,
-                                       nsresult status, uint64_t progress)
+                                       nsresult status, int64_t progress)
 {
   if (mActivityDistributor) {
     NS_DispatchToMainThread(new CallObserveActivity(mActivityDistributor,
                                   mConnectionInfo->GetHost(),
                                   mConnectionInfo->Port(),
                                   mConnectionInfo->EndToEndSSL(),
                                   NS_HTTP_ACTIVITY_TYPE_SOCKET_TRANSPORT,
                                   static_cast<uint32_t>(status),
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -102,17 +102,17 @@ child:
                      uint64_t  progress,
                      uint64_t  progressMax,
                      nsCString data,
                      uint64_t  offset,
                      uint32_t  count);
 
   OnStopRequest(nsresult channelStatus, ResourceTimingStruct timing);
 
-  OnProgress(uint64_t progress, uint64_t progressMax);
+  OnProgress(int64_t progress, int64_t progressMax);
 
   OnStatus(nsresult status);
 
   // Used to cancel child channel if we hit errors during creating and
   // AsyncOpen of nsHttpChannel on the parent.
   FailedAsyncOpen(nsresult status);
 
   // Called to initiate content channel redirect, starts talking to sinks
--- a/netwerk/protocol/http/SpdyPush31.cpp
+++ b/netwerk/protocol/http/SpdyPush31.cpp
@@ -205,17 +205,17 @@ SpdyPush31TransactionBuffer::Connection(
 void
 SpdyPush31TransactionBuffer::GetSecurityCallbacks(nsIInterfaceRequestor **outCB)
 {
   *outCB = nullptr;
 }
 
 void
 SpdyPush31TransactionBuffer::OnTransportStatus(nsITransport* transport,
-                                               nsresult status, uint64_t progress)
+                                               nsresult status, int64_t progress)
 {
 }
 
 nsHttpConnectionInfo *
 SpdyPush31TransactionBuffer::ConnectionInfo()
 {
   if (!mPushStream) {
     return nullptr;
--- a/netwerk/protocol/http/SpdySession31.cpp
+++ b/netwerk/protocol/http/SpdySession31.cpp
@@ -1762,17 +1762,17 @@ SpdySession31::HandleCredential(SpdySess
 //-----------------------------------------------------------------------------
 // nsAHttpTransaction. It is expected that nsHttpConnection is the caller
 // of these methods
 //-----------------------------------------------------------------------------
 
 void
 SpdySession31::OnTransportStatus(nsITransport* aTransport,
                                  nsresult aStatus,
-                                 uint64_t aProgress)
+                                 int64_t aProgress)
 {
   MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
   switch (aStatus) {
     // These should appear only once, deliver to the first
     // transaction on the session.
   case NS_NET_STATUS_RESOLVING_HOST:
   case NS_NET_STATUS_RESOLVED_HOST:
--- a/netwerk/protocol/http/TunnelUtils.cpp
+++ b/netwerk/protocol/http/TunnelUtils.cpp
@@ -551,17 +551,17 @@ TLSFilterTransaction::GetSecurityCallbac
   if (!mTransaction) {
     return;
   }
   mTransaction->GetSecurityCallbacks(outCB);
 }
 
 void
 TLSFilterTransaction::OnTransportStatus(nsITransport* aTransport,
-                                        nsresult aStatus, uint64_t aProgress)
+                                        nsresult aStatus, int64_t aProgress)
 {
   if (!mTransaction) {
     return;
   }
   mTransaction->OnTransportStatus(aTransport, aStatus, aProgress);
 }
 
 nsHttpConnectionInfo *
--- a/netwerk/protocol/http/nsAHttpTransaction.h
+++ b/netwerk/protocol/http/nsAHttpTransaction.h
@@ -50,17 +50,17 @@ public:
     virtual nsAHttpConnection *Connection() = 0;
 
     // called by the connection to get security callbacks to set on the
     // socket transport.
     virtual void GetSecurityCallbacks(nsIInterfaceRequestor **) = 0;
 
     // called to report socket status (see nsITransportEventSink)
     virtual void OnTransportStatus(nsITransport* transport,
-                                   nsresult status, uint64_t progress) = 0;
+                                   nsresult status, int64_t progress) = 0;
 
     // called to check the transaction status.
     virtual bool     IsDone() = 0;
     virtual nsresult Status() = 0;
     virtual uint32_t Caps() = 0;
 
     // called to notify that a requested DNS cache entry was refreshed.
     virtual void     SetDNSWasRefreshed() = 0;
@@ -193,17 +193,17 @@ public:
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsAHttpTransaction, NS_AHTTPTRANSACTION_IID)
 
 #define NS_DECL_NSAHTTPTRANSACTION \
     void SetConnection(nsAHttpConnection *) MOZ_OVERRIDE; \
     nsAHttpConnection *Connection() MOZ_OVERRIDE; \
     void GetSecurityCallbacks(nsIInterfaceRequestor **) MOZ_OVERRIDE;       \
     void OnTransportStatus(nsITransport* transport, \
-                           nsresult status, uint64_t progress) MOZ_OVERRIDE; \
+                           nsresult status, int64_t progress) MOZ_OVERRIDE; \
     bool     IsDone() MOZ_OVERRIDE; \
     nsresult Status() MOZ_OVERRIDE; \
     uint32_t Caps() MOZ_OVERRIDE;   \
     void     SetDNSWasRefreshed() MOZ_OVERRIDE; \
     uint64_t Available() MOZ_OVERRIDE; \
     virtual nsresult ReadSegments(nsAHttpSegmentReader *, uint32_t, uint32_t *) MOZ_OVERRIDE; \
     virtual nsresult WriteSegments(nsAHttpSegmentWriter *, uint32_t, uint32_t *) MOZ_OVERRIDE; \
     virtual void Close(nsresult reason) MOZ_OVERRIDE;                                \
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5589,18 +5589,18 @@ nsHttpChannel::OnStopRequest(nsIRequest 
 // nsHttpChannel::nsIStreamListener
 //-----------------------------------------------------------------------------
 
 class OnTransportStatusAsyncEvent : public nsRunnable
 {
 public:
     OnTransportStatusAsyncEvent(nsITransportEventSink* aEventSink,
                                 nsresult aTransportStatus,
-                                uint64_t aProgress,
-                                uint64_t aProgressMax)
+                                int64_t aProgress,
+                                int64_t aProgressMax)
     : mEventSink(aEventSink)
     , mTransportStatus(aTransportStatus)
     , mProgress(aProgress)
     , mProgressMax(aProgressMax)
     {
         MOZ_ASSERT(!NS_IsMainThread(), "Shouldn't be created on main thread");
     }
 
@@ -5611,18 +5611,18 @@ public:
             mEventSink->OnTransportStatus(nullptr, mTransportStatus,
                                           mProgress, mProgressMax);
         }
         return NS_OK;
     }
 private:
     nsCOMPtr<nsITransportEventSink> mEventSink;
     nsresult mTransportStatus;
-    uint64_t mProgress;
-    uint64_t mProgressMax;
+    int64_t mProgress;
+    int64_t mProgressMax;
 };
 
 NS_IMETHODIMP
 nsHttpChannel::OnDataAvailable(nsIRequest *request, nsISupports *ctxt,
                                nsIInputStream *input,
                                uint64_t offset, uint32_t count)
 {
     PROFILER_LABEL("nsHttpChannel", "OnDataAvailable",
@@ -5657,22 +5657,32 @@ nsHttpChannel::OnDataAvailable(nsIReques
         else
             transportStatus = NS_NET_STATUS_RECEIVING_FROM;
 
         // mResponseHead may reference new or cached headers, but either way it
         // holds our best estimate of the total content length.  Even in the case
         // of a byte range request, the content length stored in the cached
         // response headers is what we want to use here.
 
-        uint64_t progressMax(uint64_t(mResponseHead->ContentLength()));
-        uint64_t progress = mLogicalOffset + uint64_t(count);
-
-        if (progress > progressMax)
+        int64_t progressMax(mResponseHead->ContentLength());
+        int64_t progress = mLogicalOffset + count;
+
+        if ((progress > progressMax) && (progressMax != -1)) {
             NS_WARNING("unexpected progress values - "
                        "is server exceeding content length?");
+        }
+
+        // make sure params are in range for js
+        if (!InScriptableRange(progressMax)) {
+            progressMax = -1;
+        }
+
+        if (!InScriptableRange(progress)) {
+            progress = -1;
+        }
 
         if (NS_IsMainThread()) {
             OnTransportStatus(nullptr, transportStatus, progress, progressMax);
         } else {
             nsresult rv = NS_DispatchToMainThread(
                 new OnTransportStatusAsyncEvent(this, transportStatus,
                                                 progress, progressMax));
             NS_ENSURE_SUCCESS(rv, rv);
@@ -5791,17 +5801,17 @@ nsHttpChannel::CheckListenerChain()
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsITransportEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::OnTransportStatus(nsITransport *trans, nsresult status,
-                                 uint64_t progress, uint64_t progressMax)
+                                 int64_t progress, int64_t progressMax)
 {
     MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread only");
     // cache the progress sink so we don't have to query for it each time.
     if (!mProgressSink)
         GetCallback(mProgressSink);
 
     if (status == NS_NET_STATUS_CONNECTED_TO ||
         status == NS_NET_STATUS_WAITING_FOR) {
@@ -5811,29 +5821,29 @@ nsHttpChannel::OnTransportStatus(nsITran
             socketTransport->GetSelfAddr(&mSelfAddr);
             socketTransport->GetPeerAddr(&mPeerAddr);
         }
     }
 
     // block socket status event after Cancel or OnStopRequest has been called.
     if (mProgressSink && NS_SUCCEEDED(mStatus) && mIsPending) {
         LOG(("sending progress%s notification [this=%p status=%x"
-             " progress=%llu/%llu]\n",
+             " progress=%lld/%lld]\n",
             (mLoadFlags & LOAD_BACKGROUND)? "" : " and status",
             this, status, progress, progressMax));
 
         if (!(mLoadFlags & LOAD_BACKGROUND)) {
             nsAutoCString host;
             mURI->GetHost(host);
             mProgressSink->OnStatus(this, nullptr, status,
                                     NS_ConvertUTF8toUTF16(host).get());
         }
 
         if (progress > 0) {
-            if (progress > progressMax) {
+            if ((progress > progressMax) && (progressMax != -1)) {
                 NS_WARNING("unexpected progress values");
             }
 
             // Try to get mProgressSink if it was nulled out during OnStatus.
             if (!mProgressSink) {
                 GetCallback(mProgressSink);
             }
             if (mProgressSink) {
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -2075,18 +2075,18 @@ nsHttpConnection::OnOutputStreamReady(ns
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection::nsITransportEventSink
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpConnection::OnTransportStatus(nsITransport *trans,
                                     nsresult status,
-                                    uint64_t progress,
-                                    uint64_t progressMax)
+                                    int64_t progress,
+                                    int64_t progressMax)
 {
     if (mTransaction)
         mTransaction->OnTransportStatus(trans, status, progress);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // nsHttpConnection::nsIInterfaceRequestor
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -3421,18 +3421,18 @@ nsHalfOpenSocket::OnOutputStreamReady(ns
 
     return rv;
 }
 
 // method for nsITransportEventSink
 NS_IMETHODIMP
 nsHttpConnectionMgr::nsHalfOpenSocket::OnTransportStatus(nsITransport *trans,
                                                          nsresult status,
-                                                         uint64_t progress,
-                                                         uint64_t progressMax)
+                                                         int64_t progress,
+                                                         int64_t progressMax)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     if (mTransaction)
         mTransaction->OnTransportStatus(trans, status, progress);
 
     MOZ_ASSERT(trans == mSocketTransport || trans == mBackupTransport);
     if (status == NS_NET_STATUS_CONNECTED_TO) {
--- a/netwerk/protocol/http/nsHttpPipeline.cpp
+++ b/netwerk/protocol/http/nsHttpPipeline.cpp
@@ -435,19 +435,19 @@ nsHttpPipeline::GetSecurityCallbacks(nsI
         trans->GetSecurityCallbacks(result);
     else {
         *result = nullptr;
     }
 }
 
 void
 nsHttpPipeline::OnTransportStatus(nsITransport* transport,
-                                  nsresult status, uint64_t progress)
+                                  nsresult status, int64_t progress)
 {
-    LOG(("nsHttpPipeline::OnStatus [this=%p status=%x progress=%llu]\n",
+    LOG(("nsHttpPipeline::OnStatus [this=%p status=%x progress=%lld]\n",
         this, status, progress));
 
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     nsAHttpTransaction *trans;
     int32_t i, count;
 
     switch (status) {
--- a/netwerk/protocol/http/nsHttpPipeline.h
+++ b/netwerk/protocol/http/nsHttpPipeline.h
@@ -89,16 +89,16 @@ private:
     char     *mPushBackBuf;
     uint32_t  mPushBackLen;
     uint32_t  mPushBackMax;
 
     // The number of transactions completed on this pipeline.
     uint32_t  mHttp1xTransactionCount;
 
     // For support of OnTransportStatus()
-    uint64_t  mReceivingFromProgress;
-    uint64_t  mSendingToProgress;
-    bool      mSuppressSendEvents;
+    int64_t  mReceivingFromProgress;
+    int64_t  mSendingToProgress;
+    bool     mSuppressSendEvents;
 };
 
 }} // namespace mozilla::net
 
 #endif // nsHttpPipeline_h__
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -618,17 +618,17 @@ void
 nsHttpResponseHead::Reset()
 {
     LOG(("nsHttpResponseHead::Reset\n"));
 
     ClearHeaders();
 
     mVersion = NS_HTTP_VERSION_1_1;
     mStatus = 200;
-    mContentLength = UINT64_MAX;
+    mContentLength = -1;
     mCacheControlPrivate = false;
     mCacheControlNoStore = false;
     mCacheControlNoCache = false;
     mPragmaNoCache = false;
     mStatusText.Truncate();
     mContentType.Truncate();
     mContentCharset.Truncate();
 }
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -17,17 +17,17 @@ namespace mozilla { namespace net {
 // response.
 //-----------------------------------------------------------------------------
 
 class nsHttpResponseHead
 {
 public:
     nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
                          , mStatus(200)
-                         , mContentLength(UINT64_MAX)
+                         , mContentLength(-1)
                          , mCacheControlPrivate(false)
                          , mCacheControlNoStore(false)
                          , mCacheControlNoCache(false)
                          , mPragmaNoCache(false) {}
 
     const nsHttpHeaderArray & Headers()   const { return mHeaders; }
     nsHttpHeaderArray    &Headers()             { return mHeaders; }
     nsHttpVersion         Version()       const { return mVersion; }
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -362,18 +362,24 @@ nsHttpTransaction::Init(uint32_t caps,
         // necessary to workaround some common server bugs (see bug 137155).
         rv = NS_NewBufferedInputStream(getter_AddRefs(mRequestStream), multi,
                                        nsIOService::gDefaultSegmentSize);
         if (NS_FAILED(rv)) return rv;
     }
     else
         mRequestStream = headers;
 
-    rv = mRequestStream->Available(&mRequestSize);
-    if (NS_FAILED(rv)) return rv;
+    uint64_t size_u64;
+    rv = mRequestStream->Available(&size_u64);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
+    // make sure it fits within js MAX_SAFE_INTEGER
+    mRequestSize = InScriptableRange(size_u64) ? static_cast<int64_t>(size_u64) : -1;
 
     // create pipe for response stream
     rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
                      getter_AddRefs(mPipeOut),
                      true, true,
                      nsIOService::gDefaultSegmentSize,
                      nsIOService::gDefaultSegmentCount);
     if (NS_FAILED(rv)) return rv;
@@ -494,19 +500,19 @@ nsHttpTransaction::SetSecurityCallbacks(
     if (gSocketTransportService) {
         nsRefPtr<UpdateSecurityCallbacks> event = new UpdateSecurityCallbacks(this, aCallbacks);
         gSocketTransportService->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
     }
 }
 
 void
 nsHttpTransaction::OnTransportStatus(nsITransport* transport,
-                                     nsresult status, uint64_t progress)
+                                     nsresult status, int64_t progress)
 {
-    LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%x progress=%llu]\n",
+    LOG(("nsHttpTransaction::OnSocketStatus [this=%p status=%x progress=%lld]\n",
         this, status, progress));
 
     if (TimingEnabled()) {
         if (status == NS_NET_STATUS_RESOLVING_HOST) {
             mTimings.domainLookupStart = TimeStamp::Now();
         } else if (status == NS_NET_STATUS_RESOLVED_HOST) {
             mTimings.domainLookupEnd = TimeStamp::Now();
         } else if (status == NS_NET_STATUS_CONNECTING_TO) {
@@ -543,40 +549,40 @@ nsHttpTransaction::OnTransportStatus(nsI
                 progress,
                 EmptyCString());
     }
 
     // nsHttpChannel synthesizes progress events in OnDataAvailable
     if (status == NS_NET_STATUS_RECEIVING_FROM)
         return;
 
-    uint64_t progressMax;
+    int64_t progressMax;
 
     if (status == NS_NET_STATUS_SENDING_TO) {
         // suppress progress when only writing request headers
         if (!mHasRequestBody) {
             LOG(("nsHttpTransaction::OnTransportStatus %p "
                  "SENDING_TO without request body\n", this));
             return;
         }
 
         nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mRequestStream);
         if (!seekable) {
             LOG(("nsHttpTransaction::OnTransportStatus %p "
                  "SENDING_TO without seekable request stream\n", this));
-            return;
+            progress = 0;
+        } else {
+            int64_t prog = 0;
+            seekable->Tell(&prog);
+            progress = prog;
         }
 
-        int64_t prog = 0;
-        seekable->Tell(&prog);
-        progress = prog;
-
         // when uploading, we include the request headers in the progress
         // notifications.
-        progressMax = mRequestSize; // XXX mRequestSize is 32-bit!
+        progressMax = mRequestSize;
     }
     else {
         progress = 0;
         progressMax = 0;
     }
 
     mTransportSink->OnTransportStatus(transport, status, progress, progressMax);
 }
@@ -1614,20 +1620,16 @@ nsHttpTransaction::HandleContent(char *b
         mContentRead += ignore;
         mRestartInProgressVerifier.HaveReadBeforeRestart(ignore);
         memmove(buf, buf + ignore, *contentRead + *contentRemaining);
     }
 
     if (*contentRead) {
         // update count of content bytes read and report progress...
         mContentRead += *contentRead;
-        /* when uncommenting, take care of 64-bit integers w/ std::max...
-        if (mProgressSink)
-            mProgressSink->OnProgress(nullptr, nullptr, mContentRead, std::max(0, mContentLength));
-        */
     }
 
     LOG(("nsHttpTransaction::HandleContent [this=%p count=%u read=%u mContentRead=%lld mContentLength=%lld]\n",
         this, count, *contentRead, mContentRead, mContentLength));
 
     // Check the size of chunked responses. If we exceed the max pipeline size
     // for this response reschedule the pipeline
     if ((mClassification != CLASS_SOLO) &&
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -209,17 +209,17 @@ private:
     nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
     nsCOMPtr<nsILoadGroupConnectionInfo> mLoadGroupCI;
 
     nsCOMPtr<nsISupports>             mChannel;
     nsCOMPtr<nsIHttpActivityObserver> mActivityDistributor;
 
     nsCString                       mReqHeaderBuf;    // flattened request headers
     nsCOMPtr<nsIInputStream>        mRequestStream;
-    uint64_t                        mRequestSize;
+    int64_t                         mRequestSize;
 
     nsRefPtr<nsAHttpConnection>     mConnection;
     nsRefPtr<nsHttpConnectionInfo>  mConnInfo;
     nsHttpRequestHead              *mRequestHead;     // weak ref
     nsHttpResponseHead             *mResponseHead;    // owning pointer
 
     nsAHttpSegmentReader           *mReader;
     nsAHttpSegmentWriter           *mWriter;
--- a/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
+++ b/netwerk/protocol/wyciwyg/WyciwygChannelChild.cpp
@@ -249,17 +249,17 @@ WyciwygChannelChild::OnDataAvailable(con
   
   rv = mListener->OnDataAvailable(this, mListenerContext,
                                   stringStream, offset, data.Length());
   if (NS_FAILED(rv))
     Cancel(rv);
 
   if (mProgressSink && NS_SUCCEEDED(rv)) {
     mProgressSink->OnProgress(this, nullptr, offset + data.Length(),
-                              uint64_t(mContentLength));
+                              mContentLength);
   }
 }
 
 class WyciwygStopRequestEvent : public ChannelEvent
 {
 public:
   WyciwygStopRequestEvent(WyciwygChannelChild* child,
                           const nsresult& statusCode)
--- a/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
+++ b/netwerk/protocol/wyciwyg/nsWyciwygChannel.cpp
@@ -704,18 +704,17 @@ nsWyciwygChannel::OnDataAvailable(nsIReq
       this, request, offset, count));
 
   nsresult rv;
   
   rv = mListener->OnDataAvailable(this, mListenerContext, input, offset, count);
 
   // XXX handle 64-bit stuff for real
   if (mProgressSink && NS_SUCCEEDED(rv)) {
-    mProgressSink->OnProgress(this, nullptr, offset + count,
-                              uint64_t(mContentLength));
+    mProgressSink->OnProgress(this, nullptr, offset + count, mContentLength);
   }
 
   return rv; // let the pump cancel on failure
 }
 
 //////////////////////////////////////////////////////////////////////////////
 // nsIRequestObserver
 //////////////////////////////////////////////////////////////////////////////
--- a/netwerk/test/TestIncrementalDownload.cpp
+++ b/netwerk/test/TestIncrementalDownload.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 cin et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include <inttypes.h>
 #include <stdlib.h>
 #include "TestCommon.h"
 #include "nsNetUtil.h"
 #include "nsIIncrementalDownload.h"
 #include "nsIRequestObserver.h"
 #include "nsIProgressEventSink.h"
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
@@ -35,20 +36,20 @@ NS_IMETHODIMP
 FetchObserver::OnStartRequest(nsIRequest *request, nsISupports *context)
 {
   printf("FetchObserver::OnStartRequest\n");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchObserver::OnProgress(nsIRequest *request, nsISupports *context,
-                          uint64_t progress, uint64_t progressMax)
+                          int64_t progress, int64_t progressMax)
 {
-  printf("FetchObserver::OnProgress [%lu/%lu]\n",
-         (unsigned long)progress, (unsigned long)progressMax);
+  printf("FetchObserver::OnProgress [%" PRId64 "/%" PRId64 "]\n",
+         progress, progressMax);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 FetchObserver::OnStatus(nsIRequest *request, nsISupports *context,
                         nsresult status, const char16_t *statusText)
 {
   return NS_OK;
--- a/netwerk/test/unit/test_http2.js
+++ b/netwerk/test/unit/test_http2.js
@@ -42,35 +42,34 @@ var Http2CheckListener = function() {};
 Http2CheckListener.prototype = {
   onStartRequestFired: false,
   onDataAvailableFired: false,
   isHttp2Connection: false,
   shouldBeHttp2 : true,
 
   onStartRequest: function testOnStartRequest(request, ctx) {
     this.onStartRequestFired = true;
-
     if (!Components.isSuccessCode(request.status))
       do_throw("Channel should have a success code! (" + request.status + ")");
-    if (!(request instanceof Components.interfaces.nsIHttpChannel))
-      do_throw("Expecting an HTTP channel");
 
+    do_check_true(request instanceof Components.interfaces.nsIHttpChannel);
     do_check_eq(request.responseStatus, 200);
     do_check_eq(request.requestSucceeded, true);
   },
 
   onDataAvailable: function testOnDataAvailable(request, ctx, stream, off, cnt) {
     this.onDataAvailableFired = true;
     this.isHttp2Connection = checkIsHttp2(request);
 
     read_stream(stream, cnt);
   },
 
   onStopRequest: function testOnStopRequest(request, ctx, status) {
     do_check_true(this.onStartRequestFired);
+    do_check_true(Components.isSuccessCode(status));
     do_check_true(this.onDataAvailableFired);
     do_check_true(this.isHttp2Connection == this.shouldBeHttp2);
 
     run_next_test();
     do_test_finished();
   }
 };
 
@@ -135,18 +134,18 @@ Http2HeaderListener.prototype.onDataAvai
 
 var Http2PushListener = function() {};
 
 Http2PushListener.prototype = new Http2CheckListener();
 
 Http2PushListener.prototype.onDataAvailable = function(request, ctx, stream, off, cnt) {
   this.onDataAvailableFired = true;
   this.isHttp2Connection = checkIsHttp2(request);
-  if (ctx.originalURI.spec == "https://localhost:6944/push.js" ||
-      ctx.originalURI.spec == "https://localhost:6944/push2.js") {
+  if (ctx.originalURI.spec == "https://localhost:" + serverPort + "/push.js" ||
+      ctx.originalURI.spec == "https://localhost:" + serverPort + "/push2.js") {
     do_check_eq(request.getResponseHeader("pushed"), "yes");
   }
   read_stream(stream, cnt);
 };
 
 // Does the appropriate checks for a large GET response
 var Http2BigListener = function() {};
 
@@ -221,32 +220,32 @@ function makeChan(url) {
                              Ci.nsILoadInfo.SEC_NORMAL,
                              Ci.nsIContentPolicy.TYPE_OTHER).QueryInterface(Ci.nsIHttpChannel);
 
   return chan;
 }
 
 // Make sure we make a HTTP2 connection and both us and the server mark it as such
 function test_http2_basic() {
-  var chan = makeChan("https://localhost:6944/");
+  var chan = makeChan("https://localhost:" + serverPort + "/");
   var listener = new Http2CheckListener();
   chan.asyncOpen(listener, null);
 }
 
 function test_http2_basic_unblocked_dep() {
-  var chan = makeChan("https://localhost:6944/basic_unblocked_dep");
+  var chan = makeChan("https://localhost:" + serverPort + "/basic_unblocked_dep");
   var cos = chan.QueryInterface(Ci.nsIClassOfService);
   cos.addClassFlags(Ci.nsIClassOfService.Unblocked);
   var listener = new Http2CheckListener();
   chan.asyncOpen(listener, null);
 }
 
 // make sure we don't use h2 when disallowed
 function test_http2_nospdy() {
-  var chan = makeChan("https://localhost:6944/");
+  var chan = makeChan("https://localhost:" + serverPort + "/");
   var listener = new Http2CheckListener();
   var internalChannel = chan.QueryInterface(Ci.nsIHttpChannelInternal);
   internalChannel.allowSpdy = false;
   listener.shouldBeHttp2 = false;
   chan.asyncOpen(listener, null);
 }
 
 // Support for making sure XHR works over SPDY
@@ -260,17 +259,17 @@ function checkXhr(xhr) {
   run_next_test();
   do_test_finished();
 }
 
 // Fires off an XHR request over SPDY
 function test_http2_xhr() {
   var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
             .createInstance(Ci.nsIXMLHttpRequest);
-  req.open("GET", "https://localhost:6944/", true);
+  req.open("GET", "https://localhost:" + serverPort + "/", true);
   req.addEventListener("readystatechange", function (evt) { checkXhr(req); },
                        false);
   req.send(null);
 }
 
 var concurrent_channels = [];
 
 var Http2ConcurrentListener = function() {};
@@ -287,46 +286,46 @@ Http2ConcurrentListener.prototype.onStop
     do_test_finished();
   }
 };
 
 function test_http2_concurrent() {
   var concurrent_listener = new Http2ConcurrentListener();
   concurrent_listener.target = 201;
   for (var i = 0; i < concurrent_listener.target; i++) {
-    concurrent_channels[i] = makeChan("https://localhost:6944/750ms");
+    concurrent_channels[i] = makeChan("https://localhost:" + serverPort + "/750ms");
     concurrent_channels[i].loadFlags = Ci.nsIRequest.LOAD_BYPASS_CACHE;
     concurrent_channels[i].asyncOpen(concurrent_listener, null);
   }
 }
 
 // Test to make sure we get multiplexing right
 function test_http2_multiplex() {
-  var chan1 = makeChan("https://localhost:6944/multiplex1");
-  var chan2 = makeChan("https://localhost:6944/multiplex2");
+  var chan1 = makeChan("https://localhost:" + serverPort + "/multiplex1");
+  var chan2 = makeChan("https://localhost:" + serverPort + "/multiplex2");
   var listener1 = new Http2MultiplexListener();
   var listener2 = new Http2MultiplexListener();
   chan1.asyncOpen(listener1, null);
   chan2.asyncOpen(listener2, null);
 }
 
 // Test to make sure we gateway non-standard headers properly
 function test_http2_header() {
-  var chan = makeChan("https://localhost:6944/header");
+  var chan = makeChan("https://localhost:" + serverPort + "/header");
   var hvalue = "Headers are fun";
   chan.setRequestHeader("X-Test-Header", hvalue, false);
   var listener = new Http2HeaderListener("X-Received-Test-Header", function(received_hvalue) {
     do_check_eq(received_hvalue, hvalue);
   });
   chan.asyncOpen(listener, null);
 }
 
 // Test to make sure cookies are split into separate fields before compression
 function test_http2_cookie_crumbling() {
-  var chan = makeChan("https://localhost:6944/cookie_crumbling");
+  var chan = makeChan("https://localhost:" + serverPort + "/cookie_crumbling");
   var cookiesSent = ['a=b', 'c=d01234567890123456789', 'e=f'].sort();
   chan.setRequestHeader("Cookie", cookiesSent.join('; '), false);
   var listener = new Http2HeaderListener("X-Received-Header-Pairs", function(pairsReceived) {
     var cookiesReceived = JSON.parse(pairsReceived).filter(function(pair) {
       return pair[0] == 'cookie';
     }).map(function(pair) {
       return pair[1];
     }).sort();
@@ -334,60 +333,60 @@ function test_http2_cookie_crumbling() {
     cookiesReceived.forEach(function(cookieReceived, index) {
       do_check_eq(cookiesSent[index], cookieReceived)
     });
   });
   chan.asyncOpen(listener, null);
 }
 
 function test_http2_push1() {
-  var chan = makeChan("https://localhost:6944/push");
+  var chan = makeChan("https://localhost:" + serverPort + "/push");
   chan.loadGroup = loadGroup;
   var listener = new Http2PushListener();
   chan.asyncOpen(listener, chan);
 }
 
 function test_http2_push2() {
-  var chan = makeChan("https://localhost:6944/push.js");
+  var chan = makeChan("https://localhost:" + serverPort + "/push.js");
   chan.loadGroup = loadGroup;
   var listener = new Http2PushListener();
   chan.asyncOpen(listener, chan);
 }
 
 function test_http2_push3() {
-  var chan = makeChan("https://localhost:6944/push2");
+  var chan = makeChan("https://localhost:" + serverPort + "/push2");
   chan.loadGroup = loadGroup;
   var listener = new Http2PushListener();
   chan.asyncOpen(listener, chan);
 }
 
 function test_http2_push4() {
-  var chan = makeChan("https://localhost:6944/push2.js");
+  var chan = makeChan("https://localhost:" + serverPort + "/push2.js");
   chan.loadGroup = loadGroup;
   var listener = new Http2PushListener();
   chan.asyncOpen(listener, chan);
 }
 
 // this is a basic test where the server sends a simple document with 2 header
 // blocks. bug 1027364
 function test_http2_doubleheader() {
-  var chan = makeChan("https://localhost:6944/doubleheader");
+  var chan = makeChan("https://localhost:" + serverPort + "/doubleheader");
   var listener = new Http2CheckListener();
   chan.asyncOpen(listener, null);
 }
 
 // Make sure we handle GETs that cover more than 2 frames properly
 function test_http2_big() {
-  var chan = makeChan("https://localhost:6944/big");
+  var chan = makeChan("https://localhost:" + serverPort + "/big");
   var listener = new Http2BigListener();
   chan.asyncOpen(listener, null);
 }
 
 function test_http2_huge_suspended() {
-  var chan = makeChan("https://localhost:6944/huge");
+  var chan = makeChan("https://localhost:" + serverPort + "/huge");
   var listener = new Http2HugeSuspendedListener();
   chan.asyncOpen(listener, null);
   chan.suspend();
   do_timeout(500, chan.resume);
 }
 
 // Support for doing a POST
 function do_post(content, chan, listener) {
@@ -400,24 +399,24 @@ function do_post(content, chan, listener
 
   chan.requestMethod = "POST";
 
   chan.asyncOpen(listener, null);
 }
 
 // Make sure we can do a simple POST
 function test_http2_post() {
-  var chan = makeChan("https://localhost:6944/post");
+  var chan = makeChan("https://localhost:" + serverPort + "/post");
   var listener = new Http2PostListener(md5s[0]);
   do_post(posts[0], chan, listener);
 }
 
 // Make sure we can do a POST that covers more than 2 frames
 function test_http2_post_big() {
-  var chan = makeChan("https://localhost:6944/post");
+  var chan = makeChan("https://localhost:" + serverPort + "/post");
   var listener = new Http2PostListener(md5s[1]);
   do_post(posts[1], chan, listener);
 }
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Services.jsm");
 
 var httpserv = null;
@@ -453,17 +452,17 @@ var altsvcClientListener = {
 	run_next_test();
     }
   }
 };
 
 function altsvcHttp1Server(metadata, response) {
   response.setStatusLine(metadata.httpVersion, 200, "OK");
   response.setHeader("Content-Type", "text/plain", false);
-  response.setHeader("Alt-Svc", 'h2-16=":6944"', false);
+  response.setHeader("Alt-Svc", 'h2-16=":' + serverPort + '"', false);
   var body = "this is where a cool kid would write something neat.\n";
   response.bodyOutputStream.write(body, body.length);
 }
 
 function test_http2_altsvc() {
   httpserv = new HttpServer();
   httpserv.registerPathHandler("/altsvc1", altsvcHttp1Server);
   httpserv.start(-1);
@@ -493,50 +492,50 @@ Http2PushApiListener.prototype = {
     if (aIID.equals(Ci.nsIHttpPushListener) ||
         aIID.equals(Ci.nsIStreamListener))
       return this;
     throw Components.results.NS_ERROR_NO_INTERFACE;
   },
 
   // nsIHttpPushListener
   onPush: function onPush(associatedChannel, pushChannel) {
-    do_check_eq(associatedChannel.originalURI.spec, "https://localhost:6944/pushapi1");
+    do_check_eq(associatedChannel.originalURI.spec, "https://localhost:" + serverPort + "/pushapi1");
     do_check_eq (pushChannel.getRequestHeader("x-pushed-request"), "true");
 
     pushChannel.asyncOpen(this, pushChannel);
-    if (pushChannel.originalURI.spec == "https://localhost:6944/pushapi1/2") {
+    if (pushChannel.originalURI.spec == "https://localhost:" + serverPort + "/pushapi1/2") {
 	pushChannel.cancel(Components.results.NS_ERROR_ABORT);
     }
   },
 
  // normal Channel listeners
   onStartRequest: function pushAPIOnStart(request, ctx) {
   },
 
   onDataAvailable: function pushAPIOnDataAvailable(request, ctx, stream, offset, cnt) {
-    do_check_neq(ctx.originalURI.spec, "https://localhost:6944/pushapi1/2");
+    do_check_neq(ctx.originalURI.spec, "https://localhost:" + serverPort + "/pushapi1/2");
 
     var data = read_stream(stream, cnt);
 
-    if (ctx.originalURI.spec == "https://localhost:6944/pushapi1") {
+    if (ctx.originalURI.spec == "https://localhost:" + serverPort + "/pushapi1") {
 	do_check_eq(data[0], '0');
 	--this.checksPending;
-    } else if (ctx.originalURI.spec == "https://localhost:6944/pushapi1/1") {
+    } else if (ctx.originalURI.spec == "https://localhost:" + serverPort + "/pushapi1/1") {
 	do_check_eq(data[0], '1');
 	--this.checksPending; // twice
-    } else if (ctx.originalURI.spec == "https://localhost:6944/pushapi1/3") {
+    } else if (ctx.originalURI.spec == "https://localhost:" + serverPort + "/pushapi1/3") {
 	do_check_eq(data[0], '3');
 	--this.checksPending;
     } else