Merge inbound and m-c. IGNORE BAD COMMIT MESSAGES
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 18 Jun 2013 19:04:48 -0400
changeset 146984 d2a7cfa34154439232ac1d6230b3945b32fe4d5e
parent 146931 2e810d6c8779a1908c5d6785d00954a0e5018c58 (current diff)
parent 146983 dd9245e218125a081c46d35dede11d13f704866f (diff)
child 147026 a8a28bd8abf391bec27a465597d247e78639ed76
child 147065 568921d73dd121b23b7c60e6a5b34daa703a0bd4
child 147084 531048ab5fedcfe21629e2ac761dd2a353a0cfb9
push id2697
push userbbajaj@mozilla.com
push dateMon, 05 Aug 2013 18:49:53 +0000
treeherdermozilla-beta@dfec938c7b63 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
d2a7cfa34154 / 24.0a1 / 20130619031048 / files
nightly linux64
d2a7cfa34154 / 24.0a1 / 20130619031048 / files
nightly mac
d2a7cfa34154 / 24.0a1 / 20130619031048 / files
nightly win32
d2a7cfa34154 / 24.0a1 / 20130619031048 / files
nightly win64
d2a7cfa34154 / 24.0a1 / 20130619031048 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound and m-c. IGNORE BAD COMMIT MESSAGES
layout/reftests/svg/non-scaling-stroke-03-ref.svg
layout/reftests/svg/non-scaling-stroke-03.svg
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -1145,8 +1145,12 @@ function openBrowserWindow(callback, url
 // Helper for calling code at window close
 function closeBrowserWindow(window, callback) {
   window.addEventListener("unload", function unload() {
     window.removeEventListener("unload", unload, false);
     callback();
   }, false);
   window.close();
 }
+
+// Test disabled on Linux because of bug 882867
+if (require("sdk/system/runtime").OS == "Linux")
+  module.exports = {};
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -483,27 +483,32 @@
           openUILinkIn(submission.uri.spec, aWhere, null, submission.postData);
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="command"><![CDATA[
         const target = event.originalTarget;
-        if (target.classList.contains("addengine-item")) {
+        if (target.engine) {
+          this.currentEngine = target.engine;
+        } else if (target.classList.contains("addengine-item")) {
           var searchService =
             Components.classes["@mozilla.org/browser/search-service;1"]
                       .getService(Components.interfaces.nsIBrowserSearchService);
           // We only detect OpenSearch files
           var type = Components.interfaces.nsISearchEngine.DATA_XML;
+          // Select the installed engine if the installation succeeds
+          var installCallback = {
+            onSuccess: engine => this.currentEngine = engine
+          }
           searchService.addEngine(target.getAttribute("uri"), type,
-                                  target.getAttribute("src"), false);
+                                  target.getAttribute("src"), false,
+                                  installCallback);
         }
-        else if (target.engine)
-          this.currentEngine = target.engine;
         else
           return;
 
         this.focus();
         this.select();
       ]]></handler>
 
       <handler event="popupshowing" action="this.rebuildPopupDynamic();"/>
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -25,18 +25,17 @@ function test() {
 
   let testIterator;
 
   function observer(aSub, aTopic, aData) {
     switch (aData) {
       case "engine-added":
         var engine = ss.getEngineByName("Bug 426329");
         ok(engine, "Engine was added.");
-        //XXX Bug 493051
-        //ss.currentEngine = engine;
+        ss.currentEngine = engine;
         break;
       case "engine-current":
         ok(ss.currentEngine.name == "Bug 426329", "currentEngine set");
         testReturn();
         break;
       case "engine-removed":
         Services.obs.removeObserver(observer, "browser-search-engine-modified");
         finish();
--- a/browser/components/search/test/browser_contextmenu.js
+++ b/browser/components/search/test/browser_contextmenu.js
@@ -11,18 +11,17 @@ function test() {
   const ENGINE_NAME = "Foo";
   var contextMenu;
 
   function observer(aSub, aTopic, aData) {
     switch (aData) {
       case "engine-added":
         var engine = ss.getEngineByName(ENGINE_NAME);
         ok(engine, "Engine was added.");
-        //XXX Bug 493051
-        //ss.currentEngine = engine;
+        ss.currentEngine = engine;
         break;
       case "engine-current":
         is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set");
         startTest();
         break;
       case "engine-removed":
         Services.obs.removeObserver(observer, "browser-search-engine-modified");
         finish();
--- a/browser/components/search/test/browser_private_search_perwindowpb.js
+++ b/browser/components/search/test/browser_private_search_perwindowpb.js
@@ -37,30 +37,28 @@ function test() {
     onPageLoad(aWin, aCallback);
 
     searchBar.value = aIsPrivate ? "private test" : "public test";
     searchBar.focus();
     EventUtils.synthesizeKey("VK_RETURN", {}, aWin);
   }
 
   function addEngine(aCallback) {
-    function observer(aSub, aTopic, aData) {
-      switch (aData) {
-        case "engine-current":
-          ok(Services.search.currentEngine.name == "Bug 426329",
-             "currentEngine set");
-          aCallback();
-          break;
+    let installCallback = {
+      onSuccess: function (engine) {
+        Services.search.currentEngine = engine;
+        aCallback();
+      },
+      onError: function (errorCode) {
+        ok(false, "failed to install engine: " + errorCode);
       }
-    }
-
-    Services.obs.addObserver(observer, "browser-search-engine-modified", false);
-    Services.search.addEngine(
-      engineURL + "426329.xml", Ci.nsISearchEngine.DATA_XML,
-      "data:image/x-icon,%00", false);
+    };
+    Services.search.addEngine(engineURL + "426329.xml",
+                              Ci.nsISearchEngine.DATA_XML,
+                              "data:image/x-icon,%00", false, installCallback);
   }
 
   function testOnWindow(aIsPrivate, aCallback) {
     let win = OpenBrowserWindow({ private: aIsPrivate });
     waitForFocus(function() {
       windowsToClose.push(win);
       executeSoon(function() aCallback(win));
     }, win);
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -827,45 +827,35 @@ var SelectionHelperUI = {
       // shutdown but leave focus alone. the event will fall through
       // and the dom will update focus for us. If the user tapped on
       // another input, we'll get a attachToCaret call soonish on the
       // new input.
       this.closeEditSession(false);
       return;
     }
 
-    let selectionTap = this._hitTestSelection(aEvent);
-
-    // If the tap is in the selection, just ignore it. We disallow this
-    // since we always get a single tap before a double, and double tap
-    // copies selected text.
-    if (selectionTap) {
-      if (!this._targetIsEditable) {
-        this.closeEditSession(false);
-        return;
-      }
+    if (this._hitTestSelection(aEvent) && this._targetIsEditable) {
       // Attach to the newly placed caret position
       this._sendAsyncMessage("Browser:CaretAttach", {
         xPos: aEvent.clientX,
         yPos: aEvent.clientY
       });
       return;
     }
 
     // A tap within an editable but outside active selection, clear the
     // selection and flip back to caret mode.
     if (this.startMark.visible && pointInTargetElement &&
         this._targetIsEditable) {
       this._transitionFromSelectionToCaret(clientCoords.x, clientCoords.y);
+      return;
     }
 
-    // If we have active selection in anything else don't let the event get
-    // to content. Prevents random taps from killing active selection.
-    aEvent.stopPropagation();
-    aEvent.preventDefault();
+    // Close when we get a single tap in content.
+    this.closeEditSession(false);
   },
 
   _onKeypress: function _onKeypress() {
     this.closeEditSession();
   },
 
   _onResize: function _onResize() {
     this._sendAsyncMessage("Browser:SelectionUpdate", {});
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js
@@ -301,125 +301,56 @@ gTests.push({
       clearSelection(gFrame);
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
     yield hideContextUI();
   },
 });
 
-/*
-disable until bug 860248 is addressed.
 gTests.push({
-  desc: "double-tap copy text in content",
-  setUp: setUpHelper,
+  desc: "tap on selection clears selection in content",
+  setUp: setUpAndTearDown,
   run: function test() {
 
     sendContextMenuClick(30, 20);
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
-    sendDoubleTap(gWindow, 30, 20);
+    sendTap(gWindow, 30, 20);
 
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
-    
-    // check copy text results
-    let text = SpecialPowers.getClipboardData("text/unicode").trim();
-    is(text, "There", "copy text test");
-
-    // check for active selection
-    is(getTrimmedSelection(gWindow).toString(), "", "selection test");
   },
-  tearDown: tearDownHelper,
+  tearDown: setUpAndTearDown,
 });
 
 gTests.push({
-  desc: "double-tap copy text in scrolled content",
-  setUp: setUpHelper,
+  desc: "tap off selection clears selection in content",
+  setUp: setUpAndTearDown,
   run: function test() {
-    let scrollPromise = waitForEvent(gWindow, "scroll");
-    gWindow.scrollBy(0, 200);
-    yield scrollPromise;
-    ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error");
 
-    sendContextMenuClick(30, 100);
+    sendContextMenuClick(30, 20);
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
-    sendDoubleTap(gWindow, 42, 100);
-
-    yield waitForCondition(function () {
-        return !SelectionHelperUI.isSelectionUIVisible;
-      }, kCommonWaitMs, kCommonPollMs);
-
-    // check copy text results
-    let text = SpecialPowers.getClipboardData("text/unicode");
-    is(text, "suddenly", "copy text test");
+    sendTap(gWindow, 30, 100);
 
-    // check for active selection
-    is(getTrimmedSelection(gWindow).toString(), "", "selection test");
-  },
-  tearDown: function tearDown() {
-    emptyClipboard();
-    clearSelection(gWindow);
-    let scrollPromise = waitForEvent(gWindow, "scroll");
-    gWindow.scrollBy(0, -200);
-    yield scrollPromise;
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
   },
+  tearDown: setUpAndTearDown,
 });
 
-gTests.push({
-  desc: "single clicks on selection in non-editable content",
-  setUp: setUpHelper,
-  run: function test() {
-    sendContextMenuClick(100, 20);
-
-    yield waitForCondition(function () {
-        return SelectionHelperUI.isSelectionUIVisible;
-      }, kCommonWaitMs, kCommonPollMs);
-
-    // active state
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
-    let touchdrag = new TouchDragAndHold();
-    yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos);
-    touchdrag.end();
-
-    yield waitForCondition(function () {
-        return !SelectionHelperUI.hasActiveDrag;
-      }, kCommonWaitMs, kCommonPollMs);
-    yield SelectionHelperUI.pingSelectionHandler();
-
-    // active state
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    // click on selected text - nothing should change
-    sendTap(gWindow, 240, 20);
-
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    // click outside the text - nothing should change
-    sendTap(gWindow, 197, 119);
-
-    is(SelectionHelperUI.isActive, true, "selection active");
-  },
-  tearDown: tearDownHelper,
-});
-*/
-
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
 
   requestLongerTimeout(3);
   runTests();
--- a/browser/metro/shell/testing/metrotestharness.cpp
+++ b/browser/metro/shell/testing/metrotestharness.cpp
@@ -35,60 +35,71 @@ char buffer[PIPE_BUFFER_SIZE + 1];
 
 CString sAppParams;
 CString sFirefoxPath;
 
 // The tests file we write out for firefox.exe which contains test
 // startup command line paramters.
 #define kMetroTestFile "tests.ini"
 
+// Process exit codes for buildbotcustom logic. These are currently ignored, but
+// at some point releng expects to use these.
+#define SUCCESS   0
+#define WARNINGS  1
+#define FAILURE   2
+#define EXCEPTION 3
+#define RETRY     4
+
 static void Log(const wchar_t *fmt, ...)
 {
   va_list a = NULL;
   wchar_t szDebugString[1024];
   if(!lstrlenW(fmt))
     return;
   va_start(a,fmt);
   vswprintf(szDebugString, 1024, fmt, a);
   va_end(a);
   if(!lstrlenW(szDebugString))
     return;
 
   wprintf(L"INFO | metrotestharness.exe | %s\n", szDebugString);
   fflush(stdout);
 }
 
-static void Fail(const wchar_t *fmt, ...)
+static void Fail(bool aRequestRetry, const wchar_t *fmt, ...)
 {
   va_list a = NULL;
   wchar_t szDebugString[1024];
   if(!lstrlenW(fmt))
     return;
   va_start(a,fmt);
   vswprintf(szDebugString, 1024, fmt, a);
   va_end(a);
   if(!lstrlenW(szDebugString))
     return;
-
-  wprintf(L"TEST-UNEXPECTED-FAIL | metrotestharness.exe | %s\n", szDebugString);
+  if (aRequestRetry) {
+    wprintf(L"FAIL-SHOULD-RETRY | metrotestharness.exe | %s\n", szDebugString);
+  } else {
+    wprintf(L"TEST-UNEXPECTED-FAIL | metrotestharness.exe | %s\n", szDebugString);
+  }
   fflush(stdout);
 }
 
 /*
  * Retrieve our module dir path.
  *
  * @aPathBuffer Buffer to fill
  */
 static bool GetModulePath(CStringW& aPathBuffer)
 {
   WCHAR buffer[MAX_PATH];
   memset(buffer, 0, sizeof(buffer));
 
   if (!GetModuleFileName(NULL, buffer, MAX_PATH)) {
-    Fail(L"GetModuleFileName failed.");
+    Fail(false, L"GetModuleFileName failed.");
     return false;
   }
 
   WCHAR* slash = wcsrchr(buffer, '\\');
   if (!slash)
     return false;
   *slash = '\0';
 
@@ -185,44 +196,37 @@ static void ReadPipe()
   DWORD numBytesRead;
   while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) &&
          numBytesRead) {
     buffer[numBytesRead] = '\0';
     printf("%s", buffer);
   }
 }
 
-// From buildbotcustom logic:
-#define SUCCESS   0
-#define WARNINGS  1
-#define FAILURE   2
-#define EXCEPTION 3
-#define RETRY     4 /* will retry endlessly on new slaves, be careful with this! */
-
 static int Launch()
 {
   Log(L"Launching browser...");
 
   DWORD processID;
 
   // The interface that allows us to activate the browser
   CComPtr<IApplicationActivationManager> activateMgr;
   if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL,
                               CLSCTX_LOCAL_SERVER,
                               IID_IApplicationActivationManager,
                               (void**)&activateMgr))) {
-    Fail(L"CoCreateInstance CLSID_ApplicationActivationManager failed.");
+    Fail(false, L"CoCreateInstance CLSID_ApplicationActivationManager failed.");
     return FAILURE;
   }
   
   HRESULT hr;
   WCHAR appModelID[256];
   // Activation is based on the browser's registered app model id
   if (!GetDefaultBrowserAppModelID(appModelID, (sizeof(appModelID)/sizeof(WCHAR)))) {
-    Fail(L"GetDefaultBrowserAppModelID failed.");
+    Fail(false, L"GetDefaultBrowserAppModelID failed.");
     return FAILURE;
   }
   Log(L"App model id='%s'", appModelID);
 
   // Hand off focus rights if the terminal has focus to the out-of-process
   // activation server (explorer.exe). Without this the metro interface
   // won't launch.
   hr = CoAllowSetForegroundWindow(activateMgr, NULL);
@@ -246,93 +250,93 @@ static int Launch()
 
   // Because we can't pass command line args, we store params in a
   // tests.ini file in dist/bin which the browser picks up on launch.
   CStringA testFilePath;
   if (sFirefoxPath.GetLength()) {
     // Use the firefoxpath passed to us by the test harness
     int index = sFirefoxPath.ReverseFind('\\');
     if (index == -1) {
-      Fail(L"Bad firefoxpath path");
+      Fail(false, L"Bad firefoxpath path");
       return FAILURE;
     }
     testFilePath = sFirefoxPath.Mid(0, index);
     testFilePath += "\\";
     testFilePath += kMetroTestFile;
   } else {
     // Use the module path
     char path[MAX_PATH];
     if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
-      Fail(L"GetModuleFileNameA errorno=%d", GetLastError());
+      Fail(false, L"GetModuleFileNameA errorno=%d", GetLastError());
       return FAILURE;
     }
     char* slash = strrchr(path, '\\');
     if (!slash)
       return FAILURE;
     *slash = '\0'; // no trailing slash
     testFilePath = path;
     testFilePath += "\\";
     sFirefoxPath = testFilePath;
     sFirefoxPath += kFirefoxExe;
     testFilePath += kMetroTestFile;
   }
 
   // Make sure the firefox bin exists
   if (GetFileAttributesW(sFirefoxPath) == INVALID_FILE_ATTRIBUTES) {
-    Fail(L"Invalid bin path: '%s'", sFirefoxPath);
+    Fail(false, L"Invalid bin path: '%s'", sFirefoxPath);
     return FAILURE;
   }
 
   Log(L"Using bin path: '%s'", sFirefoxPath);
 
   Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath));
   HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE,
                                  0, NULL, CREATE_ALWAYS,
                                  FILE_ATTRIBUTE_NORMAL,
                                  NULL);
   if (hTestFile == INVALID_HANDLE_VALUE) {
-    Fail(L"CreateFileA errorno=%d", GetLastError());
+    Fail(false, L"CreateFileA errorno=%d", GetLastError());
     return FAILURE;
   }
 
   DeleteTestFileHelper dtf(testFilePath);
 
   // nsAppRunner expects the first param to be the bin path, just like a
   // normal startup. So prepend our bin path to our param string we write.
   CStringA asciiParams = sFirefoxPath;
   asciiParams += " ";
   asciiParams += sAppParams;
   asciiParams.Trim();
   Log(L"Browser command line args: '%s'", CString(asciiParams));
   if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) {
     CloseHandle(hTestFile);
-    Fail(L"WriteFile errorno=%d", GetLastError());
+    Fail(false, L"WriteFile errorno=%d", GetLastError());
     return FAILURE;
   }
   FlushFileBuffers(hTestFile);
   CloseHandle(hTestFile);
 
   // Create a named stdout pipe for the browser
   if (!SetupTestOutputPipe()) {
-    Fail(L"SetupTestOutputPipe failed (errno=%d)", GetLastError());
+    Fail(false, L"SetupTestOutputPipe failed (errno=%d)", GetLastError());
     return FAILURE;
   }
 
   // Launch firefox
   hr = activateMgr->ActivateApplication(appModelID, L"", AO_NOERRORUI, &processID);
   if (FAILED(hr)) {
-    Fail(L"ActivateApplication result %X", hr);
+    Fail(true, L"ActivateApplication result %X", hr);
     return RETRY;
   }
 
   Log(L"Activation succeeded. processid=%d", processID);
 
   HANDLE child = OpenProcess(SYNCHRONIZE, FALSE, processID);
   if (!child) {
-    Fail(L"Couldn't find child process. (%d)", GetLastError());
+    Fail(false, L"Couldn't find child process. (%d)", GetLastError());
     return FAILURE;
   }
 
   Log(L"Waiting on child process...");
 
   MSG msg;
   DWORD waitResult = WAIT_TIMEOUT;
   HANDLE handles[2] = { child, gTestOutputPipe };
--- a/build/autoconf/android.m4
+++ b/build/autoconf/android.m4
@@ -300,25 +300,40 @@ case "$target" in
             AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($1 or higher required).])
         fi
     fi
 
     android_platform_tools="$android_sdk"/../../platform-tools
     if test ! -d "$android_platform_tools" ; then
         android_platform_tools="$android_sdk"/tools # SDK Tools < r8
     fi
+    # The build tools got moved around to different directories in
+    # SDK Tools r22.  Try to locate them.
+    android_build_tools=""
+    for suffix in 17.0.0 android-4.2.2; do
+        tools_directory="$android_sdk/../../build-tools/$suffix"
+        if test -d "$tools_directory" ; then
+            android_build_tools="$tools_directory"
+            break
+        fi
+    done
+    if test -z "$android_build_tools" ; then
+        android_build_tools="$android_platform_tools" # SDK Tools < r22
+    fi
     ANDROID_SDK="${android_sdk}"
     if test -e "${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" ; then
         ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar"
     else
         ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/support/v4/android-support-v4.jar";
     fi
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
+    ANDROID_BUILD_TOOLS="${android_build_tools}"
     AC_SUBST(ANDROID_SDK)
     AC_SUBST(ANDROID_COMPAT_LIB)
     if ! test -e $ANDROID_COMPAT_LIB ; then
         AC_MSG_ERROR([You must download the Android support library when targeting Android.   Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)])
     fi
     AC_SUBST(ANDROID_PLATFORM_TOOLS)
+    AC_SUBST(ANDROID_BUILD_TOOLS)
     ;;
 esac
 
 ])
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -8,16 +8,18 @@ srcdir      = @srcdir@
 VPATH       = @srcdir@
 
 mobile-tests := mobile/android/base/tests
 TESTPATH     := $(topsrcdir)/$(mobile-tests)
 dir-tests    := $(DEPTH)/$(mobile-tests)
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := robocop-debug
+
 ROBOTIUM_PATH = $(srcdir)/robotium-solo-3.6.jar
 
 JAVAFILES = \
   R.java \
   $(NULL)
 
 RES_FILES = \
   res/values/strings.xml \
@@ -76,49 +78,55 @@ MOCHITEST_ROBOCOP_FILES := \
   $(wildcard $(TESTPATH)/*.xml) \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml \
   $(java-tests-dep) \
   $(_JAVA_HARNESS) \
   classes.dex \
-  robocop.ap_ \
-  robocop-debug-signed.apk \
-  robocop-debug-signed-unaligned.apk \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(robocop-deps) \
   $(NULL)
 
 DEFINES += \
   -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
   $(NULL)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(ROBOTIUM_PATH)
 
 include $(topsrcdir)/config/rules.mk
 
 # Override rules.mk java flags with the android specific ones
 include $(topsrcdir)/config/android-common.mk
 
 GENERATED_DIRS_tools = classes $(dir-tests)
 
-libs:: robocop-debug-signed.apk
+tools:: $(ANDROID_APK_NAME).apk
 
-classes.dex: robocop.ap_
+classes.dex: $(ANDROID_APK_NAME).ap_
 classes.dex: $(robocop-deps)
 classes.dex: $(java-harness-dep)
 classes.dex: $(java-tests-dep)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes $(JAVAFILES) $(_JAVA_HARNESS) $(java-tests-dep)
 	$(DX) --dex --output=$@ classes $(ROBOTIUM_PATH) $(ANDROID_COMPT_LIB)
 
-robocop.ap_: AndroidManifest.xml $(TESTPATH)/assets/*
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml $(TESTPATH)/assets/*
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -I . -S res -A $(TESTPATH)/assets -F $@ -J ./
 
-robocop-debug-signed-unaligned.apk: robocop.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z robocop.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -v0 $@ classes.dex
 
-robocop-debug-signed.apk: robocop-debug-signed-unaligned.apk
-	$(ZIPALIGN) -f -v 4 $^ $@
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
+
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
 
 # PP_java-tests not fully usable here
 # Intermediate step toward a library rule.
 $(dir-tests)/%.java: $(TESTPATH)/%.java.in $(call mkdir_deps,$(dir-tests))
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
--- a/build/mobile/sutagent/android/Makefile.in
+++ b/build/mobile/sutagent/android/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := sutAgentAndroid
+
 JAVAFILES = \
   AlertLooperThread.java \
   ASMozStub.java \
   CmdWorkerThread.java \
   DataWorkerThread.java \
   DoAlert.java \
   DoCommand.java \
   FindProcThread.java \
@@ -34,45 +36,44 @@ RES_FILES = \
   res/drawable/ic_stat_warning.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  sutAgentAndroid.apk  \
-  sutAgentAndroid.ap_ \
-  sutAgentAndroid-unsigned-unaligned.apk \
-  sutAgentAndroid-unaligned.apk \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 EXTRA_JARS = $(srcdir)/network-libs/commons-net-2.0.jar:$(srcdir)/network-libs/jmdns.jar
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar:$(EXTRA_JARS)
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: sutAgentAndroid.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes $(subst :, ,$(EXTRA_JARS))
 
-sutAgentAndroid.ap_: $(srcdir)/AndroidManifest.xml
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
 	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-sutAgentAndroid-unsigned-unaligned.apk: sutAgentAndroid.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z sutAgentAndroid.ap_ -f classes.dex
-
-sutAgentAndroid-unaligned.apk: sutAgentAndroid-unsigned-unaligned.apk
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
 	cp $< $@
-ifdef JARSIGNER
-	$(JARSIGNER) $@
-endif
+	$(ZIP) -v0 $@ classes.dex
 
-sutAgentAndroid.apk: sutAgentAndroid-unaligned.apk
-	$(ZIPALIGN) -f -v 4 sutAgentAndroid-unaligned.apk $@
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
+
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/fencp/Makefile.in
+++ b/build/mobile/sutagent/android/fencp/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := FenCP
+
 JAVAFILES = \
   DirCursor.java \
   FenCP.java \
   FenCPFP.java \
   FileCursor.java \
   R.java \
   $(NULL)
 
@@ -23,40 +25,42 @@ RES_FILES = \
   res/drawable-mdpi/icon.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  FenCP.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: FenCP.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-FenCP.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-FenCP-unsigned-unaligned.apk: FenCP.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FenCP.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -v0 $@ classes.dex
 
-FenCP-unaligned.apk: FenCP-unsigned-unaligned.apk
-	cp  FenCP-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-FenCP.apk: FenCP-unaligned.apk
-	$(ZIPALIGN) -f -v 4 FenCP-unaligned.apk $@
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/ffxcp/Makefile.in
+++ b/build/mobile/sutagent/android/ffxcp/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := FfxCP
+
 JAVAFILES = \
   DirCursor.java \
   ffxcp.java \
   FfxCPFP.java \
   FileCursor.java \
   R.java \
   $(NULL)
 
@@ -23,40 +25,42 @@ RES_FILES = \
   res/drawable-mdpi/icon.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  FfxCP.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: FfxCP.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-FfxCP.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-FfxCP-unsigned-unaligned.apk: FfxCP.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z FfxCP.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -v0 $@ classes.dex
 
-FfxCP-unaligned.apk: FfxCP-unsigned-unaligned.apk
-	cp  FfxCP-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-FfxCP.apk: FfxCP-unaligned.apk
-	$(ZIPALIGN) -f -v 4 FfxCP-unaligned.apk $@
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/build/mobile/sutagent/android/watcher/Makefile.in
+++ b/build/mobile/sutagent/android/watcher/Makefile.in
@@ -4,16 +4,18 @@
 
 DEPTH		= @DEPTH@
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
+ANDROID_APK_NAME := Watcher
+
 JAVAFILES = \
   IWatcherService.java \
   RedirOutputThread.java \
   R.java \
   WatcherMain.java \
   WatcherReceiver.java \
   WatcherService.java \
   $(NULL)
@@ -27,46 +29,43 @@ RES_FILES = \
   res/drawable-mdpi/ateamlogo.png \
   res/layout/main.xml \
   res/values/strings.xml \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml  \
   classes.dex  \
-  Watcher.apk  \
+  $(ANDROID_APK_NAME).ap_ \
+  $(ANDROID_APK_NAME)-unsigned-unaligned.apk \
+  $(ANDROID_APK_NAME)-unaligned.apk \
+  $(ANDROID_APK_NAME).apk \
   $(NULL)
 
 GARBAGE_DIRS += res classes network-libs
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 include $(topsrcdir)/config/rules.mk
 
 # include Android specific java flags - using these instead of what's in rules.mk
 include $(topsrcdir)/config/android-common.mk
 
-tools:: Watcher.apk
+tools:: $(ANDROID_APK_NAME).apk
 
 classes.dex: $(JAVAFILES)
 	$(NSINSTALL) -D classes
 	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES))
 	$(DX) --dex --output=$@ classes
 
-Watcher.ap_: $(srcdir)/AndroidManifest.xml
-	$(AAPT) package -f -M $(srcdir)/AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+$(ANDROID_APK_NAME).ap_: AndroidManifest.xml
+	$(AAPT) package -f -M $< -I $(ANDROID_SDK)/android.jar -S res -F $@
 
-Watcher-unsigned-unaligned.apk: Watcher.ap_ classes.dex
-	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z Watcher.ap_ -f classes.dex
+$(ANDROID_APK_NAME)-unsigned-unaligned.apk: $(ANDROID_APK_NAME).ap_ classes.dex
+	cp $< $@
+	$(ZIP) -v0 $@ classes.dex
 
-Watcher-unaligned.apk: Watcher-unsigned-unaligned.apk
-	cp  Watcher-unsigned-unaligned.apk $@
-ifdef JARSIGNER
-  $(JARSIGNER) $@
-endif
+$(ANDROID_APK_NAME)-unaligned.apk: $(ANDROID_APK_NAME)-unsigned-unaligned.apk
+	cp $< $@
+	$(DEBUG_JARSIGNER) $@
 
-Watcher.apk: Watcher-unaligned.apk
-	$(ZIPALIGN) -f -v 4 Watcher-unaligned.apk $@
-
-export::
-	$(NSINSTALL) -D res
-	@(cd $(srcdir)/res && tar $(TAR_CREATE_FLAGS) - *) | (cd $(DEPTH)/build/mobile/sutagent/android/watcher/res && tar -xf -)
-
+$(ANDROID_APK_NAME).apk: $(ANDROID_APK_NAME)-unaligned.apk
+	$(ZIPALIGN) -f -v 4 $< $@
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -16,17 +16,16 @@ include $(DEPTH)/config/autoconf.mk
 VISIBILITY_FLAGS =
 
 # STDCXX_COMPAT is not needed here, and will actually fail because
 # libstdc++-compat is not built yet.
 STDCXX_COMPAT =
 
 ifneq (WINNT,$(HOST_OS_ARCH))
 HOST_PROGRAM	= nsinstall_real$(HOST_BIN_SUFFIX)
-DISABLED_HOST_CSRCS	= nsinstall.c pathsub.c
 endif
 
 ifndef CROSS_COMPILE
 ifdef USE_ELF_DYNSTR_GC
 export:: elf-dynstr-gc
 endif
 endif
 
--- a/config/android-common.mk
+++ b/config/android-common.mk
@@ -7,24 +7,21 @@
 ifndef ANDROID_SDK
   $(error ANDROID_SDK must be defined before including android-common.mk)
 endif
 
 ifndef JAVA_CLASSPATH
   $(error JAVA_CLASSPATH must be defined before including android-common.mk)
 endif
 
-DX=$(ANDROID_PLATFORM_TOOLS)/dx
-AAPT=$(ANDROID_PLATFORM_TOOLS)/aapt
-APKBUILDER=$(ANDROID_SDK)/../../tools/apkbuilder
+DX=$(ANDROID_BUILD_TOOLS)/dx
+AAPT=$(ANDROID_BUILD_TOOLS)/aapt
 ZIPALIGN=$(ANDROID_SDK)/../../tools/zipalign
-
-ifdef JARSIGNER
-  APKBUILDER_FLAGS += -u
-endif
+# DEBUG_JARSIGNER always debug signs.
+DEBUG_JARSIGNER=$(PYTHON) $(call core_abspath,$(topsrcdir)/mobile/android/debug_sign_tool.py)
 
 # For Android, this defaults to $(ANDROID_SDK)/android.jar
 ifndef JAVA_BOOTCLASSPATH
   JAVA_BOOTCLASSPATH = $(ANDROID_SDK)/android.jar:$(ANDROID_COMPAT_LIB)
 endif
 
 # For Android, we default to 1.5
 ifndef JAVA_VERSION
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -11,16 +11,17 @@ ifndef topsrcdir
 endif
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
   DIRS \
+  HOST_CSRCS \
   MODULE \
   PARALLEL_DIRS \
   TEST_DIRS \
   TIERS \
   TOOL_DIRS \
   XPIDL_MODULE \
   $(NULL)
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -505,27 +505,16 @@ public:
    * @return boolean indicating whether a BOM was detected.
    */
   static bool CheckForBOM(const unsigned char* aBuffer, uint32_t aLength,
                           nsACString& aCharset);
 
   static nsresult GuessCharset(const char *aData, uint32_t aDataLen,
                                nsACString &aCharset);
 
-  /**
-   * Determine whether aContent is in some way associated with aForm.  If the
-   * form is a container the only elements that are considered to be associated
-   * with a form are the elements that are contained within the form. If the
-   * form is a leaf element then all elements will be accepted into this list,
-   * since this can happen due to content fixup when a form spans table rows or
-   * table cells.
-   */
-  static bool BelongsInForm(nsIContent *aForm,
-                              nsIContent *aContent);
-
   static nsresult CheckQName(const nsAString& aQualifiedName,
                              bool aNamespaceAware = true,
                              const PRUnichar** aColon = nullptr);
 
   static nsresult SplitQName(const nsIContent* aNamespaceResolver,
                              const nsAFlatString& aQName,
                              int32_t *aNamespace, nsIAtom **aLocalName);
 
--- a/content/base/src/nsContentList.cpp
+++ b/content/base/src/nsContentList.cpp
@@ -135,36 +135,16 @@ NS_IMPL_ADDREF_INHERITED(nsSimpleContent
 NS_IMPL_RELEASE_INHERITED(nsSimpleContentList, nsBaseContentList)
 
 JSObject*
 nsSimpleContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
 {
   return NodeListBinding::Wrap(cx, scope, this);
 }
 
-// nsFormContentList
-
-nsFormContentList::nsFormContentList(nsIContent *aForm,
-                                     nsBaseContentList& aContentList)
-  : nsSimpleContentList(aForm)
-{
-
-  // move elements that belong to mForm into this content list
-
-  uint32_t i, length = 0;
-  aContentList.GetLength(&length);
-
-  for (i = 0; i < length; i++) {
-    nsIContent *c = aContentList.Item(i);
-    if (c && nsContentUtils::BelongsInForm(aForm, c)) {
-      AppendElement(c);
-    }
-  }
-}
-
 // Hashtable for storing nsContentLists
 static PLDHashTable gContentListHashTable;
 
 struct ContentListHashEntry : public PLDHashEntryHdr
 {
   nsContentList* mContentList;
 };
 
--- a/content/base/src/nsContentList.h
+++ b/content/base/src/nsContentList.h
@@ -119,26 +119,16 @@ public:
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
 
 private:
   // This has to be a strong reference, the root might go away before the list.
   nsCOMPtr<nsINode> mRoot;
 };
 
-// This class is used only by form element code and this is a static
-// list of elements. NOTE! This list holds strong references to
-// the elements in the list.
-class nsFormContentList : public nsSimpleContentList
-{
-public:
-  nsFormContentList(nsIContent *aForm,
-                    nsBaseContentList& aContentList);
-};
-
 /**
  * Class that's used as the key to hash nsContentList implementations
  * for fast retrieval
  */
 struct nsContentListKey
 {
   nsContentListKey(nsINode* aRootNode,
                    int32_t aMatchNameSpaceId,
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2361,72 +2361,16 @@ nsContentUtils::NewURIWithDocumentCharse
                                           nsIURI* aBaseURI)
 {
   return NS_NewURI(aResult, aSpec,
                    aDocument ? aDocument->GetDocumentCharacterSet().get() : nullptr,
                    aBaseURI, sIOService);
 }
 
 // static
-bool
-nsContentUtils::BelongsInForm(nsIContent *aForm,
-                              nsIContent *aContent)
-{
-  NS_PRECONDITION(aForm, "Must have a form");
-  NS_PRECONDITION(aContent, "Must have a content node");
-
-  if (aForm == aContent) {
-    // A form does not belong inside itself, so we return false here
-
-    return false;
-  }
-
-  nsIContent* content = aContent->GetParent();
-
-  while (content) {
-    if (content == aForm) {
-      // aContent is contained within the form so we return true.
-
-      return true;
-    }
-
-    if (content->Tag() == nsGkAtoms::form &&
-        content->IsHTML()) {
-      // The child is contained within a form, but not the right form
-      // so we ignore it.
-
-      return false;
-    }
-
-    content = content->GetParent();
-  }
-
-  if (aForm->GetChildCount() > 0) {
-    // The form is a container but aContent wasn't inside the form,
-    // return false
-
-    return false;
-  }
-
-  // The form is a leaf and aContent wasn't inside any other form so
-  // we check whether the content comes after the form.  If it does,
-  // return true.  If it does not, then it couldn't have been inside
-  // the form in the HTML.
-  if (PositionIsBefore(aForm, aContent)) {
-    // We could be in this form!
-    // In the future, we may want to get document.forms, look at the
-    // form after aForm, and if aContent is after that form after
-    // aForm return false here....
-    return true;
-  }
-
-  return false;
-}
-
-// static
 nsresult
 nsContentUtils::CheckQName(const nsAString& aQualifiedName,
                            bool aNamespaceAware,
                            const PRUnichar** aColon)
 {
   const char* colon = nullptr;
   const PRUnichar* begin = aQualifiedName.BeginReading();
   const PRUnichar* end = aQualifiedName.EndReading();
--- a/content/base/src/nsDOMFileReader.h
+++ b/content/base/src/nsDOMFileReader.h
@@ -140,12 +140,12 @@ protected:
   nsCString mCharset;
   uint32_t mDataLen;
 
   eDataFormat mDataFormat;
 
   nsString mResult;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   
-  JSObject* mResultArrayBuffer;
+  JS::Heap<JSObject*> mResultArrayBuffer;
 };
 
 #endif
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1781,17 +1781,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 struct CustomPrototypeTraceArgs {
   const TraceCallbacks& callbacks;
   void* closure;
 };
 
 
 static PLDHashOperator
-CustomPrototypeTrace(const nsAString& aName, JSObject*& aObject, void *aArg)
+CustomPrototypeTrace(const nsAString& aName, JS::Heap<JSObject*>& aObject, void *aArg)
 {
   CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg);
   MOZ_ASSERT(aObject, "Protocol object value must not be null");
   traceArgs->callbacks.Trace(&aObject, "mCustomPrototypes entry", traceArgs->closure);
   return PL_DHASH_NEXT;
 }
 
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -26,16 +26,17 @@
 #include "nsIContent.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMNodeSelector.h"
 #include "nsIPrincipal.h"
 #include "nsIParser.h"
 #include "nsBindingManager.h"
 #include "nsINodeInfo.h"
 #include "nsInterfaceHashtable.h"
+#include "nsJSThingHashtable.h"
 #include "nsIBoxObject.h"
 #include "nsPIBoxObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsScriptLoader.h"
 #include "nsIRadioGroupContainer.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIRequest.h"
@@ -1198,17 +1199,17 @@ protected:
   nsTArray<nsWeakPtr> mFullScreenStack;
 
   // The root of the doc tree in which this document is in. This is only
   // non-null when this document is in fullscreen mode.
   nsWeakPtr mFullscreenRoot;
 
   // Hashtable for custom element prototypes in web components.
   // Custom prototypes are in the document's compartment.
-  nsDataHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
+  nsJSThingHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
 
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -20,16 +20,17 @@
 #include "nsCOMArray.h"
 #include "nsPIDOMWindow.h"
 #include "nsDocument.h"
 #ifdef MOZ_XUL
 #include "nsXULElement.h"
 #endif
 #include "nsBindingManager.h"
 #include "nsGenericHTMLElement.h"
+#include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "nsWrapperCacheInlines.h"
 #include "nsObjectLoadingContent.h"
 #include "nsDOMMutationObserver.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 
 using namespace mozilla::dom;
@@ -211,16 +212,21 @@ nsNodeUtils::LastRelease(nsINode* aNode)
 
     // I wonder whether it's faster to do the HasFlag check first....
     if (aNode->IsNodeOfType(nsINode::eHTML_FORM_CONTROL) &&
         aNode->HasFlag(ADDED_TO_FORM)) {
       // Tell the form (if any) this node is going away.  Don't
       // notify, since we're being destroyed in any case.
       static_cast<nsGenericHTMLFormElement*>(aNode)->ClearForm(true);
     }
+
+    if (aNode->IsElement() && aNode->AsElement()->IsHTML(nsGkAtoms::img)) {
+      HTMLImageElement* imageElem = static_cast<HTMLImageElement*>(aNode);
+      imageElem->ClearForm(true);
+    }
   }
   aNode->UnsetFlags(NODE_HAS_PROPERTIES);
 
   if (aNode->NodeType() != nsIDOMNode::DOCUMENT_NODE &&
       aNode->HasFlag(NODE_HAS_LISTENERMANAGER)) {
 #ifdef DEBUG
     if (nsContentUtils::IsInitialized()) {
       nsEventListenerManager* manager =
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -777,22 +777,24 @@ nsresult
 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
 {
   if (!aCx) {
     return NS_ERROR_FAILURE;
   }
   RootJSResultObjects();
 
   // The Unicode converter has already zapped the BOM if there was one
+  JS::Rooted<JS::Value> value(aCx);
   if (!JS_ParseJSON(aCx,
                     static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(),
-                    JS::MutableHandle<JS::Value>::fromMarkedLocation(&mResultJSON))) {
+                    &value)) {
     return NS_ERROR_FAILURE;
   }
 
+  mResultJSON = value;
   return NS_OK;
 }
 
 void
 nsXMLHttpRequest::CreatePartialBlob()
 {
   if (mDOMFile) {
     if (mLoadTotal == mLoadTransferred) {
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -652,24 +652,24 @@ protected:
    * @param aType The progress event type.
    * @param aFlag A XML_HTTP_REQUEST_* state flag defined in
    *              nsXMLHttpRequest.cpp.
    */
   void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag);
 
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
-  
+
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
-  
-  JS::Value mResultJSON;
+
+  JS::Heap<JS::Value> mResultJSON;
 
   js::ArrayBufferBuilder mArrayBufferBuilder;
-  JSObject* mResultArrayBuffer;
+  JS::Heap<JSObject*> mResultArrayBuffer;
 
   void ResetResponse();
 
   struct RequestHeader
   {
     nsCString header;
     nsCString value;
   };
--- a/content/canvas/src/ImageData.h
+++ b/content/canvas/src/ImageData.h
@@ -64,15 +64,15 @@ public:
 
 private:
   void HoldData();
   void DropData();
 
   ImageData() MOZ_DELETE;
 
   uint32_t mWidth, mHeight;
-  JSObject* mData;
+  JS::Heap<JSObject*> mData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImageData_h
--- a/content/events/src/nsDOMMessageEvent.h
+++ b/content/events/src/nsDOMMessageEvent.h
@@ -60,15 +60,15 @@ public:
                         nsIDOMWindow* aSource,
                         mozilla::ErrorResult& aRv)
   {
     aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData,
                            aOrigin, aLastEventId, aSource);
   }
 
 private:
-  JS::Value mData;
+  JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mSource;
 };
 
 #endif // nsDOMMessageEvent_h__
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.h
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.h
@@ -65,13 +65,13 @@ public:
                                uint32_t aFrameBufferLength,
                                float aTime,
                                bool aAllowAudioData,
                                mozilla::ErrorResult& aRv);
 private:
   nsAutoArrayPtr<float> mFrameBuffer;
   uint32_t mFrameBufferLength;
   float mTime;
-  JSObject* mCachedArray;
+  JS::Heap<JSObject*> mCachedArray;
   bool mAllowAudioData;
 };
 
 #endif // nsDOMNotifyAudioAvailableEvent_h_
--- a/content/html/content/src/HTMLImageElement.cpp
+++ b/content/html/content/src/HTMLImageElement.cpp
@@ -20,16 +20,17 @@
 #include "nsNetUtil.h"
 #include "nsContentUtils.h"
 #include "nsIFrame.h"
 #include "nsNodeInfoManager.h"
 #include "nsGUIEvent.h"
 #include "nsContentPolicyUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsFocusManager.h"
+#include "nsHTMLFormElement.h"
 
 #include "imgIContainer.h"
 #include "imgILoader.h"
 #include "imgINotificationObserver.h"
 #include "imgRequestProxy.h"
 
 #include "nsILoadGroup.h"
 
@@ -62,16 +63,17 @@ NS_NewHTMLImageElement(already_AddRefed<
   return new mozilla::dom::HTMLImageElement(nodeInfo.forget());
 }
 
 namespace mozilla {
 namespace dom {
 
 HTMLImageElement::HTMLImageElement(already_AddRefed<nsINodeInfo> aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo)
+  , mForm(nullptr)
 {
   // We start out broken
   AddStatesSilently(NS_EVENT_STATE_BROKEN);
   SetIsDOMBinding();
 }
 
 HTMLImageElement::~HTMLImageElement()
 {
@@ -79,17 +81,17 @@ HTMLImageElement::~HTMLImageElement()
 }
 
 
 NS_IMPL_ADDREF_INHERITED(HTMLImageElement, Element)
 NS_IMPL_RELEASE_INHERITED(HTMLImageElement, Element)
 
 
 // QueryInterface implementation for HTMLImageElement
-NS_INTERFACE_TABLE_HEAD(HTMLImageElement)
+NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(HTMLImageElement)
   NS_HTML_CONTENT_INTERFACES(nsGenericHTMLElement)
   NS_INTERFACE_TABLE_INHERITED4(HTMLImageElement,
                                 nsIDOMHTMLImageElement,
                                 nsIImageLoadingContent,
                                 imgIOnloadBlocker,
                                 imgINotificationObserver)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE
 NS_ELEMENT_INTERFACE_MAP_END
@@ -292,16 +294,57 @@ HTMLImageElement::IsAttributeMapped(cons
 nsMapRuleToAttributesFunc
 HTMLImageElement::GetAttributeMappingFunction() const
 {
   return &MapAttributesIntoRule;
 }
 
 
 nsresult
+HTMLImageElement::BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValueOrString* aValue,
+                                bool aNotify)
+{
+
+  if (aNameSpaceID == kNameSpaceID_None && mForm &&
+      (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
+    // remove the image from the hashtable as needed
+    nsAutoString tmp;
+    GetAttr(kNameSpaceID_None, aName, tmp);
+
+    if (!tmp.IsEmpty()) {
+      mForm->RemoveImageElementFromTable(this, tmp,
+                                         nsHTMLFormElement::AttributeUpdated);
+    }
+  }
+
+  return nsGenericHTMLElement::BeforeSetAttr(aNameSpaceID, aName,
+                                             aValue, aNotify);
+}
+
+nsresult
+HTMLImageElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                               const nsAttrValue* aValue, bool aNotify)
+{
+  if (aNameSpaceID == kNameSpaceID_None && mForm &&
+      (aName == nsGkAtoms::name || aName == nsGkAtoms::id) &&
+      aValue && !aValue->IsEmptyString()) {
+    // add the image to the hashtable as needed
+    NS_ABORT_IF_FALSE(aValue->Type() == nsAttrValue::eAtom,
+      "Expected atom value for name/id");
+    mForm->AddImageElementToTable(this,
+      nsDependentAtomString(aValue->GetAtomValue()));
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName,
+                                            aValue, aNotify);
+}
+
+
+nsresult
 HTMLImageElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
 {
   // If we are a map and get a mouse click, don't let it be handled by
   // the Generic Element as this could cause a click event to fire
   // twice, once by the image frame for the map and once by the Anchor
   // element. (bug 39723)
   if (aVisitor.mEvent->eventStructType == NS_MOUSE_EVENT &&
       aVisitor.mEvent->message == NS_MOUSE_CLICK &&
@@ -409,16 +452,20 @@ HTMLImageElement::BindToTree(nsIDocument
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsImageLoadingContent::BindToTree(aDocument, aParent, aBindingParent,
                                     aCompileEventHandlers);
 
+  if (aParent) {
+    UpdateFormOwner();
+  }
+
   if (HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
     // FIXME: Bug 660963 it would be nice if we could just have
     // ClearBrokenState update our state and do it fast...
     ClearBrokenState();
     RemoveStatesSilently(NS_EVENT_STATE_BROKEN);
     // If loading is temporarily disabled, don't even launch MaybeLoadImage.
     // Otherwise MaybeLoadImage may run later when someone has reenabled
     // loading.
@@ -429,21 +476,56 @@ HTMLImageElement::BindToTree(nsIDocument
   }
 
   return rv;
 }
 
 void
 HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
+  if (mForm) {
+    if (aNullParent || !FindAncestorForm(mForm)) {
+      ClearForm(true);
+    } else {
+      UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
+    }
+  }
+
   nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 }
 
 void
+HTMLImageElement::UpdateFormOwner()
+{
+  if (!mForm) {
+    mForm = FindAncestorForm();
+  }
+
+  if (mForm && !HasFlag(ADDED_TO_FORM)) {
+    // Now we need to add ourselves to the form
+    nsAutoString nameVal, idVal;
+    GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
+    GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
+
+    SetFlags(ADDED_TO_FORM);
+
+    mForm->AddImageElement(this);
+
+    if (!nameVal.IsEmpty()) {
+      mForm->AddImageElementToTable(this, nameVal);
+    }
+
+    if (!idVal.IsEmpty()) {
+      mForm->AddImageElementToTable(this, idVal);
+    }
+  }
+}
+
+void
 HTMLImageElement::MaybeLoadImage()
 {
   // Our base URI may have changed; claim that our URI changed, and the
   // nsImageLoadingContent will decide whether a new image load is warranted.
   // Note, check LoadingEnabled() after LoadImage call.
   nsAutoString uri;
   if (GetAttr(kNameSpaceID_None, nsGkAtoms::src, uri) &&
       (NS_FAILED(LoadImage(uri, false, true)) ||
@@ -572,10 +654,60 @@ HTMLImageElement::GetCORSMode()
 }
 
 JSObject*
 HTMLImageElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return HTMLImageElementBinding::Wrap(aCx, aScope, this);
 }
 
+#ifdef DEBUG
+nsIDOMHTMLFormElement*
+HTMLImageElement::GetForm() const
+{
+  return mForm;
+}
+#endif
+
+void
+HTMLImageElement::SetForm(nsIDOMHTMLFormElement* aForm)
+{
+  NS_PRECONDITION(aForm, "Don't pass null here");
+  NS_ASSERTION(!mForm,
+               "We don't support switching from one non-null form to another.");
+
+  mForm = static_cast<nsHTMLFormElement*>(aForm);
+}
+
+void
+HTMLImageElement::ClearForm(bool aRemoveFromForm)
+{
+  NS_ASSERTION((mForm != nullptr) == HasFlag(ADDED_TO_FORM),
+               "Form control should have had flag set correctly");
+
+  if (!mForm) {
+    return;
+  }
+
+  if (aRemoveFromForm) {
+    nsAutoString nameVal, idVal;
+    GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
+    GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
+
+    mForm->RemoveImageElement(this);
+
+    if (!nameVal.IsEmpty()) {
+      mForm->RemoveImageElementFromTable(this, nameVal,
+                                         nsHTMLFormElement::ElementRemoved);
+    }
+
+    if (!idVal.IsEmpty()) {
+      mForm->RemoveImageElementFromTable(this, idVal,
+                                         nsHTMLFormElement::ElementRemoved);
+    }
+  }
+
+  UnsetFlags(ADDED_TO_FORM);
+  mForm = nullptr;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/HTMLImageElement.h
+++ b/content/html/content/src/HTMLImageElement.h
@@ -169,20 +169,38 @@ public:
   int32_t X();
   int32_t Y();
   // Uses XPCOM GetLowsrc.
   void SetLowsrc(const nsAString& aLowsrc, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::lowsrc, aLowsrc, aError);
   }
 
+#ifdef DEBUG
+  nsIDOMHTMLFormElement* GetForm() const;
+#endif
+  void SetForm(nsIDOMHTMLFormElement* aForm);
+  void ClearForm(bool aRemoveFromForm);
+
 protected:
   CSSIntPoint GetXY();
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
   virtual JSObject* WrapNode(JSContext *aCx,
                              JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
+  void UpdateFormOwner();
+
+  virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                 const nsAttrValueOrString* aValue,
+                                 bool aNotify) MOZ_OVERRIDE;
+
+  virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE;
+
+  // This is a weak reference that this element and the HTMLFormElement
+  // cooperate in maintaining.
+  nsHTMLFormElement* mForm;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_HTMLImageElement_h */
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2174,21 +2174,23 @@ nsGenericHTMLFormElement::ClearForm(bool
   if (aRemoveFromForm) {
     nsAutoString nameVal, idVal;
     GetAttr(kNameSpaceID_None, nsGkAtoms::name, nameVal);
     GetAttr(kNameSpaceID_None, nsGkAtoms::id, idVal);
 
     mForm->RemoveElement(this, true);
 
     if (!nameVal.IsEmpty()) {
-      mForm->RemoveElementFromTable(this, nameVal);
+      mForm->RemoveElementFromTable(this, nameVal,
+                                    nsHTMLFormElement::ElementRemoved);
     }
 
     if (!idVal.IsEmpty()) {
-      mForm->RemoveElementFromTable(this, idVal);
+      mForm->RemoveElementFromTable(this, idVal,
+                                    nsHTMLFormElement::ElementRemoved);
     }
   }
 
   UnsetFlags(ADDED_TO_FORM);
   mForm = nullptr;
 }
 
 Element*
@@ -2309,31 +2311,34 @@ nsGenericHTMLFormElement::BeforeSetAttr(
     nsAutoString tmp;
 
     // remove the control from the hashtable as needed
 
     if (mForm && (aName == nsGkAtoms::name || aName == nsGkAtoms::id)) {
       GetAttr(kNameSpaceID_None, aName, tmp);
 
       if (!tmp.IsEmpty()) {
-        mForm->RemoveElementFromTable(this, tmp);
+        mForm->RemoveElementFromTable(this, tmp,
+                                      nsHTMLFormElement::AttributeUpdated);
       }
     }
 
     if (mForm && aName == nsGkAtoms::type) {
       GetAttr(kNameSpaceID_None, nsGkAtoms::name, tmp);
 
       if (!tmp.IsEmpty()) {
-        mForm->RemoveElementFromTable(this, tmp);
+        mForm->RemoveElementFromTable(this, tmp,
+                                      nsHTMLFormElement::AttributeUpdated);
       }
 
       GetAttr(kNameSpaceID_None, nsGkAtoms::id, tmp);
 
       if (!tmp.IsEmpty()) {
-        mForm->RemoveElementFromTable(this, tmp);
+        mForm->RemoveElementFromTable(this, tmp,
+                                      nsHTMLFormElement::AttributeUpdated);
       }
 
       mForm->RemoveElement(this, false);
 
       // Removing the element from the form can make it not be the default
       // control anymore.  Go ahead and notify on that change, though we might
       // end up readding and becoming the default control again in
       // AfterSetAttr.
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -1042,25 +1042,26 @@ namespace dom {
 class HTMLFieldSetElement;
 }
 }
 
 #define FORM_ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + (n_))
 
 // Form element specific bits
 enum {
-  // If this flag is set on an nsGenericHTMLFormElement, that means that we have
-  // added ourselves to our mForm.  It's possible to have a non-null mForm, but
-  // not have this flag set.  That happens when the form is set via the content
-  // sink.
+  // If this flag is set on an nsGenericHTMLFormElement or an HTMLImageElement,
+  // that means that we have added ourselves to our mForm.  It's possible to
+  // have a non-null mForm, but not have this flag set.  That happens when the
+  // form is set via the content sink.
   ADDED_TO_FORM =                         FORM_ELEMENT_FLAG_BIT(0),
 
-  // If this flag is set on an nsGenericHTMLFormElement, that means that its form
-  // is in the process of being unbound from the tree, and this form element
-  // hasn't re-found its form in nsGenericHTMLFormElement::UnbindFromTree yet.
+  // If this flag is set on an nsGenericHTMLFormElement or an HTMLImageElement,
+  // that means that its form is in the process of being unbound from the tree,
+  // and this form element hasn't re-found its form in
+  // nsGenericHTMLFormElement::UnbindFromTree yet.
   MAYBE_ORPHAN_FORM_ELEMENT =             FORM_ELEMENT_FLAG_BIT(1)
 };
 
 // NOTE: I don't think it's possible to have the above two flags set at the
 // same time, so if it becomes an issue we can probably merge them into the
 // same bit.  --bz
 
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET + 2);
--- a/content/html/content/src/nsHTMLFormElement.cpp
+++ b/content/html/content/src/nsHTMLFormElement.cpp
@@ -49,16 +49,19 @@
 
 #include "nsIConstraintValidation.h"
 
 #include "nsIDOMHTMLButtonElement.h"
 #include "mozilla/dom/HTMLCollectionBinding.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "nsSandboxFlags.h"
 
+// images
+#include "mozilla/dom/HTMLImageElement.h"
+
 using namespace mozilla::dom;
 
 static const int NS_FORM_CONTROL_LIST_HASHTABLE_SIZE = 16;
 
 static const uint8_t NS_FORM_AUTOCOMPLETE_ON  = 1;
 static const uint8_t NS_FORM_AUTOCOMPLETE_OFF = 0;
 
 static const nsAttrValue::EnumTable kFormAutocompleteTable[] = {
@@ -99,16 +102,18 @@ public:
   }
 
   virtual JSObject* NamedItem(JSContext* cx, const nsAString& name,
                               mozilla::ErrorResult& error);
   virtual void GetSupportedNames(nsTArray<nsString>& aNames);
 
   nsresult AddElementToTable(nsGenericHTMLFormElement* aChild,
                              const nsAString& aName);
+  nsresult AddImageElementToTable(HTMLImageElement* aChild,
+                                  const nsAString& aName);
   nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aChild,
                                   const nsAString& aName);
   nsresult IndexOfControl(nsIFormControl* aControl,
                           int32_t* aIndex);
 
   nsISupports* NamedItemInternal(const nsAString& aName, bool aFlushContent);
   
   /**
@@ -236,23 +241,27 @@ nsHTMLFormElement::nsHTMLFormElement(alr
     mPendingSubmission(nullptr),
     mSubmittingRequest(nullptr),
     mDefaultSubmitElement(nullptr),
     mFirstSubmitInElements(nullptr),
     mFirstSubmitNotInElements(nullptr),
     mInvalidElementsCount(0),
     mEverTriedInvalidSubmit(false)
 {
+  mImageNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE);
+  mPastNameLookupTable.Init(NS_FORM_CONTROL_LIST_HASHTABLE_SIZE);
 }
 
 nsHTMLFormElement::~nsHTMLFormElement()
 {
   if (mControls) {
     mControls->DropFormReference();
   }
+
+  Clear();
 }
 
 nsresult
 nsHTMLFormElement::Init()
 {
   mControls = new nsFormControlList(this);
   if (!mControls) {
     return NS_ERROR_OUT_OF_MEMORY;
@@ -285,19 +294,26 @@ ElementTraverser(const nsAString& key, n
  
   cb->NoteXPCOMChild(element);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsHTMLFormElement,
                                                   nsGenericHTMLElement)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControls)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImageNameLookupTable)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPastNameLookupTable)
   tmp->mSelectedRadioButtons.EnumerateRead(ElementTraverser, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsHTMLFormElement,
+                                                nsGenericHTMLElement)
+  tmp->Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
 NS_IMPL_ADDREF_INHERITED(nsHTMLFormElement, Element)
 NS_IMPL_RELEASE_INHERITED(nsHTMLFormElement, Element)
 
 
 DOMCI_NODE_DATA(HTMLFormElement, nsHTMLFormElement)
 
 // QueryInterface implementation for nsHTMLFormElement
 NS_INTERFACE_TABLE_HEAD_CYCLE_COLLECTION_INHERITED(nsHTMLFormElement)
@@ -450,18 +466,19 @@ nsHTMLFormElement::BindToTree(nsIDocumen
   nsCOMPtr<nsIHTMLDocument> htmlDoc(do_QueryInterface(aDocument));
   if (htmlDoc) {
     htmlDoc->AddedForm();
   }
 
   return rv;
 }
 
+template<typename T>
 static void
-MarkOrphans(const nsTArray<nsGenericHTMLFormElement*>& aArray)
+MarkOrphans(const nsTArray<T*>& aArray)
 {
   uint32_t length = aArray.Length();
   for (uint32_t i = 0; i < length; ++i) {
     aArray[i]->SetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
   }
 }
 
 static void
@@ -478,18 +495,17 @@ CollectOrphans(nsINode* aRemovalRoot,
   // Walk backwards so that if we remove elements we can just keep iterating
   uint32_t length = aArray.Length();
   for (uint32_t i = length; i > 0; --i) {
     nsGenericHTMLFormElement* node = aArray[i-1];
 
     // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the
     // node is in fact a descendant of the form and hence should stay in the
     // form.  If it _is_ set, then we need to check whether the node is a
-    // descendant of aRemovalRoot.  If it is, we leave it in the form.  See
-    // also the code in nsGenericHTMLFormElement::FindForm.
+    // descendant of aRemovalRoot.  If it is, we leave it in the form.
 #ifdef DEBUG
     bool removed = false;
 #endif
     if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
       node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
       if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
         node->ClearForm(true);
 
@@ -506,51 +522,97 @@ CollectOrphans(nsINode* aRemovalRoot,
       nsCOMPtr<nsIDOMHTMLFormElement> form;
       node->GetForm(getter_AddRefs(form));
       NS_ASSERTION(form == aThisForm, "How did that happen?");
     }
 #endif /* DEBUG */
   }
 }
 
+static void
+CollectOrphans(nsINode* aRemovalRoot,
+               const nsTArray<HTMLImageElement*>& aArray
+#ifdef DEBUG
+               , nsIDOMHTMLFormElement* aThisForm
+#endif
+               )
+{
+  // Walk backwards so that if we remove elements we can just keep iterating
+  uint32_t length = aArray.Length();
+  for (uint32_t i = length; i > 0; --i) {
+    HTMLImageElement* node = aArray[i-1];
+
+    // Now if MAYBE_ORPHAN_FORM_ELEMENT is not set, that would mean that the
+    // node is in fact a descendant of the form and hence should stay in the
+    // form.  If it _is_ set, then we need to check whether the node is a
+    // descendant of aRemovalRoot.  If it is, we leave it in the form.
+#ifdef DEBUG
+    bool removed = false;
+#endif
+    if (node->HasFlag(MAYBE_ORPHAN_FORM_ELEMENT)) {
+      node->UnsetFlags(MAYBE_ORPHAN_FORM_ELEMENT);
+      if (!nsContentUtils::ContentIsDescendantOf(node, aRemovalRoot)) {
+        node->ClearForm(true);
+
+#ifdef DEBUG
+        removed = true;
+#endif
+      }
+    }
+
+#ifdef DEBUG
+    if (!removed) {
+      nsCOMPtr<nsIDOMHTMLFormElement> form = node->GetForm();
+      NS_ASSERTION(form == aThisForm, "How did that happen?");
+    }
+#endif /* DEBUG */
+  }
+}
+
 void
 nsHTMLFormElement::UnbindFromTree(bool aDeep, bool aNullParent)
 {
   nsCOMPtr<nsIHTMLDocument> oldDocument = do_QueryInterface(GetCurrentDoc());
 
   // Mark all of our controls as maybe being orphans
   MarkOrphans(mControls->mElements);
   MarkOrphans(mControls->mNotInElements);
+  MarkOrphans(mImageElements);
 
   nsGenericHTMLElement::UnbindFromTree(aDeep, aNullParent);
 
   nsINode* ancestor = this;
   nsINode* cur;
   do {
     cur = ancestor->GetParentNode();
     if (!cur) {
       break;
     }
     ancestor = cur;
   } while (1);
   
   CollectOrphans(ancestor, mControls->mElements
 #ifdef DEBUG
                  , this
-#endif                 
+#endif
                  );
   CollectOrphans(ancestor, mControls->mNotInElements
 #ifdef DEBUG
                  , this
-#endif                 
+#endif
+                 );
+  CollectOrphans(ancestor, mImageElements
+#ifdef DEBUG
+                 , this
+#endif
                  );
 
   if (oldDocument) {
     oldDocument->RemovedForm();
-  }     
+  }
   ForgetCurrentSubmission();
 }
 
 nsresult
 nsHTMLFormElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
 {
   aVisitor.mWantsWillHandleEvent = true;
   if (aVisitor.mEvent->originalTarget == static_cast<nsIContent*>(this)) {
@@ -1029,45 +1091,44 @@ nsHTMLFormElement::GetElementAt(int32_t 
  * @param aControl1 First control to compare.
  * @param aControl2 Second control to compare.
  * @param aForm Parent form of the controls.
  * @return < 0 if aControl1 is before aControl2,
  *         > 0 if aControl1 is after aControl2,
  *         0 otherwise
  */
 static inline int32_t
-CompareFormControlPosition(nsGenericHTMLFormElement *aControl1,
-                           nsGenericHTMLFormElement *aControl2,
+CompareFormControlPosition(Element *aElement1, Element *aElement2,
                            const nsIContent* aForm)
 {
-  NS_ASSERTION(aControl1 != aControl2, "Comparing a form control to itself");
+  NS_ASSERTION(aElement1 != aElement2, "Comparing a form control to itself");
 
   // If an element has a @form, we can assume it *might* be able to not have
   // a parent and still be in the form.
-  NS_ASSERTION((aControl1->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
-                aControl1->GetParent()) &&
-               (aControl2->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
-                aControl2->GetParent()),
+  NS_ASSERTION((aElement1->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
+                aElement1->GetParent()) &&
+               (aElement2->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
+                aElement2->GetParent()),
                "Form controls should always have parents");
 
   // If we pass aForm, we are assuming both controls are form descendants which
   // is not always the case. This function should work but maybe slower.
   // However, checking if both elements are form descendants may be slow too...
   // TODO: remove the prevent asserts fix, see bug 598468.
 #ifdef DEBUG
   nsLayoutUtils::gPreventAssertInCompareTreePosition = true;
-  int32_t rVal = nsLayoutUtils::CompareTreePosition(aControl1, aControl2, aForm);
+  int32_t rVal = nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
   nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
 
   return rVal;
 #else // DEBUG
-  return nsLayoutUtils::CompareTreePosition(aControl1, aControl2, aForm);
+  return nsLayoutUtils::CompareTreePosition(aElement1, aElement2, aForm);
 #endif // DEBUG
 }
- 
+
 #ifdef DEBUG
 /**
  * Checks that all form elements are in document order. Asserts if any pair of
  * consecutive elements are not in increasing document order.
  *
  * @param aControls List of form controls to check.
  * @param aForm Parent form of the controls.
  */
@@ -1101,72 +1162,84 @@ nsHTMLFormElement::PostPasswordEvent()
   }
 
   nsRefPtr<FormPasswordEvent> event =
     new FormPasswordEvent(this, NS_LITERAL_STRING("DOMFormHasPassword"));
   mFormPasswordEvent = event;
   event->PostDOMEvent();
 }
 
+// This function return true if the element, once appended, is the last one in
+// the array.
+template<typename ElementType>
+static bool
+AddElementToList(nsTArray<ElementType*>& aList, ElementType* aChild,
+                 nsHTMLFormElement* aForm)
+{
+  NS_ASSERTION(aList.IndexOf(aChild) == aList.NoIndex,
+               "aChild already in aList");
+
+  uint32_t count = aList.Length();
+  ElementType* element;
+  bool lastElement = false;
+
+  // Optimize most common case where we insert at the end.
+  int32_t position = -1;
+  if (count > 0) {
+    element = aList[count - 1];
+    position = CompareFormControlPosition(aChild, element, aForm);
+  }
+
+  // If this item comes after the last element, or the elements array is
+  // empty, we append to the end. Otherwise, we do a binary search to
+  // determine where the element should go.
+  if (position >= 0 || count == 0) {
+    // WEAK - don't addref
+    aList.AppendElement(aChild);
+    lastElement = true;
+  }
+  else {
+    int32_t low = 0, mid, high;
+    high = count - 1;
+
+    while (low <= high) {
+      mid = (low + high) / 2;
+
+      element = aList[mid];
+      position = CompareFormControlPosition(aChild, element, aForm);
+      if (position >= 0)
+        low = mid + 1;
+      else
+        high = mid - 1;
+    }
+
+    // WEAK - don't addref
+    aList.InsertElementAt(low, aChild);
+  }
+
+  return lastElement;
+}
+
 nsresult
 nsHTMLFormElement::AddElement(nsGenericHTMLFormElement* aChild,
                               bool aUpdateValidity, bool aNotify)
 {
   // If an element has a @form, we can assume it *might* be able to not have
   // a parent and still be in the form.
   NS_ASSERTION(aChild->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
                aChild->GetParent(),
                "Form control should have a parent");
 
   // Determine whether to add the new element to the elements or
   // the not-in-elements list.
   bool childInElements = ShouldBeInElements(aChild);
   nsTArray<nsGenericHTMLFormElement*>& controlList = childInElements ?
       mControls->mElements : mControls->mNotInElements;
-  
-  NS_ASSERTION(controlList.IndexOf(aChild) == controlList.NoIndex,
-               "Form control already in form");
-
-  uint32_t count = controlList.Length();
-  nsGenericHTMLFormElement* element;
-  
-  // Optimize most common case where we insert at the end.
-  bool lastElement = false;
-  int32_t position = -1;
-  if (count > 0) {
-    element = controlList[count - 1];
-    position = CompareFormControlPosition(aChild, element, this);
-  }
-
-  // If this item comes after the last element, or the elements array is
-  // empty, we append to the end. Otherwise, we do a binary search to
-  // determine where the element should go.
-  if (position >= 0 || count == 0) {
-    // WEAK - don't addref
-    controlList.AppendElement(aChild);
-    lastElement = true;
-  }
-  else {
-    int32_t low = 0, mid, high;
-    high = count - 1;
-      
-    while (low <= high) {
-      mid = (low + high) / 2;
-        
-      element = controlList[mid];
-      position = CompareFormControlPosition(aChild, element, this);
-      if (position >= 0)
-        low = mid + 1;
-      else
-        high = mid - 1;
-    }
-      
-    // WEAK - don't addref
-    controlList.InsertElementAt(low, aChild);
-  }
+
+  bool lastElement = AddElementToList(controlList, aChild, this);
 
 #ifdef DEBUG
   AssertDocumentOrder(controlList, this);
 #endif
 
   int32_t type = aChild->GetType();
 
   //
@@ -1354,41 +1427,113 @@ nsHTMLFormElement::HandleDefaultSubmitRe
                    "What happened here?");
 
   // Notify about change if needed.
   if (mDefaultSubmitElement) {
     mDefaultSubmitElement->UpdateState(true);
   }
 }
 
+static nsresult
+RemoveElementFromTableInternal(
+  nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
+  nsIContent* aChild, const nsAString& aName)
+{
+  nsCOMPtr<nsISupports> supports;
+
+  if (!aTable.Get(aName, getter_AddRefs(supports)))
+    return NS_OK;
+
+  // Single element in the hash, just remove it if it's the one
+  // we're trying to remove...
+  if (supports == aChild) {
+    aTable.Remove(aName);
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIContent> content(do_QueryInterface(supports));
+  if (content) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
+  NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
+
+  // Upcast, uggly, but it works!
+  nsBaseContentList *list = static_cast<nsBaseContentList*>(nodeList.get());
+
+  list->RemoveElement(aChild);
+
+  uint32_t length = 0;
+  list->GetLength(&length);
+
+  if (!length) {
+    // If the list is empty we remove if from our hash, this shouldn't
+    // happen tho
+    aTable.Remove(aName);
+  } else if (length == 1) {
+    // Only one element left, replace the list in the hash with the
+    // single element.
+    nsIContent* node = list->Item(0);
+    if (node) {
+      aTable.Put(aName, node);
+    }
+  }
+
+  return NS_OK;
+}
+
+static PLDHashOperator
+RemovePastNames(const nsAString& aName,
+                nsCOMPtr<nsISupports>& aData,
+                void* aClosure)
+{
+  return aClosure == aData ? PL_DHASH_REMOVE : PL_DHASH_NEXT;
+}
+
 nsresult
 nsHTMLFormElement::RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
-                                          const nsAString& aName)
+                                          const nsAString& aName,
+                                          RemoveElementReason aRemoveReason)
 {
+  // If the element is being removed from the form, we have to remove it from
+  // the past names map.
+  if (aRemoveReason == ElementRemoved) {
+    mPastNameLookupTable.Enumerate(RemovePastNames, aElement);
+  }
+
   return mControls->RemoveElementFromTable(aElement, aName);
 }
 
 already_AddRefed<nsISupports>
 nsHTMLFormElement::FindNamedItem(const nsAString& aName,
                                  nsWrapperCache** aCache)
 {
   nsCOMPtr<nsISupports> result = DoResolveName(aName, true);
   if (result) {
     // FIXME Get the wrapper cache from DoResolveName.
     *aCache = nullptr;
+    AddToPastNamesMap(aName, result);
     return result.forget();
   }
 
-  nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(GetCurrentDoc());
-  if (!htmlDoc) {
+  result = mImageNameLookupTable.GetWeak(aName);
+  if (result) {
     *aCache = nullptr;
-    return nullptr;
+    AddToPastNamesMap(aName, result);
+    return result.forget();
   }
 
-  return htmlDoc->ResolveName(aName, this, aCache);
+  result = mPastNameLookupTable.GetWeak(aName);
+  if (result) {
+    *aCache = nullptr;
+    return result.forget();
+  }
+
+  return nullptr;
 }
 
 already_AddRefed<nsISupports>
 nsHTMLFormElement::DoResolveName(const nsAString& aName,
                                  bool aFlushContent)
 {
   nsCOMPtr<nsISupports> result =
     mControls->NamedItemInternal(aName, aFlushContent);
@@ -1905,17 +2050,17 @@ nsHTMLFormElement::OnStatusChange(nsIWeb
 NS_IMETHODIMP
 nsHTMLFormElement::OnSecurityChange(nsIWebProgress* aWebProgress,
                                     nsIRequest* aRequest,
                                     uint32_t state)
 {
   NS_NOTREACHED("notification excluded in AddProgressListener(...)");
   return NS_OK;
 }
- 
+
 NS_IMETHODIMP_(int32_t)
 nsHTMLFormElement::IndexOfControl(nsIFormControl* aControl)
 {
   int32_t index = 0;
   return mControls->IndexOfControl(aControl, &index) == NS_OK ? index : 0;
 }
 
 void
@@ -2136,16 +2281,27 @@ nsHTMLFormElement::IntrinsicState() cons
     state |= NS_EVENT_STATE_INVALID;
   } else {
       state |= NS_EVENT_STATE_VALID;
   }
 
   return state;
 }
 
+void
+nsHTMLFormElement::Clear()
+{
+  for (int32_t i = mImageElements.Length() - 1; i >= 0; i--) {
+    mImageElements[i]->ClearForm(false);
+  }
+  mImageElements.Clear();
+  mImageNameLookupTable.Clear();
+  mPastNameLookupTable.Clear();
+}
+
 //----------------------------------------------------------------------
 // nsFormControlList implementation, this could go away if there were
 // a lightweight collection implementation somewhere
 
 nsFormControlList::nsFormControlList(nsHTMLFormElement* aForm) :
   mForm(aForm),
   // Initialize the elements list to have an initial capacity
   // of 8 to reduce allocations on small forms.
@@ -2224,17 +2380,17 @@ NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFormControlList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFormControlList)
 
 
 // nsIDOMHTMLCollection interface
 
-NS_IMETHODIMP    
+NS_IMETHODIMP
 nsFormControlList::GetLength(uint32_t* aLength)
 {
   FlushPendingNotifications();
   *aLength = mElements.Length();
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2293,63 +2449,60 @@ nsFormControlList::NamedItemInternal(con
 {
   if (aFlushContent) {
     FlushPendingNotifications();
   }
 
   return mNameLookupTable.GetWeak(aName);
 }
 
-nsresult
-nsFormControlList::AddElementToTable(nsGenericHTMLFormElement* aChild,
-                                     const nsAString& aName)
+static nsresult
+AddElementToTableInternal(
+  nsInterfaceHashtable<nsStringHashKey,nsISupports>& aTable,
+  nsIContent* aChild, const nsAString& aName, nsHTMLFormElement* aForm)
 {
-  if (!ShouldBeInElements(aChild)) {
-    return NS_OK;
-  }
-
   nsCOMPtr<nsISupports> supports;
-  mNameLookupTable.Get(aName, getter_AddRefs(supports));
+  aTable.Get(aName, getter_AddRefs(supports));
 
   if (!supports) {
-    // No entry found, add the form control
-    mNameLookupTable.Put(aName, NS_ISUPPORTS_CAST(nsIContent*, aChild));
+    // No entry found, add the element
+    aTable.Put(aName, aChild);
   } else {
     // Found something in the hash, check its type
     nsCOMPtr<nsIContent> content = do_QueryInterface(supports);
 
     if (content) {
       // Check if the new content is the same as the one we found in the
       // hash, if it is then we leave it in the hash as it is, this will
       // happen if a form control has both a name and an id with the same
       // value
       if (content == aChild) {
         return NS_OK;
       }
 
       // Found an element, create a list, add the element to the list and put
       // the list in the hash
-      nsSimpleContentList *list = new nsSimpleContentList(mForm);
+      nsSimpleContentList *list = new nsSimpleContentList(aForm);
 
       // If an element has a @form, we can assume it *might* be able to not have
       // a parent and still be in the form.
       NS_ASSERTION(content->HasAttr(kNameSpaceID_None, nsGkAtoms::form) ||
                    content->GetParent(), "Item in list without parent");
 
       // Determine the ordering between the new and old element.
       bool newFirst = nsContentUtils::PositionIsBefore(aChild, content);
 
-      list->AppendElement(newFirst ? aChild : content);
-      list->AppendElement(newFirst ? content : aChild);
+      list->AppendElement(newFirst ? aChild : content.get());
+      list->AppendElement(newFirst ? content.get() : aChild);
 
 
       nsCOMPtr<nsISupports> listSupports = do_QueryObject(list);
 
       // Replace the element with the list.
-      mNameLookupTable.Put(aName, listSupports);
+      aTable.Put(aName, listSupports);
     } else {
       // There's already a list in the hash, add the child to the list
       nsCOMPtr<nsIDOMNodeList> nodeList = do_QueryInterface(supports);
       NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
 
       // Upcast, uggly, but it works!
       nsSimpleContentList *list =
         static_cast<nsSimpleContentList*>(nodeList.get());
@@ -2391,16 +2544,27 @@ nsFormControlList::AddElementToTable(nsG
       list->InsertElementAt(aChild, first);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
+nsFormControlList::AddElementToTable(nsGenericHTMLFormElement* aChild,
+                                     const nsAString& aName)
+{
+  if (!ShouldBeInElements(aChild)) {
+    return NS_OK;
+  }
+
+  return AddElementToTableInternal(mNameLookupTable, aChild, aName, mForm);
+}
+
+nsresult
 nsFormControlList::IndexOfControl(nsIFormControl* aControl,
                                   int32_t* aIndex)
 {
   // Note -- not a DOM method; callers should handle flushing themselves
   
   NS_ENSURE_ARG_POINTER(aIndex);
 
   *aIndex = mElements.IndexOf(aControl);
@@ -2411,58 +2575,17 @@ nsFormControlList::IndexOfControl(nsIFor
 nsresult
 nsFormControlList::RemoveElementFromTable(nsGenericHTMLFormElement* aChild,
                                           const nsAString& aName)
 {
   if (!ShouldBeInElements(aChild)) {
     return NS_OK;
   }
 
-  nsCOMPtr<nsISupports> supports;
-
-  if (!mNameLookupTable.Get(aName, getter_AddRefs(supports)))
-    return NS_OK;
-
-  nsCOMPtr<nsIFormControl> fctrl(do_QueryInterface(supports));
-
-  if (fctrl) {
-    // Single element in the hash, just remove it if it's the one
-    // we're trying to remove...
-    if (fctrl == aChild) {
-      mNameLookupTable.Remove(aName);
-    }
-
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIDOMNodeList> nodeList(do_QueryInterface(supports));
-  NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
-
-  // Upcast, uggly, but it works!
-  nsBaseContentList *list = static_cast<nsBaseContentList*>(nodeList.get());
-
-  list->RemoveElement(aChild);
-
-  uint32_t length = 0;
-  list->GetLength(&length);
-
-  if (!length) {
-    // If the list is empty we remove if from our hash, this shouldn't
-    // happen tho
-    mNameLookupTable.Remove(aName);
-  } else if (length == 1) {
-    // Only one element left, replace the list in the hash with the
-    // single element.
-    nsIContent* node = list->Item(0);
-    if (node) {
-      mNameLookupTable.Put(aName, node);
-    }
-  }
-
-  return NS_OK;
+  return RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
 }
 
 nsresult
 nsFormControlList::GetSortedControls(nsTArray<nsGenericHTMLFormElement*>& aControls) const
 {
 #ifdef DEBUG
   AssertDocumentOrder(mElements, mForm);
   AssertDocumentOrder(mNotInElements, mForm);
@@ -2576,8 +2699,60 @@ void
 nsFormControlList::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   FlushPendingNotifications();
   // Just enumerate mNameLookupTable.  This won't guarantee order, but
   // that's OK, because the HTML5 spec doesn't define an order for
   // this enumeration.
   mNameLookupTable.EnumerateRead(CollectNames, &aNames);
 }
+
+nsresult
+nsHTMLFormElement::AddImageElement(HTMLImageElement* aChild)
+{
+  AddElementToList(mImageElements, aChild, this);
+  return NS_OK;
+}
+
+nsresult
+nsHTMLFormElement::AddImageElementToTable(HTMLImageElement* aChild,
+                                          const nsAString& aName)
+{
+  return AddElementToTableInternal(mImageNameLookupTable, aChild, aName, this);
+}
+
+nsresult
+nsHTMLFormElement::RemoveImageElement(HTMLImageElement* aChild)
+{
+  uint32_t index = mImageElements.IndexOf(aChild);
+  NS_ENSURE_STATE(index != mImageElements.NoIndex);
+
+  mImageElements.RemoveElementAt(index);
+  return NS_OK;
+}
+
+nsresult
+nsHTMLFormElement::RemoveImageElementFromTable(HTMLImageElement* aElement,
+                                               const nsAString& aName,
+                                               RemoveElementReason aRemoveReason)
+{
+  // If the element is being removed from the form, we have to remove it from
+  // the past names map.
+  if (aRemoveReason == ElementRemoved) {
+    mPastNameLookupTable.Enumerate(RemovePastNames, aElement);
+  }
+
+  return RemoveElementFromTableInternal(mImageNameLookupTable, aElement, aName);
+}
+
+void
+nsHTMLFormElement::AddToPastNamesMap(const nsAString& aName,
+                                     nsISupports* aChild)
+{
+  // If candidates contains exactly one node. Add a mapping from name to the
+  // node in candidates in the form element's past names map, replacing the
+  // previous entry with the same name, if any.
+  nsCOMPtr<nsIContent> node = do_QueryInterface(aChild);
+  if (node) {
+    mPastNameLookupTable.Put(aName, aChild);
+  }
+}
+
--- a/content/html/content/src/nsHTMLFormElement.h
+++ b/content/html/content/src/nsHTMLFormElement.h
@@ -27,16 +27,22 @@
 #undef GetClassInfo
 #endif
 #endif
 
 class nsFormControlList;
 class nsIMutableArray;
 class nsIURI;
 
+namespace mozilla {
+namespace dom {
+class HTMLImageElement;
+}
+}
+
 class nsHTMLFormElement : public nsGenericHTMLElement,
                           public nsIDOMHTMLFormElement,
                           public nsIWebProgressListener,
                           public nsIForm,
                           public nsIRadioGroupContainer
 {
 public:
   nsHTMLFormElement(already_AddRefed<nsINodeInfo> aNodeInfo);
@@ -116,18 +122,18 @@ public:
   /**
    * Forget all information about the current submission (and the fact that we
    * are currently submitting at all).
    */
   void ForgetCurrentSubmission();
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED_NO_UNLINK(nsHTMLFormElement,
-                                                     nsGenericHTMLElement)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHTMLFormElement,
+                                           nsGenericHTMLElement)
 
   /**
    * Remove an element from this form's list of elements
    *
    * @param aElement the element to remove
    * @param aUpdateValidity If true, updates the form validity.
    * @return NS_OK if the element was successfully removed.
    */
@@ -138,41 +144,92 @@ public:
    * Remove an element from the lookup table maintained by the form.
    * We can't fold this method into RemoveElement() because when
    * RemoveElement() is called it doesn't know if the element is
    * removed because the id attribute has changed, or bacause the
    * name attribute has changed.
    *
    * @param aElement the element to remove
    * @param aName the name or id of the element to remove
+   * @param aRemoveReason describe why this element is removed. If the element
+   *        is removed because it's removed from the form, it will be removed
+   *        from the past names map too, otherwise it will stay in the past
+   *        names map.
    * @return NS_OK if the element was successfully removed.
    */
+  enum RemoveElementReason {
+    AttributeUpdated,
+    ElementRemoved
+  };
   nsresult RemoveElementFromTable(nsGenericHTMLFormElement* aElement,
-                                  const nsAString& aName);
+                                  const nsAString& aName,
+                                  RemoveElementReason aRemoveReason);
+
   /**
    * Add an element to end of this form's list of elements
    *
    * @param aElement the element to add
    * @param aUpdateValidity If true, the form validity will be updated.
    * @param aNotify If true, send nsIDocumentObserver notifications as needed.
    * @return NS_OK if the element was successfully added
    */
   nsresult AddElement(nsGenericHTMLFormElement* aElement, bool aUpdateValidity,
                       bool aNotify);
 
-  /**    
+  /**
    * Add an element to the lookup table maintained by the form.
    *
    * We can't fold this method into AddElement() because when
    * AddElement() is called, the form control has no
    * attributes.  The name or id attributes of the form control
    * are used as a key into the table.
    */
   nsresult AddElementToTable(nsGenericHTMLFormElement* aChild,
                              const nsAString& aName);
+
+  /**
+   * Remove an image element from this form's list of image elements
+   *
+   * @param aElement the image element to remove
+   * @return NS_OK if the element was successfully removed.
+   */
+  nsresult RemoveImageElement(mozilla::dom::HTMLImageElement* aElement);
+
+  /**
+   * Remove an image element from the lookup table maintained by the form.
+   * We can't fold this method into RemoveImageElement() because when
+   * RemoveImageElement() is called it doesn't know if the element is
+   * removed because the id attribute has changed, or because the
+   * name attribute has changed.
+   *
+   * @param aElement the image element to remove
+   * @param aName the name or id of the element to remove
+   * @return NS_OK if the element was successfully removed.
+   */
+  nsresult RemoveImageElementFromTable(mozilla::dom::HTMLImageElement* aElement,
+                                      const nsAString& aName,
+                                      RemoveElementReason aRemoveReason);
+  /**
+   * Add an image element to the end of this form's list of image elements
+   *
+   * @param aElement the element to add
+   * @return NS_OK if the element was successfully added
+   */
+  nsresult AddImageElement(mozilla::dom::HTMLImageElement* aElement);
+
+  /**
+   * Add an image element to the lookup table maintained by the form.
+   *
+   * We can't fold this method into AddImageElement() because when
+   * AddImageElement() is called, the image attributes can change.
+   * The name or id attributes of the image are used as a key into the table.
+   */
+  nsresult AddImageElementToTable(mozilla::dom::HTMLImageElement* aChild,
+                                 const nsAString& aName);
+
    /**
     * Return whether there is one and only one input text control.
     *
     * @return Whether there is exactly one input text control.
     */
   bool HasSingleTextControl() const;
 
   /**
@@ -358,16 +415,22 @@ protected:
    *
    * @param aInvalidElements [out] parameter containing the list of unhandled
    * invalid controls.
    *
    * @return Whether the form is currently valid.
    */
   bool CheckFormValidity(nsIMutableArray* aInvalidElements) const;
 
+  // Clear the mImageNameLookupTable and mImageElements.
+  void Clear();
+
+  // Insert a element into the past names map.
+  void AddToPastNamesMap(const nsAString& aName, nsISupports* aChild);
+
 public:
   /**
    * Flush a possible pending submission. If there was a scripted submission
    * triggered by a button or image, the submission was defered. This method
    * forces the pending submission to be submitted. (happens when the handler
    * returns false or there is an action/target change in the script)
    */
   void FlushPendingSubmission();
@@ -412,16 +475,35 @@ protected:
   nsGenericHTMLFormElement* mDefaultSubmitElement;
 
   /** The first submit element in mElements -- WEAK */
   nsGenericHTMLFormElement* mFirstSubmitInElements;
 
   /** The first submit element in mNotInElements -- WEAK */
   nsGenericHTMLFormElement* mFirstSubmitNotInElements;
 
+  // This array holds on to all HTMLImageElement(s).
+  // This is needed to properly clean up the bi-directional references
+  // (both weak and strong) between the form and its HTMLImageElements.
+
+  nsTArray<mozilla::dom::HTMLImageElement*> mImageElements;  // Holds WEAK references
+
+  // A map from an ID or NAME attribute to the HTMLImageElement(s), this
+  // hash holds strong references either to the named HTMLImageElement, or
+  // to a list of named HTMLImageElement(s), in the case where this hash
+  // holds on to a list of named HTMLImageElement(s) the list has weak
+  // references to the HTMLImageElement.
+
+  nsInterfaceHashtable<nsStringHashKey,nsISupports> mImageNameLookupTable;
+
+  // A map from names to elements that were gotten by those names from this
+  // form in that past.  See "past names map" in the HTML5 specification.
+
+  nsInterfaceHashtable<nsStringHashKey,nsISupports> mPastNameLookupTable;
+
   /**
    * Number of invalid and candidate for constraint validation elements in the
    * form the last time UpdateValidity has been called.
    * @note Should only be used by UpdateValidity() and GetValidity()!
    */
   int32_t mInvalidElementsCount;
 
   /**
--- a/content/html/content/test/Makefile.in
+++ b/content/html/content/test/Makefile.in
@@ -373,16 +373,18 @@ MOCHITEST_FILES = \
 		test_bug839371.html \
 		test_element_prototype.html \
 		test_formData.html \
 		test_audio_wakelock.html \
 		test_video_wakelock.html \
 		wakelock.ogg \
 		wakelock.ogv \
 		test_bug869040.html \
+		test_bug870787.html \
+		test_bug879319.html \
 		allowMedia.sjs \
 		test_bug874758.html \
 		$(NULL)
 
 MOCHITEST_CHROME_FILES = \
 		test_allowMedia.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug870787.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=870787
+-->
+<head>
+  <title>Test for Bug 870787</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="reflect.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=870787">Mozilla Bug 870787</a>
+
+<p id="msg"></p>
+
+<form id="form0"></form>
+<img name="img0" id="img0id">
+
+<img name="img1" id="img1id" />
+<form id="form1">
+  <img name="img2" id="img2id" />
+</form>
+<img name="img3" id="img3id" />
+
+<table>
+  <form id="form2">
+  <tr><td>
+    <button name="input1" id="input1id" />
+    <input name="input2" id="input2id" />
+  </form>
+</table>
+
+<table>
+  <form id="form3">
+  <tr><td>
+    <img name="img4" id="img4id" />
+    <img name="img5" id="img5id" />
+  </form>
+</table>
+
+<form id="form4"><img id="img6"></form>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 870787 **/
+
+var form0 = document.getElementById("form0");
+ok(form0, "Form0 exists");
+ok(!form0.img0, "Form0.img0 doesn't exist");
+ok(!form0.img0id, "Form0.img0id doesn't exist");
+
+var form1 = document.getElementById("form1");
+ok(form1, "Form1 exists");
+ok(!form1.img1, "Form1.img1 doesn't exist");
+ok(!form1.img1id, "Form1.img1id doesn't exist");
+is(form1.img2, document.getElementById("img2id"), "Form1.img2 exists");
+is(form1.img2id, document.getElementById("img2id"), "Form1.img2id exists");
+ok(!form1.img3, "Form1.img3 doesn't exist");
+ok(!form1.img3id, "Form1.img3id doesn't exist");
+
+var form2 = document.getElementById("form2");
+ok(form2, "Form2 exists");
+is(form2.input1, document.getElementById("input1id"), "Form2.input1 exists");
+is(form2.input1id, document.getElementById("input1id"), "Form2.input1id exists");
+is(form2.input2, document.getElementById("input2id"), "Form2.input2 exists");
+is(form2.input2id, document.getElementById("input2id"), "Form2.input2id exists");
+
+var form3 = document.getElementById("form3");
+ok(form3, "Form3 exists");
+is(form3.img4, document.getElementById("img4id"), "Form3.img4 doesn't exists");
+is(form3.img4id, document.getElementById("img4id"), "Form3.img4id doesn't exists");
+is(form3.img5, document.getElementById("img5id"), "Form3.img5 doesn't exists");
+is(form3.img5id, document.getElementById("img5id"), "Form3.img5id doesn't exists");
+
+var form4 = document.getElementById("form4");
+ok(form4, "Form4 exists");
+is(Object.getOwnPropertyNames(form4.elements).indexOf("img6"), -1, "Form4.elements should not contain img6");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/html/content/test/test_bug879319.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=879319
+-->
+<head>
+  <title>Test for Bug 879319</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="reflect.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=879319">Mozilla Bug 879319</a>
+
+<p id="msg"></p>
+
+<form id="form">
+  <img id="img0" name="bar0" />
+</form>
+<input id="input0" name="foo0" form="form" />
+<input id="input1" name="foo1" form="form" />
+<input id="input2" name="foo2" form="form" />
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 879319 **/
+
+var input0 = document.getElementById("input0");
+ok(input0, "input0 exists");
+
+var form = document.getElementById("form");
+ok(form, "form exists");
+is(form.foo0, input0, "Form.foo0 should exist");
+
+ok("foo0" in form.elements, "foo0 in form.elements");
+is(input0.form, form, "input0.form is form");
+
+input0.setAttribute("name", "tmp0");
+ok("tmp0" in form.elements, "tmp0 is in form.elements");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+is(form.tmp0, input0, "Form.tmp0 == input0");
+is(form.foo0, input0, "Form.foo0 is still here");
+
+input0.setAttribute("name", "tmp1");
+ok("tmp1" in form.elements, "tmp1 is in form.elements");
+ok(!("tmp0" in form.elements), "tmp0 is not in form.elements");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+is(form.tmp0, input0, "Form.tmp0 == input0");
+is(form.tmp1, input0, "Form.tmp1 == input0");
+is(form.foo0, input0, "Form.foo0 is still here");
+
+input0.setAttribute("form", "");
+ok(!("foo0" in form.elements), "foo0 is not in form.elements");
+todo_is(form.foo0, undefined, "Form.foo0 should not still be here");
+todo_is(form.tmp0, undefined, "Form.tmp0 should not still be here");
+todo_is(form.tmp1, undefined, "Form.tmp1 should not still be here");
+
+var input1 = document.getElementById("input1");
+ok(input1, "input1 exists");
+is(form.foo1, input1, "Form.foo1 should exist");
+
+ok("foo1" in form.elements, "foo1 in form.elements");
+is(input1.form, form, "input1.form is form");
+
+input1.setAttribute("name", "foo0");
+ok("foo0" in form.elements, "foo0 is in form.elements");
+is(form.foo0, input1, "Form.foo0 should be input1");
+is(form.foo1, input1, "Form.foo1 should be input1");
+
+var input2 = document.getElementById("input2");
+ok(input2, "input2 exists");
+is(form.foo2, input2, "Form.foo2 should exist");
+input2.parentNode.removeChild(input2);
+ok(!("foo2" in form.elements), "foo2 is not in form.elements");
+todo_is(form.foo2, undefined, "Form.foo2 should not longer be there");
+
+var img0 = document.getElementById("img0");
+ok(img0, "img0 exists");
+is(form.bar0, img0, "Form.bar0 should exist");
+
+img0.setAttribute("name", "old_bar0");
+is(form.bar0, img0, "Form.bar0 is still here");
+
+img0.parentNode.removeChild(img0);
+todo_is(form.bar0, undefined, "Form.bar0 should not be here");
+
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2290,49 +2290,16 @@ nsHTMLDocument::ResolveName(const nsAStr
     *aCache = e;
     return e;
   }
 
   *aCache = nullptr;
   return nullptr;
 }
 
-already_AddRefed<nsISupports>
-nsHTMLDocument::ResolveName(const nsAString& aName,
-                            nsIContent *aForm,
-                            nsWrapperCache **aCache)
-{
-  nsISupports* result = ResolveName(aName, aCache);
-  if (!result) {
-    return nullptr;
-  }
-
-  nsCOMPtr<nsIContent> node = do_QueryInterface(result);
-  if (!node) {
-    // We create a nsFormContentList which will filter out the elements in the
-    // list that don't belong to aForm.
-    nsRefPtr<nsBaseContentList> list =
-      new nsFormContentList(aForm, *static_cast<nsBaseContentList*>(result));
-    if (list->Length() > 1) {
-      *aCache = list;
-      return list.forget();
-    }
-
-    // After the nsFormContentList is done filtering there's either nothing or
-    // one element in the list. Return that element, or null if there's no
-    // element in the list.
-    node = list->Item(0);
-  } else if (!nsContentUtils::BelongsInForm(aForm, node)) {
-    node = nullptr;
-  }
-
-  *aCache = node;
-  return node.forget();
-}
-
 JSObject*
 nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
                             ErrorResult& rv)
 {
   nsWrapperCache* cache;
   nsISupports* supp = ResolveName(aName, &cache);
   if (!supp) {
     aFound = false;
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -110,19 +110,16 @@ public:
    * id.
    */
   nsISupports *GetDocumentAllResult(const nsAString& aID,
                                     nsWrapperCache **aCache,
                                     nsresult *aResult);
   JSObject* GetAll(JSContext* aCx, mozilla::ErrorResult& aRv);
 
   nsISupports* ResolveName(const nsAString& aName, nsWrapperCache **aCache);
-  virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName,
-                                                    nsIContent *aForm,
-                                                    nsWrapperCache **aCache) MOZ_OVERRIDE;
 
   virtual void AddedForm() MOZ_OVERRIDE;
   virtual void RemovedForm() MOZ_OVERRIDE;
   virtual int32_t GetNumFormsSynchronous() MOZ_OVERRIDE;
   virtual void TearingDownEditor(nsIEditor *aEditor) MOZ_OVERRIDE;
   virtual void SetIsXHTML(bool aXHTML) MOZ_OVERRIDE { mIsRegularHTML = !aXHTML; }
   virtual void SetDocWriteDisabled(bool aDisabled) MOZ_OVERRIDE
   {
@@ -292,17 +289,17 @@ protected:
   nsRefPtr<nsContentList> mApplets;
   nsRefPtr<nsContentList> mEmbeds;
   nsRefPtr<nsContentList> mLinks;
   nsRefPtr<nsContentList> mAnchors;
   nsRefPtr<nsContentList> mScripts;
   nsRefPtr<nsContentList> mForms;
   nsRefPtr<nsContentList> mFormControls;
 
-  JSObject* mAll;
+  JS::Heap<JSObject*> mAll;
 
   /** # of forms in the document, synchronously set */
   int32_t mNumForms;
 
   static uint32_t gWyciwygSessionCnt;
 
   static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
                              int32_t& aCharsetSource,
--- a/content/html/document/src/nsIHTMLDocument.h
+++ b/content/html/document/src/nsIHTMLDocument.h
@@ -28,20 +28,16 @@ class nsIHTMLDocument : public nsISuppor
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IHTMLDOCUMENT_IID)
 
   /**
    * Set compatibility mode for this document
    */
   virtual void SetCompatibilityMode(nsCompatibility aMode) = 0;
 
-  virtual already_AddRefed<nsISupports> ResolveName(const nsAString& aName,
-                                                    nsIContent *aForm,
-                                                    nsWrapperCache **aCache) = 0;
-
   /**
    * Called when form->BindToTree() is called so that document knows
    * immediately when a form is added
    */
   virtual void AddedForm() = 0;
   /**
    * Called when form->SetDocument() is called so that document knows
    * immediately when a form is removed
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1041,42 +1041,16 @@ void MediaDecoder::NotifyBytesConsumed(i
   NS_ENSURE_TRUE_VOID(mDecoderStateMachine);
   MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
   if (!mIgnoreProgressData) {
     mDecoderPosition += aBytes;
     mPlaybackStatistics.AddBytes(aBytes);
   }
 }
 
-void MediaDecoder::NextFrameUnavailableBuffering()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING);
-}
-
-void MediaDecoder::NextFrameAvailable()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
-}
-
-void MediaDecoder::NextFrameUnavailable()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
-}
-
 void MediaDecoder::UpdateReadyStateForData()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mOwner || mShuttingDown || !mDecoderStateMachine)
     return;
   MediaDecoderOwner::NextFrameStatus frameStatus =
     mDecoderStateMachine->GetNextFrameStatus();
   mOwner->UpdateReadyStateForData(frameStatus);
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -705,22 +705,16 @@ public:
   // thread.
   void SeekingStarted();
 
   // Called when the backend has changed the current playback
   // position. It dispatches a timeupdate event and invalidates the frame.
   // This must be called on the main thread only.
   void PlaybackPositionChanged();
 
-  // Calls mElement->UpdateReadyStateForData, telling it which state we have
-  // entered.  Main thread only.
-  void NextFrameUnavailableBuffering();
-  void NextFrameAvailable();
-  void NextFrameUnavailable();
-
   // Calls mElement->UpdateReadyStateForData, telling it whether we have
   // data for the next frame and if we're buffering. Main thread only.
   void UpdateReadyStateForData();
 
   // Find the end of the cached data starting at the current decoder
   // position.
   int64_t GetDownloadPosition();
 
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2615,31 +2615,26 @@ void MediaDecoderStateMachine::UpdateRea
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
   if (nextFrameStatus == mLastFrameStatus) {
     return;
   }
   mLastFrameStatus = nextFrameStatus;
 
+  /* This is a bit tricky. MediaDecoder::UpdateReadyStateForData will run on
+   * the main thread and re-evaluate GetNextFrameStatus there, passing it to
+   * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of
+   * GetNextFrameStatus we computed here, because what we're computing here
+   * could be stale by the time MediaDecoder::UpdateReadyStateForData runs.
+   * We only compute GetNextFrameStatus here to avoid posting runnables to the main
+   * thread unnecessarily.
+   */
   nsCOMPtr<nsIRunnable> event;
-  switch (nextFrameStatus) {
-    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering);
-      break;
-    case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable);
-      break;
-    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable);
-      break;
-    default:
-      PR_NOT_REACHED("unhandled frame state");
-  }
-
+  event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 bool MediaDecoderStateMachine::JustExitedQuickBuffering()
 {
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -74,17 +74,17 @@ AudioBuffer::InitializeBuffers(uint32_t 
   if (!mJSChannels.SetCapacity(aNumberOfChannels)) {
     return false;
   }
   for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
     JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength));
     if (!array) {
       return false;
     }
-    mJSChannels.AppendElement(array);
+    mJSChannels.AppendElement(array.get());
   }
 
   return true;
 }
 
 JSObject*
 AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
--- a/content/media/webaudio/AudioBuffer.h
+++ b/content/media/webaudio/AudioBuffer.h
@@ -111,17 +111,17 @@ public:
   void MixToMono(JSContext* aJSContext);
 
 protected:
   bool RestoreJSChannelData(JSContext* aJSContext);
   void ClearJSChannels();
 
   nsRefPtr<AudioContext> mContext;
   // Float32Arrays
-  AutoFallibleTArray<JSObject*,2> mJSChannels;
+  AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels;
 
   // mSharedChannels aggregates the data from mJSChannels. This is non-null
   // if and only if the mJSChannels are neutered.
   nsRefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels;
 
   uint32_t mLength;
   float mSampleRate;
 };
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -32,15 +32,15 @@ public:
     return mCurve;
   }
   void SetCurve(const Float32Array* aData);
 
 private:
   void ClearCurve();
 
 private:
-  JSObject* mCurve;
+  JS::Heap<JSObject*> mCurve;
 };
 
 }
 }
 
 #endif
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLMaybeCompiled.h
@@ -0,0 +1,145 @@
+/* -*- 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/. */
+
+#ifndef nsXBLMaybeCompiled_h__
+#define nsXBLMaybeCompiled_h__
+
+#include "js/RootingAPI.h"
+
+/*
+ * A union containing either a pointer representing uncompiled source or a
+ * JSObject* representing the compiled result.  The class is templated on the
+ * source object type.
+ *
+ * The purpose of abstracting this as a separate class is to allow it to be
+ * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
+ * pointer, when present.
+ */
+template <class UncompiledT>
+class nsXBLMaybeCompiled
+{
+public:
+  nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {}
+
+  nsXBLMaybeCompiled(UncompiledT* uncompiled)
+    : mUncompiled(reinterpret_cast<uintptr_t>(uncompiled) | BIT_UNCOMPILED) {}
+
+  nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {}
+
+  bool IsCompiled() const
+  {
+    return !(mUncompiled & BIT_UNCOMPILED);
+  }
+
+  UncompiledT* GetUncompiled() const
+  {
+    MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled");
+    uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED;
+    return reinterpret_cast<UncompiledT*>(unmasked);
+  }
+
+  JSObject* GetJSFunction() const
+  {
+    MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled");
+    return mCompiled;
+  }
+
+private:
+  JSObject*& UnsafeGetJSFunction()
+  {
+    MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled");
+    return mCompiled;
+  }
+
+  enum { BIT_UNCOMPILED = 1 << 0 };
+
+  union
+  {
+    // An pointer that represents the function before being compiled, with
+    // BIT_UNCOMPILED set.
+    uintptr_t mUncompiled;
+
+    // The JS object for the compiled result.
+    JSObject* mCompiled;
+  };
+
+  friend class js::RootMethods<nsXBLMaybeCompiled<UncompiledT> >;
+};
+
+/* Add support for JS::Heap<nsXBLMaybeCompiled>. */
+namespace js {
+
+template <class UncompiledT>
+struct RootMethods<nsXBLMaybeCompiled<UncompiledT> > : public RootMethods<JSObject *>
+{
+  typedef struct RootMethods<JSObject *> Base;
+
+  static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
+
+  static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
+  {
+    return function.IsCompiled() && Base::poisoned(function.GetJSFunction());
+  }
+
+  static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function)
+  {
+    return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction());
+  }
+
+#ifdef JSGC_GENERATIONAL
+  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  {
+    Base::postBarrier(&functionp->UnsafeGetJSFunction());
+  }
+
+  static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  {
+    Base::relocate(&functionp->UnsafeGetJSFunction());
+  }
+#endif
+};
+
+template <class UncompiledT>
+class HeapBase<nsXBLMaybeCompiled<UncompiledT> >
+{
+  const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const {
+    return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
+  }
+
+  JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() {
+    return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
+  }
+
+  const nsXBLMaybeCompiled<UncompiledT>* extract() const {
+    return wrapper().address();
+  }
+
+  nsXBLMaybeCompiled<UncompiledT>* extract() {
+    return wrapper().unsafeGet();
+  }
+
+public:
+  bool IsCompiled() const { return extract()->IsCompiled(); }
+  UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); }
+  JSObject* GetJSFunction() const { return extract()->GetJSFunction(); }
+
+  void SetUncompiled(UncompiledT* source) {
+    wrapper().set(nsXBLMaybeCompiled<UncompiledT>(source));
+  }
+
+  void SetJSFunction(JSObject* function) {
+    wrapper().set(nsXBLMaybeCompiled<UncompiledT>(function));
+  }
+
+  JS::Heap<JSObject*>& AsHeapObject()
+  {
+    MOZ_ASSERT(extract()->IsCompiled());
+    return *reinterpret_cast<JS::Heap<JSObject*>*>(this);
+  }
+};
+
+} /* namespace js */
+
+#endif // nsXBLMaybeCompiled_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -19,18 +19,18 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "xpcpublic.h"
 #include "nsXBLPrototypeBinding.h"
 
 using namespace mozilla;
 
 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
-  nsXBLProtoImplMember(aName), 
-  mUncompiledMethod(BIT_UNCOMPILED)
+  nsXBLProtoImplMember(aName),
+  mMethod()
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
 }
 
 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
 
@@ -103,22 +103,23 @@ nsXBLProtoImplMethod::InstallMember(JSCo
                   "Should not be installing an uncompiled method");
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
 
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
   JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   // now we want to reevaluate our property using aContext and the script object for this window...
-  if (mJSMethodObject) {
+  JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
+  if (jsMethodObject) {
     nsDependentString name(mName);
 
     // First, make the function in the compartment of the scope object.
     JSAutoCompartment ac(aCx, scopeObject);
-    JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, mJSMethodObject, scopeObject));
+    JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject));
     if (!method) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Then, enter the content compartment, wrap the method pointer, and define
     // the wrapped version on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
     if (!JS_WrapObject(aCx, method.address()))
@@ -144,27 +145,27 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   NS_PRECONDITION(aClassObject,
                   "Must have class object to compile");
 
   nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
 
   // No parameters or body was supplied, so don't install method.
   if (!uncompiledMethod) {
     // Early return after which we consider ourselves compiled.
-    mJSMethodObject = nullptr;
+    SetCompiledMethod(nullptr);
 
     return NS_OK;
   }
 
   // Don't install method if no name was supplied.
   if (!mName) {
     delete uncompiledMethod;
 
     // Early return after which we consider ourselves compiled.
-    mJSMethodObject = nullptr;
+    SetCompiledMethod(nullptr);
 
     return NS_OK;
   }
 
   // We have a method.
   // Allocate an array for our arguments.
   int32_t paramCount = uncompiledMethod->GetParameterCount();
   char** args = nullptr;
@@ -214,73 +215,69 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   // Destroy our uncompiled method and delete our arg list.
   delete uncompiledMethod;
   delete [] args;
   if (NS_FAILED(rv)) {
     SetUncompiledMethod(nullptr);
     return rv;
   }
 
-  mJSMethodObject = methodObject;
+  SetCompiledMethod(methodObject);
 
   return NS_OK;
 }
 
 void
 nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
 {
-  if (IsCompiled() && mJSMethodObject) {
-    aCallbacks.Trace(&mJSMethodObject, "mJSMethodObject", aClosure);
+  if (IsCompiled() && GetCompiledMethod()) {
+    aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure);
   }
 }
 
 nsresult
 nsXBLProtoImplMethod::Read(nsIScriptContext* aContext,
                            nsIObjectInputStream* aStream)
 {
   JS::Rooted<JSObject*> methodObject(aContext->GetNativeContext());
   nsresult rv = XBL_DeserializeFunction(aContext, aStream, &methodObject);
   if (NS_FAILED(rv)) {
     SetUncompiledMethod(nullptr);
     return rv;
   }
 
-  mJSMethodObject = methodObject;
-
-#ifdef DEBUG
-  mIsCompiled = true;
-#endif
+  SetCompiledMethod(methodObject);
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplMethod::Write(nsIScriptContext* aContext,
                             nsIObjectOutputStream* aStream)
 {
-  if (mJSMethodObject) {
+  MOZ_ASSERT(IsCompiled());
+  if (GetCompiledMethod()) {
     nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = aStream->WriteWStringZ(mName);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return XBL_SerializeFunction(aContext, aStream,
-                                 JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject));
+    return XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject());
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
   NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
   
-  if (!mJSMethodObject) {
+  if (!GetCompiledMethod()) {
     // Nothing to do here
     return NS_OK;
   }
 
   // Get the script context the same way
   // nsXBLProtoImpl::InstallImplementation does.
   nsIDocument* document = aBoundElement->OwnerDoc();
 
@@ -322,17 +319,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n
 
   JSAutoCompartment ac(cx, scopeObject);
   if (!JS_WrapObject(cx, thisObject.address()))
       return NS_ERROR_OUT_OF_MEMORY;
 
   // Clone the function object, using thisObject as the parent so "this" is in
   // the scope chain of the resulting function (for backwards compat to the
   // days when this was an event handler).
-  JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject));
+  JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject));
   if (!method)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Now call the method
 
   // Check whether it's OK to call the method.
   rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
                                                                  thisObject);
@@ -359,19 +356,19 @@ nsXBLProtoImplAnonymousMethod::Execute(n
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext,
                                      nsIObjectOutputStream* aStream,
                                      XBLBindingSerializeDetails aType)
 {
-  if (mJSMethodObject) {
+  MOZ_ASSERT(IsCompiled());
+  if (GetCompiledMethod()) {
     nsresult rv = aStream->Write8(aType);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = XBL_SerializeFunction(aContext, aStream,
-                               JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -6,16 +6,17 @@
 #ifndef nsXBLProtoImplMethod_h__
 #define nsXBLProtoImplMethod_h__
 
 #include "mozilla/Attributes.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsString.h"
+#include "nsXBLMaybeCompiled.h"
 #include "nsXBLProtoImplMember.h"
 #include "nsXBLSerialize.h"
 
 class nsIContent;
 
 struct nsXBLParameter {
   nsXBLParameter* mNext;
   char* mName;
@@ -97,39 +98,41 @@ public:
 
   virtual void Trace(const TraceCallbacks& aCallbacks, void *aClosure) MOZ_OVERRIDE;
 
   nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
   virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE;
 
   bool IsCompiled() const
   {
-    return !(mUncompiledMethod & BIT_UNCOMPILED);
+    return mMethod.IsCompiled();
   }
+
   void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod)
   {
-    mUncompiledMethod = uintptr_t(aUncompiledMethod) | BIT_UNCOMPILED;
+    mMethod.SetUncompiled(aUncompiledMethod);
   }
+
   nsXBLUncompiledMethod* GetUncompiledMethod() const
   {
-    uintptr_t unmasked = mUncompiledMethod & ~BIT_UNCOMPILED;
-    return reinterpret_cast<nsXBLUncompiledMethod*>(unmasked);
+    return mMethod.GetUncompiled();
   }
 
 protected:
-  enum { BIT_UNCOMPILED = 1 << 0 };
+  void SetCompiledMethod(JSObject* aCompiledMethod)
+  {
+    mMethod.SetJSFunction(aCompiledMethod);
+  }
 
-  union {
-    uintptr_t mUncompiledMethod; // An object that represents the method before being compiled.
-    JSObject* mJSMethodObject;    // The JS object for the method (after compilation)
-  };
+  JSObject* GetCompiledMethod() const
+  {
+    return mMethod.GetJSFunction();
+  }
 
-#ifdef DEBUG
-  bool mIsCompiled;
-#endif
+  JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod;
 };
 
 class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod {
 public:
   nsXBLProtoImplAnonymousMethod() :
     nsXBLProtoImplMethod(EmptyString().get())
   {}
   
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -22,18 +22,16 @@
 using namespace mozilla;
 
 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
                                                const PRUnichar* aGetter, 
                                                const PRUnichar* aSetter,
                                                const PRUnichar* aReadOnly,
                                                uint32_t aLineNumber) :
   nsXBLProtoImplMember(aName), 
-  mGetterText(nullptr),
-  mSetterText(nullptr),
   mJSAttributes(JSPROP_ENUMERATE)
 #ifdef DEBUG
   , mIsCompiled(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
 
   if (aReadOnly) {
@@ -50,125 +48,112 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr
     AppendSetterText(nsDependentString(aSetter));
     SetSetterLineNumber(aLineNumber);
   }
 }
 
 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
                                                const bool aIsReadOnly)
   : nsXBLProtoImplMember(aName),
-    mGetterText(nullptr),
-    mSetterText(nullptr),
     mJSAttributes(JSPROP_ENUMERATE)
 #ifdef DEBUG
   , mIsCompiled(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
 
   if (aIsReadOnly)
     mJSAttributes |= JSPROP_READONLY;
 }
 
 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
 
-  if (!(mJSAttributes & JSPROP_GETTER)) {
-    delete mGetterText;
+  if (!mGetter.IsCompiled()) {
+    delete mGetter.GetUncompiled();
   }
 
-  if (!(mJSAttributes & JSPROP_SETTER)) {
-    delete mSetterText;
+  if (!mSetter.IsCompiled()) {
+    delete mSetter.GetUncompiled();
+  }
+}
+
+void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
+{
+  if (!aPropertyOp.GetUncompiled()) {
+    nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
+    aPropertyOp.SetUncompiled(text);
   }
 }
 
 void 
 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing getter text");
-  if (!mGetterText) {
-    mGetterText = new nsXBLTextWithLineNumber();
-    if (!mGetterText)
-      return;
-  }
-
-  mGetterText->AppendText(aText);
+  EnsureUncompiledText(mGetter);
+  mGetter.GetUncompiled()->AppendText(aText);
 }
 
 void 
 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing setter text");
-  if (!mSetterText) {
-    mSetterText = new nsXBLTextWithLineNumber();
-    if (!mSetterText)
-      return;
-  }
-
-  mSetterText->AppendText(aText);
+  EnsureUncompiledText(mSetter);
+  mSetter.GetUncompiled()->AppendText(aText);
 }
 
 void
 nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing getter text");
-  if (!mGetterText) {
-    mGetterText = new nsXBLTextWithLineNumber();
-    if (!mGetterText)
-      return;
-  }
-
-  mGetterText->SetLineNumber(aLineNumber);
+  EnsureUncompiledText(mGetter);
+  mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
 }
 
 void
 nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing setter text");
-  if (!mSetterText) {
-    mSetterText = new nsXBLTextWithLineNumber();
-    if (!mSetterText)
-      return;
-  }
-
-  mSetterText->SetLineNumber(aLineNumber);
+  EnsureUncompiledText(mSetter);
+  mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
 }
 
 const char* gPropertyArgs[] = { "val" };
 
 nsresult
 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
                                       JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(mIsCompiled,
                   "Should not be installing an uncompiled property");
+  MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
   JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   // now we want to reevaluate our property using aContext and the script object for this window...
-  if (mJSGetterObject || mJSSetterObject) {
+  if (mGetter.GetJSFunction() || mSetter.GetJSFunction()) {
     // First, enter the compartment of the scope object and clone the functions.
     JSAutoCompartment ac(aCx, scopeObject);
 
     JS::Rooted<JSObject*> getter(aCx, nullptr);
-    if (mJSGetterObject) {
-      if (!(getter = ::JS_CloneFunctionObject(aCx, mJSGetterObject, scopeObject)))
+    if (mGetter.GetJSFunction()) {
+      if (!(getter = ::JS_CloneFunctionObject(aCx, mGetter.GetJSFunction(), scopeObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     JS::Rooted<JSObject*> setter(aCx, nullptr);
-    if (mJSSetterObject) {
-      if (!(setter = ::JS_CloneFunctionObject(aCx, mJSSetterObject, scopeObject)))
+    if (mSetter.GetJSFunction()) {
+      if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Now, enter the content compartment, wrap the getter/setter, and define
     // them on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
     nsDependentString name(mName);
     if (!JS_WrapObject(aCx, getter.address()) ||
@@ -187,161 +172,164 @@ nsXBLProtoImplProperty::InstallMember(JS
 nsresult
 nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
                                       JS::Handle<JSObject*> aClassObject)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Trying to compile an already-compiled property");
   NS_PRECONDITION(aClassObject,
                   "Must have class object to compile");
+  MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
 
   if (!mName)
     return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
 
   // We have a property.
   nsresult rv = NS_OK;
 
   nsAutoCString functionUri;
-  if (mGetterText || mSetterText) {
+  if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
     functionUri = aClassStr;
     int32_t hash = functionUri.RFindChar('#');
     if (hash != kNotFound) {
       functionUri.Truncate(hash);
     }
   }
 
   bool deletedGetter = false;
-  if (mGetterText && mGetterText->GetText()) {
-    nsDependentString getter(mGetterText->GetText());
+  nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
+  if (getterText && getterText->GetText()) {
+    nsDependentString getter(getterText->GetText());
     if (!getter.IsEmpty()) {
       AutoPushJSContext cx(aContext->GetNativeContext());
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), mGetterText->GetLineNumber())
+      options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
              .setVersion(JSVERSION_LATEST);
       nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
       JS::RootedObject rootedNull(cx, nullptr); // See bug 781070.
       JS::RootedObject getterObject(cx);
       rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 0, nullptr,
                                       getter, getterObject.address());
 
-      // Make sure we free mGetterText here before setting mJSGetterObject, since
-      // that'll overwrite mGetterText
-      delete mGetterText;
+      delete getterText;
       deletedGetter = true;
-      mJSGetterObject = getterObject;
+
+      mGetter.SetJSFunction(getterObject);
     
-      if (mJSGetterObject && NS_SUCCEEDED(rv)) {
+      if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
       }
       if (NS_FAILED(rv)) {
-        mJSGetterObject = nullptr;
+        mGetter.SetJSFunction(nullptr);
         mJSAttributes &= ~JSPROP_GETTER;
         /*chaining to return failure*/
       }
     }
   } // if getter is not empty
 
   if (!deletedGetter) {  // Empty getter
-    delete mGetterText;
-    mJSGetterObject = nullptr;
+    delete getterText;
+    mGetter.SetJSFunction(nullptr);
   }
   
   if (NS_FAILED(rv)) {
     // We failed to compile our getter.  So either we've set it to null, or
     // it's still set to the text object.  In either case, it's safe to return
     // the error here, since then we'll be cleaned up as uncompiled and that
     // will be ok.  Going on and compiling the setter and _then_ returning an
     // error, on the other hand, will try to clean up a compiled setter as
     // uncompiled and crash.
     return rv;
   }
 
   bool deletedSetter = false;
-  if (mSetterText && mSetterText->GetText()) {
-    nsDependentString setter(mSetterText->GetText());
+  nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
+  if (setterText && setterText->GetText()) {
+    nsDependentString setter(setterText->GetText());
     if (!setter.IsEmpty()) {
       AutoPushJSContext cx(aContext->GetNativeContext());
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), mSetterText->GetLineNumber())
+      options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
              .setVersion(JSVERSION_LATEST);
       nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
       JS::RootedObject rootedNull(cx, nullptr); // See bug 781070.
       JS::RootedObject setterObject(cx);
       rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 1,
                                       gPropertyArgs, setter, setterObject.address());
 
-      // Make sure we free mSetterText here before setting mJSGetterObject, since
-      // that'll overwrite mSetterText
-      delete mSetterText;
+      delete setterText;
       deletedSetter = true;
-      mJSSetterObject = setterObject;
+      mSetter.SetJSFunction(setterObject);
 
-      if (mJSSetterObject && NS_SUCCEEDED(rv)) {
+      if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
       }
       if (NS_FAILED(rv)) {
-        mJSSetterObject = nullptr;
+        mSetter.SetJSFunction(nullptr);
         mJSAttributes &= ~JSPROP_SETTER;
         /*chaining to return failure*/
       }
     }
   } // if setter wasn't empty....
 
   if (!deletedSetter) {  // Empty setter
-    delete mSetterText;
-    mJSSetterObject = nullptr;
+    delete setterText;
+    mSetter.SetJSFunction(nullptr);
   }
 
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
 
   return rv;
 }
 
 void
 nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
 {
   if (mJSAttributes & JSPROP_GETTER) {
-    aCallbacks.Trace(&mJSGetterObject, "mJSGetterObject", aClosure);
+    aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
   }
 
   if (mJSAttributes & JSPROP_SETTER) {
-    aCallbacks.Trace(&mJSSetterObject, "mJSSetterObject", aClosure);
+    aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
   }
 }
 
 nsresult
 nsXBLProtoImplProperty::Read(nsIScriptContext* aContext,
                              nsIObjectInputStream* aStream,
                              XBLBindingSerializeDetails aType)
 {
+  MOZ_ASSERT(!mIsCompiled);
+  MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
+
   JSContext *cx = aContext->GetNativeContext();
 
+  JS::Rooted<JSObject*> getterObject(cx);
   if (aType == XBLBinding_Serialize_GetterProperty ||
       aType == XBLBinding_Serialize_GetterSetterProperty) {
-    JS::Rooted<JSObject*> getterObject(cx);
     nsresult rv = XBL_DeserializeFunction(aContext, aStream, &getterObject);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mJSGetterObject = getterObject;
     mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
   }
-
+  mGetter.SetJSFunction(getterObject);
+  
+  JS::Rooted<JSObject*> setterObject(cx);
   if (aType == XBLBinding_Serialize_SetterProperty ||
       aType == XBLBinding_Serialize_GetterSetterProperty) {
-    JS::Rooted<JSObject*> setterObject(cx);
     nsresult rv = XBL_DeserializeFunction(aContext, aStream, &setterObject);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mJSSetterObject = setterObject;
     mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
   }
+  mSetter.SetJSFunction(setterObject);
 
 #ifdef DEBUG
   mIsCompiled = true;
 #endif
 
   return NS_OK;
 }
 
@@ -365,21 +353,19 @@ nsXBLProtoImplProperty::Write(nsIScriptC
   }
 
   nsresult rv = aStream->Write8(type);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteWStringZ(mName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mJSAttributes & JSPROP_GETTER) {
-    rv = XBL_SerializeFunction(aContext, aStream,
-      JS::Handle<JSObject*>::fromMarkedLocation(&mJSGetterObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mGetter.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (mJSAttributes & JSPROP_SETTER) {
-    rv = XBL_SerializeFunction(aContext, aStream,
-      JS::Handle<JSObject*>::fromMarkedLocation(&mJSSetterObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mSetter.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -7,16 +7,17 @@
 #define nsXBLProtoImplProperty_h__
 
 #include "mozilla/Attributes.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsString.h"
 #include "nsXBLSerialize.h"
+#include "nsXBLMaybeCompiled.h"
 #include "nsXBLProtoImplMember.h"
 
 class nsXBLProtoImplProperty: public nsXBLProtoImplMember
 {
 public:
   nsXBLProtoImplProperty(const PRUnichar* aName,
                          const PRUnichar* aGetter, 
                          const PRUnichar* aSetter,
@@ -43,30 +44,26 @@ public:
 
   nsresult Read(nsIScriptContext* aContext,
                 nsIObjectInputStream* aStream,
                 XBLBindingSerializeDetails aType);
   virtual nsresult Write(nsIScriptContext* aContext,
                          nsIObjectOutputStream* aStream) MOZ_OVERRIDE;
 
 protected:
-  union {
-    // The raw text for the getter (prior to compilation).
-    nsXBLTextWithLineNumber* mGetterText;
-    // The JS object for the getter (after compilation)
-    JSObject *               mJSGetterObject;
-  };
+  typedef JS::Heap<nsXBLMaybeCompiled<nsXBLTextWithLineNumber> > PropertyOp;
+
+  void EnsureUncompiledText(PropertyOp& aPropertyOp);
 
-  union {
-    // The raw text for the setter (prior to compilation).
-    nsXBLTextWithLineNumber* mSetterText;
-    // The JS object for the setter (after compilation)
-    JSObject *               mJSSetterObject;
-  };
+  // The raw text for the getter, or the JS object (after compilation).
+  PropertyOp mGetter;
+
+  // The raw text for the setter, or the JS object (after compilation).
+  PropertyOp mSetter;
   
-  unsigned mJSAttributes;          // A flag for all our JS properties (getter/setter/readonly/shared/enum)
+  unsigned mJSAttributes;  // A flag for all our JS properties (getter/setter/readonly/shared/enum)
 
 #ifdef DEBUG
   bool mIsCompiled;
 #endif
 };
 
 #endif // nsXBLProtoImplProperty_h__
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2378,18 +2378,17 @@ nsXULPrototypeScript::Serialize(nsIObjec
 
     // Write basic prototype data
     nsresult rv;
     rv = aStream->Write32(mLineNo);
     if (NS_FAILED(rv)) return rv;
     rv = aStream->Write32(mLangVersion);
     if (NS_FAILED(rv)) return rv;
     // And delegate the writing to the nsIScriptContext
-    rv = context->Serialize(aStream,
-                            JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject));
+    rv = context->Serialize(aStream, mScriptObject);
     if (NS_FAILED(rv)) return rv;
 
     return NS_OK;
 }
 
 nsresult
 nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
                                          nsIScriptGlobalObject* aGlobal)
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -238,23 +238,23 @@ public:
     void Set(JSScript* aObject);
 
     // It's safe to return a handle because we trace mScriptObject, no one ever
     // uses the handle (or the script object) past the point at which the
     // nsXULPrototypeScript dies, and we can't get memmoved so the
     // &mScriptObject pointer can't go stale.
     JS::Handle<JSScript*> GetScriptObject()
     {
-        return JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject);
+        return JS::Handle<JSScript*>(mScriptObject);
     }
 
     void TraceScriptObject(JSTracer* aTrc)
     {
         if (mScriptObject) {
-            JS_CallScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script");
+            JS_CallHeapScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script");
         }
     }
 
     void Trace(const TraceCallbacks& aCallbacks, void* aClosure)
     {
         if (mScriptObject) {
             aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure);
         }
@@ -262,17 +262,17 @@ public:
 
     nsCOMPtr<nsIURI>         mSrcURI;
     uint32_t                 mLineNo;
     bool                     mSrcLoading;
     bool                     mOutOfLine;
     mozilla::dom::XULDocument* mSrcLoadWaiters;   // [OWNER] but not COMPtr
     uint32_t                 mLangVersion;
 private:
-    JSScript*                mScriptObject;
+    JS::Heap<JSScript*>      mScriptObject;
 };
 
 class nsXULPrototypeText : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypeText()
         : nsXULPrototypeNode(eType_Text)
     {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7885,27 +7885,16 @@ nsDocShell::RestoreFromHistory()
     newVM = nullptr;
 
     // Simulate the completion of the load.
     nsDocShell::FinishRestore();
 
     // Restart plugins, and paint the content.
     if (shell) {
         shell->Thaw();
-
-        newVM = shell->GetViewManager();
-        if (newVM) {
-            // When we insert the root view above the resulting invalidate is
-            // dropped because painting is suppressed in the presshell until we
-            // call Thaw. So we issue the invalidate here.
-            newRootView = newVM->GetRootView();
-            if (newRootView) {
-                newVM->InvalidateView(newRootView);
-            }
-        }
     }
 
     return privWin->FireDelayedDOMEvents();
 }
 
 NS_IMETHODIMP
 nsDocShell::CreateContentViewer(const char *aContentType,
                                 nsIRequest * request,
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -17,17 +17,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest : public nsDOMEventTargetHelper,
                    public nsIDOMDOMRequest
 {
 protected:
-  JS::Value mResult;
+  JS::Heap<JS::Value> mResult;
   nsRefPtr<DOMError> mError;
   bool mDone;
   bool mRooted;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMDOMREQUEST
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -89,17 +89,17 @@ CPP_SOURCES += [
     'nsMimeTypeArray.cpp',
     'nsPerformance.cpp',
     'nsPluginArray.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
-    'nsWindowRoot.cpp',
+    'nsWindowRoot.cpp'
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.js',
     'ConsoleAPI.manifest',
     'SiteSpecificUserAgent.js',
     'SiteSpecificUserAgent.manifest',
 ]
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1583,26 +1583,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   END_OUTER_WINDOW_ONLY
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
 
 static PLDHashOperator
-MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure)
+MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
 {
   xpc_UnmarkGrayObject(aData);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
   if (tmp->IsBlackForCC()) {
     if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
-      tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr);
+      tmp->mCachedXBLPrototypeHandlers.Enumerate(MarkXBLHandlers, nullptr);
     }
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
       elm->MarkForCC();
     }
     tmp->UnmarkGrayTimers();
     return true;
   }
@@ -1744,17 +1744,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 struct TraceData
 {
   const TraceCallbacks& callbacks;
   void* closure;
 };
 
 static PLDHashOperator
-TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject*& aData, void* aClosure)
+TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
 {
   TraceData* data = static_cast<TraceData*>(aClosure);
   data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
   if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -10,16 +10,17 @@
 #include "mozilla/XPCOM.h" // for TimeStamp/TimeDuration
 
 // Local Includes
 // Helper Classes
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 #include "nsDataHashtable.h"
+#include "nsJSThingHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 
 // Interfaces Needed
 #include "nsDOMWindowList.h"
 #include "nsIBaseWindow.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellTreeItem.h"
@@ -1229,17 +1230,17 @@ protected:
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
   bool mCleanedUp, mCallCleanUpAfterModalDialogCloses;
 
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
-  nsDataHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers;
+  nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers;
 
   nsCOMPtr<nsIDocument> mSuspendedDoc;
 
   nsRefPtr<mozilla::dom::indexedDB::IDBFactory> mIndexedDB;
 
   // This counts the number of windows that have been opened in rapid succession
   // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
   // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
--- a/dom/base/nsIJSEventListener.h
+++ b/dom/base/nsIJSEventListener.h
@@ -263,17 +263,17 @@ protected:
     NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
   }
 
   // Update our mScopeObject; we have to make sure we properly handle
   // the hold/drop stuff, so have to do it in nsJSEventListener.
   virtual void UpdateScopeObject(JS::Handle<JSObject*> aScopeObject) = 0;
 
   nsCOMPtr<nsIScriptContext> mContext;
-  JSObject* mScopeObject;
+  JS::Heap<JSObject*> mScopeObject;
   nsISupports* mTarget;
   nsCOMPtr<nsIAtom> mEventName;
   nsEventHandler mHandler;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID)
 
 /* factory function.  aHandler must already be bound to aTarget.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3518,30 +3518,31 @@ public:
 
   // nsIJSArgArray
   nsresult GetArgs(uint32_t *argc, void **argv);
 
   void ReleaseJSObjects();
 
 protected:
   JSContext *mContext;
-  JS::Value *mArgv;
+  JS::Heap<JS::Value> *mArgv;
   uint32_t mArgc;
 };
 
 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                            nsresult *prv) :
     mContext(aContext),
     mArgv(nullptr),
     mArgc(argc)
 {
   // copy the array - we don't know its lifetime, and ours is tied to xpcom
-  // refcounting.  Alloc zero'd array so cleanup etc is safe.
+  // refcounting.
   if (argc) {
-    mArgv = (JS::Value *) PR_CALLOC(argc * sizeof(JS::Value));
+    static const fallible_t fallible = fallible_t();
+    mArgv = new (fallible) JS::Heap<JS::Value>[argc];
     if (!mArgv) {
       *prv = NS_ERROR_OUT_OF_MEMORY;
       return;
     }
   }
 
   // Callers are allowed to pass in a null argv even for argc > 0. They can
   // then use GetArgs to initialize the values.
@@ -3561,17 +3562,17 @@ nsJSArgArray::~nsJSArgArray()
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSArgArray::ReleaseJSObjects()
 {
   if (mArgv) {
-    PR_DELETE(mArgv);
+    delete [] mArgv;
   }
   if (mArgc > 0) {
     mArgc = 0;
     NS_DROP_JS_OBJECTS(this, nsJSArgArray);
   }
 }
 
 // QueryInterface implementation for nsJSArgArray
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -62,20 +62,21 @@ public:
 
   void ReleaseJSObjects();
 
 private:
   // filename, line number and JS language version string of the
   // caller of setTimeout()
   nsCString mFileName;
   uint32_t mLineNo;
-  nsTArray<JS::Value> mArgs;
+  nsTArray<JS::Heap<JS::Value> > mArgs;
 
   // The JS expression to evaluate or function to call, if !mExpr
-  JSFlatString *mExpr;
+  // Note this is always a flat string.
+  JS::Heap<JSString*> mExpr;
   nsRefPtr<Function> mFunction;
 };
 
 
 // nsJSScriptTimeoutHandler
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
   tmp->ReleaseJSObjects();
@@ -276,17 +277,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
           // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
           return NS_ERROR_DOM_TYPE_ERR;
         }
       }
     } // if there's no document, we don't have to do anything.
 
     NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
 
-    mExpr = expr;
+    mExpr = JS_FORGET_STRING_FLATNESS(expr);
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
@@ -294,17 +295,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     mFunction = new Function(funobj);
 
     // Create our arg array.  argc is the number of arguments passed
     // to setTimeout or setInterval; the first two are our callback
     // and the delay, so only arguments after that need to go in our
     // array.
     // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
     uint32_t argCount = std::max(argc, 2u) - 2;
-    FallibleTArray<JS::Value> args;
+
+    FallibleTArray<JS::Heap<JS::Value> > args;
     if (!args.SetCapacity(argCount)) {
       // No need to drop here, since we already have a non-null mFunction
       return NS_ERROR_OUT_OF_MEMORY;
     }
     for (uint32_t idx = 0; idx < argCount; ++idx) {
       *args.AppendElement() = argv[idx + 2];
     }
     args.SwapElements(mArgs);
@@ -314,17 +316,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
   *aInterval = interval;
   return NS_OK;
 }
 
 const PRUnichar *
 nsJSScriptTimeoutHandler::GetHandlerText()
 {
   NS_ASSERTION(mExpr, "No expression, so no handler text!");
-  return ::JS_GetFlatStringChars(mExpr);
+  return ::JS_GetFlatStringChars(JS_ASSERT_STRING_IS_FLAT(mExpr));
 }
 
 nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                                    bool *aIsInterval,
                                    int32_t *aInterval,
                                    nsIScriptTimeoutHandler **aRet)
 {
   *aRet = nullptr;
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -3,17 +3,17 @@
  * 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 nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
-#include "js/RootingAPI.h"
+#include "js/Value.h"
 
 struct JSTracer;
 class JSObject;
 struct JSContext;
 class XPCWrappedNativeScope;
 
 namespace mozilla {
 namespace dom {
@@ -177,17 +177,17 @@ public:
     else {
       UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
     }
   }
 
   void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
   {
     if (PreservingWrapper() && mWrapper) {
-        aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
+      aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
     }
   }
 
   /* 
    * The following methods for getting and manipulating flags allow the unused
    * bits of mFlags to be used by derived classes.
    */
 
@@ -275,18 +275,18 @@ private:
    * (regular JS object or proxy) that has a system only wrapper for same-origin
    * access.
    */
   enum { WRAPPER_HAS_SOW = 1 << 2 };
 
   enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING |
                               WRAPPER_HAS_SOW) };
 
-  JSObject* mWrapper;
-  uint32_t  mFlags;
+  JS::Heap<JSObject*> mWrapper;
+  uint32_t            mFlags;
 };
 
 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 3 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -45,12 +45,12 @@ nsWrapperCache::IsBlackAndDoesNotNeedTra
     return !hasGrayObjects;
   }
   return false;
 }
 
 inline void
 nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName)
 {
-  JS_CallObjectTracer(aTrc, &mWrapper, aName);
+  JS_CallHeapObjectTracer(aTrc, &mWrapper, aName);
 }
 
 #endif /* nsWrapperCache_h___ */
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -71,17 +71,17 @@ public:
    * This should only be called if you are certain that the return value won't
    * be passed into a JS API function and that it won't be stored without being
    * rooted (or otherwise signaling the stored value to the CC).
    *
    * This can return a handle because we trace our mCallback.
    */
   JS::Handle<JSObject*> CallbackPreserveColor() const
   {
-    return JS::Handle<JSObject*>::fromMarkedLocation(&mCallback);
+    return mCallback;
   }
 
   enum ExceptionHandling {
     eReportExceptions,
     eRethrowExceptions
   };
 
 protected:
@@ -106,17 +106,17 @@ protected:
   {
     if (mCallback) {
       mCallback = nullptr;
       NS_DROP_JS_OBJECTS(this, CallbackObject);
       nsLayoutStatics::Release();
     }
   }
 
-  JSObject* mCallback;
+  JS::Heap<JSObject*> mCallback;
 
   class MOZ_STACK_CLASS CallSetup
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -257,39 +257,43 @@ BluetoothAdapter::SetPropertyByValue(con
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
-    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) {
+    JS::Rooted<JSObject*> uuids(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) {
       NS_WARNING("Cannot set JS UUIDs object!");
       return;
     }
+    mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Devices")) {
     mDeviceAddresses = value.get_ArrayOfnsString();
 
     uint32_t length = mDeviceAddresses.Length();
     for (int i = 0; i < length; i++) {
       mDeviceAddresses[i] = GetAddressFromObjectPath(mDeviceAddresses[i]);
     }
 
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
+    JS::Rooted<JSObject*> deviceAddresses(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses,
-                                    &mJsDeviceAddresses))) {
+                                    deviceAddresses.address()))) {
       NS_WARNING("Cannot set JS Devices object!");
       return;
     }
+    mJsDeviceAddresses = deviceAddresses;
     Root();
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling adapter property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
 #endif
--- a/dom/bluetooth/BluetoothAdapter.h
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -67,16 +67,16 @@ private:
   bool mDiscovering;
   bool mPairable;
   bool mPowered;
   uint32_t mPairableTimeout;
   uint32_t mDiscoverableTimeout;
   uint32_t mClass;
   nsTArray<nsString> mDeviceAddresses;
   nsTArray<nsString> mUuids;
-  JSObject* mJsUuids;
-  JSObject* mJsDeviceAddresses;
+  JS::Heap<JSObject*> mJsUuids;
+  JS::Heap<JSObject*> mJsDeviceAddresses;
   bool mIsRooted;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -119,33 +119,37 @@ BluetoothDevice::SetPropertyByValue(cons
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
 
-    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) {
+    JS::Rooted<JSObject*> uuids(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) {
       NS_WARNING("Cannot set JS UUIDs object!");
       return;
     }
+    mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Services")) {
     mServices = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
 
-    if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &mJsServices))) {
+    JS::Rooted<JSObject*> services(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) {
       NS_WARNING("Cannot set JS Services object!");
       return;
     }
+    mJsServices = services;
     Root();
   } else {
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
   }
 }
--- a/dom/bluetooth/BluetoothDevice.h
+++ b/dom/bluetooth/BluetoothDevice.h
@@ -53,18 +53,18 @@ public:
 
   void Unroot();
 private:
   BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
                   const BluetoothValue& aValue);
   ~BluetoothDevice();
   void Root();
 
-  JSObject* mJsUuids;
-  JSObject* mJsServices;
+  JS::Heap<JSObject*> mJsUuids;
+  JS::Heap<JSObject*> mJsServices;
 
   nsString mAdapterPath;
   nsString mAddress;
   nsString mName;
   nsString mIcon;
   uint32_t mClass;
   bool mConnected;
   bool mPaired;
--- a/dom/future/Future.h
+++ b/dom/future/Future.h
@@ -102,17 +102,17 @@ private:
 
   nsRefPtr<nsPIDOMWindow> mWindow;
 
   nsRefPtr<FutureResolver> mResolver;
 
   nsTArray<nsRefPtr<FutureCallback> > mResolveCallbacks;
   nsTArray<nsRefPtr<FutureCallback> > mRejectCallbacks;
 
-  JS::Value mResult;
+  JS::Heap<JS::Value> mResult;
   FutureState mState;
   bool mTaskPending;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Future_h
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -539,17 +539,17 @@ IDBCursor::GetKey(JSContext* aCx,
   }
 
   if (!mHaveCachedKey) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBCursor);
       mRooted = true;
     }
 
-    nsresult rv = mKey.ToJSVal(aCx, &mCachedKey);
+    nsresult rv = mKey.ToJSVal(aCx, mCachedKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedKey = true;
   }
 
   *aKey = mCachedKey;
   return NS_OK;
 }
@@ -573,17 +573,17 @@ IDBCursor::GetPrimaryKey(JSContext* aCx,
 
     JSAutoRequest ar(aCx);
 
     NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
                                         !mObjectKey.IsUnset(), "Bad key!");
 
     const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
 
-    nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey);
+    nsresult rv = key.ToJSVal(aCx, mCachedPrimaryKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedPrimaryKey = true;
   }
 
   *aValue = mCachedPrimaryKey;
   return NS_OK;
 }
@@ -734,17 +734,17 @@ IDBCursor::Update(const jsval& aValue,
 
     rv = mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, getter_AddRefs(request));
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
   else {
     JS::Rooted<JS::Value> keyVal(aCx);
-    rv = objectKey.ToJSVal(aCx, keyVal.address());
+    rv = objectKey.ToJSVal(aCx, &keyVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mObjectStore->Put(aValue, keyVal, aCx, 1, getter_AddRefs(request));
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
@@ -805,17 +805,17 @@ IDBCursor::Delete(JSContext* aCx,
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
 
   Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   JS::Rooted<JS::Value> key(aCx);
-  nsresult rv = objectKey.ToJSVal(aCx, key.address());
+  nsresult rv = objectKey.ToJSVal(aCx, &key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIDBRequest> request;
   rv = mObjectStore->Delete(key, aCx, getter_AddRefs(request));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -163,27 +163,27 @@ protected:
                const nsACString& aContinueQuery,
                const nsACString& aContinueToQuery);
 
   nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<IDBTransaction> mTransaction;
   nsRefPtr<IDBObjectStore> mObjectStore;
   nsRefPtr<IDBIndex> mIndex;
 
-  JSObject* mScriptOwner;
+  JS::Heap<JSObject*> mScriptOwner;
 
   Type mType;
   Direction mDirection;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
 
   // These are cycle-collected!
-  jsval mCachedKey;
-  jsval mCachedPrimaryKey;
-  jsval mCachedValue;
+  JS::Heap<JS::Value> mCachedKey;
+  JS::Heap<JS::Value> mCachedPrimaryKey;
+  JS::Heap<JS::Value> mCachedValue;
 
   Key mRangeKey;
 
   Key mKey;
   Key mObjectKey;
   StructuredCloneReadInfo mCloneReadInfo;
   Key mContinueToKey;
 
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -178,17 +178,17 @@ private:
   Open(JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
        const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv);
 
   nsCString mASCIIOrigin;
 
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
-  JSObject* mOwningObject;
+  JS::Heap<JSObject*> mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
 
   mozilla::dom::ContentParent* mContentParent;
 
   bool mRootedOwningObject;
 };
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -805,17 +805,17 @@ IDBIndex::GetKeyPath(JSContext* aCx,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     *aVal = mCachedKeyPath;
     return NS_OK;
   }
 
-  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
     NS_HOLD_JS_OBJECTS(this, IDBIndex);
     mRooted = true;
   }
 
   *aVal = mCachedKeyPath;
@@ -1175,17 +1175,22 @@ GetKeyHelper::DoDatabaseWork(mozIStorage
 
   return NS_OK;
 }
 
 nsresult
 GetKeyHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
-  return mKey.ToJSVal(aCx, aVal);
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = mKey.ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    *aVal = value;
+  }
+  return rv;
 }
 
 void
 GetKeyHelper::ReleaseMainThreadObjects()
 {
   mKeyRange = nullptr;
   IndexHelper::ReleaseMainThreadObjects();
 }
@@ -1503,17 +1508,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32_t index = 0, count = keys.Length(); index < count; index++) {
       const Key& key = keys[index];
       NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
       JS::Rooted<JS::Value> value(aCx);
-      nsresult rv = key.ToJSVal(aCx, value.address());
+      nsresult rv = key.ToJSVal(aCx, &value);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to get jsval for key!");
         return rv;
       }
 
       if (!JS_SetElement(aCx, array, index, value.address())) {
         NS_WARNING("Failed to set array element!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -152,17 +152,17 @@ private:
   IDBIndex();
   ~IDBIndex();
 
   nsRefPtr<IDBObjectStore> mObjectStore;
 
   int64_t mId;
   nsString mName;
   KeyPath mKeyPath;
-  JS::Value mCachedKeyPath;
+  JS::Heap<JS::Value> mCachedKeyPath;
 
   IndexedDBIndexChild* mActorChild;
   IndexedDBIndexParent* mActorParent;
 
   bool mUnique;
   bool mMultiEntry;
   bool mRooted;
 };
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -358,17 +358,17 @@ IDBKeyRange::GetLower(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedLowerVal) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
       mRooted = true;
     }
 
-    nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal);
+    nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedLowerVal = true;
   }
 
   *aLower = mCachedLowerVal;
   return NS_OK;
 }
@@ -380,17 +380,17 @@ IDBKeyRange::GetUpper(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedUpperVal) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
       mRooted = true;
     }
 
-    nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal);
+    nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedUpperVal = true;
   }
 
   *aUpper = mCachedUpperVal;
   return NS_OK;
 }
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -152,18 +152,18 @@ public:
 
   void DropJSObjects();
 
 private:
   ~IDBKeyRange();
 
   Key mLower;
   Key mUpper;
-  jsval mCachedLowerVal;
-  jsval mCachedUpperVal;
+  JS::Heap<JS::Value> mCachedLowerVal;
+  JS::Heap<JS::Value> mCachedUpperVal;
   bool mLowerOpen;
   bool mUpperOpen;
   bool mIsOnly;
   bool mHaveCachedLowerVal;
   bool mHaveCachedUpperVal;
   bool mRooted;
 };
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2382,17 +2382,17 @@ IDBObjectStore::GetKeyPath(JSContext* aC
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     *aVal = mCachedKeyPath;
     return NS_OK;
   }
 
-  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
     NS_HOLD_JS_OBJECTS(this, IDBObjectStore);
     mRooted = true;
   }
 
   *aVal = mCachedKeyPath;
@@ -3129,17 +3129,22 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 nsresult
 AddHelper::GetSuccessResult(JSContext* aCx,
                             jsval* aVal)
 {
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   mCloneWriteInfo.mCloneBuffer.clear();
 
-  return mKey.ToJSVal(aCx, aVal);
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = mKey.ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    *aVal = value;
+  }
+  return rv;
 }
 
 void
 AddHelper::ReleaseMainThreadObjects()
 {
   IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
   ObjectStoreHelper::ReleaseMainThreadObjects();
 }
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -282,17 +282,17 @@ protected:
                  uint32_t aTag,
                  BlobOrFileData* aRetval);
 private:
   nsRefPtr<IDBTransaction> mTransaction;
 
   int64_t mId;
   nsString mName;
   KeyPath mKeyPath;
-  JS::Value mCachedKeyPath;
+  JS::Heap<JS::Value> mCachedKeyPath;
   bool mRooted;
   bool mAutoIncrement;
   nsCOMPtr<nsIAtom> mDatabaseId;
   nsRefPtr<ObjectStoreInfo> mInfo;
 
   nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;
 
   IndexedDBObjectStoreChild* mActorChild;
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -115,23 +115,25 @@ IDBRequest::NotifyHelperCompleted(Helper
   }
 
   JS::Rooted<JSObject*> global(cx, GetParentObject());
   NS_ASSERTION(global, "This should never be null!");
 
   JSAutoCompartment ac(cx, global);
   AssertIsRooted();
 
-  rv = aHelper->GetSuccessResult(cx, &mResultVal);
+  JS::Rooted<JS::Value> value(cx);
+  rv = aHelper->GetSuccessResult(cx, value.address());
   if (NS_FAILED(rv)) {
     NS_WARNING("GetSuccessResult failed!");
   }
 
   if (NS_SUCCEEDED(rv)) {
     mError = nullptr;
+    mResultVal = value;
   }
   else {
     SetError(rv);
     mResultVal = JSVAL_VOID;
   }
 
   return rv;
 }
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -104,17 +104,17 @@ public:
 
 protected:
   IDBRequest();
   ~IDBRequest();
 
   nsCOMPtr<nsISupports> mSource;
   nsRefPtr<IDBTransaction> mTransaction;
 
-  jsval mResultVal;
+  JS::Heap<JS::Value> mResultVal;
   nsRefPtr<mozilla::dom::DOMError> mError;
   IndexedDBRequestParentBase* mActorParent;
   nsString mFilename;
 #ifdef MOZ_ENABLE_PROFILER_SPS
   uint64_t mSerialNumber;
 #endif
   nsresult mErrorCode;
   uint32_t mLineNo;
--- a/dom/indexedDB/IDBWrapperCache.h
+++ b/dom/indexedDB/IDBWrapperCache.h
@@ -57,14 +57,14 @@ public:
 protected:
   IDBWrapperCache()
   : mScriptOwner(nullptr)
   { }
 
   virtual ~IDBWrapperCache();
 
 private:
-  JSObject* mScriptOwner;
+  JS::Heap<JSObject*> mScriptOwner;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbwrappercache_h__
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -180,17 +180,17 @@ Key::EncodeJSValInternal(JSContext* aCx,
   }
 
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
 Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
-                         JSContext* aCx, uint8_t aTypeOffset, jsval* aVal,
+                         JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal,
                          uint16_t aRecursionDepth)
 {
   NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
 
   if (*aPos - aTypeOffset >= eArray) {
     JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0, nullptr));
     if (!array) {
       NS_WARNING("Failed to make array!");
@@ -203,52 +203,52 @@ Key::DecodeJSValInternal(const unsigned 
       ++aPos;
       aTypeOffset = 0;
     }
 
     uint32_t index = 0;
     while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
       JS::Rooted<JS::Value> val(aCx);
       nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset,
-                                        val.address(), aRecursionDepth + 1);
+                                        &val, aRecursionDepth + 1);
       NS_ENSURE_SUCCESS(rv, rv);
 
       aTypeOffset = 0;
 
       if (!JS_SetElement(aCx, array, index++, val.address())) {
         NS_WARNING("Failed to set array element!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
     NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator,
                  "Should have found end-of-array marker");
     ++aPos;
 
-    *aVal = OBJECT_TO_JSVAL(array);
+    aVal.setObject(*array);
   }
   else if (*aPos - aTypeOffset == eString) {
     nsString key;
     DecodeString(aPos, aEnd, key);
-    if (!xpc::StringToJsval(aCx, key, aVal)) {
+    if (!xpc::StringToJsval(aCx, key, aVal.address())) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
   else if (*aPos - aTypeOffset == eDate) {
     double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
     JSObject* date = JS_NewDateObjectMsec(aCx, msec);
     if (!date) {
       NS_WARNING("Failed to make date!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    *aVal = OBJECT_TO_JSVAL(date);
+    aVal.setObject(*date);
   }
   else if (*aPos - aTypeOffset == eFloat) {
-    *aVal = DOUBLE_TO_JSVAL(DecodeNumber(aPos, aEnd));
+    aVal.setDouble(DecodeNumber(aPos, aEnd));
   }
   else {
     NS_NOTREACHED("Unknown key type!");
   }
 
   return NS_OK;
 }
 
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -174,33 +174,44 @@ public:
       return rv;
     }
     TrimBuffer();
 
     return NS_OK;
   }
 
   nsresult ToJSVal(JSContext* aCx,
-                   jsval* aVal) const
+                   JS::MutableHandle<JS::Value> aVal) const
   {
     if (IsUnset()) {
-      *aVal = JSVAL_VOID;
+      aVal.set(JSVAL_VOID);
       return NS_OK;
     }
 
     const unsigned char* pos = BufferStart();
     nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(pos >= BufferEnd(),
                  "Didn't consume whole buffer");
 
     return NS_OK;
   }
 
+  nsresult ToJSVal(JSContext* aCx,
+                   JS::Heap<JS::Value>& aVal) const
+  {
+    JS::Rooted<JS::Value> value(aCx);
+    nsresult rv = ToJSVal(aCx, &value);
+    if (NS_SUCCEEDED(rv)) {
+      aVal = value;
+    }
+    return rv;
+  }
+
   nsresult AppendItem(JSContext* aCx,
                       bool aFirstOfArray,
                       const jsval aVal)
   {
     nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0);
     if (NS_FAILED(rv)) {
       Unset();
       return rv;
@@ -299,17 +310,17 @@ private:
   }
   void EncodeString(const nsAString& aString, uint8_t aTypeOffset);
   void EncodeNumber(double aFloat, uint8_t aType);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
   // past the consumed value.
   static inline nsresult DecodeJSVal(const unsigned char*& aPos,
                                      const unsigned char* aEnd, JSContext* aCx,
-                                     uint8_t aTypeOffset, jsval* aVal)
+                                     uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal)
   {
     return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0);
   }
 
   static void DecodeString(const unsigned char*& aPos,
                            const unsigned char* aEnd,
                            nsString& aString);
   static double DecodeNumber(const unsigned char*& aPos,
@@ -319,14 +330,14 @@ private:
 
 private:
   nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal,
                                uint8_t aTypeOffset, uint16_t aRecursionDepth);
 
   static nsresult DecodeJSValInternal(const unsigned char*& aPos,
                                       const unsigned char* aEnd,
                                       JSContext* aCx, uint8_t aTypeOffset,
-                                      jsval* aVal, uint16_t aRecursionDepth);
+                                      JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth);
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_key_h__ */
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -452,17 +452,17 @@ KeyPath::DeserializeFromString(const nsA
 
   keyPath.SetType(STRING);
   keyPath.mStrings.AppendElement(aString);
 
   return keyPath;
 }
 
 nsresult
-KeyPath::ToJSVal(JSContext* aCx, JS::Value* aValue) const
+KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const
 {
   if (IsArray()) {
     uint32_t len = mStrings.Length();
     JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len, nullptr));
     if (!array) {
       NS_WARNING("Failed to make array!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
@@ -474,32 +474,43 @@ KeyPath::ToJSVal(JSContext* aCx, JS::Val
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       if (!JS_SetElement(aCx, array, i, val.address())) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
-    *aValue = OBJECT_TO_JSVAL(array);
+    aValue.setObject(*array);
     return NS_OK;
   }
 
   if (IsString()) {
     nsString tmp(mStrings[0]);
-    if (!xpc::StringToJsval(aCx, tmp, aValue)) {
+    if (!xpc::StringToJsval(aCx, tmp, aValue.address())) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
     return NS_OK;
   }
 
-  *aValue = JSVAL_NULL;
+  aValue.setNull();
   return NS_OK;
 }
 
+nsresult
+KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const
+{
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    aValue = value;
+  }
+  return rv;
+}
+
 bool
 KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const
 {
   // Any keypath that passed validation is allowed for non-autoIncrement
   // objectStores.
   if (!aAutoIncrement) {
     return true;
   }
--- a/dom/indexedDB/KeyPath.h
+++ b/dom/indexedDB/KeyPath.h
@@ -82,17 +82,18 @@ public:
   bool operator==(const KeyPath& aOther) const
   {
     return mType == aOther.mType && mStrings == aOther.mStrings;
   }
 
   void SerializeToString(nsAString& aString) const;
   static KeyPath DeserializeFromString(const nsAString& aString);
 
-  nsresult ToJSVal(JSContext* aCx, JS::Value* aValue) const;
+  nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const;
+  nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const;
 
   bool IsAllowedForObjectStore(bool aAutoIncrement) const;
 
   KeyPathType mType;
 
   nsTArray<nsString> mStrings;
 };
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1159,22 +1159,17 @@ public:
   NS_METHOD GetDescription(nsAString& aDescription)
   {
     CopyUTF8toUTF16(mPluginTag.mDescription, aDescription);
     return NS_OK;
   }
 
   NS_METHOD GetFilename(nsAString& aFilename)
   {
-    if (Preferences::GetBool("plugin.expose_full_path", false)) {
-      CopyUTF8toUTF16(mPluginTag.mFullPath, aFilename);
-    } else {
-      CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
-    }
-
+    CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
     return NS_OK;
   }
 
   NS_METHOD GetVersion(nsAString& aVersion)
   {
     CopyUTF8toUTF16(mPluginTag.mVersion, aVersion);
     return NS_OK;
   }
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -36,17 +36,17 @@ class Telephony : public nsDOMEventTarge
   nsCOMPtr<nsITelephonyProvider> mProvider;
   nsRefPtr<Listener> mListener;
 
   TelephonyCall* mActiveCall;
   nsTArray<nsRefPtr<TelephonyCall> > mCalls;
 
   // Cached calls array object. Cleared whenever mCalls changes and then rebuilt
   // once a page looks for the liveCalls attribute.
-  JSObject* mCallsArray;
+  JS::Heap<JSObject*> mCallsArray;
 
   bool mRooted;
   bool mEnumerated;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMTELEPHONY
   NS_DECL_NSITELEPHONYLISTENER
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -10,22 +10,23 @@
 #include "skia/SkDevice.h"
 #include "HelpersSkia.h"
 #include "DrawTargetSkia.h"
 
 namespace mozilla {
 namespace gfx {
 
 SourceSurfaceSkia::SourceSurfaceSkia()
-  : mDrawTarget(nullptr)
+  : mDrawTarget(nullptr), mLocked(false)
 {
 }
 
 SourceSurfaceSkia::~SourceSurfaceSkia()
 {
+  MaybeUnlock();
   MarkIndependent();
 }
 
 IntSize
 SourceSurfaceSkia::GetSize() const
 {
   return mSize;
 }
@@ -80,27 +81,31 @@ SourceSurfaceSkia::InitFromData(unsigned
   mFormat = aFormat;
   mStride = aStride;
   return true;
 }
 
 unsigned char*
 SourceSurfaceSkia::GetData()
 {
-  mBitmap.lockPixels();
+  if (!mLocked) {
+    mBitmap.lockPixels();
+    mLocked = true;
+  }
+
   unsigned char *pixels = (unsigned char *)mBitmap.getPixels();
-  mBitmap.unlockPixels();
   return pixels;
-
 }
 
 void
 SourceSurfaceSkia::DrawTargetWillChange()
 {
   if (mDrawTarget) {
+    MaybeUnlock();
+
     mDrawTarget = nullptr;
     SkBitmap temp = mBitmap;
     mBitmap.reset();
     temp.copyTo(&mBitmap, temp.getConfig());
   }
 }
 
 void
@@ -113,10 +118,19 @@ void
 SourceSurfaceSkia::MarkIndependent()
 {
   if (mDrawTarget) {
     mDrawTarget->RemoveSnapshot(this);
     mDrawTarget = nullptr;
   }
 }
 
+void
+SourceSurfaceSkia::MaybeUnlock()
+{
+  if (mLocked) {
+    mBitmap.unlockPixels();
+    mLocked = false;
+  }
+}
+
 }
 }
--- a/gfx/2d/SourceSurfaceSkia.h
+++ b/gfx/2d/SourceSurfaceSkia.h
@@ -42,20 +42,22 @@ public:
   virtual int32_t Stride() { return mStride; }
 
 private:
   friend class DrawTargetSkia;
 
   void DrawTargetWillChange();
   void DrawTargetDestroyed();
   void MarkIndependent();
+  void MaybeUnlock();
 
   SkBitmap mBitmap;
   SurfaceFormat mFormat;
   IntSize mSize;
   int32_t mStride;
   DrawTargetSkia* mDrawTarget;
+  bool mLocked;
 };
 
 }
 }
 
 #endif /* MOZILLA_GFX_SOURCESURFACESKIA_H_ */
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -461,16 +461,31 @@ public:
    */
   virtual void ClearCachedResources(Layer* aSubtree = nullptr) {}
 
   /**
    * Flag the next paint as the first for a document.
    */
   virtual void SetIsFirstPaint() {}
 
+  /**
+   * Make sure that the previous transaction has been entirely
+   * completed.
+   *
+   * Note: This may sychronously wait on a remote compositor
+   * to complete rendering.
+   */
+  virtual void FlushRendering() { }
+
+  /**
+   * Checks if we need to invalidate the OS widget to trigger
+   * painting when updating this layer manager.
+   */
+  virtual bool NeedsWidgetInvalidation() { return true; }
+
   // We always declare the following logging symbols, because it's
   // extremely tricky to conditionally declare them.  However, for
   // ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial
   // definitions in Layers.cpp.
   virtual const char* Name() const { return "???"; }
 
   /**
    * Dump information about this layer manager and its managed tree to
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -256,16 +256,26 @@ ClientLayerManager::MakeSnapshotIfRequir
         ShadowLayerForwarder::DestroySharedSurface(&snapshot);
       }
     }
   }
   mShadowTarget = nullptr;
 }
 
 void
+ClientLayerManager::FlushRendering()
+{
+  if (mWidget) {
+    if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+      remoteRenderer->SendFlushRendering();
+    }
+  }
+}
+
+void
 ClientLayerManager::ForwardTransaction()
 {
   mPhase = PHASE_FORWARD;
 
   // forward this transaction's changeset to our LayerManagerComposite
   AutoInfallibleTArray<EditReply, 10> replies;
   if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) {
     for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -48,16 +48,20 @@ public:
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
   virtual already_AddRefed<RefLayer> CreateRefLayer();
 
+  virtual void FlushRendering() MOZ_OVERRIDE;
+
+  virtual bool NeedsWidgetInvalidation() MOZ_OVERRIDE { return Compositor::GetBackend() == LAYERS_BASIC; }
+
   ShadowableLayer* Hold(Layer* aLayer);
 
   bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); }
 
   virtual bool IsCompositingCheap();
   virtual bool HasShadowManagerInternal() const { return HasShadowManager(); }
 
   virtual void SetIsFirstPaint() MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -244,16 +244,28 @@ CompositorParent::RecvMakeSnapshot(const
 {
   AutoOpenSurface opener(OPEN_READ_WRITE, aInSnapshot);
   nsRefPtr<gfxContext> target = new gfxContext(opener.Get());
   ComposeToTarget(target);
   *aOutSnapshot = aInSnapshot;
   return true;
 }
 
+bool
+CompositorParent::RecvFlushRendering()
+{
+  // If we're waiting to do a composite, then cancel it
+  // and do it immediately instead.
+  if (mCurrentCompositeTask) {
+    mCurrentCompositeTask->Cancel();
+    ComposeToTarget(nullptr);
+  }
+  return true;
+}
+
 void
 CompositorParent::ActorDestroy(ActorDestroyReason why)
 {
   mPaused = true;
   RemoveCompositor(mCompositorID);
 
   if (mLayerManager) {
     mLayerManager->Destroy();
@@ -738,16 +750,17 @@ public:
   // FIXME/bug 774388: work out what shutdown protocol we need.
   virtual bool RecvWillStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvPause() MOZ_OVERRIDE { return true; }
   virtual bool RecvResume() MOZ_OVERRIDE { return true; }
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot)
   { return true; }
+  virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
 
   virtual PLayerTransactionParent*
     AllocPLayerTransaction(const LayersBackend& aBackendType,
                            const uint64_t& aId,
                            TextureFactoryIdentifier* aTextureFactoryIdentifier) MOZ_OVERRIDE;
 
   virtual bool DeallocPLayerTransaction(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -50,16 +50,17 @@ public:
   virtual ~CompositorParent();
 
   virtual bool RecvWillStop() MOZ_OVERRIDE;
   virtual bool RecvStop() MOZ_OVERRIDE;
   virtual bool RecvPause() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot);
+  virtual bool RecvFlushRendering() MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
   /**
    * This forces the is-first-paint flag to true. This is intended to
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -48,14 +48,18 @@ parent:
   // or format of |inSnapshot| doesn't match our render target,
   // results are undefined.
   //
   // NB: this message will result in animations, transforms, effects,
   // and so forth being interpolated.  That's what we want to happen.
   sync MakeSnapshot(SurfaceDescriptor inSnapshot)
     returns (SurfaceDescriptor outSnapshot);
 
+  // Make sure any pending composites are started immediately and
+  // block until they are completed.
+  sync FlushRendering();
+
   sync PLayerTransaction(LayersBackend layersBackendHint, uint64_t id)
     returns (TextureFactoryIdentifier textureFactoryIdentifier);
 };
 
 } // layers
 } // mozilla
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -176,28 +176,28 @@ struct JS_PUBLIC_API(NullPtr)
  */
 template <typename T>
 class Heap : public js::HeapBase<T>
 {
   public:
     Heap() {
         MOZ_STATIC_ASSERT(sizeof(T) == sizeof(Heap<T>),
                           "Heap<T> must be binary compatible with T.");
-        set(js::RootMethods<T>::initial());
+        init(js::RootMethods<T>::initial());
     }
-    explicit Heap(T p) { set(p); }
-    explicit Heap(const Heap<T> &p) { set(p.ptr); }
+    explicit Heap(T p) { init(p); }
+    explicit Heap(const Heap<T> &p) { init(p.ptr); }
 
     ~Heap() {
         if (js::RootMethods<T>::needsPostBarrier(ptr))
             relocate();
     }
 
-    bool operator!=(const T &other) { return *ptr != other; }
-    bool operator==(const T &other) { return *ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
     operator T() const { return ptr; }
     T operator->() const { return ptr; }
     const T *address() const { return &ptr; }
     const T &get() const { return ptr; }
 
     T *unsafeGet() { return &ptr; }
 
@@ -215,16 +215,23 @@ class Heap : public js::HeapBase<T>
             relocate();  /* Called before overwriting ptr. */
             ptr = newPtr;
         } else {
             ptr = newPtr;
         }
     }
 
   private:
+    void init(T newPtr) {
+        JS_ASSERT(!js::RootMethods<T>::poisoned(newPtr));
+        ptr = newPtr;
+        if (js::RootMethods<T>::needsPostBarrier(ptr))
+            post();
+    }
+
     void post() {
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(js::RootMethods<T>::needsPostBarrier(ptr));
         js::RootMethods<T>::postBarrier(&ptr);
 #endif
     }
 
     void relocate() {
@@ -315,18 +322,18 @@ class MOZ_NONHEAP_CLASS Handle : public 
 
     /*
      * Return a reference so passing a Handle<T> to something that
      * takes a |const T&| is not a GC hazard.
      */
     operator const T&() const { return get(); }
     T operator->() const { return get(); }
 
-    bool operator!=(const T &other) { return *ptr != other; }
-    bool operator==(const T &other) { return *ptr == other; }
+    bool operator!=(const T &other) const { return *ptr != other; }
+    bool operator==(const T &other) const { return *ptr == other; }
 
   private:
     Handle() {}
 
     const T *ptr;
 
     template <typename S>
     void operator=(S v) MOZ_DELETE;
@@ -600,18 +607,18 @@ class MOZ_STACK_CLASS Rooted : public js
         return ptr;
     }
 
     void set(T value) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(value));
         ptr = value;
     }
 
-    bool operator!=(const T &other) { return ptr != other; }
-    bool operator==(const T &other) { return ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
   private:
     void commonInit(Rooted<void*> **thingGCRooters) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         js::ThingRootKind kind = js::RootMethods<T>::kind();
         this->stack = &thingGCRooters[kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
@@ -761,18 +768,18 @@ class FakeRooted : public RootedBase<T>
     const T &get() const { return ptr; }
 
     T &operator=(T value) {
         JS_ASSERT(!RootMethods<T>::poisoned(value));
         ptr = value;
         return ptr;
     }
 
-    bool operator!=(const T &other) { return ptr != other; }
-    bool operator==(const T &other) { return ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
   private:
     T ptr;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     FakeRooted(const FakeRooted &) MOZ_DELETE;
 };
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1415,28 +1415,31 @@ template <> struct RootMethods<JS::Value
     static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
     static bool needsPostBarrier(const JS::Value &v) { return v.isMarkable(); }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); }
     static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); }
 #endif
 };
 
+template <class Outer> class UnbarrieredMutableValueOperations;
 template <class Outer> class MutableValueOperations;
 
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
  * ValueOperations<Outer> with a visible extract() method returning the
  * const Value* abstracted by Outer.
  */
 template <class Outer>
 class ValueOperations
 {
+    friend class UnbarrieredMutableValueOperations<Outer>;
     friend class MutableValueOperations<Outer>;
+
     const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     bool isUndefined() const { return value()->isUndefined(); }
     bool isNull() const { return value()->isNull(); }
     bool isBoolean() const { return value()->isBoolean(); }
     bool isTrue() const { return value()->isTrue(); }
     bool isFalse() const { return value()->isFalse(); }
@@ -1464,51 +1467,92 @@ class ValueOperations
     void *toGCThing() const { return value()->toGCThing(); }
 
     JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); }
 
     JSWhyMagic whyMagic() const { return value()->whyMagic(); }
 };
 
 /*
- * A class designed for CRTP use in implementing the mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations<Outer> with visible extractMutable() and extract()
- * methods returning the const Value* and Value* abstracted by Outer.
+ * A class designed for CRTP use in implementing the mutating parts of the Value
+ * interface in Value-like classes that don't need post barriers.  Outer must be
+ * a class inheriting UnbarrieredMutableValueOperations<Outer> with visible
+ * extractMutable() and extract() methods returning the const Value* and Value*
+ * abstracted by Outer.
  */
 template <class Outer>
-class MutableValueOperations : public ValueOperations<Outer>
+class UnbarrieredMutableValueOperations : public ValueOperations<Outer>
 {
+    friend class MutableValueOperations<Outer>;
     JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
     void setNull() { value()->setNull(); }
     void setUndefined() { value()->setUndefined(); }
     void setInt32(int32_t i) { value()->setInt32(i); }
     void setDouble(double d) { value()->setDouble(d); }
-    void setString(JSString *str) { value()->setString(str); }
-    void setString(const JS::Anchor<JSString *> &str) { value()->setString(str); }
-    void setObject(JSObject &obj) { value()->setObject(obj); }
     void setBoolean(bool b) { value()->setBoolean(b); }
     void setMagic(JSWhyMagic why) { value()->setMagic(why); }
     bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
     bool setNumber(double d) { return value()->setNumber(d); }
-    void setObjectOrNull(JSObject *arg) { value()->setObjectOrNull(arg); }
+};
+
+/*
+ * A class designed for CRTP use in implementing all the mutating parts of the
+ * Value interface in Value-like classes.  Outer must be a class inheriting
+ * MutableValueOperations<Outer> with visible extractMutable() and extract()
+ * methods returning the const Value* and Value* abstracted by Outer.
+ */
+template <class Outer>
+class MutableValueOperations : public UnbarrieredMutableValueOperations<Outer>
+{
+  public:
+    void setString(JSString *str) { this->value()->setString(str); }
+    void setString(const JS::Anchor<JSString *> &str) { this->value()->setString(str); }
+    void setObject(JSObject &obj) { this->value()->setObject(obj); }
+    void setObjectOrNull(JSObject *arg) { this->value()->setObjectOrNull(arg); }
 };
 
 /*
- * Augment the generic Heap<T> interface when T = Value with type-querying
- * and value-extracting operations.
+ * Augment the generic Heap<T> interface when T = Value with
+ * type-querying, value-extracting, and mutating operations.
  */
 template <>
-class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
+class HeapBase<JS::Value> : public UnbarrieredMutableValueOperations<JS::Heap<JS::Value> >
 {
-    friend class ValueOperations<JS::Heap<JS::Value> >;
-    const JS::Value * extract() const {
-        return static_cast<const JS::Heap<JS::Value>*>(this)->address();
+    typedef JS::Heap<JS::Value> Outer;
+
+    friend class ValueOperations<Outer>;
+    friend class UnbarrieredMutableValueOperations<Outer>;
+
+    const JS::Value * extract() const { return static_cast<const Outer*>(this)->address(); }
+    JS::Value * extractMutable() { return static_cast<Outer*>(this)->unsafeGet(); }
+
+    /*
+     * Setters that potentially change the value to a GC thing from a non-GC
+     * thing must call JS::Heap::set() to trigger the post barrier.
+     *
+     * Changing from a GC thing to a non-GC thing value will leave the heap
+     * value in the store buffer, but it will be ingored so this is not a
+     * problem.
+     */
+    void setBarriered(const JS::Value &v) {
+        static_cast<JS::Heap<JS::Value> *>(this)->set(v);
+    }
+
+  public:
+    void setString(JSString *str) { setBarriered(JS::StringValue(str)); }
+    void setString(const JS::Anchor<JSString *> &str) { setBarriered(JS::StringValue(str.get())); }
+    void setObject(JSObject &obj) { setBarriered(JS::ObjectValue(obj)); }
+
+    void setObjectOrNull(JSObject *arg) {
+        if (arg)
+            setObject(*arg);
+        else
+            setNull();
     }
 };
 
 /*
  * Augment the generic Handle<T> interface when T = Value with type-querying
  * and value-extracting operations.
  */
 template <>
@@ -1527,16 +1571,17 @@ class HandleBase<JS::Value> : public Val
 template <>
 class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
 {
     friend class ValueOperations<JS::MutableHandle<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const JS::MutableHandle<JS::Value>*>(this)->address();
     }
 
+    friend class UnbarrieredMutableValueOperations<JS::MutableHandle<JS::Value> >;
     friend class MutableValueOperations<JS::MutableHandle<JS::Value> >;
     JS::Value * extractMutable() {
         return static_cast<JS::MutableHandle<JS::Value>*>(this)->address();
     }
 };
 
 /*
  * Augment the generic Rooted<T> interface when T = Value with type-querying,
@@ -1545,16 +1590,17 @@ class MutableHandleBase<JS::Value> : pub
 template <>
 class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
 {
     friend class ValueOperations<JS::Rooted<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const JS::Rooted<JS::Value>*>(this)->address();
     }
 
+    friend class UnbarrieredMutableValueOperations<JS::Rooted<JS::Value> >;
     friend class MutableValueOperations<JS::Rooted<JS::Value> >;
     JS::Value * extractMutable() {
         return static_cast<JS::Rooted<JS::Value>*>(this)->address();
     }
 };
 
 } // namespace js
 
--- a/js/src/build/autoconf/android.m4
+++ b/js/src/build/autoconf/android.m4
@@ -300,25 +300,40 @@ case "$target" in
             AC_MSG_ERROR([The given Android SDK provides API level $android_api_level ($1 or higher required).])
         fi
     fi
 
     android_platform_tools="$android_sdk"/../../platform-tools
     if test ! -d "$android_platform_tools" ; then
         android_platform_tools="$android_sdk"/tools # SDK Tools < r8
     fi
+    # The build tools got moved around to different directories in
+    # SDK Tools r22.  Try to locate them.
+    android_build_tools=""
+    for suffix in 17.0.0 android-4.2.2; do
+        tools_directory="$android_sdk/../../build-tools/$suffix"
+        if test -d "$tools_directory" ; then
+            android_build_tools="$tools_directory"
+            break
+        fi
+    done
+    if test -z "$android_build_tools" ; then
+        android_build_tools="$android_platform_tools" # SDK Tools < r22
+    fi
     ANDROID_SDK="${android_sdk}"
     if test -e "${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar" ; then
         ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/compatibility/v4/android-support-v4.jar"
     else
         ANDROID_COMPAT_LIB="${android_sdk}/../../extras/android/support/v4/android-support-v4.jar";
     fi
     ANDROID_PLATFORM_TOOLS="${android_platform_tools}"
+    ANDROID_BUILD_TOOLS="${android_build_tools}"
     AC_SUBST(ANDROID_SDK)
     AC_SUBST(ANDROID_COMPAT_LIB)
     if ! test -e $ANDROID_COMPAT_LIB ; then
         AC_MSG_ERROR([You must download the Android support library when targeting Android.   Run the Android SDK tool and install Android Support Library under Extras.  See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_COMPAT_LIB)])
     fi
     AC_SUBST(ANDROID_PLATFORM_TOOLS)
+    AC_SUBST(ANDROID_BUILD_TOOLS)
     ;;
 esac
 
 ])
--- a/js/src/config/Makefile.in
+++ b/js/src/config/Makefile.in
@@ -11,17 +11,16 @@ VPATH		= @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 # For sanity's sake, we compile nsinstall without the wrapped system
 # headers, so that we can use it to set up the wrapped system headers.
 VISIBILITY_FLAGS =
 
 ifneq (WINNT,$(HOST_OS_ARCH))
 HOST_PROGRAM	= nsinstall_real$(HOST_BIN_SUFFIX)
-DISABLED_HOST_CSRCS	= nsinstall.c pathsub.c
 endif
 
 # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have
 # a recursive rule for finding nsinstall and the Perl scripts.
 ifdef NSBUILDROOT
 override NSBUILDROOT :=
 endif
 
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -11,16 +11,17 @@ ifndef topsrcdir
 endif
 
 # Integrate with mozbuild-generated make files. We first verify that no
 # variables provided by the automatically generated .mk files are
 # present. If they are, this is a violation of the separation of
 # responsibility between Makefile.in and mozbuild files.
 _MOZBUILD_EXTERNAL_VARIABLES := \
   DIRS \
+  HOST_CSRCS \
   MODULE \
   PARALLEL_DIRS \
   TEST_DIRS \
   TIERS \
   TOOL_DIRS \
   XPIDL_MODULE \
   $(NULL)
 
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -2544,19 +2544,20 @@ DoBinaryArithFallback(JSContext *cx, Bas
             stub->addNewStub(doubleStub);
             return true;
           }
           default:
             break;
         }
     }
 
-    // TODO: unlink previous !allowDouble stub.
     if (lhs.isInt32() && rhs.isInt32()) {
         bool allowDouble = ret.isDouble();
+        if (allowDouble)
+            stub->unlinkStubsWithKind(cx, ICStub::BinaryArith_Int32);
         IonSpew(IonSpew_BaselineIC, "  Generating %s(Int32, Int32%s) stub", js_CodeName[op],
                 allowDouble ? " => Double" : "");
         ICBinaryArith_Int32::Compiler compilerInt32(cx, op, allowDouble);
         ICStub *int32Stub = compilerInt32.getStub(compilerInt32.getStubSpace(script));
         if (!int32Stub)
             return false;
         stub->addNewStub(int32Stub);
         return true;
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -2400,24 +2400,30 @@ class ICBinaryArith_Fallback : public IC
         }
     };
 };
 
 class ICBinaryArith_Int32 : public ICStub
 {
     friend class ICStubSpace;
 
-    ICBinaryArith_Int32(IonCode *stubCode)
-      : ICStub(BinaryArith_Int32, stubCode) {}
+    ICBinaryArith_Int32(IonCode *stubCode, bool allowDouble)
+      : ICStub(BinaryArith_Int32, stubCode)
+    {
+        extra_ = allowDouble;
+    }
 
   public:
-    static inline ICBinaryArith_Int32 *New(ICStubSpace *space, IonCode *code) {
+    static inline ICBinaryArith_Int32 *New(ICStubSpace *space, IonCode *code, bool allowDouble) {
         if (!code)
             return NULL;
-        return space->allocate<ICBinaryArith_Int32>(code);
+        return space->allocate<ICBinaryArith_Int32>(code, allowDouble);
+    }
+    bool allowDouble() const {
+        return extra_;
     }
 
     // Compiler for this stub kind.
     class Compiler : public ICStubCompiler {
       protected:
         JSOp op_;
         bool allowDouble_;
 
@@ -2430,17 +2436,17 @@ class ICBinaryArith_Int32 : public ICStu
         }
 
       public:
         Compiler(JSContext *cx, JSOp op, bool allowDouble)
           : ICStubCompiler(cx, ICStub::BinaryArith_Int32),
             op_(op), allowDouble_(allowDouble) {}
 
         ICStub *getStub(ICStubSpace *space) {
-            return ICBinaryArith_Int32::New(space, getStubCode());
+            return ICBinaryArith_Int32::New(space, getStubCode(), allowDouble_);
         }
     };
 };
 
 class ICBinaryArith_StringConcat : public ICStub
 {
     friend class ICStubSpace;
 
--- a/js/src/ion/BaselineInspector.cpp
+++ b/js/src/ion/BaselineInspector.cpp
@@ -155,16 +155,19 @@ BaselineInspector::expectedResultType(js
     // returning MIRType_None otherwise.
 
     ICStub *stub = monomorphicStub(pc);
     if (!stub)
         return MIRType_None;
 
     switch (stub->kind()) {
       case ICStub::BinaryArith_Int32:
+        if (stub->toBinaryArith_Int32()->allowDouble())
+            return MIRType_Double;
+        return MIRType_Int32;
       case ICStub::BinaryArith_BooleanWithInt32:
       case ICStub::UnaryArith_Int32:
         return MIRType_Int32;
       case ICStub::BinaryArith_Double:
       case ICStub::BinaryArith_DoubleWithInt32:
       case ICStub::UnaryArith_Double:
         return MIRType_Double;
       case ICStub::BinaryArith_StringConcat:
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1134,32 +1134,32 @@ CodeGenerator::visitParDump(LParDump *li
     masm.freeStack(sizeof(Value));
     return true;
 }
 
 bool
 CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
-    Register scratch = ToRegister(lir->temp());
+    Register scratch = ToTempUnboxRegister(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
 }
 
 bool
 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
 {
     ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
-    Register scratch = ToRegister(lir->temp());
+    Register scratch = ToTempUnboxRegister(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
@@ -1283,17 +1283,17 @@ CodeGenerator::visitPostWriteBarrierV(LP
     } else {
         Label tenured;
         Register objreg = ToRegister(lir->object());
         masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured);
         masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin());
         masm.bind(&tenured);
     }
 
-    Register valuereg = masm.extractObject(value, ToRegister(lir->temp()));
+    Register valuereg = masm.extractObject(value, ToTempUnboxRegister(lir->temp()));
     masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin());
     masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
@@ -3539,26 +3539,27 @@ bool
 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
 {
     JSOp op = lir->mir()->jsop();
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
-    Register temp = ToRegister(lir->temp0());
+    Register temp = ToRegister(lir->temp());
+    Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
 
     Label string, done;
 
     masm.branchTestString(Assembler::Equal, leftV, &string);
     masm.move32(Imm32(op == JSOP_STRICTNE), output);
     masm.jump(&done);
 
     masm.bind(&string);
-    Register left = masm.extractString(leftV, ToRegister(lir->temp1()));
+    Register left = masm.extractString(leftV, tempToUnbox);
     if (!emitCompareS(lir, op, left, right, output, temp))
         return false;
 
     masm.bind(&done);
 
     return true;
 }
 
@@ -3705,19 +3706,19 @@ CodeGenerator::visitIsNullOrLikeUndefine
         masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
         masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
 
         if (ool) {
             // Check whether it's a truthy object or a falsy object that emulates
             // undefined.
             masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
 
-            Register objreg = masm.extractObject(value, ToRegister(lir->temp0()));
+            Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
             testObjectTruthy(objreg, notNullOrLikeUndefined, nullOrLikeUndefined,
-                             ToRegister(lir->temp1()), ool);
+                             ToRegister(lir->temp()), ool);
         }
 
         Label done;
 
         // It's not null or undefined, and if it's an object it doesn't
         // emulate undefined, so it's not like undefined.
         masm.bind(notNullOrLikeUndefined);
         masm.move32(Imm32(op == JSOP_NE), output);
@@ -3784,18 +3785,18 @@ CodeGenerator::visitIsNullOrLikeUndefine
 
         masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
         masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
 
         if (ool) {
             masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
 
             // Objects that emulate undefined are loosely equal to null/undefined.
-            Register objreg = masm.extractObject(value, ToRegister(lir->temp0()));
-            testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp1()), ool);
+            Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
+            testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp()), ool);
         } else {
             masm.jump(ifFalseLabel);
         }
         return true;
     }
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -3259,17 +3259,17 @@ IonBuilder::jsop_bitop(JSOp op)
         break;
 
       default:
         JS_NOT_REACHED("unexpected bitop");
         return false;
     }
 
     current->add(ins);
-    ins->infer();
+    ins->infer(inspector, pc);
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
 
     return true;
 }
 
@@ -6464,17 +6464,17 @@ IonBuilder::convertShiftToMaskForStaticT
     if (!value.isInt32() || uint32_t(value.toInt32()) != TypedArrayShift(viewType))
         return NULL;
 
     // Instead of shifting, mask off the low bits of the index so that
     // a non-scaled access on the typed array can be performed.
     MConstant *mask = MConstant::New(Int32Value(~((1 << value.toInt32()) - 1)));
     MBitAnd *ptr = MBitAnd::New(id->getOperand(0), mask);
 
-    ptr->infer();
+    ptr->infer(NULL, NULL);
     JS_ASSERT(!ptr->isEffectful());
 
     current->add(mask);
     current->add(ptr);
 
     return ptr;
 }
 
@@ -6602,25 +6602,51 @@ IonBuilder::jsop_getelem_typed(int array
         current->add(load);
         current->push(load);
 
         // Note: we can ignore the type barrier here, we know the type must
         // be valid and unbarriered.
         load->setResultType(knownType);
         return true;
     } else {
+        // We need a type barrier if the array's element type has never been
+        // observed (we've only read out-of-bounds values). Note that for
+        // Uint32Array, we only check for int32: if allowDouble is false we
+        // will bailout when we read a double.
+        bool needsBarrier = true;
+        switch (arrayType) {
+          case TypedArray::TYPE_INT8:
+          case TypedArray::TYPE_UINT8:
+          case TypedArray::TYPE_UINT8_CLAMPED:
+          case TypedArray::TYPE_INT16:
+          case TypedArray::TYPE_UINT16:
+          case TypedArray::TYPE_INT32:
+          case TypedArray::TYPE_UINT32:
+            if (types->hasType(types::Type::Int32Type()))
+                needsBarrier = false;
+            break;
+          case TypedArray::TYPE_FLOAT32:
+          case TypedArray::TYPE_FLOAT64:
+            if (allowDouble)
+                needsBarrier = false;
+            break;
+          default:
+            JS_NOT_REACHED("Unknown typed array type");
+            return false;
+        }
+
         // Assume we will read out-of-bound values. In this case the
         // bounds check will be part of the instruction, and the instruction
         // will always return a Value.
         MLoadTypedArrayElementHole *load =
             MLoadTypedArrayElementHole::New(obj, id, arrayType, allowDouble);
         current->add(load);
         current->push(load);
 
-        return resumeAfter(load) && pushTypeBarrier(load, types, false);
+        return resumeAfter(load) && pushTypeBarrier(load, types, needsBarrier);
     }
 }
 
 bool
 IonBuilder::jsop_getelem_string()
 {
     MDefinition *id = current->pop();
     MDefinition *str = current->pop();
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -78,16 +78,17 @@ MacroAssembler::guardTypeSet(const Sourc
     if (types->hasType(types::Type::NullType()))
         branchTestNull(Equal, tag, matched);
     if (types->hasType(types::Type::MagicArgType()))
         branchTestMagic(Equal, tag, matched);
 
     if (types->hasType(types::Type::AnyObjectType())) {
         branchTestObject(Equal, tag, matched);
     } else if (types->getObjectCount()) {
+        JS_ASSERT(scratch != InvalidReg);
         branchTestObject(NotEqual, tag, miss);
         Register obj = extractObject(address, scratch);
 
         unsigned count = types->getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             if (JSObject *object = types->getSingleObject(i))
                 branchPtr(Equal, obj, ImmGCPtr(object), matched);
         }
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1556,20 +1556,20 @@ class LCompareStrictS : public LInstruct
         setTemp(1, temp1);
     }
 
     static const size_t Lhs = 0;
 
     const LAllocation *right() {
         return getOperand(BOX_PIECES);
     }
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
 // Used for strict-equality comparisons where one side is a boolean
@@ -1675,65 +1675,65 @@ class LCompareVM : public LCallInstructi
     }
 };
 
 class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefined)
 
-    LIsNullOrLikeUndefined(const LDefinition &temp0, const LDefinition &temp1)
+    LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox)
     {
-        setTemp(0, temp0);
-        setTemp(1, temp1);
+        setTemp(0, temp);
+        setTemp(1, tempToUnbox);
     }
 
     static const size_t Value = 0;
 
     MCompare *mir() {
         return mir_->toCompare();
     }
 
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
 
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
 class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefinedAndBranch)
 
-    LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp0, const LDefinition &temp1)
+    LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp, const LDefinition &tempToUnbox)
     {
         setSuccessor(0, ifTrue);
         setSuccessor(1, ifFalse);
-        setTemp(0, temp0);
-        setTemp(1, temp1);
+        setTemp(0, temp);
+        setTemp(1, tempToUnbox);
     }
 
     static const size_t Value = 0;
 
     MBasicBlock *ifTrue() const {
         return getSuccessor(0);
     }
     MBasicBlock *ifFalse() const {
         return getSuccessor(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
 // Takes an object and tests whether it emulates |undefined|, as determined by
 // the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects.  See also
 // js::EmulatesUndefined.
 class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -601,27 +601,27 @@ LIRGenerator::visitTest(MTest *test)
                 MOZ_ASSERT(comp->operandMightEmulateUndefined(),
                            "MCompare::tryFold should handle the never-emulates-undefined case");
 
                 LEmulatesUndefinedAndBranch *lir =
                     new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp());
                 return add(lir, comp);
             }
 
-            LDefinition temp0, temp1;
+            LDefinition tmp, tmpToUnbox;
             if (comp->operandMightEmulateUndefined()) {
-                temp0 = temp();
-                temp1 = temp();
+                tmp = temp();
+                tmpToUnbox = tempToUnbox();
             } else {
-                temp0 = LDefinition::BogusTemp();
-                temp1 = LDefinition::BogusTemp();
+                tmp = LDefinition::BogusTemp();
+                tmpToUnbox = LDefinition::BogusTemp();
             }
 
             LIsNullOrLikeUndefinedAndBranch *lir =
-                new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, temp0, temp1);
+                new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, tmp, tmpToUnbox);
             if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left))
                 return false;
             return add(lir, comp);
         }
 
         // Compare and branch booleans.
         if (comp->compareType() == MCompare::Compare_Boolean) {
             JS_ASSERT(left->type() == MIRType_Value);
@@ -744,17 +744,17 @@ LIRGenerator::visitCompare(MCompare *com
         return assignSafepoint(lir, comp);
     }
 
     // Strict compare between value and string
     if (comp->compareType() == MCompare::Compare_StrictString) {
         JS_ASSERT(left->type() == MIRType_Value);
         JS_ASSERT(right->type() == MIRType_String);
 
-        LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp());
+        LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), tempToUnbox());
         if (!useBox(lir, LCompareStrictS::Lhs, left))
             return false;
         if (!define(lir, comp))
             return false;
         return assignSafepoint(lir, comp);
     }
 
     // Unknown/unspecialized compare use a VM call.
@@ -780,26 +780,26 @@ LIRGenerator::visitCompare(MCompare *com
     {
         if (left->type() == MIRType_Object) {
             MOZ_ASSERT(comp->operandMightEmulateUndefined(),
                        "MCompare::tryFold should have folded this away");
 
             return define(new LEmulatesUndefined(useRegister(left)), comp);
         }
 
-        LDefinition temp0, temp1;
+        LDefinition tmp, tmpToUnbox;
         if (comp->operandMightEmulateUndefined()) {
-            temp0 = temp();
-            temp1 = temp();
+            tmp = temp();
+            tmpToUnbox = tempToUnbox();
         } else {
-            temp0 = LDefinition::BogusTemp();
-            temp1 = LDefinition::BogusTemp();
+            tmp = LDefinition::BogusTemp();
+            tmpToUnbox = LDefinition::BogusTemp();
         }
 
-        LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(temp0, temp1);
+        LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(tmp, tmpToUnbox);
         if (!useBox(lir, LIsNullOrLikeUndefined::Value, left))
             return false;
         return define(lir, comp);
     }
 
     // Compare booleans.
     if (comp->compareType() == MCompare::Compare_Boolean) {
         JS_ASSERT(left->type() == MIRType_Value);
@@ -1727,30 +1727,40 @@ LIRGenerator::visitStoreSlot(MStoreSlot 
     return true;
 }
 
 bool
 LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type barrier test.
-    LTypeBarrier *barrier = new LTypeBarrier(temp());
+
+    const types::StackTypeSet *types = ins->resultTypeSet();
+    bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
+    LDefinition tmp = needTemp ? temp() : tempToUnbox();
+
+    LTypeBarrier *barrier = new LTypeBarrier(tmp);
     if (!useBox(barrier, LTypeBarrier::Input, ins->input()))
         return false;
     if (!assignSnapshot(barrier, ins->bailoutKind()))
         return false;
     return redefine(ins, ins->input()) && add(barrier, ins);
 }
 
 bool
 LIRGenerator::visitMonitorTypes(MMonitorTypes *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type check.
-    LMonitorTypes *lir = new LMonitorTypes(temp());
+
+    const types::StackTypeSet *types = ins->typeSet();
+    bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
+    LDefinition tmp = needTemp ? temp() : tempToUnbox();
+
+    LMonitorTypes *lir = new LMonitorTypes(tmp);
     if (!useBox(lir, LMonitorTypes::Input, ins->input()))
         return false;
     return assignSnapshot(lir, Bailout_Normal) && add(lir, ins);
 }
 
 bool
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
 {
@@ -1758,17 +1768,17 @@ LIRGenerator::visitPostWriteBarrier(MPos
     switch (ins->value()->type()) {
       case MIRType_Object: {
         LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
                                                          useRegister(ins->value()));
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       case MIRType_Value: {
         LPostWriteBarrierV *lir =
-            new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp());
+            new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox());
         if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
             return false;
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       default:
         // Currently, only objects can be in the nursery. Other instruction
         // types cannot hold nursery pointers.
         return true;
--- a/js/src/ion/MIR.cpp
+++ b/js/src/ion/MIR.cpp
@@ -914,17 +914,17 @@ MBinaryBitwiseInstruction::foldUnnecessa
 
     if (EqualValues(false, lhs, rhs))
         return foldIfEqual();
 
     return this;
 }
 
 void
-MBinaryBitwiseInstruction::infer()
+MBinaryBitwiseInstruction::infer(BaselineInspector *, jsbytecode *)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) {
         specialization_ = MIRType_None;
     } else {
         specialization_ = MIRType_Int32;
         setCommutative();
     }
 }
@@ -933,40 +933,41 @@ void
 MBinaryBitwiseInstruction::specializeForAsmJS()
 {
     specialization_ = MIRType_Int32;
     JS_ASSERT(type() == MIRType_Int32);
     setCommutative();
 }
 
 void
-MShiftInstruction::infer()
+MShiftInstruction::infer(BaselineInspector *, jsbytecode *)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object))
         specialization_ = MIRType_None;
     else
         specialization_ = MIRType_Int32;
 }
 
 void
-MUrsh::infer()
+MUrsh::infer(BaselineInspector *inspector, jsbytecode *pc)
 {
     if (getOperand(0)->mightBeType(MIRType_Object) || getOperand(1)->mightBeType(MIRType_Object)) {
         specialization_ = MIRType_None;
         setResultType(MIRType_Value);
         return;
     }
 
-    if (type() == MIRType_Int32) {
-        specialization_ = MIRType_Int32;
+    if (inspector->expectedResultType(pc) == MIRType_Double) {
+        specialization_ = MIRType_Double;
+        setResultType(MIRType_Double);
         return;
     }
 
-    specialization_ = MIRType_Double;
-    setResultType(MIRType_Double);
+    specialization_ = MIRType_Int32;
+    setResultType(MIRType_Int32);
 }
 
 static inline bool
 NeedNegativeZeroCheck(MDefinition *def)
 {
     // Test if all uses have the same semantics for -0 and 0
     for (MUseIterator use = def->usesBegin(); use != def->usesEnd(); use++) {
         if (use->consumer()->isResumePoint())
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -2711,17 +2711,17 @@ class MBinaryBitwiseInstruction
         return this;
     }
 
     MDefinition *foldsTo(bool useValueNumbers);
     MDefinition *foldUnnecessaryBitop();
     virtual MDefinition *foldIfZero(size_t operand) = 0;
     virtual MDefinition *foldIfNegOne(size_t operand) = 0;
     virtual MDefinition *foldIfEqual()  = 0;
-    virtual void infer();
+    virtual void infer(BaselineInspector *inspector, jsbytecode *pc);
 
     bool congruentTo(MDefinition *const &ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         if (specialization_ >= MIRType_Object)
             return AliasSet::Store(AliasSet::Any);
         return AliasSet::None();
@@ -2807,17 +2807,17 @@ class MShiftInstruction
 
   public:
     MDefinition *foldIfNegOne(size_t operand) {
         return this;
     }
     MDefinition *foldIfEqual() {
         return this;
     }
-    virtual void infer();
+    virtual void infer(BaselineInspector *inspector, jsbytecode *pc);
 };
 
 class MLsh : public MShiftInstruction
 {
     MLsh(MDefinition *left, MDefinition *right)
       : MShiftInstruction(left, right)
     { }
 
@@ -2871,17 +2871,17 @@ class MUrsh : public MShiftInstruction
     MDefinition *foldIfZero(size_t operand) {
         // 0 >>> x => 0
         if (operand == 0)
             return getOperand(0);
 
         return this;
     }
 
-    void infer();
+    void infer(BaselineInspector *inspector, jsbytecode *pc);
 
     bool canOverflow() {
         // solution is only negative when lhs < 0 and rhs & 0x1f == 0
         MDefinition *lhs = getOperand(0);
         MDefinition *rhs = getOperand(1);
 
         if (lhs->isConstant()) {
             Value lhsv = lhs->toConstant()->value();
--- a/js/src/ion/arm/Lowering-arm.h
+++ b/js/src/ion/arm/Lowering-arm.h
@@ -21,16 +21,20 @@ class LIRGeneratorARM : public LIRGenera
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
+    inline LDefinition tempToUnbox() {
+        return LDefinition::BogusTemp();
+    }
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                        MDefinition *rhs);
     bool lowerUrshD(MUrsh *mir);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
                      MDefinition *input);
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h
+++ b/js/src/ion/shared/CodeGenerator-shared-inl.h
@@ -41,16 +41,24 @@ ToRegister(const LAllocation *a)
 
 static inline Register
 ToRegister(const LDefinition *def)
 {
     return ToRegister(*def->output());
 }
 
 static inline Register
+ToTempUnboxRegister(const LDefinition *def)
+{
+    if (def->isBogusTemp())
+        return InvalidReg;
+    return ToRegister(def);
+}
+
+static inline Register
 ToRegisterOrInvalid(const LAllocation *a)
 {
     return a ? ToRegister(*a) : InvalidReg;
 }
 
 static inline FloatRegister
 ToFloatRegister(const LAllocation &a)
 {
--- a/js/src/ion/x64/Lowering-x64.h
+++ b/js/src/ion/x64/Lowering-x64.h
@@ -23,16 +23,20 @@ class LIRGeneratorX64 : public LIRGenera
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     // Adds a use at operand |n| of a value-typed insturction.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
 
+    inline LDefinition tempToUnbox() {
+        return temp();
+    }
+
     LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
--- a/js/src/ion/x86/Lowering-x86.h
+++ b/js/src/ion/x86/Lowering-x86.h
@@ -21,16 +21,20 @@ class LIRGeneratorX86 : public LIRGenera
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
+    inline LDefinition tempToUnbox() {
+        return LDefinition::BogusTemp();
+    }
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug882323.js
@@ -0,0 +1,23 @@
+var ints = new Int8Array(16);
+ints[0] = 42;
+function intElementAt(index) {
+    return ints[index];
+}
+assertEq(intElementAt(16), undefined);
+assertEq(intElementAt(0), 42);
+
+var floats = new Float64Array(16);
+floats[0] = 3.14;
+function floatElementAt(index) {
+    return floats[index];
+}
+assertEq(floatElementAt(16), undefined);
+assertEq(floatElementAt(0), 3.14);
+
+var uints = new Uint32Array(16);
+uints[0] = 123;
+function uintElementAt(index) {
+    return uints[index];
+}
+assertEq(uintElementAt(16), undefined);
+assertEq(uintElementAt(0), 123);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -26,16 +26,21 @@
     ((uintptr_t)(sp) < (limit)+(tolerance))
 #else
 # define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance)  \
     ((uintptr_t)(sp) > (limit)-(tolerance))
 #endif
 
 #define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0)
 
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
 extern JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 extern JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt);
 
 extern JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSObject *obj);
@@ -885,17 +890,17 @@ NukeCrossCompartmentWrappers(JSContext* 
  */
 
 struct ExpandoAndGeneration {
   ExpandoAndGeneration()
     : expando(UndefinedValue()),
       generation(0)
   {}
 
-  Value expando;
+  JS::Heap<JS::Value> expando;
   uint32_t generation;
 };
 
 typedef enum DOMProxyShadowsResult {
   ShadowCheckFailed,
   Shadows,
   DoesntShadow,
   DoesntShadowUnique
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -331,17 +331,17 @@ ContextStack::popInlineFrame(FrameRegs &
 inline JSScript *
 ContextStack::currentScript(jsbytecode **ppc,
                             MaybeAllowCrossCompartment allowCrossCompartment) const
 {
     if (ppc)
         *ppc = NULL;
 
     Activation *act = cx_->mainThread().activation();
-    while (act && act->cx() != cx_)
+    while (act && (act->cx() != cx_ || !act->isActive()))
         act = act->prev();
 
     if (!act)
         return NULL;
 
     JS_ASSERT(act->cx() == cx_);
 
 #ifdef JS_ION
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -498,30 +498,30 @@ void XPCJSRuntime::TraceGrayJS(JSTracer*
     XPCJSRuntime* self = (XPCJSRuntime*)data;
 
     // Mark these roots as gray so the CC can walk them later.
     self->TraceXPConnectRoots(trc);
 }
 
 struct JsGcTracer : public TraceCallbacks
 {
-    virtual void Trace(JS::Value *p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallValueTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapValueTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(jsid *p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallIdTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<jsid> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapIdTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSObject **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallObjectTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapObjectTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSString **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallStringTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSString *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapStringTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSScript **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallScriptTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSScript *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapScriptTracer(static_cast<JSTracer*>(closure), p, name);
     }
 };
 
 static PLDHashOperator
 TraceJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg)
 {
     tracer->Trace(holder, JsGcTracer(), arg);
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -322,17 +322,18 @@ NS_NewResizerFrame (nsIPresShell* aPresS
 
 
 #endif
 
 nsIFrame*
 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
 
 nsIFrame*
-NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
+NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext,
+                      bool aIsRoot, bool aClipAllDescendants);
 
 nsIFrame*
 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 nsIFrame*
 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 nsIFrame*
@@ -4111,18 +4112,21 @@ nsCSSFrameConstructor::BeginBuildingScro
 
   nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
 
   if (!gfxScrollFrame) {
     // Build a XULScrollFrame when the child is a box, otherwise an
     // HTMLScrollFrame
     // XXXbz this is the lone remaining consumer of IsXULDisplayType.
     // I wonder whether we can eliminate that somehow.
-    if (IsXULDisplayType(aContentStyle->StyleDisplay())) {
-      gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot);
+    const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
+    if (IsXULDisplayType(displayStyle)) {
+      gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot,
+          displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK ||
+          displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK);
     } else {
       gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
     }
 
     InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
   }
 
   // if there are any anonymous children for the scroll frame, create
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -593,18 +593,20 @@ void nsDisplayListBuilder::MarkOutOfFlow
   }
 
   if (mHasDisplayPort && IsFixedFrame(aFrame)) {
     dirty = overflowRect;
   }
 
   if (!dirty.IntersectRect(dirty, overflowRect))
     return;
-  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(),
-    new OutOfFlowDisplayData(mClipState.GetClipForContainingBlockDescendants(), dirty));
+  const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants();
+  OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty)
+    : new OutOfFlowDisplayData(dirty);
+  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
 }
 
 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
   nsPresContext* presContext = aFrame->PresContext();
   presContext->PropertyTable()->
     Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
@@ -803,24 +805,30 @@ void
 nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
                                      const nsRect& aDirtyRect) {
   if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) {
     // Must have not allocated a state for this presshell, presumably due
     // to OOM.
     return;
   }
 
+  ResetMarkedFramesForDisplayList();
+  mPresShellStates.SetLength(mPresShellStates.Length() - 1);
+}
+
+void
+nsDisplayListBuilder::ResetMarkedFramesForDisplayList()
+{
   // Unmark and pop off the frames marked for display in this pres shell.
   uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
   for (uint32_t i = firstFrameForShell;
        i < mFramesMarkedForDisplay.Length(); ++i) {
     UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
   }
   mFramesMarkedForDisplay.SetLength(firstFrameForShell);
-  mPresShellStates.SetLength(mPresShellStates.Length() - 1);
 }
 
 void
 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
                                                const nsFrameList& aFrames,
                                                const nsRect& aDirtyRect) {
   for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
     mFramesMarkedForDisplay.AppendElement(e.get());
@@ -1194,28 +1202,31 @@ void nsDisplayList::PaintForFrame(nsDisp
 
   nsIntRegion invalid;
   if (props) {
     invalid = props->ComputeDifferences(root, computeInvalidFunc);
   } else if (widgetTransaction) {
     LayerProperties::ClearInvalidations(root);
   }
 
+  bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
   if (view) {
     if (props) {
       if (!invalid.IsEmpty()) {
         nsIntRect bounds = invalid.GetBounds();
         nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
                     presContext->DevPixelsToAppUnits(bounds.y),
                     presContext->DevPixelsToAppUnits(bounds.width),
                     presContext->DevPixelsToAppUnits(bounds.height));
-        view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
+        if (shouldInvalidate) {
+          view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
+        }
         presContext->NotifyInvalidation(bounds, 0);
       }
-    } else {
+    } else if (shouldInvalidate) {
       view->GetViewManager()->InvalidateView(view);
     }
   }
 
   if (aFlags & PAINT_FLUSH_LAYERS) {
     FrameLayerBuilder::InvalidateAllLayers(layerManager);
   }
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -330,16 +330,24 @@ public:
   nsCaret* GetCaret();
   /**
    * Notify the display list builder that we're entering a presshell.
    * aReferenceFrame should be a frame in the new presshell and aDirtyRect
    * should be the current dirty rect in aReferenceFrame's coordinate space.
    */
   void EnterPresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
   /**
+   * For print-preview documents, we sometimes need to build display items for
+   * the same frames multiple times in the same presentation, with different
+   * clipping. Between each such batch of items, call
+   * ResetMarkedFramesForDisplayList to make sure that the results of
+   * MarkFramesForDisplayList do not carry over between batches.
+   */
+  void ResetMarkedFramesForDisplayList();
+  /**
    * Notify the display list builder that we're leaving a presshell.
    */
   void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
 
   /**
    * Returns true if we're currently building a display list that's
    * directly or indirectly under an nsDisplayTransform.
    */
@@ -561,22 +569,25 @@ public:
     bool                  mOldValue;
   };
 
   // Helpers for tables
   nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
   void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
 
   struct OutOfFlowDisplayData {
-    OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
+    OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip,
                          const nsRect &aDirtyRect)
       : mContainingBlockClip(aContainingBlockClip)
       , mDirtyRect(aDirtyRect)
     {}
-    const DisplayItemClip* mContainingBlockClip;
+    OutOfFlowDisplayData(const nsRect &aDirtyRect)
+      : mDirtyRect(aDirtyRect)
+    {}
+    DisplayItemClip mContainingBlockClip;
     nsRect mDirtyRect;
   };
   static void DestroyOutOfFlowDisplayData(void* aPropertyValue)
   {
     delete static_cast<OutOfFlowDisplayData*>(aPropertyValue);
   }
 
   NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty, DestroyOutOfFlowDisplayData)
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1536,22 +1536,16 @@ nsDocumentViewer::Destroy()
 
     // Remove our root view from the view hierarchy.
     if (mPresShell) {
       nsViewManager *vm = mPresShell->GetViewManager();
       if (vm) {
         nsView *rootView = vm->GetRootView();
 
         if (rootView) {
-          // The invalidate that removing this view causes is dropped because
-          // the Freeze call above sets painting to be suppressed for our
-          // document. So we do it ourselves and make it happen.
-          vm->InvalidateViewNoSuppression(rootView,
-            rootView->GetBounds() - rootView->GetPosition());
-
           nsView *rootViewParent = rootView->GetParent();
           if (rootViewParent) {
             nsViewManager *parentVM = rootViewParent->GetViewManager();
             if (parentVM) {
               parentVM->RemoveChild(rootView);
             }
           }
         }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5484,27 +5484,32 @@ PresShell::Paint(nsView*        aViewToP
   NS_ASSERTION(aViewToPaint, "null view");
 
   MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
 
   if (!mIsActive || mIsZombie) {
     return;
   }
 
-  nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
-
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   bool isRetainingManager;
   LayerManager* layerManager =
     aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
   NS_ASSERTION(layerManager, "Must be in paint event");
+  bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
+
+  uint32_t didPaintFlags = aFlags;
+  if (!shouldInvalidate) {
+    didPaintFlags |= PAINT_COMPOSITE;
+  }
+  nsAutoNotifyDidPaint notifyDidPaint(this, didPaintFlags);
 
   // Whether or not we should set first paint when painting is
   // suppressed is debatable. For now we'll do it because
   // B2G relies on first paint to configure the viewport and
   // we only want to do that when we have real content to paint.
   // See Bug 798245
   if (mIsFirstPaint && !mPaintingSuppressed) {
     layerManager->SetIsFirstPaint();
@@ -5514,19 +5519,16 @@ PresShell::Paint(nsView*        aViewToP
   if (frame && isRetainingManager) {
     // Try to do an empty transaction, if the frame tree does not
     // need to be updated. Do not try to do an empty transaction on
     // a non-retained layer manager (like the BasicLayerManager that
     // draws the window title bar on Mac), because a) it won't work
     // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
     // that will cause us to forget to update the real layer manager!
     if (!(aFlags & PAINT_LAYERS)) {
-      if (layerManager->HasShadowManager() && Compositor::GetBackend() != LAYERS_BASIC) {
-        return;
-      }
       layerManager->BeginTransaction();
       if (layerManager->EndEmptyTransaction()) {
         return;
       }
       NS_WARNING("Must complete empty transaction when compositing!");
     } else {
       layerManager->BeginTransaction();
     }
@@ -5551,20 +5553,22 @@ PresShell::Paint(nsView*        aViewToP
         }
         if (props) {
           if (!invalid.IsEmpty()) {
             nsIntRect bounds = invalid.GetBounds();
             nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
                         presContext->DevPixelsToAppUnits(bounds.y),
                         presContext->DevPixelsToAppUnits(bounds.width),
                         presContext->DevPixelsToAppUnits(bounds.height));
-            aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
+            if (shouldInvalidate) {
+              aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
+            }
             presContext->NotifyInvalidation(bounds, 0);
           }
-        } else {
+        } else if (shouldInvalidate) {
           aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
         }
 
         frame->UpdatePaintCountForPaintedPresShells();
         return;
       }
     }
     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2102,17 +2102,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG;
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
 
   if (savedOutOfFlowData) {
     clipState.SetClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockClip);
+      &savedOutOfFlowData->mContainingBlockClip);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -885,28 +885,32 @@ NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
   NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 //----------nsXULScrollFrame-------------------------------------------
 
 nsIFrame*
-NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
+NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+                     bool aIsRoot, bool aClipAllDescendants)
 {
-  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot);
+  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot,
+                                           aClipAllDescendants);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
 
-nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot)
+nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext,
+                                   bool aIsRoot, bool aClipAllDescendants)
   : nsBoxFrame(aShell, aContext, aIsRoot),
     mInner(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
 {
   SetLayoutManager(nullptr);
+  mInner.mClipAllDescendants = aClipAllDescendants;
 }
 
 nsMargin
 nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState* aState)
 {
   NS_ASSERTION(aState && aState->GetRenderingContext(),
                "Must have rendering context in layout state for size "
                "computations");
@@ -1498,16 +1502,17 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI
   , mLastUpdateImagesPos(-1, -1)
   , mNeverHasVerticalScrollbar(false)
   , mNeverHasHorizontalScrollbar(false)
   , mHasVerticalScrollbar(false)
   , mHasHorizontalScrollbar(false)
   , mFrameIsUpdatingScrollbar(false)
   , mDidHistoryRestore(false)
   , mIsRoot(aIsRoot)
+  , mClipAllDescendants(aIsRoot)
   , mSupppressScrollbarUpdate(false)
   , mSkippedScrollbarLayout(false)
   , mHadNonInitialReflow(false)
   , mHorizontalOverflow(false)
   , mVerticalOverflow(false)
   , mPostedReflowCallback(false)
   , mMayHaveDirtyFixedChildren(false)
   , mUpdateScrollbarAttributes(false)
@@ -2193,26 +2198,26 @@ nsGfxScrollFrameInner::BuildDisplayList(
   }
 
   nsDisplayListCollection set;
   {
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
     if (usingDisplayport) {
       nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter);
-      if (mIsRoot) {
+      if (mClipAllDescendants) {
         clipState.ClipContentDescendants(clip);
       } else {
         clipState.ClipContainingBlockDescendants(clip, nullptr);
       }
     } else {
       nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
       // Our override of GetBorderRadii ensures we never have a radius at
       // the corners where we have a scrollbar.
-      if (mIsRoot) {
+      if (mClipAllDescendants) {
 #ifdef DEBUG
         nscoord radii[8];
 #endif
         NS_ASSERTION(!mOuter->GetPaddingBoxBorderRadii(radii),
                      "Roots with radii not supported");
         clipState.ClipContentDescendants(clip);
       } else {
         nscoord radii[8];
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -315,16 +315,19 @@ public:
   bool mNeverHasVerticalScrollbar:1;
   bool mNeverHasHorizontalScrollbar:1;
   bool mHasVerticalScrollbar:1;
   bool mHasHorizontalScrollbar:1;
   bool mFrameIsUpdatingScrollbar:1;
   bool mDidHistoryRestore:1;
   // Is this the scrollframe for the document's viewport?
   bool mIsRoot:1;
+  // True if we should clip all descendants, false if we should only clip
+  // descendants for which we are the containing block.
+  bool mClipAllDescendants:1;
   // If true, don't try to layout the scrollbars in Reflow().  This can be
   // useful if multiple passes are involved, because we don't want to place the
   // scrollbars at the wrong size.
   bool mSupppressScrollbarUpdate:1;
   // If true, we skipped a scrollbar layout due to mSupppressScrollbarUpdate
   // being set at some point.  That means we should lay out scrollbars even if
   // it might not strictly be needed next time mSupppressScrollbarUpdate is
   // false.
@@ -625,17 +628,18 @@ private:
 class nsXULScrollFrame : public nsBoxFrame,
                          public nsIScrollableFrame,
                          public nsIAnonymousContentCreator,
                          public nsIStatefulFrame {
 public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
-  friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
+  friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+                                        bool aIsRoot, bool aClipAllDescendants);
 
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   NS_IMETHOD SetInitialChildList(ChildListID     aListID,
                                  nsFrameList&    aChildList) MOZ_OVERRIDE;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
@@ -848,17 +852,18 @@ public:
     return nsBoxFrame::IsFrameOfType(aFlags);
   }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
-  nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot);
+  nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot,
+                   bool aClipAllDescendants);
 
   void ClampAndSetBounds(nsBoxLayoutState& aState, 
                          nsRect& aRect,
                          nsPoint aScrollPosition,
                          bool aRemoveOverflowAreas = false) {
     /* 
      * For RTL frames, restore the original scrolled position of the right
      * edge, then subtract the current width to find the physical position.
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -813,16 +813,17 @@ nsSimplePageSequenceFrame::BuildDisplayL
     // nsDisplayTransform, since they'll be in a different coordinate system.
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
     clipState.Clear();
 
     nsIFrame* child = GetFirstPrincipalChild();
     while (child) {
       child->BuildDisplayListForStackingContext(aBuilder,
           child->GetVisualOverflowRectRelativeToSelf(), &content);
+      aBuilder->ResetMarkedFramesForDisplayList();
       child = child->GetNextSibling();
     }
   }
 
   content.AppendNewToTop(new (aBuilder)
       nsDisplayTransform(aBuilder, this, &content, ::ComputePageSequenceTransform));
 
   aLists.Content()->AppendToTop(&content);
--- a/layout/reftests/bugs/598726-1.html
+++ b/layout/reftests/bugs/598726-1.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html class="reftest-wait">
+<html class="reftest-wait reftest-snapshot-all">
   <head>
     <style type="text/css">
       input {
           -webkit-transition: -webkit-transform 200ms ease-in-out;
           transition: transform 200ms ease-in-out;
       }
       input:focus {
           -webkit-transform: scale(1.05);
@@ -26,17 +26,17 @@
           i.getClientRects();
           i.addEventListener("transitionend", function(aEvent) {
             if (aEvent.propertyName != 'transform' &&
                 aEvent.propertyName != '-webkit-transform') {
               return;
             }
             i.removeEventListener("transitionend", arguments.callee, true);
             i.getClientRects();
-            document.documentElement.removeAttribute("class");
+            document.documentElement.setAttribute("class", "reftest-snapshot-all");
           }, true);
           i.blur();
           i.getClientRects();
         }, true);
         i.focus();
         i.getClientRects();
       }
     </script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/875060-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="display:-moz-inline-stack; width:100px; height:100px; background:yellow">
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/875060-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="display:-moz-inline-stack; overflow:hidden; position:relative; width:100px; height:100px;">
+  <div style="width:200px; height:200px; background:yellow; position:absolute"></div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1755,8 +1755,9 @@ fails-if(Android) == 836844-1.html 83684
 == 846144-1.html 846144-1-ref.html
 == 847850-1.html 847850-1-ref.html
 == 848421-1.html 848421-1-ref.html
 test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html
 == 849996-1.html 849996-1-ref.html
 == 858803-1.html 858803-1-ref.html
 != 860370.html 860370-notref.html
 == 871338-1.html 871338-1-ref.html
+== 875060-1.html 875060-1-ref.html
--- a/layout/reftests/forms/input/range/reftest.list
+++ b/layout/reftests/forms/input/range/reftest.list
@@ -16,18 +16,18 @@ default-preferences pref(dom.experimenta
 
 # dynamic value changes:
 == input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html
 == input-value-prop.html input-75pct-common-ref.html
 == input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html
 == input-valueAsNumber-prop.html input-75pct-common-ref.html
 fails-if(B2G) == input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html
 fails-if(B2G) == input-stepDown.html input-75pct-common-ref.html
-fails-if(B2G) == input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html
-fails-if(B2G) == input-stepUp.html input-75pct-common-ref.html
+== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html
+== input-stepUp.html input-75pct-common-ref.html
 
 # 'direction' property:
 == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html
 
 # ::-moz-range-progress pseudo-element:
 fails-if(B2G) == input-range-moz-range-progress-1.html input-range-moz-range-progress-1-ref.html
 == input-range-moz-range-progress-2.html input-range-moz-range-progress-2-ref.html
 == input-range-moz-range-progress-3.html input-range-moz-range-progress-3-ref.html
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -299,17 +299,20 @@ CommonElementAnimationData::CanAnimatePr
     }
   }
   bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled();
   if (!enabled && shouldLog) {
     nsCString message;
     message.AppendLiteral("Performance warning: Async animations are disabled");
     LogAsyncAnimationFailure(message);
   }
-  return enabled && (aFlags & CanAnimate_AllowPartial);
+  bool propertyAllowed = (aProperty == eCSSProperty_transform) ||
+                         (aProperty == eCSSProperty_opacity) ||
+                         (aFlags & CanAnimate_AllowPartial);
+  return enabled && propertyAllowed;
 }
 
 /* static */ void
 CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage,
                                                      const nsIContent* aContent)
 {
   if (aContent) {
     aMessage.AppendLiteral(" [");
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -236,16 +236,24 @@ function shouldWaitForPendingPaints() {
 function shouldWaitForReftestWaitRemoval(contentRootElement) {
     // use getAttribute because className works differently in HTML and SVG
     return contentRootElement &&
            contentRootElement.hasAttribute('class') &&
            contentRootElement.getAttribute('class').split(/\s+/)
                              .indexOf("reftest-wait") != -1;
 }
 
+function shouldSnapshotWholePage(contentRootElement) {
+    // use getAttribute because className works differently in HTML and SVG
+    return contentRootElement &&
+           contentRootElement.hasAttribute('class') &&
+           contentRootElement.getAttribute('class').split(/\s+/)
+                             .indexOf("reftest-snapshot-all") != -1;
+}
+
 function getNoPaintElements(contentRootElement) {
   return contentRootElement.getElementsByClassName('reftest-no-paint');
 }
 
 // Initial state. When the document has loaded and all MozAfterPaint events and
 // all explicit paint waits are flushed, we can fire the MozReftestInvalidate
 // event and move to the next state.
 const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0;
@@ -301,17 +309,17 @@ function WaitForTestEnd(contentRootEleme
     function AfterPaintListener(event) {
         LogInfo("AfterPaintListener in " + event.target.document.location.href);
         if (event.target.document != currentDoc) {
             // ignore paint events for subframes or old documents in the window.
             // Invalidation in subframes will cause invalidation in the toplevel document anyway.
             return;
         }
 
-        SendUpdateCanvasForEvent(event);
+        SendUpdateCanvasForEvent(event, contentRootElement);
         // These events are fired immediately after a paint. Don't
         // confuse ourselves by firing synchronously if we triggered the
         // paint ourselves.
         setTimeout(MakeProgress, 0);
     }
 
     function AttrModifiedListener() {
         LogInfo("AttrModifiedListener fired");
@@ -835,22 +843,34 @@ function SendTestDone(runtimeMs)
     sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs });
 }
 
 function roundTo(x, fraction)
 {
     return Math.round(x/fraction)*fraction;
 }
 
-function SendUpdateCanvasForEvent(event)
+function SendUpdateCanvasForEvent(event, contentRootElement)
 {
     var win = content;
     var scale = markupDocumentViewer().fullZoom;
 
     var rects = [ ];
+    if (shouldSnapshotWholePage) {
+      // See comments in SendInitCanvasWithSnapshot() re: the split
+      // logic here.
+      if (!gBrowserIsRemote) {
+          sendSyncMessage("reftest:UpdateWholeCanvasForInvalidation");
+      } else {
+          SynchronizeForSnapshot(SYNC_ALLOW_DISABLE);
+          sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation");
+      }
+      return;
+    }
+    
     var rectList = event.clientRects;
     LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects");
     for (var i = 0; i < rectList.length; ++i) {
         var r = rectList[i];
         // Set left/top/right/bottom to "device pixel" boundaries
         var left = Math.floor(roundTo(r.left*scale, 0.001));
         var top = Math.floor(roundTo(r.top*scale, 0.001));
         var right = Math.ceil(roundTo(r.right*scale, 0.001));
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -1387,16 +1387,28 @@ function UpdateCurrentCanvasForInvalidat
 
         ctx.save();
         ctx.translate(left, top);
         DoDrawWindow(ctx, left, top, right - left, bottom - top);
         ctx.restore();
     }
 }
 
+function UpdateWholeCurrentCanvasForInvalidation()
+{
+    LogInfo("Updating entire canvas for invalidation");
+
+    if (!gCurrentCanvas) {
+        return;
+    }
+
+    var ctx = gCurrentCanvas.getContext("2d");
+    DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
+}
+
 function RecordResult(testRunTime, errorMsg, scriptResults)
 {
     LogInfo("RecordResult fired");
 
     // Keep track of which test was slowest, and how long it took.
     if (testRunTime > gSlowestTestTime) {
         gSlowestTestTime = testRunTime;
         gSlowestTestURL  = gCurrentURL;
@@ -1786,16 +1798,20 @@ function RegisterMessageListenersAndLoad
         "reftest:TestDone",
         function (m) { RecvTestDone(m.json.runtimeMs); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:UpdateCanvasForInvalidation",
         function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); }
     );
     gBrowserMessageManager.addMessageListener(
+        "reftest:UpdateWholeCanvasForInvalidation",
+        function (m) { RecvUpdateWholeCanvasForInvalidation(); }
+    );
+    gBrowserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:EnableAsyncScroll",
         function (m) { SetAsyncScroll(true); }
     );
 
@@ -1865,16 +1881,21 @@ function RecvTestDone(runtimeMs)
     RecordResult(runtimeMs, '', [ ]);
 }
 
 function RecvUpdateCanvasForInvalidation(rects)
 {
     UpdateCurrentCanvasForInvalidation(rects);
 }
 
+function RecvUpdateWholeCanvasForInvalidation()
+{
+    UpdateWholeCurrentCanvasForInvalidation();
+}
+
 function OnProcessCrashed(subject, topic, data)
 {
     var id;
     subject = subject.QueryInterface(CI.nsIPropertyBag2);
     if (topic == "plugin-crashed") {
         id = subject.getPropertyAsAString("pluginDumpID");
     } else if (topic == "ipc:content-shutdown") {
         id = subject.getPropertyAsAString("dumpID");
--- a/media/libvpx/Makefile.in
+++ b/media/libvpx/Makefile.in
@@ -405,17 +405,16 @@ ifdef VPX_NEED_OBJ_INT_EXTRACT
 # parse that format, and so only has limited support for cross-compilation.
 
 ifdef VPX_ARM_ASM
 VPX_OIE_FORMAT := rvds
 else
 VPX_OIE_FORMAT := gas
 endif
 
-DISABLED_HOST_CSRCS = obj_int_extract.c
 HOST_PROGRAM = host_obj_int_extract$(HOST_BIN_SUFFIX)
 
 CSRCS += asm_com_offsets.c
 
 GARBAGE += asm_com_offsets.$(OBJ_SUFFIX) asm_com_offsets.asm
 
 ifdef MOZ_VP8_ENCODER
 CSRCS += asm_enc_offsets.c
--- a/mobile/android/chrome/content/aboutReader.js
+++ b/mobile/android/chrome/content/aboutReader.js
@@ -348,23 +348,56 @@ AboutReader.prototype = {
       bodyClasses.remove("font-size" + this._fontSize);
 
     this._fontSize = newFontSize;
     bodyClasses.add("font-size" + this._fontSize);
 
     Services.prefs.setIntPref("reader.font_size", this._fontSize);
   },
 
-  _handleDeviceLight: function Reader_handleDeviceLight(luxValue) {
+  _handleDeviceLight: function Reader_handleDeviceLight(newLux) {
+    // Desired size of the this._luxValues array.
+    let luxValuesSize = 10;
+    // Add new lux value at the front of the array.
+    this._luxValues.unshift(newLux);
+    // Add new lux value to this._totalLux for averaging later.
+    this._totalLux += newLux;
+
+    // Don't update when length of array is less than luxValuesSize except when it is 1.
+    if (this._luxValues.length < luxValuesSize) {
+      // Use the first lux value to set the color scheme until our array equals luxValuesSize.
+      if (this._luxValues.length == 1) {
+        this._updateColorScheme(newLux);
+      }
+      return;
+    }
+    // Holds the average of the lux values collected in this._luxValues.
+    let averageLuxValue = this._totalLux/luxValuesSize;
+
+    this._updateColorScheme(averageLuxValue);
+    // Pop the oldest value off the array.
+    let oldLux = this._luxValues.pop();
+    // Subtract oldLux since it has been discarded from the array.
+    this._totalLux -= oldLux;
+  },
+
+  _updateColorScheme: function Reader_updateColorScheme(luxValue) {
+    // Upper bound value for "dark" color scheme beyond which it changes to "light".
+    let upperBoundDark = 50;
+    // Lower bound value for "light" color scheme beyond which it changes to "dark".
+    let lowerBoundLight = 10;
+    // Threshold for color scheme change.
+    let colorChangeThreshold = 20;
+
     // Ignore changes that are within a certain threshold of previous lux values.
-    if ((this._colorScheme === "dark" && luxValue < 50) ||
-        (this._colorScheme === "light" && luxValue > 25))
+    if ((this._colorScheme === "dark" && luxValue < upperBoundDark) ||
+        (this._colorScheme === "light" && luxValue > lowerBoundLight))
       return;
 
-    if (luxValue < 30)
+    if (luxValue < colorChangeThreshold)
       this._setColorScheme("dark");
     else
       this._setColorScheme("light");
   },
 
   _setColorScheme: function Reader_setColorScheme(newColorScheme) {
     if (this._colorScheme === newColorScheme)
       return;
@@ -378,19 +411,23 @@ AboutReader.prototype = {
     bodyClasses.add(this._colorScheme);
   },
 
   // Pref values include "dark", "light", and "auto", which automatically switches
   // between light and dark color schemes based on the ambient light level.
   _setColorSchemePref: function Reader_setColorSchemePref(colorSchemePref) {
     if (colorSchemePref === "auto") {
       this._win.addEventListener("devicelight", this, false);
+      this._luxValues = [];
+      this._totalLux = 0;
     } else {
       this._win.removeEventListener("devicelight", this, false);
       this._setColorScheme(colorSchemePref);
+      delete this._luxValues;
+      delete this._totalLux;
     }
 
     Services.prefs.setCharPref("reader.color_scheme", colorSchemePref);
   },
 
   _setFontType: function Reader_setFontType(newFontType) {
     if (this._fontType === newFontType)
       return;
--- a/mobile/android/components/FilePicker.js
+++ b/mobile/android/components/FilePicker.js
@@ -230,17 +230,17 @@ FilePicker.prototype = {
       mIndex: 0,
       hasMoreElements: function() {
         return (this.mIndex < this.mFiles.length);
       },
       getNext: function() {
         if (this.mIndex >= this.mFiles.length) {
           throw Components.results.NS_ERROR_FAILURE;
         }
-        return map(this.mFiles[this.mIndex++]);
+        return mapFunction(this.mFiles[this.mIndex++]);
       }
     };
   },
 
   classID: Components.ID("{18a4e042-7c7c-424b-a583-354e68553a7f}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIFilePicker, Ci.nsIObserver])
 };
 
new file mode 100755
--- /dev/null
+++ b/mobile/android/debug_sign_tool.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python
+
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+"""
+Sign Android packages using an Android debug keystore, creating the
+keystore if it does not exist.
+
+This and |zip| can be combined to replace the Android |apkbuilder|
+tool, which was deprecated in SDK r22.
+
+Exits with code 0 if creating the keystore and every signing succeeded,
+or with code 1 if any creation or signing failed.
+"""
+
+from argparse import ArgumentParser
+import errno
+import logging
+import os
+import subprocess
+import sys
+
+
+log = logging.getLogger(os.path.basename(__file__))
+log.setLevel(logging.INFO)
+sh = logging.StreamHandler(stream=sys.stdout)
+sh.setFormatter(logging.Formatter('%(name)s: %(message)s'))
+log.addHandler(sh)
+
+
+class DebugKeystore:
+    """
+    A thin abstraction on top of an Android debug key store.
+    """
+    def __init__(self, keystore):
+        self._keystore = os.path.abspath(os.path.expanduser(keystore))
+        self._alias = 'debug'
+        self.verbose = False
+        self.keytool = 'keytool'
+        self.jarsigner = 'jarsigner'
+
+    @property
+    def keystore(self):
+        return self._keystore
+
+    @property
+    def alias(self):
+        return self._alias
+
+    def _ensure_keystore(self):
+        if os.path.exists(self.keystore):
+            if self.verbose:
+                log.debug('Keystore exists at %s' % self.keystore)
+        else:
+            self.create_keystore()
+
+    def create_keystore(self):
+        try:
+            path = os.path.dirname(self.keystore)
+            os.makedirs(path)
+        except OSError as exception:
+            if exception.errno != errno.EEXIST:
+                raise
+
+        args = [ self.keytool,
+                 '-genkey',
+                 '-v',
+                 '-keystore', self.keystore,
+                 '-storepass', 'android',
+                 '-alias', self.alias,
+                 '-keypass', 'android',
+                 '-dname', 'CN=Android Debug,O=Android,C=US',
+                 '-keyalg', 'RSA',
+                 '-validity', '365',
+               ]
+        subprocess.check_call(args)
+        if self.verbose:
+            log.info('Created keystore at %s' % self.keystore)
+
+    def sign(self, apk):
+        self._ensure_keystore()
+
+        args = [ self.jarsigner,
+                 '-digestalg', 'SHA1',
+                 '-sigalg', 'MD5withRSA',
+                 '-keystore', self.keystore,
+                 '-storepass', 'android',
+                 apk,
+                 self.alias,
+               ]
+        subprocess.check_call(args)
+        if self.verbose:
+            log.info('Signed %s with keystore at %s' % (apk, self.keystore))
+
+
+def parse_args(argv):
+    parser = ArgumentParser(description='Sign Android packages using an Android debug keystore.')
+    parser.add_argument('apks', nargs='+',
+                        metavar='APK',
+                        help='Android packages to be signed')
+    parser.add_argument('-q', '--quiet',
+                        dest='verbose',
+                        default=True,
+                        action='store_false',
+                        help='quiet output')
+    parser.add_argument('--keytool',
+                        metavar='PATH',
+                        default='keytool',
+                        help='path to Java keytool')
+    parser.add_argument('--jarsigner',
+                        metavar='PATH',
+                        default='jarsigner',
+                        help='path to Java jarsigner')
+    parser.add_argument('--keystore',
+                        metavar='PATH',
+                        default='~/.android/debug.keystore',
+                        help='path to keystore (default: ~/.android/debug.keystore)')
+    parser.add_argument('-f', '--force-create-keystore',
+                        dest='force',
+                        default=False,
+                        action='store_true',
+                        help='force creating keystore')
+    return parser.parse_args(argv)
+
+
+def main():
+    args = parse_args(sys.argv[1:])
+
+    keystore = DebugKeystore(args.keystore)
+    keystore.verbose = args.verbose
+    keystore.keytool = args.keytool
+    keystore.jarsigner = args.jarsigner
+
+    if args.force:
+        try:
+            keystore.create_keystore()
+        except subprocess.CalledProcessError as e:
+            log.error('Failed to force-create keystore')
+            log.error(e)
+            return 1
+
+    for apk in args.apks:
+        try:
+            keystore.sign(apk)
+        except subprocess.CalledProcessError as e:
+            log.error('Failed to sign %s', apk)
+            log.error(e)
+            return 1
+
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
--- a/modules/libbz2/src/Makefile.in
+++ b/modules/libbz2/src/Makefile.in
@@ -29,16 +29,15 @@ CSRCS		= \
 		bzlib.c \
 		compress.c \
 		crctable.c \
 		decompress.c \
 		huffman.c \
 		randtable.c \
 		$(NULL)
 
-DISABLED_HOST_CSRCS	= $(CSRCS)
 
 include $(topsrcdir)/config/rules.mk
 
 # The intermediate (.ii/.s) files for host and target can have the same name...
 # disable parallel builds
 .NOTPARALLEL:
 
--- a/modules/libmar/src/Makefile.in
+++ b/modules/libmar/src/Makefile.in
@@ -15,24 +15,19 @@ LIBRARY_NAME	= mar
 DISABLED_HOST_LIBRARY_NAME = hostmar
 FORCE_STATIC_LIB = 1
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 endif
 
 # This makefile just builds support for reading archives.
 
-
-DISABLED_HOST_CSRCS	= \
-		mar_create.c \
-		mar_extract.c \
-		mar_read.c \
-		$(NULL)
-
-CSRCS		= \
-		$(HOST_CSRCS) \
-		$(NULL)
+CSRCS = \
+    mar_create.c \
+    mar_extract.c \
+    mar_read.c \
+    $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 # The intermediate (.ii/.s) files for host and target can have the same name...
 # disable parallel builds
 .NOTPARALLEL:
--- a/modules/libmar/tool/Makefile.in
+++ b/modules/libmar/tool/Makefile.in
@@ -35,19 +35,16 @@ DEFINES += \
   $(NULL)
 endif
 
 HOST_CFLAGS += \
   -DNO_SIGN_VERIFY \
   $(DEFINES) \
   $(NULL)
 
-DISABLED_HOST_CSRCS = \
-  mar.c \
-  $(NULL)
 DISABLED_CSRCS = $(HOST_CSRCS)
 
 HOST_LIBS = $(DIST)/host/lib/$(LIB_PREFIX)hostmar.$(LIB_SUFFIX)
 LIBS = $(DEPTH)/modules/libmar/src/$(LIB_PREFIX)mar.$(LIB_SUFFIX)
 
 ifdef MOZ_ENABLE_SIGNMAR
 LIBS += \
   $(DEPTH)/modules/libmar/sign/$(LIB_PREFIX)signmar.$(LIB_SUFFIX) \
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1799,17 +1799,16 @@ pref("layout.frame_rate", -1);
 // heavily loaded.
 pref("layout.frame_rate.precise", false);
 
 // pref to permit users to make verified SOAP calls by default
 pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader", "allAccess");
 
 // if true, allow plug-ins to override internal imglib decoder mime types in full-page mode
 pref("plugin.override_internal_types", false);
-pref("plugin.expose_full_path", false); // if true navigator.plugins reveals full path
 
 // See bug 136985.  Gives embedders a pref to hook into to show
 // a popup blocker if they choose.
 pref("browser.popups.showPopupBlocker", true);
 
 // Pref to control whether the viewmanager code does double-buffering or not
 // See http://bugzilla.mozilla.org/show_bug.cgi?id=169483 for further details...
 pref("viewmanager.do_doublebuffering", true);
--- a/netwerk/base/public/nsIBrowserSearchService.idl
+++ b/netwerk/base/public/nsIBrowserSearchService.idl
@@ -135,16 +135,40 @@ interface nsISearchEngine : nsISupports
   /**
    * An optional unique identifier for this search engine within the context of
    * the distribution, as provided by the distributing entity.
    */
   readonly attribute AString identifier;
 
 };
 
+[scriptable, uuid(9fc39136-f08b-46d3-b232-96f4b7b0e235)]
+interface nsISearchInstallCallback : nsISupports
+{
+  const unsigned long ERROR_UNKNOWN_FAILURE = 0x1;
+  const unsigned long ERROR_DUPLICATE_ENGINE = 0x2;
+
+  /**
+   * Called to indicate that the engine addition process succeeded.
+   *
+   * @param engine
+   *        The nsISearchEngine object that was added (will not be null).
+   */
+  void onSuccess(in nsISearchEngine engine);
+
+  /**
+   * Called to indicate that the engine addition process failed.
+   *
+   * @param errorCode
+   *        One of the ERROR_* values described above indicating the cause of
+   *        the failure.
+   */
+  void onError(in unsigned long errorCode);
+};
+
 /**
  * Callback for asynchronous initialization of nsIBrowserSearchService
  */
 [scriptable, function, uuid(02256156-16e4-47f1-9979-76ff98ceb590)]
 interface nsIBrowserSearchInitObserver : nsISupports
 {
   /**
    * Called once initialization of the browser search service is complete.
@@ -181,18 +205,17 @@ interface nsIBrowserSearchService : nsIS
    * initialization has not been triggered yet.
    */
   readonly attribute bool isInitialized;
 
   /**
    * Adds a new search engine from the file at the supplied URI, optionally
    * asking the user for confirmation first.  If a confirmation dialog is
    * shown, it will offer the option to begin using the newly added engine
-   * right away; if no confirmation dialog is shown, the new engine will be
-   * used right away automatically.
+   * right away.
    *
    * @param engineURL
    *        The URL to the search engine's description file.
    *
    * @param dataType
    *        An integer representing the plugin file format. Must be one
    *        of the supported search engine data types defined above.
    *
@@ -202,21 +225,26 @@ interface nsIBrowserSearchService : nsIS
    *        engine description file.
    *
    * @param confirm
    *        A boolean value indicating whether the user should be asked for
    *        confirmation before this engine is added to the list.  If this
    *        value is false, the engine will be added to the list upon successful
    *        load, but it will not be selected as the current engine.
    *
+   * @param callback
+   *        A nsISearchInstallCallback that will be notified when the
+   *        addition is complete, or if the addition fails. It will not be
+   *        called if addEngine throws an exception.
+   *
    * @throws NS_ERROR_FAILURE if the type is invalid, or if the description
    *         file cannot be successfully loaded.
    */
   void addEngine(in AString engineURL, in long dataType, in AString iconURL,
-                 in boolean confirm);
+                 in boolean confirm, [optional] in nsISearchInstallCallback callback);
 
   /**
    * Adds a new search engine, without asking the user for confirmation and
    * without starting to use it right away.
    *
    * @param name
    *        The search engine's name. Must be unique. Must not be null.
    *
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -405,41 +405,42 @@ nsSocketInputStream::CloseWithStatus(nsr
 NS_IMETHODIMP
 nsSocketInputStream::AsyncWait(nsIInputStreamCallback *callback,
                                uint32_t flags,
                                uint32_t amount,
                                nsIEventTarget *target)
 {
     SOCKET_LOG(("nsSocketInputStream::AsyncWait [this=%p]\n", this));
 
-    // This variable will be non-null when we want to call the callback
-    // directly from this function, but outside the lock.
-    // (different from callback when target is not null)
-    nsCOMPtr<nsIInputStreamCallback> directCallback;
+    bool hasError = false;
     {
         MutexAutoLock lock(mTransport->mLock);
 
         if (callback && target) {
             //
             // build event proxy
             //
             mCallback = NS_NewInputStreamReadyEvent(callback, target);
         }
         else
             mCallback = callback;
+        mCallbackFlags = flags;
 
-        if (NS_FAILED(mCondition))
-            directCallback.swap(mCallback);
-        else
-            mCallbackFlags = flags;
+        hasError = NS_FAILED(mCondition);
+    } // unlock mTransport->mLock
+
+    if (hasError) {
+        // OnSocketEvent will call OnInputStreamReady with an error code after
+        // going through the event loop. We do this because most socket callers
+        // do not expect AsyncWait() to synchronously execute the OnInputStreamReady
+        // callback.
+        mTransport->PostEvent(nsSocketTransport::MSG_INPUT_PENDING);
+    } else {
+        mTransport->OnInputPending();
     }
-    if (directCallback)
-        directCallback->OnInputStreamReady(this);
-    else
-        mTransport->OnInputPending();
 
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 // socket output stream impl 
 //-----------------------------------------------------------------------------
 
--- a/other-licenses/bsdiff/Makefile.in
+++ b/other-licenses/bsdiff/Makefile.in
@@ -27,17 +27,16 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 # This program is output to dist/host/bin because it is only needed by the
 # build system and is not intended to be included in Mozilla distributions.
 HOST_PROGRAM = mbsdiff$(BIN_SUFFIX)
-DISABLED_HOST_CSRCS = bsdiff.c
 
 ifdef MOZ_NATIVE_BZ2
 HOST_LIBS += $(MOZ_BZ2_LIBS)
 else
 HOST_LIBS += $(DIST)/host/lib/$(LIB_PREFIX)hostbz2.$(LIB_SUFFIX)
 endif
 
 ifeq ($(HOST_OS_ARCH),WINNT)
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -27,16 +27,17 @@
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "nsIMutationObserver.h"
 #include "nsIFormProcessor.h"
 #include "nsIServiceManager.h"
 #include "nsEscape.h"
 #include "mozilla/dom/Comment.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/HTMLImageElement.h"
 #include "mozilla/dom/HTMLTemplateElement.h"
 #include "nsHtml5SVGLoadDispatcher.h"
 #include "nsIURI.h"
 #include "nsIProtocolHandler.h"
 #include "nsNetUtil.h"
 #include "nsIHTMLDocument.h"
 #include "mozilla/Likely.h"
 #include "nsTextNode.h"
@@ -436,25 +437,32 @@ nsHtml5TreeOperation::Perform(nsHtml5Tre
       }
 
       return rv;
     }
     case eTreeOpSetFormElement: {
       nsIContent* node = *(mOne.node);
       nsIContent* parent = *(mTwo.node);
       nsCOMPtr<nsIFormControl> formControl(do_QueryInterface(node));
+      nsCOMPtr<nsIDOMHTMLImageElement> domImageElement = do_QueryInterface(node);
       // NS_ASSERTION(formControl, "Form-associated element did not implement nsIFormControl.");
       // TODO: uncomment the above line when <keygen> (bug 101019) is supported by Gecko
       nsCOMPtr<nsIDOMHTMLFormElement> formElement(do_QueryInterface(parent));
       NS_ASSERTION(formElement, "The form element doesn't implement nsIDOMHTMLFormElement.");
       // avoid crashing on <keygen>
       if (formControl &&
           !node->HasAttr(kNameSpaceID_None, nsGkAtoms::form)) {
         formControl->SetForm(formElement);
+      } else if (domImageElement) {
+        nsRefPtr<dom::HTMLImageElement> imageElement =
+          static_cast<dom::HTMLImageElement*>(domImageElement.get());
+        MOZ_ASSERT(imageElement);
+        imageElement->SetForm(formElement);
       }
+
       return rv;
     }
     case eTreeOpAppendText: {
       nsIContent* parent = *mOne.node;
       PRUnichar* buffer = mTwo.unicharPtr;
       uint32_t length = mFour.integer;
       return AppendText(buffer, length, parent, aBuilder);
     }
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -88,16 +88,17 @@ class TreeMetadataEmitter(object):
             EXTRA_JS_MODULES='EXTRA_JS_MODULES',
             EXTRA_PP_COMPONENTS='EXTRA_PP_COMPONENTS',
             HOST_CSRCS='HOST_CSRCS',
             HOST_LIBRARY_NAME='HOST_LIBRARY_NAME',
             JS_MODULES_PATH='JS_MODULES_PATH',
             LIBRARY_NAME='LIBRARY_NAME',
             LIBS='LIBS',
             MODULE='MODULE',
+            SDK_LIBRARY='SDK_LIBRARY',
             SIMPLE_PROGRAMS='SIMPLE_PROGRAMS',
             SSRCS='SSRCS',
             XPIDL_FLAGS='XPIDL_FLAGS',
             XPIDL_MODULE='XPIDL_MODULE',
             XPIDLSRCS='XPIDL_SOURCES',
             )
         for mak, moz in varmap.items():
             if sandbox[moz]:
--- a/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
+++ b/python/mozbuild/mozbuild/frontend/sandbox_symbols.py
@@ -155,16 +155,22 @@ VARIABLES = {
         """),
 
     'LIBS': (StrictOrderingOnAppendList, list, [],
         """Linker libraries and flags.
 
         A list of libraries and flags to include when linking.
         """),
 
+    'SDK_LIBRARY': (StrictOrderingOnAppendList, list, [],
+        """Elements of the distributed SDK.
+
+        Files on this list will be copied into SDK_LIB_DIR ($DIST/sdk/lib).
+        """),
+
     'SIMPLE_PROGRAMS': (StrictOrderingOnAppendList, list, [],
         """Generate a list of binaries from source.
 
         A list of sources, one per program, to compile & link with libs into standalone programs.
         """),
 
     'SSRCS': (StrictOrderingOnAppendList, list, [],
         """Assembly source files.
--- a/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/backend/data/variable_passthru/moz.build
@@ -15,15 +15,17 @@ EXTRA_PP_COMPONENTS = ['bar.pp.js', 'foo
 
 HOST_CSRCS = ['bar.c', 'foo.c']
 
 HOST_LIBRARY_NAME = 'host_bar'
 
 LIBRARY_NAME = 'lib_name'
 LIBS = ['bar.lib', 'foo.lib']
 
+SDK_LIBRARY = ['bar.sdk', 'foo.sdk']
+
 SIMPLE_PROGRAMS = ['bar.x', 'foo.x']
 
 CSRCS += ['bar.c', 'foo.c']
 
 CMMSRCS = ['bar.mm', 'foo.mm']
 
 SSRCS = ['bar.S', 'foo.S']
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -167,16 +167,20 @@ class TestRecursiveMakeBackend(BackendTe
                 'HOST_CSRCS += foo.c',
             ],
             'HOST_LIBRARY_NAME': [
                 'HOST_LIBRARY_NAME := host_bar',
             ],
             'LIBRARY_NAME': [
                 'LIBRARY_NAME := lib_name',
             ],
+            'SDK_LIBRARY': [
+                'SDK_LIBRARY += bar.sdk',
+                'SDK_LIBRARY += foo.sdk',
+            ],
             'SIMPLE_PROGRAMS': [
                 'SIMPLE_PROGRAMS += bar.x',
                 'SIMPLE_PROGRAMS += foo.x',
             ],
             'SSRCS': [
                 'SSRCS += bar.S',
                 'SSRCS += foo.S',
             ],
--- a/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
+++ b/python/mozbuild/mozbuild/test/frontend/data/variable-passthru/moz.build
@@ -15,15 +15,17 @@ EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans
 
 HOST_CSRCS += ['fans.c', 'tans.c']
 
 HOST_LIBRARY_NAME = 'host_fans'
 
 LIBRARY_NAME = 'lib_name'
 LIBS += ['fans.lib', 'tans.lib']
 
+SDK_LIBRARY += ['fans.sdk', 'tans.sdk']
+
 SIMPLE_PROGRAMS += ['fans.x', 'tans.x']
 
 CSRCS += ['fans.c', 'tans.c']
 
 CMMSRCS = ['fans.mm', 'tans.mm']
 
 SSRCS = ['fans.S', 'tans.S']
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -130,16 +130,17 @@ class TestEmitterBasic(unittest.TestCase
             CSRCS=['fans.c', 'tans.c'],
             DEFINES=['-Dfans', '-Dtans'],
             EXTRA_COMPONENTS=['fans.js', 'tans.js'],
             EXTRA_PP_COMPONENTS=['fans.pp.js', 'tans.pp.js'],
             HOST_CSRCS=['fans.c', 'tans.c'],
             HOST_LIBRARY_NAME='host_fans',
             LIBRARY_NAME='lib_name',
             LIBS=['fans.lib', 'tans.lib'],
+            SDK_LIBRARY=['fans.sdk', 'tans.sdk'],
             SIMPLE_PROGRAMS=['fans.x', 'tans.x'],
             SSRCS=['fans.S', 'tans.S'],
             XPIDLSRCS=['bar.idl', 'biz.idl', 'foo.idl'],
             XPIDL_MODULE='module_name',
             XPIDL_FLAGS=['-Idir1', '-Idir2', '-Idir3'],
             )
 
         variables = objs[1].variables
--- a/testing/talos/talos.json
+++ b/testing/talos/talos.json
@@ -1,6 +1,6 @@
 {
     "talos.zip": {
-        "url": "http://build.mozilla.org/talos/zips/talos.dfe08f329e2c.zip",
+        "url": "http://build.mozilla.org/talos/zips/talos.8cf5b862d113.zip",
         "path": ""
     }
 }
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -61,17 +61,17 @@ RUN_MOCHITEST_REMOTE = \
     --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
     --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
     --testing-modules-dir=$(call core_abspath,_tests/modules) \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 RUN_MOCHITEST_ROBOCOP = \
   rm -f ./$@.log && \
   $(PYTHON) _tests/testing/mochitest/runtestsremote.py \
-    --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug-signed.apk \
+    --robocop-apk=$(DEPTH)/build/mobile/robocop/robocop-debug.apk \
     --robocop-ids=$(DEPTH)/mobile/android/base/fennec_ids.txt \
     --robocop-ini=$(DEPTH)/build/mobile/robocop/robocop.ini \
     --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) --dm_trans=$(DM_TRANS) \
     --app=$(TEST_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
     $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
 ifndef NO_FAIL_ON_TEST_ERRORS
 define check_test_error_internal
--- a/toolkit/components/search/nsSearchService.js
+++ b/toolkit/components/search/nsSearchService.js
@@ -1,15 +1,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cr = Components.results;
+const Cu = Components.utils;
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
   "resource://gre/modules/DeferredTask.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "OS",
@@ -1145,17 +1146,20 @@ Engine.prototype = {
   set _uri(aValue) {
     this.__uri = aValue;
   },
   // Whether to obtain user confirmation before adding the engine. This is only
   // used when the engine is first added to the list.
   _confirm: false,
   // Whether to set this as the current engine as soon as it is loaded.  This
   // is only used when the engine is first added to the list.
-  _useNow: true,
+  _useNow: false,
+  // A function to be invoked when this engine object's addition completes (or
+  // fails). Only used for installation via addEngine.
+  _installCallback: null,
   // Where the engine was loaded from. Can be one of: SEARCH_APP_DIR,
   // SEARCH_PROFILE_DIR, SEARCH_IN_EXTENSION.
   __installLocation: null,
   // The number of days between update checks for new versions
   _updateInterval: null,
   // The url to check at for a new update
   _updateURL: null,
   // The url to check for a new icon
@@ -1299,41 +1303,50 @@ Engine.prototype = {
 
   /**
    * Handle the successful download of an engine. Initializes the engine and
    * triggers parsing of the data. The engine is then flushed to disk. Notifies
    * the search service once initialization is complete.
    */
   _onLoad: function SRCH_ENG_onLoad(aBytes, aEngine) {
     /**
-     * Handle an error during the load of an engine by prompting the user to
-     * notify him that the load failed.
+     * Handle an error during the load of an engine by notifying the engine's
+     * error callback, if any.
      */
-    function onError(aErrorString, aTitleString) {
+    function onError(errorCode = Ci.nsISearchInstallCallback.ERROR_UNKNOWN_FAILURE) {
+      // Notify the callback of the failure
+      if (aEngine._installCallback) {
+        aEngine._installCallback(errorCode);
+      }
+    }
+
+    function promptError(strings = {}, error = undefined) {
+      onError(error);
+
       if (aEngine._engineToUpdate) {
         // We're in an update, so just fail quietly
         LOG("updating " + aEngine._engineToUpdate.name + " failed");
         return;
       }
       var brandBundle = Services.strings.createBundle(BRAND_BUNDLE);
       var brandName = brandBundle.GetStringFromName("brandShortName");
 
       var searchBundle = Services.strings.createBundle(SEARCH_BUNDLE);
-      var msgStringName = aErrorString || "error_loading_engine_msg2";
-      var titleStringName = aTitleString || "error_loading_engine_title";
+      var msgStringName = strings.error || "error_loading_engine_msg2";
+      var titleStringName = strings.title || "error_loading_engine_title";
       var title = searchBundle.GetStringFromName(titleStringName);
       var text = searchBundle.formatStringFromName(msgStringName,
                                                    [brandName, aEngine._location],
                                                    2);
 
       Services.ww.getNewPrompter(null).alert(title, text);
     }
 
     if (!aBytes) {
-      onError();
+      promptError();
       return;
     }
 
     var engineToUpdate = null;
     if (aEngine._engineToUpdate) {
       engineToUpdate = aEngine._engineToUpdate.wrappedJSObject;
 
       // Make this new engine use the old engine's file.
@@ -1346,53 +1359,55 @@ Engine.prototype = {
                      createInstance(Ci.nsIDOMParser);
         var doc = parser.parseFromBuffer(aBytes, aBytes.length, "text/xml");
         aEngine._data = doc.documentElement;
         break;
       case SEARCH_DATA_TEXT:
         aEngine._data = aBytes;
         break;
       default:
-        onError();
+        promptError();
         LOG("_onLoad: Bogus engine _dataType: \"" + this._dataType + "\"");
         return;
     }
 
     try {
       // Initialize the engine from the obtained data
       aEngine._initFromData();
     } catch (ex) {
       LOG("_onLoad: Failed to init engine!\n" + ex);
       // Report an error to the user
-      onError();
+      promptError();
       return;
     }
 
     // Check to see if this is a duplicate engine. If we're confirming the
     // engine load, then we display a "this is a duplicate engine" prompt,
     // otherwise we fail silently.
     if (!engineToUpdate) {
       if (Services.search.getEngineByName(aEngine.name)) {
-        if (aEngine._confirm)
-          onError("error_duplicate_engine_msg", "error_invalid_engine_title");
-
+        promptError({ error: "error_duplicate_engine_msg",
+                      title: "error_invalid_engine_title"
+                    }, Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE);
         LOG("_onLoad: duplicate engine found, bailing");
         return;
       }
     }
 
     // If requested, confirm the addition now that we have the title.
     // This property is only ever true for engines added via
     // nsIBrowserSearchService::addEngine.
     if (aEngine._confirm) {
       var confirmation = aEngine._confirmAddEngine();
       LOG("_onLoad: confirm is " + confirmation.confirmed +
           "; useNow is " + confirmation.useNow);
-      if (!confirmation.confirmed)
+      if (!confirmation.confirmed) {
+        onError();
         return;
+      }
       aEngine._useNow = confirmation.useNow;
     }
 
     // If we don't yet have a file, get one now. The only case where we would
     // already have a file is if this is an update and _file was set above.
     if (!aEngine._file)
       aEngine._file = getSanitizedFile(aEngine.name);
 
@@ -1409,44 +1424,47 @@ Engine.prototype = {
         let newUpdateURL = aEngine._updateURL;
         let oldSelfURL = engineToUpdate._getURLOfType(URLTYPE_OPENSEARCH);
         if (oldSelfURL && oldSelfURL._hasRelation("self")) {
           oldUpdateURL = oldSelfURL.template;
           let newSelfURL = aEngine._getURLOfType(URLTYPE_OPENSEARCH);
           if (!newSelfURL || !newSelfURL._hasRelation("self")) {
             LOG("_onLoad: updateURL missing in updated engine for " +
                 aEngine.name + " aborted");
+            onError();
             return;
           }
           newUpdateURL = newSelfURL.template;
         }
 
         if (oldUpdateURL != newUpdateURL) {
           LOG("_onLoad: updateURLs do not match! Update of " + aEngine.name + " aborted");
+          onError();
           return;
         }
       }
 
       // Set the new engine's icon, if it doesn't yet have one.
       if (!aEngine._iconURI && engineToUpdate._iconURI)
         aEngine._iconURI = engineToUpdate._iconURI;
-
-      // Clear the "use now" flag since we don't want to be changing the
-      // current engine for an update.
-      aEngine._useNow = false;
     }
 
     // Write the engine to file. For readOnly engines, they'll be stored in the
     // cache following the notification below.
     if (!aEngine._readOnly)
       aEngine._serializeToFile();
 
     // Notify the search service of the successful load. It will deal with
     // updates by checking aEngine._engineToUpdate.
     notifyAction(aEngine, SEARCH_ENGINE_LOADED);
+
+    // Notify the callback if needed
+    if (aEngine._installCallback) {
+      aEngine._installCallback();
+    }
   },
 
   /**
    * Sets the .iconURI property of the engine.
    *
    *  @param aIconURL
    *         A URI string pointing to the engine's icon. Must have a http[s],
    *         ftp, or data scheme. Icons with HTTP[S] or FTP schemes will be
@@ -3336,24 +3354,41 @@ SearchService.prototype = {
     var engine = new Engine(getSanitizedFile(aName), SEARCH_DATA_XML, false);
     engine._initFromMetadata(aName, aIconURL, aAlias, aDescription,
                              aMethod, aTemplate);
     this._addEngineToStore(engine);
     this._batchCacheInvalidation();
   },
 
   addEngine: function SRCH_SVC_addEngine(aEngineURL, aDataType, aIconURL,
-                                         aConfirm) {
+                                         aConfirm, aCallback) {
     LOG("addEngine: Adding \"" + aEngineURL + "\".");
     this._ensureInitialized();
     try {
       var uri = makeURI(aEngineURL);
       var engine = new Engine(uri, aDataType, false);
+      if (aCallback) {
+        engine._installCallback = function (errorCode) {
+          try {
+            if (errorCode == null)
+              aCallback.onSuccess(engine);
+            else
+              aCallback.onError(errorCode);
+          } catch (ex) {
+            Cu.reportError("Error invoking addEngine install callback: " + ex);
+          }
+          // Clear the reference to the callback now that it's been invoked.
+          engine._installCallback = null;
+        };
+      }
       engine._initFromURI();
     } catch (ex) {
+      // Drop the reference to the callback, if set
+      if (engine)
+        engine._installCallback = null;
       FAIL("addEngine: Error adding engine:\n" + ex, Cr.NS_ERROR_FAILURE);
     }
     engine._setIcon(aIconURL, false);
     engine._confirm = aConfirm;
   },
 
   removeEngine: function SRCH_SVC_removeEngine(aEngine) {
     this._ensureInitialized();
--- a/toolkit/components/search/tests/xpcshell/head_search.js
+++ b/toolkit/components/search/tests/xpcshell/head_search.js
@@ -92,17 +92,17 @@ function afterCache(callback)
       callback(result);
     } else {
       dump("TOPIC: " + topic+ "\n");
     }
   }
   Services.obs.addObserver(obs, "browser-search-service", false);
 }
 
-function  parseJsonFromStream(aInputStream) {
+function parseJsonFromStream(aInputStream) {
   const json = Cc["@mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON);
   const data = json.decodeFromStream(aInputStream, aInputStream.available());
   return data;
 }
 
 /**
  * Read a JSON file and return the JS object
  */
new file mode 100644
--- /dev/null
+++ b/toolkit/components/search/tests/xpcshell/test_addEngine_callback.js
@@ -0,0 +1,123 @@
+/* Any copyright is dedicated to the Public Domain.
+ *    http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*
+ * Tests covering nsIBrowserSearchService::addEngine's optional callback.
+ */
+
+"use strict";
+
+const Ci = Components.interfaces;
+
+Components.utils.import("resource://testing-common/httpd.js");
+
+// Override the prompt service and nsIPrompt, since the search service currently
+// prompts in response to certain installation failures we test here
+// XXX this should disappear once bug 863474 is fixed
+function replaceService(contractID, component) {
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  let cid = registrar.contractIDToCID(contractID);
+
+  let oldFactory = Components.manager.getClassObject(Components.classes[contractID],
+                                                     Ci.nsIFactory);
+  registrar.unregisterFactory(cid, oldFactory);
+
+  let factory = {
+    createInstance: function(aOuter, aIid) {
+      if (aOuter != null)
+        throw Components.results.NS_ERROR_NO_AGGREGATION;
+      return component.QueryInterface(aIid);
+    }
+  };
+
+  registrar.registerFactory(cid, "", contractID, factory);
+}
+// Only need to stub the methods actually called by nsSearchService
+let promptService = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPromptService]),
+  confirmEx: function() {}
+};
+let prompt = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIPrompt]),
+  alert: function() {}
+};
+replaceService("@mozilla.org/embedcomp/prompt-service;1", promptService);
+replaceService("@mozilla.org/prompter;1", prompt);
+
+
+// First test inits the search service
+add_test(function init_search_service() {
+  Services.search.init(function (status) {
+    if (!Components.isSuccessCode(status))
+      do_throw("Failed to initialize search service");
+
+    run_next_test();
+  });
+});
+
+// Simple test of the search callback
+add_test(function simple_callback_test() {
+  let searchCallback = {
+    onSuccess: function (engine) {
+      do_check_true(!!engine);
+      do_check_neq(engine.name, Services.search.defaultEngine.name);
+      run_next_test();
+    },
+    onError: function (errorCode) {
+      do_throw("search callback returned error: " + errorCode);
+    }
+  }
+  Services.search.addEngine("http://localhost:4444/data/engine.xml",
+                            Ci.nsISearchEngine.DATA_XML,
+                            null, false, searchCallback);
+});
+
+// Test of the search callback on duplicate engine failures
+add_test(function duplicate_failure_test() {
+  let searchCallback = {
+    onSuccess: function (engine) {
+      do_throw("this addition should not have succeeded");
+    },
+    onError: function (errorCode) {
+      do_check_true(!!errorCode);
+      do_check_eq(errorCode, Ci.nsISearchInstallCallback.ERROR_DUPLICATE_ENGINE);
+      run_next_test();
+    }
+  }
+  // Re-add the same engine added in the previous test
+  Services.search.addEngine("http://localhost:4444/data/engine.xml",
+                            Ci.nsISearchEngine.DATA_XML,
+                            null, false, searchCallback);
+});
+
+// Test of the search callback on failure to load the engine failures
+add_test(function load_failure_test() {
+  let searchCallback = {
+    onSuccess: function (engine) {
+      do_throw("this addition should not have succeeded");
+    },
+    onError: function (errorCode) {
+      do_check_true(!!errorCode);
+      do_check_eq(errorCode, Ci.nsISearchInstallCallback.ERROR_UNKNOWN_FAILURE);
+      run_next_test();
+    }
+  }
+  // Try adding an engine that doesn't exist
+  Services.search.addEngine("http://invalid/data/engine.xml",
+                            Ci.nsISearchEngine.DATA_XML,
+                            null, false, searchCallback);
+});
+
+function run_test() {
+  updateAppInfo();
+
+  let httpServer = new HttpServer();
+  httpServer.start(4444);
+  httpServer.registerDirectory("/", do_get_cwd());
+
+  do_register_cleanup(function cleanup() {
+    httpServer.stop(function() {});
+  });
+
+  run_next_test();
+}
--- a/toolkit/components/search/tests/xpcshell/test_notifications.js
+++ b/toolkit/components/search/tests/xpcshell/test_notifications.js
@@ -35,18 +35,17 @@ function search_observer(subject, topic,
 
   do_print("Observer: " + data + " for " + engine.name);
 
   switch (data) {
     case "engine-added":
       let retrievedEngine = Services.search.getEngineByName("Test search engine");
       do_check_eq(engine, retrievedEngine);
       Services.search.defaultEngine = engine;
-      // XXX bug 493051
-      // Services.search.currentEngine = engine;
+      Services.search.currentEngine = engine;
       do_execute_soon(function () {
         Services.search.removeEngine(engine);
       });
       break;
     case "engine-removed":
       let engineNameOutput = " for Test search engine";
       expectedLog = expectedLog.map(logLine => logLine + engineNameOutput);
       do_print("expectedLog:\n" + expectedLog.join("\n"))
--- a/toolkit/components/search/tests/xpcshell/xpcshell.ini
+++ b/toolkit/components/search/tests/xpcshell/xpcshell.ini
@@ -14,9 +14,9 @@ skip-if = debug && os == "linux"
 [test_migratedb.js]
 [test_nodb.js]
 [test_nodb_pluschanges.js]
 [test_save_sorted_engines.js]
 [test_purpose.js]
 [test_defaultEngine.js]
 [test_prefSync.js]
 [test_notifications.js]
-
+[test_addEngine_callback.js]
--- a/toolkit/components/social/FrameWorker.jsm
+++ b/toolkit/components/social/FrameWorker.jsm
@@ -25,26 +25,26 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 this.EXPORTED_SYMBOLS = ["getFrameWorkerHandle"];
 
 var workerCache = {}; // keyed by URL.
 var _nextPortId = 1;
 
 // Retrieves a reference to a WorkerHandle associated with a FrameWorker and a
 // new ClientPort.
 this.getFrameWorkerHandle =
- function getFrameWorkerHandle(url, clientWindow, name, origin) {
+ function getFrameWorkerHandle(url, clientWindow, name, origin, exposeLocalStorage = false) {
   // first create the client port we are going to use.  Later we will
   // message the worker to create the worker port.
   let portid = _nextPortId++;
   let clientPort = new ClientPort(portid, clientWindow);
 
   let existingWorker = workerCache[url];
   if (!existingWorker) {
     // setup the worker and add this connection to the pending queue
-    let worker = new FrameWorker(url, name, origin);
+    let worker = new FrameWorker(url, name, origin, exposeLocalStorage);
     worker.pendingPorts.push(clientPort);
     existingWorker = workerCache[url] = worker;
   } else {
     // already have a worker - either queue or make the connection.
     if (existingWorker.loaded) {
       try {
         clientPort._createWorkerAndEntangle(existingWorker);
       }
@@ -64,25 +64,26 @@ this.getFrameWorkerHandle =
  * FrameWorker
  *
  * A FrameWorker is an iframe that is attached to the hiddenWindow,
  * which contains a pair of MessagePorts.  It is constructed with the
  * URL of some JavaScript that will be run in the context of the window;
  * the script does not have a full DOM but is instead run in a sandbox
  * that has a select set of methods cloned from the URL's domain.
  */
-function FrameWorker(url, name, origin) {
+function FrameWorker(url, name, origin, exposeLocalStorage) {
   this.url = url;
   this.name = name || url;
   this.ports = new Map();
   this.pendingPorts = [];
   this.loaded = false;
   this.reloading = false;
   this.origin = origin;
   this._injectController = null;
+  this.exposeLocalStorage = exposeLocalStorage;
 
   this.frame = makeHiddenFrame();
   this.load();
 }
 
 FrameWorker.prototype = {
   load: function FrameWorker_loadWorker() {
     this._injectController = function(doc, topic, data) {
@@ -128,21 +129,27 @@ FrameWorker.prototype = {
 
   createSandbox: function createSandbox() {
     let workerWindow = this.frame.contentWindow;
     let sandbox = new Cu.Sandbox(workerWindow);
 
     // copy the window apis onto the sandbox namespace only functions or
     // objects that are naturally a part of an iframe, I'm assuming they are
     // safe to import this way
-    let workerAPI = ['WebSocket', 'localStorage', 'atob', 'btoa',
+    let workerAPI = ['WebSocket', 'atob', 'btoa',
                      'clearInterval', 'clearTimeout', 'dump',
                      'setInterval', 'setTimeout', 'XMLHttpRequest',
                      'FileReader', 'Blob', 'EventSource', 'indexedDB',
                      'location'];
+
+    // Only expose localStorage if the caller opted-in
+    if (this.exposeLocalStorage) {
+      workerAPI.push('localStorage');
+    }
+
     // Bug 798660 - XHR and WebSocket have issues in a sandbox and need
     // to be unwrapped to work
     let needsWaive = ['XMLHttpRequest', 'WebSocket'];
     // Methods need to be bound with the proper |this|.
     let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval',
                      'setTimeout', 'clearTimeout'];
     workerAPI.forEach(function(fn) {
       try {
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -132,16 +132,52 @@ let SocialServiceInternal = {
         }
       });
     } finally {
       stmt.finalize();
     }
   }
 };
 
+XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
+  initService();
+  let providers = {};
+  for (let manifest of this.manifests) {
+    try {
+      if (ActiveProviders.has(manifest.origin)) {
+        let activationType = getOriginActivationType(manifest.origin);
+        let blessed = activationType == "builtin" ||
+                      activationType == "whitelist";
+        let provider = new SocialProvider(manifest, blessed);
+        providers[provider.origin] = provider;
+      }
+    } catch (err) {
+      Cu.reportError("SocialService: failed to load provider: " + manifest.origin +
+                     ", exception: " + err);
+    }
+  }
+  return providers;
+});
+
+function getOriginActivationType(origin) {
+  let prefname = SocialServiceInternal.getManifestPrefname(origin);
+  if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
+    return 'builtin';
+
+  let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
+  if (whitelist.indexOf(origin) >= 0)
+    return 'whitelist';
+
+  let directories = Services.prefs.getCharPref("social.directories").split(',');
+  if (directories.indexOf(origin) >= 0)
+    return 'directory';
+
+  return 'foreign';
+}
+
 let ActiveProviders = {
   get _providers() {
     delete this._providers;
     this._providers = {};
     try {
       let pref = Services.prefs.getComplexValue("social.activeProviders",
                                                 Ci.nsISupportsString);
       this._providers = JSON.parse(pref);
@@ -299,33 +335,16 @@ function initService() {
     // enabled providers are not migrated.
     Cu.reportError("Error migrating social settings: " + e);
   }
   // Initialize the MozSocialAPI
   if (SocialServiceInternal.enabled)
     MozSocialAPI.enabled = true;
 }
 
-XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
-  initService();
-  let providers = {};
-  for (let manifest of this.manifests) {
-    try {
-      if (ActiveProviders.has(manifest.origin)) {
-        let provider = new SocialProvider(manifest);
-        providers[provider.origin] = provider;
-      }
-    } catch (err) {
-      Cu.reportError("SocialService: failed to load provider: " + manifest.origin +
-                     ", exception: " + err);
-    }
-  }
-  return providers;
-});
-
 function schedule(callback) {
   Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL);
 }
 
 // Public API
 this.SocialService = {
   get enabled() {
     return SocialServiceInternal.enabled;
@@ -439,30 +458,18 @@ this.SocialService = {
     });
   },
 
   // Returns an array of installed providers, sorted by frecency
   getOrderedProviderList: function(onDone) {
     SocialServiceInternal.orderedProviders(onDone);
   },
 
-  getOriginActivationType: function(origin) {
-    let prefname = SocialServiceInternal.getManifestPrefname(origin);
-    if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
-      return 'builtin';
-
-    let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
-    if (whitelist.indexOf(origin) >= 0)
-      return 'whitelist';
-
-    let directories = Services.prefs.getCharPref("social.directories").split(',');
-    if (directories.indexOf(origin) >= 0)
-      return 'directory';
-
-    return 'foreign';
+  getOriginActivationType: function (origin) {
+    return getOriginActivationType(origin);
   },
 
   _providerListeners: new Map(),
   registerProviderListener: function registerProviderListener(listener) {
     this._providerListeners.set(listener, 1);
   },
   unregisterProviderListener: function unregisterProviderListener(listener) {
     this._providerListeners.delete(listener);
@@ -580,17 +587,17 @@ this.SocialService = {
       }.bind(this));
     }.bind(this));
   },
 
   _installProvider: function(aDOMDocument, data, installCallback) {
     let sourceURI = aDOMDocument.location.href;
     let installOrigin = aDOMDocument.nodePrincipal.origin;
 
-    let installType = this.getOriginActivationType(installOrigin);
+    let installType = getOriginActivationType(installOrigin);
     let manifest;
     if (data) {
       // if we get data, we MUST have a valid manifest generated from the data
       manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
       if (!manifest)
         throw new Error("SocialService.installProvider: service configuration is invalid from " + sourceURI);
     }
     let installer;
@@ -644,18 +651,19 @@ this.SocialService = {
 };
 
 /**
  * The SocialProvider object represents a social provider, and allows
  * access to its FrameWorker (if it has one).
  *
  * @constructor
  * @param {jsobj} object representing the manifest file describing this provider
+ * @param {bool} boolean indicating whether this provider is "built in"
  */
-function SocialProvider(input) {
+function SocialProvider(input, blessed = false) {
   if (!input.name)
     throw new Error("SocialProvider must be passed a name");
   if (!input.origin)
     throw new Error("SocialProvider must be passed an origin");
 
   let id = getAddonIDFromOrigin(input.origin);
   if (Services.blocklist.getAddonBlocklistState(id, input.version || "0") == Ci.nsIBlocklistService.STATE_BLOCKED)
     throw new Error("SocialProvider: provider with origin [" +
@@ -669,16 +677,17 @@ function SocialProvider(input) {
   this.sidebarURL = input.sidebarURL;
   this.shareURL = input.shareURL;
   this.origin = input.origin;
   let originUri = Services.io.newURI(input.origin, null, null);
   this.principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
   this.ambientNotificationIcons = {};
   this.errorState = null;
   this.frecency = 0;
+  this.blessed = blessed;
   try {
     this.domain = etld.getBaseDomainFromHost(originUri.host);
   } catch(e) {
     this.domain = originUri.host;
   }
 }
 
 SocialProvider.prototype = {
@@ -864,18 +873,22 @@ SocialProvider.prototype = {
    *
    * Returns null if this provider has no workerURL, or is disabled.
    *
    * @param {DOMWindow} window (optional)
    */
   getWorkerPort: function getWorkerPort(window) {
     if (!this.workerURL || !this.enabled)
       return null;
-    return getFrameWorkerHandle(this.workerURL, window,
-                                "SocialProvider:" + this.origin, this.origin).port;
+    // Only allow localStorage in the frameworker for blessed providers
+    let allowLocalStorage = this.blessed;
+    let handle = getFrameWorkerHandle(this.workerURL, window,
+                                      "SocialProvider:" + this.origin, this.origin,
+                                      allowLocalStorage);
+    return handle.port;
   },
 
   /**
    * Checks if a given URI is of the same origin as the provider.
    *
    * Returns true or false.
    *
    * @param {URI or string} uri
--- a/toolkit/components/social/test/browser/browser_frameworker.js
+++ b/toolkit/components/social/test/browser/browser_frameworker.js
@@ -236,26 +236,50 @@ let tests = {
           ok = localStorage["foo"] == 1;
         } catch (e) {
           port.postMessage({topic: "done", result: "FAILED to read localStorage, " + e.toString() });
           return;
         }
         port.postMessage({topic: "done", result: "ok"});
       }
     }
-    let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage");
+    let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testLocalStorage", null, true);
     worker.port.onmessage = function(e) {
       if (e.data.topic == "done") {
         is(e.data.result, "ok", "check the localStorage test worked");
         worker.terminate();
         cbnext();
       }
     }
   },
 
+  testNoLocalStorage: function(cbnext) {
+    let run = function() {
+      onconnect = function(e) {
+        let port = e.ports[0];
+        try {
+          localStorage.setItem("foo", "1");
+        } catch(e) {
+          port.postMessage({topic: "done", result: "ok"});
+          return;
+        }
+
+        port.postMessage({topic: "done", result: "FAILED because localStorage was exposed" });
+      }
+    }
+    let worker = getFrameWorkerHandle(makeWorkerUrl(run), undefined, "testNoLocalStorage");
+    worker.port.onmessage = function(e) {
+      if (e.data.topic == "done") {
+        is(e.data.result, "ok", "check that retrieving localStorage fails by default");
+        worker.terminate();
+        cbnext();
+      }
+    }
+  },
+
   testBase64: function (cbnext) {
     let run = function() {
       onconnect = function(e) {
         let port = e.ports[0];
         var ok = false;
         try {
           ok = btoa("1234") == "MTIzNA==";
         } catch(e) {
--- a/toolkit/crashreporter/google-breakpad/src/common/Makefile.in
+++ b/toolkit/crashreporter/google-breakpad/src/common/Makefile.in
@@ -74,17 +74,16 @@ OS_CXXFLAGS += -DHAVE_A_OUT_H
 endif
 ifeq (Darwin,$(OS_ARCH))
 HOST_CXXFLAGS += -DHAVE_MACH_O_NLIST_H
 OS_CXXFLAGS += -DHAVE_MACH_O_NLIST_H
 endif
 
 ifneq (WINNT,$(OS_TARGET))
 ifdef MOZ_CRASHREPORTER
-DISABLED_HOST_CSRCS = $(CSRCS)
 endif
 endif
 
 # need static lib
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -242,20 +242,22 @@ INNER_UNMAKE_PACKAGE = $(error Try using
 endif #Create an RPM file
 
 
 ifeq ($(MOZ_PKG_FORMAT),APK)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 include $(MOZILLA_DIR)/config/android-common.mk
 
+# DEBUG_JARSIGNER is defined by android-common.mk and always debug
+# signs.  We want to release sign if possible.
 ifdef MOZ_SIGN_CMD
-JARSIGNER := $(MOZ_SIGN_CMD) -f jar
+RELEASE_JARSIGNER := $(MOZ_SIGN_CMD) -f jar
 else
-JARSIGNER ?= echo
+RELEASE_JARSIGNER := $(DEBUG_JARSIGNER)
 endif
 
 DIST_FILES =
 
 # Place the files in the order they are going to be opened by the linker
 DIST_FILES += libmozalloc.so
 ifndef MOZ_FOLD_LIBS
 DIST_FILES += \
@@ -322,20 +324,22 @@ endif
 GECKO_APP_AP_PATH = $(call core_abspath,$(DEPTH)/mobile/android/base)
 
 ifdef ENABLE_TESTS
 INNER_ROBOCOP_PACKAGE=echo
 ifeq ($(MOZ_BUILD_APP),mobile/android)
 UPLOAD_EXTRA_FILES += robocop.apk
 UPLOAD_EXTRA_FILES += fennec_ids.txt
 ROBOCOP_PATH = $(call core_abspath,$(_ABS_DIST)/../build/mobile/robocop)
+# Robocop and Fennec need to be signed with the same key, which means
+# release signing them both.
 INNER_ROBOCOP_PACKAGE= \
   $(NSINSTALL) $(GECKO_APP_AP_PATH)/fennec_ids.txt $(_ABS_DIST) && \
-  cp $(ROBOCOP_PATH)/robocop-debug-signed-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \
-  $(JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \
+  cp $(ROBOCOP_PATH)/robocop-debug-unsigned-unaligned.apk $(_ABS_DIST)/robocop-unaligned.apk && \
+  $(RELEASE_JARSIGNER) $(_ABS_DIST)/robocop-unaligned.apk && \
   $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/robocop-unaligned.apk $(_ABS_DIST)/robocop.apk
 endif
 else
 INNER_ROBOCOP_PACKAGE=echo 'Testing is disabled - No Robocop for you'
 endif
 
 ifdef MOZ_OMX_PLUGIN
 DIST_FILES += libomxplugin.so libomxplugingb.so libomxplugingb235.so libomxpluginhc.so libomxpluginsony.so libomxpluginfroyo.so libomxpluginjb-htc.so
@@ -365,19 +369,20 @@ INNER_MAKE_PACKAGE	= \
     mv libmozglue.so $(MOZ_CHILD_PROCESS_NAME) lib/$(ABI_DIR) && \
     mv $(SO_LIBRARIES) assets && \
     unzip -o $(_ABS_DIST)/gecko.ap_ && \
     rm $(_ABS_DIST)/gecko.ap_ && \
     $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(ASSET_SO_LIBRARIES) && \
     $(ZIP) -r9D $(_ABS_DIST)/gecko.ap_ $(DIST_FILES) -x $(NON_DIST_FILES) $(SZIP_LIBRARIES) && \
     $(ZIP) -0 $(_ABS_DIST)/gecko.ap_ $(OMNIJAR_NAME)) && \
   rm -f $(_ABS_DIST)/gecko.apk && \
-  $(APKBUILDER) $(_ABS_DIST)/gecko.apk -v $(APKBUILDER_FLAGS) -z $(_ABS_DIST)/gecko.ap_ -f $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \
+  cp $(_ABS_DIST)/gecko.ap_ $(_ABS_DIST)/gecko.apk && \
+  $(ZIP) -vj0 $(_ABS_DIST)/gecko.apk $(STAGEPATH)$(MOZ_PKG_DIR)$(_BINPATH)/classes.dex && \
   cp $(_ABS_DIST)/gecko.apk $(_ABS_DIST)/gecko-unsigned-unaligned.apk && \
-  $(JARSIGNER) $(_ABS_DIST)/gecko.apk && \
+  $(RELEASE_JARSIGNER) $(_ABS_DIST)/gecko.apk && \
   $(ZIPALIGN) -f -v 4 $(_ABS_DIST)/gecko.apk $(PACKAGE) && \
   $(INNER_ROBOCOP_PACKAGE)
 
 INNER_UNMAKE_PACKAGE	= \
   mkdir $(MOZ_PKG_DIR) && \
   ( cd $(MOZ_PKG_DIR) && \
     $(UNZIP) $(UNPACKAGE) && \
     mv lib/$(ABI_DIR)/libmozglue.so . && \
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -1148,16 +1148,18 @@
  * @param   _VALICON
  *          The path to the binary that contains the icon group for the default icon
  *          followed by a comma and either the icon group's resource index or the icon
  *          group's resource id prefixed with a minus sign
  * @param   _DISPNAME
  *          The display name for the handler. If emtpy no value will be set.
  * @param   _ISPROTOCOL
  *          Sets protocol handler specific registry values when "true".
+ *          Deletes protocol handler specific registry values when "delete".
+ *          Otherwise doesn't touch handler specific registry values.
  * @param   _ISDDE
  *          Sets DDE specific registry values when "true".
  *
  * $R3 = string value of the current registry key path.
  * $R4 = _KEY
  * $R5 = _VALOPEN
  * $R6 = _VALICON
  * $R7 = _DISPNAME
@@ -1189,16 +1191,18 @@
       ReadRegStr $R3 SHCTX "$R4" "FriendlyTypeName"
 
       StrCmp "$R3" "" +1 +3
       WriteRegStr SHCTX "$R4" "" "$R7"
       WriteRegStr SHCTX "$R4" "FriendlyTypeName" "$R7"
 
       StrCmp "$R8" "true" +1 +2
       WriteRegStr SHCTX "$R4" "URL Protocol" ""
+      StrCmp "$R8" "delete" +1 +2
+      DeleteRegValue SHCTX "$R4" "URL Protocol"
       StrCpy $R3 ""
       ReadRegDWord $R3 SHCTX "$R4" "EditFlags"
       StrCmp $R3 "" +1 +3  ; Only add EditFlags if a value doesn't exist
       DeleteRegValue SHCTX "$R4" "EditFlags"
       WriteRegDWord SHCTX "$R4" "EditFlags" 0x00000002
 
       StrCmp "$R6" "" +2 +1
       WriteRegStr SHCTX "$R4\DefaultIcon" "" "$R6"
@@ -1292,16 +1296,18 @@
  * @param   _VALICON
  *          The path to the binary that contains the icon group for the default icon
  *          followed by a comma and either the icon group's resource index or the icon
  *          group's resource id prefixed with a minus sign
  * @param   _DISPNAME
  *          The display name for the handler. If emtpy no value will be set.
  * @param   _ISPROTOCOL
  *          Sets protocol handler specific registry values when "true".
+ *          Deletes protocol handler specific registry values when "delete".
+ *          Otherwise doesn't touch handler specific registry values.
  * @param   _DDE_APPNAME
  *          Sets DDE specific registry values when not an empty string.
  *
  * $R0 = storage for SOFTWARE\Classes
  * $R1 = string value of the current registry key path.
  * $R2 = _KEY
  * $R3 = _VALOPEN
  * $R4 = _VALICON
@@ -1342,16 +1348,18 @@
       ReadRegStr $R1 SHCTX "$R2" "FriendlyTypeName"
 
       StrCmp "$R1" "" +1 +3
       WriteRegStr SHCTX "$R0\$R2" "" "$R5"
       WriteRegStr SHCTX "$R0\$R2" "FriendlyTypeName" "$R5"
 
       StrCmp "$R6" "true" +1 +2
       WriteRegStr SHCTX "$R0\$R2" "URL Protocol" ""
+      StrCmp "$R6" "delete" +1 +2
+      DeleteRegValue SHCTX "$R0\$R2" "URL Protocol"
       StrCpy $R1 ""
       ReadRegDWord $R1 SHCTX "$R0\$R2" "EditFlags"
       StrCmp $R1 "" +1 +3  ; Only add EditFlags if a value doesn't exist
       DeleteRegValue SHCTX "$R0\$R2" "EditFlags"
       WriteRegDWord SHCTX "$R0\$R2" "EditFlags" 0x00000002
 
       StrCmp "$R4" "" +2 +1
       WriteRegStr SHCTX "$R0\$R2\DefaultIcon" "" "$R4"
@@ -1451,16 +1459,18 @@
  * @param   _VALICON
  *          The path to the binary that contains the icon group for the default icon
  *          followed by a comma and either the icon group's resource index or the icon
  *          group's resource id prefixed with a minus sign
  * @param   _DISPNAME
  *          The display name for the handler. If emtpy no value will be set.
  * @param   _ISPROTOCOL
  *          Sets protocol handler specific registry values when "true".
+ *          Deletes protocol handler specific registry values when "delete".
+ *          Otherwise doesn't touch handler specific registry values.
  *
  * $R3 = storage for SOFTWARE\Classes
  * $R4 = string value of the current registry key path.
  * $R5 = _KEY
  * $R6 = _VALOPEN
  * $R7 = _VALICON
  * $R8 = _DISPNAME
  * $R9 = _ISPROTOCOL
@@ -1468,17 +1478,17 @@
 !macro AddDisabledDDEHandlerValues
 
   !ifndef ${_MOZFUNC_UN}AddDisabledDDEHandlerValues
     !verbose push
     !verbose ${_MOZFUNC_VERBOSE}
     !define ${_MOZFUNC_UN}AddDisabledDDEHandlerValues "!insertmacro ${_MOZFUNC_UN}AddDisabledDDEHandlerValuesCall"
 
     Function ${_MOZFUNC_UN}AddDisabledDDEHandlerValues
-      Exch $R9 ; true if a protocol handler
+      Exch $R9 ; _ISPROTOCOL
       Exch 1
       Exch $R8 ; FriendlyTypeName
       Exch 2
       Exch $R7 ; icon index
       Exch 3
       Exch $R6 ; shell\open\command
       Exch 4
       Exch $R5 ; reg key
--- a/uriloader/exthandler/tests/mochitest/Makefile.in
+++ b/uriloader/exthandler/tests/mochitest/Makefile.in
@@ -11,11 +11,12 @@ relativesrcdir	= @relativesrcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MOCHITEST_FILES =	\
 		test_handlerApps.xhtml \
 		test_unsafeBidiChars.xhtml \
 		handlerApps.js \
 		handlerApp.xhtml \
 		unsafeBidiFileName.sjs \
+		test_badMimeType.html \
 		$(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/uriloader/exthandler/tests/mochitest/test_badMimeType.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Crashtest for bad MIME type</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+  const Cc = SpecialPowers.Cc;
+  const Ci = SpecialPowers.Ci;
+  const mimeService = Cc["@mozilla.org/mime;1"].
+                      getService(Ci.nsIMIMEService);
+  // "text/plain" has an 0xFF character appended to it.  This means it's an
+  // invalid string, which is tricky to enter using a text editor (I used
+  // emacs' hexl-mode).  It also means an ordinary text editor might drop it
+  // or convert it to something that *is* valid (in UTF8).  So we measure
+  // its length to make sure this hasn't happened.
+  var badMimeType = "text/plain˙";
+  ok(badMimeType.length == 11, "badMimeType has changed, making this test invalid");
+  mimeService.getFromTypeAndExtension(badMimeType, "txt");
+  ok(true, "The test shouldn't trigger a crash");
+</script>
+</pre>
+</body>
+</html>
--- a/view/public/nsViewManager.h
+++ b/view/public/nsViewManager.h
@@ -339,20 +339,16 @@ private:
   void ReparentWidgets(nsView* aView, nsView *aParent);
   void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion);
 
   void InvalidateViews(nsView *aView);
 
   // aView is the view for aWidget and aRegion is relative to aWidget.
   void Refresh(nsView *aView, const nsIntRegion& aRegion);
 
-  void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut);
-  void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
-                                          nscoord aY1, nscoord aY2, bool aInCutOut);
-
   // Utilities
 
   bool IsViewInserted(nsView *aView);
 
   /**
    * Intersects aRect with aView's bounds and then transforms it from aView's
    * coordinate system to the coordinate system of the widget attached to
    * aView.
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -20,16 +20,17 @@
 #include "nsPresContext.h"
 #include "nsEventStateManager.h"
 #include "mozilla/StartupTimeline.h"
 #include "GeckoProfiler.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
+#include "mozilla/layers/Compositor.h"
 
 /**
    XXX TODO XXX
 
    DeCOMify newly private methods
    Optimize view storage
 */
 
@@ -37,16 +38,18 @@
    A note about platform assumptions:
 
    We assume that a widget is z-ordered on top of its parent.
    
    We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
    we ask for a specific z-order, we don't assume that widget z-ordering actually works.
 */
 
+using namespace mozilla::layers;
+
 #define NSCOORD_NONE      INT32_MIN
 
 #undef DEBUG_MOUSE_LOCATION
 
 int32_t nsViewManager::mVMCount = 0;
 
 // Weakly held references to all of the view managers
 nsVoidArray* nsViewManager::gViewManagers = nullptr;
@@ -325,18 +328,24 @@ void nsViewManager::Refresh(nsView *aVie
                  "Widgets that we paint must all be display roots");
 
     if (mPresShell) {
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("--COMPOSITE-- %p\n", mPresShell);
       }
 #endif
-      mPresShell->Paint(aView, damageRegion,
-                        nsIPresShell::PAINT_COMPOSITE);
+      uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
+      LayerManager *manager = widget->GetLayerManager();
+      if (!manager->NeedsWidgetInvalidation()) {
+        manager->FlushRendering();
+      } else {
+        mPresShell->Paint(aView, damageRegion,
+                          paintFlags);
+      }
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("--ENDCOMPOSITE--\n");
       }
 #endif
       mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
     }
 
@@ -379,17 +388,16 @@ void nsViewManager::ProcessPendingUpdate
       for (nsViewManager *vm = this; vm;
            vm = vm->mRootView->GetParent()
                   ? vm->mRootView->GetParent()->GetViewManager()
                   : nullptr) {
         if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
             vm->mRootView->IsEffectivelyVisible() &&
             mPresShell && mPresShell->IsVisible()) {
           vm->FlushDelayedResize(true);
-          vm->InvalidateView(vm->mRootView);
         }
       }
 
       NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
 
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
@@ -837,21 +845,16 @@ nsViewManager::InsertChild(nsView *aPare
           aParent->InsertChild(aChild, kid);
           ReparentWidgets(aChild, aParent);
         }
       }
 
       // if the parent view is marked as "floating", make the newly added view float as well.
       if (aParent->GetFloating())
         aChild->SetFloating(true);
-
-      //and mark this area as dirty if the view is visible...
-
-      if (nsViewVisibility_kHide != aChild->GetVisibility())
-        aChild->GetViewManager()->InvalidateView(aChild);
     }
 }
 
 void
 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex)
 {
   // no-one really calls this with anything other than aZIndex == 0 on a fresh view
   // XXX this method should simply be eliminated and its callers redirected to the real method
@@ -864,103 +867,35 @@ nsViewManager::RemoveChild(nsView *aChil
 {
   NS_ASSERTION(aChild, "aChild must not be null");
 
   nsView* parent = aChild->GetParent();
 
   if (nullptr != parent) {
     NS_ASSERTION(aChild->GetViewManager() == this ||
                  parent->GetViewManager() == this, "wrong view manager");
-    aChild->GetViewManager()->InvalidateView(aChild);
     parent->RemoveChild(aChild);
   }
 }
 
 void
 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
-  nsPoint oldPt = aView->GetPosition();
-  nsRect oldBounds = aView->GetBoundsInParentUnits();
   aView->SetPosition(aX, aY);
-
-  // only do damage control if the view is visible
-
-  if ((aX != oldPt.x) || (aY != oldPt.y)) {
-    if (aView->GetVisibility() != nsViewVisibility_kHide) {
-      nsView* parentView = aView->GetParent();
-      if (parentView) {
-        nsViewManager* parentVM = parentView->GetViewManager();
-        parentVM->InvalidateView(parentView, oldBounds);
-        parentVM->InvalidateView(parentView, aView->GetBoundsInParentUnits());
-      }
-    }
-  }
-}
-
-void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
-  nscoord aY1, nscoord aY2, bool aInCutOut) {
-  nscoord height = aY2 - aY1;
-  if (aRect.x < aCutOut.x) {
-    nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
-    InvalidateView(aView, r);
-  }
-  if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
-    nsRect r(aCutOut.x, aY1, aCutOut.width, height);
-    InvalidateView(aView, r);
-  }
-  if (aCutOut.XMost() < aRect.XMost()) {
-    nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
-    InvalidateView(aView, r);
-  }
-}
-
-void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) {
-  NS_ASSERTION(aView->GetViewManager() == this,
-               "InvalidateRectDifference called on view we don't own");
-  if (aRect.y < aCutOut.y) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false);
-  }
-  if (aCutOut.y < aCutOut.YMost()) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true);
-  }
-  if (aCutOut.YMost() < aRect.YMost()) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false);
-  }
 }
 
 void
 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   nsRect oldDimensions = aView->GetDimensions();
   if (!oldDimensions.IsEqualEdges(aRect)) {
-    // resize the view.
-    // Prevent Invalidation of hidden views 
-    if (aView->GetVisibility() == nsViewVisibility_kHide) {
-      aView->SetDimensions(aRect, false);
-    } else {
-      nsView* parentView = aView->GetParent();
-      if (!parentView) {
-        parentView = aView;
-      }
-      nsRect oldBounds = aView->GetBoundsInParentUnits();
-      aView->SetDimensions(aRect, true);
-      nsViewManager* parentVM = parentView->GetViewManager();
-      if (!aRepaintExposedAreaOnly) {
-        // Invalidate the union of the old and new size
-        InvalidateView(aView, aRect);
-        parentVM->InvalidateView(parentView, oldBounds);
-      } else {
-        InvalidateRectDifference(aView, aRect, oldDimensions);
-        nsRect newBounds = aView->GetBoundsInParentUnits();
-        parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds);
-      } 
-    }
+    aView->SetDimensions(aRect, true);
   }
 
   // Note that if layout resizes the view and the view has a custom clip
   // region set, then we expect layout to update the clip region too. Thus
   // in the case where mClipRect has been optimized away to just be a null
   // pointer, and this resize is implicitly changing the clip rect, it's OK
   // because layout will change it back again if necessary.
 }
@@ -975,31 +910,16 @@ nsViewManager::SetViewFloating(nsView *a
 
 void
 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   if (aVisible != aView->GetVisibility()) {
     aView->SetVisibility(aVisible);
-
-    if (IsViewInserted(aView)) {
-      if (!aView->HasWidget()) {
-        if (nsViewVisibility_kHide == aVisible) {
-          nsView* parentView = aView->GetParent();
-          if (parentView) {
-            parentView->GetViewManager()->
-              InvalidateView(parentView, aView->GetBoundsInParentUnits());
-          }
-        }
-        else {
-          InvalidateView(aView);
-        }
-      }
-    }
   }
 }
 
 bool nsViewManager::IsViewInserted(nsView *aView)
 {
   if (mRootView == aView) {
     return true;
   } else if (aView->GetParent() == nullptr) {
@@ -1022,30 +942,21 @@ nsViewManager::SetViewZIndex(nsView *aVi
   NS_ASSERTION((aView != nullptr), "no view");
 
   // don't allow the root view's z-index to be changed. It should always be zero.
   // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
   if (aView == mRootView) {
     return;
   }
 
-  bool oldTopMost = aView->IsTopMost();
-  bool oldIsAuto = aView->GetZIndexIsAuto();
-
   if (aAutoZIndex) {
     aZIndex = 0;
   }
 
-  int32_t oldidx = aView->GetZIndex();
   aView->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
-
-  if (oldidx != aZIndex || oldTopMost != aTopMost ||
-      oldIsAuto != aAutoZIndex) {
-    InvalidateView(aView);
-  }
 }
 
 nsViewManager*
 nsViewManager::IncrementDisableRefreshCount()
 {
   if (!IsRootVM()) {
     return RootViewManager()->IncrementDisableRefreshCount();
   }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1381,16 +1381,20 @@ static void blinkRgn(RgnHandle rgn)
 // Invalidate this component's visible area
 NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (!mView || !mVisible)
     return NS_OK;
 
+  NS_ASSERTION(GetLayerManager()->GetBackendType() != LAYERS_CLIENT ||
+               Compositor::GetBackend() == LAYERS_BASIC,
+               "Shouldn't need to invalidate with accelerated OMTC layers!");
+
   if ([NSView focusView]) {
     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
     // don't lose it.
     [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
   else {
     [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -41,16 +41,17 @@ SDK_HEADERS = \
 		nsIClassInfoImpl.h \
 		nsID.h \
 		nsIInterfaceRequestorUtils.h \
 		nsINIParser.h \
 		nsISupportsImpl.h \
 		nsISupportsUtils.h \
 		nsIWeakReferenceUtils.h \
 		nsInterfaceHashtable.h \
+		nsJSThingHashtable.h \
 		nsMemory.h \
 		nsQuickSort.h \
 		nsRefPtrHashtable.h \
 		nsServiceManagerUtils.h \
 		nsStringAPI.h \
 		nsStringGlue.h \
 		nsTArray.h \
 		nsTArray-inl.h \
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -64,39 +64,39 @@ CycleCollectionNoteEdgeNameImpl(nsCycleC
   nsAutoCString arrayEdgeName(aName);
   if (aFlags & CycleCollectionEdgeNameArrayFlag) {
     arrayEdgeName.AppendLiteral("[i]");
   }
   aCallback.NoteNextEdgeName(arrayEdgeName.get());
 }
 
 void
-TraceCallbackFunc::Trace(JS::Value* p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const
 {
-  mCallback(JSVAL_TO_TRACEABLE(*p), name, closure);
+  mCallback(JSVAL_TO_TRACEABLE(p->get()), name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(jsid* p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<jsid>* p, const char* name, void* closure) const
 {
   void *thing = JSID_TO_GCTHING(*p);
   if (thing) {
     mCallback(thing, name, closure);
   }
 }
 
 void
-TraceCallbackFunc::Trace(JSObject** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const
 {
   mCallback(*p, name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(JSString** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const
 {
   mCallback(*p, name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(JSScript** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const
 {
-   mCallback(*p, name, closure);
+  mCallback(p->get(), name, closure);
 }
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -46,49 +46,49 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCol
 
 /**
  * Forward declarations
  */
 class nsCycleCollectionParticipant;
 class nsScriptObjectTracer;
 class nsXPCOMCycleCollectionParticipant;
 
+namespace JS {
+template <class T> class Heap;
+} /* namespace JS */
+
 /*
  * A struct defining pure virtual methods which are called when tracing cycle
  * collection paticipants.  The appropriate method is called depending on the
  * type of JS GC thing.
  */
 struct TraceCallbacks
 {
-    virtual void Trace(JS::Value* p, const char* name, void* closure) const = 0;
-    virtual void Trace(jsid* p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSObject** p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSString** p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSScript** p, const char* name, void* closure) const = 0;
-
-    void Trace(JSFlatString** p, const char* name, void* closure) const {
-        Trace(reinterpret_cast<JSString**>(p), name, closure);
-    }
+    virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const = 0;
 };
 
 /*
  * An implementation of TraceCallbacks that calls a single function for all JS
  * GC thing types encountered.
  */
 struct TraceCallbackFunc : public TraceCallbacks
 {
     typedef void (* Func)(void* p, const char* name, void* closure);
 
     explicit TraceCallbackFunc(Func cb) : mCallback(cb) {}
 
-    virtual void Trace(JS::Value* p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(jsid* p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSObject** p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSString** p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSScript** p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
 
   private:
     Func mCallback;
 };
 
 /**
  * VTables
  *
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsJSThingHashtable.h
@@ -0,0 +1,58 @@
+/* -*- 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/. */
+
+#ifndef nsJSThingHashtable_h__
+#define nsJSThingHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
+/**
+ * A wrapper for hash keys that sets ALLOW_MEMMOVE to false.
+ *
+ * This is used in the implementation of nsJSThingHashtable and is not intended
+ * to be used directly.
+ *
+ * It is necessary for hash tables containing JS::Heap<T> values as these must
+ * be copied rather than memmoved.
+ */
+template<class T>
+class nsHashKeyDisallowMemmove : public T
+{
+ public:
+  nsHashKeyDisallowMemmove(const T& key) : T(key) {}
+  enum { ALLOW_MEMMOVE = false };
+};
+
+
+/**
+ * Templated hashtable class for use on the heap where the values are JS GC things.
+ *
+ * Storing JS GC thing pointers on the heap requires wrapping them in a
+ * JS::Heap<T>, and this class takes care of that while presenting an interface
+ * in terms of the wrapped type T.
+ *
+ * For example, to store a hashtable mapping strings to JSObject pointers, you
+ * can declare a data member like this:
+ *
+ *   nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap;
+ *
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ *   for a complete specification.
+ * @param DataType the datatype being wrapped, must be a JS GC thing.
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass,class DataType>
+class nsJSThingHashtable :
+  public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>, JS::Heap<DataType>, DataType>
+{ };
+
+#endif // nsJSThingHashtable_h__
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -3,32 +3,32 @@
 /* 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 nsTArray_h__
 #  error "Don't include this file directly"
 #endif
 
-template<class Alloc>
-nsTArray_base<Alloc>::nsTArray_base()
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::nsTArray_base()
   : mHdr(EmptyHdr()) {
   MOZ_COUNT_CTOR(nsTArray_base);
 }
 
-template<class Alloc>
-nsTArray_base<Alloc>::~nsTArray_base() {
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::~nsTArray_base() {
   if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
     Alloc::Free(mHdr);
   }
   MOZ_COUNT_DTOR(nsTArray_base);
 }
 
-template<class Alloc>
-const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
+template<class Alloc, class Copy>
+const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
 
   const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf;
 
   // If we're on a 32-bit system and elemAlign is 8, we need to adjust our
   // pointer to take into account the extra alignment in the auto array.
 
@@ -43,18 +43,18 @@ const nsTArrayHeader* nsTArray_base<Allo
   NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment.");
   if (sizeof(void*) == 4 && elemAlign == 8) {
     autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
   }
 
   return reinterpret_cast<const Header*>(autoBuf);
 }
 
-template<class Alloc>
-bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
+template<class Alloc, class Copy>
+bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const {
   if (!mHdr->mIsAutoArray) {
     return false;
   }
 
   // This is nuts.  If we were sane, we'd pass elemAlign as a parameter to
   // this function.  Unfortunately this function is called in nsTArray_base's
   // destructor, at which point we don't know elem_type's alignment.
   //
@@ -91,19 +91,19 @@ bool nsTArray_base<Alloc>::UsesAutoArray
                    reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
   NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect.");
 #endif
 
   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
 }
 
 
-template<class Alloc>
+template<class Alloc, class Copy>
 typename Alloc::ResultTypeProxy
-nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) {
+nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) {
   // This should be the most common case so test this first
   if (capacity <= mHdr->mCapacity)
     return Alloc::SuccessResult();
 
   // If the requested memory allocation exceeds size_type(-1)/2, then
   // our doubling algorithm may not be able to allocate it.
   // Additionally we couldn't fit in the Header::mCapacity
   // member. Just bail out in cases like that.  We don't want to be
@@ -150,23 +150,26 @@ nsTArray_base<Alloc>::EnsureCapacity(siz
     bytesToAlloc |= bytesToAlloc >> 16;
     bytesToAlloc++;
 
     MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0,
                "nsTArray's allocation size should be a power of two!");
   }
 
   Header *header;
-  if (UsesAutoArrayBuffer()) {
+  if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
     // Malloc() and copy
     header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc));
     if (!header)
       return Alloc::FailureResult();
 
-    memcpy(header, mHdr, sizeof(Header) + Length() * elemSize);
+    Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
+
+    if (!UsesAutoArrayBuffer())
+      Alloc::Free(mHdr);
   } else {
     // Realloc() existing data
     header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc));
     if (!header)
       return Alloc::FailureResult();
   }
 
   // How many elements can we fit in bytesToAlloc?
@@ -174,33 +177,33 @@ nsTArray_base<Alloc>::EnsureCapacity(siz
   MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!");
   header->mCapacity = newCapacity;
 
   mHdr = header;
 
   return Alloc::SuccessResult();
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 void
-nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
+nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
   if (mHdr == EmptyHdr() || UsesAutoArrayBuffer())
     return;
 
   if (mHdr->mLength >= mHdr->mCapacity)  // should never be greater than...
     return;
 
   size_type length = Length();
 
   if (IsAutoArray() && GetAutoArrayBuffer(elemAlign)->mCapacity >= length) {
     Header* header = GetAutoArrayBuffer(elemAlign);
 
     // Copy data, but don't copy the header to avoid overwriting mCapacity
     header->mLength = length;
-    memcpy(header + 1, mHdr + 1, length * elemSize);
+    Copy::CopyElements(header + 1, mHdr + 1, length, elemSize);
 
     Alloc::Free(mHdr);
     mHdr = header;
     return;
   }
 
   if (length == 0) {
     MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
@@ -212,19 +215,19 @@ nsTArray_base<Alloc>::ShrinkCapacity(siz
   size_type size = sizeof(Header) + length * elemSize;
   void *ptr = Alloc::Realloc(mHdr, size);
   if (!ptr)
     return;
   mHdr = static_cast<Header*>(ptr);
   mHdr->mCapacity = length;
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 void
-nsTArray_base<Alloc>::ShiftData(index_type start,
+nsTArray_base<Alloc, Copy>::ShiftData(index_type start,
                                 size_type oldLen, size_type newLen,
                                 size_type elemSize, size_t elemAlign) {
   if (oldLen == newLen)
     return;
 
   // Determine how many elements need to be shifted
   size_type num = mHdr->mLength - (start + oldLen);
 
@@ -235,88 +238,86 @@ nsTArray_base<Alloc>::ShiftData(index_ty
   } else {
     // Maybe nothing needs to be shifted
     if (num == 0)
       return;
     // Perform shift (change units to bytes first)
     start *= elemSize;
     newLen *= elemSize;
     oldLen *= elemSize;
-    num *= elemSize;
     char *base = reinterpret_cast<char*>(mHdr + 1) + start;
-    memmove(base + newLen, base + oldLen, num);
+    Copy::MoveElements(base + newLen, base + oldLen, num, elemSize);
   }
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 bool
-nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count,
+nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count,
                                     size_type elementSize, size_t elemAlign)  {
   MOZ_ASSERT(index <= Length(), "Bogus insertion index");
   size_type newLen = Length() + count;
 
   EnsureCapacity(newLen, elementSize);
 
   // Check for out of memory conditions
   if (Capacity() < newLen)
     return false;
 
   // Move the existing elements as needed.  Note that this will
   // change our mLength, so no need to call IncrementLength.
   ShiftData(index, 0, count, elementSize, elemAlign);
-      
+
   return true;
 }
 
 // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
 // |nsTArray_base &array| in its constructor.  When it's destructed, it ensures
 // that
 //
 //   * array.mIsAutoArray has the same value as it did when we started, and
 //   * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
 //     array.mHdr points to array's auto buffer.
 
-template<class Alloc>
-nsTArray_base<Alloc>::IsAutoArrayRestorer::IsAutoArrayRestorer(
-  nsTArray_base<Alloc> &array,
-  size_t elemAlign) 
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
+  nsTArray_base<Alloc, Copy> &array,
+  size_t elemAlign)
   : mArray(array),
     mElemAlign(elemAlign),
     mIsAuto(array.IsAutoArray())
 {
 }
 
-template<class Alloc>
-nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
   // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
   if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
     // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
     // that mHdr->mIsAutoArray is true, which surely isn't the case here.
     mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
     mArray.mHdr->mLength = 0;
   }
   else if (mArray.mHdr != mArray.EmptyHdr()) {
     mArray.mHdr->mIsAutoArray = mIsAuto;
   }
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 template<class Allocator>
 typename Alloc::ResultTypeProxy
-nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
-                                        size_type elemSize,
-                                        size_t elemAlign) {
+nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
+                                              size_type elemSize, size_t elemAlign) {
 
   // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
   // auto buffer.  We need to point mHdr back to our auto buffer before we
   // return, otherwise we'll forget that we have an auto buffer at all!
   // IsAutoArrayRestorer takes care of this for us.
 
   IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign);
-  typename nsTArray_base<Allocator>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
+  typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
 
   // If neither array uses an auto buffer which is big enough to store the
   // other array's elements, then ensure that both arrays use malloc'ed storage
   // and swap their mHdr pointers.
   if ((!UsesAutoArrayBuffer() || Capacity() < other.Length()) &&
       (!other.UsesAutoArrayBuffer() || other.Capacity() < Length())) {
 
     if (!EnsureNotUsingAutoArrayBuffer(elemSize) ||
@@ -326,17 +327,17 @@ nsTArray_base<Alloc>::SwapArrayElements(
 
     Header *temp = mHdr;
     mHdr = other.mHdr;
     other.mHdr = temp;
 
     return Alloc::SuccessResult();
   }
 
-  // Swap the two arrays using memcpy, since at least one is using an auto
+  // Swap the two arrays by copying, since at least one is using an auto
   // buffer which is large enough to hold all of the other's elements.  We'll
   // copy the shorter array into temporary storage.
   //
   // (We could do better than this in some circumstances.  Suppose we're
   // swapping arrays X and Y.  X has space for 2 elements in its auto buffer,
   // but currently has length 4, so it's using malloc'ed storage.  Y has length
   // 2.  When we swap X and Y, we don't need to use a temporary buffer; we can
   // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
@@ -370,34 +371,34 @@ nsTArray_base<Alloc>::SwapArrayElements(
   // job for AutoTArray!  (One of the two arrays we're swapping is using an
   // auto buffer, so we're likely not allocating a lot of space here.  But one
   // could, in theory, allocate a huge AutoTArray on the heap.)
   nsAutoArrayBase<nsTArray_Impl<uint8_t, Alloc>, 64> temp;
   if (!Alloc::Successful(temp.EnsureCapacity(smallerLength, elemSize))) {
     return Alloc::FailureResult();
   }
 
-  memcpy(temp.Elements(), smallerElements, smallerLength * elemSize);
-  memcpy(smallerElements, largerElements, largerLength * elemSize);
-  memcpy(largerElements, temp.Elements(), smallerLength * elemSize);
+  Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize);
+  Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize);
+  Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize);
 
   // Swap the arrays' lengths.
   NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) &&
                     (Length() == 0 || other.mHdr != EmptyHdr()),
                     "Don't set sEmptyHdr's length.");
   size_type tempLength = Length();
   mHdr->mLength = other.Length();
   other.mHdr->mLength = tempLength;
 
   return Alloc::SuccessResult();
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 bool
-nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
+nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
   if (UsesAutoArrayBuffer()) {
 
     // If you call this on a 0-length array, we'll set that array's mHdr to
     // sEmptyHdr, in flagrant violation of the nsAutoTArray invariants.  It's
     // up to you to set it back!  (If you don't, the nsAutoTArray will forget
     // that it has an auto buffer.)
     if (Length() == 0) {
       mHdr = EmptyHdr();
@@ -405,15 +406,15 @@ nsTArray_base<Alloc>::EnsureNotUsingAuto
     }
 
     size_type size = sizeof(Header) + Length() * elemSize;
 
     Header* header = static_cast<Header*>(Alloc::Malloc(size));
     if (!header)
       return false;
 
-    memcpy(header, mHdr, size);
+    Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
     header->mCapacity = Length();
     mHdr = header;
   }
-  
+
   return true;
 }
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -17,16 +17,21 @@
 #include "nsCycleCollectionNoteChild.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
 #include "nsDebug.h"
 #include "nsTraceRefcnt.h"
 #include NEW_H
 
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
 //
 // nsTArray is a resizable array class, like std::vector.
 //
 // Unlike std::vector, which follows C++'s construction/destruction rules,
 // nsTArray assumes that your "T" can be memmoved()'ed safely.
 //
 // The public classes defined in this header are
 //
@@ -51,16 +56,19 @@
 // The template parameter (i.e., T in nsTArray<T>) specifies the type of the
 // elements and has the following requirements:
 //
 //   T MUST be safely memmove()'able.
 //   T MUST define a copy-constructor.
 //   T MAY define operator< for sorting.
 //   T MAY define operator== for searching.
 //
+// (Note that the memmove requirement may be relaxed for certain types - see
+// nsTArray_CopyElements below.)
+//
 // For methods taking a Comparator instance, the Comparator must be a class
 // defining the following methods:
 //
 //   class Comparator {
 //     public:
 //       /** @return True if the elements are equals; false otherwise. */
 //       bool Equals(const elem_type& a, const Item& b) const;
 //
@@ -337,23 +345,23 @@ struct nsTArray_SafeElementAtHelper<nsRe
 {
 };
 
 //
 // This class serves as a base class for nsTArray.  It shouldn't be used
 // directly.  It holds common implementation code that does not depend on the
 // element type of the nsTArray.
 //
-template<class Alloc>
+template<class Alloc, class Copy>
 class nsTArray_base
 {
   // Allow swapping elements with |nsTArray_base|s created using a
   // different allocator.  This is kosher because all allocators use
   // the same free().
-  template<class Allocator>
+  template<class Allocator, class Copier>
   friend class nsTArray_base;
 
 protected:
   typedef nsTArrayHeader Header;
 
 public:
   typedef uint32_t size_type;
   typedef uint32_t index_type;
@@ -391,17 +399,17 @@ protected:
   // @param elemSize     The size of an array element.
   // @return False if insufficient memory is available; true otherwise.
   typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize);
 
   // Resize the storage to the minimum required amount.
   // @param elemSize     The size of an array element.
   // @param elemAlign    The alignment in bytes of an array element.
   void ShrinkCapacity(size_type elemSize, size_t elemAlign);
-    
+
   // This method may be called to resize a "gap" in the array by shifting
   // elements around.  It updates mLength appropriately.  If the resulting
   // array has zero elements, then the array's memory is free'd.
   // @param start        The starting index of the gap.
   // @param oldLen       The current length of the gap.
   // @param newLen       The desired length of the gap.
   // @param elemSize     The size of an array element.
   // @param elemAlign    The alignment in bytes of an array element.
@@ -430,28 +438,28 @@ protected:
   // @param elementSize the size of an array element.
   // @param elemAlign the alignment in bytes of an array element.
   bool InsertSlotsAt(index_type index, size_type count,
                        size_type elementSize, size_t elemAlign);
 
 protected:
   template<class Allocator>
   typename Alloc::ResultTypeProxy
-  SwapArrayElements(nsTArray_base<Allocator>& other,
+  SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
                     size_type elemSize,
                     size_t elemAlign);
 
   // This is an RAII class used in SwapArrayElements.
   class IsAutoArrayRestorer {
     public:
-      IsAutoArrayRestorer(nsTArray_base<Alloc> &array, size_t elemAlign);
+      IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign);
       ~IsAutoArrayRestorer();
 
     private:
-      nsTArray_base<Alloc> &mArray;
+      nsTArray_base<Alloc, Copy> &mArray;
       size_t mElemAlign;
       bool mIsAuto;
   };
 
   // Helper function for SwapArrayElements. Ensures that if the array
   // is an nsAutoTArray that it doesn't use the built-in buffer.
   bool EnsureNotUsingAutoArrayBuffer(size_type elemSize);
 
@@ -468,30 +476,30 @@ protected:
   const Header* GetAutoArrayBuffer(size_t elemAlign) const {
     MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
     return GetAutoArrayBufferUnsafe(elemAlign);
   }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
   // assert that we are an nsAutoTArray.
   Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
-    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)->
+    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)->
                                GetAutoArrayBufferUnsafe(elemAlign));
   }
   const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
 
   // Returns true if this is an nsAutoTArray and it currently uses the
   // built-in buffer to store its elements.
   bool UsesAutoArrayBuffer() const;
 
   // The array's elements (prefixed with a Header).  This pointer is never
   // null.  If the array is empty, then this will point to sEmptyHdr.
   Header *mHdr;
 
-  Header* Hdr() const { 
+  Header* Hdr() const {
     return mHdr;
   }
 
   Header** PtrToHdr() {
     return &mHdr;
   }
 
   static Header* EmptyHdr() {
@@ -559,38 +567,172 @@ struct AssignRangeAlgorithm<true, true> 
   template<class Item, class ElemType, class IndexType, class SizeType>
   static void implementation(ElemType* elements, IndexType start,
                              SizeType count, const Item *values) {
     memcpy(elements + start, values, count * sizeof(ElemType));
   }
 };
 
 //
+// Normally elements are copied with memcpy and memmove, but for some element
+// types that is problematic.  The nsTArray_CopyElements template class can be
+// specialized to ensure that copying calls constructors and destructors
+// instead, as is done below for JS::Heap<E> elements.
+//
+
+//
+// A class that defines how to copy elements using memcpy/memmove.
+//
+struct nsTArray_CopyWithMemutils
+{
+  const static bool allowRealloc = true;
+
+  static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memcpy(dest, src, count * elemSize);
+  }
+
+  static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize);
+  }
+
+  static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memmove(dest, src, count * elemSize);
+  }
+};
+
+//
+// A template class that defines how to copy elements calling their constructors
+// and destructors appropriately.
+//
+template <class ElemType>
+struct nsTArray_CopyWithConstructors
+{
+  typedef nsTArrayElementTraits<ElemType> traits;
+
+  const static bool allowRealloc = false;
+
+  static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) {
+    ElemType* destElem = static_cast<ElemType*>(dest);
+    ElemType* srcElem = static_cast<ElemType*>(src);
+    ElemType* destElemEnd = destElem + count;
+#ifdef DEBUG
+    ElemType* srcElemEnd = srcElem + count;
+    MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
+#endif
+    while (destElem != destElemEnd) {
+      traits::Construct(destElem, *srcElem);
+      traits::Destruct(srcElem);
+      ++destElem;
+      ++srcElem;
+    }
+  }
+
+  static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) {
+    nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest);
+    nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src);
+    *destHeader = *srcHeader;
+    CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader),
+                 static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader),
+                 count, elemSize);
+  }
+
+  static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) {
+    ElemType* destElem = static_cast<ElemType*>(dest);
+    ElemType* srcElem = static_cast<ElemType*>(src);
+    ElemType* destElemEnd = destElem + count;
+    ElemType* srcElemEnd = srcElem + count;
+    if (destElem == srcElem) {
+      return;  // In practice, we don't do this.
+    } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
+      while (destElemEnd != destElem) {
+        --destElemEnd;
+        --srcElemEnd;
+        traits::Construct(destElemEnd, *srcElemEnd);
+        traits::Destruct(srcElem);
+      }
+    } else {
+      CopyElements(dest, src, count, elemSize);
+    }
+  }
+};
+
+//
+// The default behaviour is to use memcpy/memmove for everything.
+//
+template <class E>
+struct nsTArray_CopyElements : public nsTArray_CopyWithMemutils {};
+
+//
+// JS::Heap<E> elements require constructors/destructors to be called and so is
+// specialized here.
+//
+template <class E>
+struct nsTArray_CopyElements<JS::Heap<E> > : public nsTArray_CopyWithConstructors<E> {};
+
+//
+// Base class for nsTArray_Impl that is templated on element type and derived
+// nsTArray_Impl class, to allow extra conversions to be added for specific
+// types.
+//
+template <class E, class Derived>
+struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {};
+
+//
+// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
+// elements.
+//
+// These conversions are safe because JS::Heap<E> and E share the same
+// representation, and since the result of the conversions are const references
+// we won't miss any barriers.
+//
+// The static_cast is necessary to obtain the correct address for the derived
+// class since we are a base class used in multiple inheritance.
+//
+template <class E, class Derived>
+struct nsTArray_TypedBase<JS::Heap<E>, Derived>
+ : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
+{
+  operator const nsTArray<E>& () {
+    MOZ_STATIC_ASSERT(sizeof(E) == sizeof(JS::Heap<E>),
+                      "JS::Heap<E> must be binary compatible with E.");
+    Derived* self = static_cast<Derived*>(this);
+    return *reinterpret_cast<nsTArray<E> *>(self);
+  }
+
+  operator const FallibleTArray<E>& () {
+    Derived* self = static_cast<Derived*>(this);
+    return *reinterpret_cast<FallibleTArray<E> *>(self);
+  }
+};
+
+
+//
 // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
 // nsAutoTArray, and AutoFallibleTArray.
 //
 // The only situation in which you might need to use nsTArray_Impl in your code
 // is if you're writing code which mutates a TArray which may or may not be
 // infallible.
 //
 // Code which merely reads from a TArray which may or may not be infallible can
 // simply cast the TArray to |const nsTArray&|; both fallible and infallible
 // TArrays can be cast to |const nsTArray&|.
 //
 template<class E, class Alloc>
-class nsTArray_Impl : public nsTArray_base<Alloc>,
-                      public nsTArray_SafeElementAtHelper<E, nsTArray_Impl<E, Alloc> >
+class nsTArray_Impl : public nsTArray_base<Alloc, nsTArray_CopyElements<E> >,
+                      public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> >
 {
 public:
-  typedef nsTArray_base<Alloc>           base_type;
-  typedef typename base_type::size_type  size_type;
-  typedef typename base_type::index_type index_type;
-  typedef E                              elem_type;
-  typedef nsTArray_Impl<E, Alloc>        self_type;
-  typedef nsTArrayElementTraits<E>       elem_traits;
+  typedef nsTArray_CopyElements<E>                   copy_type;
+  typedef nsTArray_base<Alloc, copy_type>            base_type;
+  typedef typename base_type::size_type              size_type;
+  typedef typename base_type::index_type             index_type;
+  typedef E                                          elem_type;
+  typedef nsTArray_Impl<E, Alloc>                    self_type;
+  typedef nsTArrayElementTraits<E>                   elem_traits;
   typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
 
   using safeelementat_helper_type::SafeElementAt;
   using base_type::EmptyHdr;
 
   // A special value that is used to indicate an invalid or unknown index
   // into the array.
   enum {
@@ -711,17 +853,17 @@ public:
   }
 
   // This method provides direct, readonly access to the array elements.
   // @return A pointer to the first element of the array.  If the array is
   // empty, then this pointer must not be dereferenced.
   const elem_type* Elements() const {
     return reinterpret_cast<const elem_type *>(Hdr() + 1);
   }
-    
+
   // This method provides direct access to the i'th element of the array.
   // The given index must be within the array bounds.
   // @param i  The index of an element in the array.
   // @return   A reference to the i'th element of the array.
   elem_type& ElementAt(index_type i) {
     MOZ_ASSERT(i < Length(), "invalid array index");
     return Elements()[i];
   }
@@ -1078,28 +1220,28 @@ public:
 
   // Append a new element without copy-constructing. This is useful to avoid
   // temporaries.
   // @return A pointer to the newly appended element, or null on OOM.
   elem_type *AppendElement() {
     return AppendElements(1);
   }
 
-  // Move all elements from another array to the end of this array without 
+  // Move all elements from another array to the end of this array without
   // calling copy constructors or destructors.
   // @return A pointer to the newly appended elements, or null on OOM.
   template<class Item, class Allocator>
   elem_type *MoveElementsFrom(nsTArray_Impl<Item, Allocator>& array) {
     MOZ_ASSERT(&array != this, "argument must be different array");
     index_type len = Length();
     index_type otherLen = array.Length();
     if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type))))
       return nullptr;
-    memcpy(Elements() + len, array.Elements(), otherLen * sizeof(elem_type));
-    this->IncrementLength(otherLen);      
+    copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type));
+    this->IncrementLength(otherLen);
     array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
     return Elements() + len;
   }
 
   // This method removes a range of elements from this array.
   // @param start  The starting index of the elements to remove.
   // @param count  The number of elements to remove.
   void RemoveElementsAt(index_type start, size_type count) {
@@ -1196,17 +1338,17 @@ public:
   // @return        True if the operation succeeded; false otherwise.
   // See also TruncateLength if the new length is guaranteed to be
   // smaller than the old.
   bool SetLength(size_type newLen) {
     size_type oldLen = Length();
     if (newLen > oldLen) {
       return InsertElementsAt(oldLen, newLen - oldLen) != nullptr;
     }
-      
+
     TruncateLength(newLen);
     return true;
   }
 
   // This method modifies the length of the array, but may only be
   // called when the new length is shorter than the old.  It can
   // therefore be called when elem_type has no default constructor,
   // unlike SetLength.  It removes elements from the array (see also