merge fx-team to m-c
authorTim Taubert <tim.taubert@gmx.de>
Tue, 24 Jan 2012 15:05:47 +0100
changeset 86407 70fafe57af70a9641791d0e5bf01033b9665f20e
parent 86406 5591b74cdf3101516a0178dbc56197e1828e611e (current diff)
parent 86402 294722039fc9f1ee87125e190b4b560c21e78b72 (diff)
child 86408 1982c882af0ff513c528196d6e14365ff9829bcf
child 86559 db26afebdd5102e050313f10b9e6df0bd235d20d
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone12.0a1
merge fx-team to m-c
accessible/tests/mochitest/relations/test_embeds.xul
browser/base/content/browser.js
browser/components/nsBrowserGlue.js
browser/themes/gnomestripe/newtab/strip.png
browser/themes/gnomestripe/newtab/toolbar.png
browser/themes/pinstripe/newtab/strip.png
browser/themes/pinstripe/newtab/toolbar.png
browser/themes/winstripe/newtab/strip.png
browser/themes/winstripe/newtab/toolbar.png
js/src/vm/StackSpace.h
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -611,16 +611,19 @@ nsAccessible::VisibilityState()
 
     const nsIView* view = frame->GetView();
     if (view && view->GetVisibility() == nsViewVisibility_kHide)
       return vstates;
     
   } while (accessible = accessible->Parent());
 
   nsIFrame* frame = GetFrame();
+  if (!frame)
+    return vstates;
+
   const nsCOMPtr<nsIPresShell> shell(GetPresShell());
 
   // We need to know if at least a kMinPixels around the object is visible,
   // otherwise it will be marked states::OFFSCREEN.
   const PRUint16 kMinPixels  = 12;
   const nsSize frameSize = frame->GetSize();
   const nsRectVisibility rectVisibility =
     shell->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
--- a/accessible/src/base/nsRootAccessible.cpp
+++ b/accessible/src/base/nsRootAccessible.cpp
@@ -569,42 +569,84 @@ nsRootAccessible::Shutdown()
 {
   // Called manually or by nsAccessNode::LastRelease()
   if (!mWeakShell)
     return;  // Already shutdown
 
   nsDocAccessibleWrap::Shutdown();
 }
 
+// nsRootAccessible protected member
+already_AddRefed<nsIDocShellTreeItem>
+nsRootAccessible::GetContentDocShell(nsIDocShellTreeItem *aStart)
+{
+  if (!aStart) {
+    return nsnull;
+  }
+
+  PRInt32 itemType;
+  aStart->GetItemType(&itemType);
+  if (itemType == nsIDocShellTreeItem::typeContent) {
+    nsDocAccessible *accDoc = nsAccUtils::GetDocAccessibleFor(aStart);
+
+    // Hidden documents don't have accessibles (like SeaMonkey's sidebar),
+    // they are of no interest for a11y.
+    if (!accDoc)
+      return nsnull;
+
+    // If ancestor chain of accessibles is not completely visible,
+    // don't use this one. This happens for example if it's inside
+    // a background tab (tabbed browsing)
+    nsAccessible* parent = accDoc->Parent();
+    while (parent) {
+      if (parent->State() & states::INVISIBLE)
+        return nsnull;
+
+      if (parent == this)
+        break; // Don't check past original root accessible we started with
+
+      parent = parent->Parent();
+    }
+
+    NS_ADDREF(aStart);
+    return aStart;
+  }
+  nsCOMPtr<nsIDocShellTreeNode> treeNode(do_QueryInterface(aStart));
+  if (treeNode) {
+    PRInt32 subDocuments;
+    treeNode->GetChildCount(&subDocuments);
+    for (PRInt32 count = 0; count < subDocuments; count ++) {
+      nsCOMPtr<nsIDocShellTreeItem> treeItemChild, contentTreeItem;
+      treeNode->GetChildAt(count, getter_AddRefs(treeItemChild));
+      NS_ENSURE_TRUE(treeItemChild, nsnull);
+      contentTreeItem = GetContentDocShell(treeItemChild);
+      if (contentTreeItem) {
+        NS_ADDREF(aStart = contentTreeItem);
+        return aStart;
+      }
+    }
+  }
+  return nsnull;
+}
+
 // nsIAccessible method
 Relation
 nsRootAccessible::RelationByType(PRUint32 aType)
 {
   if (!mDocument || aType != nsIAccessibleRelation::RELATION_EMBEDS)
     return nsDocAccessibleWrap::RelationByType(aType);
 
-  nsIDOMWindow* rootWindow = mDocument->GetWindow();
-  if (rootWindow) {
-    nsCOMPtr<nsIDOMWindow> contentWindow;
-    rootWindow->GetContent(getter_AddRefs(contentWindow));
-    if (contentWindow) {
-      nsCOMPtr<nsIDOMDocument> contentDOMDocument;
-      contentWindow->GetDocument(getter_AddRefs(contentDOMDocument));
-      nsCOMPtr<nsIDocument> contentDocumentNode =
-        do_QueryInterface(contentDOMDocument);
-      if (contentDocumentNode) {
-        nsDocAccessible* contentDocument =
-          GetAccService()->GetDocAccessible(contentDocumentNode);
-        if (contentDocument)
-          return Relation(contentDocument);
-      }
-    }
-  }
+  nsCOMPtr<nsIDocShellTreeItem> treeItem =
+    nsCoreUtils::GetDocShellTreeItemFor(mDocument);
+  nsCOMPtr<nsIDocShellTreeItem> contentTreeItem = GetContentDocShell(treeItem);
+  // there may be no content area, so we need a null check
+  if (!contentTreeItem)
+    return Relation();
 
-  return Relation();
+  return Relation(nsAccUtils::GetDocAccessibleFor(contentTreeItem));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Protected members
 
 void
 nsRootAccessible::HandlePopupShownEvent(nsAccessible* aAccessible)
 {
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -122,17 +122,18 @@ protected:
 #ifdef MOZ_XUL
     void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
                                         nsXULTreeAccessible* aAccessible);
     void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
                                     nsXULTreeAccessible* aAccessible);
 
     PRUint32 GetChromeFlags();
 #endif
-
+    already_AddRefed<nsIDocShellTreeItem>
+           GetContentDocShell(nsIDocShellTreeItem *aStart);
     nsRefPtr<nsCaretAccessible> mCaretAccessible;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsRootAccessible, NS_ROOTACCESSIBLE_IMPL_CID)
 
 inline nsRootAccessible*
 nsAccessible::AsRoot()
 {
--- a/accessible/src/mac/Makefile.in
+++ b/accessible/src/mac/Makefile.in
@@ -51,16 +51,17 @@ LIBXUL_LIBRARY = 1
 CMMSRCS = nsAccessNodeWrap.mm \
           nsDocAccessibleWrap.mm \
           nsRootAccessibleWrap.mm \
           nsAccessibleWrap.mm \
           mozAccessible.mm \
           mozDocAccessible.mm \
           mozActionElements.mm \
           mozTextAccessible.mm \
+          mozHTMLAccessible.mm \
           $(NULL)
           
 
 EXPORTS = \
   nsAccessNodeWrap.h \
   nsTextAccessibleWrap.h \
   nsAccessibleWrap.h \
   nsARIAGridAccessibleWrap.h \
new file mode 100644
--- /dev/null
+++ b/accessible/src/mac/mozHTMLAccessible.h
@@ -0,0 +1,45 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Original Author: Hub Figuière <hub@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 ***** */
+
+#import "mozAccessible.h"
+
+@interface mozHeadingAccessible : mozAccessible
+
+@end
new file mode 100644
--- /dev/null
+++ b/accessible/src/mac/mozHTMLAccessible.mm
@@ -0,0 +1,56 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:expandtab:shiftwidth=2:tabstop=2:
+ */
+/* ***** 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 Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Original Author: Hub Figuière <hub@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 ***** */
+
+#import "mozHTMLAccessible.h"
+
+#import "nsHyperTextAccessible.h"
+
+@implementation mozHeadingAccessible
+
+- (id)value
+{
+  if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText())
+    return nil;
+
+  PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal();
+  return [NSNumber numberWithInt:level];
+}
+
+@end
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -40,16 +40,17 @@
 #include "nsObjCExceptions.h"
 
 #import "nsRoleMap.h"
 
 #include "Role.h"
 
 #import "mozAccessible.h"
 #import "mozActionElements.h"
+#import "mozHTMLAccessible.h"
 #import "mozTextAccessible.h"
 
 using namespace mozilla::a11y;
 
 nsAccessibleWrap::
   nsAccessibleWrap(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessible(aContent, aShell), mNativeObject(nil),
   mNativeInited(false)
@@ -103,20 +104,22 @@ nsAccessibleWrap::GetNativeType ()
              [mozButtonAccessible class];
     }
     
     case roles::CHECKBUTTON:
       return [mozCheckboxAccessible class];
       
     case roles::AUTOCOMPLETE:
       return [mozComboboxAccessible class];
-      
+
+    case roles::HEADING:
+      return [mozHeadingAccessible class];
+
     case roles::ENTRY:
     case roles::STATICTEXT:
-    case roles::HEADING:
     case roles::LABEL:
     case roles::CAPTION:
     case roles::ACCEL_LABEL:
     case roles::TEXT_LEAF:
       // normal textfield (static or editable)
       return [mozTextAccessible class]; 
       
     case roles::COMBOBOX:
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -142,17 +142,17 @@ static const NSString* AXRoles [] = {
   NSAccessibilityGroupRole,                     // ROLE_FOOTER
   NSAccessibilityGroupRole,                     // ROLE_PARAGRAPH
   @"AXRuler",                                   // ROLE_RULER. 10.4+ only, so we re-define the constant.
   NSAccessibilityComboBoxRole,                  // ROLE_AUTOCOMPLETE
   NSAccessibilityTextFieldRole,                 // ROLE_EDITBAR
   NSAccessibilityTextFieldRole,                 // ROLE_ENTRY
   NSAccessibilityStaticTextRole,                // ROLE_CAPTION
   @"AXWebArea",                                 // ROLE_DOCUMENT_FRAME
-  NSAccessibilityStaticTextRole,                // ROLE_HEADING
+  @"AXHeading",                                 // ROLE_HEADING
   NSAccessibilityGroupRole,                     // ROLE_PAGE
   NSAccessibilityGroupRole,                     // ROLE_SECTION
   NSAccessibilityUnknownRole,                   // ROLE_REDUNDANT_OBJECT
   NSAccessibilityGroupRole,                     // ROLE_FORM
   NSAccessibilityUnknownRole,                   // ROLE_IME
   NSAccessibilityUnknownRole,                   // ROLE_APP_ROOT. unused on OS X
   NSAccessibilityMenuItemRole,                  // ROLE_PARENT_MENUITEM
   NSAccessibilityGroupRole,                     // ROLE_CALENDAR
--- a/accessible/tests/mochitest/name/markuprules.xml
+++ b/accessible/tests/mochitest/name/markuprules.xml
@@ -134,16 +134,28 @@
 
     <ruleset id="htmloption">
       <ruleset ref="aria"/>
       <rule attr="label" type="string"/>
       <rule fromsubtree="true"/>
       <rule attr="title" type="string"/>
     </ruleset>
 
+    <ruleset id="htmlimage">
+      <ruleset ref="aria"/>
+      <rule attr="alt" type="string"/>
+      <ruleset ref="htmlelm_end"/>
+    </ruleset>
+
+    <ruleset id="htmlimageemptyalt">
+      <ruleset ref="aria"/>
+      <ruleset ref="htmlelm_end"/>
+      <rule attr="alt" type="string"/>
+    </ruleset>
+
     <ruleset id="htmltable">
       <ruleset ref="htmlelm_start"/>
       <rule elm="caption"/>
       <rule attr="summary" type="string"/>
       <ruleset ref="htmlelm_end"/>
     </ruleset>
   </ruledfn>
 
@@ -185,16 +197,38 @@
                      aria-labelledby="l1 l2"
                      label="test4"
                      title="test5"
                      a11yname="option1">option1</html:option>
         <html:option>option2</html:option>
       </html:select>
     </markup>
 
+    <markup ref="html:img" ruleset="htmlimage">
+      <html:span id="l1" a11yname="test2">test2</html:span>
+      <html:span id="l2" a11yname="test3">test3</html:span>
+      <html:img id="img"
+                aria-label="Logo of Mozilla"
+                aria-labelledby="l1 l2"
+                alt="Mozilla logo"
+                title="This is a logo"
+                src="moz.png"/>
+    </markup>
+
+    <markup ref="html:img" ruleset="htmlimageemptyalt">
+      <html:span id="l1" a11yname="test2">test2</html:span>
+      <html:span id="l2" a11yname="test3">test3</html:span>
+      <html:img id="img"
+                 aria-label="Logo of Mozilla"
+                 aria-labelledby="l1 l2"
+                 title="This is a logo"
+                 alt=""
+                 src="moz.png"/>
+    </markup>
+
     <markup ref="html:table/html:tr/html:td" ruleset="htmlelm"
             id="markup4test">
       <html:span id="l1" a11yname="test2">test2</html:span>
       <html:span id="l2" a11yname="test3">test3</html:span>
       <html:label for="tc" a11yname="test4">test4</html:label>
       <html:table>
         <html:tr>
           <html:td id="tc"
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -43,17 +43,16 @@ VPATH		= @srcdir@
 relativesrcdir  = accessible/relations
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 # test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
 
 _TEST_FILES =\
-		test_embeds.xul \
 		test_general.html \
 		test_general.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
deleted file mode 100644
--- a/accessible/tests/mochitest/relations/test_embeds.xul
+++ /dev/null
@@ -1,152 +0,0 @@
-<?xml version="1.0"?>
-<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
-<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
-                 type="text/css"?>
-
-<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        title="Embeds relation tests">
-
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
-  <script type="application/javascript"
-          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
-
-  <script type="application/javascript"
-          src="../common.js"></script>
-  <script type="application/javascript"
-          src="../role.js"></script>
-  <script type="application/javascript"
-          src="../states.js"></script>
-  <script type="application/javascript"
-          src="../events.js"></script>
-  <script type="application/javascript"
-          src="../relations.js"></script>
-
-  <script type="application/javascript">
-  <![CDATA[
-    ////////////////////////////////////////////////////////////////////////////
-    // Helpers
-
-    function tabBrowser()
-    {
-      return gBrowserWnd.gBrowser;
-    }
-
-    function currentBrowser()
-    {
-      return tabBrowser().selectedBrowser;
-    }
-
-    function currentTabDocument()
-    {
-      return currentBrowser().contentDocument;
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Invokers
-
-    function loadURI(aURI)
-    {
-      this.invoke = function loadURI_invoke()
-      {
-        tabBrowser().loadURI(aURI);
-      }
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, currentBrowser)
-      ];
-
-      this.finalCheck = function loadURI_finalCheck()
-      {
-        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
-                     getAccessible(currentTabDocument()));
-      }
-
-      this.getID = function loadURI_getID()
-      {
-        return "load uri " + aURI;
-      }
-    }
-
-    function loadOneTab(aURI)
-    {
-      this.invoke = function loadOneTab_invoke()
-      {
-        tabBrowser().loadOneTab(aURI, null, null, null, false);
-      }
-
-      this.eventSeq = [
-        new invokerChecker(EVENT_REORDER, currentBrowser)
-      ];
-
-      this.finalCheck = function loadURI_finalCheck()
-      {
-        testRelation(gBrowserWnd.document, RELATION_EMBEDS,
-                     getAccessible(currentTabDocument()));
-      }
-
-      this.getID = function loadOneTab_getID()
-      {
-        return "load uri '" + aURI + "' in new tab";
-      }
-    }
-
-    ////////////////////////////////////////////////////////////////////////////
-    // Testing
-
-    var gBrowserWnd = null;
-    function loadBrowser()
-    {
-      gBrowserWnd = window.openDialog("chrome://browser/content/", "_blank",
-                                      "chrome,all,dialog=no", "about:");
-
-      addA11yLoadEvent(startTests, gBrowserWnd);
-    }
-
-    function startTests()
-    {
-      // Wait for tab load.
-      var browser = currentBrowser();
-      addA11yLoadEvent(doTests, browser.contentWindow);
-    }
-
-    //gA11yEventDumpToConsole = true; // debug
-
-    var gQueue = null;
-    function doTests()
-    {
-      testRelation(gBrowserWnd.document, RELATION_EMBEDS,
-                   getAccessible(currentTabDocument()));
-
-      gQueue = new eventQueue();
-
-      gQueue.push(new loadURI("about:about"));
-      gQueue.push(new loadOneTab("about:mozilla"));
-
-      gQueue.onFinish = function()
-      {
-        gBrowserWnd.close();
-      }
-      gQueue.invoke();
-    }
-
-    SimpleTest.waitForExplicitFinish();
-    addLoadEvent(loadBrowser);
-  ]]>
-  </script>
-
-  <vbox flex="1" style="overflow: auto;">
-  <body xmlns="http://www.w3.org/1999/xhtml">
-    <a target="_blank"
-       href="https://bugzilla.mozilla.org/show_bug.cgi?id=707654"
-       title="Embeds relation on root accessible can return not content document">
-      Mozilla Bug 707654
-    </a>
-    <p id="display"></p>
-    <div id="content" style="display: none">
-    </div>
-    <pre id="test">
-    </pre>
-  </body>
-  </vbox>
-</window>
--- a/accessible/tests/mochitest/relations/test_general.html
+++ b/accessible/tests/mochitest/relations/test_general.html
@@ -121,16 +121,29 @@
       testRelation("caption", RELATION_LABEL_FOR, "table");
       testRelation("table", RELATION_LABELLED_BY, "caption");
 
       // 'labelled by'/'label for' relation for html:fieldset and
       // html:legend
       testRelation("legend", RELATION_LABEL_FOR, "fieldset");
       testRelation("fieldset", RELATION_LABELLED_BY, "legend");
 
+      // 'embeds' relation for root accessible
+      var docAcc = null;
+      var parentOfDocAcc = null;
+      var parentDocAcc = getAccessible(document);
+      do {
+        docAcc = parentDocAcc;
+        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
+        parentDocAcc = getAccessible(parentOfDocAcc.document,
+                                     [nsIAccessible]);
+      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
+
+      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
+
       // finish test
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 
--- a/accessible/tests/mochitest/relations/test_general.xul
+++ b/accessible/tests/mochitest/relations/test_general.xul
@@ -98,16 +98,29 @@
       // aria-flowto, multiple relations
       testRelation("flowto1", RELATION_FLOWS_TO, ["flowfrom1", "flowfrom2"]);
       testRelation("flowfrom1", RELATION_FLOWS_FROM, "flowto1");
       testRelation("flowfrom2", RELATION_FLOWS_FROM, "flowto1");
 
       // 'default button' relation
       testRelation("textbox", RELATION_DEFAULT_BUTTON, "submit");
 
+      // 'embeds' relation for root accessible
+      var docAcc = null;
+      var parentOfDocAcc = null;
+      var parentDocAcc = getAccessible(document);
+      do {
+        docAcc = parentDocAcc;
+        parentOfDocAcc = getAccessible(docAcc.parent, [nsIAccessNode]);
+        parentDocAcc = getAccessible(parentOfDocAcc.document,
+                                     [nsIAccessible]);
+      } while (getRole(parentDocAcc) != ROLE_CHROME_WINDOW)
+
+      testRelation(parentDocAcc, RELATION_EMBEDS, docAcc);
+
       // 'labelled by'/'label for' relation for xul:goupbox and xul:label of
       // xul:caption
       var groupboxAcc = getAccessible("groupbox");
       var labelAcc = groupboxAcc.firstChild;
       testRelation(labelAcc, RELATION_LABEL_FOR, groupboxAcc);
       testRelation(groupboxAcc, RELATION_LABELLED_BY, labelAcc);
 
       // 'labelled by'/'label for' relations for xul:tab and xul:tabpanel
--- a/accessible/tests/mochitest/test_nsIAccessibleImage.html
+++ b/accessible/tests/mochitest/test_nsIAccessibleImage.html
@@ -71,25 +71,23 @@ https://bugzilla.mozilla.org/show_bug.cg
       }
 
       var width = {}, height = {};
       aAcc.getImageSize(width, height);
       is(width.value, aWidth, "Wrong width for " + aID + "!");
       is(height.value, aHeight, "wrong height for " + aID + "!");
     }
 
-    function testThis(aID, aName, aSRC, aWidth, aHeight,
+    function testThis(aID, aSRC, aWidth, aHeight,
                       aNumActions, aActionNames)
     {
       var acc = getAccessible(aID, [nsIAccessibleImage]);
       if (!acc)
         return;
 
-      is(acc.name, aName, "wrong name for " + aID + "!");
-
       // Test role
       testRole(aID, ROLE_GRAPHIC);
 
       // test coordinates and size
       testCoordinates(aID, acc, aWidth, aHeight);
 
       // bug 429659: Make sure the SRC attribute is set for any image
       var attributes = {"src": aSRC};
@@ -103,60 +101,36 @@ https://bugzilla.mozilla.org/show_bug.cg
           is(acc.getActionName(index), aActionNames[index],
              "Wrong action name for " + aID + ", index " + index +"!");
       }
     }
 
     function doTest()
     {
       // Test non-linked image
-      testThis("nonLinkedImage", null, "moz.png", 89, 38);
+      testThis("nonLinkedImage", "moz.png", 89, 38);
 
       // Test linked image
-      testThis("linkedImage", null, "moz.png", 89, 38);
-
-      // Test non-linked image with alt attribute
-      testThis("nonLinkedImageWithAlt", "MoFo", "moz.png", 89, 38);
-
-      // Test linked image with alt attribute
-      testThis("linkedImageWithAlt", "MoFo link", "moz.png", 89, 38);
-
-      // Test non-linked image with title attribute
-      testThis("nonLinkedImageWithTitle", "MoFo logo", "moz.png", 89, 38);
-
-      // Test linked image with title attribute
-      testThis("linkedImageWithTitle", "Link to MoFo", "moz.png", 89, 38);
-
-      // Test simple image with empty alt attribute
-      testThis("nonLinkedImageEmptyAlt", "", "moz.png", 89, 38);
-
-      // Test linked image with empty alt attribute
-      testThis("linkedImageEmptyAlt", "", "moz.png", 89, 38);
-
-      // Test simple image with empty alt attribute and title
-      testThis("nonLinkedImageEmptyAltAndTitle", "MozillaFoundation", "moz.png", 89, 38);
-
-      // Test linked image with empty alt attribute and title
-      testThis("linkedImageEmptyAltAndTitle", "Link to Mozilla Foundation", "moz.png", 89, 38);
+      testThis("linkedImage", "moz.png", 89, 38);
 
       // Image with long desc
       var actionNamesArray = new Array("showlongdesc");
-      testThis("longdesc", "Image of Mozilla logo", "moz.png", 89, 38, 1,
+      testThis("longdesc", "moz.png", 89, 38, 1,
                actionNamesArray);
 
       // Image with click and long desc
       actionNamesArray = null;
       actionNamesArray = new Array("click", "showlongdesc");
-      testThis("clickAndLongdesc", "Another image of Mozilla logo", "moz.png",
+      testThis("clickAndLongdesc", "moz.png",
                89, 38, 2, actionNamesArray);
       
       // Image with click
       actionNamesArray = null;
       actionNamesArray = new Array("click");
-      testThis("click", "A third image of Mozilla logo", "moz.png",
+      testThis("click", "moz.png",
                89, 38, 1, actionNamesArray);
       
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
@@ -167,33 +141,16 @@ https://bugzilla.mozilla.org/show_bug.cg
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
   <br>Simple image:<br>
   <img id="nonLinkedImage" src="moz.png"/>
   <br>Linked image:<br>
   <a href="http://www.mozilla.org"><img id="linkedImage" src="moz.png"></a>
-  <br>Simple image with alt:<br>
-  <img id="nonLinkedImageWithAlt" src="moz.png" alt="MoFo"/>
-  <br>Linked image with alt:<br>
-  <a href="http://www.mozilla.org"><img id="linkedImageWithAlt" src="moz.png" alt="MoFo link"/></a>
-  <br>Simple image with title:<br>
-  <img id="nonLinkedImageWithTitle" src="moz.png" title="MoFo logo"/>
-  <br>Linked image with title:<br>
-  <a href="http://www.mozilla.org"><img id="linkedImageWithTitle" src="moz.png" title="Link to MoFo"/></a>
-  <br>Simple image with empty alt:<br>
-  <img id="nonLinkedImageEmptyAlt" src="moz.png" alt=""/>
-  <br>Linked image with empty alt:<br>
-  <a href="http://www.mozilla.org"><img id="linkedImageEmptyAlt" src="moz.png" alt=""/></a>
-  <br>Simple image with empty alt and title:<br>
-  <img id="nonLinkedImageEmptyAltAndTitle" src="moz.png" alt="" title="MozillaFoundation"/>
-  <br>Linked image with empty alt and title:<br>
-  <a href="http://www.mozilla.org"><img id="linkedImageEmptyAltAndTitle" src="moz.png" alt=""
-     title="Link to Mozilla Foundation"/></a>
   <br>Image with longdesc:<br>
   <img id="longdesc" src="moz.png" longdesc="longdesc_src.html"
        alt="Image of Mozilla logo"/>
   <br>Image with click and longdesc:<br>
   <img id="clickAndLongdesc" src="moz.png" longdesc="longdesc_src.html"
        alt="Another image of Mozilla logo" onclick="alert('Clicked!');"/>
   <br>Image with click:<br>
   <img id="click" src="moz.png"
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -390,20 +390,18 @@
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/nsFilePicker.js
 @BINPATH@/components/nsFilePicker.manifest
 #ifdef MOZ_B2G_RIL
-@BINPATH@/components/nsTelephonyWorker.manifest
-@BINPATH@/components/nsTelephonyWorker.js
-@BINPATH@/components/Telephony.manifest
-@BINPATH@/components/Telephony.js
+@BINPATH@/components/RadioInterfaceLayer.manifest
+@BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/nsWifiWorker.js
 @BINPATH@/components/nsWifiWorker.manifest
 #endif
 #ifdef XP_MACOSX
 @BINPATH@/components/libalerts.dylib
 #endif
 #ifdef MOZ_ENABLE_DBUS
 @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -239,18 +239,17 @@ pref("general.skins.selectedSkin", "clas
 
 pref("general.smoothScroll", true);
 #ifdef UNIX_BUT_NOT_MAC
 pref("general.autoScroll", false);
 #else
 pref("general.autoScroll", true);
 #endif
 
-// Whether or not the application should check at startup each time if it 
-// is the default browser.
+// At startup, check if we're the default browser and prompt user if not.
 pref("browser.shell.checkDefaultBrowser", true);
 
 // 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
 // The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
 pref("browser.startup.page",                1);
 pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
 
 // This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
--- a/browser/base/content/aboutDialog.css
+++ b/browser/base/content/aboutDialog.css
@@ -52,17 +52,16 @@
 
 .update-throbber {
   width: 16px;
   min-height: 16px;
   -moz-margin-end: 3px;
   list-style-image: url("chrome://global/skin/icons/loading_16.png");
 }
 
-.trademark-label,
 .text-link,
 .text-link:focus {
   margin: 0px;
   padding: 0px;
 }
 
 .bottom-link,
 .bottom-link:focus {
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -72,27 +72,16 @@ function init(aEvent)
   if (/a\d+$/.test(version)) {
     let buildID = Services.appinfo.appBuildID;
     let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
     document.getElementById("version").textContent += " (" + buildDate + ")";
     document.getElementById("experimental").hidden = false;
     document.getElementById("communityDesc").hidden = true;
   }
 
-#ifdef MOZ_OFFICIAL_BRANDING
-  // Hide the Charlton trademark attribution for non-en-US/en-GB
-  // DO NOT REMOVE without consulting people involved with bug 616193
-  let chromeRegistry = Components.classes["@mozilla.org/chrome/chrome-registry;1"].
-                       getService(Components.interfaces.nsIXULChromeRegistry);
-  let currentLocale = chromeRegistry.getSelectedLocale("global");
-  if (currentLocale != "en-US" && currentLocale != "en-GB") {
-    document.getElementById("extra-trademark").hidden = true;
-  }
-#endif
-
 #ifdef MOZ_UPDATER
   gAppUpdater = new appUpdater();
 
   let defaults = Services.prefs.getDefaultBranch("");
   let channelLabel = document.getElementById("currentChannel");
   channelLabel.value = defaults.getCharPref("app.update.channel");
 #endif
 
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -141,23 +141,17 @@
       </vbox>
     </hbox>
     <vbox id="bottomBox">
       <hbox pack="center">
         <label class="text-link bottom-link" href="about:license">&bottomLinks.license;</label>
         <label class="text-link bottom-link" href="about:rights">&bottomLinks.rights;</label>
         <label class="text-link bottom-link" href="http://www.mozilla.com/legal/privacy/">&bottomLinks.privacy;</label>
       </hbox>
-      <description id="trademark">
-        <label class="trademark-label">&trademarkInfo.part1;</label>
-#ifdef MOZ_OFFICIAL_BRANDING
-        <!-- DO NOT REMOVE without consulting people involved with bug 616193 -->
-        <label id="extra-trademark" class="trademark-label">Some of the trademarks used under license from The Charlton Company.</label>
-#endif
-      </description>
+      <description id="trademark">&trademarkInfo.part1;</description>
     </vbox>
   </vbox>
   
   <keyset>
     <key keycode="VK_ESCAPE" oncommand="window.close();"/>
   </keyset>
 
 #ifdef XP_MACOSX
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1549,17 +1549,21 @@ function delayedStartup(isLoadingBlank, 
   var homeButton = document.getElementById("home-button");
   gHomeButton.updateTooltip(homeButton);
   gHomeButton.updatePersonalToolbarStyle(homeButton);
 
 #ifdef HAVE_SHELL_SERVICE
   // Perform default browser checking (after window opens).
   var shell = getShellService();
   if (shell) {
+#ifdef DEBUG
+    var shouldCheck = false;
+#else
     var shouldCheck = shell.shouldCheckDefaultBrowser;
+#endif
     var willRecoverSession = false;
     try {
       var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
                getService(Ci.nsISessionStartup);
       willRecoverSession =
         (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
     }
     catch (ex) { /* never mind; suppose SessionStore is broken */ }
@@ -5192,17 +5196,17 @@ nsBrowserAccess.prototype = {
         // Pass all params to openDialog to ensure that "url" isn't passed through
         // loadOneOrMoreURIs, which splits based on "|"
         newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
         break;
       case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
         let win, needToFocusWin;
 
         // try the current window.  if we're in a popup, fall back on the most recent browser window
-        if (!window.document.documentElement.getAttribute("chromehidden"))
+        if (window.toolbar.visible)
           win = window;
         else {
           win = Cc["@mozilla.org/browser/browserglue;1"]
                   .getService(Ci.nsIBrowserGlue)
                   .getMostRecentBrowserWindow();
           needToFocusWin = true;
         }
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -529,22 +529,22 @@
                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="gIdentityHandler.handleIdentityButtonEvent(event);"
                onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                ondragstart="gIdentityHandler.onDragStart(event);">
             <hbox id="identity-box-inner" align="center">
-              <stack id="page-proxy-stack"
-                     onclick="PageProxyClickHandler(event);">
+              <hbox id="page-proxy-stack"
+                    onclick="PageProxyClickHandler(event);">
                 <image id="page-proxy-favicon" validate="never"
                        pageproxystate="invalid"
                        onerror="this.removeAttribute('src');"/>
-              </stack>
+              </hbox>
               <hbox id="identity-icon-labels">
                 <label id="identity-icon-label" class="plain" flex="1"/>
                 <label id="identity-icon-country-label" class="plain"/>
               </hbox>
             </hbox>
           </box>
           <box id="urlbar-display-box" align="center">
             <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -3806,59 +3806,44 @@
         this.style.MozUserFocus = '';
       </handler>
     </handlers>
   </binding>
 
   <binding id="tabbrowser-alltabs-popup"
            extends="chrome://global/content/bindings/popup.xml#popup">
     <implementation implements="nsIDOMEventListener">
-      <method name="_menuItemOnCommand">
-        <parameter name="aEvent"/>
-        <body><![CDATA[
-          gBrowser.selectedTab = aEvent.target.tab;
-        ]]></body>
-      </method>
-
       <method name="_tabOnAttrModified">
         <parameter name="aEvent"/>
         <body><![CDATA[
           var tab = aEvent.target;
-          this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
+          if (tab.mCorrespondingMenuitem)
+            this._setMenuitemAttributes(tab.mCorrespondingMenuitem, tab);
         ]]></body>
       </method>
 
       <method name="_tabOnTabClose">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var menuItem = aEvent.target.mCorrespondingMenuitem;
-          if (menuItem)
-            this.removeChild(menuItem);
+          var tab = aEvent.target;
+          if (tab.mCorrespondingMenuitem)
+            this.removeChild(tab.mCorrespondingMenuitem);
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          if (!aEvent.isTrusted)
-            return;
-
           switch (aEvent.type) {
-            case "command":
-              this._menuItemOnCommand(aEvent);
-              break;
             case "TabAttrModified":
               this._tabOnAttrModified(aEvent);
               break;
             case "TabClose":
               this._tabOnTabClose(aEvent);
               break;
-            case "TabOpen":
-              this._createTabMenuItem(aEvent.originalTarget);
-              break;
             case "scroll":
               this._updateTabsVisibilityStatus();
               break;
           }
         ]]></body>
       </method>
 
       <method name="_updateTabsVisibilityStatus">
@@ -3869,18 +3854,16 @@
             return;
 
           var tabstripBO = tabContainer.mTabstrip.scrollBoxObject;
           for (var i = 0; i < this.childNodes.length; i++) {
             let curTab = this.childNodes[i].tab;
             if (!curTab) // "Tab Groups" menuitem and its menuseparator
               continue;
             let curTabBO = curTab.boxObject;
-            if (!curTabBO) // "Tabs From Other Computers" menuitem
-              continue;
             if (curTabBO.screenX >= tabstripBO.screenX &&
                 curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width)
               this.childNodes[i].setAttribute("tabIsVisible", "true");
             else
               this.childNodes[i].removeAttribute("tabIsVisible");
           }
         ]]></body>
       </method>
@@ -3891,24 +3874,20 @@
           var menuItem = document.createElementNS(
             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
             "menuitem");
 
           menuItem.setAttribute("class", "menuitem-iconic alltabs-item menuitem-with-favicon");
 
           this._setMenuitemAttributes(menuItem, aTab);
 
-          // Keep some attributes of the menuitem in sync with its
-          // corresponding tab (e.g. the tab label)
           aTab.mCorrespondingMenuitem = menuItem;
           menuItem.tab = aTab;
-          menuItem.addEventListener("command", this, false);
 
           this.appendChild(menuItem);
-          return menuItem;
         ]]></body>
       </method>
 
       <method name="_setMenuitemAttributes">
         <parameter name="aMenuitem"/>
         <parameter name="aTab"/>
         <body><![CDATA[
           aMenuitem.setAttribute("label", aTab.label);
@@ -3933,46 +3912,43 @@
             aMenuitem.removeAttribute("selected");
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="popupshowing">
       <![CDATA[
-        // set up the menu popup
         var tabcontainer = gBrowser.tabContainer;
-        let tabs = gBrowser.visibleTabs;
 
         // Listen for changes in the tab bar.
-        tabcontainer.addEventListener("TabOpen", this, false);
         tabcontainer.addEventListener("TabAttrModified", this, false);
         tabcontainer.addEventListener("TabClose", this, false);
         tabcontainer.mTabstrip.addEventListener("scroll", this, false);
 
+        let tabs = gBrowser.visibleTabs;
         for (var i = 0; i < tabs.length; i++) {
-          this._createTabMenuItem(tabs[i]);
+          if (!tabs[i].pinned)
+            this._createTabMenuItem(tabs[i]);
         }
         this._updateTabsVisibilityStatus();
       ]]></handler>
 
       <handler event="popuphidden">
       <![CDATA[
         // clear out the menu popup and remove the listeners
         for (let i = this.childNodes.length - 1; i > 0; i--) {
           let menuItem = this.childNodes[i];
           if (menuItem.tab) {
-            menuItem.removeEventListener("command", this, false);
             menuItem.tab.mCorrespondingMenuitem = null;
             this.removeChild(menuItem);
           }
         }
         var tabcontainer = gBrowser.tabContainer;
         tabcontainer.mTabstrip.removeEventListener("scroll", this, false);
-        tabcontainer.removeEventListener("TabOpen", this, false);
         tabcontainer.removeEventListener("TabAttrModified", this, false);
         tabcontainer.removeEventListener("TabClose", this, false);
       ]]></handler>
 
       <handler event="DOMMenuItemActive">
       <![CDATA[
         var tab = event.target.tab;
         if (tab) {
@@ -3983,16 +3959,21 @@
         }
       ]]></handler>
 
       <handler event="DOMMenuItemInactive">
       <![CDATA[
         XULBrowserWindow.setOverLink("", null);
       ]]></handler>
 
+      <handler event="command"><![CDATA[
+        if (event.target.tab)
+          gBrowser.selectedTab = event.target.tab;
+      ]]></handler>
+
     </handlers>
   </binding>
 
   <binding id="statuspanel" display="xul:hbox">
     <content>
       <xul:hbox class="statuspanel-inner">
         <xul:label class="statuspanel-label"
                    role="status"
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -51,17 +51,17 @@ function getBrowserURL()
   return "chrome://browser/content/browser.xul";
 }
 
 function getTopWin(skipPopups) {
   // If this is called in a browser window, use that window regardless of
   // whether it's the frontmost window, since commands can be executed in
   // background windows (bug 626148).
   if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" &&
-      (!skipPopups || !top.document.documentElement.getAttribute("chromehidden")))
+      (!skipPopups || top.toolbar.visible))
     return top;
 
   if (skipPopups) {
     return Components.classes["@mozilla.org/browser/browserglue;1"]
                      .getService(Components.interfaces.nsIBrowserGlue)
                      .getMostRecentBrowserWindow();
   }
   return Services.wm.getMostRecentWindow("navigator:browser");
@@ -201,17 +201,17 @@ function openLinkIn(url, where, params) 
     saveURL(url, null, null, true, null, aReferrerURI);
     return;
   }
   const Cc = Components.classes;
   const Ci = Components.interfaces;
 
   var w = getTopWin();
   if ((where == "tab" || where == "tabshifted") &&
-      w && w.document.documentElement.getAttribute("chromehidden")) {
+      w && !w.toolbar.visible) {
     w = getTopWin(true);
     aRelatedToCurrent = false;
   }
 
   if (!w || where == "window") {
     var sa = Cc["@mozilla.org/supports-array;1"].
              createInstance(Ci.nsISupportsArray);
 
index 85eb545601f66f0aada8d7d02144ae7627ef53aa..c09d6c1344f7b281220ca2e9c3a55b15e97c8472
GIT binary patch
literal 15364
zc%1E-&2Jk;6u{p&ZfqxRviWLBQ_4b=CWzD@P0}C>DUK6pB3dQFX_}}B>Du1d+t|C-
z?mB4+0i#Mt91sVPP?5M4i4#Z&^}+!aR1VFFONHox@)r<=0}=-$^JaIP*p5kPL8J=v
zlV;!S%$v9K-h6n+3jidNO9TN#0C0mX1us;^&C(7WOg3O5*dDYPHG?<d>XtYh4u`|x
z>^sC3?)v@h28Y9W459mj$Yhwwye$c<cQENJX(KQxG8twvZ%e}T985ZybTcV1DKZ&m
zGH*+^X%Tk#AneJ6oluCtq{w9Wp<L#0{#V#%cX2$e#bfER^ZSRG+=W6cY-#u+FP|M{
z8s5pc8Ns!G3!re@t{oqlo``8$n%K9o6(h@(zldeiieUx<KM+Ta^H8nJjd<rSDr!Po
ziD)@BVO)x)w3VomkYkcQZ{<zN%o!0$w`wLOGZka`DSQUz=a}|IC1Iu(g!a|dzB8wL
z#b96{u+}TC_6G-g#lGO++8TYB@U6S;8Q%+IQ_|WSZ|2{!{Vwh!aJV8M*KH3LSCqh3
zKo-aq@^+0!iL1*@%hFg*87WQ2n{K;Ai6=GIhVDjAmaLlPVvUITe#onuD#tW4V_7R|
z0wjQJwO|o+zFUmo&q8A{hMu~5@8O0<p{cp0we85!C)$s79B)6dAhg7!_)=1*VI9%Z
znjT5#<QXNdsZr%Mc|I-0<n)9Z(M(gz%$V|3lNK)s%@wU)krS_O7foHuqE2gt&RJcS
z)k{fTNlYv*8nQW4lAoQ!z^sg$a%Nmz)X@I2BCkwjO+{0UX<0W=&Puv2sYy9<t<3az
zMoP-VX+<&?ghtB<_I*;9sR8Q&(X13#)MV$-racgy;mdZ$(;4nzMa4S7v8`4d-mQ#V
zfGIi%6HSiq=sjNXC#7~cr)#=Yb*ZfX@}MyG92^30bvJBx*_Tz5RrPnrb$Wf{tNG$x
z0l~TGs8Fi+Z9j)jM4VRZ8%Kw*rpeC#`Z2KIP**qh|7pAXp^|2y%APjB2~07NAP!5A
z1RXTUfr?Yl2U(O!XvF$j7{*dsr=w;wbofhb`YUa=K%;+JOXo7O;V(w8m87T(ctx+g
z=jpJSnvoL6Mk}1Wd;iKB#ubO;+y`}bYhNPsC^1Do`Teu?jXOV;$JHZN27gpGmP{?n
zVtV)f&9Ap(`p1)2Oue)}nBD<+zYBdhRq8_Mf`RimWSDX`C95H3E>zn41~>wBWl{8D
zj5AhzkD>MR)(9vVaTDteNZ}jL7>r`9Ma#IK8TT{e{u1LYK>Pr0*sBKWA5HGg@GSGr
z4bQ?5+M0$1w0#{uf=}T~xDEH<0sKZ>q=g(OUF0P3ljli@jF5BW0+}W=WZv23JZYP7
z6t~Bx$^+;0LvedD?adFoc1c!=oA!o9e;2Ilp6lt>_y6vCdhb;ExF4|fG(%m$D|+RM
znpl{WtHyP+BWkTD$35{MuP4Wk`Spz(pO?pVul2OPxpoJ3t|#mdrh0(abLc}4b7AkL
zwA)G&zy(oj1<~>($d9^y=<GndRmSnZf0+pe27|rgz`z;$GBY?B=oS0>@MXq>2;V&H
z%S>hDB%GDm^7lQU@+Kw^i$oK=ggcXt(OkrKSO12EIJHnH*z<$K;cz&QH8r*n>Hq&P
z{`L2N4u^AqA<oh0Xau%C2=0I$qeM)Xiw$7+4*?GQ53n9q7J%(`VJ7po+^3HL{QS?)
b|NQ*V;cz${4u`|xa5x+ehr{7;4k&*D(Qxb<
index ea1c05db0dc9a467a75998c7673ce327a8b01971..360fd08066b0a0f695363cabafc776db28210f48
GIT binary patch
literal 15364
zc%1E7-%le&9RF@vY_UC9ehD0xbA{k>9%+lfku(}=4?T$9ffhN)amQ`jrQKt@+w5-X
zAyy&A2NU1U_~wK72dIhhMU5I?z{L1sjQ8M=H=oX&F}`W)cXwuKDHsmCh~CX7nSN$>
zX1+V$&&+prrV9XEET8HJ5COmkgoOa?3Li~7;Gx`riNp4wCu}o#F|xZQ1_lNO1_p*h
z2S>=~KiqCGFfjZFVXqG&<tXJ<!W=F4Q06Pz2$V(2QOc`?Ia<#{nWyZdEKn9HM=7ro
zri&Iw4<8(TGDlA+oIqKm9Nm}83=H=a$k|;SSM_8<-FAL|6O*r0N<^I+!PwJhM`?|~
zWYUUZ?0*bU`jnK8pP8OW=(@_0%S6G<(aP^7a;jol;qbSdr-rY+&+9`3<}N5&N-xCp
zyp}RA#xr^$uB7CIWL$FcmSpA4m}EF5laiH5(EJoW1M_pV_5~$nW#)yBm6d_hkM@cE
z;h}J`Ph1)7AL<ha`iF}}`(eW8zSgebV`EcN@uinn*NETlIs!+k1bKycuuxFKTY}7U
zSGiYf>Xf9mL~A)U%z~<GX}o&Ny|a`tGrEBn50g7eGObf9_WH!r<diJAB_ierVO7&L
zIiXuw$G%$=AO+l33#uu9-PQC*p}|$P?tXvZNPUCQ*woz8`aoNI$I;GX9mnT|=7f}7
zOdGb)XLMCJVrpKVQIfhAS6-0kg_d&hcvecwPpFDy&RFs_i<CE2HGfV{T`u36TFz~^
z;nX>6$g*}ZZ78XUg#}Z#W-9WtbEr^Z+>*27+JcVumlU}$k+T$CGpA+4M4pulL(<Z6
zZ26KZC1iC%i|Ll7XXk|mr!Dq<(va<Tj@@`pN-A2qd*l`wn(pWol9_1qd}%ZO;>*H&
zUP)&xb$PcS*u42eY3vl#VyzSPWoLppXLR=qZ|~;KpWb(`N(h`~o~lazVEH-r6vOGU
z`AeG}ca0?9{wvV8qxS^%zl(N1Qqe9_)zx}9jwt~WBw-QKV1N#J&~OR`A%~pCXcxc>
z=Rv`I1}sov8JeIoRADq!?SC@}p=n*sXJs?wYS_Z5P86uNvVn8<09d-25ywWW+}yZv
zwTQ~%^nd0JKW!aMWe*}N!1}_oI8uM6<Rv4OG8c)mbz-&rqq4bZ={c(E#*Ot)%Br?K
z?x-5D`-AB<fa^WzL$K0?$nz#n#E5Chxs0qusJTdG@9Uu*{8c&zQRl3q@lmvY&KU&-
zRkv^yOspls7>uIY4>{&T)O?7V4{Z~t8MqKYEB373_R&t=(O%-;)<hRP<vH&<zm@R`
zFlDbOb-wQCta}em!U*<a9(z-S*Wn#_7e0aw_#S?S-#IVW%pK!;YJRKvgRkYiyuf$x
zU3@p+!#~2G;`@jRkGpI>xLtI92;4==B1<3y$eo;$w8*k$zb~BMJ)`l1ukUO$ez@jd
z-KEiZ*>(bdZDr$@Zs0<`tFpH`<4!bsz7hX%G<v>U!;$)Gf6=&ktM~=%j7IDaCO^Q7
zdGz5Vb>TJwagRu}^^H3bXy7=cK|Xl=*|P*K?=qhM{${5?Jlx+W4h@~QH#@__;XZM2
z05>~z2;sB+Zg!gBDOAruZ5Gg@Ydf@*aJJ^Q?+@V$rngHaGUkplx6?|(v~JIdQYjfd
z(41pnU|?Xl6R061+W-GQ`S;)d85kJuVsQLud^84I9|U*6J}!wswtWD5e+cl9zq@+r
zy)cy9MJcZmKBSKU?EKHp|CP80&;JLIl7WGNfq{X6fq{X6fq{X6fq{YHe}caNn2h^T
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1437,17 +1437,17 @@ BrowserGlue.prototype = {
 #ifndef XP_WIN
 #define BROKEN_WM_Z_ORDER
 #endif
 
   // this returns the most recent non-popup browser window
   getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() {
     function isFullBrowserWindow(win) {
       return !win.closed &&
-             !win.document.documentElement.getAttribute("chromehidden");
+             win.toolbar.visible;
     }
 
 #ifdef BROKEN_WM_Z_ORDER
     var win = Services.wm.getMostRecentWindow("navigator:browser");
 
     // if we're lucky, this isn't a popup, and we can just return this
     if (win && !isFullBrowserWindow(win)) {
       win = null;
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -364,20 +364,18 @@
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 #ifdef MOZ_B2G_RIL
-@BINPATH@/components/nsTelephonyWorker.manifest
-@BINPATH@/components/nsTelephonyWorker.js
-@BINPATH@/components/Telephony.manifest
-@BINPATH@/components/Telephony.js
+@BINPATH@/components/RadioInterfaceLayer.manifest
+@BINPATH@/components/RadioInterfaceLayer.js
 @BINPATH@/components/nsWifiWorker.js
 @BINPATH@/components/nsWifiWorker.manifest
 #endif
 @BINPATH@/components/BrowserProfileMigrators.manifest
 @BINPATH@/components/ChromeProfileMigrator.js
 @BINPATH@/components/FirefoxProfileMigrator.js
 #ifdef XP_MACOSX
 @BINPATH@/components/libalerts.dylib
deleted file mode 100644
index 2527df6e72b5c80b91e180af9644ba96a5c148d6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 389f689aa2a7611f53977594c048f79c4991affd..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2527df6e72b5c80b91e180af9644ba96a5c148d6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 75d6f13d8c6e6a1cfc0d5766cfec91378e0b60c0..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2527df6e72b5c80b91e180af9644ba96a5c148d6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 33bb4a320e76cc7a346ca4beb60e89a85a48aa2c..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/configure.in
+++ b/configure.in
@@ -1819,19 +1819,33 @@ if test "$OS_TARGET" = "Android"; then
        AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
     fi
     CPPFLAGS="$CPPFLAGS $STLPORT_CPPFLAGS"
     LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS"
     LIBS="$LIBS $STLPORT_LIBS"
 fi
 
 dnl ========================================================
+dnl Suppress Clang Argument Warnings
+dnl ========================================================
+if test -n "$CLANG_CC"; then
+    _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
+    CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
+fi
+if test -n "$CLANG_CXX"; then
+    _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
+fi
+
+dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
+    # Per bug 719659 comment 2, some of the headers on ancient build machines
+    # require gnu89 inline semantics.  But otherwise, we use C99.
+    CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
     # FIXME: Let us build with strict aliasing. bug 414641.
     CFLAGS="$CFLAGS -fno-strict-aliasing"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     DSO_LDOPTS='-shared'
     if test "$GCC_USE_GNU_LD"; then
         # Don't allow undefined symbols in libraries
         DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1447,23 +1447,16 @@ public:
   /**
    * Increases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    */
   static void AddScriptBlocker();
 
   /**
-   * Increases the count of blockers preventing scripts from running.
-   * Also, while this script blocker is active, script runners must not be
-   * added --- we'll assert if one is, and ignore it.
-   */
-  static void AddScriptBlockerAndPreventAddingRunners();
-
-  /**
    * Decreases the count of blockers preventing scripts from running.
    * NOTE: You might want to use nsAutoScriptBlocker rather than calling
    * this directly
    *
    * WARNING! Calling this function could synchronously execute scripts.
    */
   static void RemoveScriptBlocker();
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -255,17 +255,16 @@ PRUint32 nsContentUtils::sJSGCThingRootC
 nsIBidiKeyboard *nsContentUtils::sBidiKeyboard = nsnull;
 #endif
 PRUint32 nsContentUtils::sScriptBlockerCount = 0;
 #ifdef DEBUG
 PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0;
 #endif
 nsTArray< nsCOMPtr<nsIRunnable> >* nsContentUtils::sBlockedScriptRunners = nsnull;
 PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0;
-PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0;
 nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull;
 
 bool nsContentUtils::sIsHandlingKeyBoardEvent = false;
 bool nsContentUtils::sAllowXULXBL_for_file = false;
 
 nsString* nsContentUtils::sShiftText = nsnull;
 nsString* nsContentUtils::sControlText = nsnull;
 nsString* nsContentUtils::sMetaText = nsnull;
@@ -4414,33 +4413,20 @@ nsContentUtils::AddScriptBlocker()
                  "Should not already have a count");
     sRunnersCountAtFirstBlocker = sBlockedScriptRunners->Length();
   }
   ++sScriptBlockerCount;
 }
 
 /* static */
 void
-nsContentUtils::AddScriptBlockerAndPreventAddingRunners()
-{
-  AddScriptBlocker();
-  if (sScriptBlockerCountWhereRunnersPrevented == 0) {
-    sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount;
-  }
-}
-
-/* static */
-void
 nsContentUtils::RemoveScriptBlocker()
 {
   NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
   --sScriptBlockerCount;
-  if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) {
-    sScriptBlockerCountWhereRunnersPrevented = 0;
-  }
   if (sScriptBlockerCount) {
     return;
   }
 
   PRUint32 firstBlocker = sRunnersCountAtFirstBlocker;
   PRUint32 lastBlocker = sBlockedScriptRunners->Length();
   PRUint32 originalFirstBlocker = firstBlocker;
   PRUint32 blockersCount = lastBlocker - firstBlocker;
@@ -4464,20 +4450,16 @@ nsContentUtils::RemoveScriptBlocker()
 bool
 nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
 {
   if (!aRunnable) {
     return false;
   }
 
   if (sScriptBlockerCount) {
-    if (sScriptBlockerCountWhereRunnersPrevented > 0) {
-      NS_ERROR("Adding a script runner when that is prevented!");
-      return false;
-    }
     return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull;
   }
   
   nsCOMPtr<nsIRunnable> run = aRunnable;
   run->Run();
 
   return true;
 }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -7092,18 +7092,26 @@ nsDocument::BlockOnload()
   // If mScriptGlobalObject is null, we shouldn't be messing with the loadgroup
   // -- it's not ours.
   if (mOnloadBlockCount == 0 && mScriptGlobalObject) {
     if (!nsContentUtils::IsSafeToRunScript()) {
       // Because AddRequest may lead to OnStateChange calls in chrome,
       // block onload only when there are no script blockers.
       ++mAsyncOnloadBlockCount;
       if (mAsyncOnloadBlockCount == 1) {
-        nsContentUtils::AddScriptRunner(
+        bool success = nsContentUtils::AddScriptRunner(
           NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload));
+
+        // The script runner shouldn't fail to add. But if somebody broke
+        // something and it does, we'll thrash at 100% cpu forever. The best
+        // response is just to ignore the onload blocking request. See bug 579535.
+        if (!success) {
+          NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!");
+          mAsyncOnloadBlockCount = 0;
+        }
       }
       return;
     }
     nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
     if (loadGroup) {
       loadGroup->AddRequest(mOnloadBlocker, nsnull);
     }
   }
--- a/content/base/test/test_fileapi_slice.html
+++ b/content/base/test/test_fileapi_slice.html
@@ -8,18 +8,18 @@ https://bugzilla.mozilla.org/show_bug.cg
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="fileutils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=575946">Mozilla Bug 575946</a>
 <p id="display">
-  <canvas id=canvas width=1000 height=1000 hidden></canvas>
-  <canvas id=testcanvas hidden></canvas>
+  <canvas id=canvas width=1100 height=1100 hidden moz-opaque></canvas>
+  <canvas id=testcanvas hidden moz-opaque></canvas>
   <input id="fileList" type="file"></input>
 </p>
 <div id="content" style="display: none">
 </div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var fileNum = 1;
--- a/content/svg/content/test/bbox-helper.svg
+++ b/content/svg/content/test/bbox-helper.svg
@@ -1,9 +1,17 @@
 <?xml version="1.0"?>
 <svg xmlns="http://www.w3.org/2000/svg">
   <g transform="scale(0.5)">
     <foreignObject id="f" width="100" height="100"/>
   </g>
   <text id="b" x="20" y="20">b</text>
   <text id="a" x="20" y="30">a</text>
   <text id="y" x="20" y="40">y</text>
+  <g id="v">
+    <circle cx="100" cy="50" r="5"/>
+    <path d="M 100,100 L 100,200"/>
+  </g>
+  <g id="h">
+    <circle cx="200" cy="50" r="5"/>
+    <path d="M 200,100 L 300,100"/>
+  </g>
 </svg>
--- a/content/svg/content/test/test_bbox.xhtml
+++ b/content/svg/content/test/test_bbox.xhtml
@@ -37,16 +37,18 @@ function run()
     var bbox1 = getBBox(id1);
     var bbox2 = getBBox(id2);
     is(bbox1.height, bbox2.height, id1 + ".getBBox().height");
   }
 
   checkBBox("f", 0, 0, 100, 100);
   checkBBoxHeight("a", "b");
   checkBBoxHeight("a", "y");
+  checkBBox("v", 95, 45, 10, 155);
+  checkBBox("h", 195, 45, 105, 55);
   
   SimpleTest.finish();
 }
 
 window.addEventListener("load", run, false);
 </script>
 </pre>
 </body>
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -304,16 +304,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NA
                                             tmp->mContent);
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContent)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mNextBinding)
   delete tmp->mInsertionPointTable;
   tmp->mInsertionPointTable = nsnull;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding)
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+                                     "mPrototypeBinding->XBLDocumentInfo()");
   cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObjectOwner*>(
                       tmp->mPrototypeBinding->XBLDocumentInfo()));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_MEMBER(mNextBinding, nsXBLBinding)
   if (tmp->mInsertionPointTable)
     tmp->mInsertionPointTable->EnumerateRead(TraverseKey, &cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXBLBinding, AddRef)
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -471,16 +471,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
       nsCCUncollectableMarker::InGeneration(cb, tmp->mDocument->GetMarkedCCGeneration())) {
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
   if (tmp->mBindingTable) {
     tmp->mBindingTable->Enumerate(TraverseProtos, &cb);
   }
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mGlobalObject");
   cb.NoteXPCOMChild(static_cast<nsIScriptGlobalObject*>(tmp->mGlobalObject));
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXBLDocumentInfo)
   if (tmp->mBindingTable) {
     ProtoTracer closure = { aCallback, aClosure };
     tmp->mBindingTable->Enumerate(TraceProtos, &closure);
   }
--- a/content/xbl/src/nsXBLPrototypeBinding.cpp
+++ b/content/xbl/src/nsXBLPrototypeBinding.cpp
@@ -380,26 +380,30 @@ TraverseInsertionPoint(nsHashKey* aKey, 
   return kHashEnumerateNext;
 }
 
 static bool
 TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure)
 {
   nsCycleCollectionTraversalCallback *cb = 
     static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME((*cb), "proto mInterfaceTable data");
   cb->NoteXPCOMChild(static_cast<nsISupports*>(aData));
   return kHashEnumerateNext;
 }
 
 void
 nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const
 {
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding");
   cb.NoteXPCOMChild(mBinding);
-  if (mResources)
+  if (mResources) {
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mResources mLoader");
     cb.NoteXPCOMChild(mResources->mLoader);
+  }
   if (mInsertionPointTable)
     mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb);
   if (mInterfaceTable)
     mInterfaceTable->Enumerate(TraverseBinding, &cb);
 }
 
 void
 nsXBLPrototypeBinding::UnlinkJSObjects()
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -700,16 +700,17 @@ nsXULElement::PerformAccesskey(bool aKey
 //----------------------------------------------------------------------
 // nsIScriptEventHandlerOwner interface
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptEventHandlerOwnerTearoff)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptEventHandlerOwnerTearoff)
   tmp->mElement = nsnull;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptEventHandlerOwnerTearoff)
+  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mElement");
   cb.NoteXPCOMChild(static_cast<nsIContent*>(tmp->mElement));
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptEventHandlerOwnerTearoff)
   NS_INTERFACE_MAP_ENTRY(nsIScriptEventHandlerOwner)
 NS_INTERFACE_MAP_END_AGGREGATED(mElement)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptEventHandlerOwnerTearoff)
@@ -2540,22 +2541,26 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NA
     else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
         static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
     }
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode)
     if (tmp->mType == nsXULPrototypeNode::eType_Element) {
         nsXULPrototypeElement *elem =
             static_cast<nsXULPrototypeElement*>(tmp);
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo");
         cb.NoteXPCOMChild(elem->mNodeInfo);
         PRUint32 i;
         for (i = 0; i < elem->mNumAttributes; ++i) {
             const nsAttrName& name = elem->mAttributes[i].mName;
-            if (!name.IsAtom())
+            if (!name.IsAtom()) {
+                NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
+                    "mAttributes[i].mName.NodeInfo()");
                 cb.NoteXPCOMChild(name.NodeInfo());
+            }
         }
         for (i = 0; i < elem->mChildren.Length(); ++i) {
             NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i].get(),
                                                          nsXULPrototypeNode,
                                                          "mChildren[i]")
         }
     }
     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -6510,17 +6510,17 @@ nsWindowSH::GlobalResolve(nsGlobalWindow
       nsCOMPtr<nsIDOMGlobalPropertyInitializer> gpi(do_QueryInterface(native));
 
       if (gpi) {
         rv = gpi->Init(aWin, &prop_val);
         NS_ENSURE_SUCCESS(rv, rv);
       }
     }
 
-    if (JSVAL_IS_PRIMITIVE(prop_val)) {
+    if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
       JSObject *scope;
 
       if (aWin->IsOuterWindow()) {
         nsGlobalWindow *inner = aWin->GetCurrentInnerWindowInternal();
         NS_ENSURE_TRUE(inner, NS_ERROR_UNEXPECTED);
 
         scope = inner->GetGlobalJSObject();
       } else {
@@ -7263,17 +7263,17 @@ nsNavigatorSH::NewResolve(nsIXPConnectWr
     if (!window) {
       return NS_ERROR_UNEXPECTED;
     }
 
     rv = gpi->Init(window, &prop_val);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  if (JSVAL_IS_PRIMITIVE(prop_val)) {
+  if (JSVAL_IS_PRIMITIVE(prop_val) && !JSVAL_IS_NULL(prop_val)) {
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = WrapNative(cx, obj, native, true, &prop_val,
                     getter_AddRefs(holder));
 
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (!JS_WrapValue(cx, &prop_val)) {
--- a/dom/tests/mochitest/bugs/test_bug389366.html
+++ b/dom/tests/mochitest/bugs/test_bug389366.html
@@ -26,17 +26,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 SimpleTest.waitForExplicitFinish();
 
 function test()
 {
   // Draw to canvas1
   var canvas1 = document.getElementById("canvas1");
   var ctx1 = canvas1.getContext("2d");
 
-  ctx1.globalAlpha = 0.5;
+  ctx1.globalAlpha = 0.502;
   ctx1.fillStyle = "#FF0000";
   ctx1.fillRect(0, 0, 10, 10);
 
   var data1 = ctx1.getImageData(0,0,1,1).data;
   isDeeply(data1, [255, 0, 0, 128], "expected half-transparent red canvas 1");
 
   // half-transparent red 10x10 square
   var imgData = {
--- a/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
+++ b/gfx/cairo/cairo/src/cairo-d2d-surface.cpp
@@ -3276,17 +3276,17 @@ static cairo_int_status_t
 	    isSolidAlphaMask = true;
 	    solidAlphaValue = solidPattern->color.alpha;
 	}
     }
 
     cairo_box_t box;
     _cairo_box_from_rectangle(&box, &extents);
 
-    if (clip) {
+    if (clip && isSolidAlphaMask) {
 	// We do some work here to try and avoid pushing and popping clips for rectangular areas,
 	// if we do this fill rects will occur without rectangular clips being pushed and popped.
 	// This is faster for non-axis aligned clips in general and allows more efficient batching
 	// of the pop-clip calls.
 	int num_boxes = 1;
 	cairo_box_t box_stack;
 	cairo_box_t *boxes;
 	boxes = &box_stack;
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -421,17 +421,18 @@ nsBMPDecoder::WriteInternal(const char* 
         PRUint32 toCopy = (WIN_HEADER_LENGTH + BITFIELD_LENGTH) - mPos;
         if (toCopy > aCount)
             toCopy = aCount;
         memcpy(mRawBuf + (mPos - WIN_HEADER_LENGTH), aBuffer, toCopy);
         mPos += toCopy;
         aBuffer += toCopy;
         aCount -= toCopy;
     }
-    if (mBIH.compression == BI_BITFIELDS && mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH) {
+    if (mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH && 
+        mBIH.compression == BI_BITFIELDS) {
         mBitFields.red = LITTLE_TO_NATIVE32(*(PRUint32*)mRawBuf);
         mBitFields.green = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 4));
         mBitFields.blue = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 8));
         CalcBitShift();
     }
     while (aCount && (mPos < mBFH.dataoffset)) { // Skip whatever is between header and data
         mPos++; aBuffer++; aCount--;
     }
--- a/image/test/mochitest/test_bug466586.html
+++ b/image/test/mochitest/test_bug466586.html
@@ -3,47 +3,56 @@
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=466586
 -->
 <head>
   <title>Test for Bug 466586</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
-<body onload="loadSmall()">
+
+<body onload="loadSmall();">
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=89419">Mozilla Bug 466586</a>
 <p id="display"></p>
 <div id="content" style="display: none">
-<img src="big.png" width="100" height="100" id="big" />
+  <img id="big" src="big.png"/>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 SimpleTest.waitForExplicitFinish();
 
-// We have loaded the large png with id "big". We want to test if it'll be
+var jsBig = new Image();
+
+// We have loaded the large png with id "big". We want to test if it will be
 // kicked out of the cache and thus have to be reloaded, but to ensure that, we
-// need to get the cache to look at what's there. So we load another image,
+// need to get the cache to look at what is there. So we load another image,
 // this one small.
 function loadSmall()
 {
-  var small = new Image();
+  // Trivial check, for reference.
+  is(document.getElementById("big").width, 3000,
+     "HTML 'big' image width after page onload()");
 
+  var small = new Image();
   small.onload = smallLoaded;
-  small.src="red.png";
+  small.src = "red.png";
 }
 
 function smallLoaded()
 {
-  // Make sure that we don't need another load.
-  var big = document.getElementById('big');
-  var img = new Image();
-  img.src = big.src;
-// temporarily disabled while we work out why it's failing (bug 490384)
-//  ok(img.width != 0);
-  ok(true);
+  jsBig.src = document.getElementById("big").src;
+  // Check that it is not needed to wait for onload().
+  is(jsBig.width, 3000, "JS 'big' image width before its onload()");
+  // Check again after onload(), for reference.
+  jsBig.onload = jsBigLoaded;
+}
+
+function jsBigLoaded()
+{
+  is(jsBig.width, 3000, "JS 'big' image width after its onload()");
 
   SimpleTest.finish();
 }
 </script>
 </pre>
 </body>
 </html>
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1757,19 +1757,33 @@ if test "$OS_TARGET" = "Android"; then
     # save these for libffi's subconfigure,
     # which doesn't know how to figure this stuff out on its own
     ANDROID_CFLAGS="$CFLAGS"
     ANDROID_CPPFLAGS="$CPPFLAGS"
     ANDROID_LDFLAGS="$LDFLAGS"
 fi
 
 dnl ========================================================
+dnl Suppress Clang Argument Warnings
+dnl ========================================================
+if test -n "$CLANG_CC"; then
+    _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
+    CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
+fi
+if test -n "$CLANG_CXX"; then
+    _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
+fi
+
+dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
+    # Per bug 719659 comment 2, some of the headers on ancient build machines
+    # require gnu89 inline semantics.  But otherwise, we use C99.
+    CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     DSO_LDOPTS='-shared'
     if test "$GCC_USE_GNU_LD"; then
         # Don't allow undefined symbols in libraries
         DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
     fi
     WARNINGS_AS_ERRORS='-Werror -Wno-error=uninitialized'
--- a/js/src/ds/LifoAlloc.cpp
+++ b/js/src/ds/LifoAlloc.cpp
@@ -77,39 +77,16 @@ BumpChunk::delete_(BumpChunk *chunk)
 bool
 BumpChunk::canAlloc(size_t n)
 {
     char *aligned = AlignPtr(bump);
     char *bumped = aligned + n;
     return bumped <= limit && bumped > headerBase();
 }
 
-bool
-BumpChunk::canAllocUnaligned(size_t n)
-{
-    char *bumped = bump + n;
-    return bumped <= limit && bumped > headerBase();
-}
-
-void *
-BumpChunk::tryAllocUnaligned(size_t n)
-{
-    char *oldBump = bump;
-    char *newBump = bump + n;
-    if (newBump > limit)
-        return NULL;
-
-    if (JS_UNLIKELY(newBump < oldBump))
-        return NULL;
-
-    JS_ASSERT(canAllocUnaligned(n)); /* Ensure consistency between "can" and "try". */
-    setBump(newBump);
-    return oldBump;
-}
-
 } /* namespace detail */
 } /* namespace js */
 
 void
 LifoAlloc::freeAll()
 {
     while (first) {
         BumpChunk *victim = first;
@@ -190,42 +167,8 @@ LifoAlloc::getOrCreateChunk(size_t n)
         latest = first = newChunk;
     } else {
         JS_ASSERT(latest && !latest->next());
         latest->setNext(newChunk);
         latest = newChunk;
     }
     return newChunk;
 }
-
-void *
-LifoAlloc::allocUnaligned(size_t n)
-{
-    void *result;
-    if (latest && (result = latest->tryAllocUnaligned(n)))
-        return result;
-
-    return alloc(n);
-}
-
-void *
-LifoAlloc::reallocUnaligned(void *origPtr, size_t origSize, size_t incr)
-{
-    JS_ASSERT(first && latest);
-
-    /*
-     * Maybe we can grow the latest allocation in a BumpChunk.
-     *
-     * Note: we could also realloc the whole BumpChunk in the !canAlloc
-     * case, but this should not be a frequently hit case.
-     */
-    if (latest
-        && origPtr == (char *) latest->mark() - origSize
-        && latest->canAllocUnaligned(incr)) {
-        JS_ALWAYS_TRUE(allocUnaligned(incr));
-        return origPtr;
-    }
-
-    /* Otherwise, memcpy. */
-    size_t newSize = origSize + incr;
-    void *newPtr = allocUnaligned(newSize);
-    return newPtr ? js_memcpy(newPtr, origPtr, origSize) : NULL;
-}
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -129,17 +129,16 @@ class BumpChunk
         setBump(mark);
     }
 
     bool contains(void *mark) const {
         return bumpBase() <= mark && mark <= limit;
     }
 
     bool canAlloc(size_t n);
-    bool canAllocUnaligned(size_t n);
 
     /* Try to perform an allocation of size |n|, return null if not possible. */
     JS_ALWAYS_INLINE
     void *tryAlloc(size_t n) {
         char *aligned = AlignPtr(bump);
         char *newBump = aligned + n;
 
         if (newBump > limit)
@@ -149,18 +148,16 @@ class BumpChunk
         if (JS_UNLIKELY(newBump < bump))
             return NULL;
 
         JS_ASSERT(canAlloc(n)); /* Ensure consistency between "can" and "try". */
         setBump(newBump);
         return aligned;
     }
 
-    void *tryAllocUnaligned(size_t n);
-
     void *allocInfallible(size_t n) {
         void *result = tryAlloc(n);
         JS_ASSERT(result);
         return result;
     }
 
     static BumpChunk *new_(size_t chunkSize);
     static void delete_(BumpChunk *chunk);
@@ -315,21 +312,16 @@ class LifoAlloc
     /* Doesn't perform construction; useful for lazily-initialized POD types. */
     template <typename T>
     JS_ALWAYS_INLINE
     T *newPod() {
         return static_cast<T *>(alloc(sizeof(T)));
     }
 
     JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE)
-
-    /* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */
-
-    void *allocUnaligned(size_t n);
-    void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr);
 };
 
 class LifoAllocScope
 {
     LifoAlloc   *lifoAlloc;
     void        *mark;
     bool        shouldRelease;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -329,21 +329,17 @@ frontend::CompileScript(JSContext *cx, J
         code = bce.base();
         for (end = code + bce.offset(); code != end; code += len) {
             JS_ASSERT(code < end);
             op = (JSOp) *code;
             cs = &js_CodeSpec[op];
             len = (cs->length > 0)
                   ? (uintN) cs->length
                   : js_GetVariableBytecodeLength(code);
-            if ((cs->format & JOF_SHARPSLOT) ||
-                JOF_TYPE(cs->format) == JOF_LOCAL ||
-                (JOF_TYPE(cs->format) == JOF_SLOTATOM)) {
-                JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT),
-                             JOF_TYPE(cs->format) != JOF_SLOTATOM);
+            if ((cs->format & JOF_SHARPSLOT) || JOF_TYPE(cs->format) == JOF_LOCAL) {
                 slot = GET_SLOTNO(code);
                 if (!(cs->format & JOF_SHARPSLOT))
                     slot += bce.sharpSlots();
                 if (slot >= SLOTNO_LIMIT)
                     goto too_many_slots;
                 SET_SLOTNO(code, slot);
             }
         }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -534,17 +534,17 @@ EmitBackPatchOp(JSContext *cx, BytecodeE
 static JSBool
 UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, uintN line)
 {
     UPDATE_LINE_NUMBER_NOTES(cx, bce, line);
     return JS_TRUE;
 }
 
 static ptrdiff_t
-EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
 {
     if (nextpn) {
         /*
          * Try to give the JSOP_LOOPHEAD the same line number as the next
          * instruction. nextpn is often a block, in which case the next
          * instruction typically comes from the first statement inside.
          */
         JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
@@ -552,16 +552,31 @@ EmitTraceOp(JSContext *cx, BytecodeEmitt
             nextpn = nextpn->pn_head;
         if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
             return -1;
     }
 
     return Emit1(cx, bce, JSOP_LOOPHEAD);
 }
 
+static bool
+EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn)
+{
+    if (nextpn) {
+        /* Update the line number, as for LOOPHEAD. */
+        JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST));
+        if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head)
+            nextpn = nextpn->pn_head;
+        if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno))
+            return false;
+    }
+
+    return Emit1(cx, bce, JSOP_LOOPENTRY) >= 0;
+}
+
 /*
  * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve
  * a type set to store its result.
  */
 static inline void
 CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op)
 {
     if (js_CodeSpec[op].format & JOF_TYPESET) {
@@ -995,16 +1010,39 @@ EmitAtomOp(JSContext *cx, ParseNode *pn,
 
 static JSBool
 EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT);
     return EmitIndexOp(cx, op, bce->objectList.index(objbox), bce);
 }
 
+static bool
+EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce)
+{
+    const size_t len = 1 + UINT32_INDEX_LEN;
+    ptrdiff_t offset = EmitCheck(cx, bce, len);
+    if (offset < 0)
+        return false;
+
+    jsbytecode *next = bce->next();
+    next[0] = jsbytecode(op);
+    SET_UINT32_INDEX(next, index);
+    bce->current->next = next + len;
+    UpdateDepth(cx, bce, offset);
+    CheckTypeSet(cx, bce, op);
+    return true;
+}
+
+static bool
+EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce)
+{
+    return EmitIndex32(cx, JSOP_REGEXP, index, bce);
+}
+
 /*
  * What good are ARGNO_LEN and SLOTNO_LEN, you ask?  The answer is that, apart
  * from EmitSlotIndexOp, they abstract out the detail that both are 2, and in
  * other parts of the code there's no necessary relationship between the two.
  * The abstraction cracks here in order to share EmitSlotIndexOp code among
  * the JSOP_DEFLOCALFUN and JSOP_GET{ARG,VAR,LOCAL}PROP cases.
  */
 JS_STATIC_ASSERT(ARGNO_LEN == 2);
@@ -1012,18 +1050,17 @@ JS_STATIC_ASSERT(SLOTNO_LEN == 2);
 
 static JSBool
 EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, BytecodeEmitter *bce)
 {
     JSOp bigSuffix;
     ptrdiff_t off;
     jsbytecode *pc;
 
-    JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM ||
-              JOF_OPTYPE(op) == JOF_SLOTOBJECT);
+    JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT);
     bigSuffix = EmitBigIndexPrefix(cx, bce, index);
     if (bigSuffix == JSOP_FALSE)
         return JS_FALSE;
 
     /* Emit [op, slot, index]. */
     off = EmitN(cx, bce, op, 2 + INDEX_LEN);
     if (off < 0)
         return JS_FALSE;
@@ -3215,17 +3252,17 @@ EmitDestructuringOpsHelper(JSContext *cx
                  * so emit a pick to produce the intermediate state
                  *   | x | y | to-be-decompiled-value |
                  * before destructuring z. This gives the loop invariant that
                  * the to-be-compiled-value is always on top of the stack.
                  */
                 JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1);
                 uintN pickDistance = (uintN)((bce->stackDepth + 1) - depthBefore);
                 if (pickDistance > 0) {
-                    if (pickDistance > jsbytecode(-1)) {
+                    if (pickDistance > UINT8_MAX) {
                         ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR,
                                                  JSMSG_TOO_MANY_LOCALS);
                         return JS_FALSE;
                     }
                     if (Emit2(cx, bce, JSOP_PICK, (jsbytecode)pickDistance) < 0)
                         return false;
                 }
             }
@@ -4826,17 +4863,17 @@ EmitForIn(JSContext *cx, BytecodeEmitter
      * least one iteration, as the other loop forms do.
      */
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
-    if (EmitTraceOp(cx, bce, NULL) < 0)
+    if (EmitLoopHead(cx, bce, NULL) < 0)
         return false;
 
 #ifdef DEBUG
     intN loopDepth = bce->stackDepth;
 #endif
 
     /*
      * Emit code to get the next enumeration value and assign it to the
@@ -4869,16 +4906,18 @@ EmitForIn(JSContext *cx, BytecodeEmitter
     do {
         stmt->update = bce->offset();
     } while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
 
     /*
      * Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
      */
     SetJumpOffsetAt(bce, jmp);
+    if (!EmitLoopEntry(cx, bce, NULL))
+        return false;
     if (Emit1(cx, bce, JSOP_MOREITER) < 0)
         return false;
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
     /* Set the first srcnote offset so we can find the start of the loop body. */
     if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp2 - jmp))
@@ -4973,17 +5012,19 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
         if (jmp < 0)
             return false;
     }
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
 
     /* Emit code for the loop body. */
-    if (EmitTraceOp(cx, bce, forBody) < 0)
+    if (EmitLoopHead(cx, bce, forBody) < 0)
+        return false;
+    if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody))
         return false;
     if (!EmitTree(cx, bce, forBody))
         return false;
 
     /* Set the second note offset so we can find the update part. */
     JS_ASSERT(noteIndex != -1);
     ptrdiff_t tmp2 = bce->offset();
 
@@ -5021,16 +5062,18 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
     }
 
     ptrdiff_t tmp3 = bce->offset();
 
     if (forHead->pn_kid2) {
         /* Fix up the goto from top to target the loop condition. */
         JS_ASSERT(jmp >= 0);
         SetJumpOffsetAt(bce, jmp);
+        if (!EmitLoopEntry(cx, bce, forHead->pn_kid2))
+            return false;
 
         if (!EmitTree(cx, bce, forHead->pn_kid2))
             return false;
     }
 
     /* Set the first note offset so we can find the loop condition. */
     if (!SetSrcNoteOffset(cx, bce, (uintN)noteIndex, 0, tmp3 - tmp))
         return false;
@@ -5186,19 +5229,21 @@ EmitDo(JSContext *cx, BytecodeEmitter *b
     if (noteIndex < 0 || Emit1(cx, bce, JSOP_NOP) < 0)
         return false;
 
     ptrdiff_t noteIndex2 = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex2 < 0)
         return false;
 
     /* Compile the loop body. */
-    ptrdiff_t top = EmitTraceOp(cx, bce, pn->pn_left);
+    ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left);
     if (top < 0)
         return false;
+    if (!EmitLoopEntry(cx, bce, NULL))
+        return false;
 
     StmtInfo stmtInfo;
     PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top);
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
 
     /* Set loop and enclosing label update offsets, for continue. */
     ptrdiff_t off = bce->offset();
@@ -5254,24 +5299,26 @@ EmitWhile(JSContext *cx, BytecodeEmitter
     ptrdiff_t noteIndex = NewSrcNote(cx, bce, SRC_WHILE);
     if (noteIndex < 0)
         return false;
 
     ptrdiff_t jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
     if (jmp < 0)
         return false;
 
-    top = EmitTraceOp(cx, bce, pn->pn_right);
+    top = EmitLoopHead(cx, bce, pn->pn_right);
     if (top < 0)
         return false;
 
     if (!EmitTree(cx, bce, pn->pn_right))
         return false;
 
     SetJumpOffsetAt(bce, jmp);
+    if (!EmitLoopEntry(cx, bce, pn->pn_left))
+        return false;
     if (!EmitTree(cx, bce, pn->pn_left))
         return false;
 
     ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset());
     if (beq < 0)
         return false;
 
     if (!SetSrcNoteOffset(cx, bce, noteIndex, 0, beq - jmp))
@@ -6492,22 +6539,24 @@ frontend::EmitTree(JSContext *cx, Byteco
       {
         JS_ASSERT(!bce->inStrictMode());
 
         if (!EmitTree(cx, bce, pn->pn_left))
             return JS_FALSE;
         ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0);
         if (jmp < 0)
             return JS_FALSE;
-        top = EmitTraceOp(cx, bce, pn->pn_right);
+        top = EmitLoopHead(cx, bce, pn->pn_right);
         if (top < 0)
             return JS_FALSE;
         if (!EmitTree(cx, bce, pn->pn_right))
             return JS_FALSE;
         SetJumpOffsetAt(bce, jmp);
+        if (!EmitLoopEntry(cx, bce, NULL))
+            return false;
         if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0)
             return JS_FALSE;
         break;
       }
 #endif
 
       case PNK_DOT:
         /*
@@ -6639,17 +6688,17 @@ frontend::EmitTree(JSContext *cx, Byteco
         break;
 
       case PNK_NUMBER:
         ok = EmitNumberOp(cx, pn->pn_dval, bce);
         break;
 
       case PNK_REGEXP:
         JS_ASSERT(pn->isOp(JSOP_REGEXP));
-        ok = EmitIndexOp(cx, JSOP_REGEXP, bce->regexpList.index(pn->pn_objbox), bce);
+        ok = EmitRegExp(cx, bce->regexpList.index(pn->pn_objbox), bce);
         break;
 
 #if JS_HAS_XML_SUPPORT
       case PNK_ANYNAME:
 #endif
       case PNK_TRUE:
       case PNK_FALSE:
       case PNK_THIS:
new file mode 100644
--- /dev/null
+++ b/js/src/gc/Root.h
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** 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 SpiderMonkey global object code.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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 ***** */
+
+#ifndef jsgc_root_h__
+#define jsgc_root_h__
+
+#include "jsapi.h"
+#include "jsprvtd.h"
+
+namespace js {
+
+/*
+ * Moving GC Stack Rooting
+ *
+ * A moving GC may change the physical location of GC allocated things, even
+ * when they are rooted, updating all pointers to the thing to refer to its new
+ * location. The GC must therefore know about all live pointers to a thing,
+ * not just one of them, in order to behave correctly.
+ *
+ * The classes below are used to root stack locations whose value may be held
+ * live across a call that can trigger GC (i.e. a call which might allocate any
+ * GC things). For a code fragment such as:
+ *
+ * Foo();
+ * ... = obj->lastProperty();
+ *
+ * If Foo() can trigger a GC, the stack location of obj must be rooted to
+ * ensure that the GC does not move the JSObject referred to by obj without
+ * updating obj's location itself. This rooting must happen regardless of
+ * whether there are other roots which ensure that the object itself will not
+ * be collected.
+ *
+ * If Foo() cannot trigger a GC, and the same holds for all other calls made
+ * between obj's definitions and its last uses, then no rooting is required.
+ *
+ * Several classes are available for rooting stack locations. All are templated
+ * on the type T of the value being rooted, for which RootMethods<T> must
+ * have an instantiation.
+ *
+ * - Root<T> roots an existing stack allocated variable or other location of
+ *   type T. This is typically used either when a variable only needs to be
+ *   rooted on certain rare paths, or when a function takes a bare GC thing
+ *   pointer as an argument and needs to root it. In the latter case a
+ *   Handle<T> is generally preferred, see below.
+ *
+ * - RootedVar<T> declares a variable of type T, whose value is always rooted.
+ *
+ * - Handle<T> is a const reference to a Root<T> or RootedVar<T>. Handles are
+ *   coerced automatically from such a Root<T> or RootedVar<T>. Functions which
+ *   take GC things or values as arguments and need to root those arguments
+ *   should generally replace those arguments with handles and avoid any
+ *   explicit rooting. This has two benefits. First, when several such
+ *   functions call each other then redundant rooting of multiple copies of the
+ *   GC thing can be avoided. Second, if the caller does not pass a rooted
+ *   value a compile error will be generated, which is quicker and easier to
+ *   fix than when relying on a separate rooting analysis.
+ */
+
+template <> struct RootMethods<const jsid>
+{
+    static jsid initial() { return JSID_VOID; }
+    static ThingRootKind kind() { return THING_ROOT_ID; }
+    static bool poisoned(jsid id) { return IsPoisonedId(id); }
+};
+
+template <> struct RootMethods<jsid>
+{
+    static jsid initial() { return JSID_VOID; }
+    static ThingRootKind kind() { return THING_ROOT_ID; }
+    static bool poisoned(jsid id) { return IsPoisonedId(id); }
+};
+
+template <> struct RootMethods<const Value>
+{
+    static Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
+};
+
+template <> struct RootMethods<Value>
+{
+    static Value initial() { return UndefinedValue(); }
+    static ThingRootKind kind() { return THING_ROOT_VALUE; }
+    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
+};
+
+template <typename T>
+struct RootMethods<T *>
+{
+    static T *initial() { return NULL; }
+    static ThingRootKind kind() { return T::rootKind(); }
+    static bool poisoned(T *v) { return IsPoisonedPtr(v); }
+};
+
+/*
+ * Root a stack location holding a GC thing. This takes a stack pointer
+ * and ensures that throughout its lifetime the referenced variable
+ * will remain pinned against a moving GC.
+ *
+ * It is important to ensure that the location referenced by a Root is
+ * initialized, as otherwise the GC may try to use the the uninitialized value.
+ * It is generally preferable to use either RootedVar for local variables, or
+ * Handle for arguments.
+ */
+template <typename T>
+class Root
+{
+  public:
+    Root(JSContext *cx, const T *ptr
+         JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+#ifdef JSGC_ROOT_ANALYSIS
+        ThingRootKind kind = RootMethods<T>::kind();
+        this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]);
+        this->prev = *stack;
+        *stack = this;
+#endif
+
+        JS_ASSERT(!RootMethods<T>::poisoned(*ptr));
+
+        this->ptr = ptr;
+
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~Root()
+    {
+#ifdef JSGC_ROOT_ANALYSIS
+        JS_ASSERT(*stack == this);
+        *stack = prev;
+#endif
+    }
+
+#ifdef JSGC_ROOT_ANALYSIS
+    Root<T> *previous() { return prev; }
+#endif
+
+    const T *address() const { return ptr; }
+
+  private:
+
+#ifdef JSGC_ROOT_ANALYSIS
+    Root<T> **stack, *prev;
+#endif
+    const T *ptr;
+
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+template<typename T> template <typename S>
+inline
+Handle<T>::Handle(const Root<S> &root)
+{
+    testAssign<S>();
+    ptr = reinterpret_cast<const T *>(root.address());
+}
+
+typedef Root<JSObject*>          RootObject;
+typedef Root<JSFunction*>        RootFunction;
+typedef Root<Shape*>             RootShape;
+typedef Root<BaseShape*>         RootBaseShape;
+typedef Root<types::TypeObject*> RootTypeObject;
+typedef Root<JSString*>          RootString;
+typedef Root<JSAtom*>            RootAtom;
+typedef Root<jsid>               RootId;
+typedef Root<Value>              RootValue;
+
+/* Mark a stack location as a root for a rooting analysis. */
+class CheckRoot
+{
+#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
+
+    CheckRoot **stack, *prev;
+    const uint8_t *ptr;
+
+  public:
+    template <typename T>
+    CheckRoot(JSContext *cx, const T *ptr
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        this->stack = &cx->checkGCRooters;
+        this->prev = *stack;
+        *stack = this;
+        this->ptr = static_cast<const uint8_t*>(ptr);
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+    ~CheckRoot()
+    {
+        JS_ASSERT(*stack == this);
+        *stack = prev;
+    }
+
+    CheckRoot *previous() { return prev; }
+
+    bool contains(const uint8_t *v, size_t len) {
+        return ptr >= v && ptr < v + len;
+    }
+
+#else /* DEBUG && JSGC_ROOT_ANALYSIS */
+
+  public:
+    template <typename T>
+    CheckRoot(JSContext *cx, const T *ptr
+              JS_GUARD_OBJECT_NOTIFIER_PARAM)
+    {
+        JS_GUARD_OBJECT_NOTIFIER_INIT;
+    }
+
+#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
+
+    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
+/* Make a local variable which stays rooted throughout its lifetime. */
+template <typename T>
+class RootedVar
+{
+  public:
+    RootedVar(JSContext *cx)
+        : ptr(RootMethods<T>::initial()), root(cx, &ptr)
+    {}
+
+    RootedVar(JSContext *cx, T initial)
+        : ptr(initial), root(cx, &ptr)
+    {}
+
+    operator T () { return ptr; }
+    T operator ->() { return ptr; }
+    T * address() { return &ptr; }
+    const T * address() const { return &ptr; }
+    T raw() { return ptr; }
+
+    T & operator =(T value)
+    {
+        JS_ASSERT(!RootMethods<T>::poisoned(value));
+        ptr = value;
+        return ptr;
+    }
+
+  private:
+    T ptr;
+    Root<T> root;
+};
+
+template <typename T> template <typename S>
+inline
+Handle<T>::Handle(const RootedVar<S> &root)
+{
+    ptr = reinterpret_cast<const T *>(root.address());
+}
+
+typedef RootedVar<JSObject*>          RootedVarObject;
+typedef RootedVar<JSFunction*>        RootedVarFunction;
+typedef RootedVar<Shape*>             RootedVarShape;
+typedef RootedVar<BaseShape*>         RootedVarBaseShape;
+typedef RootedVar<types::TypeObject*> RootedVarTypeObject;
+typedef RootedVar<JSString*>          RootedVarString;
+typedef RootedVar<JSAtom*>            RootedVarAtom;
+typedef RootedVar<jsid>               RootedVarId;
+typedef RootedVar<Value>              RootedVarValue;
+
+}  /* namespace js */
+#endif  /* jsgc_root_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug716013.js
@@ -0,0 +1,4 @@
+f = (function() {
+    for (x in [arguments, arguments]) yield(gczeal(4, function(){}))
+})
+for (i in f()) {}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/terminate.js
@@ -0,0 +1,9 @@
+try {
+    terminate();
+    assertEq("execution continued", "execution should not continue");
+} catch (x) {
+    assertEq("caught exception", "uncatchable");
+} finally {
+    assertEq("'finally' clause ran", "'finally' clause should not run");
+}
+assertEq("top-level execution continued", "top-level execution should not continue");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onExceptionUnwind-08.js
@@ -0,0 +1,18 @@
+// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
+// throws an uncaught exception.
+var g = newGlobal('new-compartment');
+var dbg = Debugger(g);
+var frame;
+dbg.onExceptionUnwind = function (f, x) {
+    frame = f;
+    assertEq(frame.live, true);
+    throw 'unhandled';
+};
+dbg.onDebuggerStatement = function(f) {
+    assertEq(f.eval('throw 42'), null);
+    assertEq(frame.live, false);
+};
+g.eval('debugger');
+
+// Don't fail just because we reported an uncaught exception.
+quit(0);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onExceptionUnwind-09.js
@@ -0,0 +1,15 @@
+// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
+// terminates execution.
+var g = newGlobal('new-compartment');
+var dbg = Debugger(g);
+var frame;
+dbg.onExceptionUnwind = function (f, x) {
+    frame = f;
+    assertEq(frame.live, true);
+    return null;
+};
+dbg.onDebuggerStatement = function(f) {
+    assertEq(f.eval('throw 42'), null);
+    assertEq(frame.live, false);
+};
+g.eval('debugger');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onExceptionUnwind-10.js
@@ -0,0 +1,16 @@
+// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind
+// terminates execution.
+var g = newGlobal('new-compartment');
+var dbg = Debugger(g);
+var frame;
+dbg.onExceptionUnwind = function (f, x) {
+    frame = f;
+    assertEq(frame.type, 'eval');
+    assertEq(frame.live, true);
+    terminate();
+};
+dbg.onDebuggerStatement = function(f) {
+    assertEq(f.eval('throw 42'), null);
+    assertEq(frame.live, false);
+};
+g.eval('debugger');
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug719918.js
@@ -0,0 +1,19 @@
+function test(m) {
+  do {
+    if (m = arr[0]) break;
+    m = 0;
+  }
+  while (0);
+  arr[1] = m;
+}
+
+mjitChunkLimit(10);
+
+arr = new Float64Array(2);
+
+// run function a lot to trigger methodjit compile
+for(var i=0; i<200; i++)
+  test(0);
+
+// should return 0, not NaN
+assertEq(arr[1], 0)
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -52,21 +52,21 @@ namespace analyze {
 // Bytecode
 /////////////////////////////////////////////////////////////////////
 
 #ifdef DEBUG
 void
 PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     printf("#%u:", script->id());
-    LifoAlloc lifoAlloc(1024);
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return;
     js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter);
-    fprintf(stdout, "%s", sprinter.base);
+    fprintf(stdout, "%s", sprinter.string());
 }
 #endif
 
 /////////////////////////////////////////////////////////////////////
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
@@ -810,24 +810,26 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                 uint32_t entry = targetOffset;
                 if (entry) {
                     do {
                         entry--;
                     } while (!maybeCode(entry));
 
                     jsbytecode *entrypc = script->code + entry;
 
-                    if (JSOp(*entrypc) == JSOP_GOTO)
+                    if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
                         loop->entry = entry + GET_JUMP_OFFSET(entrypc);
                     else
                         loop->entry = targetOffset;
                 } else {
                     /* Do-while loop at the start of the script. */
                     loop->entry = targetOffset;
                 }
+                JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
+                          script->code[loop->entry] == JSOP_LOOPENTRY);
             } else {
                 for (unsigned i = 0; i < savedCount; i++) {
                     LifetimeVariable &var = *saved[i];
                     JS_ASSERT(!var.lifetime && var.saved);
                     if (var.live(targetOffset)) {
                         /*
                          * Jumping to a place where this variable is live. Make a new
                          * lifetime segment for the variable.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5595,17 +5595,17 @@ JS_PUBLIC_API(JSString *)
 JS_NewStringCopyZ(JSContext *cx, const char *s)
 {
     size_t n;
     jschar *js;
     JSString *str;
 
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    if (!s)
+    if (!s || !*s)
         return cx->runtime->emptyString;
     n = strlen(s);
     js = InflateString(cx, s, &n);
     if (!js)
         return NULL;
     str = js_NewString(cx, js, n);
     if (!str)
         cx->free_(js);
@@ -6709,89 +6709,16 @@ JS_ScheduleGC(JSContext *cx, uint32_t co
 JS_FRIEND_API(void *)
 js_GetCompartmentPrivate(JSCompartment *compartment)
 {
     return compartment->data;
 }
 
 /************************************************************************/
 
-JS_PUBLIC_API(void)
-JS_RegisterReference(void **ref)
-{
-}
-
-JS_PUBLIC_API(void)
-JS_ModifyReference(void **ref, void *newval)
-{
-    // XPConnect uses the lower bits of its JSObject refs for evil purposes,
-    // so we need to fix this.
-    void *thing = *ref;
-    *ref = newval;
-    thing = (void *)((uintptr_t)thing & ~7);
-    if (!thing)
-        return;
-    JS_ASSERT(!static_cast<gc::Cell *>(thing)->compartment()->rt->gcRunning);
-    uint32_t kind = GetGCThingTraceKind(thing);
-    if (kind == JSTRACE_OBJECT)
-        JSObject::writeBarrierPre((JSObject *) thing);
-    else if (kind == JSTRACE_STRING)
-        JSString::writeBarrierPre((JSString *) thing);
-    else
-        JS_NOT_REACHED("invalid trace kind");
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterReference(void **ref)
-{
-    // For now we just want to trigger a write barrier.
-    JS_ModifyReference(ref, NULL);
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterReferenceRT(JSRuntime *rt, void **ref)
-{
-    // For now we just want to trigger a write barrier.
-    if (!rt->gcRunning)
-        JS_ModifyReference(ref, NULL);
-}
-
-JS_PUBLIC_API(void)
-JS_RegisterValue(jsval *val)
-{
-}
-
-JS_PUBLIC_API(void)
-JS_ModifyValue(jsval *val, jsval newval)
-{
-    HeapValue::writeBarrierPre(*val);
-    *val = newval;
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterValue(jsval *val)
-{
-    JS_ModifyValue(val, JSVAL_VOID);
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterValueRT(JSRuntime *rt, jsval *val)
-{
-    if (!rt->gcRunning)
-        JS_ModifyValue(val, JSVAL_VOID);
-}
-
-JS_PUBLIC_API(JSTracer *)
-JS_GetIncrementalGCTracer(JSRuntime *rt)
-{
-    return rt->gcIncrementalTracer;
-}
-
-/************************************************************************/
-
 #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN)
 
 #include "jswin.h"
 
 /*
  * Initialization routine for the JS DLL.
  */
 BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3223,115 +3223,16 @@ JS_GetTraceEdgeName(JSTracer *trc, char 
  */
 extern JS_PUBLIC_API(JSBool)
 JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind kind,
             void *thingToFind, size_t maxDepth, void *thingToIgnore);
 
 #endif
 
 /*
- * Write barrier API.
- *
- * This API is used to inform SpiderMonkey of pointers to JS GC things in the
- * malloc heap. There is no need to use this API unless incremental GC is
- * enabled. When they are, the requirements for using the API are as follows:
- *
- * All pointers to JS GC things from the malloc heap must be registered and
- * unregistered with the API functions below. This is *in addition* to the
- * normal rooting and tracing that must be done normally--these functions will
- * not take care of rooting for you.
- *
- * Besides registration, the JS_ModifyReference function must be called to
- * change the value of these references. You should not change them using
- * assignment.
- *
- * Only the RT versions of these functions (which take a JSRuntime argument)
- * should be called during GC. Without a JSRuntime, it is not possible to know
- * if the object being barriered has already been finalized.
- *
- * To avoid the headache of using these API functions, the JSBarrieredObjectPtr
- * C++ class is provided--simply replace your JSObject* with a
- * JSBarrieredObjectPtr. It will take care of calling the registration and
- * modification APIs.
- *
- * For more explanation, see the comment in gc/Barrier.h.
- */
-
-/* These functions are to be used for objects and strings. */
-extern JS_PUBLIC_API(void)
-JS_RegisterReference(void **ref);
-
-extern JS_PUBLIC_API(void)
-JS_ModifyReference(void **ref, void *newval);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterReference(void **ref);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterReferenceRT(JSRuntime *rt, void **ref);
-
-/* These functions are for values. */
-extern JS_PUBLIC_API(void)
-JS_RegisterValue(jsval *val);
-
-extern JS_PUBLIC_API(void)
-JS_ModifyValue(jsval *val, jsval newval);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterValue(jsval *val);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterValueRT(JSRuntime *rt, jsval *val);
-
-extern JS_PUBLIC_API(JSTracer *)
-JS_GetIncrementalGCTracer(JSRuntime *rt);
-
-#ifdef __cplusplus
-JS_END_EXTERN_C
-
-namespace JS {
-
-class HeapPtrObject
-{
-    JSObject *value;
-
-  public:
-    HeapPtrObject() : value(NULL) { JS_RegisterReference((void **) &value); }
-
-    HeapPtrObject(JSObject *obj) : value(obj) { JS_RegisterReference((void **) &value); }
-
-    /* Always call finalize before the destructor. */
-    ~HeapPtrObject() { JS_ASSERT(!value); }
-
-    void finalize(JSRuntime *rt) {
-        JS_UnregisterReferenceRT(rt, (void **) &value);
-        value = NULL;
-    }
-    void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
-
-    void init(JSObject *obj) { value = obj; }
-
-    JSObject *get() const { return value; }
-
-    HeapPtrObject &operator=(JSObject *obj) {
-        JS_ModifyReference((void **) &value, obj);
-        return *this;
-    }
-
-    JSObject &operator*() const { return *value; }
-    JSObject *operator->() const { return value; }
-    operator JSObject *() const { return value; }
-};
-
-} /* namespace JS */
-
-JS_BEGIN_EXTERN_C
-#endif
-
-/*
  * Garbage collector API.
  */
 extern JS_PUBLIC_API(void)
 JS_GC(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_CompartmentGC(JSContext *cx, JSCompartment *comp);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1620,25 +1620,46 @@ array_toString_sub(JSContext *cx, JSObje
 
     jsuint length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
 
     StringBuffer sb(cx);
 
     if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
-        /* Elements beyond the initialized length are 'undefined' and thus can be ignored. */
-        const Value *beg = obj->getDenseArrayElements();
-        const Value *end = beg + Min(length, obj->getDenseArrayInitializedLength());
-        for (const Value *vp = beg; vp != end; ++vp) {
+        const Value *start = obj->getDenseArrayElements();
+        const Value *end = start + obj->getDenseArrayInitializedLength();
+        const Value *elem;
+        for (elem = start; elem < end; elem++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
-            if (!vp->isMagic(JS_ARRAY_HOLE) && !vp->isNullOrUndefined()) {
-                if (!ValueToStringBuffer(cx, *vp, sb))
+            /*
+             * Object stringifying is slow; delegate it to a separate loop to
+             * keep this one tight.
+             */
+            if (elem->isObject())
+                break;
+
+            if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) {
+                if (!ValueToStringBuffer(cx, *elem, sb))
+                    return false;
+            }
+        }
+
+        for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) {
+            if (!JS_CHECK_OPERATION_LIMIT(cx))
+                return false;
+
+            JSBool hole;
+            Value v;
+            if (!GetElement(cx, obj, i, &hole, &v))
+                return false;
+            if (!hole && !v.isNullOrUndefined()) {
+                if (!ValueToStringBuffer(cx, v, sb))
                     return false;
             }
         }
     } else {
         for (jsuint index = 0; index < length; index++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
@@ -1976,44 +1997,145 @@ CompareStringValues(JSContext *cx, const
     int32_t result;
     if (!CompareStrings(cx, astr, bstr, &result))
         return false;
 
     *lessOrEqualp = (result <= 0);
     return true;
 }
 
-struct SortComparatorStrings {
+static int32_t const powersOf10Int32[] = {
+    1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
+};
+
+static inline int
+NumDigitsBase10(int32_t n)
+{
+    /*
+     * This is just floor_log10(n) + 1
+     * Algorithm taken from
+     * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10
+     */
+    int32_t log2, t;
+    JS_CEILING_LOG2(log2, n);
+    t = log2 * 1233 >> 12;
+    return t - (n < powersOf10Int32[t]) + 1;
+}
+
+inline bool
+CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp)
+{
+    int32_t aint = a.toInt32();
+    int32_t bint = b.toInt32();
+
+    /*
+     * If both numbers are equal ... trivial
+     * If only one of both is negative --> arithmetic comparison as char code
+     * of '-' is always less than any other digit
+     * If both numbers are negative convert them to positive and continue
+     * handling ...
+     */
+    if (aint == bint) {
+        *lessOrEqualp = true;
+    } else if ((aint < 0) && (bint >= 0)) {
+        *lessOrEqualp = true;
+    } else if ((aint >= 0) && (bint < 0)) {
+        *lessOrEqualp = false;
+    } else if (bint == INT32_MIN) { /* a is negative too --> a <= b */
+        *lessOrEqualp = true;
+    } else if (aint == INT32_MIN) { /* b is negative too but not INT32_MIN --> a > b */
+        *lessOrEqualp = false;
+    } else {
+        if (aint < 0) { /* b is also negative */
+            aint = -aint;
+            bint = -bint;
+        }
+
+        /*
+         *  ... get number of digits of both integers.
+         * If they have the same number of digits --> arithmetic comparison.
+         * If digits_a > digits_b: a < b*10e(digits_a - digits_b).
+         * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b.
+         */
+        int digitsa = NumDigitsBase10(aint);
+        int digitsb = NumDigitsBase10(bint);
+        if (digitsa == digitsb)
+            *lessOrEqualp = (aint <= bint);
+        else if (digitsa > digitsb)
+            *lessOrEqualp = (aint < bint*powersOf10Int32[digitsa - digitsb]);
+        else /* if (digitsb > digitsa) */
+            *lessOrEqualp = (aint*powersOf10Int32[digitsb - digitsa] <= bint);
+    }
+
+    return true;
+}
+
+inline bool
+CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1,
+                       const jschar *s2, size_t l2, bool *lessOrEqualp)
+{
+    if (!JS_CHECK_OPERATION_LIMIT(cx))
+        return false;
+
+    int32_t result;
+    if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result))
+        return false;
+
+    *lessOrEqualp = (result <= 0);
+    return true;
+}
+
+struct SortComparatorStrings
+{
     JSContext   *const cx;
 
     SortComparatorStrings(JSContext *cx)
       : cx(cx) {}
 
     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
         return CompareStringValues(cx, a, b, lessOrEqualp);
     }
 };
 
-struct StringValuePair {
-    Value   str;
-    Value   v;
-};
-
-struct SortComparatorStringValuePairs {
+struct SortComparatorLexicographicInt32
+{
     JSContext   *const cx;
 
-    SortComparatorStringValuePairs(JSContext *cx)
+    SortComparatorLexicographicInt32(JSContext *cx)
       : cx(cx) {}
 
-    bool operator()(const StringValuePair &a, const StringValuePair &b, bool *lessOrEqualp) {
-        return CompareStringValues(cx, a.str, b.str, lessOrEqualp);
+    bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) {
+        return CompareLexicographicInt32(cx, a, b, lessOrEqualp);
     }
 };
 
-struct SortComparatorFunction {
+struct StringifiedElement
+{
+    size_t charsBegin;
+    size_t charsEnd;
+    size_t elementIndex;
+};
+
+struct SortComparatorStringifiedElements
+{
+    JSContext           *const cx;
+    const StringBuffer  &sb;
+
+    SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb)
+      : cx(cx), sb(sb) {}
+
+    bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) {
+        return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin,
+                                      sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin,
+                                      lessOrEqualp);
+    }
+};
+
+struct SortComparatorFunction
+{
     JSContext          *const cx;
     const Value        &fval;
     InvokeArgsGuard    &ag;
 
     SortComparatorFunction(JSContext *cx, const Value &fval, InvokeArgsGuard &ag)
       : cx(cx), fval(fval), ag(ag) { }
 
     bool operator()(const Value &a, const Value &b, bool *lessOrEqualp);
@@ -2118,16 +2240,17 @@ js::array_sort(JSContext *cx, uintN argc
          * call a "hole") is always greater than an existing property with
          * value undefined and that is always greater than any other property.
          * Thus to sort holes and undefs we simply count them, sort the rest
          * of elements, append undefs after them and then make holes after
          * undefs.
          */
         undefs = 0;
         bool allStrings = true;
+        bool allInts = true;
         for (jsuint i = 0; i < len; i++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
             /* Clear vec[newlen] before including it in the rooted set. */
             JSBool hole;
             Value v;
             if (!GetElement(cx, obj, i, &hole, &v))
@@ -2135,98 +2258,91 @@ js::array_sort(JSContext *cx, uintN argc
             if (hole)
                 continue;
             if (v.isUndefined()) {
                 ++undefs;
                 continue;
             }
             vec.infallibleAppend(v);
             allStrings = allStrings && v.isString();
+            allInts = allInts && v.isInt32();
         }
 
         n = vec.length();
         if (n == 0) {
             args.rval().setObject(*obj);
             return true; /* The array has only holes and undefs. */
         }
 
         JS_ALWAYS_TRUE(vec.resize(n * 2));
 
         /* Here len == n + undefs + number_of_holes. */
+        Value *result = vec.begin();
         if (fval.isNull()) {
             /*
              * Sort using the default comparator converting all elements to
              * strings.
              */
             if (allStrings) {
                 if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx)))
                     return false;
+            } else if (allInts) {
+                if (!MergeSort(vec.begin(), n, vec.begin() + n,
+                               SortComparatorLexicographicInt32(cx))) {
+                    return false;
+                }
             } else {
                 /*
-                 * To avoid string conversion on each compare we do it only once
-                 * prior to sorting. But we also need the space for the original
-                 * values to recover the sorting result. For that we move the
-                 * original values to the odd indexes in vec, put the string
-                 * conversion results in the even indexes and do the merge sort
-                 * over resulting string-value pairs using an extra allocated
-                 * scratch space.
+                 * Convert all elements to a jschar array in StringBuffer.
+                 * Store the index and length of each stringified element with
+                 * the corresponding index of the element in the array. Sort
+                 * the stringified elements and with this result order the
+                 * original array.
                  */
-                size_t i = n;
-                do {
-                    --i;
+                StringBuffer sb(cx);
+                Vector<StringifiedElement, 0, TempAllocPolicy> strElements(cx);
+                if (!strElements.reserve(2 * n))
+                    return false;
+
+                int cursor = 0;
+                for (size_t i = 0; i < n; i++) {
                     if (!JS_CHECK_OPERATION_LIMIT(cx))
                         return false;
-                    const Value &v = vec[i];
-                    JSString *str = ToString(cx, v);
-                    if (!str)
+
+                    if (!ValueToStringBuffer(cx, vec[i], sb))
                         return false;
 
-                    /*
-                     * Copying v must come first, because the following line
-                     * overwrites v when i == 0.
-                     */
-                    vec[2 * i + 1] = v;
-                    vec[2 * i].setString(str);
-                } while (i != 0);
-
-                AutoValueVector extraScratch(cx);
-                if (!extraScratch.resize(n * 2))
-                    return false;
-                if (!MergeSort(reinterpret_cast<StringValuePair *>(vec.begin()), n,
-                               reinterpret_cast<StringValuePair *>(extraScratch.begin()),
-                               SortComparatorStringValuePairs(cx))) {
+                    StringifiedElement el = { cursor, sb.length(), i };
+                    strElements.infallibleAppend(el);
+                    cursor = sb.length();
+                }
+
+                /* Resize strElements so we can perform the sorting */
+                JS_ALWAYS_TRUE(strElements.resize(2 * n));
+
+                if (!MergeSort(strElements.begin(), n, strElements.begin() + n,
+                               SortComparatorStringifiedElements(cx, sb))) {
                     return false;
                 }
 
-                /*
-                 * We want to unroot the cached results of toString calls
-                 * before the operation callback has a chance to run the GC.
-                 * So we do not call JS_CHECK_OPERATION_LIMIT in the loop.
-                 */
-                i = 0;
-                do {
-                    vec[i] = vec[2 * i + 1];
-                } while (++i != n);
+                /* Order vec[n:2n-1] using strElements.index */
+                for (size_t i = 0; i < n; i ++)
+                    vec[n + i] = vec[strElements[i].elementIndex];
+
+                result = vec.begin() + n;
             }
         } else {
             InvokeArgsGuard args;
             if (!MergeSort(vec.begin(), n, vec.begin() + n,
-                           SortComparatorFunction(cx, fval, args)))
-            {
+                           SortComparatorFunction(cx, fval, args))) {
                 return false;
             }
         }
 
-        /*
-         * We no longer need to root the scratch space for the merge sort, so
-         * unroot it now to make the job of a potential GC under
-         * InitArrayElements easier.
-         */
-        vec.resize(n);
-        if (!InitArrayElements(cx, obj, 0, jsuint(n), vec.begin(), DontUpdateTypes))
+        if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes))
             return false;
     }
 
     /* Set undefs that sorted after the rest of elements. */
     while (undefs != 0) {
         --undefs;
         if (!JS_CHECK_OPERATION_LIMIT(cx) || !SetArrayElement(cx, obj, n++, UndefinedValue()))
             return false;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -59,17 +59,17 @@
 #include "jspropertytree.h"
 #include "jsutil.h"
 #include "prmjtime.h"
 
 #include "ds/LifoAlloc.h"
 #include "gc/Statistics.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
-#include "vm/StackSpace.h"
+#include "vm/Stack.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #pragma warning(push)
 #pragma warning(disable:4355) /* Silence warning about "this" used in base member initializer list */
 #endif
 
@@ -916,21 +916,21 @@ struct JSContext
     void setThread(JSThread *thread);
     static const size_t threadOffset() { return offsetof(JSContext, thread_); }
 #endif
 
     /* Current execution stack. */
     js::ContextStack    stack;
 
     /* ContextStack convenience functions */
-    inline bool hasfp() const;
-    inline js::StackFrame* fp() const;
-    inline js::StackFrame* maybefp() const;
-    inline js::FrameRegs& regs() const;
-    inline js::FrameRegs* maybeRegs() const;
+    inline bool hasfp() const               { return stack.hasfp(); }
+    inline js::StackFrame* fp() const       { return stack.fp(); }
+    inline js::StackFrame* maybefp() const  { return stack.maybefp(); }
+    inline js::FrameRegs& regs() const      { return stack.regs(); }
+    inline js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); }
 
     /* Set cx->compartment based on the current scope chain. */
     void resetCompartment();
 
     /* Wrap cx->exception for the current compartment. */
     void wrapPendingException();
 
   private:
@@ -1301,267 +1301,16 @@ struct AutoResolving {
     JSContext           *const context;
     JSObject            *const object;
     jsid                const id;
     Kind                const kind;
     AutoResolving       *const link;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-/*
- * Moving GC Stack Rooting
- *
- * A moving GC may change the physical location of GC allocated things, even
- * when they are rooted, updating all pointers to the thing to refer to its new
- * location. The GC must therefore know about all live pointers to a thing,
- * not just one of them, in order to behave correctly.
- *
- * The classes below are used to root stack locations whose value may be held
- * live across a call that can trigger GC (i.e. a call which might allocate any
- * GC things). For a code fragment such as:
- *
- * Foo();
- * ... = obj->lastProperty();
- *
- * If Foo() can trigger a GC, the stack location of obj must be rooted to
- * ensure that the GC does not move the JSObject referred to by obj without
- * updating obj's location itself. This rooting must happen regardless of
- * whether there are other roots which ensure that the object itself will not
- * be collected.
- *
- * If Foo() cannot trigger a GC, and the same holds for all other calls made
- * between obj's definitions and its last uses, then no rooting is required.
- *
- * Several classes are available for rooting stack locations. All are templated
- * on the type T of the value being rooted, for which RootMethods<T> must
- * have an instantiation.
- *
- * - Root<T> roots an existing stack allocated variable or other location of
- *   type T. This is typically used either when a variable only needs to be
- *   rooted on certain rare paths, or when a function takes a bare GC thing
- *   pointer as an argument and needs to root it. In the latter case a
- *   Handle<T> is generally preferred, see below.
- *
- * - RootedVar<T> declares a variable of type T, whose value is always rooted.
- *
- * - Handle<T> is a const reference to a Root<T> or RootedVar<T>. Handles are
- *   coerced automatically from such a Root<T> or RootedVar<T>. Functions which
- *   take GC things or values as arguments and need to root those arguments
- *   should generally replace those arguments with handles and avoid any
- *   explicit rooting. This has two benefits. First, when several such
- *   functions call each other then redundant rooting of multiple copies of the
- *   GC thing can be avoided. Second, if the caller does not pass a rooted
- *   value a compile error will be generated, which is quicker and easier to
- *   fix than when relying on a separate rooting analysis.
- */
-
-template <> struct RootMethods<const jsid>
-{
-    static jsid initial() { return JSID_VOID; }
-    static ThingRootKind kind() { return THING_ROOT_ID; }
-    static bool poisoned(jsid id) { return IsPoisonedId(id); }
-};
-
-template <> struct RootMethods<jsid>
-{
-    static jsid initial() { return JSID_VOID; }
-    static ThingRootKind kind() { return THING_ROOT_ID; }
-    static bool poisoned(jsid id) { return IsPoisonedId(id); }
-};
-
-template <> struct RootMethods<const Value>
-{
-    static Value initial() { return UndefinedValue(); }
-    static ThingRootKind kind() { return THING_ROOT_VALUE; }
-    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
-};
-
-template <> struct RootMethods<Value>
-{
-    static Value initial() { return UndefinedValue(); }
-    static ThingRootKind kind() { return THING_ROOT_VALUE; }
-    static bool poisoned(const Value &v) { return IsPoisonedValue(v); }
-};
-
-template <typename T>
-struct RootMethods<T *>
-{
-    static T *initial() { return NULL; }
-    static ThingRootKind kind() { return T::rootKind(); }
-    static bool poisoned(T *v) { return IsPoisonedPtr(v); }
-};
-
-/*
- * Root a stack location holding a GC thing. This takes a stack pointer
- * and ensures that throughout its lifetime the referenced variable
- * will remain pinned against a moving GC.
- *
- * It is important to ensure that the location referenced by a Root is
- * initialized, as otherwise the GC may try to use the the uninitialized value.
- * It is generally preferable to use either RootedVar for local variables, or
- * Handle for arguments.
- */
-template <typename T>
-class Root
-{
-  public:
-    Root(JSContext *cx, const T *ptr
-         JS_GUARD_OBJECT_NOTIFIER_PARAM)
-    {
-#ifdef JSGC_ROOT_ANALYSIS
-        ThingRootKind kind = RootMethods<T>::kind();
-        this->stack = reinterpret_cast<Root<T>**>(&cx->thingGCRooters[kind]);
-        this->prev = *stack;
-        *stack = this;
-#endif
-
-        JS_ASSERT(!RootMethods<T>::poisoned(*ptr));
-
-        this->ptr = ptr;
-
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~Root()
-    {
-#ifdef JSGC_ROOT_ANALYSIS
-        JS_ASSERT(*stack == this);
-        *stack = prev;
-#endif
-    }
-
-#ifdef JSGC_ROOT_ANALYSIS
-    Root<T> *previous() { return prev; }
-#endif
-
-    const T *address() const { return ptr; }
-
-  private:
-
-#ifdef JSGC_ROOT_ANALYSIS
-    Root<T> **stack, *prev;
-#endif
-    const T *ptr;
-
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
-template<typename T> template <typename S>
-inline
-Handle<T>::Handle(const Root<S> &root)
-{
-    testAssign<S>();
-    ptr = reinterpret_cast<const T *>(root.address());
-}
-
-typedef Root<JSObject*>          RootObject;
-typedef Root<JSFunction*>        RootFunction;
-typedef Root<Shape*>             RootShape;
-typedef Root<BaseShape*>         RootBaseShape;
-typedef Root<types::TypeObject*> RootTypeObject;
-typedef Root<JSString*>          RootString;
-typedef Root<JSAtom*>            RootAtom;
-typedef Root<jsid>               RootId;
-typedef Root<Value>              RootValue;
-
-/* Mark a stack location as a root for a rooting analysis. */
-class CheckRoot
-{
-#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS)
-
-    CheckRoot **stack, *prev;
-    const uint8_t *ptr;
-
-  public:
-    template <typename T>
-    CheckRoot(JSContext *cx, const T *ptr
-              JS_GUARD_OBJECT_NOTIFIER_PARAM)
-    {
-        this->stack = &cx->checkGCRooters;
-        this->prev = *stack;
-        *stack = this;
-        this->ptr = static_cast<const uint8_t*>(ptr);
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    ~CheckRoot()
-    {
-        JS_ASSERT(*stack == this);
-        *stack = prev;
-    }
-
-    CheckRoot *previous() { return prev; }
-
-    bool contains(const uint8_t *v, size_t len) {
-        return ptr >= v && ptr < v + len;
-    }
-
-#else /* DEBUG && JSGC_ROOT_ANALYSIS */
-
-  public:
-    template <typename T>
-    CheckRoot(JSContext *cx, const T *ptr
-              JS_GUARD_OBJECT_NOTIFIER_PARAM)
-    {
-        JS_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-#endif /* DEBUG && JSGC_ROOT_ANALYSIS */
-
-    JS_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
-/* Make a local variable which stays rooted throughout its lifetime. */
-template <typename T>
-class RootedVar
-{
-  public:
-    RootedVar(JSContext *cx)
-        : ptr(RootMethods<T>::initial()), root(cx, &ptr)
-    {}
-
-    RootedVar(JSContext *cx, T initial)
-        : ptr(initial), root(cx, &ptr)
-    {}
-
-    operator T () { return ptr; }
-    T operator ->() { return ptr; }
-    T * address() { return &ptr; }
-    const T * address() const { return &ptr; }
-    T raw() { return ptr; }
-
-    T & operator =(T value)
-    {
-        JS_ASSERT(!RootMethods<T>::poisoned(value));
-        ptr = value;
-        return ptr;
-    }
-
-  private:
-    T ptr;
-    Root<T> root;
-};
-
-template <typename T> template <typename S>
-inline
-Handle<T>::Handle(const RootedVar<S> &root)
-{
-    ptr = reinterpret_cast<const T *>(root.address());
-}
-
-typedef RootedVar<JSObject*>          RootedVarObject;
-typedef RootedVar<JSFunction*>        RootedVarFunction;
-typedef RootedVar<Shape*>             RootedVarShape;
-typedef RootedVar<BaseShape*>         RootedVarBaseShape;
-typedef RootedVar<types::TypeObject*> RootedVarTypeObject;
-typedef RootedVar<JSString*>          RootedVarString;
-typedef RootedVar<JSAtom*>            RootedVarAtom;
-typedef RootedVar<jsid>               RootedVarId;
-typedef RootedVar<Value>              RootedVarValue;
-
 #ifdef JS_HAS_XML_SUPPORT
 class AutoXMLRooter : private AutoGCRooter {
   public:
     AutoXMLRooter(JSContext *cx, JSXML *xml
                   JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, XML), xml(xml)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1615,40 +1615,40 @@ js_ResumeVtune()
 }
 
 #endif /* MOZ_VTUNE */
 
 JS_PUBLIC_API(void)
 JS_DumpBytecode(JSContext *cx, JSScript *script)
 {
 #if defined(DEBUG)
-    LifoAlloc lifoAlloc(1024);
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return;
 
     fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
     js_Disassemble(cx, script, true, &sprinter);
-    fputs(sprinter.base, stdout);
+    fputs(sprinter.string(), stdout);
     fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
 #endif
 }
 
 extern JS_PUBLIC_API(void)
 JS_DumpPCCounts(JSContext *cx, JSScript *script)
 {
 #if defined(DEBUG)
     JS_ASSERT(script->pcCounters);
 
-    LifoAlloc lifoAlloc(1024);
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return;
 
     fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno);
     js_DumpPCCounts(cx, script, &sprinter);
-    fputs(sprinter.base, stdout);
+    fputs(sprinter.string(), stdout);
     fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno);
 #endif
 }
 
 static void
 DumpBytecodeScriptCallback(JSContext *cx, void *data, void *thing,
                            JSGCTraceKind traceKind, size_t thingSize)
 {
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -318,16 +318,22 @@ js::GetFunctionNativeReserved(JSObject *
 
 JS_FRIEND_API(void)
 js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val)
 {
     JS_ASSERT(fun->toFunction()->isNative());
     fun->toFunction()->setExtendedSlot(which, val);
 }
 
+JS_FRIEND_API(void)
+js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value)
+{
+    obj->setSlot(slot, value);
+}
+
 void
 js::SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback)
 {
     rt->preserveWrapperCallback = callback;
 }
 
 /*
  * The below code is for temporary telemetry use. It can be removed when
@@ -464,16 +470,49 @@ js::DumpHeapComplete(JSContext *cx, FILE
 
     dtrc.visited.finish();
 }
 
 #endif
 
 namespace js {
 
+JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSRuntime *rt)
+{
+    return !!rt->gcIncrementalTracer && !rt->gcRunning;
+}
+
+JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSContext *cx)
+{
+    return IsIncrementalBarrierNeeded(cx->runtime);
+}
+
+extern JS_FRIEND_API(void)
+IncrementalReferenceBarrier(void *ptr)
+{
+    if (!ptr)
+        return;
+    JS_ASSERT(!static_cast<gc::Cell *>(ptr)->compartment()->rt->gcRunning);
+    uint32_t kind = gc::GetGCThingTraceKind(ptr);
+    if (kind == JSTRACE_OBJECT)
+        JSObject::writeBarrierPre((JSObject *) ptr);
+    else if (kind == JSTRACE_STRING)
+        JSString::writeBarrierPre((JSString *) ptr);
+    else
+        JS_NOT_REACHED("invalid trace kind");
+}
+
+extern JS_FRIEND_API(void)
+IncrementalValueBarrier(const Value &v)
+{
+    HeapValue::writeBarrierPre(v);
+}
+
 /* static */ void
 AutoLockGC::LockGC(JSRuntime *rt)
 {
     JS_ASSERT(rt);
     JS_LOCK_GC(rt);
 }
 
 /* static */ void
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -373,21 +373,28 @@ GetObjectPrivate(JSObject *obj)
  */
 inline const Value &
 GetReservedSlot(const JSObject *obj, size_t slot)
 {
     JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
     return reinterpret_cast<const shadow::Object *>(obj)->slotRef(slot);
 }
 
+JS_FRIEND_API(void)
+SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const Value &value);
+
 inline void
 SetReservedSlot(JSObject *obj, size_t slot, const Value &value)
 {
     JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj)));
-    reinterpret_cast<shadow::Object *>(obj)->slotRef(slot) = value;
+    shadow::Object *sobj = reinterpret_cast<shadow::Object *>(obj);
+    if (sobj->slotRef(slot).isMarkable())
+        SetReservedSlotWithBarrier(obj, slot, value);
+    else
+        sobj->slotRef(slot) = value;
 }
 
 JS_FRIEND_API(uint32_t)
 GetObjectSlotSpan(JSObject *obj);
 
 inline const Value &
 GetObjectSlot(JSObject *obj, size_t slot)
 {
@@ -560,16 +567,66 @@ TriggerOperationCallbacksForActiveContex
 class SystemAllocPolicy;
 typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
 extern JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt);
 
 extern JS_FRIEND_API(size_t)
 SizeOfJSContext();
 
+extern JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSRuntime *rt);
+
+extern JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSContext *cx);
+
+extern JS_FRIEND_API(void)
+IncrementalReferenceBarrier(void *ptr);
+
+extern JS_FRIEND_API(void)
+IncrementalValueBarrier(const Value &v);
+
+class ObjectPtr
+{
+    JSObject *value;
+
+  public:
+    ObjectPtr() : value(NULL) {}
+
+    ObjectPtr(JSObject *obj) : value(obj) {}
+
+    /* Always call finalize before the destructor. */
+    ~ObjectPtr() { JS_ASSERT(!value); }
+
+    void finalize(JSRuntime *rt) {
+        if (IsIncrementalBarrierNeeded(rt))
+            IncrementalReferenceBarrier(value);
+        value = NULL;
+    }
+    void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
+
+    void init(JSObject *obj) { value = obj; }
+
+    JSObject *get() const { return value; }
+
+    void writeBarrierPre(JSRuntime *rt) {
+        IncrementalReferenceBarrier(value);
+    }
+
+    ObjectPtr &operator=(JSObject *obj) {
+        IncrementalReferenceBarrier(value);
+        value = obj;
+        return *this;
+    }
+
+    JSObject &operator*() const { return *value; }
+    JSObject *operator->() const { return value; }
+    operator JSObject *() const { return value; }
+};
+
 } /* namespace js */
 
 /*
  * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is
  * JSProto_Null, clasp must non-null.
  */
 extern JS_FRIEND_API(JSBool)
 js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -2148,20 +2148,18 @@ TypeCompartment::processPendingRecompile
 
 #ifdef JS_METHODJIT
 
     mjit::ExpandInlineFrames(cx->compartment);
 
     for (unsigned i = 0; i < pending->length(); i++) {
         const RecompileInfo &info = (*pending)[i];
         mjit::JITScript *jit = info.script->getJIT(info.constructing);
-        if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) {
-            mjit::Recompiler::clearStackReferences(cx, info.script);
-            jit->destroyChunk(cx, info.chunkIndex);
-        }
+        if (jit && jit->chunkDescriptor(info.chunkIndex).chunk)
+            mjit::Recompiler::clearStackReferencesAndChunk(cx, info.script, jit, info.chunkIndex);
     }
 
 #endif /* JS_METHODJIT */
 
     cx->delete_(pending);
 }
 
 void
@@ -3252,17 +3250,17 @@ static inline TypeObject *
 GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     if (!script->hasGlobal())
         return NULL;
 
     JSOp op = JSOp(*pc);
     JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
 
-    bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array));
+    bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
     return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object);
 }
 
 /*
  * Detach nesting state for script from its parent, removing it entirely if it
  * has no children of its own. This happens when walking type information while
  * initially resolving NAME accesses, thus will not invalidate any compiler
  * dependencies.
@@ -3425,16 +3423,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
       case JSOP_NOP:
       case JSOP_LOOPHEAD:
+      case JSOP_LOOPENTRY:
       case JSOP_GOTO:
       case JSOP_IFEQ:
       case JSOP_IFNE:
       case JSOP_LINENO:
       case JSOP_DEFCONST:
       case JSOP_LEAVEWITH:
       case JSOP_LEAVEBLOCK:
       case JSOP_RETRVAL:
@@ -3556,17 +3555,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[1]);
         poppedTypes(pc, 1)->addSubset(cx, &pushed[2]);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[3]);
         break;
 
       case JSOP_SWAP:
       case JSOP_PICK: {
-        unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
+        unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc));
         /* The last popped value is the last pushed. */
         poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]);
         for (unsigned i = 0; i < pickedDepth; i++)
             poppedTypes(pc, i)->addSubset(cx, &pushed[pickedDepth - 1 - i]);
         break;
       }
 
       case JSOP_GETGNAME:
@@ -4019,17 +4018,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
          * 'for each' loops together, oh well.
          */
         if (!state.forTypes) {
           state.forTypes = TypeSet::make(cx, "forTypes");
           if (!state.forTypes)
               return false;
         }
 
-        if (pc[1] & JSITER_FOREACH)
+        if (GET_UINT8(pc) & JSITER_FOREACH)
             state.forTypes->addType(cx, Type::UnknownType());
         else
             state.forTypes->addType(cx, Type::StringType());
         break;
       }
 
       case JSOP_ITERNEXT:
         state.forTypes->addSubset(cx, &pushed[0]);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1598,18 +1598,18 @@ js::Interpret(JSContext *cx, StackFrame 
 
     if (!entryFrame)
         entryFrame = regs.fp();
 
     /*
      * Initialize the index segment register used by LOAD_ATOM and
      * GET_FULL_INDEX macros below. As a register we use a pointer based on
      * the atom map to turn frequently executed LOAD_ATOM into simple array
-     * access. For less frequent object and regexp loads we have to recover
-     * the segment from atoms pointer first.
+     * access. For less frequent object loads we have to recover the segment
+     * from atoms pointer first.
      */
     JSAtom **atoms = script->atoms;
 
 #if JS_HAS_GENERATORS
     if (JS_UNLIKELY(regs.fp()->isGeneratorFrame())) {
         JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
         JS_ASSERT((size_t) (regs.sp - regs.fp()->base()) <= StackDepth(script));
 
@@ -1798,16 +1798,17 @@ ADD_EMPTY_CASE(JSOP_UNUSED12)
 ADD_EMPTY_CASE(JSOP_UNUSED13)
 ADD_EMPTY_CASE(JSOP_CONDSWITCH)
 ADD_EMPTY_CASE(JSOP_TRY)
 #if JS_HAS_XML_SUPPORT
 ADD_EMPTY_CASE(JSOP_STARTXML)
 ADD_EMPTY_CASE(JSOP_STARTXMLEXPR)
 #endif
 ADD_EMPTY_CASE(JSOP_LOOPHEAD)
+ADD_EMPTY_CASE(JSOP_LOOPENTRY)
 END_EMPTY_CASES
 
 BEGIN_CASE(JSOP_LABEL)
 END_CASE(JSOP_LABEL)
 
 check_backedge:
 {
     CHECK_BRANCH();
@@ -2041,17 +2042,17 @@ END_CASE(JSOP_AND)
             if (!js_InternNonIntElementId(cx, obj, idval_, &id, &regs.sp[n])) \
                 goto error;                                                   \
         }                                                                     \
     JS_END_MACRO
 
 #define TRY_BRANCH_AFTER_COND(cond,spdec)                                     \
     JS_BEGIN_MACRO                                                            \
         JS_ASSERT(js_CodeSpec[op].length == 1);                               \
-        uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ;                 \
+        uintN diff_ = (uintN) GET_UINT8(regs.pc) - (uintN) JSOP_IFEQ;         \
         if (diff_ <= 1) {                                                     \
             regs.sp -= spdec;                                                 \
             if (cond == (diff_ != 0)) {                                       \
                 ++regs.pc;                                                    \
                 len = GET_JUMP_OFFSET(regs.pc);                               \
                 BRANCH(len);                                                  \
             }                                                                 \
             len = 1 + JSOP_IFEQ_LENGTH;                                       \
@@ -2078,17 +2079,17 @@ BEGIN_CASE(JSOP_IN)
     regs.sp--;
     regs.sp[-1].setBoolean(cond);
 }
 END_CASE(JSOP_IN)
 
 BEGIN_CASE(JSOP_ITER)
 {
     JS_ASSERT(regs.sp > regs.fp()->base());
-    uintN flags = regs.pc[1];
+    uint8_t flags = GET_UINT8(regs.pc);
     if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
         goto error;
     CHECK_INTERRUPT_HANDLER();
     JS_ASSERT(!regs.sp[-1].isPrimitive());
 }
 END_CASE(JSOP_ITER)
 
 BEGIN_CASE(JSOP_MOREITER)
@@ -2149,20 +2150,20 @@ BEGIN_CASE(JSOP_SWAP)
     Value &lref = regs.sp[-2];
     Value &rref = regs.sp[-1];
     lref.swap(rref);
 }
 END_CASE(JSOP_SWAP)
 
 BEGIN_CASE(JSOP_PICK)
 {
-    jsint i = regs.pc[1];
-    JS_ASSERT(regs.sp - (i+1) >= regs.fp()->base());
-    Value lval = regs.sp[-(i+1)];
-    memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i);
+    unsigned i = GET_UINT8(regs.pc);
+    JS_ASSERT(regs.sp - (i + 1) >= regs.fp()->base());
+    Value lval = regs.sp[-int(i + 1)];
+    memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i);
     regs.sp[-1] = lval;
 }
 END_CASE(JSOP_PICK)
 
 BEGIN_CASE(JSOP_SETCONST)
 {
     PropertyName *name;
     LOAD_NAME(0, name);
@@ -3079,17 +3080,17 @@ BEGIN_CASE(JSOP_OBJECT)
 END_CASE(JSOP_OBJECT)
 
 BEGIN_CASE(JSOP_REGEXP)
 {
     /*
      * Push a regexp object cloned from the regexp literal object mapped by the
      * bytecode at pc.
      */
-    jsatomid index = GET_FULL_INDEX(0);
+    uint32_t index = GET_UINT32_INDEX(regs.pc);
     JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx);
     if (!proto)
         goto error;
     JSObject *obj = js_CloneRegExpObject(cx, script->getRegExp(index), proto);
     if (!obj)
         goto error;
     PUSH_OBJECT(*obj);
 }
@@ -3766,28 +3767,26 @@ BEGIN_CASE(JSOP_SETTER)
 }
 
 BEGIN_CASE(JSOP_HOLE)
     PUSH_HOLE();
 END_CASE(JSOP_HOLE)
 
 BEGIN_CASE(JSOP_NEWINIT)
 {
-    jsint i = regs.pc[1];
-
+    uint8_t i = GET_UINT8(regs.pc);
     JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
+
     JSObject *obj;
-
     if (i == JSProto_Array) {
         obj = NewDenseEmptyArray(cx);
     } else {
         gc::AllocKind kind = GuessObjectGCKind(0);
         obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
     }
-
     if (!obj)
         goto error;
 
     TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, (JSProtoKey) i);
     if (!type)
         goto error;
     obj->setType(type);
 
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -87,21 +87,18 @@ typedef union jsdpun {
     jsdouble d;
 } jsdpun;
 
 static inline int
 JSDOUBLE_IS_NaN(jsdouble d)
 {
     jsdpun u;
     u.d = d;
-#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_)
-    return (u.u64 & ~JSDOUBLE_SIGNBIT) > JSDOUBLE_EXPMASK;
-#else
-    return (u.s.hi & JSDOUBLE_HI32_NAN) == JSDOUBLE_HI32_NAN;
-#endif
+    return (u.u64 & JSDOUBLE_EXPMASK) == JSDOUBLE_EXPMASK &&
+           (u.u64 & JSDOUBLE_MANTMASK) != 0;
 }
 
 static inline int
 JSDOUBLE_IS_FINITE(jsdouble d)
 {
     /* -0 is finite. NaNs are not. */
     jsdpun u;
     u.d = d;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -418,46 +418,45 @@ JS_FRIEND_API(JSBool)
 js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp)
 {
     return js_DisassembleAtPC(cx, script, lines, NULL, sp);
 }
 
 JS_FRIEND_API(JSBool)
 js_DumpPC(JSContext *cx)
 {
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return JS_FALSE;
     JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter);
-    fprintf(stdout, "%s", sprinter.base);
+    fprintf(stdout, "%s", sprinter.string());
     return ok;
 }
 
 JSBool
 js_DumpScript(JSContext *cx, JSScript *script)
 {
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return JS_FALSE;
     JSBool ok = js_Disassemble(cx, script, true, &sprinter);
-    fprintf(stdout, "%s", sprinter.base);
+    fprintf(stdout, "%s", sprinter.string());
     return ok;
 }
 
 static char *
 QuoteString(Sprinter *sp, JSString *str, uint32_t quote);
 
 static bool
 ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes)
 {
     if (JSVAL_IS_STRING(v)) {
-        Sprinter sprinter;
-        LifoAlloc &tla = cx->tempLifoAlloc();
-        LifoAllocScope las(&tla);
-        INIT_SPRINTER(cx, &sprinter, &tla, 0);
+        Sprinter sprinter(cx);
+        if (!sprinter.init())
+            return false;
         char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"');
         if (!nbytes)
             return false;
         nbytes = JS_sprintf_append(NULL, "%s", nbytes);
         if (!nbytes)
             return false;
         bytes->initBytes(nbytes);
         return true;
@@ -566,50 +565,56 @@ js_Disassemble1(JSContext *cx, JSScript 
 
       case JOF_JUMP: {
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         Sprint(sp, " %u (%+d)", loc + (intN) off, (intN) off);
         break;
       }
 
       case JOF_ATOM:
-      case JOF_OBJECT:
-      case JOF_REGEXP: {
+      case JOF_OBJECT: {
         uintN index = js_GetIndexFromBytecode(script, pc, 0);
         jsval v;
         if (type == JOF_ATOM) {
             if (op == JSOP_DOUBLE) {
                 v = script->getConst(index);
             } else {
                 JSAtom *atom = script->getAtom(index);
                 v = STRING_TO_JSVAL(atom);
             }
         } else {
-            JSObject *obj;
-            if (type == JOF_OBJECT) {
-                /* Don't call obj.toSource if analysis/inference is active. */
-                if (cx->compartment->activeAnalysis) {
-                    Sprint(sp, " object");
-                    break;
-                }
-                obj = script->getObject(index);
-            } else {
-                obj = script->getRegExp(index);
+            JS_ASSERT(type == JOF_OBJECT);
+
+            /* Don't call obj.toSource if analysis/inference is active. */
+            if (cx->compartment->activeAnalysis) {
+                Sprint(sp, " object");
+                break;
             }
+
+            JSObject *obj = script->getObject(index);
             v = OBJECT_TO_JSVAL(obj);
         }
         {
             JSAutoByteString bytes;
             if (!ToDisassemblySource(cx, v, &bytes))
                 return 0;
             Sprint(sp, " %s", bytes.ptr());
         }
         break;
       }
 
+      case JOF_REGEXP: {
+        JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc));
+        JSAutoByteString bytes;
+        if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes))
+            return 0;
+        Sprint(sp, " %s", bytes.ptr());
+        break;
+      }
+
       case JOF_TABLESWITCH:
       {
         jsint i, low, high;
 
         ptrdiff_t off = GET_JUMP_OFFSET(pc);
         jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
         low = GET_JUMP_OFFSET(pc2);
         pc2 += JUMP_OFFSET_LEN;
@@ -653,28 +658,20 @@ js_Disassemble1(JSContext *cx, JSScript 
       case JOF_QARG:
         Sprint(sp, " %u", GET_ARGNO(pc));
         break;
 
       case JOF_LOCAL:
         Sprint(sp, " %u", GET_SLOTNO(pc));
         break;
 
-      case JOF_SLOTATOM:
       case JOF_SLOTOBJECT: {
         Sprint(sp, " %u", GET_SLOTNO(pc));
         uintN index = js_GetIndexFromBytecode(script, pc, SLOTNO_LEN);
-        jsval v;
-        if (type == JOF_SLOTATOM) {
-            JSAtom *atom = script->getAtom(index);
-            v = STRING_TO_JSVAL(atom);
-        } else {
-            v = OBJECT_TO_JSVAL(script->getObject(index));
-        }
-
+        jsval v = OBJECT_TO_JSVAL(script->getObject(index));
         JSAutoByteString bytes;
         if (!ToDisassemblySource(cx, v, &bytes))
             return 0;
         Sprint(sp, " %s", bytes.ptr());
         break;
       }
 
       {
@@ -691,17 +688,17 @@ js_Disassemble1(JSContext *cx, JSScript 
         goto print_int;
 
       case JOF_UINT24:
         JS_ASSERT(op == JSOP_UINT24 || op == JSOP_NEWARRAY);
         i = (jsint)GET_UINT24(pc);
         goto print_int;
 
       case JOF_UINT8:
-        i = pc[1];
+        i = GET_UINT8(pc);
         goto print_int;
 
       case JOF_INT8:
         i = GET_INT8(pc);
         goto print_int;
 
       case JOF_INT32:
         JS_ASSERT(op == JSOP_INT32);
@@ -722,107 +719,247 @@ js_Disassemble1(JSContext *cx, JSScript 
     SprintCString(sp, "\n");
     return len;
 }
 
 #endif /* DEBUG */
 
 /************************************************************************/
 
-#define OFF2STR(sp,off) ((sp)->base + (off))
-#define STR2OFF(sp,str) ((str) - (sp)->base)
-#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str))
-
-static JSBool
-SprintEnsureBuffer(Sprinter *sp, size_t len)
+const size_t Sprinter::DefaultSize = 64;
+
+bool 
+Sprinter::realloc_(size_t newSize)
 {
-    if (sp->offset + len < sp->size)
-        return JS_TRUE;
-
-    ptrdiff_t nb = Max(Max((size_t)1024, sp->size * 2), sp->offset + len + 1);
-    char *base = sp->base;
+    JS_ASSERT(newSize > (size_t) offset);
+    char *newBuf = (char *) context->realloc_(base, newSize);
+    if (!newBuf)
+        return false;
+    base = newBuf;
+    size = newSize;
+    base[size - 1] = 0;
+    return true;
+}
+
+Sprinter::Sprinter(JSContext *cx)
+  : context(cx),
+#ifdef DEBUG
+    initialized(false),
+#endif
+    base(NULL), size(0), offset(0)
+{ }
+
+Sprinter::~Sprinter()
+{
+#ifdef DEBUG
+    if (initialized)
+        checkInvariants();
+#endif
+    context->free_(base);
+}
+
+bool
+Sprinter::init()
+{
+    JS_ASSERT(!initialized);
+    base = (char *) context->malloc_(DefaultSize);
     if (!base)
-        base = static_cast<char *>(sp->pool->allocUnaligned(nb));
-    else
-        base = static_cast<char *>(sp->pool->reallocUnaligned(base, sp->size, nb - sp->size));
-    if (!base) {
-        js_ReportOutOfMemory(sp->context);
-        return JS_FALSE;
-    }
-    sp->base = base;
-    sp->size = nb;
-    return JS_TRUE;
+        return false;
+#ifdef DEBUG
+    initialized = true;
+#endif
+    *base = 0;
+    size = DefaultSize;
+    base[size - 1] = 0;
+    return true;
 }
 
-namespace js {
+void
+Sprinter::checkInvariants() const
+{
+    JS_ASSERT(initialized);
+    JS_ASSERT((size_t) offset < size);
+    JS_ASSERT(base[size - 1] == 0);
+}
+
+const char *
+Sprinter::string() const
+{
+    return base;
+}
+
+const char *
+Sprinter::stringEnd() const
+{
+    return base + offset;
+}
 
 char *
-SprintReserveAmount(Sprinter *sp, size_t len)
+Sprinter::stringAt(ptrdiff_t off) const
+{
+    JS_ASSERT(off >= 0 && (size_t) off < size);
+    return base + off;
+}
+
+char &
+Sprinter::operator[](size_t off)
+{
+    JS_ASSERT(off >= 0 && (size_t) off < size);
+    return *(base + off);
+}
+
+bool
+Sprinter::empty() const
 {
-    /* Allocate space for s, including the '\0' at the end. */
-    if (!SprintEnsureBuffer(sp, len))
-        return NULL;
-
-    /* Advance offset and return the previous offset for caller to fill. */
-    ptrdiff_t offset = sp->offset;
-    sp->offset += len;
-    return sp->base + offset;
+    return *base == 0;
+}
+
+char *
+Sprinter::reserve(size_t len)
+{
+    InvariantChecker ic(this);
+
+    while (len + 1 > size - offset) { /* Include trailing \0 */
+        if (!realloc_(size * 2))
+            return NULL;
+    }
+
+    char *sb = base + offset;
+    offset += len;
+    return sb;
+}
+
+char *
+Sprinter::reserveAndClear(size_t len)
+{
+    char *sb = reserve(len);
+    if (sb)
+        memset(sb, 0, len);
+    return sb;
 }
 
 ptrdiff_t
-SprintPut(Sprinter *sp, const char *s, size_t len)
+Sprinter::put(const char *s, size_t len)
 {
-    ptrdiff_t offset = sp->size; /* save old size */
-    char *bp = sp->base;         /* save old base */
-
-    /* Allocate space for s, including the '\0' at the end. */
-    if (!SprintEnsureBuffer(sp, len))
+    InvariantChecker ic(this);
+
+    const char *oldBase = base;
+    const char *oldEnd = base + size;
+
+    ptrdiff_t oldOffset = offset;
+    char *bp = reserve(len);
+    if (!bp)
+        return -1;
+
+    /* s is within the buffer already */
+    if (s >= oldBase && s < oldEnd) {
+        /* buffer was realloc'ed */
+        if (base != oldBase)
+            s = stringAt(s - oldBase);  /* this is where it lives now */
+        memmove(bp, s, len);
+    } else {
+        JS_ASSERT(s < base || s >= base + size);
+        memcpy(bp, s, len);
+    }
+
+    bp[len] = 0;
+    return oldOffset;
+}
+
+ptrdiff_t
+Sprinter::putString(JSString *s)
+{
+    InvariantChecker ic(this);
+
+    size_t length = s->length();
+    const jschar *chars = s->getChars(context);
+    if (!chars)
+        return -1;
+
+    size_t size = GetDeflatedStringLength(context, chars, length);
+    if (size == (size_t) -1)
         return -1;
 
-    if (sp->base != bp &&               /* buffer was realloc'ed */
-        s >= bp && s < bp + offset) {   /* s was within the buffer */
-        s = sp->base + (s - bp);        /* this is where it lives now */
-    }
-
-    /* Advance offset and copy s into sp's buffer. */
-    offset = sp->offset;
-    sp->offset += len;
-    bp = sp->base + offset;
-    memmove(bp, s, len);
-    bp[len] = 0;
+    ptrdiff_t oldOffset = offset;
+    char *buffer = reserve(size);
+    if (!buffer)
+        return -1;
+    DeflateStringToBuffer(context, chars, length, buffer, &size);
+    buffer[size] = 0;
+
+    return oldOffset;
+}
+
+int
+Sprinter::printf(const char *fmt, ...)
+{
+    InvariantChecker ic(this);
+
+    do {
+        va_list va;
+        va_start(va, fmt);
+        int i = vsnprintf(base + offset, size - offset, fmt, va);
+        va_end(va);
+
+        if (i > -1 && (size_t) i < size - offset) {
+            offset += i;
+            return i;
+        }
+    } while (realloc_(size * 2));
+
+    return -1;
+}
+
+void
+Sprinter::setOffset(const char *end)
+{
+    JS_ASSERT(end >= base && end < base + size);
+    offset = end - base;
+}
+
+void
+Sprinter::setOffset(ptrdiff_t off)
+{
+    JS_ASSERT(off >= 0 && (size_t) off < size);
+    offset = off;
+}
+
+ptrdiff_t
+Sprinter::getOffset() const
+{
     return offset;
 }
 
 ptrdiff_t
-SprintCString(Sprinter *sp, const char *s)
+Sprinter::getOffsetOf(const char *string) const
+{
+    JS_ASSERT(string >= base && string < base + size);
+    return string - base;
+}
+
+ptrdiff_t
+js::SprintPut(Sprinter *sp, const char *s, size_t len)
+{
+    return sp->put(s, len);
+}
+
+ptrdiff_t
+js::SprintCString(Sprinter *sp, const char *s)
 {
     return SprintPut(sp, s, strlen(s));
 }
 
 ptrdiff_t
-SprintString(Sprinter *sp, JSString *str)
+js::SprintString(Sprinter *sp, JSString *str)
 {
-    size_t length = str->length();
-    const jschar *chars = str->getChars(sp->context);
-    if (!chars)
-        return -1;
-
-    size_t size = GetDeflatedStringLength(sp->context, chars, length);
-    if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size))
-        return -1;
-
-    ptrdiff_t offset = sp->offset;
-    sp->offset += size;
-    DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, &size);
-    sp->base[sp->offset] = 0;
-    return offset;
+    return sp->putString(str);
 }
 
 ptrdiff_t
-Sprint(Sprinter *sp, const char *format, ...)
+js::Sprint(Sprinter *sp, const char *format, ...)
 {
     va_list ap;
     char *bp;
     ptrdiff_t offset;
 
     va_start(ap, format);
     bp = JS_vsmprintf(format, ap);      /* XXX vsaprintf */
     va_end(ap);
@@ -830,18 +967,16 @@ Sprint(Sprinter *sp, const char *format,
         JS_ReportOutOfMemory(sp->context);
         return -1;
     }
     offset = SprintCString(sp, bp);
     sp->context->free_(bp);
     return offset;
 }
 
-} // namespace js
-
 const char js_EscapeMap[] = {
     '\b', 'b',
     '\f', 'f',
     '\n', 'n',
     '\r', 'r',
     '\t', 't',
     '\v', 'v',
     '"',  '"',
@@ -853,17 +988,17 @@ const char js_EscapeMap[] = {
 #define DONT_ESCAPE     0x10000
 
 static char *
 QuoteString(Sprinter *sp, JSString *str, uint32_t quote)
 {
     /* Sample off first for later return value pointer computation. */
     JSBool dontEscape = (quote & DONT_ESCAPE) != 0;
     jschar qc = (jschar) quote;
-    ptrdiff_t off = sp->offset;
+    ptrdiff_t offset = sp->getOffset();
     if (qc && Sprint(sp, "%c", (char)qc) < 0)
         return NULL;
 
     const jschar *s = str->getChars(sp->context);
     if (!s)
         return NULL;
     const jschar *z = s + str->length();
 
@@ -871,28 +1006,28 @@ QuoteString(Sprinter *sp, JSString *str,
     for (const jschar *t = s; t < z; s = ++t) {
         /* Move t forward from s past un-quote-worthy characters. */
         jschar c = *t;
         while (c < 127 && isprint(c) && c != qc && c != '\\' && c != '\t') {
             c = *++t;
             if (t == z)
                 break;
         }
-        ptrdiff_t len = t - s;
-
-        /* Allocate space for s, including the '\0' at the end. */
-        if (!SprintEnsureBuffer(sp, len))
-            return NULL;
-
-        /* Advance sp->offset and copy s into sp's buffer. */
-        char *bp = sp->base + sp->offset;
-        sp->offset += len;
-        while (--len >= 0)
-            *bp++ = (char) *s++;
-        *bp = '\0';
+
+        {
+            ptrdiff_t len = t - s;
+            ptrdiff_t base = sp->getOffset();
+            char *bp = sp->reserve(len);
+            if (!bp)
+                return NULL;
+
+            for (ptrdiff_t i = 0; i < len; ++i)
+                (*sp)[base + i] = (char) *s++;
+            (*sp)[base + len] = 0;
+        }
 
         if (t == z)
             break;
 
         /* Use js_EscapeMap, \u, or \x only if necessary. */
         bool ok;
         const char *e;
         if (!(c >> 8) && c != 0 && (e = strchr(js_EscapeMap, (int)c)) != NULL) {
@@ -912,29 +1047,30 @@ QuoteString(Sprinter *sp, JSString *str,
     }
 
     /* Sprint the closing quote and return the quoted string. */
     if (qc && Sprint(sp, "%c", (char)qc) < 0)
         return NULL;
 
     /*
      * If we haven't Sprint'd anything yet, Sprint an empty string so that
-     * the OFF2STR below gives a valid result.
+     * the return below gives a valid result.
      */
-    if (off == sp->offset && Sprint(sp, "") < 0)
+    if (offset == sp->getOffset() && Sprint(sp, "") < 0)
         return NULL;
-    return OFF2STR(sp, off);
+
+    return sp->stringAt(offset);
 }
 
 JSString *
 js_QuoteString(JSContext *cx, JSString *str, jschar quote)
 {
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return NULL;
     char *bytes = QuoteString(&sprinter, str, quote);
     JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL;
     return escstr;
 }
 
 /************************************************************************/
 
 /*
@@ -991,17 +1127,19 @@ struct JSPrinter
 
 JSPrinter *
 js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun,
               uintN indent, JSBool pretty, JSBool grouped, JSBool strict)
 {
     JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter));
     if (!jp)
         return NULL;
-    INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
+    new (&jp->sprinter) Sprinter(cx);
+    if (!jp->sprinter.init())
+        return NULL;
     new (&jp->pool) LifoAlloc(1024);
     jp->indent = indent;
     jp->pretty = !!pretty;
     jp->grouped = !!grouped;
     jp->strict = !!strict;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->pcstack = NULL;
@@ -1025,19 +1163,17 @@ js_DestroyPrinter(JSPrinter *jp)
     Foreground::delete_(jp->localNames);
     jp->sprinter.context->free_(jp);
 }
 
 JSString *
 js_GetPrinterOutput(JSPrinter *jp)
 {
     JSContext *cx = jp->sprinter.context;
-    if (!jp->sprinter.base)
-        return cx->runtime->emptyString;
-    return JS_NewStringCopyZ(cx, jp->sprinter.base);
+    return JS_NewStringCopyZ(cx, jp->sprinter.string());
 }
 
 /* Mark the parent and offset into the parent's text for a printed opcode. */
 static inline void
 UpdateDecompiledParent(JSPrinter *jp, jsbytecode *pc, jsbytecode *parent, size_t offset)
 {
     if (jp->decompiledOpcodes && pc) {
         jp->decompiled(pc).parent = parent;
@@ -1128,29 +1264,35 @@ struct SprintStack
     Sprinter    sprinter;       /* sprinter for postfix to infix buffering */
     ptrdiff_t   *offsets;       /* stack of postfix string offsets */
     jsbytecode  *opcodes;       /* parallel stack of JS opcodes */
     jsbytecode  **bytecodes;    /* actual script bytecode pushing the value */
     uintN       top;            /* top of stack index */
     uintN       inArrayInit;    /* array initialiser/comprehension level */
     JSBool      inGenExp;       /* in generator expression */
     JSPrinter   *printer;       /* permanent output goes here */
+
+    explicit SprintStack(JSContext *cx)
+      : sprinter(cx), offsets(NULL),
+        opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0),
+        inGenExp(JS_FALSE), printer(NULL)
+    { }
 };
 
 /*
  * Set the decompiled text of an opcode, according to an offset into the
  * print stack's sprinter buffer.
  */
 static inline bool
 UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo)
 {
     JSPrinter *jp = ss->printer;
 
     if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) {
-        const char *text = OFF2STR(&ss->sprinter, todo);
+        const char *text = ss->sprinter.stringAt(todo);
         size_t len = strlen(text) + 1;
 
         char *ntext = ss->printer->pool.newArrayUninitialized<char>(len);
         if (!ntext) {
             js_ReportOutOfMemory(ss->sprinter.context);
             return false;
         }
 
@@ -1176,34 +1318,36 @@ SprintDupeStr(SprintStack *ss, const cha
 
     return nstr;
 }
 
 /* Place an opcode's decompiled text into a printer's permanent output. */
 static inline void
 SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc)
 {
-    UpdateDecompiledParent(jp, pc, NULL, jp->sprinter.offset);
+    ptrdiff_t offset = jp->sprinter.getOffset();
+    UpdateDecompiledParent(jp, pc, NULL, offset);
     js_printf(jp, "%s", str);
 }
 
 /*
  * Place an opcode's decompiled text into the printed output for another
  * opcode parentpc, where startOffset indicates the printer offset for the
  * start of parentpc.
  */
 static inline void
 SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc,
              jsbytecode *parentpc, ptrdiff_t startOffset)
 {
     if (startOffset < 0) {
         JS_ASSERT(ss->sprinter.context->isExceptionPending());
         return;
     }
-    UpdateDecompiledParent(ss->printer, pc, parentpc, ss->sprinter.offset - startOffset);
+    ptrdiff_t offset = ss->sprinter.getOffset();
+    UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset);
     SprintCString(&ss->sprinter, str);
 }
 
 /*
  * Copy the decompiled text for an opcode to all other ops which it was
  * decomposed into.
  */
 static inline void
@@ -1284,76 +1428,68 @@ GetOff(SprintStack *ss, uintN i)
         if (bytes != FAILED_EXPRESSION_DECOMPILER) {
             off = SprintCString(&ss->sprinter, bytes);
             if (off < 0)
                 off = 0;
             ss->offsets[i] = off;
             ss->sprinter.context->free_(bytes);
             return off;
         }
-        if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) {
-            memset(ss->sprinter.base, 0, ss->sprinter.offset);
+
+        if (!*ss->sprinter.string()) {
+            memset(ss->sprinter.stringAt(0), 0, ss->sprinter.getOffset());
             ss->offsets[i] = -1;
         }
     }
     return 0;
 }
 
 static const char *
 GetStr(SprintStack *ss, uintN i)
 {
-    ptrdiff_t off;
-
-    /*
-     * Must call GetOff before using ss->sprinter.base, since it may be null
-     * until bootstrapped by GetOff.
-     */
-    off = GetOff(ss, i);
-    return OFF2STR(&ss->sprinter, off);
+    ptrdiff_t off = GetOff(ss, i);
+    return ss->sprinter.stringAt(off);
 }
 
 /*
  * Gap between stacked strings to allow for insertion of parens and commas
  * when auto-parenthesizing expressions and decompiling array initialisers.
  */
 #define PAREN_SLOP      (2 + 1)
 
 /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */
 JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255);
 
-static void
+static inline void
 AddParenSlop(SprintStack *ss)
 {
-    memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP);
-    ss->sprinter.offset += PAREN_SLOP;
+    ss->sprinter.reserveAndClear(PAREN_SLOP);
 }
 
 static JSBool
 PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL)
 {
     uintN top;
 
-    if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP))
-        return JS_FALSE;
-
     /* ss->top points to the next free slot; be paranoid about overflow. */
     top = ss->top;
     JS_ASSERT(top < StackDepth(ss->printer->script));
     if (top >= StackDepth(ss->printer->script)) {
         JS_ReportOutOfMemory(ss->sprinter.context);
         return JS_FALSE;
     }
 
     /* The opcodes stack must contain real bytecodes that index js_CodeSpec. */
     ss->offsets[top] = off;
     ss->opcodes[top] = jsbytecode((op == JSOP_GETPROP2) ? JSOP_GETPROP
                                 : (op == JSOP_GETELEM2) ? JSOP_GETELEM
                                 : op);
     ss->bytecodes[top] = pc;
     ss->top = ++top;
+
     AddParenSlop(ss);
     return JS_TRUE;
 }
 
 static bool
 PushStr(SprintStack *ss, const char *str, JSOp op)
 {
     ptrdiff_t off = SprintCString(&ss->sprinter, str);
@@ -1382,33 +1518,34 @@ PopOffPrec(SprintStack *ss, uint8_t prec
     off = GetOff(ss, top);
     topcs = &js_CodeSpec[ss->opcodes[top]];
 
     jsbytecode *pc = ss->bytecodes[top];
     if (ppc)
         *ppc = pc;
 
     if (topcs->prec != 0 && topcs->prec < prec) {
-        ss->sprinter.offset = ss->offsets[top] = off - 2;
-        off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
+        ss->offsets[top] = off - 2;
+        ss->sprinter.setOffset(off - 2);
+        off = Sprint(&ss->sprinter, "(%s)", ss->sprinter.stringAt(off));
         if (ss->printer->decompiledOpcodes && pc)
             ss->printer->decompiled(pc).parenthesized = true;
     } else {
-        ss->sprinter.offset = off;
+        ss->sprinter.setOffset(off);
     }
     return off;
 }
 
 static const char *
 PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL)
 {
     ptrdiff_t off;
 
     off = PopOffPrec(ss, prec, ppc);
-    return OFF2STR(&ss->sprinter, off);
+    return ss->sprinter.stringAt(off);
 }
 
 /*
  * As for PopStrPrec, but duplicates the string into the printer's arena.
  * Strings returned by PopStrPrec are otherwise invalidated if any new text
  * is printed into ss.
  */
 static const char *
@@ -1512,17 +1649,17 @@ Decompile(SprintStack *ss, jsbytecode *p
 static JSBool
 DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength,
                 jsbytecode *pc, ptrdiff_t switchLength,
                 ptrdiff_t defaultOffset, JSBool isCondSwitch)
 {
     JSContext *cx;
     JSPrinter *jp;
     ptrdiff_t off, off2, diff, caseExprOff, todo;
-    char *rval;
+    const char *rval;
     uintN i;
     jsval key;
     JSString *str;
 
     cx = ss->sprinter.context;
     jp = ss->printer;
 
     jsbytecode *lvalpc;
@@ -1592,24 +1729,24 @@ DecompileSwitch(SprintStack *ss, TableEn
                         return JS_FALSE;
                     str = NULL;
                 } else {
                     str = ToString(cx, key);
                     if (!str)
                         return JS_FALSE;
                 }
                 if (todo >= 0) {
-                    rval = OFF2STR(&ss->sprinter, todo);
+                    rval = ss->sprinter.stringAt(todo);
                 } else {
                     rval = QuoteString(&ss->sprinter, str, (jschar)
                                        (JSVAL_IS_STRING(key) ? '"' : 0));
                     if (!rval)
                         return JS_FALSE;
                 }
-                RETRACT(&ss->sprinter, rval);
+                ss->sprinter.setOffset(rval);
                 jp->indent += 2;
                 js_printf(jp, "\tcase %s:\n", rval);
             }
 
             jp->indent += 2;
             if (off <= defaultOffset && defaultOffset < off2) {
                 diff = defaultOffset - off;
                 if (diff != 0) {
@@ -1678,30 +1815,30 @@ GetLocalInSlot(SprintStack *ss, jsint i,
             if (!JSID_IS_ATOM(shape.propid()))
                 continue;
 
             JSAtom *atom = JSID_TO_ATOM(shape.propid());
             const char *rval = QuoteString(&ss->sprinter, atom, 0);
             if (!rval)
                 return NULL;
 
-            RETRACT(&ss->sprinter, rval);
+            ss->sprinter.setOffset(rval);
             return rval;
         }
     }
 
     return GetStr(ss, i);
 }
 
 const char *
 GetLocal(SprintStack *ss, jsint i)
 {
     ptrdiff_t off = ss->offsets[i];
     if (off >= 0)
-        return OFF2STR(&ss->sprinter, off);
+        return ss->sprinter.stringAt(off);
 
     /*
      * We must be called from js_DecompileValueGenerator (via Decompile) when
      * dereferencing a local that's undefined or null. Search script->objects
      * for the block containing this local by its stack index, i.
      *
      * In case of destructuring's use of JSOP_GETLOCAL, however, there may be
      * no such local. This could mean no blocks (no script objects at all, or
@@ -1917,28 +2054,28 @@ DecompileDestructuringLHS(SprintStack *s
         LOCAL_ASSERT(!letNames);
         /*
          * We may need to auto-parenthesize the left-most value decompiled
          * here, so add back PAREN_SLOP temporarily.  Then decompile until the
          * opcode that would reduce the stack depth to (ss->top-1), which we
          * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for
          * the nb parameter.
          */
-        ptrdiff_t todo = ss->sprinter.offset;
-        ss->sprinter.offset = todo + PAREN_SLOP;
+        ptrdiff_t todo = ss->sprinter.getOffset();
+        ss->sprinter.setOffset(todo + PAREN_SLOP);
         pc = Decompile(ss, pc, -((intN)ss->top));
         if (!pc)
             return NULL;
         if (pc == endpc)
             return pc;
         LOAD_OP_DATA(pc);
         LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM);
         xval = PopStr(ss, JSOP_NOP);
         lval = PopStr(ss, JSOP_GETPROP);
-        ss->sprinter.offset = todo;
+        ss->sprinter.setOffset(todo);
         if (*lval == '\0') {
             /* lval is from JSOP_BINDNAME, so just print xval. */
             todo = SprintCString(&ss->sprinter, xval);
         } else if (*xval == '\0') {
             /* xval is from JSOP_SETCALL or JSOP_BINDXMLNAME, print lval. */
             todo = SprintCString(&ss->sprinter, lval);
         } else {
             todo = Sprint(&ss->sprinter,
@@ -1980,19 +2117,19 @@ DecompileDestructuring(SprintStack *ss, 
     /*
      * Set head so we can rewrite '[' to '{' as needed.  Back up PAREN_SLOP
      * chars so the destructuring decompilation accumulates contiguously in
      * ss->sprinter starting with "[".
      */
     ptrdiff_t head = SprintPut(&ss->sprinter, "[", 1);
     if (head < 0 || !PushOff(ss, head, JSOP_NOP))
         return NULL;
-    ss->sprinter.offset -= PAREN_SLOP;
-    LOCAL_ASSERT(head == ss->sprinter.offset - 1);
-    LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '[');
+    ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
+    LOCAL_ASSERT(head == ss->sprinter.getOffset() - 1);
+    LOCAL_ASSERT(ss->sprinter[head] == '[');
 
     int lasti = -1;
 
     while (pc < endpc) {
 #if JS_HAS_DESTRUCTURING_SHORTHAND
         ptrdiff_t nameoff = -1;
 #endif
 
@@ -2029,17 +2166,17 @@ DecompileDestructuring(SprintStack *ss, 
             pc += oplen;
             if (pc == endpc)
                 return pc;
             LOAD_OP_DATA(pc);
             LOCAL_ASSERT(op == JSOP_GETELEM);
 
             /* Distinguish object from array by opcode or source note. */
             if (sn && SN_TYPE(sn) == SRC_INITPROP) {
-                *OFF2STR(&ss->sprinter, head) = '{';
+                ss->sprinter[head] = '{';
                 if (Sprint(&ss->sprinter, "%g: ", d) < 0)
                     return NULL;
             } else {
                 /* Sanity check for the gnarly control flow above. */
                 LOCAL_ASSERT(i == d);
 
                 /* Fill in any holes (holes at the end don't matter). */
                 while (++lasti < i) {
@@ -2050,19 +2187,19 @@ DecompileDestructuring(SprintStack *ss, 
             break;
           }
 
           case JSOP_GETPROP:
           case JSOP_LENGTH:
           {
             JSAtom *atom;
             LOAD_ATOM(0);
-            *OFF2STR(&ss->sprinter, head) = '{';
+            ss->sprinter[head] = '{';
 #if JS_HAS_DESTRUCTURING_SHORTHAND
-            nameoff = ss->sprinter.offset;
+            nameoff = ss->sprinter.getOffset();
 #endif
             if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\''))
                 return NULL;
             if (SprintPut(&ss->sprinter, ": ", 2) < 0)
                 return NULL;
             break;
           }
 
@@ -2083,37 +2220,37 @@ DecompileDestructuring(SprintStack *ss, 
         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole, letNames);
         if (!pc)
             return NULL;
 
 #if JS_HAS_DESTRUCTURING_SHORTHAND
         if (nameoff >= 0) {
             ptrdiff_t offset, initlen;
 
-            offset = ss->sprinter.offset;
-            LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0');
+            offset = ss->sprinter.getOffset();
+            LOCAL_ASSERT(ss->sprinter[offset] == '\0');
             initlen = offset - nameoff;
             LOCAL_ASSERT(initlen >= 4);
 
             /* Early check to rule out odd "name: lval" length. */
             if (((size_t)initlen & 1) == 0) {
                 size_t namelen;
                 const char *name;
 
                 /*
                  * Even "name: lval" string length: check for "x: x" and the
                  * like, and apply the shorthand if we can.
                  */
                 namelen = (size_t)(initlen - 2) >> 1;
-                name = OFF2STR(&ss->sprinter, nameoff);
+                name = ss->sprinter.stringAt(nameoff);
                 if (!strncmp(name + namelen, ": ", 2) &&
                     !strncmp(name, name + namelen + 2, namelen)) {
                     offset -= namelen + 2;
-                    *OFF2STR(&ss->sprinter, offset) = '\0';
-                    ss->sprinter.offset = offset;
+                    ss->sprinter[offset] = '\0';
+                    ss->sprinter.setOffset(offset);
                 }
             }
         }
 #endif
 
         if (pc == endpc || *pc != JSOP_DUP)
             break;
 
@@ -2135,17 +2272,17 @@ DecompileDestructuring(SprintStack *ss, 
 
         if (!hole && SprintPut(&ss->sprinter, ", ", 2) < 0)
             return NULL;
 
         pc += JSOP_DUP_LENGTH;
     }
 
 out:
-    const char *lval = OFF2STR(&ss->sprinter, head);
+    const char *lval = ss->sprinter.stringAt(head);
     if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0)
         return NULL;
     return pc;
 }
 
 static jsbytecode *
 DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc,
                          jssrcnote *sn, ptrdiff_t *todop)
@@ -2158,17 +2295,17 @@ DecompileGroupAssignment(SprintStack *ss
     const char *rval;
 
     LOAD_OP_DATA(pc);
     LOCAL_ASSERT(op == JSOP_GETLOCAL);
 
     todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn));
     if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
         return NULL;
-    ss->sprinter.offset -= PAREN_SLOP;
+    ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
 
     for (;;) {
         pc += oplen;
         if (pc == endpc)
             return pc;
         pc = DecompileDestructuringLHS(ss, pc, endpc, &hole);
         if (!pc)
             return NULL;
@@ -2193,17 +2330,17 @@ DecompileGroupAssignment(SprintStack *ss
                    (i == start) ? "%s" : ", %s",
                    (i == end - 1 && *rval == '\0') ? ", " : rval) < 0) {
             return NULL;
         }
     }
 
     if (SprintPut(&ss->sprinter, "]", 1) < 0)
         return NULL;
-    ss->sprinter.offset = ss->offsets[i];
+    ss->sprinter.setOffset(ss->offsets[i]);
     ss->top = start;
     *todop = todo;
     return pc;
 }
 
 #undef LOCAL_ASSERT
 #undef LOAD_OP_DATA
 
@@ -2240,17 +2377,17 @@ GetBlockNames(JSContext *cx, StaticBlock
     return true;
 }
 
 static bool
 PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms)
 {
     for (size_t i = 0; i < atoms.length(); i++) {
         const char *name = QuoteString(&ss->sprinter, atoms[i], 0);
-        if (!name || !PushOff(ss, STR2OFF(&ss->sprinter, name), JSOP_ENTERBLOCK))
+        if (!name || !PushOff(ss, ss->sprinter.getOffsetOf(name), JSOP_ENTERBLOCK))
             return false;
     }
     return true;
 }
 
 /*
  * In the scope of a let, the variables' (decompiler) stack slots must contain
  * the corresponding variable's name. This function updates the N top slots
@@ -2385,17 +2522,17 @@ SprintNormalFor(JSContext *cx, JSPrinter
          */
         uintN saveTop = ss->top;
 
         if (!Decompile(ss, pc + next, cond - next - JSOP_POP_LENGTH))
             return -1;
         LOCAL_ASSERT(ss->top - saveTop <= 1U);
         jsbytecode *updatepc = NULL;
         const char *update = (ss->top == saveTop)
-                             ? ss->sprinter.base + ss->sprinter.offset
+                             ? ss->sprinter.stringEnd()
                              : PopStr(ss, JSOP_NOP, &updatepc);
         js_printf(jp, " ");
         SprintOpcodePermanent(jp, update, updatepc);
     }
 
     /* Do the loop body. */
     js_printf(jp, ") {\n");
     jp->indent += 4;
@@ -2411,17 +2548,19 @@ SprintNormalFor(JSContext *cx, JSPrinter
     return -2;
 }
 
 #undef LOCAL_ASSERT
 
 static JSBool
 InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth)
 {
-    INIT_SPRINTER(cx, &ss->sprinter, &cx->tempLifoAlloc(), PAREN_SLOP);
+    if (!ss->sprinter.init())
+        return JS_FALSE;
+    ss->sprinter.setOffset(PAREN_SLOP);
 
     /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */
     size_t offsetsz = depth * sizeof(ptrdiff_t);
     size_t opcodesz = depth * sizeof(jsbytecode);
     size_t bytecodesz = depth * sizeof(jsbytecode *);
     void *space = cx->tempLifoAlloc().alloc(offsetsz + opcodesz + bytecodesz);
     if (!space) {
         js_ReportOutOfMemory(cx);
@@ -2524,19 +2663,16 @@ Decompile(SprintStack *ss, jsbytecode *p
     JS_END_MACRO
 
 #define LOAD_OBJECT(PCOFF)                                                    \
     GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
 
 #define LOAD_FUNCTION(PCOFF)                                                  \
     GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun)
 
-#define LOAD_REGEXP(PCOFF)                                                    \
-    GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj)
-
 #define GET_SOURCE_NOTE_ATOM(sn, atom)                                        \
     JS_BEGIN_MACRO                                                            \
         jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0);        \
                                                                               \
         LOCAL_ASSERT(atomIndex_ < jp->script->natoms);                        \
         (atom) = jp->script->atoms[atomIndex_];                               \
     JS_END_MACRO
 
@@ -2738,17 +2874,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     rval = PopStr(ss, op, &lastrvalpc);
                     (void)PopStr(ss, op, &lastlvalpc);
 
                     /* Print only the right operand of the assignment-op. */
                     todo = SprintCString(&ss->sprinter, rval);
                 } else if (!inXML) {
                     rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc);
                     lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc);
-                    todo = ss->sprinter.offset;
+                    todo = ss->sprinter.getOffset();
                     SprintOpcode(ss, lval, lvalpc, pc, todo);
                     Sprint(&ss->sprinter, " %s ", token);
                     SprintOpcode(ss, rval, rvalpc, pc, todo);
                 } else {
                     /* In XML, just concatenate the two operands. */
                     LOCAL_ASSERT(op == JSOP_ADD);
                     rval = POP_STR();
                     lval = POP_STR();
@@ -2822,18 +2958,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                   do_function:
                     js_puts(jp, "\n");
                     jp2 = js_NewPrinter(cx, "nested_function", fun,
                                         jp->indent, jp->pretty, jp->grouped,
                                         jp->strict);
                     if (!jp2)
                         return NULL;
                     ok = js_DecompileFunction(jp2);
-                    if (ok && jp2->sprinter.base)
-                        js_puts(jp, jp2->sprinter.base);
+                    if (ok && !jp2->sprinter.empty())
+                        js_puts(jp, jp2->sprinter.string());
                     js_DestroyPrinter(jp2);
                     if (!ok)
                         return NULL;
                     js_puts(jp, "\n\n");
                     break;
 
                   case SRC_BRACE:
                     js_printf(jp, "\t{\n");
@@ -2853,27 +2989,27 @@ Decompile(SprintStack *ss, jsbytecode *p
                 todo = -2;
                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
                   case SRC_LABEL:
                     GET_SOURCE_NOTE_ATOM(sn, atom);
                     jp->indent -= 4;
                     rval = QuoteString(&ss->sprinter, atom, 0);
                     if (!rval)
                         return NULL;
-                    RETRACT(&ss->sprinter, rval);
+                    ss->sprinter.setOffset(rval);
                     js_printf(jp, "\t%s:\n", rval);
                     jp->indent += 4;
                     break;
 
                   case SRC_LABELBRACE:
                     GET_SOURCE_NOTE_ATOM(sn, atom);
                     rval = QuoteString(&ss->sprinter, atom, 0);
                     if (!rval)
                         return NULL;
-                    RETRACT(&ss->sprinter, rval);
+                    ss->sprinter.setOffset(rval);
                     js_printf(jp, "\t%s: {\n", rval);
                     jp->indent += 4;
                     break;
 
                   default:
                     JS_NOT_REACHED("JSOP_LABEL without source note");
                     break;
                 }
@@ -2943,17 +3079,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     break;
 #if JS_HAS_DESTRUCTURING
                 if (sn && SN_TYPE(sn) == SRC_GROUPASSIGN) {
                     todo = Sprint(&ss->sprinter, "%s[] = [",
                                   VarPrefix(sn));
                     if (todo < 0)
                         return NULL;
                     for (uintN i = newtop; i < oldtop; i++) {
-                        rval = OFF2STR(&ss->sprinter, ss->offsets[i]);
+                        rval = ss->sprinter.stringAt(ss->offsets[i]);
                         if (Sprint(&ss->sprinter, ss_format,
                                    (i == newtop) ? "" : ", ",
                                    (i == oldtop - 1 && *rval == '\0')
                                    ? ", " : rval) < 0) {
                             return NULL;
                         }
                     }
                     if (SprintPut(&ss->sprinter, "]", 1) < 0)
@@ -2961,24 +3097,24 @@ Decompile(SprintStack *ss, jsbytecode *p
 
                     /*
                      * If this is an empty group assignment, we have no stack
                      * budget into which we can push our result string. Adjust
                      * ss->sprinter.offset so that our consumer can find the
                      * empty group assignment decompilation.
                      */
                     if (newtop == oldtop) {
-                        ss->sprinter.offset = todo;
+                        ss->sprinter.setOffset(todo);
                     } else {
                         /*
                          * Kill newtop before the end_groupassignment: label by
                          * retracting/popping early.
                          */
                         LOCAL_ASSERT(newtop < oldtop);
-                        ss->sprinter.offset = GetOff(ss, newtop);
+                        ss->sprinter.setOffset(GetOff(ss, newtop));
                         ss->top = newtop;
                     }
 
                   end_groupassignment:
                     LOCAL_ASSERT(*pc == JSOP_POPN);
 
                     /*
                      * Thread directly to the next opcode if we can, to handle
@@ -2986,17 +3122,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * last part of a for(;;) loop head, or in a let block or
                      * expression head.
                      *
                      * NB: todo at this point indexes space in ss->sprinter
                      * that is liable to be overwritten.  The code below knows
                      * exactly how long rval lives, or else copies it down via
                      * SprintCString.
                      */
-                    rval = OFF2STR(&ss->sprinter, todo);
+                    rval = ss->sprinter.stringAt(todo);
                     rvalpc = NULL;
                     todo = -2;
                     pc2 = pc + oplen;
 
                     if (*pc2 == JSOP_NOP) {
                         sn = js_GetSrcNote(jp->script, pc2);
                         if (sn) {
                             if (SN_TYPE(sn) == SRC_FOR) {
@@ -3026,17 +3162,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * just print rval as an expression statement.
                      */
                     if (todo == -2)
                         js_printf(jp, "\t%s;\n", rval);
                     break;
                 }
 #endif
                 if (newtop < oldtop) {
-                    ss->sprinter.offset = GetOff(ss, newtop);
+                    ss->sprinter.setOffset(GetOff(ss, newtop));
                     ss->top = newtop;
                 }
                 break;
               }
 
               case JSOP_EXCEPTION:
                 /* The catch decompiler handles this op itself. */
                 LOCAL_ASSERT(JS_FALSE);
@@ -3079,17 +3215,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     pc += js_GetSrcNoteOffset(sn, 0);
                     len = 0;
 
                     if (!Decompile(ss, done, pc - done))
                         return NULL;
 
                     /* Pop Decompile result and print comma expression. */
                     rval = PopStrDupe(ss, op, &rvalpc);
-                    todo = ss->sprinter.offset;
+                    todo = ss->sprinter.getOffset();
                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
                     SprintCString(&ss->sprinter, ", ");
                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
                     break;
 
                   case SRC_HIDDEN:
                     /* Hide this pop, it's from a goto in a with or for/in. */
                     todo = -2;
@@ -3288,17 +3424,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                         LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0);
                     }
                 }
                 top = ss->top;
                 depth = GET_UINT16(pc);
                 LOCAL_ASSERT(top >= depth);
                 top -= depth;
                 ss->top = top;
-                ss->sprinter.offset = GetOff(ss, top);
+                ss->sprinter.setOffset(GetOff(ss, top));
                 if (op == JSOP_LEAVEBLOCKEXPR)
                     todo = SprintCString(&ss->sprinter, rval);
                 break;
               }
 
               case JSOP_ENTERLET0:
               {
                 LOAD_OBJECT(0);
@@ -3319,24 +3455,21 @@ Decompile(SprintStack *ss, jsbytecode *p
                 /*
                  * Build the list of decompiled rhs expressions. Do this before
                  * sprinting the let-head since GetStr can inject stuff on top
                  * of the stack (in case js_DecompileValueGenerator).
                  */
                 Vector<const char *> rhsExprs(cx);
                 if (!rhsExprs.resize(atoms.length()))
                     return NULL;
-                for (size_t i = 0; i < rhsExprs.length(); ++i) {
-                    rhsExprs[i] = GetStr(ss, letDepth + i);
-                    if (!rhsExprs[i])
-                        return NULL;
-                }
+                for (size_t i = 0; i < rhsExprs.length(); ++i)
+                    rhsExprs[i] = SprintDupeStr(ss, GetStr(ss, letDepth + i));
 
                 /* Build the let head starting at headBegin. */
-                ptrdiff_t headBegin = ss->sprinter.offset;
+                ptrdiff_t headBegin = ss->sprinter.getOffset();
 
                 /*
                  * For group assignment, prepend the '[lhs-vars] = [' here,
                  * append rhsExprs in the next loop and append ']' after.
                  */
                 if (groupAssign) {
                     if (Sprint(&ss->sprinter, "[") < 0)
                         return NULL;
@@ -3379,17 +3512,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     }
                 }
 
                 if (groupAssign && Sprint(&ss->sprinter, "]") < 0)
                     return NULL;
 
                 /* Clone the let head chars before clobbering the stack. */
                 DupBuffer head(cx);
-                if (!Dup(OFF2STR(&ss->sprinter, headBegin), &head))
+                if (!Dup(ss->sprinter.stringAt(headBegin), &head))
                     return NULL;
                 if (!AssignBlockNamesToPushedSlots(cx, ss, atoms))
                     return NULL;
 
                 /* Detect 'for (let ...)' desugared into 'let (...) {for}'. */
                 jsbytecode *nextpc = pc + JSOP_ENTERLET0_LENGTH;
                 if (*nextpc == JSOP_NOP) {
                     jssrcnote *nextsn = js_GetSrcNote(jp->script, nextpc);
@@ -3674,18 +3807,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * Generator expression: decompile just rval followed by
                      * the string starting at forpos. Leave the result string
                      * in ss->offsets[0] so it can be recovered by our caller
                      * (the JSOP_ANONFUNOBJ with SRC_GENEXP case). Bump the
                      * top of stack to balance yield, which is an expression
                      * (so has neutral stack balance).
                      */
                     LOCAL_ASSERT(pos == 0);
-                    xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
-                    ss->sprinter.offset = PAREN_SLOP;
+                    xval = ss->sprinter.stringAt(ss->offsets[forpos]);
+                    ss->sprinter.setOffset(PAREN_SLOP);
                     todo = Sprint(&ss->sprinter, ss_format, rval, xval);
                     if (todo < 0)
                         return NULL;
                     ss->offsets[0] = todo;
                     ++ss->top;
                     return pc;
                 }
 #endif /* JS_HAS_GENERATOR_EXPRS */
@@ -3693,22 +3826,22 @@ Decompile(SprintStack *ss, jsbytecode *p
                 /*
                  * Array comprehension: retract the sprinter to the beginning
                  * of the array initialiser and decompile "[<rval> for ...]".
                  */
                 JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
                 LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
 
                 ptrdiff_t start = ss->offsets[pos];
-                LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
-                             ss->sprinter.base[start] == '#');
+                LOCAL_ASSERT(ss->sprinter[start] == '[' ||
+                             ss->sprinter[start] == '#');
                 LOCAL_ASSERT(forpos < ss->top);
-                xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]);
-                lval = OFF2STR(&ss->sprinter, start);
-                RETRACT(&ss->sprinter, lval);
+                xval = ss->sprinter.stringAt(ss->offsets[forpos]);
+                lval = ss->sprinter.stringAt(start);
+                ss->sprinter.setOffset(lval);
 
                 todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
                 if (todo < 0)
                     return NULL;
                 ss->offsets[pos] = todo;
                 todo = -2;
                 break;
               }
@@ -3725,17 +3858,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     break;
                 rval = PopStr(ss, op, &rvalpc);
                 js_printf(jp, "\t%s ", js_throw_str);
                 SprintOpcodePermanent(jp, rval, rvalpc);
                 js_printf(jp, ";\n");
                 break;
 
               case JSOP_ITER:
-                foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
+                foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
                           JSITER_FOREACH;
                 todo = -2;
                 break;
 
               case JSOP_MOREITER:
                 JS_NOT_REACHED("JSOP_MOREITER");
                 break;
 
@@ -3778,32 +3911,34 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * We never decompile the obligatory JSOP_POP,
                      * JSOP_MOREITER or JSOP_IFNE, though we do quick asserts
                      * to check that they are there.
                      */
                     cond = GET_JUMP_OFFSET(pc);
                     next = js_GetSrcNoteOffset(sn, 0);
                     tail = js_GetSrcNoteOffset(sn, 1);
                     JS_ASSERT(pc[next] == JSOP_POP);
+                    JS_ASSERT(pc[cond] == JSOP_LOOPENTRY);
+                    cond += JSOP_LOOPENTRY_LENGTH;
                     JS_ASSERT(pc[cond] == JSOP_MOREITER);
                     DECOMPILE_CODE(pc + oplen, next - oplen);
                     lval = POP_STR();
 
                     /*
                      * This string "<next>" comes from jsopcode.tbl. It stands
                      * for the result pushed by JSOP_ITERNEXT.
                      */
                     JS_ASSERT(strcmp(lval + strlen(lval) - 9, " = <next>") == 0);
                     const_cast<char *>(lval)[strlen(lval) - 9] = '\0';
                     LOCAL_ASSERT(ss->top >= 1);
 
                     if (ss->inArrayInit || ss->inGenExp) {
                         rval = POP_STR();
                         if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
-                            ss->sprinter.offset = ss->offsets[ss->top] - PAREN_SLOP;
+                            ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP);
                             if (Sprint(&ss->sprinter, " %s (%s in %s)",
                                        foreach ? js_for_each_str : js_for_str,
                                        lval, rval) < 0) {
                                 return NULL;
                             }
 
                             /*
                              * Do not AddParenSlop here, as we will push the
@@ -3859,30 +3994,30 @@ Decompile(SprintStack *ss, jsbytecode *p
                     todo = -2;
                     break;
 
                   case SRC_CONT2LABEL:
                     GET_SOURCE_NOTE_ATOM(sn, atom);
                     rval = QuoteString(&ss->sprinter, atom, 0);
                     if (!rval)
                         return NULL;
-                    RETRACT(&ss->sprinter, rval);
+                    ss->sprinter.setOffset(rval);
                     js_printf(jp, "\tcontinue %s;\n", rval);
                     break;
 
                   case SRC_CONTINUE:
                     js_printf(jp, "\tcontinue;\n");
                     break;
 
                   case SRC_BREAK2LABEL:
                     GET_SOURCE_NOTE_ATOM(sn, atom);
                     rval = QuoteString(&ss->sprinter, atom, 0);
                     if (!rval)
                         return NULL;
-                    RETRACT(&ss->sprinter, rval);
+                    ss->sprinter.setOffset(rval);
                     js_printf(jp, "\tbreak %s;\n", rval);
                     break;
 
                   case SRC_HIDDEN:
                     break;
 
                   default:
                     js_printf(jp, "\tbreak;\n");
@@ -3900,17 +4035,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 sn = js_GetSrcNote(jp->script, pc);
 
                 switch (sn ? SN_TYPE(sn) : SRC_NULL) {
                   case SRC_IF:
                   case SRC_IF_ELSE:
                     rval = PopCondStr(ss, &rvalpc);
                     if (ss->inArrayInit || ss->inGenExp) {
                         LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
-                        ss->sprinter.offset -= PAREN_SLOP;
+                        ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP);
                         if (Sprint(&ss->sprinter, " if (%s)", rval) < 0)
                             return NULL;
                         AddParenSlop(ss);
                     } else {
                         js_printf(jp, elseif ? " if (" : "\tif (");
                         SprintOpcodePermanent(jp, rval, rvalpc);
                         js_printf(jp, ") {\n");
                         jp->indent += 4;
@@ -3966,17 +4101,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     lval = PopStrDupe(ss, op, &lvalpc);
                     pushpc = pc;
                     pc += len;
                     LOCAL_ASSERT(*pc == JSOP_GOTO);
                     oplen = js_CodeSpec[*pc].length;
                     len = GET_JUMP_OFFSET(pc);
                     DECOMPILE_CODE(pc + oplen, len - oplen);
                     rval = PopStrDupe(ss, op, &rvalpc);
-                    todo = ss->sprinter.offset;
+                    todo = ss->sprinter.getOffset();
                     SprintOpcode(ss, xval, xvalpc, pushpc, todo);
                     SprintCString(&ss->sprinter, " ? ");
                     SprintOpcode(ss, lval, lvalpc, pushpc, todo);
                     SprintCString(&ss->sprinter, " : ");
                     SprintOpcode(ss, rval, rvalpc, pushpc, todo);
                     break;
 
                   default:
@@ -4001,17 +4136,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 JS_ASSERT(*pc == JSOP_POP);
                 pc += JSOP_POP_LENGTH;
                 len = done - pc;
                 if (!Decompile(ss, pc, len))
                     return NULL;
                 rval = PopStrDupe(ss, op, &rvalpc);
                 if (!rval)
                     return NULL;
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pushpc, todo);
                 if (jp->pretty &&
                     jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) {
                     Sprint(&ss->sprinter, " %s\n", xval);
                     Sprint(&ss->sprinter, "%*s", jp->indent + 4, "");
                 } else {
                     Sprint(&ss->sprinter, " %s ", xval);
                 }
@@ -4201,17 +4336,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 if (!lval)
                     return NULL;
                 rval = PopStrDupe(ss, op, &rvalpc);
                 if (op == JSOP_SETNAME || op == JSOP_SETGNAME)
                     (void) PopOff(ss, op);
 
               do_setlval:
                 sn = js_GetSrcNote(jp->script, pc - 1);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) {
                     const char *token =
                         GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
                                               &lastlvalpc, &lastrvalpc);
                     Sprint(&ss->sprinter, "%s %s= ", lval, token);
                     SprintOpcode(ss, rval, rvalpc, pc, todo);
                 } else {
                     sn = js_GetSrcNote(jp->script, pc);
@@ -4270,17 +4405,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                                        op == JSOP_CALLPROP ||
                                        op == JSOP_CALLELEM))
                                      ? JSOP_NAME
                                      : saveop,
                                      &lvalpc);
                 op = saveop;
 
                 lval = "(", rval = ")";
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 if (op == JSOP_NEW) {
                     if (argc == 0)
                         lval = rval = "";
                     Sprint(&ss->sprinter, "%s ", js_new_str);
                 }
                 SprintOpcode(ss, argv[0], lvalpc, pc, todo);
                 SprintCString(&ss->sprinter, lval);
 
@@ -4301,17 +4436,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 todo = Sprint(&ss->sprinter, "");
                 break;
 
               case JSOP_DELNAME:
                 LOAD_ATOM(0);
                 lval = QuoteString(&ss->sprinter, atom, 0);
                 if (!lval)
                     return NULL;
-                RETRACT(&ss->sprinter, lval);
+                ss->sprinter.setOffset(lval);
               do_delete_lval:
                 todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval);
                 break;
 
               case JSOP_DELPROP:
                 GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
                 op = JSOP_GETPROP;
                 lval = POP_STR();
@@ -4343,17 +4478,17 @@ Decompile(SprintStack *ss, jsbytecode *p
 #endif
 
               case JSOP_TYPEOFEXPR:
               case JSOP_TYPEOF:
               case JSOP_VOID:
               {
                 const char *prefix = (op == JSOP_VOID) ? js_void_str : js_typeof_str;
                 rval = PopStrDupe(ss, op, &rvalpc);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 Sprint(&ss->sprinter, "%s ", prefix);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
               }
 
               case JSOP_INCARG:
               case JSOP_DECARG:
                 atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
@@ -4364,17 +4499,17 @@ Decompile(SprintStack *ss, jsbytecode *p
               case JSOP_DECNAME:
               case JSOP_INCGNAME:
               case JSOP_DECGNAME:
                 LOAD_ATOM(0);
               do_incatom:
                 lval = QuoteString(&ss->sprinter, atom, 0);
                 if (!lval)
                     return NULL;
-                RETRACT(&ss->sprinter, lval);
+                ss->sprinter.setOffset(lval);
               do_inclval:
                 todo = Sprint(&ss->sprinter, ss_format,
                               js_incop_strs[!(cs->format & JOF_INC)], lval);
                 if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
                     len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
                 break;
 
               case JSOP_INCPROP:
@@ -4424,17 +4559,17 @@ Decompile(SprintStack *ss, jsbytecode *p
               case JSOP_NAMEDEC:
               case JSOP_GNAMEINC:
               case JSOP_GNAMEDEC:
                 LOAD_ATOM(0);
               do_atominc:
                 lval = QuoteString(&ss->sprinter, atom, 0);
                 if (!lval)
                     return NULL;
-                RETRACT(&ss->sprinter, lval);
+                ss->sprinter.setOffset(lval);
               do_lvalinc:
                 todo = Sprint(&ss->sprinter, ss_format,
                               lval, js_incop_strs[!(cs->format & JOF_INC)]);
                 if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
                     len += GetDecomposeLength(pc, js_CodeSpec[*pc].length);
                 break;
 
               case JSOP_PROPINC:
@@ -4482,17 +4617,17 @@ Decompile(SprintStack *ss, jsbytecode *p
               case JSOP_GETPROP:
               case JSOP_GETXPROP:
               case JSOP_LENGTH:
                 LOAD_ATOM(0);
 
                 GET_QUOTE_AND_FMT("[%s]", ".%s", rval);
                 PROPAGATE_CALLNESS();
                 lval = PopStr(ss, op, &lvalpc);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 Sprint(&ss->sprinter, fmt, rval);
                 break;
 
               case JSOP_SETPROP:
               case JSOP_SETMETHOD:
               {
                 LOAD_ATOM(0);
@@ -4505,34 +4640,34 @@ Decompile(SprintStack *ss, jsbytecode *p
                  * around the left-hand side of dot.
                  */
                 op = JSOP_GETPROP;
                 lval = PopStr(ss, op, &lvalpc);
                 sn = js_GetSrcNote(jp->script, pc - 1);
                 const char *token =
                     GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
                                           &lastlvalpc, &lastrvalpc);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 Sprint(&ss->sprinter, fmt, xval, token);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
               }
 
               case JSOP_GETELEM2:
                 (void) PopOff(ss, lastop);
                 /* FALL THROUGH */
               case JSOP_CALLELEM:
               case JSOP_GETELEM:
                 op = JSOP_NOP;          /* turn off parens */
                 xval = PopStrDupe(ss, op, &xvalpc);
                 op = saveop;
                 PROPAGATE_CALLNESS();
                 lval = PopStr(ss, op, &lvalpc);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 if (*xval != '\0') {
                     bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME);
                     SprintCString(&ss->sprinter, xml ? "." : "[");
                     SprintOpcode(ss, xval, xvalpc, pc, todo);
                     SprintCString(&ss->sprinter, xml ? "" : "]");
                 }
                 break;
@@ -4548,17 +4683,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 op = saveop;
                 if (*xval == '\0')
                     goto do_setlval;
                 sn = js_GetSrcNote(jp->script, pc - 1);
                 bool xml = (JOF_MODE(cs->format) == JOF_XMLNAME);
                 const char *token =
                     GetTokenForAssignment(jp, sn, lastop, pc, rvalpc,
                                           &lastlvalpc, &lastrvalpc);
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 SprintCString(&ss->sprinter, xml ? "." : "[");
                 SprintOpcode(ss, xval, xvalpc, pc, todo);
                 SprintCString(&ss->sprinter, xml ? "" : "]");
                 Sprint(&ss->sprinter, " %s= ", token);
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 break;
               }
@@ -4586,17 +4721,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 lval = "";
 #if JS_HAS_XML_SUPPORT
               do_qname:
 #endif
                 sn = js_GetSrcNote(jp->script, pc);
                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0);
                 if (!rval)
                     return NULL;
-                RETRACT(&ss->sprinter, rval);
+                ss->sprinter.setOffset(rval);
                 todo = Sprint(&ss->sprinter, sss_format,
                               VarPrefix(sn), lval, rval);
                 break;
 
               case JSOP_UINT16:
                 i = (jsint) GET_UINT16(pc);
                 goto do_sprint_int;
 
@@ -4623,29 +4758,29 @@ Decompile(SprintStack *ss, jsbytecode *p
                 break;
               }
 
               case JSOP_STRING:
                 LOAD_ATOM(0);
                 rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"');
                 if (!rval)
                     return NULL;
-                todo = STR2OFF(&ss->sprinter, rval);
+                todo = ss->sprinter.getOffsetOf(rval);
                 break;
 
               case JSOP_LAMBDA:
               case JSOP_LAMBDA_FC:
 #if JS_HAS_GENERATOR_EXPRS
                 sn = js_GetSrcNote(jp->script, pc);
                 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
                     Vector<JSAtom *> *innerLocalNames;
                     Vector<JSAtom *> *outerLocalNames;
                     JSScript *inner, *outer;
                     Vector<DecompiledOpcode> *decompiledOpcodes;
-                    SprintStack ss2;
+                    SprintStack ss2(cx);
                     JSFunction *outerfun;
 
                     LOAD_FUNCTION(0);
 
                     /*
                      * All allocation when decompiling is LIFO, using malloc or,
                      * more commonly, arena-allocating from cx->tempLifoAlloc
                      * Therefore after InitSprintStack succeeds, we must release
@@ -4797,17 +4932,17 @@ Decompile(SprintStack *ss, jsbytecode *p
               case JSOP_OBJECT:
                 LOAD_OBJECT(0);
                 str = js_ValueToSource(cx, ObjectValue(*obj));
                 if (!str)
                     return NULL;
                 goto sprint_string;
 
               case JSOP_REGEXP:
-                GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj);
+                obj = jp->script->getRegExp(GET_UINT32_INDEX(pc));
                 str = obj->asRegExp().toString(cx);
                 if (!str)
                     return NULL;
                 goto sprint_string;
 
               case JSOP_TABLESWITCH:
               {
                 ptrdiff_t off, off2;
@@ -5005,20 +5140,20 @@ Decompile(SprintStack *ss, jsbytecode *p
                 break;
 
               case JSOP_HOLE:
                 todo = SprintPut(&ss->sprinter, "", 0);
                 break;
 
               case JSOP_NEWINIT:
               {
-                i = pc[1];
+                i = GET_UINT8(pc);
                 LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object);
 
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
 #if JS_HAS_SHARP_VARS
                 op = (JSOp)pc[len];
                 if (op == JSOP_SHARPINIT)
                     op = (JSOp)pc[len += JSOP_SHARPINIT_LENGTH];
                 if (op == JSOP_DEFSHARP) {
                     pc += len;
                     cs = &js_CodeSpec[op];
                     len = cs->length;
@@ -5037,26 +5172,26 @@ Decompile(SprintStack *ss, jsbytecode *p
                     if (SprintCString(&ss->sprinter, "{") < 0)
                         return NULL;
                 }
                 break;
               }
 
               case JSOP_NEWARRAY:
               {
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 ++ss->inArrayInit;
                 if (SprintCString(&ss->sprinter, "[") < 0)
                     return NULL;
                 break;
               }
 
               case JSOP_NEWOBJECT:
               {
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 if (SprintCString(&ss->sprinter, "{") < 0)
                     return NULL;
                 break;
               }
 
               case JSOP_ENDINIT:
               {
                 JSBool inArray;
@@ -5066,17 +5201,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 sn = js_GetSrcNote(jp->script, pc);
 
                 /* Skip any #n= prefix to find the opening bracket. */
                 for (xval = rval; *xval != '[' && *xval != '{'; xval++)
                     continue;
                 inArray = (*xval == '[');
                 if (inArray)
                     --ss->inArrayInit;
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, rval, rvalpc, pc, todo);
                 Sprint(&ss->sprinter, "%s%c",
                        (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "",
                        inArray ? ']' : '}');
                 break;
               }
 
               {
@@ -5110,17 +5245,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 if (!xval)
                     return NULL;
                 isFirst = IsInitializerOp(ss->opcodes[ss->top - 2]);
                 rval = PopStrDupe(ss, op, &rvalpc);
                 lval = PopStr(ss, op, &lvalpc);
                 /* fall through */
 
               do_initprop:
-                todo = ss->sprinter.offset;
+                todo = ss->sprinter.getOffset();
                 SprintOpcode(ss, lval, lvalpc, pc, todo);
                 maybeComma = isFirst ? "" : ", ";
                 if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) {
                     const char *end = rval + strlen(rval);
 
                     if (*rval == '(')
                         ++rval, --end;
                     LOCAL_ASSERT(strncmp(rval, js_function_str, 8) == 0);
@@ -5192,17 +5327,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                 }
                 goto do_name;
 
               case JSOP_QNAMECONST:
                 LOAD_ATOM(0);
                 rval = QuoteString(&ss->sprinter, atom, 0);
                 if (!rval)
                     return NULL;
-                RETRACT(&ss->sprinter, rval);
+                ss->sprinter.setOffset(rval);
                 lval = POP_STR();
                 todo = Sprint(&ss->sprinter, "%s::%s", lval, rval);
                 break;
 
               case JSOP_QNAME:
                 rval = POP_STR();
                 lval = POP_STR();
                 todo = Sprint(&ss->sprinter, "%s::[%s]", lval, rval);
@@ -5376,17 +5511,17 @@ DecompileCode(JSPrinter *jp, JSScript *s
 {
     JSContext *cx = jp->sprinter.context;
 
     uintN depth = StackDepth(script);
     JS_ASSERT(pcdepth <= depth);
 
     /* Initialize a sprinter for use with the offset stack. */
     LifoAllocScope las(&cx->tempLifoAlloc());
-    SprintStack ss;
+    SprintStack ss(cx);
     if (!InitSprintStack(cx, &ss, jp, depth))
         return false;
 
     /*
      * If we are called from js_DecompileValueGenerator with a portion of
      * script's bytecode that starts with a non-zero model stack depth given
      * by pcdepth, attempt to initialize the missing string offsets in ss to
      * |spindex| negative indexes from fp->sp for the activation fp in which
@@ -5407,19 +5542,19 @@ DecompileCode(JSPrinter *jp, JSScript *s
     /* Call recursive subroutine to do the hard work. */
     JSScript *oldscript = jp->script;
     jp->script = script;
     bool ok = Decompile(&ss, pc, len) != NULL;
     jp->script = oldscript;
 
     /* If the given code didn't empty the stack, do it now. */
     if (ok && ss.top) {
-        char *last;
+        const char *last;
         do {
-            last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP));
+            last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP));
         } while (ss.top > pcdepth);
         js_printf(jp, "%s", last);
     }
 
     return ok;
 }
 
 /*
@@ -5519,28 +5654,27 @@ js_DecompileFunction(JSPrinter *jp)
         js_printf(jp, ") {\n");
         jp->indent += 4;
         js_printf(jp, native_code_str);
         jp->indent -= 4;
         js_printf(jp, "\t}");
     } else {
         JSScript *script = fun->script();
 #if JS_HAS_DESTRUCTURING
-        SprintStack ss;
+        SprintStack ss(jp->sprinter.context);
 #endif
 
         /* Print the parameters. */
         jsbytecode *pc = script->main();
         jsbytecode *endpc = pc + script->length;
         JSBool ok = JS_TRUE;
 
 #if JS_HAS_DESTRUCTURING
         ss.printer = NULL;
         jp->script = script;
-        LifoAllocScope las(&jp->sprinter.context->tempLifoAlloc());
 #endif
 
         for (uintN i = 0; i < fun->nargs; i++) {
             if (i > 0)
                 js_puts(jp, ", ");
 
             JSAtom *param = GetArgOrVarAtom(jp, i);
 
@@ -5581,17 +5715,16 @@ js_DecompileFunction(JSPrinter *jp)
             if (!QuoteString(&jp->sprinter, param, 0)) {
                 ok = JS_FALSE;
                 break;
             }
         }
 
 #if JS_HAS_DESTRUCTURING
         jp->script = NULL;
-        las.releaseEarly();
 #endif
         if (!ok)
             return JS_FALSE;
         js_printf(jp, ") ");
         if (!(fun->flags & JSFUN_EXPR_CLOSURE)) {
             js_printf(jp, "{\n");
             jp->indent += 4;
         }
@@ -5813,18 +5946,17 @@ DecompileExpression(JSContext *cx, JSScr
     if (!g.printer)
         return NULL;
 
     g.printer->dvgfence = end;
     g.printer->pcstack = g.pcstack;
     if (!DecompileCode(g.printer, script, begin, (uintN) len, (uintN) pcdepth))
         return NULL;
 
-    const char *name = (g.printer->sprinter.base) ? g.printer->sprinter.base : "";
-    return JS_strdup(cx, name);
+    return JS_strdup(cx, g.printer->sprinter.string());
 }
 
 uintN
 js_ReconstructStackDepth(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     return ReconstructPCStack(cx, script, pc, NULL, NULL);
 }
 
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -77,24 +77,24 @@ typedef enum JSOp {
 #define JOF_BYTE          0       /* single bytecode, no immediates */
 #define JOF_JUMP          1       /* signed 16-bit jump offset immediate */
 #define JOF_ATOM          2       /* unsigned 16-bit constant index */
 #define JOF_UINT16        3       /* unsigned 16-bit immediate operand */
 #define JOF_TABLESWITCH   4       /* table switch */
 #define JOF_LOOKUPSWITCH  5       /* lookup switch */
 #define JOF_QARG          6       /* quickened get/set function argument ops */
 #define JOF_LOCAL         7       /* var or block-local variable */
-#define JOF_SLOTATOM      8       /* uint16_t slot + constant index */
+/* 8 is unused */
 #define JOF_UINT24        12      /* extended unsigned 24-bit literal (index) */
 #define JOF_UINT8         13      /* uint8_t immediate, e.g. top 8 bits of 24-bit
                                      atom index */
 #define JOF_INT32         14      /* int32_t immediate operand */
 #define JOF_OBJECT        15      /* unsigned 16-bit object index */
 #define JOF_SLOTOBJECT    16      /* uint16_t slot index + object index */
-#define JOF_REGEXP        17      /* unsigned 16-bit regexp index */
+#define JOF_REGEXP        17      /* unsigned 32-bit regexp index */
 #define JOF_INT8          18      /* int8_t immediate operand */
 #define JOF_ATOMOBJECT    19      /* uint16_t constant index + object index */
 #define JOF_UINT16PAIR    20      /* pair of uint16_t immediates */
 #define JOF_TYPEMASK      0x001f  /* mask for above immediate types */
 
 #define JOF_NAME          (1U<<5) /* name operation */
 #define JOF_PROP          (2U<<5) /* obj.prop operation */
 #define JOF_ELEM          (3U<<5) /* obj[index] operation */
@@ -146,16 +146,28 @@ typedef enum JSOp {
 
 #define JOF_TYPE_IS_EXTENDED_JUMP(t) \
     ((unsigned)((t) - JOF_JUMP) <= (unsigned)(JOF_LOOKUPSWITCH - JOF_JUMP))
 
 /*
  * Immediate operand getters, setters, and bounds.
  */
 
+static JS_ALWAYS_INLINE uint8_t
+GET_UINT8(jsbytecode *pc)
+{
+    return (uint8_t) pc[1];
+}
+
+static JS_ALWAYS_INLINE void
+SET_UINT8(jsbytecode *pc, uint8_t u)
+{
+    pc[1] = (jsbytecode) u;
+}
+
 /* Common uint16_t immediate format helpers. */
 #define UINT16_LEN              2
 #define UINT16_HI(i)            ((jsbytecode)((i) >> 8))
 #define UINT16_LO(i)            ((jsbytecode)(i))
 #define GET_UINT16(pc)          ((uintN)(((pc)[1] << 8) | (pc)[2]))
 #define SET_UINT16(pc,i)        ((pc)[1] = UINT16_HI(i), (pc)[2] = UINT16_LO(i))
 #define UINT16_LIMIT            ((uintN)1 << 16)
 
@@ -174,20 +186,37 @@ static JS_ALWAYS_INLINE void
 SET_JUMP_OFFSET(jsbytecode *pc, int32_t off)
 {
     pc[1] = (jsbytecode)(off >> 24);
     pc[2] = (jsbytecode)(off >> 16);
     pc[3] = (jsbytecode)(off >> 8);
     pc[4] = (jsbytecode)off;
 }
 
+#define UINT32_INDEX_LEN        4
+
+static JS_ALWAYS_INLINE uint32_t
+GET_UINT32_INDEX(jsbytecode *pc)
+{
+    return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4];
+}
+
+static JS_ALWAYS_INLINE void
+SET_UINT32_INDEX(jsbytecode *pc, uint32_t index)
+{
+    pc[1] = (jsbytecode)(index >> 24);
+    pc[2] = (jsbytecode)(index >> 16);
+    pc[3] = (jsbytecode)(index >> 8);
+    pc[4] = (jsbytecode)index;
+}
+
 /*
  * A literal is indexed by a per-script atom or object maps. Most scripts
- * have relatively few literals, so the standard JOF_ATOM, JOF_OBJECT and
- * JOF_REGEXP formats specifies a fixed 16 bits of immediate operand index.
+ * have relatively few literals, so the standard JOF_ATOM and JOF_OBJECT
+ * format specifies a fixed 16 bits of immediate operand index.
  * A script with more than 64K literals must wrap the bytecode into
  * JSOP_INDEXBASE and JSOP_RESETBASE pair.
  */
 #define INDEX_LEN               2
 #define INDEX_HI(i)             ((jsbytecode)((i) >> 8))
 #define INDEX_LO(i)             ((jsbytecode)(i))
 #define GET_INDEX(pc)           GET_UINT16(pc)
 #define SET_INDEX(pc,i)         ((pc)[1] = INDEX_HI(i), (pc)[2] = INDEX_LO(i))
@@ -340,22 +369,16 @@ js_GetIndexFromBytecode(JSScript *script
     JS_END_MACRO
 
 #define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun)                    \
     JS_BEGIN_MACRO                                                            \
         uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff));      \
         fun = (script)->getFunction(index_);                                  \
     JS_END_MACRO
 
-#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj)                      \
-    JS_BEGIN_MACRO                                                            \
-        uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff));      \
-        obj = (script)->getRegExp(index_);                                    \
-    JS_END_MACRO
-
 #ifdef __cplusplus
 namespace js {
 
 extern uintN
 StackUses(JSScript *script, jsbytecode *pc);
 
 extern uintN
 StackDefs(JSScript *script, jsbytecode *pc);
@@ -435,36 +458,90 @@ DecompileValueGenerator(JSContext *cx, i
                         JSString *fallback)
 {
     return js_DecompileValueGenerator(cx, spindex, v, fallback);
 }
 
 /*
  * Sprintf, but with unlimited and automatically allocated buffering.
  */
-struct Sprinter {
-    JSContext       *context;       /* context executing the decompiler */
-    LifoAlloc       *pool;          /* string allocation pool */
-    char            *base;          /* base address of buffer in pool */
-    size_t          size;           /* size of buffer allocated at base */
-    ptrdiff_t       offset;         /* offset of next free char in buffer */
-};
+class Sprinter
+{
+  public:
+    struct InvariantChecker
+    {
+        const Sprinter *parent;
+
+        explicit InvariantChecker(const Sprinter *p) : parent(p) {
+            parent->checkInvariants();
+        }
+
+        ~InvariantChecker() {
+            parent->checkInvariants();
+        }
+    };
+
+    JSContext               *context;       /* context executing the decompiler */
+
+  private:
+    static const size_t     DefaultSize;
+#ifdef DEBUG
+    bool                    initialized;    /* true if this is initialized, use for debug builds */
+#endif
+    char                    *base;          /* malloc'd buffer address */
+    size_t                  size;           /* size of buffer allocated at base */
+    ptrdiff_t               offset;         /* offset of next free char in buffer */
+
+    bool realloc_(size_t newSize);
+
+  public:
+    explicit Sprinter(JSContext *cx);
+    ~Sprinter();
+
+    /* Initialize this sprinter, returns false on error */
+    bool init();
 
-#define INIT_SPRINTER(cx, sp, ap, off) \
-    ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0,  \
-     (sp)->offset = off)
+    void checkInvariants() const;
+
+    const char *string() const;
+    const char *stringEnd() const;
+    /* Returns the string at offset |off| */
+    char *stringAt(ptrdiff_t off) const;
+    /* Returns the char at offset |off| */
+    char &operator[](size_t off);
+    /* Test if this Sprinter is empty */
+    bool empty() const;
 
-/*
- * Attempt to reserve len space in sp (including a trailing NULL byte). If the
- * attempt succeeds, return a pointer to the start of that space and adjust the
- * length of sp's contents. The caller *must* completely fill this space
- * (including the space for the trailing NULL byte) on success.
- */
-extern char *
-SprintReserveAmount(Sprinter *sp, size_t len);
+    /*
+     * Attempt to reserve len + 1 space (for a trailing NULL byte). If the
+     * attempt succeeds, return a pointer to the start of that space and adjust the
+     * internal content. The caller *must* completely fill this space on success.
+     */
+    char *reserve(size_t len);
+    /* Like reserve, but memory is initialized to 0 */
+    char *reserveAndClear(size_t len);
+
+    /*
+     * Puts |len| characters from |s| at the current position and return an offset to
+     * the beginning of this new data
+     */
+    ptrdiff_t put(const char *s, size_t len);
+    ptrdiff_t putString(JSString *str);
+
+    /* Prints a formatted string into the buffer */
+    int printf(const char *fmt, ...);
+
+    /* Change the offset */
+    void setOffset(const char *end);
+    void setOffset(ptrdiff_t off);
+
+    /* Get the offset */
+    ptrdiff_t getOffset() const;
+    ptrdiff_t getOffsetOf(const char *string) const;
+};
 
 extern ptrdiff_t
 SprintPut(Sprinter *sp, const char *s, size_t len);
 
 extern ptrdiff_t
 SprintCString(Sprinter *sp, const char *s);
 
 extern ptrdiff_t
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -406,17 +406,17 @@ OPDEF(JSOP_RETRVAL,       153,"retrval",
 OPDEF(JSOP_GETGNAME,      154,"getgname",  NULL,       3,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_TYPESET|JOF_GNAME)
 OPDEF(JSOP_SETGNAME,      155,"setgname",  NULL,       3,  2,  1,  3,  JOF_ATOM|JOF_NAME|JOF_SET|JOF_DETECTING|JOF_GNAME)
 OPDEF(JSOP_INCGNAME,      156,"incgname",  NULL,       4,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
 OPDEF(JSOP_DECGNAME,      157,"decgname",  NULL,       4,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
 OPDEF(JSOP_GNAMEINC,      158,"gnameinc",  NULL,       4,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
 OPDEF(JSOP_GNAMEDEC,      159,"gnamedec",  NULL,       4,  0,  1, 15,  JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE)
 
 /* Regular expression literal requiring special "fork on exec" handling. */
-OPDEF(JSOP_REGEXP,        160,"regexp",   NULL,       3,  0,  1, 19,  JOF_REGEXP)
+OPDEF(JSOP_REGEXP,        160,"regexp",   NULL,       5,  0,  1, 19,  JOF_REGEXP)
 
 /* XML (ECMA-357, a.k.a. "E4X") support. */
 OPDEF(JSOP_DEFXMLNS,      161,"defxmlns",   NULL,     1,  1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_ANYNAME,       162,"anyname",    NULL,     1,  0,  1, 19,  JOF_BYTE|JOF_XMLNAME)
 OPDEF(JSOP_QNAMEPART,     163,"qnamepart",  NULL,     3,  0,  1, 19,  JOF_ATOM|JOF_XMLNAME)
 OPDEF(JSOP_QNAMECONST,    164,"qnameconst", NULL,     3,  1,  1, 19,  JOF_ATOM|JOF_XMLNAME)
 OPDEF(JSOP_QNAME,         165,"qname",      NULL,     1,  2,  1,  0,  JOF_BYTE|JOF_XMLNAME)
 OPDEF(JSOP_TOATTRNAME,    166,"toattrname", NULL,     1,  1,  1, 19,  JOF_BYTE|JOF_XMLNAME)
@@ -567,8 +567,11 @@ OPDEF(JSOP_INITMETHOD,    223,"initmetho
 
 OPDEF(JSOP_SHARPINIT,     224,"sharpinit",     NULL,  3,  0,  0,  0,  JOF_UINT16|JOF_SHARPSLOT)
 
 /* Pop the stack, convert to a jsid (int or string), and push back. */
 OPDEF(JSOP_TOID,          225, "toid",         NULL,  1,  1,  1,  0,  JOF_BYTE)
 
 /* Push the implicit 'this' value for calls to the associated name. */
 OPDEF(JSOP_IMPLICITTHIS,  226, "implicitthis", "",    3,  0,  1,  0,  JOF_ATOM)
+
+/* This opcode is the target of the entry jump for some loop. */
+OPDEF(JSOP_LOOPENTRY,     227, "loopentry",    NULL,  1,  0,  0,  0,  JOF_BYTE)
--- a/js/src/jsopcodeinlines.h
+++ b/js/src/jsopcodeinlines.h
@@ -49,19 +49,18 @@ GetNameFromBytecode(JSContext *cx, jsbyt
         return cx->runtime->atomState.lengthAtom;
 
     // The method JIT's implementation of instanceof contains an internal lookup
     // of the prototype property.
     if (op == JSOP_INSTANCEOF)
         return cx->runtime->atomState.classPrototypeAtom;
 
     JSScript *script = cx->stack.currentScript();
-    ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
     PropertyName *name;
-    GET_NAME_FROM_BYTECODE(script, pc, pcoff, name);
+    GET_NAME_FROM_BYTECODE(script, pc, 0, name);
     return name;
 }
 
 class BytecodeRange {
   public:
     BytecodeRange(JSScript *script)
       : script(script), pc(script->code), end(pc + script->length) {}
     bool empty() const { return pc == end; }
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -43,25 +43,23 @@
 /*
  * JS symbol tables.
  */
 #include <new>
 #ifdef DEBUG
 #include <stdio.h>
 #endif
 
+#include "jsdhash.h"
+#include "jsobj.h"
+#include "jspropertytree.h"
 #include "jstypes.h"
 
-#include "jscntxt.h"
-#include "jsobj.h"
-#include "jsprvtd.h"
-#include "jspubtd.h"
-#include "jspropertytree.h"
-
 #include "js/HashTable.h"
+#include "gc/Root.h"
 #include "mozilla/Attributes.h"
 
 #ifdef _MSC_VER
 #pragma warning(push)
 #pragma warning(disable:4800)
 #pragma warning(push)
 #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
 #endif
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1741,17 +1741,17 @@ JSScript::ensureHasDebug(JSContext *cx)
     return true;
 }
 
 bool
 JSScript::recompileForStepMode(JSContext *cx)
 {
 #ifdef JS_METHODJIT
     if (jitNormal || jitCtor) {
-        mjit::ClearAllFrames(cx->compartment);
+        mjit::Recompiler::clearStackReferences(cx, this);
         mjit::ReleaseScriptCode(cx, this);
     }
 #endif
     return true;
 }
 
 bool
 JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3425,35 +3425,25 @@ CompareStringsImpl(JSContext *cx, JSStri
     JS_ASSERT(str1);
     JS_ASSERT(str2);
 
     if (str1 == str2) {
         *result = 0;
         return true;
     }
 
-    size_t l1 = str1->length();
     const jschar *s1 = str1->getChars(cx);
     if (!s1)
         return false;
 
-    size_t l2 = str2->length();
     const jschar *s2 = str2->getChars(cx);
     if (!s2)
         return false;
 
-    size_t n = JS_MIN(l1, l2);
-    for (size_t i = 0; i < n; i++) {
-        if (int32_t cmp = s1[i] - s2[i]) {
-            *result = cmp;
-            return true;
-        }
-    }
-    *result = (int32_t)(l1 - l2);
-    return true;
+    return CompareChars(s1, str1->length(), s2, str2->length(), result);
 }
 
 bool
 CompareStrings(JSContext *cx, JSString *str1, JSString *str2, int32_t *result)
 {
     return CompareStringsImpl(cx, str1, str2, result);
 }
 
--- a/js/src/jsstrinlines.h
+++ b/js/src/jsstrinlines.h
@@ -324,11 +324,30 @@ SkipSpace(const jschar *s, const jschar 
     JS_ASSERT(s <= end);
 
     while (s < end && unicode::IsSpace(*s))
         s++;
 
     return s;
 }
 
+/*
+ * Return less than, equal to, or greater than zero depending on whether
+ * s1 is less than, equal to, or greater than s2.
+ */
+inline bool
+CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result)
+{
+    size_t n = JS_MIN(l1, l2);
+    for (size_t i = 0; i < n; i++) {
+        if (int32_t cmp = s1[i] - s2[i]) {
+            *result = cmp;
+            return true;
+        }
+    }
+
+    *result = (int32_t)(l1 - l2);
+    return true;
+}
+
 }  /* namespace js */
 
 #endif /* jsstrinlines_h___ */
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -221,17 +221,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 103)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 105)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -727,20 +727,16 @@ MakeJITScript(JSContext *cx, JSScript *s
                      */
                     preserveChunk = true;
                 } else {
                     if (!currentEdges.append(edge))
                         return NULL;
                 }
             }
 
-            /*
-             * Watch for cross-chunk edges in a table switch. Don't handle
-             * lookup switches, as these are always stubbed.
-             */
             if (op == JSOP_TABLESWITCH) {
                 jsbytecode *pc2 = pc;
                 unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
                 pc2 += JUMP_OFFSET_LEN;
                 jsint low = GET_JUMP_OFFSET(pc2);
                 pc2 += JUMP_OFFSET_LEN;
                 jsint high = GET_JUMP_OFFSET(pc2);
                 pc2 += JUMP_OFFSET_LEN;
@@ -763,16 +759,41 @@ MakeJITScript(JSContext *cx, JSScript *s
                         edge.target = targetOffset;
                         if (!currentEdges.append(edge))
                             return NULL;
                     }
                     pc2 += JUMP_OFFSET_LEN;
                 }
             }
 
+            if (op == JSOP_LOOKUPSWITCH) {
+                unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc);
+                jsbytecode *pc2 = pc + JUMP_OFFSET_LEN;
+                unsigned npairs = GET_UINT16(pc2);
+                pc2 += UINT16_LEN;
+
+                CrossChunkEdge edge;
+                edge.source = offset;
+                edge.target = defaultOffset;
+                if (!currentEdges.append(edge))
+                    return NULL;
+
+                while (npairs) {
+                    pc2 += INDEX_LEN;
+                    unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
+                    CrossChunkEdge edge;
+                    edge.source = offset;
+                    edge.target = targetOffset;
+                    if (!currentEdges.append(edge))
+                        return NULL;
+                    pc2 += JUMP_OFFSET_LEN;
+                    npairs--;
+                }
+            }
+
             if (unsigned(offset - chunkStart) > CHUNK_LIMIT)
                 finishChunk = true;
 
             if (nextOffset >= script->length || !analysis->maybeCode(nextOffset)) {
                 /* Ensure that chunks do not start on unreachable opcodes. */
                 preserveChunk = true;
             } else {
                 /*
@@ -1835,23 +1856,22 @@ mjit::Compiler::finishThisUp()
 
     return Compile_Okay;
 }
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     JS_BEGIN_MACRO                                                            \
         if (IsJaegerSpewChannelActive(JSpew_JSOps)) {                         \
-            LifoAllocScope las(&cx->tempLifoAlloc());                         \
-            Sprinter sprinter;                                                \
-            INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);            \
+            Sprinter sprinter(cx);                                            \
+            sprinter.init();                                                  \
             js_Disassemble1(cx, script, PC, PC - script->code,                \
                             JS_TRUE, &sprinter);                              \
             JaegerSpew(JSpew_JSOps, "    %2d %s",                             \
-                       frame.stackDepth(), sprinter.base);                    \
+                       frame.stackDepth(), sprinter.string());                \
         }                                                                     \
     JS_END_MACRO;
 #else
 #define SPEW_OPCODE()
 #endif /* DEBUG */
 
 #define BEGIN_CASE(name)        case name:
 #define END_CASE(name)                      \
@@ -1988,16 +2008,18 @@ mjit::Compiler::generateMethod()
                 frame.syncAndForgetFe(fe);
             }
         }
         fixedIntToDoubleEntries.clear();
         fixedDoubleToAnyEntries.clear();
 
         if (PC >= script->code + chunkEnd) {
             if (fallthrough) {
+                if (opinfo->jumpTarget)
+                    fixDoubleTypes(PC);
                 frame.syncAndForgetEverything();
                 jsbytecode *curPC = PC;
                 do {
                     PC--;
                 } while (!analysis->maybeCode(PC));
                 if (!jumpAndRun(masm.jump(), curPC, NULL, NULL, /* fallthrough = */ true))
                     return Compile_Error;
                 PC = curPC;
@@ -2255,30 +2277,30 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_SWAP)
             frame.dup2();
             frame.shift(-3);
             frame.shift(-1);
           END_CASE(JSOP_SWAP)
 
           BEGIN_CASE(JSOP_PICK)
           {
-            int32_t amt = GET_INT8(PC);
+            uint32_t amt = GET_UINT8(PC);
 
             // Push -(amt + 1), say amt == 2
             // Stack before: X3 X2 X1
             // Stack after:  X3 X2 X1 X3
-            frame.dupAt(-(amt + 1));
+            frame.dupAt(-int32_t(amt + 1));
 
             // For each item X[i...1] push it then move it down.
             // The above would transition like so:
             //   X3 X2 X1 X3 X2 (dupAt)
             //   X2 X2 X1 X3    (shift)
             //   X2 X2 X1 X3 X1 (dupAt)
             //   X2 X1 X1 X3    (shift)
-            for (int32_t i = -amt; i < 0; i++) {
+            for (int32_t i = -int32_t(amt); i < 0; i++) {
                 frame.dupAt(i - 1);
                 frame.shift(i - 2);
             }
 
             // The stack looks like:
             // Xn ... X1 X1 X{n+1}
             // So shimmy the last value down.
             frame.shimmy(1);
@@ -2677,17 +2699,17 @@ mjit::Compiler::generateMethod()
              */
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
 #if defined JS_CPU_ARM /* Need to implement jump(BaseIndex) for ARM */
             frame.syncAndKillEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
-            INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
+            INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
             frame.pop();
 
             masm.jump(Registers::ReturnReg);
 #else
             if (!jsop_tableswitch(PC))
                 return Compile_Error;
 #endif
             PC += js_GetVariableBytecodeLength(PC);
@@ -2696,17 +2718,17 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_LOOKUPSWITCH)
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
             frame.syncAndForgetEverything();
             masm.move(ImmPtr(PC), Registers::ArgReg1);
 
             /* prepareStubCall() is not needed due to syncAndForgetEverything() */
-            INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_JUMP);
+            INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE);
             frame.pop();
 
             masm.jump(Registers::ReturnReg);
             PC += js_GetVariableBytecodeLength(PC);
             break;
           END_CASE(JSOP_LOOKUPSWITCH)
 
           BEGIN_CASE(JSOP_CASE)
@@ -2727,17 +2749,17 @@ mjit::Compiler::generateMethod()
             if (script->pcCounters) {
                 updateArithCounters(PC, NULL, arithFirstUseType, arithSecondUseType);
                 arithUpdated = true;
             }
             jsop_stricteq(op);
           END_CASE(JSOP_STRICTEQ)
 
           BEGIN_CASE(JSOP_ITER)
-            if (!iter(PC[1]))
+            if (!iter(GET_UINT8(PC)))
                 return Compile_Error;
           END_CASE(JSOP_ITER)
 
           BEGIN_CASE(JSOP_MOREITER)
           {
             /* At the byte level, this is always fused with IFNE or IFNEX. */
             if (script->pcCounters)
                 updatePCCounters(PC, &codeStart, &countersUpdated);
@@ -3222,16 +3244,19 @@ mjit::Compiler::generateMethod()
           {
             if (analysis->jumpTarget(PC)) {
                 interruptCheckHelper();
                 recompileCheckHelper();
             }
           }
           END_CASE(JSOP_LOOPHEAD)
 
+          BEGIN_CASE(JSOP_LOOPENTRY)
+          END_CASE(JSOP_LOOPENTRY)
+
           BEGIN_CASE(JSOP_DEBUGGER)
           {
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(PC), Registers::ArgReg1);
             INLINE_STUBCALL(stubs::DebuggerStatement, REJOIN_FALLTHROUGH);
           }
           END_CASE(JSOP_DEBUGGER)
 
@@ -6770,17 +6795,17 @@ mjit::Compiler::jsop_arguments(RejoinSta
 bool
 mjit::Compiler::jsop_newinit()
 {
     bool isArray;
     unsigned count = 0;
     JSObject *baseobj = NULL;
     switch (*PC) {
       case JSOP_NEWINIT:
-        isArray = (PC[1] == JSProto_Array);
+        isArray = (GET_UINT8(PC) == JSProto_Array);
         break;
       case JSOP_NEWARRAY:
         isArray = true;
         count = GET_UINT24(PC);
         break;
       case JSOP_NEWOBJECT:
         /*
          * Scripts with NEWOBJECT must be compileAndGo, but treat these like
@@ -6863,17 +6888,17 @@ mjit::Compiler::jsop_newinit()
     frame.extra(frame.peek(-1)).initObject = baseobj;
 
     return true;
 }
 
 bool
 mjit::Compiler::jsop_regexp()
 {
-    JSObject *obj = script->getRegExp(fullAtomIndex(PC));
+    JSObject *obj = script->getRegExp(GET_UINT32_INDEX(PC));
     RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL;
 
     if (!globalObj ||
         &obj->global() != globalObj ||
         !cx->typeInferenceEnabled() ||
         analysis->localsAliasStack() ||
         types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx),
                                        types::OBJECT_FLAG_REGEXP_FLAGS_SET)) {
@@ -7349,17 +7374,17 @@ mjit::Compiler::jsop_tableswitch(jsbytec
     JS_ASSERT(numJumps >= 0);
 
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_INT32) || numJumps > 256) {
         frame.syncAndForgetEverything();
         masm.move(ImmPtr(originalPC), Registers::ArgReg1);
 
         /* prepareStubCall() is not needed due to forgetEverything() */
-        INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
+        INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
         frame.pop();
         masm.jump(Registers::ReturnReg);
         return true;
     }
 
     RegisterID dataReg;
     if (fe->isConstant()) {
         JS_ASSERT(fe->isType(JSVAL_TYPE_INT32));
@@ -7396,17 +7421,17 @@ mjit::Compiler::jsop_tableswitch(jsbytec
     Jump defaultCase = masm.branch32(Assembler::AboveOrEqual, dataReg, Imm32(numJumps));
     BaseIndex jumpTarget(reg, dataReg, Assembler::ScalePtr);
     masm.jump(jumpTarget);
 
     if (notInt.isSet()) {
         stubcc.linkExitDirect(notInt.get(), stubcc.masm.label());
         stubcc.leave();
         stubcc.masm.move(ImmPtr(originalPC), Registers::ArgReg1);
-        OOL_STUBCALL(stubs::TableSwitch, REJOIN_JUMP);
+        OOL_STUBCALL(stubs::TableSwitch, REJOIN_NONE);
         stubcc.masm.jump(Registers::ReturnReg);
     }
     frame.pop();
     return jumpAndRun(defaultCase, originalPC + defaultTarget);
 #endif
 }
 
 void
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -542,20 +542,20 @@ js_InternalThrow(VMFrame &f)
                 if (st == JSTRAP_CONTINUE && handler) {
                     st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval,
                                  cx->debugHooks->throwHookData);
                 }
 
                 switch (st) {
                 case JSTRAP_ERROR:
                     cx->clearPendingException();
-                    return NULL;
+                    break;
 
                 case JSTRAP_CONTINUE:
-                  break;
+                    break;
 
                 case JSTRAP_RETURN:
                     cx->clearPendingException();
                     cx->fp()->setReturnValue(rval);
                     return cx->jaegerCompartment()->forceReturnFromExternC();
 
                 case JSTRAP_THROW:
                     cx->setPendingException(rval);
@@ -867,21 +867,16 @@ js_InternalInterpret(void *returnData, v
         if (script->hasBreakpointsAt(pc))
             skipTrap = true;
         break;
 
       case REJOIN_FALLTHROUGH:
         f.regs.pc = nextpc;
         break;
 
-      case REJOIN_JUMP:
-        f.regs.pc = (jsbytecode *) returnReg;
-        JS_ASSERT(unsigned(f.regs.pc - script->code) < script->length);
-        break;
-
       case REJOIN_NATIVE:
       case REJOIN_NATIVE_LOWERED:
       case REJOIN_NATIVE_GETTER: {
         /*
          * We don't rejoin until after the native stub finishes execution, in
          * which case the return value will be in memory. For lowered natives,
          * the return value will be in the 'this' value's slot.
          */
--- a/js/src/methodjit/LoopState.cpp
+++ b/js/src/methodjit/LoopState.cpp
@@ -1892,16 +1892,17 @@ LoopState::analyzeLoopBody(unsigned fram
           }
 
           case JSOP_ENUMELEM:
           case JSOP_ENUMCONSTELEM:
             unknownModset = true;
             break;
 
           case JSOP_LOOPHEAD:
+          case JSOP_LOOPENTRY:
           case JSOP_POP:
           case JSOP_ZERO:
           case JSOP_ONE:
           case JSOP_INT8:
           case JSOP_INT32:
           case JSOP_UINT16:
           case JSOP_UINT24:
           case JSOP_FALSE:
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -1291,16 +1291,19 @@ JITChunk::~JITChunk()
 #endif
 }
 
 void
 JITScript::destroy(JSContext *cx)
 {
     for (unsigned i = 0; i < nchunks; i++)
         destroyChunk(cx, i);
+
+    if (shimPool)
+        shimPool->release();
 }
 
 void
 JITScript::destroyChunk(JSContext *cx, unsigned chunkIndex, bool resetUses)
 {
     ChunkDescriptor &desc = chunkDescriptor(chunkIndex);
 
     if (desc.chunk) {
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -304,19 +304,16 @@ enum RejoinState {
      * State is coherent for the start of the current bytecode, which is a TRAP
      * that has already been invoked and should not be invoked again.
      */
     REJOIN_TRAP,
 
     /* State is coherent for the start of the next (fallthrough) bytecode. */
     REJOIN_FALLTHROUGH,
 
-    /* State is coherent for the start of the bytecode returned by the call. */
-    REJOIN_JUMP,
-
     /*
      * As for REJOIN_FALLTHROUGH, but holds a reference on the compartment's
      * orphaned native pools which needs to be reclaimed by InternalInterpret.
      * The return value needs to be adjusted if REJOIN_NATIVE_LOWERED, and
      * REJOIN_NATIVE_GETTER is for ABI calls made for property accesses.
      */
     REJOIN_NATIVE,
     REJOIN_NATIVE_LOWERED,
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -451,13 +451,46 @@ Recompiler::clearStackReferences(JSConte
         }
 
         patchFrame(cx->compartment, f, script);
     }
 
     cx->compartment->types.recompilations++;
 }
 
+void
+Recompiler::clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
+                                         JITScript *jit, size_t chunkIndex,
+                                         bool resetUses)
+{
+    Recompiler::clearStackReferences(cx, script);
+
+    bool releaseChunk = true;
+    if (jit->nchunks > 1) {
+        // If we are in the middle of a native call from a native or getter IC,
+        // we need to make sure all JIT code for the script is purged, as
+        // otherwise we will have orphaned the native stub but pointers to it
+        // still exist in the containing chunk.
+        for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame();
+             f != NULL;
+             f = f->previous) {
+            if (f->fp()->script() == script) {
+                JS_ASSERT(f->stubRejoin != REJOIN_NATIVE &&
+                          f->stubRejoin != REJOIN_NATIVE_LOWERED &&
+                          f->stubRejoin != REJOIN_NATIVE_GETTER);
+                if (f->stubRejoin == REJOIN_NATIVE_PATCHED) {
+                    mjit::ReleaseScriptCode(cx, script);
+                    releaseChunk = false;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (releaseChunk)
+        jit->destroyChunk(cx, chunkIndex, resetUses);
+}
+
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* JS_METHODJIT */
 
--- a/js/src/methodjit/Retcon.h
+++ b/js/src/methodjit/Retcon.h
@@ -60,19 +60,28 @@ namespace mjit {
  * and patching up those existing frames to go into the interpreter. If you
  * ever change the code associated with a JSScript, or otherwise would cause
  * existing JITed code to be incorrect, you /must/ use this to invalidate the
  * JITed code, fixing up the stack in the process.
  */
 class Recompiler {
 public:
 
+    // Clear all uses of compiled code for script on the stack. This must be
+    // followed by destroying all JIT code for the script.
     static void
     clearStackReferences(JSContext *cx, JSScript *script);
 
+    // Clear all uses of compiled code for script on the stack, along with
+    // the specified compiled chunk.
+    static void
+    clearStackReferencesAndChunk(JSContext *cx, JSScript *script,
+                                 JITScript *jit, size_t chunkIndex,
+                                 bool resetUses = true);
+
     static void
     expandInlineFrames(JSCompartment *compartment, StackFrame *fp, mjit::CallSite *inlined,
                        StackFrame *next, VMFrame *f);
 
     static void patchFrame(JSCompartment *compartment, VMFrame *f, JSScript *script);
 
 private:
 
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -891,33 +891,18 @@ stubs::Interrupt(VMFrame &f, jsbytecode 
     if (!js_HandleExecutionInterrupt(f.cx))
         THROW();
 }
 
 void JS_FASTCALL
 stubs::RecompileForInline(VMFrame &f)
 {
     ExpandInlineFrames(f.cx->compartment);
-    Recompiler::clearStackReferences(f.cx, f.script());
-
-    bool releaseChunk = true;
-    if (f.jit()->nchunks > 1) {
-        StackFrame *fp = f.fp();
-        for (FrameRegsIter i(f.cx); !i.done(); ++i) {
-            StackFrame *xfp = i.fp();
-            if (xfp->script() == fp->script() && xfp != fp) {
-                mjit::ReleaseScriptCode(f.cx, fp->script());
-                releaseChunk = false;
-                break;
-            }
-        }
-    }
-
-    if (releaseChunk)
-        f.jit()->destroyChunk(f.cx, f.chunkIndex(), /* resetUses = */ false);
+    Recompiler::clearStackReferencesAndChunk(f.cx, f.script(), f.jit(), f.chunkIndex(),
+                                             /* resetUses = */ false);
 }
 
 void JS_FASTCALL
 stubs::Trap(VMFrame &f, uint32_t trapTypes)
 {
     Value rval;
 
     /*
@@ -1496,23 +1481,28 @@ stubs::LeaveBlock(VMFrame &f)
 
 inline void *
 FindNativeCode(VMFrame &f, jsbytecode *target)
 {
     void* native = f.fp()->script()->nativeCodeForPC(f.fp()->isConstructing(), target);
     if (native)
         return native;
 
-    CompileStatus status = CanMethodJIT(f.cx, f.script(), target, f.fp()->isConstructing(),
-                                        CompileRequest_Interpreter);
-    if (status == Compile_Error)
-        THROWV(NULL);
+    uint32_t sourceOffset = f.pc() - f.script()->code;
+    uint32_t targetOffset = target - f.script()->code;
 
-    mjit::ClearAllFrames(f.cx->compartment);
-    return target;
+    CrossChunkEdge *edges = f.jit()->edges();
+    for (size_t i = 0; i < f.jit()->nedges; i++) {
+        const CrossChunkEdge &edge = edges[i];
+        if (edge.source == sourceOffset && edge.target == targetOffset)
+            return edge.shimLabel;
+    }
+
+    JS_NOT_REACHED("Missing edge");
+    return NULL;
 }
 
 void * JS_FASTCALL
 stubs::LookupSwitch(VMFrame &f, jsbytecode *pc)
 {
     jsbytecode *jpc = pc;
     JSScript *script = f.fp()->script();
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1911,19 +1911,19 @@ SrcNotes(JSContext *cx, JSScript *script
           case SRC_LABEL:
           case SRC_LABELBRACE:
           case SRC_BREAK2LABEL:
           case SRC_CONT2LABEL: {
             uint32_t index = js_GetSrcNoteOffset(sn, 0);
             JSAtom *atom = script->getAtom(index);
             Sprint(sp, " atom %u (", index);
             size_t len = PutEscapedString(NULL, 0, atom, '\0');
-            if (char *buf = SprintReserveAmount(sp, len)) {
+            if (char *buf = sp->reserve(len)) {
                 PutEscapedString(buf, len, atom, 0);
-                buf[len] = '\0';
+                buf[len] = 0;
             }
             Sprint(sp, ")");
             break;
           }
           case SRC_FUNCDEF: {
             uint32_t index = js_GetSrcNoteOffset(sn, 0);
             JSObject *obj = script->getObject(index);
             JSFunction *fun = obj->toFunction();
@@ -1959,30 +1959,30 @@ SrcNotes(JSContext *cx, JSScript *script
         }
         Sprint(sp, "\n");
     }
 }
 
 static JSBool
 Notes(JSContext *cx, uintN argc, jsval *vp)
 {
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return JS_FALSE;
 
     jsval *argv = JS_ARGV(cx, vp);
     for (uintN i = 0; i < argc; i++) {
         JSScript *script = ValueToScript(cx, argv[i]);
         if (!script)
             continue;
 
         SrcNotes(cx, script, &sprinter);
     }
 
-    JSString *str = JS_NewStringCopyZ(cx, sprinter.base);
+    JSString *str = JS_NewStringCopyZ(cx, sprinter.string());
     if (!str)
         return JS_FALSE;
     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
     return JS_TRUE;
 }
 
 JS_STATIC_ASSERT(JSTRY_CATCH == 0);
 JS_STATIC_ASSERT(JSTRY_FINALLY == 1);
@@ -2112,82 +2112,80 @@ struct DisassembleOptionParser {
 
 static JSBool
 DisassembleToString(JSContext *cx, uintN argc, jsval *vp)
 {
     DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
     if (!p.parse(cx))
         return false;
 
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
-    Sprinter *sp = &sprinter;
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return false;
 
     bool ok = true;
     if (p.argc == 0) {
         /* Without arguments, disassemble the current script. */
         if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
             JSScript *script = JS_GetFrameScript(cx, frame);
-            if (js_Disassemble(cx, script, p.lines, sp)) {
-                SrcNotes(cx, script, sp);
-                TryNotes(cx, script, sp);
+            if (js_Disassemble(cx, script, p.lines, &sprinter)) {
+                SrcNotes(cx, script, &sprinter);
+                TryNotes(cx, script, &sprinter);
             } else {
                 ok = false;
             }
         }
     } else {
         for (uintN i = 0; i < p.argc; i++) {
             JSFunction *fun;
             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
-            ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
+            ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
         }
     }
 
-    JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.base) : NULL;
+    JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.string()) : NULL;
     if (!str)
         return false;
     JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str));
     return true;
 }
 
 static JSBool
 Disassemble(JSContext *cx, uintN argc, jsval *vp)
 {
     DisassembleOptionParser p(argc, JS_ARGV(cx, vp));
     if (!p.parse(cx))
         return false;
 
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
-    Sprinter *sp = &sprinter;
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return false;
 
     bool ok = true;
     if (p.argc == 0) {
         /* Without arguments, disassemble the current script. */
         if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) {
             JSScript *script = JS_GetFrameScript(cx, frame);
-            if (js_Disassemble(cx, script, p.lines, sp)) {
-                SrcNotes(cx, script, sp);
-                TryNotes(cx, script, sp);
+            if (js_Disassemble(cx, script, p.lines, &sprinter)) {
+                SrcNotes(cx, script, &sprinter);
+                TryNotes(cx, script, &sprinter);
             } else {
                 ok = false;
             }
         }
     } else {
         for (uintN i = 0; i < p.argc; i++) {
             JSFunction *fun;
             JSScript *script = ValueToScript(cx, p.argv[i], &fun);
-            ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp);
+            ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter);
         }
     }
 
     if (ok)
-        fprintf(stdout, "%s\n", sprinter.base);
+        fprintf(stdout, "%s\n", sprinter.string());
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return ok;
 }
 
 static JSBool
 DisassFile(JSContext *cx, uintN argc, jsval *vp)
 {
     /* Support extra options at the start, just like Dissassemble. */
@@ -2213,22 +2211,22 @@ DisassFile(JSContext *cx, uintN argc, js
 
     uint32_t oldopts = JS_GetOptions(cx);
     JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
     JSScript *script = JS_CompileUTF8File(cx, thisobj, filename.ptr());
     JS_SetOptions(cx, oldopts);
     if (!script)
         return false;
 
-    LifoAllocScope las(&cx->tempLifoAlloc());
-    Sprinter sprinter;
-    INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0);
+    Sprinter sprinter(cx);
+    if (!sprinter.init())
+        return false;
     bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter);
     if (ok)
-        fprintf(stdout, "%s\n", sprinter.base);
+        fprintf(stdout, "%s\n", sprinter.string());
     if (!ok)
         return false;
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
     return true;
 }
 
 static JSBool
@@ -2262,20 +2260,21 @@ DisassWithSrc(JSContext *cx, uintN argc,
                                  JSSMSG_CANT_OPEN, script->filename,
                                  strerror(errno));
             return JS_FALSE;
         }
 
         pc = script->code;
         end = pc + script->length;
 
-        LifoAllocScope las(&cx->tempLifoAlloc());
-        Sprinter sprinter;
-        Sprinter *sp = &sprinter;
-        INIT_SPRINTER(cx, sp, &cx->tempLifoAlloc(), 0);
+        Sprinter sprinter(cx);
+        if (!sprinter.init()) {
+            ok = JS_FALSE;
+            goto bail;
+        }
 
         /* burn the leading lines */
         line2 = JS_PCToLineNumber(cx, script, pc);
         for (line1 = 0; line1 < line2 - 1; line1++) {
             char *tmp = fgets(linebuf, LINE_BUF_LEN, file);
             if (!tmp) {
                 JS_ReportError(cx, "failed to read %s fully", script->filename);
                 ok = JS_FALSE;
@@ -2285,36 +2284,36 @@ DisassWithSrc(JSContext *cx, uintN argc,
 
         bupline = 0;
         while (pc < end) {
             line2 = JS_PCToLineNumber(cx, script, pc);
 
             if (line2 < line1) {
                 if (bupline != line2) {
                     bupline = line2;
-                    Sprint(sp, "%s %3u: BACKUP\n", sep, line2);
+                    Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2);
                 }
             } else {
                 if (bupline && line1 == line2)
-                    Sprint(sp, "%s %3u: RESTORE\n", sep, line2);
+                    Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2);
                 bupline = 0;
                 while (line1 < line2) {
                     if (!fgets(linebuf, LINE_BUF_LEN, file)) {
                         JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
                                              JSSMSG_UNEXPECTED_EOF,
                                              script->filename);
                         ok = JS_FALSE;
                         goto bail;
                     }
                     line1++;
-                    Sprint(sp, "%s %3u: %s", sep, line1, linebuf);
+                    Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf);
                 }
             }
 
-            len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, sp);
+            len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, &sprinter);
             if (!len) {
                 ok = JS_FALSE;
                 goto bail;
             }
             pc += len;
         }
 
       bail:
@@ -3954,16 +3953,23 @@ EnableStackWalkingAssertion(JSContext *c
 
 static JSBool
 GetMaxArgs(JSContext *cx, uintN arg, jsval *vp)
 {
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(StackSpace::ARGS_LENGTH_MAX));
     return JS_TRUE;
 }
 
+static JSBool
+Terminate(JSContext *cx, uintN arg, jsval *vp)
+{
+    JS_ClearPendingException(cx);
+    return JS_FALSE;
+}
+
 static JSFunctionSpec shell_functions[] = {
     JS_FN("version",        Version,        0,0),
     JS_FN("revertVersion",  RevertVersion,  0,0),
     JS_FN("options",        Options,        0,0),
     JS_FN("load",           Load,           1,0),
     JS_FN("evaluate",       Evaluate,       1,0),
     JS_FN("run",            Run,            1,0),
     JS_FN("readline",       ReadLine,       0,0),
@@ -4042,16 +4048,17 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("mjitdatastats",  MJitDataStats,  0,0),
 #endif
     JS_FN("mjitChunkLimit", MJitChunkLimit, 1,0),
     JS_FN("stringstats",    StringStats,    0,0),
     JS_FN("newGlobal",      NewGlobal,      1,0),
     JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0),
     JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0),
     JS_FN("getMaxArgs",     GetMaxArgs,     0,0),
+    JS_FN("terminate",      Terminate,      0,0),
     JS_FS_END
 };
 
 static const char shell_help_header[] =
 "Command                  Description\n"
 "=======                  ===========\n";
 
 static const char *const shell_help_messages[] = {
@@ -4196,16 +4203,18 @@ static const char *const shell_help_mess
 "parseLegacyJSON(str)     Parse str as legacy JSON, returning the result if the\n"
 "                         parse succeeded and throwing a SyntaxError if not.",
 "enableStackWalkingAssertion(enabled)\n"
 "  Enables or disables a particularly expensive assertion in stack-walking\n"
 "  code.  If your test isn't ridiculously thorough, such that performing this\n"
 "  assertion increases test duration by an order of magnitude, you shouldn't\n"
 "  use this.",
 "getMaxArgs()             Return the maximum number of supported args for a call.",
+"terminate()              Terminate JavaScript execution, as if we had run out of\n"
+"                         memory or been terminated by the slow script dialog.",
 
 /* Keep these last: see the static assertion below. */
 #ifdef MOZ_PROFILING
 "startProfiling([profileName])\n"
 "                         Start a profiling session\n"
 "                         Profiler must be running with programatic sampling",
 "stopProfiling([profileName])\n"
 "                         Stop a running profiling session",
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/join-01.js
@@ -0,0 +1,76 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+//-----------------------------------------------------------------------------
+print("ES5: Array.prototype.join");
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var count;
+var stringifyCounter = { toString: function() { count++; return "obj"; } };
+
+var arr = [1, 2, 3, 4, 5];
+assertEq(arr.join(), "1,2,3,4,5");
+assertEq(arr.join(","), "1,2,3,4,5");
+assertEq(arr.join(undefined), "1,2,3,4,5");
+assertEq(arr.join(4), "142434445");
+assertEq(arr.join(""), "12345");
+
+count = 0;
+assertEq(arr.join(stringifyCounter), "1obj2obj3obj4obj5");
+assertEq(count, 1);
+
+var holey = [1, 2, , 4, 5];
+assertEq(holey.join(), "1,2,,4,5");
+assertEq(holey.join(","), "1,2,,4,5");
+assertEq(holey.join(undefined), "1,2,,4,5");
+assertEq(holey.join(4), "14244445");
+
+count = 0;
+assertEq(holey.join(stringifyCounter), "1obj2objobj4obj5");
+assertEq(count, 1);
+
+var nully = [1, 2, 3, null, 5];
+assertEq(nully.join(), "1,2,3,,5");
+assertEq(nully.join(","), "1,2,3,,5");
+assertEq(nully.join(undefined), "1,2,3,,5");
+assertEq(nully.join(4), "14243445");
+
+count = 0;
+assertEq(nully.join(stringifyCounter), "1obj2obj3objobj5");
+assertEq(count, 1);
+
+var undefiney = [1, undefined, 3, 4, 5];
+assertEq(undefiney.join(), "1,,3,4,5");
+assertEq(undefiney.join(","), "1,,3,4,5");
+assertEq(undefiney.join(undefined), "1,,3,4,5");
+assertEq(undefiney.join(4), "14434445");
+
+count = 0;
+assertEq(undefiney.join(stringifyCounter), "1objobj3obj4obj5");
+assertEq(count, 1);
+
+var funky =
+  {
+    toString: function()
+    {
+      Array.prototype[1] = "chorp";
+      Object.prototype[3] = "fnord";
+      return "funky";
+    }
+  };
+var trailingHoles = [0, funky, /* 2 */, /* 3 */,];
+assertEq(trailingHoles.join(""), "0funkyfnord");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_5/Array/jstests.list
+++ b/js/src/tests/ecma_5/Array/jstests.list
@@ -1,9 +1,10 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Array/
+script join-01.js
 script length-01.js
 script length-set-object.js
 script regress-599159.js
 script sort-01.js
 script splice-return-array-elements-defined-not-set.js
 script splice-suppresses-unvisited-indexes.js
 script toLocaleString-01.js
 script toString-01.js
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -789,16 +789,27 @@ ContextStack::pushGeneratorFrame(JSConte
 
     StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen);
     Value *stackvp = (Value *)stackfp - vplen;
 
     /* Save this for popGeneratorFrame. */
     gfg->gen_ = gen;
     gfg->stackvp_ = stackvp;
 
+    /*
+     * Trigger incremental barrier on the floating frame's generator object.
+     * This is normally traced through only by associated arguments/call
+     * objects, but only when the generator is not actually on the stack.
+     * We don't need to worry about generational barriers as the generator
+     * object has a trace hook and cannot be nursery allocated.
+     */
+    JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj;
+    JS_ASSERT(genobj->getClass()->trace);
+    JSObject::writeBarrierPre(genobj);
+
     /* Copy from the generator's floating frame to the stack. */
     stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp);
     stackfp->resetGeneratorPrev(cx);
     stackfp->unsetFloatingGenerator();
     gfg->regs_.rebaseFromTo(gen->regs, *stackfp);
 
     gfg->prevRegs_ = seg_->pushRegs(gfg->regs_);
     JS_ASSERT(space().firstUnused() == gfg->regs_.sp);
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -41,52 +41,56 @@
 #ifndef Stack_h__
 #define Stack_h__
 
 #include "jsfun.h"
 
 struct JSContext;
 struct JSCompartment;
 
+#ifdef JS_METHODJIT
+namespace js { namespace mjit { struct CallSite; }}
+typedef js::mjit::CallSite JSInlinedSite;
+#else
+struct JSInlinedSite {};
+#endif
+
+typedef /* js::mjit::RejoinState */ size_t JSRejoinState;
+
 namespace js {
 
 class StackFrame;
 class FrameRegs;
 class StackSegment;
 class StackSpace;
 class ContextStack;
 
 class InvokeArgsGuard;
 class InvokeFrameGuard;
 class FrameGuard;
 class ExecuteFrameGuard;
 class DummyFrameGuard;
 class GeneratorFrameGuard;
 
-namespace mjit {
-    struct JITScript;
-    struct CallSite;
-    jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline);
-}
-namespace detail { struct OOMCheck; }
-
 class CallIter;
 class FrameRegsIter;
 class AllFramesIter;
 
 class ArgumentsObject;
 class StaticBlockObject;
 
-#ifdef JS_METHODJIT
-typedef js::mjit::CallSite JSInlinedSite;
-#else
-struct JSInlinedSite {};
-#endif
+namespace mjit {
+    struct JITScript;
+    jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline);
+}
+namespace detail {
+    struct OOMCheck;
+}
 
-typedef /* js::mjit::RejoinState */ size_t JSRejoinState;
+/*****************************************************************************/
 
 /*
  * VM stack layout
  *
  * SpiderMonkey uses a per-thread stack to store the activation records,
  * parameters, locals, and expression temporaries for the stack of actively
  * executing scripts, functions and generators. The per-thread stack is owned
  * by the StackSpace object stored in the thread's ThreadData.
@@ -308,16 +312,32 @@ CallArgsListFromArgv(uintN argc, Value *
 JS_ALWAYS_INLINE CallArgsList
 CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev)
 {
     return CallArgsListFromArgv(argc, vp + 2, prev);
 }
 
 /*****************************************************************************/
 
+/* Flags specified for a frame as it is constructed. */
+enum InitialFrameFlags {
+    INITIAL_NONE           =          0,
+    INITIAL_CONSTRUCT      =       0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
+    INITIAL_LOWERED        =   0x800000  /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
+};
+
+enum ExecuteType {
+    EXECUTE_GLOBAL         =        0x1, /* == StackFrame::GLOBAL */
+    EXECUTE_DIRECT_EVAL    =        0x8, /* == StackFrame::EVAL */
+    EXECUTE_INDIRECT_EVAL  =        0x9, /* == StackFrame::GLOBAL | EVAL */
+    EXECUTE_DEBUG          =       0x18  /* == StackFrame::EVAL | DEBUGGER */
+};
+
+/*****************************************************************************/
+
 class StackFrame
 {
   public:
     enum Flags {
         /* Primary frame type */
         GLOBAL             =        0x1,  /* frame pushed for a global script */
         FUNCTION           =        0x2,  /* frame pushed for a scripted call */
         DUMMY              =        0x4,  /* frame pushed for bookkeeping */
@@ -1392,37 +1412,312 @@ class StackSegment
     /* For jit access: */
 
     static const size_t offsetOfRegs() { return offsetof(StackSegment, regs_); }
 };
 
 static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value);
 JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0);
 
-inline Value *
-StackSpace::firstUnused() const { return seg_ ? seg_->end() : base_; }
+/*****************************************************************************/
+
+class StackSpace
+{
+    StackSegment  *seg_;
+    Value         *base_;
+    mutable Value *conservativeEnd_;
+#ifdef XP_WIN
+    mutable Value *commitEnd_;
+#endif
+    Value         *defaultEnd_;
+    Value         *trustedEnd_;
+
+    void assertInvariants() const {
+        JS_ASSERT(base_ <= conservativeEnd_);
+#ifdef XP_WIN
+        JS_ASSERT(conservativeEnd_ <= commitEnd_);
+        JS_ASSERT(commitEnd_ <= trustedEnd_);
+#endif
+        JS_ASSERT(conservativeEnd_ <= defaultEnd_);
+        JS_ASSERT(defaultEnd_ <= trustedEnd_);
+    }
+
+    /* The total number of values/bytes reserved for the stack. */
+    static const size_t CAPACITY_VALS  = 512 * 1024;
+    static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
+
+    /* How much of the stack is initially committed. */
+    static const size_t COMMIT_VALS    = 16 * 1024;
+    static const size_t COMMIT_BYTES   = COMMIT_VALS * sizeof(Value);
+
+    /* How much space is reserved at the top of the stack for trusted JS. */
+    static const size_t BUFFER_VALS    = 16 * 1024;
+    static const size_t BUFFER_BYTES   = BUFFER_VALS * sizeof(Value);
+
+    static void staticAsserts() {
+        JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
+    }
+
+    friend class AllFramesIter;
+    friend class ContextStack;
+    friend class StackFrame;
+
+    /*
+     * Except when changing compartment (see pushDummyFrame), the 'dest'
+     * parameter of ensureSpace is cx->compartment. Ideally, we'd just pass
+     * this directly (and introduce a helper that supplies cx->compartment when
+     * no 'dest' is given). For some compilers, this really hurts performance,
+     * so, instead, a trivially sinkable magic constant is used to indicate
+     * that dest should be cx->compartment.
+     */
+    static const size_t CX_COMPARTMENT = 0xc;
+
+    inline bool ensureSpace(JSContext *cx, MaybeReportError report,
+                            Value *from, ptrdiff_t nvals,
+                            JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const;
+    JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
+                                        Value *from, ptrdiff_t nvals,
+                                        JSCompartment *dest) const;
+
+    StackSegment &findContainingSegment(const StackFrame *target) const;
+
+  public:
+    StackSpace();
+    bool init();
+    ~StackSpace();
 
-inline bool         ContextStack::hasfp() const     { return seg_ && seg_->maybeRegs(); }
-inline FrameRegs *  ContextStack::maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
-inline StackFrame * ContextStack::maybefp() const   { return seg_ ? seg_->maybefp() : NULL; }
-inline FrameRegs &  ContextStack::regs() const      { JS_ASSERT(hasfp()); return seg_->regs(); }
-inline StackFrame * ContextStack::fp() const        { JS_ASSERT(hasfp()); return seg_->fp(); }
+    /*
+     * Maximum supported value of arguments.length. This bounds the maximum
+     * number of arguments that can be supplied to Function.prototype.apply.
+     * This value also bounds the number of elements parsed in an array
+     * initialiser.
+     *
+     * Since arguments are copied onto the stack, the stack size is the
+     * limiting factor for this constant. Use the max stack size (available to
+     * untrusted code) with an extra buffer so that, after such an apply, the
+     * callee can do a little work without OOMing.
+     */
+    static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
+
+    /* See stack layout comment in Stack.h. */
+    inline Value *firstUnused() const { return seg_ ? seg_->end() : base_; }
+
+    StackSegment &containingSegment(const StackFrame *target) const;
+
+    /*
+     * Extra space to reserve on the stack for method JIT frames, beyond the
+     * frame's nslots. This may be used for inlined stack frames, slots storing
+     * loop invariant code, or to reserve space for pushed callee frames. Note
+     * that this space should be reserved when pushing interpreter frames as
+     * well, so that we don't need to check the stack when entering the method
+     * JIT at loop heads or safe points.
+     */
+    static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10;
 
-} /* namespace js */
+    /*
+     * Return a limit against which jit code can check for. This limit is not
+     * necessarily the end of the stack since we lazily commit stack memory on
+     * some platforms. Thus, when the stack limit is exceeded, the caller should
+     * use tryBumpLimit to attempt to increase the stack limit by committing
+     * more memory. If the stack is truly exhausted, tryBumpLimit will report an
+     * error and return NULL.
+     *
+     * An invariant of the methodjit is that there is always space to push a
+     * frame on top of the current frame's expression stack (which can be at
+     * most script->nslots deep). getStackLimit ensures that the returned limit
+     * does indeed have this required space and reports an error and returns
+     * NULL if this reserve space cannot be allocated.
+     */
+    inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
+    bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
+
+    /* Called during GC: mark segments, frames, and slots under firstUnused. */
+    void mark(JSTracer *trc);
+
+    /* We only report the committed size;  uncommitted size is uninteresting. */
+    JS_FRIEND_API(size_t) sizeOfCommitted();
+};
+
+/*****************************************************************************/
+
+class ContextStack
+{
+    StackSegment *seg_;
+    StackSpace *space_;
+    JSContext *cx_;
+
+    /*
+     * Return whether this ContextStack is at the top of the contiguous stack.
+     * This is a precondition for extending the current segment by pushing
+     * stack frames or overrides etc.
+     *
+     * NB: Just because a stack is onTop() doesn't mean there is necessarily
+     * a frame pushed on the stack. For this, use hasfp().
+     */
+    bool onTop() const;
+
+#ifdef DEBUG
+    void assertSpaceInSync() const;
+#else
+    void assertSpaceInSync() const {}
+#endif
 
-inline bool            JSContext::hasfp() const      { return stack.hasfp(); }
-inline js::StackFrame* JSContext::fp() const         { return stack.fp(); }
-inline js::StackFrame* JSContext::maybefp() const    { return stack.maybefp(); }
-inline js::FrameRegs&  JSContext::regs() const       { return stack.regs(); }
-inline js::FrameRegs*  JSContext::maybeRegs() const  { return stack.maybeRegs(); }
+    /* Implementation details of push* public interface. */
+    StackSegment *pushSegment(JSContext *cx);
+    enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
+    Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
+                       MaybeExtend extend, bool *pushedSeg,
+                       JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT);
+
+    inline StackFrame *
+    getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
+                 JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const;
+
+    /* Make pop* functions private since only called by guard classes. */
+    void popSegment();
+    friend class InvokeArgsGuard;
+    void popInvokeArgs(const InvokeArgsGuard &iag);
+    friend class FrameGuard;
+    void popFrame(const FrameGuard &fg);
+    friend class GeneratorFrameGuard;
+    void popGeneratorFrame(const GeneratorFrameGuard &gfg);
+
+    friend class StackIter;
+
+  public:
+    ContextStack(JSContext *cx);
+    ~ContextStack();
+
+    /*** Stack accessors ***/
+
+    /*
+     * A context's stack is "empty" if there are no scripts or natives
+     * executing. Note that JS_SaveFrameChain does not factor into this definition.
+     */
+    bool empty() const                { return !seg_; }
+
+    /*
+     * Return whether there has been at least one frame pushed since the most
+     * recent call to JS_SaveFrameChain. Note that natives do not have frames
+     * and dummy frames are frames that do not represent script execution hence
+     * this query has little semantic meaning past "you can call fp()".
+     */
+    inline bool hasfp() const { return seg_ && seg_->maybeRegs(); }
+
+    /*
+     * Return the most recent script activation's registers with the same
+     * caveat as hasfp regarding JS_SaveFrameChain.
+     */
+    inline FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; }
+    inline StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; }
+
+    /* Faster alternatives to maybe* functions. */
+    inline FrameRegs &regs() const { JS_ASSERT(hasfp()); return seg_->regs(); }
+    inline StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); }
+
+    /* The StackSpace currently hosting this ContextStack. */
+    StackSpace &space() const { return *space_; }
+
+    /* Return whether the given frame is in this context's stack. */
+    bool containsSlow(const StackFrame *target) const;
+
+    /*** Stack manipulation ***/
+
+    /*
+     * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
+     * the arguments to Invoke. A single allocation can be used for multiple
+     * Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from
+     * an immediately-enclosing (stack-wise) call to pushInvokeArgs.
+     */
+    bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
+
+    /* Called by Invoke for a scripted function call. */
+    bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
+                         InitialFrameFlags initial, InvokeFrameGuard *ifg);
 
-namespace js {
+    /* Called by Execute for execution of eval or global code. */
+    bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
+                          JSObject &scopeChain, ExecuteType type,
+                          StackFrame *evalInFrame, ExecuteFrameGuard *efg);
+
+    /*
+     * Called by SendToGenerator to resume a yielded generator. In addition to
+     * pushing a frame onto the VM stack, this function copies over the
+     * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
+     * will copy the frame back to the floating frame.
+     */
+    bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
+
+    /*
+     * When changing the compartment of a cx, it is necessary to immediately
+     * change the scope chain to a global in the right compartment since any
+     * amount of general VM code can run before the first scripted frame is
+     * pushed (if at all). This is currently and hackily accomplished by
+     * pushing a "dummy frame" with the correct scope chain. On success, this
+     * function will change the compartment to 'scopeChain.compartment()' and
+     * push a dummy frame for 'scopeChain'. On failure, nothing is changed.
+     */
+    bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg);
+
+    /*
+     * An "inline frame" may only be pushed from within the top, active
+     * segment. This is the case for calls made inside mjit code and Interpret.
+     * The 'stackLimit' overload updates 'stackLimit' if it changes.
+     */
+    bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
+                         JSFunction &callee, JSScript *script,
+                         InitialFrameFlags initial);
+    bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
+                         JSFunction &callee, JSScript *script,
+                         InitialFrameFlags initial, Value **stackLimit);
+    void popInlineFrame(FrameRegs &regs);
+
+    /* Pop a partially-pushed frame after hitting the limit before throwing. */
+    void popFrameAfterOverflow();
 
-inline void
-ContextStack::repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
+    /* Get the topmost script and optional pc on the stack. */
+    inline JSScript *currentScript(jsbytecode **pc = NULL) const;
+
+    /* Get the scope chain for the topmost scripted call on the stack. */
+    inline JSObject *currentScriptedScopeChain() const;
+
+    /*
+     * Called by the methodjit for an arity mismatch. Arity mismatch can be
+     * hot, so getFixupFrame avoids doing call setup performed by jit code when
+     * FixupArity returns. In terms of work done:
+     *
+     *   getFixupFrame = pushInlineFrame -
+     *                   (fp->initJitFrameLatePrologue + regs->prepareToRun)
+     */
+    StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
+                              const CallArgs &args, JSFunction *fun, JSScript *script,
+                              void *ncode, InitialFrameFlags initial, Value **stackLimit);
+
+    bool saveFrameChain();
+    void restoreFrameChain();
+
+    /*
+     * As an optimization, the interpreter/mjit can operate on a local
+     * FrameRegs instance repoint the ContextStack to this local instance.
+     */
+    inline void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); }
+
+    /*** For JSContext: ***/
+
+    /*
+     * To avoid indirection, ContextSpace caches a pointer to the StackSpace.
+     * This must be kept coherent with cx->thread->data.space by calling
+     * 'threadReset' whenver cx->thread changes.
+     */
+    void threadReset();
+
+    /*** For jit compiler: ***/
+
+    static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
+};
 
 /*****************************************************************************/
 
 class InvokeArgsGuard : public CallArgsList
 {
     friend class ContextStack;
     ContextStack *stack_;
     bool pushedSeg_;
@@ -1480,16 +1775,17 @@ class GeneratorFrameGuard : public Frame
  *
  *   for (Stackiter i(cx); !i.done(); ++i) {
  *     if (i.isScript()) {
  *       ... i.fp() ... i.sp() ... i.pc()
  *     } else {
  *       JS_ASSERT(i.isNativeCall());
  *       ... i.args();
  *     }
+ *   }
  *
  * The SavedOption parameter additionally lets the iterator continue through
  * breaks in the callstack (from JS_SaveFrameChain). The default is to stop.
  */
 class StackIter
 {
     friend class ContextStack;
     JSContext    *cx_;
@@ -1577,10 +1873,9 @@ class AllFramesIter
 
   private:
     void settle();
     StackSegment *seg_;
     StackFrame *fp_;
 };
 
 }  /* namespace js */
-
 #endif /* Stack_h__ */
deleted file mode 100644
--- a/js/src/vm/StackSpace.h
+++ /dev/null
@@ -1,367 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=4 sw=4 et tw=79 ft=cpp:
- *
- * ***** 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 SpiderMonkey JavaScript engine.
- *
- * The Initial Developer of the Original Code is
- * Mozilla Corporation.
- * Portions created by the Initial Developer are Copyright (C) 2009
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Luke Wagner <luke@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 ***** */
-
-#ifndef StackSpace_h__
-#define StackSpace_h__
-
-#include "jsprvtd.h"
-
-namespace js {
-
-/* Forward declarations. */
-class FrameGuard;
-class DummyFrameGuard;
-class ExecuteFrameGuard;
-class GeneratorFrameGuard;
-
-/* Flags specified for a frame as it is constructed. */
-enum InitialFrameFlags {
-    INITIAL_NONE           =          0,
-    INITIAL_CONSTRUCT      =       0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */
-    INITIAL_LOWERED        =   0x800000  /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */
-};
-
-enum ExecuteType {
-    EXECUTE_GLOBAL         =        0x1, /* == StackFrame::GLOBAL */
-    EXECUTE_DIRECT_EVAL    =        0x8, /* == StackFrame::EVAL */
-    EXECUTE_INDIRECT_EVAL  =        0x9, /* == StackFrame::GLOBAL | EVAL */
-    EXECUTE_DEBUG          =       0x18  /* == StackFrame::EVAL | DEBUGGER */
-};
-
-/*****************************************************************************/
-
-class StackSpace
-{
-    StackSegment  *seg_;
-    Value         *base_;
-    mutable Value *conservativeEnd_;
-#ifdef XP_WIN
-    mutable Value *commitEnd_;
-#endif
-    Value         *defaultEnd_;
-    Value         *trustedEnd_;
-
-    void assertInvariants() const {
-        JS_ASSERT(base_ <= conservativeEnd_);
-#ifdef XP_WIN
-        JS_ASSERT(conservativeEnd_ <= commitEnd_);
-        JS_ASSERT(commitEnd_ <= trustedEnd_);
-#endif
-        JS_ASSERT(conservativeEnd_ <= defaultEnd_);
-        JS_ASSERT(defaultEnd_ <= trustedEnd_);
-    }
-
-    /* The total number of values/bytes reserved for the stack. */
-    static const size_t CAPACITY_VALS  = 512 * 1024;
-    static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
-
-    /* How much of the stack is initially committed. */
-    static const size_t COMMIT_VALS    = 16 * 1024;
-    static const size_t COMMIT_BYTES   = COMMIT_VALS * sizeof(Value);
-
-    /* How much space is reserved at the top of the stack for trusted JS. */
-    static const size_t BUFFER_VALS    = 16 * 1024;
-    static const size_t BUFFER_BYTES   = BUFFER_VALS * sizeof(Value);
-
-    static void staticAsserts() {
-        JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0);
-    }
-
-    friend class AllFramesIter;
-    friend class ContextStack;
-    friend class StackFrame;
-
-    /*
-     * Except when changing compartment (see pushDummyFrame), the 'dest'
-     * parameter of ensureSpace is cx->compartment. Ideally, we'd just pass
-     * this directly (and introduce a helper that supplies cx->compartment when
-     * no 'dest' is given). For some compilers, this really hurts performance,
-     * so, instead, a trivially sinkable magic constant is used to indicate
-     * that dest should be cx->compartment.
-     */
-    static const size_t CX_COMPARTMENT = 0xc;
-
-    inline bool ensureSpace(JSContext *cx, MaybeReportError report,
-                            Value *from, ptrdiff_t nvals,
-                            JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const;
-    JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report,
-                                        Value *from, ptrdiff_t nvals,
-                                        JSCompartment *dest) const;
-
-    StackSegment &findContainingSegment(const StackFrame *target) const;
-
-  public:
-    StackSpace();
-    bool init();
-    ~StackSpace();
-
-    /*
-     * Maximum supported value of arguments.length. This bounds the maximum
-     * number of arguments that can be supplied to Function.prototype.apply.
-     * This value also bounds the number of elements parsed in an array
-     * initialiser.
-     *
-     * Since arguments are copied onto the stack, the stack size is the
-     * limiting factor for this constant. Use the max stack size (available to
-     * untrusted code) with an extra buffer so that, after such an apply, the
-     * callee can do a little work without OOMing.
-     */
-    static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS);
-
-    /* See stack layout comment in Stack.h. */
-    inline Value *firstUnused() const;
-
-    StackSegment &containingSegment(const StackFrame *target) const;
-
-    /*
-     * Extra space to reserve on the stack for method JIT frames, beyond the
-     * frame's nslots. This may be used for inlined stack frames, slots storing
-     * loop invariant code, or to reserve space for pushed callee frames. Note
-     * that this space should be reserved when pushing interpreter frames as
-     * well, so that we don't need to check the stack when entering the method
-     * JIT at loop heads or safe points.
-     */
-    static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10;
-
-    /*
-     * Return a limit against which jit code can check for. This limit is not
-     * necessarily the end of the stack since we lazily commit stack memory on
-     * some platforms. Thus, when the stack limit is exceeded, the caller should
-     * use tryBumpLimit to attempt to increase the stack limit by committing
-     * more memory. If the stack is truly exhausted, tryBumpLimit will report an
-     * error and return NULL.
-     *
-     * An invariant of the methodjit is that there is always space to push a
-     * frame on top of the current frame's expression stack (which can be at
-     * most script->nslots deep). getStackLimit ensures that the returned limit
-     * does indeed have this required space and reports an error and returns
-     * NULL if this reserve space cannot be allocated.
-     */
-    inline Value *getStackLimit(JSContext *cx, MaybeReportError report);
-    bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit);
-
-    /* Called during GC: mark segments, frames, and slots under firstUnused. */
-    void mark(JSTracer *trc);
-
-    /* We only report the committed size;  uncommitted size is uninteresting. */
-    JS_FRIEND_API(size_t) sizeOfCommitted();
-};
-
-/*****************************************************************************/
-
-class ContextStack
-{
-    StackSegment *seg_;
-    StackSpace *space_;
-    JSContext *cx_;
-
-    /*
-     * Return whether this ContextStack is at the top of the contiguous stack.
-     * This is a precondition for extending the current segment by pushing
-     * stack frames or overrides etc.
-     *
-     * NB: Just because a stack is onTop() doesn't mean there is necessarily
-     * a frame pushed on the stack. For this, use hasfp().
-     */
-    bool onTop() const;
-
-#ifdef DEBUG
-    void assertSpaceInSync() const;
-#else
-    void assertSpaceInSync() const {}
-#endif
-
-    /* Implementation details of push* public interface. */
-    StackSegment *pushSegment(JSContext *cx);
-    enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false };
-    Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars,
-                       MaybeExtend extend, bool *pushedSeg,
-                       JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT);
-
-    inline StackFrame *
-    getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args,
-                 JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const;
-
-    /* Make pop* functions private since only called by guard classes. */
-    void popSegment();
-    friend class InvokeArgsGuard;
-    void popInvokeArgs(const InvokeArgsGuard &iag);
-    friend class FrameGuard;
-    void popFrame(const FrameGuard &fg);
-    friend class GeneratorFrameGuard;
-    void popGeneratorFrame(const GeneratorFrameGuard &gfg);
-
-    friend class StackIter;
-
-  public:
-    ContextStack(JSContext *cx);
-    ~ContextStack();
-
-    /*** Stack accessors ***/
-
-    /*
-     * A context's stack is "empty" if there are no scripts or natives
-     * executing. Note that JS_SaveFrameChain does factor into this definition.
-     */
-    bool empty() const                { return !seg_; }
-
-    /*
-     * Return whether there has been at least one frame pushed since the most
-     * recent call to JS_SaveFrameChain. Note that natives do not have frames
-     * and dummy frames are frames that do not represent script execution hence
-     * this query has little semantic meaning past "you can call fp()".
-     */
-    inline bool hasfp() const;
-
-    /*
-     * Return the most recent script activation's registers with the same
-     * caveat as hasfp regarding JS_SaveFrameChain.
-     */
-    inline FrameRegs *maybeRegs() const;
-    inline StackFrame *maybefp() const;
-
-    /* Faster alternatives to maybe* functions. */
-    inline FrameRegs &regs() const;
-    inline StackFrame *fp() const;
-
-    /* The StackSpace currently hosting this ContextStack. */
-    StackSpace &space() const    { assertSpaceInSync(); return *space_; }
-
-    /* Return whether the given frame is in this context's stack. */
-    bool containsSlow(const StackFrame *target) const;
-
-    /*** Stack manipulation ***/
-
-    /*
-     * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as
-     * the arguments to Invoke. A single allocation can be used for multiple
-     * Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from
-     * an immediately-enclosing (stack-wise) call to pushInvokeArgs.
-     */
-    bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag);
-
-    /* Called by Invoke for a scripted function call. */
-    bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
-                         InitialFrameFlags initial, InvokeFrameGuard *ifg);
-
-    /* Called by Execute for execution of eval or global code. */
-    bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
-                          JSObject &scopeChain, ExecuteType type,
-                          StackFrame *evalInFrame, ExecuteFrameGuard *efg);
-
-    /*
-     * Called by SendToGenerator to resume a yielded generator. In addition to
-     * pushing a frame onto the VM stack, this function copies over the
-     * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor
-     * will copy the frame back to the floating frame.
-     */
-    bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg);
-
-    /*
-     * When changing the compartment of a cx, it is necessary to immediately
-     * change the scope chain to a global in the right compartment since any
-     * amount of general VM code can run before the first scripted frame is
-     * pushed (if at all). This is currently and hackily accomplished by
-     * pushing a "dummy frame" with the correct scope chain. On success, this
-     * function will change the compartment to 'scopeChain.compartment()' and
-     * push a dummy frame for 'scopeChain'. On failure, nothing is changed.
-     */
-    bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg);
-
-    /*
-     * An "inline frame" may only be pushed from within the top, active
-     * segment. This is the case for calls made inside mjit code and Interpret.
-     * The 'stackLimit' overload updates 'stackLimit' if it changes.
-     */
-    bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                         JSFunction &callee, JSScript *script,
-                         InitialFrameFlags initial);
-    bool pushInlineFrame(JSContext *cx, FrameRegs &regs, const CallArgs &args,
-                         JSFunction &callee, JSScript *script,
-                         InitialFrameFlags initial, Value **stackLimit);
-    void popInlineFrame(FrameRegs &regs);
-
-    /* Pop a partially-pushed frame after hitting the limit before throwing. */
-    void popFrameAfterOverflow();
-
-    /* Get the topmost script and optional pc on the stack. */
-    inline JSScript *currentScript(jsbytecode **pc = NULL) const;
-
-    /* Get the scope chain for the topmost scripted call on the stack. */
-    inline JSObject *currentScriptedScopeChain() const;
-
-    /*
-     * Called by the methodjit for an arity mismatch. Arity mismatch can be
-     * hot, so getFixupFrame avoids doing call setup performed by jit code when
-     * FixupArity returns. In terms of work done:
-     *
-     *   getFixupFrame = pushInlineFrame -
-     *                   (fp->initJitFrameLatePrologue + regs->prepareToRun)
-     */
-    StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report,
-                              const CallArgs &args, JSFunction *fun, JSScript *script,
-                              void *ncode, InitialFrameFlags initial, Value **stackLimit);
-
-    bool saveFrameChain();
-    void restoreFrameChain();
-
-    /*
-     * As an optimization, the interpreter/mjit can operate on a local
-     * FrameRegs instance repoint the ContextStack to this local instance.
-     */
-    inline void repointRegs(FrameRegs *regs);
-
-    /*** For JSContext: ***/
-
-    /*
-     * To avoid indirection, ContextSpace caches a pointer to the StackSpace.
-     * This must be kept coherent with cx->thread->data.space by calling
-     * 'threadReset' whenver cx->thread changes.
-     */
-    void threadReset();
-
-    /*** For jit compiler: ***/
-
-    static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); }
-};
-
-} /* namespace js */
-
-#endif /* StackSpace_h__ */
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -113,19 +113,21 @@ void
 XPCTraceableVariant::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     JS_snprintf(buf, bufsize, "XPCVariant[0x%p].mJSVal", trc->debugPrintArg);
 }
 #endif
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
     jsval val = tmp->GetJSValPreserveColor();
-    if (JSVAL_IS_OBJECT(val))
+    if (JSVAL_IS_OBJECT(val)) {
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal");
         cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                            JSVAL_TO_OBJECT(val));
+    }
 
     nsVariant::Traverse(tmp->mData, cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCVariant)
     jsval val = tmp->GetJSValPreserveColor();
 
     // We're sharing val's buffer, clear the pointer to it so Cleanup() won't
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -70,31 +70,37 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
             JS_snprintf(name, sizeof(name), "nsXPCWrappedJS");
         cb.DescribeRefCountedNode(refcnt, sizeof(nsXPCWrappedJS), name);
     } else {
         NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsXPCWrappedJS, refcnt)
     }
 
     // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the
     // comment above nsXPCWrappedJS::AddRef.
+    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self");
     cb.NoteXPCOMChild(s);
 
-    if (refcnt > 1)
+    if (refcnt > 1) {
         // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see
         // the comment above nsXPCWrappedJS::AddRef.
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj");
         cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                            tmp->GetJSObjectPreserveColor());
+    }
 
     nsXPCWrappedJS* root = tmp->GetRootWrapper();
-    if (root == tmp)
+    if (root == tmp) {
         // The root wrapper keeps the aggregated native object alive.
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native");
         cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
-    else
+    } else {
         // Non-root wrappers keep their root alive.
+        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root");
         cb.NoteXPCOMChild(static_cast<nsIXPConnectWrappedJS*>(root));
+    }
 
     return NS_OK;
 }
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPCWrappedJS)
     tmp->Unlink();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -821,18 +821,16 @@ XPCWrappedNative::XPCWrappedNative(alrea
 #endif
 {
     mIdentity = aIdentity.get();
 
     NS_ASSERTION(mMaybeProto, "bad ctor param");
     NS_ASSERTION(mSet, "bad ctor param");
 
     DEBUG_TrackNewWrapper(this);
-
-    JS_RegisterReference((void **) &mWrapperWord);
 }
 
 // This ctor is used if this object will NOT have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
                                    XPCWrappedNativeScope* aScope,
                                    XPCNativeSet* aSet)
 
     : mMaybeScope(TagScope(aScope)),
@@ -845,18 +843,16 @@ XPCWrappedNative::XPCWrappedNative(alrea
 #endif
 {
     mIdentity = aIdentity.get();
 
     NS_ASSERTION(aScope, "bad ctor param");
     NS_ASSERTION(aSet, "bad ctor param");
 
     DEBUG_TrackNewWrapper(this);
-
-    JS_RegisterReference((void **) &mWrapperWord);
 }
 
 XPCWrappedNative::~XPCWrappedNative()
 {
     DEBUG_TrackDeleteWrapper(this);
 
     Destroy();
 }
@@ -904,49 +900,49 @@ XPCWrappedNative::Destroy()
 
     /*
      * The only time GetRuntime() will be NULL is if Destroy is called a second
      * time on a wrapped native. Since we already unregistered the pointer the
      * first time, there's no need to unregister again. Unregistration is safe
      * the first time because mWrapperWord isn't used afterwards.
      */
     if (XPCJSRuntime *rt = GetRuntime()) {
-        JS_UnregisterReferenceRT(rt->GetJSRuntime(), (void **) &mWrapperWord);
+        if (js::IsIncrementalBarrierNeeded(rt->GetJSRuntime()))
+            js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
         mWrapperWord = WRAPPER_WORD_POISON;
     } else {
         MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON);
     }
 
     mMaybeScope = nsnull;
 }
 
 void
 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
 {
     NS_ASSERTION(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
 
     // Write barrier for incremental GC.
     JSRuntime* rt = GetRuntime()->GetJSRuntime();
-    if (JS_GetIncrementalGCTracer(rt))
+    if (js::IsIncrementalBarrierNeeded(rt))
         mScriptableInfo->Mark();
 
     mScriptableInfo = si;
 }
 
 void
 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
 {
     NS_ASSERTION(!IsWrapperExpired(), "bad ptr!");
 
+    MOZ_ASSERT(HasProto());
+
     // Write barrier for incremental GC.
-    if (HasProto()) {
-        JSRuntime* rt = GetRuntime()->GetJSRuntime();
-        if (JS_GetIncrementalGCTracer(rt))
-            GetProto()->TraceJS(JS_GetIncrementalGCTracer(rt));
-    }
+    JSRuntime* rt = GetRuntime()->GetJSRuntime();
+    GetProto()->WriteBarrierPre(rt);
 
     mMaybeProto = p;
 }
 
 // This is factored out so that it can be called publicly
 // static
 void
 XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1018,18 +1018,18 @@ public:
         JSContext *cx = static_cast<JSContext*>(n);
 
         // Add outstandingRequests to the count, if there are outstanding
         // requests the context needs to be kept alive and adding unknown
         // edges will ensure that any cycles this context is in won't be
         // collected.
         unsigned refCount = nsXPConnect::GetXPConnect()->GetOutstandingRequests(cx) + 1;
         cb.DescribeRefCountedNode(refCount, js::SizeOfJSContext(), "JSContext");
-        NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]");
         if (JSObject *global = JS_GetGlobalObject(cx)) {
+            NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]");
             cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, global);
         }
 
         return NS_OK;
     }
 };
 
 static JSContextParticipant JSContext_cycleCollectorGlobal;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1649,20 +1649,20 @@ private:
     ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
     ClassInfo2WrappedNativeProtoMap* mMainThreadWrappedNativeProtoMap;
     nsXPCComponents*                 mComponents;
     XPCWrappedNativeScope*           mNext;
     // The JS global object for this scope.  If non-null, this will be the
     // default parent for the XPCWrappedNatives that have us as the scope,
     // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
     // constructor).
-    JS::HeapPtrObject                mGlobalJSObject;
+    js::ObjectPtr                    mGlobalJSObject;
 
     // Cached value of Object.prototype
-    JS::HeapPtrObject                mPrototypeJSObject;
+    js::ObjectPtr                    mPrototypeJSObject;
     // Prototype to use for wrappers with no helper.
     JSObject*                        mPrototypeNoHelper;
 
     XPCContext*                      mContext;
 
     // The script object principal instance corresponding to our current global
     // JS object.
     // XXXbz what happens if someone calls JS_SetPrivate on mGlobalJSObject.
@@ -2301,16 +2301,22 @@ public:
         if (mJSProtoObject) {
             JS_CALL_OBJECT_TRACER(trc, mJSProtoObject,
                                   "XPCWrappedNativeProto::mJSProtoObject");
         }
         if (mScriptableInfo && JS_IsGCMarkingTracer(trc))
             mScriptableInfo->Mark();
     }
 
+    void WriteBarrierPre(JSRuntime* rt)
+    {
+        if (js::IsIncrementalBarrierNeeded(rt) && mJSProtoObject)
+            mJSProtoObject.writeBarrierPre(rt);
+    }
+
     // NOP. This is just here to make the AutoMarkingPtr code compile.
     inline void AutoTrace(JSTracer* trc) {}
 
     // Yes, we *do* need to mark the mScriptableInfo in both cases.
     void Mark() const
         {mSet->Mark();
          if (mScriptableInfo) mScriptableInfo->Mark();}
 
@@ -2343,17 +2349,17 @@ private:
 private:
     bool
     InitedOffsets()
     {
         return mOffsets != UNKNOWN_OFFSETS;
     }
 
     XPCWrappedNativeScope*   mScope;
-    JS::HeapPtrObject        mJSProtoObject;
+    js::ObjectPtr            mJSProtoObject;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
     PRUint32                 mClassInfoFlags;
     XPCNativeSet*            mSet;
     void*                    mSecurityInfo;
     XPCNativeScriptableInfo* mScriptableInfo;
     QITableEntry*            mOffsets;
 };
 
@@ -2732,18 +2738,19 @@ public:
             xpc_UnmarkGrayObject(wrapper);
             // Call this to unmark mFlatJSObject.
             GetFlatJSObject();
         }
         return wrapper;
     }
     void SetWrapper(JSObject *obj)
     {
+        js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
         PRWord newval = PRWord(obj) | (mWrapperWord & FLAG_MASK);
-        JS_ModifyReference((void **)&mWrapperWord, (void *)newval);
+        mWrapperWord = newval;
     }
 
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     QITableEntry* GetOffsets()
     {
         if (!HasProto() || !GetProto()->ClassIsDOMObject())
             return nsnull;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1240,17 +1240,17 @@ TransformGfxRectFromAncestor(nsIFrame *a
 }
 
 static gfxRect
 TransformGfxRectToAncestor(nsIFrame *aFrame,
                            const gfxRect &aRect,
                            nsIFrame *aAncestor)
 {
   gfx3DMatrix ctm = nsLayoutUtils::GetTransformToAncestor(aFrame, aAncestor);
-  return ctm.ProjectRectBounds(aRect);
+  return ctm.TransformBounds(aRect);
 }
 
 nsPoint
 nsLayoutUtils::TransformRootPointToFrame(nsIFrame *aFrame,
                                          const nsPoint &aPoint)
 {
     float factor = aFrame->PresContext()->AppUnitsPerDevPixel();
     gfxPoint result(NSAppUnitsToFloatPixels(aPoint.x, factor),
--- a/layout/base/tests/Makefile.in
+++ b/layout/base/tests/Makefile.in
@@ -176,16 +176,17 @@ DEFINES += -D_IMPL_NS_LAYOUT
 		test_scroll_selection_into_view.html \
 		test_bug583889.html \
 		bug583889_inner1.html \
 		bug583889_inner2.html \
 		test_bug582771.html \
 		test_bug603550.html \
 		test_bug629838.html \
 		test_bug646757.html \
+		test_bug718809.html \
 		test_font_inflation_reftests.html \
 		$(NULL)
 
 # Tests for bugs 441782, 467672 and 570378 don't pass reliably on Windows, because of bug 469208
 ifeq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
 # THESE TESTS (BELOW) DO NOT RUN ON WINDOWS
 _TEST_FILES += \
 		bidi_numeral_test.js \
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug718809.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=718809
+-->
+<head>
+  <title>Test for Bug 718809</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+   
+</head>
+<body style=margin:0>
+<div style="background:blue;height:50px;width:100px; -moz-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 10, 1); -moz-transform-origin:0 0"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=718809">Mozilla Bug 718809</a>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+  var rect = document.querySelector("div").getBoundingClientRect();
+
+   is(rect.top, 0, "Incorrect bounding rect");
+   is(rect.left, 0, "Incorrect bounding rect");
+   is(rect.right, 100, "Incorrect bounding rect");
+   is(rect.bottom, 50, "Incorrect bounding rect");
+</script>
+</pre>
+</body>
+</html>
+
--- a/layout/build/Makefile.in
+++ b/layout/build/Makefile.in
@@ -230,24 +230,22 @@ include $(topsrcdir)/ipc/chromium/chromi
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	+= -I$(srcdir)/../base \
 		   -I$(srcdir)/../generic \
 		   -I$(srcdir)/../forms \
 		   -I$(srcdir)/../tables \
 		   -I$(srcdir)/../style \
-		   -I$(srcdir)/../xul/content/src \
 		   -I$(srcdir)/../xul/base/src \
 		   -I$(srcdir)/../mathml \
 		   -I$(topsrcdir)/content/base/src \
  		   -I$(topsrcdir)/content/canvas/src \
 		   -I$(topsrcdir)/content/html/content/src \
 		   -I$(topsrcdir)/content/html/document/src \
-		   -I$(topsrcdir)/content/html/style/src \
 		   -I$(topsrcdir)/content/xslt/src/base \
 		   -I$(topsrcdir)/content/xslt/src/xml \
 		   -I$(topsrcdir)/content/xslt/src/xpath \
 		   -I$(topsrcdir)/content/xslt/src/xslt \
 		   -I$(topsrcdir)/content/xul/content/src \
 		   -I$(topsrcdir)/content/xul/document/src \
 		   -I$(topsrcdir)/content/xul/templates/src \
 		   -I$(topsrcdir)/content/events/src \
--- a/layout/mathml/Makefile.in
+++ b/layout/mathml/Makefile.in
@@ -49,17 +49,16 @@ LIBRARY_NAME = gkmathml_s
 LIBXUL_LIBRARY	= 1
 
 
 LOCAL_INCLUDES = \
 	-I$(srcdir)/../style \
 	-I$(srcdir)/../base \
 	-I$(srcdir)/../generic \
 	-I$(srcdir)/../tables \
-	-I$(srcdir)/content/src \
 	-I$(topsrcdir)/content/base/src \
 	-I$(topsrcdir)/content/mathml/content/src \
 	-I$(srcdir)/../xul/base/src \
 	$(NULL)
 
 CPPSRCS =         nsMathMLChar.cpp		        \
                   nsMathMLFrame.cpp		\
                   nsMathMLContainerFrame.cpp		\
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1683,9 +1683,9 @@ needs-focus != 703186-1.html 703186-2.ht
 == 711359-1.html 711359-1-ref.html
 == 712849-1.html 712849-1-ref.html
 == 713856-static.html  713856-ref.html
 == 713856-dynamic.html 713856-ref.html
 == 714519-1-as.html 714519-1-ref.html
 == 714519-1-q.html 714519-1-ref.html
 == 714519-2-as.html 714519-2-ref.html
 == 714519-2-q.html 714519-2-ref.html
-fuzzy == 718521.html 718521-ref.html
+fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html
--- a/layout/reftests/css-gradients/reftest.list
+++ b/layout/reftests/css-gradients/reftest.list
@@ -1,14 +1,14 @@
-fuzzy-if(azureQuartz) fails-if(Android) == linear-1a.html linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-1b.html linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-percent.html linear-percent-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-mix.html linear-mix-ref.html
+fuzzy-if(azureQuartz,1,34919) fails-if(Android) == linear-1a.html linear-1-ref.html
+fuzzy-if(azureQuartz,1,34919) fails-if(Android) == linear-1b.html linear-1-ref.html
+fuzzy-if(azureQuartz,1,11483) fails-if(Android) == linear-keywords-1a.html linear-keywords-1-ref.html
+fuzzy-if(azureQuartz,1,11483) fails-if(Android) == linear-keywords-1b.html linear-keywords-1-ref.html
+fuzzy-if(azureQuartz,1,10230) fails-if(Android) == linear-percent.html linear-percent-ref.html
+fuzzy-if(azureQuartz,1,17068) fails-if(Android) == linear-mix.html linear-mix-ref.html
 == linear-diagonal-1a.html linear-diagonal-1-ref.html
 == linear-diagonal-1b.html linear-diagonal-1-ref.html
 == linear-diagonal-1c.html linear-diagonal-1-ref.html
 == linear-diagonal-2a.html linear-diagonal-2-ref.html
 == linear-diagonal-2b.html linear-diagonal-2-ref.html
 == linear-diagonal-2c.html linear-diagonal-2-ref.html
 == linear-diagonal-3a.html linear-diagonal-3-ref.html
 == linear-diagonal-3b.html linear-diagonal-3-ref.html
@@ -31,30 +31,30 @@ fails-if(d2d) == linear-repeat-1f.html l
 fails-if(d2d) == linear-repeat-1g.html linear-repeat-1-ref.html # bug 582236
 == linear-size-1a.html linear-size-1-ref.html
 == linear-stops-1a.html linear-stops-1-ref.html
 == linear-stops-1b.html linear-stops-1-ref.html
 == linear-stops-1c.html linear-stops-1-ref.html
 == linear-stops-1d.html linear-stops-1-ref.html
 == linear-stops-1e.html linear-stops-1-ref.html
 == linear-stops-1f.html linear-stops-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz,1,9674) fails-if(Android) == linear-vertical-1a.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz,1,9674) fails-if(Android) == linear-vertical-1b.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz,1,9674) fails-if(Android) == linear-vertical-1c.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz,1,9674) fails-if(Android) == linear-vertical-1d.html linear-vertical-1-ref.html
+fuzzy-if(azureQuartz,1,9674) fails-if(Android) == linear-vertical-1e.html linear-vertical-1-ref.html
 == linear-viewport.html linear-viewport-ref.html
 == linear-zero-length-1a.html linear-zero-length-1-ref.html
 == linear-zero-length-1b.html linear-zero-length-1-ref.html
 == linear-zero-length-1c.html linear-zero-length-1-ref.html
 == nostops.html about:blank
 == onestop.html about:blank
-fuzzy-if(azureQuartz) fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == radial-2a.html radial-2-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == radial-2b.html radial-2-ref.html
+fuzzy-if(azureQuartz,1,1997) fails-if(Android) random-if(d2d) == radial-1a.html radial-1-ref.html
+fuzzy-if(azureQuartz,1,1926) fails-if(Android) == radial-2a.html radial-2-ref.html
+fuzzy-if(azureQuartz,1,1926) fails-if(Android) == radial-2b.html radial-2-ref.html
 == radial-position-1a.html radial-position-1-ref.html
 == radial-shape-closest-corner-1a.html radial-shape-closest-corner-1-ref.html
 == radial-shape-closest-side-1a.html radial-shape-closest-side-1-ref.html
 == radial-shape-farthest-corner-1a.html radial-shape-farthest-corner-1-ref.html
 == radial-shape-farthest-side-1a.html radial-shape-farthest-side-1-ref.html
 == radial-size-1a.html radial-size-1-ref.html
 == radial-zero-length-1a.html radial-zero-length-1-ref.html
 == radial-zero-length-1b.html radial-zero-length-1-ref.html
@@ -72,33 +72,33 @@ fails-if(d2d) == repeating-linear-1b.htm
 == twostops-1b.html twostops-1-ref.html
 fails-if(/Mac\x20OS\x20X\x2010\.[56]/.test(http.oscpu)) == twostops-1c.html twostops-1-ref.html # bug 524173
 == twostops-1d.html twostops-1-ref.html
 == twostops-1e.html twostops-1-ref.html
 == twostops-1f.html twostops-1-ref.html
 == twostops-1g.html twostops-1-ref.html
 
 # from http://www.xanthir.com/:4bhipd by way of http://a-ja.net/newgrad.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1a.html aja-linear-1-ref.html
 fails-if(!d2d) == aja-linear-1b.html aja-linear-1-ref.html # bug 526694
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1c.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1d.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1e.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1f.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4646) fails-if(Android) == aja-linear-1g.html aja-linear-1-ref.html
+fuzzy-if(azureQuartz,1,4675) fails-if(Android) == aja-linear-2a.html aja-linear-2-ref.html
+fuzzy-if(azureQuartz,1,4675) fails-if(Android) == aja-linear-2b.html aja-linear-2-ref.html
 fails == aja-linear-2c.html aja-linear-2-ref.html # bug 522607
 fails-if(!d2d) == aja-linear-2d.html aja-linear-2-ref.html # bug 526694
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
-fuzzy-if(azureQuartz) fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
+fuzzy-if(azureQuartz,1,4900) fails-if(Android) == aja-linear-3a.html aja-linear-3-ref.html
+fuzzy-if(azureQuartz,1,4900) fails-if(Android) == aja-linear-3b.html aja-linear-3-ref.html
+fuzzy-if(azureQuartz,1,8655) fails-if(Android) == aja-linear-4a.html aja-linear-4-ref.html
+fuzzy-if(azureQuartz,1,8655) fails-if(Android) == aja-linear-4b.html aja-linear-4-ref.html
+fuzzy-if(azureQuartz,2,7878) fails-if(Android) == aja-linear-5a.html aja-linear-5-ref.html
+fuzzy-if(azureQuartz,1,3910) fails-if(Android) fails-if(/Mac\x20OS\x20X\x2010\.5/.test(http.oscpu)) == aja-linear-6a.html aja-linear-6-ref.html # bug 526708
 fails == aja-linear-6b.html aja-linear-6-ref.html # bug 522607
 == height-dependence-1.html height-dependence-1-ref.html
 fails-if(cocoaWidget) == height-dependence-2.html height-dependence-2-ref.html # bug 535007
 == height-dependence-3.html height-dependence-3-ref.html
 
 fails-if(d2d) == linear-onestopposition-1.html linear-onestopposition-1-ref.html # bug 638664
 == linear-onestopposition-1.html linear-onestopposition-1-ref2.html
 fails-if(d2d) fails-if(cocoaWidget) == radial-onestopposition-1.html radial-onestopposition-1-ref.html # bug 638664
--- a/layout/reftests/reftest-sanity/reftest.list
+++ b/layout/reftests/reftest-sanity/reftest.list
@@ -124,15 +124,15 @@ fails pref(font.size.variable.x-western,
 fails pref(font.size.variable.x-western,"foo") == font-size-16.html font-default.html
 # a string pref
 pref(font.default.x-western,"serif") == font-serif.html font-default.html
 pref(font.default.x-western,"serif") != font-sans-serif.html font-default.html
 pref(font.default.x-western,"sans-serif") == font-sans-serif.html font-default.html
 pref(font.default.x-western,"sans-serif") != font-serif.html font-default.html
 fails pref(font.default.x-western,true) == font-serif.html font-default.html
 fails pref(font.default.x-western,0) == font-serif.html font-default.html
-# reftest syntax: fuzzy
-fuzzy == fuzzy.html fuzzy-ref.html
-fuzzy != too-fuzzy.html fuzzy-ref.html
-fuzzy-if(true) == fuzzy.html fuzzy-ref.html
-fuzzy-if(false) == fuzzy-ref.html fuzzy-ref.html
+# reftest syntax: fuzzy(maxPixelDifference,maxNumberDifferingPixels)
+fuzzy(1,250000) == fuzzy.html fuzzy-ref.html
+fuzzy(1,250000) != too-fuzzy.html fuzzy-ref.html
+fuzzy-if(true,1,250000) == fuzzy.html fuzzy-ref.html
+fuzzy-if(false,2,1) == fuzzy-ref.html fuzzy-ref.html
 # When using 565 fuzzy.html and fuzzy-ref.html will compare as equal
-fails fuzzy-if(false) random-if(Android) == fuzzy.html fuzzy-ref.html
+fails fuzzy-if(false,2,1) random-if(Android) == fuzzy.html fuzzy-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-text-08-ref.svg
@@ -0,0 +1,10 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg">
+
+  <title>Reference for scaling from zero</title>
+
+  <text x="100" y="50" font-size="50" text-anchor="middle">ABC</text>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/dynamic-text-08.svg
@@ -0,0 +1,24 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait" onload="m();">
+
+  <title>Testcase to scaling from zero</title>
+
+  <g id="g" transform="scale(0)">
+    <text x="100" y="50" font-size="50" text-anchor="middle">ABC</text>
+  </g>
+  <script>
+    function m()
+    {
+      // Force frame construction
+      document.documentElement.getBoundingClientRect();
+
+      // A dynamic change
+      document.getElementById("g").removeAttribute("transform");
+      
+      document.documentElement.removeAttribute("class");
+    }
+  </script>
+</svg>
--- a/layout/svg/base/src/nsSVGContainerFrame.cpp
+++ b/layout/svg/base/src/nsSVGContainerFrame.cpp
@@ -260,28 +260,37 @@ nsSVGDisplayContainerFrame::NotifyRedraw
   return NS_OK;
 }
 
 gfxRect
 nsSVGDisplayContainerFrame::GetBBoxContribution(
   const gfxMatrix &aToBBoxUserspace,
   PRUint32 aFlags)
 {
-  gfxRect bboxUnion(0.0, 0.0, 0.0, 0.0);
+  gfxRect bboxUnion;
+  bool firstChild = true;
 
   nsIFrame* kid = mFrames.FirstChild();
   while (kid) {
     nsISVGChildFrame* svgKid = do_QueryFrame(kid);
     if (svgKid) {
       gfxMatrix transform = aToBBoxUserspace;
       nsIContent *content = kid->GetContent();
       if (content->IsSVG() && !content->IsNodeOfType(nsINode::eTEXT)) {
         transform = static_cast<nsSVGElement*>(content)->
                       PrependLocalTransformTo(aToBBoxUserspace);
       }
-      bboxUnion =
-        bboxUnion.Union(svgKid->GetBBoxContribution(transform, aFlags));
+      // We need to include zero width/height vertical/horizontal lines, so we have
+      // to use UnionEdges, but we must special case the first bbox so that we don't
+      // include the initial gfxRect(0,0,0,0).
+      gfxRect childBBox = svgKid->GetBBoxContribution(transform, aFlags);
+      if (firstChild) {
+        bboxUnion = childBBox;
+        firstChild = false;
+        continue;
+      }
+      bboxUnion = bboxUnion.UnionEdges(childBBox);
     }
     kid = kid->GetNextSibling();
   }
 
   return bboxUnion;
 }
--- a/layout/svg/base/src/nsSVGMarkerFrame.cpp
+++ b/layout/svg/base/src/nsSVGMarkerFrame.cpp
@@ -198,33 +198,44 @@ nsSVGMarkerFrame::GetMarkBBoxContributio
     return gfxRect();
   }
 
   mStrokeWidth = aStrokeWidth;
   mX = aMark->x;
   mY = aMark->y;
   mAutoAngle = aMark->angle;
 
-  gfxRect bbox;
-
   gfxMatrix markerTM =
     content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle);
   gfxMatrix viewBoxTM = content->GetViewBoxTransform();
 
   gfxMatrix tm = viewBoxTM * markerTM * aToBBoxUserspace;
 
+  gfxRect bbox;
+  bool firstChild = true;
+
   for (nsIFrame* kid = mFrames.FirstChild();
        kid;
        kid = kid->GetNextSibling()) {
     nsISVGChildFrame* child = do_QueryFrame(kid);
     if (child) {
       // When we're being called to obtain the invalidation area, we need to
       // pass down all the flags so that stroke is included. However, once DOM
       // getBBox() accepts flags, maybe we should strip some of those here?
-      bbox.UnionRect(bbox, child->GetBBoxContribution(tm, aFlags));
+
+      // We need to include zero width/height vertical/horizontal lines, so we have
+      // to use UnionEdges, but we must special case the first bbox so that we don't
+      // include the initial gfxRect(0,0,0,0).
+      gfxRect childBBox = child->GetBBoxContribution(tm, aFlags);
+      if (firstChild) {
+        bbox = childBBox;
+        firstChild = false;
+        continue;
+      }
+      bbox = bbox.UnionEdges(childBBox);
     }
   }
 
   return bbox;
 }
 
 void
 nsSVGMarkerFrame::SetParentCoordCtxProvider(nsSVGSVGElement *aContext)
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -683,27 +683,29 @@ function ReadManifest(aURL, inherited_st
 
         var expected_status = EXPECTED_PASS;
         var allow_silent_fail = false;
         var minAsserts = 0;
         var maxAsserts = 0;
         var needs_focus = false;
         var slow = false;
         var prefSettings = [];
+        var fuzzy_max_delta = 2;
+        var fuzzy_max_pixels = 1;
         
         while (items[0].match(/^(fails|needs-focus|random|skip|asserts|slow|require-or|silentfail|pref|fuzzy)/)) {
             var item = items.shift();
             var stat;
             var cond;
-            var m = item.match(/^(fails|random|skip|silentfail|fuzzy)-if(\(.*\))$/);
+            var m = item.match(/^(fails|random|skip|silentfail)-if(\(.*\))$/);
             if (m) {
                 stat = m[1];
                 // Note: m[2] contains the parentheses, and we want them.
                 cond = Components.utils.evalInSandbox(m[2], sandbox);
-            } else if (item.match(/^(fails|random|skip|fuzzy)$/)) {
+            } else if (item.match(/^(fails|random|skip)$/)) {
                 stat = item;
                 cond = true;
             } else if (item == "needs-focus") {
                 needs_focus = true;
                 cond = false;
             } else if ((m = item.match(/^asserts\((\d+)(-\d+)?\)$/))) {
                 cond = false;
                 minAsserts = Number(m[1]);
@@ -764,29 +766,39 @@ function ReadManifest(aURL, inherited_st
                 } else if (valType == "number" && (parseInt(prefVal) == prefVal)) {
                     prefType = PREF_INTEGER;
                 } else {
                     throw "Error in pref value in manifest file " + aURL.spec + " line " + lineNo;
                 }
                 prefSettings.push( { name: prefName,
                                      type: prefType,
                                      value: prefVal } );
+            } else if ((m = item.match(/^fuzzy\((\d+),(\d+)\)$/))) {
+              cond = false;
+              expected_status = EXPECTED_FUZZY;
+              fuzzy_max_delta = Number(m[1]);
+              fuzzy_max_pixels = Number(m[2]);
+            } else if ((m = item.match(/^fuzzy-if\((.*?),(\d+),(\d+)\)$/))) {
+              cond = false;
+              if (Components.utils.evalInSandbox("(" + m[1] + ")", sandbox)) {
+                expected_status = EXPECTED_FUZZY;
+                fuzzy_max_delta = Number(m[2]);
+                fuzzy_max_pixels = Number(m[3]);
+              }
             } else {
                 throw "Error 1 in manifest file " + aURL.spec + " line " + lineNo;
             }
 
             if (cond) {
                 if (stat == "fails") {
                     expected_status = EXPECTED_FAIL;
                 } else if (stat == "random") {
                     expected_status = EXPECTED_RANDOM;
                 } else if (stat == "skip") {
                     expected_status = EXPECTED_DEATH;
-                } else if (stat == "fuzzy") {
-                    expected_status = EXPECTED_FUZZY;
                 } else if (stat == "silentfail") {
                     allow_silent_fail = true;
                 }
             }
         }
 
         expected_status = Math.max(expected_status, inherited_status);
 
@@ -845,16 +857,18 @@ function ReadManifest(aURL, inherited_st
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings: prefSettings,
+                          fuzzyMaxDelta: fuzzy_max_delta,
+                          fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
                           url2: null } );
         } else if (items[0] == TYPE_SCRIPT) {
             if (items.length != 2)
                 throw "Error 4 in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI] = runHttp
                             ? ServeFiles(aURL, httpDepth,
                                          listURL, [items[1]])
@@ -868,16 +882,18 @@ function ReadManifest(aURL, inherited_st
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings: prefSettings,
+                          fuzzyMaxDelta: fuzzy_max_delta,
+                          fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
                           url2: null } );
         } else if (items[0] == TYPE_REFTEST_EQUAL || items[0] == TYPE_REFTEST_NOTEQUAL) {
             if (items.length != 3)
                 throw "Error 5 in manifest file " + aURL.spec + " line " + lineNo;
             var [testURI, refURI] = runHttp
                                   ? ServeFiles(aURL, httpDepth,
                                                listURL, [items[1], items[2]])
@@ -894,16 +910,18 @@ function ReadManifest(aURL, inherited_st
                           expected: expected_status,
                           allowSilentFail: allow_silent_fail,
                           prettyPath: prettyPath,
                           minAsserts: minAsserts,
                           maxAsserts: maxAsserts,
                           needsFocus: needs_focus,
                           slow: slow,
                           prefSettings: prefSettings,
+                          fuzzyMaxDelta: fuzzy_max_delta,
+                          fuzzyMaxPixels: fuzzy_max_pixels,
                           url1: testURI,
                           url2: refURI } );
         } else {
             throw "Error 6 in manifest file " + aURL.spec + " line " + lineNo;
         }
     }
 }
 
@@ -1408,17 +1426,18 @@ function RecordResult(testRunTime, error
             var maxDifference = {};
 
             differences = gWindowUtils.compareCanvases(gCanvas1, gCanvas2, maxDifference);
             equal = (differences == 0);
 
             // what is expected on this platform (PASS, FAIL, or RANDOM)
             var expected = gURLs[0].expected;
 
-            if (maxDifference.value > 0 && maxDifference.value <= 2) {
+            if (maxDifference.value > 0 && maxDifference.value <= gURLs[0].fuzzyMaxDelta &&
+                differences <= gURLs[0].fuzzyMaxPixels) {
                 if (equal) {
                     throw "Inconsistent result from compareCanvases.";
                 }
                 equal = expected == EXPECTED_FUZZY;
                 gDumpLog("REFTEST fuzzy match\n");
             }
 
             // whether the comparison result matches what is in the manifest
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -192,32 +192,16 @@ abstract public class GeckoApp
         synchronized(sLaunchState) {
             if (sLaunchState != checkState)
                 return false;
             sLaunchState = setState;
             return true;
         }
     }
 
-    void showErrorDialog(String message)
-    {
-        new AlertDialog.Builder(this)
-            .setMessage(message)
-            .setCancelable(false)
-            .setPositiveButton(R.string.exit_label,
-                               new DialogInterface.OnClickListener() {
-                                   public void onClick(DialogInterface dialog,
-                                                       int id)
-                                   {
-                                       GeckoApp.this.finish();
-                                       System.exit(0);
-                                   }
-                               }).show();
-    }
-
     public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
 
     /**
      * A plugin that wish to be loaded in the WebView must provide this permission
      * in their AndroidManifest.xml.
      */
     public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
 
@@ -435,16 +419,17 @@ abstract public class GeckoApp
             aMenu.findItem(R.id.settings).setEnabled(false);
 
         Tab tab = Tabs.getInstance().getSelectedTab();
         MenuItem bookmark = aMenu.findItem(R.id.bookmark);
         MenuItem forward = aMenu.findItem(R.id.forward);
         MenuItem share = aMenu.findItem(R.id.share);
         MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
         MenuItem downloads = aMenu.findItem(R.id.downloads);
+        MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
 
         if (tab == null) {
             bookmark.setEnabled(false);
             forward.setEnabled(false);
             share.setEnabled(false);
             saveAsPDF.setEnabled(false);
             return true;
         }
@@ -471,16 +456,18 @@ abstract public class GeckoApp
         // Disable save as PDF for about:home and xul pages
         saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
                                tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
 
         // DownloadManager support is tied to level 12 and higher
         if (Build.VERSION.SDK_INT < 12)
             downloads.setVisible(false);
 
+        charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
+
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
         Tab tab = null;
         Intent intent = null;
         switch (item.getItemId()) {
@@ -533,16 +520,19 @@ abstract public class GeckoApp
                 return true;
             case R.id.addons:
                 loadUrlInTab("about:addons");
                 return true;
             case R.id.downloads:
                 intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
                 startActivity(intent);
                 return true;
+            case R.id.char_encoding:
+                GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Get", null));
+                return true;
             default:
                 return super.onOptionsItemSelected(item);
         }
     }
 
     public String getLastViewport() {
         return mLastViewport;
     }
@@ -980,16 +970,61 @@ abstract public class GeckoApp
                 showSiteSettingsDialog(host, permissions);
             } else if (event.equals("Downloads:Done")) {
                 String displayName = message.getString("displayName");
                 String path = message.getString("path");
                 String mimeType = message.getString("mimeType");
                 int size = message.getInt("size");
 
                 handleDownloadDone(displayName, path, mimeType, size);
+            } else if (event.equals("CharEncoding:Data")) {
+                final JSONArray charsets = message.getJSONArray("charsets");
+                int selected = message.getInt("selected");
+
+                final int len = charsets.length();
+                final String[] titleArray = new String[len];
+                for (int i = 0; i < len; i++) {
+                    JSONObject charset = charsets.getJSONObject(i);
+                    titleArray[i] = charset.getString("title");
+                }
+
+                final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+                dialogBuilder.setSingleChoiceItems(titleArray, selected, new AlertDialog.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        try {
+                            JSONObject charset = charsets.getJSONObject(which);
+                            GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Set", charset.getString("code")));
+                            dialog.dismiss();
+                        } catch (JSONException e) {
+                            Log.e(LOGTAG, "error parsing json", e);
+                        }
+                    }
+                });
+                dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                    }
+                });
+                mMainHandler.post(new Runnable() {
+                    public void run() {
+                        dialogBuilder.show();
+                    }
+                });
+            } else if (event.equals("CharEncoding:State")) {
+                final boolean visible = message.getString("visible").equals("true");
+                GeckoPreferences.setCharEncodingState(visible);
+                if (sMenu != null) {
+                    mMainHandler.post(new Runnable() {
+                        public void run() {
+                            sMenu.findItem(R.id.char_encoding).setVisible(visible);
+                        }
+                    });
+                }
             }
         } catch (Exception e) {
             Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
         }
     }
 
     public void showAboutHome() {
         Runnable r = new AboutHomeRunnable(true);
@@ -1554,22 +1589,17 @@ abstract public class GeckoApp
 
         if (!sTryCatchAttached) {
             sTryCatchAttached = true;
             mMainHandler.post(new Runnable() {
                 public void run() {
                     try {
                         Looper.loop();
                     } catch (Exception e) {
-                        Log.e(LOGTAG, "top level exception", e);
-                        StringWriter sw = new StringWriter();
-                        PrintWriter pw = new PrintWriter(sw);
-                        e.printStackTrace(pw);
-                        pw.flush();
-                        GeckoAppShell.reportJavaCrash(sw.toString());
+                        GeckoAppShell.reportJavaCrash(e);
                     }
                     // resetting this is kinda pointless, but oh well
                     sTryCatchAttached = false;
                 }
             });
         }
 
         //register for events
@@ -1593,16 +1623,18 @@ abstract public class GeckoApp
         GeckoAppShell.registerGeckoEventListener("Menu:Remove", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
         GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
+        GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
+        GeckoAppShell.registerGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
 
         mConnectivityFilter = new IntentFilter();
         mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mConnectivityReceiver = new GeckoConnectivityReceiver();
 
         IntentFilter batteryFilter = new IntentFilter();
         batteryFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
         mBatteryReceiver = new GeckoBatteryManager();
@@ -1921,16 +1953,18 @@ abstract public class GeckoApp
         GeckoAppShell.unregisterGeckoEventListener("Menu:Remove", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Gecko:Ready", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Toast:Show", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Hide", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("ToggleChrome:Show", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext);
         GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext);
+        GeckoAppShell.unregisterGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext);
 
         mFavicons.close();
 
         if (SmsManager.getInstance() != null) {
           SmsManager.getInstance().shutdown();
         }
 
         GeckoNetworkManager.getInstance().stop();
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -128,17 +128,28 @@ public class GeckoAppShell
     public static native void putenv(String map);
     public static native void onResume();
     public static native void onLowMemory();
     public static native void callObserver(String observerKey, String topic, String data);
     public static native void removeObserver(String observerKey);
     public static native void loadGeckoLibsNative(String apkName);
     public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract);
     public static native void onChangeNetworkLinkStatus(String status);
-    public static native void reportJavaCrash(String stack);
+
+    public static void reportJavaCrash(Throwable e) {
+        Log.e(LOGTAG, "top level exception", e);
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        e.printStackTrace(pw);
+        pw.flush();
+        reportJavaCrash(sw.toString());
+    }
+
+    private static native void reportJavaCrash(String stackTrace);
+
     public static void notifyUriVisited(String uri) {
         sendEventToGecko(new GeckoEvent(GeckoEvent.VISTITED, uri));
     }
 
     public static native void processNextNativeEvent();
 
     public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
 
--- a/mobile/android/base/GeckoPreferences.java
+++ b/mobile/android/base/GeckoPreferences.java
@@ -68,16 +68,17 @@ import org.json.JSONObject;
 public class GeckoPreferences
     extends PreferenceActivity
     implements OnPreferenceChangeListener, GeckoEventListener
 {
     private static final String LOGTAG = "GeckoPreferences";
 
     private ArrayList<String> mPreferencesList = new ArrayList<String>();
     private PreferenceScreen mPreferenceScreen;
+    private static boolean sIsCharEncodingEnabled = false;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         if (Build.VERSION.SDK_INT >= 11)
             new GeckoActionBar().setDisplayHomeAsUpEnabled(this, true);
 
@@ -135,22 +136,32 @@ public class GeckoPreferences
         }
 
         return super.onOptionsItemSelected(item);
     }
 
     final private int DIALOG_CREATE_MASTER_PASSWORD = 0;
     final private int DIALOG_REMOVE_MASTER_PASSWORD = 1;
 
+    public static void setCharEncodingState(boolean enabled) {
+        sIsCharEncodingEnabled = enabled;
+    }
+
+    public static boolean getCharEncodingState() {
+        return sIsCharEncodingEnabled;
+    }
+
     @Override
     public boolean onPreferenceChange(Preference preference, Object newValue) {
         String prefName = preference.getKey();
         if (prefName != null && prefName.equals("privacy.masterpassword.enabled")) {
             showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
             return false;
+        } else if (prefName != null && prefName.equals("browser.menu.showCharacterEncoding")) {
+            setCharEncodingState(((String) newValue).equals("true"));
         }
 
         setPreference(prefName, newValue);
         if (preference instanceof ListPreference) {
             // We need to find the entry for the new value
             int newIndex = ((ListPreference)preference).findIndexOfValue((String) newValue);
             CharSequence newEntry = ((ListPreference)preference).getEntries()[newIndex];
             ((ListPreference)preference).setSummary(newEntry);
--- a/mobile/android/base/GeckoThread.java
+++ b/mobile/android/base/GeckoThread.java
@@ -101,17 +101,12 @@ public class GeckoThread extends Thread 
         try {
             Log.w(LOGTAG, "RunGecko - URI = " + mUri);
 
             GeckoAppShell.runGecko(app.getApplication().getPackageResourcePath(),
                                    mIntent.getStringExtra("args"),
                                    mUri,
                                    mRestoreSession);
         } catch (Exception e) {
-            Log.e(LOGTAG, "top level exception", e);
-            StringWriter sw = new StringWriter();
-            PrintWriter pw = new PrintWriter(sw);
-            e.printStackTrace(pw);
-            pw.flush();
-            GeckoAppShell.reportJavaCrash(sw.toString());
+            GeckoAppShell.reportJavaCrash(e);
         }
     }
 }
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -120,32 +120,34 @@ FENNEC_JAVA_FILES = \
   gfx/SingleTileLayer.java \
   gfx/TextLayer.java \
   gfx/TextureReaper.java \
   gfx/TileLayer.java \
   gfx/ViewportMetrics.java \
   gfx/WidgetTileLayer.java \
   ui/Axis.java \
   ui/PanZoomController.java \
+  ui/SimpleScaleGestureDetector.java \
   ui/SubdocumentScrollHelper.java \
   GeckoNetworkManager.java \
   $(NULL)
 
 ifdef MOZ_WEBSMS_BACKEND
 FENNEC_JAVA_FILES += GeckoSmsManager.java
 endif
 
 FENNEC_PP_JAVA_FILES = \
   App.java \
   LauncherShortcuts.java \
   NotificationHandler.java \
   Restarter.java \
   db/BrowserContract.java \
   db/BrowserProvider.java \
   SmsManager.java \
+  SyncPreference.java \
   $(NULL)
 
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/SyncPreference.java.in
@@ -0,0 +1,81 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * ***** 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 Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009-2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brian Nicholson <bnicholson@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 ***** */
+
+#filter substitution
+package org.mozilla.gecko;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.util.Log;
+
+class SyncPreference extends Preference {
+    private static final String FEEDS_PACKAGE_NAME = "com.android.providers.subscribedfeeds";
+    private static final String ACCOUNT_SYNC_CLASS_NAME = "com.android.settings.AccountSyncSettings";
+    private static final String ACCOUNT_KEY = "account";
+    private static final String FENNEC_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@";
+    private static final String FENNEC_SYNC_CLASS_NAME = "org.mozilla.gecko.sync.setup.activities.SetupSyncActivity";
+    private static final String FENNEC_ACCOUNT_TYPE = "org.mozilla.firefox_sync";
+
+    private Context mContext;
+
+    public SyncPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+    }
+
+    @Override
+    protected void onClick() {
+        Intent intent = new Intent(Intent.ACTION_MAIN);
+        Account[] accounts = AccountManager.get(mContext).getAccountsByType(FENNEC_ACCOUNT_TYPE);
+        if (accounts.length > 0) {
+            // show sync account
+            // we assume there's exactly one sync account. see bugs 716906 and 710407.
+            intent.setComponent(new ComponentName(FEEDS_PACKAGE_NAME, ACCOUNT_SYNC_CLASS_NAME));
+            Account account = accounts[0];
+            intent.putExtra(ACCOUNT_KEY, account);
+        } else {
+            // show sync setup
+            intent.setComponent(new ComponentName(FENNEC_PACKAGE_NAME, FENNEC_SYNC_CLASS_NAME));
+        }
+        mContext.startActivity(intent);
+    }
+}
--- a/mobile/android/base/gfx/LayerController.java
+++ b/mobile/android/base/gfx/LayerController.java
@@ -38,16 +38,17 @@
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.gfx.IntSize;
 import org.mozilla.gecko.gfx.Layer;
 import org.mozilla.gecko.gfx.LayerClient;
 import org.mozilla.gecko.gfx.LayerView;
 import org.mozilla.gecko.ui.PanZoomController;
+import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
 import org.mozilla.gecko.GeckoApp;
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Point;
 import android.graphics.PointF;
 import android.graphics.Rect;
@@ -142,17 +143,19 @@ public class LayerController {
         return mViewportMetrics.getZoomFactor();
     }
 
     public Bitmap getBackgroundPattern()    { return getDrawable("background"); }
     public Bitmap getCheckerboardPattern()  { return getDrawable("checkerboard"); }
     public Bitmap getShadowPattern()        { return getDrawable("shadow"); }
 
     public GestureDetector.OnGestureListener getGestureListener()                   { return mPanZoomController; }
-    public ScaleGestureDetector.OnScaleGestureListener getScaleGestureListener()    { return mPanZoomController; }
+    public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() {
+        return mPanZoomController;
+    }
     public GestureDetector.OnDoubleTapListener getDoubleTapListener()               { return mPanZoomController; }
 
     private Bitmap getDrawable(String name) {
         Resources resources = mContext.getResources();
         int resourceID = resources.getIdentifier(name, "drawable", mContext.getPackageName());
         BitmapFactory.Options options = new BitmapFactory.Options();
         options.inScaled = false;
         return BitmapFactory.decodeResource(mContext.getResources(), resourceID, options);
--- a/mobile/android/base/gfx/LayerView.java
+++ b/mobile/android/base/gfx/LayerView.java
@@ -35,51 +35,52 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
 import org.mozilla.gecko.gfx.FloatSize;
 import org.mozilla.gecko.gfx.InputConnectionHandler;
 import org.mozilla.gecko.gfx.LayerController;
+import org.mozilla.gecko.ui.SimpleScaleGestureDetector;
 import android.content.Context;
 import android.opengl.GLSurfaceView;
 import android.view.GestureDetector;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
-import android.view.ScaleGestureDetector;
 
 /**
  * A view rendered by the layer compositor.
  *
  * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
  * mediator between the LayerRenderer and the LayerController.
  */
 public class LayerView extends GLSurfaceView {
     private Context mContext;
     private LayerController mController;
     private InputConnectionHandler mInputConnectionHandler;
     private LayerRenderer mRenderer;
     private GestureDetector mGestureDetector;
-    private ScaleGestureDetector mScaleGestureDetector;
+    private SimpleScaleGestureDetector mScaleGestureDetector;
     private long mRenderTime;
     private boolean mRenderTimeReset;
 
     public LayerView(Context context, LayerController controller) {
         super(context);
 
         mContext = context;
         mController = controller;
         mRenderer = new LayerRenderer(this);
         setRenderer(mRenderer);
         setRenderMode(RENDERMODE_WHEN_DIRTY);
         mGestureDetector = new GestureDetector(context, controller.getGestureListener());
-        mScaleGestureDetector = new ScaleGestureDetector(context, controller.getScaleGestureListener());
+        mScaleGestureDetector =
+            new SimpleScaleGestureDetector(controller.getScaleGestureListener());
         mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener());
         mInputConnectionHandler = null;
 
         setFocusable(true);
         setFocusableInTouchMode(true);
     }
 
     @Override
--- a/mobile/android/base/gfx/PointUtils.java
+++ b/mobile/android/base/gfx/PointUtils.java
@@ -34,16 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 package org.mozilla.gecko.gfx;
 
 import android.graphics.Point;
 import android.graphics.PointF;
+import android.util.FloatMath;
 
 import org.json.JSONObject;
 import org.json.JSONException;
 import org.mozilla.gecko.FloatUtils;
 
 import java.lang.Math;
 
 public final class PointUtils {
@@ -72,16 +73,21 @@ public final class PointUtils {
        return new PointF(x, y);
    }
 
    /* Computes the magnitude of the given vector. */
    public static float distance(PointF point) {
         return (float)Math.sqrt(point.x * point.x + point.y * point.y);
    }
 
+    /** Computes the scalar distance between two points. */
+    public static float distance(PointF one, PointF two) {
+        return PointF.length(one.x - two.x, one.y - two.y);
+    }
+
     public static JSONObject toJSON(PointF point) throws JSONException {
         // Ensure we put ints, not longs, because Gecko message handlers call getInt().
         int x = Math.round(point.x);
         int y = Math.round(point.y);
         JSONObject json = new JSONObject();
         json.put("x", x);
         json.put("y", y);
         return json;
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -46,37 +46,41 @@
 <!ENTITY pref_category_general "General">
 <!ENTITY pref_category_privacy "Privacy &amp; Security">
 <!ENTITY pref_category_content "Content">
 <!ENTITY pref_about_firefox "About &brandShortName;">
 <!ENTITY pref_do_not_track "Tell sites not to track me">
 <!ENTITY pref_telemetry "Send performance data">
 <!ENTITY pref_remember_signons "Remember passwords">
 <!ENTITY pref_cookies "Enable cookies">
-<!ENTITY pref_char_encoding "Show character encoding">
+<!ENTITY pref_char_encoding "Character encoding">
+<!ENTITY pref_char_encoding_on "Show menu">
+<!ENTITY pref_char_encoding_off "Don\'t show menu">
 <!ENTITY pref_clear_history "Clear history">
 <!ENTITY pref_clear_history_confirm "Browsing history will be deleted">
 <!ENTITY pref_clear_private_data "Clear private data">
 <!ENTITY pref_clear_private_data_confirm "Browsing settings, including passwords and cookies, will be deleted">
 <!ENTITY pref_plugins "Plugins">
 <!ENTITY pref_plugins_enabled "Enabled">
 <!ENTITY pref_plugins_tap_to_play "Tap to play">
 <!ENTITY pref_plugins_disabled "Disabled">
 <!ENTITY pref_text_size "Text size">
 <!ENTITY pref_font_size_tiny "Tiny">
 <!ENTITY pref_font_size_small "Small">
 <!ENTITY pref_font_size_medium "Medium">
 <!ENTITY pref_font_size_large "Large">
 <!ENTITY pref_font_size_xlarge "Extra Large">
 <!ENTITY pref_use_master_password "Use master password">
+<!ENTITY pref_sync "Sync">
 
 <!ENTITY quit "Quit">
 
 <!ENTITY addons "Add-ons">
 <!ENTITY downloads "Downloads">
+<!ENTITY char_encoding "Character Encoding">
 
 <!ENTITY share "Share">
 <!ENTITY save_as_pdf "Save as PDF">
 
 <!ENTITY contextmenu_open_new_tab "Open in New Tab">
 <!ENTITY contextmenu_remove_bookmark "Remove">
 <!ENTITY contextmenu_add_to_launcher "Add to Home Screen">
 <!ENTITY contextmenu_share "Share">
--- a/mobile/android/base/resources/layout/gecko_menu.xml
+++ b/mobile/android/base/resources/layout/gecko_menu.xml
@@ -25,14 +25,18 @@
           android:title="@string/site_settings_title" />
 
     <item android:id="@+id/addons"
           android:title="@string/addons"/>
 
     <item android:id="@+id/downloads"
           android:title="@string/downloads"/>
 
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
     <item android:id="@+id/settings"
           android:title="@string/settings" />
 
     <item android:id="@+id/quit"
           android:title="@string/quit" />
 </menu>
--- a/mobile/android/base/resources/values/arrays.xml
+++ b/mobile/android/base/resources/values/arrays.xml
@@ -20,9 +20,17 @@
     </string-array>
     <string-array name="pref_font_size_values">
         <item>0</item>
         <item>80</item>
         <item>120</item>
         <item>160</item>
         <item>240</item>
     </string-array>
+    <string-array name="pref_char_encoding_entries">
+        <item>@string/pref_char_encoding_on</item>
+        <item>@string/pref_char_encoding_off</item>
+    </string-array>
+    <string-array name="pref_char_encoding_values">
+        <item>true</item>
+        <item>false</item>
+    </string-array>
 </resources>
--- a/mobile/android/base/resources/xml/preferences.xml
+++ b/mobile/android/base/resources/xml/preferences.xml
@@ -2,26 +2,27 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                         android:enabled="false">
 
     <PreferenceCategory android:title="@string/pref_category_general">
         <org.mozilla.gecko.LinkPreference android:title="@string/pref_about_firefox"
                                           url="about:" />
 
-        <!-- TODO: Default Search Engine -->
-
-        <!-- TODO: Sync -->
+        <org.mozilla.gecko.SyncPreference android:title="@string/pref_sync"
+                                          android:persistent="false" />
 
     </PreferenceCategory>
 
     <PreferenceCategory android:title="@string/pref_category_content">
 
-        <CheckBoxPreference android:key="browser.menu.showCharacterEncoding"
+        <ListPreference     android:key="browser.menu.showCharacterEncoding"
                             android:title="@string/pref_char_encoding"
+                            android:entries="@array/pref_char_encoding_entries"
+                            android:entryValues="@array/pref_char_encoding_values"
                             android:persistent="false" />
 
         <ListPreference     android:key="plugin.enable"
                             android:title="@string/pref_plugins"
                             android:entries="@array/pref_plugins_entries"
                             android:entryValues="@array/pref_plugins_values"
                             android:persistent="false" />
 
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -53,36 +53,40 @@
   <string name="pref_category_privacy">&pref_category_privacy;</string>
   <string name="pref_category_content">&pref_category_content;</string>
   <string name="pref_about_firefox">&pref_about_firefox;</string>
   <string name="pref_do_not_track">&pref_do_not_track;</string>
   <string name="pref_telemetry">&pref_telemetry;</string>
   <string name="pref_remember_signons">&pref_remember_signons;</string>
   <string name="pref_cookies">&pref_cookies;</string>
   <string name="pref_char_encoding">&pref_char_encoding;</string>
+  <string name="pref_char_encoding_on">&pref_char_encoding_on;</string>
+  <string name="pref_char_encoding_off">&pref_char_encoding_off;</string>
   <string name="pref_clear_history">&pref_clear_history;</string>
   <string name="pref_clear_history_confirm">&pref_clear_history_confirm;</string>
   <string name="pref_clear_private_data">&pref_clear_private_data;</string>
   <string name="pref_clear_private_data_confirm">&pref_clear_private_data_confirm;</string>
   <string name="pref_plugins">&pref_plugins;</string>
   <string name="pref_plugins_enabled">&pref_plugins_enabled;</string>
   <string name="pref_plugins_tap_to_play">&pref_plugins_tap_to_play;</string>
   <string name="pref_plugins_disabled">&pref_plugins_disabled;</string>
   <string name="pref_text_size">&pref_text_size;</string>
   <string name="pref_font_size_tiny">&pref_font_size_tiny;</string>
   <string name="pref_font_size_small">&pref_font_size_small;</string>
   <string name="pref_font_size_medium">&pref_font_size_medium;</string>
   <string name="pref_font_size_large">&pref_font_size_large;</string>
   <string name="pref_font_size_xlarge">&pref_font_size_xlarge;</string>
+  <string name="pref_sync">&pref_sync;</string>
 
   <string name="reload">&reload;</string>
   <string name="forward">&forward;</string>
   <string name="new_tab">&new_tab;</string>
   <string name="addons">&addons;</string>
   <string name="downloads">&downloads;</string>
+  <string name="char_encoding">&char_encoding;</string>
 
   <string name="site_settings_title">&site_settings_title;</string>
   <string name="site_settings_cancel">&site_settings_cancel;</string>
   <string name="site_settings_clear">&site_settings_clear;</string>
   <string name="site_settings_no_settings">&site_settings_no_settings;</string>
 
   <string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
   <string name="contextmenu_remove_bookmark">&contextmenu_remove_bookmark;</string>
--- a/mobile/android/base/ui/PanZoomController.java
+++ b/mobile/android/base/ui/PanZoomController.java
@@ -50,29 +50,28 @@ import org.mozilla.gecko.GeckoAppShell;
 import org.mozilla.gecko.GeckoEvent;
 import org.mozilla.gecko.GeckoEventListener;
 import android.graphics.PointF;
 import android.graphics.RectF;
 import android.util.FloatMath;
 import android.util.Log;
 import android.view.GestureDetector;
 import android.view.MotionEvent;
-import android.view.ScaleGestureDetector;
 import java.util.Timer;
 import java.util.TimerTask;
 
 /*
  * Handles the kinetic scrolling and zooming physics for a layer controller.
  *
  * Many ideas are from Joe Hewitt's Scrollability:
  *   https://github.com/joehewitt/scrollability/
  */
 public class PanZoomController
     extends GestureDetector.SimpleOnGestureListener
-    implements ScaleGestureDetector.OnScaleGestureListener, GeckoEventListener
+    implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener
 {
     private static final String LOGTAG = "GeckoPanZoomController";
 
     private static String MESSAGE_ZOOM_RECT = "Browser:ZoomToRect";
     private static String MESSAGE_ZOOM_PAGE = "Browser:ZoomToPageWidth";
 
     // Animation stops if the velocity is below this value when overscrolled or panning.
     private static final float STOPPED_THRESHOLD = 4.0f;
@@ -716,33 +715,33 @@ public class PanZoomController
         @Override
         protected float getPageLength() { return mController.getPageSize().height; }
     }
 
     /*
      * Zooming
      */
     @Override
-    public boolean onScaleBegin(ScaleGestureDetector detector) {
+    public boolean onScaleBegin(SimpleScaleGestureDetector detector) {
         Log.d(LOGTAG, "onScaleBegin in " + mState);
 
         if (mState == PanZoomState.ANIMATED_ZOOM)
             return false;
 
         mState = PanZoomState.PINCHING;
         mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY());
         GeckoApp.mAppContext.hidePluginViews();
         GeckoApp.mAppContext.mAutoCompletePopup.hide();
         cancelTouch();
 
         return true;
     }
 
     @Override
-    public boolean onScale(ScaleGestureDetector detector) {
+    public boolean onScale(SimpleScaleGestureDetector detector) {
         Log.d(LOGTAG, "onScale in state " + mState);
 
         if (mState == PanZoomState.ANIMATED_ZOOM)
             return false;
 
         float prevSpan = detector.getPreviousSpan();
         if (FloatUtils.fuzzyEquals(prevSpan, 0.0f)) {
             // let's eat this one to avoid setting the new zoom to infinity (bug 711453)
@@ -779,17 +778,17 @@ public class PanZoomController
         }
 
         mLastZoomFocus.set(detector.getFocusX(), detector.getFocusY());
 
         return true;
     }
 
     @Override
-    public void onScaleEnd(ScaleGestureDetector detector) {
+    public void onScaleEnd(SimpleScaleGestureDetector detector) {
         Log.d(LOGTAG, "onScaleEnd in " + mState);
 
         if (mState == PanZoomState.ANIMATED_ZOOM)
             return;
 
         // switch back to the touching state
         startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime());
 
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/ui/SimpleScaleGestureDetector.java
@@ -0,0 +1,327 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** 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 Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Patrick Walton <pcwalton@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 ***** */
+
+package org.mozilla.gecko.ui;
+
+import org.mozilla.gecko.gfx.PointUtils;
+import org.json.JSONException;
+import android.graphics.PointF;
+import android.util.Log;
+import android.view.MotionEvent;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Stack;
+
+/**
+ * A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector.
+ *
+ * This gesture detector is more reliable than the built-in ScaleGestureDetector because:
+ *
+ *   - It doesn't assume that pointer IDs are numbered 0 and 1.
+ *
+ *   - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some
+ *     devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many
+ *     pointers are down, with disastrous results (bug 706684).
+ *
+ *   - Cancelling a zoom into a pan is handled correctly.
+ *
+ *   - Starting with three or more fingers down, releasing fingers so that only two are down, and
+ *     then performing a scale gesture is handled correctly.
+ *
+ *   - It doesn't take pressure into account, which results in smoother scaling.
+ */
+public class SimpleScaleGestureDetector {
+    private static final String LOGTAG = "GeckoSimpleScaleGestureDetector";
+
+    private SimpleScaleGestureListener mListener;
+    private long mLastEventTime;
+
+    /* Information about all pointers that are down. */
+    private LinkedList<PointerInfo> mPointerInfo;
+
+    /** Creates a new gesture detector with the given listener. */
+    public SimpleScaleGestureDetector(SimpleScaleGestureListener listener) {
+        mListener = listener;
+        mPointerInfo = new LinkedList<PointerInfo>();
+    }
+
+    /** Forward touch events to this function. */
+    public void onTouchEvent(MotionEvent event) {
+        switch (event.getAction() & event.ACTION_MASK) {
+        case MotionEvent.ACTION_DOWN:
+        case MotionEvent.ACTION_POINTER_DOWN:
+            onTouchStart(event);
+            break;
+        case MotionEvent.ACTION_MOVE:
+            onTouchMove(event);
+            break;
+        case MotionEvent.ACTION_POINTER_UP:
+        case MotionEvent.ACTION_UP:
+        case MotionEvent.ACTION_CANCEL:
+            onTouchEnd(event);
+            break;
+        }
+    }
+
+    private int getPointersDown() {
+        return mPointerInfo.size();
+    }
+
+    private void onTouchStart(MotionEvent event) {
+        mLastEventTime = event.getEventTime();
+        mPointerInfo.push(PointerInfo.create(event, event.getActionIndex()));
+        if (getPointersDown() == 2) {
+            sendScaleGesture(EventType.BEGIN);
+        }
+    }
+
+    private void onTouchMove(MotionEvent event) {
+        mLastEventTime = event.getEventTime();
+        for (int i = 0; i < event.getPointerCount(); i++) {
+            PointerInfo pointerInfo = pointerInfoForEventIndex(event, i);
+            if (pointerInfo != null) {
+                pointerInfo.populate(event, i);
+            }
+        }
+
+        if (getPointersDown() == 2) {
+            sendScaleGesture(EventType.CONTINUE);
+        }
+    }
+
+    private void onTouchEnd(MotionEvent event) {
+        mLastEventTime = event.getEventTime();
+
+        int id = event.getPointerId(event.getActionIndex());
+        ListIterator<PointerInfo> iterator = mPointerInfo.listIterator();
+        while (iterator.hasNext()) {
+            PointerInfo pointerInfo = iterator.next();
+            if (pointerInfo.getId() != id) {
+                continue;
+            }
+
+            // One of the pointers we were tracking was lifted. Remove its info object from the
+            // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this
+            // ended the gesture.
+            iterator.remove();
+            pointerInfo.recycle();
+            if (getPointersDown() == 1) {
+                sendScaleGesture(EventType.END);
+            }
+        }
+    }
+
+    /**
+     * Returns the X coordinate of the focus location (the midpoint of the two fingers). If only
+     * one finger is down, returns the location of that finger.
+     */
+    public float getFocusX() {
+        switch (getPointersDown()) {
+        case 1:
+            return mPointerInfo.getFirst().getCurrent().x;
+        case 2:
+            PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
+            return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f;
+        }
+
+        Log.e(LOGTAG, "No gesture taking place in getFocusX()!");
+        return 0.0f;
+    }
+
+    /**
+     * Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only
+     * one finger is down, returns the location of that finger.
+     */
+    public float getFocusY() {
+        switch (getPointersDown()) {
+        case 1:
+            return mPointerInfo.getFirst().getCurrent().y;
+        case 2:
+            PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
+            return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f;
+        }
+
+        Log.e(LOGTAG, "No gesture taking place in getFocusY()!");
+        return 0.0f;
+    }
+
+    /** Returns the most recent distance between the two pointers. */
+    public float getCurrentSpan() {
+        if (getPointersDown() != 2) {
+            Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!");
+            return 0.0f;
+        }
+
+        PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
+        return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent());
+    }
+
+    /** Returns the second most recent distance between the two pointers. */
+    public float getPreviousSpan() {
+        if (getPointersDown() != 2) {
+            Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!");
+            return 0.0f;
+        }
+
+        PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
+        PointF a = pointerA.getPrevious(), b = pointerB.getPrevious();
+        if (a == null || b == null) {
+            a = pointerA.getCurrent();
+            b = pointerB.getCurrent();
+        }
+
+        return PointUtils.distance(a, b);
+    }
+
+    /** Returns the time of the last event related to the gesture. */
+    public long getEventTime() {
+        return mLastEventTime;
+    }
+
+    /** Returns true if the scale gesture is in progress and false otherwise. */
+    public boolean isInProgress() {
+        return getPointersDown() == 2;
+    }
+
+    /* Sends the requested scale gesture notification to the listener. */
+    private void sendScaleGesture(EventType eventType) {
+        switch (eventType) {
+        case BEGIN:     mListener.onScaleBegin(this);   break;
+        case CONTINUE:  mListener.onScale(this);        break;
+        case END:       mListener.onScaleEnd(this);     break;
+        }
+    }
+
+    /*
+     * Returns the pointer info corresponding to the given pointer index, or null if the pointer
+     * isn't one that's being tracked.
+     */
+    private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) {
+        int id = event.getPointerId(index);
+        for (PointerInfo pointerInfo : mPointerInfo) {
+            if (pointerInfo.getId() == i