Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 09 May 2013 13:19:31 -0400
changeset 142308 6c48ce88a31ad3476e3403008a02b589ac44e2ba
parent 142285 17f93f12b0843cf6c52e90bc88d8b2117cc745ec (current diff)
parent 142307 4a1cbbb07ce23516c12662db6b154f95439bbca0 (diff)
child 142309 3269501068511f667f681c91907902035a85ab6d
child 142358 29a83a39f02a5dfdf5c3acbafdade9faab3ef5e6
child 142428 74dbf4035f0720dc64b610c8a8f85c1f77e2ab21
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
mobile/android/config/tooltool-manifests/android-x86/android/releng.manifest
--- a/accessible/src/jsat/EventManager.jsm
+++ b/accessible/src/jsat/EventManager.jsm
@@ -184,17 +184,17 @@ this.EventManager = {
         break;
       }
       case Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED:
       case Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED:
       {
         if (aEvent.isFromUserInput) {
           // XXX support live regions as well.
           let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
-          let isInserted = event.isInserted();
+          let isInserted = event.isInserted;
           let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
 
           let text = '';
           try {
             text = txtIface.
               getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
           } catch (x) {
             // XXX we might have gotten an exception with of a
--- a/browser/components/tabview/favicons.js
+++ b/browser/components/tabview/favicons.js
@@ -73,17 +73,17 @@ let FavIcons = {
   },
 
   // ----------
   // Function: _getFavIconFromTabImage
   // Retrieves the favicon for tab with a tab image.
   _getFavIconFromTabImage:
     function FavIcons_getFavIconFromTabImage(tab, callback) {
 
-    let tabImage = tab.image;
+    let tabImage = gBrowser.getIcon(tab);
 
     // If the tab image's url starts with http(s), fetch icon from favicon
     // service via the moz-anno protocol.
     if (/^https?:/.test(tabImage)) {
       let tabImageURI = gWindow.makeURI(tabImage);
       tabImage = this._favIconService.getFaviconLinkForIcon(tabImageURI).spec;
     }
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -26,27 +26,27 @@ xpinstallPromptAllowButton=Allow
 # See http://www.mozilla.org/access/keyboard/accesskey for details
 xpinstallPromptAllowButton.accesskey=A
 xpinstallDisabledMessageLocked=Software installation has been disabled by your system administrator.
 xpinstallDisabledMessage=Software installation is currently disabled. Click Enable and try again.
 xpinstallDisabledButton=Enable
 xpinstallDisabledButton.accesskey=n
 
 # LOCALIZATION NOTE (addonDownloading, addonDownloadCancelled, addonDownloadRestart):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
 addonDownloading=Add-on downloading;Add-ons downloading
 addonDownloadCancelled=Add-on download cancelled.;Add-on downloads cancelled.
 addonDownloadRestart=Restart Download;Restart Downloads
 addonDownloadRestart.accessKey=R
 addonDownloadCancelTooltip=Cancel
 
 # LOCALIZATION NOTE (addonsInstalled, addonsInstalledNeedsRestart):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 first add-on's name, #2 number of add-ons, #3 application name
 addonsInstalled=#1 has been installed successfully.;#2 add-ons have been installed successfully.
 addonsInstalledNeedsRestart=#1 will be installed after you restart #3.;#2 add-ons will be installed after you restart #3.
 addonInstallRestartButton=Restart Now
 addonInstallRestartButton.accesskey=R
 
 # LOCALIZATION NOTE (addonError-1, addonError-2, addonError-3, addonError-4):
@@ -80,16 +80,17 @@ lwthemePostInstallNotification.manageBut
 
 # LOCALIZATION NOTE (lwthemeNeedsRestart.message):
 # %S will be replaced with the new theme name.
 lwthemeNeedsRestart.message=%S will be installed after you restart.
 lwthemeNeedsRestart.button=Restart Now
 lwthemeNeedsRestart.accesskey=R
 
 # LOCALIZATION NOTE (popupWarning.message): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is brandShortName and #2 is the number of pop-ups blocked.
 popupWarning.message=#1 prevented this site from opening a pop-up window.;#1 prevented this site from opening #2 pop-up windows.
 popupWarningButton=Options
 popupWarningButton.accesskey=O
 popupWarningButtonUnix=Preferences
 popupWarningButtonUnix.accesskey=P
 popupAllow=Allow pop-ups for %S
 popupBlock=Block pop-ups for %S
@@ -174,17 +175,17 @@ feedShowFeedNew=Subscribe to '%S'…
 
 menuOpenAllInTabs.label=Open All in Tabs
 
 # History menu
 menuRestoreAllTabs.label=Restore All Tabs
 # LOCALIZATION NOTE (menuRestoreAllWindows, menuUndoCloseWindowLabel, menuUndoCloseWindowSingleTabLabel):
 # see bug 394759
 menuRestoreAllWindows.label=Restore All Windows
-# LOCALIZATION NOTE (menuUndoCloseWindowLabel): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (menuUndoCloseWindowLabel): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 Window Title, #2 Number of tabs
 menuUndoCloseWindowLabel=#1 (and #2 other tab);#1 (and #2 other tabs)
 menuUndoCloseWindowSingleTabLabel=#1
 
 # Unified Back-/Forward Popup
 tabHistory.current=Stay on this page
 tabHistory.goBack=Go back to this page
@@ -233,19 +234,19 @@ identity.unknown.tooltip=This website do
 identity.ownerUnknown2=(unknown)
 
 # Edit Bookmark UI
 editBookmarkPanel.pageBookmarkedTitle=Page Bookmarked
 editBookmarkPanel.pageBookmarkedDescription=%S will always remember this page for you.
 editBookmarkPanel.bookmarkedRemovedTitle=Bookmark Removed
 editBookmarkPanel.editBookmarkTitle=Edit This Bookmark
 
-# LOCALIZATION NOTE (editBookmark.removeBookmarks.label)
-# Semi-colon list of plural forms. Replacement for #1 is
-# the number of bookmarks to be removed.
+# LOCALIZATION NOTE (editBookmark.removeBookmarks.label): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# Replacement for #1 is the number of bookmarks to be removed.
 # If this causes problems with localization you can also do "Remove Bookmarks (#1)"
 # instead of "Remove #1 Bookmarks".
 editBookmark.removeBookmarks.label=Remove Bookmark;Remove #1 Bookmarks
 
 # Post Update Notifications
 pu.notifyButton.label=Details…
 pu.notifyButton.accesskey=D
 # LOCALIZATION NOTE %S will be replaced by the short name of the application.
@@ -309,17 +310,17 @@ ctrlTab.showAll.label=;Show all #1 tabs
 
 # LOCALIZATION NOTE (addKeywordTitleAutoFill): %S will be replaced by the page's title
 # Used as the bookmark name when saving a keyword for a search field.
 addKeywordTitleAutoFill=Search %S
 
 # TabView
 # LOCALIZATION NOTE (tabview.title): %S is the application name.
 tabview.title=%S - Group Your Tabs
-# LOCALIZATION NOTE (tabview.moveToUnnamedGroup.label): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (tabview.moveToUnnamedGroup.label): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the page title of the first tab in the unnamed group, 
 # #2 is the number of remaining tabs.
 tabview.moveToUnnamedGroup.label=#1 and 1 more;#1 and #2 more
 
 extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name=Default
 extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description=The default theme.
 
@@ -424,17 +425,17 @@ identity.next.accessKey = n
 # LOCALIZATION NOTE: shown in the popup notification when a user successfully logs into a website
 # LOCALIZATION NOTE (identity.loggedIn.description): %S is the user's identity (e.g. user@example.com)
 identity.loggedIn.description = Signed in as: %S
 identity.loggedIn.signOut.label = Sign Out
 identity.loggedIn.signOut.accessKey = O
 
 # LOCALIZATION NOTE (getUserMedia.shareCamera.message, getUserMedia.shareMicrophone.message, getUserMedia.shareCameraAndMicrophone.message, getUserMedia.sharingCamera.message, getUserMedia.sharingMicrophone.message, getUserMedia.sharingCameraAndMicrophone.message): %S is the website origin (e.g. www.mozilla.org)
 # LOCALIZATION NOTE (getUserMedia.shareSelectedDevices.label):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # The number of devices can be either one or two.
 getUserMedia.shareCamera.message = Would you like to share your camera with %S?
 getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
 getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
 getUserMedia.noVideo.label = No Video
 getUserMedia.noAudio.label = No Audio
 getUserMedia.shareSelectedDevices.label = Share Selected Device;Share Selected Devices
--- a/browser/locales/en-US/chrome/browser/devtools/styleeditor.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/styleeditor.properties
@@ -20,17 +20,17 @@ chromeWindowTitle=Style Editor [%S]
 # the argument is the index (order) of the containing <style> element in the
 # document.
 inlineStyleSheet=<inline style sheet #%S>
 
 # LOCALIZATION NOTE  (newStyleSheet): This is the default name for a new
 # user-created style sheet.
 newStyleSheet=New style sheet #%S
 
-# LOCALIZATION NOTE  (ruleCount.label): Semi-colon list of plural forms.
+# LOCALIZATION NOTE  (ruleCount.label): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # This is shown in the style sheets list.
 # #1 rule.
 # example: 111 rules.
 ruleCount.label=#1 rule.;#1 rules.
 
 # LOCALIZATION NOTE  (error-load): This is shown when loading fails.
 error-load=Style sheet could not be loaded.
--- a/browser/locales/en-US/chrome/browser/places/places.properties
+++ b/browser/locales/en-US/chrome/browser/places/places.properties
@@ -52,17 +52,17 @@ tabs.openWarningTitle=Confirm open
 tabs.openWarningMultipleBranded=You are about to open %S tabs.  This might slow down %S while the pages are loading.  Are you sure you want to continue?
 tabs.openButtonMultiple=Open tabs
 tabs.openWarningPromptMeBranded=Warn me when opening multiple tabs might slow down %S
 
 SelectImport=Import Bookmarks File
 EnterExport=Export Bookmarks File
 
 detailsPane.noItems=No items
-# LOCALIZATION NOTE (detailsPane.itemsCountLabel): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (detailsPane.itemsCountLabel): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of items
 # example: 111 items
 detailsPane.itemsCountLabel=One item;#1 items
 
 mostVisitedTitle=Most Visited
 recentlyBookmarkedTitle=Recently Bookmarked
 recentTagsTitle=Recent Tags
--- a/browser/locales/en-US/chrome/browser/preferences/aboutPermissions.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/aboutPermissions.properties
@@ -1,9 +1,13 @@
 # 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/.
 
-# LOCALIZATION NOTE (visitCount): #1 is the number of history visits for a site
+# LOCALIZATION NOTE (visitCount): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of history visits for a site
 visitCount=#1 visit;#1 visits
 
+# LOCALIZATION NOTE (passwordsCount, cookiesCount): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 passwordsCount=#1 password is stored for this website.;#1 passwords are stored for this website.
 cookiesCount=#1 cookie is set for this website.;#1 cookies are set for this website.
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -8,36 +8,37 @@ button.syncOptionsCancel.label = Cancel
 
 invalidEmail.label          = Invalid email address
 serverInvalid.label         = Please enter a valid server URL
 usernameNotAvailable.label  = Already in use
 
 verifying.label = Verifying…
 
 # LOCALIZATION NOTE (additionalClientCount.label):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of additional clients (was %S for a short while, use #1 instead, even if both work)
 additionalClientCount.label = and #1 additional device;and #1 additional devices
 # LOCALIZATION NOTE (bookmarksCount.label):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of bookmarks (was %S for a short while, use #1 instead, even if both work)
 bookmarksCount.label        = #1 bookmark;#1 bookmarks
 # LOCALIZATION NOTE (historyDaysCount.label):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of days (was %S for a short while, use #1 instead, even if both work)
 historyDaysCount.label      = #1 day of history;#1 days of history
 # LOCALIZATION NOTE (passwordsCount.label):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of passwords (was %S for a short while, use #1 instead, even if both work)
 passwordsCount.label        = #1 password;#1 passwords
-# LOCALIZATION NOTE (addonsCount.label):
+# LOCALIZATION NOTE (addonsCount.label): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 is the number of add-ons, see the link above for forms
 addonsCount.label        = #1 addon;#1 addons
 
 save.recoverykey.title = Save Recovery Key
 save.recoverykey.defaultfilename = Firefox Recovery Key.html
 
 newAccount.action.label = Firefox Sync is now set up to automatically sync all of your browser data.
 newAccount.change.label = You can choose exactly what to sync by selecting Sync Options below.
--- a/browser/modules/WindowsPreviewPerTab.jsm
+++ b/browser/modules/WindowsPreviewPerTab.jsm
@@ -204,19 +204,21 @@ PreviewController.prototype = {
   // updateCanvasPreview() will detect the size mismatch as a resize event
   // the next time it is called.
   resetCanvasPreview: function () {
     this.canvasPreview.width = 0;
     this.canvasPreview.height = 0;
   },
 
   get zoom() {
-    // We use this property instead of the fullZoom property because this
-    // accurately reflects the actual zoom factor used when drawing.
-    return this.winutils.screenPixelsPerCSSPixel;
+    // Note that winutils.fullZoom accounts for "quantization" of the zoom factor
+    // from nsIMarkupDocumentViewer due to conversion through appUnits.
+    // We do -not- want screenPixelsPerCSSPixel here, because that would -also-
+    // incorporate any scaling that is applied due to hi-dpi resolution options.
+    return this.winutils.fullZoom;
   },
 
   // Updates the controller's canvas with the parts of the <browser> that need
   // to be redrawn.
   updateCanvasPreview: function () {
     let win = this.linkedBrowser.contentWindow;
     let bx = this.linkedBrowser.boxObject;
     // Check for resize
@@ -296,28 +298,43 @@ PreviewController.prototype = {
     let self = this;
     this.win.tabbrowser.previewTab(this.tab, function () self.previewTabCallback(ctx));
 
     // We must avoid having the frame drawn around the window. See bug 520807
     return false;
   },
 
   previewTabCallback: function (ctx) {
+    // This will extract the resolution-scale component of the scaling we need,
+    // which should be applied to both chrome and content;
+    // the page zoom component is applied (to content only) within updateCanvasPreview.
+    let scale = this.winutils.screenPixelsPerCSSPixel / this.winutils.fullZoom;
+    ctx.save();
+    ctx.scale(scale, scale);
     let width = this.win.width;
     let height = this.win.height;
     // Draw our toplevel window
     ctx.drawWindow(this.win.win, 0, 0, width, height, "transparent");
 
-    // Compositor, where art thou?
-    // Draw the tab content on top of the toplevel window
-    this.updateCanvasPreview();
+    // XXX (jfkthame): Pending tabs don't seem to draw with the proper scaling
+    // unless we use this block of code; but doing this for "normal" (loaded) tabs
+    // results in blurry rendering on hidpi systems, so we avoid it if possible.
+    // I don't understand why pending and loaded tabs behave differently here...
+    // (see bug 857061).
+    if (this.tab.hasAttribute("pending")) {
+      // Compositor, where art thou?
+      // Draw the tab content on top of the toplevel window
+      this.updateCanvasPreview();
 
-    let boxObject = this.linkedBrowser.boxObject;
-    ctx.translate(boxObject.x, boxObject.y);
-    ctx.drawImage(this.canvasPreview, 0, 0);
+      let boxObject = this.linkedBrowser.boxObject;
+      ctx.translate(boxObject.x, boxObject.y);
+      ctx.drawImage(this.canvasPreview, 0, 0);
+    }
+
+    ctx.restore();
   },
 
   drawThumbnail: function (ctx, width, height) {
     this.updateCanvasPreview();
 
     let scale = width/this.linkedBrowser.boxObject.width;
     ctx.scale(scale, scale);
     ctx.drawImage(this.canvasPreview, 0, 0);
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -78,23 +78,31 @@ except:
 
 
 if _IS_WIN32:
   import ctypes, ctypes.wintypes, time, msvcrt
 else:
   import errno
 
 
+def getGlobalLog():
+  return _log
+
+def resetGlobalLog(log):
+  while _log.handlers:
+    _log.removeHandler(_log.handlers[0])
+  handler = logging.StreamHandler(log)
+  _log.setLevel(logging.INFO)
+  _log.addHandler(handler)
+
 # We use the logging system here primarily because it'll handle multiple
 # threads, which is needed to process the output of the server and application
 # processes simultaneously.
 _log = logging.getLogger()
-handler = logging.StreamHandler(sys.stdout)
-_log.setLevel(logging.INFO)
-_log.addHandler(handler)
+resetGlobalLog(sys.stdout)
 
 
 #################
 # PROFILE SETUP #
 #################
 
 class SyntaxError(Exception):
   "Signifies a syntax error on a particular line in server-locations.txt."
@@ -741,18 +749,19 @@ user_pref("camino.use_system_proxy_setti
 
     env['GNOME_DISABLE_CRASH_DIALOG'] = '1'
     env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1'
     env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1'
 
     # Additional temporary logging while we try to debug some intermittent
     # WebRTC conditions. This is necessary to troubleshoot bugs 841496,
     # 841150, and 839677 (at least)
+    # Also (temporary) bug 870002 (mediastreamgraph)
     if 'NSPR_LOG_MODULES' not in env:
-      env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3'
+      env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3,mediastreamgraph:4'
     env['R_LOG_LEVEL'] = '5'
     env['R_LOG_DESTINATION'] = 'stderr'
     env['R_LOG_VERBOSE'] = '1'
 
     # ASan specific environment stuff
     if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC):
       try:
         totalMemory = int(os.popen("free").readlines()[1].split()[1])
@@ -899,20 +908,24 @@ user_pref("camino.use_system_proxy_setti
     except IOError, err:
         self.log.info("Failed to read image from %s", imgoutput)
 
     import base64
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, processPID, utilityPath, debuggerInfo):
-    """Kill the process, preferrably in a way that gets us a stack trace."""
+    """Kill the process, preferrably in a way that gets us a stack trace.
+       Also attempts to obtain a screenshot before killing the process."""
     if not debuggerInfo:
       self.dumpScreen(utilityPath)
+    self.killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo)
 
+  def killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo):
+    """Kill the process, preferrably in a way that gets us a stack trace."""
     if self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(processPID, signal.SIGABRT)
         return
       elif self.IS_WIN32:
         # We should have a "crashinject" program in our utility path
         crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe"))
--- a/build/macosx/universal/flight.mk
+++ b/build/macosx/universal/flight.mk
@@ -39,16 +39,18 @@ ifdef ENABLE_TESTS
 # automation.py differs because it hardcodes a path to
 # dist/bin. It doesn't matter which one we use.
 	if test -d $(DIST_ARCH_1)/test-package-stage -a                 \
                 -d $(DIST_ARCH_2)/test-package-stage; then              \
            cp $(DIST_ARCH_1)/test-package-stage/mochitest/automation.py \
              $(DIST_ARCH_2)/test-package-stage/mochitest/;              \
            cp -RL $(DIST_ARCH_1)/test-package-stage/mochitest/extensions/specialpowers \
              $(DIST_ARCH_2)/test-package-stage/mochitest/extensions/;              \
+           cp $(DIST_ARCH_1)/test-package-stage/xpcshell/automation.py  \
+             $(DIST_ARCH_2)/test-package-stage/xpcshell/;               \
            cp $(DIST_ARCH_1)/test-package-stage/reftest/automation.py   \
              $(DIST_ARCH_2)/test-package-stage/reftest/;                \
            cp -RL $(DIST_ARCH_1)/test-package-stage/reftest/specialpowers \
              $(DIST_ARCH_2)/test-package-stage/reftest/;              \
            $(TOPSRCDIR)/build/macosx/universal/unify                 \
              --unify-with-sort "\.manifest$$" \
              --unify-with-sort "all-test-dirs\.list$$"               \
              $(DIST_ARCH_1)/test-package-stage                          \
--- a/config/makefiles/xpcshell.mk
+++ b/config/makefiles/xpcshell.mk
@@ -35,64 +35,69 @@ ifndef NO_XPCSHELL_MANIFEST_CHECK #{
 	  $(addprefix $(MOZILLA_DIR)/$(relativesrcdir)/,$(XPCSHELL_TESTS))
 endif #} NO_XPCSHELL_MANIFEST_CHECK 
 
 ###########################################################################
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --tests-root-dir=$(testxpcobjdir) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
 	  --xunit-suite-name=xpcshell \
 	  --test-plugin-path=$(DIST)/plugins \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 xpcshell-tests-remote: DM_TRANS?=adb
 xpcshell-tests-remote:
-	$(PYTHON) -u $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
+	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
+	  $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  $(EXTRA_TEST_ARGS) \
 	  --dm_trans=$(DM_TRANS) \
 	  --deviceIP=${TEST_DEVICE} \
 	  --objdir=$(DEPTH) \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 ###########################################################################
 # Execute a single test, specified in $(SOLO_FILE), but don't automatically
 # start the test. Instead, present the xpcshell prompt so the user can
 # attach a debugger and then start the test.
 check-interactive:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --profile-name=$(MOZ_APP_NAME) \
 	  --test-plugin-path=$(DIST)/plugins \
 	  --interactive \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE)
 check-one:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --profile-name=$(MOZ_APP_NAME) \
@@ -100,16 +105,17 @@ check-one:
 	  --verbose \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 check-one-remote: DM_TRANS?=adb
 check-one-remote:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
 	  -I$(topsrcdir)/build/mobile \
 	  -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
 	  $(testxpcsrcdir)/remotexpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -1664,18 +1664,18 @@ nsContentUtils::GetWindowFromCaller()
 
   return nullptr;
 }
 
 nsIDocument*
 nsContentUtils::GetDocumentFromCaller()
 {
   JSContext *cx = nullptr;
-  JSObject *obj = nullptr;
-  sXPConnect->GetCaller(&cx, &obj);
+  JS::Rooted<JSObject*> obj(cx);
+  sXPConnect->GetCaller(&cx, obj.address());
   NS_ASSERTION(cx && obj, "Caller ensures something is running");
 
   JSAutoCompartment ac(cx, obj);
 
   nsCOMPtr<nsPIDOMWindow> win =
     do_QueryInterface(nsJSUtils::GetStaticScriptGlobal(obj));
   if (!win) {
     return nullptr;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1038,21 +1038,19 @@ public:
   // Posts an event to call UpdateVisibilityState
   virtual void PostVisibilityUpdateEvent();
 
   virtual void DocSizeOfExcludingThis(nsWindowSizes* aWindowSizes) const;
   // DocSizeOfIncludingThis is inherited from nsIDocument.
 
   virtual nsIDOMNode* AsDOMNode() { return this; }
 
-  JSObject* GetCustomPrototype(const nsAString& aElementName)
+  void GetCustomPrototype(const nsAString& aElementName, JS::MutableHandle<JSObject*> prototype)
   {
-    JSObject* prototype = nullptr;
-    mCustomPrototypes.Get(aElementName, &prototype);
-    return prototype;
+    mCustomPrototypes.Get(aElementName, prototype.address());
   }
 
   static bool RegisterEnabled();
 
   // WebIDL bits
   virtual mozilla::dom::DOMImplementation*
     GetImplementation(mozilla::ErrorResult& rv);
   virtual JSObject*
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1023,26 +1023,26 @@ nsFrameScriptExecutor::TryCacheLoadAndCo
                                                     CacheFailedBehavior aBehavior)
 {
   nsCString url = NS_ConvertUTF16toUTF8(aURL);
   nsCOMPtr<nsIURI> uri;
   nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
   if (NS_FAILED(rv)) {
     return;
   }
-  
+
   bool hasFlags;
   rv = NS_URIChainHasFlags(uri,
                            nsIProtocolHandler::URI_IS_LOCAL_RESOURCE,
                            &hasFlags);
   if (NS_FAILED(rv) || !hasFlags) {
     NS_WARNING("Will not load a frame script!");
     return;
   }
-  
+
   nsCOMPtr<nsIChannel> channel;
   NS_NewChannel(getter_AddRefs(channel), uri);
   if (!channel) {
     return;
   }
 
   nsCOMPtr<nsIInputStream> input;
   channel->Open(getter_AddRefs(input));
@@ -1124,25 +1124,25 @@ nsFrameScriptExecutor::InitTabChildGloba
   JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
   JSAutoRequest ar(cx);
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
 
-  
+
   JS_SetContextPrivate(cx, aScope);
 
   nsresult rv =
     xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
                                          flags, JS::SystemZone, getter_AddRefs(mGlobal));
   NS_ENSURE_SUCCESS(rv, false);
 
-    
+
   JS::Rooted<JSObject*> global(cx);
   rv = mGlobal->GetJSObject(global.address());
   NS_ENSURE_SUCCESS(rv, false);
 
   JS_SetGlobalObject(cx, global);
 
   // Set the location information for the new global, so that tools like
   // about:memory may use that information.
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -508,17 +508,17 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
         mediaElem->NotifyOwnerDocumentActivityChanged();
       }
       nsCOMPtr<nsIObjectLoadingContent> objectLoadingContent(do_QueryInterface(aNode));
       if (objectLoadingContent) {
         nsObjectLoadingContent* olc = static_cast<nsObjectLoadingContent*>(objectLoadingContent.get());
         olc->NotifyOwnerDocumentActivityChanged();
       }
     }
- 
+
     if (oldDoc != newDoc && oldDoc->MayHaveDOMMutationObservers()) {
       newDoc->SetMayHaveDOMMutationObservers();
     }
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
--- a/content/html/content/src/HTMLUnknownElement.cpp
+++ b/content/html/content/src/HTMLUnknownElement.cpp
@@ -19,18 +19,18 @@ JSObject*
 HTMLUnknownElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aScope)
 {
   JS::Rooted<JSObject*> obj(aCx,
     HTMLUnknownElementBinding::Wrap(aCx, aScope, this));
   if (obj && Substring(NodeName(), 0, 2).LowerCaseEqualsLiteral("x-")) {
     // If we have a registered x-tag then we fix the prototype.
     JSAutoCompartment ac(aCx, obj);
     nsDocument* document = static_cast<nsDocument*>(OwnerDoc());
-    JS::Rooted<JSObject*> prototype(aCx,
-      document->GetCustomPrototype(LocalName()));
+    JS::Rooted<JSObject*> prototype(aCx);
+    document->GetCustomPrototype(LocalName(), &prototype);
     if (prototype) {
       NS_ENSURE_TRUE(JS_WrapObject(aCx, prototype.address()), nullptr);
       NS_ENSURE_TRUE(JS_SetPrototype(aCx, obj, prototype), nullptr);
     }
   }
   return obj;
 }
 
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -114,19 +114,19 @@ MediaStreamGraphImpl::ExtractPendingInpu
     if (aStream->mPullEnabled && !aStream->mFinished &&
         !aStream->mListeners.IsEmpty()) {
       // Compute how much stream time we'll need assuming we don't block
       // the stream at all between mBlockingDecisionsMadeUntilTime and
       // aDesiredUpToTime.
       StreamTime t =
         GraphTimeToStreamTime(aStream, mStateComputedTime) +
         (aDesiredUpToTime - mStateComputedTime);
-      LOG(PR_LOG_DEBUG, ("Calling NotifyPull aStream=%p t=%f current end=%f", aStream,
-                         MediaTimeToSeconds(t),
-                         MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
+      LOG(PR_LOG_DEBUG+1, ("Calling NotifyPull aStream=%p t=%f current end=%f", aStream,
+                           MediaTimeToSeconds(t),
+                           MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
       if (t > aStream->mBuffer.GetEnd()) {
         *aEnsureNextIteration = true;
 #ifdef DEBUG
         if (aStream->mListeners.Length() == 0) {
           LOG(PR_LOG_ERROR, ("No listeners in NotifyPull aStream=%p desired=%f current end=%f",
                              aStream, MediaTimeToSeconds(t),
                              MediaTimeToSeconds(aStream->mBuffer.GetEnd())));
           aStream->DumpTrackInfo();
@@ -158,20 +158,20 @@ MediaStreamGraphImpl::ExtractPendingInpu
                            int64_t(segment->GetDuration())));
         aStream->mBuffer.AddTrack(data->mID, data->mRate, data->mStart, segment);
         // The track has taken ownership of data->mData, so let's replace
         // data->mData with an empty clone.
         data->mData = segment->CreateEmptyClone();
         data->mCommands &= ~SourceMediaStream::TRACK_CREATE;
       } else if (data->mData->GetDuration() > 0) {
         MediaSegment* dest = aStream->mBuffer.FindTrack(data->mID)->GetSegment();
-        LOG(PR_LOG_DEBUG, ("SourceMediaStream %p track %d, advancing end from %lld to %lld",
-                           aStream, data->mID,
-                           int64_t(dest->GetDuration()),
-                           int64_t(dest->GetDuration() + data->mData->GetDuration())));
+        LOG(PR_LOG_DEBUG+1, ("SourceMediaStream %p track %d, advancing end from %lld to %lld",
+                             aStream, data->mID,
+                             int64_t(dest->GetDuration()),
+                             int64_t(dest->GetDuration() + data->mData->GetDuration())));
         dest->AppendFrom(data->mData);
       }
       if (data->mCommands & SourceMediaStream::TRACK_END) {
         aStream->mBuffer.FindTrack(data->mID)->SetEnded();
         aStream->mUpdateTracks.RemoveElementAt(i);
       }
     }
     aStream->mBuffer.AdvanceKnownTracksTime(aStream->mUpdateKnownTracksTime);
@@ -317,20 +317,20 @@ MediaStreamGraphImpl::UpdateCurrentTime(
   GraphTime nextCurrentTime =
     SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + mCurrentTime;
   if (mStateComputedTime < nextCurrentTime) {
     LOG(PR_LOG_WARNING, ("Media graph global underrun detected"));
     nextCurrentTime = mStateComputedTime;
   }
   mCurrentTimeStamp = now;
 
-  LOG(PR_LOG_DEBUG, ("Updating current time to %f (real %f, mStateComputedTime %f)",
-                     MediaTimeToSeconds(nextCurrentTime),
-                     (now - mInitialTimeStamp).ToSeconds(),
-                     MediaTimeToSeconds(mStateComputedTime)));
+  LOG(PR_LOG_DEBUG+1, ("Updating current time to %f (real %f, mStateComputedTime %f)",
+                       MediaTimeToSeconds(nextCurrentTime),
+                       (now - mInitialTimeStamp).ToSeconds(),
+                       MediaTimeToSeconds(mStateComputedTime)));
 
   if (prevCurrentTime >= nextCurrentTime) {
     NS_ASSERTION(prevCurrentTime == nextCurrentTime, "Time can't go backwards!");
     // This could happen due to low clock resolution, maybe?
     LOG(PR_LOG_DEBUG, ("Time did not advance"));
     // There's not much left to do here, but the code below that notifies
     // listeners that streams have ended still needs to run.
   }
@@ -375,19 +375,19 @@ MediaStreamGraphImpl::UpdateCurrentTime(
       stream->mNotifiedFinished = true;
       stream->mLastPlayedVideoFrame.SetNull();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
         l->NotifyFinished(this);
       }
     }
 
-    LOG(PR_LOG_DEBUG, ("MediaStream %p bufferStartTime=%f blockedTime=%f",
-                       stream, MediaTimeToSeconds(stream->mBufferStartTime),
-                       MediaTimeToSeconds(blockedTime)));
+    LOG(PR_LOG_DEBUG+1, ("MediaStream %p bufferStartTime=%f blockedTime=%f",
+                         stream, MediaTimeToSeconds(stream->mBufferStartTime),
+                         MediaTimeToSeconds(blockedTime)));
   }
 
   mCurrentTime = nextCurrentTime;
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
@@ -408,30 +408,30 @@ MediaStreamGraphImpl::WillUnderrun(Media
                        aStream, MediaTimeToSeconds(bufferEnd), MediaTimeToSeconds(mCurrentTime),
                        bufferEnd, mCurrentTime, aStream->GetBufferEnd()));
     aStream->DumpTrackInfo();
     NS_ASSERTION(bufferEnd >= mCurrentTime, "Buffer underran");
   }
 #endif
   // We should block after bufferEnd.
   if (bufferEnd <= aTime) {
-    LOG(PR_LOG_DEBUG, ("MediaStream %p will block due to data underrun, "
-                       "bufferEnd %f",
-                       aStream, MediaTimeToSeconds(bufferEnd)));
+    LOG(PR_LOG_DEBUG+1, ("MediaStream %p will block due to data underrun, "
+                         "bufferEnd %f",
+                         aStream, MediaTimeToSeconds(bufferEnd)));
     return true;
   }
   // We should keep blocking if we're currently blocked and we don't have
   // data all the way through to aEndBlockingDecisions. If we don't have
   // data all the way through to aEndBlockingDecisions, we'll block soon,
   // but we might as well remain unblocked and play the data we've got while
   // we can.
   if (bufferEnd <= aEndBlockingDecisions && aStream->mBlocked.GetBefore(aTime)) {
-    LOG(PR_LOG_DEBUG, ("MediaStream %p will block due to speculative data underrun, "
-                       "bufferEnd %f",
-                       aStream, MediaTimeToSeconds(bufferEnd)));
+    LOG(PR_LOG_DEBUG+1, ("MediaStream %p will block due to speculative data underrun, "
+                         "bufferEnd %f",
+                         aStream, MediaTimeToSeconds(bufferEnd)));
     return true;
   }
   // Reconsider decisions at bufferEnd
   *aEnd = std::min(*aEnd, bufferEnd);
   return false;
 }
 
 void
@@ -514,18 +514,18 @@ MediaStreamGraphImpl::UpdateStreamOrder(
   }
 }
 
 void
 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
 {
   bool blockingDecisionsWillChange = false;
 
-  LOG(PR_LOG_DEBUG, ("Media graph %p computing blocking for time %f",
-                     this, MediaTimeToSeconds(mStateComputedTime)));
+  LOG(PR_LOG_DEBUG+1, ("Media graph %p computing blocking for time %f",
+                       this, MediaTimeToSeconds(mStateComputedTime)));
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
     if (!stream->mInBlockingSet) {
       // Compute a partition of the streams containing 'stream' such that we can
       // compute the blocking status of each subset independently.
       nsAutoTArray<MediaStream*,10> streamSet;
       AddBlockingRelatedStreamsToSet(&streamSet, stream);
 
@@ -541,19 +541,19 @@ MediaStreamGraphImpl::RecomputeBlocking(
     }
 
     GraphTime end;
     stream->mBlocked.GetAt(mCurrentTime, &end);
     if (end < GRAPH_TIME_MAX) {
       blockingDecisionsWillChange = true;
     }
   }
-  LOG(PR_LOG_DEBUG, ("Media graph %p computed blocking for interval %f to %f",
-                     this, MediaTimeToSeconds(mStateComputedTime),
-                     MediaTimeToSeconds(aEndBlockingDecisions)));
+  LOG(PR_LOG_DEBUG+1, ("Media graph %p computed blocking for interval %f to %f",
+                       this, MediaTimeToSeconds(mStateComputedTime),
+                       MediaTimeToSeconds(aEndBlockingDecisions)));
   mStateComputedTime = aEndBlockingDecisions;
  
   if (blockingDecisionsWillChange) {
     // Make sure we wake up to notify listeners about these changes.
     EnsureNextIteration();
   }
 }
 
@@ -617,34 +617,34 @@ MediaStreamGraphImpl::RecomputeBlockingA
   }
 
   for (uint32_t i = 0; i < aStreams.Length(); ++i) {
     MediaStream* stream = aStreams[i];
 
     if (stream->mFinished) {
       GraphTime endTime = StreamTimeToGraphTime(stream, stream->GetBufferEnd());
       if (endTime <= aTime) {
-        LOG(PR_LOG_DEBUG, ("MediaStream %p is blocked due to being finished", stream));
+        LOG(PR_LOG_DEBUG+1, ("MediaStream %p is blocked due to being finished", stream));
         // We'll block indefinitely
         MarkStreamBlocking(stream);
         *aEnd = aEndBlockingDecisions;
         continue;
       } else {
-        LOG(PR_LOG_DEBUG, ("MediaStream %p is finished, but not blocked yet (end at %f, with blocking at %f)",
-                           stream, MediaTimeToSeconds(stream->GetBufferEnd()),
-                           MediaTimeToSeconds(endTime)));
+        LOG(PR_LOG_DEBUG+1, ("MediaStream %p is finished, but not blocked yet (end at %f, with blocking at %f)",
+                             stream, MediaTimeToSeconds(stream->GetBufferEnd()),
+                             MediaTimeToSeconds(endTime)));
         *aEnd = std::min(*aEnd, endTime);
       }
     }
 
     GraphTime end;
     bool explicitBlock = stream->mExplicitBlockerCount.GetAt(aTime, &end) > 0;
     *aEnd = std::min(*aEnd, end);
     if (explicitBlock) {
-      LOG(PR_LOG_DEBUG, ("MediaStream %p is blocked due to explicit blocker", stream));
+      LOG(PR_LOG_DEBUG+1, ("MediaStream %p is blocked due to explicit blocker", stream));
       MarkStreamBlocking(stream);
       continue;
     }
 
     bool underrun = WillUnderrun(stream, aTime, aEndBlockingDecisions, aEnd);
     if (underrun) {
       // We'll block indefinitely
       MarkStreamBlocking(stream);
@@ -770,18 +770,18 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
         // more than one sample away from the ideal amount.
         TrackTicks startTicks =
             TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
         audioOutput.mBlockedAudioTime += end - t;
         TrackTicks endTicks =
             TimeToTicksRoundDown(track->GetRate(), audioOutput.mBlockedAudioTime);
 
         output.InsertNullDataAtStart(endTicks - startTicks);
-        LOG(PR_LOG_DEBUG, ("MediaStream %p writing blocking-silence samples for %f to %f",
-                         aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end)));
+        LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing blocking-silence samples for %f to %f",
+                             aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end)));
       } else {
         TrackTicks startTicks =
             track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, t));
         TrackTicks endTicks =
             track->TimeToTicksRoundDown(GraphTimeToStreamTime(aStream, end));
 
         // If startTicks is before the track start, then that part of 'audio'
         // will just be silence, which is fine here. But if endTicks is after
@@ -791,19 +791,19 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
         if (sliceEnd > startTicks) {
           output.AppendSlice(*audio, startTicks, sliceEnd);
         }
         // Play silence where the track has ended
         output.AppendNullData(endTicks - sliceEnd);
         NS_ASSERTION(endTicks == sliceEnd || track->IsEnded(),
                      "Ran out of data but track not ended?");
         output.ApplyVolume(volume);
-        LOG(PR_LOG_DEBUG, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)",
-                           aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
-                           startTicks, endTicks));
+        LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing samples for %f to %f (samples %lld to %lld)",
+                             aStream, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
+                             startTicks, endTicks));
       }
       output.WriteTo(audioOutput.mStream);
       t = end;
     }
   }
 }
 
 void
@@ -833,19 +833,19 @@ MediaStreamGraphImpl::PlayVideo(MediaStr
       start = thisStart;
       frame = thisFrame;
       track = tracks.get();
     }
   }
   if (!frame || *frame == aStream->mLastPlayedVideoFrame)
     return;
 
-  LOG(PR_LOG_DEBUG, ("MediaStream %p writing video frame %p (%dx%d)",
-                     aStream, frame->GetImage(), frame->GetIntrinsicSize().width,
-                     frame->GetIntrinsicSize().height));
+  LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing video frame %p (%dx%d)",
+                       aStream, frame->GetImage(), frame->GetIntrinsicSize().width,
+                       frame->GetIntrinsicSize().height));
   GraphTime startTime = StreamTimeToGraphTime(aStream,
       track->TicksToTimeRoundDown(start), INCLUDE_TRAILING_BLOCKED_INTERVAL);
   TimeStamp targetTime = mCurrentTimeStamp +
       TimeDuration::FromMilliseconds(double(startTime - mCurrentTime));
   for (uint32_t i = 0; i < aStream->mVideoOutputs.Length(); ++i) {
     VideoFrameContainer* output = aStream->mVideoOutputs[i];
     output->SetCurrentFrame(frame->GetIntrinsicSize(), frame->GetImage(),
                             targetTime);
@@ -1059,27 +1059,27 @@ MediaStreamGraphImpl::RunThread()
         TimeStamp now = TimeStamp::Now();
         if (mNeedAnotherIteration) {
           int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
             int64_t((now - mCurrentTimeStamp).ToMilliseconds());
           // Make sure timeoutMS doesn't overflow 32 bits by waking up at
           // least once a minute, if we need to wake up at all
           timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
           timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
-          LOG(PR_LOG_DEBUG, ("Waiting for next iteration; at %f, timeout=%f",
-                             (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
+          LOG(PR_LOG_DEBUG+1, ("Waiting for next iteration; at %f, timeout=%f",
+                               (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
           mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
         } else {
           mWaitState = WAITSTATE_WAITING_INDEFINITELY;
         }
         if (timeout > 0) {
           mMonitor.Wait(timeout);
-          LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
-                             (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
-                             (TimeStamp::Now() - now).ToSeconds()));
+          LOG(PR_LOG_DEBUG+1, ("Resuming after timeout; at %f, elapsed=%f",
+                               (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
+                               (TimeStamp::Now() - now).ToSeconds()));
         }
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
     }
   }
 }
--- a/content/media/TrackUnionStream.h
+++ b/content/media/TrackUnionStream.h
@@ -215,32 +215,32 @@ protected:
           inputTrackEndPoint = aInputTrack->GetEnd();
           *aOutputTrackFinished = true;
         }
       }
 
       if (interval.mInputIsBlocked) {
         // Maybe the input track ended?
         segment->AppendNullData(ticks);
-        LOG(PR_LOG_DEBUG, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
+        LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
             this, (long long)ticks, outputTrack->GetID()));
       } else {
         // Figuring out which samples to use from the input stream is tricky
         // because its start time and our start time may differ by a fraction
         // of a tick. Assuming the input track hasn't ended, we have to ensure
         // that 'ticks' samples are gathered, even though a tick boundary may
         // occur between outputStart and outputEnd but not between inputStart
         // and inputEnd.
         // We'll take the latest samples we can.
         TrackTicks inputEndTicks = TimeToTicksRoundUp(rate, inputEnd);
         TrackTicks inputStartTicks = inputEndTicks - ticks;
         segment->AppendSlice(*aInputTrack->GetSegment(),
                              std::min(inputTrackEndPoint, inputStartTicks),
                              std::min(inputTrackEndPoint, inputEndTicks));
-        LOG(PR_LOG_DEBUG, ("TrackUnionStream %p appending %lld ticks of input data to track %d",
+        LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of input data to track %d",
             this, (long long)(std::min(inputTrackEndPoint, inputEndTicks) - std::min(inputTrackEndPoint, inputStartTicks)),
             outputTrack->GetID()));
       }
       for (uint32_t j = 0; j < mListeners.Length(); ++j) {
         MediaStreamListener* l = mListeners[j];
         l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
                                     outputTrack->GetRate(), startTicks, 0,
                                     *segment);
--- a/content/media/webaudio/test/Makefile.in
+++ b/content/media/webaudio/test/Makefile.in
@@ -28,16 +28,17 @@ MOCHITEST_FILES := \
   test_AudioListener.html \
   test_AudioParam.html \
   test_audioParamExponentialRamp.html \
   test_audioParamLinearRamp.html \
   test_audioParamSetCurveAtTime.html \
   test_audioParamSetTargetAtTime.html \
   test_audioParamTimelineDestinationOffset.html \
   test_audioBufferSourceNode.html \
+  test_audioBufferSourceNodeEnded.html \
   test_audioBufferSourceNodeLazyLoopParam.html \
   test_audioBufferSourceNodeLoop.html \
   test_audioBufferSourceNodeLoopStartEnd.html \
   test_audioBufferSourceNodeLoopStartEndSame.html \
   test_audioBufferSourceNodeNullBuffer.html \
   test_badConnect.html \
   test_biquadFilterNode.html \
   test_channelMergerNode.html \
--- a/content/media/webaudio/test/test_analyserNode.html
+++ b/content/media/webaudio/test/test_analyserNode.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test GainNode</title>
+  <title>Test AnalyserNode</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
--- a/content/media/webaudio/test/test_audioBufferSourceNode.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNode.html
@@ -5,60 +5,42 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
+var gTest = {
+  length: 4096,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
 
-  var source = context.createBufferSource();
-
-  var eventsReceived = 0;
-  function eventReceived() {
-    if (++eventsReceived == 2) {
-      SpecialPowers.clearUserPref("media.webaudio.enabled");
-      SimpleTest.finish();
-    }
-  }
+    var source = context.createBufferSource();
 
-  source.onended = function(e) {
-    is(e.target, source, "Correct target for the ended event");
-    eventReceived();
-  };
-
-  var sp = context.createScriptProcessor(2048);
-  source.start(0);
-  source.buffer = buffer;
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    compareBuffers(e.inputBuffer.getChannelData(0), buffer.getChannelData(0));
-    compareBuffers(e.inputBuffer.getChannelData(1), buffer.getChannelData(0));
+    var sp = context.createScriptProcessor(2048);
+    source.start(0);
+    source.buffer = buffer;
+    return source;
+  },
+  createExpectedBuffers: function(context) {
+    var buffers = [];
+    var buffer = context.createBuffer(2, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+      buffer.getChannelData(1)[i] = buffer.getChannelData(0)[i];
+    }
+    buffers.push(buffer);
+    buffers.push(getEmptyBuffer(context, 2048));
+    return buffers;
+  },
+};
 
-    // On the next iteration, we'll get a silence buffer
-    sp.onaudioprocess = function(e) {
-      var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
-      compareBuffers(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0));
-      compareBuffers(e.inputBuffer.getChannelData(1), emptyBuffer.getChannelData(0));
-
-      sp.onaudioprocess = null;
-      sp.disconnect(context.destination);
-
-      eventReceived();
-    };
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
copy from content/media/webaudio/test/test_audioBufferSourceNodeLoop.html
copy to content/media/webaudio/test/test_audioBufferSourceNodeEnded.html
--- a/content/media/webaudio/test/test_audioBufferSourceNodeLoop.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeEnded.html
@@ -1,12 +1,12 @@
 <!DOCTYPE HTML>
 <html>
 <head>
-  <title>Test AudioBufferSourceNode looping</title>
+  <title>Test ended event on AudioBufferSourceNode</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
@@ -15,38 +15,25 @@ addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(1, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
   }
 
-  var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
-  for (var i = 0; i < 4; ++i) {
-    for (var j = 0; j < 2048; ++j) {
-      expectedBuffer.getChannelData(0)[i * 2048 + j] = buffer.getChannelData(0)[j];
-    }
-  }
-
   var source = context.createBufferSource();
-  source.buffer = buffer;
 
-  var sp = context.createScriptProcessor(2048 * 4, 1);
-  source.start(0);
-  source.loop = true;
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "input buffer must have only one channel");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-
-    sp.onaudioprocess = null;
-
+  source.onended = function(e) {
+    is(e.target, source, "Correct target for the ended event");
     SpecialPowers.clearUserPref("media.webaudio.enabled");
     SimpleTest.finish();
   };
+
+  source.start(0);
+  source.buffer = buffer;
+  source.connect(context.destination);
 });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioBufferSourceNodeLazyLoopParam.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeLazyLoopParam.html
@@ -5,52 +5,43 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 4096,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    // silence for half of the buffer, ones after that.
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 1024; i < 2048; i++) {
+      buffer.getChannelData(0)[i] = 1;
+    }
 
-  var context = new AudioContext();
-  // silence for half of the buffer, ones after that.
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 1024; i < 2048; i++) {
-    buffer.getChannelData(0)[i] = 1;
-  }
-  for (var i = 0; i < 2048; i++) {
-    expectedBuffer.getChannelData(0)[i] = 1;
-  }
-
-  var source = context.createBufferSource();
+    var source = context.createBufferSource();
 
-  var sp = context.createScriptProcessor(2048);
-  // we start at the 1024 frames, we should only have ones.
-  source.loop = true;
-  source.loopStart = 1024 / context.sampleRate;
-  source.loopEnd = 2048 / context.sampleRate;
-  source.buffer = buffer;
-  source.start(0, 1024 / context.sampleRate, 1024 / context.sampleRate);
-  source.connect(sp);
-  sp.connect(context.destination);
-  var eventReceived = 0;
-  sp.onaudioprocess = function(e) {
-    eventReceived++;
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    // we start at the 1024 frames, we should only have ones.
+    source.loop = true;
+    source.loopStart = 1024 / context.sampleRate;
+    source.loopEnd = 2048 / context.sampleRate;
+    source.buffer = buffer;
+    source.start(0, 1024 / context.sampleRate, 1024 / context.sampleRate);
+    return source;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 4096, context.sampleRate);
+    for (var i = 0; i < 4096; i++) {
+      expectedBuffer.getChannelData(0)[i] = 1;
+    }
+    return expectedBuffer;
+  },
+};
 
-    if (eventReceived > 2){
-      sp.onaudioprocess = null;
-
-      SpecialPowers.clearUserPref("media.webaudio.enabled");
-      SimpleTest.finish();
-    }
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioBufferSourceNodeLoop.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeLoop.html
@@ -5,48 +5,41 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 2048 * 4,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
 
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-
-  var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
-  for (var i = 0; i < 4; ++i) {
-    for (var j = 0; j < 2048; ++j) {
-      expectedBuffer.getChannelData(0)[i * 2048 + j] = buffer.getChannelData(0)[j];
-    }
-  }
+    var source = context.createBufferSource();
+    source.buffer = buffer;
 
-  var source = context.createBufferSource();
-  source.buffer = buffer;
+    source.start(0);
+    source.loop = true;
+    return source;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
+    for (var i = 0; i < 4; ++i) {
+      for (var j = 0; j < 2048; ++j) {
+        expectedBuffer.getChannelData(0)[i * 2048 + j] = Math.sin(440 * 2 * Math.PI * j / context.sampleRate);
+      }
+    }
+    return expectedBuffer;
+  },
+};
 
-  var sp = context.createScriptProcessor(2048 * 4, 1);
-  source.start(0);
-  source.loop = true;
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "input buffer must have only one channel");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioBufferSourceNodeLoopStartEnd.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeLoopStartEnd.html
@@ -5,56 +5,50 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-
-  var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
-  for (var j = 0; j < 1536; ++j) {
-    expectedBuffer.getChannelData(0)[j] = buffer.getChannelData(0)[j];
-  }
-  for (var i = 0; i < 6; ++i) {
-    for (var j = 512; j < 1536; ++j) {
-      expectedBuffer.getChannelData(0)[1536 + i * 1024 + j - 512] = buffer.getChannelData(0)[j];
+var gTest = {
+  length: 2048 * 4,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
     }
-  }
-  for (var j = 7680; j < 2048 * 4; ++j) {
-    expectedBuffer.getChannelData(0)[j] = buffer.getChannelData(0)[j - 7168];
-  }
 
-  var source = context.createBufferSource();
-  source.buffer = buffer;
+    var source = context.createBufferSource();
+    source.buffer = buffer;
 
-  var sp = context.createScriptProcessor(2048 * 4, 1);
-  source.start(0);
-  source.loop = true;
-  source.loopStart = buffer.duration * 0.25;
-  source.loopEnd = buffer.duration * 0.75;
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "input buffer must have only one channel");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    var sp = context.createScriptProcessor(2048 * 4, 1);
+    source.start(0);
+    source.loop = true;
+    source.loopStart = buffer.duration * 0.25;
+    source.loopEnd = buffer.duration * 0.75;
+    return source;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 2048 * 4, context.sampleRate);
+    for (var i = 0; i < 1536; ++i) {
+      expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
+    for (var i = 0; i < 6; ++i) {
+      for (var j = 512; j < 1536; ++j) {
+        expectedBuffer.getChannelData(0)[1536 + i * 1024 + j - 512] = Math.sin(440 * 2 * Math.PI * j / context.sampleRate);
+      }
+    }
+    for (var j = 7680; j < 2048 * 4; ++j) {
+      expectedBuffer.getChannelData(0)[j] = Math.sin(440 * 2 * Math.PI * (j - 7168) / context.sampleRate);
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioBufferSourceNodeLoopStartEndSame.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeLoopStartEndSame.html
@@ -5,40 +5,29 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 0, context.sampleRate);
-
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-
-  var source = context.createBufferSource();
-  source.buffer = buffer;
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 0, context.sampleRate);
 
-  var sp = context.createScriptProcessor(2048, 1);
-  source.loop = true;
-  source.start(0);
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "input buffer must have only one channel");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    var source = context.createBufferSource();
+    source.buffer = buffer;
 
-    sp.onaudioprocess = null;
+    source.loop = true;
+    source.start(0);
+    return source;
+  },
+};
 
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioBufferSourceNodeNullBuffer.html
+++ b/content/media/webaudio/test/test_audioBufferSourceNodeNullBuffer.html
@@ -5,38 +5,27 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate); // silence
-
-  var source = context.createBufferSource();
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var source = context.createBufferSource();
 
-  var sp = context.createScriptProcessor(2048);
-  source.start(0);
-  source.buffer = null;
-  is(source.buffer, null, "Try playing back a null buffer");
-  source.connect(sp);
-  sp.connect(context.destination);
-  sp.onaudioprocess = function(e) {
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-    compareBuffers(e.inputBuffer.getChannelData(1), expectedBuffer.getChannelData(0));
+    source.start(0);
+    source.buffer = null;
+    is(source.buffer, null, "Try playing back a null buffer");
+    return source;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioParamExponentialRamp.html
+++ b/content/media/webaudio/test/test_audioParamExponentialRamp.html
@@ -5,59 +5,50 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-
-  var V0 = 0.1;
-  var V1 = 0.9;
-  var T0 = 0;
-  var T1 = 2048 / context.sampleRate;
+var V0 = 0.1;
+var V1 = 0.9;
+var T0 = 0;
 
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    var t = i / context.sampleRate;
-    expectedBuffer.getChannelData(0)[i] = V0 * Math.pow(V1 / V0, (t - T0) / (T1 - T0));
-  }
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
 
-  var destination = context.destination;
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
 
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
+    var gain = context.createGain();
+    gain.gain.setValueAtTime(V0, 0);
+    gain.gain.exponentialRampToValueAtTime(V1, 2048/context.sampleRate);
 
-  var gain = context.createGain();
-  gain.gain.setValueAtTime(V0, 0);
-  gain.gain.exponentialRampToValueAtTime(V1, 2048/context.sampleRate);
-
-  var sp = context.createScriptProcessor(2048, 1);
-  source.connect(gain);
-  gain.connect(sp);
-  sp.connect(destination);
+    source.connect(gain);
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    var T1 = 2048 / context.sampleRate;
+    var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      var t = i / context.sampleRate;
+      expectedBuffer.getChannelData(0)[i] = V0 * Math.pow(V1 / V0, (t - T0) / (T1 - T0));
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioParamLinearRamp.html
+++ b/content/media/webaudio/test/test_audioParamLinearRamp.html
@@ -5,59 +5,50 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-
-  var V0 = 0.1;
-  var V1 = 0.9;
-  var T0 = 0;
-  var T1 = 2048 / context.sampleRate;
+var V0 = 0.1;
+var V1 = 0.9;
+var T0 = 0;
 
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    var t = i / context.sampleRate;
-    expectedBuffer.getChannelData(0)[i] = V0 + (V1 - V0) * ((t - T0) / (T1 - T0));
-  }
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
 
-  var destination = context.destination;
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
 
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
+    var gain = context.createGain();
+    gain.gain.setValueAtTime(V0, 0);
+    gain.gain.linearRampToValueAtTime(V1, 2048/context.sampleRate);
 
-  var gain = context.createGain();
-  gain.gain.setValueAtTime(V0, 0);
-  gain.gain.linearRampToValueAtTime(V1, 2048/context.sampleRate);
-
-  var sp = context.createScriptProcessor(2048, 1);
-  source.connect(gain);
-  gain.connect(sp);
-  sp.connect(destination);
+    source.connect(gain);
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    var T1 = 2048 / context.sampleRate;
+    var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      var t = i / context.sampleRate;
+      expectedBuffer.getChannelData(0)[i] = V0 + (V1 - V0) * ((t - T0) / (T1 - T0));
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioParamSetCurveAtTime.html
+++ b/content/media/webaudio/test/test_audioParamSetCurveAtTime.html
@@ -5,60 +5,51 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-
-  var T0 = 0;
-  var duration = 1024 / context.sampleRate;
+var T0 = 0;
 
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var curve = new Float32Array(100);
-  for (var i = 0; i < 100; ++i) {
-    curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    var t = i / context.sampleRate;
-    expectedBuffer.getChannelData(0)[i] = curve[Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / duration)))];
-  }
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
+
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
+
+    var gain = context.createGain();
+    gain.gain.setValueCurveAtTime(this.curve, T0, this.duration);
+
+    source.connect(gain);
 
-  var destination = context.destination;
-
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
-
-  var gain = context.createGain();
-  gain.gain.setValueCurveAtTime(curve, T0, duration);
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    this.duration = 1024 / context.sampleRate;
+    this.curve = new Float32Array(100);
+    for (var i = 0; i < 100; ++i) {
+      this.curve[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
+    var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      var t = i / context.sampleRate;
+      expectedBuffer.getChannelData(0)[i] = this.curve[Math.min(99, Math.floor(100 * Math.min(1.0, (t - T0) / this.duration)))];
+    }
+    return expectedBuffer;
+  },
+};
 
-  var sp = context.createScriptProcessor(2048, 1);
-  source.connect(gain);
-  gain.connect(sp);
-  sp.connect(destination);
-
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioParamSetTargetAtTime.html
+++ b/content/media/webaudio/test/test_audioParamSetTargetAtTime.html
@@ -5,60 +5,51 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-
-  var V0 = 0.1;
-  var V1 = 0.9;
-  var T0 = 0;
-  var T1 = 2048 / context.sampleRate;
-  var TimeConstant = 10;
+var V0 = 0.1;
+var V1 = 0.9;
+var T0 = 0;
+var TimeConstant = 10;
 
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    var t = i / context.sampleRate;
-    expectedBuffer.getChannelData(0)[i] = V1 + (V0 - V1) * Math.exp(-(t - T0) / TimeConstant);
-  }
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
 
-  var destination = context.destination;
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
 
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
+    var gain = context.createGain();
+    gain.gain.value = V0;
+    gain.gain.setTargetAtTime(V1, T0, TimeConstant);
 
-  var gain = context.createGain();
-  gain.gain.value = V0;
-  gain.gain.setTargetAtTime(V1, T0, TimeConstant);
-
-  var sp = context.createScriptProcessor(2048, 1);
-  source.connect(gain);
-  gain.connect(sp);
-  sp.connect(destination);
+    source.connect(gain);
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    var T1 = 2048 / context.sampleRate;
+    var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      var t = i / context.sampleRate;
+      expectedBuffer.getChannelData(0)[i] = V1 + (V0 - V1) * Math.exp(-(t - T0) / TimeConstant);
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_audioParamTimelineDestinationOffset.html
+++ b/content/media/webaudio/test/test_audioParamTimelineDestinationOffset.html
@@ -5,51 +5,39 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var emptyBuffer = context.createBuffer(1, 16384, context.sampleRate);
-
-  setTimeout(function() {
-    var source = context.createBufferSource();
-    source.buffer = sourceBuffer;
-    source.start(context.currentTime);
-    source.stop(context.currentTime + sourceBuffer.duration);
+var gTest = {
+  length: 16384,
+  numberOfChannels: 1,
+  createGraphAsync: function(context, callback) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
 
-    var gain = context.createGain();
-    gain.gain.setValueAtTime(0, context.currentTime);
-    gain.gain.setTargetAtTime(0, context.currentTime + sourceBuffer.duration, 1);
-    source.connect(gain);
-
-    var sp = context.createScriptProcessor(16384, 1);
-    gain.connect(sp);
-    sp.connect(context.destination);
+    setTimeout(function() {
+      var source = context.createBufferSource();
+      source.buffer = sourceBuffer;
+      source.start(context.currentTime);
+      source.stop(context.currentTime + sourceBuffer.duration);
 
-    sp.onaudioprocess = function(e) {
-      is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-      compareBuffers(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0));
-
-      sp.onaudioprocess = null;
+      var gain = context.createGain();
+      gain.gain.setValueAtTime(0, context.currentTime);
+      gain.gain.setTargetAtTime(0, context.currentTime + sourceBuffer.duration, 1);
+      source.connect(gain);
 
-      SpecialPowers.clearUserPref("media.webaudio.enabled");
-      SimpleTest.finish();
-    };
-  }, 100);
-});
+      callback(gain);
+    }, 100);
+  },
+};
+
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_channelMergerNode.html
+++ b/content/media/webaudio/test/test_channelMergerNode.html
@@ -5,60 +5,53 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 2048,
+  numberOfChannels: 8,
+  createGraph: function(context) {
+    var buffers = [];
+    for (var j = 0; j < 4; ++j) {
+      var buffer = context.createBuffer(2, 2048, context.sampleRate);
+      for (var i = 0; i < 2048; ++i) {
+        buffer.getChannelData(0)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+        // Second channel is silent
+      }
+      buffers.push(buffer);
+    }
+    var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
 
-  var context = new AudioContext();
-  var buffers = [];
-  for (var j = 0; j < 4; ++j) {
-    var buffer = context.createBuffer(2, 2048, context.sampleRate);
-    for (var i = 0; i < 2048; ++i) {
-      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
-      // Second channel is silent
-    }
-    buffers.push(buffer);
-  }
-  var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
-
-  var destination = context.destination;
-
-  var merger = context.createChannelMerger();
-  is(merger.channelCount, 2, "merger node has 2 input channels by default");
-  is(merger.channelCountMode, "max", "Correct channelCountMode for the merger node");
-  is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
+    var merger = context.createChannelMerger();
+    is(merger.channelCount, 2, "merger node has 2 input channels by default");
+    is(merger.channelCountMode, "max", "Correct channelCountMode for the merger node");
+    is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
 
-  for (var i = 0; i < 4; ++i) {
-    var source = context.createBufferSource();
-    source.buffer = buffers[i];
-    source.connect(merger, 0, i);
-    source.start(0);
-  }
-
-  var sp = context.createScriptProcessor(2048, 8);
-  merger.connect(sp);
-  sp.connect(destination);
+    for (var i = 0; i < 4; ++i) {
+      var source = context.createBufferSource();
+      source.buffer = buffers[i];
+      source.connect(merger, 0, i);
+      source.start(0);
+    }
+    return merger;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(8, 2048, context.sampleRate);
+    for (var i = 0; i < 4; ++i) {
+      for (var j = 0; j < 2048; ++j) {
+        expectedBuffer.getChannelData(i * 2)[j] = Math.sin(440 * 2 * (i + 1) * Math.PI * j / context.sampleRate);
+      }
+    }
+    return expectedBuffer;
+  },
+};
 
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 8, "Correct input channel count");
-    for (var i = 0; i < 4; ++i) {
-      compareBuffers(e.inputBuffer.getChannelData(i * 2), buffers[i].getChannelData(0));
-      compareBuffers(e.inputBuffer.getChannelData(i * 2 + 1), emptyBuffer.getChannelData(0));
-    }
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_channelMergerNodeWithVolume.html
+++ b/content/media/webaudio/test/test_channelMergerNodeWithVolume.html
@@ -5,67 +5,56 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var buffers = [];
-  var expectedBuffers = [];
-  for (var j = 0; j < 4; ++j) {
-    var buffer = context.createBuffer(2, 2048, context.sampleRate);
-    var expectedBuffer = context.createBuffer(2, 2048, context.sampleRate);
-    for (var i = 0; i < 2048; ++i) {
-      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
-      expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i] / 2;
-      // Second channel is silent
-    }
-    buffers.push(buffer);
-    expectedBuffers.push(expectedBuffer);
-  }
-  var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate);
-
-  var destination = context.destination;
-
-  var merger = context.createChannelMerger();
-  is(merger.channelCount, 2, "merger node has 2 input channels by default");
-  is(merger.channelCountMode, "max", "Correct channelCountMode for the merger node");
-  is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
-
-  for (var i = 0; i < 4; ++i) {
-    var source = context.createBufferSource();
-    source.buffer = buffers[i];
-    var gain = context.createGain();
-    gain.gain.value = 0.5;
-    source.connect(gain);
-    gain.connect(merger, 0, i);
-    source.start(0);
-  }
-
-  var sp = context.createScriptProcessor(2048, 8);
-  merger.connect(sp);
-  sp.connect(destination);
-
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 8, "Correct input channel count");
-    for (var i = 0; i < 4; ++i) {
-      compareBuffers(e.inputBuffer.getChannelData(i * 2), expectedBuffers[i].getChannelData(0));
-      compareBuffers(e.inputBuffer.getChannelData(i * 2 + 1), emptyBuffer.getChannelData(0));
+var gTest = {
+  length: 2048,
+  numberOfChannels: 8,
+  createGraph: function(context) {
+    var buffers = [];
+    for (var j = 0; j < 4; ++j) {
+      var buffer = context.createBuffer(2, 2048, context.sampleRate);
+      for (var i = 0; i < 2048; ++i) {
+        buffer.getChannelData(0)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+        // Second channel is silent
+      }
+      buffers.push(buffer);
     }
 
-    sp.onaudioprocess = null;
+    var merger = context.createChannelMerger();
+    is(merger.channelCount, 2, "merger node has 2 input channels by default");
+    is(merger.channelCountMode, "max", "Correct channelCountMode for the merger node");
+    is(merger.channelInterpretation, "speakers", "Correct channelCountInterpretation for the merger node");
+
+    for (var i = 0; i < 4; ++i) {
+      var source = context.createBufferSource();
+      source.buffer = buffers[i];
+      var gain = context.createGain();
+      gain.gain.value = 0.5;
+      source.connect(gain);
+      gain.connect(merger, 0, i);
+      source.start(0);
+    }
 
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+    return merger;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(8, 2048, context.sampleRate);
+    for (var i = 0; i < 4; ++i) {
+      for (var j = 0; j < 2048; ++j) {
+        expectedBuffer.getChannelData(i * 2)[j] = Math.sin(440 * 2 * (i + 1) * Math.PI * j / context.sampleRate) / 2;
+      }
+    }
+    return expectedBuffer;
+  },
+};
+
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_channelSplitterNode.html
+++ b/content/media/webaudio/test/test_channelSplitterNode.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+// We do not use our generic graph test framework here because
+// the splitter node is special in that it creates multiple
+// output ports.
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(4, 2048, context.sampleRate);
   for (var j = 0; j < 4; ++j) {
     for (var i = 0; i < 2048; ++i) {
--- a/content/media/webaudio/test/test_channelSplitterNodeWithVolume.html
+++ b/content/media/webaudio/test/test_channelSplitterNodeWithVolume.html
@@ -5,16 +5,20 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+// We do not use our generic graph test framework here because
+// the splitter node is special in that it creates multiple
+// output ports.
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(4, 2048, context.sampleRate);
   var expectedBuffer = context.createBuffer(4, 2048, context.sampleRate);
   for (var j = 0; j < 4; ++j) {
--- a/content/media/webaudio/test/test_delayNode.html
+++ b/content/media/webaudio/test/test_delayNode.html
@@ -5,88 +5,80 @@
   <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 src="webaudio.js" type="text/javascript"></script>
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 4096,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
 
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-  var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
-  for (var i = 2048; i < 2048 * 2; ++i) {
-    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i - 2048];
-  }
+    var source = context.createBufferSource();
+
+    var delay = context.createDelay();
 
-  var destination = context.destination;
+    var delay2 = context.createDelayNode();
+    isnot(delay, delay2, "createDelayNode should create a different delay node");
 
-  var source = context.createBufferSource();
+    source.buffer = buffer;
 
-  var delay = context.createDelay();
+    source.connect(delay);
 
-  var delay2 = context.createDelayNode();
-  isnot(delay, delay2, "createDelayNode should create a different delay node");
-
-  source.buffer = buffer;
-
-  source.connect(delay);
-  var sp = context.createScriptProcessor(2048 * 2, 1);
-  delay.connect(sp);
-  sp.connect(destination);
+    ok(delay.delayTime, "The audioparam member must exist");
+    is(delay.delayTime.value, 0, "Correct initial value");
+    is(delay.delayTime.defaultValue, 0, "Correct default value");
+    delay.delayTime.value = 0.5;
+    is(delay.delayTime.value, 0.5, "Correct initial value");
+    is(delay.delayTime.defaultValue, 0, "Correct default value");
+    is(delay.channelCount, 2, "delay node has 2 input channels by default");
+    is(delay.channelCountMode, "max", "Correct channelCountMode for the delay node");
+    is(delay.channelInterpretation, "speakers", "Correct channelCountInterpretation for the delay node");
 
-  ok(delay.delayTime, "The audioparam member must exist");
-  is(delay.delayTime.value, 0, "Correct initial value");
-  is(delay.delayTime.defaultValue, 0, "Correct default value");
-  delay.delayTime.value = 0.5;
-  is(delay.delayTime.value, 0.5, "Correct initial value");
-  is(delay.delayTime.defaultValue, 0, "Correct default value");
-  is(delay.channelCount, 2, "delay node has 2 input channels by default");
-  is(delay.channelCountMode, "max", "Correct channelCountMode for the delay node");
-  is(delay.channelInterpretation, "speakers", "Correct channelCountInterpretation for the delay node");
-
-  var delay2 = context.createDelay(2);
-  is(delay2.delayTime.value, 0, "Correct initial value");
-  is(delay2.delayTime.defaultValue, 0, "Correct default value");
-  delay2.delayTime.value = 0.5;
-  is(delay2.delayTime.value, 0.5, "Correct initial value");
-  is(delay2.delayTime.defaultValue, 0, "Correct default value");
+    var delay2 = context.createDelay(2);
+    is(delay2.delayTime.value, 0, "Correct initial value");
+    is(delay2.delayTime.defaultValue, 0, "Correct default value");
+    delay2.delayTime.value = 0.5;
+    is(delay2.delayTime.value, 0.5, "Correct initial value");
+    is(delay2.delayTime.defaultValue, 0, "Correct default value");
 
-  expectException(function() {
-    context.createDelay(0);
-  }, DOMException.NOT_SUPPORTED_ERR);
-  expectException(function() {
-    context.createDelay(180);
-  }, DOMException.NOT_SUPPORTED_ERR);
-  expectTypeError(function() {
-    context.createDelay(NaN);
-  }, DOMException.NOT_SUPPORTED_ERR);
-  expectException(function() {
-    context.createDelay(-1);
-  }, DOMException.NOT_SUPPORTED_ERR);
-  context.createDelay(1); // should not throw
+    expectException(function() {
+      context.createDelay(0);
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectException(function() {
+      context.createDelay(180);
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectTypeError(function() {
+      context.createDelay(NaN);
+    }, DOMException.NOT_SUPPORTED_ERR);
+    expectException(function() {
+      context.createDelay(-1);
+    }, DOMException.NOT_SUPPORTED_ERR);
+    context.createDelay(1); // should not throw
 
-  // Delay the source stream by 2048 frames
-  delay.delayTime.value = 2048 / context.sampleRate;
+    // Delay the source stream by 2048 frames
+    delay.delayTime.value = 2048 / context.sampleRate;
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    source.start(0);
+    return delay;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
+    for (var i = 2048; i < 2048 * 2; ++i) {
+      expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * (i - 2048) / context.sampleRate);
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_delayNodeWithGain.html
+++ b/content/media/webaudio/test/test_delayNodeWithGain.html
@@ -5,58 +5,50 @@
   <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 src="webaudio.js" type="text/javascript"></script>
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 4096,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
 
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-  var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
-  for (var i = 2048; i < 2048 * 2; ++i) {
-    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i - 2048] / 2;
-  }
+    var source = context.createBufferSource();
+
+    var delay = context.createDelay();
 
-  var destination = context.destination;
-
-  var source = context.createBufferSource();
+    source.buffer = buffer;
 
-  var delay = context.createDelay();
-
-  source.buffer = buffer;
+    var gain = context.createGain();
+    gain.gain.value = 0.5;
 
-  var gain = context.createGain();
-  gain.gain.value = 0.5;
+    source.connect(gain);
+    gain.connect(delay);
 
-  source.connect(gain);
-  gain.connect(delay);
-  var sp = context.createScriptProcessor(2048 * 2, 1);
-  delay.connect(sp);
-  sp.connect(destination);
-
-  // Delay the source stream by 2048 frames
-  delay.delayTime.value = 2048 / context.sampleRate;
+    // Delay the source stream by 2048 frames
+    delay.delayTime.value = 2048 / context.sampleRate;
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
+    source.start(0);
+    return delay;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 2048 * 2, context.sampleRate);
+    for (var i = 2048; i < 2048 * 2; ++i) {
+      expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * (i - 2048) / context.sampleRate) / 2;
+    }
+    return expectedBuffer;
+  },
+};
 
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_gainNode.html
+++ b/content/media/webaudio/test/test_gainNode.html
@@ -5,64 +5,56 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+var gTest = {
+  length: 2048,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var buffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
+    }
 
-  var context = new AudioContext();
-  var buffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate);
-  }
-  var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i] / 2;
-  }
+    var source = context.createBufferSource();
+
+    var gain = context.createGain();
 
-  var destination = context.destination;
-
-  var source = context.createBufferSource();
+    var gain2 = context.createGainNode();
+    isnot(gain, gain2, "createGainNode should create a different gain node");
 
-  var gain = context.createGain();
+    source.buffer = buffer;
 
-  var gain2 = context.createGainNode();
-  isnot(gain, gain2, "createGainNode should create a different gain node");
-
-  source.buffer = buffer;
+    source.connect(gain);
 
-  source.connect(gain);
-  var sp = context.createScriptProcessor(2048, 1);
-  gain.connect(sp);
-  sp.connect(destination);
+    ok(gain.gain, "The audioparam member must exist");
+    is(gain.gain.value, 1.0, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+    gain.gain.value = 0.5;
+    is(gain.gain.value, 0.5, "Correct initial value");
+    is(gain.gain.defaultValue, 1.0, "Correct default value");
+    is(gain.channelCount, 2, "gain node has 2 input channels by default");
+    is(gain.channelCountMode, "max", "Correct channelCountMode for the gain node");
+    is(gain.channelInterpretation, "speakers", "Correct channelCountInterpretation for the gain node");
 
-  ok(gain.gain, "The audioparam member must exist");
-  is(gain.gain.value, 1.0, "Correct initial value");
-  is(gain.gain.defaultValue, 1.0, "Correct default value");
-  gain.gain.value = 0.5;
-  is(gain.gain.value, 0.5, "Correct initial value");
-  is(gain.gain.defaultValue, 1.0, "Correct default value");
-  is(gain.channelCount, 2, "gain node has 2 input channels by default");
-  is(gain.channelCountMode, "max", "Correct channelCountMode for the gain node");
-  is(gain.channelInterpretation, "speakers", "Correct channelCountInterpretation for the gain node");
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      expectedBuffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate) / 2;
+    }
+    return expectedBuffer;
+  },
+};
 
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_gainNodeInLoop.html
+++ b/content/media/webaudio/test/test_gainNodeInLoop.html
@@ -5,53 +5,44 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
+var gTest = {
+  length: 4096,
+  numberOfChannels: 1,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+    }
 
-  var sourceBuffer = context.createBuffer(1, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-  }
-  var expectedBuffer = context.createBuffer(1, 4096, context.sampleRate);
-  for (var i = 0; i < 4096; ++i) {
-    expectedBuffer.getChannelData(0)[i] = 0.5;
-  }
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
+    source.loop = true;
+    source.start(0);
+    source.stop(sourceBuffer.duration * 2);
 
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
-  source.loop = true;
-  source.start(0);
-  source.stop(sourceBuffer.duration * 2);
-
-  var gain = context.createGain();
-  // Adjust the gain in a way that we don't just end up modifying AudioChunk::mVolume
-  gain.gain.setValueAtTime(0.5, 0);
-  source.connect(gain);
+    var gain = context.createGain();
+    // Adjust the gain in a way that we don't just end up modifying AudioChunk::mVolume
+    gain.gain.setValueAtTime(0.5, 0);
+    source.connect(gain);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    var expectedBuffer = context.createBuffer(1, 4096, context.sampleRate);
+    for (var i = 0; i < 4096; ++i) {
+      expectedBuffer.getChannelData(0)[i] = 0.5;
+    }
+    return expectedBuffer;
+  },
+};
 
-  var sp = context.createScriptProcessor(4096, 1);
-  gain.connect(sp);
-  sp.connect(context.destination);
-
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_nodeToParamConnection.html
+++ b/content/media/webaudio/test/test_nodeToParamConnection.html
@@ -5,65 +5,56 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
-SimpleTest.waitForExplicitFinish();
-addLoadEvent(function() {
-  SpecialPowers.setBoolPref("media.webaudio.enabled", true);
-
-  var context = new AudioContext();
-  var buffer = context.createBuffer(2, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    for (var j = 0; j < 2; ++j) {
-      buffer.getChannelData(j)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+var gTest = {
+  length: 2048,
+  createGraph: function(context) {
+    var sourceBuffer = context.createBuffer(2, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      sourceBuffer.getChannelData(0)[i] = 1;
+      sourceBuffer.getChannelData(1)[i] = -1;
     }
-  }
-  var sourceBuffer = context.createBuffer(2, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    sourceBuffer.getChannelData(0)[i] = 1;
-    sourceBuffer.getChannelData(1)[i] = -1;
-  }
-  var expectedBuffer = context.createBuffer(2, 2048, context.sampleRate);
-  for (var i = 0; i < 2048; ++i) {
-    expectedBuffer.getChannelData(0)[i] = 1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2;
-    expectedBuffer.getChannelData(1)[i] = -(1 + (buffer.getChannelData(0)[i] + buffer.getChannelData(1)[i]) / 2);
-  }
+
+    var destination = context.destination;
+
+    var paramSource = context.createBufferSource();
+    paramSource.buffer = this.buffer;
 
-  var destination = context.destination;
+    var source = context.createBufferSource();
+    source.buffer = sourceBuffer;
+
+    var gain = context.createGain();
+
+    paramSource.connect(gain.gain);
+    source.connect(gain);
 
-  var paramSource = context.createBufferSource();
-  paramSource.buffer = buffer;
-
-  var source = context.createBufferSource();
-  source.buffer = sourceBuffer;
-
-  var gain = context.createGain();
-
-  paramSource.connect(gain.gain);
-  source.connect(gain);
+    paramSource.start(0);
+    source.start(0);
+    return gain;
+  },
+  createExpectedBuffers: function(context) {
+    this.buffer = context.createBuffer(2, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      for (var j = 0; j < 2; ++j) {
+        this.buffer.getChannelData(j)[i] = Math.sin(440 * 2 * (j + 1) * Math.PI * i / context.sampleRate);
+      }
+    }
+    var expectedBuffer = context.createBuffer(2, 2048, context.sampleRate);
+    for (var i = 0; i < 2048; ++i) {
+      expectedBuffer.getChannelData(0)[i] = 1 + (this.buffer.getChannelData(0)[i] + this.buffer.getChannelData(1)[i]) / 2;
+      expectedBuffer.getChannelData(1)[i] = -(1 + (this.buffer.getChannelData(0)[i] + this.buffer.getChannelData(1)[i]) / 2);
+    }
+    return expectedBuffer;
+  },
+};
 
-  var sp = context.createScriptProcessor(2048);
-  gain.connect(sp);
-  sp.connect(destination);
-
-  paramSource.start(0);
-  source.start(0);
-  sp.onaudioprocess = function(e) {
-    is(e.inputBuffer.numberOfChannels, 2, "Correct input channel count");
-    compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0));
-    compareBuffers(e.inputBuffer.getChannelData(1), expectedBuffer.getChannelData(1));
-
-    sp.onaudioprocess = null;
-
-    SpecialPowers.clearUserPref("media.webaudio.enabled");
-    SimpleTest.finish();
-  };
-});
+runTest();
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webaudio/test/test_scriptProcessorNode.html
+++ b/content/media/webaudio/test/test_scriptProcessorNode.html
@@ -5,16 +5,21 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+// We do not use our generic graph test framework here because
+// the testing logic here is sort of complicated, and would
+// not be easy to map to OfflineAudioContext, as ScriptProcessorNodes
+// can experience delays.
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = null;
 
   var sourceSP = context.createJavaScriptNode(2048);
--- a/content/media/webaudio/test/test_scriptProcessorNodeChannelCount.html
+++ b/content/media/webaudio/test/test_scriptProcessorNodeChannelCount.html
@@ -5,16 +5,21 @@
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="webaudio.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
+// We do not use our generic graph test framework here because
+// the testing logic here is sort of complicated, and would
+// not be easy to map to OfflineAudioContext, as ScriptProcessorNodes
+// can experience delays.
+
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   SpecialPowers.setBoolPref("media.webaudio.enabled", true);
 
   var context = new AudioContext();
   var buffer = context.createBuffer(6, 2048, context.sampleRate);
   for (var i = 0; i < 2048; ++i) {
     for (var j = 0; j < 6; ++j) {
--- a/content/media/webaudio/test/webaudio.js
+++ b/content/media/webaudio/test/webaudio.js
@@ -51,8 +51,97 @@ function compareBuffers(buf1, buf2,
     }
   };
 
   is(difference, 0, "Found " + difference + " different samples, maxDifference: " +
      maxDifference + ", first bad index: " + firstBadIndex +
      " with source offset " + sourceOffset + " and desitnation offset " +
      destOffset);
 }
+
+function getEmptyBuffer(context, length) {
+  return context.createBuffer(gTest.numberOfChannels, length, context.sampleRate);
+}
+
+/**
+ * This function assumes that the test file defines a single gTest variable with
+ * the following properties and methods:
+ *
+ * + length: mandatory property equal to the total number of frames which we
+ *           are waiting to see in the output.
+ * + numberOfChannels: optional property which specifies the number of channels
+ *                     in the output.  The default value is 2.
+ * + createGraph: mandatory method which takes a context object and does
+ *                everything needed in order to set up the Web Audio graph.
+ *                This function returns the node to be inspected.
+ * + createGraphAsync: async version of createGraph.  This function takes
+ *                     a callback which should be called with an argument
+ *                     set to the node to be inspected when the callee is
+ *                     ready to proceed with the test.  Either this function
+ *                     or createGraph must be provided.
+ * + createExpectedBuffers: optional method which takes a context object and
+ *                          returns either one expected buffer or an array of
+ *                          them, designating what is expected to be observed
+ *                          in the output.  If omitted, the output is expected
+ *                          to be silence.  The sum of the length of the expected
+ *                          buffers should be equal to gTest.length.  This
+ *                          function is guaranteed to be called before createGraph.
+ */
+function runTest()
+{
+  function done() {
+    SpecialPowers.clearUserPref("media.webaudio.enabled");
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  addLoadEvent(function() {
+    SpecialPowers.setBoolPref("media.webaudio.enabled", true);
+
+    if (!gTest.numberOfChannels) {
+      gTest.numberOfChannels = 2; // default
+    }
+
+    var context = new AudioContext();
+    if (!gTest.createExpectedBuffers) {
+      // Assume that the output is silence
+      var expectedBuffers = getEmptyBuffer(context, gTest.length);
+    } else {
+      var expectedBuffers = gTest.createExpectedBuffers(context);
+    }
+    if (!(expectedBuffers instanceof Array)) {
+      expectedBuffers = [expectedBuffers];
+    }
+    var expectedFrames = 0;
+    for (var i = 0; i < expectedBuffers.length; ++i) {
+      is(expectedBuffers[i].numberOfChannels, gTest.numberOfChannels,
+         "Correct number of channels for expected buffer " + i);
+      expectedFrames += expectedBuffers[i].length;
+    }
+    is(expectedFrames, gTest.length, "Correct number of expected frames");
+
+    if (gTest.createGraphAsync) {
+      gTest.createGraphAsync(context, function(nodeToInspect) {
+        testOutput(nodeToInspect);
+      });
+    } else {
+      testOutput(gTest.createGraph(context));
+    }
+
+    function testOutput(nodeToInspect) {
+      var sp = context.createScriptProcessor(expectedBuffers[0].length, gTest.numberOfChannels);
+      nodeToInspect.connect(sp);
+      sp.connect(context.destination);
+      sp.onaudioprocess = function(e) {
+        var expectedBuffer = expectedBuffers.shift();
+        is(e.inputBuffer.numberOfChannels, expectedBuffer.numberOfChannels,
+           "Correct number of input buffer channels");
+        for (var i = 0; i < e.inputBuffer.numberOfChannels; ++i) {
+          compareBuffers(e.inputBuffer.getChannelData(i), expectedBuffer.getChannelData(i));
+        }
+        if (expectedBuffers.length == 0) {
+          sp.onaudioprocess = null;
+          done();
+        }
+      };
+    }
+  });
+}
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6621,17 +6621,17 @@ PostMessageReadStructuredClone(JSContext
   }
 
   return nullptr;
 }
 
 static JSBool
 PostMessageWriteStructuredClone(JSContext* cx,
                                 JSStructuredCloneWriter* writer,
-                                JSObject* obj,
+                                JS::Handle<JSObject*> obj,
                                 void *closure)
 {
   StructuredCloneInfo* scInfo = static_cast<StructuredCloneInfo*>(closure);
   NS_ASSERTION(scInfo, "Must have scInfo!");
 
   nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative;
   nsContentUtils::XPConnect()->
     GetWrappedNativeOfJSObject(cx, obj, getter_AddRefs(wrappedNative));
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3418,17 +3418,17 @@ NS_DOMReadStructuredClone(JSContext* cx,
   // Don't know what this is. Bail.
   xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
   return nullptr;
 }
 
 JSBool
 NS_DOMWriteStructuredClone(JSContext* cx,
                            JSStructuredCloneWriter* writer,
-                           JSObject* obj,
+                           JS::Handle<JSObject*> obj,
                            void *closure)
 {
   ImageData* imageData;
   nsresult rv = UnwrapObject<ImageData>(cx, obj, imageData);
   if (NS_FAILED(rv)) {
     // Don't know what this is. Bail.
     xpc::Throw(cx, NS_ERROR_DOM_DATA_CLONE_ERR);
     return JS_FALSE;
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -321,13 +321,13 @@ nsresult NS_CreateJSRuntime(nsIScriptRun
 void NS_ScriptErrorReporter(JSContext *cx, const char *message, JSErrorReport *report);
 
 JSObject* NS_DOMReadStructuredClone(JSContext* cx,
                                     JSStructuredCloneReader* reader, uint32_t tag,
                                     uint32_t data, void* closure);
 
 JSBool NS_DOMWriteStructuredClone(JSContext* cx,
                                   JSStructuredCloneWriter* writer,
-                                  JSObject* obj, void *closure);
+                                  JS::Handle<JSObject*> obj, void *closure);
 
 void NS_DOMStructuredCloneError(JSContext* cx, uint32_t errorid);
 
 #endif /* nsJSEnvironment_h */
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -8648,21 +8648,44 @@ class CGJSImplMethod(CGNativeMember):
         callbackArgs = [arg.name for arg in self.getArgs(self.signature[0], self.signature[1])]
         return 'return mImpl->%s(%s);' % (self.name, ", ".join(callbackArgs))
 
     def getConstructorImpl(self):
         assert self.descriptor.interface.isJSImplemented()
         if self.name != 'Constructor':
             raise TypeError("Named constructors are not supported for JS implemented WebIDL. See bug 851287.")
         if len(self.signature[1]) != 0:
-            raise TypeError("Constructors with arguments are unsupported. See bug 851178.")
-        return genConstructorBody(self.descriptor)
-
-def genConstructorBody(descriptor):
-        return string.Template(
+            args = self.getArgs(self.signature[0], self.signature[1])
+            # The first two arguments to the constructor implementation are not
+            # arguments to the WebIDL constructor, so don't pass them to __Init()
+            assert args[0].argType == 'const GlobalObject&'
+            assert args[1].argType == 'JSContext*'
+            args = args[2:]
+            constructorArgs = [arg.name for arg in args]
+            initCall = """
+  // Wrap the object before calling __Init so that __DOM_IMPL__ is available.
+  nsCOMPtr<nsIGlobalObject> globalHolder = do_QueryInterface(window);
+  JS::Rooted<JSObject*> scopeObj(cx, globalHolder->GetGlobalJSObject());
+  JS::Rooted<JS::Value> wrappedVal(cx);
+  if (!WrapNewBindingObject(cx, scopeObj, impl, wrappedVal.address())) {
+    MOZ_ASSERT(JS_IsExceptionPending(cx));
+    aRv.Throw(NS_ERROR_UNEXPECTED);
+    return nullptr;
+  }
+  // Initialize the object with the constructor arguments.
+  impl->mImpl->__Init(%s);
+  if (aRv.Failed()) {
+    return nullptr;
+  }""" % (", ".join(constructorArgs))
+        else:
+            initCall = ""
+        return genConstructorBody(self.descriptor, initCall)
+
+def genConstructorBody(descriptor, initCall=""):
+    return string.Template(
 """  // Get the window to use as a parent and for initialization.
   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global.Get());
   if (!window) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   JS::Rooted<JSObject*> jsImplObj(cx);
@@ -8700,19 +8723,20 @@ def genConstructorBody(descriptor):
       return nullptr;
     }
     if (NS_FAILED(implWrapped->GetJSObject(jsImplObj.address()))) {
       aRv.Throw(NS_ERROR_FAILURE);
       return nullptr;
     }
   }
   // Build the C++ implementation.
-  nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);
+  nsRefPtr<${implClass}> impl = new ${implClass}(jsImplObj, window);${initCall}
   return impl.forget();""").substitute({"implClass" : descriptor.name,
-                 "contractId" : descriptor.interface.getJSImplementation()
+                 "contractId" : descriptor.interface.getJSImplementation(),
+                 "initCall" : initCall
                  })
 
 # We're always fallible
 def callbackGetterName(attr):
     return "Get" + MakeNativeName(attr.identifier.name)
 
 def callbackSetterName(attr):
     return "Set" + MakeNativeName(attr.identifier.name)
@@ -8979,16 +9003,21 @@ class CGCallbackInterface(CGCallback):
         attrs = [m for m in iface.members if m.isAttr() and not m.isStatic()]
         getters = [CallbackGetter(a, descriptor) for a in attrs]
         setters = [CallbackSetter(a, descriptor) for a in attrs
                    if not a.readonly]
         methods = [m for m in iface.members
                    if m.isMethod() and not m.isStatic()]
         methods = [CallbackOperation(m, sig, descriptor) for m in methods
                    for sig in m.signatures()]
+        if iface.isJSImplemented() and iface.ctor():
+            sigs = descriptor.interface.ctor().signatures()
+            if len(sigs) != 1:
+                raise TypeError("We only handle one constructor.  See bug 869268.")
+            methods.append(CGJSImplInitOperation(sigs[0], descriptor))
         CGCallback.__init__(self, iface, descriptor, "CallbackInterface",
                             methods, getters=getters, setters=setters)
 
 class FakeMember():
     def __init__(self):
         self.treatUndefinedAs = self.treatNullAs = "Default"
     def isStatic(self):
         return False
@@ -9251,25 +9280,24 @@ class CallCallback(CallbackMethod):
                                 descriptorProvider, needThisHandling=True)
 
     def getThisObj(self):
         return "aThisObj"
 
     def getCallableDecl(self):
         return "JS::Rooted<JS::Value> callable(cx, JS::ObjectValue(*mCallback));\n"
 
-class CallbackOperation(CallbackMethod):
-    def __init__(self, method, signature, descriptor):
-        self.singleOperation = descriptor.interface.isSingleOperationInterface()
-        self.ensureASCIIName(method)
-        self.methodName = method.identifier.name
-        CallbackMethod.__init__(self, signature,
-                                MakeNativeName(self.methodName),
-                                descriptor,
-                                self.singleOperation)
+class CallbackOperationBase(CallbackMethod):
+    """
+    Common class for implementing various callback operations.
+    """
+    def __init__(self, signature, jsName, nativeName, descriptor, singleOperation):
+        self.singleOperation = singleOperation
+        self.methodName = jsName
+        CallbackMethod.__init__(self, signature, nativeName, descriptor, singleOperation)
 
     def getThisObj(self):
         if not self.singleOperation:
             return "mCallback"
         # This relies on getCallableDecl declaring a boolean
         # isCallable in the case when we're a single-operation
         # interface.
         return "isCallable ? aThisObj : mCallback"
@@ -9290,16 +9318,27 @@ class CallbackOperation(CallbackMethod):
             'bool isCallable = JS_ObjectIsCallable(cx, mCallback);\n'
             'JS::Rooted<JS::Value> callable(cx);\n'
             'if (isCallable) {\n'
             '  callable = JS::ObjectValue(*mCallback);\n'
             '} else {\n'
             '%s'
             '}\n' % CGIndenter(CGGeneric(getCallableFromProp)).define())
 
+class CallbackOperation(CallbackOperationBase):
+    """
+    Codegen actual WebIDL operations on callback interfaces.
+    """
+    def __init__(self, method, signature, descriptor):
+        self.ensureASCIIName(method)
+        jsName = method.identifier.name
+        CallbackOperationBase.__init__(self, signature,
+                                       jsName, MakeNativeName(jsName),
+                                       descriptor, descriptor.interface.isSingleOperationInterface())
+
 class CallbackGetter(CallbackMember):
     def __init__(self, attr, descriptor):
         self.ensureASCIIName(attr)
         self.attrName = attr.identifier.name
         CallbackMember.__init__(self,
                                 (attr.type, []),
                                 callbackGetterName(attr),
                                 descriptor,
@@ -9345,16 +9384,25 @@ class CallbackSetter(CallbackMember):
             'if (!JS_SetProperty(cx, mCallback, "${attrName}", ${argv})) {\n'
             '  aRv.Throw(NS_ERROR_UNEXPECTED);\n'
             '  return${errorReturn};\n'
             '}\n').substitute(replacements)
 
     def getArgcDecl(self):
         return None
 
+class CGJSImplInitOperation(CallbackOperationBase):
+    """
+    Codegen the __Init() method used to pass along constructor arguments for JS-implemented WebIDL.
+    """
+    def __init__(self, sig, descriptor):
+        assert sig in descriptor.interface.ctor().signatures()
+        CallbackOperationBase.__init__(self, (BuiltinTypes[IDLBuiltinType.Types.void], sig[1]),
+                                       "__init", "__Init", descriptor, False)
+
 class GlobalGenRoots():
     """
     Roots for global codegen.
 
     To generate code, call the method associated with the target, and then
     call the appropriate define/declare method.
     """
 
--- a/dom/bindings/test/TestExampleGen.webidl
+++ b/dom/bindings/test/TestExampleGen.webidl
@@ -5,17 +5,20 @@
  */
 [Constructor,
  Constructor(DOMString str),
  Constructor(unsigned long num, boolean? boolArg),
  Constructor(TestInterface? iface),
  Constructor(long arg1, IndirectlyImplementedInterface iface),
  // Constructor(long arg1, long arg2, (TestInterface or OnlyForUseInConstructor) arg3),
  NamedConstructor=Example,
- NamedConstructor=Example(DOMString str)
+ NamedConstructor=Example(DOMString str),
+ NamedConstructor=Example2(DictForConstructor dict, any any1, object obj1,
+                           object? obj2, sequence<Dict> seq, optional any any2,
+                           optional object obj3, optional object? obj4)
  ]
 interface TestExampleInterface {
   // Integer types
   // XXXbz add tests for throwing versions of all the integer stuff
   readonly attribute byte readonlyByte;
   attribute byte writableByte;
   void passByte(byte arg);
   byte receiveByte();
--- a/dom/bindings/test/TestJSImplGen.webidl
+++ b/dom/bindings/test/TestJSImplGen.webidl
@@ -12,17 +12,26 @@ callback MyTestCallback = void();
 
 TestInterface implements ImplementedInterface;
 
 enum MyTestEnum {
   "a",
   "b"
 };
 
-[Constructor, JSImplementation="@mozilla.org/test-js-impl-interface;1"]
+// We don't support multiple constructors (bug 869268) or named constructors
+// for JS-implemented WebIDL.
+[Constructor(DOMString str, unsigned long num, boolean? boolArg,
+             TestInterface? iface, long arg1,
+             DictForConstructor dict, any any1,
+             /* (BUG 856911) object obj1,*/
+             object? obj2, sequence<Dict> seq, optional any any2,
+             /* (BUG 856911) optional object obj3, */
+             optional object? obj4),
+ JSImplementation="@mozilla.org/test-js-impl-interface;1"]
 interface TestJSImplInterface {
   // Integer types
   // XXXbz add tests for throwing versions of all the integer stuff
   readonly attribute byte readonlyByte;
   attribute byte writableByte;
   void passByte(byte arg);
   byte receiveByte();
   void passOptionalByte(optional byte arg);
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -1456,17 +1456,17 @@ IDBObjectStore::StructuredCloneReadCallb
 
   return nullptr;
 }
 
 // static
 JSBool
 IDBObjectStore::StructuredCloneWriteCallback(JSContext* aCx,
                                              JSStructuredCloneWriter* aWriter,
-                                             JSObject* aObj,
+                                             JS::Handle<JSObject*> aObj,
                                              void* aClosure)
 {
   StructuredCloneWriteInfo* cloneWriteInfo =
     reinterpret_cast<StructuredCloneWriteInfo*>(aClosure);
 
   if (JS_GetClass(aObj) == &sDummyPropJSClass) {
     NS_ASSERTION(cloneWriteInfo->mOffsetToKeyProp == 0,
                  "We should not have been here before!");
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -106,17 +106,17 @@ public:
   StructuredCloneReadCallback(JSContext* aCx,
                               JSStructuredCloneReader* aReader,
                               uint32_t aTag,
                               uint32_t aData,
                               void* aClosure);
   static JSBool
   StructuredCloneWriteCallback(JSContext* aCx,
                                JSStructuredCloneWriter* aWriter,
-                               JSObject* aObj,
+                               JS::Handle<JSObject*> aObj,
                                void* aClosure);
 
   static nsresult
   ConvertFileIdsToArray(const nsAString& aFileIds,
                         nsTArray<int64_t>& aResult);
 
   // Called only in the main process.
   static nsresult
--- a/dom/ipc/StructuredCloneUtils.cpp
+++ b/dom/ipc/StructuredCloneUtils.cpp
@@ -102,18 +102,18 @@ Read(JSContext* aCx, JSStructuredCloneRe
 
     return &wrappedBlob.toObject();
   }
 
   return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
 }
 
 JSBool
-Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
-      void* aClosure)
+Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+      JS::Handle<JSObject*> aObj, void* aClosure)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aClosure);
 
   StructuredCloneClosure* closure =
     static_cast<StructuredCloneClosure*>(aClosure);
 
   // See if this is a wrapped native.
--- a/dom/workers/EventListenerManager.cpp
+++ b/dom/workers/EventListenerManager.cpp
@@ -282,54 +282,55 @@ EventListenerManager::GetEventListener(c
     }
   }
 
   return NULL;
 }
 
 bool
 EventListenerManager::DispatchEvent(JSContext* aCx, const EventTarget& aTarget,
-                                    JSObject* aEvent, ErrorResult& aRv) const
+                                    JSObject* event, ErrorResult& aRv) const
 {
+  JS::Rooted<JSObject*> aEvent(aCx, event);
   using namespace mozilla::dom::workers::events;
 
   if (!IsSupportedEventClass(aEvent)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
-  jsval val;
-  if (!JS_GetProperty(aCx, aEvent, "target", &val)) {
+  JS::Rooted<JS::Value> val(aCx);
+  if (!JS_GetProperty(aCx, aEvent, "target", val.address())) {
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   if (!JSVAL_IS_NULL(val)) {
     // Already has a target, must be recursively dispatched. Throw.
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   if (mCollections.isEmpty()) {
     return false;
   }
 
-  JSString* eventType;
+  JS::Rooted<JSString*> eventType(aCx);
   JSBool eventIsTrusted;
 
-  if (!JS_GetProperty(aCx, aEvent, "type", &val) ||
+  if (!JS_GetProperty(aCx, aEvent, "type", val.address()) ||
       !(eventType = JS_ValueToString(aCx, val)) ||
       !(eventType = JS_InternJSString(aCx, eventType))) {
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   // We have already ensure that the event is one of our types of events so
   // there is no need to worry about this property being faked.
-  if (!JS_GetProperty(aCx, aEvent, "isTrusted", &val) ||
+  if (!JS_GetProperty(aCx, aEvent, "isTrusted", val.address()) ||
       !JS_ValueToBoolean(aCx, val, &eventIsTrusted)) {
     aRv.Throw(NS_ERROR_FAILURE);
     return false;
   }
 
   ListenerCollection* collection =
     GetCollectionForType(mCollections, INTERNED_STRING_TO_JSID(aCx, eventType));
   if (!collection) {
@@ -377,42 +378,42 @@ EventListenerManager::DispatchEvent(JSCo
     }
 
     // If anything fails in here we want to report the exception and continue on
     // to the next listener rather than bailing out. If something fails and
     // does not set an exception then we bail out entirely as we've either run
     // out of memory or the operation callback has indicated that we should
     // stop running.
 
-    jsval listenerVal = listeners[index];
+    JS::Rooted<JS::Value> listenerVal(aCx, listeners[index]);
 
-    JSObject* listenerObj;
-    if (!JS_ValueToObject(aCx, listenerVal, &listenerObj)) {
+    JS::Rooted<JSObject*> listenerObj(aCx);
+    if (!JS_ValueToObject(aCx, listenerVal, listenerObj.address())) {
       if (!JS_ReportPendingException(aCx)) {
         aRv.Throw(NS_ERROR_FAILURE);
         return false;
       }
       continue;
     }
 
     static const char sHandleEventChars[] = "handleEvent";
 
-    JSObject* thisObj = aTarget.GetJSObject();
+    JS::Rooted<JSObject*> thisObj(aCx, aTarget.GetJSObject());
 
     JSBool hasHandleEvent;
     if (!JS_HasProperty(aCx, listenerObj, sHandleEventChars, &hasHandleEvent)) {
       if (!JS_ReportPendingException(aCx)) {
         aRv.Throw(NS_ERROR_FAILURE);
         return false;
       }
       continue;
     }
 
     if (hasHandleEvent) {
-      if (!JS_GetProperty(aCx, listenerObj, sHandleEventChars, &listenerVal)) {
+      if (!JS_GetProperty(aCx, listenerObj, sHandleEventChars, listenerVal.address())) {
         if (!JS_ReportPendingException(aCx)) {
           aRv.Throw(NS_ERROR_FAILURE);
           return false;
         }
         continue;
       }
 
       thisObj = listenerObj;
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -82,18 +82,18 @@ public:
     if (proto && !JS_DefineProperties(aCx, proto, sStaticProperties)) {
       return NULL;
     }
 
     return proto;
   }
 
   static JSObject*
-  Create(JSContext* aCx, JSObject* aParent, JSString* aType, bool aBubbles,
-         bool aCancelable, bool aMainRuntime)
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JS::Handle<JSString*> aType,
+         bool aBubbles, bool aCancelable, bool aMainRuntime)
   {
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     JSObject* obj = JS_NewObject(aCx, clasp, NULL, aParent);
     if (obj) {
       Event* priv = new Event();
       SetJSPrivateSafeish(obj, priv);
       InitEventCommon(obj, priv, aType, aBubbles, aCancelable, true);
@@ -406,17 +406,17 @@ public:
   {
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, JSObject* aParent, JSAutoStructuredCloneBuffer& aData,
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JSAutoStructuredCloneBuffer& aData,
          nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime)
   {
     JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "message"));
     if (!type) {
       return NULL;
     }
 
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
@@ -632,18 +632,18 @@ public:
   {
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
     return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, JSObject* aParent, JSString* aMessage,
-         JSString* aFilename, uint32_t aLineNumber, bool aMainRuntime)
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JS::Handle<JSString*> aMessage,
+         JS::Handle<JSString*> aFilename, uint32_t aLineNumber, bool aMainRuntime)
   {
     JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "error"));
     if (!type) {
       return NULL;
     }
 
     JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass;
 
@@ -813,17 +813,17 @@ public:
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
   {
     return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0,
                         sProperties, sFunctions, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, JSObject* aParent, JSString* aType,
+  Create(JSContext* aCx, JS::Handle<JSObject*> aParent, JSString* aType,
          bool aLengthComputable, double aLoaded, double aTotal)
   {
     JS::Rooted<JSString*> type(aCx, JS_InternJSString(aCx, aType));
     if (!type) {
       return NULL;
     }
 
     JS::Rooted<JSObject*> obj(aCx, JS_NewObject(aCx, &sClass, NULL, aParent));
@@ -919,17 +919,17 @@ private:
 
     aVp.set(JS_GetReservedSlot(aObj, slot));
     return true;
   }
 
   static JSBool
   InitProgressEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     ProgressEvent* event = GetInstancePrivate(aCx, obj, sFunctions[0].name);
     if (!event) {
       return false;
     }
@@ -1001,46 +1001,47 @@ InitClasses(JSContext* aCx, JS::Handle<J
   }
 
   return MessageEvent::InitClass(aCx, aGlobal, eventProto, aMainRuntime) &&
          ErrorEvent::InitClass(aCx, aGlobal, eventProto, aMainRuntime) &&
          ProgressEvent::InitClass(aCx, aGlobal, eventProto);
 }
 
 JSObject*
-CreateGenericEvent(JSContext* aCx, JSString* aType, bool aBubbles,
+CreateGenericEvent(JSContext* aCx, JS::Handle<JSString*> aType, bool aBubbles,
                    bool aCancelable, bool aMainRuntime)
 {
-  JSObject* global = JS_GetGlobalForScopeChain(aCx);
+  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
   return Event::Create(aCx, global, aType, aBubbles, aCancelable, aMainRuntime);
 }
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
                    nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime)
 {
-  JSObject* global = JS_GetGlobalForScopeChain(aCx);
+  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
   return MessageEvent::Create(aCx, global, aData, aClonedObjects, aMainRuntime);
 }
 
 JSObject*
-CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
-                 uint32_t aLineNumber, bool aMainRuntime)
+CreateErrorEvent(JSContext* aCx, JS::Handle<JSString*> aMessage,
+                 JS::Handle<JSString*> aFilename, uint32_t aLineNumber,
+                 bool aMainRuntime)
 {
-  JSObject* global = JS_GetGlobalForScopeChain(aCx);
+  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
   return ErrorEvent::Create(aCx, global, aMessage, aFilename, aLineNumber,
                             aMainRuntime);
 }
 
 JSObject*
 CreateProgressEvent(JSContext* aCx, JSString* aType, bool aLengthComputable,
                     double aLoaded, double aTotal)
 {
-  JSObject* global = JS_GetGlobalForScopeChain(aCx);
+  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
   return ProgressEvent::Create(aCx, global, aType, aLengthComputable, aLoaded,
                                aTotal);
 }
 
 bool
 IsSupportedEventClass(JSObject* aEvent)
 {
   return Event::IsSupportedClass(aEvent);
@@ -1060,18 +1061,18 @@ EventWasCanceled(JSObject* aEvent)
 
 bool
 EventImmediatePropagationStopped(JSObject* aEvent)
 {
   return Event::ImmediatePropagationStopped(aEvent);
 }
 
 bool
-DispatchEventToTarget(JSContext* aCx, JSObject* aTarget, JSObject* aEvent,
-                      bool* aPreventDefaultCalled)
+DispatchEventToTarget(JSContext* aCx, JS::Handle<JSObject*> aTarget,
+                      JS::Handle<JSObject*> aEvent, bool* aPreventDefaultCalled)
 {
   static const char kFunctionName[] = "dispatchEvent";
   JSBool hasProperty;
   if (!JS_HasProperty(aCx, aTarget, kFunctionName, &hasProperty)) {
     return false;
   }
 
   JSBool preventDefaultCalled = false;
--- a/dom/workers/Events.h
+++ b/dom/workers/Events.h
@@ -13,26 +13,27 @@ class JSAutoStructuredCloneBuffer;
 BEGIN_WORKERS_NAMESPACE
 
 namespace events {
 
 bool
 InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal, bool aMainRuntime);
 
 JSObject*
-CreateGenericEvent(JSContext* aCx, JSString* aType, bool aBubbles,
+CreateGenericEvent(JSContext* aCx, JS::Handle<JSString*> aType, bool aBubbles,
                    bool aCancelable, bool aMainRuntime);
 
 JSObject*
 CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData,
                    nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects,
                    bool aMainRuntime);
 
 JSObject*
-CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename,
+CreateErrorEvent(JSContext* aCx, JS::Handle<JSString*> aMessage,
+                 JS::Handle<JSString*> aFilename,
                  uint32_t aLineNumber, bool aMainRuntime);
 
 JSObject*
 CreateProgressEvent(JSContext* aCx, JSString* aType, bool aLengthComputable,
                     double aLoaded, double aTotal);
 
 bool
 IsSupportedEventClass(JSObject* aEvent);
@@ -42,16 +43,16 @@ SetEventTarget(JSObject* aEvent, JSObjec
 
 bool
 EventWasCanceled(JSObject* aEvent);
 
 bool
 EventImmediatePropagationStopped(JSObject* aEvent);
 
 bool
-DispatchEventToTarget(JSContext* aCx, JSObject* aTarget, JSObject* aEvent,
-                      bool* aPreventDefaultCalled);
+DispatchEventToTarget(JSContext* aCx, JS::Handle<JSObject*> aTarget,
+                      JS::Handle<JSObject*> aEvent, bool* aPreventDefaultCalled);
 
 } // namespace events
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_events_h__
--- a/dom/workers/Location.cpp
+++ b/dom/workers/Location.cpp
@@ -40,19 +40,20 @@ public:
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj)
   {
     return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
                         sFunctions, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, JSString* aHref, JSString* aProtocol, JSString* aHost,
-         JSString* aHostname, JSString* aPort, JSString* aPathname,
-         JSString* aSearch, JSString* aHash)
+  Create(JSContext* aCx, JS::Handle<JSString*> aHref, JS::Handle<JSString*> aProtocol,
+         JS::Handle<JSString*> aHost, JS::Handle<JSString*> aHostname,
+         JS::Handle<JSString*> aPort, JS::Handle<JSString*> aPathname,
+         JS::Handle<JSString*> aSearch, JS::Handle<JSString*> aHash)
   {
     JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
     if (!obj) {
       return NULL;
     }
 
     jsval empty = JS_GetEmptyStringValue(aCx);
 
@@ -186,19 +187,21 @@ namespace location {
 
 bool
 InitClass(JSContext* aCx, JSObject* aGlobal)
 {
   return !!Location::InitClass(aCx, aGlobal);
 }
 
 JSObject*
-Create(JSContext* aCx, JSString* aHref, JSString* aProtocol, JSString* aHost,
-       JSString* aHostname, JSString* aPort, JSString* aPathname,
-       JSString* aSearch, JSString* aHash)
+Create(JSContext* aCx,
+       JS::Handle<JSString*> aHref, JS::Handle<JSString*> aProtocol,
+       JS::Handle<JSString*> aHost, JS::Handle<JSString*> aHostname,
+       JS::Handle<JSString*> aPort, JS::Handle<JSString*> aPathname,
+       JS::Handle<JSString*> aSearch, JS::Handle<JSString*> aHash)
 {
   return Location::Create(aCx, aHref, aProtocol, aHost, aHostname, aPort,
                           aPathname, aSearch, aHash);
 }
 
 } // namespace location
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/Location.h
+++ b/dom/workers/Location.h
@@ -13,17 +13,18 @@
 BEGIN_WORKERS_NAMESPACE
 
 namespace location {
 
 bool
 InitClass(JSContext* aCx, JSObject* aGlobal);
 
 JSObject*
-Create(JSContext* aCx, JSString* aHref, JSString* aProtocol, JSString* aHost,
-       JSString* aHostname, JSString* aPort, JSString* aPathname,
-       JSString* aSearch, JSString* aHash);
+Create(JSContext* aCx, JS::Handle<JSString*> aHref, JS::Handle<JSString*> aProtocol,
+       JS::Handle<JSString*> aHost, JS::Handle<JSString*> aHostname,
+       JS::Handle<JSString*> aPort, JS::Handle<JSString*> aPathname,
+       JS::Handle<JSString*> aSearch, JS::Handle<JSString*> aHash);
 
 } // namespace location
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_location_h__
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -209,18 +209,18 @@ struct WorkerStructuredCloneCallbacks
       return obj;
     }
 
     Error(aCx, 0);
     return nullptr;
   }
 
   static JSBool
-  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
-        void* aClosure)
+  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+        JS::Handle<JSObject*> aObj, void* aClosure)
   {
     NS_ASSERTION(aClosure, "Null pointer!");
 
     // We'll stash any nsISupports pointers that need to be AddRef'd here.
     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
 
     // See if this is a File object.
@@ -356,18 +356,18 @@ struct MainThreadWorkerStructuredCloneCa
       }
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   }
 
   static JSBool
-  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
-        void* aClosure)
+  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+        JS::Handle<JSObject*> aObj, void* aClosure)
   {
     AssertIsOnMainThread();
 
     NS_ASSERTION(aClosure, "Null pointer!");
 
     // We'll stash any nsISupports pointers that need to be AddRef'd here.
     nsTArray<nsCOMPtr<nsISupports> >* clonedObjects =
       static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure);
@@ -444,18 +444,18 @@ struct ChromeWorkerStructuredCloneCallba
   Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aTag,
        uint32_t aData, void* aClosure)
   {
     return WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData,
                                                 aClosure);
   }
 
   static JSBool
-  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
-        void* aClosure)
+  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+        JS::Handle<JSObject*> aObj, void* aClosure)
   {
     return WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure);
   }
 
   static void
   Error(JSContext* aCx, uint32_t aErrorId)
   {
     return WorkerStructuredCloneCallbacks::Error(aCx, aErrorId);
@@ -490,18 +490,18 @@ struct MainThreadChromeWorkerStructuredC
       return clone;
     }
 
     JS_ClearPendingException(aCx);
     return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nullptr);
   }
 
   static JSBool
-  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj,
-        void* aClosure)
+  Write(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+        JS::Handle<JSObject*> aObj, void* aClosure)
   {
     AssertIsOnMainThread();
 
     if (MainThreadWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
                                                         aClosure) ||
         ChromeWorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj,
                                                     aClosure) ||
         NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nullptr)) {
@@ -719,22 +719,22 @@ public:
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JS::Rooted<JSObject*> target(aCx, JS_GetGlobalForScopeChain(aCx));
     NS_ASSERTION(target, "This must never be null!");
 
     aWorkerPrivate->CloseHandlerStarted();
 
-    JSString* type = JS_InternString(aCx, "close");
+    JS::Rooted<JSString*> type(aCx, JS_InternString(aCx, "close"));
     if (!type) {
       return false;
     }
 
-    JSObject* event = CreateGenericEvent(aCx, type, false, false, false);
+    JS::Rooted<JSObject*> event(aCx, CreateGenericEvent(aCx, type, false, false, false));
     if (!event) {
       return false;
     }
 
     bool ignored;
     return DispatchEventToTarget(aCx, target, event, &ignored);
   }
 
@@ -808,18 +808,18 @@ public:
       NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx),
                    "Badness!");
       mainRuntime = false;
       target = JS_GetGlobalForScopeChain(aCx);
     }
 
     NS_ASSERTION(target, "This should never be null!");
 
-    JSObject* event =
-      CreateMessageEvent(aCx, buffer, mClonedObjects, mainRuntime);
+    JS::Rooted<JSObject*> event(aCx,
+      CreateMessageEvent(aCx, buffer, mClonedObjects, mainRuntime));
     if (!event) {
       return false;
     }
 
     bool dummy;
     return DispatchEventToTarget(aCx, target, event, &dummy);
   }
 
@@ -975,21 +975,22 @@ public:
   {
     // Notify before WorkerRunnable::PostRun, since that can kill aWorkerPrivate
     NotifyScriptExecutedIfNeeded();
     WorkerRunnable::PostRun(aCx, aWorkerPrivate, aRunResult);
   }
 
   static bool
   ReportError(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
-              bool aFireAtScope, JSObject* aTarget, const nsString& aMessage,
+              bool aFireAtScope, JSObject* target, const nsString& aMessage,
               const nsString& aFilename, const nsString& aLine,
               uint32_t aLineNumber, uint32_t aColumnNumber, uint32_t aFlags,
               uint32_t aErrorNumber, uint64_t aInnerWindowId)
   {
+    JS::Rooted<JSObject*> aTarget(aCx, target);
     if (aWorkerPrivate) {
       aWorkerPrivate->AssertIsOnWorkerThread();
     }
     else {
       AssertIsOnMainThread();
     }
 
     JS::Rooted<JSString*> message(aCx, JS_NewUCStringCopyN(aCx, aMessage.get(),
@@ -1004,18 +1005,18 @@ public:
       return false;
     }
 
     // We should not fire error events for warnings but instead make sure that
     // they show up in the error console.
     if (!JSREPORT_IS_WARNING(aFlags)) {
       // First fire an ErrorEvent at the worker.
       if (aTarget) {
-        JSObject* event =
-          CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate);
+        JS::Rooted<JSObject*> event(aCx,
+          CreateErrorEvent(aCx, message, filename, aLineNumber, !aWorkerPrivate));
         if (!event) {
           return false;
         }
 
         bool preventDefaultCalled;
         if (!DispatchEventToTarget(aCx, aTarget, event, &preventDefaultCalled)) {
           return false;
         }
@@ -1032,18 +1033,18 @@ public:
         NS_ASSERTION(aTarget, "This should never be null!");
 
         bool preventDefaultCalled;
         nsIScriptGlobalObject* sgo;
 
         if (aWorkerPrivate ||
             !(sgo = nsJSUtils::GetStaticScriptGlobal(aTarget))) {
           // Fire a normal ErrorEvent if we're running on a worker thread.
-          JSObject* event =
-            CreateErrorEvent(aCx, message, filename, aLineNumber, false);
+          JS::Rooted<JSObject*> event(aCx,
+            CreateErrorEvent(aCx, message, filename, aLineNumber, false));
           if (!event) {
             return false;
           }
 
           if (!DispatchEventToTarget(aCx, aTarget, event,
                                      &preventDefaultCalled)) {
             return false;
           }
@@ -1785,17 +1786,18 @@ private:
   }
 };
 
 NS_IMPL_THREADSAFE_ISUPPORTS1(WorkerPrivate::MemoryReporter,
                               nsIMemoryMultiReporter)
 
 template <class Derived>
 WorkerPrivateParent<Derived>::WorkerPrivateParent(
-                                     JSContext* aCx, JSObject* aObject,
+                                     JSContext* aCx,
+                                     JS::Handle<JSObject*> aObject,
                                      WorkerPrivate* aParent,
                                      JSContext* aParentJSContext,
                                      const nsAString& aScriptURL,
                                      bool aIsChromeWorker,
                                      const nsACString& aDomain,
                                      nsCOMPtr<nsPIDOMWindow>& aWindow,
                                      nsCOMPtr<nsIScriptContext>& aScriptContext,
                                      nsCOMPtr<nsIURI>& aBaseURI,
@@ -2375,17 +2377,17 @@ WorkerPrivateParent<Derived>::ParentJSCo
 
     NS_ASSERTION(mParentJSContext == mScriptContext->GetNativeContext(),
                  "Native context has changed!");
   }
 
   return mParentJSContext;
 }
 
-WorkerPrivate::WorkerPrivate(JSContext* aCx, JSObject* aObject,
+WorkerPrivate::WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject,
                              WorkerPrivate* aParent,
                              JSContext* aParentJSContext,
                              const nsAString& aScriptURL, bool aIsChromeWorker,
                              const nsACString& aDomain,
                              nsCOMPtr<nsPIDOMWindow>& aWindow,
                              nsCOMPtr<nsIScriptContext>& aParentScriptContext,
                              nsCOMPtr<nsIURI>& aBaseURI,
                              nsCOMPtr<nsIPrincipal>& aPrincipal,
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -283,17 +283,17 @@ private:
   bool mParentSuspended;
   bool mIsChromeWorker;
   bool mPrincipalIsSystem;
   bool mMainThreadObjectsForgotten;
   bool mEvalAllowed;
   bool mReportCSPViolations;
 
 protected:
-  WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
+  WorkerPrivateParent(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
                       JSContext* aParentJSContext, const nsAString& aScriptURL,
                       bool aIsChromeWorker, const nsACString& aDomain,
                       nsCOMPtr<nsPIDOMWindow>& aWindow,
                       nsCOMPtr<nsIScriptContext>& aScriptContext,
                       nsCOMPtr<nsIURI>& aBaseURI,
                       nsCOMPtr<nsIPrincipal>& aPrincipal,
                       nsCOMPtr<nsIChannel>& aChannel,
                       nsCOMPtr<nsIContentSecurityPolicy>& aCSP,
@@ -891,17 +891,17 @@ public:
   EndCTypesCallback()
   {
     // If a callback is ending then we need to do the exact same thing as
     // when a ctypes call begins.
     BeginCTypesCall();
   }
 
 private:
-  WorkerPrivate(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent,
+  WorkerPrivate(JSContext* aCx, JS::Handle<JSObject*> aObject, WorkerPrivate* aParent,
                 JSContext* aParentJSContext, const nsAString& aScriptURL,
                 bool aIsChromeWorker, const nsACString& aDomain,
                 nsCOMPtr<nsPIDOMWindow>& aWindow,
                 nsCOMPtr<nsIScriptContext>& aScriptContext,
                 nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal,
                 nsCOMPtr<nsIChannel>& aChannel,
                 nsCOMPtr<nsIContentSecurityPolicy>& aCSP, bool aEvalAllowed,
                 bool aReportCSPViolations, bool aXHRParamsAllowed);
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -223,18 +223,18 @@ private:
   {
     WorkerGlobalScope* scope =
       GetInstancePrivate(aCx, aObj, sProperties[SLOT_location].name);
     if (!scope) {
       return false;
     }
 
     if (JSVAL_IS_VOID(scope->mSlots[SLOT_location])) {
-      JSString* href, *protocol, *host, *hostname;
-      JSString* port, *pathname, *search, *hash;
+      JS::Rooted<JSString*> href(aCx), protocol(aCx), host(aCx), hostname(aCx);
+      JS::Rooted<JSString*> port(aCx), pathname(aCx), search(aCx), hash(aCx);
 
       WorkerPrivate::LocationInfo& info = scope->mWorker->GetLocationInfo();
 
 #define COPY_STRING(_jsstr, _cstr)                                             \
   if (info. _cstr .IsEmpty()) {                                                \
     _jsstr = NULL;                                                             \
   }                                                                            \
   else {                                                                       \
@@ -273,22 +273,22 @@ private:
   UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ASSERT(aArgc == 1);
     JS_ASSERT((JS_ARGV(aCx, aVp)[0]).isObject());
 
     JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
     JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
 
-    jsval scope = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope);
-    jsval listener = js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction);
+    JS::Rooted<JS::Value> scope(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedScope));
+    JS::Rooted<JS::Value> listener(aCx, js::GetFunctionNativeReserved(wrapper, SLOT_wrappedFunction));
 
     JS_ASSERT(scope.isObject());
 
-    JSObject* event = &JS_ARGV(aCx, aVp)[0].toObject();
+    JS::Rooted<JSObject*> event(aCx, &JS_ARGV(aCx, aVp)[0].toObject());
 
     jsval argv[3] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
     if (!JS_GetProperty(aCx, event, "message", &argv[0]) ||
         !JS_GetProperty(aCx, event, "filename", &argv[1]) ||
         !JS_GetProperty(aCx, event, "lineno", &argv[2])) {
       return false;
     }
 
@@ -563,23 +563,23 @@ private:
     if (!obj) {
       return false;
     }
 
     if (!GetInstancePrivate(aCx, obj, sFunctions[7].name)) {
       return false;
     }
 
-    jsval string;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &string)) {
+    JS::Rooted<JS::Value> string(aCx);
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", string.address())) {
       return false;
     }
 
-    jsval result;
-    if (!xpc::Base64Decode(aCx, string, &result)) {
+    JS::Rooted<JS::Value> result(aCx);
+    if (!xpc::Base64Decode(aCx, string, result.address())) {
       return false;
     }
 
     JS_SET_RVAL(aCx, aVp, result);
     return true;
   }
 
   static JSBool
@@ -589,23 +589,23 @@ private:
     if (!obj) {
       return false;
     }
 
     if (!GetInstancePrivate(aCx, obj, sFunctions[8].name)) {
       return false;
     }
 
-    jsval binary;
-    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", &binary)) {
+    JS::Rooted<JS::Value> binary(aCx);
+    if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "v", binary.address())) {
       return false;
     }
 
-    jsval result;
-    if (!xpc::Base64Encode(aCx, binary, &result)) {
+    JS::Rooted<JS::Value> result(aCx);
+    if (!xpc::Base64Encode(aCx, binary, result.address())) {
       return false;
     }
 
     JS_SET_RVAL(aCx, aVp, result);
     return true;
   }
 };
 
@@ -980,18 +980,18 @@ CreateDedicatedWorkerGlobalScope(JSConte
   }
 
   // Proto chain should be:
   //   global -> DedicatedWorkerGlobalScope
   //          -> WorkerGlobalScope
   //          -> EventTarget
   //          -> Object
 
-  JSObject* eventTargetProto =
-    EventTargetBinding_workers::GetProtoObject(aCx, global);
+  JS::Rooted<JSObject*> eventTargetProto(aCx,
+    EventTargetBinding_workers::GetProtoObject(aCx, global));
   if (!eventTargetProto) {
     return NULL;
   }
 
   JSObject* scopeProto =
     WorkerGlobalScope::InitClass(aCx, global, eventTargetProto);
   if (!scopeProto) {
     return NULL;
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -714,33 +714,33 @@ public:
 
     XMLHttpRequest* xhr = mProxy->mXMLHttpRequestPrivate;
     xhr->UpdateState(state);
 
     if (mUploadEvent && !xhr->GetUploadObjectNoCreate()) {
       return true;
     }
 
-    JSString* type = JS_NewUCStringCopyN(aCx, mType.get(), mType.Length());
+    JS::Rooted<JSString*> type(aCx, JS_NewUCStringCopyN(aCx, mType.get(), mType.Length()));
     if (!type) {
       return false;
     }
 
-    JSObject* event = mProgressEvent ?
+    JS::Rooted<JSObject*> event(aCx, mProgressEvent ?
                       events::CreateProgressEvent(aCx, type, mLengthComputable,
                                                   mLoaded, mTotal) :
                       events::CreateGenericEvent(aCx, type, false, false,
-                                                 false);
+                                                 false));
     if (!event) {
       return false;
     }
 
-    JSObject* target = mUploadEvent ?
+    JS::Rooted<JSObject*> target(aCx, mUploadEvent ?
                        xhr->GetUploadObjectNoCreate()->GetJSObject() :
-                       xhr->GetJSObject();
+                       xhr->GetJSObject());
     MOZ_ASSERT(target);
 
     bool dummy;
     if (!events::DispatchEventToTarget(aCx, target, event, &dummy)) {
       JS_ReportPendingException(aCx);
     }
 
     // After firing the event set mResponse to JSVAL_NULL for chunked response
@@ -1528,34 +1528,34 @@ XMLHttpRequest::MaybeDispatchPrematureAb
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(mProxy);
 
   mStateData.mReadyState = 4;
 
   if (mProxy->mSeenUploadLoadStart) {
     MOZ_ASSERT(mUpload);
 
-    JSObject* target = mUpload->GetJSObject();
+    JS::Rooted<JSObject*> target(GetJSContext(), mUpload->GetJSObject());
     MOZ_ASSERT(target);
 
     DispatchPrematureAbortEvent(target, STRING_abort, true, aRv);
     if (aRv.Failed()) {
       return;
     }
 
     DispatchPrematureAbortEvent(target, STRING_loadend, true, aRv);
     if (aRv.Failed()) {
       return;
     }
 
     mProxy->mSeenUploadLoadStart = false;
   }
 
   if (mProxy->mSeenLoadStart) {
-    JSObject* target = GetJSObject();
+    JS::Rooted<JSObject*> target(GetJSContext(), GetJSObject());
     MOZ_ASSERT(target);
 
     DispatchPrematureAbortEvent(target, STRING_readystatechange, false, aRv);
     if (aRv.Failed()) {
       return;
     }
 
     DispatchPrematureAbortEvent(target, STRING_abort, false, aRv);
@@ -1568,39 +1568,39 @@ XMLHttpRequest::MaybeDispatchPrematureAb
       return;
     }
 
     mProxy->mSeenLoadStart = false;
   }
 }
 
 void
-XMLHttpRequest::DispatchPrematureAbortEvent(JSObject* aTarget,
+XMLHttpRequest::DispatchPrematureAbortEvent(JS::Handle<JSObject*> aTarget,
                                             uint8_t aEventType,
                                             bool aUploadTarget,
                                             ErrorResult& aRv)
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aTarget);
   MOZ_ASSERT(aEventType <= STRING_COUNT);
 
   if (!mProxy) {
     aRv.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   JSContext* cx = GetJSContext();
 
-  JSString* type = JS_NewStringCopyZ(cx, sEventStrings[aEventType]);
+  JS::Rooted<JSString*> type(cx, JS_NewStringCopyZ(cx, sEventStrings[aEventType]));
   if (!type) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
-  JSObject* event;
+  JS::Rooted<JSObject*> event(cx);
   if (aEventType == STRING_readystatechange) {
     event = events::CreateGenericEvent(cx, type, false, false, false);
   }
   else if (aUploadTarget) {
     event = events::CreateProgressEvent(cx, type,
                                         mProxy->mLastUploadLengthComputable,
                                         mProxy->mLastUploadLoaded,
                                         mProxy->mLastUploadTotal);
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -284,17 +284,17 @@ private:
 
   void
   MaybePin(ErrorResult& aRv);
 
   void
   MaybeDispatchPrematureAbortEvents(ErrorResult& aRv);
 
   void
-  DispatchPrematureAbortEvent(JSObject* aTarget, uint8_t aEventType,
+  DispatchPrematureAbortEvent(JS::Handle<JSObject*> aTarget, uint8_t aEventType,
                               bool aUploadTarget, ErrorResult& aRv);
 
   bool
   SendInProgress() const
   {
     return mJSObjectRooted;
   }
 
--- a/js/src/config/makefiles/xpcshell.mk
+++ b/js/src/config/makefiles/xpcshell.mk
@@ -35,64 +35,69 @@ ifndef NO_XPCSHELL_MANIFEST_CHECK #{
 	  $(addprefix $(MOZILLA_DIR)/$(relativesrcdir)/,$(XPCSHELL_TESTS))
 endif #} NO_XPCSHELL_MANIFEST_CHECK 
 
 ###########################################################################
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --tests-root-dir=$(testxpcobjdir) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
 	  --xunit-suite-name=xpcshell \
 	  --test-plugin-path=$(DIST)/plugins \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 xpcshell-tests-remote: DM_TRANS?=adb
 xpcshell-tests-remote:
-	$(PYTHON) -u $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
+	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
+	  $(topsrcdir)/testing/xpcshell/remotexpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  $(EXTRA_TEST_ARGS) \
 	  --dm_trans=$(DM_TRANS) \
 	  --deviceIP=${TEST_DEVICE} \
 	  --objdir=$(DEPTH) \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 ###########################################################################
 # Execute a single test, specified in $(SOLO_FILE), but don't automatically
 # start the test. Instead, present the xpcshell prompt so the user can
 # attach a debugger and then start the test.
 check-interactive:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --profile-name=$(MOZ_APP_NAME) \
 	  --test-plugin-path=$(DIST)/plugins \
 	  --interactive \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 # Execute a single test, specified in $(SOLO_FILE)
 check-one:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
       -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
 	  --profile-name=$(MOZ_APP_NAME) \
@@ -100,16 +105,17 @@ check-one:
 	  --verbose \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 check-one-remote: DM_TRANS?=adb
 check-one-remote:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
 	  -I$(topsrcdir)/build/mobile \
 	  -I$(topsrcdir)/testing/mozbase/mozdevice/mozdevice \
 	  $(testxpcsrcdir)/remotexpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --test-path=$(SOLO_FILE) \
 	  --testing-modules-dir=$(DEPTH)/_tests/modules \
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -256,17 +256,17 @@ ConvertFrames(JSContext *cx, IonActivati
         return BAILOUT_RETURN_FATAL_ERROR;
     activation->setBailout(br);
 
     StackFrame *fp;
     if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) {
         // Avoid creating duplicate interpreter frames. This is necessary to
         // avoid blowing out the interpreter stack, and must be used in
         // conjunction with inline-OSR from within bailouts (since each Ion
-        // activation must be tied to a unique JSStackFrame for StackIter to
+        // activation must be tied to a unique StackFrame for ScriptFrameIter to
         // work).
         //
         // Note: If the entry frame is a placeholder (a stub frame pushed for
         // JM -> Ion calls), then we cannot re-use it as it does not have
         // enough slots.
         JS_ASSERT(cx->fp() == activation->entryfp());
         fp = cx->fp();
         cx->regs().sp = fp->base();
@@ -662,17 +662,17 @@ ion::ThunkToInterpreter(Value *vp)
                 PCToLineNumber(cx->fp()->script(), cx->regs().pc));
 
         // We want to OSR again. We need to avoid the problem where frequent
         // bailouts cause recursive nestings of Interpret and EnterIon. The
         // interpreter therefore shortcuts out, and now we're responsible for
         // completing the OSR inline.
         //
         // Note that we set runningInIon so that if we re-enter C++ from within
-        // the inlined OSR, StackIter will know to traverse these frames.
+        // the inlined OSR, ScriptFrameIter will know to traverse these frames.
         StackFrame *fp = cx->fp();
 
         fp->setRunningInIon();
         vp->setPrivate(fp);
         js_delete(br);
         return Interpret_OSR;
     }
 
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -2075,17 +2075,17 @@ ion::SideCannon(JSContext *cx, StackFram
                  script);
     }
 #endif
 
     return status;
 }
 
 IonExecStatus
-ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgsList &args)
+ion::FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args)
 {
     JS_CHECK_RECURSION(cx, return IonExec_Error);
 
     IonScript *ion = fun->nonLazyScript()->ionScript();
     IonCode *code = ion->method();
     void *jitcode = code->raw();
 
     JS_ASSERT(ion::IsEnabled(cx));
@@ -2117,20 +2117,18 @@ ion::FastInvoke(JSContext *cx, HandleFun
 
     EnterIonCode enter = cx->compartment->ionCompartment()->enterJIT();
     void *calleeToken = CalleeToToken(fun);
 
     RootedValue result(cx, Int32Value(args.length()));
     JS_ASSERT(args.length() >= fun->nargs);
 
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
-    args.setActive();
     enter(jitcode, args.length() + 1, args.array() - 1, fp, calleeToken,
           /* scopeChain = */ NULL, 0, result.address());
-    args.setInactive();
 
     if (clearCallingIntoIon)
         fp->clearCallingIntoIon();
 
     JS_ASSERT(fp == cx->fp());
     JS_ASSERT(!cx->runtime->hasIonReturnOverride());
 
     args.rval().set(result);
--- a/js/src/ion/Ion.h
+++ b/js/src/ion/Ion.h
@@ -308,17 +308,17 @@ IsErrorStatus(IonExecStatus status)
 {
     return status == IonExec_Error || status == IonExec_Aborted;
 }
 
 IonExecStatus Cannon(JSContext *cx, StackFrame *fp);
 IonExecStatus SideCannon(JSContext *cx, StackFrame *fp, jsbytecode *pc);
 
 // Used to enter Ion from C++ natives like Array.map. Called from FastInvokeGuard.
-IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgsList &args);
+IonExecStatus FastInvoke(JSContext *cx, HandleFunction fun, CallArgs &args);
 
 // Walk the stack and invalidate active Ion frames for the invalid scripts.
 void Invalidate(types::TypeCompartment &types, FreeOp *fop,
                 const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 void Invalidate(JSContext *cx, const Vector<types::RecompileInfo> &invalid, bool resetUses = true);
 bool Invalidate(JSContext *cx, JSScript *script, ExecutionMode mode, bool resetUses = true);
 bool Invalidate(JSContext *cx, JSScript *script, bool resetUses = true);
 
--- a/js/src/ion/IonCompartment.h
+++ b/js/src/ion/IonCompartment.h
@@ -319,17 +319,17 @@ class IonActivation
     JSCompartment *compartment_;
     IonActivation *prev_;
     StackFrame *entryfp_;
     BailoutClosure *bailout_;
     uint8_t *prevIonTop_;
     JSContext *prevIonJSContext_;
 
     // When creating an activation without a StackFrame, this field is used
-    // to communicate the calling pc for StackIter.
+    // to communicate the calling pc for ScriptFrameIter.
     jsbytecode *prevpc_;
 
   public:
     IonActivation(JSContext *cx, StackFrame *fp);
     ~IonActivation();
 
     StackFrame *entryfp() const {
         return entryfp_;
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -83,31 +83,27 @@ CalleeToken
 IonFrameIterator::calleeToken() const
 {
     return ((IonJSFrameLayout *) current_)->calleeToken();
 }
 
 JSFunction *
 IonFrameIterator::callee() const
 {
-    if (isScripted()) {
-        JS_ASSERT(isFunctionFrame() || isParallelFunctionFrame());
-        if (isFunctionFrame())
-            return CalleeTokenToFunction(calleeToken());
-        return CalleeTokenToParallelFunction(calleeToken());
-    }
-
-    JS_ASSERT(isNative());
-    return exitFrame()->nativeExit()->vp()[0].toObject().toFunction();
+    JS_ASSERT(isScripted());
+    JS_ASSERT(isFunctionFrame() || isParallelFunctionFrame());
+    if (isFunctionFrame())
+        return CalleeTokenToFunction(calleeToken());
+    return CalleeTokenToParallelFunction(calleeToken());
 }
 
 JSFunction *
 IonFrameIterator::maybeCallee() const
 {
-    if ((isScripted() && (isFunctionFrame() || isParallelFunctionFrame())) || isNative())
+    if (isScripted() && (isFunctionFrame() || isParallelFunctionFrame()))
         return callee();
     return NULL;
 }
 
 bool
 IonFrameIterator::isNative() const
 {
     if (type_ != IonFrame_Exit || isFakeExitFrame())
@@ -518,18 +514,18 @@ HandleException(ResumeFromException *rfe
 
         IonJSFrameLayout *current = iter.isScripted() ? iter.jsFrame() : NULL;
 
         ++iter;
 
         if (current) {
             // Unwind the frame by updating ionTop. This is necessary so that
             // (1) debugger exception unwind and leave frame hooks don't see this
-            // frame when they use StackIter, and (2) StackIter does not crash
-            // when accessing an IonScript that's destroyed by the
+            // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does
+            // not crash when accessing an IonScript that's destroyed by the
             // ionScript->decref call.
             EnsureExitFrame(current);
             cx->mainThread().ionTop = (uint8_t *)current;
         }
     }
 
     rfe->stackPointer = iter.fp();
 }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1130,17 +1130,17 @@ typedef JSObject *(*ReadStructuredCloneO
  * the JS_WriteUint32Pair API to write an object header, passing a value
  * greater than JS_SCTAG_USER to the tag parameter. Then it can use the
  * JS_Write* APIs to write any other relevant parts of the value v to the
  * writer w. closure is any value passed to the JS_WriteStructuredCLone function.
  *
  * Return true on success, false on error/exception.
  */
 typedef JSBool (*WriteStructuredCloneOp)(JSContext *cx, JSStructuredCloneWriter *w,
-                                         JSObject *obj, void *closure);
+                                         JS::Handle<JSObject*> obj, void *closure);
 
 /*
  * This is called when JS_WriteStructuredClone is given an invalid transferable.
  * To follow HTML5, the application must throw a DATA_CLONE_ERR DOMException
  * with error set to one of the JS_SCERR_* values.
  */
 typedef void (*StructuredCloneErrorOp)(JSContext *cx, uint32_t errorid);
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1365,48 +1365,48 @@ JSAbstractFramePtr::evaluateUCInStackFra
 JSBrokenFrameIterator::JSBrokenFrameIterator(JSContext *cx)
 {
     NonBuiltinScriptFrameIter iter(cx);
     data_ = iter.copyData();
 }
 
 JSBrokenFrameIterator::~JSBrokenFrameIterator()
 {
-    js_free((StackIter::Data *)data_);
+    js_free((ScriptFrameIter::Data *)data_);
 }
 
 bool
 JSBrokenFrameIterator::done() const
 {
-    NonBuiltinScriptFrameIter iter(*(StackIter::Data *)data_);
+    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
     return iter.done();
 }
 
 JSBrokenFrameIterator &
 JSBrokenFrameIterator::operator++()
 {
-    StackIter::Data *data = (StackIter::Data *)data_;
+    ScriptFrameIter::Data *data = (ScriptFrameIter::Data *)data_;
     NonBuiltinScriptFrameIter iter(*data);
     ++iter;
     *data = iter.data_;
     return *this;
 }
 
 JSAbstractFramePtr
 JSBrokenFrameIterator::abstractFramePtr() const
 {
-    NonBuiltinScriptFrameIter iter(*(StackIter::Data *)data_);
+    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
     return Jsvalify(iter.abstractFramePtr());
 }
 
 jsbytecode *
 JSBrokenFrameIterator::pc() const
 {
-    NonBuiltinScriptFrameIter iter(*(StackIter::Data *)data_);
+    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
     return iter.pc();
 }
 
 bool
 JSBrokenFrameIterator::isConstructing() const
 {
-    NonBuiltinScriptFrameIter iter(*(StackIter::Data *)data_);
+    NonBuiltinScriptFrameIter iter(*(ScriptFrameIter::Data *)data_);
     return iter.isConstructing();
 }
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -130,17 +130,17 @@ fun_getProperty(JSContext *cx, HandleObj
 #endif
 
         vp.setObject(*argsobj);
         return true;
     }
 
 #ifdef JS_METHODJIT
     StackFrame *fp = NULL;
-    if (iter.isScript() && !iter.isIon())
+    if (!iter.isIon())
         fp = iter.interpFrame();
 
     if (JSID_IS_ATOM(id, cx->names().caller) && fp && fp->prev()) {
         /*
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
@@ -914,19 +914,19 @@ js_fun_apply(JSContext *cx, unsigned arg
          * function and read actuals out of the frame.
          *
          * N.B. Changes here need to be propagated to stubs::SplatApplyArgs.
          */
         /* Steps 4-6. */
         StackFrame *fp = cx->fp();
 
 #ifdef JS_ION
-        // We do not want to use StackIter to abstract here because this is
-        // supposed to be a fast path as opposed to StackIter which is doing
-        // complex logic to settle on the next frame twice.
+        // We do not want to use ScriptFrameIter to abstract here because this
+        // is supposed to be a fast path as opposed to ScriptFrameIter which is
+        // doing complex logic to settle on the next frame twice.
         if (fp->beginsIonActivation()) {
             ion::IonActivationIterator activations(cx);
             ion::IonFrameIterator frame(activations);
             if (frame.isNative()) {
                 // Stop on the next Ion JS Frame.
                 ++frame;
                 if (frame.isOptimizedJS()) {
                     ion::InlineFrameIterator iter(cx, &frame);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -303,25 +303,23 @@ js::RunScript(JSContext *cx, StackFrame 
 #endif
 
     JS_CHECK_RECURSION(cx, return false);
 
     // Check to see if useNewType flag should be set for this frame.
     if (fp->isFunctionFrame() && fp->isConstructing() && !fp->isGeneratorFrame() &&
         cx->typeInferenceEnabled())
     {
-        StackIter iter(cx);
+        ScriptFrameIter iter(cx);
         if (!iter.done()) {
             ++iter;
-            if (iter.isScript()) {
-                JSScript *script = iter.script();
-                jsbytecode *pc = iter.pc();
-                if (UseNewType(cx, script, pc))
-                    fp->setUseNewType();
-            }
+            JSScript *script = iter.script();
+            jsbytecode *pc = iter.pc();
+            if (UseNewType(cx, script, pc))
+                fp->setUseNewType();
         }
     }
 
 #ifdef DEBUG
     struct CheckStackBalance {
         JSContext *cx;
         StackFrame *fp;
         CheckStackBalance(JSContext *cx)
@@ -387,17 +385,17 @@ js::RunScript(JSContext *cx, StackFrame 
 
 /*
  * Find a function reference and its 'this' value implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
 bool
-js::InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct)
+js::Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct)
 {
     JS_ASSERT(args.length() <= StackSpace::ARGS_LENGTH_MAX);
     JS_ASSERT(!cx->compartment->activeAnalysis);
 
     /* We should never enter a new script while cx->iterValue is live. */
     JS_ASSERT(cx->iterValue.isMagic(JS_NO_ITER_VALUE));
 
     /* MaybeConstruct is a subset of InitialFrameFlags */
@@ -475,17 +473,17 @@ js::Invoke(JSContext *cx, const Value &t
     if (!Invoke(cx, args))
         return false;
 
     *rval = args.rval();
     return true;
 }
 
 bool
-js::InvokeConstructorKernel(JSContext *cx, CallArgs args)
+js::InvokeConstructor(JSContext *cx, CallArgs args)
 {
     JS_ASSERT(!FunctionClass.construct);
 
     args.setThis(MagicValue(JS_IS_CONSTRUCTING));
 
     if (!args.calleev().isObject())
         return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, CONSTRUCT);
 
@@ -496,17 +494,17 @@ js::InvokeConstructorKernel(JSContext *c
         if (fun->isNativeConstructor()) {
             bool ok = CallJSNativeConstructor(cx, fun->native(), args);
             return ok;
         }
 
         if (!fun->isInterpretedConstructor())
             return ReportIsNotFunction(cx, args.calleev().get(), args.length() + 1, CONSTRUCT);
 
-        if (!InvokeKernel(cx, args, CONSTRUCT))
+        if (!Invoke(cx, args, CONSTRUCT))
             return false;
 
         JS_ASSERT(args.rval().isObject());
         return true;
     }
 
     Class *clasp = callee.getClass();
     if (!clasp->construct)
@@ -2337,17 +2335,17 @@ END_CASE(JSOP_ENUMELEM)
 
 BEGIN_CASE(JSOP_EVAL)
 {
     CallArgs args = CallArgsFromSp(GET_ARGC(regs.pc), regs.sp);
     if (IsBuiltinEvalForScope(regs.fp()->scopeChain(), args.calleev())) {
         if (!DirectEval(cx, args))
             goto error;
     } else {
-        if (!InvokeKernel(cx, args))
+        if (!Invoke(cx, args))
             goto error;
     }
     regs.sp = args.spAfterCall();
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
 }
 END_CASE(JSOP_EVAL)
 
 BEGIN_CASE(JSOP_FUNAPPLY)
@@ -2384,20 +2382,20 @@ BEGIN_CASE(JSOP_FUNCALL)
                 goto error;
             args.setCallee(ObjectValue(*fun));
         }
     }
 
     /* Don't bother trying to fast-path calls to scripted non-constructors. */
     if (!isFunction || !fun->isInterpretedConstructor()) {
         if (construct) {
-            if (!InvokeConstructorKernel(cx, args))
+            if (!InvokeConstructor(cx, args))
                 goto error;
         } else {
-            if (!InvokeKernel(cx, args))
+            if (!Invoke(cx, args))
                 goto error;
         }
         Value *newsp = args.spAfterCall();
         TypeScript::Monitor(cx, script, regs.pc, newsp[-1]);
         regs.sp = newsp;
         len = JSOP_CALL_LENGTH;
         DO_NEXT_OP(len);
     }
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -111,35 +111,21 @@ ReportIsNotFunction(JSContext *cx, const
                     MaybeConstruct construct = NO_CONSTRUCT);
 
 /* See ReportIsNotFunction comment for the meaning of numToSkip. */
 extern JSObject *
 ValueToCallable(JSContext *cx, const Value &vp, int numToSkip = -1,
                 MaybeConstruct construct = NO_CONSTRUCT);
 
 /*
- * InvokeKernel assumes that the given args have been pushed on the top of the
- * VM stack. Additionally, if 'args' is contained in a CallArgsList, that they
- * have already been marked 'active'.
+ * Invoke assumes that the given args have been pushed on the top of the
+ * VM stack.
  */
 extern bool
-InvokeKernel(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT);
-
-/*
- * Invoke assumes that 'args' has been pushed (via ContextStack::pushInvokeArgs)
- * and is currently at the top of the VM stack.
- */
-inline bool
-Invoke(JSContext *cx, InvokeArgsGuard &args, MaybeConstruct construct = NO_CONSTRUCT)
-{
-    args.setActive();
-    bool ok = InvokeKernel(cx, args, construct);
-    args.setInactive();
-    return ok;
-}
+Invoke(JSContext *cx, CallArgs args, MaybeConstruct construct = NO_CONSTRUCT);
 
 /*
  * This Invoke overload places the least requirements on the caller: it may be
  * called at any time and it takes care of copying the given callee, this, and
  * arguments onto the stack.
  */
 extern bool
 Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
@@ -149,31 +135,21 @@ Invoke(JSContext *cx, const Value &thisv
  * This helper takes care of the infinite-recursion check necessary for
  * getter/setter calls.
  */
 extern bool
 InvokeGetterOrSetter(JSContext *cx, JSObject *obj, const Value &fval, unsigned argc, Value *argv,
                      Value *rval);
 
 /*
- * InvokeConstructor* implement a function call from a constructor context
+ * InvokeConstructor implement a function call from a constructor context
  * (e.g. 'new') handling the the creation of the new 'this' object.
  */
 extern bool
-InvokeConstructorKernel(JSContext *cx, CallArgs args);
-
-/* See the InvokeArgsGuard overload of Invoke. */
-inline bool
-InvokeConstructor(JSContext *cx, InvokeArgsGuard &args)
-{
-    args.setActive();
-    bool ok = InvokeConstructorKernel(cx, ImplicitCast<CallArgs>(args));
-    args.setInactive();
-    return ok;
-}
+InvokeConstructor(JSContext *cx, CallArgs args);
 
 /* See the fval overload of Invoke. */
 extern bool
 InvokeConstructor(JSContext *cx, const Value &fval, unsigned argc, Value *argv, Value *rval);
 
 /*
  * Executes a script with the given scopeChain/this. The 'type' indicates
  * whether this is eval code or global code. To support debugging, the
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -5199,17 +5199,17 @@ MaybeDumpValue(const char *name, const V
         fputc('\n', stderr);
     }
 }
 
 JS_FRIEND_API(void)
 js_DumpStackFrame(JSContext *cx, StackFrame *start)
 {
     /* This should only called during live debugging. */
-    ScriptFrameIter i(cx, StackIter::GO_THROUGH_SAVED);
+    ScriptFrameIter i(cx, ScriptFrameIter::GO_THROUGH_SAVED);
     if (!start) {
         if (i.done()) {
             fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
             return;
         }
     } else {
         while (!i.done() && !i.isIon() && i.interpFrame() != start)
             ++i;
@@ -5273,27 +5273,23 @@ js_DumpStackFrame(JSContext *cx, StackFr
 #endif /* DEBUG */
 
 JS_FRIEND_API(void)
 js_DumpBacktrace(JSContext *cx)
 {
     Sprinter sprinter(cx);
     sprinter.init();
     size_t depth = 0;
-    for (StackIter i(cx); !i.done(); ++i, ++depth) {
-        if (i.isScript()) {
-            const char *filename = JS_GetScriptFilename(cx, i.script());
-            unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
-            JSScript *script = i.script();
-            sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
-                            depth, (i.isIon() ? 0 : i.interpFrame()), filename, line,
-                            script, i.pc() - script->code);
-        } else {
-            sprinter.printf("#%d ???\n", depth);
-        }
+    for (ScriptFrameIter i(cx); !i.done(); ++i, ++depth) {
+        const char *filename = JS_GetScriptFilename(cx, i.script());
+        unsigned line = JS_PCToLineNumber(cx, i.script(), i.pc());
+        JSScript *script = i.script();
+        sprinter.printf("#%d %14p   %s:%d (%p @ %d)\n",
+                        depth, (i.isIon() ? 0 : i.interpFrame()), filename, line,
+                        script, i.pc() - script->code);
     }
     fprintf(stdout, "%s", sprinter.string());
 }
 void
 JSObject::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf, JS::ObjectsExtraSizes *sizes)
 {
     if (hasDynamicSlots())
         sizes->slots = mallocSizeOf(slots);
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -1622,29 +1622,25 @@ DecompileArgumentFromStack(JSContext *cx
     /* See note in DecompileExpressionFromStack. */
     return true;
 #endif
 
     /*
      * Settle on the nearest script frame, which should be the builtin that
      * called the intrinsic.
      */
-    StackIter frameIter(cx);
-    while (!frameIter.done() && !frameIter.isScript())
-        ++frameIter;
+    ScriptFrameIter frameIter(cx);
     JS_ASSERT(!frameIter.done());
 
     /*
      * Get the second-to-top frame, the caller of the builtin that called the
      * intrinsic.
      */
     ++frameIter;
-
-    /* If this frame isn't a script, we can't decompile. */
-    if (frameIter.done() || !frameIter.isScript())
+    if (frameIter.done())
         return true;
 
     RootedScript script(cx, frameIter.script());
     AutoCompartment ac(cx, &script->global());
     jsbytecode *current = frameIter.pc();
     RootedFunction fun(cx, frameIter.isFunctionFrame()
                        ? frameIter.callee()
                        : NULL);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2815,17 +2815,17 @@ JSScript::argumentsOptimizationFailed(JS
     for (AllFramesIter i(cx->runtime); !i.done(); ++i) {
         /*
          * We cannot reliably create an arguments object for Ion activations of
          * this script.  To maintain the invariant that "script->needsArgsObj
          * implies fp->hasArgsObj", the Ion bail mechanism will create an
          * arguments object right after restoring the StackFrame and before
          * entering the interpreter (in ion::ThunkToInterpreter).  This delay is
          * safe since the engine avoids any observation of a StackFrame when it
-         * beginsIonActivation (see StackIter::interpFrame comment).
+         * beginsIonActivation (see ScriptFrameIter::interpFrame comment).
          */
         if (i.isIonOptimizedJS())
             continue;
         AbstractFramePtr frame = i.abstractFramePtr();
         if (frame.isFunctionFrame() && frame.script() == script) {
             ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame);
             if (!argsobj) {
                 /*
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -156,31 +156,31 @@ stubs::SlowCall(VMFrame &f, uint32_t arg
     if (*f.regs.pc == JSOP_FUNAPPLY && !GuardFunApplyArgumentsOptimization(f.cx))
         THROW();
 
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     RootedScript fscript(f.cx, f.script());
 
     if (!MaybeCloneAndPatchCallee(f.cx, args, fscript, f.pc()))
         THROW();
-    if (!InvokeKernel(f.cx, args))
+    if (!Invoke(f.cx, args))
         THROW();
 
     types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
 }
 
 void JS_FASTCALL
 stubs::SlowNew(VMFrame &f, uint32_t argc)
 {
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     RootedScript fscript(f.cx, f.script());
 
     if (!MaybeCloneAndPatchCallee(f.cx, args, fscript, f.pc()))
         THROW();
-    if (!InvokeConstructorKernel(f.cx, args))
+    if (!InvokeConstructor(f.cx, args))
         THROW();
 
     types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
 }
 
 static inline bool
 CheckStackQuota(VMFrame &f)
 {
@@ -427,17 +427,17 @@ stubs::UncachedNewHelper(VMFrame &f, uin
     if (!ucr.setFunction(cx, args, fscript, f.pc()))
         THROW();
 
     /* Try to do a fast inline call before the general Invoke path. */
     if (ucr.fun && ucr.fun->isInterpretedConstructor()) {
         if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr.codeAddr, &ucr.unjittable, argc))
             THROW();
     } else {
-        if (!InvokeConstructorKernel(cx, args))
+        if (!InvokeConstructor(cx, args))
             THROW();
         types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
     }
 }
 
 void * JS_FASTCALL
 stubs::UncachedCall(VMFrame &f, uint32_t argc)
 {
@@ -455,17 +455,17 @@ stubs::UncachedLoweredCall(VMFrame &f, u
 }
 
 void JS_FASTCALL
 stubs::Eval(VMFrame &f, uint32_t argc)
 {
     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
 
     if (!IsBuiltinEvalForScope(f.fp()->scopeChain(), args.calleev())) {
-        if (!InvokeKernel(f.cx, args))
+        if (!Invoke(f.cx, args))
             THROW();
 
         RootedScript fscript(f.cx, f.script());
         types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
         return;
     }
 
     JS_ASSERT(f.fp() == f.cx->fp());
@@ -500,17 +500,17 @@ stubs::UncachedCallHelper(VMFrame &f, ui
             if (!CallJSNative(cx, ucr.fun->native(), args))
                 THROW();
             RootedScript fscript(cx, f.script());
             types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
             return;
         }
     }
 
-    if (!InvokeKernel(f.cx, args))
+    if (!Invoke(f.cx, args))
         THROW();
 
     types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval());
     return;
 }
 
 static void
 RemoveOrphanedNative(JSContext *cx, StackFrame *fp)
@@ -1021,17 +1021,17 @@ js_InternalInterpret(void *returnData, v
         }
         break;
 
       case REJOIN_CALL_SPLAT: {
         /* Leave analysis early and do the Invoke which SplatApplyArgs prepared. */
         nextDepth = analysis->getCode(nextpc).stackDepth;
         enter.destroy();
         f.regs.sp = nextsp + 2 + f.u.call.dynamicArgc;
-        if (!InvokeKernel(cx, CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp)))
+        if (!Invoke(cx, CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp)))
             return js_InternalThrow(f);
         nextsp[-1] = nextsp[0];
         f.regs.pc = nextpc;
         break;
       }
 
       case REJOIN_GETTER:
         /*
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -136,21 +136,21 @@ struct CopyIonJSFrameArgs
      * call object is the canonical location for formals.
      */
     void maybeForwardToCallObject(JSObject *obj, ArgumentsData *data) {
         ArgumentsObject::MaybeForwardToCallObject(frame_, callObj_, obj, data);
     }
 };
 #endif
 
-struct CopyStackIterArgs
+struct CopyScriptFrameIterArgs
 {
-    StackIter &iter_;
+    ScriptFrameIter &iter_;
 
-    CopyStackIterArgs(StackIter &iter)
+    CopyScriptFrameIterArgs(ScriptFrameIter &iter)
       : iter_(iter)
     { }
 
     void copyArgs(JSContext *cx, HeapValue *dstBase, unsigned totalArgs) const {
         if (!iter_.isIon()) {
             CopyStackFrameArguments(iter_.abstractFramePtr(), dstBase, totalArgs);
             return;
         }
@@ -255,21 +255,21 @@ ArgumentsObject::createExpected(JSContex
     if (!argsobj)
         return NULL;
 
     frame.initArgsObj(*argsobj);
     return argsobj;
 }
 
 ArgumentsObject *
-ArgumentsObject::createUnexpected(JSContext *cx, StackIter &iter)
+ArgumentsObject::createUnexpected(JSContext *cx, ScriptFrameIter &iter)
 {
     RootedScript script(cx, iter.script());
     RootedFunction callee(cx, iter.callee());
-    CopyStackIterArgs copy(iter);
+    CopyScriptFrameIterArgs copy(iter);
     return create(cx, script, callee, iter.numActualArgs(), copy);
 }
 
 ArgumentsObject *
 ArgumentsObject::createUnexpected(JSContext *cx, AbstractFramePtr frame)
 {
     RootedScript script(cx, frame.script());
     RootedFunction callee(cx, frame.callee());
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -118,17 +118,17 @@ class ArgumentsObject : public JSObject
     static ArgumentsObject *createExpected(JSContext *cx, AbstractFramePtr frame);
 
     /*
      * Purposefully disconnect the returned arguments object from the frame
      * by always creating a new copy that does not alias formal parameters.
      * This allows function-local analysis to determine that formals are
      * not aliased and generally simplifies arguments objects.
      */
-    static ArgumentsObject *createUnexpected(JSContext *cx, StackIter &iter);
+    static ArgumentsObject *createUnexpected(JSContext *cx, ScriptFrameIter &iter);
     static ArgumentsObject *createUnexpected(JSContext *cx, AbstractFramePtr frame);
 #if defined(JS_ION)
     static ArgumentsObject *createForIon(JSContext *cx, ion::IonJSFrameLayout *frame,
                                          HandleObject scopeChain);
 #endif
 
     /*
      * Return the initial length of the arguments.  This may differ from the
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -425,17 +425,17 @@ Debugger::getScriptFrame(JSContext *cx, 
     FrameMap::AddPtr p = frames.lookupForAdd(iter.abstractFramePtr());
     if (!p) {
         /* Create and populate the Debugger.Frame object. */
         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_FRAME_PROTO).toObject();
         JSObject *frameobj =
             NewObjectWithGivenProto(cx, &DebuggerFrame_class, proto, NULL);
         if (!frameobj)
             return false;
-        StackIter::Data *data = iter.copyData();
+        ScriptFrameIter::Data *data = iter.copyData();
         if (!data)
             return false;
         frameobj->setPrivate(data);
         frameobj->setReservedSlot(JSSLOT_DEBUGFRAME_OWNER, ObjectValue(*object));
 
         if (!frames.add(p, iter.abstractFramePtr(), frameobj)) {
             js_ReportOutOfMemory(cx);
             return false;
@@ -508,17 +508,17 @@ Debugger::slowPathOnEnterFrame(JSContext
                 return status;
         }
     }
 
     return JSTRAP_CONTINUE;
 }
 
 static void
-DebuggerFrame_freeStackIterData(FreeOp *fop, JSObject *obj);
+DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj);
 
 /*
  * Handle leaving a frame with debuggers watching. |frameOk| indicates whether
  * the frame is exiting normally or abruptly. Set |cx|'s exception and/or
  * |cx->fp()|'s return value, and return a new success value.
  */
 bool
 Debugger::slowPathOnLeaveFrame(JSContext *cx, AbstractFramePtr frame, bool frameOk)
@@ -584,17 +584,17 @@ Debugger::slowPathOnLeaveFrame(JSContext
      * debugger's onPop handler could have caused another debugger to create its
      * own Debugger.Frame instance.
      */
     for (FrameRange r(frame, global); !r.empty(); r.popFront()) {
         RootedObject frameobj(cx, r.frontFrame());
         Debugger *dbg = r.frontDebugger();
         JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
 
-        DebuggerFrame_freeStackIterData(cx->runtime->defaultFreeOp(), frameobj);
+        DebuggerFrame_freeScriptFrameIterData(cx->runtime->defaultFreeOp(), frameobj);
 
         /* If this frame had an onStep handler, adjust the script's count. */
         if (!frameobj->getReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER).isUndefined() &&
             !frame.script()->changeStepModeCount(cx, -1))
         {
             status = JSTRAP_ERROR;
             /* Don't exit the loop; we must mark all frames as dead. */
         }
@@ -2029,17 +2029,17 @@ Debugger::getNewestFrame(JSContext *cx, 
     for (AllFramesIter i(cx->runtime); !i.done(); ++i) {
         /*
          * Debug-mode currently disables Ion compilation in the compartment of
          * the debuggee.
          */
         if (i.isIonOptimizedJS())
             continue;
         if (dbg->observesFrame(i.abstractFramePtr())) {
-            ScriptFrameIter iter(i.seg()->cx(), StackIter::GO_THROUGH_SAVED);
+            ScriptFrameIter iter(i.seg()->cx(), ScriptFrameIter::GO_THROUGH_SAVED);
             while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != i.abstractFramePtr())
                 ++iter;
             return dbg->getScriptFrame(cx, iter, args.rval());
         }
     }
     args.rval().setNull();
     return true;
 }
@@ -2221,17 +2221,17 @@ Debugger::removeDebuggeeGlobal(FreeOp *f
      * have live Frame objects. So we take the easy way out and kill them here.
      * This is a bug, since it's observable and contrary to the spec. One
      * possible fix would be to put such objects into a compartment-wide bag
      * which slowPathOnLeaveFrame would have to examine.
      */
     for (FrameMap::Enum e(frames); !e.empty(); e.popFront()) {
         AbstractFramePtr frame = e.front().key;
         if (&frame.script()->global() == global) {
-            DebuggerFrame_freeStackIterData(fop, e.front().value);
+            DebuggerFrame_freeScriptFrameIterData(fop, e.front().value);
             e.removeFront();
         }
     }
 
     GlobalObject::DebuggerVector *v = global->getDebuggers();
     Debugger **p;
     for (p = v->begin(); p != v->end(); p++) {
         if (*p == this)
@@ -3328,19 +3328,19 @@ Debugger::handleBaselineOsr(JSContext *c
     ScriptFrameIter iter(cx);
     JS_ASSERT(iter.abstractFramePtr() == to);
 
     for (FrameRange r(from); !r.empty(); r.popFront()) {
         RootedObject frameobj(cx, r.frontFrame());
         Debugger *dbg = r.frontDebugger();
         JS_ASSERT(dbg == Debugger::fromChildJSObject(frameobj));
 
-        // Update frame object's StackIter::data pointer.
-        DebuggerFrame_freeStackIterData(cx->runtime->defaultFreeOp(), frameobj);
-        StackIter::Data *data = iter.copyData();
+        // Update frame object's ScriptFrameIter::data pointer.
+        DebuggerFrame_freeScriptFrameIterData(cx->runtime->defaultFreeOp(), frameobj);
+        ScriptFrameIter::Data *data = iter.copyData();
         if (!data)
             return false;
         frameobj->setPrivate(data);
 
         // Add the frame object with |to| as key.
         if (!dbg->frames.putNew(to, frameobj)) {
             js_ReportOutOfMemory(cx);
             return false;
@@ -3477,26 +3477,26 @@ static const JSFunctionSpec DebuggerScri
     JS_FN("clearAllBreakpoints", DebuggerScript_clearAllBreakpoints, 0, 0),
     JS_FS_END
 };
 
 
 /*** Debugger.Frame ******************************************************************************/
 
 static void
-DebuggerFrame_freeStackIterData(FreeOp *fop, JSObject *obj)
-{
-    fop->delete_((StackIter::Data *)obj->getPrivate());
+DebuggerFrame_freeScriptFrameIterData(FreeOp *fop, JSObject *obj)
+{
+    fop->delete_((ScriptFrameIter::Data *)obj->getPrivate());
     obj->setPrivate(NULL);
 }
 
 static void
 DebuggerFrame_finalize(FreeOp *fop, JSObject *obj)
 {
-    DebuggerFrame_freeStackIterData(fop, obj);
+    DebuggerFrame_freeScriptFrameIterData(fop, obj);
 }
 
 Class DebuggerFrame_class = {
     "Frame", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGFRAME_COUNT),
     JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, DebuggerFrame_finalize
 };
 
@@ -3535,17 +3535,17 @@ CheckThisFrame(JSContext *cx, const Call
     return thisobj;
 }
 
 #define THIS_FRAME(cx, argc, vp, fnname, args, thisobj, iter)                \
     CallArgs args = CallArgsFromVp(argc, vp);                                \
     RootedObject thisobj(cx, CheckThisFrame(cx, args, fnname, true));        \
     if (!thisobj)                                                            \
         return false;                                                        \
-    ScriptFrameIter iter(*(StackIter::Data *)thisobj->getPrivate());
+    ScriptFrameIter iter(*(ScriptFrameIter::Data *)thisobj->getPrivate());
 
 #define THIS_FRAME_OWNER(cx, argc, vp, fnname, args, thisobj, iter, dbg)       \
     THIS_FRAME(cx, argc, vp, fnname, args, thisobj, iter);                     \
     Debugger *dbg = Debugger::fromChildJSObject(thisobj)
 
 static JSBool
 DebuggerFrame_getType(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -557,17 +557,17 @@ ContextStack::currentScript(jsbytecode *
 inline HandleObject
 ContextStack::currentScriptedScopeChain() const
 {
     return fp()->scopeChain();
 }
 
 template <class Op>
 inline void
-StackIter::ionForEachCanonicalActualArg(JSContext *cx, Op op)
+ScriptFrameIter::ionForEachCanonicalActualArg(JSContext *cx, Op op)
 {
     JS_ASSERT(isIon());
 #ifdef JS_ION
     if (data_.ionFrames_.isOptimizedJS()) {
         ionInlineFrames_.forEachCanonicalActualArg(cx, op, 0, -1);
     } else {
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         data_.ionFrames_.forEachCanonicalActualArg(op, 0, -1);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -505,27 +505,16 @@ StackSegment::contains(const StackFrame 
 }
 
 bool
 StackSegment::contains(const FrameRegs *regs) const
 {
     return regs && contains(regs->fp());
 }
 
-bool
-StackSegment::contains(const CallArgsList *call) const
-{
-    if (!call || !calls_)
-        return false;
-
-    /* NB: this depends on the continuity of segments in memory. */
-    Value *vp = call->array();
-    return vp > slotsBegin() && vp <= calls_->array();
-}
-
 StackFrame *
 StackSegment::computeNextFrame(const StackFrame *f, size_t maxDepth) const
 {
     JS_ASSERT(contains(f) && f != fp());
 
     StackFrame *next = fp();
     for (size_t i = 0; i <= maxDepth; ++i) {
         if (next->prev() == f)
@@ -535,24 +524,20 @@ StackSegment::computeNextFrame(const Sta
 
     return NULL;
 }
 
 Value *
 StackSegment::end() const
 {
     /* NB: this depends on the continuity of segments in memory. */
-    JS_ASSERT_IF(calls_ || regs_, contains(calls_) || contains(regs_));
-    Value *p = calls_
-               ? regs_
-                 ? Max(regs_->sp, calls_->end())
-                 : calls_->end()
-               : regs_
-                 ? regs_->sp
-                 : slotsBegin();
+    JS_ASSERT_IF(regs_, contains(regs_));
+    Value *p = regs_ ? regs_->sp : slotsBegin();
+    if (invokeArgsEnd_ > p)
+        p = invokeArgsEnd_;
     JS_ASSERT(p >= slotsBegin());
     return p;
 }
 
 FrameRegs *
 StackSegment::pushRegs(FrameRegs &regs)
 {
     JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
@@ -563,35 +548,16 @@ StackSegment::pushRegs(FrameRegs &regs)
 
 void
 StackSegment::popRegs(FrameRegs *regs)
 {
     JS_ASSERT_IF(regs && contains(regs->fp()), regs->fp() == regs_->fp()->prev());
     regs_ = regs;
 }
 
-void
-StackSegment::pushCall(CallArgsList &callList)
-{
-    callList.prev_ = calls_;
-    calls_ = &callList;
-}
-
-void
-StackSegment::pointAtCall(CallArgsList &callList)
-{
-    calls_ = &callList;
-}
-
-void
-StackSegment::popCall()
-{
-    calls_ = calls_->prev_;
-}
-
 /*****************************************************************************/
 
 StackSpace::StackSpace()
   : seg_(NULL),
     base_(NULL),
     conservativeEnd_(NULL),
 #ifdef XP_WIN
     commitEnd_(NULL),
@@ -940,26 +906,19 @@ ContextStack::ensureOnTop(JSContext *cx,
         if (!space().ensureSpace(cx, report, firstUnused, nvars))
             return NULL;
         return firstUnused;
     }
 
     if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars))
         return NULL;
 
-    CallArgsList *calls;
-    if (seg_ && extend) {
-        regs = seg_->maybeRegs();
-        calls = seg_->maybeCalls();
-    } else {
-        regs = NULL;
-        calls = NULL;
-    }
+    regs = (seg_ && extend) ? seg_->maybeRegs() : NULL;
 
-    seg_ = new(firstUnused) StackSegment(cx, seg_, space().seg_, regs, calls);
+    seg_ = new(firstUnused) StackSegment(cx, seg_, space().seg_, regs);
     space().seg_ = seg_;
     *pushedSeg = true;
     return seg_->slotsBegin();
 }
 
 void
 ContextStack::popSegment()
 {
@@ -980,32 +939,34 @@ ContextStack::pushInvokeArgs(JSContext *
     Value *firstUnused = ensureOnTop(cx, report, nvars, CAN_EXTEND, &iag->pushedSeg_);
     if (!firstUnused)
         return false;
 
     MakeRangeGCSafe(firstUnused, nvars);
 
     ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
 
-    seg_->pushCall(*iag);
+    seg_->pushInvokeArgsEnd(iag->end(), &iag->prevInvokeArgsEnd_);
+
     JS_ASSERT(space().firstUnused() == iag->end());
     iag->setPushed(*this);
     return true;
 }
 
 void
 ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
 {
     JS_ASSERT(iag.pushed());
     JS_ASSERT(onTop());
-    JS_ASSERT(space().firstUnused() == seg_->calls().end());
+    JS_ASSERT(space().firstUnused() == seg_->invokeArgsEnd());
 
     Value *oldend = seg_->end();
 
-    seg_->popCall();
+    seg_->popInvokeArgsEnd(iag.prevInvokeArgsEnd_);
+
     if (iag.pushedSeg_)
         popSegment();
 
     Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
 }
 
 StackFrame *
 ContextStack::pushInvokeFrame(JSContext *cx, MaybeReportError report,
@@ -1056,39 +1017,37 @@ ContextStack::pushExecuteFrame(JSContext
      * prev-links.
      *
      * Eval-in-frame is the exception since it prev-links to an arbitrary frame
      * (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
      * (to start a new segment) and link the frame and call chain manually
      * below. If |evalInFrame| is a baseline JIT frame, prev-link to its entry
      * frame.
      */
-    CallArgsList *evalInFrameCalls = NULL;  /* quell overwarning */
     MaybeExtend extend;
     StackFrame *prevLink;
     AbstractFramePtr prev = NullFramePtr();
     if (evalInFrame) {
         /* First, find the right segment. */
         AllFramesIter frameIter(cx->runtime);
         while (frameIter.isIonOptimizedJS() || frameIter.abstractFramePtr() != evalInFrame)
             ++frameIter;
         JS_ASSERT(frameIter.abstractFramePtr() == evalInFrame);
 
         StackSegment &seg = *frameIter.seg();
 
-        StackIter iter(cx->runtime, seg);
+        ScriptFrameIter iter(cx->runtime, seg);
         /* Debug-mode currently disables Ion compilation. */
         JS_ASSERT_IF(evalInFrame.isStackFrame(), !evalInFrame.asStackFrame()->runningInIon());
         JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
-        while (!iter.isScript() || iter.isIonOptimizedJS() || iter.abstractFramePtr() != evalInFrame) {
+        while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != evalInFrame) {
             ++iter;
             JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIonOptimizedJS());
         }
         JS_ASSERT(iter.abstractFramePtr() == evalInFrame);
-        evalInFrameCalls = iter.data_.calls_;
         prevLink = iter.data_.fp_;
         prev = evalInFrame;
         extend = CANT_EXTEND;
     } else {
         prevLink = maybefp();
         extend = CAN_EXTEND;
         if (maybefp()) {
             ScriptFrameIter iter(cx);
@@ -1101,20 +1060,16 @@ ContextStack::pushExecuteFrame(JSContext
     if (!firstUnused)
         return false;
 
     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
     fp->initExecuteFrame(script, prevLink, prev, seg_->maybeRegs(), thisv, *scopeChain, type);
     fp->initVarsToUndefined();
     efg->regs_.prepareToRun(*fp, script);
 
-    /* pushRegs() below links the prev-frame; manually link the prev-call. */
-    if (evalInFrame && evalInFrameCalls)
-        seg_->pointAtCall(*evalInFrameCalls);
-
     efg->prevRegs_ = seg_->pushRegs(efg->regs_);
     JS_ASSERT(space().firstUnused() == efg->regs_.sp);
     efg->setPushed(*this);
     return true;
 }
 
 #ifdef JS_ION
 bool
@@ -1252,252 +1207,197 @@ ContextStack::restoreFrameChain()
     JS_ASSERT(seg_->isEmpty());
 
     popSegment();
 }
 
 /*****************************************************************************/
 
 void
-StackIter::poisonRegs()
+ScriptFrameIter::poisonRegs()
 {
     data_.pc_ = (jsbytecode *)0xbad;
 }
 
 void
-StackIter::popFrame()
+ScriptFrameIter::popFrame()
 {
     StackFrame *oldfp = data_.fp_;
     JS_ASSERT(data_.seg_->contains(oldfp));
     data_.fp_ = data_.fp_->prev();
 
     if (data_.seg_->contains(data_.fp_)) {
         InlinedSite *inline_;
         data_.pc_ = oldfp->prevpc(&inline_);
         JS_ASSERT(!inline_);
     } else {
         poisonRegs();
     }
 }
 
 void
-StackIter::popCall()
-{
-    DebugOnly<CallArgsList*> oldCall = data_.calls_;
-    JS_ASSERT(data_.seg_->contains(oldCall));
-    data_.calls_ = data_.calls_->prev();
-    if (!data_.seg_->contains(data_.fp_))
-        poisonRegs();
-}
-
-void
-StackIter::settleOnNewSegment()
+ScriptFrameIter::settleOnNewSegment()
 {
     if (FrameRegs *regs = data_.seg_->maybeRegs())
         data_.pc_ = regs->pc;
     else
         poisonRegs();
 }
 
 void
-StackIter::startOnSegment(StackSegment *seg)
+ScriptFrameIter::startOnSegment(StackSegment *seg)
 {
     data_.seg_ = seg;
     data_.fp_ = data_.seg_->maybefp();
-    data_.calls_ = data_.seg_->maybeCalls();
     settleOnNewSegment();
 }
 
 /*
- * Given that the iterator's current value of fp_ and calls_ (initialized on
- * construction or after operator++ popped the previous scripted/native call),
- * "settle" the iterator on a new StackIter::State value. The goal is to
- * present the client a simple linear sequence of native/scripted calls while
- * covering up unpleasant stack implementation details:
+ * Given the iterator's current value of fp_ (initialized on construction or
+ * after operator++ popped the previous call), "settle" the iterator on a new
+ * ScriptFrameIter::State value. The goal is to present the client a simple
+ * linear sequence of scripted calls while covering up unpleasant stack
+ * implementation details:
  *  - The frame chain can be "saved" and "restored" (see JS_SaveFrameChain).
- *    This artificially cuts the call chain and the StackIter client may want
- *    to continue through this cut to the previous frame by passing
+ *    This artificially cuts the call chain and the ScriptFrameIter client may
+ *    want to continue through this cut to the previous frame by passing
  *    GO_THROUGH_SAVED.
  *  - fp->prev can be in a different contiguous segment from fp. In this case,
- *    the current values of sp/pc after calling popFrame/popCall are incorrect
- *    and should be recovered from fp->prev's segment.
- *  - there is no explicit relationship to determine whether fp_ or calls_ is
- *    the innermost invocation so implicit memory ordering is used since both
- *    push values on the stack.
- *  - a native call's 'callee' argument is clobbered on return while the
- *    CallArgsList element is still visible.
+ *    the current values of sp/pc after calling popFrame are incorrect and
+ *    should be recovered from fp->prev's segment.
  */
+/* PGO causes xpcshell startup crashes with VS2010. */
+#if defined(_MSC_VER)
+# pragma optimize("g", off)
+#endif
 void
-StackIter::settleOnNewState()
+ScriptFrameIter::settleOnNewState()
 {
-    /* Reset whether or we popped a call last time we settled. */
-    data_.poppedCallDuringSettle_ = false;
-
     /*
-     * There are elements of the calls_ and fp_ chains that we want to skip
-     * over so iterate until we settle on one or until there are no more.
+     * There are elements of the fp_ chain that we want to skip over so iterate
+     * until we settle on one or until there are no more.
      */
     while (true) {
-        if (!data_.fp_ && !data_.calls_) {
+        if (!data_.fp_) {
             if (data_.savedOption_ == GO_THROUGH_SAVED && data_.seg_->prevInContext()) {
                 startOnSegment(data_.seg_->prevInContext());
                 continue;
             }
             data_.state_ = DONE;
             return;
         }
 
-        /* Check if popFrame/popCall changed segment. */
+        /* Check if popFrame changed segment. */
         bool containsFrame = data_.seg_->contains(data_.fp_);
-        bool containsCall = data_.seg_->contains(data_.calls_);
-        while (!containsFrame && !containsCall) {
+        while (!containsFrame) {
             /* Eval-in-frame can cross contexts, so use prevInMemory. */
             data_.seg_ = data_.seg_->prevInMemory();
             containsFrame = data_.seg_->contains(data_.fp_);
-            containsCall = data_.seg_->contains(data_.calls_);
 
             /* Eval-in-frame allows jumping into the middle of a segment. */
-            if (containsFrame &&
-                (data_.seg_->fp() != data_.fp_ || data_.seg_->maybeCalls() != data_.calls_))
-            {
+            if (containsFrame && data_.seg_->fp() != data_.fp_) {
                 /* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
-                StackIter tmp = *this;
+                ScriptFrameIter tmp = *this;
                 tmp.startOnSegment(data_.seg_);
                 tmp.settleOnNewState();
-                while (!tmp.isScript() || tmp.data_.fp_ != data_.fp_)
+                while (tmp.data_.fp_ != data_.fp_)
                     ++tmp;
-                JS_ASSERT(tmp.isScript() &&
+                JS_ASSERT(!tmp.done() &&
                           tmp.data_.seg_ == data_.seg_ &&
                           tmp.data_.fp_ == data_.fp_);
                 *this = tmp;
                 return;
             }
 
-            /* There is no eval-in-frame equivalent for native calls. */
-            JS_ASSERT_IF(containsCall, &data_.seg_->calls() == data_.calls_);
-
             settleOnNewSegment();
         }
 
-        /*
-         * In case of both a scripted frame and call record, use linear memory
-         * ordering to decide which was the most recent.
-         */
-        if (containsFrame && (!containsCall || (Value *)data_.fp_ >= data_.calls_->array())) {
 #ifdef JS_ION
-            if (data_.fp_->beginsIonActivation()) {
-                /*
-                 * Eval-in-frame can link to an arbitrary frame on the stack.
-                 * Skip any IonActivation's until we reach the one for the
-                 * current StackFrame. Treat activations with NULL entryfp
-                 * (pushed by FastInvoke) as belonging to the previous
-                 * activation.
-                 */
-                while (true) {
-                    ion::IonActivation *act = data_.ionActivations_.activation();
-                    while (!act->entryfp())
-                        act = act->prev();
-                    if (act->entryfp() == data_.fp_)
-                        break;
+        if (data_.fp_->beginsIonActivation()) {
+            /*
+             * Eval-in-frame can link to an arbitrary frame on the stack.
+             * Skip any IonActivation's until we reach the one for the
+             * current StackFrame. Treat activations with NULL entryfp
+             * (pushed by FastInvoke) as belonging to the previous
+             * activation.
+             */
+            while (true) {
+                ion::IonActivation *act = data_.ionActivations_.activation();
+                while (!act->entryfp())
+                    act = act->prev();
+                if (act->entryfp() == data_.fp_)
+                    break;
 
-                    ++data_.ionActivations_;
-                }
-
-                data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
+                ++data_.ionActivations_;
+            }
 
-                if (data_.ionFrames_.isNative()) {
-                    data_.state_ = ION;
-                    return;
-                }
+            data_.ionFrames_ = ion::IonFrameIterator(data_.ionActivations_);
 
-                while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
-                    ++data_.ionFrames_;
+            while (!data_.ionFrames_.isScripted() && !data_.ionFrames_.done())
+                ++data_.ionFrames_;
 
-                // When invoked from JM, we don't re-use the entryfp, so we
-                // may have an empty Ion activation.
-                if (data_.ionFrames_.done()) {
-                    data_.state_ = SCRIPTED;
-                    return;
-                }
-
-                data_.state_ = ION;
-                nextIonFrame();
+            // When invoked from JM, we don't re-use the entryfp, so we
+            // may have an empty Ion activation.
+            if (data_.ionFrames_.done()) {
+                data_.state_ = SCRIPTED;
                 return;
             }
-#endif /* JS_ION */
 
-            data_.state_ = SCRIPTED;
+            data_.state_ = ION;
+            nextIonFrame();
             return;
         }
+#endif /* JS_ION */
 
-        /*
-         * A CallArgsList element is pushed for any call to Invoke, regardless
-         * of whether the callee is a scripted function or even a callable
-         * object. Thus, it is necessary to filter calleev for natives.
-         *
-         * Second, stuff can happen after the args are pushed but before/after
-         * the actual call, so only consider "active" calls. (Since Invoke
-         * necessarily clobbers the callee, "active" is also necessary to
-         * ensure that the callee slot is valid.)
-         */
-        if (data_.calls_->active() && IsNativeFunction(data_.calls_->calleev())) {
-            data_.state_ = NATIVE;
-            data_.args_ = *data_.calls_;
-            return;
-        }
-
-        /* Pop the call and keep looking. */
-        popCall();
-        data_.poppedCallDuringSettle_ = true;
+        data_.state_ = SCRIPTED;
+        return;
     }
 }
+#if defined(_MSC_VER)
+# pragma optimize("", on)
+#endif
 
-StackIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption)
+ScriptFrameIter::Data::Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption)
   : perThread_(perThread),
     cx_(cx),
-    savedOption_(savedOption),
-    poppedCallDuringSettle_(false)
+    savedOption_(savedOption)
 #ifdef JS_ION
     , ionActivations_(cx),
     ionFrames_((uint8_t *)NULL)
 #endif
 {
 }
 
-StackIter::Data::Data(JSContext *cx, JSRuntime *rt, StackSegment *seg)
+ScriptFrameIter::Data::Data(JSContext *cx, JSRuntime *rt, StackSegment *seg)
   : perThread_(&rt->mainThread),
     cx_(cx),
-    savedOption_(STOP_AT_SAVED),
-    poppedCallDuringSettle_(false)
+    savedOption_(STOP_AT_SAVED)
 #ifdef JS_ION
     , ionActivations_(rt),
     ionFrames_((uint8_t *)NULL)
 #endif
 {
 }
 
-StackIter::Data::Data(const StackIter::Data &other)
+ScriptFrameIter::Data::Data(const ScriptFrameIter::Data &other)
   : perThread_(other.perThread_),
     cx_(other.cx_),
     savedOption_(other.savedOption_),
     state_(other.state_),
     fp_(other.fp_),
-    calls_(other.calls_),
     seg_(other.seg_),
-    pc_(other.pc_),
-    args_(other.args_),
-    poppedCallDuringSettle_(other.poppedCallDuringSettle_)
+    pc_(other.pc_)
 #ifdef JS_ION
     , ionActivations_(other.ionActivations_),
     ionFrames_(other.ionFrames_)
 #endif
 {
 }
 
-StackIter::StackIter(JSContext *cx, SavedOption savedOption)
+ScriptFrameIter::ScriptFrameIter(JSContext *cx, SavedOption savedOption)
   : data_(cx, &cx->runtime->mainThread, savedOption)
 #ifdef JS_ION
     , ionInlineFrames_(cx, (js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     for (ZonesIter zone(cx->runtime); !zone.done(); zone.next())
         mjit::ExpandInlineFrames(zone);
@@ -1506,63 +1406,63 @@ StackIter::StackIter(JSContext *cx, Save
     if (StackSegment *seg = cx->stack.seg_) {
         startOnSegment(seg);
         settleOnNewState();
     } else {
         data_.state_ = DONE;
     }
 }
 
-StackIter::StackIter(JSRuntime *rt, StackSegment &seg)
+ScriptFrameIter::ScriptFrameIter(JSRuntime *rt, StackSegment &seg)
   : data_(seg.cx(), rt, &seg)
 #ifdef JS_ION
     , ionInlineFrames_(seg.cx(), (js::ion::IonFrameIterator*) NULL)
 #endif
 {
 #ifdef JS_METHODJIT
     for (ZonesIter zone(rt); !zone.done(); zone.next())
         mjit::ExpandInlineFrames(zone);
 #endif
     startOnSegment(&seg);
     settleOnNewState();
 }
 
-StackIter::StackIter(const StackIter &other)
+ScriptFrameIter::ScriptFrameIter(const ScriptFrameIter &other)
   : data_(other.data_)
 #ifdef JS_ION
     , ionInlineFrames_(other.data_.seg_->cx(),
                        data_.ionFrames_.isScripted() ? &other.ionInlineFrames_ : NULL)
 #endif
 {
 }
 
-StackIter::StackIter(const Data &data)
+ScriptFrameIter::ScriptFrameIter(const Data &data)
   : data_(data)
 #ifdef JS_ION
     , ionInlineFrames_(data.cx_, data_.ionFrames_.isOptimizedJS() ? &data_.ionFrames_ : NULL)
 #endif
 {
     JS_ASSERT(data.cx_);
 }
 
 #ifdef JS_ION
 void
-StackIter::nextIonFrame()
+ScriptFrameIter::nextIonFrame()
 {
     if (data_.ionFrames_.isOptimizedJS()) {
         ionInlineFrames_.resetOn(&data_.ionFrames_);
         data_.pc_ = ionInlineFrames_.pc();
     } else {
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         data_.ionFrames_.baselineScriptAndPc(NULL, &data_.pc_);
     }
 }
 
 void
-StackIter::popIonFrame()
+ScriptFrameIter::popIonFrame()
 {
     // Keep fp which describes all ion frames.
     poisonRegs();
     if (data_.ionFrames_.isOptimizedJS() && ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         data_.pc_ = ionInlineFrames_.pc();
     } else {
         ++data_.ionFrames_;
@@ -1594,263 +1494,237 @@ StackIter::popIonFrame()
             data_.state_ = SCRIPTED;
             data_.pc_ = data_.ionActivations_.activation()->prevpc();
             ++data_.ionActivations_;
         }
     }
 }
 
 void
-StackIter::popBaselineDebuggerFrame()
+ScriptFrameIter::popBaselineDebuggerFrame()
 {
     ion::BaselineFrame *prevBaseline = data_.fp_->prevBaselineFrame();
 
     popFrame();
     settleOnNewState();
 
-    /* Pop native and Ion frames until we reach the target frame. */
-    while (data_.state_ == NATIVE) {
-        popCall();
-        settleOnNewState();
-    }
-
     JS_ASSERT(data_.state_ == ION);
     while (!data_.ionFrames_.isBaselineJS() || data_.ionFrames_.baselineFrame() != prevBaseline)
         popIonFrame();
 }
 #endif
 
-StackIter &
-StackIter::operator++()
+ScriptFrameIter &
+ScriptFrameIter::operator++()
 {
     switch (data_.state_) {
       case DONE:
         JS_NOT_REACHED("Unexpected state");
       case SCRIPTED:
 #ifdef JS_ION
         if (data_.fp_->isDebuggerFrame() && data_.fp_->prevBaselineFrame()) {
             /* Eval-in-frame with a baseline JIT frame. */
             popBaselineDebuggerFrame();
             break;
         }
 #endif
         popFrame();
         settleOnNewState();
         break;
-      case NATIVE:
-        popCall();
-        settleOnNewState();
-        break;
       case ION:
 #ifdef JS_ION
         popIonFrame();
         break;
 #else
         JS_NOT_REACHED("Unexpected state");
 #endif
     }
     return *this;
 }
 
 bool
-StackIter::operator==(const StackIter &rhs) const
+ScriptFrameIter::operator==(const ScriptFrameIter &rhs) const
 {
     return done() == rhs.done() &&
-           (done() ||
-            (isScript() == rhs.isScript() &&
-             ((isScript() && data_.fp_ == rhs.data_.fp_) ||
-              (!isScript() && nativeArgs().base() == rhs.nativeArgs().base()))));
+           (done() || data_.fp_ == rhs.data_.fp_);
 }
 
-StackIter::Data *
-StackIter::copyData() const
+ScriptFrameIter::Data *
+ScriptFrameIter::copyData() const
 {
 #ifdef JS_ION
     /*
      * This doesn't work for optimized Ion frames since ionInlineFrames_ is
      * not copied.
      */
     JS_ASSERT(data_.ionFrames_.type() != ion::IonFrame_OptimizedJS);
 #endif
     return data_.cx_->new_<Data>(data_);
 }
 
 JSCompartment *
-StackIter::compartment() const
+ScriptFrameIter::compartment() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return data_.fp_->compartment();
       case ION:
 #ifdef  JS_ION
         return data_.ionActivations_.activation()->compartment();
 #else
         break;
 #endif
-      case NATIVE:
-        return data_.calls_->callee().compartment();
     }
     JS_NOT_REACHED("Unexpected state");
     return NULL;
 }
 
 bool
-StackIter::isFunctionFrame() const
+ScriptFrameIter::isFunctionFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isFunctionFrame();
       case ION:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isScripted());
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.isFunctionFrame();
         return ionInlineFrames_.isFunctionFrame();
 #else
         break;
 #endif
-      case NATIVE:
-        return false;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
-StackIter::isGlobalFrame() const
+ScriptFrameIter::isGlobalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isGlobalFrame();
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame()->isGlobalFrame();
         JS_ASSERT(!script()->isForEval());
         return !script()->function();
 #else
         break;
 #endif
-      case NATIVE:
-        return false;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
-StackIter::isEvalFrame() const
+ScriptFrameIter::isEvalFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isEvalFrame();
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame()->isEvalFrame();
         JS_ASSERT(!script()->isForEval());
         return false;
 #else
         break;
 #endif
-      case NATIVE:
-        return false;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
-StackIter::isNonEvalFunctionFrame() const
+ScriptFrameIter::isNonEvalFunctionFrame() const
 {
     JS_ASSERT(!done());
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isNonEvalFunctionFrame();
       case ION:
-      case NATIVE:
         return !isEvalFrame() && isFunctionFrame();
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
-StackIter::isGeneratorFrame() const
+ScriptFrameIter::isGeneratorFrame() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->isGeneratorFrame();
       case ION:
-      case NATIVE:
         return false;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 bool
-StackIter::isConstructing() const
+ScriptFrameIter::isConstructing() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.isConstructing();
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.isConstructing();
 #else
         break;
 #endif        
       case SCRIPTED:
-      case NATIVE:
         return interpFrame()->isConstructing();
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 AbstractFramePtr
-StackIter::abstractFramePtr() const
+ScriptFrameIter::abstractFramePtr() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.baselineFrame();
 #endif
         break;
       case SCRIPTED:
         JS_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return NullFramePtr();
 }
 
 void
-StackIter::updatePcQuadratic()
+ScriptFrameIter::updatePcQuadratic()
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         data_.pc_ = interpFrame()->pcQuadratic(data_.cx_);
         return;
       case ION:
@@ -1872,73 +1746,65 @@ StackIter::updatePcQuadratic()
 
             // Update the pc.
             JS_ASSERT(data_.ionFrames_.baselineFrame() == frame);
             data_.ionFrames_.baselineScriptAndPc(NULL, &data_.pc_);
             return;
         }
 #endif
         break;
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
 }
 
 JSFunction *
-StackIter::callee() const
+ScriptFrameIter::callee() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return &interpFrame()->callee();
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return data_.ionFrames_.callee();
-        if (data_.ionFrames_.isOptimizedJS())
-            return ionInlineFrames_.callee();
-        JS_ASSERT(data_.ionFrames_.isNative());
-        return data_.ionFrames_.callee();
+        JS_ASSERT(data_.ionFrames_.isOptimizedJS());
+        return ionInlineFrames_.callee();
 #else
         break;
 #endif
-      case NATIVE:
-        return nativeArgs().callee().toFunction();
     }
     JS_NOT_REACHED("Unexpected state");
     return NULL;
 }
 
 Value
-StackIter::calleev() const
+ScriptFrameIter::calleev() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return interpFrame()->calleev();
       case ION:
 #ifdef JS_ION
         return ObjectValue(*callee());
 #else
         break;
 #endif
-      case NATIVE:
-        return nativeArgs().calleev();
     }
     JS_NOT_REACHED("Unexpected state");
     return Value();
 }
 
 unsigned
-StackIter::numActualArgs() const
+ScriptFrameIter::numActualArgs() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         JS_ASSERT(isFunctionFrame());
         return interpFrame()->numActualArgs();
       case ION:
@@ -1946,207 +1812,192 @@ StackIter::numActualArgs() const
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.numActualArgs();
 
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.numActualArgs();
 #else
         break;
 #endif
-      case NATIVE:
-        return nativeArgs().length();
     }
     JS_NOT_REACHED("Unexpected state");
     return 0;
 }
 
 Value
-StackIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
+ScriptFrameIter::unaliasedActual(unsigned i, MaybeCheckAliasing checkAliasing) const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->unaliasedActual(i, checkAliasing);
       case ION:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->unaliasedActual(i, checkAliasing);
 #else
         break;
 #endif
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return NullValue();
 }
 
 JSObject *
-StackIter::scopeChain() const
+ScriptFrameIter::scopeChain() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.scopeChain();
         return data_.ionFrames_.baselineFrame()->scopeChain();
 #else
         break;
 #endif
       case SCRIPTED:
         return interpFrame()->scopeChain();
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return NULL;
 }
 
 CallObject &
-StackIter::callObj() const
+ScriptFrameIter::callObj() const
 {
     JS_ASSERT(callee()->isHeavyweight());
 
     JSObject *pobj = scopeChain();
     while (!pobj->isCall())
         pobj = pobj->enclosingScope();
     return pobj->asCall();
 }
 
 bool
-StackIter::hasArgsObj() const
+ScriptFrameIter::hasArgsObj() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case SCRIPTED:
         return interpFrame()->hasArgsObj();
       case ION:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->hasArgsObj();
 #else
         break;
 #endif
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return false;
 }
 
 ArgumentsObject &
-StackIter::argsObj() const
+ScriptFrameIter::argsObj() const
 {
     JS_ASSERT(hasArgsObj());
 
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         JS_ASSERT(data_.ionFrames_.isBaselineJS());
         return data_.ionFrames_.baselineFrame()->argsObj();
 #else
         break;
 #endif
       case SCRIPTED:
         return interpFrame()->argsObj();
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return interpFrame()->argsObj();
 }
 
 bool
-StackIter::computeThis() const
+ScriptFrameIter::computeThis() const
 {
-    if (isScript() && !isIonOptimizedJS()) {
+    JS_ASSERT(!done());
+    if (!isIonOptimizedJS()) {
         JS_ASSERT(data_.cx_);
         return ComputeThis(data_.cx_, abstractFramePtr());
     }
     return true;
 }
 
 Value
-StackIter::thisv() const
+ScriptFrameIter::thisv() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ObjectValue(*ionInlineFrames_.thisObject());
         return data_.ionFrames_.baselineFrame()->thisValue();
 #else
         break;
 #endif
       case SCRIPTED:
-      case NATIVE:
         return interpFrame()->thisValue();
     }
     JS_NOT_REACHED("Unexpected state");
     return NullValue();
 }
 
 Value
-StackIter::returnValue() const
+ScriptFrameIter::returnValue() const
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS())
             return *data_.ionFrames_.baselineFrame()->returnValue();
 #endif
         break;
       case SCRIPTED:
         return interpFrame()->returnValue();
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
     return NullValue();
 }
 
 void
-StackIter::setReturnValue(const Value &v)
+ScriptFrameIter::setReturnValue(const Value &v)
 {
     switch (data_.state_) {
       case DONE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isBaselineJS()) {
             data_.ionFrames_.baselineFrame()->setReturnValue(v);
             return;
         }
 #endif
         break;
       case SCRIPTED:
         interpFrame()->setReturnValue(v);
         return;
-      case NATIVE:
-        break;
     }
     JS_NOT_REACHED("Unexpected state");
 }
 
 size_t
-StackIter::numFrameSlots() const
+ScriptFrameIter::numFrameSlots() const
 {
     switch (data_.state_) {
       case DONE:
-      case NATIVE:
         break;
      case ION: {
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.snapshotIterator().slots() - ionInlineFrames_.script()->nfixed;
         ion::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
         return frame->numValueSlots() - data_.ionFrames_.script()->nfixed;
 #else
@@ -2158,21 +2009,20 @@ StackIter::numFrameSlots() const
         JS_ASSERT(data_.cx_->regs().spForStackDepth(0) == interpFrame()->base());
         return data_.cx_->regs().sp - interpFrame()->base();
     }
     JS_NOT_REACHED("Unexpected state");
     return 0;
 }
 
 Value
-StackIter::frameSlotValue(size_t index) const
+ScriptFrameIter::frameSlotValue(size_t index) const
 {
     switch (data_.state_) {
       case DONE:
-      case NATIVE:
         break;
       case ION:
 #ifdef JS_ION
         if (data_.ionFrames_.isOptimizedJS()) {
             ion::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
             index += ionInlineFrames_.script()->nfixed;
             return si.maybeReadSlotByIndex(index);
         }
@@ -2307,18 +2157,18 @@ JSObject *
 AbstractFramePtr::evalPrevScopeChain(JSRuntime *rt) const
 {
     /* Find the stack segment containing this frame. */
     AllFramesIter alliter(rt);
     while (alliter.isIonOptimizedJS() || alliter.abstractFramePtr() != *this)
         ++alliter;
 
     /* Eval frames are not compiled by Ion, though their caller might be. */
-    StackIter iter(rt, *alliter.seg());
-    while (!iter.isScript() || iter.isIonOptimizedJS() || iter.abstractFramePtr() != *this)
+    ScriptFrameIter iter(rt, *alliter.seg());
+    while (iter.isIonOptimizedJS() || iter.abstractFramePtr() != *this)
         ++iter;
     ++iter;
     return iter.scopeChain();
 }
 
 #ifdef DEBUG
 void
 js::CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -106,48 +106,20 @@ namespace ion {
  * the expression become the arguments of a call. There are also layout
  * invariants concerning the arguments and StackFrame; see "Arguments" comment
  * in StackFrame for more details.
  *
  * The top of a segment's current frame's expression stack is pointed to by the
  * segment's "current regs", which contains the stack pointer 'sp'. In the
  * interpreter, sp is adjusted as individual values are pushed and popped from
  * the stack and the FrameRegs struct (pointed by the StackSegment) is a local
- * var of js::Interpret. JIT code simulates this by lazily updating FrameRegs
+ * var of js::Interpret. JM JIT code simulates this by lazily updating FrameRegs
  * when calling from JIT code into the VM. Ideally, we'd like to remove all
  * dependence on FrameRegs outside the interpreter.
  *
- * A call to a native (C++) function does not push a frame. Instead, an array
- * of values is passed to the native. The layout of this array is abstracted by
- * JS::CallArgs. With respect to the StackSegment layout above, the args to a
- * native call are inserted anywhere there can be values. A sample memory layout
- * looks like:
- *
- *                          regs
- *       .------------------------------------------.
- *       |                                          V
- *       |                                fp .--FrameRegs--. sp
- *       |                                   V             V
- * |StackSegment| native call | values |StackFrame| values | native call |
- *       |       vp <--argc--> end                        vp <--argc--> end
- *       |           CallArgs <------------------------------ CallArgs
- *       |                                 prev                  ^
- *       `-------------------------------------------------------'
- *                                    calls
- *
- * Here there are two native calls on the stack. The start of each native arg
- * range is recorded by a CallArgs element which is prev-linked like stack
- * frames. Note that, in full generality, native and scripted calls can
- * interleave arbitrarily. Thus, the end of a segment is the maximum of its
- * current frame and its current native call. Similarly, the top of the entire
- * thread stack is the end of its current segment.
- *
- * Note that, between any two StackFrames there may be any number
- * of native calls, so the meaning of 'prev' is not 'directly called by'.
- *
  * An additional feature (perhaps not for much longer: bug 650361) is that
  * multiple independent "contexts" can interleave (LIFO) on a single contiguous
  * stack. "Independent" here means that each context has its own callstack.
  * Note, though, that eval-in-frame allows one context's callstack to join
  * another context's callstack. Thus, in general, the structure of calls in a
  * StackSpace is a forest.
  *
  * More concretely, an embedding may enter the JS engine on cx1 and then, from
@@ -158,61 +130,16 @@ namespace ion {
  * perspective of cx1 and cx2. Thus, each segment has two links: prevInMemory
  * and prevInContext. Each independent stack is encapsulated and managed by
  * the js::ContextStack object stored in JSContext. ContextStack is the primary
  * interface to the rest of the engine for pushing and popping the stack.
  */
 
 /*****************************************************************************/
 
-/*
- * For calls to natives, the InvokeArgsGuard object provides a record of the
- * call for the debugger's callstack. For this to work, the InvokeArgsGuard
- * record needs to know when the call is actually active (because the
- * InvokeArgsGuard can be pushed long before and popped long after the actual
- * call, during which time many stack-observing things can happen).
- */
-class MOZ_STACK_CLASS CallArgsList : public JS::CallArgs
-{
-    friend class StackSegment;
-    CallArgsList *prev_;
-    bool active_;
-  protected:
-    CallArgsList() : prev_(NULL), active_(false) {}
-  public:
-    friend CallArgsList CallArgsListFromVp(unsigned, Value *, CallArgsList *);
-    friend CallArgsList CallArgsListFromArgv(unsigned, Value *, CallArgsList *);
-    CallArgsList *prev() const { return prev_; }
-    bool active() const { return active_; }
-    void setActive() { active_ = true; }
-    void setInactive() { active_ = false; }
-};
-
-JS_ALWAYS_INLINE CallArgsList
-CallArgsListFromArgv(unsigned argc, Value *argv, CallArgsList *prev)
-{
-    CallArgsList args;
-#ifdef DEBUG
-    args.usedRval_ = false;
-#endif
-    args.argv_ = argv;
-    args.argc_ = argc;
-    args.prev_ = prev;
-    args.active_ = false;
-    return args;
-}
-
-JS_ALWAYS_INLINE CallArgsList
-CallArgsListFromVp(unsigned argc, Value *vp, CallArgsList *prev)
-{
-    return CallArgsListFromArgv(argc, vp + 2, prev);
-}
-
-/*****************************************************************************/
-
 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
 
 /*****************************************************************************/
 
 #ifdef DEBUG
 extern void
 CheckLocalUnaliased(MaybeCheckAliasing checkAliasing, JSScript *script,
                     StaticBlockObject *maybeBlock, unsigned i);
@@ -465,17 +392,17 @@ class StackFrame
     Value *formals() const { return (Value *)this - fun()->nargs; }
     Value *actuals() const { return formals() - (flags_ & OVERFLOW_ARGS ? 2 + u.nactual : 0); }
     unsigned nactual() const { return u.nactual; }
 
   private:
     friend class FrameRegs;
     friend class ContextStack;
     friend class StackSpace;
-    friend class StackIter;
+    friend class ScriptFrameIter;
     friend class CallObject;
     friend class ClonedBlockObject;
     friend class ArgumentsObject;
 #ifdef JS_METHODJIT
     friend class mjit::CallCompiler;
     friend class mjit::GetPropCompiler;
     friend struct mjit::ic::GetElementIC;
 #endif
@@ -605,18 +532,18 @@ class StackFrame
     StackFrame *prev() const {
         return prev_;
     }
 
 #ifdef JS_ION
     /*
      * To handle eval-in-frame with a baseline JIT frame, |prev_| points to the
      * entry frame and prevBaselineFrame_ to the actual BaselineFrame. This is
-     * done so that StackIter can skip JIT frames pushed on top of the baseline
-     * frame (these frames should not appear in stack traces).
+     * done so that ScriptFrameIter can skip JIT frames pushed on top of the
+     * baseline frame (these frames should not appear in stack traces).
      */
     ion::BaselineFrame *prevBaselineFrame() const {
         JS_ASSERT(isEvalFrame());
         return prevBaselineFrame_;
     }
 #endif
 
     inline void resetGeneratorPrev(JSContext *cx);
@@ -1383,39 +1310,38 @@ class StackSegment
     StackSegment *const prevInContext_;
 
     /* Previous segment sequentially in memory. */
     StackSegment *const prevInMemory_;
 
     /* Execution registers for most recent script in this segment (or null). */
     FrameRegs *regs_;
 
-    /* Call args for most recent native call in this segment (or null). */
-    CallArgsList *calls_;
+    /* End of CallArgs pushed by pushInvokeArgs. */
+    Value *invokeArgsEnd_;
 
 #if JS_BITS_PER_WORD == 32
     /*
      * Ensure StackSegment is Value-aligned. Protected to silence Clang warning
      * about unused private fields.
      */
   protected:
     uint32_t padding_;
 #endif
 
   public:
     StackSegment(JSContext *cx,
                  StackSegment *prevInContext,
                  StackSegment *prevInMemory,
-                 FrameRegs *regs,
-                 CallArgsList *calls)
+                 FrameRegs *regs)
       : cx_(cx),
         prevInContext_(prevInContext),
         prevInMemory_(prevInMemory),
         regs_(regs),
-        calls_(calls)
+        invokeArgsEnd_(NULL)
     {}
 
     /* A segment is followed in memory by the arguments of the first call. */
 
     Value *slotsBegin() const {
         return (Value *)(this + 1);
     }
 
@@ -1437,33 +1363,16 @@ class StackSegment
     StackFrame *maybefp() const {
         return regs_ ? regs_->fp() : NULL;
     }
 
     jsbytecode *maybepc() const {
         return regs_ ? regs_->pc : NULL;
     }
 
-    CallArgsList &calls() const {
-        JS_ASSERT(calls_);
-        return *calls_;
-    }
-
-    CallArgsList *maybeCalls() const {
-        return calls_;
-    }
-
-    Value *callArgv() const {
-        return calls_->array();
-    }
-
-    Value *maybeCallArgv() const {
-        return calls_ ? calls_->array() : NULL;
-    }
-
     JSContext *cx() const {
         return cx_;
     }
 
     StackSegment *prevInContext() const {
         return prevInContext_;
     }
 
@@ -1471,32 +1380,39 @@ class StackSegment
         return prevInMemory_;
     }
 
     void repointRegs(FrameRegs *regs) {
         regs_ = regs;
     }
 
     bool isEmpty() const {
-        return !calls_ && !regs_;
+        return !regs_;
     }
 
     bool contains(const StackFrame *fp) const;
     bool contains(const FrameRegs *regs) const;
-    bool contains(const CallArgsList *call) const;
 
     StackFrame *computeNextFrame(const StackFrame *fp, size_t maxDepth) const;
 
     Value *end() const;
 
     FrameRegs *pushRegs(FrameRegs &regs);
     void popRegs(FrameRegs *regs);
-    void pushCall(CallArgsList &callList);
-    void pointAtCall(CallArgsList &callList);
-    void popCall();
+
+    Value *invokeArgsEnd() const {
+        return invokeArgsEnd_;
+    }
+    void pushInvokeArgsEnd(Value *end, Value **prev) {
+        *prev = invokeArgsEnd_;
+        invokeArgsEnd_ = end;
+    }
+    void popInvokeArgsEnd(Value *prev) {
+        invokeArgsEnd_ = prev;
+    }
 
     /* For jit access: */
 
     static const size_t offsetOfRegs() { return offsetof(StackSegment, regs_); }
 };
 
 static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
 JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
@@ -1664,17 +1580,17 @@ class ContextStack
     void popSegment();
     friend class InvokeArgsGuard;
     void popInvokeArgs(const InvokeArgsGuard &iag);
     friend class FrameGuard;
     void popFrame(const FrameGuard &fg);
     friend class GeneratorFrameGuard;
     void popGeneratorFrame(const GeneratorFrameGuard &gfg);
 
-    friend class StackIter;
+    friend class ScriptFrameIter;
 
   public:
     ContextStack(JSContext *cx);
     ~ContextStack();
 
     /*** Stack accessors ***/
 
     /*
@@ -1794,24 +1710,25 @@ class ContextStack
      * As an optimization, the interpreter/mjit can operate on a local
      * FrameRegs instance repoint the ContextStack to this local instance.
      */
     inline void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
 };
 
 /*****************************************************************************/
 
-class InvokeArgsGuard : public CallArgsList
+class InvokeArgsGuard : public JS::CallArgs
 {
     friend class ContextStack;
     ContextStack *stack_;
+    Value *prevInvokeArgsEnd_;
     bool pushedSeg_;
     void setPushed(ContextStack &stack) { JS_ASSERT(!pushed()); stack_ = &stack; }
   public:
-    InvokeArgsGuard() : CallArgsList(), stack_(NULL), pushedSeg_(false) {}
+    InvokeArgsGuard() : CallArgs(), stack_(NULL), prevInvokeArgsEnd_(NULL), pushedSeg_(false) {}
     ~InvokeArgsGuard() { if (pushed()) stack_->popInvokeArgs(*this); }
     bool pushed() const { return !!stack_; }
     void pop() { stack_->popInvokeArgs(*this); stack_ = NULL; }
 };
 
 class FrameGuard
 {
   protected:
@@ -1879,42 +1796,38 @@ struct DefaultHasher<AbstractFramePtr> {
  *       JS_ASSERT(i.isNativeCall());
  *       ... i.args();
  *     }
  *   }
  *
  * The SavedOption parameter additionally lets the iterator continue through
  * breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
  */
-class StackIter
+class ScriptFrameIter
 {
   public:
     enum SavedOption { STOP_AT_SAVED, GO_THROUGH_SAVED };
-    enum State { DONE, SCRIPTED, NATIVE, ION };
+    enum State { DONE, SCRIPTED, ION };
 
     /*
-     * Unlike StackIter itself, StackIter::Data can be allocated on the heap,
-     * so this structure should not contain any GC things.
+     * Unlike ScriptFrameIter itself, ScriptFrameIter::Data can be allocated on
+     * the heap, so this structure should not contain any GC things.
      */
     struct Data
     {
         PerThreadData *perThread_;
         JSContext    *cx_;
         SavedOption  savedOption_;
 
         State        state_;
 
         StackFrame   *fp_;
-        CallArgsList *calls_;
 
         StackSegment *seg_;
         jsbytecode   *pc_;
-        CallArgs      args_;
-
-        bool          poppedCallDuringSettle_;
 
 #ifdef JS_ION
         ion::IonActivationIterator ionActivations_;
         ion::IonFrameIterator ionFrames_;
 #endif
 
         Data(JSContext *cx, PerThreadData *perThread, SavedOption savedOption);
         Data(JSContext *cx, JSRuntime *rt, StackSegment *seg);
@@ -1926,54 +1839,43 @@ class StackIter
   private:
     Data data_;
 #ifdef JS_ION
     ion::InlineFrameIterator ionInlineFrames_;
 #endif
 
     void poisonRegs();
     void popFrame();
-    void popCall();
 #ifdef JS_ION
     void nextIonFrame();
     void popIonFrame();
     void popBaselineDebuggerFrame();
 #endif
     void settleOnNewSegment();
     void settleOnNewState();
     void startOnSegment(StackSegment *seg);
 
   public:
-    StackIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
-    StackIter(JSRuntime *rt, StackSegment &seg);
-    StackIter(const StackIter &iter);
-    StackIter(const Data &data);
+    ScriptFrameIter(JSContext *cx, SavedOption = STOP_AT_SAVED);
+    ScriptFrameIter(JSRuntime *rt, StackSegment &seg);
+    ScriptFrameIter(const ScriptFrameIter &iter);
+    ScriptFrameIter(const Data &data);
 
     bool done() const { return data_.state_ == DONE; }
-    StackIter &operator++();
+    ScriptFrameIter &operator++();
 
     Data *copyData() const;
 
-    bool operator==(const StackIter &rhs) const;
-    bool operator!=(const StackIter &rhs) const { return !(*this == rhs); }
+    bool operator==(const ScriptFrameIter &rhs) const;
+    bool operator!=(const ScriptFrameIter &rhs) const { return !(*this == rhs); }
 
     JSCompartment *compartment() const;
 
-    bool poppedCallDuringSettle() const { return data_.poppedCallDuringSettle_; }
-
-    bool isScript() const {
+    JSScript *script() const {
         JS_ASSERT(!done());
-#ifdef JS_ION
-        if (data_.state_ == ION)
-            return data_.ionFrames_.isScripted();
-#endif
-        return data_.state_ == SCRIPTED;
-    }
-    JSScript *script() const {
-        JS_ASSERT(isScript());
         if (data_.state_ == SCRIPTED)
             return interpFrame()->script();
 #ifdef JS_ION
         JS_ASSERT(data_.state_ == ION);
         if (data_.ionFrames_.isOptimizedJS())
             return ionInlineFrames_.script();
         return data_.ionFrames_.script();
 #else
@@ -1996,25 +1898,16 @@ class StackIter
     bool isIonBaselineJS() const {
 #ifdef JS_ION
         return isIon() && data_.ionFrames_.isBaselineJS();
 #else
         return false;
 #endif
     }
 
-    bool isNativeCall() const {
-        JS_ASSERT(!done());
-#ifdef JS_ION
-        if (data_.state_ == ION)
-            return data_.ionFrames_.isNative();
-#endif
-        return data_.state_ == NATIVE;
-    }
-
     bool isFunctionFrame() const;
     bool isGlobalFrame() const;
     bool isEvalFrame() const;
     bool isNonEvalFunctionFrame() const;
     bool isGeneratorFrame() const;
     bool isConstructing() const;
 
     bool hasArgs() const { return isNonEvalFunctionFrame(); }
@@ -2022,19 +1915,19 @@ class StackIter
     AbstractFramePtr abstractFramePtr() const;
 
     /*
      * When entering IonMonkey, the top interpreter frame (pushed by the caller)
      * is kept on the stack as bookkeeping (with runningInIon() set). The
      * contents of the frame are ignored by Ion code (and GC) and thus
      * immediately become garbage and must not be touched directly.
      */
-    StackFrame *interpFrame() const { JS_ASSERT(isScript() && !isIon()); return data_.fp_; }
+    StackFrame *interpFrame() const { JS_ASSERT(data_.state_ == SCRIPTED); return data_.fp_; }
 
-    jsbytecode *pc() const { JS_ASSERT(isScript()); return data_.pc_; }
+    jsbytecode *pc() const { JS_ASSERT(!done()); return data_.pc_; }
     void        updatePcQuadratic();
     JSFunction *callee() const;
     Value       calleev() const;
     unsigned    numActualArgs() const;
     unsigned    numFormalArgs() const { return script()->function()->nargs; }
     Value       unaliasedActual(unsigned i, MaybeCheckAliasing = CHECK_ALIASING) const;
 
     JSObject   *scopeChain() const;
@@ -2053,58 +1946,37 @@ class StackIter
     JSFunction *maybeCallee() const {
         return isFunctionFrame() ? callee() : NULL;
     }
 
     // These are only valid for the top frame.
     size_t      numFrameSlots() const;
     Value       frameSlotValue(size_t index) const;
 
-    CallArgs nativeArgs() const { JS_ASSERT(isNativeCall()); return data_.args_; }
-
     template <class Op>
     inline void ionForEachCanonicalActualArg(JSContext *cx, Op op);
 };
 
-/* A filtering of the StackIter to only stop at scripts. */
-class ScriptFrameIter : public StackIter
+/* A filtering of the ScriptFrameIter to only stop at non-self-hosted scripts. */
+class NonBuiltinScriptFrameIter : public ScriptFrameIter
 {
     void settle() {
-        while (!done() && !isScript())
-            StackIter::operator++();
+        while (!done() && script()->selfHosted)
+            ScriptFrameIter::operator++();
     }
 
   public:
-    ScriptFrameIter(JSContext *cx, StackIter::SavedOption opt = StackIter::STOP_AT_SAVED)
-      : StackIter(cx, opt) { settle(); }
+    NonBuiltinScriptFrameIter(JSContext *cx, ScriptFrameIter::SavedOption opt = ScriptFrameIter::STOP_AT_SAVED)
+        : ScriptFrameIter(cx, opt) { settle(); }
 
-    ScriptFrameIter(const StackIter::Data &data)
-      : StackIter(data)
+    NonBuiltinScriptFrameIter(const ScriptFrameIter::Data &data)
+      : ScriptFrameIter(data)
     {}
 
-    ScriptFrameIter &operator++() { StackIter::operator++(); settle(); return *this; }
-};
-
-/* A filtering of the StackIter to only stop at non-self-hosted scripts. */
-class NonBuiltinScriptFrameIter : public StackIter
-{
-    void settle() {
-        while (!done() && (!isScript() || script()->selfHosted))
-            StackIter::operator++();
-    }
-
-  public:
-    NonBuiltinScriptFrameIter(JSContext *cx, StackIter::SavedOption opt = StackIter::STOP_AT_SAVED)
-        : StackIter(cx, opt) { settle(); }
-
-    NonBuiltinScriptFrameIter(const StackIter::Data &data)
-      : StackIter(data)
-    {}
-
-    NonBuiltinScriptFrameIter &operator++() { StackIter::operator++(); settle(); return *this; }
+    NonBuiltinScriptFrameIter &operator++() { ScriptFrameIter::operator++(); settle(); return *this; }
 };
 
 /*****************************************************************************/
 
 /*
  * Blindly iterate over all frames in the current thread's stack. These frames
  * can be from different contexts and compartments, so beware. Iterates over
  * Ion frames, but does not handle inlined frames.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -1412,20 +1412,22 @@ nsXPCComponents_Results::NewEnumerate(ns
             return NS_OK;
     }
 }
 
 
 /* bool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id, in uint32_t flags, out JSObjectPtr objp); */
 NS_IMETHODIMP
 nsXPCComponents_Results::NewResolve(nsIXPConnectWrappedNative *wrapper,
-                                    JSContext * cx, JSObject * obj,
-                                    jsid id, uint32_t flags,
+                                    JSContext *cx, JSObject *objArg,
+                                    jsid idArg, uint32_t flags,
                                     JSObject * *objp, bool *_retval)
 {
+    RootedObject obj(cx, objArg);
+    RootedId id(cx, idArg);
     JSAutoByteString name;
 
     if (JSID_IS_STRING(id) && name.encodeLatin1(cx, JSID_TO_STRING(id))) {
         const char* rv_name;
         void* iter = nullptr;
         nsresult rv;
         while (nsXPCException::IterateNSResults(&rv, &rv_name, nullptr, &iter)) {
             if (!strcmp(name.ptr(), rv_name)) {
@@ -4952,24 +4954,24 @@ ContentComponentsGetterOp(JSContext *cx,
     return true;
 }
 
 // static
 JSBool
 nsXPCComponents::AttachComponentsObject(XPCCallContext& ccx,
                                         XPCWrappedNativeScope* aScope)
 {
-    JSObject *components = aScope->GetComponentsJSObject(ccx);
+    RootedObject components(ccx, aScope->GetComponentsJSObject(ccx));
     if (!components)
         return false;
 
-    JSObject *global = aScope->GetGlobalJSObject();
+    RootedObject global(ccx, aScope->GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, ccx));
 
-    jsid id = ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_COMPONENTS);
+    RootedId id(ccx, ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_COMPONENTS));
     JSPropertyOp getter = AccessCheck::isChrome(global) ? nullptr
                                                         : &ContentComponentsGetterOp;
     return JS_DefinePropertyById(ccx, global, id, js::ObjectValue(*components),
                                  getter, nullptr, JSPROP_PERMANENT | JSPROP_READONLY);
 }
 
 /* void lookupMethod (); */
 NS_IMETHODIMP
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -208,21 +208,21 @@ GetMemberInfo(JSObject *obj, jsid member
             }
         }
     }
 }
 
 static void
 GetMethodInfo(JSContext *cx, jsval *vp, const char **ifaceNamep, jsid *memberIdp)
 {
-    JSObject *funobj = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
+    RootedObject funobj(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)));
     NS_ASSERTION(JS_ObjectIsFunction(cx, funobj),
                  "JSNative callee should be Function object");
-    JSString *str = JS_GetFunctionId(JS_GetObjectFunction(funobj));
-    jsid methodId = str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID;
+    RootedString str(cx, JS_GetFunctionId(JS_GetObjectFunction(funobj)));
+    RootedId methodId(cx, str ? INTERNED_STRING_TO_JSID(cx, str) : JSID_VOID);
     GetMemberInfo(JSVAL_TO_OBJECT(vp[1]), methodId, ifaceNamep);
     *memberIdp = methodId;
 }
 
 static bool
 ThrowCallFailed(JSContext *cx, nsresult rv,
                 const char *ifaceName, HandleId memberId, const char *memberName)
 {
@@ -356,18 +356,18 @@ ThrowBadArg(JSContext *cx, nsresult rv, 
     if (sz)
         JS_smprintf_free(sz);
 }
 
 void
 xpc_qsThrowBadArg(JSContext *cx, nsresult rv, jsval *vp, unsigned paramnum)
 {
     const char *ifaceName;
-    jsid memberId;
-    GetMethodInfo(cx, vp, &ifaceName, &memberId);
+    RootedId memberId(cx);
+    GetMethodInfo(cx, vp, &ifaceName, memberId.address());
     ThrowBadArg(cx, rv, ifaceName, memberId, NULL, paramnum);
 }
 
 void
 xpc_qsThrowBadArgWithCcx(XPCCallContext &ccx, nsresult rv, unsigned paramnum)
 {
     XPCThrower::ThrowBadParam(rv, paramnum, ccx);
 }
@@ -376,18 +376,19 @@ void
 xpc_qsThrowBadArgWithDetails(JSContext *cx, nsresult rv, unsigned paramnum,
                              const char *ifaceName, const char *memberName)
 {
     ThrowBadArg(cx, rv, ifaceName, JSID_VOID, memberName, paramnum);
 }
 
 void
 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
-                          JSObject *obj, jsid propId)
+                          JSObject *obj, jsid propIdArg)
 {
+    RootedId propId(cx, propIdArg);
     const char *ifaceName;
     GetMemberInfo(obj, propId, &ifaceName);
     ThrowBadArg(cx, rv, ifaceName, propId, NULL, 0);
 }
 
 void
 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
                           JSObject *objArg, const char* propName)
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -411,151 +411,178 @@ XPCVariant::VariantDataToJS(XPCLazyCallC
 
     // We just fall through to the code below and let it do what it does.
 
     // The nsIVariant is not a XPCVariant (or we act like it isn't).
     // So we extract the data and do the Right Thing.
 
     // We ASSUME that the variant implementation can do these conversions...
 
-    nsXPTCVariant xpctvar;
     nsID iid;
-    nsAutoString astring;
-    nsAutoCString cString;
-    nsUTF8String utf8String;
-    uint32_t size;
-    xpctvar.flags = 0;
-    JSBool success;
 
     NS_ABORT_IF_FALSE(js::IsObjectInContextCompartment(lccx.GetScopeForNewJSObjects(), cx),
                       "bad scope for new JSObjects");
 
     switch (type) {
         case nsIDataType::VTYPE_INT8:
         case nsIDataType::VTYPE_INT16:
         case nsIDataType::VTYPE_INT32:
         case nsIDataType::VTYPE_INT64:
         case nsIDataType::VTYPE_UINT8:
         case nsIDataType::VTYPE_UINT16:
         case nsIDataType::VTYPE_UINT32:
         case nsIDataType::VTYPE_UINT64:
         case nsIDataType::VTYPE_FLOAT:
         case nsIDataType::VTYPE_DOUBLE:
         {
-            // Easy. Handle inline.
-            if (NS_FAILED(variant->GetAsDouble(&xpctvar.val.d)))
+            double d;
+            if (NS_FAILED(variant->GetAsDouble(&d)))
                 return false;
-            *pJSVal = JS_NumberValue(xpctvar.val.d);
+            *pJSVal = JS_NumberValue(d);
             return true;
         }
         case nsIDataType::VTYPE_BOOL:
         {
-            // Easy. Handle inline.
-            if (NS_FAILED(variant->GetAsBool(&xpctvar.val.b)))
+            bool b;
+            if (NS_FAILED(variant->GetAsBool(&b)))
                 return false;
-            *pJSVal = BOOLEAN_TO_JSVAL(xpctvar.val.b);
+            *pJSVal = BOOLEAN_TO_JSVAL(b);
             return true;
         }
         case nsIDataType::VTYPE_CHAR:
-            if (NS_FAILED(variant->GetAsChar(&xpctvar.val.c)))
+        {
+            char c;
+            if (NS_FAILED(variant->GetAsChar(&c)))
                 return false;
-            xpctvar.type = (uint8_t)TD_CHAR;
-            break;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&c, TD_CHAR, &iid, pErr);
+        }
         case nsIDataType::VTYPE_WCHAR:
-            if (NS_FAILED(variant->GetAsWChar(&xpctvar.val.wc)))
+        {
+            PRUnichar wc;
+            if (NS_FAILED(variant->GetAsWChar(&wc)))
                 return false;
-            xpctvar.type = (uint8_t)TD_WCHAR;
-            break;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&wc, TD_WCHAR, &iid, pErr);
+        }
         case nsIDataType::VTYPE_ID:
+        {
             if (NS_FAILED(variant->GetAsID(&iid)))
                 return false;
-            xpctvar.type = (uint8_t)TD_PNSIID;
-            xpctvar.val.p = &iid;
-            break;
+            nsID *v = &iid;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&v, TD_PNSIID, &iid, pErr);
+        }
         case nsIDataType::VTYPE_ASTRING:
+        {
+            nsAutoString astring;
             if (NS_FAILED(variant->GetAsAString(astring)))
                 return false;
-            xpctvar.type = (uint8_t)TD_ASTRING;
-            xpctvar.val.p = &astring;
-            break;
+            nsAutoString *v = &astring;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&v, TD_ASTRING, &iid, pErr);
+        }
         case nsIDataType::VTYPE_DOMSTRING:
+        {
+            nsAutoString astring;
             if (NS_FAILED(variant->GetAsAString(astring)))
                 return false;
-            xpctvar.type = (uint8_t)TD_DOMSTRING;
-            xpctvar.val.p = &astring;
-            break;
+            nsAutoString *v = &astring;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&v,
+                                             TD_DOMSTRING, &iid, pErr);
+        }
         case nsIDataType::VTYPE_CSTRING:
+        {
+            nsAutoCString cString;
             if (NS_FAILED(variant->GetAsACString(cString)))
                 return false;
-            xpctvar.type = (uint8_t)TD_CSTRING;
-            xpctvar.val.p = &cString;
-            break;
+            nsAutoCString *v = &cString;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&v,
+                                             TD_CSTRING, &iid, pErr);
+        }
         case nsIDataType::VTYPE_UTF8STRING:
+        {
+            nsUTF8String utf8String;
             if (NS_FAILED(variant->GetAsAUTF8String(utf8String)))
                 return false;
-            xpctvar.type = (uint8_t)TD_UTF8STRING;
-            xpctvar.val.p = &utf8String;
-            break;
+            nsUTF8String *v = &utf8String;
+            return XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&v,
+                                             TD_UTF8STRING, &iid, pErr);
+        }
         case nsIDataType::VTYPE_CHAR_STR:
-            if (NS_FAILED(variant->GetAsString((char**)&xpctvar.val.p)))
+        {
+            char *pc;
+            if (NS_FAILED(variant->GetAsString(&pc)))
                 return false;
-            xpctvar.type = (uint8_t)TD_PSTRING;
-            xpctvar.SetValNeedsCleanup();
-            break;
+            bool success = XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&pc,
+                                                     TD_PSTRING, &iid, pErr);
+            nsMemory::Free(pc);
+            return success;
+        }
         case nsIDataType::VTYPE_STRING_SIZE_IS:
-            if (NS_FAILED(variant->GetAsStringWithSize(&size,
-                                                       (char**)&xpctvar.val.p)))
+        {
+            char *pc;
+            uint32_t size;
+            if (NS_FAILED(variant->GetAsStringWithSize(&size, &pc)))
                 return false;
-            xpctvar.type = (uint8_t)TD_PSTRING_SIZE_IS;
-            xpctvar.SetValNeedsCleanup();
-            break;
+            bool success = XPCConvert::NativeStringWithSize2JS(cx, pJSVal, (const void*)&pc,
+                                                               TD_PSTRING_SIZE_IS, size, pErr);
+            nsMemory::Free(pc);
+            return success;
+        }
         case nsIDataType::VTYPE_WCHAR_STR:
-            if (NS_FAILED(variant->GetAsWString((PRUnichar**)&xpctvar.val.p)))
+        {
+            PRUnichar *pwc;
+            if (NS_FAILED(variant->GetAsWString(&pwc)))
                 return false;
-            xpctvar.type = (uint8_t)TD_PWSTRING;
-            xpctvar.SetValNeedsCleanup();
-            break;
+            bool success = XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&pwc,
+                                                     TD_PSTRING, &iid, pErr);
+            nsMemory::Free(pwc);
+            return success;
+        }
         case nsIDataType::VTYPE_WSTRING_SIZE_IS:
-            if (NS_FAILED(variant->GetAsWStringWithSize(&size,
-                                                        (PRUnichar**)&xpctvar.val.p)))
+        {
+            PRUnichar *pwc;
+            uint32_t size;
+            if (NS_FAILED(variant->GetAsWStringWithSize(&size, &pwc)))
                 return false;
-            xpctvar.type = (uint8_t)TD_PWSTRING_SIZE_IS;
-            xpctvar.SetValNeedsCleanup();
-            break;
+            bool success = XPCConvert::NativeStringWithSize2JS(cx, pJSVal, (const void*)&pwc,
+                                                               TD_PWSTRING_SIZE_IS, size, pErr);
+            nsMemory::Free(pwc);
+            return success;
+        }
         case nsIDataType::VTYPE_INTERFACE:
         case nsIDataType::VTYPE_INTERFACE_IS:
         {
+            nsISupports *pi;
             nsID* piid;
-            if (NS_FAILED(variant->GetAsInterface(&piid, &xpctvar.val.p)))
+            if (NS_FAILED(variant->GetAsInterface(&piid, (void **)&pi)))
                 return false;
 
             iid = *piid;
             nsMemory::Free((char*)piid);
 
-            xpctvar.type = (uint8_t)TD_INTERFACE_IS_TYPE;
-            if (xpctvar.val.p)
-                xpctvar.SetValNeedsCleanup();
-            break;
+            bool success = XPCConvert::NativeData2JS(lccx, pJSVal, (const void*)&pi,
+                                                     TD_INTERFACE_IS_TYPE, &iid, pErr);
+            if (pi)
+                pi->Release();
+            return success;
         }
         case nsIDataType::VTYPE_ARRAY:
         {
             nsDiscriminatedUnion du;
             nsVariant::Initialize(&du);
             nsresult rv;
 
             rv = variant->GetAsArray(&du.u.array.mArrayType,
                                      &du.u.array.mArrayInterfaceID,
                                      &du.u.array.mArrayCount,
                                      &du.u.array.mArrayValue);
             if (NS_FAILED(rv))
                 return false;
 
             // must exit via VARIANT_DONE from here on...
             du.mType = nsIDataType::VTYPE_ARRAY;
-            success = false;
+            bool success = false;
 
             nsXPTType conversionType;
             uint16_t elementType = du.u.array.mArrayType;
             const nsID* pid = nullptr;
 
             switch (elementType) {
                 case nsIDataType::VTYPE_INT8:
                 case nsIDataType::VTYPE_INT16:
@@ -628,42 +655,16 @@ VARIANT_DONE:
             return true;
         case nsIDataType::VTYPE_EMPTY:
             *pJSVal = JSVAL_NULL;
             return true;
         default:
             NS_ERROR("bad type in variant!");
             return false;
     }
-
-    // If we are here then we need to convert the data in the xpctvar.
-
-    if (xpctvar.type.TagPart() == TD_PSTRING_SIZE_IS ||
-        xpctvar.type.TagPart() == TD_PWSTRING_SIZE_IS) {
-        success = XPCConvert::NativeStringWithSize2JS(cx, pJSVal,
-                                                      (const void*)&xpctvar.val,
-                                                      xpctvar.type,
-                                                      size, pErr);
-    } else {
-        success = XPCConvert::NativeData2JS(lccx, pJSVal,
-                                            (const void*)&xpctvar.val,
-                                            xpctvar.type,
-                                            &iid, pErr);
-    }
-
-    // We may have done something in the above code that requires cleanup.
-    if (xpctvar.DoesValNeedCleanup()) {
-        if (type == nsIDataType::VTYPE_INTERFACE ||
-            type == nsIDataType::VTYPE_INTERFACE_IS)
-            ((nsISupports*)xpctvar.val.p)->Release();
-        else
-            nsMemory::Free((char*)xpctvar.val.p);
-    }
-
-    return success;
 }
 
 /***************************************************************************/
 /***************************************************************************/
 // XXX These default implementations need to be improved to allow for
 // some more interesting conversions.
 
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -2053,17 +2053,17 @@ XPCWrappedNative::InitTearOffJSObject(XP
     return true;
 }
 
 JSObject*
 XPCWrappedNative::GetSameCompartmentSecurityWrapper(JSContext *cx)
 {
     // Grab the current state of affairs.
     RootedObject flat(cx, GetFlatJSObject());
-    JSObject *wrapper = GetWrapper();
+    RootedObject wrapper(cx, GetWrapper());
 
     // If we already have a wrapper, it must be what we want.
     if (wrapper)
         return wrapper;
 
     // Chrome callers don't need same-compartment security wrappers.
     JSCompartment *cxCompartment = js::GetContextCompartment(cx);
     MOZ_ASSERT(cxCompartment == js::GetObjectCompartment(flat));
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -2026,25 +2026,25 @@ xpc_ActivateDebugMode()
     nsXPConnect::GetXPConnect()->SetDebugModeWhenPossible(true, true);
     nsXPConnect::CheckForDebugMode(rt->GetJSRuntime());
 }
 
 /* virtual */
 JSContext*
 nsXPConnect::GetCurrentJSContext()
 {
-    JSContext *cx = XPCJSRuntime::Get()->GetJSContextStack()->Peek();
+    JSContext *cx = GetRuntime()->GetJSContextStack()->Peek();
     return xpc_UnmarkGrayContext(cx);
 }
 
 /* virtual */
 JSContext*
 nsXPConnect::GetSafeJSContext()
 {
-    return XPCJSRuntime::Get()->GetJSContextStack()->GetSafeJSContext();
+    return GetRuntime()->GetJSContextStack()->GetSafeJSContext();
 }
 
 namespace xpc {
 namespace danger {
 
 NS_EXPORT_(bool)
 PushJSContext(JSContext *aCx)
 {
@@ -2368,24 +2368,24 @@ nsXPConnect::SetDebugModeWhenPossible(bo
     if (!mode && allowSyncDisable)
         CheckForDebugMode(mRuntime->GetJSRuntime());
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::GetTelemetryValue(JSContext *cx, jsval *rval)
 {
-    JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
+    RootedObject obj(cx, JS_NewObject(cx, NULL, NULL, NULL));
     if (!obj)
         return NS_ERROR_OUT_OF_MEMORY;
 
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT;
 
     size_t i = JS_SetProtoCalled(cx);
-    jsval v = DOUBLE_TO_JSVAL(i);
+    RootedValue v(cx, DOUBLE_TO_JSVAL(i));
     if (!JS_DefineProperty(cx, obj, "setProto", v, NULL, NULL, attrs))
         return NS_ERROR_OUT_OF_MEMORY;
 
     i = JS_GetCustomIteratorCount(cx);
     v = DOUBLE_TO_JSVAL(i);
     if (!JS_DefineProperty(cx, obj, "customIter", v, NULL, NULL, attrs))
         return NS_ERROR_OUT_OF_MEMORY;
 
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -278,17 +278,17 @@ WrapperFactory::PrepareForWrapping(JSCon
         newwn->SetSet(unionSet);
     }
 
     return DoubleWrap(cx, obj, flags);
 }
 
 #ifdef DEBUG
 static void
-DEBUG_CheckUnwrapSafety(JSObject *obj, js::Wrapper *handler,
+DEBUG_CheckUnwrapSafety(HandleObject obj, js::Wrapper *handler,
                         JSCompartment *origin, JSCompartment *target)
 {
     if (AccessCheck::isChrome(target) || xpc::IsUniversalXPConnectEnabled(target)) {
         // If the caller is chrome (or effectively so), unwrap should always be allowed.
         MOZ_ASSERT(handler->isSafeToUnwrap());
     } else if (WrapperFactory::IsComponentsObject(obj)) {
         // The Components object that is restricted regardless of origin.
         MOZ_ASSERT(!handler->isSafeToUnwrap());
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/722888-1-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <body>
+    <div style="-moz-column-count: 2;
+                height: 50px;
+                width: 100px;
+                background: yellow">
+      a
+    </div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/722888-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+  <head>
+    <script>
+      function tweak() {
+        var b = document.getElementsByTagName("span")[0];
+        b.parentNode.removeChild(b);
+        document.documentElement.removeAttribute("class");
+      }
+      window.addEventListener("MozReftestInvalidate", tweak, false);
+    </script>
+  </head>
+  <body>
+    <div style="-moz-column-count: 2;
+                height: 50px;
+                width: 100px;
+                background: yellow">
+      <span>0 1 2 3 4 5 6 7 8 9 0</span>a
+    </div>
+  </body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1691,16 +1691,17 @@ needs-focus != 703186-1.html 703186-2.ht
 == 713856-static.html  713856-ref.html
 == 713856-dynamic.html 713856-ref.html
 == 714519-1-as.html 714519-1-ref.html
 == 714519-1-q.html 714519-1-ref.html
 == 714519-2-as.html 714519-2-ref.html
 == 714519-2-q.html 714519-2-ref.html
 skip-if(B2G) fuzzy-if(true,1,21) fuzzy-if(cocoaWidget,1,170) fuzzy-if(Android&&browserIsRemote,7,157) fails-if(Android&&!browserIsRemote) == 718521.html 718521-ref.html # bug 760270 # bug 773482
 == 720987.html 720987-ref.html
+== 722888-1.html 722888-1-ref.html
 == 722923-1.html 722923-1-ref.html
 == 723484-1.html 723484-1-ref.html
 == 728983-1.html 728983-1-ref.html
 skip-if(B2G) == 729143-1.html 729143-1-ref.html
 == 731521-1.html 731521-1-ref.html
 needs-focus == 731726-1.html 731726-1-ref.html
 == 735481-1.html 735481-1-ref.html
 == 745934-1.html 745934-1-ref.html
--- a/mobile/android/app/recommended-addons.json
+++ b/mobile/android/app/recommended-addons.json
@@ -1,15 +1,15 @@
 {
     "addons": [{
         "id": "fullscreen@mbrubeck.limpet.net",
         "name": "Full Screen",
         "version": "3.4",
         "iconURL": "https://addons.cdn.mozilla.net/img/uploads/addon_icons/252/252573-32.png?modified=1354183977",
-        "homepageURL": "https://addons.mozilla.org/en-US/android/addon/full-screen-252573/?src=api"
+        "learnmoreURL": "https://addons.mozilla.org/en-US/android/addon/full-screen-252573/?src=api"
     }, {
         "id": "cloudviewer@starkravingfinkle.org",
         "name": "Cloud Viewer",
         "version": "2.1",
         "iconURL": "https://addons.cdn.mozilla.net/img/uploads/addon_icons/295/295895-32.png?modified=1353947644",
-        "homepageURL": "https://addons.mozilla.org/en-US/android/addon/cloud-viewer/?src=api"
+        "learnmoreURL": "https://addons.mozilla.org/en-US/android/addon/cloud-viewer/?src=api"
     }]
 }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -299,16 +299,20 @@ else
 ANDROID_VERSION_CODE=$(shell echo $$((`cat $(DEPTH)/config/buildid | cut -c1-10` - 1)))
 endif
 endif
 
 UA_BUILDID=$(shell echo $(ANDROID_VERSION_CODE) | cut -c1-8)
 
 MOZ_BUILD_TIMESTAMP=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-timestamp`)
 
+ifdef MOZ_UPDATER
+DEFINES +=  -DMOZ_UPDATER=$(MOZ_UPDATER)
+endif
+
 # Mangle our package name to avoid Bug 750548.
 DEFINES += \
   -DMANGLED_ANDROID_PACKAGE_NAME=$(subst fennec,f3nn3c,$(ANDROID_PACKAGE_NAME)) \
   -DANDROID_PACKAGE_NAME=$(ANDROID_PACKAGE_NAME) \
   -DANDROID_CPU_ARCH=$(ANDROID_CPU_ARCH) \
   -DMOZ_APP_DISPLAYNAME="$(MOZ_APP_DISPLAYNAME)" \
   -DMOZ_APP_NAME=$(MOZ_APP_NAME) \
   -DMOZ_APP_VERSION=$(MOZ_APP_VERSION) \
@@ -319,17 +323,16 @@ DEFINES += \
   -DMOZ_UPDATE_CHANNEL=$(MOZ_UPDATE_CHANNEL) \
   -DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
   -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
   -DUA_BUILDID=$(UA_BUILDID) \
   -DMOZ_APP_BASENAME=$(MOZ_APP_BASENAME) \
   -DMOZ_APP_BUILDID=$(MOZ_APP_BUILDID) \
   -DMOZ_APP_ABI=$(TARGET_XPCOM_ABI) \
   -DMOZ_BUILD_TIMESTAMP=$(MOZ_BUILD_TIMESTAMP) \
-  -DMOZ_UPDATER=$(MOZ_UPDATER) \
   -DOS_TARGET=$(OS_TARGET) \
   $(NULL)
 
 ifdef MOZ_PKG_SPECIAL
 DEFINES += -DMOZ_PKG_SPECIAL=$(MOZ_PKG_SPECIAL)
 endif
 
 ifdef MOZ_LINKER_EXTRACT
--- a/mobile/android/base/tests/testAddSearchEngine.java.in
+++ b/mobile/android/base/tests/testAddSearchEngine.java.in
@@ -4,40 +4,40 @@ package @ANDROID_PACKAGE_NAME@.tests;
 import @ANDROID_PACKAGE_NAME@.*;
 import android.view.View;
 import android.widget.ListAdapter;
 import android.widget.ListView;
 import java.util.ArrayList;
 
 /**
  * Test adding a search engine from an input field context menu
- * Starting a test
- * expected values.
+ * 1. Get the number of existing search engines
+ * 2. Load a page with a text field, open the context menu and add a search engine from the page
+ * 3. Get the number of search engines after adding the new one
  */
 public class testAddSearchEngine extends BaseTest {
 
+    final int MAX_TRIES = 5;
+
     @Override
     protected int getTestType() {
         return TEST_MOCHITEST;
     }
 
     public void testAddSearchEngine() {
         int height,width;
         final int initialNumSearchEngines;
         String blank = getAbsoluteUrl("/robocop/robocop_blank_01.html");
         String url = getAbsoluteUrl("/robocop/robocop_search.html");
 
         blockForGeckoReady();
         loadUrl(blank);
         waitForText("Browser Blank Page 01");
-
-        initialNumSearchEngines = getNumSearchEngines();
-        mActions.sendSpecialKey(Actions.SpecialKey.BACK);
-        waitForText("Browser Blank Page 01");
-
+        initialNumSearchEngines = getNumSearchEngines("Browser Blank Page 01");
+        
         loadUrl(url);
         waitForText("Robocop Search Engine");
 
         // Open the context menu for the intput field
         height = mDriver.getGeckoTop() + 10;
         width = mDriver.getGeckoLeft() + 20;
         mAsserter.dumpLog("Long Clicking at width = " + String.valueOf(width) + " and height = " + String.valueOf(height));
         mSolo.clickLongOnScreen(width,height);
@@ -56,35 +56,49 @@ public class testAddSearchEngine extends
             // If the OS is ICS the vkb is opened when the popup is triggered so we need to close it in order to click the OK button
             mActions.sendSpecialKey(Actions.SpecialKey.BACK);
             waitForText("OK"); // Make sure the OK button is visible
         }
         mSolo.clickOnButton("OK");
         mAsserter.ok(!mSolo.searchText("Add Search Engine"), "Adding the search engine", "The add serach engine pop-up has been cloesed");
 
         // Check that the number of search results has increased
-        mAsserter.is(getNumSearchEngines() ,initialNumSearchEngines + 1 , "The number of search results has increased");
+        mAsserter.is(getNumSearchEngines("Robocop Search Engine") ,initialNumSearchEngines + 1 , "The number of search results has increased");
     }
 
-    public int getNumSearchEngines() {
+    public int getNumSearchEngines(String waitText) {
         ArrayList<ListView> views;
         int searchEngineCount = 0;
+        int oldSearchEngineCount = -1;
 
-        // Start a search and wait for the search engine data to be displayed
-        Actions.EventExpecter enginesEventExpecter = mActions.expectGeckoEvent("SearchEngines:Data");
-        clickOnAwesomeBar();
-        waitForText("Bookmarks");
-        mActions.sendKeys("Firefox for Android");
-        enginesEventExpecter.blockForEvent();
+        for (int i = 0; i < MAX_TRIES; i++ ) {
+            // Start a search and wait for the search engine data to be displayed
+            Actions.EventExpecter enginesEventExpecter = mActions.expectGeckoEvent("SearchEngines:Data");
+            clickOnAwesomeBar();
+            waitForText("Bookmarks");
+            mActions.sendKeys("Firefox for Android");
+            enginesEventExpecter.blockForEvent();
 
-        views = mSolo.getCurrentListViews();
-        for (ListView view : views) {
-             ListAdapter adapter = view.getAdapter();
-             if (adapter != null) {
-                 searchEngineCount = adapter.getCount();
-             } else {
-                 searchEngineCount = -1;
-             }
+            views = mSolo.getCurrentListViews();
+            for (ListView view : views) {
+                 ListAdapter adapter = view.getAdapter();
+                 if (adapter != null) {
+                     searchEngineCount = adapter.getCount();
+                 } else {
+                     searchEngineCount = -1;
+                 }
+            }
+            // Close the Awesomescreen
+            mActions.sendSpecialKey(Actions.SpecialKey.BACK);
+            waitForText(waitText);
+            if (searchEngineCount == oldSearchEngineCount) {
+                mAsserter.isnot(searchEngineCount, -1, "Search engine number could be determined correctly");
+                return searchEngineCount;
+            } else {
+                oldSearchEngineCount = searchEngineCount;
+            }
         }
-        mAsserter.isnot(searchEngineCount, -1, "There should be search engines displayed when text is entered");
-        return searchEngineCount;
+
+    // Since the number of Search Engine count was different on every try fail the test
+    mAsserter.is(searchEngineCount,oldSearchEngineCount, "The search engine count could not be established correctly");
+    return searchEngineCount;
     }
 }
--- a/mobile/android/base/widget/AddonsSection.java
+++ b/mobile/android/base/widget/AddonsSection.java
@@ -199,17 +199,19 @@ public class AddonsSection extends About
 
         Drawable drawable = mContext.getResources().getDrawable(R.drawable.ic_addons_empty);
         drawable.setBounds(sIconBounds);
         row.setCompoundDrawables(drawable, null, null, null);
 
         String iconUrl = addonJSON.getString("iconURL");
         String pageUrl = getPageUrlFromIconUrl(iconUrl);
 
-        final String homepageUrl = addonJSON.getString("homepageURL");
+        // homepageURL may point to non-AMO installs. For now we use learnmoreURL instead
+        // which is more likely to point to a mobile AMO page
+        final String homepageUrl = addonJSON.getString("learnmoreURL");
         row.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if (mUriLoadListener != null)
                     mUriLoadListener.onAboutHomeUriLoad(homepageUrl);
             }
         });
         row.setOnKeyListener(GamepadUtils.getClickDispatcher());
--- a/mobile/android/components/AddonUpdateService.js
+++ b/mobile/android/components/AddonUpdateService.js
@@ -170,17 +170,17 @@ var RecommendedSearchResults = {
         addons: []
       };
 
       addons.forEach(function(aAddon) {
         json.addons.push({
           id: aAddon.id,
           name: aAddon.name,
           version: aAddon.version,
-          homepageURL: aAddon.homepageURL,
+          learnmoreURL: aAddon.learnmoreURL,
           iconURL: aAddon.iconURL
         })
       });
 
       let file = self._getFile();
       self._writeFile(file, JSON.stringify(json));
     });
   },
--- a/mobile/android/config/mozconfigs/android-armv6/debug
+++ b/mobile/android/config/mozconfigs/android-armv6/debug
@@ -4,18 +4,25 @@
 ac_add_options --enable-debug
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-arch=armv6
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 export MOZ_PKG_SPECIAL=armv6
--- a/mobile/android/config/mozconfigs/android-armv6/l10n-nightly
+++ b/mobile/android/config/mozconfigs/android-armv6/l10n-nightly
@@ -10,18 +10,25 @@ ac_add_options --disable-tests
 ac_add_options --enable-js-diagnostics
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-arch=armv6
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android-armv6/l10n-release
+++ b/mobile/android/config/mozconfigs/android-armv6/l10n-release
@@ -7,18 +7,25 @@ ac_add_options --with-l10n-base=..
 ac_add_options --disable-tests
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-arch=armv6
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android-armv6/nightly
+++ b/mobile/android/config/mozconfigs/android-armv6/nightly
@@ -1,18 +1,25 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-arch=armv6
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
--- a/mobile/android/config/mozconfigs/android-armv6/release
+++ b/mobile/android/config/mozconfigs/android-armv6/release
@@ -1,18 +1,25 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
 ac_add_options --with-arch=armv6
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android-noion/nightly
+++ b/mobile/android/config/mozconfigs/android-noion/nightly
@@ -1,17 +1,24 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 # IonMonkey disabled in bug 789373
 ac_add_options --disable-ion
 
--- a/mobile/android/config/mozconfigs/android-x86/debug
+++ b/mobile/android/config/mozconfigs/android-x86/debug
@@ -3,18 +3,25 @@
 # Global options
 ac_add_options --enable-debug
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=i386-linux-android
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-x86-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/mobile/android/config/mozconfigs/android-x86/l10n-nightly
+++ b/mobile/android/config/mozconfigs/android-x86/l10n-nightly
@@ -9,18 +9,25 @@ ac_add_options --disable-tests
 # Mozilla-Central nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=i386-linux-android
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-x86-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android-x86/l10n-release
+++ b/mobile/android/config/mozconfigs/android-x86/l10n-release
@@ -6,18 +6,25 @@ ac_add_options --with-l10n-base=..
 # Global options
 ac_add_options --disable-tests
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=i386-linux-android
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-x86-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android-x86/nightly
+++ b/mobile/android/config/mozconfigs/android-x86/nightly
@@ -1,17 +1,24 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=i386-linux-android
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-x86-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
--- a/mobile/android/config/mozconfigs/android-x86/release
+++ b/mobile/android/config/mozconfigs/android-x86/release
@@ -1,17 +1,24 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=i386-linux-android
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-x86-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android/debug
+++ b/mobile/android/config/mozconfigs/android/debug
@@ -3,18 +3,25 @@
 # Global options
 ac_add_options --enable-debug
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
 export MOZ_TELEMETRY_REPORTING=1
 
--- a/mobile/android/config/mozconfigs/android/l10n-nightly
+++ b/mobile/android/config/mozconfigs/android/l10n-nightly
@@ -9,18 +9,25 @@ ac_add_options --disable-tests
 # Mozilla-Central nightlies only since this has a cost in performance
 ac_add_options --enable-js-diagnostics
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android/l10n-release
+++ b/mobile/android/config/mozconfigs/android/l10n-release
@@ -6,18 +6,25 @@ ac_add_options --with-l10n-base=..
 # Global options
 ac_add_options --disable-tests
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android/nightly
+++ b/mobile/android/config/mozconfigs/android/nightly
@@ -1,17 +1,24 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 ac_add_options --enable-profiling
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
--- a/mobile/android/config/mozconfigs/android/release
+++ b/mobile/android/config/mozconfigs/android/release
@@ -1,17 +1,24 @@
 . "$topsrcdir/mobile/android/config/mozconfigs/common"
 
 # Build Fennec
 ac_add_options --enable-application=mobile/android
 
 # Android
 ac_add_options --target=arm-linux-androideabi
-ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
-ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+
+if test `uname -m` = 'x86_64'; then
+  ac_add_options --with-android-ndk="$topsrcdir/android-ndk-r8e-arm-gcc4.6"
+  ac_add_options --with-android-sdk="$topsrcdir/android-sdk-linux/platforms/android-16"
+else
+  ac_add_options --with-android-ndk="/tools/android-ndk-r8c"
+  ac_add_options --with-android-sdk="/tools/android-sdk-r16/platforms/android-16"
+fi
+
 ac_add_options --with-android-gnu-compiler-version=4.6
 ac_add_options --with-android-version=9
 ac_add_options --with-system-zlib
 ac_add_options --enable-updater
 ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
 
 export JAVA_HOME=/tools/jdk6
 export MOZILLA_OFFICIAL=1
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/tooltool-manifests/android-armv6/releng.manifest
@@ -0,0 +1,20 @@
+[
+{
+"size": 75650889,
+"digest": "225ee9bb331a08ec496acc47ef8a1e9d9a21c290bfe22271cdc6ee2d10befecc372faf2a718e3e0ad5c36938a9b94f5d6b6b2d999880bc476c576856f1c99812",
+"algorithm": "sha512",
+"filename": "android-ndk-r8e-arm-gcc4.6.tar.bz2"
+},
+{
+"size": 147088338,
+"digest": "56fc5dc8c5e8cf46605667db153477e32bdf724bfa989663bbbed7e52ee47a4cb8cf7f8bf49b82b7e15fc89efe59a0f1656ca4b7a4ffcc8de31f9b2229549dab",
+"algorithm": "sha512",
+"filename": "android-sdk-16.tar.bz2"
+},
+{
+"size": 269,
+"digest": "0c21a793d87773b3ed82d141bea33869a6b647e5dddab3ecfee1738751e5c8d3d0b9ca45d7453ef88d335949c73f935c37de3521978b318ca51d8369be894b92",
+"algorithm": "sha512",
+"filename": "setup.sh"
+}
+]
deleted file mode 100644
--- a/mobile/android/config/tooltool-manifests/android-x86/android/releng.manifest
+++ /dev/null
@@ -1,1 +0,0 @@
-[]
new file mode 100644
--- /dev/null
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -0,0 +1,20 @@
+[
+{
+"size": 78190771,
+"digest": "57c69c5f55722f197ca6a5bd7d366d3562df474da8d98a6df753830c8234c269c1a174e518168b801e39290db081ef192a8fa24fcc5133039b1ad489dfe45f48",
+"algorithm": "sha512",
+"filename": "android-ndk-r8e-x86-gcc4.6.tar.bz2"
+},
+{
+"size": 147088338,
+"digest": "56fc5dc8c5e8cf46605667db153477e32bdf724bfa989663bbbed7e52ee47a4cb8cf7f8bf49b82b7e15fc89efe59a0f1656ca4b7a4ffcc8de31f9b2229549dab",
+"algorithm": "sha512",
+"filename": "android-sdk-16.tar.bz2"
+},
+{
+"size": 273,
+"digest": "3db7c58c2002e96e6c0c34468f3349ca7de17c7473e07edec054ee3083ae39e6ead0e2a8f9177c1ce5891539073280e1afda1a6518f78e999dddc5d3f02a1e18",
+"algorithm": "sha512",
+"filename": "setup.sh"
+}
+]
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -1,1 +1,20 @@
-[]
+[
+{
+"size": 75650889,
+"digest": "225ee9bb331a08ec496acc47ef8a1e9d9a21c290bfe22271cdc6ee2d10befecc372faf2a718e3e0ad5c36938a9b94f5d6b6b2d999880bc476c576856f1c99812",
+"algorithm": "sha512",
+"filename": "android-ndk-r8e-arm-gcc4.6.tar.bz2"
+},
+{
+"size": 147088338,
+"digest": "56fc5dc8c5e8cf46605667db153477e32bdf724bfa989663bbbed7e52ee47a4cb8cf7f8bf49b82b7e15fc89efe59a0f1656ca4b7a4ffcc8de31f9b2229549dab",
+"algorithm": "sha512",
+"filename": "android-sdk-16.tar.bz2"
+},
+{
+"size": 269,
+"digest": "0c21a793d87773b3ed82d141bea33869a6b647e5dddab3ecfee1738751e5c8d3d0b9ca45d7453ef88d335949c73f935c37de3521978b318ca51d8369be894b92",
+"algorithm": "sha512",
+"filename": "setup.sh"
+}
+]
--- a/mobile/android/locales/en-US/chrome/aboutDownloads.properties
+++ b/mobile/android/locales/en-US/chrome/aboutDownloads.properties
@@ -1,14 +1,14 @@
 # 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/.
 
 # LOCALIZATION NOTE (downloadMessage.deleteAll):
-# Semi-colon list of plural forms. See:
+# Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 downloadMessage.deleteAll=Delete this download?;Delete #1 downloads?
 
 downloadAction.deleteAll=Delete All
 
 downloadState.downloading=Downloading…
 downloadState.canceled=Canceled
 downloadState.failed=Failed
--- a/mobile/android/locales/en-US/chrome/browser.properties
+++ b/mobile/android/locales/en-US/chrome/browser.properties
@@ -103,17 +103,17 @@ desktopNotification.ask=Allow %S to use 
 # LOCALIZATION NOTE (desktopNotification.useNotifications): Label that will be
 # used in site settings dialog.
 desktopNotification.useNotifications=Use Notifications
 # LOCALIZATION NOTE (desktopNotification.dontAskAgain): This label appears next to a
 # checkbox to indicate whether or not the user wants to make a permanent decision.
 desktopNotification.dontAskAgain=Don't ask again for this site
 
 # New Tab Popup
-# LOCALIZATION NOTE (newtabpopup, newprivatetabpopup): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (newtabpopup, newprivatetabpopup): Semicolon-separated list of plural forms.
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # #1 number of tabs
 newtabpopup.opened=New tab opened;#1 new tabs opened
 newprivatetabpopup.opened=New private tab opened;#1 new private tabs opened
 
 # Offline web applications
 offlineApps.ask=Allow %S to store data on your device for offline use?
 offlineApps.dontAskAgain=Don't ask again for this site
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -266,32 +266,35 @@ jstestbrowser:
 
 GARBAGE += $(addsuffix .log,$(MOCHITESTS) reftest crashtest jstestbrowser)
 
 # Execute all xpcshell tests in the directories listed in the manifest.
 # See also config/rules.mk 'xpcshell-tests' target for local execution.
 # Usage: |make [TEST_PATH=...] [EXTRA_TEST_ARGS=...] xpcshell-tests|.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
-	  -I$(topsrcdir)/build -I$(DEPTH)/_tests/mozbase/mozinfo \
+	  -I$(DEPTH)/build \
+	  -I$(topsrcdir)/build \
+	  -I$(DEPTH)/_tests/mozbase/mozinfo \
 	  $(topsrcdir)/testing/xpcshell/runxpcshelltests.py \
 	  --manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --no-logfiles \
 	  --tests-root-dir=$(call core_abspath,_tests/xpcshell) \
 	  --testing-modules-dir=$(call core_abspath,_tests/modules) \
 	  --xunit-file=$(call core_abspath,_tests/xpcshell/results.xml) \
 	  --xunit-suite-name=xpcshell \
           $(SYMBOLS_PATH) \
 	  $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell
 
 B2G_XPCSHELL = \
 	rm -f ./@.log && \
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
+	  -I$(DEPTH)/build \
 	  -I$(topsrcdir)/build \
 	  $(topsrcdir)/testing/xpcshell/runtestsb2g.py \
 	  --manifest=$(DEPTH)/_tests/xpcshell/xpcshell.ini \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
 	  --no-logfiles \
 	  --use-device-libs \
 	  --no-clean \
 	  --objdir=$(DEPTH) \
--- a/testing/xpcshell/Makefile.in
+++ b/testing/xpcshell/Makefile.in
@@ -52,19 +52,20 @@ libs::
 	$(INSTALL) $(srcdir)/xpcshell.ini $(DEPTH)/_tests/xpcshell
 	$(INSTALL) $(srcdir)/xpcshell_b2g.ini $(DEPTH)/_tests/xpcshell
 	$(INSTALL) $(srcdir)/xpcshell_android.ini $(DEPTH)/_tests/xpcshell
 	cp $(srcdir)/xpcshell.ini $(DEPTH)/_tests/xpcshell/all-test-dirs.list
 
 # Run selftests
 check::
 	OBJDIR=$(DEPTH) $(PYTHON) $(topsrcdir)/config/pythonpath.py \
-	  -I$(topsrcdir)/build -I$(topsrcdir)/testing/mozbase/mozinfo/mozinfo $(srcdir)/selftest.py
+	  -I$(DEPTH)/build -I$(topsrcdir)/build -I$(topsrcdir)/testing/mozbase/mozinfo/mozinfo $(srcdir)/selftest.py
 
 stage-package:
 	$(NSINSTALL) -D $(PKG_STAGE)/xpcshell/tests
 	@(cd $(topsrcdir)/testing/mozbase/mozinfo/mozinfo && tar $(TAR_CREATE_FLAGS) - $(MOZINFO_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -)
 	@(cd $(srcdir) && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -)
 	@(cd $(topsrcdir)/build && tar $(TAR_CREATE_FLAGS) - $(EXTRA_BUILD_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -)
 	@cp $(DEPTH)/mozinfo.json $(PKG_STAGE)/xpcshell
+	@cp $(DEPTH)/build/automation.py $(PKG_STAGE)/xpcshell
 	@(cd $(topsrcdir)/testing/mozbase/mozdevice/mozdevice && tar $(TAR_CREATE_FLAGS) - $(MOZDEVICE_FILES)) | (cd $(PKG_STAGE)/xpcshell && tar -xf -)
 	(cd $(DEPTH)/_tests/xpcshell/ && tar $(TAR_CREATE_FLAGS_QUIET) - *) | (cd $(PKG_STAGE)/xpcshell/tests && tar -xf -)
 	@(cd $(DIST)/bin/components && tar $(TAR_CREATE_FLAGS) - $(TEST_HARNESS_COMPONENTS)) | (cd $(PKG_STAGE)/bin/components && tar -xf -)
--- a/testing/xpcshell/mach_commands.py
+++ b/testing/xpcshell/mach_commands.py
@@ -42,16 +42,20 @@ class XPCShellRunner(MozbuildObject):
         manifest = os.path.join(self.topobjdir, '_tests', 'xpcshell',
             'xpcshell.ini')
 
         return self._run_xpcshell_harness(manifest=manifest, **kwargs)
 
     def run_test(self, test_file, debug=False, interactive=False,
         keep_going=False, shuffle=False):
         """Runs an individual xpcshell test."""
+        # TODO Bug 794506 remove once mach integrates with virtualenv.
+        build_path = os.path.join(self.topobjdir, 'build')
+        if build_path not in sys.path:
+            sys.path.append(build_path)
 
         if test_file == 'all':
             self.run_suite(debug=debug, interactive=interactive,
                 keep_going=keep_going, shuffle=shuffle)
             return
 
         path_arg = self._wrap_path_argument(test_file)
 
--- a/testing/xpcshell/runxpcshelltests.py
+++ b/testing/xpcshell/runxpcshelltests.py
@@ -5,24 +5,28 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import re, sys, os, os.path, logging, shutil, signal, math, time, traceback
 import xml.dom.minidom
 from glob import glob
 from optparse import OptionParser
 from subprocess import Popen, PIPE, STDOUT
 from tempfile import mkdtemp, gettempdir
+from threading import Timer
 import manifestparser
 import mozinfo
 import random
 import socket
 import time
 
+from automation import Automation, getGlobalLog, resetGlobalLog
 from automationutils import *
 
+HARNESS_TIMEOUT = 5 * 60
+
 # --------------------------------------------------------------
 # TODO: this is a hack for mozbase without virtualenv, remove with bug 849900
 #
 here = os.path.dirname(__file__)
 mozbase = os.path.realpath(os.path.join(os.path.dirname(here), 'mozbase'))
 
 try:
     import mozcrash
@@ -47,24 +51,23 @@ def parse_json(j):
 """ Control-C handling """
 gotSIGINT = False
 def markGotSIGINT(signum, stackFrame):
     global gotSIGINT
     gotSIGINT = True
 
 class XPCShellTests(object):
 
-    log = logging.getLogger()
+    log = getGlobalLog()
     oldcwd = os.getcwd()
 
-    def __init__(self, log=sys.stdout):
+    def __init__(self, log=None):
         """ Init logging and node status """
-        handler = logging.StreamHandler(log)
-        self.log.setLevel(logging.INFO)
-        self.log.addHandler(handler)
+        if log:
+            resetGlobalLog(log)
         self.nodeProc = None
 
     def buildTestList(self):
         """
           read the xpcshell.ini manifest and set self.alltests to be
           an array of test objects.
 
           if we are chunking tests, it will be done here as well
@@ -568,16 +571,20 @@ class XPCShellTests(object):
             testsuite.appendChild(testcase)
 
         testsuite.setAttribute("tests", str(total))
         testsuite.setAttribute("failures", str(failed))
         testsuite.setAttribute("skip", str(skipped))
 
         doc.writexml(fh, addindent="  ", newl="\n", encoding="utf-8")
 
+    def testTimeout(self, test, processPID):
+        self.log.error("TEST-UNEXPECTED-FAIL | %s | Test timed out" % test)
+        Automation().killAndGetStackNoScreenshot(processPID, self.appPath, None)
+
     def post_to_autolog(self, results, name):
         from moztest.results import TestContext, TestResult, TestResultCollection
         from moztest.output.autolog import AutologOutput
 
         context = TestContext(
             testgroup='b2g xpcshell testsuite',
             operating_system='android',
             arch='emulator',
@@ -878,16 +885,21 @@ class XPCShellTests(object):
         cmdT = self.buildCmdTestFile(name)
 
         args = self.xpcsRunArgs[:]
         if 'debug' in test:
             args.insert(0, '-d')
 
         completeCmd = cmdH + cmdT + args
 
+        testTimer = None
+        if not interactive and not self.debuggerInfo:
+            testTimer = Timer(HARNESS_TIMEOUT, lambda: self.testTimeout(name, proc.pid))
+            testTimer.start()
+
         proc = None
 
         try:
             self.log.info("TEST-INFO | %s | running test ..." % name)
             if verbose:
                 self.logCommand(name, completeCmd, test_dir)
 
             startTime = time.time()
@@ -901,17 +913,20 @@ class XPCShellTests(object):
             # - don't move this line above launchProcess, or child will inherit the SIG_IGN
             signal.signal(signal.SIGINT, markGotSIGINT)
             # |stderr == None| as |pStderr| was either |None| or redirected to |stdout|.
             stdout, stderr = self.communicate(proc)
             signal.signal(signal.SIGINT, signal.SIG_DFL)
 
             if interactive:
                 # Not sure what else to do here...
-                return True
+                return True, xunit_result
+
+            if testTimer:
+                testTimer.cancel()
 
             def print_stdout(stdout):
                 """Print stdout line-by-line to avoid overflowing buffers."""
                 self.log.info(">>>>>>>")
                 if (stdout):
                     for line in stdout.splitlines():
                         self.log.info(line)
                 self.log.info("<<<<<<<")
--- a/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
+++ b/toolkit/locales/en-US/chrome/mozapps/extensions/extensions.properties
@@ -8,17 +8,19 @@ aboutWindowCloseButton=Close
 #LOCALIZATION NOTE (aboutWindowVersionString) %S is the addon version
 aboutWindowVersionString=version %S
 #LOCALIZATION NOTE (aboutAddon) %S is the addon name
 aboutAddon=About %S
 
 #LOCALIZATION NOTE (uninstallNotice) %S is the add-on name
 uninstallNotice=%S has been removed.
 
-#LOCALIZATION NOTE (numReviews) #1 is the number of reviews
+#LOCALIZATION NOTE (numReviews): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the number of reviews
 numReviews=#1 review;#1 reviews
 
 #LOCALIZATION NOTE (dateUpdated) %S is the date the addon was last updated
 dateUpdated=Updated %S
 
 #LOCALIZATION NOTE (notification.incompatible) %1$S is the add-on name, %2$S is brand name, %3$S is application version
 notification.incompatible=%1$S is incompatible with %2$S %3$S.
 #LOCALIZATION NOTE (notification.blocked) %1$S is the add-on name
@@ -101,17 +103,19 @@ installFromFile.filterName=Add-ons
 
 uninstallAddonTooltip=Uninstall this add-on
 uninstallAddonRestartRequiredTooltip=Uninstall this add-on (restart required)
 enableAddonTooltip=Enable this add-on
 enableAddonRestartRequiredTooltip=Enable this add-on (restart required)
 disableAddonTooltip=Disable this add-on
 disableAddonRestartRequiredTooltip=Disable this add-on (restart required)
 
-#LOCALIZATION NOTE (showAllSearchResults) #1 is the total number of search results
+#LOCALIZATION NOTE (showAllSearchResults): Semicolon-separated list of plural forms.
+# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
+# #1 is the total number of search results
 showAllSearchResults=See one result;See all #1 results
 
 #LOCALIZATION NOTE (addon.purchase.label) displayed on a button in the list
 # view, %S is the price of the add-on including currency symbol
 addon.purchase.label=Purchase for %S…
 addon.purchase.tooltip=Visit the add-ons gallery to purchase this add-on
 #LOCALIZATION NOTE (cmd.purchaseAddon.label) displayed on a button in the detail
 # view, %S is the price of the add-on including currency symbol
--- a/toolkit/mozapps/extensions/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/AddonRepository.jsm
@@ -217,16 +217,21 @@ AddonSearchResult.prototype = {
   screenshots: null,
 
   /**
    * The homepage for the add-on
    */
   homepageURL: null,
 
   /**
+   * The homepage for the add-on
+   */
+  learnmoreURL: null,
+
+  /**
    * The support URL for the add-on
    */
   supportURL: null,
 
   /**
    * The contribution url of the add-on
    */
   contributionURL: null,
@@ -1068,17 +1073,18 @@ this.AddonRepository = {
 
             if (previewNode.getAttribute("primary") == 1)
               addon.screenshots.unshift(screenshot);
             else
               addon.screenshots.push(screenshot);
           }
           break;
         case "learnmore":
-          addon.homepageURL = addon.homepageURL || this._getTextContent(node);
+          addon.learnmoreURL = this._getTextContent(node);
+          addon.homepageURL = addon.homepageURL || addon.learnmoreURL;
           break;
         case "contribution_data":
           let meetDevelopers = this._getDescendantTextContent(node, "meet_developers");
           let suggestedAmount = this._getDescendantTextContent(node, "suggested_amount");
           if (meetDevelopers != null) {
             addon.contributionURL = meetDevelopers;
             addon.contributionAmount = suggestedAmount;
           }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository.js
@@ -67,16 +67,17 @@ var GET_RESULTS = [{
                             thumbnailHeight: 150,
                             caption:         "Caption 1 - 1"
                           }, {
                             url:          BASE_URL + "/full2-1.png",
                             thumbnailURL: BASE_URL + "/thumbnail2-1.png",
                             caption:      "Caption 2 - 1"
                           }],
   homepageURL:            BASE_URL + "/learnmore1.html",
+  learnmoreURL:           BASE_URL + "/learnmore1.html",
   supportURL:             BASE_URL + "/support1.html",
   contributionURL:        BASE_URL + "/meetDevelopers1.html",
   contributionAmount:     "$11.11",
   averageRating:          4,
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/review1.html",
   totalDownloads:         2222,
   weeklyDownloads:        3333,
@@ -146,16 +147,17 @@ var SEARCH_RESULTS = [{
                             thumbnailURL: BASE_URL + "/thumbnail1-2.png"
                           }, {
                             url:          BASE_URL + "/full2-2.png",
                             thumbnailURL: BASE_URL + "/thumbnail2-2.png",
                             caption:      "Caption 2"
                           }],
   homepageURL:            BASE_URL + "/learnmore2.html",
   supportURL:             BASE_URL + "/support2.html",
+  learnmoreURL:           BASE_URL + "/learnmore2.html",
   contributionURL:        BASE_URL + "/meetDevelopers2.html",
   contributionAmount:     null,
   repositoryStatus:       4,
   sourceURI:              BASE_URL + "/test2.xpi"
 }, {
   id:                     "test3@tests.mozilla.org",
   type:                   "theme",
   version:                "1.3",
@@ -185,16 +187,17 @@ var SEARCH_RESULTS = [{
                             caption:      "Caption 2 - 3"
                           }, {
                             url:          BASE_URL + "/full3-3.png",
                             thumbnailURL: BASE_URL + "/thumbnail3-3.png",
                             caption:      "Caption 3 - 3"
                           }],
   homepageURL:            BASE_URL + "/homepage3.html",
   supportURL:             BASE_URL + "/support3.html",
+  learnmoreURL:           BASE_URL + "/learnmore3.html",
   contributionURL:        BASE_URL + "/meetDevelopers3.html",
   contributionAmount:     "$11.11",
   averageRating:          2,
   reviewCount:            1111,
   reviewURL:              BASE_URL + "/review3.html",
   totalDownloads:         2222,
   weeklyDownloads:        3333,
   dailyUsers:             4444,
--- a/widget/windows/TaskbarPreview.cpp
+++ b/widget/windows/TaskbarPreview.cpp
@@ -16,16 +16,17 @@
 #include <nsIObserverService.h>
 #include <nsServiceManagerUtils.h>
 
 #include "nsUXThemeData.h"
 #include "nsWindow.h"
 #include "nsAppShell.h"
 #include "TaskbarPreviewButton.h"
 #include "WinUtils.h"
+#include "gfxWindowsPlatform.h"
 
 #include <nsIBaseWindow.h>
 #include <nsICanvasRenderingContextInternal.h>
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include <imgIContainer.h>
 #include <nsIDocShell.h>
 
 #include "mozilla/Telemetry.h"
@@ -285,17 +286,21 @@ TaskbarPreview::WndProc(UINT nMsg, WPARA
         nsresult rv;
         rv = mController->GetWidth(&width);
         if (NS_FAILED(rv))
           break;
         rv = mController->GetHeight(&height);
         if (NS_FAILED(rv))
           break;
 
-        DrawBitmap(width, height, true);
+        double scale = nsIWidget::DefaultScaleOverride();
+        if (scale <= 0.0)
+          scale = gfxWindowsPlatform::GetPlatform()->GetDPIScale();
+
+        DrawBitmap(NSToIntRound(scale * width), NSToIntRound(scale * height), true);
       }
       break;
   }
   return ::DefWindowProcW(PreviewWindow(), nMsg, wParam, lParam);
 }
 
 bool
 TaskbarPreview::CanMakeTaskbarCalls() {