Merge cvs-trunk-mirror -> mozilla-central
authorbenjamin@smedbergs.us
Thu, 13 Mar 2008 10:32:07 -0400
changeset 13007 8f4b429275a5e05b3baa46a147237f53fe26b208
parent 12958 53dea32196b947f4786a8ef1795cb52541c1a4d0 (current diff)
parent 13006 9d654e6a00086e093ca780c2cfd900ff08911dca (diff)
child 13024 5be6ee5eedaffe5c440670f908dd8d6a2f1b22ab
push idunknown
push userunknown
push dateunknown
milestone2.0a1pre
Merge cvs-trunk-mirror -> mozilla-central
config/rules.mk
js/src/js.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jscntxt.cpp
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsstr.cpp
js/src/jsxml.cpp
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -695,27 +695,32 @@ function BrowserStartup()
   var sidebarSplitter;
   if (window.opener && !window.opener.closed) {
     var openerFindBar = window.opener.gFindBar;
     if (openerFindBar && !openerFindBar.hidden &&
         openerFindBar.findMode == gFindBar.FIND_NORMAL)
       gFindBar.open();
 
     var openerSidebarBox = window.opener.document.getElementById("sidebar-box");
+    // If the opener had a sidebar, open the same sidebar in our window.
     // The opener can be the hidden window too, if we're coming from the state
     // where no windows are open, and the hidden window has no sidebar box.
     if (openerSidebarBox && !openerSidebarBox.hidden) {
       var sidebarBox = document.getElementById("sidebar-box");
       var sidebarTitle = document.getElementById("sidebar-title");
       sidebarTitle.setAttribute("value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
       sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
       var sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
       sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
+      // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on the
+      // <browser id="sidebar">. This lets us delay the actual load until
+      // delayedStartup().
       sidebarBox.setAttribute("src", window.opener.document.getElementById("sidebar").getAttribute("src"));
       gMustLoadSidebar = true;
+
       sidebarBox.hidden = false;
       sidebarSplitter = document.getElementById("sidebar-splitter");
       sidebarSplitter.hidden = false;
       document.getElementById(sidebarCmd).setAttribute("checked", "true");
     }
   }
   else {
     var box = document.getElementById("sidebar-box");
@@ -1940,21 +1945,21 @@ function checkForDirectoryListing()
 {
   if ( "HTTPIndex" in content &&
        content.HTTPIndex instanceof Components.interfaces.nsIHTTPIndex ) {
     content.wrappedJSObject.defaultCharacterset =
       getMarkupDocumentViewer().defaultCharacterSet;
   }
 }
 
-function URLBarSetURI(aURI) {
+function URLBarSetURI(aURI, aMustUseURI) {
   var value = getBrowser().userTypedValue;
   var state = "invalid";
 
-  if (!value) {
+  if (!value || aMustUseURI) {
     if (aURI) {
       // If the url has "wyciwyg://" as the protocol, strip it off.
       // Nobody wants to see it on the urlbar for dynamically generated
       // pages.
       if (!gURIFixup)
         gURIFixup = Cc["@mozilla.org/docshell/urifixup;1"]
                       .getService(Ci.nsIURIFixup);
       try {
@@ -2316,16 +2321,32 @@ function BrowserOnCommand(event) {
             content.location = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
                               .getService(Components.interfaces.nsIURLFormatter)
                               .formatURLPref("browser.safebrowsing.warning.infoURL");
           } catch (e) {
             Components.utils.reportError("Couldn't get phishing info URL: " + e);
           }
         }
       }
+      else if (ot == errorDoc.getElementById('ignoreWarningButton')) {
+        // Allow users to override and continue through to the site,
+        // but add a notify bar as a reminder, so that they don't lose
+        // track after, e.g., tab switching.
+        gBrowser.loadURIWithFlags(content.location.href,
+                                  nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
+                                  null, null, null);
+        var notificationBox = gBrowser.getNotificationBox();
+        notificationBox.appendNotification(
+          errorDoc.title, /* Re-use the error page's title, e.g. "Reported Web Forgery!" */
+          "blocked-badware-page",
+          "chrome://global/skin/icons/blacklist_favicon.png",
+          notificationBox.PRIORITY_CRITICAL_HIGH,
+          null
+        );
+      }
     }
 }
 
 /**
  * Re-direct the browser to a known-safe page.  This function is
  * used when, for example, the user browses to a known malware page
  * and is presented with about:blocked.  The "Get me out of here!"
  * button should take the user to the default start page so that even
@@ -4472,16 +4493,18 @@ function toggleSidebar(commandID, forceO
   if (sidebarBroadcaster.getAttribute("checked") == "true") {
     if (!forceOpen) {
       sidebarBroadcaster.removeAttribute("checked");
       sidebarBox.setAttribute("sidebarcommand", "");
       sidebarTitle.value = "";
       sidebarBox.hidden = true;
       sidebarSplitter.hidden = true;
       content.focus();
+    } else {
+      fireSidebarFocusedEvent();
     }
     return;
   }
 
   // now we need to show the specified sidebar
 
   // ..but first update the 'checked' state of all sidebar broadcasters
   var broadcasters = document.getElementsByAttribute("group", "sidebar");
@@ -4499,23 +4522,53 @@ function toggleSidebar(commandID, forceO
 
   sidebarBox.hidden = false;
   sidebarSplitter.hidden = false;
 
   var url = sidebarBroadcaster.getAttribute("sidebarurl");
   var title = sidebarBroadcaster.getAttribute("sidebartitle");
   if (!title)
     title = sidebarBroadcaster.getAttribute("label");
-  sidebar.setAttribute("src", url);
+  sidebar.setAttribute("src", url); // kick off async load
   sidebarBox.setAttribute("sidebarcommand", sidebarBroadcaster.id);
   sidebarTitle.value = title;
 
-  // This is used because we want to delay sidebar load a bit
-  // when a browser window opens. See delayedStartup()
+  // We set this attribute here in addition to setting it on the <browser>
+  // element itself, because the code in BrowserShutdown persists this
+  // attribute, not the "src" of the <browser id="sidebar">. The reason it
+  // does that is that we want to delay sidebar load a bit when a browser
+  // window opens. See delayedStartup().
   sidebarBox.setAttribute("src", url);
+
+  if (sidebar.contentDocument.location.href != url)
+    sidebar.addEventListener("load", sidebarOnLoad, true);
+  else // older code handled this case, so we do it too
+    fireSidebarFocusedEvent();
+}
+
+function sidebarOnLoad(event) {
+  var sidebar = document.getElementById("sidebar");
+  sidebar.removeEventListener("load", sidebarOnLoad, true);
+  // We're handling the 'load' event before it bubbles up to the usual
+  // (non-capturing) event handlers. Let it bubble up before firing the
+  // SidebarFocused event.
+  setTimeout(fireSidebarFocusedEvent, 0);
+}
+
+/**
+ * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
+ * a chance to adjust focus as needed. An additional event is needed, because
+ * we don't want to focus the sidebar when it's opened on startup or in a new
+ * window, only when the user opens the sidebar.
+ */
+function fireSidebarFocusedEvent() {
+  var sidebar = document.getElementById("sidebar");
+  var event = document.createEvent("Events");
+  event.initEvent("SidebarFocused", true, false);
+  sidebar.contentWindow.dispatchEvent(event);
 }
 
 var gHomeButton = {
   prefDomain: "browser.startup.homepage",
   observe: function (aSubject, aTopic, aPrefName)
   {
     if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
       return;
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -326,18 +326,17 @@
                code fires onmousedown, and hence eats our favicon drag events.
                We only add the identity-box button to the tab order when the location bar
                has focus, otherwise pressing F6 focuses it instead of the location bar -->
           <box id="identity-box" role="button"
                onclick="getIdentityHandler().handleIdentityButtonEvent(event);"
                onkeypress="getIdentityHandler().handleIdentityButtonEvent(event);">
             <hbox align="center">
               <stack id="page-proxy-stack"
-                     onclick="PageProxyClickHandler(event);"
-                     tooltiptext="&proxyIcon.tooltip;">
+                     onclick="PageProxyClickHandler(event);">
                 <image id="urlbar-throbber" busy="false"/>
                 <image id="page-proxy-favicon" validate="never"
                        ondraggesture="PageProxyDragGesture(event);"
                        onerror="this.removeAttribute('src');"/>
               </stack>
               <label id="identity-icon-label"/>
             </hbox>
           </box>
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -237,16 +237,44 @@
               this._initURLTooltip();
               break;
             case "mouseout":
               this._hideURLTooltip();
               break;
           }
         ]]></body>
       </method>
+
+      <property name="textValue"
+                onget="return this.value;">
+        <setter>
+          <![CDATA[
+          // Force load the value into the urlbar to get it unescaped
+          try {
+            let uri = Cc["@mozilla.org/network/io-service;1"].
+                      getService(Ci.nsIIOService).
+                      newURI(val, null, null);
+            URLBarSetURI(uri, true);
+          } catch (ex) {
+            // Incase the value isn't actually a URI
+            this.value = val;
+          }
+
+          // Completing a result should simulate the user typing the result, so
+          // fire an input event.
+          let evt = document.createEvent("UIEvents");
+          evt.initUIEvent("input", true, false, window, 0);
+          this.mIgnoreInput = true;
+          this.dispatchEvent(evt);
+          this.mIgnoreInput = false;
+
+          return this.value;
+          ]]>
+        </setter>
+      </property>
     </implementation>
 
     <handlers>
       <handler event="focus" phase="capturing" action="this._hideURLTooltip();"/>
       <handler event="dragover" phase="capturing" action="nsDragAndDrop.dragOver(event, this);"/>
       <handler event="dragdrop" phase="capturing" action="nsDragAndDrop.drop(event, this);"/>
     </handlers>
 
--- a/browser/components/places/content/bookmarksPanel.js
+++ b/browser/components/places/content/bookmarksPanel.js
@@ -33,21 +33,25 @@
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 function init() {
   document.getElementById("bookmarks-view").place =
     "place:queryType=1&folder=" + window.top.PlacesUtils.allBookmarksFolderId;
-  document.getElementById("search-box").focus();
 }
 
 function searchBookmarks(aSearchString) {
   var tree = document.getElementById('bookmarks-view');
   if (!aSearchString)
     tree.place = tree.place;
   else
     tree.applyFilter(aSearchString,
                      [PlacesUtils.bookmarksMenuFolderId,
                       PlacesUtils.unfiledBookmarksFolderId,
                       PlacesUtils.toolbarFolderId]);
 }
+
+window.addEventListener("SidebarFocused",
+                        function()
+                          document.getElementById("search-box").focus(),
+                        false);
--- a/browser/components/places/content/history-panel.js
+++ b/browser/components/places/content/history-panel.js
@@ -60,18 +60,16 @@ function HistorySidebarInit()
   else if (gHistoryGrouping == "dayandsite")
     document.getElementById("bydayandsite").setAttribute("checked", "true");
   else
     document.getElementById("byday").setAttribute("checked", "true");
 
   initContextMenu();
   
   searchHistory("");
-
-  gSearchBox.focus();
 }
 
 function initContextMenu() {
   // Force-hide items in the context menu which never apply to this view
   var alwaysHideElements = ["placesContext_new:bookmark",
                             "placesContext_new:folder",
                             "placesContext_new:separator",
                             "placesContext_cut",
@@ -148,8 +146,13 @@ function searchHistory(aInput)
   options.sortingMode = sortingMode;
   options.resultType = resultType;
 
   // call load() on the tree manually
   // instead of setting the place attribute in history-panel.xul
   // otherwise, we will end up calling load() twice
   gHistoryTree.load([query], options);
 }
+
+window.addEventListener("SidebarFocused",
+                        function()
+                          gSearchBox.focus(),
+                        false);
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -585,17 +585,17 @@
           if (aBefore)
             aParentPopup.insertBefore(element, aBefore);
           else {
             // Add the new element to the menu.  If there is static content at
             // the end of the menu, add the element before that.  Otherwise,
             // just add to the end.
             if (aParentPopup._endMarker != -1)
               aParentPopup.insertBefore(element,
-                                        aParentPopup.childNodes[this._endMarker++]);
+                                        aParentPopup.childNodes[aParentPopup._endMarker++]);
             else
               aParentPopup.appendChild(element);
           }
         ]]></body>
       </method>
 
       <method name="_showEmptyMenuItem">
         <parameter name="aPopup"/>
--- a/browser/components/places/content/utils.js
+++ b/browser/components/places/content/utils.js
@@ -1917,18 +1917,18 @@ var PlacesUtils = {
     }
 
     // If static items at the beginning were found, remove all items between
     // them and the static content at the end.
     for (var i = 0; i < items.length; ++i) {
       // skip the empty menu item
       if (aPopup._emptyMenuItem != items[i]) {
         aPopup.removeChild(items[i]);
-        if (this._endMarker > 0)
-          --this._endMarker;
+        if (aPopup._endMarker > 0)
+          --aPopup._endMarker;
       }
     }
 
     // If no static items were found at the beginning, remove all items before
     // the static items at the end.
     if (aPopup._startMarker == -1) {
       var end = aPopup._endMarker == -1 ?
                 aPopup.childNodes.length - 1 : aPopup._endMarker - 1;
--- a/browser/components/preferences/content.js
+++ b/browser/components/preferences/content.js
@@ -181,17 +181,17 @@ var gContentPane = {
   {
     const kFontNameFmtSerif         = "font.name.serif.%LANG%";
     const kFontNameFmtSansSerif     = "font.name.sans-serif.%LANG%";
     const kFontNameListFmtSerif     = "font.name-list.serif.%LANG%";
     const kFontNameListFmtSansSerif = "font.name-list.sans-serif.%LANG%";
     const kFontSizeFmtVariable      = "font.size.variable.%LANG%";
 
     var prefs = [{ format   : aIsSerif ? kFontNameFmtSerif : kFontNameFmtSansSerif,
-                   type     : "unichar",
+                   type     : "fontname",
                    element  : "defaultFont",
                    fonttype : aIsSerif ? "serif" : "sans-serif" },
                  { format   : aIsSerif ? kFontNameListFmtSerif : kFontNameListFmtSansSerif,
                    type     : "unichar",
                    element  : null,
                    fonttype : aIsSerif ? "serif" : "sans-serif" },
                  { format   : kFontSizeFmtVariable,
                    type     : "int",
--- a/browser/components/preferences/fonts.js
+++ b/browser/components/preferences/fonts.js
@@ -48,26 +48,26 @@ const kFontNameListFmtSansSerif = "font.
 const kFontNameListFmtMonospace = "font.name-list.monospace.%LANG%";
 const kFontSizeFmtVariable      = "font.size.variable.%LANG%";
 const kFontSizeFmtFixed         = "font.size.fixed.%LANG%";
 const kFontMinSizeFmt           = "font.minimum-size.%LANG%";
 
 var gFontsDialog = {
   _selectLanguageGroup: function (aLanguageGroup)
   {
-    var prefs = [{ format: kDefaultFontType,          type: "string", element: "defaultFontType", fonttype: null},
-                 { format: kFontNameFmtSerif,         type: "unichar", element: "serif",      fonttype: "serif"       },
-                 { format: kFontNameFmtSansSerif,     type: "unichar", element: "sans-serif", fonttype: "sans-serif"  },
-                 { format: kFontNameFmtMonospace,     type: "unichar", element: "monospace",  fonttype: "monospace"   },
-                 { format: kFontNameListFmtSerif,     type: "unichar", element: null,         fonttype: "serif"       },
-                 { format: kFontNameListFmtSansSerif, type: "unichar", element: null,         fonttype: "sans-serif"  },
-                 { format: kFontNameListFmtMonospace, type: "unichar", element: null,         fonttype: "monospace"   },
-                 { format: kFontSizeFmtVariable,      type: "int",     element: "sizeVar",    fonttype: null          },
-                 { format: kFontSizeFmtFixed,         type: "int",     element: "sizeMono",   fonttype: null          },
-                 { format: kFontMinSizeFmt,           type: "int",     element: "minSize",    fonttype: null          }];
+    var prefs = [{ format: kDefaultFontType,          type: "string",   element: "defaultFontType", fonttype: null},
+                 { format: kFontNameFmtSerif,         type: "fontname", element: "serif",      fonttype: "serif"       },
+                 { format: kFontNameFmtSansSerif,     type: "fontname", element: "sans-serif", fonttype: "sans-serif"  },
+                 { format: kFontNameFmtMonospace,     type: "fontname", element: "monospace",  fonttype: "monospace"   },
+                 { format: kFontNameListFmtSerif,     type: "unichar",  element: null,         fonttype: "serif"       },
+                 { format: kFontNameListFmtSansSerif, type: "unichar",  element: null,         fonttype: "sans-serif"  },
+                 { format: kFontNameListFmtMonospace, type: "unichar",  element: null,         fonttype: "monospace"   },
+                 { format: kFontSizeFmtVariable,      type: "int",      element: "sizeVar",    fonttype: null          },
+                 { format: kFontSizeFmtFixed,         type: "int",      element: "sizeMono",   fonttype: null          },
+                 { format: kFontMinSizeFmt,           type: "int",      element: "minSize",    fonttype: null          }];
     var preferences = document.getElementById("fontPreferences");
     for (var i = 0; i < prefs.length; ++i) {
       var preference = document.getElementById(prefs[i].format.replace(/%LANG%/, aLanguageGroup));
       if (!preference) {
         preference = document.createElement("preference");
         var name = prefs[i].format.replace(/%LANG%/, aLanguageGroup);
         preference.id = name;
         preference.setAttribute("name", name);
--- a/browser/components/safebrowsing/Makefile.in
+++ b/browser/components/safebrowsing/Makefile.in
@@ -40,16 +40,20 @@ DEPTH     = ../../..
 topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH     = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS      = src
 
+ifdef MOZ_MOCHITEST
+DIRS += content/test
+endif
+
 ifneq (,$(BUILD_OFFICIAL)$(MOZILLA_OFFICIAL))
 DEFINES += -DOFFICIAL_BUILD=1
 endif
 
 # EXTRA_COMPONENTS installs components written in JS to dist/bin/components
 EXTRA_PP_COMPONENTS = \
          src/nsSafebrowsingApplication.js \
          $(NULL)
--- a/browser/components/safebrowsing/content/blockedSite.xhtml
+++ b/browser/components/safebrowsing/content/blockedSite.xhtml
@@ -142,22 +142,50 @@
         el.parentNode.removeChild(el);
 
         el = document.getElementById("errorShortDescText_malware");
         el.parentNode.removeChild(el);
 
         el = document.getElementById("errorLongDescText_malware");
         el.parentNode.removeChild(el);
 
+        // Unhide clickthrough button
+        el = document.getElementById("ignoreWarningButton");
+        el.style.display = "-moz-box";
+        
         // Set sitename
         document.getElementById("phishing_sitename").textContent = getHostString();
         document.title = document.getElementById("errorTitleText_phishing")
                                  .innerHTML;
       }
     ]]></script>
+    <style type="text/css">
+      /* Style warning button to look like a small text link in the
+         bottom right. This is preferable to just using a text link
+         since there is already a mechanism in browser.js for trapping
+         oncommand events from unprivileged chrome pages (BrowserOnCommand).*/
+      #ignoreWarningButton {
+        -moz-appearance: none;
+        background: transparent;
+        border: none;
+        color: white;  /* Hard coded because netError.css forces this page's background to dark red */
+        text-decoration: underline;
+        margin: 0;
+        padding: 0;
+        position: relative;
+        top: 23px;
+        left: 20px;
+        font-size: smaller;
+        display: none; /* Hide the button by default */
+      }
+      
+      #ignoreWarning {
+        text-align: right;
+      }
+    </style>
   </head>
 
   <body dir="&locale.dir;">
     <div id="errorPageContainer">
     
       <!-- Error Title -->
       <div id="errorTitle">
         <h1 id="errorTitleText_phishing">&safeb.blocked.phishing.title;</h1>
@@ -182,16 +210,20 @@
         <div id="buttons">
           <!-- Commands handled in browser.js -->
           <xul:button xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                       id="getMeOutButton" label="&safeb.palm.accept.label;"/>
           <xul:button xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
                       id="reportButton" label="&safeb.palm.report.label;"/>
         </div>
       </div>
+      <div id="ignoreWarning">
+        <xul:button xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+                    id="ignoreWarningButton" label="&safeb.palm.decline.label;"/>
+      </div>
     </div>
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
     - LOAD_BACKGROUND, which means that onload handlers will not be executed.
     -->
     <script type="application/javascript">initPage();</script>
   </body>
new file mode 100644
--- /dev/null
+++ b/browser/components/safebrowsing/content/test/Makefile.in
@@ -0,0 +1,53 @@
+#
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org code.
+#
+# The Initial Developer of the Original Code is
+#    Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 1998
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Johnathan Nightingale <johnath@mozilla.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either of the GNU General Public License Version 2 or later (the "GPL"),
+# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = browser/components/safebrowsing/content/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_BROWSER_FILES = browser_bug400731.js \
+    $(NULL)
+
+libs::	$(_BROWSER_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+
new file mode 100644
--- /dev/null
+++ b/browser/components/safebrowsing/content/test/browser_bug400731.js
@@ -0,0 +1,39 @@
+/* Check for the intended visibility of the "Ignore this warning" text*/
+var newBrowser
+
+function test() {
+  waitForExplicitFinish();
+  
+  var newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  newBrowser = gBrowser.getBrowserForTab(newTab);
+  
+  // Navigate to malware site.  Can't use an onload listener here since
+  // error pages don't fire onload
+  newBrowser.contentWindow.location = 'http://www.mozilla.com/firefox/its-an-attack.html';
+  window.setTimeout(testMalware, 2000);
+}
+
+function testMalware() {
+  // Confirm that "Ignore this warning" is hidden
+  var el = newBrowser.contentDocument.getElementById("ignoreWarningButton");
+  ok(el, "Ignore warning button should be present (but hidden) for malware");
+  
+  var style = newBrowser.contentWindow.getComputedStyle(el, null);
+  is(style.display, "none", "Ignore Warning button should be display:none for malware");
+  
+  // Now launch the phishing test
+  newBrowser.contentWindow.location = 'http://www.mozilla.com/firefox/its-a-trap.html';
+  window.setTimeout(testPhishing, 2000);
+}
+
+function testPhishing() {
+  var el = newBrowser.contentDocument.getElementById("ignoreWarningButton");
+  ok(el, "Ignore warning button should be present for phishing");
+  
+  var style = newBrowser.contentWindow.getComputedStyle(el, null);
+  is(style.display, "-moz-box", "Ignore Warning button should be display:-moz-box for phishing");
+  
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -84,18 +84,16 @@
 <!ENTITY printButton.label            "Print">
 <!ENTITY printButton.tooltip          "Print this page">
 
 <!ENTITY locationItem.title           "Location">
 <!ENTITY searchItem.title             "Search">
 <!ENTITY throbberItem.title           "Activity Indicator">
 <!ENTITY bookmarksItem.title          "Bookmarks">
 
-<!ENTITY proxyIcon.tooltip            "Drag and drop this icon to create a link to this page">
-
 <!-- Toolbar items --> 
 <!ENTITY  homeButton.label            "Home">
 
 <!ENTITY bookmarksButton.label          "Bookmarks">
 <!ENTITY bookmarksButton.tooltip        "Display your bookmarks">
 <!ENTITY bookmarksSidebarCmd.accesskey  "B">
 <!ENTITY bookmarksSidebarCmd.commandkey "b">
 <!ENTITY bookmarksSidebarWinCmd.commandkey "i">
--- a/browser/modules/Distribution.jsm
+++ b/browser/modules/Distribution.jsm
@@ -29,17 +29,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-EXPORTED_SYMBOLS = [ "DistributionCustomizer" ];
+var EXPORTED_SYMBOLS = [ "DistributionCustomizer" ];
 
 const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cr = Components.results;
 const Cu = Components.utils;
 
 function DistributionCustomizer() {
   this._distroDir = this._dirSvc.get("XCurProcD", Ci.nsIFile);
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -1320,16 +1320,19 @@ endif #STRICT_CPLUSPLUS_SUFFIX
 	$(CCC) -C -E $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) > $*.i
 
 %.i: %.cc
 	$(CCC) -C -E $(COMPILE_CXXFLAGS) $(_VPATH_SRCS) > $*.i
 
 %.i: %.c
 	$(CC) -C -E $(COMPILE_CFLAGS) $(_VPATH_SRCS) > $*.i
 
+%.i: %.mm
+	$(CCC) -C -E $(COMPILE_CXXFLAGS) $(COMPILE_CMMFLAGS) $(_VPATH_SRCS) > $*.i
+
 %.res: %.rc
 	@echo Creating Resource file: $@
 ifeq ($(OS_ARCH),OS2)
 	$(RC) $(RCFLAGS:-D%=-d %) -i $(subst /,\,$(srcdir)) -r $< $@
 else
 ifdef GNU_CC
 	$(RC) $(RCFLAGS) $(filter-out -U%,$(DEFINES)) $(INCLUDES:-I%=--include-dir %) $(OUTOPTION)$@ $(_VPATH_SRCS)
 else
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -354,16 +354,19 @@ NS_IMETHODIMP
 nsObjectLoadingContent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
   if (aRequest != mChannel) {
     // This is a bit of an edge case - happens when a new load starts before the
     // previous one got here
     return NS_BINDING_ABORTED;
   }
 
+  // We're done with the classifier
+  mClassifier = nsnull;
+
   AutoNotifier notifier(this, PR_TRUE);
 
   if (!IsSuccessfulRequest(aRequest)) {
     LOG(("OBJLC [%p]: OnStartRequest: Request failed\n", this));
     Fallback(PR_FALSE);
     return NS_BINDING_ABORTED;
   }
 
@@ -799,16 +802,20 @@ nsObjectLoadingContent::OnChannelRedirec
                                           nsIChannel *aNewChannel,
                                           PRUint32    aFlags)
 {
   // If we're already busy with a new load, cancel the redirect
   if (aOldChannel != mChannel) {
     return NS_BINDING_ABORTED;
   }
 
+  if (mClassifier) {
+    mClassifier->OnRedirect(aOldChannel, aNewChannel);
+  }
+
   mChannel = aNewChannel;
   return NS_OK;
 }
 
 // <public>
 PRInt32
 nsObjectLoadingContent::ObjectState() const
 {
@@ -959,16 +966,22 @@ nsObjectLoadingContent::LoadObject(nsIUR
   if (!doc) {
     return NS_OK;
   }
 
   // From here on, we will always change the content. This means that a
   // possibly-loading channel should be aborted.
   if (mChannel) {
     LOG(("OBJLC [%p]: Cancelling existing load\n", this));
+
+    if (mClassifier) {
+      mClassifier->Cancel();
+      mClassifier = nsnull;
+    }
+
     // These three statements are carefully ordered:
     // - onStopRequest should get a channel whose status is the same as the
     //   status argument
     // - onStopRequest must get a non-null channel
     mChannel->Cancel(NS_BINDING_ABORTED);
     if (mFinalListener) {
       // NOTE: Since mFinalListener is only set in onStartRequest, which takes
       // care of calling mFinalListener->OnStartRequest, mFinalListener is only
@@ -1219,16 +1232,23 @@ nsObjectLoadingContent::LoadObject(nsIUR
       SetExecutionPolicy(nsIScriptChannel::EXECUTE_NORMAL);
   }
 
   // AsyncOpen can fail if a file does not exist.
   // Show fallback content in that case.
   rv = chan->AsyncOpen(this, nsnull);
   if (NS_SUCCEEDED(rv)) {
     LOG(("OBJLC [%p]: Channel opened.\n", this));
+
+    rv = CheckClassifier(chan);
+    if (NS_FAILED(rv)) {
+      chan->Cancel(rv);
+      return rv;
+    }
+
     mChannel = chan;
     mType = eType_Loading;
   }
   return NS_OK;
 }
 
 PRUint32
 nsObjectLoadingContent::GetCapabilities() const
@@ -1630,16 +1650,36 @@ nsObjectLoadingContent::Instantiate(nsIO
 
   // We'll always have a type or a URI by the time we get here
   NS_ASSERTION(aURI || !typeToUse.IsEmpty(), "Need a URI or a type");
   LOG(("OBJLC [%p]: Calling [%p]->Instantiate(<%s>, %p)\n", this, aFrame,
        typeToUse.get(), aURI));
   return aFrame->Instantiate(typeToUse.get(), aURI);
 }
 
+nsresult
+nsObjectLoadingContent::CheckClassifier(nsIChannel *aChannel)
+{
+  nsresult rv;
+  nsCOMPtr<nsIChannelClassifier> classifier =
+    do_CreateInstance(NS_CHANNELCLASSIFIER_CONTRACTID, &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = classifier->Start(aChannel);
+  if (rv == NS_ERROR_FACTORY_NOT_REGISTERED) {
+    // no URI classifier, ignore this
+    return NS_OK;
+  }
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mClassifier = classifier;
+
+  return NS_OK;
+}
+
 /* static */ PRBool
 nsObjectLoadingContent::ShouldShowDefaultPlugin(nsIContent* aContent,
                                                 const nsCString& aContentType)
 {
   if (nsContentUtils::GetBoolPref("plugin.default_plugin_disabled", PR_FALSE)) {
     return PR_FALSE;
   }
 
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -47,16 +47,17 @@
 
 #include "nsImageLoadingContent.h"
 #include "nsIStreamListener.h"
 #include "nsFrameLoader.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIChannelEventSink.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIRunnable.h"
+#include "nsIChannelClassifier.h"
 
 struct nsAsyncInstantiateEvent;
 class  AutoNotifier;
 class  AutoFallback;
 class  AutoSetInstantiatingToFalse;
 
 /**
  * INVARIANTS OF THIS CLASS
@@ -299,16 +300,23 @@ class nsObjectLoadingContent : public ns
      * GetFrame()->Instantiate() in that it ensures that the URI will
      * be non-null, and that a MIME type will be passed. Note that
      * this can cause the frame to be deleted while we're
      * instantiating the plugin.
      */
     nsresult Instantiate(nsIObjectFrame* aFrame, const nsACString& aMIMEType, nsIURI* aURI);
 
     /**
+     * Check the channel load against the URI classifier service (if it
+     * exists).  The channel will be suspended until the classification is
+     * complete.
+     */
+    nsresult CheckClassifier(nsIChannel *aChannel);
+
+    /**
      * Whether to treat this content as a plugin, even though we can't handle
      * the type. This function impl should match the checks in the plugin host.
      * aContentType is the MIME type we ended up with.
      */
     static PRBool ShouldShowDefaultPlugin(nsIContent* aContent,
                                           const nsCString& aContentType);
 
     enum PluginSupportState {
@@ -375,16 +383,21 @@ class nsObjectLoadingContent : public ns
      * Non-null between asyncOpen and onStopRequest.
      */
     nsIChannel*                 mChannel;
 
     // The data we were last asked to load
     nsCOMPtr<nsIURI>            mURI;
 
     /**
+     * Suspends/resumes channels based on the URI classifier.
+     */
+    nsCOMPtr<nsIChannelClassifier> mClassifier;
+
+    /**
      * Type of the currently-loaded content.
      */
     ObjectType                  mType          : 16;
 
     /**
      * Whether we are about to call instantiate on our frame. If we aren't,
      * SetFrame needs to asynchronously call Instantiate.
      */
--- a/content/base/src/nsRange.cpp
+++ b/content/base/src/nsRange.cpp
@@ -340,21 +340,20 @@ nsRange::NodeWillBeDestroyed(const nsINo
   // An alternative solution would be to make mRoot a strong pointer.
   DoSetRange(nsnull, 0, nsnull, 0, nsnull);
 }
 
 void
 nsRange::ParentChainChanged(nsIContent *aContent)
 {
   NS_ASSERTION(mRoot == aContent, "Wrong ParentChainChanged notification?");
-  nsINode* newRoot = mRoot;
-  nsINode* tmp;
-  while ((tmp = newRoot->GetNodeParent())) {
-    newRoot = tmp;
-  }
+  nsINode* newRoot = IsValidBoundary(mStartParent);
+  NS_ASSERTION(newRoot, "No valid boundary or root found!");
+  NS_ASSERTION(newRoot == IsValidBoundary(mEndParent),
+               "Start parent and end parent give different root!");
   DoSetRange(mStartParent, mStartOffset, mEndParent, mEndOffset, newRoot);
 }
 
 /********************************************************
  * Utilities for comparing points: API from nsIDOMNSRange
  ********************************************************/
 NS_IMETHODIMP
 nsRange::IsPointInRange(nsIDOMNode* aParent, PRInt32 aOffset, PRBool* aResult)
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -2088,20 +2088,22 @@ nsXMLHttpRequest::Send(nsIVariant *aBody
   // Ignore argument if method is GET, there is no point in trying to
   // upload anything
   nsCAutoString method;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
 
   if (httpChannel) {
     httpChannel->GetRequestMethod(method); // If GET, method name will be uppercase
 
-    nsCOMPtr<nsIURI> codebase;
-    mPrincipal->GetURI(getter_AddRefs(codebase));
-
-    httpChannel->SetReferrer(codebase);
+    if (!IsSystemPrincipal(mPrincipal)) {
+      nsCOMPtr<nsIURI> codebase;
+      mPrincipal->GetURI(getter_AddRefs(codebase));
+
+      httpChannel->SetReferrer(codebase);
+    }
   }
 
   if (aBody && httpChannel && !method.EqualsLiteral("GET")) {
     nsXPIDLString serial;
     nsCOMPtr<nsIInputStream> postDataStream;
     nsCAutoString charset(NS_LITERAL_CSTRING("UTF-8"));
 
     PRUint16 dataType;
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -169,16 +169,17 @@ include $(topsrcdir)/config/rules.mk
 		test_bug413974.html \
 		test_bug415860.html \
 		test_bug414190.html \
 		test_bug414796.html \
 		test_bug416383.html \
 		test_bug417255.html \
 		test_bug417384.html \
 		test_bug418214.html \
+		test_bug419527.xhtml \
 		test_bug420609.xhtml \
 		test_bug420700.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 check::
--- a/content/base/test/test_bug338541.xhtml
+++ b/content/base/test/test_bug338541.xhtml
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 338541 **/
 function getName(aNode, f)
 {
   return (aNode ? aNode.nodeName : "(null)");
 }
 
 function walkDOM()
 {
-  var walker = document.createTreeWalker($('content'), Components.interfaces.nsIDOMNodeFilter.SHOW_ELEMENT, null, true);
+  var walker = document.createTreeWalker($('content'), NodeFilter.SHOW_ELEMENT, null, true);
   var output = "";
   while (walker.nextNode())
   {
     output += getName(walker.currentNode) + "\n";
   }
   output += "Final currentNode: " + getName(walker.currentNode);
   is(output, "foo\nbar\nhtml:b\nqux\nbaz\nFinal currentNode: baz","treewalker returns correct nodeName");
   SimpleTest.finish();
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug419527.xhtml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=419527
+-->
+<head>
+  <title>Test for Bug 419527</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <bindings xmlns="http://www.mozilla.org/xbl"
+            xmlns:html="http://www.w3.org/1999/xhtml">
+    <binding id="rangebinding">
+      <content><html:span>Foo</html:span>
+      </content>
+      <implementation>
+        <constructor>
+          var span = document.getAnonymousNodes(this)[0];
+          ok(span.localName == "span", "Wrong anon node!");
+          var range = document.createRange();
+          range.selectNode(span.firstChild);
+          ok(range.startContainer == span, "Wrong start container!");
+          ok(range.endContainer == span, "Wrong end container!");
+          window.newSubTree.appendChild(this);
+          range.setStart(window.newSubTree.firstChild, 0);
+          ok(range.startContainer == window.newSubTree.firstChild,
+             "Range should have been collapsed to newSubTree.firstChild!");
+          ok(range.endContainer == window.newSubTree.firstChild,
+             "Range should have been collapsed to newSubTree.firstChild!");
+          SimpleTest.finish();
+        </constructor>
+      </implementation>
+    </binding>
+  </bindings>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=419527">Mozilla Bug 419527</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 419527 **/
+
+function runRangeTest() {
+  window.newSubTree = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+  newSubTree.appendChild(document.createElementNS("http://www.w3.org/1999/xhtml", "div"));
+
+  var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+  document.loadBindingDocument(window.location);
+  document.addBinding(d, window.location + "#rangebinding");
+}
+
+SimpleTest.waitForExplicitFinish();
+setTimeout(runRangeTest, 0);
+
+
+]]>
+</script>
+</pre>
+</body>
+</html>
+
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -822,34 +822,22 @@ nsEventStateManager::PreHandleEvent(nsPr
       StopTrackingDragGesture();
 #endif
     case nsMouseEvent::eMiddleButton:
       SetClickCount(aPresContext, (nsMouseEvent*)aEvent, aStatus);
       break;
     }
     break;
   case NS_MOUSE_EXIT:
-    // If the event coordinate is within the bounds of the view,
-    // and this is not the top-level window, then it's not really
-    // an exit --- we may have traversed widget boundaries but
+    // If the event is not a top-level window exit, then it's not
+    // really an exit --- we may have traversed widget boundaries but
     // we're still in our toplevel window.
-    // On the other hand, if we exit a toplevel window, then
-    // it's really an exit even if the mouse is still in the
-    // window bounds --- the mouse probably moved into some
-    // "on top" window.
     {
       nsMouseEvent* mouseEvent = static_cast<nsMouseEvent*>(aEvent);
-      nsIWidget* parentWidget = mouseEvent->widget->GetParent();
-      nsPoint eventPoint;
-      eventPoint = nsLayoutUtils::TranslateWidgetToView(aPresContext,
-                                                        mouseEvent->widget,
-                                                        mouseEvent->refPoint,
-                                                        aView);
-      if (parentWidget &&
-          (aView->GetBounds() - aView->GetPosition()).Contains(eventPoint)) {
+      if (mouseEvent->exit != nsMouseEvent::eTopLevel) {
         // treat it as a move so we don't generate spurious "exit"
         // events Any necessary exit events will be generated by
         // GenerateMouseEnterExit
         aEvent->message = NS_MOUSE_MOVE;
         // then fall through...
       } else {
         GenerateMouseEnterExit((nsGUIEvent*)aEvent);
         //This is a window level mouse exit event and should stop here
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -109,16 +109,17 @@ XPIDLSRCS	= \
 		nsIWebNavigationInfo.idl        \
 		nsIContentViewer.idl		\
 		nsIContentViewerEdit.idl	\
 		nsIContentViewerFile.idl	\
 		nsIURIFixup.idl			\
 		nsIEditorDocShell.idl		\
 		nsIWebPageDescriptor.idl	\
 		nsIURIClassifier.idl		\
+		nsIChannelClassifier.idl	\
 		nsIDownloadHistory.idl \
 		$(NULL)
 
 EXPORTS		= nsDocShellLoadTypes.h
 
 CPPSRCS		= \
 		nsDocShell.cpp		\
 		nsWebShell.cpp		\
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -4953,22 +4953,17 @@ nsDocShell::OnRedirectStateChange(nsICha
     NS_ASSERTION(aStateFlags & STATE_REDIRECTING,
                  "Calling OnRedirectStateChange when there is no redirect");
     if (!(aStateFlags & STATE_IS_DOCUMENT))
         return; // not a toplevel document
 
     // If this load is being checked by the URI classifier, we need to
     // query the classifier again for the new URI.
     if (mClassifier) {
-        mClassifier->SetChannel(aNewChannel);
-
-        // we call the nsClassifierCallback:Run() from the main loop to
-        // give the channel a chance to AsyncOpen() the channel before
-        // we suspend it.
-        NS_DispatchToCurrentThread(mClassifier);
+        mClassifier->OnRedirect(aOldChannel, aNewChannel);
     }
 
     nsCOMPtr<nsIGlobalHistory3> history3(do_QueryInterface(mGlobalHistory));
     nsresult result = NS_ERROR_NOT_IMPLEMENTED;
     if (history3) {
         // notify global history of this redirect
         result = history3->AddDocumentRedirect(aOldChannel, aNewChannel,
                                                aRedirectFlags, !IsFrame());
@@ -7498,18 +7493,17 @@ nsresult nsDocShell::DoChannelLoad(nsICh
 }
 
 nsresult
 nsDocShell::CheckClassifier(nsIChannel *aChannel)
 {
     nsRefPtr<nsClassifierCallback> classifier = new nsClassifierCallback();
     if (!classifier) return NS_ERROR_OUT_OF_MEMORY;
 
-    classifier->SetChannel(aChannel);
-    nsresult rv = classifier->Run();
+    nsresult rv = classifier->Start(aChannel);
     if (rv == NS_ERROR_FACTORY_NOT_REGISTERED ||
         rv == NS_ERROR_NOT_AVAILABLE) {
         // no URI classifier => ignored cases
         return NS_OK;
     }
     NS_ENSURE_SUCCESS(rv, rv);
 
     mClassifier = classifier;
@@ -9321,19 +9315,20 @@ nsDocShell::IsOKToLoadURI(nsIURI* aURI)
         secMan &&
         NS_SUCCEEDED(secMan->CheckSameOriginURI(aURI, mLoadingURI, PR_FALSE));
 }
 
 //*****************************************************************************
 // nsClassifierCallback
 //*****************************************************************************
 
-NS_IMPL_THREADSAFE_ISUPPORTS2(nsClassifierCallback,
-                              nsIURIClassifierCallback,
-                              nsIRunnable)
+NS_IMPL_ISUPPORTS3(nsClassifierCallback,
+                   nsIChannelClassifier,
+                   nsIURIClassifierCallback,
+                   nsIRunnable)
 
 NS_IMETHODIMP
 nsClassifierCallback::Run()
 {
     if (!mChannel) {
         return NS_OK;
     }
 
@@ -9492,25 +9487,47 @@ nsClassifierCallback::OnClassifyComplete
 #endif
         mSuspendedChannel->Resume();
         mSuspendedChannel = nsnull;
     }
 
     return NS_OK;
 }
 
-void
+NS_IMETHODIMP
+nsClassifierCallback::Start(nsIChannel *aChannel)
+{
+    mChannel = aChannel;
+    return Run();
+}
+
+NS_IMETHODIMP
+nsClassifierCallback::OnRedirect(nsIChannel *aOldChannel,
+                                nsIChannel *aNewChannel)
+{
+    mChannel = aNewChannel;
+
+    // we call the Run() from the main loop to give the channel a
+    // chance to AsyncOpen() before we suspend it.
+    NS_DispatchToCurrentThread(this);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsClassifierCallback::Cancel()
 {
     if (mSuspendedChannel) {
 #ifdef DEBUG
         PR_LOG(gDocShellLog, PR_LOG_DEBUG,
                ("nsClassifierCallback[%p]: resuming channel %p from Cancel()",
                 this, mSuspendedChannel.get()));
 #endif
         mSuspendedChannel->Resume();
         mSuspendedChannel = nsnull;
     }
 
     if (mChannel) {
         mChannel = nsnull;
     }
-}
+
+    return NS_OK;
+}
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -101,16 +101,17 @@
 #include "nsIHttpChannel.h"
 #include "nsDocShellTransferableHooks.h"
 #include "nsIAuthPromptProvider.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIObserver.h"
 #include "nsDocShellLoadTypes.h"
 #include "nsPIDOMEventTarget.h"
 #include "nsIURIClassifier.h"
+#include "nsIChannelClassifier.h"
 
 class nsIScrollableView;
 
 /* load commands were moved to nsIDocShell.h */
 /* load types were moved to nsDocShellLoadTypes.h */
 
 /* internally used ViewMode types */
 enum ViewMode {
@@ -137,31 +138,29 @@ public:
     PRInt32               mDelay;
     PRPackedBool          mRepeat;
     PRPackedBool          mMetaRefresh;
     
 protected:
     virtual ~nsRefreshTimer();
 };
 
-class nsClassifierCallback : public nsIURIClassifierCallback
+class nsClassifierCallback : public nsIChannelClassifier
+                           , public nsIURIClassifierCallback
                            , public nsIRunnable
 {
 public:
     nsClassifierCallback() {}
     ~nsClassifierCallback() {}
 
     NS_DECL_ISUPPORTS
+    NS_DECL_NSICHANNELCLASSIFIER
     NS_DECL_NSIURICLASSIFIERCALLBACK
     NS_DECL_NSIRUNNABLE
 
-    void SetChannel(nsIChannel * aChannel)
-        { mChannel = aChannel; }
-
-    void Cancel();
 private:
     nsCOMPtr<nsIChannel> mChannel;
     nsCOMPtr<nsIChannel> mSuspendedChannel;
 
     void MarkEntryClassified(nsresult status);
     PRBool HasBeenClassified();
 };
 
new file mode 100644
--- /dev/null
+++ b/docshell/base/nsIChannelClassifier.idl
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: ft=cpp tw=78 sw=4 et ts=4 sts=4 cin
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Dave Camp <dcamp@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+
+interface nsIChannel;
+
+/**
+ * An nsIChannelClassifier object checks a channel's URI against the
+ * URI classifier service, and cancels the channel before OnStartRequest
+ * if it is found on a blacklist.
+ */
+[scriptable, uuid(d17f8f74-d403-4dea-b124-3ace5dbe44dc)]
+interface nsIChannelClassifier : nsISupports
+{
+  /**
+   * Checks a channel against the URI classifier service (if it exists).
+   *
+   * The channel will be suspended while the classifier is checked.  The
+   * channel may be cancelled with an error code determined by the classifier
+   * before it is resumed.
+   *
+   * If there is no URI classifier service, NS_ERROR_FACTORY_NOT_REGISTERED
+   * will be returned.
+   *
+   * @param aChannel
+   *        The channel to suspend.
+   */
+  void start(in nsIChannel aChannel);
+
+  /**
+   * Notify the classifier that the channel was redirected.  The new channel
+   * will be suspended pending a new classifier lookup.
+   *
+   * @param aOldChannel
+   *        The channel that's being redirected.
+   * @param aNewChannel
+   *        The new channel. This channel is not opened yet.
+   */
+  void onRedirect(in nsIChannel aOldChannel, in nsIChannel aNewChannel);
+
+  /**
+   * Cancel an existing query.  If a channel has been suspended, it will
+   * be resumed.
+   */
+  void cancel();
+};
--- a/docshell/build/nsDocShellCID.h
+++ b/docshell/build/nsDocShellCID.h
@@ -62,16 +62,25 @@
 
 /**
  * Contract ID for a service implementing nsIURIClassifier that identifies
  * phishing and malware sites.
  */
 #define NS_URICLASSIFIERSERVICE_CONTRACTID "@mozilla.org/uriclassifierservice"
 
 /**
+ * Class and contract ID for an nsIChannelClassifier implementation for
+ * checking a channel load against the URI classifier service.
+ */
+#define NS_CHANNELCLASSIFIER_CID \
+ { 0xce02d538, 0x0217, 0x47a3,{0xa5, 0x89, 0xb5, 0x17, 0x90, 0xfd, 0xd8, 0xce}}
+
+#define NS_CHANNELCLASSIFIER_CONTRACTID "@mozilla.org/channelclassifier"
+
+/**
  * An observer service topic that can be listened to to catch creation
  * of content browsing areas (both toplevel ones and subframes).  The
  * subject of the notification will be the nsIWebNavigation being
  * created.  At this time the additional data wstring is not defined
  * to be anything in particular.
  */
 #define NS_WEBNAVIGATION_CREATE "webnavigation-create"
 
--- a/docshell/build/nsDocShellModule.cpp
+++ b/docshell/build/nsDocShellModule.cpp
@@ -95,16 +95,17 @@ Shutdown(nsIModule* aSelf)
   nsSHEntry::Shutdown();
   gInitialized = PR_FALSE;
 }
 
 // docshell
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWebShell, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDefaultURIFixup)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsWebNavigationInfo, Init)
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsClassifierCallback)
 
 // uriloader
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsURILoader)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsDocLoader, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSHelperAppService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPrefetchService, Init)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsOfflineCacheUpdateService,
@@ -144,16 +145,22 @@ static const nsModuleComponentInfo gDocS
       NS_URIFIXUP_CONTRACTID,
       nsDefaultURIFixupConstructor
     },
     { "Webnavigation info service",
       NS_WEBNAVIGATION_INFO_CID,
       NS_WEBNAVIGATION_INFO_CONTRACTID,
       nsWebNavigationInfoConstructor
     },
+    {
+      "Channel classifier",
+      NS_CHANNELCLASSIFIER_CID,
+      NS_CHANNELCLASSIFIER_CONTRACTID,
+      nsClassifierCallbackConstructor
+    },
 
     // about redirector
     { "about:config",
       NS_ABOUT_REDIRECTOR_MODULE_CID,
       NS_ABOUT_MODULE_CONTRACTID_PREFIX "config",
       nsAboutRedirector::Create
     },
 #ifdef MOZ_CRASHREPORTER
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -6184,18 +6184,18 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     // Something went wrong, or the property got resolved. Return.
     return rv;
   }
 
   // Make a fast expando if we're assigning to (not declaring or
   // binding a name) a new undefined property that's not already
   // defined on our prototype chain. This way we can access this
   // expando w/o ever getting back into XPConnect.
-  if ((flags & (JSRESOLVE_ASSIGNING)) && (JSOp)*cx->fp->pc != JSOP_BINDNAME &&
-      win->IsInnerWindow()) {
+  if ((flags & (JSRESOLVE_ASSIGNING)) && cx->fp->regs &&
+      (JSOp)*cx->fp->regs->pc != JSOP_BINDNAME && win->IsInnerWindow()) {
     JSObject *realObj;
     wrapper->GetJSObject(&realObj);
 
     if (obj == realObj) {
       JSObject *proto = STOBJ_GET_PROTO(obj);
       if (proto) {
         jsid interned_id;
         JSProperty *prop = nsnull;
--- a/gfx/idl/nsIFontEnumerator.idl
+++ b/gfx/idl/nsIFontEnumerator.idl
@@ -34,17 +34,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
-[scriptable, uuid(a6cf9114-15b3-11d2-932e-00805f8add32)]
+[scriptable, uuid(924d98d9-3518-4cb4-8708-c74fe8e3ec3c)]
 interface nsIFontEnumerator : nsISupports
 {
   /**
    * Return a sorted array of the names of all installed fonts.
    *
    * @param  aCount     returns number of names returned
    * @param  aResult    returns array of names
    * @return void
@@ -78,9 +78,17 @@ interface nsIFontEnumerator : nsISupport
    */
   wstring getDefaultFont(in string aLangGroup, in string aGeneric);
 
   /**
    * update the global font list
    * return true if font list is changed
    */
   boolean updateFontList();
+
+  /**
+   * get the standard family name on the system from given family
+   * @param  aName family name which may be alias
+   * @return the standard family name on the system, if given name does not
+   *         exist, returns empty string
+   */
+  wstring getStandardFamilyName(in wstring aName);
 };
--- a/gfx/src/thebes/nsThebesFontEnumerator.cpp
+++ b/gfx/src/thebes/nsThebesFontEnumerator.cpp
@@ -123,8 +123,32 @@ nsThebesFontEnumerator::GetDefaultFont(c
 
 NS_IMETHODIMP
 nsThebesFontEnumerator::UpdateFontList(PRBool *_retval)
 {
     nsresult rv = gfxPlatform::GetPlatform()->UpdateFontList();
     *_retval = PR_FALSE; // always return false for now
     return NS_OK;
 }
+
+NS_IMETHODIMP
+nsThebesFontEnumerator::GetStandardFamilyName(const PRUnichar *aName,
+                                              PRUnichar **aResult)
+{
+    NS_ENSURE_ARG_POINTER(aResult);
+    NS_ENSURE_ARG_POINTER(aName);
+
+    nsAutoString name(aName);
+    if (name.IsEmpty()) {
+        *aResult = nsnull;
+        return NS_OK;
+    }
+
+    nsAutoString family;
+    nsresult rv = gfxPlatform::GetPlatform()->
+        GetStandardFamilyName(nsDependentString(aName), family);
+    if (NS_FAILED(rv) || family.IsEmpty()) {
+        *aResult = nsnull;
+        return NS_OK;
+    }
+    *aResult = ToNewUnicode(family);
+    return NS_OK;
+}
--- a/gfx/thebes/public/gfxAtsuiFonts.h
+++ b/gfx/thebes/public/gfxAtsuiFonts.h
@@ -71,24 +71,26 @@ public:
     cairo_font_face_t *CairoFontFace() { return mFontFace; }
     cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; }
 
     ATSUStyle GetATSUStyle() { return mATSUStyle; }
 
     virtual nsString GetUniqueName();
 
     virtual PRUint32 GetSpaceGlyph() { return mSpaceGlyph; }
-    
+
     PRBool HasMirroringInfo();
 
     virtual void SetupGlyphExtents(gfxContext *aContext, PRUint32 aGlyphID,
             PRBool aNeedTight, gfxGlyphExtents *aExtents);
 
     PRBool TestCharacterMap(PRUint32 aCh);
 
+    MacOSFontEntry* GetFontEntry();
+
 protected:
     const gfxFontStyle *mFontStyle;
 
     ATSUStyle mATSUStyle;
 
     nsRefPtr<MacOSFontEntry> mFontEntry;
 
     PRBool mHasMirroring;
--- a/gfx/thebes/public/gfxBeOSPlatform.h
+++ b/gfx/thebes/public/gfxBeOSPlatform.h
@@ -15,17 +15,16 @@
  * The Original Code is BeOS code in Thebes.
  *
  * The Initial Developer of the Original Code is
  * Christian Biesinger <cbiesinger@web.de>.
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
- *   Masayuki Nakano <masayuki@d-toybox.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -62,13 +61,15 @@ public:
                          const nsACString& aGenericFamily,
                          nsStringArray& aListOfFonts);
 
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
+
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 protected:
     static gfxFontconfigUtils *sFontconfigUtils;
 };
 
 #endif /* GFX_PLATFORM_BEOS_H */
--- a/gfx/thebes/public/gfxFont.h
+++ b/gfx/thebes/public/gfxFont.h
@@ -545,17 +545,18 @@ public:
 protected:
     // The family name of the font
     nsString                   mName;
     nsExpirationState          mExpirationState;
     gfxFontStyle               mStyle;
     nsAutoTArray<gfxGlyphExtents*,1> mGlyphExtentsArray;
 
     // some fonts have bad metrics, this method sanitize them.
-    void SanitizeMetrics(gfxFont::Metrics *aMetrics);
+    // if this font has bad underline offset, aIsBadUnderlineFont should be true.
+    void SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont);
 };
 
 class THEBES_API gfxTextRunFactory {
     THEBES_INLINE_DECL_REFCOUNTING(gfxTextRunFactory)
 
 public:
     // Flags in the mask 0xFFFF0000 are reserved for textrun clients
     // Flags in the mask 0x0000F000 are reserved for per-platform fonts
@@ -1448,20 +1449,37 @@ public:
     static PRBool ForEachFont(const nsAString& aFamilies,
                               const nsACString& aLangGroup,
                               FontCreationCallback fc,
                               void *closure);
     PRBool ForEachFont(FontCreationCallback fc, void *closure);
 
     const nsString& GetFamilies() { return mFamilies; }
 
+    // This returns the preferred underline for this font group.
+    // Some CJK fonts have wrong underline offset in its metrics.
+    // If this group has such "bad" font, each platform's gfxFontGroup initialized mUnderlineOffset.
+    // The value should be lower value of first font's metrics and the bad font's metrics.
+    // Otherwise, this returns from first font's metrics.
+    gfxFloat GetUnderlineOffset() {
+        if (mUnderlineOffset == 0)
+            mUnderlineOffset = GetFontAt(0)->GetMetrics().underlineOffset;
+        return mUnderlineOffset;
+    }
+
 protected:
     nsString mFamilies;
     gfxFontStyle mStyle;
     nsTArray< nsRefPtr<gfxFont> > mFonts;
+    gfxFloat mUnderlineOffset;
+
+    // Init this font group's font metrics. If there no bad fonts, you don't need to call this.
+    // But if there are one or more bad fonts which have bad underline offset,
+    // you should call this with the *first* bad font.
+    void InitMetricsForBadFont(gfxFont* aBadFont);
 
     /* If aResolveGeneric is true, then CSS/Gecko generic family names are
      * replaced with preferred fonts.
      *
      * If aResolveFontName is true then fc() is called only for existing fonts
      * and with actual font names.  If false then fc() is called with each
      * family name in aFamilies (after resolving CSS/Gecko generic family names
      * if aResolveGeneric).
--- a/gfx/thebes/public/gfxOS2Platform.h
+++ b/gfx/thebes/public/gfxOS2Platform.h
@@ -61,16 +61,17 @@ public:
 
     nsresult GetFontList(const nsACString& aLangGroup,
                          const nsACString& aGenericFamily,
                          nsStringArray& aListOfFonts);
     nsresult UpdateFontList();
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
 
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle);
 protected:
     static gfxFontconfigUtils *sFontconfigUtils;
 };
 
 #endif /* GFX_OS2_PLATFORM_H */
--- a/gfx/thebes/public/gfxPlatform.h
+++ b/gfx/thebes/public/gfxPlatform.h
@@ -165,16 +165,22 @@ public:
     typedef PRBool (*FontResolverCallback) (const nsAString& aName,
                                             void *aClosure);
     virtual nsresult ResolveFontName(const nsAString& aFontName,
                                      FontResolverCallback aCallback,
                                      void *aClosure,
                                      PRBool& aAborted) = 0;
 
     /**
+     * Resolving a font name to family name. The result MUST be in the result of GetFontList().
+     * If the name doesn't in the system, aFamilyName will be empty string, but not failed.
+     */
+    virtual nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName) = 0;
+
+    /**
      * Create the appropriate platform font group
      */
     virtual gfxFontGroup *CreateFontGroup(const nsAString& aFamilies,
                                           const gfxFontStyle *aStyle) = 0;
 
     void GetPrefFonts(const char *aLangGroup, nsString& array, PRBool aAppendUnicode = PR_TRUE);
 
     /**
--- a/gfx/thebes/public/gfxPlatformGtk.h
+++ b/gfx/thebes/public/gfxPlatformGtk.h
@@ -62,16 +62,18 @@ public:
                          nsStringArray& aListOfFonts);
 
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle);
 
     static PRInt32 DPI() {
         if (sDPI == -1) {
             InitDPI();
         }
         NS_ASSERTION(sDPI > 0, "Something is wrong");
--- a/gfx/thebes/public/gfxPlatformMac.h
+++ b/gfx/thebes/public/gfxPlatformMac.h
@@ -55,16 +55,18 @@ public:
 
     already_AddRefed<gfxASurface> gfxPlatformMac::OptimizeImage(gfxImageSurface *aSurface,
                                                                 gfxASurface::gfxImageFormat format);
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle);
 
     nsresult GetFontList(const nsACString& aLangGroup,
                          const nsACString& aGenericFamily,
                          nsStringArray& aListOfFonts);
     nsresult UpdateFontList();
 
--- a/gfx/thebes/public/gfxWindowsFonts.h
+++ b/gfx/thebes/public/gfxWindowsFonts.h
@@ -57,26 +57,26 @@
  * It may be extended to also keep basic metrics of the fonts so that we can better
  * compare one FontEntry to another.
  */
 class FontEntry
 {
 public:
     THEBES_INLINE_DECL_REFCOUNTING(FontEntry)
 
-    FontEntry(const nsAString& aName, PRUint16 aFontType) : 
-        mName(aName), mFontType(aFontType), mDefaultWeight(0),
-        mUnicodeFont(PR_FALSE), mSymbolFont(PR_FALSE),
-        mCharset(0), mUnicodeRanges(0)
+    FontEntry(const nsAString& aName) : 
+        mName(aName), mDefaultWeight(0),
+        mUnicodeFont(PR_FALSE), mSymbolFont(PR_FALSE), mIsType1(PR_FALSE),
+        mIsBadUnderlineFont(PR_FALSE), mCharset(0), mUnicodeRanges(0)
     {
     }
 
     PRBool IsCrappyFont() const {
-        /* return if it is a bitmap, old school font or not a unicode font */
-        return (!mUnicodeFont || mSymbolFont || mFontType != TRUETYPE_FONTTYPE);
+        /* return if it is a bitmap not a unicode font */
+        return (!mUnicodeFont || mSymbolFont);
     }
 
     PRBool MatchesGenericFamily(const nsACString& aGeneric) const {
         if (aGeneric.IsEmpty())
             return PR_TRUE;
 
         // Japanese 'Mincho' fonts do not belong to FF_MODERN even if
         // they are fixed pitch because they have variable stroke width.
@@ -174,26 +174,31 @@ public:
         void SetWeight(PRUint8 aWeight, PRBool aValue) {
             mWeights[aWeight - 1] = aValue;
             mWeights[aWeight - 1 + 10] = PR_TRUE;
         }
     private:
         std::bitset<20> mWeights;
     };
 
+    // whether this font family is in "bad" underline offset blacklist.
+    PRBool IsBadUnderlineFont() { return mIsBadUnderlineFont != 0; }
+
     // The family name of the font
     nsString mName;
 
-    PRUint16 mFontType;
     PRUint16 mDefaultWeight;
 
     PRUint8 mFamily;
     PRUint8 mPitch;
+
     PRPackedBool mUnicodeFont;
     PRPackedBool mSymbolFont;
+    PRPackedBool mIsType1;
+    PRPackedBool mIsBadUnderlineFont;
 
     std::bitset<256> mCharset;
     std::bitset<128> mUnicodeRanges;
 
     WeightTable mWeightTable;
 
     gfxSparseBitSet mCharacterMap;
 };
--- a/gfx/thebes/public/gfxWindowsPlatform.h
+++ b/gfx/thebes/public/gfxWindowsPlatform.h
@@ -64,16 +64,18 @@ public:
                          nsStringArray& aListOfFonts);
 
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
     gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
                                   const gfxFontStyle *aStyle);
 
     /* Given a string and a font we already have find the font that
      * supports the most code points and most closely resembles aFont
      *
      * this involves looking at the fonts on your machine and seeing which
      * code points they support as well as looking at things like the font
@@ -85,16 +87,18 @@ public:
     FontEntry *FindFontEntry(const nsAString& aName);
 
     PRBool GetPrefFontEntries(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> > *array);
     void SetPrefFontEntries(const char *aLangGroup, nsTArray<nsRefPtr<FontEntry> >& array);
 
 private:
     void Init();
 
+    void InitBadUnderlineList();
+
     static int CALLBACK FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
                                      const NEWTEXTMETRICEXW *metrics,
                                      DWORD fontType, LPARAM data);
 
     static PLDHashOperator PR_CALLBACK FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
                                                            nsRefPtr<FontEntry>& aFontEntry,
                                                            void* userArg);
 
--- a/gfx/thebes/src/gfxAtsuiFonts.cpp
+++ b/gfx/thebes/src/gfxAtsuiFonts.cpp
@@ -226,17 +226,17 @@ gfxAtsuiFont::InitMetrics(ATSUFontID aFo
 
     mMetrics.strikeoutOffset = mMetrics.xHeight / 2.0;
     mMetrics.strikeoutSize = mMetrics.underlineSize;
 
     PRUint32 glyphID;
     mMetrics.spaceWidth = GetCharWidth(' ', &glyphID);
     mSpaceGlyph = glyphID;
 
-    SanitizeMetrics(&mMetrics);
+    SanitizeMetrics(&mMetrics, mFontEntry->FamilyEntry()->IsBadUnderlineFontFamily());
 
 #if 0
     fprintf (stderr, "Font: %p size: %f (fixed: %d)", this, size, gfxQuartzFontCache::SharedFontCache()->IsFixedPitch(aFontID));
     fprintf (stderr, "    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
     fprintf (stderr, "    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
     fprintf (stderr, "    internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
     fprintf (stderr, "    spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
     fprintf (stderr, "    uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset);
@@ -371,16 +371,22 @@ gfxAtsuiFont::HasMirroringInfo()
     
     return mHasMirroring;
 }
 
 PRBool gfxAtsuiFont::TestCharacterMap(PRUint32 aCh) {
     return mFontEntry->TestCharacterMap(aCh);
 }
 
+MacOSFontEntry*
+gfxAtsuiFont::GetFontEntry()
+{
+    return mFontEntry.get();
+}
+
 /**
  * Look up the font in the gfxFont cache. If we don't find it, create one.
  * In either case, add a ref and return it ---
  * except for OOM in which case we do nothing and return null.
  */
  
 static already_AddRefed<gfxAtsuiFont>
 GetOrMakeFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aStyle)
@@ -420,18 +426,28 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(con
         NS_ASSERTION(defaultFont, "invalid default font returned by GetDefaultFont");
 
         nsRefPtr<gfxAtsuiFont> font = GetOrMakeFont(defaultFont, aStyle);
 
         if (font) {
             mFonts.AppendElement(font);
         }
     }
-    
+
     mPageLang = gfxPlatform::GetFontPrefLangFor(mStyle.langGroup.get());
+
+    for (PRUint32 i = 0; i < mFonts.Length(); ++i) {
+        gfxAtsuiFont* font = static_cast<gfxAtsuiFont*>(mFonts[i].get());
+        if (font->GetFontEntry()->FamilyEntry()->IsBadUnderlineFontFamily()) {
+            gfxFloat first = mFonts[0]->GetMetrics().underlineOffset;
+            gfxFloat bad = font->GetMetrics().underlineOffset;
+            mUnderlineOffset = PR_MIN(first, bad);
+            break;
+        }
+    }
 }
 
 PRBool
 gfxAtsuiFontGroup::FindATSUFont(const nsAString& aName,
                                 const nsACString& aGenericName,
                                 void *closure)
 {
     gfxAtsuiFontGroup *fontGroup = (gfxAtsuiFontGroup*) closure;
--- a/gfx/thebes/src/gfxBeOSPlatform.cpp
+++ b/gfx/thebes/src/gfxBeOSPlatform.cpp
@@ -104,8 +104,14 @@ nsresult
 gfxBeOSPlatform::ResolveFontName(const nsAString& aFontName,
                                 FontResolverCallback aCallback,
                                 void *aClosure,
                                 PRBool& aAborted)
 {
     return sFontconfigUtils->ResolveFontName(aFontName, aCallback,
                                              aClosure, aAborted);
 }
+
+nsresult
+gfxBeOSPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
+}
--- a/gfx/thebes/src/gfxFont.cpp
+++ b/gfx/thebes/src/gfxFont.cpp
@@ -522,17 +522,17 @@ gfxFont::SetupGlyphExtents(gfxContext *a
 
     double d2a = appUnitsPerDevUnit;
     gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
                    extents.width*d2a, extents.height*d2a);
     aExtents->SetTightGlyphExtents(aGlyphID, bounds);
 }
 
 void
-gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics)
+gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, PRBool aIsBadUnderlineFont)
 {
     // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
     // If the values are not suitable, we should use x-height instead of them.
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
     if (aMetrics->superscriptOffset == 0 ||
         aMetrics->superscriptOffset >= aMetrics->maxAscent) {
         aMetrics->superscriptOffset = aMetrics->xHeight;
     }
@@ -542,22 +542,45 @@ gfxFont::SanitizeMetrics(gfxFont::Metric
         aMetrics->subscriptOffset = aMetrics->xHeight;
     }
 
     aMetrics->underlineSize = PR_MAX(1.0, aMetrics->underlineSize);
     aMetrics->strikeoutSize = PR_MAX(1.0, aMetrics->strikeoutSize);
 
     aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -1.0);
 
-    // XXX we need to adjust the underline offset for "bad" CJK fonts, here.
+    /**
+     * Some CJK fonts have bad underline offset. Therefore, if this is such font,
+     * we need to lower the underline offset to bottom of *em* descent.
+     * However, if this is system font, we should not do this for the rendering compatibility with
+     * another application's UI on the platform.
+     */
+    if (!mStyle.systemFont && aIsBadUnderlineFont) {
+        // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
+        // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
+        aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -2.0);
 
+        // Next, we put the underline to bottom of below of the descent space.
+        // Note that the underline might overlap to next line when the line height is 1em.
+        // However, in CJK text, such case is very rare, so, we don't need to worry about such case.
+        // Becasue most CJK glyphs use top of the em square. Therefore, for readability, CJK text needs
+        // larger line gap than Western text, generally.
+        if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
+            aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -aMetrics->emDescent);
+        } else {
+            aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset,
+                                               aMetrics->underlineSize - aMetrics->emDescent);
+        }
+    }
     // If underline positioned is too far from the text, descent position is preferred so that underline
     // will stay within the boundary.
-    if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent)
+    else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
         aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
+        aMetrics->underlineOffset = PR_MIN(aMetrics->underlineOffset, -1.0);
+    }
 }
 
 gfxGlyphExtents::~gfxGlyphExtents()
 {
 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
     gGlyphExtentsWidthsTotalSize += mContainedGlyphWidths.ComputeSize();
     gGlyphExtentsCount++;
 #endif
@@ -664,17 +687,17 @@ gfxGlyphExtents::SetTightGlyphExtents(PR
         return;
     entry->x = aExtentsAppUnits.pos.x;
     entry->y = aExtentsAppUnits.pos.y;
     entry->width = aExtentsAppUnits.size.width;
     entry->height = aExtentsAppUnits.size.height;
 }
 
 gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, const gfxFontStyle *aStyle)
-    : mFamilies(aFamilies), mStyle(*aStyle)
+    : mFamilies(aFamilies), mStyle(*aStyle), mUnderlineOffset(0)
 {
 
 }
 
 PRBool
 gfxFontGroup::ForEachFont(FontCreationCallback fc,
                           void *closure)
 {
--- a/gfx/thebes/src/gfxFontconfigUtils.cpp
+++ b/gfx/thebes/src/gfxFontconfigUtils.cpp
@@ -364,16 +364,125 @@ gfxFontconfigUtils::GetResolvedFonts(con
     if (pat)
         FcPatternDestroy(pat);
     if (fs)
         FcFontSetDestroy(fs);
     return rv;
 }
 
 nsresult
+gfxFontconfigUtils::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    aFamilyName.Truncate();
+
+    // The fontconfig has generic family names in the font list.
+    if (aFontName.EqualsLiteral("serif") ||
+        aFontName.EqualsLiteral("sans-serif") ||
+        aFontName.EqualsLiteral("monospace")) {
+        aFamilyName.Assign(aFontName);
+        return NS_OK;
+    }
+
+    NS_ConvertUTF16toUTF8 fontname(aFontName);
+
+    if (mFonts.IndexOf(fontname) >= 0) {
+        aFamilyName.Assign(aFontName);
+        return NS_OK;
+    }
+
+    if (mNonExistingFonts.IndexOf(fontname) >= 0)
+        return NS_OK;
+
+    FcPattern *pat = NULL;
+    FcObjectSet *os = NULL;
+    FcFontSet *givenFS = NULL;
+    nsCStringArray candidates;
+    FcFontSet *candidateFS = NULL;
+    nsresult rv = NS_ERROR_FAILURE;
+
+    pat = FcPatternCreate();
+    if (!pat)
+        goto end;
+
+    FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)fontname.get());
+
+    os = FcObjectSetBuild(FC_FAMILY, FC_FILE, FC_INDEX, NULL);
+    if (!os)
+        goto end;
+
+    givenFS = FcFontList(NULL, pat, os);
+    if (!givenFS)
+        goto end;
+
+    // The first value associated with a FC_FAMILY property is the family
+    // returned by GetFontList(), so use this value if appropriate.
+
+    // See if there is a font face with first family equal to the given family.
+    for (int i = 0; i < givenFS->nfont; ++i) {
+        char *firstFamily;
+        if (FcPatternGetString(givenFS->fonts[i], FC_FAMILY, 0,
+                               (FcChar8 **) &firstFamily) != FcResultMatch)
+            continue;
+
+        nsDependentCString first(firstFamily);
+        if (candidates.IndexOf(first) < 0) {
+            candidates.AppendCString(first);
+
+            if (fontname.Equals(first)) {
+                aFamilyName.Assign(aFontName);
+                rv = NS_OK;
+                goto end;
+            }
+        }
+    }
+
+    // See if any of the first family names represent the same set of font
+    // faces as the given family.
+    for (PRInt32 j = 0; j < candidates.Count(); ++j) {
+        FcPatternDel(pat, FC_FAMILY);
+        FcPatternAddString(pat, FC_FAMILY, (FcChar8 *)candidates[j]->get());
+
+        candidateFS = FcFontList(NULL, pat, os);
+        if (!candidateFS)
+            goto end;
+
+        if (candidateFS->nfont != givenFS->nfont)
+            continue;
+
+        PRBool equal = PR_TRUE;
+        for (int i = 0; i < givenFS->nfont; ++i) {
+            if (!FcPatternEqual(candidateFS->fonts[i], givenFS->fonts[i])) {
+                equal = PR_FALSE;
+                break;
+            }
+        }
+        if (equal) {
+            AppendUTF8toUTF16(*candidates[j], aFamilyName);
+            rv = NS_OK;
+            goto end;
+        }
+    }
+
+    // No match found; return empty string.
+    rv = NS_OK;
+
+  end:
+    if (pat)
+        FcPatternDestroy(pat);
+    if (os)
+        FcObjectSetDestroy(os);
+    if (givenFS)
+        FcFontSetDestroy(givenFS);
+    if (candidateFS)
+        FcFontSetDestroy(candidateFS);
+
+    return rv;
+}
+
+nsresult
 gfxFontconfigUtils::ResolveFontName(const nsAString& aFontName,
                                     gfxPlatform::FontResolverCallback aCallback,
                                     void *aClosure,
                                     PRBool& aAborted)
 {
     aAborted = PR_FALSE;
 
     nsresult rv = UpdateFontListInternal();
--- a/gfx/thebes/src/gfxFontconfigUtils.h
+++ b/gfx/thebes/src/gfxFontconfigUtils.h
@@ -70,16 +70,18 @@ public:
                          nsStringArray& aListOfFonts);
 
     nsresult UpdateFontList();
 
     nsresult ResolveFontName(const nsAString& aFontName,
                              gfxPlatform::FontResolverCallback aCallback,
                              void *aClosure, PRBool& aAborted);
 
+    nsresult GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
+
 protected:
     static gfxFontconfigUtils* sUtils;
 
     PRInt32 IsExistingFont(const nsACString& aFontName);
     nsresult GetResolvedFonts(const nsACString& aName,
                               gfxFontNameList* aResult);
 
     nsresult GetFontListInternal(nsCStringArray& aListOfFonts,
--- a/gfx/thebes/src/gfxOS2Fonts.cpp
+++ b/gfx/thebes/src/gfxOS2Fonts.cpp
@@ -177,17 +177,17 @@ const gfxFont::Metrics& gfxOS2Font::GetM
         double lineHeight = mMetrics->maxAscent + mMetrics->maxDescent;
         if (lineHeight > mMetrics->emHeight) {
             mMetrics->internalLeading = lineHeight - mMetrics->emHeight;
         } else {
             mMetrics->internalLeading = 0;
         }
         mMetrics->externalLeading = 0; // normal value for OS/2 fonts, too
 
-        SanitizeMetrics(mMetrics);
+        SanitizeMetrics(mMetrics, PR_FALSE);
 
 #ifdef DEBUG_thebes_1
         printf("gfxOS2Font[%#x]::GetMetrics():\n"
                "  %s (%s)\n"
                "  emHeight=%f == %f=gfxFont::style.size == %f=adjSz\n"
                "  maxHeight=%f  xHeight=%f\n"
                "  aveCharWidth=%f==xWidth  spaceWidth=%f\n"
                "  supOff=%f SubOff=%f   strOff=%f strSz=%f\n"
--- a/gfx/thebes/src/gfxOS2Platform.cpp
+++ b/gfx/thebes/src/gfxOS2Platform.cpp
@@ -142,14 +142,20 @@ gfxOS2Platform::ResolveFontName(const ns
     free(fontname);
 #endif
     return sFontconfigUtils->ResolveFontName(aFontName, aCallback, aClosure,
                                              aAborted);
     //aAborted = !(*aCallback)(aFontName, aClosure);
     //return NS_OK;
 }
 
+nsresult
+gfxOS2Platform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
+}
+
 gfxFontGroup *
 gfxOS2Platform::CreateFontGroup(const nsAString &aFamilies,
 				const gfxFontStyle *aStyle)
 {
     return new gfxOS2FontGroup(aFamilies, aStyle);
 }
--- a/gfx/thebes/src/gfxPangoFonts.cpp
+++ b/gfx/thebes/src/gfxPangoFonts.cpp
@@ -649,17 +649,17 @@ gfxPangoFont::GetMetrics()
         }
 
         pango_fc_font_unlock_face(PANGO_FC_FONT(font));
     } else {
         mMetrics.superscriptOffset = mMetrics.xHeight;
         mMetrics.subscriptOffset = mMetrics.xHeight;
     }
 
-    SanitizeMetrics(&mMetrics);
+    SanitizeMetrics(&mMetrics, PR_FALSE);
 
 #if 0
     //    printf("font name: %s %f %f\n", NS_ConvertUTF16toUTF8(mName).get(), GetStyle()->size, mAdjustedSize);
     //    printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
 
     fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(mName).get());
     fprintf (stderr, "    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
     fprintf (stderr, "    maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
--- a/gfx/thebes/src/gfxPlatformGtk.cpp
+++ b/gfx/thebes/src/gfxPlatformGtk.cpp
@@ -248,16 +248,22 @@ gfxPlatformGtk::ResolveFontName(const ns
                                 FontResolverCallback aCallback,
                                 void *aClosure,
                                 PRBool& aAborted)
 {
     return sFontconfigUtils->ResolveFontName(aFontName, aCallback,
                                              aClosure, aAborted);
 }
 
+nsresult
+gfxPlatformGtk::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    return sFontconfigUtils->GetStandardFamilyName(aFontName, aFamilyName);
+}
+
 gfxFontGroup *
 gfxPlatformGtk::CreateFontGroup(const nsAString &aFamilies,
                                 const gfxFontStyle *aStyle)
 {
     return new gfxPangoFontGroup(aFamilies, aStyle);
 }
 
 /* static */
--- a/gfx/thebes/src/gfxPlatformMac.cpp
+++ b/gfx/thebes/src/gfxPlatformMac.cpp
@@ -175,16 +175,23 @@ gfxPlatformMac::ResolveFontName(const ns
              ResolveFontName(aFontName, resolvedName)) {
         aAborted = PR_FALSE;
         return NS_OK;
     }
     aAborted = !(*aCallback)(resolvedName, aClosure);
     return NS_OK;
 }
 
+nsresult
+gfxPlatformMac::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    gfxQuartzFontCache::SharedFontCache()->GetStandardFamilyName(aFontName, aFamilyName);
+    return NS_OK;
+}
+
 gfxFontGroup *
 gfxPlatformMac::CreateFontGroup(const nsAString &aFamilies,
                                 const gfxFontStyle *aStyle)
 {
     return new gfxAtsuiFontGroup(aFamilies, aStyle);
 }
 
 nsresult
--- a/gfx/thebes/src/gfxQuartzFontCache.h
+++ b/gfx/thebes/src/gfxQuartzFontCache.h
@@ -67,36 +67,39 @@ class MacOSFamilyEntry;
 class gfxQuartzFontCache;
 
 // a single member of a font family (i.e. a single face, such as Times Italic)
 class MacOSFontEntry
 {
 public:
     THEBES_INLINE_DECL_REFCOUNTING(MacOSFontEntry)
 
+    friend class gfxQuartzFontCache;
+
     // initialize with Apple-type weight [1..14]
     MacOSFontEntry(const nsAString& aPostscriptName, PRInt32 aAppleWeight, PRUint32 aTraits, 
-                    MacOSFamilyEntry *aFamily);  
+                    MacOSFamilyEntry *aFamily);
 
     const nsString& Name() { return mPostscriptName; }
     const nsString& FamilyName();
     PRInt32 Weight() { return mWeight; }
     PRUint32 Traits() { return mTraits; }
     
     PRBool IsFixedPitch();
     PRBool IsItalicStyle();
     PRBool IsBold();
 
-    ATSUFontID GetFontID();                   
+    ATSUFontID GetFontID();
     nsresult ReadCMAP();
     inline PRBool TestCharacterMap(PRUint32 aCh) {
         if ( !mCmapInitialized ) ReadCMAP();
         return mCharacterMap.test(aCh);
     }
-        
+
+    MacOSFamilyEntry* FamilyEntry() { return mFamily; }
 protected:
     nsString mPostscriptName;
     PRInt32 mWeight; // CSS-type value: [1..9] which map to 100, 200, ..., 900
     PRUint32 mTraits;
     MacOSFamilyEntry *mFamily;
 
     ATSUFontID mATSUFontID;
     std::bitset<128> mUnicodeRanges;
@@ -110,24 +113,27 @@ protected:
 class AddOtherFamilyNameFunctor;
 
 // a single font family, referencing one or more faces 
 class MacOSFamilyEntry
 {
 public:
     THEBES_INLINE_DECL_REFCOUNTING(MacOSFamilyEntry)
 
+    friend class gfxQuartzFontCache;
+
     MacOSFamilyEntry(nsString &aName) :
-        mName(aName), mOtherFamilyNamesInitialized(PR_FALSE), mHasOtherFamilyNames(PR_FALSE)
+        mName(aName), mOtherFamilyNamesInitialized(PR_FALSE), mHasOtherFamilyNames(PR_FALSE),
+        mIsBadUnderlineFontFamily(PR_FALSE)
     {}
   
     virtual ~MacOSFamilyEntry() {}
         
     const nsString& Name() { return mName; }
-    virtual void LocalizedName(nsString& aLocalizedName);
+    virtual void LocalizedName(nsAString& aLocalizedName);
     virtual PRBool HasOtherFamilyNames();
     
     nsTArray<nsRefPtr<MacOSFontEntry> >& GetFontList() { return mAvailableFonts; }
     
     void AddFontEntry(nsRefPtr<MacOSFontEntry> aFontEntry) {
         mAvailableFonts.AppendElement(aFontEntry);
     }
     
@@ -139,45 +145,49 @@ public:
     // used as part of the font fallback process
     void FindFontForChar(FontSearch *aMatchData);
     
     // read in other family names, if any, and use functor to add each into cache
     virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor);
     
     // search for a specific face using the Postscript name
     MacOSFontEntry* FindFont(const nsString& aPostscriptName);
-    
+
+    // whether this font family is in "bad" underline offset blacklist.
+    PRBool IsBadUnderlineFontFamily() { return mIsBadUnderlineFontFamily != 0; }
+
 protected:
     
     // add font entries into array that match specified traits, returned in array listed by weight
     // i.e. aFontsForWeights[4] ==> pointer to the font entry for a 400-weight face on return
     // returns true if one or more faces found
     PRBool FindFontsWithTraits(MacOSFontEntry* aFontsForWeights[], PRUint32 aPosTraitsMask, 
                                 PRUint32 aNegTraitsMask);
 
     // choose font based on CSS font-weight selection rules, never null
     MacOSFontEntry* FindFontWeight(MacOSFontEntry* aFontsForWeights[], const gfxFontStyle* aStyle);
     
     nsString mName;  // canonical font family name returned from NSFontManager
     nsTArray<nsRefPtr<MacOSFontEntry> >  mAvailableFonts;
     PRPackedBool mOtherFamilyNamesInitialized;
     PRPackedBool mHasOtherFamilyNames;
+    PRPackedBool mIsBadUnderlineFontFamily;
 };
 
 // special-case situation where specific faces need to be treated as separate font family
 class SingleFaceFamily : public MacOSFamilyEntry
 {
 public:
     SingleFaceFamily(nsString &aName) :
         MacOSFamilyEntry(aName)
     {}
     
     virtual ~SingleFaceFamily() {}
     
-    virtual void LocalizedName(nsString& aLocalizedName);
+    virtual void LocalizedName(nsAString& aLocalizedName);
     
     // read in other family names, if any, and use functor to add each into cache
     virtual void ReadOtherFamilyNames(AddOtherFamilyNameFunctor& aOtherFamilyFunctor);
 };
 
 class gfxQuartzFontCache {
 public:
     static gfxQuartzFontCache* SharedFontCache() {
@@ -198,16 +208,17 @@ public:
 
     // methods used by gfxPlatformMac
     
     void GetFontList (const nsACString& aLangGroup,
                       const nsACString& aGenericFamily,
                       nsStringArray& aListOfFonts);
     PRBool ResolveFontName(const nsAString& aFontName,
                            nsAString& aResolvedFontName);
+    PRBool GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName);
     void UpdateFontList() { InitFontList(); }
 
 
     MacOSFontEntry* FindFontForChar(const PRUint32 aCh, gfxAtsuiFont *aPrevFont);
 
     MacOSFamilyEntry* FindFamily(const nsAString& aFamily);
     
     MacOSFontEntry* FindFontForFamily(const nsAString& aFamily, const gfxFontStyle* aStyle);
@@ -237,17 +248,20 @@ private:
     // separate initialization for reading in name tables, since this is expensive
     void InitOtherFamilyNames();
     
     // special case font faces treated as font families (set via prefs)
     void InitSingleFaceList();
     
     // commonly used fonts for which the name table should be loaded at startup
     void PreloadNamesList();
-    
+
+    // initialize the MacOSFamilyEntry::mIsBadUnderlineFontFamily from pref.
+    void InitBadUnderlineList();
+
     // eliminate faces which have the same ATSUI id
     void EliminateDuplicateFaces(const nsAString& aFamilyName);
                                                              
     static PLDHashOperator PR_CALLBACK InitOtherFamilyNamesProc(nsStringHashKey::KeyType aKey,
                                                              nsRefPtr<MacOSFamilyEntry>& aFamilyEntry,
                                                              void* userArg);
     
     void GenerateFontListKey(const nsAString& aKeyName, nsAString& aResult);
--- a/gfx/thebes/src/gfxQuartzFontCache.mm
+++ b/gfx/thebes/src/gfxQuartzFontCache.mm
@@ -251,17 +251,17 @@ public:
         
     void operator() (MacOSFamilyEntry *aFamilyEntry, nsAString& aOtherName) {
         mFontCache->AddOtherFamilyName(aFamilyEntry, aOtherName);
     }
     
     gfxQuartzFontCache *mFontCache;
 };
 
-void MacOSFamilyEntry::LocalizedName(nsString& aLocalizedName)
+void MacOSFamilyEntry::LocalizedName(nsAString& aLocalizedName)
 {
     // no other names ==> only one name, just return it
     if (!HasOtherFamilyNames()) {
         aLocalizedName = mName;
         return;
     }
     
     NSFontManager *fontManager = [NSFontManager sharedFontManager];
@@ -651,17 +651,17 @@ MacOSFamilyEntry::ReadOtherFamilyNames(A
             ReadOtherFamilyNamesForFace(aOtherFamilyFunctor, this, familyName, fe->GetFontID());
         }
     }
 }
 
 /* SingleFaceFamily */
 #pragma mark-
 
-void SingleFaceFamily::LocalizedName(nsString& aLocalizedName)
+void SingleFaceFamily::LocalizedName(nsAString& aLocalizedName)
 {
     MacOSFontEntry *fontEntry;
     
     // use the display name of the single face
     fontEntry = mAvailableFonts[0];
     if (!fontEntry) 
         return;
         
@@ -789,17 +789,18 @@ gfxQuartzFontCache::InitFontList()
     // even though only bold faces exist so test for this using ATSUI id's (10.5 has proper faces)
     EliminateDuplicateFaces(NS_LITERAL_STRING("Courier"));
     EliminateDuplicateFaces(NS_LITERAL_STRING("Helvetica"));
     
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
     mCodepointsWithNoFonts.set(0xfffd);          // unknown
-       
+
+    InitBadUnderlineList();
 }
 
 void 
 gfxQuartzFontCache::InitOtherFamilyNames()
 {
     mOtherFamilyNamesInitialized = PR_TRUE;
     
     // iterate over all font families and read in other family names
@@ -852,22 +853,23 @@ gfxQuartzFontCache::InitSingleFaceList()
                     nsAutoString displayName, key;
                     
                     // use the display name the canonical name
                     NSString *display = [font displayName];
                     GetStringForNSString(display, displayName);
                     GenerateFontListKey(displayName, key);
 
                     // add only if doesn't exist already
-                    if (!mFontFamilies.GetWeak(key, &found)) {
+                    if (!(familyEntry = mFontFamilies.GetWeak(key, &found))) {
                         familyEntry = new SingleFaceFamily(displayName);
                         familyEntry->AddFontEntry(fontEntry);
                         mFontFamilies.Put(key, familyEntry);
                         PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-singleface) family: %s, psname: %s\n", [display UTF8String], [faceName UTF8String]));
                     }
+                    fontEntry->mFamily = familyEntry;
                 }
             }
         }
         
     }
        
 }
 
@@ -938,27 +940,95 @@ gfxQuartzFontCache::EliminateDuplicateFa
             // the italic face is bogus so remove it
             if (italic && italic->GetFontID() == nonitalic->GetFontID()) {
                 fontlist.RemoveElementAt(italicIndex);
             }
         }
     }
 }
 
+void
+gfxQuartzFontCache::InitBadUnderlineList()
+{
+    nsAutoTArray<nsAutoString, 10> blacklist;
+    gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
+    PRUint32 numFonts = blacklist.Length();
+    for (PRUint32 i = 0; i < numFonts; i++) {
+        PRBool found;
+        nsAutoString key;
+        GenerateFontListKey(blacklist[i], key);
+
+        MacOSFamilyEntry *familyEntry = mFontFamilies.GetWeak(key, &found);
+        if (familyEntry)
+            familyEntry->mIsBadUnderlineFontFamily = 1;
+    }
+}
+
 PRBool 
 gfxQuartzFontCache::ResolveFontName(const nsAString& aFontName, nsAString& aResolvedFontName)
 {
     MacOSFamilyEntry *family = FindFamily(aFontName);
     if (family) {
         aResolvedFontName = family->Name();
         return PR_TRUE;
     }
     return PR_FALSE;
 }
 
+PRBool
+gfxQuartzFontCache::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    MacOSFamilyEntry *family = FindFamily(aFontName);
+    if (family) {
+        family->LocalizedName(aFamilyName);
+        return PR_TRUE;
+    }
+
+    // Gecko 1.8 used Quickdraw font api's which produce a slightly different set of "family"
+    // names.  Try to resolve based on these names, in case this is stored in an old profile
+    // 1.8: "Futura", "Futura Condensed" ==> 1.9: "Futura
+    FMFont fmFont;
+
+    // convert of a NSString
+    NSString *fontName = GetNSStringForString(aFontName);
+
+    // name ==> family id ==> old-style FMFont
+    ATSFontFamilyRef fmFontFamily = ATSFontFamilyFindFromName((CFStringRef)fontName, kATSOptionFlagsDefault);
+    OSStatus err = FMGetFontFromFontFamilyInstance(fmFontFamily, 0, &fmFont, nsnull);
+    if (err != noErr || fmFont == kInvalidFont)
+        return PR_FALSE;
+
+    ATSFontRef atsFont = FMGetATSFontRefFromFont(fmFont);
+    if (!atsFont)
+        return PR_FALSE;
+
+    NSString *psname;
+
+    // now lookup the Postscript name
+    err = ATSFontGetPostScriptName(atsFont, kATSOptionFlagsDefault, (CFStringRef*) (&psname));
+    if (err != noErr)
+        return PR_FALSE;
+
+    // given an NSFont instance, Cocoa api's return the canonical family name
+    NSString *canonicalfamily = [[NSFont fontWithName:psname size:0.0] familyName];
+    [psname release];
+
+    nsAutoString familyName;
+
+    // lookup again using the canonical family name
+    GetStringForNSString(canonicalfamily, familyName);
+    family = FindFamily(familyName);
+    if (family) {
+        family->LocalizedName(aFamilyName);
+        return PR_TRUE;
+    }
+
+    return PR_FALSE;
+}
+
 void
 gfxQuartzFontCache::ATSNotification(ATSFontNotificationInfoRef aInfo,
                                     void* aUserArg)
 {
     // xxx - should be carefully pruning the list of fonts, not rebuilding it from scratch
     gfxQuartzFontCache *qfc = (gfxQuartzFontCache*)aUserArg;
     qfc->UpdateFontList();
 }
--- a/gfx/thebes/src/gfxWindowsFonts.cpp
+++ b/gfx/thebes/src/gfxWindowsFonts.cpp
@@ -352,17 +352,17 @@ gfxWindowsFont::ComputeMetrics()
             mSpaceGlyph = glyph;
         }
     }
 
     SelectObject(dc, oldFont);
 
     ReleaseDC((HWND)nsnull, dc);
 
-    SanitizeMetrics(mMetrics);
+    SanitizeMetrics(mMetrics, mFontEntry->IsBadUnderlineFont());
 }
 
 void
 gfxWindowsFont::FillLogFont(gfxFloat aSize, PRInt16 aWeight)
 {
 #define CLIP_TURNOFF_FONTASSOCIATION 0x40
     
     mLogFont.lfHeight = (LONG)-ROUND(aSize);
@@ -500,16 +500,25 @@ gfxWindowsFontGroup::gfxWindowsFontGroup
             NS_ERROR("Failed to create font group");
             return;
         }
         nsRefPtr<FontEntry> fe = gfxWindowsPlatform::GetPlatform()->FindFontEntry(nsDependentString(logFont.lfFaceName));
         mFontEntries.AppendElement(fe);
     }
 
     mFonts.AppendElements(mFontEntries.Length());
+
+    for (PRUint32 i = 0; i < mFontEntries.Length(); ++i) {
+        if (mFontEntries[i]->IsBadUnderlineFont()) {
+            gfxFloat first = GetFontAt(0)->GetMetrics().underlineOffset;
+            gfxFloat bad = GetFontAt(i)->GetMetrics().underlineOffset;
+            mUnderlineOffset = PR_MIN(first, bad);
+            break;
+        }
+    }
 }
 
 gfxWindowsFontGroup::~gfxWindowsFontGroup()
 {
 }
 
 gfxWindowsFont *
 gfxWindowsFontGroup::GetFontAt(PRInt32 i)
@@ -898,19 +907,19 @@ public:
     ~UniscribeItem() {
         free(mAlternativeString);
     }
 
     /* possible return values:
      * S_OK - things succeeded
      * GDI_ERROR - things failed to shape.  Might want to try again after calling DisableShaping()
      */
-    HRESULT Shape() {
+
+    HRESULT ShapeUniscribe() {
         HRESULT rv;
-
         HDC shapeDC = nsnull;
 
         const PRUnichar *str = mAlternativeString ? mAlternativeString : mRangeString;
 
         mScriptItem->a.fLogicalOrder = PR_TRUE; 
         SCRIPT_ANALYSIS sa = mScriptItem->a;
         /*
           fLinkBefore and fLinkAfter in the SCRIPT_ANALYSIS structure refer to
@@ -946,30 +955,44 @@ public:
                     return GDI_ERROR;
                 }
 
                 SelectFont();
 
                 shapeDC = mDC;
                 continue;
             }
-#ifdef DEBUG_pavlov
-            if (rv == USP_E_SCRIPT_NOT_IN_FONT) {
-                ScriptGetCMap(mDC, mCurrentFont->ScriptCache(), str, mRangeString, 0, mGlyphs.Elements());
-                PRUnichar foo[LF_FACESIZE+1];
-                GetTextFaceW(mDC, LF_FACESIZE, foo);
-                printf("bah\n");
-            }
-            else if (FAILED(rv))
-                printf("%d\n", rv);
-#endif
+
             return rv;
         }
     }
 
+    HRESULT ShapeGDI() {
+        SelectFont();
+
+        mGlyphs.SetLength(mRangeLength);
+        mNumGlyphs = mRangeLength;
+        GetGlyphIndicesW(mDC, mRangeString, mRangeLength,
+                         (WORD*) mGlyphs.Elements(),
+                         GGI_MARK_NONEXISTING_GLYPHS);
+
+        for (PRUint32 i = 0; i < mItemLength; ++i)
+            mClusters[i] = i;
+
+        return S_OK;
+    }
+
+    HRESULT Shape() {
+        /* Type1 fonts don't like Uniscribe */
+        if (mCurrentFont->GetFontEntry()->mIsType1)
+            return ShapeGDI();
+
+        return ShapeUniscribe();
+    }
+
     PRBool ShapingEnabled() {
         return (mScriptItem->a.eScript != SCRIPT_UNDEFINED);
     }
     void DisableShaping() {
         mScriptItem->a.eScript = SCRIPT_UNDEFINED;
         // Note: If we disable the shaping by using SCRIPT_UNDEFINED and
         // the string has the surrogate pair, ScriptShape API is
         // *sometimes* crashed. Therefore, we should replace the surrogate
@@ -1039,26 +1062,31 @@ public:
         return 0;
     }
 
     HRESULT Place() {
         mOffsets.SetLength(mNumGlyphs);
         mAdvances.SetLength(mNumGlyphs);
 
         PRBool allCJK = PR_TRUE;
-        for (PRUint32 i = 0; i < mRangeLength; i++) {
-            const PRUnichar ch = mRangeString[i];
-            if (ch == ' ' || FindCharUnicodeRange(ch) == kRangeSetCJK)
-                continue;
 
-            allCJK = PR_FALSE;
-            break;
+        /* Type1 fonts need to use GDI to be rendered so only do this
+         * check if we're not a type1 font */
+        if (!mCurrentFont->GetFontEntry()->mIsType1) {
+            for (PRUint32 i = 0; i < mRangeLength; i++) {
+                const PRUnichar ch = mRangeString[i];
+                if (ch == ' ' || FindCharUnicodeRange(ch) == kRangeSetCJK)
+                    continue;
+
+                allCJK = PR_FALSE;
+                break;
+            }
         }
 
-        if (allCJK)
+        if (allCJK || mCurrentFont->GetFontEntry()->mIsType1)
             return PlaceGDI();
 
         return PlaceUniscribe();
     }
 
     const SCRIPT_PROPERTIES *ScriptProperties() {
         /* we can use this to figure out in some cases the language of the item */
         static const SCRIPT_PROPERTIES **gScriptProperties;
--- a/gfx/thebes/src/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/src/gfxWindowsPlatform.cpp
@@ -117,34 +117,32 @@ gfxWindowsPlatform::FontEnumProc(const E
         return 1;
     }
 
     nsString name(logFont.lfFaceName);
     ToLowerCase(name);
 
     nsRefPtr<FontEntry> fe;
     if (!thisp->mFonts.Get(name, &fe)) {
-        fe = new FontEntry(nsDependentString(logFont.lfFaceName), (PRUint16)fontType);
+        fe = new FontEntry(nsDependentString(logFont.lfFaceName));
         thisp->mFonts.Put(name, fe);
     }
 
+    if (metrics.ntmFlags & NTM_TYPE1)
+        fe->mIsType1 = PR_TRUE;
+
     // mark the charset bit
     fe->mCharset[metrics.tmCharSet] = 1;
 
     // put the default weight in the weight table
     fe->mWeightTable.SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
 
     // store the default font weight
     fe->mDefaultWeight = metrics.tmWeight;
 
-    if (metrics.ntmFlags & NTM_TYPE1) {
-        fe->mSymbolFont = PR_TRUE;
-        fe->mUnicodeFont = PR_FALSE;
-    }
-
     fe->mFamily = logFont.lfPitchAndFamily & 0xF0;
     fe->mPitch = logFont.lfPitchAndFamily & 0x0F;
 
     if (nmetrics->ntmFontSig.fsUsb[0] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[1] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[2] == 0x00000000 &&
         nmetrics->ntmFontSig.fsUsb[3] == 0x00000000) {
         // no unicode ranges
@@ -208,18 +206,20 @@ gfxWindowsPlatform::FontGetCMapDataProc(
     HFONT font = CreateFontIndirectW(&logFont);
 
     if (font) {
         HFONT oldFont = (HFONT)SelectObject(hdc, font);
 
         nsresult rv = ReadCMAP(hdc, aFontEntry);
 
         if (NS_FAILED(rv)) {
+            if (aFontEntry->mIsType1)
+                aFontEntry->mSymbolFont = PR_TRUE;
             aFontEntry->mUnicodeFont = PR_FALSE;
-            //printf("%s failed to get cmap\n", NS_ConvertUTF16toUTF8(aFontEntry->mName).get());
+            //printf("%d, %s failed to get cmap\n", aFontEntry->mIsType1, NS_ConvertUTF16toUTF8(aFontEntry->mName).get());
         }
 
         SelectObject(hdc, oldFont);
         DeleteObject(font);
     }
 
     ReleaseDC(nsnull, hdc);
 
@@ -359,19 +359,55 @@ gfxWindowsPlatform::UpdateFontList()
         else
             mNonExistingFonts.AppendString(substituteName);
     }
 
     // initialize ranges of characters for which system-wide font search should be skipped
     mCodepointsWithNoFonts.SetRange(0,0x1f);     // C0 controls
     mCodepointsWithNoFonts.SetRange(0x7f,0x9f);  // C1 controls
 
+    InitBadUnderlineList();
+
     return NS_OK;
 }
 
+static PRBool SimpleResolverCallback(const nsAString& aName, void* aClosure)
+{
+    nsString* result = static_cast<nsString*>(aClosure);
+    result->Assign(aName);
+    return PR_FALSE;
+}
+
+void
+gfxWindowsPlatform::InitBadUnderlineList()
+{
+    nsAutoTArray<nsAutoString, 10> blacklist;
+    gfxFontUtils::GetPrefsFontList("font.blacklist.underline_offset", blacklist);
+    PRUint32 numFonts = blacklist.Length();
+    for (PRUint32 i = 0; i < numFonts; i++) {
+        PRBool aborted;
+        nsAutoString resolved;
+        ResolveFontName(blacklist[i], SimpleResolverCallback, &resolved, aborted);
+        if (resolved.IsEmpty())
+            continue;
+        FontEntry* fe = FindFontEntry(resolved);
+        if (!fe)
+            continue;
+        fe->mIsBadUnderlineFont = 1;
+    }
+}
+
+nsresult
+gfxWindowsPlatform::GetStandardFamilyName(const nsAString& aFontName, nsAString& aFamilyName)
+{
+    aFamilyName.Truncate();
+    PRBool aborted;
+    return ResolveFontName(aFontName, SimpleResolverCallback, &aFamilyName, aborted);
+}
+
 struct ResolveData {
     ResolveData(gfxPlatform::FontResolverCallback aCallback,
                 gfxWindowsPlatform *aCaller, const nsAString *aFontName,
                 void *aClosure) :
         mFoundCount(0), mCallback(aCallback), mCaller(aCaller),
         mFontName(aFontName), mClosure(aClosure) {}
     PRUint32 mFoundCount;
     gfxPlatform::FontResolverCallback mCallback;
--- a/intl/locale/src/PluralForm.jsm
+++ b/intl/locale/src/PluralForm.jsm
@@ -29,17 +29,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-EXPORTED_SYMBOLS = [ "PluralForm" ];
+var EXPORTED_SYMBOLS = [ "PluralForm" ];
 
 /**
  * This module provides the PluralForm object which contains a method to figure
  * out which plural form of a word to use for a given number based on the
  * current localization. There is also a makeGetter method that creates a get
  * function for the desired plural rule. This is useful for extensions that
  * specify their own plural rule instead of relying on the browser default.
  * (I.e., the extension hasn't been localized to the browser's locale.)
--- a/jpeg/jdapimin.c
+++ b/jpeg/jdapimin.c
@@ -16,17 +16,58 @@
  * case.
  */
 
 #define JPEG_INTERNALS
 #include "jinclude.h"
 #include "jpeglib.h"
 
 #ifdef HAVE_MMX_INTEL_MNEMONICS
+#if _MSC_VER >= 1400
 #include "intrin.h"
+#else
+/* no __cpuid intrinsic, use a manually rewritten replacement */
+void __stdcall __cpuid( int CPUInfo[4], int InfoType )
+{
+  int my_eax = 0, my_ebx = 0, my_ecx = 0, my_edx = 0;
+  __asm {
+    /* check eflags bit 21 to see if cpuid is supported */
+    pushfd             /* save eflags to stack */
+    pop eax            /* and put it in eax */
+    mov ecx, eax       /* save a copy in ecx to compare against */
+    xor eax, 0x200000  /* toggle ID bit (bit 21) in eflags */
+    push eax           /* save modified eflags to stack */
+    popfd              /* set eflags register with modified value */
+    pushfd             /* read eflags back out */
+    pop eax
+    xor eax, ecx       /* check for modified eflags */
+    jz NOT_SUPPORTED   /* cpuid not supported */
+
+    /* check to see if the requested cpuid type is supported */
+    xor eax, eax       /* set eax to zero */
+    cpuid
+    cmp eax, InfoType
+    jl NOT_SUPPORTED   /* the requested cpuid type is not supported */
+
+    /* actually make the cpuid call */
+    mov eax, InfoType
+    cpuid
+    mov my_eax, eax
+    mov my_ebx, ebx
+    mov my_ecx, ecx
+    mov my_edx, edx
+NOT_SUPPORTED:
+  }
+  CPUInfo[0] = my_eax;
+  CPUInfo[1] = my_ebx;
+  CPUInfo[2] = my_ecx;
+  CPUInfo[3] = my_edx;
+}
+#endif /* _MSC_VER >= 1400 */
+
 int MMXAvailable;
 static int mmxsupport();
 #endif
 
 #ifdef HAVE_SSE2_INTRINSICS
 int SSE2Available = 0;
 #ifdef HAVE_SSE2_INTEL_MNEMONICS
 static int sse2support();
--- a/js/src/js.cpp
+++ b/js/src/js.cpp
@@ -2475,17 +2475,18 @@ EvalInContext(JSContext *cx, JSObject *o
         *rval = OBJECT_TO_JSVAL(sobj);
         ok = JS_TRUE;
     } else {
         fp = JS_GetScriptedCaller(cx, NULL);
         JS_SetGlobalObject(scx, sobj);
         JS_ToggleOptions(scx, JSOPTION_DONT_REPORT_UNCAUGHT);
         ok = JS_EvaluateUCScript(scx, sobj, src, srclen,
                                  fp->script->filename,
-                                 JS_PCToLineNumber(cx, fp->script, fp->pc),
+                                 JS_PCToLineNumber(cx, fp->script,
+                                                   fp->regs->pc),
                                  rval);
         if (!ok) {
             if (JS_GetPendingException(scx, &v))
                 JS_SetPendingException(cx, v);
             else
                 JS_ReportOutOfMemory(cx);
         }
     }
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -80,17 +80,17 @@
  */
 
 MSG_DEF(JSMSG_NOT_AN_ERROR,             0, 0, JSEXN_NONE, "<Error #0 is reserved>")
 MSG_DEF(JSMSG_NOT_DEFINED,              1, 1, JSEXN_REFERENCEERR, "{0} is not defined")
 MSG_DEF(JSMSG_INACTIVE,                 2, 0, JSEXN_INTERNALERR, "nothing active on context")
 MSG_DEF(JSMSG_MORE_ARGS_NEEDED,         3, 3, JSEXN_TYPEERR, "{0} requires more than {1} argument{2}")
 MSG_DEF(JSMSG_BAD_CHAR,                 4, 1, JSEXN_INTERNALERR, "invalid format character {0}")
 MSG_DEF(JSMSG_BAD_TYPE,                 5, 1, JSEXN_TYPEERR, "unknown type {0}")
-MSG_DEF(JSMSG_UNUSED6,                  6, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_ALLOC_OVERFLOW,           6, 0, JSEXN_INTERNALERR, "allocation size overflow")
 MSG_DEF(JSMSG_CANT_UNLOCK,              7, 0, JSEXN_INTERNALERR, "can't unlock memory")
 MSG_DEF(JSMSG_INCOMPATIBLE_PROTO,       8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}")
 MSG_DEF(JSMSG_NO_CONSTRUCTOR,           9, 1, JSEXN_TYPEERR, "{0} has no constructor")
 MSG_DEF(JSMSG_CANT_ALIAS,              10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}")
 MSG_DEF(JSMSG_NOT_SCRIPTED_FUNCTION,   11, 1, JSEXN_TYPEERR, "{0} is not a scripted function")
 MSG_DEF(JSMSG_BAD_SORT_ARG,            12, 0, JSEXN_TYPEERR, "invalid Array.prototype.sort argument")
 MSG_DEF(JSMSG_BAD_ATOMIC_NUMBER,       13, 1, JSEXN_INTERNALERR, "internal error: no index for atom {0}")
 MSG_DEF(JSMSG_TOO_MANY_LITERALS,       14, 0, JSEXN_INTERNALERR, "too many literals")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5113,23 +5113,22 @@ JS_IsConstructing(JSContext *cx)
 {
     return cx->fp && (cx->fp->flags & JSFRAME_CONSTRUCTING);
 }
 
 JS_FRIEND_API(JSBool)
 JS_IsAssigning(JSContext *cx)
 {
     JSStackFrame *fp;
-    jsbytecode *pc;
 
     for (fp = cx->fp; fp && !fp->script; fp = fp->down)
         continue;
-    if (!fp || !(pc = fp->pc))
+    if (!fp || !fp->regs)
         return JS_FALSE;
-    return (js_CodeSpec[*pc].format & JOF_ASSIGNING) != 0;
+    return (js_CodeSpec[*fp->regs->pc].format & JOF_ASSIGNING) != 0;
 }
 
 JS_PUBLIC_API(void)
 JS_SetCallReturnValue2(JSContext *cx, jsval v)
 {
 #if JS_HAS_LVALUE_RETURN
     cx->rval2 = v;
     cx->rval2set = JS_TRUE;
@@ -5519,16 +5518,22 @@ JS_ReportErrorFlagsAndNumberUC(JSContext
 }
 
 JS_PUBLIC_API(void)
 JS_ReportOutOfMemory(JSContext *cx)
 {
     js_ReportOutOfMemory(cx);
 }
 
+JS_PUBLIC_API(void)
+JS_ReportAllocationOverflow(JSContext *cx)
+{
+    js_ReportAllocationOverflow(cx);
+}
+
 JS_PUBLIC_API(JSErrorReporter)
 JS_SetErrorReporter(JSContext *cx, JSErrorReporter er)
 {
     JSErrorReporter older;
 
     older = cx->errorReporter;
     cx->errorReporter = er;
     return older;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2462,16 +2462,22 @@ JS_ReportErrorFlagsAndNumberUC(JSContext
                                const uintN errorNumber, ...);
 
 /*
  * Complain when out of memory.
  */
 extern JS_PUBLIC_API(void)
 JS_ReportOutOfMemory(JSContext *cx);
 
+/*
+ * Complain when an allocation size overflows the maximum supported limit.
+ */
+extern JS_PUBLIC_API(void)
+JS_ReportAllocationOverflow(JSContext *cx);
+
 struct JSErrorReport {
     const char      *filename;      /* source file name, URL, etc., or null */
     uintN           lineno;         /* source line number */
     const char      *linebuf;       /* offending source line without final \n */
     const char      *tokenptr;      /* pointer to error token in linebuf */
     const jschar    *uclinebuf;     /* unicode (original) line buffer */
     const jschar    *uctokenptr;    /* unicode (original) token pointer */
     uintN           flags;          /* error/warning, etc. */
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -324,17 +324,17 @@ ResizeSlots(JSContext *cx, JSObject *obj
         if (obj->dslots) {
             JS_free(cx, obj->dslots - 1);
             obj->dslots = NULL;
         }
         return JS_TRUE;
     }
 
     if (len > ~(uint32)0 / sizeof(jsval)) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
     slots = obj->dslots ? obj->dslots - 1 : NULL;
     newslots = (jsval *) JS_realloc(cx, slots, sizeof (jsval) * (len + 1));
     if (!newslots)
         return JS_FALSE;
 
@@ -1793,17 +1793,17 @@ array_sort(JSContext *cx, uintN argc, js
     /*
      * We need a temporary array of 2 * len jsvals to hold the array elements
      * and the scratch space for merge sort. Check that its size does not
      * overflow size_t, which would allow for indexing beyond the end of the
      * malloc'd vector.
      */
 #if JS_BITS_PER_WORD == 32
     if ((size_t)len > ~(size_t)0 / (2 * sizeof(jsval))) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 #endif
     vec = (jsval *) JS_malloc(cx, 2 * (size_t) len * sizeof(jsval));
     if (!vec)
         return JS_FALSE;
 
     /*
@@ -1897,17 +1897,17 @@ array_sort(JSContext *cx, uintN argc, js
              * scratch space for the merge sort. Since vec already contains
              * the rooted scratch space for newlen elements at the tail, we
              * can use it to rearrange and convert to strings first and try
              * realloc only when we know that we successfully converted all
              * the elements.
              */
 #if JS_BITS_PER_WORD == 32
             if ((size_t)newlen > ~(size_t)0 / (4 * sizeof(jsval))) {
-                JS_ReportOutOfMemory(cx);
+                js_ReportAllocationOverflow(cx);
                 ok = JS_FALSE;
                 goto out;
             }
 #endif
 
             /*
              * Rearrange and string-convert the elements of the vector from
              * the tail here and, after sorting, move the results back
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -885,19 +885,19 @@ js_ReportOutOfMemory(JSContext *cx)
     report.flags = JSREPORT_ERROR;
     report.errorNumber = JSMSG_OUT_OF_MEMORY;
 
     /*
      * Walk stack until we find a frame that is associated with some script
      * rather than a native frame.
      */
     for (fp = cx->fp; fp; fp = fp->down) {
-        if (fp->script && fp->pc) {
+        if (fp->regs) {
             report.filename = fp->script->filename;
-            report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
+            report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
             break;
         }
     }
 
     /*
      * If debugErrorHook is present then we give it a chance to veto sending
      * the error on to the regular ErrorReporter. We also clear a pending
      * exception if any now so the hooks can replace the out-of-memory error
@@ -924,16 +924,22 @@ js_ReportOutOfScriptQuota(JSContext *cx)
 }
 
 void
 js_ReportOverRecursed(JSContext *cx)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 }
 
+void
+js_ReportAllocationOverflow(JSContext *cx)
+{
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ALLOC_OVERFLOW);
+}
+
 JSBool
 js_ReportErrorVA(JSContext *cx, uintN flags, const char *format, va_list ap)
 {
     char *message;
     jschar *ucmessage;
     size_t messagelen;
     JSStackFrame *fp;
     JSErrorReport report;
@@ -949,19 +955,19 @@ js_ReportErrorVA(JSContext *cx, uintN fl
 
     memset(&report, 0, sizeof (struct JSErrorReport));
     report.flags = flags;
     report.errorNumber = JSMSG_USER_DEFINED_ERROR;
     report.ucmessage = ucmessage = js_InflateString(cx, message, &messagelen);
 
     /* Find the top-most active script frame, for best line number blame. */
     for (fp = cx->fp; fp; fp = fp->down) {
-        if (fp->script && fp->pc) {
+        if (fp->regs) {
             report.filename = fp->script->filename;
-            report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
+            report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
             break;
         }
     }
 
     warning = JSREPORT_IS_WARNING(report.flags);
     if (warning && JS_HAS_WERROR_OPTION(cx)) {
         report.flags &= ~JSREPORT_WARNING;
         warning = JS_FALSE;
@@ -1161,19 +1167,19 @@ js_ReportErrorNumberVA(JSContext *cx, ui
     report.flags = flags;
     report.errorNumber = errorNumber;
 
     /*
      * If we can't find out where the error was based on the current frame,
      * see if the next frame has a script/pc combo we can use.
      */
     for (fp = cx->fp; fp; fp = fp->down) {
-        if (fp->script && fp->pc) {
+        if (fp->regs) {
             report.filename = fp->script->filename;
-            report.lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
+            report.lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
             break;
         }
     }
 
     if (!js_ExpandErrorArguments(cx, callback, userRef, errorNumber,
                                  &message, &report, &warning, charArgs, ap)) {
         return JS_FALSE;
     }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -988,16 +988,19 @@ js_ReportOutOfMemory(JSContext *cx);
  * Report that cx->scriptStackQuota is exhausted.
  */
 extern void
 js_ReportOutOfScriptQuota(JSContext *cx);
 
 extern void
 js_ReportOverRecursed(JSContext *cx);
 
+extern void
+js_ReportAllocationOverflow(JSContext *cx);
+
 #define JS_CHECK_RECURSION(cx, onerror)                                       \
     JS_BEGIN_MACRO                                                            \
         int stackDummy_;                                                      \
                                                                               \
         if (!JS_CHECK_STACK_SIZE(cx, stackDummy_)) {                          \
             js_ReportOverRecursed(cx);                                        \
             onerror;                                                          \
         }                                                                     \
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -553,16 +553,17 @@ js_watch_set(JSContext *cx, JSObject *ob
                 JSClass *clasp;
                 JSFunction *fun;
                 JSScript *script;
                 JSBool injectFrame;
                 uintN nslots;
                 jsval smallv[5];
                 jsval *argv;
                 JSStackFrame frame;
+                JSFrameRegs regs;
 
                 closure = (JSObject *) wp->closure;
                 clasp = OBJ_GET_CLASS(cx, closure);
                 if (clasp == &js_FunctionClass) {
                     fun = GET_FUNCTION_PRIVATE(cx, closure);
                     script = FUN_SCRIPT(fun);
                 } else if (clasp == &js_ScriptClass) {
                     fun = NULL;
@@ -595,20 +596,23 @@ js_watch_set(JSContext *cx, JSObject *ob
                     }
 
                     argv[0] = OBJECT_TO_JSVAL(closure);
                     argv[1] = JSVAL_NULL;
                     memset(argv + 2, 0, (nslots - 2) * sizeof(jsval));
 
                     memset(&frame, 0, sizeof(frame));
                     frame.script = script;
+                    frame.regs = NULL;
                     if (script) {
                         JS_ASSERT(script->length >= JSOP_STOP_LENGTH);
-                        frame.pc = script->code + script->length
-                                   - JSOP_STOP_LENGTH;
+                        regs.pc = script->code + script->length
+                                  - JSOP_STOP_LENGTH;
+                        regs.sp = NULL;
+                        frame.regs = &regs;
                     }
                     frame.callee = closure;
                     frame.fun = fun;
                     frame.argv = argv + 2;
                     frame.down = cx->fp;
                     frame.scopeChain = OBJ_GET_PARENT(cx, closure);
 
                     cx->fp = &frame;
@@ -951,17 +955,17 @@ JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
 {
     return fp->script;
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
 {
-    return fp->pc;
+    return fp->regs ? fp->regs->pc : NULL;
 }
 
 JS_PUBLIC_API(JSStackFrame *)
 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
 {
     if (!fp)
         fp = cx->fp;
     while (fp) {
--- a/js/src/jsdtracef.c
+++ b/js/src/jsdtracef.c
@@ -69,18 +69,18 @@ jsdtrace_filename(JSStackFrame *fp)
            : dempty;
 }
 
 int
 jsdtrace_linenumber(JSContext *cx, JSStackFrame *fp)
 {
     while (fp && fp->script == NULL)
         fp = fp->down;
-    return (fp && fp->script && fp->pc)
-           ? js_PCToLineNumber(cx, fp->script, fp->pc)
+    return (fp && fp->regs)
+           ? js_PCToLineNumber(cx, fp->script, fp->regs->pc)
            : -1;
 }
 
 /*
  * This function is used to convert function arguments and return value (jsval)
  * into the following based on each value's type tag:
  *
  *      jsval      returned
@@ -126,39 +126,41 @@ jsdtrace_jsvaltovoid(JSContext *cx, jsva
     /* NOTREACHED */
 }
 
 char *
 jsdtrace_function_name(JSContext *cx, JSStackFrame *fp, JSFunction *fun)
 {
     JSAtom *atom;
     JSScript *script;
+    JSFrameRegs *regs;
     jsbytecode *pc;
     char *name;
 
     atom = fun->atom;
     if (!atom) {
         if (fp->fun != fun || !fp->down)
             return dempty;
 
         script = fp->down->script;
-        pc = fp->down->pc;
-        if (!script || !pc)
+        regs = fp->down->regs;
+        if (!regs)
             return dempty;
 
         /*
          * An anonymous function called from an active script or interpreted
          * function: try to fetch the variable or property name by which the
          * anonymous function was invoked. First handle call ops by recovering
          * the generating pc for the callee expression at argv[-2].
          */
+        pc = regs->pc;
         switch ((JSOp) *pc) {
           case JSOP_CALL:
           case JSOP_EVAL:
-            JS_ASSERT(fp->argv == fp->down->sp - (int)GET_ARGC(pc));
+            JS_ASSERT(fp->argv == fp->down->regs->sp - (int)GET_ARGC(pc));
 
             pc = (jsbytecode *) fp->argv[-2 - (int)script->depth];
 
             /*
              * Be paranoid about bugs to-do with generating pc storage when
              * attempting to descend into the operand stack basement.
              */
             if ((uintptr_t)(pc - script->code) >= script->length)
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -291,17 +291,17 @@ InitExnPrivate(JSContext *cx, JSObject *
     fpstop = fp;
 
     size = offsetof(JSExnPrivate, stackElems);
     overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
     size += stackDepth * sizeof(JSStackTraceElem);
     overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
     size += valueCount * sizeof(jsval);
     if (overflow) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
     priv = (JSExnPrivate *)JS_malloc(cx, size);
     if (!priv)
         return JS_FALSE;
 
     /*
      * We initialize errorReport with a copy of report after setting the
@@ -327,18 +327,18 @@ InitExnPrivate(JSContext *cx, JSObject *
             elem->argc = fp->argc;
             memcpy(values, fp->argv, fp->argc * sizeof(jsval));
             values += fp->argc;
         }
         elem->ulineno = 0;
         elem->filename = NULL;
         if (fp->script) {
             elem->filename = fp->script->filename;
-            if (fp->pc)
-                elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->pc);
+            if (fp->regs)
+                elem->ulineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
         }
         ++elem;
     }
     JS_ASSERT(priv->stackElems + stackDepth == elem);
     JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
 
     STOBJ_SET_SLOT(exnObject, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(priv));
 
@@ -794,17 +794,19 @@ Exception(JSContext *cx, JSObject *obj, 
     /* Set the 'lineNumber' property. */
     if (argc > 2) {
         lineno = js_ValueToECMAUint32(cx, &argv[2]);
         if (JSVAL_IS_NULL(argv[2]))
             return JS_FALSE;
     } else {
         if (!fp)
             fp = JS_GetScriptedCaller(cx, NULL);
-        lineno = (fp && fp->pc) ? js_PCToLineNumber(cx, fp->script, fp->pc) : 0;
+        lineno = (fp && fp->regs)
+                 ? js_PCToLineNumber(cx, fp->script, fp->regs->pc)
+                 : 0;
     }
 
     return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) ||
             InitExnPrivate(cx, obj, message, filename, lineno, NULL);
 }
 
 /*
  * Convert to string.
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1796,27 +1796,27 @@ Function(JSContext *cx, JSObject *obj, u
 
             /*
              * Check for overflow.  The < test works because the maximum
              * JSString length fits in 2 fewer bits than size_t has.
              */
             old_args_length = args_length;
             args_length = old_args_length + JSSTRING_LENGTH(arg);
             if (args_length < old_args_length) {
-                JS_ReportOutOfMemory(cx);
+                js_ReportAllocationOverflow(cx);
                 return JS_FALSE;
             }
         }
 
         /* Add 1 for each joining comma and check for overflow (two ways). */
         old_args_length = args_length;
         args_length = old_args_length + n - 1;
         if (args_length < old_args_length ||
             args_length >= ~(size_t)0 / sizeof(jschar)) {
-            JS_ReportOutOfMemory(cx);
+            js_ReportAllocationOverflow(cx);
             return JS_FALSE;
         }
 
         /*
          * Allocate a string to hold the concatenated arguments, including room
          * for a terminating 0.  Mark cx->tempPool for later release, to free
          * collected_args and its tokenstream in one swoop.
          */
@@ -2196,35 +2196,36 @@ js_ValueToCallableObject(JSContext *cx, 
 
 void
 js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
 {
     JSStackFrame *fp;
     uintN error;
     const char *name, *source;
 
-    for (fp = cx->fp; fp && !fp->spbase; fp = fp->down)
+    for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
         continue;
     name = NULL;
     source = NULL;
     if (flags & JSV2F_ITERATOR) {
         error = JSMSG_BAD_ITERATOR;
         name = js_iterator_str;
         source = js_ValueToPrintableSource(cx, *vp);
         if (!source)
             return;
     } else if (flags & JSV2F_CONSTRUCT) {
         error = JSMSG_NOT_CONSTRUCTOR;
     } else {
         error = JSMSG_NOT_FUNCTION;
     }
 
     js_ReportValueError3(cx, error,
-                         (fp && fp->spbase <= vp && vp < fp->sp)
-                         ? vp - fp->sp
+                         (fp && fp->regs &&
+                          fp->spbase <= vp && vp < fp->regs->sp)
+                         ? vp - fp->regs->sp
                          : (flags & JSV2F_SEARCH_STACK)
                          ? JSDVG_SEARCH_STACK
                          : JSDVG_IGNORE_STACK,
                          *vp, NULL,
                          name, source);
 }
 
 /*
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -87,18 +87,20 @@
 # define HAS_POSIX_MEMALIGN 0
 #endif
 
 /*
  * jemalloc provides posix_memalign but the function has to be explicitly
  * declared on Windows.
  */
 #if HAS_POSIX_MEMALIGN && MOZ_MEMORY_WINDOWS
+JS_BEGIN_EXTERN_C
 extern int
 posix_memalign(void **memptr, size_t alignment, size_t size);
+JS_END_EXTERN_C
 #endif
 
 /*
  * Include the headers for mmap unless we have posix_memalign and do not
  * insist on mmap.
  */
 #if JS_GC_USE_MMAP || (!defined JS_GC_USE_MMAP && !HAS_POSIX_MEMALIGN)
 # if defined(XP_WIN)
@@ -2653,19 +2655,21 @@ js_TraceStackFrame(JSTracer *trc, JSStac
     if (fp->varobj)
         JS_CALL_OBJECT_TRACER(trc, fp->varobj, "variables");
     if (fp->script) {
         js_TraceScript(trc, fp->script);
         /*
          * Don't mark what has not been pushed yet, or what has been
          * popped already.
          */
-        nslots = (uintN) (fp->sp - fp->spbase);
-        JS_ASSERT(nslots <= fp->script->depth);
-        TRACE_JSVALS(trc, nslots, fp->spbase, "operand");
+        if (fp->regs) {
+            nslots = (uintN) (fp->regs->sp - fp->spbase);
+            JS_ASSERT(nslots <= fp->script->depth);
+            TRACE_JSVALS(trc, nslots, fp->spbase, "operand");
+        }
     }
 
     /* Allow for primitive this parameter due to JSFUN_THISP_* flags. */
     JS_ASSERT(JSVAL_IS_OBJECT((jsval)fp->thisp) ||
               (fp->fun && JSFUN_THISP_FLAGS(fp->fun->flags)));
     JS_CALL_VALUE_TRACER(trc, (jsval)fp->thisp, "this");
 
     if (fp->callee)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -112,17 +112,17 @@ js_FillPropertyCache(JSContext *cx, JSOb
     const JSCodeSpec *cs;
     jsuword vword;
     ptrdiff_t pcoff;
     jsuword khash;
     JSAtom *atom;
     JSPropCacheEntry *entry;
 
     cache = &JS_PROPERTY_CACHE(cx);
-    pc = cx->fp->pc;
+    pc = cx->fp->regs->pc;
     if (cache->disabled) {
         PCMETER(cache->disfills++);
         *entryp = NULL;
         return;
     }
 
     /*
      * Check for fill from js_SetPropertyHelper where the setter removed sprop
@@ -1208,17 +1208,17 @@ have_fun:
         /* Push void to initialize local variables. */
         i = nvars;
         do {
             *sp++ = JSVAL_VOID;
         } while (--i != 0);
     }
 
     /*
-     * Initialize the frame, except for sp (set by SAVE_SP later).
+     * Initialize the frame.
      *
      * To set thisp we use an explicit cast and not JSVAL_TO_OBJECT, as vp[1]
      * can be a primitive value here for those native functions specified with
      * JSFUN_THISP_(NUMBER|STRING|BOOLEAN) flags.
      */
     frame.thisp = (JSObject *)vp[1];
     frame.varobj = NULL;
     frame.callobj = frame.argsobj = NULL;
@@ -1230,18 +1230,17 @@ have_fun:
 
     /* Default return value for a constructor is the new object. */
     frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
     frame.nvars = nvars;
     frame.vars = sp - nvars;
     frame.down = cx->fp;
     frame.annotation = NULL;
     frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
-    frame.pc = NULL;
-    frame.sp = NULL;
+    frame.regs = NULL;
     frame.spbase = NULL;
     frame.sharpDepth = 0;
     frame.sharpArray = NULL;
     frame.flags = flags | rootedArgsFlag;
     frame.dormantNext = NULL;
     frame.xmlNamespace = NULL;
     frame.blockChain = NULL;
 
@@ -1480,18 +1479,17 @@ js_Execute(JSContext *cx, JSObject *chai
             frame.vars = NULL;
         }
         frame.annotation = NULL;
         frame.sharpArray = NULL;
     }
     frame.rval = JSVAL_VOID;
     frame.down = down;
     frame.scopeChain = chain;
-    frame.pc = NULL;
-    frame.sp = NULL;
+    frame.regs = NULL;
     frame.spbase = NULL;
     frame.sharpDepth = 0;
     frame.flags = flags;
     frame.dormantNext = NULL;
     frame.xmlNamespace = NULL;
     frame.blockChain = NULL;
 
     /*
@@ -1898,17 +1896,17 @@ js_InternNonIntElementId(JSContext *cx, 
 JSBool
 js_EnterWith(JSContext *cx, jsint stackIndex)
 {
     JSStackFrame *fp;
     jsval *sp;
     JSObject *obj, *parent, *withobj;
 
     fp = cx->fp;
-    sp = fp->sp;
+    sp = fp->regs->sp;
     JS_ASSERT(stackIndex < 0);
     JS_ASSERT(fp->spbase <= sp + stackIndex);
 
     if (!JSVAL_IS_PRIMITIVE(sp[-1])) {
         obj = JSVAL_TO_OBJECT(sp[-1]);
     } else {
         obj = js_ValueToNonNullObject(cx, sp[-1]);
         if (!obj)
@@ -1986,17 +1984,17 @@ js_CountWithBlocks(JSContext *cx, JSStac
 JSBool
 js_UnwindScope(JSContext *cx, JSStackFrame *fp, jsint stackDepth,
                JSBool normalUnwind)
 {
     JSObject *obj;
     JSClass *clasp;
 
     JS_ASSERT(stackDepth >= 0);
-    JS_ASSERT(fp->spbase + stackDepth <= fp->sp);
+    JS_ASSERT(fp->spbase + stackDepth <= fp->regs->sp);
 
     for (obj = fp->blockChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
         JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
         if (OBJ_BLOCK_DEPTH(cx, obj) < stackDepth)
             break;
     }
     fp->blockChain = obj;
 
@@ -2008,17 +2006,17 @@ js_UnwindScope(JSContext *cx, JSStackFra
         if (clasp == &js_BlockClass) {
             /* Don't fail until after we've updated all stacks. */
             normalUnwind &= js_PutBlockObject(cx, normalUnwind);
         } else {
             js_LeaveWith(cx);
         }
     }
 
-    fp->sp = fp->spbase + stackDepth;
+    fp->regs->sp = fp->spbase + stackDepth;
     return normalUnwind;
 }
 
 JSBool
 js_DoIncDec(JSContext *cx, const JSCodeSpec *cs, jsval *vp, jsval *vp2)
 {
     jsval v;
     jsdouble d;
@@ -2188,83 +2186,52 @@ js_DumpOpMeters()
     }
     fclose(fp);
 }
 
 #endif /* JS_OPSMETER */
 
 #else /* !defined js_invoke_c__ */
 
-/*
- * Stack macros and functions.  These all use a local variable, jsval *sp, to
- * point to the next free stack slot.  SAVE_SP must be called before any call
- * to a function that may invoke the interpreter.  RESTORE_SP must be called
- * only after return from js_Invoke, because only js_Invoke changes fp->sp.
- */
-#define SAVE_SP(fp)                                                           \
-    (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase),        \
-     (fp)->sp = sp)
-#define RESTORE_SP(fp)  (sp = (fp)->sp)
-
-/*
- * SAVE_SP_AND_PC commits deferred stores of interpreter registers to their
- * homes in fp, when calling out of the interpreter loop or threaded code.
- * RESTORE_SP_AND_PC copies the other way, to update registers after a call
- * to a subroutine that interprets a piece of the current script.
- * ASSERT_SAVED_SP_AND_PC checks that SAVE_SP_AND_PC was called.
- */
-#define SAVE_SP_AND_PC(fp)      (SAVE_SP(fp), (fp)->pc = pc)
-#define RESTORE_SP_AND_PC(fp)   (RESTORE_SP(fp), pc = (fp)->pc)
-#define ASSERT_SAVED_SP_AND_PC(fp) JS_ASSERT((fp)->sp == sp && (fp)->pc == pc);
-
-#define PUSH(v)         (*sp++ = (v))
+#define PUSH(v)         (*regs.sp++ = (v))
 #define PUSH_OPND(v)    PUSH(v)
-#define STORE_OPND(n,v) (sp[n] = (v))
-#define POP()           (*--sp)
+#define STORE_OPND(n,v) (regs.sp[n] = (v))
+#define POP()           (*--regs.sp)
 #define POP_OPND()      POP()
-#define FETCH_OPND(n)   (sp[n])
+#define FETCH_OPND(n)   (regs.sp[n])
 
 /*
  * Push the jsdouble d using sp from the lexical environment. Try to convert d
  * to a jsint that fits in a jsval, otherwise GC-alloc space for it and push a
  * reference.
  */
 #define STORE_NUMBER(cx, n, d)                                                \
     JS_BEGIN_MACRO                                                            \
         jsint i_;                                                             \
                                                                               \
-        if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_)) {                \
-            sp[n] = INT_TO_JSVAL(i_);                                         \
-        } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            if (!js_NewDoubleInRootedValue(cx, d, &sp[n]))                    \
-                goto error;                                                   \
-        }                                                                     \
+        if (JSDOUBLE_IS_INT(d, i_) && INT_FITS_IN_JSVAL(i_))                  \
+            regs.sp[n] = INT_TO_JSVAL(i_);                                    \
+        else if (!js_NewDoubleInRootedValue(cx, d, &regs.sp[n]))              \
+            goto error;                                                       \
     JS_END_MACRO
 
 #define STORE_INT(cx, n, i)                                                   \
     JS_BEGIN_MACRO                                                            \
-        if (INT_FITS_IN_JSVAL(i)) {                                           \
-            sp[n] = INT_TO_JSVAL(i);                                          \
-        } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            if (!js_NewDoubleInRootedValue(cx, (jsdouble)(i), &sp[n]))        \
-                goto error;                                                   \
-        }                                                                     \
+        if (INT_FITS_IN_JSVAL(i))                                             \
+            regs.sp[n] = INT_TO_JSVAL(i);                                     \
+        else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (i), &regs.sp[n])) \
+            goto error;                                                       \
     JS_END_MACRO
 
 #define STORE_UINT(cx, n, u)                                                  \
     JS_BEGIN_MACRO                                                            \
-        if ((u) <= JSVAL_INT_MAX) {                                           \
-            sp[n] = INT_TO_JSVAL(u);                                          \
-        } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            if (!js_NewDoubleInRootedValue(cx, (jsdouble)(u), &sp[n]))        \
-                goto error;                                                   \
-        }                                                                     \
+        if ((u) <= JSVAL_INT_MAX)                                             \
+            regs.sp[n] = INT_TO_JSVAL(u);                                     \
+        else if (!js_NewDoubleInRootedValue(cx, (jsdouble) (u), &regs.sp[n])) \
+            goto error;                                                       \
     JS_END_MACRO
 
 #define FETCH_NUMBER(cx, n, d)                                                \
     JS_BEGIN_MACRO                                                            \
         jsval v_;                                                             \
                                                                               \
         v_ = FETCH_OPND(n);                                                   \
         VALUE_TO_NUMBER(cx, n, v_, d);                                        \
@@ -2273,100 +2240,94 @@ js_DumpOpMeters()
 #define FETCH_INT(cx, n, i)                                                   \
     JS_BEGIN_MACRO                                                            \
         jsval v_;                                                             \
                                                                               \
         v_= FETCH_OPND(n);                                                    \
         if (JSVAL_IS_INT(v_)) {                                               \
             i = JSVAL_TO_INT(v_);                                             \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            i = js_ValueToECMAInt32(cx, &sp[n]);                              \
-            if (JSVAL_IS_NULL(sp[n]))                                         \
+            i = js_ValueToECMAInt32(cx, &regs.sp[n]);                         \
+            if (JSVAL_IS_NULL(regs.sp[n]))                                    \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define FETCH_UINT(cx, n, ui)                                                 \
     JS_BEGIN_MACRO                                                            \
         jsval v_;                                                             \
                                                                               \
         v_= FETCH_OPND(n);                                                    \
         if (JSVAL_IS_INT(v_)) {                                               \
             ui = (uint32) JSVAL_TO_INT(v_);                                   \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            ui = js_ValueToECMAUint32(cx, &sp[n]);                            \
-            if (JSVAL_IS_NULL(sp[n]))                                         \
+            ui = js_ValueToECMAUint32(cx, &regs.sp[n]);                       \
+            if (JSVAL_IS_NULL(regs.sp[n]))                                    \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 /*
  * Optimized conversion macros that test for the desired type in v before
  * homing sp and calling a conversion function.
  */
 #define VALUE_TO_NUMBER(cx, n, v, d)                                          \
     JS_BEGIN_MACRO                                                            \
-        JS_ASSERT(v == sp[n]);                                                \
+        JS_ASSERT(v == regs.sp[n]);                                           \
         if (JSVAL_IS_INT(v)) {                                                \
             d = (jsdouble)JSVAL_TO_INT(v);                                    \
         } else if (JSVAL_IS_DOUBLE(v)) {                                      \
             d = *JSVAL_TO_DOUBLE(v);                                          \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            d = js_ValueToNumber(cx, &sp[n]);                                 \
-            if (JSVAL_IS_NULL(sp[n]))                                         \
+            d = js_ValueToNumber(cx, &regs.sp[n]);                            \
+            if (JSVAL_IS_NULL(regs.sp[n]))                                    \
                 goto error;                                                   \
-            JS_ASSERT(JSVAL_IS_NUMBER(sp[n]) || sp[n] == JSVAL_TRUE);         \
+            JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[n]) ||                          \
+                      regs.sp[n] == JSVAL_TRUE);                              \
         }                                                                     \
     JS_END_MACRO
 
 #define POP_BOOLEAN(cx, v, b)                                                 \
     JS_BEGIN_MACRO                                                            \
         v = FETCH_OPND(-1);                                                   \
         if (v == JSVAL_NULL) {                                                \
             b = JS_FALSE;                                                     \
         } else if (JSVAL_IS_BOOLEAN(v)) {                                     \
             b = JSVAL_TO_BOOLEAN(v);                                          \
         } else {                                                              \
             b = js_ValueToBoolean(v);                                         \
         }                                                                     \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
     JS_END_MACRO
 
 #define VALUE_TO_OBJECT(cx, n, v, obj)                                        \
     JS_BEGIN_MACRO                                                            \
         if (!JSVAL_IS_PRIMITIVE(v)) {                                         \
             obj = JSVAL_TO_OBJECT(v);                                         \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
             obj = js_ValueToNonNullObject(cx, v);                             \
             if (!obj)                                                         \
                 goto error;                                                   \
             STORE_OPND(n, OBJECT_TO_JSVAL(obj));                              \
         }                                                                     \
     JS_END_MACRO
 
-/* SAVE_SP_AND_PC must be already called. */
 #define FETCH_OBJECT(cx, n, v, obj)                                           \
     JS_BEGIN_MACRO                                                            \
-        ASSERT_SAVED_SP_AND_PC(fp);                                           \
         v = FETCH_OPND(n);                                                    \
         VALUE_TO_OBJECT(cx, n, v, obj);                                       \
     JS_END_MACRO
 
 #define DEFAULT_VALUE(cx, n, hint, v)                                         \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));                                    \
-        JS_ASSERT(v == sp[n]);                                                \
-        SAVE_SP_AND_PC(fp);                                                   \
-        if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &sp[n]))         \
+        JS_ASSERT(v == regs.sp[n]);                                           \
+        if (!OBJ_DEFAULT_VALUE(cx, JSVAL_TO_OBJECT(v), hint, &regs.sp[n]))    \
             goto error;                                                       \
-        v = sp[n];                                                            \
+        v = regs.sp[n];                                                       \
     JS_END_MACRO
 
 /*
  * Quickly test if v is an int from the [-2**29, 2**29) range, that is, when
  * the lowest bit of v is 1 and the bits 30 and 31 are both either 0 or 1. For
  * such v we can do increment or decrement via adding or subtracting two
  * without checking that the result overflows JSVAL_INT_MIN or JSVAL_INT_MAX.
  */
@@ -2466,18 +2427,17 @@ js_Interpret(JSContext *cx)
 {
     JSRuntime *rt;
     JSStackFrame *fp;
     JSScript *script;
     uintN inlineCallCount;
     JSAtom **atoms;
     JSVersion currentVersion, originalVersion;
     void *mark;
-    jsval *sp;
-    jsbytecode *pc;
+    JSFrameRegs regs;
     JSObject *obj, *obj2, *parent;
     JSBool ok, cond;
     JSTrapHandler interruptHandler;
     jsint len;
     jsbytecode *endpc, *pc2;
     JSOp op, op2;
     jsatomid index;
     JSAtom *atom;
@@ -2529,23 +2489,23 @@ js_Interpret(JSContext *cx)
 # undef OPDEF
     };
 
     register void **jumpTable = normalJumpTable;
 
     METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */
 
 # define DO_OP()            JS_EXTENSION_(goto *jumpTable[op])
-# define DO_NEXT_OP(n)      do { METER_OP_PAIR(op, pc[n]);                    \
-                                 op = (JSOp) *(pc += (n));                    \
+# define DO_NEXT_OP(n)      do { METER_OP_PAIR(op, regs.pc[n]);               \
+                                 op = (JSOp) *(regs.pc += (n));               \
                                  DO_OP(); } while (0)
 # define BEGIN_CASE(OP)     L_##OP:
 # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
 # define END_VARLEN_CASE    DO_NEXT_OP(len);
-# define EMPTY_CASE(OP)     BEGIN_CASE(OP) op = (JSOp) *++pc; DO_OP();
+# define EMPTY_CASE(OP)     BEGIN_CASE(OP) op = (JSOp) *++regs.pc; DO_OP();
 #else
 # define DO_OP()            goto do_op
 # define DO_NEXT_OP(n)      goto advance_pc
 # define BEGIN_CASE(OP)     case OP:
 # define END_CASE(OP)       break;
 # define END_VARLEN_CASE    break;
 # define EMPTY_CASE(OP)     BEGIN_CASE(OP) END_CASE(OP)
 #endif
@@ -2570,22 +2530,23 @@ js_Interpret(JSContext *cx)
      * access. For less frequent object and regexp loads we have to recover
      * the segment from atoms pointer first.
      */
     atoms = script->atomMap.vector;
 
 #define LOAD_ATOM(PCOFF)                                                      \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT((size_t)(atoms - script->atomMap.vector) <                  \
-                  (size_t)(script->atomMap.length - GET_INDEX(pc + PCOFF)));  \
-        atom = atoms[GET_INDEX(pc + PCOFF)];                                  \
+                  (size_t)(script->atomMap.length -                           \
+                           GET_INDEX(regs.pc + PCOFF)));                      \
+        atom = atoms[GET_INDEX(regs.pc + PCOFF)];                             \
     JS_END_MACRO
 
 #define GET_FULL_INDEX(PCOFF)                                                 \
-    (atoms - script->atomMap.vector + GET_INDEX(pc + PCOFF))
+    (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
 
 #define LOAD_OBJECT(PCOFF)                                                    \
     JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(PCOFF), obj)
 
 #define LOAD_FUNCTION(PCOFF)                                                  \
     JS_BEGIN_MACRO                                                            \
         LOAD_OBJECT(PCOFF);                                                   \
         JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);               \
@@ -2593,17 +2554,16 @@ js_Interpret(JSContext *cx)
 
     /*
      * Prepare to call a user-supplied branch handler, and abort the script
      * if it returns false.
      */
 #define CHECK_BRANCH(len)                                                     \
     JS_BEGIN_MACRO                                                            \
         if (len <= 0 && (cx->operationCount -= JSOW_SCRIPT_JUMP) <= 0) {      \
-            SAVE_SP_AND_PC(fp);                                               \
             if (!js_ResetOperationCount(cx))                                  \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
     /*
      * Optimized Get and SetVersion for proper script language versioning.
      *
@@ -2619,17 +2579,17 @@ js_Interpret(JSContext *cx)
         js_SetVersion(cx, currentVersion);
 
     ++cx->interpLevel;
 #ifdef DEBUG
     fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
 #endif
 
     /*
-     * From this point the control must flow through the label exit.
+     * From this point control must flow through the label exit2.
      *
      * Load the debugger's interrupt hook here and after calling out to native
      * functions (but not to getters, setters, or other native hooks), so we do
      * not have to reload it each time through the interpreter loop -- we hope
      * the compiler can keep it in a register when it is non-null.
      */
 #if JS_THREADED_INTERP
 # define LOAD_JUMP_TABLE()                                                    \
@@ -2647,35 +2607,37 @@ js_Interpret(JSContext *cx)
     LOAD_INTERRUPT_HANDLER(cx);
 
      /*
      * Initialize the pc register and allocate operand stack slots for the
      * script's worst-case depth, unless we're resuming a generator.
      */
     if (JS_LIKELY(!fp->spbase)) {
         ASSERT_NOT_THROWING(cx);
-        JS_ASSERT(!fp->pc);
-        JS_ASSERT(!fp->sp);
-        JS_ASSERT(!fp->spbase);
+        JS_ASSERT(!fp->regs);
         fp->spbase = js_AllocRawStack(cx, script->depth, &mark);
         if (!fp->spbase) {
-            mark = NULL;
             ok = JS_FALSE;
-            goto exit;
+            goto exit2;
         }
         JS_ASSERT(mark);
-        fp->pc = script->code;
-        fp->sp = fp->spbase;
-        RESTORE_SP_AND_PC(fp);
+        regs.pc = script->code;
+        regs.sp = fp->spbase;
+        fp->regs = &regs;
     } else {
+        JSGenerator *gen;
+
         JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
         mark = NULL;
-        RESTORE_SP_AND_PC(fp);
-        JS_ASSERT((size_t) (pc - script->code) <= script->length);
-        JS_ASSERT((size_t) (sp - fp->spbase) <= script->depth);
+        gen = FRAME_TO_GENERATOR(fp);
+        JS_ASSERT(fp->regs == &gen->savedRegs);
+        regs = gen->savedRegs;
+        fp->regs = &regs;
+        JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
+        JS_ASSERT((size_t) (regs.sp - fp->spbase) <= script->depth);
         JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
         JS_PROPERTY_CACHE(cx).disabled += js_CountWithBlocks(cx, fp);
 
         /*
          * To support generator_throw and to catch ignored exceptions,
          * fail if cx->throwing is set.
          */
         if (cx->throwing) {
@@ -2698,21 +2660,20 @@ js_Interpret(JSContext *cx)
      * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately.
      *
      * It is important that "op" be initialized before the interrupt label
      * because it is possible for "op" to be specially assigned during the
      * normally processing of an opcode while looping (in particular, this
      * happens in JSOP_TRAP while debugging).  We rely on DO_NEXT_OP to
      * correctly manage "op" in all other cases.
      */
-    op = (JSOp) *pc;
+    op = (JSOp) *regs.pc;
     if (interruptHandler) {
 interrupt:
-        SAVE_SP_AND_PC(fp);
-        switch (interruptHandler(cx, script, pc, &rval,
+        switch (interruptHandler(cx, script, regs.pc, &rval,
                                  cx->debugHooks->interruptHandlerData)) {
           case JSTRAP_ERROR:
             goto error;
           case JSTRAP_CONTINUE:
             break;
           case JSTRAP_RETURN:
             fp->rval = rval;
             ok = JS_TRUE;
@@ -2727,50 +2688,48 @@ interrupt:
     }
 
     JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
     JS_EXTENSION_(goto *normalJumpTable[op]);
 
 #else  /* !JS_THREADED_INTERP */
 
     for (;;) {
-        op = (JSOp) *pc;
+        op = (JSOp) *regs.pc;
       do_op:
         len = js_CodeSpec[op].length;
 
 #ifdef DEBUG
         tracefp = (FILE *) cx->tracefp;
         if (tracefp) {
             intN nuses, n;
 
-            fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc));
-            js_Disassemble1(cx, script, pc,
-                            PTRDIFF(pc, script->code, jsbytecode), JS_FALSE,
-                            tracefp);
+            fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, regs.pc));
+            js_Disassemble1(cx, script, regs.pc,
+                            PTRDIFF(regs.pc, script->code, jsbytecode),
+                            JS_FALSE, tracefp);
             nuses = js_CodeSpec[op].nuses;
             if (nuses) {
-                SAVE_SP_AND_PC(fp);
                 for (n = -nuses; n < 0; n++) {
-                    char *bytes = js_DecompileValueGenerator(cx, n, sp[n],
+                    char *bytes = js_DecompileValueGenerator(cx, n, regs.sp[n],
                                                              NULL);
                     if (bytes) {
                         fprintf(tracefp, "%s %s",
                                 (n == -nuses) ? "  inputs:" : ",",
                                 bytes);
                         JS_free(cx, bytes);
                     }
                 }
-                fprintf(tracefp, " @ %d\n", sp - fp->spbase);
+                fprintf(tracefp, " @ %d\n", regs.sp - fp->spbase);
             }
         }
 #endif /* DEBUG */
 
         if (interruptHandler) {
-            SAVE_SP_AND_PC(fp);
-            switch (interruptHandler(cx, script, pc, &rval,
+            switch (interruptHandler(cx, script, regs.pc, &rval,
                                      cx->debugHooks->interruptHandlerData)) {
               case JSTRAP_ERROR:
                 goto error;
               case JSTRAP_CONTINUE:
                 break;
               case JSTRAP_RETURN:
                 fp->rval = rval;
                 ok = JS_TRUE;
@@ -2792,92 +2751,89 @@ interrupt:
 
           EMPTY_CASE(JSOP_GROUP)
 
           BEGIN_CASE(JSOP_PUSH)
             PUSH_OPND(JSVAL_VOID);
           END_CASE(JSOP_PUSH)
 
           BEGIN_CASE(JSOP_POP)
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_POP)
 
           BEGIN_CASE(JSOP_POPN)
-            sp -= GET_UINT16(pc);
+            regs.sp -= GET_UINT16(regs.pc);
 #ifdef DEBUG
-            JS_ASSERT(fp->spbase <= sp);
+            JS_ASSERT(fp->spbase <= regs.sp);
             obj = fp->blockChain;
-            JS_ASSERT(!obj ||
-                      fp->spbase + OBJ_BLOCK_DEPTH(cx, obj)
-                                 + OBJ_BLOCK_COUNT(cx, obj)
-                      <= sp);
+            JS_ASSERT_IF(obj,
+                         OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
+                         <= (size_t) (regs.sp - fp->spbase));
             for (obj = fp->scopeChain; obj; obj = OBJ_GET_PARENT(cx, obj)) {
                 clasp = OBJ_GET_CLASS(cx, obj);
                 if (clasp != &js_BlockClass && clasp != &js_WithClass)
                     continue;
                 if (OBJ_GET_PRIVATE(cx, obj) != fp)
                     break;
                 JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj)
                                      + ((clasp == &js_BlockClass)
-                                         ? OBJ_BLOCK_COUNT(cx, obj)
-                                         : 1)
-                          <= sp);
+                                        ? OBJ_BLOCK_COUNT(cx, obj)
+                                        : 1)
+                          <= regs.sp);
             }
 #endif
           END_CASE(JSOP_POPN)
 
           BEGIN_CASE(JSOP_SWAP)
-            rtmp = sp[-1];
-            sp[-1] = sp[-2];
-            sp[-2] = rtmp;
+            rtmp = regs.sp[-1];
+            regs.sp[-1] = regs.sp[-2];
+            regs.sp[-2] = rtmp;
           END_CASE(JSOP_SWAP)
 
           BEGIN_CASE(JSOP_SETRVAL)
           BEGIN_CASE(JSOP_POPV)
             ASSERT_NOT_THROWING(cx);
             fp->rval = POP_OPND();
           END_CASE(JSOP_POPV)
 
           BEGIN_CASE(JSOP_ENTERWITH)
-            SAVE_SP_AND_PC(fp);
             if (!js_EnterWith(cx, -1))
                 goto error;
 
             /*
              * We must ensure that different "with" blocks have different
              * stack depth associated with them. This allows the try handler
              * search to properly recover the scope chain. Thus we must keep
              * the stack at least at the current level.
              *
              * We set sp[-1] to the current "with" object to help asserting
              * the enter/leave balance in [leavewith].
              */
-            sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
+            regs.sp[-1] = OBJECT_TO_JSVAL(fp->scopeChain);
           END_CASE(JSOP_ENTERWITH)
 
           BEGIN_CASE(JSOP_LEAVEWITH)
-            JS_ASSERT(sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
-            sp--;
-            SAVE_SP_AND_PC(fp);
+            JS_ASSERT(regs.sp[-1] == OBJECT_TO_JSVAL(fp->scopeChain));
+            regs.sp--;
             js_LeaveWith(cx);
           END_CASE(JSOP_LEAVEWITH)
 
           BEGIN_CASE(JSOP_RETURN)
             CHECK_BRANCH(-1);
             fp->rval = POP_OPND();
             /* FALL THROUGH */
 
           BEGIN_CASE(JSOP_RETRVAL)    /* fp->rval already set */
           BEGIN_CASE(JSOP_STOP)
             /*
              * When the inlined frame exits with an exception or an error, ok
              * will be false after the inline_return label.
              */
             ASSERT_NOT_THROWING(cx);
-            JS_ASSERT(sp == fp->spbase);
+            JS_ASSERT(regs.sp == fp->spbase);
             ok = JS_TRUE;
             if (inlineCallCount)
           inline_return:
             {
                 JSInlineFrame *ifp = (JSInlineFrame *) fp;
                 void *hookData = ifp->hookData;
 
                 JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
@@ -2889,39 +2845,34 @@ interrupt:
                     JSBool status;
 
                     hook = cx->debugHooks->callHook;
                     if (hook) {
                         /*
                          * Do not pass &ok directly as exposing the address
                          * inhibits optimizations and uninitialised warnings.
                          */
-                        SAVE_SP_AND_PC(fp);
                         status = ok;
                         hook(cx, fp, JS_FALSE, &status, hookData);
                         ok = status;
                         LOAD_INTERRUPT_HANDLER(cx);
                     }
                 }
 
                 /*
                  * If fp has a call object, sync values and clear the back-
                  * pointer. This can happen for a lightweight function if it
                  * calls eval unexpectedly (in a way that is hidden from the
                  * compiler). See bug 325540.
                  */
-                if (fp->callobj) {
-                    SAVE_SP_AND_PC(fp);
+                if (fp->callobj)
                     ok &= js_PutCallObject(cx, fp);
-                }
-
-                if (fp->argsobj) {
-                    SAVE_SP_AND_PC(fp);
+
+                if (fp->argsobj)
                     ok &= js_PutArgsObject(cx, fp);
-                }
 
 #ifdef INCLUDE_MOZILLA_DTRACE
                 /* DTrace function return, inlines */
                 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
                     jsdtrace_function_rval(cx, fp, fp->fun);
                 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
                     jsdtrace_function_return(cx, fp, fp->fun);
 #endif
@@ -2929,163 +2880,158 @@ interrupt:
                 /* Restore context version only if callee hasn't set version. */
                 if (JS_LIKELY(cx->version == currentVersion)) {
                     currentVersion = ifp->callerVersion;
                     if (currentVersion != cx->version)
                         js_SetVersion(cx, currentVersion);
                 }
 
                 /* Store the return value in the caller's operand frame. */
-                vp = ifp->rvp;
-                *vp = fp->rval;
+                regs = ifp->callerRegs;
+                regs.sp += 1 - 2 - ifp->frame.argc;
+                regs.sp[-1] = fp->rval;
 
                 /* Restore cx->fp and release the inline frame's space. */
                 cx->fp = fp = fp->down;
+                JS_ASSERT(fp->regs == &ifp->callerRegs);
+                fp->regs = &regs;
                 JS_ARENA_RELEASE(&cx->stackPool, ifp->mark);
 
-                /* Restore sp to point just above the return value. */
-                fp->sp = vp + 1;
-                RESTORE_SP(fp);
-
                 /* Restore the calling script's interpreter registers. */
                 script = fp->script;
                 atoms = script->atomMap.vector;
-                pc = fp->pc;
 
                 /* Resume execution in the calling frame. */
                 inlineCallCount--;
                 if (JS_LIKELY(ok)) {
-                    JS_ASSERT(js_CodeSpec[*pc].length == JSOP_CALL_LENGTH);
+                    JS_ASSERT(js_CodeSpec[*regs.pc].length == JSOP_CALL_LENGTH);
                     len = JSOP_CALL_LENGTH;
                     DO_NEXT_OP(len);
                 }
                 goto error;
             }
             goto exit;
 
           BEGIN_CASE(JSOP_DEFAULT)
             (void) POP();
             /* FALL THROUGH */
           BEGIN_CASE(JSOP_GOTO)
-            len = GET_JUMP_OFFSET(pc);
+            len = GET_JUMP_OFFSET(regs.pc);
             CHECK_BRANCH(len);
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_IFEQ)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_FALSE) {
-                len = GET_JUMP_OFFSET(pc);
+                len = GET_JUMP_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_IFEQ)
 
           BEGIN_CASE(JSOP_IFNE)
             POP_BOOLEAN(cx, rval, cond);
             if (cond != JS_FALSE) {
-                len = GET_JUMP_OFFSET(pc);
+                len = GET_JUMP_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_IFNE)
 
           BEGIN_CASE(JSOP_OR)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_TRUE) {
-                len = GET_JUMP_OFFSET(pc);
+                len = GET_JUMP_OFFSET(regs.pc);
                 PUSH_OPND(rval);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_OR)
 
           BEGIN_CASE(JSOP_AND)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_FALSE) {
-                len = GET_JUMP_OFFSET(pc);
+                len = GET_JUMP_OFFSET(regs.pc);
                 PUSH_OPND(rval);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_AND)
 
           BEGIN_CASE(JSOP_DEFAULTX)
             (void) POP();
             /* FALL THROUGH */
           BEGIN_CASE(JSOP_GOTOX)
-            len = GET_JUMPX_OFFSET(pc);
+            len = GET_JUMPX_OFFSET(regs.pc);
             CHECK_BRANCH(len);
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_IFEQX)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_FALSE) {
-                len = GET_JUMPX_OFFSET(pc);
+                len = GET_JUMPX_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_IFEQX)
 
           BEGIN_CASE(JSOP_IFNEX)
             POP_BOOLEAN(cx, rval, cond);
             if (cond != JS_FALSE) {
-                len = GET_JUMPX_OFFSET(pc);
+                len = GET_JUMPX_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_IFNEX)
 
           BEGIN_CASE(JSOP_ORX)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_TRUE) {
-                len = GET_JUMPX_OFFSET(pc);
+                len = GET_JUMPX_OFFSET(regs.pc);
                 PUSH_OPND(rval);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_ORX)
 
           BEGIN_CASE(JSOP_ANDX)
             POP_BOOLEAN(cx, rval, cond);
             if (cond == JS_FALSE) {
-                len = GET_JUMPX_OFFSET(pc);
+                len = GET_JUMPX_OFFSET(regs.pc);
                 PUSH_OPND(rval);
                 DO_NEXT_OP(len);
             }
           END_CASE(JSOP_ANDX)
 
 /*
  * If the index value at sp[n] is not an int that fits in a jsval, it could
  * be an object (an XML QName, AttributeName, or AnyName), but only if we are
  * compiling with JS_HAS_XML_SUPPORT.  Otherwise convert the index value to a
  * string atom id.
- *
- * SAVE_SP_AND_PC must be already called.
  */
 #define FETCH_ELEMENT_ID(obj, n, id)                                          \
     JS_BEGIN_MACRO                                                            \
         jsval idval_ = FETCH_OPND(n);                                         \
         if (JSVAL_IS_INT(idval_)) {                                           \
             id = INT_JSVAL_TO_JSID(idval_);                                   \
         } else {                                                              \
             if (!js_InternNonIntElementId(cx, obj, idval_, &id))              \
                 goto error;                                                   \
-            sp[n] = ID_TO_VALUE(id);                                          \
+            regs.sp[n] = ID_TO_VALUE(id);                                     \
         }                                                                     \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_IN)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (JSVAL_IS_PRIMITIVE(rval)) {
                 js_ReportValueError(cx, JSMSG_IN_NOT_OBJECT, -1, rval, NULL);
                 goto error;
             }
             obj = JSVAL_TO_OBJECT(rval);
             FETCH_ELEMENT_ID(obj, -2, id);
             if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
                 goto error;
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
             if (prop)
                 OBJ_DROP_PROPERTY(cx, obj2, prop);
           END_CASE(JSOP_IN)
 
           BEGIN_CASE(JSOP_FOREACH)
             flags = JSITER_ENUMERATE | JSITER_FOREACH;
             goto value_to_iter;
@@ -3101,21 +3047,20 @@ interrupt:
              * Set JSITER_ENUMERATE to indicate that for-in loop should use
              * the enumeration protocol's iterator for compatibility if an
              * explicit iterator is not given via the optional __iterator__
              * method.
              */
             flags = JSITER_ENUMERATE;
 
           value_to_iter:
-            JS_ASSERT(sp > fp->spbase);
-            SAVE_SP_AND_PC(fp);
-            if (!js_ValueToIterator(cx, flags, &sp[-1]))
+            JS_ASSERT(regs.sp > fp->spbase);
+            if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
                 goto error;
-            JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1]));
+            JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
             JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length);
           END_CASE(JSOP_FORIN)
 
           BEGIN_CASE(JSOP_FORPROP)
             /*
              * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
              * is not paid for the more common cases.
              */
@@ -3150,46 +3095,45 @@ interrupt:
              */
             i = -1;
 
           do_forinloop:
             /*
              * Reach under the top of stack to find our property iterator, a
              * JSObject that contains the iteration state.
              */
-            JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i]));
-            iterobj = JSVAL_TO_OBJECT(sp[i]);
-
-            SAVE_SP_AND_PC(fp);
+            JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[i]));
+            iterobj = JSVAL_TO_OBJECT(regs.sp[i]);
+
             if (!js_CallIteratorNext(cx, iterobj, &rval))
                 goto error;
             if (rval == JSVAL_HOLE) {
                 rval = JSVAL_FALSE;
                 goto end_forinloop;
             }
 
             switch (op) {
               case JSOP_FORARG:
-                slot = GET_ARGNO(pc);
+                slot = GET_ARGNO(regs.pc);
                 JS_ASSERT(slot < fp->fun->nargs);
                 fp->argv[slot] = rval;
                 break;
 
               case JSOP_FORVAR:
-                slot = GET_VARNO(pc);
+                slot = GET_VARNO(regs.pc);
                 JS_ASSERT(slot < fp->fun->u.i.nvars);
                 fp->vars[slot] = rval;
                 break;
 
               case JSOP_FORCONST:
                 /* Don't update the const slot. */
                 break;
 
               case JSOP_FORLOCAL:
-                slot = GET_UINT16(pc);
+                slot = GET_UINT16(regs.pc);
                 JS_ASSERT(slot < script->depth);
                 vp = &fp->spbase[slot];
                 GC_POKE(cx, *vp);
                 *vp = rval;
                 break;
 
               case JSOP_FORELEM:
                 /* FORELEM is not a SET operation, it's more like BINDNAME. */
@@ -3197,17 +3141,16 @@ interrupt:
                 break;
 
               case JSOP_FORPROP:
                 /*
                  * We fetch object here to ensure that the iterator is called
                  * even if lval is null or undefined that throws in
                  * FETCH_OBJECT. See bug 372331.
                  */
-                SAVE_SP_AND_PC(fp);
                 FETCH_OBJECT(cx, -1, lval, obj);
                 goto set_for_property;
 
               default:
                 JS_ASSERT(op == JSOP_FORNAME);
 
                 /*
                  * We find property here after the iterator call to ensure
@@ -3229,50 +3172,48 @@ interrupt:
                     goto error;
                 break;
             }
 
             /* Push true to keep looping through properties. */
             rval = JSVAL_TRUE;
 
           end_forinloop:
-            sp += i + 1;
+            regs.sp += i + 1;
             PUSH_OPND(rval);
             len = js_CodeSpec[op].length;
             DO_NEXT_OP(len);
 
           BEGIN_CASE(JSOP_DUP)
-            JS_ASSERT(sp > fp->spbase);
+            JS_ASSERT(regs.sp > fp->spbase);
             rval = FETCH_OPND(-1);
             PUSH(rval);
           END_CASE(JSOP_DUP)
 
           BEGIN_CASE(JSOP_DUP2)
-            JS_ASSERT(sp - 2 >= fp->spbase);
+            JS_ASSERT(regs.sp - 2 >= fp->spbase);
             lval = FETCH_OPND(-2);
             rval = FETCH_OPND(-1);
             PUSH(lval);
             PUSH(rval);
           END_CASE(JSOP_DUP2)
 
 #define PROPERTY_OP(n, call)                                                  \
     JS_BEGIN_MACRO                                                            \
         /* Fetch the left part and resolve it to a non-null object. */        \
-        SAVE_SP_AND_PC(fp);                                                   \
         FETCH_OBJECT(cx, n, lval, obj);                                       \
                                                                               \
         /* Get or set the property. */                                        \
         if (!call)                                                            \
             goto error;                                                       \
     JS_END_MACRO
 
 #define ELEMENT_OP(n, call)                                                   \
     JS_BEGIN_MACRO                                                            \
         /* Fetch the left part and resolve it to a non-null object. */        \
-        SAVE_SP_AND_PC(fp);                                                   \
         FETCH_OBJECT(cx, n - 1, lval, obj);                                   \
                                                                               \
         /* Fetch index and convert it to id suitable for use with obj. */     \
         FETCH_ELEMENT_ID(obj, n, id);                                         \
                                                                               \
         /* Get or set the element. */                                         \
         if (!call)                                                            \
             goto error;                                                       \
@@ -3283,30 +3224,28 @@ interrupt:
         if (SPROP_HAS_STUB_GETTER(sprop)) {                                   \
             /* Fast path for Object instance properties. */                   \
             JS_ASSERT((sprop)->slot != SPROP_INVALID_SLOT ||                  \
                       !SPROP_HAS_STUB_SETTER(sprop));                         \
             *vp = ((sprop)->slot != SPROP_INVALID_SLOT)                       \
                   ? LOCKED_OBJ_GET_SLOT(pobj, (sprop)->slot)                  \
                   : JSVAL_VOID;                                               \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
             if (!js_NativeGet(cx, obj, pobj, sprop, vp))                      \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define NATIVE_SET(cx,obj,sprop,vp)                                           \
     JS_BEGIN_MACRO                                                            \
         if (SPROP_HAS_STUB_SETTER(sprop) &&                                   \
             (sprop)->slot != SPROP_INVALID_SLOT) {                            \
             /* Fast path for, e.g., Object instance properties. */            \
             LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *vp);            \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
             if (!js_NativeSet(cx, obj, sprop, vp))                            \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 /*
  * Deadlocks or else bad races are likely if JS_THREADSAFE, so we must rely on
  * single-thread DEBUG js shell testing to verify property cache hits.
@@ -3314,22 +3253,21 @@ interrupt:
 #if defined DEBUG && !defined JS_THREADSAFE
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry)                \
     do {                                                                      \
         JSAtom *atom_;                                                        \
         JSObject *obj_, *pobj_;                                               \
         JSProperty *prop_;                                                    \
         JSScopeProperty *sprop_;                                              \
         uint32 sample_ = rt->gcNumber;                                        \
-        SAVE_SP_AND_PC(fp);                                                   \
         if (pcoff >= 0)                                                       \
-            GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom_);                 \
+            GET_ATOM_FROM_BYTECODE(script, regs.pc, pcoff, atom_);            \
         else                                                                  \
             atom_ = rt->atomState.lengthAtom;                                 \
-        if (JOF_OPMODE(*pc) == JOF_NAME) {                                    \
+        if (JOF_OPMODE(*regs.pc) == JOF_NAME) {                               \
             ok = js_FindProperty(cx, ATOM_TO_JSID(atom_), &obj_, &pobj_,      \
                                  &prop_);                                     \
         } else {                                                              \
             obj_ = obj;                                                       \
             ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(atom_), &pobj_,      \
                                    &prop_);                                   \
         }                                                                     \
         if (!ok)                                                              \
@@ -3359,75 +3297,72 @@ interrupt:
 #else
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
 #endif
 
           BEGIN_CASE(JSOP_SETCONST)
             LOAD_ATOM(0);
             obj = fp->varobj;
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval,
                                      JS_PropertyStub, JS_PropertyStub,
                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |
                                      JSPROP_READONLY,
                                      NULL)) {
                 goto error;
             }
             STORE_OPND(-1, rval);
           END_CASE(JSOP_SETCONST)
 
 #if JS_HAS_DESTRUCTURING
           BEGIN_CASE(JSOP_ENUMCONSTELEM)
             rval = FETCH_OPND(-3);
-            SAVE_SP_AND_PC(fp);
             FETCH_OBJECT(cx, -2, lval, obj);
             FETCH_ELEMENT_ID(obj, -1, id);
             if (!OBJ_DEFINE_PROPERTY(cx, obj, id, rval,
                                      JS_PropertyStub, JS_PropertyStub,
                                      JSPROP_ENUMERATE | JSPROP_PERMANENT |
                                      JSPROP_READONLY,
                                      NULL)) {
                 goto error;
             }
-            sp -= 3;
+            regs.sp -= 3;
           END_CASE(JSOP_ENUMCONSTELEM)
 #endif
 
           BEGIN_CASE(JSOP_BINDNAME)
             do {
                 JSPropCacheEntry *entry;
 
                 obj = fp->scopeChain;
                 if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
-                    PROPERTY_CACHE_TEST(cx, pc, obj, obj2, entry, atom);
+                    PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
                     if (!atom) {
                         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
                         JS_UNLOCK_OBJ(cx, obj2);
                         break;
                     }
                 } else {
                     entry = NULL;
                     LOAD_ATOM(0);
                 }
                 id = ATOM_TO_JSID(atom);
-                SAVE_SP_AND_PC(fp);
                 obj = js_FindIdentifierBase(cx, id, entry);
                 if (!obj)
                     goto error;
             } while (0);
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_BINDNAME)
 
 #define BITWISE_OP(OP)                                                        \
     JS_BEGIN_MACRO                                                            \
         FETCH_INT(cx, -2, i);                                                 \
         FETCH_INT(cx, -1, j);                                                 \
         i = i OP j;                                                           \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
         STORE_INT(cx, -1, i);                                                 \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_BITOR)
             BITWISE_OP(|);
           END_CASE(JSOP_BITOR)
 
           BEGIN_CASE(JSOP_BITXOR)
@@ -3463,17 +3398,17 @@ interrupt:
                 str2 = JSVAL_TO_STRING(rval);                                 \
                 cond = js_CompareStrings(str, str2) OP 0;                     \
             } else {                                                          \
                 VALUE_TO_NUMBER(cx, -2, lval, d);                             \
                 VALUE_TO_NUMBER(cx, -1, rval, d2);                            \
                 cond = JSDOUBLE_COMPARE(d, OP, d2, JS_FALSE);                 \
             }                                                                 \
         }                                                                     \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
     JS_END_MACRO
 
 /*
  * NB: These macros can't use JS_BEGIN_MACRO/JS_END_MACRO around their bodies
  * because they begin if/else chains, so callers must not put semicolons after
  * the call expressions!
  */
@@ -3485,30 +3420,28 @@ interrupt:
         (rtmp == JSVAL_OBJECT &&                                              \
          (obj2 = JSVAL_TO_OBJECT(rval)) &&                                    \
          OBJECT_IS_XML(cx, obj2))) {                                          \
         JSXMLObjectOps *ops;                                                  \
                                                                               \
         ops = (JSXMLObjectOps *) obj2->map->ops;                              \
         if (obj2 == JSVAL_TO_OBJECT(rval))                                    \
             rval = lval;                                                      \
-        SAVE_SP_AND_PC(fp);                                                   \
         if (!ops->equality(cx, obj2, rval, &cond))                            \
             goto error;                                                       \
         cond = cond OP JS_TRUE;                                               \
     } else
 
 #define EXTENDED_EQUALITY_OP(OP)                                              \
     if (ltmp == JSVAL_OBJECT &&                                               \
         (obj2 = JSVAL_TO_OBJECT(lval)) &&                                     \
         ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) {   \
         JSExtendedClass *xclasp;                                              \
                                                                               \
         xclasp = (JSExtendedClass *) clasp;                                   \
-        SAVE_SP_AND_PC(fp);                                                   \
         if (!xclasp->equality(cx, obj2, rval, &cond))                         \
             goto error;                                                       \
         cond = cond OP JS_TRUE;                                               \
     } else
 #else
 #define XML_EQUALITY_OP(OP)             /* nothing */
 #define EXTENDED_EQUALITY_OP(OP)        /* nothing */
 #endif
@@ -3553,61 +3486,61 @@ interrupt:
                     cond = js_EqualStrings(str, str2) OP JS_TRUE;             \
                 } else {                                                      \
                     VALUE_TO_NUMBER(cx, -2, lval, d);                         \
                     VALUE_TO_NUMBER(cx, -1, rval, d2);                        \
                     cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN);                \
                 }                                                             \
             }                                                                 \
         }                                                                     \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_EQ)
             EQUALITY_OP(==, JS_FALSE);
           END_CASE(JSOP_EQ)
 
           BEGIN_CASE(JSOP_NE)
             EQUALITY_OP(!=, JS_TRUE);
           END_CASE(JSOP_NE)
 
 #define STRICT_EQUALITY_OP(OP)                                                \
     JS_BEGIN_MACRO                                                            \
         rval = FETCH_OPND(-1);                                                \
         lval = FETCH_OPND(-2);                                                \
         cond = js_StrictlyEqual(cx, lval, rval) OP JS_TRUE;                   \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
         STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));                               \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_STRICTEQ)
             STRICT_EQUALITY_OP(==);
           END_CASE(JSOP_STRICTEQ)
 
           BEGIN_CASE(JSOP_STRICTNE)
             STRICT_EQUALITY_OP(!=);
           END_CASE(JSOP_STRICTNE)
 
           BEGIN_CASE(JSOP_CASE)
             STRICT_EQUALITY_OP(==);
             (void) POP();
             if (cond) {
-                len = GET_JUMP_OFFSET(pc);
+                len = GET_JUMP_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
             PUSH(lval);
           END_CASE(JSOP_CASE)
 
           BEGIN_CASE(JSOP_CASEX)
             STRICT_EQUALITY_OP(==);
             (void) POP();
             if (cond) {
-                len = GET_JUMPX_OFFSET(pc);
+                len = GET_JUMPX_OFFSET(regs.pc);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
             PUSH(lval);
           END_CASE(JSOP_CASEX)
 
           BEGIN_CASE(JSOP_LT)
             RELATIONAL_OP(<);
@@ -3636,30 +3569,29 @@ interrupt:
 #define FETCH_SHIFT(shift)                                                    \
     JS_BEGIN_MACRO                                                            \
         jsval v_;                                                             \
                                                                               \
         v_ = FETCH_OPND(-1);                                                  \
         if (v_ & JSVAL_INT) {                                                 \
             shift = JSVAL_TO_INT(v_);                                         \
         } else {                                                              \
-            SAVE_SP_AND_PC(fp);                                               \
-            shift = js_ValueToECMAInt32(cx, &sp[-1]);                         \
-            if (JSVAL_IS_NULL(sp[-1]))                                        \
+            shift = js_ValueToECMAInt32(cx, &regs.sp[-1]);                    \
+            if (JSVAL_IS_NULL(regs.sp[-1]))                                   \
                 goto error;                                                   \
         }                                                                     \
         shift &= 31;                                                          \
     JS_END_MACRO
 
 #define SIGNED_SHIFT_OP(OP)                                                   \
     JS_BEGIN_MACRO                                                            \
         FETCH_INT(cx, -2, i);                                                 \
         FETCH_SHIFT(j);                                                       \
         i = i OP j;                                                           \
-        sp--;                                                                 \
+        regs.sp--;                                                            \
         STORE_INT(cx, -1, i);                                                 \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_LSH)
             SIGNED_SHIFT_OP(<<);
           END_CASE(JSOP_LSH)
 
           BEGIN_CASE(JSOP_RSH)
@@ -3668,17 +3600,17 @@ interrupt:
 
           BEGIN_CASE(JSOP_URSH)
           {
             uint32 u;
 
             FETCH_UINT(cx, -2, u);
             FETCH_SHIFT(j);
             u >>= j;
-            sp--;
+            regs.sp--;
             STORE_UINT(cx, -1, u);
           }
           END_CASE(JSOP_URSH)
 
 #undef BITWISE_OP
 #undef SIGNED_SHIFT_OP
 
           BEGIN_CASE(JSOP_ADD)
@@ -3686,79 +3618,77 @@ interrupt:
             lval = FETCH_OPND(-2);
 #if JS_HAS_XML_SUPPORT
             if (!JSVAL_IS_PRIMITIVE(lval) &&
                 (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) &&
                 VALUE_IS_XML(cx, rval)) {
                 JSXMLObjectOps *ops;
 
                 ops = (JSXMLObjectOps *) obj2->map->ops;
-                SAVE_SP_AND_PC(fp);
                 if (!ops->concatenate(cx, obj2, rval, &rval))
                     goto error;
-                sp--;
+                regs.sp--;
                 STORE_OPND(-1, rval);
             } else
 #endif
             {
                 if (!JSVAL_IS_PRIMITIVE(lval))
                     DEFAULT_VALUE(cx, -2, JSTYPE_VOID, lval);
                 if (!JSVAL_IS_PRIMITIVE(rval))
                     DEFAULT_VALUE(cx, -1, JSTYPE_VOID, rval);
                 if ((cond = JSVAL_IS_STRING(lval)) || JSVAL_IS_STRING(rval)) {
-                    SAVE_SP_AND_PC(fp);
                     if (cond) {
                         str = JSVAL_TO_STRING(lval);
                         str2 = js_ValueToString(cx, rval);
                         if (!str2)
                             goto error;
-                        sp[-1] = STRING_TO_JSVAL(str2);
+                        regs.sp[-1] = STRING_TO_JSVAL(str2);
                     } else {
                         str2 = JSVAL_TO_STRING(rval);
                         str = js_ValueToString(cx, lval);
                         if (!str)
                             goto error;
-                        sp[-2] = STRING_TO_JSVAL(str);
+                        regs.sp[-2] = STRING_TO_JSVAL(str);
                     }
                     str = js_ConcatStrings(cx, str, str2);
                     if (!str)
                         goto error;
-                    sp--;
+                    regs.sp--;
                     STORE_OPND(-1, STRING_TO_JSVAL(str));
                 } else {
                     VALUE_TO_NUMBER(cx, -2, lval, d);
                     VALUE_TO_NUMBER(cx, -1, rval, d2);
                     d += d2;
-                    sp--;
+                    regs.sp--;
                     STORE_NUMBER(cx, -1, d);
                 }
             }
           END_CASE(JSOP_ADD)
 
 #define BINARY_OP(OP)                                                         \
     JS_BEGIN_MACRO                                                            \
         FETCH_NUMBER(cx, -1, d2);                                             \
         FETCH_NUMBER(cx, -2, d);                                              \
         d = d OP d2;                                                          \
-        sp--;                                                                 \
+        regs.sp--;                                                                 \
         STORE_NUMBER(cx, -1, d);                                              \
     JS_END_MACRO
 
           BEGIN_CASE(JSOP_SUB)
             BINARY_OP(-);
           END_CASE(JSOP_SUB)
 
           BEGIN_CASE(JSOP_MUL)
             BINARY_OP(*);
           END_CASE(JSOP_MUL)
 
           BEGIN_CASE(JSOP_DIV)
             FETCH_NUMBER(cx, -1, d2);
             FETCH_NUMBER(cx, -2, d);
-            sp--;
+            regs.sp--;
             if (d2 == 0) {
 #ifdef XP_WIN
                 /* XXX MSVC miscompiles such that (NaN == 0) */
                 if (JSDOUBLE_IS_NaN(d2))
                     rval = DOUBLE_TO_JSVAL(rt->jsNaN);
                 else
 #endif
                 if (d == 0 || JSDOUBLE_IS_NaN(d))
@@ -3772,17 +3702,17 @@ interrupt:
                 d /= d2;
                 STORE_NUMBER(cx, -1, d);
             }
           END_CASE(JSOP_DIV)
 
           BEGIN_CASE(JSOP_MOD)
             FETCH_NUMBER(cx, -1, d2);
             FETCH_NUMBER(cx, -2, d);
-            sp--;
+            regs.sp--;
             if (d2 == 0) {
                 STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN));
             } else {
 #ifdef XP_WIN
               /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */
               if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2)))
 #endif
                 d = fmod(d, d2);
@@ -3806,107 +3736,101 @@ interrupt:
              * Optimize the case of an int-tagged operand by noting that
              * INT_FITS_IN_JSVAL(i) => INT_FITS_IN_JSVAL(-i) unless i is 0
              * when -i is the negative zero which is jsdouble.
              */
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_INT(rval) && (i = JSVAL_TO_INT(rval)) != 0) {
                 i = -i;
                 JS_ASSERT(INT_FITS_IN_JSVAL(i));
-                sp[-1] = INT_TO_JSVAL(i);
+                regs.sp[-1] = INT_TO_JSVAL(i);
             } else {
-                SAVE_SP_AND_PC(fp);
                 if (JSVAL_IS_DOUBLE(rval)) {
                     d = *JSVAL_TO_DOUBLE(rval);
                 } else {
-                    d = js_ValueToNumber(cx, &sp[-1]);
-                    if (JSVAL_IS_NULL(sp[-1]))
+                    d = js_ValueToNumber(cx, &regs.sp[-1]);
+                    if (JSVAL_IS_NULL(regs.sp[-1]))
                         goto error;
-                    JS_ASSERT(JSVAL_IS_NUMBER(sp[-1]) || sp[-1] == JSVAL_TRUE);
+                    JS_ASSERT(JSVAL_IS_NUMBER(regs.sp[-1]) ||
+                              regs.sp[-1] == JSVAL_TRUE);
                 }
 #ifdef HPUX
                 /*
                  * Negation of a zero doesn't produce a negative
                  * zero on HPUX. Perform the operation by bit
                  * twiddling.
                  */
                 JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT;
 #else
                 d = -d;
 #endif
-                if (!js_NewNumberInRootedValue(cx, d, &sp[-1]))
+                if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
                     goto error;
             }
           END_CASE(JSOP_NEG)
 
           BEGIN_CASE(JSOP_POS)
             rval = FETCH_OPND(-1);
             if (!JSVAL_IS_NUMBER(rval)) {
-                SAVE_SP_AND_PC(fp);
-                d = js_ValueToNumber(cx, &sp[-1]);
-                rval = sp[-1];
+                d = js_ValueToNumber(cx, &regs.sp[-1]);
+                rval = regs.sp[-1];
                 if (JSVAL_IS_NULL(rval))
                     goto error;
                 if (rval == JSVAL_TRUE) {
-                    if (!js_NewNumberInRootedValue(cx, d, &sp[-1]))
+                    if (!js_NewNumberInRootedValue(cx, d, &regs.sp[-1]))
                         goto error;
                 } else {
                     JS_ASSERT(JSVAL_IS_NUMBER(rval));
                 }
             }
           END_CASE(JSOP_POS)
 
           BEGIN_CASE(JSOP_NEW)
             /* Get immediate argc and find the constructor function. */
-            argc = GET_ARGC(pc);
-            SAVE_SP_AND_PC(fp);
-            vp = sp - (2 + argc);
+            argc = GET_ARGC(regs.pc);
+            vp = regs.sp - (2 + argc);
             JS_ASSERT(vp >= fp->spbase);
 
             if (!js_InvokeConstructor(cx, vp, argc))
                 goto error;
-            sp = vp + 1;
+            regs.sp = vp + 1;
             LOAD_INTERRUPT_HANDLER(cx);
           END_CASE(JSOP_NEW)
 
           BEGIN_CASE(JSOP_DELNAME)
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
-
-            SAVE_SP_AND_PC(fp);
             if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
                 goto error;
 
             /* ECMA says to return true if name is undefined or inherited. */
-            rval = JSVAL_TRUE;
+            PUSH_OPND(JSVAL_TRUE);
             if (prop) {
                 OBJ_DROP_PROPERTY(cx, obj2, prop);
-                if (!OBJ_DELETE_PROPERTY(cx, obj, id, &rval))
+                if (!OBJ_DELETE_PROPERTY(cx, obj, id, &regs.sp[-1]))
                     goto error;
             }
-            PUSH_OPND(rval);
           END_CASE(JSOP_DELNAME)
 
           BEGIN_CASE(JSOP_DELPROP)
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
             PROPERTY_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
             STORE_OPND(-1, rval);
           END_CASE(JSOP_DELPROP)
 
           BEGIN_CASE(JSOP_DELELEM)
             ELEMENT_OP(-1, OBJ_DELETE_PROPERTY(cx, obj, id, &rval));
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_DELELEM)
 
           BEGIN_CASE(JSOP_TYPEOFEXPR)
           BEGIN_CASE(JSOP_TYPEOF)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             type = JS_TypeOfValue(cx, rval);
             atom = rt->atomState.typeAtoms[type];
             STORE_OPND(-1, ATOM_KEY(atom));
           END_CASE(JSOP_TYPEOF)
 
           BEGIN_CASE(JSOP_VOID)
             STORE_OPND(-1, JSVAL_VOID);
           END_CASE(JSOP_VOID)
@@ -3927,95 +3851,90 @@ interrupt:
           BEGIN_CASE(JSOP_DECPROP)
           BEGIN_CASE(JSOP_PROPINC)
           BEGIN_CASE(JSOP_PROPDEC)
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
             i = -1;
 
           fetch_incop_obj:
-            SAVE_SP_AND_PC(fp);
             FETCH_OBJECT(cx, i, lval, obj);
             if (id == 0)
                 FETCH_ELEMENT_ID(obj, -1, id);
             goto do_incop;
 
           BEGIN_CASE(JSOP_INCNAME)
           BEGIN_CASE(JSOP_DECNAME)
           BEGIN_CASE(JSOP_NAMEINC)
           BEGIN_CASE(JSOP_NAMEDEC)
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
-
-            SAVE_SP_AND_PC(fp);
             if (!js_FindProperty(cx, id, &obj, &obj2, &prop))
                 goto error;
             if (!prop)
                 goto atom_not_defined;
             OBJ_DROP_PROPERTY(cx, obj2, prop);
 
           do_incop:
           {
             const JSCodeSpec *cs;
             jsval v;
 
             /*
              * We need a root to store the value to leave on the stack until
              * we have done with OBJ_SET_PROPERTY.
              */
             PUSH_OPND(JSVAL_NULL);
-            SAVE_SP(fp);
-            if (!OBJ_GET_PROPERTY(cx, obj, id, &sp[-1]))
+            if (!OBJ_GET_PROPERTY(cx, obj, id, &regs.sp[-1]))
                 goto error;
 
             cs = &js_CodeSpec[op];
             JS_ASSERT(cs->ndefs == 1);
             JS_ASSERT((cs->format & JOF_TMPSLOT_MASK) == JOF_TMPSLOT2);
-            v = sp[-1];
+            v = regs.sp[-1];
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(v))) {
                 jsval incr;
 
                 incr = (cs->format & JOF_INC) ? 2 : -2;
                 if (cs->format & JOF_POST) {
-                    sp[-1] = v + incr;
+                    regs.sp[-1] = v + incr;
                 } else {
                     v += incr;
-                    sp[-1] = v;
+                    regs.sp[-1] = v;
                 }
                 fp->flags |= JSFRAME_ASSIGNING;
-                ok = OBJ_SET_PROPERTY(cx, obj, id, &sp[-1]);
+                ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
                 fp->flags &= ~JSFRAME_ASSIGNING;
                 if (!ok)
                     goto error;
 
                 /*
-                 * We must set sp[-1] to v for both post and pre increments
-                 * as the setter overwrites sp[-1].
+                 * We must set regs.sp[-1] to v for both post and pre increments
+                 * as the setter overwrites regs.sp[-1].
                  */
-                sp[-1] = v;
+                regs.sp[-1] = v;
             } else {
                 /* We need an extra root for the result. */
                 PUSH_OPND(JSVAL_NULL);
-                SAVE_SP(fp);
-                if (!js_DoIncDec(cx, cs, &sp[-2], &sp[-1]))
+                if (!js_DoIncDec(cx, cs, &regs.sp[-2], &regs.sp[-1]))
                     goto error;
                 fp->flags |= JSFRAME_ASSIGNING;
-                ok = OBJ_SET_PROPERTY(cx, obj, id, &sp[-1]);
+                ok = OBJ_SET_PROPERTY(cx, obj, id, &regs.sp[-1]);
                 fp->flags &= ~JSFRAME_ASSIGNING;
                 if (!ok)
                     goto error;
-                sp--;
+                regs.sp--;
             }
 
             if (cs->nuses == 0) {
-                /* sp[-1] already contains the result of name increment. */
+                /* regs.sp[-1] already contains the result of name increment. */
             } else {
-                rtmp = sp[-1];
-                sp -= cs->nuses;
-                sp[-1] = rtmp;
+                rtmp = regs.sp[-1];
+                regs.sp -= cs->nuses;
+                regs.sp[-1] = rtmp;
             }
             len = cs->length;
             DO_NEXT_OP(len);
           }
 
           {
             jsval incr, incr2;
 
@@ -4025,33 +3944,33 @@ interrupt:
           BEGIN_CASE(JSOP_ARGDEC)
             incr = -2; incr2 =  0; goto do_arg_incop;
           BEGIN_CASE(JSOP_INCARG)
             incr =  2; incr2 =  2; goto do_arg_incop;
           BEGIN_CASE(JSOP_ARGINC)
             incr =  2; incr2 =  0;
 
           do_arg_incop:
-            slot = GET_ARGNO(pc);
+            slot = GET_ARGNO(regs.pc);
             JS_ASSERT(slot < fp->fun->nargs);
             METER_SLOT_OP(op, slot);
             vp = fp->argv + slot;
             goto do_int_fast_incop;
 
           BEGIN_CASE(JSOP_DECLOCAL)
             incr = -2; incr2 = -2; goto do_local_incop;
           BEGIN_CASE(JSOP_LOCALDEC)
             incr = -2; incr2 =  0; goto do_local_incop;
           BEGIN_CASE(JSOP_INCLOCAL)
             incr =  2; incr2 =  2; goto do_local_incop;
           BEGIN_CASE(JSOP_LOCALINC)
             incr =  2; incr2 =  0;
 
           do_local_incop:
-            slot = GET_UINT16(pc);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot < script->depth);
             vp = fp->spbase + slot;
             goto do_int_fast_incop;
 
           BEGIN_CASE(JSOP_DECVAR)
             incr = -2; incr2 = -2; goto do_var_incop;
           BEGIN_CASE(JSOP_VARDEC)
             incr = -2; incr2 =  0; goto do_var_incop;
@@ -4061,30 +3980,29 @@ interrupt:
             incr =  2; incr2 =  0;
 
           /*
            * do_var_incop comes right before do_int_fast_incop as we want to
            * avoid an extra jump for variable cases as var++ is more frequent
            * than arg++ or local++;
            */
           do_var_incop:
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->fun->u.i.nvars);
             METER_SLOT_OP(op, slot);
             vp = fp->vars + slot;
 
           do_int_fast_incop:
             rval = *vp;
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
                 *vp = rval + incr;
                 PUSH_OPND(rval + incr2);
             } else {
                 PUSH_OPND(rval);
-                SAVE_SP_AND_PC(fp);
-                if (!js_DoIncDec(cx, &js_CodeSpec[op], &sp[-1], vp))
+                if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
                     goto error;
             }
             len = JSOP_INCARG_LENGTH;
             JS_ASSERT(len == js_CodeSpec[op].length);
             DO_NEXT_OP(len);
           }
 
 /* NB: This macro doesn't use JS_BEGIN_MACRO/JS_END_MACRO around its body. */
@@ -4106,37 +4024,36 @@ interrupt:
           BEGIN_CASE(JSOP_GVARINC)
             FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC,  2,  0);
 
 #undef FAST_GLOBAL_INCREMENT_OP
 
           do_global_incop:
             JS_ASSERT((js_CodeSpec[op].format & JOF_TMPSLOT_MASK) ==
                       JOF_TMPSLOT2);
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->nvars);
             METER_SLOT_OP(op, slot);
             lval = fp->vars[slot];
             if (JSVAL_IS_NULL(lval)) {
                 op = op2;
                 DO_OP();
             }
             slot = JSVAL_TO_INT(lval);
             rval = OBJ_GET_SLOT(cx, fp->varobj, slot);
             if (JS_LIKELY(CAN_DO_FAST_INC_DEC(rval))) {
                 PUSH_OPND(rval + incr2);
                 rval += incr;
             } else {
                 PUSH_OPND(rval);
                 PUSH_OPND(JSVAL_NULL);  /* Extra root */
-                SAVE_SP_AND_PC(fp);
-                if (!js_DoIncDec(cx, &js_CodeSpec[op], &sp[-2], &sp[-1]))
+                if (!js_DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-2], &regs.sp[-1]))
                     goto error;
-                rval = sp[-1];
-                --sp;
+                rval = regs.sp[-1];
+                --regs.sp;
             }
             OBJ_SET_SLOT(cx, fp->varobj, slot, rval);
             len = JSOP_INCGVAR_LENGTH;  /* all gvar incops are same length */
             JS_ASSERT(len == js_CodeSpec[op].length);
             DO_NEXT_OP(len);
           }
 
 #define COMPUTE_THIS(cx, fp, obj)                                             \
@@ -4163,33 +4080,33 @@ interrupt:
             PUSH(JSVAL_NULL);
             len = JSOP_GETTHISPROP_LENGTH;
             goto do_getprop_with_obj;
 
 #undef COMPUTE_THIS
 
           BEGIN_CASE(JSOP_GETARGPROP)
             i = ARGNO_LEN;
-            slot = GET_ARGNO(pc);
+            slot = GET_ARGNO(regs.pc);
             JS_ASSERT(slot < fp->fun->nargs);
             PUSH_OPND(fp->argv[slot]);
             len = JSOP_GETARGPROP_LENGTH;
             goto do_getprop_body;
 
           BEGIN_CASE(JSOP_GETVARPROP)
             i = VARNO_LEN;
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->fun->u.i.nvars);
             PUSH_OPND(fp->vars[slot]);
             len = JSOP_GETVARPROP_LENGTH;
             goto do_getprop_body;
 
           BEGIN_CASE(JSOP_GETLOCALPROP)
             i = UINT16_LEN;
-            slot = GET_UINT16(pc);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot < script->depth);
             PUSH_OPND(fp->spbase[slot]);
             len = JSOP_GETLOCALPROP_LENGTH;
             goto do_getprop_body;
 
           BEGIN_CASE(JSOP_GETPROP)
           BEGIN_CASE(JSOP_GETXPROP)
             i = 0;
@@ -4201,75 +4118,71 @@ interrupt:
           do_getprop_with_lval:
             VALUE_TO_OBJECT(cx, -1, lval, obj);
 
           do_getprop_with_obj:
             do {
                 JSPropCacheEntry *entry;
 
                 if (JS_LIKELY(obj->map->ops->getProperty == js_GetProperty)) {
-                    PROPERTY_CACHE_TEST(cx, pc, obj, obj2, entry, atom);
+                    PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
                     if (!atom) {
                         ASSERT_VALID_PROPERTY_CACHE_HIT(i, obj, obj2, entry);
                         if (PCVAL_IS_OBJECT(entry->vword)) {
                             rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
                         } else if (PCVAL_IS_SLOT(entry->vword)) {
                             slot = PCVAL_TO_SLOT(entry->vword);
                             JS_ASSERT(slot < obj2->map->freeslot);
                             rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
                         } else {
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                             sprop = PCVAL_TO_SPROP(entry->vword);
-                            SAVE_SP_AND_PC(fp);
                             NATIVE_GET(cx, obj, obj2, sprop, &rval);
                         }
                         JS_UNLOCK_OBJ(cx, obj2);
                         break;
                     }
                 } else {
                     entry = NULL;
                     if (i < 0)
                         atom = rt->atomState.lengthAtom;
                     else
                         LOAD_ATOM(i);
                 }
                 id = ATOM_TO_JSID(atom);
-                SAVE_SP_AND_PC(fp);
                 if (entry
                     ? !js_GetPropertyHelper(cx, obj, id, &rval, &entry)
                     : !OBJ_GET_PROPERTY(cx, obj, id, &rval)) {
                     goto error;
                 }
             } while (0);
 
             STORE_OPND(-1, rval);
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_LENGTH)
             lval = FETCH_OPND(-1);
             if (JSVAL_IS_STRING(lval)) {
                 str = JSVAL_TO_STRING(lval);
-                sp[-1] = INT_TO_JSVAL(JSSTRING_LENGTH(str));
+                regs.sp[-1] = INT_TO_JSVAL(JSSTRING_LENGTH(str));
             } else if (!JSVAL_IS_PRIMITIVE(lval) &&
                        (obj = JSVAL_TO_OBJECT(lval), OBJ_IS_ARRAY(cx, obj))) {
                 jsuint length;
 
                 /*
                  * We know that the array is created with only its 'length'
                  * private data in a fixed slot at JSSLOT_ARRAY_LENGTH. See
                  * also JSOP_ARRAYPUSH, far below.
                  */
                 length = obj->fslots[JSSLOT_ARRAY_LENGTH];
                 if (length <= JSVAL_INT_MAX) {
-                    sp[-1] = INT_TO_JSVAL(length);
-                } else {
-                    SAVE_SP_AND_PC(fp);
-                    if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,
-                                                   &sp[-1]))
-                        goto error;
+                    regs.sp[-1] = INT_TO_JSVAL(length);
+                } else if (!js_NewDoubleInRootedValue(cx, (jsdouble) length,
+                                                      &regs.sp[-1])) {
+                    goto error;
                 }
             } else {
                 i = -1;
                 len = JSOP_LENGTH_LENGTH;
                 goto do_getprop_with_lval;
             }
           END_CASE(JSOP_LENGTH)
 
@@ -4277,18 +4190,16 @@ interrupt:
           {
             JSObject *aobj;
             JSPropCacheEntry *entry;
 
             lval = FETCH_OPND(-1);
             if (!JSVAL_IS_PRIMITIVE(lval)) {
                 obj = JSVAL_TO_OBJECT(lval);
             } else {
-                SAVE_SP_AND_PC(fp);
-
                 if (JSVAL_IS_STRING(lval)) {
                     i = JSProto_String;
                 } else if (JSVAL_IS_NUMBER(lval)) {
                     i = JSProto_Number;
                 } else if (JSVAL_IS_BOOLEAN(lval)) {
                     i = JSProto_Boolean;
                 } else {
                     JS_ASSERT(JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval));
@@ -4297,17 +4208,17 @@ interrupt:
                 }
 
                 if (!js_GetClassPrototype(cx, NULL, INT_TO_JSID(i), &obj))
                     goto error;
             }
 
             aobj = OBJ_IS_DENSE_ARRAY(cx, obj) ? OBJ_GET_PROTO(cx, obj) : obj;
             if (JS_LIKELY(aobj->map->ops->getProperty == js_GetProperty)) {
-                PROPERTY_CACHE_TEST(cx, pc, aobj, obj2, entry, atom);
+                PROPERTY_CACHE_TEST(cx, regs.pc, aobj, obj2, entry, atom);
                 if (!atom) {
                     ASSERT_VALID_PROPERTY_CACHE_HIT(0, aobj, obj2, entry);
                     if (PCVAL_IS_OBJECT(entry->vword)) {
                         rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
                     } else if (PCVAL_IS_SLOT(entry->vword)) {
                         slot = PCVAL_TO_SLOT(entry->vword);
                         JS_ASSERT(slot < obj2->map->freeslot);
                         rval = LOCKED_OBJ_GET_SLOT(obj2, slot);
@@ -4327,17 +4238,16 @@ interrupt:
             }
 
             /*
              * Cache miss: use the immediate atom that was loaded for us under
              * PROPERTY_CACHE_TEST.
              */
             id = ATOM_TO_JSID(atom);
             PUSH(JSVAL_NULL);
-            SAVE_SP_AND_PC(fp);
             if (!JSVAL_IS_PRIMITIVE(lval)) {
 #if JS_HAS_XML_SUPPORT
                 /* Special-case XML object method lookup, per ECMA-357. */
                 if (OBJECT_IS_XML(cx, obj)) {
                     JSXMLObjectOps *ops;
 
                     ops = (JSXMLObjectOps *) obj->map->ops;
                     obj = ops->getMethod(cx, obj, id, &rval);
@@ -4363,26 +4273,25 @@ interrupt:
           end_callprop:
             /* Wrap primitive lval in object clothing if necessary. */
             if (JSVAL_IS_PRIMITIVE(lval)) {
                 /* FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=412571 */
                 if (!VALUE_IS_FUNCTION(cx, rval) ||
                     (obj = JSVAL_TO_OBJECT(rval),
                      fun = GET_FUNCTION_PRIVATE(cx, obj),
                      !PRIMITIVE_THIS_TEST(fun, lval))) {
-                    if (!js_PrimitiveToObject(cx, &sp[-1]))
+                    if (!js_PrimitiveToObject(cx, &regs.sp[-1]))
                         goto error;
                 }
             }
 #if JS_HAS_NO_SUCH_METHOD
             if (JS_UNLIKELY(rval == JSVAL_VOID)) {
                 LOAD_ATOM(0);
-                sp[-2] = ATOM_KEY(atom);
-                SAVE_SP_AND_PC(fp);
-                if (!js_OnUnknownMethod(cx, sp - 2))
+                regs.sp[-2] = ATOM_KEY(atom);
+                if (!js_OnUnknownMethod(cx, regs.sp - 2))
                     goto error;
             }
 #endif
           }
           END_CASE(JSOP_CALLPROP)
 
           BEGIN_CASE(JSOP_SETNAME)
           BEGIN_CASE(JSOP_SETPROP)
@@ -4414,20 +4323,21 @@ interrupt:
                      * of property additions. And second:
                      *
                      *   o.p = x;
                      *
                      * in a frequently executed method or loop body, where p
                      * will (possibly after the first iteration) always exist
                      * in native object o.
                      */
-                    entry = &cache->table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
+                    entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc,
+                                                                 kshape)];
                     PCMETER(cache->tests++);
                     PCMETER(cache->settests++);
-                    if (entry->kpc == pc && entry->kshape == kshape) {
+                    if (entry->kpc == regs.pc && entry->kshape == kshape) {
                         JSScope *scope;
 
                         JS_LOCK_OBJ(cx, obj);
                         scope = OBJ_SCOPE(obj);
                         if (scope->shape == kshape) {
                             JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                             sprop = PCVAL_TO_SPROP(entry->vword);
                             JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
@@ -4541,17 +4451,17 @@ interrupt:
 
                             PCMETER(cache->setpcmisses++);
                             atom = NULL;
                         }
 
                         JS_UNLOCK_OBJ(cx, obj);
                     }
 
-                    atom = js_FullTestPropertyCache(cx, pc, &obj, &obj2,
+                    atom = js_FullTestPropertyCache(cx, regs.pc, &obj, &obj2,
                                                     &entry);
                     if (atom) {
                         PCMETER(cache->misses++);
                         PCMETER(cache->setmisses++);
                     } else {
                         ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
                         if (obj == obj2) {
                             if (PCVAL_IS_SLOT(entry->vword)) {
@@ -4569,31 +4479,29 @@ interrupt:
                         if (obj == obj2 && !PCVAL_IS_OBJECT(entry->vword))
                             break;
                     }
                 }
 
                 if (!atom)
                     LOAD_ATOM(0);
                 id = ATOM_TO_JSID(atom);
-                SAVE_SP_AND_PC(fp);
                 if (entry
                     ? !js_SetPropertyHelper(cx, obj, id, &rval, &entry)
                     : !OBJ_SET_PROPERTY(cx, obj, id, &rval)) {
                     goto error;
                 }
             } while (0);
 
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_SETPROP)
 
           BEGIN_CASE(JSOP_GETELEM)
             /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
-            SAVE_SP_AND_PC(fp);
             lval = FETCH_OPND(-2);
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_STRING(lval) && JSVAL_IS_INT(rval)) {
                 str = JSVAL_TO_STRING(lval);
                 i = JSVAL_TO_INT(rval);
                 if ((size_t)i < JSSTRING_LENGTH(str)) {
                     str = js_GetUnitString(cx, str, (size_t)i);
                     if (!str)
@@ -4621,44 +4529,42 @@ interrupt:
             } else {
                 if (!js_InternNonIntElementId(cx, obj, rval, &id))
                     goto error;
             }
 
             if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
                 goto error;
           end_getelem:
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_GETELEM)
 
           BEGIN_CASE(JSOP_CALLELEM)
             /*
              * FIXME: JSOP_CALLELEM should call getMethod on XML objects as
              * CALLPROP does. See bug 362910.
              */
             ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));
 #if JS_HAS_NO_SUCH_METHOD
             if (JS_UNLIKELY(rval == JSVAL_VOID)) {
-                sp[-2] = sp[-1];
-                sp[-1] = OBJECT_TO_JSVAL(obj);
-                SAVE_SP_AND_PC(fp);
-                if (!js_OnUnknownMethod(cx, sp - 2))
+                regs.sp[-2] = regs.sp[-1];
+                regs.sp[-1] = OBJECT_TO_JSVAL(obj);
+                if (!js_OnUnknownMethod(cx, regs.sp - 2))
                     goto error;
             } else
 #endif
             {
                 STORE_OPND(-2, rval);
                 STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
             }
           END_CASE(JSOP_CALLELEM)
 
           BEGIN_CASE(JSOP_SETELEM)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             FETCH_OBJECT(cx, -3, lval, obj);
             FETCH_ELEMENT_ID(obj, -2, id);
             if (OBJ_IS_DENSE_ARRAY(cx, obj) && JSID_IS_INT(id)) {
                 jsuint length;
 
                 length = ARRAY_DENSE_LENGTH(obj);
                 i = JSID_TO_INT(id);
                 if ((jsuint)i < length) {
@@ -4669,106 +4575,103 @@ interrupt:
                     }
                     obj->dslots[i] = rval;
                     goto end_setelem;
                 }
             }
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
         end_setelem:
-            sp -= 2;
+            regs.sp -= 2;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_SETELEM)
 
           BEGIN_CASE(JSOP_ENUMELEM)
             /* Funky: the value to set is under the [obj, id] pair. */
             rval = FETCH_OPND(-3);
-            SAVE_SP_AND_PC(fp);
             FETCH_OBJECT(cx, -2, lval, obj);
             FETCH_ELEMENT_ID(obj, -1, id);
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
-            sp -= 3;
+            regs.sp -= 3;
           END_CASE(JSOP_ENUMELEM)
 
           BEGIN_CASE(JSOP_CALL)
           BEGIN_CASE(JSOP_EVAL)
-            argc = GET_ARGC(pc);
-            vp = sp - (argc + 2);
+            argc = GET_ARGC(regs.pc);
+            vp = regs.sp - (argc + 2);
             lval = *vp;
-            SAVE_SP_AND_PC(fp);
             if (VALUE_IS_FUNCTION(cx, lval)) {
                 obj = JSVAL_TO_OBJECT(lval);
                 fun = GET_FUNCTION_PRIVATE(cx, obj);
 
                 if (FUN_INTERPRETED(fun)) {
-                    uintN nframeslots, nvars, nslots, missing;
+                    uintN nframeslots, nvars, missing;
                     JSArena *a;
-                    jsuword avail, nbytes;
+                    jsuword nbytes;
                     void *newmark;
-                    jsval *newsp, *rvp;
-                    JSBool overflow;
+                    jsval *newsp;
                     JSInlineFrame *newifp;
                     JSInterpreterHook hook;
 
                     /* Compute the total number of stack slots needed by fun. */
                     nframeslots = JS_HOWMANY(sizeof(JSInlineFrame),
                                              sizeof(jsval));
                     nvars = fun->u.i.nvars;
                     script = fun->u.i.script;
                     atoms = script->atomMap.vector;
-                    nslots = nframeslots + nvars + script->depth;
+                    nbytes = (nframeslots + nvars + script->depth) *
+                             sizeof(jsval);
 
                     /* Allocate missing expected args adjacent to actuals. */
-                    missing = (fun->nargs > argc) ? fun->nargs - argc : 0;
                     a = cx->stackPool.current;
-                    avail = a->avail;
-                    newmark = (void *) avail;
-                    if (missing) {
-                        newsp = sp + missing;
-                        overflow = (jsuword) newsp > a->limit;
-                        if (overflow)
-                            nslots += 2 + argc + missing;
-                        else if ((jsuword) newsp > avail)
-                            avail = a->avail = (jsuword) newsp;
+                    newmark = (void *) a->avail;
+                    if (fun->nargs <= argc) {
+                        missing = 0;
+                    } else {
+                        newsp = vp + 2 + fun->nargs;
+                        JS_ASSERT(newsp > regs.sp);
+                        if ((jsuword) newsp <= a->limit) {
+                            if ((jsuword) newsp > a->avail)
+                                a->avail = (jsuword) newsp;
+                            do {
+                                *--newsp = JSVAL_VOID;
+                            } while (newsp != regs.sp);
+                            missing = 0;
+                        } else {
+                            missing = fun->nargs - argc;
+                            nbytes += (2 + fun->nargs) * sizeof(jsval);
+                        }
                     }
-#ifdef __GNUC__
-                    else overflow = JS_FALSE; /* suppress bogus gcc warnings */
-#endif
 
                     /* Allocate the inline frame with its vars and operands. */
-                    newsp = (jsval *) avail;
-                    nbytes = nslots * sizeof(jsval);
-                    avail += nbytes;
-                    if (avail <= a->limit) {
-                        a->avail = avail;
+                    if (a->avail + nbytes <= a->limit) {
+                        newsp = (jsval *) a->avail;
+                        a->avail += nbytes;
+                        JS_ASSERT(missing == 0);
                     } else {
                         JS_ARENA_ALLOCATE_CAST(newsp, jsval *, &cx->stackPool,
                                                nbytes);
                         if (!newsp) {
                             js_ReportOutOfScriptQuota(cx);
                             goto bad_inline_call;
                         }
-                    }
-
-                    /*
-                     * Move args if missing overflow arena a, then push any
-                     * missing args.
-                     */
-                    rvp = vp;
-                    if (missing) {
-                        if (overflow) {
+
+                        /*
+                         * Move args if missing overflow arena a, then push
+                         * any missing args.
+                         */
+                        if (missing) {
                             memcpy(newsp, vp, (2 + argc) * sizeof(jsval));
                             vp = newsp;
-                            sp = vp + 2 + argc;
-                            newsp = sp + missing;
+                            newsp = vp + 2 + argc;
+                            do {
+                                *newsp++ = JSVAL_VOID;
+                            } while (--missing != 0);
                         }
-                        do {
-                            PUSH(JSVAL_VOID);
-                        } while (--missing != 0);
                     }
 
                     /* Claim space for the stack frame and initialize it. */
                     newifp = (JSInlineFrame *) newsp;
                     newsp += nframeslots;
                     newifp->frame.callobj = NULL;
                     newifp->frame.argsobj = NULL;
                     newifp->frame.varobj = NULL;
@@ -4788,35 +4691,33 @@ interrupt:
                     newifp->frame.flags = 0;
                     newifp->frame.dormantNext = NULL;
                     newifp->frame.xmlNamespace = NULL;
                     newifp->frame.blockChain = NULL;
 #ifdef DEBUG
                     newifp->frame.pcDisabledSave =
                         JS_PROPERTY_CACHE(cx).disabled;
 #endif
-                    newifp->rvp = rvp;
                     newifp->mark = newmark;
 
                     /* Compute the 'this' parameter now that argv is set. */
                     JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
                     JS_ASSERT(JSVAL_IS_OBJECT(vp[1]));
                     newifp->frame.thisp = (JSObject *)vp[1];
 
                     /* Push void to initialize local variables. */
-                    sp = newsp;
                     while (nvars--)
-                        PUSH(JSVAL_VOID);
-                    newifp->frame.spbase = sp;
-                    SAVE_SP(&newifp->frame);
+                        *newsp++ = JSVAL_VOID;
+
+                    newifp->frame.regs = NULL;
+                    newifp->frame.spbase = NULL;
 
                     /* Call the debugger hook if present. */
                     hook = cx->debugHooks->callHook;
                     if (hook) {
-                        newifp->frame.pc = NULL;
                         newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
                                                 cx->debugHooks->callHookData);
                         LOAD_INTERRUPT_HANDLER(cx);
                     } else {
                         newifp->hookData = NULL;
                     }
 
                     /* Scope with a call object parented by callee's parent. */
@@ -4829,38 +4730,42 @@ interrupt:
                     newifp->callerVersion = (JSVersion) cx->version;
                     if (JS_LIKELY(cx->version == currentVersion)) {
                         currentVersion = (JSVersion) script->version;
                         if (currentVersion != cx->version)
                             js_SetVersion(cx, currentVersion);
                     }
 
                     /* Push the frame and set interpreter registers. */
+                    newifp->callerRegs = regs;
+                    fp->regs = &newifp->callerRegs;
+                    regs.sp = newifp->frame.spbase = newsp;
+                    regs.pc = script->code;
+                    newifp->frame.regs = &regs;
                     cx->fp = fp = &newifp->frame;
-                    pc = script->code;
+
                     inlineCallCount++;
                     JS_RUNTIME_METER(rt, inlineCalls);
 
 #ifdef INCLUDE_MOZILLA_DTRACE
                     /* DTrace function entry, inlines */
                     if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED())
                         jsdtrace_function_entry(cx, fp, fun);
                     if (JAVASCRIPT_FUNCTION_INFO_ENABLED())
                         jsdtrace_function_info(cx, fp, fp->down, fun);
                     if (JAVASCRIPT_FUNCTION_ARGS_ENABLED())
                         jsdtrace_function_args(cx, fp, fun);
 #endif
 
                     /* Load first op and dispatch it (safe since JSOP_STOP). */
-                    op = (JSOp) *pc;
+                    op = (JSOp) *regs.pc;
                     DO_OP();
 
                   bad_inline_call:
-                    RESTORE_SP(fp);
-                    JS_ASSERT(fp->pc == pc);
+                    JS_ASSERT(fp->regs == &regs);
                     script = fp->script;
                     atoms = script->atomMap.vector;
                     js_FreeRawStack(cx, newmark);
                     goto error;
                 }
 
 #ifdef INCLUDE_MOZILLA_DTRACE
                 /* DTrace function entry, non-inlines */
@@ -4879,55 +4784,54 @@ interrupt:
                     if (argc < fun->u.n.minargs) {
                         uintN nargs;
 
                         /*
                          * If we can't fit missing args and local roots in
                          * this frame's operand stack, take the slow path.
                          */
                         nargs = fun->u.n.minargs - argc;
-                        if (sp + nargs > fp->spbase + script->depth)
+                        if (regs.sp + nargs > fp->spbase + script->depth)
                             goto do_invoke;
                         do {
                             PUSH(JSVAL_VOID);
                         } while (--nargs != 0);
-                        SAVE_SP(fp);
                     }
 
                     JS_ASSERT(JSVAL_IS_OBJECT(vp[1]) ||
                               PRIMITIVE_THIS_TEST(fun, vp[1]));
 
                     ok = ((JSFastNative) fun->u.n.native)(cx, argc, vp);
 #ifdef INCLUDE_MOZILLA_DTRACE
                     if (VALUE_IS_FUNCTION(cx, lval)) {
                         if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
                             jsdtrace_function_rval(cx, fp, fun);
                         if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
                             jsdtrace_function_return(cx, fp, fun);
                     }
 #endif
                     if (!ok)
                         goto error;
-                    sp = vp + 1;
+                    regs.sp = vp + 1;
                     goto end_call;
                 }
             }
 
           do_invoke:
             ok = js_Invoke(cx, argc, vp, 0);
 #ifdef INCLUDE_MOZILLA_DTRACE
             /* DTrace function return, non-inlines */
             if (VALUE_IS_FUNCTION(cx, lval)) {
                 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
                     jsdtrace_function_rval(cx, fp, fun);
                 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
                     jsdtrace_function_return(cx, fp, fun);
             }
 #endif
-            sp = vp + 1;
+            regs.sp = vp + 1;
             LOAD_INTERRUPT_HANDLER(cx);
             if (!ok)
                 goto error;
             JS_RUNTIME_METER(rt, nonInlineCalls);
 
           end_call:
 #if JS_HAS_LVALUE_RETURN
             if (cx->rval2set) {
@@ -4943,30 +4847,29 @@ interrupt:
                  * ECMA "reference type", which can be used on the right- or
                  * left-hand side of assignment ops.  Note well: only native
                  * methods can return reference types.  See JSOP_SETCALL just
                  * below for the left-hand-side case.
                  */
                 PUSH_OPND(cx->rval2);
                 ELEMENT_OP(-1, OBJ_GET_PROPERTY(cx, obj, id, &rval));
 
-                sp--;
+                regs.sp--;
                 STORE_OPND(-1, rval);
                 cx->rval2set = JS_FALSE;
             }
 #endif /* JS_HAS_LVALUE_RETURN */
           END_CASE(JSOP_CALL)
 
 #if JS_HAS_LVALUE_RETURN
           BEGIN_CASE(JSOP_SETCALL)
-            argc = GET_ARGC(pc);
-            SAVE_SP_AND_PC(fp);
-            vp = sp - argc - 2;
+            argc = GET_ARGC(regs.pc);
+            vp = regs.sp - argc - 2;
             ok = js_Invoke(cx, argc, vp, 0);
-            sp = vp + 1;
+            regs.sp = vp + 1;
             LOAD_INTERRUPT_HANDLER(cx);
             if (!ok)
                 goto error;
             if (!cx->rval2set) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_LEFTSIDE_OF_ASS);
                 goto error;
             }
@@ -4977,17 +4880,17 @@ interrupt:
 
           BEGIN_CASE(JSOP_NAME)
           BEGIN_CASE(JSOP_CALLNAME)
           {
             JSPropCacheEntry *entry;
 
             obj = fp->scopeChain;
             if (JS_LIKELY(OBJ_IS_NATIVE(obj))) {
-                PROPERTY_CACHE_TEST(cx, pc, obj, obj2, entry, atom);
+                PROPERTY_CACHE_TEST(cx, regs.pc, obj, obj2, entry, atom);
                 if (!atom) {
                     ASSERT_VALID_PROPERTY_CACHE_HIT(0, obj, obj2, entry);
                     if (PCVAL_IS_OBJECT(entry->vword)) {
                         rval = PCVAL_OBJECT_TO_JSVAL(entry->vword);
                         JS_UNLOCK_OBJ(cx, obj2);
                         goto do_push_rval;
                     }
 
@@ -5004,24 +4907,23 @@ interrupt:
                     goto do_native_get;
                 }
             } else {
                 entry = NULL;
                 LOAD_ATOM(0);
             }
 
             id = ATOM_TO_JSID(atom);
-            SAVE_SP_AND_PC(fp);
             if (js_FindPropertyHelper(cx, id, &obj, &obj2, &prop, &entry) < 0)
                 goto error;
             if (!prop) {
                 /* Kludge to allow (typeof foo == "undefined") tests. */
                 len = JSOP_NAME_LENGTH;
                 endpc = script->code + script->length;
-                for (pc2 = pc + len; pc2 < endpc; pc2++) {
+                for (pc2 = regs.pc + len; pc2 < endpc; pc2++) {
                     op2 = (JSOp)*pc2;
                     if (op2 == JSOP_TYPEOF) {
                         PUSH_OPND(JSVAL_VOID);
                         DO_NEXT_OP(len);
                     }
                     if (op2 != JSOP_GROUP)
                         break;
                 }
@@ -5044,45 +4946,45 @@ interrupt:
           do_push_rval:
             PUSH_OPND(rval);
             if (op == JSOP_CALLNAME)
                 PUSH_OPND(OBJECT_TO_JSVAL(obj));
           }
           END_CASE(JSOP_NAME)
 
           BEGIN_CASE(JSOP_UINT16)
-            i = (jsint) GET_UINT16(pc);
+            i = (jsint) GET_UINT16(regs.pc);
             rval = INT_TO_JSVAL(i);
             PUSH_OPND(rval);
           END_CASE(JSOP_UINT16)
 
           BEGIN_CASE(JSOP_UINT24)
-            i = (jsint) GET_UINT24(pc);
+            i = (jsint) GET_UINT24(regs.pc);
             rval = INT_TO_JSVAL(i);
             PUSH_OPND(rval);
           END_CASE(JSOP_UINT24)
 
           BEGIN_CASE(JSOP_INT8)
-            i = GET_INT8(pc);
+            i = GET_INT8(regs.pc);
             rval = INT_TO_JSVAL(i);
             PUSH_OPND(rval);
           END_CASE(JSOP_INT8)
 
           BEGIN_CASE(JSOP_INT32)
-            i = GET_INT32(pc);
+            i = GET_INT32(regs.pc);
             rval = INT_TO_JSVAL(i);
             PUSH_OPND(rval);
           END_CASE(JSOP_INT32)
 
           BEGIN_CASE(JSOP_INDEXBASE)
             /*
              * Here atoms can exceed script->atomMap.length as we use atoms
              * as a segment register for object literals as well.
              */
-            atoms += GET_INDEXBASE(pc);
+            atoms += GET_INDEXBASE(regs.pc);
           END_CASE(JSOP_INDEXBASE)
 
           BEGIN_CASE(JSOP_INDEXBASE1)
           BEGIN_CASE(JSOP_INDEXBASE2)
           BEGIN_CASE(JSOP_INDEXBASE3)
             atoms += (op - JSOP_INDEXBASE1 + 1) << 16;
           END_CASE(JSOP_INDEXBASE3)
 
@@ -5163,25 +5065,16 @@ interrupt:
 
             if (JSVAL_IS_NULL(rval)) {
                 /* Compute the current global object in obj2. */
                 obj2 = fp->scopeChain;
                 while ((parent = OBJ_GET_PARENT(cx, obj2)) != NULL)
                     obj2 = parent;
 
                 /*
-                 * We must home sp here, because either js_CloneRegExpObject
-                 * or JS_SetReservedSlot could nest a last-ditch GC.  We home
-                 * pc as well, in case js_CloneRegExpObject has to lookup the
-                 * "RegExp" class in the global object, which could entail a
-                 * JSNewResolveOp call.
-                 */
-                SAVE_SP_AND_PC(fp);
-
-                /*
                  * If obj's parent is not obj2, we must clone obj so that it
                  * has the right parent, and therefore, the right prototype.
                  *
                  * Yes, this means we assume that the correct RegExp.prototype
                  * to which regexp instances (including literals) delegate can
                  * be distinguished solely by the instance's parent, which was
                  * set to the parent of the RegExp constructor function object
                  * when the instance was created.  In other words,
@@ -5237,17 +5130,17 @@ interrupt:
             PUSH_OPND(JSVAL_FALSE);
           END_CASE(JSOP_FALSE)
 
           BEGIN_CASE(JSOP_TRUE)
             PUSH_OPND(JSVAL_TRUE);
           END_CASE(JSOP_TRUE)
 
           BEGIN_CASE(JSOP_TABLESWITCH)
-            pc2 = pc;
+            pc2 = regs.pc;
             len = GET_JUMP_OFFSET(pc2);
 
             /*
              * ECMAv2+ forbids conversion of discriminant, so we will skip to
              * the default case if the discriminant isn't already an int jsval.
              * (This opcode is emitted only for dense jsint-domain switches.)
              */
             rval = POP_OPND();
@@ -5265,17 +5158,17 @@ interrupt:
                 pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
                 off = (jsint) GET_JUMP_OFFSET(pc2);
                 if (off)
                     len = off;
             }
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_TABLESWITCHX)
-            pc2 = pc;
+            pc2 = regs.pc;
             len = GET_JUMPX_OFFSET(pc2);
 
             /*
              * ECMAv2+ forbids conversion of discriminant, so we will skip to
              * the default case if the discriminant isn't already an int jsval.
              * (This opcode is emitted only for dense jsint-domain switches.)
              */
             rval = POP_OPND();
@@ -5305,17 +5198,17 @@ interrupt:
             off = JUMP_OFFSET_LEN;
 
           do_lookup_switch:
             /*
              * JSOP_LOOKUPSWITCH and JSOP_LOOKUPSWITCHX are never used if
              * any atom index in it would exceed 64K limit.
              */
             JS_ASSERT(atoms == script->atomMap.vector);
-            pc2 = pc;
+            pc2 = regs.pc;
             lval = POP_OPND();
 
             if (!JSVAL_IS_NUMBER(lval) &&
                 !JSVAL_IS_STRING(lval) &&
                 !JSVAL_IS_BOOLEAN(lval)) {
                 goto end_lookup_switch;
             }
 
@@ -5330,17 +5223,17 @@ interrupt:
         atom = atoms[GET_INDEX(pc2)];                                         \
         rval = ATOM_KEY(atom);                                                \
         MATCH_CODE                                                            \
         pc2 += INDEX_LEN;                                                     \
         if (match)                                                            \
             break;                                                            \
         pc2 += off;                                                           \
         if (--npairs == 0) {                                                  \
-            pc2 = pc;                                                         \
+            pc2 = regs.pc;                                                    \
             break;                                                            \
         }                                                                     \
     }
             if (JSVAL_IS_STRING(lval)) {
                 str = JSVAL_TO_STRING(lval);
                 SEARCH_PAIRS(
                     match = (JSVAL_IS_STRING(rval) &&
                              ((str2 = JSVAL_TO_STRING(rval)) == str ||
@@ -5365,17 +5258,16 @@ interrupt:
                   : GET_JUMPX_OFFSET(pc2);
           END_VARLEN_CASE
 
           EMPTY_CASE(JSOP_CONDSWITCH)
 
 #if JS_HAS_EXPORT_IMPORT
           BEGIN_CASE(JSOP_EXPORTALL)
             obj = fp->varobj;
-            SAVE_SP_AND_PC(fp);
             ida = JS_Enumerate(cx, obj);
             if (!ida)
                 goto error;
             ok = JS_TRUE;
             for (i = 0; i != ida->length; i++) {
                 id = ida->vector[i];
                 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
                 if (!ok)
@@ -5396,17 +5288,16 @@ interrupt:
             if (!ok)
                 goto error;
           END_CASE(JSOP_EXPORTALL)
 
           BEGIN_CASE(JSOP_EXPORTNAME)
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
             obj = fp->varobj;
-            SAVE_SP_AND_PC(fp);
             if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
                 goto error;
             if (!prop) {
                 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
                                          JS_PropertyStub, JS_PropertyStub,
                                          JSPROP_EXPORTED, NULL)) {
                     goto error;
                 }
@@ -5420,36 +5311,35 @@ interrupt:
                 if (!ok)
                     goto error;
             }
           END_CASE(JSOP_EXPORTNAME)
 
           BEGIN_CASE(JSOP_IMPORTALL)
             id = (jsid) JSVAL_VOID;
             PROPERTY_OP(-1, js_ImportProperty(cx, obj, id));
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_IMPORTALL)
 
           BEGIN_CASE(JSOP_IMPORTPROP)
             /* Get an immediate atom naming the property. */
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
             PROPERTY_OP(-1, js_ImportProperty(cx, obj, id));
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_IMPORTPROP)
 
           BEGIN_CASE(JSOP_IMPORTELEM)
             ELEMENT_OP(-1, js_ImportProperty(cx, obj, id));
-            sp -= 2;
+            regs.sp -= 2;
           END_CASE(JSOP_IMPORTELEM)
 #endif /* JS_HAS_EXPORT_IMPORT */
 
           BEGIN_CASE(JSOP_TRAP)
-            SAVE_SP_AND_PC(fp);
-            switch (JS_HandleTrap(cx, script, pc, &rval)) {
+            switch (JS_HandleTrap(cx, script, regs.pc, &rval)) {
               case JSTRAP_ERROR:
                 goto error;
               case JSTRAP_CONTINUE:
                 JS_ASSERT(JSVAL_IS_INT(rval));
                 op = (JSOp) JSVAL_TO_INT(rval);
                 JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
                 LOAD_INTERRUPT_HANDLER(cx);
                 DO_OP();
@@ -5462,141 +5352,136 @@ interrupt:
                 cx->exception = rval;
                 goto error;
               default:;
             }
             LOAD_INTERRUPT_HANDLER(cx);
           END_CASE(JSOP_TRAP)
 
           BEGIN_CASE(JSOP_ARGUMENTS)
-            SAVE_SP_AND_PC(fp);
             if (!js_GetArgsValue(cx, fp, &rval))
                 goto error;
             PUSH_OPND(rval);
           END_CASE(JSOP_ARGUMENTS)
 
           BEGIN_CASE(JSOP_ARGSUB)
-            id = INT_TO_JSID(GET_ARGNO(pc));
-            SAVE_SP_AND_PC(fp);
+            id = INT_TO_JSID(GET_ARGNO(regs.pc));
             if (!js_GetArgsProperty(cx, fp, id, &rval))
                 goto error;
             PUSH_OPND(rval);
           END_CASE(JSOP_ARGSUB)
 
           BEGIN_CASE(JSOP_ARGCNT)
             id = ATOM_TO_JSID(rt->atomState.lengthAtom);
-            SAVE_SP_AND_PC(fp);
             if (!js_GetArgsProperty(cx, fp, id, &rval))
                 goto error;
             PUSH_OPND(rval);
           END_CASE(JSOP_ARGCNT)
 
           BEGIN_CASE(JSOP_GETARG)
           BEGIN_CASE(JSOP_CALLARG)
-            slot = GET_ARGNO(pc);
+            slot = GET_ARGNO(regs.pc);
             JS_ASSERT(slot < fp->fun->nargs);
             METER_SLOT_OP(op, slot);
             PUSH_OPND(fp->argv[slot]);
             if (op == JSOP_CALLARG)
                 PUSH_OPND(JSVAL_NULL);
           END_CASE(JSOP_GETARG)
 
           BEGIN_CASE(JSOP_SETARG)
-            slot = GET_ARGNO(pc);
+            slot = GET_ARGNO(regs.pc);
             JS_ASSERT(slot < fp->fun->nargs);
             METER_SLOT_OP(op, slot);
             vp = &fp->argv[slot];
             GC_POKE(cx, *vp);
             *vp = FETCH_OPND(-1);
           END_CASE(JSOP_SETARG)
 
           BEGIN_CASE(JSOP_GETVAR)
           BEGIN_CASE(JSOP_CALLVAR)
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->fun->u.i.nvars);
             METER_SLOT_OP(op, slot);
             PUSH_OPND(fp->vars[slot]);
             if (op == JSOP_CALLVAR)
                 PUSH_OPND(JSVAL_NULL);
           END_CASE(JSOP_GETVAR)
 
           BEGIN_CASE(JSOP_SETVAR)
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->fun->u.i.nvars);
             METER_SLOT_OP(op, slot);
             vp = &fp->vars[slot];
             GC_POKE(cx, *vp);
             *vp = FETCH_OPND(-1);
           END_CASE(JSOP_SETVAR)
 
           BEGIN_CASE(JSOP_GETGVAR)
           BEGIN_CASE(JSOP_CALLGVAR)
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->nvars);
             METER_SLOT_OP(op, slot);
             lval = fp->vars[slot];
             if (JSVAL_IS_NULL(lval)) {
                 op = (op == JSOP_GETGVAR) ? JSOP_NAME : JSOP_CALLNAME;
                 DO_OP();
             }
             slot = JSVAL_TO_INT(lval);
             obj = fp->varobj;
             rval = OBJ_GET_SLOT(cx, obj, slot);
             PUSH_OPND(rval);
             if (op == JSOP_CALLGVAR)
                 PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_GETGVAR)
 
           BEGIN_CASE(JSOP_SETGVAR)
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
             JS_ASSERT(slot < fp->nvars);
             METER_SLOT_OP(op, slot);
             rval = FETCH_OPND(-1);
             lval = fp->vars[slot];
             obj = fp->varobj;
             if (JSVAL_IS_NULL(lval)) {
                 /*
                  * Inline-clone and deoptimize JSOP_SETNAME code here because
                  * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval]
                  * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME.
                  */
                 LOAD_ATOM(0);
                 id = ATOM_TO_JSID(atom);
-                SAVE_SP_AND_PC(fp);
                 if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                     goto error;
                 STORE_OPND(-1, rval);
             } else {
                 slot = JSVAL_TO_INT(lval);
                 JS_LOCK_OBJ(cx, obj);
                 LOCKED_OBJ_WRITE_BARRIER(cx, obj, slot, rval);
                 JS_UNLOCK_OBJ(cx, obj);
             }
           END_CASE(JSOP_SETGVAR)
 
           BEGIN_CASE(JSOP_DEFCONST)
           BEGIN_CASE(JSOP_DEFVAR)
-            index = GET_INDEX(pc);
+            index = GET_INDEX(regs.pc);
             atom = atoms[index];
 
             /*
              * index is relative to atoms at this point but for global var
              * code below we need the absolute value.
              */
             index += atoms - script->atomMap.vector;
             obj = fp->varobj;
             attrs = JSPROP_ENUMERATE;
             if (!(fp->flags & JSFRAME_EVAL))
                 attrs |= JSPROP_PERMANENT;
             if (op == JSOP_DEFCONST)
                 attrs |= JSPROP_READONLY;
 
             /* Lookup id in order to check for redeclaration problems. */
             id = ATOM_TO_JSID(atom);
-            SAVE_SP_AND_PC(fp);
             if (!js_CheckRedeclaration(cx, obj, id, attrs, &obj2, &prop))
                 goto error;
 
             /* Bind a variable only if it's not yet defined. */
             if (!prop) {
                 if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
                                          JS_PropertyStub, JS_PropertyStub,
                                          attrs, &prop)) {
@@ -5666,23 +5551,21 @@ interrupt:
             JS_ASSERT(fp->scopeChain == fp->varobj);
             obj2 = fp->scopeChain;
 
             /*
              * ECMA requires functions defined when entering Global code to be
              * permanent.
              */
             attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
-            SAVE_SP_AND_PC(fp);
 
           do_deffun:
-            /* The common code for JSOP_DEFFUN and JSOP_CLOSURE. */
-            ASSERT_SAVED_SP_AND_PC(fp);
-
             /*
+             * The common code for JSOP_DEFFUN and JSOP_CLOSURE.
+             *
              * Clone the function object with the current scope chain as the
              * clone's parent.  The original function object is the prototype
              * of the clone.  Do this only if re-parenting; the compiler may
              * have seen the right parent already and created a sufficiently
              * well-scoped function object.
              */
             if (OBJ_GET_PARENT(cx, obj) != obj2) {
                 obj = js_CloneFunctionObject(cx, obj, obj2);
@@ -5757,36 +5640,34 @@ interrupt:
 
             /*
              * Define a local function (i.e., one nested at the top level of
              * another function), parented by the current scope chain, and
              * stored in a local variable slot that the compiler allocated.
              * This is an optimization over JSOP_DEFFUN that avoids requiring
              * a call object for the outer function's activation.
              */
-            slot = GET_VARNO(pc);
+            slot = GET_VARNO(regs.pc);
 
             parent = js_GetScopeChain(cx, fp);
             if (!parent)
                 goto error;
 
-            SAVE_SP_AND_PC(fp);
             obj = js_CloneFunctionObject(cx, obj, parent);
             if (!obj)
                 goto error;
 
             fp->vars[slot] = OBJECT_TO_JSVAL(obj);
           END_CASE(JSOP_DEFLOCALFUN)
 
           BEGIN_CASE(JSOP_ANONFUNOBJ)
             /* Load the specified function object literal. */
             LOAD_FUNCTION(0);
 
             /* If re-parenting, push a clone of the function object. */
-            SAVE_SP_AND_PC(fp);
             parent = js_GetScopeChain(cx, fp);
             if (!parent)
                 goto error;
             if (OBJ_GET_PARENT(cx, obj) != parent) {
                 obj = js_CloneFunctionObject(cx, obj, parent);
                 if (!obj)
                     goto error;
             }
@@ -5801,17 +5682,16 @@ interrupt:
             /*
              * 1. Create a new object as if by the expression new Object().
              * 2. Add Result(1) to the front of the scope chain.
              *
              * Step 2 is achieved by making the new object's parent be the
              * current scope chain, and then making the new object the parent
              * of the Function object clone.
              */
-            SAVE_SP_AND_PC(fp);
             obj2 = js_GetScopeChain(cx, fp);
             if (!obj2)
                 goto error;
             parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2);
             if (!parent)
                 goto error;
 
             /*
@@ -5888,17 +5768,16 @@ interrupt:
             LOAD_FUNCTION(0);
 
             /*
              * Clone the function object with the current scope chain as the
              * clone's parent. Do this only if re-parenting; the compiler may
              * have seen the right parent already and created a sufficiently
              * well-scoped function object.
              */
-            SAVE_SP_AND_PC(fp);
             obj2 = js_GetScopeChain(cx, fp);
             if (!obj2)
                 goto error;
 
             /*
              * ECMA requires that functions defined when entering Eval code to
              * be impermanent.
              */
@@ -5907,21 +5786,21 @@ interrupt:
                 attrs |= JSPROP_PERMANENT;
 
             goto do_deffun;
 
 #if JS_HAS_GETTER_SETTER
           BEGIN_CASE(JSOP_GETTER)
           BEGIN_CASE(JSOP_SETTER)
           do_getter_setter:
-            op2 = (JSOp) *++pc;
+            op2 = (JSOp) *++regs.pc;
             switch (op2) {
               case JSOP_INDEXBASE:
-                atoms += GET_INDEXBASE(pc);
-                pc += JSOP_INDEXBASE_LENGTH - 1;
+                atoms += GET_INDEXBASE(regs.pc);
+                regs.pc += JSOP_INDEXBASE_LENGTH - 1;
                 goto do_getter_setter;
               case JSOP_INDEXBASE1:
               case JSOP_INDEXBASE2:
               case JSOP_INDEXBASE3:
                 atoms += (op2 - JSOP_INDEXBASE1 + 1) << 16;
                 goto do_getter_setter;
 
               case JSOP_SETNAME:
@@ -5932,40 +5811,38 @@ interrupt:
                 i = -1;
                 goto gs_pop_lval;
 
               case JSOP_SETELEM:
                 rval = FETCH_OPND(-1);
                 id = 0;
                 i = -2;
               gs_pop_lval:
-                SAVE_SP_AND_PC(fp);
                 FETCH_OBJECT(cx, i - 1, lval, obj);
                 break;
 
               case JSOP_INITPROP:
-                JS_ASSERT(sp - fp->spbase >= 2);
+                JS_ASSERT(regs.sp - fp->spbase >= 2);
                 rval = FETCH_OPND(-1);
                 i = -1;
                 LOAD_ATOM(0);
                 id = ATOM_TO_JSID(atom);
                 goto gs_get_lval;
 
               default:
                 JS_ASSERT(op2 == JSOP_INITELEM);
 
-                JS_ASSERT(sp - fp->spbase >= 3);
+                JS_ASSERT(regs.sp - fp->spbase >= 3);
                 rval = FETCH_OPND(-1);
                 id = 0;
                 i = -2;
               gs_get_lval:
                 lval = FETCH_OPND(i-1);
                 JS_ASSERT(JSVAL_IS_OBJECT(lval));
                 obj = JSVAL_TO_OBJECT(lval);
-                SAVE_SP_AND_PC(fp);
                 break;
             }
 
             /* Ensure that id has a type suitable for use with obj. */
             if (id == 0)
                 FETCH_ELEMENT_ID(obj, i, id);
 
             if (JS_TypeOfValue(cx, rval) != JSTYPE_FUNCTION) {
@@ -5999,51 +5876,50 @@ interrupt:
             if (!js_CheckRedeclaration(cx, obj, id, attrs, NULL, NULL))
                 goto error;
 
             if (!OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter,
                                      attrs, NULL)) {
                 goto error;
             }
 
-            sp += i;
+            regs.sp += i;
             if (js_CodeSpec[op2].ndefs)
                 STORE_OPND(-1, rval);
             len = js_CodeSpec[op2].length;
             DO_NEXT_OP(len);
 #endif /* JS_HAS_GETTER_SETTER */
 
           BEGIN_CASE(JSOP_NEWINIT)
-            i = GET_INT8(pc);
+            i = GET_INT8(regs.pc);
             JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
-            SAVE_SP_AND_PC(fp);
             obj = (i == JSProto_Array)
                   ? js_NewArrayObject(cx, 0, NULL)
                   : js_NewObject(cx, &js_ObjectClass, NULL, NULL);
             if (!obj)
                 goto error;
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
             fp->sharpDepth++;
             LOAD_INTERRUPT_HANDLER(cx);
           END_CASE(JSOP_NEWINIT)
 
           BEGIN_CASE(JSOP_ENDINIT)
             if (--fp->sharpDepth == 0)
                 fp->sharpArray = NULL;
 
             /* Re-set the newborn root to the top of this object tree. */
-            JS_ASSERT(sp - fp->spbase >= 1);
+            JS_ASSERT(regs.sp - fp->spbase >= 1);
             lval = FETCH_OPND(-1);
             JS_ASSERT(JSVAL_IS_OBJECT(lval));
             cx->weakRoots.newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval);
           END_CASE(JSOP_ENDINIT)
 
           BEGIN_CASE(JSOP_INITPROP)
             /* Load the property's initial value into rval. */
-            JS_ASSERT(sp - fp->spbase >= 2);
+            JS_ASSERT(regs.sp - fp->spbase >= 2);
             rval = FETCH_OPND(-1);
 
             /* Load the object being initialized into lval/obj. */
             lval = FETCH_OPND(-2);
             obj = JSVAL_TO_OBJECT(lval);
             JS_ASSERT(OBJ_IS_NATIVE(obj));
             JS_ASSERT(!OBJ_GET_CLASS(cx, obj)->reserveSlots);
             JS_ASSERT(!(LOCKED_OBJ_GET_CLASS(obj)->flags &
@@ -6055,21 +5931,21 @@ interrupt:
                 JSPropertyCache *cache;
                 JSPropCacheEntry *entry;
 
                 JS_LOCK_OBJ(cx, obj);
                 scope = OBJ_SCOPE(obj);
                 JS_ASSERT(!SCOPE_IS_SEALED(scope));
                 kshape = scope->shape;
                 cache = &JS_PROPERTY_CACHE(cx);
-                entry = &cache->table[PROPERTY_CACHE_HASH_PC(pc, kshape)];
+                entry = &cache->table[PROPERTY_CACHE_HASH_PC(regs.pc, kshape)];
                 PCMETER(cache->tests++);
                 PCMETER(cache->initests++);
 
-                if (entry->kpc == pc && entry->kshape == kshape) {
+                if (entry->kpc == regs.pc && entry->kshape == kshape) {
                     PCMETER(cache->pchits++);
                     PCMETER(cache->inipchits++);
 
                     JS_ASSERT(PCVAL_IS_SPROP(entry->vword));
                     sprop = PCVAL_TO_SPROP(entry->vword);
                     JS_ASSERT(!(sprop->attrs & JSPROP_READONLY));
 
                     /*
@@ -6149,116 +6025,111 @@ interrupt:
 
               do_initprop_miss:
                 PCMETER(cache->inipcmisses++);
                 JS_UNLOCK_SCOPE(cx, scope);
 
                 /* Get the immediate property name into id. */
                 LOAD_ATOM(0);
                 id = ATOM_TO_JSID(atom);
-                SAVE_SP_AND_PC(fp);
 
                 /* Set the property named by obj[id] to rval. */
                 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER,
                                            NULL, NULL)) {
                     goto error;
                 }
                 if (!js_SetPropertyHelper(cx, obj, id, &rval, &entry))
                     goto error;
             } while (0);
 
             /* Common tail for property cache hit and miss cases. */
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_INITPROP);
 
           BEGIN_CASE(JSOP_INITELEM)
             /* Pop the element's value into rval. */
-            JS_ASSERT(sp - fp->spbase >= 3);
+            JS_ASSERT(regs.sp - fp->spbase >= 3);
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             FETCH_ELEMENT_ID(obj, -2, id);
 
             /* Find the object being initialized at top of stack. */
             lval = FETCH_OPND(-3);
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval));
             obj = JSVAL_TO_OBJECT(lval);
 
             /* Set the property named by obj[id] to rval. */
             if (!js_CheckRedeclaration(cx, obj, id, JSPROP_INITIALIZER, NULL,
                                        NULL)) {
                 goto error;
             }
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
-            sp -= 2;
+            regs.sp -= 2;
           END_CASE(JSOP_INITELEM);
 
 #if JS_HAS_SHARP_VARS
           BEGIN_CASE(JSOP_DEFSHARP)
-            SAVE_SP_AND_PC(fp);
             obj = fp->sharpArray;
             if (!obj) {
                 obj = js_NewArrayObject(cx, 0, NULL);
                 if (!obj)
                     goto error;
                 fp->sharpArray = obj;
             }
-            i = (jsint) GET_UINT16(pc);
+            i = (jsint) GET_UINT16(regs.pc);
             id = INT_TO_JSID(i);
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_PRIMITIVE(rval)) {
                 char numBuf[12];
                 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_SHARP_DEF, numBuf);
                 goto error;
             }
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
           END_CASE(JSOP_DEFSHARP)
 
           BEGIN_CASE(JSOP_USESHARP)
-            i = (jsint) GET_UINT16(pc);
+            i = (jsint) GET_UINT16(regs.pc);
             id = INT_TO_JSID(i);
             obj = fp->sharpArray;
             if (!obj) {
                 rval = JSVAL_VOID;
             } else {
-                SAVE_SP_AND_PC(fp);
                 if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
                     goto error;
             }
             if (!JSVAL_IS_OBJECT(rval)) {
                 char numBuf[12];
+
                 JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i);
-
-                SAVE_SP_AND_PC(fp);
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_SHARP_USE, numBuf);
                 goto error;
             }
             PUSH_OPND(rval);
           END_CASE(JSOP_USESHARP)
 #endif /* JS_HAS_SHARP_VARS */
 
           /* No-ops for ease of decompilation and jit'ing. */
           EMPTY_CASE(JSOP_TRY)
           EMPTY_CASE(JSOP_FINALLY)
 
           BEGIN_CASE(JSOP_GOSUB)
             PUSH(JSVAL_FALSE);
-            i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
-            len = GET_JUMP_OFFSET(pc);
+            i = PTRDIFF(regs.pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH;
+            len = GET_JUMP_OFFSET(regs.pc);
             PUSH(INT_TO_JSVAL(i));
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_GOSUBX)
             PUSH(JSVAL_FALSE);
-            i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
-            len = GET_JUMPX_OFFSET(pc);
+            i = PTRDIFF(regs.pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH;
+            len = GET_JUMPX_OFFSET(regs.pc);
             PUSH(INT_TO_JSVAL(i));
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_RETSUB)
             rval = POP();
             lval = POP();
             JS_ASSERT(JSVAL_IS_BOOLEAN(lval));
             if (JSVAL_TO_BOOLEAN(lval)) {
@@ -6269,17 +6140,17 @@ interrupt:
                  * it points out a FIXME: 350509, due to Igor Bukanov.
                  */
                 cx->throwing = JS_TRUE;
                 cx->exception = rval;
                 goto error;
             }
             JS_ASSERT(JSVAL_IS_INT(rval));
             len = JSVAL_TO_INT(rval);
-            pc = script->main;
+            regs.pc = script->main;
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_EXCEPTION)
             JS_ASSERT(cx->throwing);
             PUSH(cx->exception);
             cx->throwing = JS_FALSE;
           END_CASE(JSOP_EXCEPTION)
 
@@ -6296,46 +6167,44 @@ interrupt:
             /* let the code at error try to catch the exception. */
             goto error;
 
           BEGIN_CASE(JSOP_SETLOCALPOP)
             /*
              * The stack must have a block with at least one local slot below
              * the exception object.
              */
-            JS_ASSERT(sp - fp->spbase >= 2);
-            slot = GET_UINT16(pc);
+            JS_ASSERT(regs.sp - fp->spbase >= 2);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot + 1 < script->depth);
             fp->spbase[slot] = POP_OPND();
           END_CASE(JSOP_SETLOCALPOP)
 
           BEGIN_CASE(JSOP_INSTANCEOF)
-            SAVE_SP_AND_PC(fp);
             rval = FETCH_OPND(-1);
             if (JSVAL_IS_PRIMITIVE(rval) ||
                 !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) {
                 js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                                     -1, rval, NULL);
                 goto error;
             }
             lval = FETCH_OPND(-2);
             cond = JS_FALSE;
             if (!obj->map->ops->hasInstance(cx, obj, lval, &cond))
                 goto error;
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
           END_CASE(JSOP_INSTANCEOF)
 
 #if JS_HAS_DEBUGGER_KEYWORD
           BEGIN_CASE(JSOP_DEBUGGER)
           {
             JSTrapHandler handler = cx->debugHooks->debuggerHandler;
             if (handler) {
-                SAVE_SP_AND_PC(fp);
-                switch (handler(cx, script, pc, &rval,
+                switch (handler(cx, script, regs.pc, &rval,
                                 cx->debugHooks->debuggerHandlerData)) {
                   case JSTRAP_ERROR:
                     goto error;
                   case JSTRAP_CONTINUE:
                     break;
                   case JSTRAP_RETURN:
                     fp->rval = rval;
                     ok = JS_TRUE;
@@ -6350,224 +6219,207 @@ interrupt:
             }
           }
           END_CASE(JSOP_DEBUGGER)
 #endif /* JS_HAS_DEBUGGER_KEYWORD */
 
 #if JS_HAS_XML_SUPPORT
           BEGIN_CASE(JSOP_DEFXMLNS)
             rval = POP();
-            SAVE_SP_AND_PC(fp);
             if (!js_SetDefaultXMLNamespace(cx, rval))
                 goto error;
           END_CASE(JSOP_DEFXMLNS)
 
           BEGIN_CASE(JSOP_ANYNAME)
-            SAVE_SP_AND_PC(fp);
             if (!js_GetAnyName(cx, &rval))
                 goto error;
             PUSH_OPND(rval);
           END_CASE(JSOP_ANYNAME)
 
           BEGIN_CASE(JSOP_QNAMEPART)
             LOAD_ATOM(0);
             PUSH_OPND(ATOM_KEY(atom));
           END_CASE(JSOP_QNAMEPART)
 
           BEGIN_CASE(JSOP_QNAMECONST)
             LOAD_ATOM(0);
             rval = ATOM_KEY(atom);
             lval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             obj = js_ConstructXMLQNameObject(cx, lval, rval);
             if (!obj)
                 goto error;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_QNAMECONST)
 
           BEGIN_CASE(JSOP_QNAME)
             rval = FETCH_OPND(-1);
             lval = FETCH_OPND(-2);
-            SAVE_SP_AND_PC(fp);
             obj = js_ConstructXMLQNameObject(cx, lval, rval);
             if (!obj)
                 goto error;
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_QNAME)
 
           BEGIN_CASE(JSOP_TOATTRNAME)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (!js_ToAttributeName(cx, &rval))
                 goto error;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_TOATTRNAME)
 
           BEGIN_CASE(JSOP_TOATTRVAL)
             rval = FETCH_OPND(-1);
             JS_ASSERT(JSVAL_IS_STRING(rval));
-            SAVE_SP_AND_PC(fp);
             str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval), JS_FALSE);
             if (!str)
                 goto error;
             STORE_OPND(-1, STRING_TO_JSVAL(str));
           END_CASE(JSOP_TOATTRVAL)
 
           BEGIN_CASE(JSOP_ADDATTRNAME)
           BEGIN_CASE(JSOP_ADDATTRVAL)
             rval = FETCH_OPND(-1);
             lval = FETCH_OPND(-2);
             str = JSVAL_TO_STRING(lval);
             str2 = JSVAL_TO_STRING(rval);
-            SAVE_SP_AND_PC(fp);
             str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2);
             if (!str)
                 goto error;
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, STRING_TO_JSVAL(str));
           END_CASE(JSOP_ADDATTRNAME)
 
           BEGIN_CASE(JSOP_BINDXMLNAME)
             lval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (!js_FindXMLProperty(cx, lval, &obj, &id))
                 goto error;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
             PUSH_OPND(ID_TO_VALUE(id));
           END_CASE(JSOP_BINDXMLNAME)
 
           BEGIN_CASE(JSOP_SETXMLNAME)
             obj = JSVAL_TO_OBJECT(FETCH_OPND(-3));
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             FETCH_ELEMENT_ID(obj, -2, id);
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
-            sp -= 2;
+            regs.sp -= 2;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_SETXMLNAME)
 
           BEGIN_CASE(JSOP_CALLXMLNAME)
           BEGIN_CASE(JSOP_XMLNAME)
             lval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (!js_FindXMLProperty(cx, lval, &obj, &id))
                 goto error;
             if (!OBJ_GET_PROPERTY(cx, obj, id, &rval))
                 goto error;
             STORE_OPND(-1, rval);
             if (op == JSOP_CALLXMLNAME)
                 PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_XMLNAME)
 
           BEGIN_CASE(JSOP_DESCENDANTS)
           BEGIN_CASE(JSOP_DELDESC)
-            SAVE_SP_AND_PC(fp);
             FETCH_OBJECT(cx, -2, lval, obj);
             rval = FETCH_OPND(-1);
             if (!js_GetXMLDescendants(cx, obj, rval, &rval))
                 goto error;
 
             if (op == JSOP_DELDESC) {
-                sp[-1] = rval;          /* set local root */
+                regs.sp[-1] = rval;          /* set local root */
                 if (!js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)))
                     goto error;
                 rval = JSVAL_TRUE;      /* always succeed */
             }
 
-            sp--;
+            regs.sp--;
             STORE_OPND(-1, rval);
           END_CASE(JSOP_DESCENDANTS)
 
           BEGIN_CASE(JSOP_FILTER)
             /*
              * We push the hole value before jumping to [enditer] so we can
              * detect the first iteration and direct js_StepXMLListFilter to
              * initialize filter's state.
              */
             PUSH_OPND(JSVAL_HOLE);
-            len = GET_JUMP_OFFSET(pc);
+            len = GET_JUMP_OFFSET(regs.pc);
             JS_ASSERT(len > 0);
           END_VARLEN_CASE
 
           BEGIN_CASE(JSOP_ENDFILTER)
-            SAVE_SP_AND_PC(fp);
-            cond = (sp[-1] != JSVAL_HOLE);
+            cond = (regs.sp[-1] != JSVAL_HOLE);
             if (cond) {
                 /* Exit the "with" block left from the previous iteration. */
                 js_LeaveWith(cx);
             }
             if (!js_StepXMLListFilter(cx, cond))
                 goto error;
-            if (sp[-1] != JSVAL_NULL) {
+            if (regs.sp[-1] != JSVAL_NULL) {
                 /*
                  * Decrease sp after EnterWith returns as we use sp[-1] there
                  * to root temporaries.
                  */
-                JS_ASSERT(VALUE_IS_XML(cx, sp[-1]));
+                JS_ASSERT(VALUE_IS_XML(cx, regs.sp[-1]));
                 if (!js_EnterWith(cx, -2))
                     goto error;
-                sp--;
-                len = GET_JUMP_OFFSET(pc);
+                regs.sp--;
+                len = GET_JUMP_OFFSET(regs.pc);
                 JS_ASSERT(len < 0);
                 CHECK_BRANCH(len);
                 DO_NEXT_OP(len);
             }
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_ENDFILTER);
 
           EMPTY_CASE(JSOP_STARTXML)
           EMPTY_CASE(JSOP_STARTXMLEXPR)
 
           BEGIN_CASE(JSOP_TOXML)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             obj = js_ValueToXMLObject(cx, rval);
             if (!obj)
                 goto error;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_TOXML)
 
           BEGIN_CASE(JSOP_TOXMLLIST)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             obj = js_ValueToXMLListObject(cx, rval);
             if (!obj)
                 goto error;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_TOXMLLIST)
 
           BEGIN_CASE(JSOP_XMLTAGEXPR)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             str = js_ValueToString(cx, rval);
             if (!str)
                 goto error;
             STORE_OPND(-1, STRING_TO_JSVAL(str));
           END_CASE(JSOP_XMLTAGEXPR)
 
           BEGIN_CASE(JSOP_XMLELTEXPR)
             rval = FETCH_OPND(-1);
-            SAVE_SP_AND_PC(fp);
             if (VALUE_IS_XML(cx, rval)) {
                 str = js_ValueToXMLString(cx, rval);
             } else {
                 str = js_ValueToString(cx, rval);
                 if (str)
                     str = js_EscapeElementValue(cx, str);
             }
             if (!str)
                 goto error;
             STORE_OPND(-1, STRING_TO_JSVAL(str));
           END_CASE(JSOP_XMLELTEXPR)
 
           BEGIN_CASE(JSOP_XMLOBJECT)
             LOAD_OBJECT(0);
-            SAVE_SP_AND_PC(fp);
             obj = js_CloneXMLObject(cx, obj);
             if (!obj)
                 goto error;
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_XMLOBJECT)
 
           BEGIN_CASE(JSOP_XMLCDATA)
             LOAD_ATOM(0);
@@ -6587,41 +6439,39 @@ interrupt:
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_XMLCOMMENT)
 
           BEGIN_CASE(JSOP_XMLPI)
             LOAD_ATOM(0);
             str = ATOM_TO_STRING(atom);
             rval = FETCH_OPND(-1);
             str2 = JSVAL_TO_STRING(rval);
-            SAVE_SP_AND_PC(fp);
             obj = js_NewXMLSpecialObject(cx,
                                          JSXML_CLASS_PROCESSING_INSTRUCTION,
                                          str, str2);
             if (!obj)
                 goto error;
             STORE_OPND(-1, OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_XMLPI)
 
           BEGIN_CASE(JSOP_GETFUNNS)
-            SAVE_SP_AND_PC(fp);
             if (!js_GetFunctionNamespace(cx, &rval))
                 goto error;
             PUSH_OPND(rval);
           END_CASE(JSOP_GETFUNNS)
 #endif /* JS_HAS_XML_SUPPORT */
 
           BEGIN_CASE(JSOP_ENTERBLOCK)
             LOAD_OBJECT(0);
-            JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == sp);
-            vp = sp + OBJ_BLOCK_COUNT(cx, obj);
+            JS_ASSERT(fp->spbase + OBJ_BLOCK_DEPTH(cx, obj) == regs.sp);
+            vp = regs.sp + OBJ_BLOCK_COUNT(cx, obj);
             JS_ASSERT(vp <= fp->spbase + script->depth);
-            while (sp < vp) {
+            while (regs.sp < vp) {
                 STORE_OPND(0, JSVAL_VOID);
-                sp++;
+                regs.sp++;
             }
 
             /*
              * If this frame had to reflect the compile-time block chain into
              * the runtime scope chain, we can't optimize block scopes out of
              * runtime any longer, because an outer block that parents obj has
              * been cloned onto the scope chain.  To avoid re-cloning such a
              * parent and accumulating redundant clones via js_GetScopeChain,
@@ -6655,100 +6505,93 @@ interrupt:
             if (fp->blockChain) {
                 JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
                 fp->blockChain = OBJ_GET_PARENT(cx, fp->blockChain);
             } else {
                 /*
                  * This block was cloned into fp->scopeChain, so clear its
                  * private data and sync its locals to their property slots.
                  */
-                SAVE_SP_AND_PC(fp);
                 if (!js_PutBlockObject(cx, JS_TRUE))
                     goto error;
             }
 
             /*
              * We will move the result of the expression to the new topmost
              * stack slot.
              */
             if (op == JSOP_LEAVEBLOCKEXPR)
                 rval = FETCH_OPND(-1);
-            sp -= GET_UINT16(pc);
+            regs.sp -= GET_UINT16(regs.pc);
             if (op == JSOP_LEAVEBLOCKEXPR) {
-                JS_ASSERT(blocksp == sp - 1);
+                JS_ASSERT(blocksp == regs.sp - 1);
                 STORE_OPND(-1, rval);
             } else {
-                JS_ASSERT(blocksp == sp);
+                JS_ASSERT(blocksp == regs.sp);
             }
           }
           END_CASE(JSOP_LEAVEBLOCK)
 
           BEGIN_CASE(JSOP_GETLOCAL)
           BEGIN_CASE(JSOP_CALLLOCAL)
-            slot = GET_UINT16(pc);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot < script->depth);
             PUSH_OPND(fp->spbase[slot]);
             if (op == JSOP_CALLLOCAL)
                 PUSH_OPND(JSVAL_NULL);
           END_CASE(JSOP_GETLOCAL)
 
           BEGIN_CASE(JSOP_SETLOCAL)
-            slot = GET_UINT16(pc);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot < script->depth);
             vp = &fp->spbase[slot];
             GC_POKE(cx, *vp);
             *vp = FETCH_OPND(-1);
           END_CASE(JSOP_SETLOCAL)
 
           BEGIN_CASE(JSOP_ENDITER)
             /*
              * Decrease the stack pointer even when !ok, see comments in the
              * exception capturing code for details.
              */
-            SAVE_SP_AND_PC(fp);
-            ok = js_CloseIterator(cx, sp[-1]);
-            sp--;
+            ok = js_CloseIterator(cx, regs.sp[-1]);
+            regs.sp--;
             if (!ok)
                 goto error;
           END_CASE(JSOP_ENDITER)
 
 #if JS_HAS_GENERATORS
           BEGIN_CASE(JSOP_GENERATOR)
             ASSERT_NOT_THROWING(cx);
-            pc += JSOP_GENERATOR_LENGTH;
-            SAVE_SP_AND_PC(fp);
+            regs.pc += JSOP_GENERATOR_LENGTH;
             obj = js_NewGenerator(cx, fp);
             if (!obj)
                 goto error;
             JS_ASSERT(!fp->callobj && !fp->argsobj);
             fp->rval = OBJECT_TO_JSVAL(obj);
             ok = JS_TRUE;
             if (inlineCallCount != 0)
                 goto inline_return;
             goto exit;
 
           BEGIN_CASE(JSOP_YIELD)
             ASSERT_NOT_THROWING(cx);
             if (FRAME_TO_GENERATOR(fp)->state == JSGEN_CLOSING) {
-                SAVE_SP_AND_PC(fp);
                 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD,
                                     JSDVG_SEARCH_STACK, fp->argv[-2], NULL);
                 goto error;
             }
             fp->rval = FETCH_OPND(-1);
             fp->flags |= JSFRAME_YIELDING;
-            pc += JSOP_YIELD_LENGTH;
-            SAVE_SP_AND_PC(fp);
-            JS_PROPERTY_CACHE(cx).disabled -= js_CountWithBlocks(cx, fp);
-            JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
+            regs.pc += JSOP_YIELD_LENGTH;
             ok = JS_TRUE;
             goto exit;
 
           BEGIN_CASE(JSOP_ARRAYPUSH)
-            slot = GET_UINT16(pc);
+            slot = GET_UINT16(regs.pc);
             JS_ASSERT(slot < script->depth);
             lval = fp->spbase[slot];
             obj  = JSVAL_TO_OBJECT(lval);
             JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_ArrayClass);
             rval = FETCH_OPND(-1);
 
             /*
              * We know that the array is created with only a 'length' private
@@ -6760,17 +6603,17 @@ interrupt:
             if (i == ARRAY_INIT_LIMIT) {
                 JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
                                        JSMSG_ARRAY_INIT_TOO_BIG);
                 goto error;
             }
             id = INT_TO_JSID(i);
             if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                 goto error;
-            sp--;
+            regs.sp--;
           END_CASE(JSOP_ARRAYPUSH)
 #endif /* JS_HAS_GENERATORS */
 
 #if JS_THREADED_INTERP
           L_JSOP_BACKPATCH:
           L_JSOP_BACKPATCH_POP:
 
 # if !JS_HAS_GENERATORS
@@ -6828,75 +6671,73 @@ interrupt:
             goto error;
           }
 
 #if !JS_THREADED_INTERP
 
         } /* switch (op) */
 
     advance_pc:
-        pc += len;
+        regs.pc += len;
 
 #ifdef DEBUG
         if (tracefp) {
             intN ndefs, n;
             jsval *siter;
 
             /*
              * op may be invalid here when a catch or finally handler jumps to
              * advance_pc.
              */
-            op = (JSOp) pc[-len];
+            op = (JSOp) regs.pc[-len];
             ndefs = js_CodeSpec[op].ndefs;
             if (ndefs) {
-                SAVE_SP_AND_PC(fp);
-                if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE)
+                if (op == JSOP_FORELEM && regs.sp[-1] == JSVAL_FALSE)
                     --ndefs;
                 for (n = -ndefs; n < 0; n++) {
-                    char *bytes = js_DecompileValueGenerator(cx, n, sp[n],
+                    char *bytes = js_DecompileValueGenerator(cx, n, regs.sp[n],
                                                              NULL);
                     if (bytes) {
                         fprintf(tracefp, "%s %s",
                                 (n == -ndefs) ? "  output:" : ",",
                                 bytes);
                         JS_free(cx, bytes);
                     }
                 }
-                fprintf(tracefp, " @ %d\n", sp - fp->spbase);
+                fprintf(tracefp, " @ %d\n", regs.sp - fp->spbase);
             }
             fprintf(tracefp, "  stack: ");
-            for (siter = fp->spbase; siter < sp; siter++) {
+            for (siter = fp->spbase; siter < regs.sp; siter++) {
                 str = js_ValueToString(cx, *siter);
                 if (!str)
                     fputs("<null>", tracefp);
                 else
                     js_FileEscapedString(tracefp, str, 0);
                 fputc(' ', tracefp);
             }
             fputc('\n', tracefp);
         }
 #endif /* DEBUG */
     }
 #endif /* !JS_THREADED_INTERP */
 
   error:
-    JS_ASSERT((size_t)(pc - script->code) < script->length);
+    JS_ASSERT((size_t)(regs.pc - script->code) < script->length);
     if (!cx->throwing) {
         /* This is an error, not a catchable exception, quit the frame ASAP. */
         ok = JS_FALSE;
     } else {
         JSTrapHandler handler;
         JSTryNote *tn, *tnlimit;
         uint32 offset;
 
         /* Call debugger throw hook if set. */
         handler = cx->debugHooks->throwHook;
         if (handler) {
-            SAVE_SP_AND_PC(fp);
-            switch (handler(cx, script, pc, &rval,
+            switch (handler(cx, script, regs.pc, &rval,
                             cx->debugHooks->throwHookData)) {
               case JSTRAP_ERROR:
                 cx->throwing = JS_FALSE;
                 goto error;
               case JSTRAP_RETURN:
                 cx->throwing = JS_FALSE;
                 fp->rval = rval;
                 ok = JS_TRUE;
@@ -6910,17 +6751,17 @@ interrupt:
         }
 
         /*
          * Look for a try block in script that can catch this exception.
          */
         if (script->trynotesOffset == 0)
             goto no_catch;
 
-        offset = (uint32)(pc - script->main);
+        offset = (uint32)(regs.pc - script->main);
         tn = JS_SCRIPT_TRYNOTES(script)->vector;
         tnlimit = tn + JS_SCRIPT_TRYNOTES(script)->length;
         do {
             if (offset - tn->start >= tn->length)
                 continue;
 
             /*
              * We have a note that covers the exception pc but we must check
@@ -6936,41 +6777,39 @@ interrupt:
              * corresponding iterators and invoked the finally blocks.
              *
              * To address this, we make [enditer] always decrease the stack
              * even when its implementation throws an exception. Thus already
              * executed [enditer] and [gosub] opcodes will have try notes
              * with the stack depth exceeding the current one and this
              * condition is what we use to filter them out.
              */
-            if (tn->stackDepth > sp - fp->spbase)
+            if (tn->stackDepth > regs.sp - fp->spbase)
                 continue;
 
             /*
              * Set pc to the first bytecode after the the try note to point
              * to the beginning of catch or finally or to [enditer] closing
              * the for-in loop.
              */
-            pc = (script)->main + tn->start + tn->length;
-
-            SAVE_SP_AND_PC(fp);
+            regs.pc = (script)->main + tn->start + tn->length;
+
             ok = js_UnwindScope(cx, fp, tn->stackDepth, JS_TRUE);
-            JS_ASSERT(fp->sp == fp->spbase + tn->stackDepth);
-            RESTORE_SP(fp);
+            JS_ASSERT(fp->regs->sp == fp->spbase + tn->stackDepth);
             if (!ok) {
                 /*
                  * Restart the handler search with updated pc and stack depth
                  * to properly notify the debugger.
                  */
                 goto error;
             }
 
             switch (tn->kind) {
               case JSTN_CATCH:
-                JS_ASSERT(*pc == JSOP_ENTERBLOCK);
+                JS_ASSERT(*regs.pc == JSOP_ENTERBLOCK);
 
 #if JS_HAS_GENERATORS
                 /* Catch cannot intercept the closing of a generator. */
                 if (JS_UNLIKELY(cx->exception == JSVAL_ARETURN))
                     break;
 #endif
 
                 /*
@@ -6993,26 +6832,25 @@ interrupt:
                 DO_NEXT_OP(len);
 
               case JSTN_ITER:
                 /*
                  * This is similar to JSOP_ENDITER in the interpreter loop
                  * except the code now uses a reserved stack slot to save and
                  * restore the exception.
                  */
-                JS_ASSERT(*pc == JSOP_ENDITER);
+                JS_ASSERT(*regs.pc == JSOP_ENDITER);
                 PUSH(cx->exception);
                 cx->throwing = JS_FALSE;
-                SAVE_SP_AND_PC(fp);
-                ok = js_CloseIterator(cx, sp[-2]);
-                sp -= 2;
+                ok = js_CloseIterator(cx, regs.sp[-2]);
+                regs.sp -= 2;
                 if (!ok)
                     goto error;
                 cx->throwing = JS_TRUE;
-                cx->exception = sp[1];
+                cx->exception = regs.sp[1];
             }
         } while (++tn != tnlimit);
 
       no_catch:
         /*
          * Propagate the exception or error to the caller unless the exception
          * is an asynchronous return from a generator.
          */
@@ -7029,20 +6867,18 @@ interrupt:
   forced_return:
     /*
      * Unwind the scope making sure that ok stays false even when UnwindScope
      * returns true.
      *
      * When a trap handler returns JSTRAP_RETURN, we jump here with ok set to
      * true bypassing any finally blocks.
      */
-    SAVE_SP_AND_PC(fp);
     ok &= js_UnwindScope(cx, fp, 0, ok || cx->throwing);
-    JS_ASSERT(fp->sp == fp->spbase);
-    RESTORE_SP(fp);
+    JS_ASSERT(regs.sp == fp->spbase);
 
     if (inlineCallCount)
         goto inline_return;
 
   exit:
     /*
      * At this point we are inevitably leaving an interpreted function or a
      * top-level script, and returning to one of:
@@ -7050,38 +6886,53 @@ interrupt:
      * (b) a js_Execute activation;
      * (c) a generator (SendToGenerator, jsiter.c).
      *
      * We must not be in an inline frame. The check above ensures that for the
      * error case and for a normal return, the code jumps directly to parent's
      * frame pc.
      */
     JS_ASSERT(inlineCallCount == 0);
-    JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
-
+
+    JS_ASSERT(fp->spbase);
+    JS_ASSERT(fp->regs == &regs);
     if (JS_LIKELY(mark != NULL)) {
         JS_ASSERT(!fp->blockChain);
         JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
         JS_ASSERT(!(fp->flags & JSFRAME_GENERATOR));
-        JS_ASSERT(fp->spbase);
-        JS_ASSERT(fp->spbase <= fp->sp);
-        fp->sp = fp->spbase = NULL;
+        fp->spbase = NULL;
+        fp->regs = NULL;
         js_FreeRawStack(cx, mark);
+    } else {
+        JS_ASSERT(fp->flags & JSFRAME_GENERATOR);
+        if (fp->flags & JSFRAME_YIELDING) {
+            JSGenerator *gen;
+
+            gen = FRAME_TO_GENERATOR(fp);
+            gen->savedRegs = regs;
+            gen->frame.regs = &gen->savedRegs;
+            JS_PROPERTY_CACHE(cx).disabled -= js_CountWithBlocks(cx, fp);
+            JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
+        } else {
+            fp->regs = NULL;
+            fp->spbase = NULL;
+        }
     }
 
+  exit2:
+    JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
     if (cx->version == currentVersion && currentVersion != originalVersion)
         js_SetVersion(cx, originalVersion);
     cx->interpLevel--;
     return ok;
 
   atom_not_defined:
     {
         const char *printable;
 
-        ASSERT_SAVED_SP_AND_PC(fp);
         printable = js_AtomToPrintableString(cx, atom);
         if (printable)
             js_ReportIsNotDefined(cx, printable);
         goto error;
     }
 }
 
 #endif /* !defined js_invoke_c__ */
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -44,29 +44,33 @@
  * JS interpreter interface.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 #include "jsopcode.h"
 
 JS_BEGIN_EXTERN_C
 
+typedef struct JSFrameRegs {
+    jsbytecode      *pc;            /* program counter */
+    jsval           *sp;            /* stack pointer */
+} JSFrameRegs;
+
 /*
  * JS stack frame, may be allocated on the C stack by native callers.  Always
  * allocated on cx->stackPool for calls from the interpreter to an interpreted
  * function.
  *
  * NB: This struct is manually initialized in jsinterp.c and jsiter.c.  If you
  * add new members, update both files.  But first, try to remove members.  The
  * sharp* and xml* members should be moved onto the stack as local variables
  * with well-known slots, if possible.
  */
 struct JSStackFrame {
-    jsval           *sp;            /* stack pointer */
-    jsbytecode      *pc;            /* program counter */
+    JSFrameRegs     *regs;
     jsval           *spbase;        /* operand stack base */
     JSObject        *callobj;       /* lazily created Call object */
     JSObject        *argsobj;       /* lazily created arguments object */
     JSObject        *varobj;        /* variables object, where vars go */
     JSObject        *callee;        /* function or script object */
     JSScript        *script;        /* script being interpreted */
     JSFunction      *fun;           /* function being called or null */
     JSObject        *thisp;         /* "this" pointer if in method */
@@ -86,17 +90,17 @@ struct JSStackFrame {
     JSObject        *blockChain;    /* active compile-time block scopes */
 #ifdef DEBUG
     jsrefcount      pcDisabledSave; /* for balanced property cache control */
 #endif
 };
 
 typedef struct JSInlineFrame {
     JSStackFrame    frame;          /* base struct */
-    jsval           *rvp;           /* ptr to caller's return value slot */
+    JSFrameRegs     callerRegs;     /* parent's frame registers */
     void            *mark;          /* mark before inline frame */
     void            *hookData;      /* debugger call hook data */
     JSVersion       callerVersion;  /* dynamic version of calling script */
 } JSInlineFrame;
 
 /* JS stack frame flags. */
 #define JSFRAME_CONSTRUCTING   0x01 /* frame is for a constructor invocation */
 #define JSFRAME_INTERNAL       0x02 /* internal call, not invoked by a script */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -715,31 +715,30 @@ JSClass js_GeneratorClass = {
  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  * if they are non-null.
  */
 JSObject *
 js_NewGenerator(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *obj;
-    uintN argc, nargs, nvars, depth, nslots;
+    uintN argc, nargs, nvars, nslots;
     JSGenerator *gen;
     jsval *newsp;
 
     /* After the following return, failing control flow must goto bad. */
     obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL);
     if (!obj)
         return NULL;
 
     /* Load and compute stack slot counts. */
     argc = fp->argc;
     nargs = JS_MAX(argc, fp->fun->nargs);
     nvars = fp->nvars;
-    depth = fp->script->depth;
-    nslots = 2 + nargs + nvars + 2 * depth;
+    nslots = 2 + nargs + nvars + fp->script->depth;
 
     /* Allocate obj's private data struct. */
     gen = (JSGenerator *)
           JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
     if (!gen)
         goto bad;
 
     gen->obj = obj;
@@ -787,20 +786,22 @@ js_NewGenerator(JSContext *cx, JSStackFr
     COPY_STACK_ARRAY(vars, nvars, nvars);
 
 #undef COPY_STACK_ARRAY
 
     /* Initialize or copy virtual machine state. */
     gen->frame.down = NULL;
     gen->frame.annotation = NULL;
     gen->frame.scopeChain = fp->scopeChain;
-    gen->frame.pc = fp->pc;
 
-    /* Allocate generating pc and operand stack space. */
-    gen->frame.spbase = gen->frame.sp = newsp + depth;
+    gen->frame.spbase = newsp;
+    JS_ASSERT(fp->spbase == fp->regs->sp);
+    gen->savedRegs.sp = newsp;
+    gen->savedRegs.pc = fp->regs->pc;
+    gen->frame.regs = &gen->savedRegs;
 
     /* Copy remaining state (XXX sharp* and xml* should be local vars). */
     gen->frame.sharpDepth = 0;
     gen->frame.sharpArray = NULL;
     gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
     gen->frame.dormantNext = NULL;
     gen->frame.xmlNamespace = NULL;
     gen->frame.blockChain = NULL;
@@ -849,17 +850,17 @@ SendToGenerator(JSContext *cx, JSGenerat
     switch (op) {
       case JSGENOP_NEXT:
       case JSGENOP_SEND:
         if (gen->state == JSGEN_OPEN) {
             /*
              * Store the argument to send as the result of the yield
              * expression.
              */
-            gen->frame.sp[-1] = arg;
+            gen->savedRegs.sp[-1] = arg;
         }
         gen->state = JSGEN_RUNNING;
         break;
 
       case JSGENOP_THROW:
         JS_SetPendingException(cx, arg);
         gen->state = JSGEN_RUNNING;
         break;
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -92,16 +92,17 @@ typedef enum JSGeneratorState {
     JSGEN_CLOSING,  /* close method is doing asynchronous return */
     JSGEN_CLOSED    /* closed, cannot be started or closed again */
 } JSGeneratorState;
 
 struct JSGenerator {
     JSObject            *obj;
     JSGeneratorState    state;
     JSStackFrame        frame;
+    JSFrameRegs         savedRegs;
     JSArena             arena;
     jsval               stack[1];
 };
 
 #define FRAME_TO_GENERATOR(fp) \
     ((JSGenerator *) ((uint8 *)(fp) - offsetof(JSGenerator, frame)))
 
 extern JSObject *
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1141,17 +1141,18 @@ js_ComputeFilename(JSContext *cx, JSStac
 
     flags = JS_GetScriptFilenameFlags(caller->script);
     if ((flags & JSFILENAME_PROTECTED) &&
         strcmp(principals->codebase, "[System Principal]")) {
         *linenop = 0;
         return principals->codebase;
     }
 
-    *linenop = js_PCToLineNumber(cx, caller->script, caller->pc);
+    *linenop = js_PCToLineNumber(cx, caller->script,
+                                 caller->regs ? caller->regs->pc : NULL);
     return caller->script->filename;
 }
 
 static JSBool
 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     JSStackFrame *fp, *caller;
     JSBool indirectCall;
@@ -1165,18 +1166,18 @@ obj_eval(JSContext *cx, JSObject *obj, u
 #if JS_HAS_EVAL_THIS_SCOPE
     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
     JSObject *setCallerScopeChain = NULL;
     JSBool setCallerVarObj = JS_FALSE;
 #endif
 
     fp = cx->fp;
     caller = JS_GetScriptedCaller(cx, fp);
-    JS_ASSERT(!caller || caller->pc);
-    indirectCall = (caller && *caller->pc != JSOP_EVAL);
+    JS_ASSERT(!caller || caller->regs);
+    indirectCall = (caller && *caller->regs->pc != JSOP_EVAL);
 
     /*
      * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
      * calls that attempt to use a non-global object as the "with" object in
      * the former indirect case.
      */
     scopeobj = OBJ_GET_PARENT(cx, obj);
     if (scopeobj) {
@@ -1954,27 +1955,27 @@ js_PutBlockObject(JSContext *cx, JSBool 
 
     fp = cx->fp;
     obj = fp->scopeChain;
     JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
     JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp);
 
     /* Block and its locals must be on the current stack for GC safety. */
     JS_ASSERT((size_t) OBJ_BLOCK_DEPTH(cx, obj) <=
-              (size_t) (fp->sp - fp->spbase));
+              (size_t) (fp->regs->sp - fp->spbase));
 
     if (normalUnwind) {
         depth = OBJ_BLOCK_DEPTH(cx, obj);
         for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
             if (sprop->getter != js_BlockClass.getProperty)
                 continue;
             if (!(sprop->flags & SPROP_HAS_SHORTID))
                 continue;
             slot = depth + (uintN) sprop->shortid;
-            JS_ASSERT(slot < (size_t) (fp->sp - fp->spbase));
+            JS_ASSERT(slot < (size_t) (fp->regs->sp - fp->spbase));
             if (!js_DefineNativeProperty(cx, obj, sprop->id,
                                          fp->spbase[slot], NULL, NULL,
                                          JSPROP_ENUMERATE | JSPROP_PERMANENT,
                                          SPROP_HAS_SHORTID, sprop->shortid,
                                          NULL)) {
                 /*
                  * Stop adding properties if we failed due to out-of-memory or
                  * other quit-asap errors.
@@ -3264,18 +3265,18 @@ js_LookupPropertyWithFlags(JSContext *cx
                 generation = cx->resolvingTable->generation;
 
                 /* Null *propp here so we can test it at cleanup: safely. */
                 *propp = NULL;
 
                 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
                     newresolve = (JSNewResolveOp)resolve;
                     if (!(flags & JSRESOLVE_CLASSNAME) &&
-                        cx->fp &&
-                        (pc = cx->fp->pc)) {
+                        cx->fp && cx->fp->regs) {
+                        pc = cx->fp->regs->pc;
                         cs = &js_CodeSpec[*pc];
                         format = cs->format;
                         if (JOF_MODE(format) != JOF_NAME)
                             flags |= JSRESOLVE_QUALIFIED;
                         if ((format & JOF_ASSIGNING) ||
                             (cx->fp->flags & JSFRAME_ASSIGNING)) {
                             flags |= JSRESOLVE_ASSIGNING;
                         } else {
@@ -3462,20 +3463,20 @@ js_FindIdentifierBase(JSContext *cx, jsi
      * Look for id's property along the "with" statement chain and the
      * statically-linked scope chain.
      */
     if (js_FindPropertyHelper(cx, id, &obj, &pobj, &prop, &entry) < 0)
         return NULL;
     if (prop) {
         OBJ_DROP_PROPERTY(cx, pobj, prop);
 
-        JS_ASSERT(!entry ||
-                  entry->kpc == ((PCVCAP_TAG(entry->vcap) > 1)
-                                 ? (jsbytecode *) JSID_TO_ATOM(id)
-                                 : cx->fp->pc));
+        JS_ASSERT_IF(entry,
+                     entry->kpc == ((PCVCAP_TAG(entry->vcap) > 1)
+                                    ? (jsbytecode *) JSID_TO_ATOM(id)
+                                    : cx->fp->regs->pc));
         return obj;
     }
 
     /*
      * Use the top-level scope from the scope chain, which won't end in the
      * same scope as cx->globalObject for cross-context function calls.
      */
     JS_ASSERT(obj);
@@ -3624,20 +3625,21 @@ js_GetPropertyHelper(JSContext *cx, JSOb
             PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
             *entryp = NULL;
         }
 
         /*
          * Give a strict warning if foo.bar is evaluated by a script for an
          * object foo with no property named 'bar'.
          */
-        if (JSVAL_IS_VOID(*vp) && cx->fp && (pc = cx->fp->pc)) {
+        if (JSVAL_IS_VOID(*vp) && cx->fp && cx->fp->regs) {
             JSOp op;
             uintN flags;
 
+            pc = cx->fp->regs->pc;
             op = (JSOp) *pc;
             if (op == JSOP_GETXPROP) {
                 flags = JSREPORT_ERROR;
             } else {
                 if (!JS_HAS_STRICT_OPTION(cx) ||
                     (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
                     return JS_TRUE;
                 }
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -610,17 +610,18 @@ js_QuoteString(JSContext *cx, JSString *
 
 struct JSPrinter {
     Sprinter        sprinter;       /* base class state */
     JSArenaPool     pool;           /* string allocation pool */
     uintN           indent;         /* indentation in spaces */
     JSPackedBool    pretty;         /* pretty-print: indent, use newlines */
     JSPackedBool    grouped;        /* in parenthesized expression context */
     JSScript        *script;        /* script being printed */
-    jsbytecode      *dvgfence;      /* js_DecompileValueGenerator fencepost */
+    jsbytecode      *dvgfence;      /* DecompileExpression fencepost */
+    jsbytecode      **pcstack;      /* DecompileExpression modelled stack */
     JSFunction      *fun;           /* interpreted function */
     jsuword         *localNames;    /* argument and variable names */
 };
 
 /*
  * Hack another flag, a la JS_DONT_PRETTY_PRINT, into uintN indent parameters
  * to functions such as js_DecompileFunction and js_NewPrinter.  This time, as
  * opposed to JS_DONT_PRETTY_PRINT back in the dark ages, we can assume that a
@@ -639,16 +640,17 @@ JS_NEW_PRINTER(JSContext *cx, const char
         return NULL;
     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
     JS_INIT_ARENA_POOL(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
     jp->indent = indent & ~JS_IN_GROUP_CONTEXT;
     jp->pretty = pretty;
     jp->grouped = (indent & JS_IN_GROUP_CONTEXT) != 0;
     jp->script = NULL;
     jp->dvgfence = NULL;
+    jp->pcstack = NULL;
     jp->fun = fun;
     jp->localNames = NULL;
     if (fun && FUN_INTERPRETED(fun) && JS_GET_LOCAL_NAME_COUNT(fun)) {
         jp->localNames = js_GetLocalNameArray(cx, fun, &jp->pool);
         if (!jp->localNames) {
             js_DestroyPrinter(jp);
             return NULL;
         }
@@ -759,54 +761,86 @@ typedef struct SprintStack {
     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
     uintN       top;            /* top of stack index */
     uintN       inArrayInit;    /* array initialiser/comprehension level */
     JSBool      inGenExp;       /* in generator expression */
     JSPrinter   *printer;       /* permanent output goes here */
 } SprintStack;
 
 /*
+ * Find the depth of the operand stack when the interpreter reaches the given
+ * pc in script. pcstack must have space for least script->depth elements. On
+ * return it will contain pointers to opcodes that populated the interpreter's
+ * current operand stack.
+ *
+ * This function cannot raise an exception or error. However, due to a risk of
+ * potential bugs when modeling the stack, the function returns -1 if it
+ * detects an inconsistency in the model. Such an inconsistency triggers an
+ * assert in a debug build.
+ */
+static intN
+ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
+                   jsbytecode **pcstack);
+
+#define FAILED_EXPRESSION_DECOMPILER ((char *) 1)
+
+/*
+ * Decompile a part of expression up to the given pc. The function returns
+ * NULL on out-of-memory, or the FAILED_EXPRESSION_DECOMPILER sentinel when
+ * the decompiler fails due to a bug and/or unimplemented feature, or the
+ * decompiled string on success.
+ */
+static char *
+DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
+                    jsbytecode *pc);
+
+/*
  * Get a stacked offset from ss->sprinter.base, or if the stacked value |off|
- * is negative, lazily fetch the generating pc at |spindex = 1 + off| and try
- * to decompile the code that generated the missing value.  This is used when
+ * is negative, fetch the generating pc from printer->pcstack[-2 - off] and
+ * decompile the code that generated the missing value.  This is used when
  * reporting errors, where the model stack will lack |pcdepth| non-negative
- * offsets (see js_DecompileValueGenerator and js_DecompileCode).
+ * offsets (see DecompileExpression and DecompileCode).
  *
  * If the stacked offset is -1, return 0 to index the NUL padding at the start
  * of ss->sprinter.base.  If this happens, it means there is a decompiler bug
  * to fix, but it won't violate memory safety.
  */
 static ptrdiff_t
 GetOff(SprintStack *ss, uintN i)
 {
     ptrdiff_t off;
+    jsbytecode *pc;
     char *bytes;
 
     off = ss->offsets[i];
-    if (off < 0) {
-#if defined DEBUG_brendan || defined DEBUG_mrbkap || defined DEBUG_crowder
-        JS_ASSERT(off < -1);
-#endif
-        if (++off == 0) {
-            if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0)
-                memset(ss->sprinter.base, 0, ss->sprinter.offset);
-            return 0;
-        }
-
-        bytes = js_DecompileValueGenerator(ss->sprinter.context, off,
-                                           JSVAL_NULL, NULL);
+    if (off >= 0)
+        return off;
+
+    JS_ASSERT(off <= -2);
+    JS_ASSERT(ss->printer->pcstack);
+    if (off < -2 && ss->printer->pcstack) {
+        pc = ss->printer->pcstack[-2 - off];
+        bytes = DecompileExpression(ss->sprinter.context, ss->printer->script,
+                                    ss->printer->fun, pc);
         if (!bytes)
             return 0;
-        off = SprintCString(&ss->sprinter, bytes);
-        if (off < 0)
-            off = 0;
-        ss->offsets[i] = off;
-        JS_free(ss->sprinter.context, bytes);
+        if (bytes != FAILED_EXPRESSION_DECOMPILER) {
+            off = SprintCString(&ss->sprinter, bytes);
+            if (off < 0)
+                off = 0;
+            ss->offsets[i] = off;
+            JS_free(ss->sprinter.context, bytes);
+            return off;
+        }
+        if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
+            memset(ss->sprinter.base, 0, ss->sprinter.offset);
+            ss->offsets[i] = -1;
+        }
     }
-    return off;
+    return 0;
 }
 
 static const char *
 GetStr(SprintStack *ss, uintN i)
 {
     ptrdiff_t off;
 
     /*
@@ -1748,17 +1782,17 @@ Decompile(SprintStack *ss, jsbytecode *p
             /*
              * Rewrite non-get ops to their "get" format if the error is in
              * the bytecode at pc, so we don't decompile more than the error
              * expression.
              */
             for (fp = cx->fp; fp && !fp->script; fp = fp->down)
                 continue;
             format = cs->format;
-            if (((fp && pc == fp->pc) ||
+            if (((fp && fp->regs && pc == fp->regs->pc) ||
                  (pc == startpc && cs->nuses != 0)) &&
                 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_IMPORT|JOF_FOR|
                           JOF_VARPROP)) {
                 mode = JOF_MODE(format);
                 if (mode == JOF_NAME) {
                     /*
                      * JOF_NAME does not imply JOF_ATOM, so we must check for
                      * the QARG and QVAR format types, and translate those to
@@ -4515,19 +4549,19 @@ Decompile(SprintStack *ss, jsbytecode *p
 #undef LOCAL_ASSERT
 #undef ATOM_IS_IDENTIFIER
 #undef GET_QUOTE_AND_FMT
 #undef GET_ATOM_QUOTE_AND_FMT
 
     return pc;
 }
 
-JSBool
-js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
-                 uintN pcdepth)
+static JSBool
+DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
+              uintN pcdepth)
 {
     uintN depth, i;
     SprintStack ss;
     JSContext *cx;
     void *mark;
     JSBool ok;
     JSScript *oldscript;
     char *last;
@@ -4550,38 +4584,19 @@ js_DecompileCode(JSPrinter *jp, JSScript
      * the error arose.
      *
      * See js_DecompileValueGenerator for how its |spindex| parameter is used,
      * and see also GetOff, which makes use of the ss.offsets[i] < -1 that are
      * potentially stored below.
      */
     ss.top = pcdepth;
     if (pcdepth != 0) {
-        JSStackFrame *fp;
-        ptrdiff_t top;
-
-        for (fp = cx->fp; fp && !fp->script; fp = fp->down)
-            continue;
-        top = fp ? fp->sp - fp->spbase : 0;
         for (i = 0; i < pcdepth; i++) {
-            ss.offsets[i] = -1;
-            ss.opcodes[i] = JSOP_NOP;
-        }
-        if (fp && fp->pc == pc && (uintN)top == pcdepth) {
-            for (i = 0; i < pcdepth; i++) {
-                ptrdiff_t off;
-                jsbytecode *genpc;
-
-                off = (intN)i - (intN)depth;
-                genpc = (jsbytecode *) fp->spbase[off];
-                if (JS_UPTRDIFF(genpc, script->code) < script->length) {
-                    ss.offsets[i] += (ptrdiff_t)i - top;
-                    ss.opcodes[i] = *genpc;
-                }
-            }
+            ss.offsets[i] = -2 - i;
+            ss.opcodes[i] = *jp->pcstack[i];
         }
     }
 
     /* Call recursive subroutine to do the hard work. */
     oldscript = jp->script;
     jp->script = script;
     ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL;
     jp->script = oldscript;
@@ -4598,17 +4613,17 @@ out:
     /* Free all temporary stuff allocated under this call. */
     JS_ARENA_RELEASE(&cx->tempPool, mark);
     return ok;
 }
 
 JSBool
 js_DecompileScript(JSPrinter *jp, JSScript *script)
 {
-    return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
+    return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
 }
 
 static const char native_code_str[] = "\t[native code]\n";
 
 JSBool
 js_DecompileFunctionBody(JSPrinter *jp)
 {
     JSScript *script;
@@ -4616,17 +4631,17 @@ js_DecompileFunctionBody(JSPrinter *jp)
     JS_ASSERT(jp->fun);
     JS_ASSERT(!jp->script);
     if (!FUN_INTERPRETED(jp->fun)) {
         js_printf(jp, native_code_str);
         return JS_TRUE;
     }
 
     script = jp->fun->u.i.script;
-    return js_DecompileCode(jp, script, script->code, (uintN)script->length, 0);
+    return DecompileCode(jp, script, script->code, (uintN)script->length, 0);
 }
 
 JSBool
 js_DecompileFunction(JSPrinter *jp)
 {
     JSFunction *fun;
     uintN i;
     JSAtom *param;
@@ -4742,139 +4757,144 @@ js_DecompileFunction(JSPrinter *jp)
         if (fun->flags & JSFUN_EXPR_CLOSURE) {
             js_printf(jp, ") ");
         } else {
             js_printf(jp, ") {\n");
             jp->indent += 4;
         }
 
         len = fun->u.i.script->code + fun->u.i.script->length - pc;
-        ok = js_DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
+        ok = DecompileCode(jp, fun->u.i.script, pc, (uintN)len, 0);
         if (!ok)
             return JS_FALSE;
 
         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
             jp->indent -= 4;
             js_printf(jp, "\t}");
         }
     }
 
     if (!jp->pretty && !jp->grouped && (fun->flags & JSFUN_LAMBDA))
         js_puts(jp, ")");
 
     return JS_TRUE;
 }
 
-/*
- * Find the depth of the operand stack when the interpreter reaches the given
- * pc in script. When pcstack is not null, it must be an array with space for
- * at least script->depth elements. On return the array will contain pointers
- * to opcodes that populated the interpreter's current operand stack.
- *
- * This function cannot raise an exception or error. However, due to a risk of
- * potential bugs when modeling the stack, the function returns -1 if it
- * detects an inconsistency in the model. Such an inconsistency triggers an
- * assert in a debug build.
- */
-static intN
-ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
-                   jsbytecode **pcstack);
-
 char *
 js_DecompileValueGenerator(JSContext *cx, intN spindex, jsval v,
                            JSString *fallback)
 {
     JSStackFrame *fp;
-    jsbytecode *pc, *begin, *end;
+    jsbytecode *pc;
     JSScript *script;
-    JSOp op;
-    const JSCodeSpec *cs;
-    jssrcnote *sn;
-    ptrdiff_t len;
+    JSFrameRegs *regs;
     intN pcdepth;
     jsval *sp;
-    JSPrinter *jp;
     char *name;
 
     JS_ASSERT(spindex < 0 ||
               spindex == JSDVG_IGNORE_STACK ||
               spindex == JSDVG_SEARCH_STACK);
 
     for (fp = cx->fp; fp && !fp->script; fp = fp->down)
         continue;
-    if (!fp)
+    if (!fp || !fp->regs)
         goto do_fallback;
 
-    pc = fp->pc;
-    if (!pc)
-        goto do_fallback;
     script = fp->script;
+    regs = fp->regs;
+    pc = regs->pc;
     if (pc < script->main || script->code + script->length <= pc) {
         JS_NOT_REACHED("bug");
         goto do_fallback;
     }
 
     if (spindex != JSDVG_IGNORE_STACK) {
         jsbytecode **pcstack;
 
         /*
          * Prepare computing pcstack containing pointers to opcodes that
          * populated interpreter's stack with its current content.
          */
         pcstack = (jsbytecode **)
                   JS_malloc(cx, script->depth * sizeof *pcstack);
         if (!pcstack)
             return NULL;
-        pcdepth = ReconstructPCStack(cx, script, fp->pc, pcstack);
+        pcdepth = ReconstructPCStack(cx, script, regs->pc, pcstack);
         if (pcdepth < 0)
             goto release_pcstack;
 
         if (spindex != JSDVG_SEARCH_STACK) {
             JS_ASSERT(spindex < 0);
             pcdepth += spindex;
             if (pcdepth < 0)
                 goto release_pcstack;
             pc = pcstack[pcdepth];
         } else {
             /*
              * We search from fp->sp to base to find the most recently
              * calculated value matching v under assumption that it is
              * it that caused exception, see bug 328664.
              */
-            JS_ASSERT((size_t) (fp->sp - fp->spbase) <= fp->script->depth);
-            sp = fp->sp;
+            JS_ASSERT((size_t) (regs->sp - fp->spbase) <= script->depth);
+            sp = regs->sp;
             do {
                 if (sp == fp->spbase) {
                     pcdepth = -1;
                     goto release_pcstack;
                 }
             } while (*--sp != v);
 
             if (sp >= fp->spbase + pcdepth) {
                 /*
                  * This happens when the value comes from a temporary slot
                  * that the interpreter uses for GC roots. Assume that it is
                  * fp->pc that caused the exception.
                  */
-                pc = fp->pc;
+                pc = regs->pc;
             } else {
                 pc = pcstack[sp - fp->spbase];
             }
         }
 
       release_pcstack:
         JS_free(cx, pcstack);
         if (pcdepth < 0)
             goto do_fallback;
     }
 
-    /*
-     * We know the address of the opcode that triggered the diagnostic. Find
-     * the decompilation limits for the opcode and its stack depth.
-     */
+    name = DecompileExpression(cx, script, fp->fun, pc);
+    if (name != FAILED_EXPRESSION_DECOMPILER)
+        return name;
+
+  do_fallback:
+    if (!fallback) {
+        fallback = js_ValueToSource(cx, v);
+        if (!fallback)
+            return NULL;
+    }
+    return js_DeflateString(cx, JSSTRING_CHARS(fallback),
+                            JSSTRING_LENGTH(fallback));
+}
+
+static char *
+DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun,
+                    jsbytecode *pc)
+{
+    JSOp op;
+    const JSCodeSpec *cs;
+    jsbytecode *begin, *end;
+    jssrcnote *sn;
+    ptrdiff_t len;
+    jsbytecode **pcstack;
+    intN pcdepth;
+    JSPrinter *jp;
+    char *name;
+
+    JS_ASSERT(script->main <= pc && pc < script->code + script->length);
     op = (JSOp) *pc;
     if (op == JSOP_TRAP)
         op = JS_GetTrapOpcode(cx, script, pc);
 
     /* None of these stack-writing ops generates novel values. */
     JS_ASSERT(op != JSOP_CASE && op != JSOP_CASEX &&
               op != JSOP_DUP && op != JSOP_DUP2 &&
               op != JSOP_SWAP);
@@ -4888,85 +4908,89 @@ js_DecompileValueGenerator(JSContext *cx
 
     /*
      * JSOP_BINDNAME is special: it generates a value, the base object of a
      * reference.  But if it is the generating op for a diagnostic produced by
      * js_DecompileValueGenerator, the name being bound is irrelevant.  Just
      * fall back to the base object.
      */
     if (op == JSOP_BINDNAME)
-        goto do_fallback;
+        return FAILED_EXPRESSION_DECOMPILER;
 
     /* NAME ops are self-contained, others require left or right context. */
     cs = &js_CodeSpec[op];
     begin = pc;
     end = pc + cs->length;
     switch (JOF_MODE(cs->format)) {
       case JOF_PROP:
       case JOF_ELEM:
       case JOF_XMLNAME:
       case 0:
         sn = js_GetSrcNote(script, pc);
         if (!sn)
-            goto do_fallback;
+            return FAILED_EXPRESSION_DECOMPILER;
         switch (SN_TYPE(sn)) {
           case SRC_PCBASE:
             begin -= js_GetSrcNoteOffset(sn, 0);
             break;
           case SRC_PCDELTA:
             end = begin + js_GetSrcNoteOffset(sn, 0);
             begin += cs->length;
             break;
           default:
-            goto do_fallback;
+            return FAILED_EXPRESSION_DECOMPILER;
         }
         break;
       default:;
     }
     len = PTRDIFF(end, begin, jsbytecode);
     if (len <= 0)
-        goto do_fallback;
-
-    pcdepth = ReconstructPCStack(cx, script, begin, NULL);
-    if (pcdepth < 0)
-         goto do_fallback;
+        return FAILED_EXPRESSION_DECOMPILER;
+
+    pcstack = (jsbytecode **) JS_malloc(cx, script->depth * sizeof *pcstack);
+    if (!pcstack)
+        return NULL;
+
+    /* From this point the control must flow through the label out. */
+    pcdepth = ReconstructPCStack(cx, script, begin, pcstack);
+    if (pcdepth < 0) {
+         name = FAILED_EXPRESSION_DECOMPILER;
+         goto out;
+    }
 
     name = NULL;
-    jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fp->fun, 0, JS_FALSE);
+    jp = JS_NEW_PRINTER(cx, "js_DecompileValueGenerator", fun, 0, JS_FALSE);
     if (jp) {
         jp->dvgfence = end;
-        if (js_DecompileCode(jp, script, begin, (uintN)len, (uintN)pcdepth)) {
+        jp->pcstack = pcstack;
+        if (DecompileCode(jp, script, begin, (uintN) len, (uintN) pcdepth)) {
             name = (jp->sprinter.base) ? jp->sprinter.base : (char *) "";
             name = JS_strdup(cx, name);
         }
         js_DestroyPrinter(jp);
     }
+
+  out:
+    JS_free(cx, pcstack);
     return name;
-
-  do_fallback:
-    if (!fallback) {
-        fallback = js_ValueToSource(cx, v);
-        if (!fallback)
-            return NULL;
-    }
-    return js_DeflateString(cx, JSSTRING_CHARS(fallback),
-                            JSSTRING_LENGTH(fallback));
 }
 
 static intN
 ReconstructPCStack(JSContext *cx, JSScript *script, jsbytecode *pc,
                    jsbytecode **pcstack)
 {
     intN pcdepth, nuses, ndefs;
     jsbytecode *begin;
     JSOp op;
     const JSCodeSpec *cs;
     ptrdiff_t oplen;
     jssrcnote *sn;
     uint32 type;
+    jsbytecode *pc2;
+    intN i;
 
 #define LOCAL_ASSERT(expr)      LOCAL_ASSERT_RV(expr, -1);
 
     /*
      * Walk forward from script->main and compute the stack depth and stack of
      * operand-generating opcode PCs in pcstack.
      *
      * FIXME: Code to compute oplen copied from js_Disassemble1 and reduced.
@@ -5021,18 +5045,17 @@ ReconstructPCStack(JSContext *cx, JSScri
             }
         }
 
         type = JOF_TYPE(cs->format);
         switch (type) {
           case JOF_TABLESWITCH:
           case JOF_TABLESWITCHX:
           {
-            jsint jmplen, i, low, high;
-            jsbytecode *pc2;
+            jsint jmplen, low, high;
 
             jmplen = (type == JOF_TABLESWITCH) ? JUMP_OFFSET_LEN
                                                : JUMPX_OFFSET_LEN;
             pc2 = pc;
             pc2 += jmplen;
             low = GET_JUMP_OFFSET(pc2);
             pc2 += JUMP_OFFSET_LEN;
             high = GET_JUMP_OFFSET(pc2);
@@ -5042,17 +5065,16 @@ ReconstructPCStack(JSContext *cx, JSScri
             oplen = 1 + pc2 - pc;
             break;
           }
 
           case JOF_LOOKUPSWITCH:
           case JOF_LOOKUPSWITCHX:
           {
             jsint jmplen;
-            jsbytecode *pc2;
             jsatomid npairs;
 
             jmplen = (type == JOF_LOOKUPSWITCH) ? JUMP_OFFSET_LEN
                                                 : JUMPX_OFFSET_LEN;
             pc2 = pc;
             pc2 += jmplen;
             npairs = GET_UINT16(pc2);
             pc2 += INDEX_LEN;
@@ -5096,66 +5118,66 @@ ReconstructPCStack(JSContext *cx, JSScri
 
             JS_ASSERT(ndefs == 0);
             GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj);
             JS_ASSERT(OBJ_BLOCK_DEPTH(cx, obj) == pcdepth);
             ndefs = OBJ_BLOCK_COUNT(cx, obj);
         }
 
         LOCAL_ASSERT((uintN)(pcdepth + ndefs) <= script->depth);
-        if (pcstack) {
-            intN i;
-            jsbytecode *pc2;
-
+
+        /*
+         * Fill the slots that the opcode defines withs its pc unless it just
+         * reshuffle the stack. In the latter case we want to preserve the
+         * opcode that generated the original value.
+         */
+        switch (op) {
+          default:
+            for (i = 0; i != ndefs; ++i)
+                pcstack[pcdepth + i] = pc;
+            break;
+
+          case JSOP_CASE:
+          case JSOP_CASEX:
+            /* Keep the switch value. */
+            JS_ASSERT(ndefs == 1);
+            break;
+
+          case JSOP_DUP:
+            JS_ASSERT(ndefs == 2);
+            pcstack[pcdepth + 1] = pcstack[pcdepth];
+            break;
+
+          case JSOP_DUP2:
+            JS_ASSERT(ndefs == 4);
+            pcstack[pcdepth + 2] = pcstack[pcdepth];
+            pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
+            break;
+
+          case JSOP_SWAP:
+            JS_ASSERT(ndefs == 2);
+            pc2 = pcstack[pcdepth];
+            pcstack[pcdepth] = pcstack[pcdepth + 1];
+            pcstack[pcdepth + 1] = pc2;
+            break;
+
+          case JSOP_LEAVEBLOCKEXPR:
             /*
-             * Fill the slots that the opcode defines withs its pc unless it
-             * just reshuffle the stack. In the latter case we want to
-             * preserve the opcode that generated the original value.
+             * The decompiler wants to see [leaveblockexpr] on pcstack, not
+             * [enterblock] or the pc that ended a simulated let expression
+             * when [enterblock] defines zero locals as in:
+             *
+             *   let ([] = []) expr
              */
-            switch (op) {
-              default:
-                for (i = 0; i != ndefs; ++i)
-                    pcstack[pcdepth + i] = pc;
-                break;
-
-              case JSOP_CASE:
-              case JSOP_CASEX:
-                /* Keep the switch value. */
-                JS_ASSERT(ndefs == 1);
-                break;
-
-              case JSOP_DUP:
-                JS_ASSERT(ndefs == 2);
-                pcstack[pcdepth + 1] = pcstack[pcdepth];
-                break;
-
-              case JSOP_DUP2:
-                JS_ASSERT(ndefs == 4);
-                pcstack[pcdepth + 2] = pcstack[pcdepth];
-                pcstack[pcdepth + 3] = pcstack[pcdepth + 1];
-                break;
-
-              case JSOP_SWAP:
-                JS_ASSERT(ndefs == 2);
-                pc2 = pcstack[pcdepth];
-                pcstack[pcdepth] = pcstack[pcdepth + 1];
-                pcstack[pcdepth + 1] = pc2;
-                break;
-
-              case JSOP_LEAVEBLOCKEXPR:
-                /*
-                 * The decompiler wants [leaveblockexpr], not [enterblock], to
-                 * be left on pcstack after a simulated let expression.
-                 */
-                JS_ASSERT(ndefs == 0);
-                LOCAL_ASSERT(pcdepth >= 1);
-                LOCAL_ASSERT(*pcstack[pcdepth - 1] == JSOP_ENTERBLOCK);
-                pcstack[pcdepth - 1] = pc;
-                break;
-            }
+            JS_ASSERT(ndefs == 0);
+            LOCAL_ASSERT(pcdepth >= 1);
+            LOCAL_ASSERT(nuses == 0 ||
+                         *pcstack[pcdepth - 1] == JSOP_ENTERBLOCK);
+            pcstack[pcdepth - 1] = pc;
+            break;
         }
         pcdepth += ndefs;
     }
     LOCAL_ASSERT(pc == begin);
     return pcdepth;
 
 #undef LOCAL_ASSERT
 }
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -357,20 +357,16 @@ extern JS_FRIEND_API(uintN)
 js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc,
                 JSBool lines, FILE *fp);
 #endif /* DEBUG */
 
 /*
  * Decompilers, for script, function, and expression pretty-printing.
  */
 extern JSBool
-js_DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len,
-                 uintN pcdepth);
-
-extern JSBool
 js_DecompileScript(JSPrinter *jp, JSScript *script);
 
 extern JSBool
 js_DecompileFunctionBody(JSPrinter *jp);
 
 extern JSBool
 js_DecompileFunction(JSPrinter *jp);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -347,23 +347,23 @@ js_str_escape(JSContext *cx, JSObject *o
             newlength += 5; /* The character will be encoded as %uXXXX */
         }
 
         /*
          * This overflow test works because newlength is incremented by at
          * most 5 on each iteration.
          */
         if (newlength < length) {
-            JS_ReportOutOfMemory(cx);
+            js_ReportAllocationOverflow(cx);
             return JS_FALSE;
         }
     }
 
     if (newlength >= ~(size_t)0 / sizeof(jschar)) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
     newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));
     if (!newchars)
         return JS_FALSE;
     for (i = 0, ni = 0; i < length; i++) {
         if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
@@ -1206,25 +1206,26 @@ match_or_replace(JSContext *cx,
              * MODE_MATCH implies str_match is being called from a script or a
              * scripted function.  If the caller cares only about testing null
              * vs. non-null return value, optimize away the array object that
              * would normally be returned in *vp.
              */
             JSStackFrame *fp;
 
             /* Skip Function.prototype.call and .apply frames. */
-            for (fp = cx->fp; fp && !fp->pc; fp = fp->down)
+            for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
                 JS_ASSERT(!fp->script);
 
             /* Assume a full array result is required, then prove otherwise. */
             test = JS_FALSE;
             if (fp) {
-                JS_ASSERT(*fp->pc == JSOP_CALL || *fp->pc == JSOP_NEW);
-                JS_ASSERT(js_CodeSpec[*fp->pc].length == 3);
-                switch (fp->pc[3]) {
+                JS_ASSERT(*fp->regs->pc == JSOP_CALL ||
+                          *fp->regs->pc == JSOP_NEW);
+                JS_ASSERT(js_CodeSpec[*fp->regs->pc].length == 3);
+                switch (fp->regs->pc[3]) {
                   case JSOP_POP:
                   case JSOP_IFEQ:
                   case JSOP_IFNE:
                   case JSOP_IFEQX:
                   case JSOP_IFNEX:
                     test = JS_TRUE;
                     break;
                   default:;
@@ -2063,17 +2064,17 @@ tagify(JSContext *cx, const char *begin,
     if (param) {
         parlen = JSSTRING_LENGTH(param);
         taglen += 2 + parlen + 1;                       /* '="param"' */
     }
     endlen = strlen(end);
     taglen += JSSTRING_LENGTH(str) + 2 + endlen + 1;    /* 'str</end>' */
 
     if (taglen >= ~(size_t)0 / sizeof(jschar)) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
     tagbuf = (jschar *) JS_malloc(cx, (taglen + 1) * sizeof(jschar));
     if (!tagbuf)
         return JS_FALSE;
 
     j = 0;
@@ -2456,17 +2457,17 @@ js_InitStringClass(JSContext *cx, JSObje
 }
 
 JSString *
 js_NewString(JSContext *cx, jschar *chars, size_t length)
 {
     JSString *str;
 
     if (length > JSSTRING_LENGTH_MASK) {
-        JS_ReportOutOfMemory(cx);
+        js_ReportAllocationOverflow(cx);
         return NULL;
     }
 
     str = (JSString *) js_NewGCThing(cx, GCX_STRING, sizeof(JSString));
     if (!str)
         return NULL;
     JSFLATSTR_INIT(str, chars, length);
 #ifdef DEBUG
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1997,25 +1997,25 @@ ParseXMLSource(JSContext *cx, JSString *
     js_strncpy(chars + offset, srcp, srclen);
     offset += srclen;
     dstlen = length - offset + 1;
     js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
                              &dstlen);
     chars [offset + dstlen] = 0;
 
     xml = NULL;
-    for (fp = cx->fp; fp && !fp->pc; fp = fp->down)
+    for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
         JS_ASSERT(!fp->script);
     filename = NULL;
     lineno = 1;
     if (fp) {
-        op = (JSOp) *fp->pc;
+        op = (JSOp) *fp->regs->pc;
         if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
             filename = fp->script->filename;
-            lineno = js_PCToLineNumber(cx, fp->script, fp->pc);
+            lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
             for (endp = srcp + srclen; srcp < endp; srcp++) {
                 if (*srcp == '\n')
                     --lineno;
             }
         }
     }
 
     if (!js_InitParseContext(cx, &pc, NULL, chars, length, NULL,
@@ -2362,17 +2362,17 @@ EscapeElementValue(JSContext *cx, JSStri
     for (cp = start, end = cp + length; cp < end; cp++) {
         c = *cp;
         if (c == '<' || c == '>')
             newlength += 3;
         else if (c == '&')
             newlength += 4;
 
         if (newlength < length) {
-            JS_ReportOutOfMemory(cx);
+            js_ReportAllocationOverflow(cx);
             return NULL;
         }
     }
     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
         JSStringBuffer localSB;
         if (!sb) {
             sb = &localSB;
             js_InitStringBuffer(sb);
@@ -2419,17 +2419,17 @@ EscapeAttributeValue(JSContext *cx, JSSt
         if (c == '"')
             newlength += 5;
         else if (c == '<')
             newlength += 3;
         else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
             newlength += 4;
 
         if (newlength < length) {
-            JS_ReportOutOfMemory(cx);
+            js_ReportAllocationOverflow(cx);
             return NULL;
         }
     }
     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
         JSStringBuffer localSB;
         if (!sb) {
             sb = &localSB;
             js_InitStringBuffer(sb);
@@ -8276,17 +8276,17 @@ js_InitXMLFilterClass(JSContext *cx, JSO
 JSBool
 js_StepXMLListFilter(JSContext *cx, JSBool initialized)
 {
     jsval *sp;
     JSObject *obj, *filterobj, *resobj, *kidobj;
     JSXML *xml, *list;
     JSXMLFilter *filter;
 
-    sp = cx->fp->sp;
+    sp = cx->fp->regs->sp;
     if (!initialized) {
         /*
          * We haven't iterated yet, so initialize the filter based on the
          * value stored in sp[-2].
          */
         if (!VALUE_IS_XML(cx, sp[-2])) {
             js_ReportValueError(cx, JSMSG_NON_XML_FILTER, -2, sp[-2], NULL);
             return JS_FALSE;
--- a/js/src/liveconnect/nsCLiveconnect.cpp
+++ b/js/src/liveconnect/nsCLiveconnect.cpp
@@ -182,17 +182,16 @@ AutoPushJSContext::AutoPushJSContext(nsI
                                                               jsprinc, "anonymous", 0, nsnull,
                                                               "", 0, "", 1);
             JSPRINCIPALS_DROP(cx, jsprinc);
 
             if (fun)
             {
                 mFrame.fun = fun;
                 mFrame.script = JS_GetFunctionScript(cx, fun);
-                mFrame.pc = mFrame.script->code;
                 mFrame.callee = JS_GetFunctionObject(fun);
                 mFrame.scopeChain = JS_GetParent(cx, mFrame.callee);
                 mFrame.down = cx->fp;
                 cx->fp = &mFrame;
             }
             else
                 mPushResult = NS_ERROR_OUT_OF_MEMORY;
         }
--- a/js/src/xpconnect/loader/ISO8601DateUtils.jsm
+++ b/js/src/xpconnect/loader/ISO8601DateUtils.jsm
@@ -36,17 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 const HOURS_TO_MINUTES = 60;
 const MINUTES_TO_SECONDS = 60;
 const SECONDS_TO_MILLISECONDS = 1000;
 const MINUTES_TO_MILLISECONDS = MINUTES_TO_SECONDS * SECONDS_TO_MILLISECONDS;
 const HOURS_TO_MILLISECONDS = HOURS_TO_MINUTES * MINUTES_TO_MILLISECONDS;
 
-EXPORTED_SYMBOLS = ["ISO8601DateUtils"];
+var EXPORTED_SYMBOLS = ["ISO8601DateUtils"];
 
 debug("*** loading ISO8601DateUtils\n");
 
 var ISO8601DateUtils = {
 
   /**
   * XXX Thunderbird's W3C-DTF function
   *
--- a/js/src/xpconnect/loader/JSON.jsm
+++ b/js/src/xpconnect/loader/JSON.jsm
@@ -46,17 +46,17 @@
  *
  * var newJSONString = JSON.toString( GIVEN_JAVASCRIPT_OBJECT );
  * var newJavaScriptObject = JSON.fromString( GIVEN_JSON_STRING );
  *
  * Note: For your own safety, Objects/Arrays returned by
  *       JSON.fromString aren't instanceof Object/Array.
  */
 
-EXPORTED_SYMBOLS = ["JSON"];
+var EXPORTED_SYMBOLS = ["JSON"];
 
 // The following code is a loose adaption of Douglas Crockford's code
 // from http://www.json.org/json.js (public domain'd)
 
 // Notable differences:
 // * Unserializable values such as |undefined| or functions aren't
 //   silently dropped but always lead to a TypeError.
 // * An optional key blacklist has been added to JSON.toString
--- a/js/src/xpconnect/loader/XPCOMUtils.jsm
+++ b/js/src/xpconnect/loader/XPCOMUtils.jsm
@@ -94,17 +94,17 @@
  * 3. Define the NSGetModule entry point:
  *  function NSGetModule(compMgr, fileSpec) {
  *    // components is the array created in step 2.
  *    return XPCOMUtils.generateModule(components);
  *  }
  */
 
 
-EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
+var EXPORTED_SYMBOLS = [ "XPCOMUtils" ];
 
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var XPCOMUtils = {
   /**
    * Generate a QueryInterface implementation. The returned function must be
    * assigned to the 'QueryInterface' property of a JS object. When invoked on
--- a/js/src/xpconnect/tests/unit/bogus_element_type.jsm
+++ b/js/src/xpconnect/tests/unit/bogus_element_type.jsm
@@ -1,1 +1,1 @@
-EXPORTED_SYMBOLS = [{}];
+var EXPORTED_SYMBOLS = [{}];
--- a/js/src/xpconnect/tests/unit/bogus_exports_type.jsm
+++ b/js/src/xpconnect/tests/unit/bogus_exports_type.jsm
@@ -1,1 +1,1 @@
-EXPORTED_SYMBOLS = "not an array";
+var EXPORTED_SYMBOLS = "not an array";
--- a/js/src/xpconnect/tests/unit/recursive_importA.jsm
+++ b/js/src/xpconnect/tests/unit/recursive_importA.jsm
@@ -29,16 +29,16 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-EXPORTED_SYMBOLS = ["foo", "bar"]
+var EXPORTED_SYMBOLS = ["foo", "bar"];
 
 function foo() {
   return "foo";
 }
 
 var bar = {}
 Components.utils.import("resource://test/recursive_importB.jsm", bar);
--- a/js/src/xpconnect/tests/unit/recursive_importB.jsm
+++ b/js/src/xpconnect/tests/unit/recursive_importB.jsm
@@ -29,17 +29,17 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-EXPORTED_SYMBOLS = ["baz", "qux"]
+var EXPORTED_SYMBOLS = ["baz", "qux"];
 
 function baz() {
   return "baz";
 }
 
 var qux = {}
 Components.utils.import("resource://test/recursive_importA.jsm", qux);
 
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -3079,21 +3079,20 @@ NS_IMETHODIMP DocumentViewerImpl::SizeTo
    nsCOMPtr<nsPresContext> presContext;
    GetPresContext(getter_AddRefs(presContext));
    NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
 
    PRInt32 width, height;
 
    // so how big is it?
    nsRect shellArea = presContext->GetVisibleArea();
-   if (shellArea.width == NS_UNCONSTRAINEDSIZE ||
-       shellArea.height == NS_UNCONSTRAINEDSIZE) {
-     // Protect against bogus returns here
-     return NS_ERROR_FAILURE;
-   }
+   // Protect against bogus returns here
+   NS_ENSURE_TRUE(shellArea.width != NS_UNCONSTRAINEDSIZE &&
+                  shellArea.height != NS_UNCONSTRAINEDSIZE,
+                  NS_ERROR_FAILURE);
    width = presContext->AppUnitsToDevPixels(shellArea.width);
    height = presContext->AppUnitsToDevPixels(shellArea.height);
 
    nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
    docShellAsItem->GetTreeOwner(getter_AddRefs(treeOwner));
    NS_ENSURE_TRUE(treeOwner, NS_ERROR_FAILURE);
 
    /* presContext's size was calculated in app units and has already been
@@ -3970,16 +3969,19 @@ DocumentViewerImpl::ReturnToGalleyPresen
   mPrintEngine->Destroy();
   mPrintEngine = nsnull;
 
   mViewManager->EnableRefresh(NS_VMREFRESH_DEFERRED);
 
   nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
   ResetFocusState(docShell);
 
+  if (mPresContext)
+    mPresContext->RestoreImageAnimationMode();
+
   SetTextZoom(mTextZoom);
   SetFullZoom(mPageZoom);
   Show();
 
 #endif // NS_PRINTING && NS_PRINT_PREVIEW
 }
 
 //------------------------------------------------------------
@@ -4048,16 +4050,18 @@ DocumentViewerImpl::OnDonePrinting()
       if (mDocument) {
         mDocument->SetScriptGlobalObject(nsnull);
         mDocument->Destroy();
         mDocument = nsnull;
       }
       mClosingWhilePrinting = PR_FALSE;
       NS_RELEASE_THIS();
     }
+    if (mPresContext)
+      mPresContext->RestoreImageAnimationMode();
   }
 #endif // NS_PRINTING && NS_PRINT_PREVIEW
 }
 
 NS_IMETHODIMP DocumentViewerImpl::SetPageMode(PRBool aPageMode, nsIPrintSettings* aPrintSettings)
 {
   // XXX Page mode is only partially working; it's currently used for
   // reftests that require a paginated context
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -215,16 +215,17 @@ public:
    * Notify the context that the document's compatibility mode has changed
    */
   NS_HIDDEN_(void) CompatibilityModeChanged();
 
   /**
    * Access the image animation mode for this context
    */
   PRUint16     ImageAnimationMode() const { return mImageAnimationMode; }
+  void RestoreImageAnimationMode() { SetImageAnimationMode(mImageAnimationModePref); }
   virtual NS_HIDDEN_(void) SetImageAnimationModeExternal(PRUint16 aMode);
   NS_HIDDEN_(void) SetImageAnimationModeInternal(PRUint16 aMode);
 #ifdef _IMPL_NS_LAYOUT
   void SetImageAnimationMode(PRUint16 aMode)
   { SetImageAnimationModeInternal(aMode); }
 #else
   void SetImageAnimationMode(PRUint16 aMode)
   { SetImageAnimationModeExternal(aMode); }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2484,24 +2484,27 @@ PresShell::sPaintSuppressionCallback(nsI
   if (self)
     self->UnsuppressPainting();
 }
 
 NS_IMETHODIMP
 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
 {
   NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
+  NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
+                  "shouldn't use unconstrained widths anymore");
   
   // If we don't have a root frame yet, that means we haven't had our initial
   // reflow... If that's the case, and aWidth or aHeight is unconstrained,
   // ignore them altogether.
   nsIFrame* rootFrame = FrameManager()->GetRootFrame();
 
-  if (!rootFrame &&
-      (aWidth == NS_UNCONSTRAINEDSIZE || aHeight == NS_UNCONSTRAINEDSIZE)) {
+  if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
+    // We can't do the work needed for SizeToContent without a root
+    // frame, and we want to return before setting the visible area.
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
 
   // There isn't anything useful we can do if the initial reflow hasn't happened
   if (!rootFrame)
     return NS_OK;
@@ -2537,16 +2540,21 @@ PresShell::ResizeReflow(nscoord aWidth, 
 
       DidCauseReflow();
       DidDoReflow();
     }
 
     batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
   }
 
+  if (aHeight == NS_UNCONSTRAINEDSIZE) {
+    mPresContext->SetVisibleArea(
+      nsRect(0, 0, aWidth, rootFrame->GetRect().height));
+  }
+
   if (!mIsDestroying) {
     CreateResizeEventTimer();
   }
 
   return NS_OK; //XXX this needs to be real. MMP
 }
 
 #define RESIZE_EVENT_DELAY 200
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -5879,22 +5879,22 @@ nsBlockFrame::IsVisibleInSelection(nsISe
   nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent));
   PRBool visible;
   nsresult rv = aSelection->ContainsNode(node, PR_TRUE, &visible);
   return NS_SUCCEEDED(rv) && visible;
 }
 
 /* virtual */ void
 nsBlockFrame::PaintTextDecorationLine(nsIRenderingContext& aRenderingContext, 
-                                      nsPoint aPt,
+                                      const nsPoint& aPt,
                                       nsLineBox* aLine,
                                       nscolor aColor, 
-                                      nscoord aOffset, 
-                                      nscoord aAscent, 
-                                      nscoord aSize,
+                                      gfxFloat aOffset, 
+                                      gfxFloat aAscent, 
+                                      gfxFloat aSize,
                                       const PRUint8 aDecoration) 
 {
   NS_ASSERTION(!aLine->IsBlock(), "Why did we ask for decorations on a block?");
 
   nscoord start = aLine->mBounds.x;
   nscoord width = aLine->mBounds.width;
 
   if (!GetPrevContinuation() && aLine == begin_lines().get()) {
@@ -5924,23 +5924,21 @@ nsBlockFrame::PaintTextDecorationLine(ns
       
   // Only paint if we have a positive width
   if (width > 0) {
     const nsStyleVisibility* visibility = GetStyleVisibility();
     PRBool isRTL = visibility->mDirection == NS_STYLE_DIRECTION_RTL;
     nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
     gfxPoint pt(PresContext()->AppUnitsToGfxUnits(start + aPt.x),
                 PresContext()->AppUnitsToGfxUnits(aLine->mBounds.y + aPt.y));
-    gfxSize size(PresContext()->AppUnitsToGfxUnits(width),
-                 PresContext()->AppUnitsToGfxUnits(aSize));
+    gfxSize size(PresContext()->AppUnitsToGfxUnits(width), aSize);
     nsCSSRendering::PaintDecorationLine(
       ctx, aColor, pt, size,
       PresContext()->AppUnitsToGfxUnits(aLine->GetAscent()),
-      PresContext()->AppUnitsToGfxUnits(aOffset),
-      aDecoration, NS_STYLE_BORDER_STYLE_SOLID, isRTL);
+      aOffset, aDecoration, NS_STYLE_BORDER_STYLE_SOLID, isRTL);
   }
 }
 
 #ifdef DEBUG
 static void DebugOutputDrawLine(PRInt32 aDepth, nsLineBox* aLine, PRBool aDrawn) {
   if (nsBlockFrame::gNoisyDamageRepair) {
     nsFrame::IndentBy(stdout, aDepth+1);
     nsRect lineArea = aLine->GetCombinedArea();
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -330,22 +330,22 @@ protected:
   }
 #endif
 
   /*
    * Overides member function of nsHTMLContainerFrame. Needed to handle the 
    * lines in a nsBlockFrame properly.
    */
   virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
-                                       nsPoint aPt,
+                                       const nsPoint& aPt,
                                        nsLineBox* aLine,
                                        nscolor aColor,
-                                       nscoord aOffset,
-                                       nscoord aAscent,
-                                       nscoord aSize,
+                                       gfxFloat aOffset,
+                                       gfxFloat aAscent,
+                                       gfxFloat aSize,
                                        const PRUint8 aDecoration);
 
   void TryAllLines(nsLineList::iterator* aIterator,
                    nsLineList::iterator* aStartIterator,
                    nsLineList::iterator* aEndIterator,
                    PRBool* aInOverflowLines);
 
   void SetFlags(PRUint32 aFlags) {
--- a/layout/generic/nsHTMLContainerFrame.cpp
+++ b/layout/generic/nsHTMLContainerFrame.cpp
@@ -57,16 +57,18 @@
 #include "nsIView.h"
 #include "nsIViewManager.h"
 #include "nsIDOMEvent.h"
 #include "nsIScrollableView.h"
 #include "nsWidgetsCID.h"
 #include "nsCOMPtr.h"
 #include "nsIDeviceContext.h"
 #include "nsIFontMetrics.h"
+#include "nsIThebesFontMetrics.h"
+#include "gfxFont.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
 #include "nsBlockFrame.h"
 #include "nsLineBox.h"
 #include "nsDisplayList.h"
 #include "nsCSSRendering.h"
 
 class nsDisplayTextDecoration : public nsDisplayItem {
@@ -95,36 +97,39 @@ private:
 
 void
 nsDisplayTextDecoration::Paint(nsDisplayListBuilder* aBuilder,
                                nsIRenderingContext* aCtx,
                                const nsRect& aDirtyRect)
 {
   nsCOMPtr<nsIFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(mFrame, getter_AddRefs(fm));
+  nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm.get());
+  gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
+  gfxFont* firstFont = fontGroup->GetFontAt(0);
+  if (!firstFont)
+    return; // OOM
+  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
 
   nsPoint pt = aBuilder->ToReferenceFrame(mFrame);
 
-  // REVIEW: From nsHTMLContainerFrame::PaintTextDecorations
-  nscoord ascent, offset, size;
   nsHTMLContainerFrame* f = static_cast<nsHTMLContainerFrame*>(mFrame);
-  fm->GetMaxAscent(ascent);
-  if (mDecoration != NS_STYLE_TEXT_DECORATION_LINE_THROUGH) {
-    fm->GetUnderline(offset, size);
-    if (mDecoration == NS_STYLE_TEXT_DECORATION_UNDERLINE) {
-      f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
-                                 offset, ascent, size, mDecoration);
-    } else if (mDecoration == NS_STYLE_TEXT_DECORATION_OVERLINE) {
-      f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
-                                 ascent, ascent, size, mDecoration);
-    }
+  if (mDecoration == NS_STYLE_TEXT_DECORATION_UNDERLINE) {
+    gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
+    f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
+                               underlineOffset, metrics.maxAscent,
+                               metrics.underlineSize, mDecoration);
+  } else if (mDecoration == NS_STYLE_TEXT_DECORATION_OVERLINE) {
+    f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
+                               metrics.maxAscent, metrics.maxAscent,
+                               metrics.underlineSize, mDecoration);
   } else {
-    fm->GetStrikeout(offset, size);
     f->PaintTextDecorationLine(*aCtx, pt, mLine, mColor,
-                               offset, ascent, size, mDecoration);
+                               metrics.strikeoutOffset, metrics.maxAscent,
+                               metrics.strikeoutSize, mDecoration);
   }
 }
 
 nsRect
 nsDisplayTextDecoration::GetBounds(nsDisplayListBuilder* aBuilder)
 {
   return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
 }
@@ -195,44 +200,42 @@ nsHTMLContainerFrame::BuildDisplayList(n
 }
 
 static PRBool 
 HasTextFrameDescendantOrInFlow(nsIFrame* aFrame);
 
 /*virtual*/ void
 nsHTMLContainerFrame::PaintTextDecorationLine(
                    nsIRenderingContext& aRenderingContext, 
-                   nsPoint aPt,
+                   const nsPoint& aPt,
                    nsLineBox* aLine,
                    nscolor aColor, 
-                   nscoord aOffset, 
-                   nscoord aAscent, 
-                   nscoord aSize,
+                   gfxFloat aOffset, 
+                   gfxFloat aAscent, 
+                   gfxFloat aSize,
                    const PRUint8 aDecoration) 
 {
   NS_ASSERTION(!aLine, "Should not have passed a linebox to a non-block frame");
   nsMargin bp = GetUsedBorderAndPadding();
   PRIntn skip = GetSkipSides();
   NS_FOR_CSS_SIDES(side) {
     if (skip & (1 << side)) {
       bp.side(side) = 0;
     }
   }
   const nsStyleVisibility* visibility = GetStyleVisibility();
   PRBool isRTL = visibility->mDirection == NS_STYLE_DIRECTION_RTL;
   nscoord innerWidth = mRect.width - bp.left - bp.right;
   nsRefPtr<gfxContext> ctx = aRenderingContext.ThebesContext();
   gfxPoint pt(PresContext()->AppUnitsToGfxUnits(bp.left + aPt.x),
               PresContext()->AppUnitsToGfxUnits(bp.top + aPt.y));
-  gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth),
-               PresContext()->AppUnitsToGfxUnits(aSize));
-  nsCSSRendering::PaintDecorationLine(
-    ctx, aColor, pt, size, PresContext()->AppUnitsToGfxUnits(aAscent),
-    PresContext()->AppUnitsToGfxUnits(aOffset),
-    aDecoration, NS_STYLE_BORDER_STYLE_SOLID, isRTL);
+  gfxSize size(PresContext()->AppUnitsToGfxUnits(innerWidth), aSize);
+  nsCSSRendering::PaintDecorationLine(ctx, aColor, pt, size, aAscent, aOffset,
+                                      aDecoration, NS_STYLE_BORDER_STYLE_SOLID,
+                                      isRTL);
 }
 
 void
 nsHTMLContainerFrame::GetTextDecorations(nsPresContext* aPresContext, 
                                          PRBool aIsBlock,
                                          PRUint8& aDecorations,
                                          nscolor& aUnderColor, 
                                          nscolor& aOverColor, 
--- a/layout/generic/nsHTMLContainerFrame.h
+++ b/layout/generic/nsHTMLContainerFrame.h
@@ -36,16 +36,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* base class #2 for rendering objects that have child lists */
 
 #ifndef nsHTMLContainerFrame_h___
 #define nsHTMLContainerFrame_h___
 
 #include "nsContainerFrame.h"
+#include "gfxPoint.h"
 class nsString;
 class nsAbsoluteFrame;
 class nsPlaceholderFrame;
 struct nsStyleDisplay;
 struct nsStylePosition;
 struct nsHTMLReflowMetrics;
 struct nsHTMLReflowState;
 class nsLineBox;
@@ -171,20 +172,20 @@ protected:
    *                                the baseline.
    *    @param aSize              the thickness of the line
    *    @param aDecoration        which line will be painted
    *                                i.e., NS_STYLE_TEXT_DECORATION_UNDERLINE or
    *                                      NS_STYLE_TEXT_DECORATION_OVERLINE or
    *                                      NS_STYLE_TEXT_DECORATION_LINE_THROUGH.
    */
   virtual void PaintTextDecorationLine(nsIRenderingContext& aRenderingContext,
-                                       nsPoint aPt,
+                                       const nsPoint& aPt,
                                        nsLineBox* aLine,
                                        nscolor aColor,
-                                       nscoord aOffset,
-                                       nscoord aAscent,
-                                       nscoord aSize,
+                                       gfxFloat aOffset,
+                                       gfxFloat aAscent,
+                                       gfxFloat aSize,
                                        const PRUint8 aDecoration);
 
   friend class nsDisplayTextDecoration;
 };
 
 #endif /* nsHTMLContainerFrame_h___ */
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -559,24 +559,16 @@ nsInlineFrame::ReflowFrames(nsPresContex
     // 4.4 (you will have to read between the lines to really see
     // it).
     //
     // The height of our box is the sum of our font size plus the top
     // and bottom border and padding. The height of children do not
     // affect our height.
     fm->GetMaxAscent(aMetrics.ascent);
     fm->GetHeight(aMetrics.height);
-    // Include the text-decoration lines to the height for readable.
-    nscoord offset, size;
-    fm->GetUnderline(offset, size);
-    nscoord ascentAndUnderline =
-      aPresContext->RoundAppUnitsToNearestDevPixels(aMetrics.ascent - offset) +
-      aPresContext->RoundAppUnitsToNearestDevPixels(size);
-    if (ascentAndUnderline > aMetrics.height)
-      aMetrics.height = ascentAndUnderline;
   } else {
     NS_WARNING("Cannot get font metrics - defaulting sizes to 0");
     aMetrics.ascent = aMetrics.height = 0;
   }
   aMetrics.ascent += aReflowState.mComputedBorderPadding.top;
   aMetrics.height += aReflowState.mComputedBorderPadding.top +
     aReflowState.mComputedBorderPadding.bottom;
 
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -51,16 +51,17 @@
 #include "nsBlockFrame.h"
 #include "nsInlineFrame.h"
 #include "nsStyleConsts.h"
 #include "nsHTMLContainerFrame.h"
 #include "nsSpaceManager.h"
 #include "nsStyleContext.h"
 #include "nsPresContext.h"
 #include "nsIFontMetrics.h"
+#include "nsIThebesFontMetrics.h"
 #include "nsIRenderingContext.h"
 #include "nsGkAtoms.h"
 #include "nsPlaceholderFrame.h"
 #include "nsIDocument.h"
 #include "nsIHTMLDocument.h"
 #include "nsIContent.h"
 #include "nsTextFragment.h"
 #include "nsBidiUtils.h"
@@ -2698,49 +2699,52 @@ nsLineLayout::CombineTextDecorations(nsP
 {
   if (!(aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE |
                         NS_STYLE_TEXT_DECORATION_OVERLINE |
                         NS_STYLE_TEXT_DECORATION_LINE_THROUGH)))
     return;
 
   nsCOMPtr<nsIFontMetrics> fm;
   nsLayoutUtils::GetFontMetricsForFrame(aFrame, getter_AddRefs(fm));
-  if (aAscentOverride == 0)
-    fm->GetMaxAscent(aAscentOverride);
-  gfxFloat ascent = aPresContext->AppUnitsToGfxUnits(aAscentOverride);
+  nsIThebesFontMetrics* tfm = static_cast<nsIThebesFontMetrics*>(fm.get());
+  gfxFontGroup* fontGroup = tfm->GetThebesFontGroup();
+  gfxFont* firstFont = fontGroup->GetFontAt(0);
+  if (!firstFont)
+      return; // OOM
+  const gfxFont::Metrics& metrics = firstFont->GetMetrics();
+
+  gfxFloat ascent = aAscentOverride == 0 ? metrics.maxAscent :
+                      aPresContext->AppUnitsToGfxUnits(aAscentOverride);
   nsRect decorationArea;
   if (aDecorations & (NS_STYLE_TEXT_DECORATION_UNDERLINE |
                       NS_STYLE_TEXT_DECORATION_OVERLINE)) {
-    nscoord offsetCoord, sizeCoord;
-    fm->GetUnderline(offsetCoord, sizeCoord);
     gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width),
-                 aPresContext->AppUnitsToGfxUnits(sizeCoord));
+                 metrics.underlineSize);
     if (aDecorations & NS_STYLE_TEXT_DECORATION_OVERLINE) {
       decorationArea =
         nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
                           ascent, NS_STYLE_TEXT_DECORATION_OVERLINE,
                           NS_STYLE_BORDER_STYLE_SOLID);
       aCombinedArea.UnionRect(aCombinedArea, decorationArea);
     }
     if (aDecorations & NS_STYLE_TEXT_DECORATION_UNDERLINE) {
-      aUnderlineSizeRatio = PR_MAX(aUnderlineSizeRatio, 1.0);
+      aUnderlineSizeRatio = PR_MAX(aUnderlineSizeRatio, 1.0f);
       size.height *= aUnderlineSizeRatio;
-      gfxFloat offset = aPresContext->AppUnitsToGfxUnits(offsetCoord);
+      gfxFloat underlineOffset = fontGroup->GetUnderlineOffset();
       decorationArea =
         nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
-                          offset, NS_STYLE_TEXT_DECORATION_UNDERLINE,
+                          underlineOffset,
+                          NS_STYLE_TEXT_DECORATION_UNDERLINE,
                           NS_STYLE_BORDER_STYLE_SOLID);
       aCombinedArea.UnionRect(aCombinedArea, decorationArea);
     }
   }
   if (aDecorations & NS_STYLE_TEXT_DECORATION_LINE_THROUGH) {
-    nscoord offsetCoord, sizeCoord;
-    fm->GetStrikeout(offsetCoord, sizeCoord);
     gfxSize size(aPresContext->AppUnitsToGfxUnits(aCombinedArea.width),
-                 aPresContext->AppUnitsToGfxUnits(sizeCoord));
-    gfxFloat offset = aPresContext->AppUnitsToGfxUnits(offsetCoord);
+                 metrics.strikeoutSize);
     decorationArea =
       nsCSSRendering::GetTextDecorationRect(aPresContext, size, ascent,
-                        offset, NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
+                        metrics.strikeoutOffset,
+                        NS_STYLE_TEXT_DECORATION_LINE_THROUGH,
                         NS_STYLE_BORDER_STYLE_SOLID);
     aCombinedArea.UnionRect(aCombinedArea, decorationArea);
   }
 }
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -3657,50 +3657,53 @@ nsTextFrame::UnionTextDecorationOverflow
   // spellchecking and IME.
   if (mState & NS_FRAME_SELECTED_CONTENT) {
     nsILookAndFeel* look = aPresContext->LookAndFeel();
     look->GetMetric(nsILookAndFeel::eMetricFloat_IMEUnderlineRelativeSize,
                     ratio);
     decorations.mDecorations |= NS_STYLE_TEXT_DECORATION_UNDERLINE;
   }
   nsLineLayout::CombineTextDecorations(aPresContext, decorations.mDecorations,
-                  this, *aOverflowRect, nscoord(NS_round(aTextMetrics.mAscent)),
+                  this, *aOverflowRect, NSToCoordRound(aTextMetrics.mAscent),
                   ratio);
 }
 
 void 
 nsTextFrame::PaintTextDecorations(gfxContext* aCtx, const gfxRect& aDirtyRect,
                                   const gfxPoint& aFramePt,
                                   const gfxPoint& aTextBaselinePt,
                                   nsTextPaintStyle& aTextPaintStyle,
                                   PropertyProvider& aProvider)
 {
   TextDecorations decorations =
     GetTextDecorations(aTextPaintStyle.PresContext());
   if (!decorations.HasDecorationlines())
     return;
 
-  gfxFont::Metrics fontMetrics = GetFontMetrics(aProvider.GetFontGroup());
+  gfxFont* firstFont = aProvider.GetFontGroup()->GetFontAt(0);
+  if (!firstFont)
+    return; // OOM
+  const gfxFont::Metrics& fontMetrics = firstFont->GetMetrics();
   gfxFloat app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
 
   // XXX aFramePt is in AppUnits, shouldn't it be nsFloatPoint?
   gfxPoint pt(aFramePt.x / app, (aTextBaselinePt.y - mAscent) / app);
   gfxSize size(GetRect().width / app, 0);
   gfxFloat ascent = gfxFloat(mAscent) / app;
 
   if (decorations.HasOverline()) {
     size.height = fontMetrics.underlineSize;
     nsCSSRendering::PaintDecorationLine(
       aCtx, decorations.mOverColor, pt, size, ascent, ascent,
       NS_STYLE_TEXT_DECORATION_OVERLINE, NS_STYLE_BORDER_STYLE_SOLID,
       mTextRun->IsRightToLeft());
   }
   if (decorations.HasUnderline()) {
     size.height = fontMetrics.underlineSize;
-    gfxFloat offset = fontMetrics.underlineOffset;
+    gfxFloat offset = aProvider.GetFontGroup()->GetUnderlineOffset();
     nsCSSRendering::PaintDecorationLine(
       aCtx, decorations.mUnderColor, pt, size, ascent, offset,
       NS_STYLE_TEXT_DECORATION_UNDERLINE, NS_STYLE_BORDER_STYLE_SOLID,
       mTextRun->IsRightToLeft());
   }
   if (decorations.HasStrikeout()) {
     size.height = fontMetrics.strikeoutSize;
     gfxFloat offset = fontMetrics.strikeoutOffset;
@@ -4045,17 +4048,22 @@ nsTextFrame::PaintTextSelectionDecoratio
       PRInt32 end = PR_MIN(contentLength, sdptr->mEnd - contentOffset);
       for (i = start; i < end; ++i) {
         selectedChars[i] = aSelectionType;
       }
     }
     sdptr = sdptr->mNext;
   }
 
-  gfxFont::Metrics decorationMetrics = GetFontMetrics(aProvider.GetFontGroup());
+  gfxFont* firstFont = aProvider.GetFontGroup()->GetFontAt(0);
+  if (!firstFont)
+    return; // OOM
+  gfxFont::Metrics decorationMetrics(firstFont->GetMetrics());
+  decorationMetrics.underlineOffset =
+    aProvider.GetFontGroup()->GetUnderlineOffset();
 
   SelectionIterator iterator(selectedChars, contentOffset, contentLength,
                              aProvider, mTextRun);
   gfxFloat xOffset, hyphenWidth;
   PRUint32 offset, length;
   PRInt32 app = aTextPaintStyle.PresContext()->AppUnitsPerDevPixel();
   // XXX aTextBaselinePt is in AppUnits, shouldn't it be nsFloatPoint?
   gfxPoint pt(0.0, (aTextBaselinePt.y - mAscent) / app);
@@ -4373,17 +4381,17 @@ nsTextFrame::SetSelected(nsPresContext* 
       found = PR_TRUE;
     }
   }
   else {
     // null range means the whole thing
     found = PR_TRUE;
   }
 
- nsFrameState oldState = mState;
+  nsFrameState oldState = mState;
   if ( aSelected )
     AddStateBits(NS_FRAME_SELECTED_CONTENT);
   else
   { //we need to see if any other selection is available.
     SelectionDetails *details = GetSelectionDetails();
     if (!details) {
       RemoveStateBits(NS_FRAME_SELECTED_CONTENT);
     } else {
--- a/layout/generic/nsViewportFrame.cpp
+++ b/layout/generic/nsViewportFrame.cpp
@@ -257,17 +257,17 @@ ViewportFrame::Reflow(nsPresContext*    
 
   // Because |Reflow| sets mComputedHeight on the child to
   // availableHeight.
   AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
   
   // Reflow the main content first so that the placeholders of the
   // fixed-position frames will be in the right places on an initial
   // reflow.
-  nsRect kidRect(0,0,aReflowState.availableWidth,aReflowState.availableHeight);
+  nscoord kidHeight = 0;
 
   nsresult rv = NS_OK;
   
   if (mFrames.NotEmpty()) {
     // Deal with a non-incremental reflow or an incremental reflow
     // targeted at our one-and-only principal child frame.
     if (aReflowState.ShouldReflowAllKids() ||
         aReflowState.mFlags.mVResize ||
@@ -279,33 +279,34 @@ ViewportFrame::Reflow(nsPresContext*    
                                          aReflowState.availableHeight);
       nsHTMLReflowState   kidReflowState(aPresContext, aReflowState,
                                          kidFrame, availableSpace);
 
       // Reflow the frame
       kidReflowState.SetComputedHeight(aReflowState.availableHeight);
       rv = ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState,
                        0, 0, 0, aStatus);
-      kidRect.width = kidDesiredSize.width;
-      kidRect.height = kidDesiredSize.height;
+      kidHeight = kidDesiredSize.height;
 
       FinishReflowChild(kidFrame, aPresContext, nsnull, kidDesiredSize, 0, 0, 0);
+    } else {
+      kidHeight = mFrames.FirstChild()->GetSize().height;
     }
   }
 
   NS_ASSERTION(aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE,
                "shouldn't happen anymore");
 
   // Return the max size as our desired size
   aDesiredSize.width = aReflowState.availableWidth;
   // Being flowed initially at an unconstrained height means we should
   // return our child's intrinsic size.
   aDesiredSize.height = aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE
                           ? aReflowState.availableHeight
-                          : kidRect.height;
+                          : kidHeight;
 
   // Make a copy of the reflow state and change the computed width and height
   // to reflect the available space for the fixed items
   nsHTMLReflowState reflowState(aReflowState);
   nsPoint offset = AdjustReflowStateForScrollbars(&reflowState);
   
 #ifdef DEBUG
   nsIFrame* f;
--- a/layout/mathml/base/src/nsMathMLChar.cpp
+++ b/layout/mathml/base/src/nsMathMLChar.cpp
@@ -204,26 +204,16 @@ LoadProperties(const nsString& aName,
   uriStr.AssignLiteral("resource://gre/res/fonts/mathfont");
   uriStr.Append(aName);
   uriStr.StripWhitespace(); // that may come from aName
   uriStr.AppendLiteral(".properties");
   return NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(aProperties), 
                                                 NS_ConvertUTF16toUTF8(uriStr));
 }
 
-// helper to get the stretchy direction of a char
-static nsStretchDirection
-GetStretchyDirection(PRUnichar aChar)
-{
-  PRInt32 k = nsMathMLOperators::FindStretchyOperator(aChar);
-  return (k == kNotFound)
-    ? NS_STRETCH_DIRECTION_UNSUPPORTED
-    : nsMathMLOperators::GetStretchyDirectionAt(k);
-}
-
 // -----------------------------------------------------------------------------------
 
 class nsGlyphTable {
 public:
   explicit nsGlyphTable(const nsString& aPrimaryFontName)
     : mType(NS_TABLE_TYPE_UNICODE),
       mFontName(1), // ensure space for primary font name.
       mState(NS_TABLE_STATE_EMPTY),
@@ -246,25 +236,22 @@ public:
   const nsAString& FontNameFor(const nsGlyphCode& aGlyphCode) const
   {
     return *mFontName.StringAt(aGlyphCode.font);
   }
 
   // True if this table contains some glyphs (variants and/or parts)
   // or contains child chars that can be used to render this char
   PRBool Has(nsPresContext* aPresContext, nsMathMLChar* aChar);
-  PRBool Has(nsPresContext* aPresContext, PRUnichar aChar);
 
   // True if this table contains variants of larger sizes to render this char
   PRBool HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
-  PRBool HasVariantsOf(nsPresContext* aPresContext, PRUnichar aChar);
 
   // True if this table contains parts (or composite parts) to render this char
   PRBool HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
-  PRBool HasPartsOf(nsPresContext* aPresContext, PRUnichar aChar);
 
   // True if aChar is to be assembled from other child chars in this table
   PRBool IsComposite(nsPresContext* aPresContext, nsMathMLChar* aChar);
 
   // The number of child chars to assemble in order to render aChar
   PRInt32 ChildCountOf(nsPresContext* aPresContext, nsMathMLChar* aChar);
 
   // Getters for the parts
@@ -497,65 +484,32 @@ nsGlyphTable::ChildCountOf(nsPresContext
 
 PRBool
 nsGlyphTable::Has(nsPresContext* aPresContext, nsMathMLChar* aChar)
 {
   return HasVariantsOf(aPresContext, aChar) || HasPartsOf(aPresContext, aChar);
 }
 
 PRBool
-nsGlyphTable::Has(nsPresContext* aPresContext, PRUnichar aChar)
-{
-  nsMathMLChar tmp;
-  tmp.mData = aChar;
-  tmp.mDirection = GetStretchyDirection(aChar);
-  return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
-    ? PR_FALSE
-    : Has(aPresContext, &tmp);
-}
-
-PRBool
 nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
 {
   //XXXkt all variants must be in the same file as size 1
   return BigOf(aPresContext, aChar, 1).Exists();
 }
 
 PRBool
-nsGlyphTable::HasVariantsOf(nsPresContext* aPresContext, PRUnichar aChar)
-{
-  nsMathMLChar tmp;
-  tmp.mData = aChar;
-  tmp.mDirection = GetStretchyDirection(aChar);
-  return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
-    ? PR_FALSE
-    : HasVariantsOf(aPresContext, &tmp);
-}
-
-PRBool
 nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, nsMathMLChar* aChar)
 {
   return GlueOf(aPresContext, aChar).Exists() ||
     TopOf(aPresContext, aChar).Exists() ||
     BottomOf(aPresContext, aChar).Exists() ||
     MiddleOf(aPresContext, aChar).Exists() ||
     IsComposite(aPresContext, aChar);
 }
 
-PRBool
-nsGlyphTable::HasPartsOf(nsPresContext* aPresContext, PRUnichar aChar)
-{
-  nsMathMLChar tmp;
-  tmp.mData = aChar;
-  tmp.mDirection = GetStretchyDirection(aChar);
-  return (tmp.mDirection == NS_STRETCH_DIRECTION_UNSUPPORTED)
-    ? PR_FALSE
-    : HasPartsOf(aPresContext, &tmp);
-}
-
 // -----------------------------------------------------------------------------------
 // This is the list of all the applicable glyph tables.
 // We will maintain a single global instance that will only reveal those
 // glyph tables that are associated to fonts currently installed on the
 // user' system. The class is an XPCOM shutdown observer to allow us to
 // free its allocated data at shutdown
 
 class nsGlyphTableList : public nsIObserver
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/413027-2-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+
+<div style="float:left; border:4px dotted blue;">
+  <div>
+    <span style="display:inline-block; width:50px; height:200px; background:yellow;">
+  </div>
+</div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/413027-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+
+<div style="display:-moz-box; height:20px; border:4px dotted blue;">
+  <div>
+    <span style="display:inline-block; width:50px; height:200px; background:yellow;">
+  </div>
+</div>
+
+</body>
+</html>
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -122,56 +122,62 @@ nsCSSValue::nsCSSValue(nsCSSValue::Image
 {
   mValue.mImage = aValue;
   mValue.mImage->AddRef();
 }
 
 nsCSSValue::nsCSSValue(const nsCSSValue& aCopy)
   : mUnit(aCopy.mUnit)
 {
-  if ((eCSSUnit_String <= mUnit) && (mUnit <= eCSSUnit_Attr)) {
+  if (mUnit <= eCSSUnit_Dummy) {
+    // nothing to do, but put this important case first
+  }
+  else if (eCSSUnit_Percent <= mUnit) {
+    mValue.mFloat = aCopy.mValue.mFloat;
+  }
+  else if (eCSSUnit_String <= mUnit && mUnit <= eCSSUnit_Attr) {
     mValue.mString = aCopy.mValue.mString;
     mValue.mString->AddRef();
   }
-  else if ((eCSSUnit_Integer <= mUnit) && (mUnit <= eCSSUnit_EnumColor)) {
+  else if (eCSSUnit_Integer <= mUnit && mUnit <= eCSSUnit_EnumColor) {
     mValue.mInt = aCopy.mValue.mInt;
   }
-  else if (eCSSUnit_Color == mUnit){
+  else if (eCSSUnit_Color == mUnit) {
     mValue.mColor = aCopy.mValue.mColor;
   }
   else if (eCSSUnit_Array <= mUnit && mUnit <= eCSSUnit_Counters) {
     mValue.mArray = aCopy.mValue.mArray;
     mValue.mArray->AddRef();
   }
-  else if (eCSSUnit_URL == mUnit){
+  else if (eCSSUnit_URL == mUnit) {
     mValue.mURL = aCopy.mValue.mURL;
     mValue.mURL->AddRef();
   }
-  else if (eCSSUnit_Image == mUnit){
+  else if (eCSSUnit_Image == mUnit) {
     mValue.mImage = aCopy.mValue.mImage;
     mValue.mImage->AddRef();
   }
   else {
-    mValue.mFloat = aCopy.mValue.mFloat;
+    NS_NOTREACHED("unknown unit");
   }
 }
 
 nsCSSValue& nsCSSValue::operator=(const nsCSSValue& aCopy)
 {
   if (this != &aCopy) {
     Reset();
     new (this) nsCSSValue(aCopy);
   }
   return *this;
 }
 
 PRBool nsCSSValue::operator==(const nsCSSValue& aOther) const
 {
   if (mUnit == aOther.mUnit) {
-    if (mUnit <= eCSSUnit_System_Font) {
+    if (mUnit <= eCSSUnit_Dummy) {
       return PR_TRUE;
     }
     else if ((eCSSUnit_String <= mUnit) && (mUnit <= eCSSUnit_Attr)) {
       return (NS_strcmp(GetBufferValue(mValue.mString),
                         GetBufferValue(aOther.mValue.mString)) == 0);
     }
     else if ((eCSSUnit_Integer <= mUnit) && (mUnit <= eCSSUnit_EnumColor)) {
       return mValue.mInt == aOther.mValue.mInt;
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -133,17 +133,17 @@ public:
 
   struct Image;
   friend struct Image;
   
   // for valueless units only (null, auto, inherit, none, normal)
   explicit nsCSSValue(nsCSSUnit aUnit = eCSSUnit_Null)
     : mUnit(aUnit)
   {
-    NS_ASSERTION(aUnit <= eCSSUnit_System_Font, "not a valueless unit");
+    NS_ASSERTION(aUnit <= eCSSUnit_Dummy, "not a valueless unit");
   }
 
   nsCSSValue(PRInt32 aValue, nsCSSUnit aUnit) NS_HIDDEN;
   nsCSSValue(float aValue, nsCSSUnit aUnit) NS_HIDDEN;
   nsCSSValue(const nsString& aValue, nsCSSUnit aUnit) NS_HIDDEN;
   explicit nsCSSValue(nscolor aValue) NS_HIDDEN;
   nsCSSValue(Array* aArray, nsCSSUnit aUnit) NS_HIDDEN;
   explicit nsCSSValue(URL* aValue) NS_HIDDEN;
--- a/layout/svg/base/src/nsSVGGlyphFrame.cpp
+++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp
@@ -1354,17 +1354,17 @@ nsSVGGlyphFrame::GetTextRun(gfxContext *
 nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::nsSVGAutoGlyphHelperContext(
     nsSVGGlyphFrame *aSource,
     const nsString &aText,
     nsSVGCharacterPosition **cp)
 {
   Init(aSource, aText);
 
   nsresult rv = aSource->GetCharacterPosition(mCT, aText, cp);
-  if NS_FAILED(rv) {
+  if (NS_FAILED(rv)) {
     NS_WARNING("failed to get character position data");
   }
 }
 
 void
 nsSVGGlyphFrame::nsSVGAutoGlyphHelperContext::Init(nsSVGGlyphFrame *aSource,
                                                    const nsString &aText)
 {
--- a/layout/xul/base/src/nsXULPopupManager.cpp
+++ b/layout/xul/base/src/nsXULPopupManager.cpp
@@ -1553,17 +1553,17 @@ nsXULPopupManager::HandleKeyboardNavigat
   
     if (NS_DIRECTION_IS_INLINE(theDirection)) {
       nsMenuFrame* nextItem = (theDirection == eNavigationDirection_End) ?
                               GetNextMenuItem(mActiveMenuBar, currentMenu, PR_FALSE) : 
                               GetPreviousMenuItem(mActiveMenuBar, currentMenu, PR_FALSE);
       mActiveMenuBar->ChangeMenuItem(nextItem, PR_TRUE);
       return PR_TRUE;
     }
-    else if NS_DIRECTION_IS_BLOCK(theDirection) {
+    else if (NS_DIRECTION_IS_BLOCK(theDirection)) {
       // Open the menu and select its first item.
       if (currentMenu) {
         nsCOMPtr<nsIContent> content = currentMenu->GetContent();
         ShowMenu(content, PR_TRUE, PR_FALSE);
       }
       return PR_TRUE;
     }
   }
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -830,16 +830,20 @@ pref("intl.locale.matchOS",             
 // fallback charset list for Unicode conversion (converting from Unicode)
 // currently used for mail send only to handle symbol characters (e.g Euro, trademark, smartquotes)
 // for ISO-8859-1
 pref("intl.fallbackCharsetList.ISO-8859-1", "windows-1252");
 pref("font.language.group",                 "chrome://global/locale/intl.properties");
 
 pref("font.mathfont-family", "STIXNonUnicode, STIXSize1, STIXGeneral, Cambria Math, Standard Symbols L, DejaVu Sans");
 
+// Some CJK fonts have bad underline offset, their CJK character glyphs are overlapped (or adjoined)  to its underline.
+// These fonts are ignored the underline offset, instead of it, the underline is lowered to bottom of its em descent.
+pref("font.blacklist.underline_offset", "FangSong,Gulim,GulimChe,MingLiU,MingLiU-ExtB,MingLiU_HKSCS,MingLiU-HKSCS-ExtB,MS Gothic,MS Mincho,MS PGothic,MS PMincho,MS UI Gothic,PMingLiU,PMingLiU-ExtB,SimHei,SimSun,SimSun-ExtB,Hei,Kai,Apple LiGothic,Apple LiSung,Osaka");
+
 pref("images.dither", "auto");
 pref("security.directory",              "");
 
 pref("signed.applets.codebase_principal_support", false);
 pref("security.checkloaduri", true);
 pref("security.xpconnect.plugin.unrestricted", true);
 // security-sensitive dialogs should delay button enabling. In milliseconds.
 pref("security.dialog_enable_delay", 2000);
@@ -2385,11 +2389,11 @@ pref("signon.expireMasterPassword",     
 pref("signon.SignonFileName",               "signons.txt"); // obsolete 
 pref("signon.SignonFileName2",              "signons2.txt"); // obsolete
 pref("signon.SignonFileName3",              "signons3.txt");
 pref("signon.autofillForms",                true); 
 pref("signon.debug",                        false); // logs to Error Console
 
 // Zoom prefs
 pref("browser.zoom.full", false);
-pref("zoom.minPercent", 50);
+pref("zoom.minPercent", 30);
 pref("zoom.maxPercent", 300);
-pref("toolkit.zoomManager.zoomValues", ".5,.75,1,1.25,1.5,2,3");
+pref("toolkit.zoomManager.zoomValues", ".3,.5,.67,.8,.9,1,1.1,1.2,1.33,1.5,1.7,2,2.4,3");
--- a/modules/plugin/base/src/nsPluginHostImpl.cpp
+++ b/modules/plugin/base/src/nsPluginHostImpl.cpp
@@ -988,17 +988,17 @@ nsresult nsPluginTag::EnsureMembersAreUT
   // XXX should we add kPlatformCharsetSel_PluginResource?
   rv = pcs->GetCharset(kPlatformCharsetSel_PlainTextInFile, charset);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!charset.LowerCaseEqualsLiteral("utf-8")) {
     rv = ccm->GetUnicodeDecoderRaw(charset.get(), getter_AddRefs(decoder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     ConvertToUTF8(decoder, mDescription);
-    for (PRInt32 i = 0; i < mMimeDescriptionArray.Length(); ++i) {
+    for (PRUint32 i = 0; i < mMimeDescriptionArray.Length(); ++i) {
       ConvertToUTF8(decoder, mMimeDescriptionArray[i]);
     }
   }
   return NS_OK;
 }
 
 void nsPluginTag::SetHost(nsPluginHostImpl * aHost)
 {
@@ -5864,17 +5864,17 @@ nsPluginHostImpl::ReadPluginInfo()
     tag->Mark(tagflag | NS_PLUGIN_FLAG_FROMCACHE);
     if (tag->mIsJavaPlugin) {
       if (mJavaEnabled)
         tag->Mark(NS_PLUGIN_FLAG_ENABLED);
       else
         tag->UnMark(NS_PLUGIN_FLAG_ENABLED);
     }
     PR_LOG(nsPluginLogging::gPluginLog, PLUGIN_LOG_BASIC,
-      ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName));
+      ("LoadCachedPluginsInfo : Loading Cached plugininfo for %s\n", tag->mFileName.get()));
     tag->mNext = mCachedPlugins;
     mCachedPlugins = tag;
 
   }
   return NS_OK;
 }
 
 void
--- a/testing/release/updates/moz18-firefox-linux.cfg
+++ b/testing/release/updates/moz18-firefox-linux.cfg
@@ -1,10 +1,12 @@
+# 2.0.0.12 linux
+release="2.0.0.12" product="Firefox" platform="Linux_x86-gcc3" build_id="2008020121" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.12/linux-i686/%locale%/firefox-2.0.0.12.tar.gz" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.13-candidates/rc1/firefox-2.0.0.13.%locale%.linux-i686.tar.gz"
 # 2.0.0.11 linux
-release="2.0.0.11" product="Firefox" platform="Linux_x86-gcc3" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.11/linux-i686/%locale%/firefox-2.0.0.11.tar.gz" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.12-candidates/rc4/firefox-2.0.0.12.%locale%.linux-i686.tar.gz"
+release="2.0.0.11" product="Firefox" platform="Linux_x86-gcc3" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.10 linux
 release="2.0.0.10" product="Firefox" platform="Linux_x86-gcc3" build_id="2007111504" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.9 linux
 release="2.0.0.9" product="Firefox" platform="Linux_x86-gcc3" build_id="2007102514" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.8 linux
 release="2.0.0.8" product="Firefox" platform="Linux_x86-gcc3" build_id="2007100816" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.7 linux
 release="2.0.0.7" product="Firefox" platform="Linux_x86-gcc3" build_id="2007091417" locales="af ar bg be ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
--- a/testing/release/updates/moz18-firefox-mac.cfg
+++ b/testing/release/updates/moz18-firefox-mac.cfg
@@ -1,10 +1,12 @@
+# 2.0.0.12 macosx
+release="2.0.0.12" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2008020121" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.12/mac/%locale%/Firefox 2.0.0.12.dmg" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.13-candidates/rc1/firefox-2.0.0.13.%locale%.mac.dmg"
 # 2.0.0.11 macosx
-release="2.0.0.11" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.11/mac/%locale%/Firefox 2.0.0.11.dmg" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.12-candidates/rc4/firefox-2.0.0.12.%locale%.mac.dmg"
+release="2.0.0.11" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.10 macosx
 release="2.0.0.10" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007111504" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.9 macosx
 release="2.0.0.9" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007102514" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.8 macosx
 release="2.0.0.8" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007100816" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.7 mac
 release="2.0.0.7" product="Firefox" platform="Darwin_Universal-gcc3" build_id="2007091417" locales="af ar bg be ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE he hu it ja-JP-mac ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
--- a/testing/release/updates/moz18-firefox-win32.cfg
+++ b/testing/release/updates/moz18-firefox-win32.cfg
@@ -1,10 +1,12 @@
+# 2.0.0.12 win32
+release="2.0.0.12" product="Firefox" platform="WINNT_x86-msvc" build_id="2008020121" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.12/win32/%locale%/Firefox Setup 2.0.0.12.exe" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.13-candidates/rc1/firefox-2.0.0.13.%locale%.win32.installer.exe"
 # 2.0.0.11 win32
-release="2.0.0.11" product="Firefox" platform="WINNT_x86-msvc" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" from="/firefox/releases/2.0.0.11/win32/%locale%/Firefox Setup 2.0.0.11.exe" aus_server="https://aus2.mozilla.org" ftp_server="stage.mozilla.org/pub/mozilla.org" to="/firefox/nightly/2.0.0.12-candidates/rc4/firefox-2.0.0.12.%locale%.win32.installer.exe"
+release="2.0.0.11" product="Firefox" platform="WINNT_x86-msvc" build_id="2007112718" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.10 win32
 release="2.0.0.10" product="Firefox" platform="WINNT_x86-msvc" build_id="2007111504" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr uk zh-CN zh-TW" channel="betatest" 
 # 2.0.0.9 win32
 release="2.0.0.9" product="Firefox" platform="WINNT_x86-msvc" build_id="2007102514" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.8 win32
 release="2.0.0.8" product="Firefox" platform="WINNT_x86-msvc" build_id="2007100816" locales="af ar be bg ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ro ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
 # 2.0.0.7 win32
 release="2.0.0.7" product="Firefox" platform="WINNT_x86-msvc" build_id="2007091417" locales="af ar bg be ca cs da de el en-GB en-US es-AR es-ES eu fi fr fy-NL ga-IE gu-IN he hu it ja ka ko ku lt mk mn nb-NO nl nn-NO pa-IN pl pt-BR pt-PT ru sk sl sv-SE tr zh-CN zh-TW" channel="betatest" 
--- a/toolkit/components/downloads/src/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/src/nsDownloadManager.cpp
@@ -1118,17 +1118,17 @@ nsDownloadManager::GetDefaultDownloadsDi
 
   // OSX:
   // Safari download folder or Desktop/Downloads
   // Vista:
   // Downloads
   // XP/2K:
   // Desktop/Downloads
   // Linux:
-  // Home/Downloads
+  // XDG user dir spec, with a fallback to Home/Downloads
 
   nsXPIDLString folderName;
   mBundle->GetStringFromName(NS_LITERAL_STRING("downloadsFolder").get(),
                              getter_Copies(folderName));
 
 #if defined (XP_MACOSX)
   nsCOMPtr<nsILocalFile> desktopDir;
   rv = dirService->Get(NS_OSX_DEFAULT_DOWNLOAD_DIR,
@@ -1162,16 +1162,29 @@ nsDownloadManager::GetDefaultDownloadsDi
 
   PRInt32 version;
   NS_NAMED_LITERAL_STRING(osVersion, "version");
   rv = infoService->GetPropertyAsInt32(osVersion, &version);
   if (version < 6) { // XP/2K
     rv = downloadDir->Append(folderName);
     NS_ENSURE_SUCCESS(rv, rv);
   }
+#elif defined(XP_UNIX)
+  rv = dirService->Get(NS_UNIX_DEFAULT_DOWNLOAD_DIR,
+                       NS_GET_IID(nsILocalFile),
+                       getter_AddRefs(downloadDir));
+  // fallback to Home/Downloads
+  if (NS_FAILED(rv)) {
+    rv = dirService->Get(NS_UNIX_HOME_DIR,
+                         NS_GET_IID(nsILocalFile),
+                         getter_AddRefs(downloadDir));
+    NS_ENSURE_SUCCESS(rv, rv);
+    rv = downloadDir->Append(folderName);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
 #else
   rv = dirService->Get(NS_OS_HOME_DIR,
                        NS_GET_IID(nsILocalFile),
                        getter_AddRefs(downloadDir));
   NS_ENSURE_SUCCESS(rv, rv);
   rv = downloadDir->Append(folderName);
   NS_ENSURE_SUCCESS(rv, rv);
 #endif
--- a/toolkit/components/places/public/nsINavHistoryService.idl
+++ b/toolkit/components/places/public/nsINavHistoryService.idl
@@ -1040,17 +1040,17 @@ interface nsINavHistoryQueryOptions : ns
   attribute unsigned short queryType;
 
   /**
    * Creates a new options item with the same parameters of this one.
    */
   nsINavHistoryQueryOptions clone();
 };
 
-[scriptable, uuid(a9bea2db-0a3e-4895-9ccc-24bac41a0674)]
+[scriptable, uuid(1972ac40-d270-40ee-995a-c97d02849596)]
 interface nsINavHistoryService : nsISupports
 {
   /**
    * This transition type means the user followed a link and got a new toplevel
    * window.
    */
   const unsigned long TRANSITION_LINK = 1;
 
@@ -1119,35 +1119,16 @@ interface nsINavHistoryService : nsISupp
    * worry about calling this, addPageToSession/addURI will always check before
    * actually adding the page. This function is public because some components
    * may want to check if this page would go in the history (i.e. for
    * annotations).
    */
   boolean canAddURI(in nsIURI aURI);
 
   /**
-   * Call to set the full information for a given page. If the page does not
-   * exist, it will be added to the database. If it does, the existing values
-   * WILL BE OVERWRITTEN. This is an updated version of addPageWithDetails
-   * for backup/restore type operations.
-   *
-   * @param aURI        Page to add/change.
-   * @param aTitle      Title as specified by the page.
-   * @param aVisitCount Number of times this page has been visited. Setting this
-   *                    to 0 may make the page invisible in some views.
-   * @param aHidden     Whether the page is hidden. If the page has only
-   *                    TRANSITION_EMBED visits, this will be true, otherwise
-   *                    false.
-   * @param aTyped      True if this URI has ever been typed.
-   */
-  void setPageDetails(in nsIURI aURI, in AString aTitle, 
-                      in unsigned long aVisitCount, in boolean aHidden,
-                      in boolean aTyped);
-
-  /**
    * Call to manually add a visit for a specific page. This will probably not
    * be commonly used other than for backup/restore type operations. If the URI
    * does not have an entry in the history database already, one will be created
    * with no visits, no title, hidden, not typed.  Adding a visit will
    * automatically increment the visit count for the visited page and will unhide
    * it and/or mark it typed according to the transition type.
    *
    * @param aURI             Visited page
--- a/toolkit/components/places/src/nsMaybeWeakPtr.cpp
+++ b/toolkit/components/places/src/nsMaybeWeakPtr.cpp
@@ -48,21 +48,22 @@ nsMaybeWeakPtr_base::GetValueAs(const ns
     if (NS_SUCCEEDED(rv)) {
       return ref;
     }
   }
 
   nsCOMPtr<nsIWeakReference> weakRef = do_QueryInterface(mPtr);
   if (weakRef) {
     rv = weakRef->QueryReferent(iid, &ref);
-    if (NS_FAILED(rv)) {
-      ref = nsnull;
+    if (NS_SUCCEEDED(rv)) {
+      return ref;
     }
   }
-  return ref;
+
+  return nsnull;
 }
 
 /* static */ nsresult
 nsMaybeWeakPtrArray_base::AppendWeakElementBase(nsTArray_base *aArray,
                                                 nsISupports *aElement,
                                                 PRBool aOwnsWeak)
 {
   nsCOMPtr<nsISupports> ref;
--- a/toolkit/components/places/src/nsNavHistory.cpp
+++ b/toolkit/components/places/src/nsNavHistory.cpp
@@ -2337,66 +2337,16 @@ nsNavHistory::CanAddURI(nsIURI* aURI, PR
       scheme.EqualsLiteral("data")) {
     *canAdd = PR_FALSE;
     return NS_OK;
   }
   *canAdd = PR_TRUE;
   return NS_OK;
 }
 
-
-// nsNavHistory::SetPageDetails
-
-NS_IMETHODIMP
-nsNavHistory::SetPageDetails(nsIURI* aURI, const nsAString& aTitle,
-                             PRUint32 aVisitCount, PRBool aHidden,
-                             PRBool aTyped)
-{
-  // look up the page ID, creating a new one if necessary
-  PRInt64 pageID;
-  nsresult rv = GetUrlIdFor(aURI, &pageID, PR_TRUE);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<mozIStorageStatement> statement;
-  rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
-      "UPDATE moz_places "
-      "SET title = ?2, "
-          "visit_count = ?4, "
-          "hidden = ?5, "
-          "typed = ?6 "
-       "WHERE id = ?1"),
-    getter_AddRefs(statement));
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = statement->BindInt64Parameter(0, pageID);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // XXX should we be calculating / setting frecency here?
-
-  // for the titles, be careful to interpret isVoid as NULL SQL command so that
-  // we can tell the difference between "set to empty" and "unset"
-  if (aTitle.IsVoid())
-    rv = statement->BindNullParameter(1);
-  else
-    rv = statement->BindStringParameter(1, StringHead(aTitle, HISTORY_TITLE_LENGTH_MAX));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = statement->BindInt32Parameter(3, aVisitCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = statement->BindInt32Parameter(4, aHidden ? 1 : 0);
-  NS_ENSURE_SUCCESS(rv, rv);
-  rv = statement->BindInt32Parameter(5, aTyped ? 1 : 0);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = statement->Execute();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return NS_OK;
-}
-
-
 // nsNavHistory::AddVisit
 //
 //    Adds or updates a page with the given URI. The ID of the new visit will
 //    be put into aVisitID.
 //
 //    THE RETURNED NEW VISIT ID MAY BE 0 indicating that this page should not be
 //    added to the history.
 
--- a/toolkit/components/places/src/nsNavHistory.h
+++ b/toolkit/components/places/src/nsNavHistory.h
@@ -338,17 +338,17 @@ public:
   // be committed when our batch level reaches 0 again.
   PRBool mBatchHasTransaction;
 
   // better alternative to QueryStringToQueries (in nsNavHistoryQuery.cpp)
   nsresult QueryStringToQueryArray(const nsACString& aQueryString,
                                    nsCOMArray<nsNavHistoryQuery>* aQueries,
                                    nsNavHistoryQueryOptions** aOptions);
 
-  // Import-friendly version of SetPageDetails + AddVisit.
+  // Import-friendly version of AddVisit.
   // This method adds a page to history along with a single last visit.
   // It is an error to call this method if aURI might already be in history.
   // The given aVisitCount should include the given last-visit date.
   // aLastVisitDate can be -1 if there is no last visit date to record.
   //
   // NOTE: This will *replace* existing records for a given URI, creating a
   // new place id, and breaking all existing relationships with for that
   // id, eg: bookmarks, annotations, tags, etc. This is only for use by
--- a/toolkit/components/places/src/nsNavHistoryAutoComplete.cpp
+++ b/toolkit/components/places/src/nsNavHistoryAutoComplete.cpp
@@ -556,17 +556,17 @@ nsNavHistory::AutoCompleteProcessSearch(
 
       // Get the URI for the favicon
       nsCAutoString faviconSpec;
       faviconService->GetFaviconSpecForIconString(
         NS_ConvertUTF16toUTF8(entryFavicon), faviconSpec);
       NS_ConvertUTF8toUTF16 faviconURI(faviconSpec);
 
       // New item: append to our results and put it in our hash table
-      rv = mCurrentResult->AppendMatch(entryURL, title, faviconURI, style);
+      rv = mCurrentResult->AppendMatch(escapedEntryURL, title, faviconURI, style);
       NS_ENSURE_SUCCESS(rv, rv);
       mCurrentResultURLs.Put(escapedEntryURL, PR_TRUE);
 
       // Stop processing if we have enough results
       if (AutoCompleteHasEnoughResults())
         break;
     }
   }
--- a/toolkit/components/places/src/nsNavHistoryResult.cpp
+++ b/toolkit/components/places/src/nsNavHistoryResult.cpp
@@ -2523,17 +2523,17 @@ nsNavHistoryQueryResultNode::OnVisit(nsI
       NS_ASSERTION(mQueries.Count() == 1, 
           "Host updated queries can have only one object");
       nsCOMPtr<nsNavHistoryQuery> queryHost = 
           do_QueryInterface(mQueries[0], &rv);
       NS_ENSURE_SUCCESS(rv, rv);
 
       PRBool hasDomain;
       queryHost->GetHasDomain(&hasDomain);
-      if (!hasDomain || !queryHost->DomainIsHost())
+      if (!hasDomain)
         return NS_OK;
 
       nsCAutoString host;
       if (NS_FAILED(aURI->GetAsciiHost(host)))
         return NS_OK;
 
       if (!queryHost->Domain().Equals(host))
         return NS_OK;
--- a/toolkit/components/places/tests/unit/test_000_frecency.js
+++ b/toolkit/components/places/tests/unit/test_000_frecency.js
@@ -46,22 +46,29 @@ Autocomplete Frecency Tests
 
 - add a visit for each score permutation
 - search
 - test number of matches
 - test each item's location in results
 
 */
 
-var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
-              getService(Ci.nsINavHistoryService);
-var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
-            getService(Ci.nsINavBookmarksService);
-var prefs = Cc["@mozilla.org/preferences-service;1"].
-            getService(Ci.nsIPrefBranch);
+try {
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+                getService(Ci.nsINavHistoryService);
+  var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
+  var ghist = Cc["@mozilla.org/browser/global-history;2"].
+              getService(Ci.nsIGlobalHistory2);
+  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+              getService(Ci.nsINavBookmarksService);
+  var prefs = Cc["@mozilla.org/preferences-service;1"].
+              getService(Ci.nsIPrefBranch);
+} catch(ex) {
+  do_throw("Could not get services\n");
+}
 
 function add_visit(aURI, aVisitDate, aVisitType) {
   var isRedirect = aVisitType == histsvc.TRANSITION_REDIRECT_PERMANENT ||
                    aVisitType == histsvc.TRANSITION_REDIRECT_TEMPORARY;
   var placeID = histsvc.addVisit(aURI, aVisitDate, null,
                                  aVisitType, isRedirect, 0);
   do_check_true(placeID > 0);
   return placeID;
@@ -127,17 +134,18 @@ bucketPrefs.every(function(bucket) {
           bonusName + ":" + bonusValue + "/cutoff:" + cutoff +
           "/weight:" + weight + "/frecency:" + frecency);
         if (bonusName == "unvisitedBookmarkBonus") {
           matchTitle = searchTerm + "UnvisitedBookmark";
           bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, calculatedURI, bmsvc.DEFAULT_INDEX, matchTitle);
         }
         else {
           matchTitle = searchTerm + "UnvisitedTyped";
-          histsvc.setPageDetails(calculatedURI, matchTitle, 1, false, true);
+          ghist.setPageTitle(calculatedURI, matchTitle);
+          bhist.markPageAsTyped(calculatedURI);
         }
       }
     }
     else {
       // visited
       var points = Math.ceil(1 * ((bonusValue / parseFloat(100.000000)).toFixed(6) * weight) / 1);
       if (!points) {
         if (visitType == Ci.nsINavHistoryService.TRANSITION_EMBED || bonusName == "defaultVisitBonus")
--- a/toolkit/components/places/tests/unit/test_413784.js
+++ b/toolkit/components/places/tests/unit/test_413784.js
@@ -1,9 +1,8 @@
-version(180);
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -142,16 +141,16 @@ function run_test() {
   input.onSearchComplete = function() {
     do_check_eq(numSearchesStarted, 1);
     do_check_eq(controller.searchStatus,
                 Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
 
     // test that we found the entry we added
     do_check_eq(controller.matchCount, 1);
 
-    // Make sure the url is decoded
-    do_check_eq(controller.getValueAt(0), decoded);
+    // Make sure the url is the same according to spec, so it can be deleted
+    do_check_eq(controller.getValueAt(0), url.spec);
 
     do_test_finished();
   };
 
   controller.startSearch(searchTerm);
 }
--- a/toolkit/components/places/tests/unit/test_416211.js
+++ b/toolkit/components/places/tests/unit/test_416211.js
@@ -49,16 +49,17 @@ Test showing bookmark title when matchin
 - test number of matches (should be exactly one)
 - make sure the bookmark title is returned
 
 */
 
 try {
   var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
                 getService(Ci.nsINavHistoryService);
+  var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
   var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
               getService(Ci.nsINavBookmarksService);
   var tagssvc = Cc["@mozilla.org/browser/tagging-service;1"].
                 getService(Ci.nsITaggingService);
 } catch (ex) {
   do_throw("Could not get services\n");
 }
 
@@ -70,18 +71,18 @@ function add_visit(aURI, aVisitDate, aVi
   do_check_true(placeID > 0);
   return placeID;
 }
 
 // create test data
 var theTag = "superTag";
 var url = uri("http://www.foobar.com/");
 var title = "Cool Title";
-histsvc.setPageDetails(url, theTag, 1, false, true);
-add_visit(url, Date.now(), Ci.nsINavHistoryService.TRANSITION_LINK);
+bhist.addPageWithDetails(url, theTag, Date.now());
+
 tagssvc.tagURI(url, [theTag]);
 bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, url, bmsvc.DEFAULT_INDEX, title);
 
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
 }
 
 AutoCompleteInput.prototype = {
--- a/toolkit/components/places/tests/unit/test_416214.js
+++ b/toolkit/components/places/tests/unit/test_416214.js
@@ -1,9 +1,8 @@
-version(180);
 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -36,16 +35,18 @@ version(180);
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 /*
 
 Test autocomplete for non-English URLs that match the tag bug 416214
+Also test bug 417441 by making sure escaped ascii characters like "+" remain
+escaped.
 
 - add a visit for a page with a non-English URL
 - add a tag for the page
 - search for the tag
 - test number of matches (should be exactly one)
 - make sure the url is decoded
 
 */
@@ -68,17 +69,17 @@ function add_visit(aURI, aVisitDate, aVi
                                  aVisitType, isRedirect, 0);
   do_check_true(placeID > 0);
   return placeID;
 }
 
 // create test data
 var searchTerm = "ユニコード";
 var theTag = "superTag";
-var decoded = "http://www.foobar.com/" + searchTerm + "/";
+var decoded = "http://www.foobar.com/" + searchTerm + "/blocking-firefox3%2B";
 var url = uri(decoded);
 add_visit(url, Date.now(), Ci.nsINavHistoryService.TRANSITION_LINK);
 tagssvc.tagURI(url, [theTag]);
 
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
 }
 
@@ -153,16 +154,16 @@ function run_test() {
   input.onSearchComplete = function() {
     do_check_eq(numSearchesStarted, 1);
     do_check_eq(controller.searchStatus,
                 Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH);
 
     // test that we found the entry we added
     do_check_eq(controller.matchCount, 1);
 
-    // Make sure the url is decoded
-    do_check_eq(controller.getValueAt(0), decoded);
+    // Make sure the url is the one with the decoded search string
+    do_check_eq(controller.getValueAt(0), url.spec);
 
     do_test_finished();
   };
 
   controller.startSearch(theTag);
 }
--- a/toolkit/components/places/tests/unit/test_417798.js
+++ b/toolkit/components/places/tests/unit/test_417798.js
@@ -158,36 +158,38 @@ function ensure_results(aSearch, aExpect
   };
 
   print("Searching for.. " + aSearch);
   controller.startSearch(aSearch);
 }
 
 // Get history services
 try {
-  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+                getService(Ci.nsINavHistoryService);
   var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
-  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
-  var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+              getService(Ci.nsINavBookmarksService);
+  var iosvc = Cc["@mozilla.org/network/io-service;1"].
+              getService(Ci.nsIIOService);
 } catch(ex) {
   do_throw("Could not get services\n");
 }
 
 // Some date not too long ago
 let gDate = new Date(Date.now() - 1000 * 60 * 60) * 1000;
 
 function addPageBook(aURI, aTitle, aBook)
 {
   let uri = iosvc.newURI(kURIs[aURI], null, null);
   let title = kTitles[aTitle];
 
   print("Adding page/book: " + [aURI, aTitle, aBook, kURIs[aURI], title].join(", "));
   // Add the page and a visit for good measure
-  histsvc.setPageDetails(uri, title, 1, false, true);
-  histsvc.addVisit(uri, gDate, null, histsvc.TRANSITION_TYPED, false, 0);
+  bhist.addPageWithDetails(uri, title, gDate);
 
   // Add a bookmark if we need to
   if (aBook != undefined) {
     let book = kTitles[aBook];
     bmsvc.insertBookmark(bmsvc.unfiledBookmarksFolder, uri, bmsvc.DEFAULT_INDEX, book);
   }
 }
 
--- a/toolkit/components/places/tests/unit/test_adaptive.js
+++ b/toolkit/components/places/tests/unit/test_adaptive.js
@@ -136,28 +136,27 @@ function ensure_results(uris, searchTerm
     do_test_finished();
   };
 
   controller.startSearch(searchTerm);
 }
 
 // Get history service
 try {
-  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+                getService(Ci.nsINavHistoryService);
   var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
-  var obs = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+  var obs = Cc["@mozilla.org/observer-service;1"].
+            getService(Ci.nsIObserverService);
 } catch(ex) {
   do_throw("Could not get history service\n");
 } 
 
 function setCountRank(aURI, aCount, aRank, aSearch)
 {
-  // Set the visit count and date for a uri
-  histsvc.setPageDetails(aURI, aURI, aCount, false, true);
-
   // Bump up the visit count for the uri
   for (let i = 0; i < aCount; i++)
     histsvc.addVisit(aURI, d1, null, histsvc.TRANSITION_TYPED, false, 0);
 
   // Make a nsIAutoCompleteController and friends for instrumentation feedback
   let thing = {
     QueryInterface: XPCOMUtils.generateQI([Ci.nsIAutoCompleteInput,
                                            Ci.nsIAutoCompletePopup,
--- a/toolkit/components/places/tests/unit/test_frecency.js
+++ b/toolkit/components/places/tests/unit/test_frecency.js
@@ -138,28 +138,27 @@ function ensure_results(uris, searchTerm
     do_test_finished();
   };
 
   controller.startSearch(searchTerm);
 }
 
 // Get history service
 try {
-  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+                getService(Ci.nsINavHistoryService);
   var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
   var tagssvc = Cc["@mozilla.org/browser/tagging-service;1"].
                 getService(Ci.nsITaggingService);
 } catch(ex) {
   do_throw("Could not get history service\n");
 } 
 
 function setCountDate(aURI, aCount, aDate)
 {
-  // Set the visit count and date for a uri
-  histsvc.setPageDetails(aURI, aURI, aCount, false, true);
   // We need visits so that frecency can be computed over multiple visits
   for (let i = 0; i < aCount; i++)
     histsvc.addVisit(aURI, aDate, null, histsvc.TRANSITION_TYPED, false, 0);
 }
 
 var uri1 = uri("http://site.tld/1");
 var uri2 = uri("http://site.tld/2");
 
--- a/toolkit/components/places/tests/unit/test_multi_word_search.js
+++ b/toolkit/components/places/tests/unit/test_multi_word_search.js
@@ -164,20 +164,23 @@ function ensure_results(aSearch, aExpect
 
   let search = kSearches[aSearch];
   print("Searching for.. " + search);
   controller.startSearch(search);
 }
 
 // Get history services
 try {
-  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
+  var histsvc = Cc["@mozilla.org/browser/nav-history-service;1"].
+                getService(Ci.nsINavHistoryService);
   var bhist = histsvc.QueryInterface(Ci.nsIBrowserHistory);
-  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
-  var iosvc = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+  var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
+              getService(Ci.nsINavBookmarksService);
+  var iosvc = Cc["@mozilla.org/network/io-service;1"].
+              getService(Ci.nsIIOService);
 } catch(ex) {
   do_throw("Could not get services\n");
 } 
 
 // Some date not too long ago
 let gDate = new Date(Date.now() - 1000 * 60 * 60) * 1000;
 
 // Define some shared uris and titles
@@ -202,19 +205,18 @@ let kSearches = [
   
 function addPageBook(aURI, aTitle, aBoo