Merge mc->Maple
authorBenoit Girard <b56girard@gmail.com>
Wed, 07 Mar 2012 11:10:45 -0500
changeset 92619 c7e65c5fa1469a5939cf3f8b236a29b86e786ea0
parent 92617 3be651e9ac2aa795d03c66f5434bf430aca06871 (current diff)
parent 91305 3dcb40ebd4873c77b0708b3089979fef6bd946dd (diff)
child 92620 4012267a4f08f2b689da1bb6e1ee426c4fb3805e
push idunknown
push userunknown
push dateunknown
milestone13.0a1
Merge mc->Maple
configure.in
gfx/gl/GLContext.cpp
gfx/layers/Makefile.in
gfx/layers/RenderTrace.cpp
gfx/layers/RenderTrace.h
gfx/layers/basic/BasicLayers.cpp
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/builtin/RegExp.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jscntxt.h
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsproxy.cpp
js/src/jspubtd.h
js/src/jsstr.cpp
js/src/shell/js.cpp
js/src/shell/jsheaptools.cpp
js/src/vm/Stack.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
layout/base/nsLayoutUtils.cpp
layout/base/nsPresContext.cpp
mobile/android/base/AutoCompletePopup.java
mobile/android/base/GeckoApp.java
mobile/android/base/Makefile.in
mobile/android/base/ui/PanZoomController.java
mobile/android/chrome/content/aboutAddons.js
mobile/android/chrome/content/browser.js
toolkit/components/telemetry/TelemetryPing.js
toolkit/mozapps/readstrings/Makefile.in
toolkit/mozapps/readstrings/errors.h
toolkit/mozapps/readstrings/readstrings.cpp
toolkit/mozapps/readstrings/readstrings.h
widget/xpwidgets/nsBaseWidget.h
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -692,16 +692,26 @@ nsApplicationAccessibleWrap::Unload()
     }
     // if (sATKLib) {
     //     PR_UnloadLibrary(sATKLib);
     //     sATKLib = nsnull;
     // }
 }
 
 NS_IMETHODIMP
+nsApplicationAccessibleWrap::GetName(nsAString& aName)
+{
+  // ATK doesn't provide a way to obtain an application name (for example,
+  // Firefox or Thunderbird) like IA2 does. Thus let's return an application
+  // name as accessible name that was used to get a branding name (for example,
+  // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
+  return GetAppName(aName);
+}
+
+NS_IMETHODIMP
 nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible)
 {
     *aOutAccessible = nsnull;
 
     if (!mAtkObject) {
         mAtkObject =
             reinterpret_cast<AtkObject *>
                             (g_object_new(MAI_TYPE_ATK_OBJECT, NULL));
--- a/accessible/src/atk/nsApplicationAccessibleWrap.h
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.h
@@ -52,16 +52,18 @@ public:
 public:
     nsApplicationAccessibleWrap();
     virtual ~nsApplicationAccessibleWrap();
 
     // nsAccessNode
     virtual bool Init();
 
     // nsAccessible
+    NS_IMETHOD GetName(nsAString &aName);
+
     virtual bool AppendChild(nsAccessible* aChild);
     virtual bool RemoveChild(nsAccessible* aChild);
 
     // return the atk object for app root accessible
     NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 };
 
 #endif   /* __NS_APP_ROOT_ACCESSIBLE_H__ */
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -42,16 +42,17 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsDocAccessible.h"
 #include "nsTextAttrs.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIClipboard.h"
+#include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
@@ -1148,25 +1149,24 @@ nsHyperTextAccessible::GetTextAttributes
                               accAtOffsetIdx);
   nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
                                            &endOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Compute spelling attributes on text accessible only.
   nsIFrame *offsetFrame = accAtOffset->GetFrame();
   if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
-    nsCOMPtr<nsIDOMNode> node = accAtOffset->DOMNode();
-
     PRInt32 nodeOffset = 0;
     nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
                                           &nodeOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set 'misspelled' text attribute.
-    rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset,
+    rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
+                               &startOffset, &endOffset,
                                aAttributes ? *aAttributes : nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
   return NS_OK;
 }
@@ -1784,23 +1784,21 @@ nsHyperTextAccessible::GetSelectionDOMRa
   PRUint32 childCount = startNode->GetChildCount();
   nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
   nsresult rv = privSel->
     GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
   NS_ENSURE_SUCCESS(rv,);
 
   // Remove collapsed ranges
   PRUint32 numRanges = aRanges->Length();
-  for (PRUint32 count = 0; count < numRanges; count ++) {
-    bool isCollapsed = false;
-    (*aRanges)[count]->GetCollapsed(&isCollapsed);
-    if (isCollapsed) {
-      aRanges->RemoveElementAt(count);
+  for (PRUint32 idx = 0; idx < numRanges; idx ++) {
+    if ((*aRanges)[idx]->Collapsed()) {
+      aRanges->RemoveElementAt(idx);
       --numRanges;
-      --count;
+      --idx;
     }
   }
 }
 
 /*
  * Gets the number of selected regions.
  */
 NS_IMETHODIMP
@@ -1832,39 +1830,29 @@ nsHyperTextAccessible::GetSelectionBound
   GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
     return NS_ERROR_INVALID_ARG;
 
   nsRange* range = ranges[aSelectionNum];
 
-  // Get start point
-  nsCOMPtr<nsIDOMNode> startDOMNode;
-  range->GetStartContainer(getter_AddRefs(startDOMNode));
-  nsCOMPtr<nsINode> startNode(do_QueryInterface(startDOMNode));
-  PRInt32 startOffset = 0;
-  range->GetStartOffset(&startOffset);
+  // Get start and end points.
+  nsINode* startNode = range->GetStartParent();
+  nsINode* endNode = range->GetEndParent();
+  PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset();
 
-  // Get end point
-  nsCOMPtr<nsIDOMNode> endDOMNode;
-  range->GetEndContainer(getter_AddRefs(endDOMNode));
-  nsCOMPtr<nsINode> endNode(do_QueryInterface(endDOMNode));
-  PRInt32 endOffset = 0;
-  range->GetEndOffset(&endOffset);
-
-  PRInt16 rangeCompareResult = 0;
-  nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range,
-                                             &rangeCompareResult);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (rangeCompareResult < 0) {
-    // Make sure start is before end, by swapping offsets
-    // This occurs when the user selects backwards in the text
-    startNode.swap(endNode);
+  // Make sure start is before end, by swapping DOM points.  This occurs when
+  // the user selects backwards in the text.
+  PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
+                                                       startNode, startOffset);
+  if (rangeCompare < 0) {
+    nsINode* tempNode = startNode;
+    startNode = endNode;
+    endNode = tempNode;
     PRInt32 tempOffset = startOffset;
     startOffset = endOffset;
     endOffset = tempOffset;
   }
 
   nsAccessible *startAccessible =
     DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
   if (!startAccessible) {
@@ -2319,78 +2307,64 @@ nsHyperTextAccessible::GetDOMPointByFram
 
 // nsHyperTextAccessible
 nsresult
 nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange,
                                                    bool aIsStartBound,
                                                    bool aIsStartHTOffset,
                                                    PRInt32 *aHTOffset)
 {
-  nsCOMPtr<nsIDOMNode> DOMNode;
+  nsINode* node = nsnull;
   PRInt32 nodeOffset = 0;
 
-  nsresult rv;
   if (aIsStartBound) {
-    rv = aRange->GetStartContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetStartOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetStartParent();
+    nodeOffset = aRange->StartOffset();
   } else {
-    rv = aRange->GetEndContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetEndOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetEndParent();
+    nodeOffset = aRange->EndOffset();
   }
 
-  nsCOMPtr<nsINode> node(do_QueryInterface(DOMNode));
   nsAccessible *startAcc =
     DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
 
   if (aIsStartHTOffset && !startAcc)
     *aHTOffset = 0;
 
   return NS_OK;
 }
 
 // nsHyperTextAccessible
 nsresult
-nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
+nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
                                              PRInt32 aNodeOffset,
                                              PRInt32 *aHTStartOffset,
                                              PRInt32 *aHTEndOffset,
                                              nsIPersistentProperties *aAttributes)
 {
   nsTArray<nsRange*> ranges;
   GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (!rangeCount)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMNode> DOMNode = do_QueryInterface(aNode);
   for (PRUint32 index = 0; index < rangeCount; index++) {
     nsRange* range = ranges[index];
 
     PRInt16 result;
-    nsresult rv = range->ComparePoint(aNode, aNodeOffset, &result);
+    nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result);
     NS_ENSURE_SUCCESS(rv, rv);
     // ComparePoint checks boundary points, but we need to check that
     // text at aNodeOffset is inside the range.
     // See also bug 460690.
     if (result == 0) {
-      nsCOMPtr<nsIDOMNode> end;
-      rv = range->GetEndContainer(getter_AddRefs(end));
-      NS_ENSURE_SUCCESS(rv, rv);
-      PRInt32 endOffset;
-      rv = range->GetEndOffset(&endOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (aNode == end && aNodeOffset == endOffset) {
+      if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset())
         result = 1;
-      }
     }
 
     if (result == 1) { // range is before point
       PRInt32 startHTOffset = 0;
       nsresult rv = RangeBoundToHypertextOffset(range, false, true,
                                                 &startHTOffset);
       NS_ENSURE_SUCCESS(rv, rv);
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -404,17 +404,17 @@ protected:
    *
    * @param aIncludeDefAttrs  [in] points whether text attributes having default
    *                          values of attributes should be included
    * @param aSourceNode       [in] the node we start to traverse from
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
-  nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
+  nsresult GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
 
 private:
   /**
    * End text offsets array.
    */
--- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -10,51 +10,56 @@
   <script type="application/javascript" 
           src="../common.js"></script>
   <script type="application/javascript" 
           src="../role.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-        var accessible = getApplicationAccessible();
-        if (!accessible) {
-          SimpleTest.finish();
-          return;
-        }
+      var accessible = getApplicationAccessible();
+      if (!accessible) {
+        SimpleTest.finish();
+        return;
+      }
+
+      var bundleServ =
+        Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService);
+      var brandBundle =
+        bundleServ.createBundle("chrome://branding/locale/brand.properties");
 
-        // nsIAccessible::name
-        var bundleServ = Components.classes["@mozilla.org/intl/stringbundle;1"]
-                         .getService(Components.interfaces.nsIStringBundleService);
-        var bundle = bundleServ.createBundle("chrome://branding/locale/brand.properties");
+      var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+        getService(Components.interfaces.nsIXULAppInfo);
 
-        var applicationName = "";
-
+      // nsIAccessible::name
+      var applicationName = "";
+      if (LINUX || SOLARIS) {
+        applicationName = appInfo.name;
+      } else {
         try {
-            applicationName = bundle.GetStringFromName("brandShortName");
-        }  catch(e) {
+          applicationName = brandBundle.GetStringFromName("brandShortName");
+        } catch(e) {
         }
 
         if (applicationName == "")
-            applicationName = "Gecko based application";
-
-        is (accessible.name, applicationName, "wrong application accessible name");
-
-        // nsIAccessibleApplication
-        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
-          getService(Components.interfaces.nsIXULAppInfo);
+          applicationName = "Gecko based application";
+      }
+      is (accessible.name, applicationName, "wrong application accessible name");
 
-        is(accessible.appName, appInfo.name, "Wrong application name");
-        is(accessible.appVersion, appInfo.version, "Wrong application version");
-        is(accessible.platformName, "Gecko", "Wrong platform name");
-        is(accessible.platformVersion, appInfo.platformVersion,
-           "Wrong platform version");
+      // nsIAccessibleApplication
+      is(accessible.appName, appInfo.name, "Wrong application name");
+      is(accessible.appVersion, appInfo.version, "Wrong application version");
+      is(accessible.platformName, "Gecko", "Wrong platform name");
+      is(accessible.platformVersion, appInfo.platformVersion,
+         "Wrong platform version");
 
-        SimpleTest.finish();
+      SimpleTest.finish();
     }
+
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
   </head>
   <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=456121"
     title="nsApplicationAccessible::GetName does not return a default value when brand.properties does not exist">
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1558,59 +1558,16 @@ function delayedStartup(isLoadingBlank, 
   gBrowser.tabContainer.updateVisibility();
 
   gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
   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 */ }
-    if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
-      // Delay the set-default-browser prompt so it doesn't block
-      // initialisation of the session store service.
-      setTimeout(function () {
-        var brandBundle = document.getElementById("bundle_brand");
-        var shellBundle = document.getElementById("bundle_shell");
-
-        var brandShortName = brandBundle.getString("brandShortName");
-        var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-        var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
-                                                           [brandShortName]);
-        var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                           [brandShortName]);
-        var checkEveryTime = { value: shouldCheck };
-        var ps = Services.prompt;
-        var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                              ps.STD_YES_NO_BUTTONS,
-                              null, null, null, checkboxLabel, checkEveryTime);
-        if (rv == 0)
-          shell.setDefaultBrowser(true, false);
-        shell.shouldCheckDefaultBrowser = checkEveryTime.value;
-      }, 0);
-    }
-  }
-#endif
-
   // BiDi UI
   gBidiUI = isBidiEnabled();
   if (gBidiUI) {
     document.getElementById("documentDirection-separator").hidden = false;
     document.getElementById("documentDirection-swap").hidden = false;
     document.getElementById("textfieldDirection-separator").hidden = false;
     document.getElementById("textfieldDirection-swap").hidden = false;
   }
@@ -5369,29 +5326,33 @@ function setToolbarVisibility(toolbar, i
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
   init: function TabsOnTop_init() {
+    this._initialized = true;
     this.syncUI();
     Services.prefs.addObserver(this._prefName, this, false);
   },
 
   uninit: function TabsOnTop_uninit() {
     Services.prefs.removeObserver(this._prefName, this);
   },
 
   toggle: function () {
     this.enabled = !Services.prefs.getBoolPref(this._prefName);
   },
 
   syncUI: function () {
+    if (!this._initialized)
+      return;
+
     let userEnabled = Services.prefs.getBoolPref(this._prefName);
     let enabled = userEnabled && gBrowser.tabContainer.visible;
 
     document.getElementById("cmd_ToggleTabsOnTop")
             .setAttribute("checked", userEnabled);
 
     document.documentElement.setAttribute("tabsontop", enabled);
     document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -421,16 +421,59 @@ BrowserGlue.prototype = {
           return;
 
         browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
       })
     });
 
     let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
     Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
+
+    // Perform default browser checking.
+    var shell;
+    try {
+      shell = Components.classes["@mozilla.org/browser/shell-service;1"]
+        .getService(Components.interfaces.nsIShellService);
+    } catch (e) { }
+    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 */ }
+      if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
+        Services.tm.mainThread.dispatch(function() {
+          var brandBundle = win.document.getElementById("bundle_brand");
+          var shellBundle = win.document.getElementById("bundle_shell");
+  
+          var brandShortName = brandBundle.getString("brandShortName");
+          var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
+          var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
+                                                             [brandShortName]);
+          var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                             [brandShortName]);
+          var checkEveryTime = { value: shouldCheck };
+          var ps = Services.prompt;
+          var rv = ps.confirmEx(win, promptTitle, promptMessage,
+                                ps.STD_YES_NO_BUTTONS,
+                                null, null, null, checkboxLabel, checkEveryTime);
+          if (rv == 0)
+            shell.setDefaultBrowser(true, false);
+          shell.shouldCheckDefaultBrowser = checkEveryTime.value;
+        }, Ci.nsIThread.DISPATCH_NORMAL);
+      }
+    }
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -162,120 +162,87 @@ PlacesViewBase.prototype = {
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = false;
     if (window.content)
       window.content.focus();
   },
 
   _cleanPopup: function PVB_cleanPopup(aPopup) {
-    // Remove places popup children and update markers to keep track of
-    // their indices.
-    let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
-    let end = aPopup._endMarker != -1 ? aPopup._endMarker :
-                                        aPopup.childNodes.length;
-    let items = [];
-
-    // Automatically adjust the start and the end markers.
-    let firstNonStaticNodeFound = false;
-    for (let i = start; i < end; ++i) {
-      let item = aPopup.childNodes[i];
-      if (item.getAttribute("builder") == "end") {
-        // we need to do this for menus that have static content at the end but
-        // are initially empty, eg. the history menu, we need to know where to
-        // start inserting new items.
-        aPopup._endMarker = i;
-        break;
-      }
-
-      if (item._placesNode) {
-        items.push(item);
-        firstNonStaticNodeFound = true;
-      }
-      else {
-        // This is static content.
-        if (!firstNonStaticNodeFound) {
-          // We are at the beginning of the popup, in static content.
-          // The markers are initialized in menu.xml, in the base binding.
-          aPopup._startMarker++;
-        }
-        else {
-          // We are at the end of the popup, after places nodes
-          aPopup._endMarker = i;
-          break;
-        }
-      }
-    }
-
-    for (let i = 0; i < items.length; ++i) {
-      aPopup.removeChild(items[i]);
-      if (aPopup._endMarker != -1)
-        aPopup._endMarker--;
+    // Remove Places nodes from the popup.
+    let child = aPopup._startMarker;
+    while (child.nextSibling != aPopup._endMarker) {
+      if (child.nextSibling._placesNode)
+        aPopup.removeChild(child.nextSibling);
+      else
+        child = child.nextSibling;
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
-    this._cleanPopup(aPopup);
-
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
 
     if (resultNode._feedURI) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem) {
-        aPopup._emptyMenuItem.hidden = true;
-      }
+      this._setEmptyPopupStatus(aPopup, false);
       aPopup._built = true;
       this._populateLivemarkPopup(aPopup);
       return;
     }
 
+    this._cleanPopup(aPopup);
+
     let cc = resultNode.childCount;
     if (cc > 0) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem)
-        aPopup._emptyMenuItem.hidden = true;
+      this._setEmptyPopupStatus(aPopup, false);
 
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
         this._insertNewItemToPopup(child, aPopup, null);
       }
     }
     else {
-      aPopup.setAttribute("emptyplacesresult", "true");
-      // This menu is empty.  If there is no static content, add
-      // an element to show it is empty.
-      if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
-        this._showEmptyMenuItem(aPopup);
+      this._setEmptyPopupStatus(aPopup, true);
     }
     aPopup._built = true;
   },
 
   _removeChild: function PVB__removeChild(aChild) {
     // If document.popupNode pointed to this child, null it out,
     // otherwise controller's command-updating may rely on the removed
     // item still being "selected".
     if (document.popupNode == aChild)
       document.popupNode = null;
 
     aChild.parentNode.removeChild(aChild);
   },
 
-  _showEmptyMenuItem: function PVB__showEmptyMenuItem(aPopup) {
-    if (aPopup._emptyMenuItem) {
-      aPopup._emptyMenuItem.hidden = false;
-      return;
+  _setEmptyPopupStatus:
+  function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
+    if (!aPopup._emptyMenuitem) {
+      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+      aPopup._emptyMenuitem = document.createElement("menuitem");
+      aPopup._emptyMenuitem.setAttribute("label", label);
+      aPopup._emptyMenuitem.setAttribute("disabled", true);
     }
 
-    let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
-    aPopup._emptyMenuItem = document.createElement("menuitem");
-    aPopup._emptyMenuItem.setAttribute("label", label);
-    aPopup._emptyMenuItem.setAttribute("disabled", true);
-    aPopup.appendChild(aPopup._emptyMenuItem);
+    if (aEmpty) {
+      aPopup.setAttribute("emptyplacesresult", "true");
+      // Don't add the menuitem if there is static content.
+      if (!aPopup._startMarker.previousSibling &&
+          !aPopup._endMarker.nextSibling)
+        aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
+    }
+    else {
+      aPopup.removeAttribute("emptyplacesresult");
+      try {
+        aPopup.removeChild(aPopup._emptyMenuitem);
+      } catch (ex) {}
+    }
   },
 
   _createMenuItemForPlacesNode:
   function PVB__createMenuItemForPlacesNode(aPlacesNode) {
     delete aPlacesNode._DOMElement;
     let element;
     let type = aPlacesNode.type;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
@@ -303,33 +270,38 @@ PlacesViewBase.prototype = {
             element.setAttribute("hostContainer", "true");
         }
         else if (itemId != -1) {
           PlacesUtils.livemarks.getLivemark(
             { id: itemId },
             function (aStatus, aLivemark) {
               if (Components.isSuccessCode(aStatus)) {
                 element.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+                // OS X native menubar doesn't track list-style-images since
+                // it doesn't have a frame (bug 733415).  Thus enforce updating.
+                element.setAttribute("image", "");
+                element.removeAttribute("image");
+#endif
                 // Set an expando on the node, controller will use it to build
                 // its metadata.
                 aPlacesNode._feedURI = aLivemark.feedURI;
                 aPlacesNode._siteURI = aLivemark.siteURI;
               }
             }
           );
         }
 
         let popup = document.createElement("menupopup");
         popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
-        if (this._nativeView) {
-          popup._startMarker = -1;
-          popup._endMarker = -1;
+
+        if (!this._nativeView) {
+          popup.setAttribute("placespopup", "true");
         }
-        else
-          popup.setAttribute("placespopup", "true");
+
 #ifdef XP_MACOSX
         // No context menu on mac.
         popup.setAttribute("context", "placesContext");
 #endif
         element.appendChild(popup);
         element.className = "menu-iconic bookmark-item";
 
         aPlacesNode._DOMElement = popup;
@@ -349,50 +321,30 @@ PlacesViewBase.prototype = {
       aPlacesNode._DOMElement = element;
 
     return element;
   },
 
   _insertNewItemToPopup:
   function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
     let element = this._createMenuItemForPlacesNode(aNewChild);
-
-    if (aBefore) {
-      aPopup.insertBefore(element, aBefore);
-    }
-    else {
-      // Add the new element to the menu.  If there is static content at
-      // the end of the menu, add the element before that.  Otherwise,
-      // just add to the end.
-      if (aPopup._endMarker != -1) {
-        let lastElt = aPopup.childNodes[aPopup._endMarker];
-        aPopup.insertBefore(element, lastElt);
-      }
-      else {
-        aPopup.appendChild(element);
-      }
-    }
-
-    if (aPopup._endMarker != -1)
-      aPopup._endMarker++;
-
+    let before = aBefore || aPopup._endMarker;
+    aPopup.insertBefore(element, before);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let siteUrl = aPopup._placesNode._siteURI ? aPopup._placesNode._siteURI.spec
                                               : null;
     if (!siteUrl && aPopup._siteURIMenuitem) {
       aPopup.removeChild(aPopup._siteURIMenuitem);
       aPopup._siteURIMenuitem = null;
-      aPopup._startMarker--;
       aPopup.removeChild(aPopup._siteURIMenuseparator);
       aPopup._siteURIMenuseparator = null;
-      aPopup._startMarker--;
     }
     else if (siteUrl && !aPopup._siteURIMenuitem) {
       // Add "Open (Feed Name)" menuitem.
       aPopup._siteURIMenuitem = document.createElement("menuitem");
       aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
       aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
       aPopup._siteURIMenuitem.setAttribute("oncommand",
         "openUILink(this.getAttribute('targetURI'), event);");
@@ -402,65 +354,58 @@ PlacesViewBase.prototype = {
       // Note: stopPropagation is needed to avoid serving middle-click
       // with BT_onClick that would open all items in tabs.
       aPopup._siteURIMenuitem.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       let label =
         PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
                                          [aPopup.parentNode.getAttribute("label")])
       aPopup._siteURIMenuitem.setAttribute("label", label);
-      aPopup.insertBefore(aPopup._siteURIMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
 
       aPopup._siteURIMenuseparator = document.createElement("menuseparator");
-      aPopup.insertBefore(aPopup._siteURIMenuseparator,
-                         aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
     }
   },
 
   /**
    * Add, update or remove the livemark status menuitem.
    * @param aPopup
    *        The livemark container popup
    * @param aStatus
    *        The livemark status
    */
   _setLivemarkStatusMenuItem:
   function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
-    let itemId = aPopup._placesNode.itemId;
     let statusMenuitem = aPopup._statusMenuitem;
     let stringId = "";
     if (aStatus == Ci.mozILivemark.STATUS_LOADING)
       stringId = "bookmarksLivemarkLoading";
     else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
       stringId = "bookmarksLivemarkFailed";
 
     if (stringId && !statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
       statusMenuitem = document.createElement("menuitem");
       statusMenuitem.setAttribute("livemarkStatus", stringId);
+      statusMenuitem.className = "livemarkstatus-menuitem";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
       statusMenuitem.setAttribute("disabled", true);
-      aPopup.insertBefore(statusMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
       aPopup._statusMenuitem = statusMenuitem;
-      aPopup._startMarker++;
     }
     else if (stringId &&
              statusMenuitem.getAttribute("livemarkStatus") != stringId) {
       // Status has changed, update the cached status menuitem.
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
     }
     else if (!stringId && statusMenuitem) {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
       aPopup._statusMenuitem = null;
-      aPopup._startMarker--;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
       if (elt.localName == "menupopup")
@@ -512,16 +457,22 @@ PlacesViewBase.prototype = {
       throw "aPlacesNode must have _DOMElement set";
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
     if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
       let menu = elt.parentNode;
       if (!menu.hasAttribute("livemark")) {
         menu.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+        // OS X native menubar doesn't track list-style-images since
+        // it doesn't have a frame (bug 733415).  Thus enforce updating.
+        menu.setAttribute("image", "");
+        menu.removeAttribute("image");
+#endif
       }
 
       PlacesUtils.livemarks.getLivemark(
         { id: aPlacesNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             // Set an expando on the node, controller will use it to build
             // its metadata.
@@ -575,23 +526,18 @@ PlacesViewBase.prototype = {
       elt = elt.parentNode;
 
     if (parentElt._built) {
       parentElt.removeChild(elt);
 
       // Figure out if we need to show the "<Empty>" menu-item.
       // TODO Bug 517701: This doesn't seem to handle the case of an empty
       // root.
-      if (!parentElt.hasChildNodes() ||
-          (parentElt.childNodes.length == 1 &&
-          parentElt.firstChild == parentElt._emptyMenuItem))
-        this._showEmptyMenuItem(parentElt);
-
-      if (parentElt._endMarker != -1)
-        parentElt._endMarker--;
+      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+        this._setEmptyPopupStatus(parentElt, true);
     }
   },
 
   nodeReplaced:
   function PVB_nodeReplaced(aParentPlacesNode, aOldPlacesNode, aNewPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
@@ -615,18 +561,19 @@ PlacesViewBase.prototype = {
     }
   },
 
   nodeHistoryDetailsChanged:
   function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
     if (aPlacesNode.parent && aPlacesNode.parent._feedURI) {
       // Find the node in the parent.
       let popup = aPlacesNode.parent._DOMElement;
-      for (let i = popup._startMarker; i < popup.childNodes.length; i++) {
-        let child = popup.childNodes[i];
+      for (let child = popup._startMarker.nextSibling;
+           child != popup._endMarker;
+           child = child.nextSibling) {
         if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
           if (aCount)
             child.setAttribute("visited", "true");
           else
             child.removeAttribute("visited");
           break;
         }
       }
@@ -644,21 +591,21 @@ PlacesViewBase.prototype = {
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
 
     if (!parentElt._built)
       return;
 
-    let index = parentElt._startMarker + 1 + aIndex;
+    let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
                                parentElt.childNodes[index]);
-    if (parentElt._emptyMenuItem)
-      parentElt._emptyMenuItem.hidden = true;
+    this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
     // use this notification when the item in question is moved from one
@@ -679,17 +626,18 @@ PlacesViewBase.prototype = {
 
     let parentElt = aNewParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aNewParentPlacesNode node must have _DOMElement set";
 
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = parentElt._startMarker + 1 + aNewIndex;
+      let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                  aNewIndex + 1;
       parentElt.insertBefore(elt, parentElt.childNodes[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
@@ -816,48 +764,83 @@ PlacesViewBase.prototype = {
       hasMultipleURIs = numURINodes > 1;
     }
 
     if (!hasMultipleURIs) {
       // We don't have to show any option.
       if (aPopup._endOptOpenAllInTabs) {
         aPopup.removeChild(aPopup._endOptOpenAllInTabs);
         aPopup._endOptOpenAllInTabs = null;
-        aPopup._endMarker--;
 
         aPopup.removeChild(aPopup._endOptSeparator);
         aPopup._endOptSeparator = null;
-        aPopup._endMarker--;
       }
     }
     else if (!aPopup._endOptOpenAllInTabs) {
       // Create a separator before options.
       aPopup._endOptSeparator = document.createElement("menuseparator");
       aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
       aPopup.appendChild(aPopup._endOptSeparator);
-      aPopup._endMarker++;
 
       // Add the "Open All in Tabs" menuitem.
       aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
       aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
       aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
         "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                "PlacesUIUtils.getViewForNode(this));");
       aPopup._endOptOpenAllInTabs.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       aPopup._endOptOpenAllInTabs.setAttribute("label",
         gNavigatorBundle.getString("menuOpenAllInTabs.label"));
       aPopup.appendChild(aPopup._endOptOpenAllInTabs);
-      aPopup._endMarker++;
+    }
+  },
+
+  _ensureMarkers: function PVB__ensureMarkers(aPopup) {
+    if (aPopup._startMarker)
+      return;
+
+    // _startMarker is an hidden menuseparator that lives before places nodes.
+    aPopup._startMarker = document.createElement("menuseparator");
+    aPopup._startMarker.hidden = true;
+    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+
+    // _endMarker is an hidden menuseparator that lives after places nodes.
+    aPopup._endMarker = document.createElement("menuseparator");
+    aPopup._endMarker.hidden = true;
+    aPopup.appendChild(aPopup._endMarker);
+
+    // Move the markers to the right position.
+    let firstNonStaticNodeFound = false;
+    for (let i = 0; i < aPopup.childNodes.length; i++) {
+      let child = aPopup.childNodes[i];
+      // Menus that have static content at the end, but are initially empty,
+      // use a special "builder" attribute to figure out where to start
+      // inserting places nodes.
+      if (child.getAttribute("builder") == "end") {
+        aPopup.insertBefore(aPopup._endMarker, child);
+        break;
+      }
+
+      if (child._placesNode && !firstNonStaticNodeFound) {
+        firstNonStaticNodeFound = true;
+        aPopup.insertBefore(aPopup._startMarker, child);
+      }
+    }
+    if (!firstNonStaticNodeFound) {
+      aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
     }
   },
 
   _onPopupShowing: function PVB__onPopupShowing(aEvent) {
     // Avoid handling popupshowing of inner views.
     let popup = aEvent.originalTarget;
+
+    this._ensureMarkers(popup);
+
     if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       if (!popup._placesNode.containerOpen)
         popup._placesNode.containerOpen = true;
       if (!popup._built)
         this._rebuildPopup(popup);
 
       this._mayAddCommandsItems(popup);
     }
@@ -1803,18 +1786,16 @@ function PlacesMenu(aPopupShowingEvent, 
   this._viewElt = this._rootElt.parentNode;   // <menu>
   this._viewElt._placesView = this;
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(window, ["unload"], false);
 
 #ifdef XP_MACOSX
   if (this._viewElt.parentNode.localName == "menubar") {
     this._nativeView = true;
-    this._rootElt._startMarker = -1;
-    this._rootElt._endMarker = -1;
   }
 #endif
 
   PlacesViewBase.call(this, aPlace);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
@@ -1824,18 +1805,16 @@ PlacesMenu.prototype = {
     if (aIID.equals(Ci.nsIDOMEventListener))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   _removeChild: function PM_removeChild(aChild) {
     PlacesViewBase.prototype._removeChild.apply(this, arguments);
-    if (this._endMarker != -1)
-      this._endMarker--;
   },
 
   uninit: function PM_uninit() {
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(window, ["unload"], false);
 
     PlacesViewBase.prototype.uninit.apply(this, arguments);
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -67,38 +67,33 @@
                                                 "menupopup-drop-indicator-bar");
       </field>
 
       <field name="_scrollBox">
         document.getAnonymousElementByAttribute(this, "class",
                                                 "popup-internal-box");
       </field>
 
-      <!-- markers for start and end of valid places items -->
-      <field name="_startMarker">-1</field>
-      <field name="_endMarker">-1</field>
-
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
       <!-- Check if we should hide the drop indicator for the target -->
       <method name="_hideDropIndicator">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var target = aEvent.target;
+          let target = aEvent.target;
 
-          // in some view we have _startMarker and _endMarker, we should not
-          // draw the drop indicator outside of them
-          var betweenMarkers = true;
-          if (this._startMarker != -1 &&
-              target.boxObject.y <= this.childNodes[this._startMarker].boxObject.y)
-            betweenMarkers = false;
-          if (this._endMarker != -1 &&
-              target.boxObject.y >= this.childNodes[this._endMarker].boxObject.y)
-            betweenMarkers = false;
+          // Don't draw the drop indicator outside of markers.
+          // The markers are hidden, since otherwise sometimes popups acquire
+          // scrollboxes on OS X, so we can't use them directly.
+          let firstChildTop = this._startMarker.nextSibling.boxObject.y;
+          let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
+                                this._endMarker.previousSibling.boxObject.height;
+          let betweenMarkers = target.boxObject.y >= firstChildTop ||
+                               target.boxObject.y <= lastChildBottom;
 
           // Hide the dropmarker if current node is not a Places node.
           return !(target && target._placesNode && betweenMarkers);
         ]]></body>
       </method>
 
       <!-- This function returns information about where to drop when
            dragging over this popup insertion point -->
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -194,17 +194,17 @@ menuitem.bookmark-item {
 }
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark items */
-.bookmark-item:not([container])  {
+.bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 .bookmark-item[container][livemark] { 
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -224,25 +224,30 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.query-item[container] {
-  list-style-image: url("chrome://browser/skin/places/history.png");
-}
-
-.bookmark-item[livemark] {
+.bookmark-item[container][livemark] {
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
-.bookmark-item[query] {
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
   list-style-image: url("chrome://browser/skin/places/query.png");
 }
 
 .bookmark-item[query][tagContainer] {
   list-style-image: url("chrome://browser/skin/places/tag.png");
 }
 
 .bookmark-item[query][dayContainer] {
@@ -252,56 +257,49 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item[query][hostContainer] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[query][hostContainer][open] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.bookmark-item[livemark] .menuitem-iconic {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[livemark] .menuitem-iconic[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item menuitem[openInTabs],
-.bookmark-item menuitem[siteURI] {
+/* Workaround for native menubar inheritance */
+.openintabs-menuitem,
+.openlivemarksite-menuitem,
+.livemarkstatus-menuitem {
   list-style-image: none;
 }
 
+.bookmark-item[cutting] > .toolbarbutton-icon,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
+  opacity: 0.5;
+}
+
+.bookmark-item[cutting] > .toolbarbutton-text,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
+  opacity: 0.7;
+}
+
 #wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
   background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center;
 }
 
 .bookmarks-toolbar-customize {
   max-width: 15em !important;
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;
 }
 
 /* ----- BOOKMARK MENUS ----- */
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
-.bookmark-item[cutting] > .toolbarbutton-icon,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
-  opacity: 0.5;
-}
-
-.bookmark-item[cutting] > .toolbarbutton-text,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
-  opacity: 0.7;
-}
-
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.png");
 }
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -454,17 +454,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 130
+  MAX_LEAK_COUNT = 123
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -72,10 +72,10 @@ public interface Driver {
     void startCheckerboardRecording();
     float stopCheckerboardRecording();
 
     /**
      * Get a copy of the painted content region.
      * @return A 2-D array of pixels (indexed by y, then x). The pixels
      * are in ARGB-8888 format.
      */
-    int[][] getPaintedSurface();
+    PaintedSurface getPaintedSurface();
 }
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -43,16 +43,18 @@ import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.IntBuffer;
 import java.util.HashMap;
 import java.util.List;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.Long;
 
@@ -286,17 +288,17 @@ public class FennecNativeDriver implemen
         for (View v : mSolo.getCurrentViews()) {
             if (v instanceof GLSurfaceView) {
                 return (GLSurfaceView)v;
             }
         }
         return null;
     }
 
-    public int[][] getPaintedSurface() {
+    public PaintedSurface getPaintedSurface() {
         GLSurfaceView view = getSurfaceView();
         if (view == null) {
             return null;
         }
         IntBuffer pixelBuffer;
         try {
             pixelBuffer = (IntBuffer)_getPixels.invoke(view);
         } catch (Exception e) {
@@ -304,24 +306,44 @@ public class FennecNativeDriver implemen
             return null;
         }
 
         // now we need to (1) flip the image, because GL likes to do things up-side-down,
         // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
         int w = view.getWidth();
         int h = view.getHeight();
         pixelBuffer.position(0);
-        int[][] pixels = new int[h][w];
-        for (int y = h - 1; y >= 0; y--) {
-            for (int x = 0; x < w; x++) {
-                int agbr = pixelBuffer.get();
-                pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+        String mapFile = "/mnt/sdcard/pixels.map";
+
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            fos = new FileOutputStream(mapFile);
+            dos = new DataOutputStream(fos);
+
+            for (int y = h - 1; y >= 0; y--) {
+                for (int x = 0; x < w; x++) {
+                    int agbr = pixelBuffer.get();
+                    dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
+                }
+            }
+            return new PaintedSurface(mapFile, w, h);
+        } catch (IOException e) {
+            throw new RoboCopException("exception with pixel writer on file: " + mapFile);
+        } finally {
+            try {
+                if (dos != null && fos != null) {
+                    dos.flush();
+                    dos.close();
+                    fos.close();
+                }
+            } catch (IOException e) {
+                throw new RoboCopException("exception closing pixel writer on file: " + mapFile);
             }
         }
-        return pixels;
     }
 
     public int mHeight=0;
     public int mScrollHeight=0;
     public int mPageHeight=10;
 
     class scrollHandler implements InvocationHandler {
         public scrollHandler(){};
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -56,16 +56,17 @@ JAVAFILES = \
   Driver.java \
   Element.java \
   FennecNativeActions.java \
   FennecMochitestAssert.java \
   FennecTalosAssert.java \
   FennecNativeDriver.java \
   FennecNativeElement.java \
   RoboCopException.java \
+  PaintedSurface.java \
   $(NULL)
 
 _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
 
 _TEST_FILES = $(wildcard $(TESTPATH)/*.html)
 
 _ROBOCOP_TOOLS = \
   $(TESTPATH)/robocop.ini \
new file mode 100644
--- /dev/null
+++ b/build/mobile/robocop/PaintedSurface.java.in
@@ -0,0 +1,62 @@
+#filter substitution
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class PaintedSurface {
+    private String mFileName = null;
+    private int mWidth = -1;
+    private int mHeight = -1;
+    private MappedByteBuffer mPixelBuffer = null;
+
+    public PaintedSurface(String filename, int width, int height) {
+        mFileName = filename;
+        mWidth = width;
+        mHeight = height;
+        
+        try {
+            File f = new File(filename);
+            int pixelSize = (int)f.length();
+            
+            FileInputStream pixelFile = new FileInputStream(filename);
+            mPixelBuffer = pixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize);
+        } catch (java.io.FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (java.io.IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final int getPixelAt(int x, int y) {
+        if (mPixelBuffer == null) {
+            throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer");
+        }
+
+        if (x >= mWidth || x < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid x value");
+        }
+
+        if (y >= mHeight || y < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid y value");
+        }
+
+        // The rows are reversed so row 0 is at the end and we start with the last row.
+        // This is why we do mHeight-y;
+        int index = (x + ((mHeight - y - 1) * mWidth)) * 4;
+        int b1 = mPixelBuffer.get(index) & 0xFF;
+        int b2 = mPixelBuffer.get(index + 1) & 0xFF;
+        int b3 = mPixelBuffer.get(index + 2) & 0xFF;
+        int b4 = mPixelBuffer.get(index + 3) & 0xFF;
+        int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
+        return value;
+    }
+}
+
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -143,16 +143,19 @@ testxpcsrcdir = $(topsrcdir)/testing/xpc
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
+	  --tests-root-dir=$(testxpcobjdir) \
+	  --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
+	  --xunit-suite-name=xpcshell \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 xpcshell-tests-remote: DM_TRANS?=adb
 xpcshell-tests-remote:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
--- a/configure.in
+++ b/configure.in
@@ -623,19 +623,20 @@ CXX_VERSION='N/A'
 if test "$GCC" = "yes"; then
     GNU_CC=1
     CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'`
 fi
 if test "$GXX" = "yes"; then
     GNU_CXX=1
     CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'`
 fi
-if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then
+if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_AS=1
 fi
+rm -f conftest.out
 if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_LD=1
 fi
 if test "$GNU_CC"; then
     if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then
         GCC_USE_GNU_LD=1
     fi
 fi
@@ -9207,17 +9208,17 @@ if test -z "$MOZ_NATIVE_NSPR"; then
     ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla"
     if test -z "$MOZ_DEBUG"; then
         ac_configure_args="$ac_configure_args --disable-debug"
     else
         ac_configure_args="$ac_configure_args --enable-debug"
     fi
     if test "$MOZ_OPTIMIZE" = "1"; then
         ac_configure_args="$ac_configure_args --enable-optimize"
-    else
+    elif test -z "$MOZ_OPTIMIZE"; then
         ac_configure_args="$ac_configure_args --disable-optimize"
     fi
     if test -n "$HAVE_64BIT_OS"; then
         ac_configure_args="$ac_configure_args --enable-64bit"
     fi
     if test -n "$USE_ARM_KUSER"; then
         ac_configure_args="$ac_configure_args --with-arm-kuser"
     fi
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -877,19 +877,17 @@ nsFrameScriptExecutor::InitTabChildGloba
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
                          nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
 
   
   JS_SetContextPrivate(cx, aScope);
 
   nsresult rv =
-    xpc->InitClassesWithNewWrappedGlobal(cx, aScope,
-                                         NS_GET_IID(nsISupports),
-                                         mPrincipal, nsnull,
+    xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
                                          flags, getter_AddRefs(mGlobal));
   NS_ENSURE_SUCCESS(rv, false);
 
     
   JSObject* global = nsnull;
   rv = mGlobal->GetJSObject(&global);
   NS_ENSURE_SUCCESS(rv, false);
 
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -592,39 +592,20 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
     if (aCx && wrapper) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
-        JSObject *preservedWrapper = nsnull;
-
-        // If reparenting moves us to a new compartment, preserving causes
-        // problems. In that case, we release ourselves and re-preserve after
-        // reparenting so we're sure to have the right JS object preserved.
-        // We use a JSObject stack copy of the wrapper to protect it from GC
-        // under ReparentWrappedNativeIfFound.
-        if (aNode->PreservingWrapper()) {
-          preservedWrapper = wrapper;
-          nsContentUtils::ReleaseWrapper(aNode, aNode);
-          NS_ASSERTION(aNode->GetWrapper(),
-                       "ReleaseWrapper cleared our wrapper, this code needs to "
-                       "be changed to deal with that!");
-        }
-
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
         rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
                                                getter_AddRefs(oldWrapper));
 
-        if (preservedWrapper) {
-          nsContentUtils::PreserveWrapper(aNode, aNode);
-        }
-
         if (NS_FAILED(rv)) {
           aNode->mNodeInfo.swap(nodeInfo);
 
           return rv;
         }
       }
     }
   }
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -597,17 +597,17 @@ nsWebSocket::Initialize(nsISupports* aOw
   nsTArray<nsString> protocolArray;
 
   if (aArgc == 2) {
     JSObject *jsobj;
 
     if (JSVAL_IS_OBJECT(aArgv[1]) &&
         (jsobj = JSVAL_TO_OBJECT(aArgv[1])) &&
         JS_IsArrayObject(aContext, jsobj)) {
-      jsuint len;
+      uint32_t len;
       JS_GetArrayLength(aContext, jsobj, &len);
       
       for (PRUint32 index = 0; index < len; ++index) {
         jsval value;
 
         if (!JS_GetElement(aContext, jsobj, index, &value))
           return NS_ERROR_DOM_SYNTAX_ERR;
 
--- a/content/canvas/src/CanvasUtils.cpp
+++ b/content/canvas/src/CanvasUtils.cpp
@@ -117,17 +117,17 @@ CoerceDouble(jsval v, double* d)
 }
 
 template<size_t N>
 static bool
 JSValToMatrixElts(JSContext* cx, const jsval& val,
                   double* (&elts)[N], nsresult* rv)
 {
     JSObject* obj;
-    jsuint length;
+    uint32_t length;
 
     if (JSVAL_IS_PRIMITIVE(val) ||
         !(obj = JSVAL_TO_OBJECT(val)) ||
         !JS_GetArrayLength(cx, obj, &length) ||
         N != length) {
         // Not an array-like thing or wrong size
         *rv = NS_ERROR_INVALID_ARG;
         return false;
--- a/content/canvas/src/CanvasUtils.h
+++ b/content/canvas/src/CanvasUtils.h
@@ -141,21 +141,21 @@ DashArrayToJSVal(FallibleTArray<T>& dash
 
 template<typename T>
 nsresult
 JSValToDashArray(JSContext* cx, const jsval& patternArray,
                  FallibleTArray<T>& dashes)
 {
     // The cap is pretty arbitrary.  16k should be enough for
     // anybody...
-    static const jsuint MAX_NUM_DASHES = 1 << 14;
+    static const uint32_t MAX_NUM_DASHES = 1 << 14;
 
     if (!JSVAL_IS_PRIMITIVE(patternArray)) {
         JSObject* obj = JSVAL_TO_OBJECT(patternArray);
-        jsuint length;
+        uint32_t length;
         if (!JS_GetArrayLength(cx, obj, &length)) {
             // Not an array-like thing
             return NS_ERROR_INVALID_ARG;
         } else if (length > MAX_NUM_DASHES) {
             // Too many dashes in the pattern
             return NS_ERROR_ILLEGAL_VALUE;
         }
 
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -37,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/XPCOM.h"
 
 #include "nsMediaCache.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
 #include "nsNetUtil.h"
 #include "prio.h"
 #include "nsThreadUtils.h"
 #include "MediaResource.h"
 #include "nsMathUtils.h"
 #include "prlog.h"
 #include "nsIPrivateBrowsingService.h"
 #include "mozilla/Preferences.h"
@@ -540,18 +541,25 @@ nsMediaCacheStream::BlockList::NotifyBlo
 }
 
 nsresult
 nsMediaCache::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mFD, "Cache file already open?");
 
+  // In single process Gecko, store the media cache in the profile directory
+  // so that multiple users can use separate media caches concurrently.
+  // In multi-process Gecko, there is no profile dir, so just store it in the
+  // system temp directory instead.
+  nsresult rv;
   nsCOMPtr<nsIFile> tmp;
-  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp));
+  const char* dir = (XRE_GetProcessType() == GeckoProcessType_Content) ?
+    NS_OS_TEMP_DIR : NS_APP_USER_PROFILE_LOCAL_50_DIR;
+  rv = NS_GetSpecialDirectory(dir, getter_AddRefs(tmp));
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsCOMPtr<nsILocalFile> tmpFile = do_QueryInterface(tmp);
   NS_ENSURE_TRUE(tmpFile != nsnull, NS_ERROR_FAILURE);
 
   // We put the media cache file in
   // ${TempDir}/mozilla-media-cache/media_cache
   rv = tmpFile->AppendNative(nsDependentCString("mozilla-media-cache"));
new file mode 100644
--- /dev/null
+++ b/dom/base/DOMRequestHelper.jsm
@@ -0,0 +1,80 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+  * helper object for APIs that deal with DOMRequest and need to release them properly
+  * when the window goes out of scope
+  */
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(Services, "rs", function() {
+  return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
+});
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
+function DOMRequestIpcHelper() {
+}
+
+DOMRequestIpcHelper.prototype = {
+  getRequestId: function(aRequest) {
+    let id = "id" + this._getRandomId();
+    this._requests[id] = aRequest;
+    return id;
+  },
+
+  getRequest: function(aId) {
+    if (this._requests[aId])
+      return this._requests[aId];
+  },
+
+  removeRequest: function(aId) {
+    if (this._requests[aId])
+      delete this._requests[aId];
+  },
+
+  _getRandomId: function() {
+    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+    if (wId == this.innerWindowID) {
+      Services.obs.removeObserver(this, "inner-window-destroyed");
+      this._requests = [];
+      this._window = null;
+      this._messages.forEach((function(msgName) {
+        cpmm.removeMessageListener(msgName, this);
+      }).bind(this));
+      if(this.uninit)
+        this.uninit();
+    }
+  },
+
+  initHelper: function(aWindow, aMessages) {
+    this._messages = aMessages;
+    this._requests = [];
+    this._window = aWindow;
+    let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+    this.innerWindowID = util.currentInnerWindowID;
+    this._id = this._getRandomId();
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+    this._messages.forEach((function(msgName) {
+      cpmm.addMessageListener(msgName, this);
+    }).bind(this));
+  },
+
+  createRequest: function() {
+    return Services.rs.createRequest(this._window);
+  }
+}
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -61,16 +61,17 @@ EXTRA_JS_MODULES = ConsoleAPIStorage.jsm
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 EXTRA_COMPONENTS = \
 	        Webapps.js \
 	        Webapps.manifest \
 		$(NULL)
 
 EXTRA_JS_MODULES += Webapps.jsm \
+        DOMRequestHelper.jsm \
 		$(NULL)
 endif
 
 XPIDLSRCS = \
   nsIDOMDOMError.idl \
   nsIDOMDOMRequest.idl \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
--- a/dom/base/Webapps.js
+++ b/dom/base/Webapps.js
@@ -1,359 +1,407 @@
-/* ***** 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 Open Web Apps.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Fabrice Desré <fabrice@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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+
+XPCOMUtils.defineLazyGetter(Services, "rs", function() {
+  return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
+});
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
+function convertAppsArray(aApps, aWindow) {
+  let apps = new Array();
+  for (let i = 0; i < aApps.length; i++) {
+    let app = aApps[i];
+    apps.push(new WebappsApplication(aWindow, app.origin, app.manifest, app.manifestURL, 
+                                     app.receipts, app.installOrigin, app.installTime));
+  }
+  return apps;
+}
 
 function WebappsRegistry() {
-  this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
-                   "Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO",
-                   "Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"];
-
-  this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
-
-  this.messages.forEach((function(msgName) {
-    this.mm.addMessageListener(msgName, this);
-  }).bind(this));
-
-  this._window = null;
-  this._id = this._getRandomId();
-  this._callbacks = [];
 }
 
 WebappsRegistry.prototype = {
-  _onerror: null,
-  _oninstall: null,
-  _onuninstall: null,
+  __proto__: DOMRequestIpcHelper.prototype,
 
   /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
    * only the name property is mandatory
    */
   checkManifest: function(aManifest, aInstallOrigin) {
     // TODO : check for install_allowed_from
     if (aManifest.name == undefined)
       return false;
-    
+
     if (aManifest.installs_allowed_from) {
       ok = false;
       aManifest.installs_allowed_from.forEach(function(aOrigin) {
         if (aOrigin == "*" || aOrigin == aInstallOrigin)
           ok = true;
       });
       return ok;
     }
     return true;
   },
-  
-  getCallbackId: function(aCallback) {
-    let id = "id" + this._getRandomId();
-    this._callbacks[id] = aCallback;
-    return id;
-  },
-  
-  getCallback: function(aId) {
-    return this._callbacks[aId];
-  },
-
-  removeCallback: function(aId) {
-    if (this._callbacks[aId])
-      delete this._callbacks[aId];
-  },
-  
-  _getRandomId: function() {
-    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-  },
-
-  _convertAppsArray: function(aApps) {
-    let apps = new Array();
-    for (let i = 0; i < aApps.length; i++) {
-      let app = aApps[i];
-      apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime));
-    }
-    return apps;
-  },
-
-  set oninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._oninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-  
-  set onuninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._onuninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
-  set onerror(aCallback) {
-    this._onerror = aCallback;
-  },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
-    if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
+    if (msg.oid != this._id)
       return
+    let req = this.getRequest(msg.requestID);
+    if (!req)
+      return;
     let app = msg.app;
-    let cb;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
-        if (this._oninstall)
-          this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt,
+        Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
                                                 app.installOrigin, app.installTime));
         break;
       case "Webapps:Install:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.DENIED));
-        break;
-      case "Webapps:Uninstall:Return:OK":
-        if (this._onuninstall)
-          this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0));
+        Services.rs.fireError(req, "DENIED");
         break;
-      case "Webapps:Uninstall:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
-        break;
-      case "Webapps:Enumerate:Return:OK":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.success) {
-          let apps = this._convertAppsArray(msg.apps);
-          cb.success.handleEvent(apps, apps.length);
+      case "Webapps:GetSelf:Return:OK":
+        if (msg.apps.length) {
+          app = msg.apps[0];
+          Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
+                                                  app.installOrigin, app.installTime));
+        } else {
+          Services.rs.fireSuccess(req, null);
         }
         break;
-      case "Webapps:Enumerate:Return:KO":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.error)
-          cb.error.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
+      case "Webapps:GetInstalled:Return:OK":
+        Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window));
+        break;
+      case "Webapps:GetSelf:Return:KO":
+      case "Webapps:GetInstalled:Return:KO":
+        Services.rs.fireError(req, "ERROR");
         break;
     }
-    this.removeCallback(msg.callbackID);
-  },
-  
-  _fireError: function(aCode) {
-    if (!this._onerror)
-      return;
-    this._onerror.handleEvent(new RegistryError(aCode));
+    this.removeRequest(msg.requestID);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
     return uri.prePath; 
   },
 
   // mozIDOMApplicationRegistry implementation
   
-  install: function(aURL, aReceipt) {
+  install: function(aURL, aParams) {
+    let request = this.createRequest();
+    let requestID = this.getRequestId(request);
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", aURL, true);
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         try {
           let installOrigin = this._getOrigin(this._window.location.href);
           let manifest = JSON.parse(xhr.responseText, installOrigin);
           if (!this.checkManifest(manifest, installOrigin)) {
-            this._fireError(Ci.mozIDOMApplicationRegistryError.INVALID_MANIFEST);
+            Services.rs.fireError(request, "INVALID_MANIFEST");
           } else {
-            this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
-                                                          origin: this._getOrigin(aURL),
-                                                          manifest: manifest,
-                                                          receipt: aReceipt },
-                                                          from: this._window.location.href,
-                                                          oid: this._id });
+            let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+            cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
+                                                              origin: this._getOrigin(aURL),
+                                                              manifestURL: aURL,
+                                                              manifest: manifest,
+                                                              receipts: receipts },
+                                                              from: this._window.location.href,
+                                                              oid: this._id,
+                                                              requestID: requestID });
           }
         } catch(e) {
-          this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR);
+          Services.rs.fireError(request, "MANIFEST_PARSE_ERROR");
         }
       }
       else {
-        this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_URL_ERROR);
+        Services.rs.fireError(request, "MANIFEST_URL_ERROR");
       }      
     }).bind(this), false);
 
     xhr.addEventListener("error", (function() {
-      this._fireError(Ci.mozIDOMApplicationRegistryError.NETWORK_ERROR);
+      Services.rs.fireError(request, "NETWORK_ERROR");
     }).bind(this), false);
 
     xhr.send(null);
-  },
-
-  uninstall: function(aOrigin) {
-    if (this.hasPrivileges)
-      this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href,
-                                                      origin: aOrigin,
-                                                      oid: this._id });
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+    return request;
   },
 
-  launch: function(aOrigin) {
-    this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin,
-                                                 from: this._window.location.href});
-  },
-  
-  enumerate: function(aSuccess, aError) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href,
-                                                    origin: this._getOrigin(this._window.location.href),
-                                                    oid: this._id,
-                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  getSelf: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href),
+                                               oid: this._id,
+                                               requestID: this.getRequestId(request) });
+    return request;
   },
 
-  enumerateAll: function(aSuccess, aError) {
-    if (this.hasPrivileges) {
-      this.mm.sendAsyncMessage("Webapps:EnumerateAll", { from: this._window.location.href,
-                                                    origin: this._getOrigin(this._window.location.href),
+  getInstalled: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href),
                                                     oid: this._id,
-                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
-    } else {
-      if (aError)
-        aError.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
-    }
+                                                    requestID: this.getRequestId(request) });
+    return request;
   },
 
-  observe: function(aSubject, aTopic, aData) {
-    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    if (wId == this.innerWindowID) {
-      Services.obs.removeObserver(this, "inner-window-destroyed");
-      this._oninstall = null;
-      this._onuninstall = null;
-      this._onerror = null;
-      this._callbacks = [];
-      this._window = null;
-    }
+  get mgmt() {
+    if (!this._mgmt)
+      this._mgmt = new WebappsApplicationMgmt(this._window);
+    return this._mgmt;
+  },
+
+  uninit: function() {
+    this._mgmt = null;
   },
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
-    dump("DOMApplicationRegistry::init() " + aWindow + "\n");
-    this._window = aWindow;
-    this._window.appId = this._id;
-    let from = Services.io.newURI(this._window.location.href, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
+    this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
+                              "Webapps:GetInstalled:Return:OK", "Webapps:GetInstalled:Return:KO",
+                              "Webapps:GetSelf:Return:OK", "Webapps:GetSelf:Return:KO"]);
 
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-    this.innerWindowID = util.currentInnerWindowID;
+    this._id = util.outerWindowID;
   },
   
   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
   
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
                                     contractID: "@mozilla.org/webapps;1",
                                     interfaces: [Ci.mozIDOMApplicationRegistry],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Registry"})
 }
 
-function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) {
+/**
+  * mozIDOMApplication object
+  */
+function WebappsApplication(aWindow, aOrigin, aManifest, aManifestURL, aReceipts, aInstallOrigin, aInstallTime) {
   this._origin = aOrigin;
   this._manifest = aManifest;
-  this._receipt = aReceipt;
+  this._manifestURL = aManifestURL;
+  this._receipts = aReceipts;
   this._installOrigin = aInstallOrigin;
   this._installTime = aInstallTime;
+
+  this.initHelper(aWindow, ["Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO"]);
 }
 
 WebappsApplication.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
   _origin: null,
   _manifest: null,
-  _receipt: null,
+  _manifestURL: null,
+  _receipts: [],
   _installOrigin: null,
   _installTime: 0,
 
   get origin() {
     return this._origin;
   },
 
   get manifest() {
     return this._manifest;
   },
 
-  get receipt() {
-    return this._receipt;
+  get manifestURL() {
+    return this._manifestURL;
+  },
+
+  get receipts() {
+    return this._receipts;
   },
 
   get installOrigin() {
     return this._installOrigin;
   },
   
   get installTime() {
     return this._installTime;
   },
 
+  launch: function(aStartPoint) {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:Launch", { origin: this._origin,
+                                              startPoint: aStartPoint,
+                                              oid: this._id,
+                                              requestID: this.getRequestId(request) });
+    return request;
+  },
+
+  uninstall: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: this._origin,
+                                                 oid: this._id,
+                                                 requestID: this.getRequestId(request) });
+    return request;
+  },
+
+  receiveMessage: function(aMessage) {
+    var msg = aMessage.json;
+    let req = this.getRequest(msg.requestID);
+    if (msg.oid != this._id || !req)
+      return;
+    switch (aMessage.name) {
+      case "Webapps:Uninstall:Return:OK":
+        Services.rs.fireSuccess(req, msg.origin);
+        break;
+      case "Webapps:Uninstall:Return:KO":
+        Services.rs.fireError(req, msg.origin);
+        break;
+    }
+    this.removeRequest(msg.requestID);
+  },
+
   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
                                     contractID: "@mozilla.org/webapps/application;1",
                                     interfaces: [Ci.mozIDOMApplication],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application"})
 }
 
-function RegistryError(aCode) {
-  this._code = aCode;
+/**
+  * mozIDOMApplicationMgmt object
+  */
+function WebappsApplicationMgmt(aWindow) {
+  let principal = aWindow.document.nodePrincipal;
+  let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+
+  let perm = principal == secMan.getSystemPrincipal() ? 
+               Ci.nsIPermissionManager.ALLOW_ACTION : 
+               Services.perms.testExactPermission(principal.URI, "webapps-manage");
+
+  //only pages with perm set can use some functions
+  this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
+
+  this.initHelper(aWindow, ["Webapps:GetAll:Return:OK", "Webapps:GetAll:Return:KO",
+                            "Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]);
+
+  this._oninstall = null;
+  this._onuninstall = null;
 }
 
-RegistryError.prototype = {
-  _code: null,
-  
-  get code() {
-    return this._code;
+WebappsApplicationMgmt.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
+
+  uninit: function() {
+    this._oninstall = null;
+    this._onuninstall = null;
+  },
+
+  getAll: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id,
+                                              requestID: this.getRequestId(request),
+                                              hasPrivileges: this.hasPrivileges });
+    return request;
+  },
+
+  get oninstall() {
+    return this._oninstall;
+  },
+
+  get onuninstall() {
+    this._onuninstall;
+  },
+
+  set oninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._oninstall = aCallback;
+    else
+
+      throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE);
+  },
+
+  set onuninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._onuninstall = aCallback;
+    else
+      throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE);
   },
-  
-  classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistryError]),
 
-  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-                                    contractID: "@mozilla.org/webapps/error;1",
-                                    interfaces: [Ci.mozIDOMApplicationRegistryError],
+  receiveMessage: function(aMessage) {
+    var msg = aMessage.json;
+    let req = this.getRequest(msg.requestID);
+    // We want Webapps:Install:Return:OK and Webapps:Uninstall:Return:OK to be boradcasted
+    // to all instances of mozApps.mgmt
+    if (!((msg.oid == this._id && req) 
+       || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
+      return;
+    switch (aMessage.name) {
+      case "Webapps:GetAll:Return:OK":
+        Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window));
+        break;
+      case "Webapps:GetAll:Return:KO":
+        Services.rs.fireError(req, "DENIED");
+        break;
+      case "Webapps:Install:Return:OK":
+        if (this._oninstall) {
+          let app = msg.app;
+          let event = new WebappsApplicationEvent(new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
+                                                app.installOrigin, app.installTime));
+          this._oninstall.handleEvent(event);
+        }
+        break;
+      case "Webapps:Uninstall:Return:OK":
+        if (this._onuninstall) {
+          let event = new WebappsApplicationEvent(new WebappsApplication(this._window, msg.origin, null, null, null, null, 0));
+          this._onuninstall.handleEvent(event);
+        }
+        break;
+    }
+    this.removeRequest(msg.requestID);
+  },
+
+  classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+                                    contractID: "@mozilla.org/webapps/application-mgmt;1",
+                                    interfaces: [Ci.mozIDOMApplicationMgmt],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Webapps Registry Error"})
+                                    classDescription: "Webapps Application Mgmt"})
 }
 
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]);
+/**
+  * mozIDOMApplicationEvent object
+  */
+function WebappsApplicationEvent(aApp) {
+  this._app = aApp;
+}
+
+WebappsApplicationEvent.prototype = {
+  get application() {
+    return this._app;
+  },
+
+  classID: Components.ID("{5bc42b2a-9acc-49d5-a336-c353c8125e48}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationEvent]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+                                    contractID: "@mozilla.org/webapps/application-event;1",
+                                    interfaces: [Ci.mozIDOMApplicationEvent],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "Webapps Application Event"})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]);
--- a/dom/base/Webapps.jsm
+++ b/dom/base/Webapps.jsm
@@ -1,71 +1,42 @@
-/* ***** 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 Mobile Browser.
- *
- * The Initial Developer of the Original Code is Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Fabrice Desré <fabrice@mozilla.com>
- *   Mark Finkle <mfinkle@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 ***** */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const Cu = Components.utils; 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
 let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
 
   init: function() {
-    this.mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
     let messages = ["Webapps:Install", "Webapps:Uninstall",
-                    "Webapps:Enumerate", "Webapps:EnumerateAll", "Webapps:Launch"];
+                    "Webapps:GetSelf", "Webapps:GetInstalled",
+                    "Webapps:Launch", "Webapps:GetAll"];
 
     messages.forEach((function(msgName) {
-      this.mm.addMessageListener(msgName, this);
+      ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     let appsDir = FileUtils.getDir("ProfD", ["webapps"], true, true);
     this.appsFile = FileUtils.getFile("ProfD", ["webapps", "webapps.json"], true);
 
     if (!this.appsFile.exists())
       return;
 
@@ -109,39 +80,39 @@ let DOMApplicationRegistry = {
       Cu.reportError("DOMApplicationRegistry: Could not read from " + aFile.path + " : " + ex);
       if (aCallback)
         aCallback(null);
     }
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
-    let from = Services.io.newURI(msg.from, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    let hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
 
     switch (aMessage.name) {
       case "Webapps:Install":
         // always ask for UI to install
         Services.obs.notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
         break;
+      case "Webapps:GetSelf":
+        this.getSelf(msg);
+        break;
       case "Webapps:Uninstall":
-        if (hasPrivileges)
-          this.uninstall(msg);
+        this.uninstall(msg);
         break;
       case "Webapps:Launch":
         Services.obs.notifyObservers(this, "webapps-launch", JSON.stringify(msg));
         break;
-      case "Webapps:Enumerate":
-        this.enumerate(msg);
+      case "Webapps:GetInstalled":
+        this.getInstalled(msg);
         break;
-      case "Webapps:EnumerateAll":
-        this.enumerateAll(msg);
+      case "Webapps:GetAll":
+        if (msg.hasPrivileges)
+          this.getAll(msg);
+        else
+          ppmm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg);
         break;
     }
   },
 
   _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
     // Initialize the file output stream.
     let ostream = FileUtils.openSafeFileOutputStream(aFile);
 
@@ -157,24 +128,25 @@ let DOMApplicationRegistry = {
     });
   },
 
   // clones a app object, without the manifest
   _cloneAppObject: function(aApp) {
     let clone = {
       installOrigin: aApp.installOrigin,
       origin: aApp.origin,
-      receipt: aApp.receipt,
-      installTime: aApp.installTime
+      receipts: aApp.receipts,
+      installTime: aApp.installTime,
+      manifestURL: aApp.manifestURL
     };
     return clone;
   },
 
   denyInstall: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
+    ppmm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
   },
 
   confirmInstall: function(aData, aFromSync) {
     let app = aData.app;
     let id = app.syncId || this._appId(app.origin);
 
     // install an application again is considered as an update
     if (id) {
@@ -197,17 +169,17 @@ let DOMApplicationRegistry = {
 
     this.webapps[id] = this._cloneAppObject(app);
     delete this.webapps[id].manifest;
     this.webapps[id].installTime = (new Date()).getTime()
 
     
     if (!aFromSync)
       this._saveApps((function() {
-        this.mm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
+        ppmm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-install", id);
       }).bind(this));
   },
 
   _appId: function(aURI) {
     for (let id in this.webapps) {
       if (this.webapps[id].origin == aURI)
         return id;
@@ -239,95 +211,88 @@ let DOMApplicationRegistry = {
       if (index == aData.length - 1)
         aFinalCallback(aData);
       else
         this._readManifests(aData, aFinalCallback, index + 1);
     }).bind(this)); 
   },
 
   uninstall: function(aData) {
+    let found = false;
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin == aData.origin) {
+        found = true;
         delete this.webapps[id];
         let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
         this._saveApps((function() {
-          this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
+          ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
           Services.obs.notifyObservers(this, "webapps-sync-uninstall", id);
         }).bind(this));
       }
     }
+    if (!found)
+      ppmm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
   },
 
-  enumerate: function(aData) {
+  getSelf: function(aData) {
     aData.apps = [];
     let tmp = [];
-    let selfId;
+    let id = this._appId(aData.origin);
 
-    let id = this._appId(aData.origin);
-    // if it's an app, add itself to the result
     if (id) {
       let app = this._cloneAppObject(this.webapps[id]);
       aData.apps.push(app);
       tmp.push({ id: id });
-      selfId = id;
     }
 
-    // check if it's a store.
-    let isStore = false;
-    for (id in this.webapps) {
-      let app = this.webapps[id];
-      if (app.installOrigin == aData.origin) {
-        isStore = true;
-        break;
-      }
-    }
+    this._readManifests(tmp, (function(aResult) {
+      for (let i = 0; i < aResult.length; i++)
+        aData.apps[i].manifest = aResult[i].manifest;
+      ppmm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
+    }).bind(this));
+  },
 
-    // add all the apps from this store
-    if (isStore) {
-      for (id in this.webapps) {
-        if (id == selfId)
-          continue;
-        let app = this._cloneAppObject(this.webapps[id]);
-        if (app.installOrigin == aData.origin) {
-          aData.apps.push(app);
-          tmp.push({ id: id });
-        }
+  getInstalled: function(aData) {
+    aData.apps = [];
+    let tmp = [];
+    let id = this._appId(aData.origin);
+
+    for (id in this.webapps) {
+      if (this.webapps[id].installOrigin == aData.origin) {
+        aData.apps.push(this._cloneAppObject(this.webapps[id]));
+        tmp.push({ id: id });
       }
     }
 
     this._readManifests(tmp, (function(aResult) {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
-      this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+      ppmm.sendAsyncMessage("Webapps:GetInstalled:Return:OK", aData);
     }).bind(this));
   },
 
-  denyEnumerate: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:KO", aData);
-  },
-
-  enumerateAll: function(aData) {
+  getAll: function(aData) {
     aData.apps = [];
     let tmp = [];
 
     for (id in this.webapps) {
       let app = this._cloneAppObject(this.webapps[id]);
       aData.apps.push(app);
       tmp.push({ id: id });
     }
 
     this._readManifests(tmp, (function(aResult) {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
-      this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+      ppmm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
     }).bind(this));
   },
 
   getManifestFor: function(aOrigin, aCallback) {
     if (!aCallback)
       return;
 
     let id = this._appId(aOrigin);
@@ -363,26 +328,26 @@ let DOMApplicationRegistry = {
           continue;
         let origin = this.webapps[record.id].origin;
         delete this.webapps[record.id];
         let dir = FileUtils.getDir("ProfD", ["webapps", record.id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
-        this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin });
+        ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin });
       } else {
         if (!!this.webapps[record.id]) {
           this.webapps[record.id] = record.value;
           delete this.webapps[record.id].manifest;
         }
         else {
           let data = { app: record.value };
           this.confirmInstall(data, true);
-          this.mm.sendAsyncMessage("Webapps:Install:Return:OK", data);
+          ppmm.sendAsyncMessage("Webapps:Install:Return:OK", data);
         }
       }
     }
     this._saveApps(aCallback);
   },
 
   /*
    * May be removed once sync API change
--- a/dom/base/Webapps.manifest
+++ b/dom/base/Webapps.manifest
@@ -1,10 +1,4 @@
 # Webapps.js
 component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js
 contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270}
 category JavaScript-navigator-property mozApps @mozilla.org/webapps;1
-
-component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js
-contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22}
-
-component {b4937718-11a3-400b-a69f-ab442a418569} Webapps.js
-contract @mozilla.org/webapps/error;1 {b4937718-11a3-400b-a69f-ab442a418569}
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -543,16 +543,17 @@ static const char kDOMStringBundleURL[] 
 
 #define WINDOW_SCRIPTABLE_FLAGS                                               \
  (nsIXPCScriptable::WANT_GETPROPERTY |                                        \
   nsIXPCScriptable::WANT_PRECREATE |                                          \
   nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_ENUMERATE |                                          \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                               \
   nsIXPCScriptable::USE_STUB_EQUALITY_HOOK |                                  \
+  nsIXPCScriptable::IS_GLOBAL_OBJECT |                                        \
   nsIXPCScriptable::WANT_OUTER_OBJECT)
 
 #define NODE_SCRIPTABLE_FLAGS                                                 \
  ((DOM_DEFAULT_SCRIPTABLE_FLAGS |                                             \
    nsIXPCScriptable::USE_STUB_EQUALITY_HOOK |                                 \
    nsIXPCScriptable::WANT_ADDPROPERTY) &                                      \
   ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY)
 
@@ -1544,17 +1545,17 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TransitionEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(AnimationEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ContentFrameMessageManager, nsEventTargetSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::IS_GLOBAL_OBJECT)
 
   NS_DEFINE_CLASSINFO_DATA(FormData, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DesktopNotification, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DesktopNotificationCenter, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -139,17 +139,21 @@ static PRLogModuleInfo* gJSDiagnostics;
 #define NS_INTERSLICE_GC_DELAY      100 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       400 // ms
 
-#define NS_CC_FORCED                (5 * 60 * PR_USEC_PER_SEC) // 5 min
+// Force a CC after this long if there's anything in the purple buffer.
+#define NS_CC_FORCED                (2 * 60 * PR_USEC_PER_SEC) // 2 min
+
+// Trigger a CC if the purple buffer exceeds this size when we check it.
+#define NS_CC_PURPLE_LIMIT          250
 
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // if you add statics here, add them to the list in nsJSRuntime::Startup
 
 static nsITimer *sGCTimer;
 static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
@@ -2222,21 +2226,19 @@ nsJSContext::CreateNativeGlobalForInner(
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   if (aIsChrome) {
     nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
     ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
   }
 
   nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
   nsresult rv = xpc->
-          InitClassesWithNewWrappedGlobal(mContext,
-                                          aNewInner, NS_GET_IID(nsISupports),
+          InitClassesWithNewWrappedGlobal(mContext, aNewInner,
                                           aIsChrome ? systemPrincipal.get() : aPrincipal,
-                                          nsnull, flags,
-                                          getter_AddRefs(jsholder));
+                                          flags, getter_AddRefs(jsholder));
   if (NS_FAILED(rv)) {
     return rv;
   }
   jsholder->GetJSObject(aNativeGlobal);
   jsholder.forget(aHolder);
   return NS_OK;
 }
 
@@ -2319,17 +2321,17 @@ nsJSContext::SetOuterObject(JSObject* aO
 
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
                                                 getter_AddRefs(wrapper));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
 
-  wrapper->RefreshPrototype();
+  wrapper->FinishInitForWrappedGlobal();
   JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner));
 
   return NS_OK;
 }
 
 nsresult
 nsJSContext::InitOuterWindow()
 {
@@ -3281,56 +3283,80 @@ GCTimerFired(nsITimer *aTimer, void *aCl
 void
 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sShrinkGCBuffersTimer);
 
   nsJSContext::ShrinkGCBuffersNow();
 }
 
-// static
-void
+static bool
+ShouldTriggerCC(PRUint32 aSuspected)
+{
+  return sNeedsFullCC ||
+         aSuspected > NS_CC_PURPLE_LIMIT ||
+         sLastCCEndTime + NS_CC_FORCED < PR_Now();
+}
+
+static void
+TimerFireForgetSkippable(PRUint32 aSuspected, bool aRemoveChildless)
+{
+  PRTime startTime = PR_Now();
+  nsCycleCollector_forgetSkippable(aRemoveChildless);
+  sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
+  sCleanupSinceLastGC = true;
+  PRTime delta = PR_Now() - startTime;
+  if (sMinForgetSkippableTime > delta) {
+    sMinForgetSkippableTime = delta;
+  }
+  if (sMaxForgetSkippableTime < delta) {
+    sMaxForgetSkippableTime = delta;
+  }
+  sTotalForgetSkippableTime += delta;
+  sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
+  ++sForgetSkippableBeforeCC;
+}
+
+static void
 CCTimerFired(nsITimer *aTimer, void *aClosure)
 {
-  if (sDidShutdown) {
-    return;
-  }
-  if (sCCLockedOut) {
+  if (sDidShutdown || sCCLockedOut) {
     return;
   }
   ++sCCTimerFireCount;
-  if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) {
-    PRUint32 suspected = nsCycleCollector_suspectedCount();
-    if ((sPreviousSuspectedCount + 100) > suspected) {
-      // Just few new suspected objects, return early.
-      return;
+
+  // During early timer fires, we only run forgetSkippable. During the first
+  // late timer fire, we decide if we are going to have a second and final
+  // late timer fire, where we may run the CC.
+  const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2;
+  bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
+  PRUint32 suspected = nsCycleCollector_suspectedCount();
+  if (isLateTimerFire && ShouldTriggerCC(suspected)) {
+    if (sCCTimerFireCount == numEarlyTimerFires + 1) {
+      TimerFireForgetSkippable(suspected, true);
+      if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
+        // Our efforts to avoid a CC have failed, so we return to let the
+        // timer fire once more to trigger a CC.
+        return;
+      }
+    } else {
+      // We are in the final timer fire and still meet the conditions for
+      // triggering a CC.
+      nsJSContext::CycleCollectNow();
     }
-    
-    PRTime startTime = PR_Now();
-    nsCycleCollector_forgetSkippable();
-    sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
-    sCleanupSinceLastGC = true;
-    PRTime delta = PR_Now() - startTime;
-    if (sMinForgetSkippableTime > delta) {
-      sMinForgetSkippableTime = delta;
-    }
-    if (sMaxForgetSkippableTime < delta) {
-      sMaxForgetSkippableTime = delta;
-    }
-    sTotalForgetSkippableTime += delta;
-    sRemovedPurples += (suspected - sPreviousSuspectedCount);
-    ++sForgetSkippableBeforeCC;
-  } else {
+  } else if ((sPreviousSuspectedCount + 100) <= suspected) {
+    // Only do a forget skippable if there are more than a few new objects.
+    TimerFireForgetSkippable(suspected, false);
+  }
+
+  if (isLateTimerFire) {
+    // We have either just run the CC or decided we don't want to run the CC
+    // next time, so kill the timer.
     sPreviousSuspectedCount = 0;
     nsJSContext::KillCCTimer();
-    if (sNeedsFullCC ||
-        nsCycleCollector_suspectedCount() > 500 ||
-        sLastCCEndTime + NS_CC_FORCED < PR_Now()) {
-      nsJSContext::CycleCollectNow();
-    }
   }
 }
 
 // static
 bool
 nsJSContext::CleanupSinceLastGC()
 {
   return sCleanupSinceLastGC;
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -85,17 +85,17 @@ ConvertCloneReadInfosToArrayInternal(
 {
   JSObject* array = JS_NewArrayObject(aCx, 0, nsnull);
   if (!array) {
     NS_WARNING("Failed to make array!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (!aReadInfos.IsEmpty()) {
-    if (!JS_SetArrayLength(aCx, array, jsuint(aReadInfos.Length()))) {
+    if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) {
       NS_WARNING("Failed to set array length!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32_t index = 0, count = aReadInfos.Length(); index < count;
          index++) {
       StructuredCloneReadInfo& readInfo = aReadInfos[index];
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -405,28 +405,28 @@ IDBDatabase::CreateObjectStore(const nsA
     // Get keyPath
     jsval val = params.keyPath;
     if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) {
       if (!JSVAL_IS_PRIMITIVE(val) &&
           JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) {
     
         JSObject* obj = JSVAL_TO_OBJECT(val);
     
-        jsuint length;
+        uint32_t length;
         if (!JS_GetArrayLength(aCx, obj, &length)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
     
         if (!length) {
           return NS_ERROR_DOM_SYNTAX_ERR;
         }
     
         keyPathArray.SetCapacity(length);
     
-        for (jsuint index = 0; index < length; index++) {
+        for (uint32_t index = 0; index < length; index++) {
           jsval val;
           JSString* jsstr;
           nsDependentJSString str;
           if (!JS_GetElement(aCx, obj, index, &val) ||
               !(jsstr = JS_ValueToString(aCx, val)) ||
               !str.init(aCx, jsstr)) {
             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
           }
@@ -561,28 +561,28 @@ IDBDatabase::Transaction(const jsval& aS
   nsresult rv;
   nsTArray<nsString> storesToOpen;
 
   if (!JSVAL_IS_PRIMITIVE(aStoreNames)) {
     JSObject* obj = JSVAL_TO_OBJECT(aStoreNames);
 
     // See if this is a JS array.
     if (JS_IsArrayObject(aCx, obj)) {
-      jsuint length;
+      uint32_t length;
       if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       if (!length) {
         return NS_ERROR_DOM_INVALID_ACCESS_ERR;
       }
 
       storesToOpen.SetCapacity(length);
 
-      for (jsuint index = 0; index < length; index++) {
+      for (uint32_t index = 0; index < length; index++) {
         jsval val;
         JSString* jsstr;
         nsDependentJSString str;
         if (!JS_GetElement(aCx, obj, index, &val) ||
             !(jsstr = JS_ValueToString(aCx, val)) ||
             !str.init(aCx, jsstr)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -899,17 +899,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon
 
   JSObject* array = JS_NewArrayObject(aCx, 0, NULL);
   if (!array) {
     NS_WARNING("Failed to make array!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (!keys.IsEmpty()) {
-    if (!JS_SetArrayLength(aCx, array, jsuint(keys.Length()))) {
+    if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) {
       NS_WARNING("Failed to set array length!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32 index = 0, count = keys.Length(); index < count; index++) {
       const Key& key = keys[index];
       NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -613,22 +613,22 @@ IDBObjectStore::AppendIndexUpdateInfo(PR
 
   jsval key;
   rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) &&
       JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) {
     JSObject* array = JSVAL_TO_OBJECT(key);
-    jsuint arrayLength;
+    uint32_t arrayLength;
     if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    for (jsuint arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
+    for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
       jsval arrayItem;
       if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       Key value;
       if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
           value.IsUnset()) {
@@ -1695,28 +1695,28 @@ IDBObjectStore::CreateIndex(const nsAStr
   nsTArray<nsString> keyPathArray;
 
   // See if this is a JS array.
   if (!JSVAL_IS_PRIMITIVE(aKeyPath) &&
       JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aKeyPath))) {
 
     JSObject* obj = JSVAL_TO_OBJECT(aKeyPath);
 
-    jsuint length;
+    uint32_t length;
     if (!JS_GetArrayLength(aCx, obj, &length)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     if (!length) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
     keyPathArray.SetCapacity(length);
 
-    for (jsuint index = 0; index < length; index++) {
+    for (uint32_t index = 0; index < length; index++) {
       jsval val;
       JSString* jsstr;
       nsDependentJSString str;
       if (!JS_GetElement(aCx, obj, index, &val) ||
           !(jsstr = JS_ValueToString(aCx, val)) ||
           !str.init(aCx, jsstr)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -165,22 +165,22 @@ Key::EncodeJSVal(JSContext* aCx, const j
       if (aTypeOffset == eMaxType * MaxArrayCollapse) {
         mBuffer.Append(aTypeOffset);
         aTypeOffset = 0;
       }
       NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
                    aTypeOffset < (eMaxType * MaxArrayCollapse),
                    "Wrong typeoffset");
 
-      jsuint length;
+      uint32_t length;
       if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
-      for (jsuint index = 0; index < length; index++) {
+      for (uint32_t index = 0; index < length; index++) {
         jsval val;
         if (!JS_GetElement(aCx, obj, index, &val)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         nsresult rv = EncodeJSVal(aCx, val, aTypeOffset);
         NS_ENSURE_SUCCESS(rv, rv);
 
@@ -215,17 +215,17 @@ Key::DecodeJSVal(const unsigned char*& a
 
     aTypeOffset += eMaxType;
 
     if (aTypeOffset == eMaxType * MaxArrayCollapse) {
       ++aPos;
       aTypeOffset = 0;
     }
 
-    jsuint index = 0;
+    uint32_t index = 0;
     while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
       jsval val;
       nsresult rv = DecodeJSVal(aPos, aEnd, aCx, aTypeOffset, &val);
       NS_ENSURE_SUCCESS(rv, rv);
 
       aTypeOffset = 0;
 
       if (!JS_SetElement(aCx, array, index++, &val)) {
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -31,109 +31,84 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
+#include "nsIArray.idl"
+#include "nsIDOMEvent.idl"
+#include "nsIDOMEventTarget.idl"
 
-[scriptable, uuid(e0c271cb-266b-48c9-a7e4-96590b445c26)]
-interface mozIDOMApplicationRegistryError : nsISupports
-{
-  const unsigned short DENIED = 1;
-  const unsigned short PERMISSION_DENIED = 2;
-  const unsigned short MANIFEST_URL_ERROR = 3;
-  const unsigned short NETWORK_ERROR = 4;
-  const unsigned short MANIFEST_PARSE_ERROR = 5;
-  const unsigned short INVALID_MANIFEST = 6;
+interface nsIDOMDOMRequest;
 
-  readonly attribute short code;
-};
-
-[scriptable, uuid(a6856a3d-dece-43ce-89b9-72dba07f4246)]
-interface mozIDOMApplication : nsISupports
+[scriptable, uuid(b70b84f1-7ac9-4a92-bc32-8b6a7eb7879e)]
+interface mozIDOMApplication  : nsISupports
 {
   readonly attribute jsval manifest;
-  readonly attribute DOMString receipt;
+  readonly attribute DOMString manifestURL;
+  readonly attribute nsIArray receipts; /* an array of strings */
   readonly attribute DOMString origin;
   readonly attribute DOMString installOrigin;
   readonly attribute unsigned long installTime;
+  
+  /* startPoint will be used when several launch_path exists for an app */
+  nsIDOMDOMRequest launch([optional] in DOMString startPoint);
+  nsIDOMDOMRequest uninstall(); 
+};
+
+[scriptable, uuid(870bfbdc-3e13-4042-99dd-18e25720782d)]
+interface mozIDOMApplicationEvent : nsIDOMEvent
+{
+  readonly attribute mozIDOMApplication application;
 };
 
-[scriptable, function, uuid(be170df5-9154-463b-9197-10a6195eba52)]
-interface mozIDOMApplicationRegistryEnumerateCallback : nsISupports
+[scriptable, uuid(a82771f6-ba46-4073-9e6e-f1ad3f42b1f6)]
+interface mozIDOMApplicationMgmt : nsIDOMEventTarget
 {
-  void handleEvent([array, size_is(count)] in mozIDOMApplication apps,
-                    in unsigned long count);
+  /**
+   * the request will return the all the applications installed. Only accessible
+   * to privileged callers.
+   */
+  nsIDOMDOMRequest getAll();
+
+  /**
+   * event listener to get notified of application installs. Only settable by
+   * privileged callers.
+   * the event will be a mozIDOMApplicationEvent
+   */
+  attribute nsIDOMEventListener oninstall;
+
+  /**
+   * event listener to get notified of application uninstalls. Only settable by
+   * privileged callers.
+   * the event will be a mozIDOMApplicationEvent
+   */
+  attribute nsIDOMEventListener onuninstall;
 };
 
-[scriptable, function, uuid(ae0ed33d-35cf-443a-837b-a6cebf16bd49)]
-interface mozIDOMApplicationRegistryErrorCallback : nsISupports
-{
-  void handleEvent(in mozIDOMApplicationRegistryError error);
-};
-
-[scriptable, uuid(ac63c0ba-1f33-4e3e-b9aa-6a3243a9adba)]
+[scriptable, uuid(f6929871-288b-4613-9a37-9a150760ac50)]
 interface mozIDOMApplicationRegistry : nsISupports
 {
   /**
    * Install a web app. onerror can be used to report errors,
    * and oninstall if the caller is privileged.
    *
-   * @param manifestUrl : the URL of the webapps manifest
-   * @param receipt : An opaque string used to track payment status
-   */
-  void install(in DOMString manifestUrl,
-	       [optional] in DOMString receipt);
-
-  /**
-   * This call is only accessible to privileged callers.
-   *
-   * @param origin : the origin of the application to uninstall.
+   * @param manifestUrl : the URL of the webapps manifest.
+   * @param parameters : A structure with optional information. 
+   *                     { receipts: ... } will be used to specify the payment receipts for this installation. 
    */
-  void uninstall(in DOMString origin);
-
-  /**
-   * Enumerate apps : either return itself if caller is an app, or
-   * apps installed from a store if caller is a store
-   *
-   * @param success: the callback that will get the array of applications
-   * @param error: optional error callback
-   */
-  void enumerate(in mozIDOMApplicationRegistryEnumerateCallback success,
-		 [optional] in mozIDOMApplicationRegistryErrorCallback error);
+  nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval parameters);
 
   /**
-   * Enumerate all apps installed from all stores.
-   * Only usable by privileged callers.
-   *
-   * @param success: the callback that will get the array of applications
-   * @param error: optional error callback
+   * the request will return the application currently installed, or null.
    */
-  void enumerateAll(in mozIDOMApplicationRegistryEnumerateCallback success,
-		 [optional] in mozIDOMApplicationRegistryErrorCallback error);
-
-  /**
-   * launch a webapp. Behavior is application dependant.
-   *
-   * @param origin : the origin of the application to launch
-   */
-  void launch(in DOMString origin);
+  nsIDOMDOMRequest getSelf();
 
   /**
-   * event listener to get notified of application installs. Only settable by
-   * privileged callers
+   * the request will return the applications installed from this origin, or null.
    */
-  attribute nsIDOMEventListener oninstall;
+  nsIDOMDOMRequest getInstalled();
 
-  /**
-   * event listener to get notified of application uninstalls. Only settable by
-   * privileged callers
-   */
-  attribute nsIDOMEventListener onuninstall;
-
-  /**
-   * event listener to get notified of errors. 
-   */
-  attribute nsIDOMEventListener onerror;
+  readonly attribute mozIDOMApplicationMgmt mgmt;
 };
--- a/dom/sms/src/SmsFilter.cpp
+++ b/dom/sms/src/SmsFilter.cpp
@@ -185,22 +185,22 @@ SmsFilter::SetNumbers(JSContext* aCx, co
     return NS_ERROR_INVALID_ARG;
   }
 
   JSObject& obj = aNumbers.toObject();
   if (!JS_IsArrayObject(aCx, &obj)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  jsuint size;
+  uint32_t size;
   JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, &obj, &size));
 
   nsTArray<nsString> numbers;
 
-  for (jsuint i=0; i<size; ++i) {
+  for (uint32_t i=0; i<size; ++i) {
     jsval jsNumber;
     if (!JS_GetElement(aCx, &obj, i, &jsNumber)) {
       return NS_ERROR_INVALID_ARG;
     }
 
     if (!jsNumber.isString()) {
       return NS_ERROR_INVALID_ARG;
     }
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -194,22 +194,22 @@ SmsManager::Send(const jsval& aNumber, c
 
   if (aNumber.isString()) {
     return Send(cx, global, aNumber.toString(), aMessage, aReturn);
   }
 
   // Must be an array then.
   JSObject& numbers = aNumber.toObject();
 
-  jsuint size;
+  uint32_t size;
   JS_ALWAYS_TRUE(JS_GetArrayLength(cx, &numbers, &size));
 
   jsval* requests = new jsval[size];
 
-  for (jsuint i=0; i<size; ++i) {
+  for (uint32_t i=0; i<size; ++i) {
     jsval number;
     if (!JS_GetElement(cx, &numbers, i, &number)) {
       return NS_ERROR_INVALID_ARG;
     }
 
     nsresult rv = Send(cx, global, number.toString(), aMessage, &requests[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
--- a/dom/sms/src/ril/SmsDatabaseService.js
+++ b/dom/sms/src/ril/SmsDatabaseService.js
@@ -6,46 +6,618 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const RIL_SMSDATABASESERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsdatabaseservice;1";
 const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d5e89249}");
 
+const DEBUG = true;
+const DB_NAME = "sms";
+const DB_VERSION = 1;
+const STORE_NAME = "sms";
+
+const DELIVERY_SENT = "sent";
+const DELIVERY_RECEIVED = "received";
+
+const FILTER_TIMESTAMP = "timestamp";
+const FILTER_NUMBERS = "numbers";
+const FILTER_DELIVERY = "delivery";
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
+                                   "@mozilla.org/sms/smsrequestmanager;1",
+                                   "nsISmsRequestManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
+                                   "@mozilla.org/dom/indexeddb/manager;1",
+                                   "nsIIndexedDatabaseManager");
+
+const GLOBAL_SCOPE = this;
+
 /**
  * SmsDatabaseService
  */
 function SmsDatabaseService() {
+  gIDBManager.initWindowless(GLOBAL_SCOPE);
+
+  let that = this;
+  this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function(error, txn, store){
+    if (error) {
+      return;
+    }
+    // In order to get the highest key value, we open a key cursor in reverse
+    // order and get only the first pointed value.
+    let request = store.openCursor(null, Ci.nsIIDBCursor.PREV);
+    request.onsuccess = function onsuccess(event) {
+      let cursor = event.target.result;
+      if (!cursor) {
+        if (DEBUG) {
+          debug("Could not get the last key from sms database. " +
+                "Probably empty database");
+        }
+        return;
+      }
+      that.lastKey = cursor.key || 0;
+      if (DEBUG) debug("Last assigned message ID was " + that.lastKey);
+    };
+    request.onerror = function onerror(event) {
+      if (DEBUG) {
+        debug("Could not get the last key from sms database " +
+              event.target.errorCode);
+      }
+    };
+  });
+
+  this.messageLists = {};
 }
 SmsDatabaseService.prototype = {
 
   classID:   RIL_SMSDATABASESERVICE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISmsDatabaseService]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISmsDatabaseService,
+                                         Ci.nsIObserver]),
+
+  /**
+   * Cache the DB here.
+   */
+  db: null,
+
+  /**
+   * This object keeps the message lists associated with each search. Each
+   * message list is stored as an array of primary keys.
+   */
+  messageLists: null,
+
+  lastMessageListId: 0,
+
+  /**
+   * Last key value stored in the database.
+   */
+  lastKey: 0,
+
+  /**
+   * nsIObserver
+   */
+  observe: function observe() {},
+
+  /**
+   * Prepare the database. This may include opening the database and upgrading
+   * it to the latest schema version.
+   *
+   * @param callback
+   *        Function that takes an error and db argument. It is called when
+   *        the database is ready to use or if an error occurs while preparing
+   *        the database.
+   *
+   * @return (via callback) a database ready for use.
+   */
+  ensureDB: function ensureDB(callback) {
+    if (this.db) {
+      if (DEBUG) debug("ensureDB: already have a database, returning early.");
+      callback(null, this.db);
+      return;
+    }
+
+    let self = this;
+    function gotDB(db) {
+      self.db = db;
+      callback(null, db);
+    }
+
+    let request = GLOBAL_SCOPE.mozIndexedDB.open(DB_NAME, DB_VERSION);
+    request.onsuccess = function (event) {
+      if (DEBUG) debug("Opened database:", DB_NAME, DB_VERSION);
+      gotDB(event.target.result);
+    };
+    request.onupgradeneeded = function (event) {
+      if (DEBUG) {
+        debug("Database needs upgrade:", DB_NAME,
+              event.oldVersion, event.newVersion);
+        debug("Correct new database version:", event.newVersion == DB_VERSION);
+      }
+
+      let db = event.target.result;
+
+      switch (event.oldVersion) {
+        case 0:
+          if (DEBUG) debug("New database");
+          self.createSchema(db);
+          break;
+
+        default:
+          event.target.transaction.abort();
+          callback("Old database version: " + event.oldVersion, null);
+          break;
+      }
+    };
+    request.onerror = function (event) {
+      //TODO look at event.target.Code and change error constant accordingly
+      callback("Error opening database!", null);
+    };
+    request.onblocked = function (event) {
+      callback("Opening database request is blocked.", null);
+    };
+  },
 
-  // nsISmsDatabaseService
+  /**
+   * Start a new transaction.
+   *
+   * @param txn_type
+   *        Type of transaction (e.g. IDBTransaction.READ_WRITE)
+   * @param callback
+   *        Function to call when the transaction is available. It will
+   *        be invoked with the transaction and the 'sms' object store.
+   */
+  newTxn: function newTxn(txn_type, callback) {
+    this.ensureDB(function (error, db) {
+      if (error) {
+        if (DEBUG) debug("Could not open database: " + error);
+        callback(error);
+        return;
+      }
+      let txn = db.transaction([STORE_NAME], txn_type);
+      if (DEBUG) debug("Started transaction " + txn + " of type " + txn_type);
+      if (DEBUG) {
+        txn.oncomplete = function oncomplete(event) {
+          debug("Transaction " + txn + " completed.");
+        };
+        txn.onerror = function onerror(event) {
+          //TODO check event.target.errorCode and show an appropiate error
+          //     message according to it.
+          debug("Error occurred during transaction: " + event.target.errorCode);
+        };
+      }
+      if (DEBUG) debug("Retrieving object store", STORE_NAME);
+      let store = txn.objectStore(STORE_NAME);
+      callback(null, txn, store);
+    });
+  },
+
+  /**
+   * Create the initial database schema.
+   *
+   * TODO need to worry about number normalization somewhere...
+   * TODO full text search on body???
+   * TODO We probably want to add a 'read' index
+   */
+  createSchema: function createSchema(db) {
+    let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" });
+    objectStore.createIndex("id", "id", { unique: true });
+    objectStore.createIndex("delivery", "delivery", { unique: false });
+    objectStore.createIndex("sender", "sender", { unique: false });
+    objectStore.createIndex("receiver", "receiver", { unique: false });
+    objectStore.createIndex("timestamp", "timestamp", { unique:false });
+    if (DEBUG) debug("Created object stores and indexes");
+  },
+
+  /**
+   * Helper function to make the intersection of the partial result arrays
+   * obtained within createMessageList.
+   *
+   * @param keys
+   *        Object containing the partial result arrays.
+   * @param fiter
+   *        Object containing the filter search criteria used to retrieved the
+   *        partial results.
+   *
+   * return Array of keys containing the final result of createMessageList.
+   */
+  keyIntersection: function keyIntersection(keys, filter) {
+    let result = keys[FILTER_TIMESTAMP];
+    if (keys[FILTER_NUMBERS].length || filter.numbers) {
+      result = keys[FILTER_NUMBERS].filter(function(i) {
+        return result.indexOf(i) != -1;
+      });
+    }
+    if (keys[FILTER_DELIVERY].length || filter.delivery) {
+      result = keys[FILTER_DELIVERY].filter(function(i) {
+        return result.indexOf(i) != -1;
+      });
+    }
+    return result;
+  },
+
+  /**
+   * Helper function called after createMessageList gets the final result array
+   * containing the list of primary keys of records that matches the provided
+   * search criteria. This function retrieves from the store the message with
+   * the primary key matching the first one in the message list array and keeps
+   * the rest of this array in memory. It also notifies via gSmsRequestManager.
+   *
+   * @param messageList
+   *        Array of primary keys retrieved within createMessageList.
+   * @param requestId
+   *        Id used by the SmsRequestManager
+   */
+  onMessageListCreated: function onMessageListCreated(messageList, requestId) {
+    if (DEBUG) debug("Message list created: " + messageList);
+    let self = this;
+    self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        gSmsRequestManager.notifyReadMessageListFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+
+      let messageId = messageList.shift();
+      if (DEBUG) debug ("Fetching message " + messageId);
+      let request = store.get(messageId);
+      let message;
+      request.onsuccess = function (event) {
+        message = request.result;
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (!message) {
+          gSmsRequestManager.notifyReadMessageListFailed(
+            requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+          return;
+        }
+        self.lastMessageListId += 1;
+        self.messageLists[self.lastMessageListId] = messageList;
+        let sms = gSmsService.createSmsMessage(message.id,
+                                               message.delivery,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.timestamp);
+        gSmsRequestManager.notifyCreateMessageList(requestId,
+                                                   self.lastMessageListId,
+                                                   sms);
+      };
+    });
+  },
+
+  saveMessage: function saveMessage(message) {
+    this.lastKey += 1;
+    message.id = this.lastKey;
+    if (DEBUG) debug("Going to store " + JSON.stringify(message));
+    this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function(error, txn, store) {
+      if (error) {
+        return;
+      }
+      let request = store.put(message);
+    });
+    // We return the key that we expect to store in the db
+    return message.id;
+  },
+
+
+  /**
+   * nsISmsDatabaseService API
+   */
 
   saveReceivedMessage: function saveReceivedMessage(sender, body, date) {
-    return -1;
+    let message = {delivery:  DELIVERY_RECEIVED,
+                   sender:    sender,
+                   receiver:  null,  //TODO see bug 733266
+                   body:      body,
+                   timestamp: date};
+    return this.saveMessage(message);
   },
 
   saveSentMessage: function saveSentMessage(receiver, body, date) {
-    return -1;
+    let message = {delivery:  DELIVERY_SENT,
+                   sender:    null, //TODO see bug 733266
+                   receiver:  receiver,
+                   body:      body,
+                   timestamp: date};
+    return this.saveMessage(message);
   },
 
   getMessage: function getMessage(messageId, requestId) {
+    if (DEBUG) debug("Retrieving message with ID " + messageId);
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        if (DEBUG) debug(error);
+        gSmsRequestManager.notifyGetSmsFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+      let request = store.getAll(messageId);
+
+      txn.oncomplete = function oncomplete() {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (request.result.length > 1) {
+          if (DEBUG) debug("Got too many results for id " + messageId);
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
+          return;
+        }
+        let data = request.result[0];
+        if (!data) {
+          if (DEBUG) debug("Message ID " + messageId + " not found");
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+          return;
+        }
+        if (data.id != messageId) {
+          if (DEBUG) {
+            debug("Requested message ID (" + messageId + ") is " +
+                  "different from the one we got");
+          }
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
+          return;
+        }
+        let message = gSmsService.createSmsMessage(data.id,
+                                                   data.delivery,
+                                                   data.sender,
+                                                   data.receiver,
+                                                   data.body,
+                                                   data.timestamp);
+        gSmsRequestManager.notifyGotSms(requestId, message);
+      };
+
+      txn.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
+        //TODO look at event.target.errorCode, pick appropriate error constant
+        gSmsRequestManager.notifyGetSmsFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   deleteMessage: function deleteMessage(messageId, requestId) {
+    let self = this;
+    this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (error, txn, store) {
+      if (error) {
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+      let request = store.delete(messageId);
+
+      request.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on request ", event.target.errorCode);
+        //TODO look at event.target.errorCode
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        // Once we transaction is done, we need to check if we actually deleted
+        // the message. As IndexedDB does not provide the affected records info,
+        // we need to try to get the message from the database again to check
+        // that it is actually gone.
+        self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+          let request = store.getAll(messageId);
+          request.onsuccess = function onsuccess(event) {
+            let deleted = (event.target.result.length == 0);
+            gSmsRequestManager.notifySmsDeleted(requestId, deleted);
+          };
+          request.onerror = function onerror(event) {
+            if (DEBUG) {
+              debug("Error checking the message deletion " +
+                    event.target.errorCode);
+            }
+            //TODO should we notify here as an internal error? The failed check
+            //     does not mean that the deletion has failed, so maybe we
+            //     should notify successfully.
+            gSmsRequestManager.notifySmsDeleteFailed(
+              requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+          };
+        });
+      };
+
+      txn.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
+        //TODO look at event.target.errorCode, pick appropriate error constant
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   createMessageList: function createMessageList(filter, reverse, requestId) {
+    if (DEBUG) {
+      debug("Creating a message list. Filters:" +
+            " startDate: " + filter.startDate +
+            " endDate: " + filter.endDate +
+            " delivery: " + filter.delivery +
+            " numbers: " + filter.numbers +
+            " reverse: " + reverse);
+    }
+    // This object keeps the lists of keys retrieved by the search specific to
+    // each nsIMozSmsFilter. Once all the keys have been retrieved from the
+    // store, the final intersection of this arrays will contain all the
+    // keys for the message list that we are creating.
+    let filteredKeys = {};
+    filteredKeys[FILTER_TIMESTAMP] = [];
+    filteredKeys[FILTER_NUMBERS] = [];
+    filteredKeys[FILTER_DELIVERY] = [];
+
+    // Callback function to iterate through request results via IDBCursor.
+    let successCb = function onsuccess(result, filter) {
+      // Once the cursor has retrieved all keys that matches its key range,
+      // the filter search is done.
+      if (!result) {
+        if (DEBUG) {
+          debug("These messages match the " + filter + " filter: " +
+                filteredKeys[filter]);
+      }
+        return;
+      }
+      // The cursor primaryKey is stored in its corresponding partial array
+      // according to the filter parameter.
+      let primaryKey = result.primaryKey;
+      filteredKeys[filter].push(primaryKey);
+      result.continue();
+    };
+
+    let errorCb = function onerror(event) {
+      //TODO look at event.target.errorCode, pick appropriate error constant.
+      if (DEBUG) debug("IDBRequest error " + event.target.errorCode);
+      gSmsRequestManager.notifyReadMessageListFailed(
+        requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      return;
+    };
+
+    let self = this;
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        errorCb(error);
+        return;
+      }
+
+      // In first place, we retrieve the keys that match the filter.startDate
+      // and filter.endDate search criteria.
+      let timeKeyRange = null;
+      if (!filter.startDate != null && filter.endDate != null) {
+        timeKeyRange = IDBKeyRange.bound(filter.startDate.getTime(),
+                                         filter.endDate.getTime());
+      } else if (filter.startDate != null) {
+        timeKeyRange = IDBKeyRange.lowerBound(filter.startDate.getTime());
+      } else if (filter.endDate != null) {
+        timeKeyRange = IDBKeyRange.upperBound(filter.endDate.getTime());
+      }
+      let direction = reverse ? Ci.nsIIDBCursor.PREV : Ci.nsIIDBCursor.NEXT;
+      let timeRequest = store.index("timestamp").openKeyCursor(timeKeyRange,
+                                                               direction);
+
+      timeRequest.onsuccess = function onsuccess(event) {
+        successCb(event.target.result, FILTER_TIMESTAMP);
+      };
+      timeRequest.onerror = errorCb;
+
+      // Retrieve the keys from the 'delivery' index that matches the
+      // value of filter.delivery.
+      if (filter.delivery) {
+        let deliveryKeyRange = IDBKeyRange.only(filter.delivery);
+        let deliveryRequest = store.index("delivery")
+                                   .openKeyCursor(deliveryKeyRange);
+        deliveryRequest.onsuccess = function onsuccess(event) {
+          successCb(event.target.result, FILTER_DELIVERY);
+        };
+        deliveryRequest.onerror = errorCb;
+      }
+
+      // Retrieve the keys from the 'sender' and 'receiver' indexes that
+      // match the values of filter.numbers
+      if (filter.numbers) {
+        for (let i = 0; i < filter.numbers.length; i++) {
+          let numberKeyRange = IDBKeyRange.only(filter.numbers[i]);
+          let senderRequest = store.index("sender")
+                                   .openKeyCursor(numberKeyRange);
+          let receiverRequest = store.index("receiver")
+                                     .openKeyCursor(numberKeyRange);
+          senderRequest.onsuccess = receiverRequest.onsuccess =
+            function onsuccess(event){
+              successCb(event.target.result, FILTER_NUMBERS);
+            };
+          senderRequest.onerror = receiverRequest.onerror = errorCb;
+        }
+      }
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        // We need to get the intersection of all the partial searches to
+        // get the final result array.
+        let result =  self.keyIntersection(filteredKeys, filter);
+        if (!result.length) {
+          if (DEBUG) debug("No messages matching the filter criteria");
+          gSmsRequestManager.notifyNoMessageInList(requestId);
+          return;
+        }
+
+        // At this point, filteredKeys should have all the keys that matches
+        // all the search filters. So we take the first key and retrieve the
+        // corresponding message. The rest of the keys are added to the
+        // messageLists object as a new list.
+        self.onMessageListCreated(result, requestId);
+      };
+
+      txn.onerror = function onerror(event) {
+        errorCb(event);
+      };
+    });
   },
 
   getNextMessageInList: function getNextMessageInList(listId, requestId) {
+    if (DEBUG) debug("Getting next message in list " + listId);
+    let messageId;
+    let list = this.messageLists[listId];
+    if (!list) {
+      if (DEBUG) debug("Wrong list id");
+      gSmsRequestManager.notifyReadMessageListFailed(
+        requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+      return;
+    }
+    messageId = list.shift();
+    if (messageId == null) {
+      if (DEBUG) debug("Reached the end of the list!");
+      gSmsRequestManager.notifyNoMessageInList(requestId);
+      return;
+    }
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (DEBUG) debug("Fetching message " + messageId);
+      let request = store.get(messageId);
+      let message;
+      request.onsuccess = function onsuccess(event) {
+        message = request.result;
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (!message) {
+          if (DEBUG) debug("Could not get message id " + messageId);
+          gSmsRequestManager.notifyReadMessageListFailed(
+            requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+        }
+        let sms = gSmsService.createSmsMessage(message.id,
+                                               message.delivery,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.timestamp);
+        gSmsRequestManager.notifyGotNextMessage(requestId, sms);
+      };
+
+      txn.onerror = function onerror(event) {
+        //TODO check event.target.errorCode
+        if (DEBUG) {
+          debug("Error retrieving message id: " + messageId +
+                ". Error code: " + event.target.errorCode);
+        }
+        gSmsRequestManager.notifyReadMessageListFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   clearMessageList: function clearMessageList(listId) {
+    if (DEBUG) debug("Clearing message list: " + listId);
+    delete this.messageLists[listId];
   }
 
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsDatabaseService]);
+
+function debug() {
+  dump("SmsDatabaseService: " + Array.slice(arguments).join(" ") + "\n");
+}
--- a/dom/system/b2g/RadioInterfaceLayer.js
+++ b/dom/system/b2g/RadioInterfaceLayer.js
@@ -64,16 +64,20 @@ const DOM_SMS_DELIVERY_SENT             
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/smsservice;1",
                                    "nsISmsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
                                    "@mozilla.org/sms/smsrequestmanager;1",
                                    "nsISmsRequestManager");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsDatabaseService",
+                                   "@mozilla.org/sms/rilsmsdatabaseservice;1",
+                                   "nsISmsDatabaseService");
+
 function convertRILCallState(state) {
   switch (state) {
     case RIL.CALL_STATE_ACTIVE:
       return nsIRadioInterfaceLayer.CALL_STATE_CONNECTED;
     case RIL.CALL_STATE_HOLDING:
       return nsIRadioInterfaceLayer.CALL_STATE_HELD;
     case RIL.CALL_STATE_DIALING:
       return nsIRadioInterfaceLayer.CALL_STATE_DIALING;
@@ -303,34 +307,39 @@ RadioInterfaceLayer.prototype = {
       }
       if (!keepGoing) {
         break;
       }
     }
   },
 
   handleSmsReceived: function handleSmsReceived(message) {
-    //TODO: put the sms into a database, assign it a proper id, yada yada
-    let sms = gSmsService.createSmsMessage(-1,
+    debug("handleSmsReceived: " + JSON.stringify(message));
+    let id = gSmsDatabaseService.saveReceivedMessage(message.sender || null,
+                                                     message.body || null,
+                                                     message.timestamp);
+    let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_RECEIVED,
                                            message.sender || null,
                                            message.receiver || null,
                                            message.body || null,
                                            message.timestamp);
     Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
   },
 
   handleSmsSent: function handleSmsSent(message) {
-    let sms = gSmsService.createSmsMessage(-1,
+    debug("handleSmsSent: " + JSON.stringify(message));
+    let timestamp = Date.now();
+    let id = gSmsDatabaseService.saveSentMessage(message.number, message.body, timestamp);
+    let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_SENT,
                                            null,
                                            message.number,
                                            message.body,
-                                           Date.now());
-    //TODO At this point we should save the sms into the DB (bug 712809)
+                                           timestamp);
     //TODO handle errors (bug 727319)
     gSmsRequestManager.notifySmsSent(message.requestId, sms);
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
--- a/dom/system/b2g/ril_consts.js
+++ b/dom/system/b2g/ril_consts.js
@@ -331,90 +331,677 @@ const PDU_DCS_MSG_CODING_8BITS_ALPHABET 
 const PDU_DCS_MSG_CODING_16BITS_ALPHABET= 0x08;
 const PDU_DCS_MSG_CLASS_ME_SPECIFIC     = 0xF1;
 const PDU_DCS_MSG_CLASS_SIM_SPECIFIC    = 0xF2;
 const PDU_DCS_MSG_CLASS_TE_SPECIFIC     = 0xF3;
 
 // Because service center timestamp omit the century. Yay.
 const PDU_TIMESTAMP_YEAR_OFFSET = 2000;
 
-// 7bit Default Alphabet
-//TODO: maybe convert this to a string? might be faster/cheaper
-const PDU_ALPHABET_7BIT_DEFAULT = [
-  "@",      // COMMERCIAL AT
-  "\xa3",   // POUND SIGN
-  "$",      // DOLLAR SIGN
-  "\xa5",   // YEN SIGN
-  "\xe8",   // LATIN SMALL LETTER E WITH GRAVE
-  "\xe9",   // LATIN SMALL LETTER E WITH ACUTE
-  "\xf9",   // LATIN SMALL LETTER U WITH GRAVE
-  "\xec",   // LATIN SMALL LETTER I WITH GRAVE
-  "\xf2",   // LATIN SMALL LETTER O WITH GRAVE
-  "\xc7",   // LATIN CAPITAL LETTER C WITH CEDILLA
-  "\n",     // LINE FEED
-  "\xd8",   // LATIN CAPITAL LETTER O WITH STROKE
-  "\xf8",   // LATIN SMALL LETTER O WITH STROKE
-  "\r",     // CARRIAGE RETURN
-  "\xc5",   // LATIN CAPITAL LETTER A WITH RING ABOVE
-  "\xe5",   // LATIN SMALL LETTER A WITH RING ABOVE
-  "\u0394", // GREEK CAPITAL LETTER DELTA
-  "_",      // LOW LINE
-  "\u03a6", // GREEK CAPITAL LETTER PHI
-  "\u0393", // GREEK CAPITAL LETTER GAMMA
-  "\u039b", // GREEK CAPITAL LETTER LAMBDA
-  "\u03a9", // GREEK CAPITAL LETTER OMEGA
-  "\u03a0", // GREEK CAPITAL LETTER PI
-  "\u03a8", // GREEK CAPITAL LETTER PSI
-  "\u03a3", // GREEK CAPITAL LETTER SIGMA
-  "\u0398", // GREEK CAPITAL LETTER THETA
-  "\u039e", // GREEK CAPITAL LETTER XI
-  "\u20ac", // (escape to extension table)
-  "\xc6",   // LATIN CAPITAL LETTER AE
-  "\xe6",   // LATIN SMALL LETTER AE
-  "\xdf",   // LATIN SMALL LETTER SHARP S (German)
-  "\xc9",   // LATIN CAPITAL LETTER E WITH ACUTE
-  " ",      // SPACE
-  "!",      // EXCLAMATION MARK
-  "\"",     // QUOTATION MARK
-  "#",      // NUMBER SIGN
-  "\xa4",   // CURRENCY SIGN
-  "%",      // PERCENT SIGN
-  "&",      // AMPERSAND
-  "'",      // APOSTROPHE
-  "(",      // LEFT PARENTHESIS
-  ")",      // RIGHT PARENTHESIS
-  "*",      // ASTERISK
-  "+",      // PLUS SIGN
-  ",",      // COMMA
-  "-",      // HYPHEN-MINUS
-  ".",      // FULL STOP
-  "/",      // SOLIDUS (SLASH)
-  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-  ":",      // COLON
-  ";",      // SEMICOLON
-  "<",      // LESS-THAN SIGN
-  "=",      // EQUALS SIGN
-  ">",      // GREATER-THAN SIGN
-  "?",      // QUESTION MARK
-  "\xa1",   // INVERTED EXCLAMATION MARK
-  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
-  "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
-  "\xc4",   // LATIN CAPITAL LETTER A WITH DIAERESIS
-  "\xd6",   // LATIN CAPITAL LETTER O WITH DIAERESIS
-  "\xd1",   // LATIN CAPITAL LETTER N WITH TILDE
-  "\xdc",   // LATIN CAPITAL LETTER U WITH DIAERESIS
-  "\xa7",   // SECTION SIGN
-  "\xbf",   // INVERTED QUESTION MARK
-  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
-  "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
-  "\xe4",   // LATIN SMALL LETTER A WITH DIAERESIS
-  "\xf6",   // LATIN SMALL LETTER O WITH DIAERESIS
-  "\xf1",   // LATIN SMALL LETTER N WITH TILDE
-  "\xfc",   // LATIN SMALL LETTER U WITH DIAERESIS
-  "\xe0"    // LATIN SMALL LETTER A WITH GRAVE
+// See 9.2.3.24 TP‑User Data (TP‑UD)
+const PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT         = 0x00;
+const PDU_IEI_SPECIAL_SMS_MESSAGE_INDICATION           = 0x01;
+const PDU_IEI_APPLICATION_PORT_ADDREESING_SCHEME_8BIT  = 0x04;
+const PDU_IEI_APPLICATION_PORT_ADDREESING_SCHEME_16BIT = 0x05;
+const PDU_IEI_SMSC_CONTROL_PARAMS                      = 0x06;
+const PDU_IEI_UDH_SOURCE_INDICATOR                     = 0x07;
+const PDU_IEI_CONCATENATED_SHORT_MESSAGES_16BIT        = 0x08;
+const PDU_IEI_WIRELESS_CONTROL_MESSAGE_PROTOCOL        = 0x09;
+const PDU_IEI_TEXT_FORMATING                           = 0x0A;
+const PDU_IEI_PREDEFINED_SOUND                         = 0x0B;
+const PDU_IEI_USER_DATA_SOUND                          = 0x0C;
+const PDU_IEI_PREDEFINED_ANIMATION                     = 0x0D;
+const PDU_IEI_LARGE_ANIMATION                          = 0x0E;
+const PDU_IEI_SMALL_ANIMATION                          = 0x0F;
+const PDU_IEI_LARGE_PICTURE                            = 0x10;
+const PDU_IEI_SMALL_PICTURE                            = 0x11;
+const PDU_IEI_VARIABLE_PICTURE                         = 0x12;
+const PDU_IEI_USER_PROMPT_INDICATOR                    = 0x13;
+const PDU_IEI_EXTENDED_OBJECT                          = 0x14;
+const PDU_IEI_REUSED_EXTENDED_OBJECT                   = 0x15;
+const PDU_IEI_COMPRESS_CONTROL                         = 0x16;
+const PDU_IEI_OBJECT_DISTRIBUTION_INDICATOR            = 0x17;
+const PDU_IEI_STANDARD_WVG_OBJECT                      = 0x18;
+const PDU_IEI_CHARACTER_SIZE_WVG_OBJECT                = 0x19;
+const PDU_IEI_EXTENDED_OBJECT_DATA_REQUEST_COMMAND     = 0x1A;
+const PDU_IEI_RFC822_EMAIL_HEADER                      = 0x20;
+const PDU_IEI_HYPERLINK_FORMAT_ELEMENT                 = 0x21;
+const PDU_IEI_REPLY_ADDRESS_ELEMENT                    = 0x22;
+const PDU_IEI_ENHANCED_VOICE_MAIL_INFORMATION          = 0x23;
+const PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT           = 0x24;
+const PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT          = 0x25;
+
+// 7bit alphabet escape character. The encoded value of this code point is left
+// undefined in official spec. Its code value is internally assigned to \uffff,
+// <noncharacter-FFFF> in Unicode basic multilingual plane.
+const PDU_NL_EXTENDED_ESCAPE = 0x1B;
+
+// <SP>, <LF>, <CR> are only defined in locking shift tables.
+const PDU_NL_SPACE = 0x20;
+const PDU_NL_LINE_FEED = 0x0A;
+const PDU_NL_CARRIAGE_RETURN = 0x0D;
+
+// 7bit alphabet page break character, only defined in single shift tables.
+// The encoded value of this code point is left undefined in official spec, but
+// the code point itself maybe be used for example in compressed CBS messages.
+// Its code value is internally assigned to \u000c, ASCII form feed, or new page.
+const PDU_NL_PAGE_BREAK = 0x0A;
+// 7bit alphabet reserved control character, only defined in single shift
+// tables. The encoded value of this code point is left undefined in official
+// spec. Its code value is internally assigned to \ufffe, <noncharacter-FFFE>
+// in Unicode basic multilingual plane.
+const PDU_NL_RESERVED_CONTROL = 0x0D;
+
+const PDU_NL_IDENTIFIER_DEFAULT    = 0;
+const PDU_NL_IDENTIFIER_TURKISH    = 1;
+const PDU_NL_IDENTIFIER_SPANISH    = 2;
+const PDU_NL_IDENTIFIER_PORTUGUESE = 3;
+const PDU_NL_IDENTIFIER_BENGALI    = 4;
+const PDU_NL_IDENTIFIER_GUJARATI   = 5;
+const PDU_NL_IDENTIFIER_HINDI      = 6;
+const PDU_NL_IDENTIFIER_KANNADA    = 7;
+const PDU_NL_IDENTIFIER_MALAYALAM  = 8;
+const PDU_NL_IDENTIFIER_ORIYA      = 9;
+const PDU_NL_IDENTIFIER_PUNJABI    = 10;
+const PDU_NL_IDENTIFIER_TAMIL      = 11;
+const PDU_NL_IDENTIFIER_TELUGU     = 12;
+const PDU_NL_IDENTIFIER_URDU       = 13;
+
+// National Language Locking Shift Tables, see 3GPP TS 23.038
+const PDU_NL_LOCKING_SHIFT_TABLES = [
+  /**
+   * National Language Identifier: 0x00
+   * 6.2.1 GSM 7 bit Default Alphabet
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0394_\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u00c6\u00e6\u00df\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00a4%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u00a1ABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c4\u00d6\u00d1\u00dc\u00a7"
+  // 0.....123456789ABCDEF
+  + "\u00bfabcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x01
+   * A.3.1 Turkish National Language Locking Shift Table
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0394_\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u015e\u015f\u00df\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00a4%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u0130ABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c4\u00d6\u00d1\u00dc\u00a7"
+  // 0.....123456789ABCDEF
+  + "\u00e7abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x02
+   * A.3.2 Void
+   */
+  // 0123456789A.BCD.EF
+    "          \n  \r  "
+  // 0123456789AB.....CDEF
+  + "           \uffff    "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x03
+   * A.3.3 Portuguese National Language Locking Shift Table
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1"
+  // 0.....12.....3.....4.....5.....67.8.....9.....AB.....C.....D.....E.....F.....
+  + "\u0394_\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|\uffff\u00c2\u00e2\u00ca\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00ba%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u00cdABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc\u00a7"
+  // 0123456789ABCDEF
+  + "~abcdefghijklmno"
+  // 0123456789AB.....C.....DE.....F.....
+  + "pqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x04
+   * A.3.4 Bengali National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....
+    "\u0981\u0982\u0983\u0985\u0986\u0987\u0988\u0989\u098a\u098b\n\u098c \r \u098f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0990  \u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\uffff\u099b\u099c\u099d\u099e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u099f\u09a0\u09a1\u09a2\u09a3\u09a4)(\u09a5\u09a6,\u09a7.\u09a8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u09aa\u09ab?"
+  // 0.....1.....2.....3.....4.....56.....789A.....B.....C.....D.....E.....F.....
+  + "\u09ac\u09ad\u09ae\u09af\u09b0 \u09b2   \u09b6\u09b7\u09b8\u09b9\u09bc\u09bd"
+  // 0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....F.....
+  + "\u09be\u09bf\u09c0\u09c1\u09c2\u09c3\u09c4  \u09c7\u09c8  \u09cb\u09cc\u09cd"
+  // 0.....123456789ABCDEF
+  + "\u09ceabcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u09d7\u09dc\u09dd\u09f0\u09f1",
+
+  /**
+   * National Language Identifier: 0x05
+   * A.3.5 Gujarati National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.EF.....
+    "\u0a81\u0a82\u0a83\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\n\u0a8c\u0a8d\r \u0a8f"
+  // 0.....1.....23.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0a90\u0a91 \u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\uffff\u0a9b\u0a9c\u0a9d\u0a9e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4)(\u0aa5\u0aa6,\u0aa7.\u0aa8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0aaa\u0aab?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0aac\u0aad\u0aae\u0aaf\u0ab0 \u0ab2\u0ab3 \u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abc\u0abd"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....CD.....E.....F.....
+  + "\u0abe\u0abf\u0ac0\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5 \u0ac7\u0ac8\u0ac9 \u0acb\u0acc\u0acd"
+  // 0.....123456789ABCDEF
+  + "\u0ad0abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0ae0\u0ae1\u0ae2\u0ae3\u0af1",
+
+  /**
+   * National Language Identifier: 0x06
+   * A.3.6 Hindi National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "\u0901\u0902\u0903\u0905\u0906\u0907\u0908\u0909\u090a\u090b\n\u090c\u090d\r\u090e\u090f"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\uffff\u091b\u091c\u091d\u091e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u091f\u0920\u0921\u0922\u0923\u0924)(\u0925\u0926,\u0927.\u0928"
+  // 0123456789ABC.....D.....E.....F
+  + "0123456789:;\u0929\u092a\u092b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093c\u093d"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u093e\u093f\u0940\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u0949\u094a\u094b\u094c\u094d"
+  // 0.....123456789ABCDEF
+  + "\u0950abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0972\u097b\u097c\u097e\u097f",
+
+  /**
+   * National Language Identifier: 0x07
+   * A.3.7 Kannada National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    " \u0c82\u0c83\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\n\u0c8c \r\u0c8e\u0c8f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0c90 \u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\uffff\u0c9b\u0c9c\u0c9d\u0c9e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4)(\u0ca5\u0ca6,\u0ca7.\u0ca8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0caa\u0cab?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3 \u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbc\u0cbd"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0cbe\u0cbf\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4 \u0cc6\u0cc7\u0cc8 \u0cca\u0ccb\u0ccc\u0ccd"
+  // 0.....123456789ABCDEF
+  + "\u0cd5abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0cd6\u0ce0\u0ce1\u0ce2\u0ce3",
+
+  /**
+   * National Language Identifier: 0x08
+   * A.3.8 Malayalam National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    " \u0d02\u0d03\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\n\u0d0c \r\u0d0e\u0d0f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0d10 \u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\uffff\u0d1b\u0d1c\u0d1d\u0d1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24)(\u0d25\u0d26,\u0d27.\u0d28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0d2a\u0d2b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....EF.....
+  + "\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39 \u0d3d"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43\u0d44 \u0d46\u0d47\u0d48 \u0d4a\u0d4b\u0d4c\u0d4d"
+  // 0.....123456789ABCDEF
+  + "\u0d57abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0d60\u0d61\u0d62\u0d63\u0d79",
+
+  /**
+   * National Language Identifier: 0x09
+   * A.3.9 Oriya National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....
+    "\u0b01\u0b02\u0b03\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\n\u0b0c \r \u0b0f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0b10  \u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\uffff\u0b1b\u0b1c\u0b1d\u0b1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24)(\u0b25\u0b26,\u0b27.\u0b28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0b2a\u0b2b?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0b2c\u0b2d\u0b2e\u0b2f\u0b30 \u0b32\u0b33 \u0b35\u0b36\u0b37\u0b38\u0b39\u0b3c\u0b3d"
+  // 0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....F.....
+  + "\u0b3e\u0b3f\u0b40\u0b41\u0b42\u0b43\u0b44  \u0b47\u0b48  \u0b4b\u0b4c\u0b4d"
+  // 0.....123456789ABCDEF
+  + "\u0b56abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0b57\u0b60\u0b61\u0b62\u0b63",
+
+  /**
+   * National Language Identifier: 0x0A
+   * A.3.10 Punjabi National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.EF.....
+    "\u0a01\u0a02\u0a03\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a \n  \r \u0a0f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0a10  \u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\uffff\u0a1b\u0a1c\u0a1d\u0a1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24)(\u0a25\u0a26,\u0a27.\u0a28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0a2a\u0a2b?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....BC.....D.....E.....F
+  + "\u0a2c\u0a2d\u0a2e\u0a2f\u0a30 \u0a32\u0a33 \u0a35\u0a36 \u0a38\u0a39\u0a3c "
+  // 0.....1.....2.....3.....4.....56789.....A.....BCD.....E.....F.....
+  + "\u0a3e\u0a3f\u0a40\u0a41\u0a42    \u0a47\u0a48  \u0a4b\u0a4c\u0a4d"
+  // 0.....123456789ABCDEF
+  + "\u0a51abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0a70\u0a71\u0a72\u0a73\u0a74",
+
+  /**
+   * National Language Identifier: 0x0B
+   * A.3.11 Tamil National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.E.....F.....
+    " \u0b82\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a \n  \r\u0b8e\u0b8f"
+  // 0.....12.....3.....4.....5.....6789.....A.....B.....CD.....EF.....
+  + "\u0b90 \u0b92\u0b93\u0b94\u0b95   \u0b99\u0b9a\uffff \u0b9c \u0b9e"
+  // 012.....3456.....7.....89ABCDEF.....
+  + " !\u0b9f   \u0ba3\u0ba4)(  , .\u0ba8"
+  // 0123456789ABC.....D.....EF
+  + "0123456789:;\u0ba9\u0baa ?"
+  // 012.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....EF
+  + "  \u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9  "
+  // 0.....1.....2.....3.....4.....5678.....9.....A.....BC.....D.....E.....F.....
+  + "\u0bbe\u0bbf\u0bc0\u0bc1\u0bc2   \u0bc6\u0bc7\u0bc8 \u0bca\u0bcb\u0bcc\u0bcd"
+  // 0.....123456789ABCDEF
+  + "\u0bd0abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0bd7\u0bf0\u0bf1\u0bf2\u0bf9",
+
+  /**
+   * National Language Identifier: 0x0C
+   * A.3.12 Telugu National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    "\u0c01\u0c02\u0c03\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\n\u0c0c \r\u0c0e\u0c0f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0c10 \u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\uffff\u0c1b\u0c1c\u0c1d\u0c1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24)(\u0c25\u0c26,\u0c27.\u0c28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0c2a\u0c2b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....C.....D.....EF.....
+  + "\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33 \u0c35\u0c36\u0c37\u0c38\u0c39 \u0c3d"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0c3e\u0c3f\u0c40\u0c41\u0c42\u0c43\u0c44 \u0c46\u0c47\u0c48 \u0c4a\u0c4b\u0c4c\u0c4d"
+  // 0.....123456789ABCDEF
+  + "\u0c55abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0c56\u0c60\u0c61\u0c62\u0c63",
+
+  /**
+   * National Language Identifier: 0x0D
+   * A.3.13 Urdu National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "\u0627\u0622\u0628\u067b\u0680\u067e\u06a6\u062a\u06c2\u067f\n\u0679\u067d\r\u067a\u067c"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u062b\u062c\u0681\u0684\u0683\u0685\u0686\u0687\u062d\u062e\u062f\uffff\u068c\u0688\u0689\u068a"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u068f\u068d\u0630\u0631\u0691\u0693)(\u0699\u0632,\u0696.\u0698"
+  // 0123456789ABC.....D.....E.....F
+  + "0123456789:;\u069a\u0633\u0634?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0635\u0636\u0637\u0638\u0639\u0641\u0642\u06a9\u06aa\u06ab\u06af\u06b3\u06b1\u0644\u0645\u0646"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u06ba\u06bb\u06bc\u0648\u06c4\u06d5\u06c1\u06be\u0621\u06cc\u06d0\u06d2\u064d\u0650\u064f\u0657"
+  // 0.....123456789ABCDEF
+  + "\u0654abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0655\u0651\u0653\u0656\u0670"
+];
+
+// National Language Single Shift Tables, see 3GPP TS 23.038
+const PDU_NL_SINGLE_SHIFT_TABLES = [
+  /**
+   * National Language Identifier: 0x00
+   * 6.2.1.1 GSM 7 bit default alphabet extension table
+   */
+  // 0123456789A.....BCD.....EF
+    "          \u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|               "
+  // 0123456789ABCDEF
+  + "                "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x01
+   * A.2.1 Turkish National Language Single Shift Table
+   */
+  // 0123456789A.....BCD.....EF
+    "          \u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01234567.....89.....ABCDEF
+  + "|      \u011e \u0130      "
+  // 0123.....456789ABCDEF
+  + "   \u015e            "
+  // 0123.....45.....67.....89.....ABCDEF
+  + "   \u00e7 \u20ac \u011f \u0131      "
+  // 0123.....456789ABCDEF
+  + "   \u015f            ",
+
+  /**
+   * National Language Identifier: 0x02
+   * A.2.2 Spanish National Language Single Shift Table
+   */
+  // 0123456789.....A.....BCD.....EF
+    "         \u00e7\u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01.....23456789.....ABCDEF.....
+  + "|\u00c1       \u00cd     \u00d3"
+  // 012345.....6789ABCDEF
+  + "     \u00da          "
+  // 01.....2345.....6789.....ABCDEF.....
+  + " \u00e1   \u20ac   \u00ed     \u00f3"
+  // 012345.....6789ABCDEF
+  + "     \u00fa          ",
+
+  /**
+   * National Language Identifier: 0x03
+   * A.2.3 Portuguese National Language Single Shift Table
+   */
+  // 012345.....6789.....A.....B.....C.....D.....E.....F.....
+    "     \u00ea   \u00e7\u000c\u00d4\u00f4\ufffe\u00c1\u00e1"
+  // 012.....3.....45.....6.....7.....8.....9.....AB.....CDEF.....
+  + "  \u03a6\u0393^\u03a9\u03a0\u03a8\u03a3\u0398 \uffff   \u00ca"
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01.....23456789.....ABCDEF.....
+  + "|\u00c0       \u00cd     \u00d3"
+  // 012345.....6789AB.....C.....DEF
+  + "     \u00da     \u00c3\u00d5   "
+  // 01.....2345.....6789.....ABCDEF.....
+  + " \u00c2   \u20ac   \u00ed     \u00f3"
+  // 012345.....6789AB.....C.....DEF.....
+  + "     \u00fa     \u00e3\u00f5  \u00e2",
+
+  /**
+   * National Language Identifier: 0x04
+   * A.2.4 Bengali National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u09e6\u09e7\uffff\u09e8\u09e9\u09ea\u09eb"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u09ec\u09ed\u09ee\u09ef\u09df\u09e0\u09e1\u09e2{}\u09e3\u09f2\u09f3\u09f4\u09f5\\"
+  // 0.....1.....2.....3.....4.....56789ABCDEF
+  + "\u09f6\u09f7\u09f8\u09f9\u09fa       [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x05
+   * A.2.5 Gujarati National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0ae6\u0ae7\u0ae8\u0ae9"
+  // 0.....1.....2.....3.....4.....5.....6789ABCDEF.
+  + "\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef  {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x06
+   * A.2.6 Hindi National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0966\u0967\u0968\u0969"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u096a\u096b\u096c\u096d\u096e\u096f\u0951\u0952{}\u0953\u0954\u0958\u0959\u095a\\"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....BCDEF
+  + "\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u0962\u0963\u0970\u0971 [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x07
+   * A.2.7 Kannada National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0ce6\u0ce7\u0ce8\u0ce9"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....BCDEF.
+  + "\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0cde\u0cf1{}\u0cf2    \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x08
+   * A.2.8 Malayalam National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0d66\u0d67\u0d68\u0d69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0d70\u0d71{}\u0d72\u0d73\u0d74\u0d75\u0d7a\\"
+  // 0.....1.....2.....3.....4.....56789ABCDEF
+  + "\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f       [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x09
+   * A.2.9 Oriya National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0b66\u0b67\u0b68\u0b69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....DEF.
+  + "\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0b5c\u0b5d{}\u0b5f\u0b70\u0b71  \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0A
+   * A.2.10 Punjabi National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0a66\u0a67\u0a68\u0a69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....EF.
+  + "\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0a59\u0a5a{}\u0a5b\u0a5c\u0a5e\u0a75 \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0B
+   * A.2.11 Tamil National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0be6\u0be7\u0be8\u0be9"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0bf3\u0bf4{}\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0C
+   * A.2.12 Telugu National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789AB.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*  \uffff\u0c66\u0c67\u0c68\u0c69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0c58\u0c59{}\u0c78\u0c79\u0c7a\u0c7b\u0c7c\\"
+  // 0.....1.....2.....3456789ABCDEF
+  + "\u0c7d\u0c7e\u0c7f         [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0D
+   * A.2.13 Urdu National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0600\u0601\uffff\u06f0\u06f1\u06f2\u06f3"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u060c\u060d{}\u060e\u060f\u0610\u0611\u0612\\"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....CDEF.....
+  + "\u0613\u0614\u061b\u061f\u0640\u0652\u0658\u066b\u066c\u0672\u0673\u06cd[~]\u06d4"
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                "
 ];
 
 const DATACALL_RADIOTECHNOLOGY_CDMA = 0;
 const DATACALL_RADIOTECHNOLOGY_GSM = 1;
 
 const DATACALL_AUTH_NONE = 0;
 const DATACALL_AUTH_PAP = 1;
 const DATACALL_AUTH_CHAP = 2;
--- a/dom/system/b2g/ril_worker.js
+++ b/dom/system/b2g/ril_worker.js
@@ -785,31 +785,36 @@ let RIL = {
    *        String containing the SMSC PDU in hex format.
    * @param number
    *        String containing the recipients address.
    * @param body
    *        String containing the message body.
    * @param dcs
    *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
    *        constants.
-   * @param bodyLengthInOctets
-   *        Byte length of the message body when encoded with the given DCS.
+   * @param userDataHeaderLength
+   *        Length of embedded user data header, in bytes. The whole header
+   *        size will be userDataHeaderLength + 1; 0 for no header.
+   * @param encodedBodyLength
+   *        Length of the message body when encoded with the given DCS. For
+   *        UCS2, in bytes; for 7-bit, in septets.
+   * @param langIndex
+   *        Table index used for normal 7-bit encoded character lookup.
+   * @param langShiftIndex
+   *        Table index used for escaped 7-bit encoded character lookup.
    */
   sendSMS: function sendSMS(options) {
     let token = Buf.newParcel(REQUEST_SEND_SMS, options);
     //TODO we want to map token to the input values so that on the
     // response from the RIL device we know which SMS request was successful
     // or not. Maybe we should build that functionality into newParcel() and
     // handle it within tokenRequestMap[].
     Buf.writeUint32(2);
     Buf.writeString(options.SMSC);
-    GsmPDUHelper.writeMessage(options.number,
-                              options.body,
-                              options.dcs,
-                              options.bodyLengthInOctets);
+    GsmPDUHelper.writeMessage(options);
     Buf.sendParcel();
   },
 
   /**
    * Acknowledge the receipt and handling of an SMS.
    *
    * @param success
    *        Boolean indicating whether the message was successfuly handled.
@@ -2160,16 +2165,23 @@ let Phone = {
  * A PDU is a string containing a series of hexadecimally encoded octets
  * or nibble-swapped binary-coded decimals (BCDs). It contains not only the
  * message text but information about the sender, the SMS service center,
  * timestamp, etc.
  */
 let GsmPDUHelper = {
 
   /**
+   * List of tuples of national language identifier pairs.
+   */
+  enabledGsmTableTuples: [
+    [PDU_NL_IDENTIFIER_DEFAULT, PDU_NL_IDENTIFIER_DEFAULT],
+  ],
+
+  /**
    * Read one character (2 bytes) from a RIL string and decode as hex.
    *
    * @return the nibble as a number.
    */
   readHexNibble: function readHexNibble() {
     let nibble = Buf.readUint16();
     if (nibble >= 48 && nibble <= 57) {
       nibble -= 48; // ASCII '0'..'9'
@@ -2278,76 +2290,136 @@ let GsmPDUHelper = {
   },
 
   /**
    * Read user data, convert to septets, look up relevant characters in a
    * 7-bit alphabet, and construct string.
    *
    * @param length
    *        Number of septets to read (*not* octets)
+   * @param paddingBits
+   *        Number of padding bits in the first byte of user data.
+   * @param langIndex
+   *        Table index used for normal 7-bit encoded character lookup.
+   * @param langShiftIndex
+   *        Table index used for escaped 7-bit encoded character lookup.
    *
    * @return a string.
-   *
-   * TODO: support other alphabets
-   * TODO: support escape chars
    */
-  readSeptetsToString: function readSeptetsToString(length) {
+  readSeptetsToString: function readSeptetsToString(length, paddingBits, langIndex, langShiftIndex) {
     let ret = "";
-    let byteLength = Math.ceil(length * 7 / 8);
+    let byteLength = Math.ceil((length * 7 + paddingBits) / 8);
+
+    /**
+     * |<-                    last byte in header                    ->|
+     * |<-           incompleteBits          ->|<- last header septet->|
+     * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+     *
+     * |<-                   1st byte in user data                   ->|
+     * |<-               data septet 1               ->|<-paddingBits->|
+     * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+     *
+     * |<-                   2nd byte in user data                   ->|
+     * |<-                   data spetet 2                   ->|<-ds1->|
+     * +===7===|===6===|===5===|===4===|===3===|===2===|===1===|===0===|
+     */
+    let data = 0;
+    let dataBits = 0;
+    if (paddingBits) {
+      data = this.readHexOctet() >> paddingBits;
+      dataBits = 8 - paddingBits;
+      --byteLength;
+    }
 
-    let leftOver = 0;
-    for (let i = 0; i < byteLength; i++) {
-      let octet = this.readHexOctet();
-      let shift = (i % 7);
-      let leftOver_mask = (0xff << (7 - shift)) & 0xff;
-      let septet_mask = (0xff >> (shift + 1));
+    let escapeFound = false;
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
+    do {
+      // Read as much as fits in 32bit word
+      let bytesToRead = Math.min(byteLength, dataBits ? 3 : 4);
+      for (let i = 0; i < bytesToRead; i++) {
+        data |= this.readHexOctet() << dataBits;
+        dataBits += 8;
+        --byteLength;
+      }
+
+      // Consume available full septets
+      for (; dataBits >= 7; dataBits -= 7) {
+        let septet = data & 0x7F;
+        data >>>= 7;
 
-      let septet = ((octet & septet_mask) << shift) | leftOver;
-      ret += PDU_ALPHABET_7BIT_DEFAULT[septet];
-      leftOver = (octet & leftOver_mask) >> (7 - shift);
+        if (escapeFound) {
+          escapeFound = false;
+          if (septet == PDU_NL_EXTENDED_ESCAPE) {
+            // According to 3GPP TS 23.038, section 6.2.1.1, NOTE 1, "On
+            // receipt of this code, a receiving entity shall display a space
+            // until another extensiion table is defined."
+            ret += " ";
+          } else if (septet == PDU_NL_RESERVED_CONTROL) {
+            // According to 3GPP TS 23.038 B.2, "This code represents a control
+            // character and therefore must not be used for language specific
+            // characters."
+            ret += " ";
+          } else {
+            ret += langShiftTable[septet];
+          }
+        } else if (septet == PDU_NL_EXTENDED_ESCAPE) {
+          escapeFound = true;
+        } else {
+          ret += langTable[septet];
+        }
+      }
+    } while (byteLength);
 
-      // Every 7th byte we have a whole septet left over that we can apply.
-      if (shift == 6) {
-        ret += PDU_ALPHABET_7BIT_DEFAULT[leftOver];
-        leftOver = 0;
-      }
-    }
     if (ret.length != length) {
       ret = ret.slice(0, length);
     }
     return ret;
   },
 
-  writeStringAsSeptets: function writeStringAsSeptets(message) {
-    let right = 0;
-    for (let i = 0; i < message.length + 1; i++) {
-      let shift = (i % 8);
-      let septet;
-      if (i < message.length) {
-        septet = PDU_ALPHABET_7BIT_DEFAULT.indexOf(message[i]);
-      } else {
-        septet = 0;
-      }
-      if (septet == -1) {
-        if (DEBUG) debug("Fffff, "  + message[i] + " not in 7 bit alphabet!");
-        septet = 0;
-      }
-      if (shift == 0) {
-        // We're at the beginning of a cycle, but we need two septet values
-        // to make an octet. So we're going to have to sit this one out.
-        right = septet;
+  writeStringAsSeptets: function writeStringAsSeptets(message, paddingBits, langIndex, langShiftIndex) {
+    const langTable = PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
+    const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
+
+    let dataBits = paddingBits;
+    let data = 0;
+    for (let i = 0; i < message.length; i++) {
+      let septet = langTable.indexOf(message[i]);
+      if (septet == PDU_NL_EXTENDED_ESCAPE) {
         continue;
       }
 
-      let left_mask = 0xff >> (8 - shift);
-      let right_mask = (0xff << shift) & 0xff;
-      let left = (septet & left_mask) << (8 - shift);
-      let octet = left | right;
-      this.writeHexOctet(left | right);
-      right = (septet & right_mask) >> shift;
+      if (septet >= 0) {
+        data |= septet << dataBits;
+        dataBits += 7;
+      } else {
+        septet = langShiftTable.indexOf(message[i]);
+        if (septet == -1) {
+          throw new Error(message[i] + " not in 7 bit alphabet "
+                          + langIndex + ":" + langShiftIndex + "!");
+        }
+
+        if (septet == PDU_NL_RESERVED_CONTROL) {
+          continue;
+        }
+
+        data |= PDU_NL_EXTENDED_ESCAPE << dataBits;
+        dataBits += 7;
+        data |= septet << dataBits;
+        dataBits += 7;
+      }
+
+      for (; dataBits >= 8; dataBits -= 8) {
+        this.writeHexOctet(data & 0xFF);
+        data >>>= 8;
+      }
+    }
+
+    if (dataBits != 0) {
+      this.writeHexOctet(data & 0xFF);
     }
   },
 
   /**
    * Read user data and decode as a UCS2 string.
    *
    * @param numOctets
    *        num of octets to read as UCS2 string.
@@ -2377,54 +2449,248 @@ let GsmPDUHelper = {
     for (let i = 0; i < message.length; ++i) {
       let code = message.charCodeAt(i);
       this.writeHexOctet((code >> 8) & 0xFF);
       this.writeHexOctet(code & 0xFF);
     }
   },
 
   /**
+   * Calculate encoded length using specified locking/single shift table
+   *
+   * @param message
+   *        message string to be encoded.
+   * @param langTable
+   *        locking shift table string.
+   * @param langShiftTable
+   *        single shift table string.
+   *
+   * @note that the algorithm used in this function must match exactly with
+   * #writeStringAsSeptets.
+   */
+  _calculateLangEncodedLength: function _calculateLangEncodedLength(message, langTable, langShiftTable) {
+    let length = 0;
+    for (let msgIndex = 0; msgIndex < message.length; msgIndex++) {
+      let septet = langTable.indexOf(message.charAt(msgIndex));
+
+      // According to 3GPP TS 23.038, section 6.1.1 General notes, "The
+      // characters marked '1)' are not used but are displayed as a space."
+      if (septet == PDU_NL_EXTENDED_ESCAPE) {
+        continue;
+      }
+
+      if (septet >= 0) {
+        length++;
+        continue;
+      }
+
+      septet = langShiftTable.indexOf(message.charAt(msgIndex));
+      if (septet == -1) {
+        return -1;
+      }
+
+      // According to 3GPP TS 23.038 B.2, "This code represents a control
+      // character and therefore must not be used for language specific
+      // characters."
+      if (septet == PDU_NL_RESERVED_CONTROL) {
+        continue;
+      }
+
+      // The character is not found in locking shfit table, but could be
+      // encoded as <escape><char> with single shift table. Note that it's
+      // still possible for septet to has the value of PDU_NL_EXTENDED_ESCAPE,
+      // but we can display it as a space in this case as said in previous
+      // comment.
+      length += 2;
+    }
+
+    return length;
+  },
+
+  /**
    * Calculate user data length and its encoding.
    *
    * The `options` parameter object should contain the `body` attribute, and
-   * the `dcs`, `bodyLengthInOctets` attributes will be set as return:
+   * the `dcs`, `userDataHeaderLength`, `encodedBodyLength`, `langIndex`,
+   * `langShiftIndex` attributes will be set as return:
    *
    * @param body
    *        String containing the message body.
    * @param dcs
    *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
    *        constants.
-   * @param bodyLengthInOctets
-   *        Byte length of the message body when encoded with the given DCS.
+   * @param userDataHeaderLength
+   *        Length of embedded user data header, in bytes. The whole header
+   *        size will be userDataHeaderLength + 1; 0 for no header.
+   * @param encodedBodyLength
+   *        Length of the message body when encoded with the given DCS. For
+   *        UCS2, in bytes; for 7-bit, in septets.
+   * @param langIndex
+   *        Table index used for normal 7-bit encoded character lookup.
+   * @param langShiftIndex
+   *        Table index used for escaped 7-bit encoded character lookup.
    */
   calculateUserDataLength: function calculateUserDataLength(options) {
-    //TODO: support language tables, see bug 729876
     //TODO: support multipart SMS, see bug 712933
-    let needUCS2 = false;
-    for (let i = 0; i < options.body.length; ++i) {
-      if (options.body.charCodeAt(i) >= 128) {
-        needUCS2 = true;
-        break;
+    options.dcs = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
+    options.langIndex = PDU_NL_IDENTIFIER_DEFAULT;
+    options.langShiftIndex = PDU_NL_IDENTIFIER_DEFAULT;
+    options.encodedBodyLength = 0;
+    options.userDataHeaderLength = 0;
+
+    let needUCS2 = true;
+    let minUserDataLength = Number.MAX_VALUE;
+    for (let i = 0; i < this.enabledGsmTableTuples.length; i++) {
+      let [langIndex, langShiftIndex] = this.enabledGsmTableTuples[i];
+
+      const langTable = PDU_NL_LOCKING_SHIFT_TABLES[langIndex];
+      const langShiftTable = PDU_NL_SINGLE_SHIFT_TABLES[langShiftIndex];
+
+      let length = this._calculateLangEncodedLength(options.body,
+                                                    langTable,
+                                                    langShiftTable);
+      if (length < 0) {
+        continue;
+      }
+
+      let headerLen = 0;
+      if (langIndex != PDU_NL_IDENTIFIER_DEFAULT) {
+        headerLen += 3; // IEI + len + langIndex
+      }
+      if (langShiftIndex != PDU_NL_IDENTIFIER_DEFAULT) {
+        headerLen += 3; // IEI + len + langShiftIndex
+      }
+
+      // Calculate full user data length, note the extra byte is for header len
+      let userDataLength = length + (headerLen ? headerLen + 1 : 0);
+      if (userDataLength >= minUserDataLength) {
+        continue;
+      }
+
+      needUCS2 = false;
+      minUserDataLength = userDataLength;
+
+      options.encodedBodyLength = length;
+      options.userDataHeaderLength = headerLen;
+      options.langIndex = langIndex;
+      options.langShiftIndex = langShiftIndex;
+
+      if (userDataLength <= options.body.length) {
+        // Found minimum user data length already
+        return;
       }
     }
 
     if (needUCS2) {
       options.dcs = PDU_DCS_MSG_CODING_16BITS_ALPHABET;
-      options.bodyLengthInOctets = options.body.length * 2;
-    } else {
-      options.dcs = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
-      options.bodyLengthInOctets = Math.ceil(options.body.length * 7 / 8);
+      options.encodedBodyLength = options.body.length * 2;
+      options.userDataHeaderLength = 0;
+    }
+  },
+
+  /**
+   * Read 1 + UDHL octets and construct user data header at return.
+   *
+   * @return A header object with properties contained in received message.
+   * The properties set include:
+   * <ul>
+   * <li>length: totoal length of the header, default 0.
+   * <li>langIndex: used locking shift table index, default
+   *     PDU_NL_IDENTIFIER_DEFAULT.
+   * <li>langShiftIndex: used locking shift table index, default
+   *     PDU_NL_IDENTIFIER_DEFAULT.
+   * </ul>
+   */
+  readUserDataHeader: function readUserDataHeader() {
+    let header = {
+      length: 0,
+      langIndex: PDU_NL_IDENTIFIER_DEFAULT,
+      langShiftIndex: PDU_NL_IDENTIFIER_DEFAULT
+    };
+
+    header.length = this.readHexOctet();
+    let dataAvailable = header.length;
+    while (dataAvailable >= 2) {
+      let id = this.readHexOctet();
+      let length = this.readHexOctet();
+      dataAvailable -= 2;
+
+      switch (id) {
+        case PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT:
+          let langShiftIndex = this.readHexOctet();
+          --dataAvailable;
+          if (langShiftIndex < PDU_NL_SINGLE_SHIFT_TABLES.length) {
+            header.langShiftIndex = langShiftIndex;
+          }
+          break;
+        case PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT:
+          let langIndex = this.readHexOctet();
+          --dataAvailable;
+          if (langIndex < PDU_NL_LOCKING_SHIFT_TABLES.length) {
+            header.langIndex = langIndex;
+          }
+          break;
+        default:
+          if (DEBUG) {
+            debug("readUserDataHeader: unsupported IEI(" + id
+                  + "), " + length + " bytes.");
+          }
+
+          // Read out unsupported data
+          if (length) {
+            let octets;
+            if (DEBUG) octets = new Uint8Array(length);
+
+            for (let i = 0; i < length; i++) {
+              let octet = this.readHexOctet();
+              if (DEBUG) octets[i] = octet;
+            }
+            dataAvailable -= length;
+
+            if (DEBUG) debug("readUserDataHeader: " + Array.slice(octets));
+          }
+          break;
+      }
+    }
+
+    if (dataAvailable != 0) {
+      throw new Error("Illegal user data header found!");
+    }
+
+    return header;
+  },
+
+  /**
+   * Write out user data header.
+   *
+   * @param options
+   *        Options containing information for user data header write-out. The
+   *        `userDataHeaderLength` property must be correctly pre-calculated.
+   */
+  writeUserDataHeader: function writeUserDataHeader(options) {
+    this.writeHexOctet(options.userDataHeaderLength);
+
+    if (options.langIndex != PDU_NL_IDENTIFIER_DEFAULT) {
+      this.writeHexOctet(PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT);
+      this.writeHexOctet(1);
+      this.writeHexOctet(options.langIndex);
+    }
+
+    if (options.langShiftIndex != PDU_NL_IDENTIFIER_DEFAULT) {
+      this.writeHexOctet(PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT);
+      this.writeHexOctet(1);
+      this.writeHexOctet(options.langShiftIndex);
     }
   },
 
   /**
    * User data can be 7 bit (default alphabet) data, 8 bit data, or 16 bit
    * (UCS2) data.
    */
-  readUserData: function readUserData(length, codingScheme) {
+  readUserData: function readUserData(length, codingScheme, hasHeader) {
     if (DEBUG) {
       debug("Reading " + length + " bytes of user data.");
       debug("Coding scheme: " + codingScheme);
     }
     // 7 bit is the default fallback encoding.
     let encoding = PDU_DCS_MSG_CODING_7BITS_ALPHABET;
     switch (codingScheme & 0xC0) {
       case 0x0:
@@ -2451,26 +2717,46 @@ let GsmPDUHelper = {
             break;
         }
         break;
       default:
         // Falling back to default encoding.
         break;
     }
 
+    let header;
+    let paddingBits = 0;
+    if (hasHeader) {
+      header = this.readUserDataHeader();
+
+      if (encoding == PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+        let headerBits = (header.length + 1) * 8;
+        let headerSeptets = Math.ceil(headerBits / 7);
+
+        length -= headerSeptets;
+        paddingBits = headerSeptets * 7 - headerBits;
+      } else {
+        length -= (header.length + 1);
+      }
+    }
+
     if (DEBUG) debug("PDU: message encoding is " + encoding + " bit.");
     switch (encoding) {
       case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
         // 7 bit encoding allows 140 octets, which means 160 characters
         // ((140x8) / 7 = 160 chars)
         if (length > PDU_MAX_USER_DATA_7BIT) {
           if (DEBUG) debug("PDU error: user data is too long: " + length);
           return null;
         }
-        return this.readSeptetsToString(length);
+
+        return this.readSeptetsToString(length,
+                                        paddingBits,
+                                        hasHeader ? header.langIndex : PDU_NL_IDENTIFIER_DEFAULT,
+                                        hasHeader ? header.langShiftIndex : PDU_NL_IDENTIFIER_DEFAULT);
       case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
         // Unsupported.
         return null;
       case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
         return this.readUCS2String(length);
     }
     return null;
   },
@@ -2500,16 +2786,20 @@ let GsmPDUHelper = {
       msg.SMSC = this.readSwappedNibbleBCD(smscLength - 1).toString();
       if ((smscTypeOfAddress >> 4) == (PDU_TOA_INTERNATIONAL >> 4)) {
         msg.SMSC = '+' + msg.SMSC;
       }
     }
 
     // First octet of this SMS-DELIVER or SMS-SUBMIT message
     let firstOctet = this.readHexOctet();
+
+    // User data header indicator
+    let hasUserDataHeader = firstOctet & PDU_UDHI;
+
     // if the sms is of SMS-SUBMIT type it would contain a TP-MR
     let isSmsSubmit = firstOctet & PDU_MTI_SMS_SUBMIT;
     if (isSmsSubmit) {
       msg.reference = this.readHexOctet(); // TP-Message-Reference
     }
 
     // - Sender Address info -
     // Address length
@@ -2577,17 +2867,19 @@ let GsmPDUHelper = {
       }
     }
 
     // - TP-User-Data-Length -
     let userDataLength = this.readHexOctet();
 
     // - TP-User-Data -
     if (userDataLength > 0) {
-      msg.body = this.readUserData(userDataLength, dataCodingScheme);
+      msg.body = this.readUserData(userDataLength,
+                                   dataCodingScheme,
+                                   hasUserDataHeader);
     }
 
     return msg;
   },
 
   /**
    * Serialize a SMS-SUBMIT PDU message and write it to the output stream.
    *
@@ -2597,23 +2889,39 @@ let GsmPDUHelper = {
    *
    * @param address
    *        String containing the address (number) of the SMS receiver
    * @param userData
    *        String containing the message to be sent as user data
    * @param dcs
    *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
    *        constants.
-   * @param userDataLengthInOctets
-   *        Byte length of the user data when encoded with the given DCS.
+   * @param userDataHeaderLength
+   *        Length of embedded user data header, in bytes. The whole header
+   *        size will be userDataHeaderLength + 1; 0 for no header.
+   * @param encodedBodyLength
+   *        Length of the user data when encoded with the given DCS. For UCS2,
+   *        in bytes; for 7-bit, in septets.
+   * @param langIndex
+   *        Table index used for normal 7-bit encoded character lookup.
+   * @param langShiftIndex
+   *        Table index used for escaped 7-bit encoded character lookup.
    */
-  writeMessage: function writeMessage(address,
-                                      userData,
-                                      dcs,
-                                      userDataLengthInOctets) {
+  writeMessage: function writeMessage(options) {
+    if (DEBUG) {
+      debug("writeMessage: " + JSON.stringify(options));
+    }
+    let address = options.number;
+    let body = options.body;
+    let dcs = options.dcs;
+    let userDataHeaderLength = options.userDataHeaderLength;
+    let encodedBodyLength = options.encodedBodyLength;
+    let langIndex = options.langIndex;
+    let langShiftIndex = options.langShiftIndex;
+
     // SMS-SUBMIT Format:
     //
     // PDU Type - 1 octet
     // Message Reference - 1 octet
     // DA - Destination Address - 2 to 12 octets
     // PID - Protocol Identifier - 1 octet
     // DCS - Data Coding Scheme - 1 octet
     // VP - Validity Period - 0, 1 or 7 octets
@@ -2623,16 +2931,30 @@ let GsmPDUHelper = {
     let addressFormat = PDU_TOA_ISDN; // 81
     if (address[0] == '+') {
       addressFormat = PDU_TOA_INTERNATIONAL | PDU_TOA_ISDN; // 91
       address = address.substring(1);
     }
     //TODO validity is unsupported for now
     let validity = 0;
 
+    let headerOctets = (userDataHeaderLength ? userDataHeaderLength + 1 : 0);
+    let paddingBits;
+    let userDataLengthInSeptets;
+    let userDataLengthInOctets;
+    if (dcs == PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+      let headerSeptets = Math.ceil(headerOctets * 8 / 7);
+      userDataLengthInSeptets = headerSeptets + encodedBodyLength;
+      userDataLengthInOctets = Math.ceil(userDataLengthInSeptets * 7 / 8);
+      paddingBits = headerSeptets * 7 - headerOctets * 8;
+    } else {
+      userDataLengthInOctets = headerOctets + encodedBodyLength;
+      paddingBits = 0;
+    }
+
     let pduOctetLength = 4 + // PDU Type, Message Ref, address length + format
                          Math.ceil(address.length / 2) +
                          3 + // PID, DCS, UDL
                          userDataLengthInOctets;
     if (validity) {
       //TODO: add more to pduOctetLength
     }
 
@@ -2667,18 +2989,18 @@ let GsmPDUHelper = {
 
     // PDU type. MTI is set to SMS-SUBMIT
     let firstOctet = PDU_MTI_SMS_SUBMIT;
 
     // Validity period
     if (validity) {
       //TODO: not supported yet, OR with one of PDU_VPF_*
     }
-    let udhi = ""; //TODO: for now this is unsupported
-    if (udhi) {
+    // User data header indicator
+    if (headerOctets) {
       firstOctet |= PDU_UDHI;
     }
     this.writeHexOctet(firstOctet);
 
     // Message reference 00
     this.writeHexOctet(0x00);
 
     // - Destination Address -
@@ -2694,30 +3016,35 @@ let GsmPDUHelper = {
     this.writeHexOctet(dcs);
 
     // - Validity Period -
     if (validity) {
       this.writeHexOctet(validity);
     }
 
     // - User Data -
-    let userDataLength = userData.length;
-    if (dcs == PDU_DCS_MSG_CODING_16BITS_ALPHABET) {
-      userDataLength = userData.length * 2;
+    if (dcs == PDU_DCS_MSG_CODING_7BITS_ALPHABET) {
+      this.writeHexOctet(userDataLengthInSeptets);
+    } else {
+      this.writeHexOctet(userDataLengthInOctets);
     }
-    this.writeHexOctet(userDataLength);
+
+    if (headerOctets) {
+      this.writeUserDataHeader(options);
+    }
+
     switch (dcs) {
       case PDU_DCS_MSG_CODING_7BITS_ALPHABET:
-        this.writeStringAsSeptets(userData);
+        this.writeStringAsSeptets(body, paddingBits, langIndex, langShiftIndex);
         break;
       case PDU_DCS_MSG_CODING_8BITS_ALPHABET:
         // Unsupported.
         break;
       case PDU_DCS_MSG_CODING_16BITS_ALPHABET:
-        this.writeUCS2String(userData);
+        this.writeUCS2String(body);
         break;
     }
 
     // End of the string. The string length is always even by definition, so
     // we write two \0 delimiters.
     Buf.writeUint16(0);
     Buf.writeUint16(0);
   }
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -523,18 +523,18 @@ private:
       JSAutoByteString buffer(aCx, str);
       if (!buffer) {
         return false;
       }
 
 #ifdef ANDROID
       __android_log_print(ANDROID_LOG_INFO, "Gecko", buffer.ptr());
 #endif
-      fputs(buffer.ptr(), stderr);
-      fflush(stderr);
+      fputs(buffer.ptr(), stdout);
+      fflush(stdout);
     }
 
     return true;
   }
 
   static JSBool
   AtoB(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
--- a/editor/libeditor/html/tests/test_bug674770-2.html
+++ b/editor/libeditor/html/tests/test_bug674770-2.html
@@ -44,16 +44,17 @@ function clickEventHnalder(aEvent)
   }
 }
 
 // NOTE: tests need to check the result *after* the content is actually
 //       modified.  Sometimes, the modification is delayed. Therefore, there
 //       are a lot of functions and SimpleTest.executeSoon()s.
 
 SimpleTest.waitForFocus(function() {
+  SpecialPowers.setBoolPref("middlemouse.contentLoadURL", false);
   SpecialPowers.setBoolPref("middlemouse.paste", true);
 
   frameWindow = iframe.contentWindow;
   frameDocument = iframe.contentDocument;
 
   frameDocument.getElementById("input").addEventListener("click", clickEventHnalder, false);
   frameDocument.getElementById("editor1").addEventListener("click", clickEventHnalder, false);
   frameDocument.getElementById("editor2").addEventListener("click", clickEventHnalder, false);
@@ -379,16 +380,18 @@ function runBodyEditableDocumentTests2()
        "pasted when middle clicked in non-editable element");
 
     SimpleTest.executeSoon(cleanup);
   });
 }
 
 function cleanup()
 {
+  SpecialPowers.clearUserPref("middlemouse.contentLoadURL");
   SpecialPowers.clearUserPref("middlemouse.paste");
+
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
+++ b/gfx/cairo/cairo/src/cairo-dwrite-font.cpp
@@ -1152,17 +1152,27 @@ cairo_int_status_t
 	rt->SetCurrentTransform(transform);
     }
     BitBlt(rt->GetMemoryDC(),
 	   0, 0,
 	   area.right - area.left, area.bottom - area.top,
 	   surface->dc,
 	   area.left, area.top, 
 	   SRCCOPY | NOMIRRORBITMAP);
-    HRESULT hr = rt->DrawGlyphRun(0, 0, DWRITE_MEASURING_MODE_NATURAL, run, params, color);
+    DWRITE_MEASURING_MODE measureMode; 
+    switch (scaled_font->rendering_mode) {
+    case cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC:
+    case cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE:
+        measureMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
+        break;
+    default:
+        measureMode = DWRITE_MEASURING_MODE_NATURAL;
+        break;
+    }
+    HRESULT hr = rt->DrawGlyphRun(0, 0, measureMode, run, params, color);
     BitBlt(surface->dc,
 	   area.left, area.top,
 	   area.right - area.left, area.bottom - area.top,
 	   rt->GetMemoryDC(),
 	   0, 0, 
 	   SRCCOPY | NOMIRRORBITMAP);
     params->Release();
     rt->Release();
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1297,16 +1297,18 @@ GLContext::ResizeOffscreenFBO(const gfxI
         }
     }
 
    // Allocate texture
     if (aUseReadFBO) {
         fBindTexture(LOCAL_GL_TEXTURE_2D, newOffscreenTexture);
         fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_LINEAR);
         fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_LINEAR);
+        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+        fTexParameteri(LOCAL_GL_TEXTURE_2D, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
 
         if (alpha) {
             fTexImage2D(LOCAL_GL_TEXTURE_2D,
                         0,
                         LOCAL_GL_RGBA,
                         aSize.width, aSize.height,
                         0,
                         LOCAL_GL_RGBA,
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
@@ -227,35 +227,35 @@ find_syllables (const hb_ot_map_t *map, 
 	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
 		goto _again;
 
 	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
 	case 2:
 #line 62 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_consonant_syllable (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 3:
 #line 63 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_vowel_syllable (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 4:
 #line 64 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_standalone_cluster (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 1:
 #line 65 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_non_indic (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 #line 256 "hb-ot-shape-complex-indic-machine.hh"
 	}
 
 _again:
 	if ( cs == 0 )
 		goto _out;
 	if ( ++p != pe )
@@ -263,35 +263,35 @@ find_syllables (const hb_ot_map_t *map, 
 	_test_eof: {}
 	if ( p == eof )
 	{
 	switch ( _indic_syllable_machine_eof_actions[cs] ) {
 	case 2:
 #line 62 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_consonant_syllable (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 3:
 #line 63 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_vowel_syllable (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 4:
 #line 64 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_standalone_cluster (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 	case 1:
 #line 65 "hb-ot-shape-complex-indic-machine.rl"
 	{ found_non_indic (map, buffer, mask_array, last, p); }
 #line 67 "hb-ot-shape-complex-indic-machine.rl"
-	{ set_cluster (buffer, p, last); last = p; }
+	{ set_cluster (buffer, last, p); last = p; }
 	break;
 #line 292 "hb-ot-shape-complex-indic-machine.hh"
 	}
 	}
 
 	_out: {}
 	}
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
@@ -59,17 +59,17 @@ z = ZWJ|ZWNJ;
 matra_group = M N? H?;
 syllable_tail = SM? (VD VD?)?;
 
 action found_consonant_syllable { found_consonant_syllable (map, buffer, mask_array, last, p); }
 action found_vowel_syllable { found_vowel_syllable (map, buffer, mask_array, last, p); }
 action found_standalone_cluster { found_standalone_cluster (map, buffer, mask_array, last, p); }
 action found_non_indic { found_non_indic (map, buffer, mask_array, last, p); }
 
-action next_syllable { set_cluster (buffer, p, last); last = p; }
+action next_syllable { set_cluster (buffer, last, p); last = p; }
 
 consonant_syllable =	(c.N? (z.H|H.z?))* c.N? A? (H.z? | matra_group*)? syllable_tail %(found_consonant_syllable);
 vowel_syllable =	(Ra H)? V N? (z.H.c | ZWJ.c)? matra_group* syllable_tail %(found_vowel_syllable);
 standalone_cluster =	(Ra H)? NBSP N? (z? H c)? matra_group* syllable_tail %(found_standalone_cluster);
 non_indic = X %(found_non_indic);
 
 syllable =
 	  consonant_syllable
--- a/gfx/layers/RenderTrace.cpp
+++ b/gfx/layers/RenderTrace.cpp
@@ -43,28 +43,28 @@
 
 
 namespace mozilla {
 namespace layers {
 
 static int colorId = 0;
 
 static gfx3DMatrix GetRootTransform(Layer *aLayer) {
-  gfx3DMatrix layerTrans = aLayer->GetTransform();
+  gfx3DMatrix layerTrans = aLayer->GetTransform().ProjectTo2D();
   if (aLayer->GetParent() != NULL) {
-    return GetRootTransform(aLayer->GetParent()) * layerTrans.ProjectTo2D();
+    return GetRootTransform(aLayer->GetParent()) * layerTrans;
   }
-  return layerTrans.ProjectTo2D();
+  return layerTrans;
 }
 
 void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx3DMatrix aRootTransform, bool aReset) {
   if (!aLayer)
     return;
 
-  gfx3DMatrix trans = aRootTransform * aLayer->GetTransform();
+  gfx3DMatrix trans = aRootTransform * aLayer->GetTransform().ProjectTo2D();
   nsIntRect clipRect = aLayer->GetEffectiveVisibleRegion().GetBounds();
   gfxRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
   trans.TransformBounds(rect);
 
   if (strcmp(aLayer->Name(), "ContainerLayer") != 0 &&
       strcmp(aLayer->Name(), "ShadowContainerLayer") != 0) {
     printf_stderr("%s RENDERTRACE %u rect #%02X%s %i %i %i %i\n",
       aLayer->Name(), (int)PR_IntervalNow(),
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -34,16 +34,17 @@
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "CompositorParent.h"
+#include "RenderTrace.h"
 #include "ShadowLayersParent.h"
 #include "LayerManagerOGL.h"
 #include "nsIWidget.h"
 #include "nsGkAtoms.h"
 #include "RenderTrace.h"
 
 #if defined(MOZ_WIDGET_ANDROID)
 #include "AndroidBridge.h"
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -373,17 +373,16 @@ ShadowLayersParent::RecvUpdate(const Inf
 
       image->SetAllocator(this);
       SharedImage newBack;
       image->Swap(op.newFrontBuffer(), &newBack);
       replyv.push_back(OpImageSwap(shadow, NULL,
                                    newBack));
 
       RenderTraceInvalidateEnd(image, "FF00FF");
-
       break;
     }
 
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -91,18 +91,18 @@ private:
     mozilla::AlignedStorage2<mozilla::gfx::SurfacePattern> mSurfacePattern;
   };
 
   gfxContext *mContext;
   Pattern *mPattern;
 };
 
 gfxContext::gfxContext(gfxASurface *surface)
-  : mSurface(surface)
-  , mRefCairo(NULL)
+  : mRefCairo(NULL)
+  , mSurface(surface)
 {
   MOZ_COUNT_CTOR(gfxContext);
 
   mCairo = cairo_create(surface->CairoSurface());
   mFlags = surface->GetDefaultContextFlags();
   if (mSurface->GetRotateForLandscape()) {
     // Rotate page 90 degrees to draw landscape page on portrait paper
     gfxIntSize size = mSurface->GetSize();
@@ -1261,18 +1261,16 @@ gfxContext::ClipContainsRect(const gfxRe
   } else {
     unsigned int lastReset = 0;
     for (int i = mStateStack.Length() - 2; i > 0; i--) {
       if (mStateStack[i].clipWasReset) {
         lastReset = i;
       }
     }
 
-    bool result = true;
-
     // Since we always return false when the clip list contains a
     // non-rectangular clip or a non-rectilinear transform, our 'total' clip
     // is always a rectangle if we hit the end of this function.
     Rect clipBounds(0, 0, Float(mDT->GetSize().width), Float(mDT->GetSize().height));
 
     for (unsigned int i = lastReset; i < mStateStack.Length(); i++) {
       for (unsigned int c = 0; c < mStateStack[i].pushedClips.Length(); c++) {
         AzureState::PushedClip &clip = mStateStack[i].pushedClips[c];
--- a/gfx/thebes/gfxUserFontSet.cpp
+++ b/gfx/thebes/gfxUserFontSet.cpp
@@ -397,19 +397,21 @@ StoreUserFontData(gfxFontEntry* aFontEnt
     userFontData->mFormat = src.mFormatFlags;
     userFontData->mRealName = aOriginalName;
     if (aMetadata) {
         userFontData->mMetadata.SwapElements(*aMetadata);
         userFontData->mMetaOrigLen = aMetaOrigLen;
     }
 }
 
-static void
-CopyWOFFMetadata(const PRUint8* aFontData, PRUint32 aLength,
-                 nsTArray<PRUint8>* aMetadata, PRUint32* aMetaOrigLen)
+void
+gfxUserFontSet::CopyWOFFMetadata(const PRUint8* aFontData,
+                                 PRUint32 aLength,
+                                 nsTArray<PRUint8>* aMetadata,
+                                 PRUint32* aMetaOrigLen)
 {
     // This function may be called with arbitrary, unvalidated "font" data
     // from @font-face, so it needs to be careful to bounds-check, etc.,
     // before trying to read anything.
     // This just saves a copy of the compressed data block; it does NOT check
     // that the block can be successfully decompressed, or that it contains
     // well-formed/valid XML metadata.
     struct WOFFHeader {
--- a/gfx/thebes/gfxUserFontSet.h
+++ b/gfx/thebes/gfxUserFontSet.h
@@ -276,16 +276,22 @@ protected:
                                 nsresult aStatus = 0) = 0;
 
     // font families defined by @font-face rules
     nsRefPtrHashtable<nsStringHashKey, gfxMixedFontFamily> mFontFamilies;
 
     PRUint64        mGeneration;
 
     static PRLogModuleInfo *sUserFontsLog;
+
+private:
+    static void CopyWOFFMetadata(const PRUint8* aFontData,
+                                 PRUint32 aLength,
+                                 nsTArray<PRUint8>* aMetadata,
+                                 PRUint32* aMetaOrigLen);
 };
 
 // acts a placeholder until the real font is downloaded
 
 class gfxProxyFontEntry : public gfxFontEntry {
     friend class gfxUserFontSet;
 
 public:
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -1164,19 +1164,17 @@ XPCShellEnvironment::Init()
     rv = rtsvc->GetBackstagePass(getter_AddRefs(backstagePass));
     if (NS_FAILED(rv)) {
         NS_ERROR("Failed to get backstage pass from rtsvc!");
         return false;
     }
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
-                                              NS_GET_IID(nsISupports),
                                               principal,
-                                              nsnull,
                                               nsIXPConnect::
                                                   FLAG_SYSTEM_GLOBAL_OBJECT,
                                               getter_AddRefs(holder));
     if (NS_FAILED(rv)) {
         NS_ERROR("InitClassesWithNewWrappedGlobal failed!");
         return false;
     }
 
--- a/js/ipc/ObjectWrapperChild.cpp
+++ b/js/ipc/ObjectWrapperChild.cpp
@@ -501,21 +501,21 @@ ObjectWrapperChild::AnswerNewEnumerateNe
     InfallibleTArray<nsString>* strIds =
         static_cast<InfallibleTArray<nsString>*>(JS_GetPrivate(state));
 
     if (!strIds)
         return false;
 
     jsval v = JS_GetReservedSlot(state, sNextIdIndexSlot);
 
-    jsuint i = JSVAL_TO_INT(v);
+    int32_t i = JSVAL_TO_INT(v);
     NS_ASSERTION(i >= 0, "Index of next jsid negative?");
     NS_ASSERTION(i <= strIds->Length(), "Index of next jsid too large?");
 
-    if (jsuint(i) == strIds->Length()) {
+    if (size_t(i) == strIds->Length()) {
         *status = JS_TRUE;
         return JSObject_to_JSVariant(cx, NULL, statep);
     }
 
     *idp = strIds->ElementAt(i);
     JS_SetReservedSlot(state, sNextIdIndexSlot, INT_TO_JSVAL(i + 1));
     *status = JS_TRUE;
     return true;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -160,24 +160,26 @@ CPPSRCS		= \
 		BytecodeCompiler.cpp \
 		BytecodeEmitter.cpp \
 		FoldConstants.cpp \
 		ParseMaps.cpp \
 		ParseNode.cpp \
 		Parser.cpp \
 		SemanticAnalysis.cpp \
 		TokenStream.cpp \
+		TestingFunctions.cpp \
 		LifoAlloc.cpp \
 		MapObject.cpp \
 		MemoryMetrics.cpp \
 		RegExpObject.cpp \
 		RegExpStatics.cpp \
 		RegExp.cpp \
 		Memory.cpp \
 		Statistics.cpp \
+		StringBuffer.cpp \
 		Unicode.cpp \
 		$(NULL)
 
 # Changes to internal header files, used externally, massively slow down
 # browser builds.  Don't add new files here unless you know what you're
 # doing!
 INSTALLED_HEADERS = \
 		js-config.h \
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -40,16 +40,17 @@
 
 #include "jscntxt.h"
 
 #include "builtin/RegExp.h"
 
 #include "vm/MethodGuard-inl.h"
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 class RegExpMatchBuilder
 {
     JSContext   * const cx;
     JSObject    * const array;
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -0,0 +1,570 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsapi.h"
+#include "jsbool.h"
+#include "jscntxt.h"
+#include "jscompartment.h"
+#include "jsfriendapi.h"
+#include "jsgc.h"
+#include "jsobj.h"
+#include "jsprf.h"
+#include "jswrapper.h"
+
+#include "methodjit/MethodJIT.h"
+
+using namespace js;
+using namespace JS;
+
+static JSBool
+GC(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JSCompartment *comp = NULL;
+    if (argc == 1) {
+        Value arg = vp[2];
+        if (arg.isObject())
+            comp = UnwrapObject(&arg.toObject())->compartment();
+    }
+
+#ifndef JS_MORE_DETERMINISTIC
+    size_t preBytes = cx->runtime->gcBytes;
+#endif
+
+    JS_CompartmentGC(cx, comp);
+
+    char buf[256] = { '\0' };
+#ifndef JS_MORE_DETERMINISTIC
+    JS_snprintf(buf, sizeof(buf), "before %lu, after %lu\n",
+                (unsigned long)preBytes, (unsigned long)cx->runtime->gcBytes);
+#endif
+    JSString *str = JS_NewStringCopyZ(cx, buf);
+    if (!str)
+        return false;
+    *vp = STRING_TO_JSVAL(str);
+    return true;
+}
+
+static const struct ParamPair {
+    const char      *name;
+    JSGCParamKey    param;
+} paramMap[] = {
+    {"maxBytes",            JSGC_MAX_BYTES },
+    {"maxMallocBytes",      JSGC_MAX_MALLOC_BYTES},
+    {"gcBytes",             JSGC_BYTES},
+    {"gcNumber",            JSGC_NUMBER},
+    {"sliceTimeBudget",     JSGC_SLICE_TIME_BUDGET}
+};
+
+static JSBool
+GCParameter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JSString *str;
+    if (argc == 0) {
+        str = JS_ValueToString(cx, JSVAL_VOID);
+        JS_ASSERT(str);
+    } else {
+        str = JS_ValueToString(cx, vp[2]);
+        if (!str)
+            return JS_FALSE;
+        vp[2] = STRING_TO_JSVAL(str);
+    }
+
+    JSFlatString *flatStr = JS_FlattenString(cx, str);
+    if (!flatStr)
+        return false;
+
+    size_t paramIndex = 0;
+    for (;; paramIndex++) {
+        if (paramIndex == ArrayLength(paramMap)) {
+            JS_ReportError(cx,
+                           "the first argument argument must be maxBytes, "
+                           "maxMallocBytes, gcStackpoolLifespan, gcBytes or "
+                           "gcNumber");
+            return false;
+        }
+        if (JS_FlatStringEqualsAscii(flatStr, paramMap[paramIndex].name))
+            break;
+    }
+    JSGCParamKey param = paramMap[paramIndex].param;
+
+    if (argc == 1) {
+        uint32_t value = JS_GetGCParameter(cx->runtime, param);
+        return JS_NewNumberValue(cx, value, &vp[0]);
+    }
+
+    if (param == JSGC_NUMBER ||
+        param == JSGC_BYTES) {
+        JS_ReportError(cx, "Attempt to change read-only parameter %s",
+                       paramMap[paramIndex].name);
+        return false;
+    }
+
+    uint32_t value;
+    if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
+        JS_ReportError(cx,
+                       "the second argument must be convertable to uint32_t "
+                       "with non-zero value");
+        return false;
+    }
+
+    if (param == JSGC_MAX_BYTES) {
+        uint32_t gcBytes = JS_GetGCParameter(cx->runtime, JSGC_BYTES);
+        if (value < gcBytes) {
+            JS_ReportError(cx,
+                           "attempt to set maxBytes to the value less than the current "
+                           "gcBytes (%u)",
+                           gcBytes);
+            return false;
+        }
+    }
+
+    JS_SetGCParameter(cx->runtime, param, value);
+    *vp = JSVAL_VOID;
+    return true;
+}
+
+static JSBool
+InternalConst(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc != 1) {
+        JS_ReportError(cx, "the function takes exactly one argument");
+        return false;
+    }
+
+    JSString *str = JS_ValueToString(cx, vp[2]);
+    if (!str)
+        return false;
+    JSFlatString *flat = JS_FlattenString(cx, str);
+    if (!flat)
+        return false;
+
+    if (JS_FlatStringEqualsAscii(flat, "MARK_STACK_LENGTH")) {
+        vp[0] = UINT_TO_JSVAL(js::MARK_STACK_LENGTH);
+    } else {
+        JS_ReportError(cx, "unknown const name");
+        return false;
+    }
+    return true;
+}
+
+#ifdef JS_GC_ZEAL
+static JSBool
+GCZeal(JSContext *cx, unsigned argc, jsval *vp)
+{
+    uint32_t zeal, frequency = JS_DEFAULT_ZEAL_FREQ;
+    JSBool compartment = JS_FALSE;
+
+    if (argc > 3) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments");
+        return JS_FALSE;
+    }
+    if (!JS_ValueToECMAUint32(cx, argc < 1 ? JSVAL_VOID : vp[2], &zeal))
+        return JS_FALSE;
+    if (argc >= 2)
+        if (!JS_ValueToECMAUint32(cx, vp[3], &frequency))
+            return JS_FALSE;
+    if (argc >= 3)
+        compartment = js_ValueToBoolean(vp[3]);
+
+    JS_SetGCZeal(cx, (uint8_t)zeal, frequency, compartment);
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
+
+static JSBool
+ScheduleGC(JSContext *cx, unsigned argc, jsval *vp)
+{
+    uint32_t count;
+    bool compartment = false;
+
+    if (argc != 1 && argc != 2) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments");
+        return JS_FALSE;
+    }
+    if (!JS_ValueToECMAUint32(cx, vp[2], &count))
+        return JS_FALSE;
+    if (argc == 2)
+        compartment = js_ValueToBoolean(vp[3]);
+
+    JS_ScheduleGC(cx, count, compartment);
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
+
+static JSBool
+VerifyBarriers(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Too many arguments");
+        return JS_FALSE;
+    }
+    gc::VerifyBarriers(cx);
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
+
+static JSBool
+GCSlice(JSContext *cx, unsigned argc, jsval *vp)
+{
+    uint32_t budget;
+
+    if (argc != 1) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments");
+        return JS_FALSE;
+    }
+
+    if (!JS_ValueToECMAUint32(cx, vp[2], &budget))
+        return JS_FALSE;
+
+    GCDebugSlice(cx, budget);
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
+
+static JSBool
+DeterministicGC(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc != 1) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments");
+        return JS_FALSE;
+    }
+
+    gc::SetDeterministicGC(cx, js_ValueToBoolean(vp[2]));
+    *vp = JSVAL_VOID;
+    return JS_TRUE;
+}
+#endif /* JS_GC_ZEAL */
+
+typedef struct JSCountHeapNode JSCountHeapNode;
+
+struct JSCountHeapNode {
+    void                *thing;
+    JSGCTraceKind       kind;
+    JSCountHeapNode     *next;
+};
+
+typedef struct JSCountHeapTracer {
+    JSTracer            base;
+    JSDHashTable        visited;
+    bool                ok;
+    JSCountHeapNode     *traceList;
+    JSCountHeapNode     *recycleList;
+} JSCountHeapTracer;
+
+static void
+CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
+{
+    JSCountHeapTracer *countTracer;
+    JSDHashEntryStub *entry;
+    JSCountHeapNode *node;
+    void *thing = *thingp;
+
+    JS_ASSERT(trc->callback == CountHeapNotify);
+    countTracer = (JSCountHeapTracer *)trc;
+    if (!countTracer->ok)
+        return;
+
+    entry = (JSDHashEntryStub *)
+            JS_DHashTableOperate(&countTracer->visited, thing, JS_DHASH_ADD);
+    if (!entry) {
+        countTracer->ok = false;
+        return;
+    }
+    if (entry->key)
+        return;
+    entry->key = thing;
+
+    node = countTracer->recycleList;
+    if (node) {
+        countTracer->recycleList = node->next;
+    } else {
+        node = (JSCountHeapNode *) js_malloc(sizeof *node);
+        if (!node) {
+            countTracer->ok = false;
+            return;
+        }
+    }
+    node->thing = thing;
+    node->kind = kind;
+    node->next = countTracer->traceList;
+    countTracer->traceList = node;
+}
+
+static const struct TraceKindPair {
+    const char       *name;
+    int32_t           kind;
+} traceKindNames[] = {
+    { "all",        -1                  },
+    { "object",     JSTRACE_OBJECT      },
+    { "string",     JSTRACE_STRING      },
+#if JS_HAS_XML_SUPPORT
+    { "xml",        JSTRACE_XML         },
+#endif
+};
+
+static JSBool
+CountHeap(JSContext *cx, unsigned argc, jsval *vp)
+{
+    void* startThing;
+    JSGCTraceKind startTraceKind;
+    jsval v;
+    int32_t traceKind;
+    JSString *str;
+    JSCountHeapTracer countTracer;
+    JSCountHeapNode *node;
+    size_t counter;
+
+    startThing = NULL;
+    startTraceKind = JSTRACE_OBJECT;
+    if (argc > 0) {
+        v = JS_ARGV(cx, vp)[0];
+        if (JSVAL_IS_TRACEABLE(v)) {
+            startThing = JSVAL_TO_TRACEABLE(v);
+            startTraceKind = JSVAL_TRACE_KIND(v);
+        } else if (!JSVAL_IS_NULL(v)) {
+            JS_ReportError(cx,
+                           "the first argument is not null or a heap-allocated "
+                           "thing");
+            return JS_FALSE;
+        }
+    }
+
+    traceKind = -1;
+    if (argc > 1) {
+        str = JS_ValueToString(cx, JS_ARGV(cx, vp)[1]);
+        if (!str)
+            return JS_FALSE;
+        JSFlatString *flatStr = JS_FlattenString(cx, str);
+        if (!flatStr)
+            return JS_FALSE;
+        for (size_t i = 0; ;) {
+            if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) {
+                traceKind = traceKindNames[i].kind;
+                break;
+            }
+            if (++i == ArrayLength(traceKindNames)) {
+                JSAutoByteString bytes(cx, str);
+                if (!!bytes)
+                    JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr());
+                return JS_FALSE;
+            }
+        }
+    }
+
+    JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify);
+    if (!JS_DHashTableInit(&countTracer.visited, JS_DHashGetStubOps(),
+                           NULL, sizeof(JSDHashEntryStub),
+                           JS_DHASH_DEFAULT_CAPACITY(100))) {
+        JS_ReportOutOfMemory(cx);
+        return JS_FALSE;
+    }
+    countTracer.ok = true;
+    countTracer.traceList = NULL;
+    countTracer.recycleList = NULL;
+
+    if (!startThing) {
+        JS_TraceRuntime(&countTracer.base);
+    } else {
+        JS_SET_TRACING_NAME(&countTracer.base, "root");
+        JS_CallTracer(&countTracer.base, startThing, startTraceKind);
+    }
+
+    counter = 0;
+    while ((node = countTracer.traceList) != NULL) {
+        if (traceKind == -1 || node->kind == traceKind)
+            counter++;
+        countTracer.traceList = node->next;
+        node->next = countTracer.recycleList;
+        countTracer.recycleList = node;
+        JS_TraceChildren(&countTracer.base, node->thing, node->kind);
+    }
+    while ((node = countTracer.recycleList) != NULL) {
+        countTracer.recycleList = node->next;
+        js_free(node);
+    }
+    JS_DHashTableFinish(&countTracer.visited);
+    if (!countTracer.ok) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
+
+    return JS_NewNumberValue(cx, (double) counter, vp);
+}
+
+static unsigned finalizeCount = 0;
+
+static void
+finalize_counter_finalize(JSContext *cx, JSObject *obj)
+{
+    JS_ATOMIC_INCREMENT(&finalizeCount);
+}
+
+static JSClass FinalizeCounterClass = {
+    "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
+    JS_PropertyStub,       /* addProperty */
+    JS_PropertyStub,       /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    finalize_counter_finalize
+};
+
+static JSBool
+MakeFinalizeObserver(JSContext *cx, unsigned argc, jsval *vp)
+{
+    JSObject *obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, NULL,
+                                               JS_GetGlobalObject(cx));
+    if (!obj)
+        return false;
+    *vp = OBJECT_TO_JSVAL(obj);
+    return true;
+}
+
+static JSBool
+FinalizeCount(JSContext *cx, unsigned argc, jsval *vp)
+{
+    *vp = INT_TO_JSVAL(finalizeCount);
+    return true;
+}
+
+JSBool
+MJitCodeStats(JSContext *cx, unsigned argc, jsval *vp)
+{
+#ifdef JS_METHODJIT
+    JSRuntime *rt = cx->runtime;
+    AutoLockGC lock(rt);
+    size_t n = 0;
+    for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c) {
+        n += (*c)->sizeOfMjitCode();
+    }
+    JS_SET_RVAL(cx, vp, INT_TO_JSVAL(n));
+#else
+    JS_SET_RVAL(cx, vp, JSVAL_VOID);
+#endif
+    return true;
+}
+
+JSBool
+MJitChunkLimit(JSContext *cx, unsigned argc, jsval *vp)
+{
+    if (argc != 1) {
+        ReportUsageError(cx, &JS_CALLEE(cx, vp).toObject(), "Wrong number of arguments");
+        return JS_FALSE;
+    }
+
+    double t;
+    if (!JS_ValueToNumber(cx, JS_ARGV(cx, vp)[0], &t))
+        return JS_FALSE;
+
+#ifdef JS_METHODJIT
+    mjit::SetChunkLimit((uint32_t) t);
+#endif
+
+    // Clear out analysis information which might refer to code compiled with
+    // the previous chunk limit.
+    JS_GC(cx);
+
+    vp->setUndefined();
+    return true;
+}
+
+static JSBool
+Terminate(JSContext *cx, unsigned arg, jsval *vp)
+{
+    JS_ClearPendingException(cx);
+    return JS_FALSE;
+}
+
+static JSFunctionSpecWithHelp TestingFunctions[] = {
+    JS_FN_HELP("gc", ::GC, 0, 0,
+"gc([obj])",
+"  Run the garbage collector. When obj is given, GC only its compartment."),
+
+    JS_FN_HELP("gcparam", GCParameter, 2, 0,
+"gcparam(name [, value])",
+"  Wrapper for JS_[GS]etGCParameter. The name is either maxBytes,\n"
+"  maxMallocBytes, gcBytes, gcNumber, or sliceTimeBudget."),
+
+    JS_FN_HELP("countHeap", CountHeap, 0, 0,
+"countHeap([start[, kind]])",
+"  Count the number of live GC things in the heap or things reachable from\n"
+"  start when it is given and is not null. kind is either 'all' (default) to\n"
+"  count all things or one of 'object', 'double', 'string', 'function',\n"
+"  'qname', 'namespace', 'xml' to count only things of that kind."),
+
+    JS_FN_HELP("makeFinalizeObserver", MakeFinalizeObserver, 0, 0,
+"makeFinalizeObserver()",
+"  Get a special object whose finalization increases the counter returned\n"
+"  by the finalizeCount function."),
+
+    JS_FN_HELP("finalizeCount", FinalizeCount, 0, 0,
+"finalizeCount()",
+"  Return the current value of the finalization counter that is incremented\n"
+"  each time an object returned by the makeFinalizeObserver is finalized."),
+
+#ifdef JS_GC_ZEAL
+    JS_FN_HELP("gczeal", GCZeal, 2, 0,
+"gczeal(level, [period], [compartmentGC?])",
+"  Specifies how zealous the garbage collector should be. Values for level:\n"
+"    0: Normal amount of collection\n"
+"    1: Collect when roots are added or removed\n"
+"    2: Collect when memory is allocated\n"
+"    3: Collect when the window paints (browser only)\n"
+"    4: Verify write barriers between instructions\n"
+"    5: Verify write barriers between paints\n"
+"  Period specifies that collection happens every n allocations.\n"
+"  If compartmentGC is true, the collections will be compartmental."),
+
+    JS_FN_HELP("schedulegc", ScheduleGC, 1, 0,
+"schedulegc(num, [compartmentGC?])",
+"  Schedule a GC to happen after num allocations."),
+
+    JS_FN_HELP("verifybarriers", VerifyBarriers, 0, 0,
+"verifybarriers()",
+"  Start or end a run of the write barrier verifier."),
+
+    JS_FN_HELP("gcslice", GCSlice, 1, 0,
+"gcslice(n)",
+"  Run an incremental GC slice that marks about n objects."),
+
+    JS_FN_HELP("deterministicgc", DeterministicGC, 1, 0,
+"deterministicgc(true|false)",
+"  If true, only allow determinstic GCs to run."),
+#endif
+
+    JS_FN_HELP("internalConst", InternalConst, 1, 0,
+"internalConst(name)",
+"  Query an internal constant for the engine. See InternalConst source for\n"
+"  the list of constant names."),
+
+#ifdef JS_METHODJIT
+    JS_FN_HELP("mjitcodestats", MJitCodeStats, 0, 0,
+"mjitcodestats()",
+"Return stats on mjit code memory usage."),
+#endif
+
+    JS_FN_HELP("mjitChunkLimit", MJitChunkLimit, 1, 0,
+"mjitChunkLimit(N)",
+"  Specify limit on compiled chunk size during mjit compilation."),
+
+    JS_FN_HELP("terminate", Terminate, 0, 0,
+"terminate()",
+"  Terminate JavaScript execution, as if we had run out of\n"
+"  memory or been terminated by the slow script dialog."),
+
+    JS_FS_END
+};
+
+namespace js {
+
+bool
+DefineTestingFunctions(JSContext *cx, JSObject *obj)
+{
+    return JS_DefineFunctionsWithHelp(cx, obj, TestingFunctions);
+}
+
+} /* namespace js */
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/TestingFunctions.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef TestingFunctions_h__
+#define TestingFunctions_h__
+
+namespace js {
+
+bool
+DefineTestingFunctions(JSContext *cx, JSObject *obj);
+
+} /* namespace js */
+
+#endif /* TestingFunctions_h__ */
--- a/js/src/config/rules.mk
+++ b/js/src/config/rules.mk
@@ -143,16 +143,19 @@ testxpcsrcdir = $(topsrcdir)/testing/xpc
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
+	  --tests-root-dir=$(testxpcobjdir) \
+	  --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
+	  --xunit-suite-name=xpcshell \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 xpcshell-tests-remote: DM_TRANS?=adb
 xpcshell-tests-remote:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -611,19 +611,20 @@ CXX_VERSION='N/A'
 if test "$GCC" = "yes"; then
     GNU_CC=1
     CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'`
 fi
 if test "$GXX" = "yes"; then
     GNU_CXX=1
     CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'`
 fi
-if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then
+if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_AS=1
 fi
+rm -f conftest.out
 if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_LD=1
 fi
 if test "$GNU_CC"; then
     if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then
         GCC_USE_GNU_LD=1
     fi
 fi
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -1569,17 +1569,17 @@ jsvalToPtrExplicit(JSContext* cx, jsval 
       return uint64_t(*result) == i;
     }
   }
   return false;
 }
 
 template<class IntegerType, class CharType, size_t N, class AP>
 void
-IntegerToString(IntegerType i, jsuint radix, Vector<CharType, N, AP>& result)
+IntegerToString(IntegerType i, int radix, Vector<CharType, N, AP>& result)
 {
   JS_STATIC_ASSERT(numeric_limits<IntegerType>::is_exact);
 
   // The buffer must be big enough for all the bits of IntegerType to fit,
   // in base-2, including '-'.
   CharType buffer[sizeof(IntegerType) * 8 + 1];
   CharType* end = buffer + sizeof(buffer) / sizeof(CharType);
   CharType* cp = end;
@@ -1950,33 +1950,33 @@ ImplicitConvert(JSContext* cx,
       default:
         return TypeError(cx, "array", val);
       }
 
     } else if (!JSVAL_IS_PRIMITIVE(val) &&
                JS_IsArrayObject(cx, JSVAL_TO_OBJECT(val))) {
       // Convert each element of the array by calling ImplicitConvert.
       JSObject* sourceArray = JSVAL_TO_OBJECT(val);
-      jsuint sourceLength;
+      uint32_t sourceLength;
       if (!JS_GetArrayLength(cx, sourceArray, &sourceLength) ||
           targetLength != size_t(sourceLength)) {
         JS_ReportError(cx, "ArrayType length does not match source array length");
         return false;
       }
 
       // Convert into an intermediate, in case of failure.
       size_t elementSize = CType::GetSize(baseType);
       size_t arraySize = elementSize * targetLength;
       AutoPtr<char>::Array intermediate(cx->array_new<char>(arraySize));
       if (!intermediate) {
         JS_ReportAllocationOverflow(cx);
         return false;
       }
 
-      for (jsuint i = 0; i < sourceLength; ++i) {
+      for (uint32_t i = 0; i < sourceLength; ++i) {
         js::AutoValueRooter item(cx);
         if (!JS_GetElement(cx, sourceArray, i, item.jsval_addr()))
           return false;
 
         char* data = intermediate.get() + elementSize * i;
         if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
           return false;
       }
@@ -4107,17 +4107,17 @@ StructType::Create(JSContext* cx, unsign
 
   JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
   return JS_TRUE;
 }
 
 JSBool
 StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj)
 {
-  jsuint len;
+  uint32_t len;
   ASSERT_OK(JS_GetArrayLength(cx, fieldsObj, &len));
 
   // Get the common prototype for CData objects of this type from
   // ctypes.CType.prototype.
   JSObject* dataProto = CType::GetProtoFromType(typeObj, SLOT_STRUCTDATAPROTO);
 
   // Set up the 'prototype' and 'prototype.constructor' properties.
   // The prototype will reflect the struct fields as properties on CData objects
@@ -4145,17 +4145,17 @@ StructType::DefineInternal(JSContext* cx
     fieldRootsArray.begin());
 
   // Process the field types.
   size_t structSize, structAlign;
   if (len != 0) {
     structSize = 0;
     structAlign = 0;
 
-    for (jsuint i = 0; i < len; ++i) {
+    for (uint32_t i = 0; i < len; ++i) {
       js::AutoValueRooter item(cx);
       if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
         return JS_FALSE;
 
       JSObject* fieldType = NULL;
       JSFlatString* name = ExtractStructField(cx, item.jsval_value(), &fieldType);
       if (!name)
         return JS_FALSE;
@@ -4920,29 +4920,29 @@ FunctionType::Create(JSContext* cx, unsi
     // Prepare an array of jsvals for the arguments.
     if (JSVAL_IS_PRIMITIVE(argv[2]) ||
         !JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[2]))) {
       JS_ReportError(cx, "third argument must be an array");
       return JS_FALSE;
     }
 
     arrayObj = JSVAL_TO_OBJECT(argv[2]);
-    jsuint len;
+    uint32_t len;
     ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
 
     if (!argTypes.appendN(JSVAL_VOID, len)) {
       JS_ReportOutOfMemory(cx);
       return JS_FALSE;
     }
   }
 
   // Pull out the argument types from the array, if any.
   JS_ASSERT(!argTypes.length() || arrayObj);
   js::AutoArrayRooter items(cx, argTypes.length(), argTypes.begin());
-  for (jsuint i = 0; i < argTypes.length(); ++i) {
+  for (uint32_t i = 0; i < argTypes.length(); ++i) {
     if (!JS_GetElement(cx, arrayObj, i, &argTypes[i]))
       return JS_FALSE;
   }
 
   JSObject* result = CreateInternal(cx, argv[0], argv[1],
       argTypes.begin(), argTypes.length());
   if (!result)
     return JS_FALSE;
@@ -4951,17 +4951,17 @@ FunctionType::Create(JSContext* cx, unsi
   return JS_TRUE;
 }
 
 JSObject*
 FunctionType::CreateInternal(JSContext* cx,
                              jsval abi,
                              jsval rtype,
                              jsval* argtypes,
-                             jsuint arglen)
+                             unsigned arglen)
 {
   // Determine and check the types, and prepare the function CIF.
   AutoPtr<FunctionInfo> fninfo(NewFunctionInfo(cx, abi, rtype, argtypes, arglen));
   if (!fninfo)
     return NULL;
 
   // Get ctypes.FunctionType.prototype and the common prototype for CData objects
   // of this type, from ctypes.CType.prototype.
@@ -5104,17 +5104,17 @@ FunctionType::Call(JSContext* cx,
   AutoValueAutoArray values;
   AutoValueAutoArray strings;
   if (!values.resize(argc)) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
 
   jsval* argv = JS_ARGV(cx, vp);
-  for (jsuint i = 0; i < argcFixed; ++i)
+  for (unsigned i = 0; i < argcFixed; ++i)
     if (!ConvertArgument(cx, argv[i], fninfo->mArgTypes[i], &values[i], &strings))
       return false;
 
   if (fninfo->mIsVariadic) {
     if (!fninfo->mFFITypes.resize(argc)) {
       JS_ReportOutOfMemory(cx);
       return false;
     }
@@ -6039,17 +6039,17 @@ Int64Base::ToString(JSContext* cx,
                     jsval* vp,
                     bool isUnsigned)
 {
   if (argc > 1) {
     JS_ReportError(cx, "toString takes zero or one argument");
     return JS_FALSE;
   }
 
-  jsuint radix = 10;
+  int radix = 10;
   if (argc == 1) {
     jsval arg = JS_ARGV(cx, vp)[0];
     if (JSVAL_IS_INT(arg))
       radix = JSVAL_TO_INT(arg);
     if (!JSVAL_IS_INT(arg) || radix < 2 || radix > 36) {
       JS_ReportError(cx, "radix argument must be an integer between 2 and 36");
       return JS_FALSE;
     }
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -491,17 +491,17 @@ namespace StructType {
   const FieldInfoHash* GetFieldInfo(JSObject* obj);
   const FieldInfo* LookupField(JSContext* cx, JSObject* obj, JSFlatString *name);
   JSObject* BuildFieldsArray(JSContext* cx, JSObject* obj);
   ffi_type* BuildFFIType(JSContext* cx, JSObject* obj);
 }
 
 namespace FunctionType {
   JSObject* CreateInternal(JSContext* cx, jsval abi, jsval rtype,
-    jsval* argtypes, jsuint arglen);
+    jsval* argtypes, unsigned arglen);
 
   JSObject* ConstructWithObject(JSContext* cx, JSObject* typeObj,
     JSObject* refObj, PRFuncPtr fnptr, JSObject* result);
 
   FunctionInfo* GetFunctionInfo(JSObject* obj);
   void BuildSymbolName(JSString* name, JSObject* typeObj,
     AutoCString& result);
 }
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1005,17 +1005,17 @@ BytecodeEmitter::shouldNoteClosedName(Pa
  * generation for top-level script, we do the adjustment via code patching in
  * js::frontend::CompileScript; see comments there.
  *
  * The function returns -1 on failures.
  */
 static int
 AdjustBlockSlot(JSContext *cx, BytecodeEmitter *bce, int slot)
 {
-    JS_ASSERT((jsuint) slot < bce->maxStackDepth);
+    JS_ASSERT((unsigned) slot < bce->maxStackDepth);
     if (bce->inFunction()) {
         slot += bce->bindings.countVars();
         if ((unsigned) slot >= SLOTNO_LIMIT) {
             ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR,
                                      JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
@@ -2463,17 +2463,17 @@ EmitSwitch(JSContext *cx, BytecodeEmitte
 
             if (switchOp != JSOP_TABLESWITCH)
                 continue;
             if (!pn3->pn_pval->isInt32()) {
                 switchOp = JSOP_LOOKUPSWITCH;
                 continue;
             }
             i = pn3->pn_pval->toInt32();
-            if ((jsuint)(i + (int)JS_BIT(15)) >= (jsuint)JS_BIT(16)) {
+            if ((unsigned)(i + (int)JS_BIT(15)) >= (unsigned)JS_BIT(16)) {
                 switchOp = JSOP_LOOKUPSWITCH;
                 continue;
             }
             if (i < low)
                 low = i;
             if (high < i)
                 high = i;
 
@@ -3063,17 +3063,17 @@ EmitDestructuringLHS(JSContext *cx, Byte
  * lhs expression. (Same post-condition as EmitDestructuringLHS)
  */
 static JSBool
 EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn,
                            VarEmitOption emitOption)
 {
     JS_ASSERT(emitOption != DefineVars);
 
-    jsuint index;
+    unsigned index;
     ParseNode *pn2, *pn3;
     JSBool doElemOp;
 
 #ifdef DEBUG
     int stackDepth = bce->stackDepth;
     JS_ASSERT(stackDepth != 0);
     JS_ASSERT(pn->isArity(PN_LIST));
     JS_ASSERT(pn->isKind(PNK_RB) || pn->isKind(PNK_RC));
@@ -3298,17 +3298,17 @@ EmitDestructuringOps(JSContext *cx, Byte
     VarEmitOption emitOption = letNotes ? PushInitialValues : InitializeVars;
     return EmitDestructuringOpsHelper(cx, bce, pn, emitOption);
 }
 
 static JSBool
 EmitGroupAssignment(JSContext *cx, BytecodeEmitter *bce, JSOp prologOp,
                     ParseNode *lhs, ParseNode *rhs)
 {
-    jsuint depth, limit, i, nslots;
+    unsigned depth, limit, i, nslots;
     ParseNode *pn;
 
     depth = limit = (unsigned) bce->stackDepth;
     for (pn = rhs->pn_head; pn; pn = pn->pn_next) {
         if (limit == JS_BIT(16)) {
             ReportCompileErrorNumber(cx, bce->tokenStream(), rhs, JSREPORT_ERROR,
                                      JSMSG_ARRAY_INIT_TOO_BIG);
             return JS_FALSE;
@@ -4926,16 +4926,19 @@ EmitNormalFor(JSContext *cx, BytecodeEmi
     ptrdiff_t tmp = bce->offset();
 
     ptrdiff_t jmp = -1;
     if (forHead->pn_kid2) {
         /* Goto the loop condition, which branches back to iterate. */
         jmp = EmitJump(cx, bce, JSOP_GOTO, 0);
         if (jmp < 0)
             return false;
+    } else {
+        if (op != JSOP_NOP && Emit1(cx, bce, JSOP_NOP) < 0)
+            return false;
     }
 
     top = bce->offset();
     SET_STATEMENT_TOP(&stmtInfo, top);
 
     /* Emit code for the loop body. */
     if (EmitLoopHead(cx, bce, forBody) < 0)
         return false;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -6805,17 +6805,17 @@ Parser::primaryExpr(TokenKind tt, bool a
         pn = functionExpr();
         if (!pn)
             return NULL;
         break;
 
       case TOK_LB:
       {
         JSBool matched;
-        jsuint index;
+        unsigned index;
 
         pn = ListNode::create(PNK_RB, tc);
         if (!pn)
             return NULL;
         pn->setOp(JSOP_NEWINIT);
         pn->makeEmpty();
 
 #if JS_HAS_GENERATORS
--- a/js/src/gc/Barrier-inl.h
+++ b/js/src/gc/Barrier-inl.h
@@ -289,18 +289,20 @@ HeapId::init(jsid id)
 
 inline void
 HeapId::pre()
 {
 #ifdef JSGC_INCREMENTAL
     if (JS_UNLIKELY(JSID_IS_OBJECT(value))) {
         JSObject *obj = JSID_TO_OBJECT(value);
         JSCompartment *comp = obj->compartment();
-        if (comp->needsBarrier())
-            js::gc::MarkObjectUnbarriered(comp->barrierTracer(), obj, "write barrier");
+        if (comp->needsBarrier()) {
+            js::gc::MarkObjectUnbarriered(comp->barrierTracer(), &obj, "write barrier");
+            JS_ASSERT(obj == JSID_TO_OBJECT(value));
+        }
     }
 #endif
 }
 
 inline void
 HeapId::post()
 {
 }
--- a/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp
+++ b/js/src/jsapi-tests/testIsAboutToBeFinalized.cpp
@@ -3,24 +3,24 @@
  */
 
 #include "tests.h"
 #include "jsstr.h"
 
 static JSGCCallback oldGCCallback;
 
 static void **checkPointers;
-static jsuint checkPointersLength;
+static unsigned checkPointersLength;
 static size_t checkPointersStaticStrings;
 
 static JSBool
 TestAboutToBeFinalizedCallback(JSContext *cx, JSGCStatus status)
 {
     if (status == JSGC_MARK_END && checkPointers) {
-        for (jsuint i = 0; i != checkPointersLength; ++i) {
+        for (unsigned i = 0; i != checkPointersLength; ++i) {
             void *p = checkPointers[i];
             JS_ASSERT(p);
             if (JS_IsAboutToBeFinalized(p))
                 checkPointers[i] = NULL;
         }
     }
 
     return !oldGCCallback || oldGCCallback(cx, status);
@@ -47,17 +47,17 @@ BEGIN_TEST(testIsAboutToBeFinalized_bug5
      * test rooted elements.
      */
     CHECK(createAndTestRooted());
     NativeFrameCleaner();
 
     JS_GC(cx);
 
     /* Everything is unrooted except unit strings. */
-    for (jsuint i = 0; i != checkPointersLength; ++i) {
+    for (unsigned i = 0; i != checkPointersLength; ++i) {
         void *p = checkPointers[i];
         if (p) {
             CHECK(JSString::isStatic(p));
             CHECK(checkPointersStaticStrings != 0);
             --checkPointersStaticStrings;
         }
     }
     CHECK_EQUAL(checkPointersStaticStrings, 0);
@@ -92,17 +92,17 @@ cls_testIsAboutToBeFinalized_bug528645::
 
     JSBool ok = JS_GetArrayLength(cx, array, &checkPointersLength);
     CHECK(ok);
 
     checkPointers = (void **) malloc(sizeof(void *) * checkPointersLength);
     CHECK(checkPointers);
 
     checkPointersStaticStrings = 0;
-    for (jsuint i = 0; i != checkPointersLength; ++i) {
+    for (unsigned i = 0; i != checkPointersLength; ++i) {
         jsval v;
         ok = JS_GetElement(cx, array, i, &v);
         CHECK(ok);
         JS_ASSERT(JSVAL_IS_GCTHING(v));
         JS_ASSERT(!JSVAL_IS_NULL(v));
         checkPointers[i] = JSVAL_TO_GCTHING(v);
         if (JSString::isStatic(checkPointers[i]))
             ++checkPointersStaticStrings;
@@ -110,27 +110,27 @@ cls_testIsAboutToBeFinalized_bug528645::
 
     oldGCCallback = JS_SetGCCallback(cx, TestAboutToBeFinalizedCallback);
     JS_GC(cx);
 
     /*
      * All GC things are rooted via the root holding the array containing them
      * and TestAboutToBeFinalizedCallback must keep them as is.
      */
-    for (jsuint i = 0; i != checkPointersLength; ++i)
+    for (unsigned i = 0; i != checkPointersLength; ++i)
         CHECK(checkPointers[i]);
 
     /*
      * Overwrite the registers and stack with new GC things to avoid false
      * positives with the finalization test.
      */
     EVAL("[]", root.addr());
 
     array = JSVAL_TO_OBJECT(root.value());
     JS_ASSERT(JS_IsArrayObject(cx, array));
 
-    jsuint tmp;
+    uint32_t tmp;
     CHECK(JS_GetArrayLength(cx, array, &tmp));
     CHECK(ok);
 
     return true;
 }
 
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -50,17 +50,17 @@ BEGIN_TEST(testNewObject_1)
     EVAL("Array", &v);
     JSObject *Array = JSVAL_TO_OBJECT(v);
 
     // With no arguments.
     JSObject *obj = JS_New(cx, Array, 0, NULL);
     CHECK(obj);
     jsvalRoot rt(cx, OBJECT_TO_JSVAL(obj));
     CHECK(JS_IsArrayObject(cx, obj));
-    jsuint len;
+    uint32_t len;
     CHECK(JS_GetArrayLength(cx, obj, &len));
     CHECK_EQUAL(len, 0);
 
     // With one argument.
     argv[0] = INT_TO_JSVAL(4);
     obj = JS_New(cx, Array, 1, argv);
     CHECK(obj);
     rt = OBJECT_TO_JSVAL(obj);
--- a/js/src/jsapi-tests/testStringBuffer.cpp
+++ b/js/src/jsapi-tests/testStringBuffer.cpp
@@ -2,17 +2,18 @@
  * vim: set ts=8 sw=4 et tw=99:
  */
 
 #include "tests.h"
 
 #include "jsatom.h"
 
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
+
+#include "vm/StringBuffer-inl.h"
 
 BEGIN_TEST(testStringBuffer_finishString)
 {
     JSString *str = JS_NewStringCopyZ(cx, "foopy");
     CHECK(str);
 
     JSAtom *atom = js_AtomizeString(cx, str);
     CHECK(atom);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -99,16 +99,17 @@
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 #if ENABLE_YARR_JIT
 #include "assembler/jit/ExecutableAllocator.h"
 #include "methodjit/Logging.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
@@ -747,16 +748,17 @@ JSRuntime::JSRuntime()
     gcIncrementalCompartment(NULL),
     gcPoke(false),
     gcRunning(false),
 #ifdef JS_GC_ZEAL
     gcZeal_(0),
     gcZealFrequency(0),
     gcNextScheduled(0),
     gcDebugCompartmentGC(false),
+    gcDeterministicOnly(false),
 #endif
     gcCallback(NULL),
     gcSliceCallback(NULL),
     gcFinalizeCallback(NULL),
     gcMallocBytes(0),
     gcBlackRootsTraceOp(NULL),
     gcBlackRootsData(NULL),
     gcGrayRootsTraceOp(NULL),
@@ -4259,17 +4261,19 @@ prop_iter_trace(JSTracer *trc, JSObject 
         return;
 
     if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() < 0) {
         /*
          * Native case: just mark the next property to visit. We don't need a
          * barrier here because the pointer is updated via setPrivate, which
          * always takes a barrier.
          */
-        MarkShapeUnbarriered(trc, (Shape *)pdata, "prop iter shape");
+        Shape *tmp = (Shape *)pdata;
+        MarkShapeUnbarriered(trc, &tmp, "prop iter shape");
+        JS_ASSERT(tmp == pdata);
     } else {
         /* Non-native case: mark each id in the JSIdArray private. */
         JSIdArray *ida = (JSIdArray *) pdata;
         MarkIdRange(trc, ida->length, ida->vector, "prop iter");
     }
 }
 
 static Class prop_iter_class = {
@@ -4399,39 +4403,39 @@ JS_SetReservedSlot(JSObject *obj, uint32
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, int length, jsval *vector)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
-    /* NB: jsuint cast does ToUint32. */
-    assertSameCompartment(cx, JSValueArray(vector, vector ? (jsuint)length : 0));
-    return NewDenseCopiedArray(cx, (jsuint)length, vector);
+    
+    assertSameCompartment(cx, JSValueArray(vector, vector ? (uint32_t)length : 0));
+    return NewDenseCopiedArray(cx, (uint32_t)length, vector);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj)
 {
     assertSameCompartment(cx, obj);
     return ObjectClassIs(*obj, ESClass_Array, cx);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp)
+JS_GetArrayLength(JSContext *cx, JSObject *obj, uint32_t *lengthp)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     return js_GetLengthProperty(cx, obj, lengthp);
 }
 
 JS_PUBLIC_API(JSBool)
-JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length)
+JS_SetArrayLength(JSContext *cx, JSObject *obj, uint32_t length)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     return js_SetLengthProperty(cx, obj, length);
 }
 
 JS_PUBLIC_API(JSBool)
@@ -6458,33 +6462,20 @@ JS_ClearPendingException(JSContext *cx)
 {
     AssertNoGC(cx);
     cx->clearPendingException();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_ReportPendingException(JSContext *cx)
 {
-    JSBool ok;
-    bool save;
-
-    AssertNoGC(cx);
-    CHECK_REQUEST(cx);
-
-    /*
-     * Set cx->generatingError to suppress the standard error-to-exception
-     * conversion done by all {js,JS}_Report* functions except for OOM.  The
-     * cx->generatingError flag was added to suppress recursive divergence
-     * under js_ErrorToException, but it serves for our purposes here too.
-     */
-    save = cx->generatingError;
-    cx->generatingError = JS_TRUE;
-    ok = js_ReportUncaughtException(cx);
-    cx->generatingError = save;
-    return ok;
+    AssertNoGC(cx);
+    CHECK_REQUEST(cx);
+
+    return js_ReportUncaughtException(cx);
 }
 
 struct JSExceptionState {
     JSBool throwing;
     jsval  exception;
 };
 
 JS_PUBLIC_API(JSExceptionState *)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1891,18 +1891,18 @@ JSID_TO_INT(jsid id)
  * js_CheckForStringIndex is still valid.
  */
 #define JSID_INT_MIN  (-(1 << 30))
 #define JSID_INT_MAX  ((1 << 30) - 1)
 
 static JS_ALWAYS_INLINE JSBool
 INT_FITS_IN_JSID(int32_t i)
 {
-    return ((jsuint)(i) - (jsuint)JSID_INT_MIN <=
-            (jsuint)(JSID_INT_MAX - JSID_INT_MIN));
+    return ((uint32_t)(i) - (uint32_t)JSID_INT_MIN <=
+            (uint32_t)(JSID_INT_MAX - JSID_INT_MIN));
 }
 
 static JS_ALWAYS_INLINE jsid
 INT_TO_JSID(int32_t i)
 {
     jsid id;
     JS_ASSERT(INT_FITS_IN_JSID(i));
     JSID_BITS(id) = ((i << 1) | JSID_TYPE_INT);
@@ -3981,20 +3981,20 @@ JS_DeleteUCProperty2(JSContext *cx, JSOb
 
 extern JS_PUBLIC_API(JSObject *)
 JS_NewArrayObject(JSContext *cx, int length, jsval *vector);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsArrayObject(JSContext *cx, JSObject *obj);
 
 extern JS_PUBLIC_API(JSBool)
-JS_GetArrayLength(JSContext *cx, JSObject *obj, jsuint *lengthp);
+JS_GetArrayLength(JSContext *cx, JSObject *obj, uint32_t *lengthp);
 
 extern JS_PUBLIC_API(JSBool)
-JS_SetArrayLength(JSContext *cx, JSObject *obj, jsuint length);
+JS_SetArrayLength(JSContext *cx, JSObject *obj, uint32_t length);
 
 extern JS_PUBLIC_API(JSBool)
 JS_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, jsval value,
                  JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs);
 
 extern JS_PUBLIC_API(JSBool)
 JS_AlreadyHasOwnElement(JSContext *cx, JSObject *obj, uint32_t index, JSBool *foundp);
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -123,16 +123,17 @@
 #include "jsstr.h"
 #include "jswrapper.h"
 #include "methodjit/MethodJIT.h"
 #include "methodjit/StubCalls.h"
 #include "methodjit/StubCalls-inl.h"
 
 #include "vm/ArgumentsObject.h"
 #include "vm/MethodGuard.h"
+#include "vm/StringBuffer-inl.h"
 
 #include "ds/Sort.h"
 
 #include "jsarrayinlines.h"
 #include "jsatominlines.h"
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
@@ -142,17 +143,17 @@
 #include "vm/Stack-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 JSBool
-js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp)
+js_GetLengthProperty(JSContext *cx, JSObject *obj, uint32_t *lengthp)
 {
     if (obj->isArray()) {
         *lengthp = obj->getArrayLength();
         return true;
     }
 
     if (obj->isArguments()) {
         ArgumentsObject &argsobj = obj->asArguments();
@@ -162,21 +163,21 @@ js_GetLengthProperty(JSContext *cx, JSOb
         }
     }
 
     AutoValueRooter tvr(cx);
     if (!obj->getProperty(cx, cx->runtime->atomState.lengthAtom, tvr.addr()))
         return false;
 
     if (tvr.value().isInt32()) {
-        *lengthp = jsuint(tvr.value().toInt32()); /* jsuint cast does ToUint32_t */
+        *lengthp = uint32_t(tvr.value().toInt32()); /* uint32_t cast does ToUint32_t */
         return true;
     }
 
-    JS_STATIC_ASSERT(sizeof(jsuint) == sizeof(uint32_t));
+    
     return ToUint32(cx, tvr.value(), (uint32_t *)lengthp);
 }
 
 namespace js {
 
 /*
  * Determine if the id represents an array index or an XML property index.
  *
@@ -192,17 +193,17 @@ namespace js {
  * In our implementation, it would be sufficient to check for JSVAL_IS_INT(id)
  * except that by using signed 31-bit integers we miss the top half of the
  * valid range. This function checks the string representation itself; note
  * that calling a standard conversion routine might allow strings such as
  * "08" or "4.0" as array indices, which they are not.
  *
  */
 JS_FRIEND_API(bool)
-StringIsArrayIndex(JSLinearString *str, jsuint *indexp)
+StringIsArrayIndex(JSLinearString *str, uint32_t *indexp)
 {
     const jschar *s = str->chars();
     uint32_t length = str->length();
     const jschar *end = s + length;
 
     if (length == 0 || length > (sizeof("4294967294") - 1) || !JS7_ISDEC(*s))
         return false;
 
@@ -231,20 +232,20 @@ StringIsArrayIndex(JSLinearString *str, 
     }
 
     return false;
 }
 
 }
 
 static JSBool
-BigIndexToId(JSContext *cx, JSObject *obj, jsuint index, JSBool createAtom,
+BigIndexToId(JSContext *cx, JSObject *obj, uint32_t index, JSBool createAtom,
              jsid *idp)
 {
-    JS_STATIC_ASSERT((jsuint)-1 == 4294967295U);
+    
     JS_ASSERT(index > JSID_INT_MAX);
 
     jschar buf[10];
     jschar *start = ArrayEnd(buf);
     do {
         --start;
         *start = (jschar)('0' + index % 10);
         index /= 10;
@@ -313,18 +314,18 @@ static bool
 IndexToId(JSContext* cx, JSObject* obj, double index, JSBool* hole, jsid* idp,
           JSBool createAtom = JS_FALSE)
 {
     if (index <= JSID_INT_MAX) {
         *idp = INT_TO_JSID(int(index));
         return JS_TRUE;
     }
 
-    if (index <= jsuint(-1)) {
-        if (!BigIndexToId(cx, obj, jsuint(index), createAtom, idp))
+    if (index <= uint32_t(-1)) {
+        if (!BigIndexToId(cx, obj, uint32_t(index), createAtom, idp))
             return JS_FALSE;
         if (hole && JSID_IS_VOID(*idp))
             *hole = JS_TRUE;
         return JS_TRUE;
     }
 
     return ReallyBigIndexToId(cx, index, idp);
 }
@@ -432,17 +433,17 @@ GetElementsSlow(JSContext *cx, JSObject 
         if (!aobj->getElement(cx, i, &vp[i]))
             return false;
     }
 
     return true;
 }
 
 bool
-GetElements(JSContext *cx, JSObject *aobj, jsuint length, Value *vp)
+GetElements(JSContext *cx, JSObject *aobj, uint32_t length, Value *vp)
 {
     if (aobj->isDenseArray() && length <= aobj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, aobj)) {
         /* The prototype does not have indexed properties so hole = undefined */
         const Value *srcbeg = aobj->getDenseArrayElements();
         const Value *srcend = srcbeg + length;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src)
@@ -470,19 +471,19 @@ static JSBool
 SetArrayElement(JSContext *cx, JSObject *obj, double index, const Value &v)
 {
     JS_ASSERT(index >= 0);
 
     if (obj->isDenseArray()) {
         /* Predicted/prefetched code should favor the remains-dense case. */
         JSObject::EnsureDenseResult result = JSObject::ED_SPARSE;
         do {
-            if (index > jsuint(-1))
+            if (index > uint32_t(-1))
                 break;
-            jsuint idx = jsuint(index);
+            uint32_t idx = uint32_t(index);
             result = obj->ensureDenseArrayElements(cx, idx, 1);
             if (result != JSObject::ED_OK)
                 break;
             if (idx >= obj->getArrayLength())
                 obj->setDenseArrayLength(idx + 1);
             obj->setDenseArrayElementWithType(cx, idx, v);
             return true;
         } while (false);
@@ -624,18 +625,18 @@ array_length_setter(JSContext *cx, JSObj
 
     if (obj->isDenseArray()) {
         /*
          * Don't reallocate if we're not actually shrinking our slots. If we do
          * shrink slots here, shrink the initialized length too.  This permits us
          * us to disregard length when reading from arrays as long we are within
          * the initialized capacity.
          */
-        jsuint oldcap = obj->getDenseArrayCapacity();
-        jsuint oldinit = obj->getDenseArrayInitializedLength();
+        uint32_t oldcap = obj->getDenseArrayCapacity();
+        uint32_t oldinit = obj->getDenseArrayInitializedLength();
         if (oldinit > newlen)
             obj->setDenseArrayInitializedLength(newlen);
         if (oldcap > newlen)
             obj->shrinkElements(cx, newlen);
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx)) {
@@ -655,23 +656,23 @@ array_length_setter(JSContext *cx, JSObj
          * oldlen, we iterate through all properties and remove those that
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
         JSObject *iter = JS_NewPropertyIterator(cx, obj);
         if (!iter)
             return false;
 
-        jsuint gap = oldlen - newlen;
+        uint32_t gap = oldlen - newlen;
         for (;;) {
             if (!JS_CHECK_OPERATION_LIMIT(cx) || !JS_NextProperty(cx, iter, &id))
                 return false;
             if (JSID_IS_VOID(id))
                 break;
-            jsuint index;
+            uint32_t index;
             Value junk;
             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
                 !obj->deleteElement(cx, index, &junk, false)) {
                 return false;
             }
         }
     }
 
@@ -858,17 +859,17 @@ array_getGeneric(JSContext *cx, JSObject
         return array_getElement(cx, obj, receiver, index, vp);
 
     return array_getProperty(cx, obj, receiver, atom->asPropertyName(), vp);
 }
 
 static JSBool
 slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
-    jsuint index, length;
+    uint32_t index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
         obj->setArrayLength(cx, index + 1);
     return JS_TRUE;
 }
@@ -1188,17 +1189,17 @@ array_deleteSpecial(JSContext *cx, JSObj
 }
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
 
     uint32_t initLength = obj->getDenseArrayInitializedLength();
-    MarkSlotRange(trc, initLength, obj->getDenseArrayElements(), "element");
+    MarkArraySlots(trc, initLength, obj->getDenseArrayElements(), "element");
 }
 
 static JSBool
 array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
 {
     JS_ASSERT(obj->isDenseArray());
 
     /*
@@ -1483,21 +1484,21 @@ array_toSource(JSContext *cx, unsigned a
         if (!sb.append("[]"))
             return false;
         goto make_string;
     }
 
     if (!sb.append('['))
         return false;
 
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
 
-    for (jsuint index = 0; index < length; index++) {
+    for (uint32_t index = 0; index < length; index++) {
         JSBool hole;
         Value elt;
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, index, &hole, &elt)) {
             return false;
         }
 
         /* Get element's character string. */
@@ -1603,17 +1604,17 @@ array_toString_sub(JSContext *cx, JSObje
     if (!detector.init())
         return false;
 
     if (detector.foundCycle()) {
         args.rval().setString(cx->runtime->atomState.emptyAtom);
         return true;
     }
 
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
 
     StringBuffer sb(cx);
 
     if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
         const Value *start = obj->getDenseArrayElements();
         const Value *end = start + obj->getDenseArrayInitializedLength();
@@ -1644,17 +1645,17 @@ array_toString_sub(JSContext *cx, JSObje
             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++) {
+        for (uint32_t index = 0; index < length; index++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
             JSBool hole;
             Value elt;
             if (!GetElement(cx, obj, index, &hole, &elt))
                 return false;
 
@@ -1788,17 +1789,17 @@ InitArrayElements(JSContext *cx, JSObjec
 
         JSObject::EnsureDenseResult result = obj->ensureDenseArrayElements(cx, start, count);
         if (result != JSObject::ED_OK) {
             if (result == JSObject::ED_FAILED)
                 return false;
             JS_ASSERT(result == JSObject::ED_SPARSE);
             break;
         }
-        jsuint newlen = start + count;
+        uint32_t newlen = start + count;
         if (newlen > obj->getArrayLength())
             obj->setDenseArrayLength(newlen);
 
         JS_ASSERT(count < UINT32_MAX / sizeof(Value));
         obj->copyDenseArrayElements(start, vector, count);
         JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
         return true;
     } while (false);
@@ -1831,17 +1832,17 @@ InitArrayElements(JSContext *cx, JSObjec
         idval.getDoubleRef() += 1;
     } while (vector != end);
 
     return true;
 }
 
 #if 0
 static JSBool
-InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
+InitArrayObject(JSContext *cx, JSObject *obj, uint32_t length, const Value *vector)
 {
     JS_ASSERT(obj->isArray());
 
     JS_ASSERT(obj->isDenseArray());
     obj->setArrayLength(cx, length);
     if (!vector || !length)
         return true;
 
@@ -1850,17 +1851,17 @@ InitArrayObject(JSContext *cx, JSObject 
 
     /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
     if (!obj->ensureElements(cx, length))
         return false;
 
     obj->setDenseArrayInitializedLength(length);
 
     bool hole = false;
-    for (jsuint i = 0; i < length; i++) {
+    for (uint32_t i = 0; i < length; i++) {
         obj->setDenseArrayElement(i, vector[i]);
         hole |= vector[i].isMagic(JS_ARRAY_HOLE);
     }
     if (hole)
         obj->markDenseArrayNotPacked(cx);
 
     return true;
 }
@@ -1893,17 +1894,17 @@ array_join(JSContext *cx, unsigned argc,
 static JSBool
 array_reverse(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
 
-    jsuint len;
+    uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
 
     do {
         if (!obj->isDenseArray())
             break;
         if (js_PrototypeHasIndexedProperties(cx, obj))
             break;
@@ -1955,17 +1956,17 @@ array_reverse(JSContext *cx, unsigned ar
          * array has trailing holes (and thus the original array began with
          * holes).
          */
         args.rval().setObject(*obj);
         return true;
     } while (false);
 
     Value lowval, hival;
-    for (jsuint i = 0, half = len / 2; i < half; i++) {
+    for (uint32_t i = 0, half = len / 2; i < half; i++) {
         JSBool hole, hole2;
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !GetElement(cx, obj, i, &hole, &lowval) ||
             !GetElement(cx, obj, len - i - 1, &hole2, &hival) ||
             !SetOrDeleteArrayElement(cx, obj, len - i - 1, hole, lowval) ||
             !SetOrDeleteArrayElement(cx, obj, i, hole2, hival)) {
             return false;
         }
@@ -2197,17 +2198,17 @@ js::array_sort(JSContext *cx, unsigned a
     } else {
         fval.setNull();
     }
 
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
 
-    jsuint len;
+    uint32_t len;
     if (!js_GetLengthProperty(cx, obj, &len))
         return false;
     if (len == 0) {
         args.rval().setObject(*obj);
         return true;
     }
 
     /*
@@ -2244,17 +2245,17 @@ js::array_sort(JSContext *cx, unsigned a
          * 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++) {
+        for (uint32_t 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))
                 return false;
@@ -2335,17 +2336,17 @@ js::array_sort(JSContext *cx, unsigned a
         } else {
             InvokeArgsGuard args;
             if (!MergeSort(vec.begin(), n, vec.begin() + n,
                            SortComparatorFunction(cx, fval, args))) {
                 return false;
             }
         }
 
-        if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes))
+        if (!InitArrayElements(cx, obj, 0, uint32_t(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;
@@ -2361,17 +2362,17 @@ js::array_sort(JSContext *cx, unsigned a
 }
 
 /*
  * Perl-inspired push, pop, shift, unshift, and splice methods.
  */
 static bool
 array_push_slowly(JSContext *cx, JSObject *obj, CallArgs &args)
 {
-    jsuint length;
+    uint32_t length;
 
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
     if (!InitArrayElements(cx, obj, length, args.length(), args.array(), UpdateTypes))
         return false;
 
     /* Per ECMA-262, return the new array length. */
     double newlength = length + double(args.length());
@@ -2445,17 +2446,17 @@ js::array_push(JSContext *cx, unsigned a
         return array_push_slowly(cx, obj, args);
 
     return array_push1_dense(cx, obj, args);
 }
 
 static JSBool
 array_pop_slowly(JSContext *cx, JSObject* obj, CallArgs &args)
 {
-    jsuint index;
+    uint32_t index;
     if (!js_GetLengthProperty(cx, obj, &index))
         return false;
 
     if (index == 0) {
         args.rval().setUndefined();
         return js_SetLengthProperty(cx, obj, index);
     }
 
@@ -2471,17 +2472,17 @@ array_pop_slowly(JSContext *cx, JSObject
 
     args.rval() = elt;
     return js_SetLengthProperty(cx, obj, index);
 }
 
 static JSBool
 array_pop_dense(JSContext *cx, JSObject* obj, CallArgs &args)
 {
-    jsuint index = obj->getArrayLength();
+    uint32_t index = obj->getArrayLength();
     if (index == 0) {
         args.rval().setUndefined();
         return JS_TRUE;
     }
 
     index--;
 
     JSBool hole;
@@ -2532,17 +2533,17 @@ mjit::stubs::ArrayShift(VMFrame &f)
 JSBool
 js::array_shift(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return JS_FALSE;
 
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     if (length == 0) {
         args.rval().setUndefined();
     } else {
         length--;
 
@@ -2561,17 +2562,17 @@ js::array_shift(JSContext *cx, unsigned 
         }
 
         JSBool hole;
         if (!GetElement(cx, obj, 0u, &hole, &args.rval()))
             return JS_FALSE;
 
         /* Slide down the array above the first element. */
         AutoValueRooter tvr(cx);
-        for (jsuint i = 0; i < length; i++) {
+        for (uint32_t i = 0; i < length; i++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                 !GetElement(cx, obj, i + 1, &hole, tvr.addr()) ||
                 !SetOrDeleteArrayElement(cx, obj, i, hole, tvr.value())) {
                 return JS_FALSE;
             }
         }
 
         /* Delete the only or last element when it exists. */
@@ -2584,17 +2585,17 @@ js::array_shift(JSContext *cx, unsigned 
 static JSBool
 array_unshift(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
 
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     double newlen = length;
     if (args.length() > 0) {
         /* Slide up the array to make room for all args at the bottom. */
         if (length > 0) {
             bool optimized = false;
@@ -2930,21 +2931,21 @@ js::array_concat(JSContext *cx, unsigned
     Value *p = JS_ARGV(cx, vp) - 1;
 
     /* Create a new Array object and root it using *vp. */
     JSObject *aobj = ToObject(cx, &vp[1]);
     if (!aobj)
         return false;
 
     JSObject *nobj;
-    jsuint length;
+    uint32_t length;
     if (aobj->isDenseArray()) {
         length = aobj->getArrayLength();
         const Value *vector = aobj->getDenseArrayElements();
-        jsuint initlen = aobj->getDenseArrayInitializedLength();
+        uint32_t initlen = aobj->getDenseArrayInitializedLength();
         nobj = NewDenseCopiedArray(cx, initlen, vector);
         if (!nobj)
             return JS_FALSE;
         TryReuseArrayType(aobj, nobj);
         nobj->setArrayLength(cx, length);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
@@ -2961,17 +2962,17 @@ js::array_concat(JSContext *cx, unsigned
     /* Loop over [0, argc] to concat args into nobj, expanding all Arrays. */
     for (unsigned i = 0; i <= argc; i++) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
         const Value &v = p[i];
         if (v.isObject()) {
             JSObject &obj = v.toObject();
             if (ObjectClassIs(obj, ESClass_Array, cx)) {
-                jsuint alength;
+                uint32_t alength;
                 if (!js_GetLengthProperty(cx, &obj, &alength))
                     return false;
                 for (uint32_t slot = 0; slot < alength; slot++) {
                     JSBool hole;
                     Value tmp;
                     if (!JS_CHECK_OPERATION_LIMIT(cx) || !GetElement(cx, &obj, slot, &hole, &tmp))
                         return false;
 
@@ -2994,17 +2995,17 @@ js::array_concat(JSContext *cx, unsigned
 
     return js_SetLengthProperty(cx, nobj, length);
 }
 
 static JSBool
 array_slice(JSContext *cx, unsigned argc, Value *vp)
 {
     JSObject *nobj;
-    jsuint length, begin, end, slot;
+    uint32_t length, begin, end, slot;
     JSBool hole;
 
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
 
@@ -3019,29 +3020,29 @@ array_slice(JSContext *cx, unsigned argc
             return false;
         if (d < 0) {
             d += length;
             if (d < 0)
                 d = 0;
         } else if (d > length) {
             d = length;
         }
-        begin = (jsuint)d;
+        begin = (uint32_t)d;
 
         if (args.hasDefined(1)) {
             if (!ToInteger(cx, args[1], &d))
                 return false;
             if (d < 0) {
                 d += length;
                 if (d < 0)
                     d = 0;
             } else if (d > length) {
                 d = length;
             }
-            end = (jsuint)d;
+            end = (uint32_t)d;
         }
     }
 
     if (begin > end)
         begin = end;
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayInitializedLength() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
@@ -3075,17 +3076,17 @@ array_slice(JSContext *cx, unsigned argc
 enum IndexOfKind {
     IndexOf,
     LastIndexOf
 };
 
 static JSBool
 array_indexOfHelper(JSContext *cx, IndexOfKind mode, CallArgs &args)
 {
-    jsuint length, i, stop;
+    uint32_t length, i, stop;
     Value tosearch;
     int direction;
     JSBool hole;
 
     JSObject *obj = ToObject(cx, &args.thisv());
     if (!obj)
         return false;
     if (!js_GetLengthProperty(cx, obj, &length))
@@ -3104,39 +3105,39 @@ array_indexOfHelper(JSContext *cx, Index
             return false;
         if (start < 0) {
             start += length;
             if (start < 0) {
                 if (mode == LastIndexOf)
                     goto not_found;
                 i = 0;
             } else {
-                i = (jsuint)start;
+                i = (uint32_t)start;
             }
         } else if (start >= length) {
             if (mode == IndexOf)
                 goto not_found;
             i = length - 1;
         } else {
-            i = (jsuint)start;
+            i = (uint32_t)start;
         }
     }
 
     if (mode == LastIndexOf) {
         stop = 0;
         direction = -1;
     } else {
         stop = length - 1;
         direction = 1;
     }
 
     for (;;) {
         Value elt;
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
-            !GetElement(cx, obj, (jsuint)i, &hole, &elt)) {
+            !GetElement(cx, obj, (uint32_t)i, &hole, &elt)) {
             return JS_FALSE;
         }
         if (!hole) {
             bool equal;
             if (!StrictlyEqual(cx, elt, tosearch, &equal))
                 return false;
             if (equal) {
                 args.rval().setNumber(i);
@@ -3730,17 +3731,17 @@ js_InitArrayClass(JSContext *cx, JSObjec
 }
 
 /*
  * Array allocation functions.
  */
 namespace js {
 
 static inline bool
-EnsureNewArrayElements(JSContext *cx, JSObject *obj, jsuint length)
+EnsureNewArrayElements(JSContext *cx, JSObject *obj, uint32_t length)
 {
     /*
      * If ensureElements creates dynamically allocated slots, then having
      * fixedSlots is a waste.
      */
     DebugOnly<uint32_t> cap = obj->getDenseArrayCapacity();
 
     if (!obj->ensureElements(cx, length))
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -52,23 +52,23 @@
 const unsigned MIN_SPARSE_INDEX = 256;
 
 namespace js {
 /* 2^32-2, inclusive */
 const uint32_t MAX_ARRAY_INDEX = 4294967294u;
 }
 
 inline JSBool
-js_IdIsIndex(jsid id, jsuint *indexp)
+js_IdIsIndex(jsid id, uint32_t *indexp)
 {
     if (JSID_IS_INT(id)) {
         int32_t i = JSID_TO_INT(id);
         if (i < 0)
             return JS_FALSE;
-        *indexp = (jsuint)i;
+        *indexp = (uint32_t)i;
         return JS_TRUE;
     }
 
     if (JS_UNLIKELY(!JSID_IS_STRING(id)))
         return JS_FALSE;
 
     return js::StringIsArrayIndex(JSID_TO_ATOM(id), indexp);
 }
@@ -132,17 +132,17 @@ NewDenseCopiedArray(JSContext *cx, uint3
 
 /* Create a sparse array. */
 extern JSObject *
 NewSlowEmptyArray(JSContext *cx);
 
 } /* namespace js */
 
 extern JSBool
-js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
+js_GetLengthProperty(JSContext *cx, JSObject *obj, uint32_t *lengthp);
 
 extern JSBool
 js_SetLengthProperty(JSContext *cx, JSObject *obj, double length);
 
 namespace js {
 
 extern JSBool
 array_defineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
@@ -153,17 +153,17 @@ array_deleteElement(JSContext *cx, JSObj
 
 /*
  * Copy 'length' elements from aobj to vp.
  *
  * This function assumes 'length' is effectively the result of calling
  * js_GetLengthProperty on aobj.
  */
 extern bool
-GetElements(JSContext *cx, JSObject *aobj, jsuint length, js::Value *vp);
+GetElements(JSContext *cx, JSObject *aobj, uint32_t length, js::Value *vp);
 
 /* Natives exposed for optimization by the interpreter and JITs. */
 
 extern JSBool
 array_sort(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSBool
 array_push(JSContext *cx, unsigned argc, js::Value *vp);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -707,19 +707,19 @@ js_CheckForStringIndex(jsid id)
 
     size_t n = atom->length() - negative;
     if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
         return id;
 
     const jschar *cp = s;
     const jschar *end = s + n;
 
-    jsuint index = JS7_UNDEC(*cp++);
-    jsuint oldIndex = 0;
-    jsuint c = 0;
+    uint32_t index = JS7_UNDEC(*cp++);
+    uint32_t oldIndex = 0;
+    uint32_t c = 0;
 
     if (index != 0) {
         while (JS7_ISDEC(*cp)) {
             oldIndex = index;
             c = JS7_UNDEC(*cp);
             index = 10 * index + c;
             cp++;
         }
--- a/js/src/jsbool.cpp
+++ b/js/src/jsbool.cpp
@@ -52,20 +52,20 @@
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsstr.h"
 
 #include "vm/GlobalObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/BooleanObject-inl.h"
 #include "vm/MethodGuard-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 Class js::BooleanClass = {
     "Boolean",
     JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_HAS_CACHED_PROTO(JSProto_Boolean),    JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -499,16 +499,47 @@ js_ReportErrorVA(JSContext *cx, unsigned
     warning = JSREPORT_IS_WARNING(report.flags);
 
     ReportError(cx, message, &report, NULL, NULL);
     Foreground::free_(message);
     Foreground::free_(ucmessage);
     return warning;
 }
 
+namespace js {
+
+/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
+void
+ReportUsageError(JSContext *cx, JSObject *callee, const char *msg)
+{
+    const char *usageStr = "usage";
+    JSAtom *usageAtom = js_Atomize(cx, usageStr, strlen(usageStr));
+    DebugOnly<const Shape *> shape = callee->nativeLookup(cx, ATOM_TO_JSID(usageAtom));
+    JS_ASSERT(!shape->configurable());
+    JS_ASSERT(!shape->writable());
+    JS_ASSERT(shape->hasDefaultGetter());
+
+    jsval usage;
+    if (!JS_LookupProperty(cx, callee, "usage", &usage))
+        return;
+
+    if (JSVAL_IS_VOID(usage)) {
+        JS_ReportError(cx, "%s", msg);
+    } else {
+        JSString *str = JSVAL_TO_STRING(usage);
+        JS::Anchor<JSString *> a_str(str);
+        const jschar *chars = JS_GetStringCharsZ(cx, str);
+        if (!chars)
+            return;
+        JS_ReportError(cx, "%s. Usage: %hs", msg, chars);
+    }
+}
+
+} /* namespace js */
+
 /*
  * The arguments from ap need to be packaged up into an array and stored
  * into the report struct.
  *
  * The format string addressed by the error number may contain operands
  * identified by the format {N}, where N is a decimal digit. Each of these
  * is to be replaced by the Nth argument from the va_list. The complete
  * message is placed into reportp->ucmessage converted to a JSString.
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -414,16 +414,17 @@ struct JSRuntime : js::RuntimeFriendFiel
      * We use gcZeal_ == 4 to enable write barrier verification. See the comment
      * in jsgc.cpp for more information about this.
      */
 #ifdef JS_GC_ZEAL
     int                 gcZeal_;
     int                 gcZealFrequency;
     int                 gcNextScheduled;
     bool                gcDebugCompartmentGC;
+    bool                gcDeterministicOnly;
 
     int gcZeal() { return gcZeal_; }
 
     bool needZealousGC() {
         if (gcNextScheduled > 0 && --gcNextScheduled == 0) {
             if (gcZeal() == js::gc::ZealAllocValue)
                 gcNextScheduled = gcZealFrequency;
             return true;
@@ -824,35 +825,32 @@ struct JSContext : js::ContextFriendFiel
 
   private:
     /* See JSContext::findVersion. */
     JSVersion           defaultVersion;      /* script compilation version */
     JSVersion           versionOverride;     /* supercedes defaultVersion when valid */
     bool                hasVersionOverride;
 
     /* Exception state -- the exception member is a GC root by definition. */
-    JSBool              throwing;           /* is there a pending exception? */
-    js::Value           exception;          /* most-recently-thrown exception */
+    JSBool              throwing;            /* is there a pending exception? */
+    js::Value           exception;           /* most-recently-thrown exception */
 
     /* Per-context run options. */
-    unsigned               runOptions;            /* see jsapi.h for JSOPTION_* */
+    unsigned            runOptions;          /* see jsapi.h for JSOPTION_* */
 
   public:
     int32_t             reportGranularity;  /* see jsprobes.h */
 
     /* Locale specific callbacks for string conversion. */
     JSLocaleCallbacks   *localeCallbacks;
 
     js::AutoResolving   *resolvingList;
 
-    /*
-     * True if generating an error, to prevent runaway recursion.
-     * NB: generatingError packs with throwing below.
-     */
-    bool        generatingError;
+    /* True if generating an error, to prevent runaway recursion. */
+    bool                generatingError;
 
     /* GC heap compartment. */
     JSCompartment       *compartment;
 
     inline void setCompartment(JSCompartment *compartment);
 
     /* Current execution stack. */
     js::ContextStack    stack;
@@ -1413,16 +1411,24 @@ js_ReportErrorNumberVA(JSContext *cx, un
 
 extern JSBool
 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
                         void *userRef, const unsigned errorNumber,
                         char **message, JSErrorReport *reportp,
                         bool charArgs, va_list ap);
 #endif
 
+namespace js {
+
+/* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */
+extern void
+ReportUsageError(JSContext *cx, JSObject *callee, const char *msg);
+
+} /* namespace js */
+
 extern void
 js_ReportOutOfMemory(JSContext *cx);
 
 extern JS_FRIEND_API(void)
 js_ReportAllocationOverflow(JSContext *cx);
 
 /*
  * Report an exception using a previously composed JSErrorReport.
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -72,20 +72,20 @@
 #include "jsobj.h"
 #include "jsstr.h"
 #include "jslibmath.h"
 
 #include "vm/GlobalObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/MethodGuard-inl.h"
 #include "vm/Stack-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::types;
 
 /*
  * The JS 'Date' object is patterned after the Java 'Date' object.
  * Here is an script:
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -63,20 +63,20 @@
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jswrapper.h"
 
 #include "vm/GlobalObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 /* Forward declarations for ErrorClass's initializer. */
 static JSBool
@@ -1079,17 +1079,17 @@ js_ErrorToException(JSContext *cx, const
     JSObject *errProto, *errObject;
     JSString *messageStr, *filenameStr;
 
     /*
      * Tell our caller to report immediately if this report is just a warning.
      */
     JS_ASSERT(reportp);
     if (JSREPORT_IS_WARNING(reportp->flags))
-        return JS_FALSE;
+        return false;
 
     /* Find the exception index associated with this error. */
     errorNumber = (JSErrNum) reportp->errorNumber;
     if (!callback || callback == js_GetErrorMessage)
         errorString = js_GetLocalizedErrorMessage(cx, NULL, NULL, errorNumber);
     else
         errorString = callback(userRef, NULL, errorNumber);
     exn = errorString ? (JSExnType) errorString->exnType : JSEXN_NONE;
@@ -1102,78 +1102,62 @@ js_ErrorToException(JSContext *cx, const
             errortoexnname[errorNumber].exception);
 #endif
 
     /*
      * Return false (no exception raised) if no exception is associated
      * with the given error number.
      */
     if (exn == JSEXN_NONE)
-        return JS_FALSE;
+        return false;
 
-    /*
-     * Prevent runaway recursion, via cx->generatingError.  If an out-of-memory
-     * error occurs, no exception object will be created, but we don't assume
-     * that OOM is the only kind of error that subroutines of this function
-     * called below might raise.
-     */
+    /* Prevent infinite recursion. */
     if (cx->generatingError)
-        return JS_FALSE;
-
-    MUST_FLOW_THROUGH("out");
-    cx->generatingError = JS_TRUE;
+        return false;
+    AutoScopedAssign<bool> asa(&cx->generatingError, false);
 
     /* Protect the newly-created strings below from nesting GCs. */
     PodArrayZero(tv);
     AutoArrayRooter tvr(cx, ArrayLength(tv), tv);
 
     /*
      * Try to get an appropriate prototype by looking up the corresponding
      * exception constructor name in the scope chain of the current context's
      * top stack frame, or in the global object if no frame is active.
      */
     ok = js_GetClassPrototype(cx, NULL, GetExceptionProtoKey(exn), &errProto);
     if (!ok)
-        goto out;
+        return false;
     tv[0] = OBJECT_TO_JSVAL(errProto);
 
     errObject = NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL);
-    if (!errObject) {
-        ok = JS_FALSE;
-        goto out;
-    }
+    if (!errObject)
+        return false;
     tv[1] = OBJECT_TO_JSVAL(errObject);
 
     messageStr = JS_NewStringCopyZ(cx, message);
-    if (!messageStr) {
-        ok = JS_FALSE;
-        goto out;
-    }
+    if (!messageStr)
+        return false;
     tv[2] = STRING_TO_JSVAL(messageStr);
 
     filenameStr = JS_NewStringCopyZ(cx, reportp->filename);
-    if (!filenameStr) {
-        ok = JS_FALSE;
-        goto out;
-    }
+    if (!filenameStr)
+        return false;
     tv[3] = STRING_TO_JSVAL(filenameStr);
 
     ok = InitExnPrivate(cx, errObject, messageStr, filenameStr,
                         reportp->lineno, reportp, exn);
     if (!ok)
-        goto out;
+        return false;
 
     JS_SetPendingException(cx, OBJECT_TO_JSVAL(errObject));
 
     /* Flag the error report passed in to indicate an exception was raised. */
     reportp->flags |= JSREPORT_EXCEPTION;
-
-out:
-    cx->generatingError = JS_FALSE;
-    return ok;
+    return true;
 }
 
 JSBool
 js_ReportUncaughtException(JSContext *cx)
 {
     jsval exn;
     JSObject *exnObject;
     jsval roots[5];
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -42,16 +42,18 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 #include "jsweakmap.h"
 #include "jswatchpoint.h"
 
+#include "builtin/TestingFunctions.h"
+
 #include "jsobjinlines.h"
 
 using namespace js;
 using namespace JS;
 
 JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
 {
@@ -175,16 +177,61 @@ JS_WrapPropertyDescriptor(JSContext *cx,
 }
 
 JS_FRIEND_API(void)
 JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
 {
     MarkCycleCollectorChildren(trc, (Shape *)shape);
 }
 
+static bool
+DefineHelpProperty(JSContext *cx, JSObject *obj, const char *prop, const char *value)
+{
+    JSAtom *atom = js_Atomize(cx, value, strlen(value));
+    if (!atom)
+        return false;
+    jsval v = STRING_TO_JSVAL(atom);
+    return JS_DefineProperty(cx, obj, prop, v,
+                             JS_PropertyStub, JS_StrictPropertyStub,
+                             JSPROP_READONLY | JSPROP_PERMANENT);
+}
+
+JS_FRIEND_API(bool)
+JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs)
+{
+    RootObject objRoot(cx, &obj);
+
+    JS_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
+
+    CHECK_REQUEST(cx);
+    assertSameCompartment(cx, obj);
+    for (; fs->name; fs++) {
+        JSAtom *atom = js_Atomize(cx, fs->name, strlen(fs->name));
+        if (!atom)
+            return false;
+
+        JSFunction *fun = js_DefineFunction(cx, objRoot,
+                                            ATOM_TO_JSID(atom), fs->call, fs->nargs, fs->flags);
+        if (!fun)
+            return false;
+
+        if (fs->usage) {
+            if (!DefineHelpProperty(cx, fun, "usage", fs->usage))
+                return false;
+        }
+
+        if (fs->help) {
+            if (!DefineHelpProperty(cx, fun, "help", fs->help))
+                return false;
+        }
+    }
+
+    return true;
+}
+
 AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx
                                                  JS_GUARD_OBJECT_NOTIFIER_PARAM_NO_INIT)
   : cx(cx), oldCompartment(cx->compartment)
 {
     JS_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 AutoPreserveCompartment::~AutoPreserveCompartment()
@@ -784,9 +831,22 @@ IncrementalReferenceBarrier(void *ptr)
 }
 
 extern JS_FRIEND_API(void)
 IncrementalValueBarrier(const Value &v)
 {
     HeapValue::writeBarrierPre(v);
 }
 
+JS_FRIEND_API(JSObject *)
+GetTestingFunctions(JSContext *cx)
+{
+    JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
+    if (!obj)
+        return NULL;
+
+    if (!DefineTestingFunctions(cx, obj))
+        return NULL;
+
+    return obj;
+}
+
 } // namespace js
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -161,16 +161,31 @@ extern JS_FRIEND_API(bool)
 JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj);
 
 extern JS_FRIEND_API(JSBool)
 JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc);
 
 extern JS_FRIEND_API(JSBool)
 JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, js::Value *statep, jsid *idp);
 
+struct JSFunctionSpecWithHelp {
+    const char      *name;
+    JSNative        call;
+    uint16_t        nargs;
+    uint16_t        flags;
+    const char      *usage;
+    const char      *help;
+};
+
+#define JS_FN_HELP(name,call,nargs,flags,usage,help)                          \
+    {name, call, nargs, (flags) | JSPROP_ENUMERATE | JSFUN_STUB_GSOPS, usage, help}
+
+extern JS_FRIEND_API(bool)
+JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs);
+
 #endif
 
 JS_END_EXTERN_C
 
 #ifdef __cplusplus
 
 struct PRLock;
 
@@ -493,17 +508,17 @@ CastAsJSStrictPropertyOp(JSObject *objec
 {
     return JS_DATA_TO_FUNC_PTR(js::StrictPropertyOp, object);
 }
 
 JS_FRIEND_API(bool)
 GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, js::AutoIdVector *props);
 
 JS_FRIEND_API(bool)
-StringIsArrayIndex(JSLinearString *str, jsuint *indexp);
+StringIsArrayIndex(JSLinearString *str, uint32_t *indexp);
 
 JS_FRIEND_API(void)
 SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback);
 
 JS_FRIEND_API(bool)
 IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx);
 
 /*
@@ -653,17 +668,17 @@ SizeOfJSContext();
     /* Reasons internal to the JS engine */     \
     D(API)                                      \
     D(MAYBEGC)                                  \
     D(LAST_CONTEXT)                             \
     D(DESTROY_CONTEXT)                          \
     D(LAST_DITCH)                               \
     D(TOO_MUCH_MALLOC)                          \
     D(ALLOC_TRIGGER)                            \
-    D(UNUSED1) /* was CHUNK */                  \
+    D(DEBUG_GC)                                 \
     D(UNUSED2) /* was SHAPE */                  \
     D(UNUSED3) /* was REFILL */                 \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
     D(COMPONENT_UTILS)                          \
     D(MEM_PRESSURE)                             \
     D(CC_WAITING)                               \
@@ -803,16 +818,19 @@ class ObjectPtr
         return *this;
     }
 
     JSObject &operator*() const { return *value; }
     JSObject *operator->() const { return value; }
     operator JSObject *() const { return value; }
 };
 
+extern JS_FRIEND_API(JSObject *)
+GetTestingFunctions(JSContext *cx);
+
 } /* namespace js */
 
 #endif
 
 /* Implemented in jsdate.cpp. */
 
 /*
  * Detect whether the internal date value is NaN.  (Because failure is
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1089,23 +1089,23 @@ JSFunction::trace(JSTracer *trc)
     }
 
     if (isExtended()) {
         MarkValueRange(trc, ArrayLength(toExtended()->extendedSlots),
                        toExtended()->extendedSlots, "nativeReserved");
     }
 
     if (atom)
-        MarkStringUnbarriered(trc, atom, "atom");
+        MarkStringUnbarriered(trc, &atom, "atom");
 
     if (isInterpreted()) {
-        if (script())
-            MarkScript(trc, &script(), "script");
-        if (environment())
-            MarkObjectUnbarriered(trc, environment(), "fun_callscope");
+        if (u.i.script_)
+            MarkScriptUnbarriered(trc, &u.i.script_, "script");
+        if (u.i.env_)
+            MarkObjectUnbarriered(trc, &u.i.env_, "fun_callscope");
     }
 }
 
 static void
 fun_trace(JSTracer *trc, JSObject *obj)
 {
     obj->toFunction()->trace(trc);
 }
@@ -1291,17 +1291,17 @@ js_fun_apply(JSContext *cx, unsigned arg
         return false;
     }
 
     /*
      * Steps 4-5 (note erratum removing steps originally numbered 5 and 7 in
      * original version of ES5).
      */
     JSObject *aobj = &vp[3].toObject();
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, aobj, &length))
         return false;
 
     /* Step 6. */
     if (length > StackSpace::ARGS_LENGTH_MAX) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_TOO_MANY_FUN_APPLY_ARGS);
         return false;
     }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -659,17 +659,17 @@ Chunk::init()
     /* Initialize the chunk info. */
     info.freeArenasHead = &arenas[0].aheader;
     info.lastDecommittedArenaOffset = 0;
     info.numArenasFree = ArenasPerChunk;
     info.numArenasFreeCommitted = ArenasPerChunk;
     info.age = 0;
 
     /* Initialize the arena header state. */
-    for (jsuint i = 0; i < ArenasPerChunk; i++) {
+    for (unsigned i = 0; i < ArenasPerChunk; i++) {
         arenas[i].aheader.setAsNotAllocated();
         arenas[i].aheader.next = (i + 1 < ArenasPerChunk)
                                  ? &arenas[i + 1].aheader
                                  : NULL;
     }
 
     /* The rest of info fields are initialized in PickChunk. */
 }
@@ -719,37 +719,37 @@ Chunk::removeFromAvailableList()
 }
 
 /*
  * Search for and return the next decommitted Arena. Our goal is to keep
  * lastDecommittedArenaOffset "close" to a free arena. We do this by setting
  * it to the most recently freed arena when we free, and forcing it to
  * the last alloc + 1 when we allocate.
  */
-jsuint
+uint32_t
 Chunk::findDecommittedArenaOffset()
 {
     /* Note: lastFreeArenaOffset can be past the end of the list. */
-    for (jsuint i = info.lastDecommittedArenaOffset; i < ArenasPerChunk; i++)
+    for (unsigned i = info.lastDecommittedArenaOffset; i < ArenasPerChunk; i++)
         if (decommittedArenas.get(i))
             return i;
-    for (jsuint i = 0; i < info.lastDecommittedArenaOffset; i++)
+    for (unsigned i = 0; i < info.lastDecommittedArenaOffset; i++)
         if (decommittedArenas.get(i))
             return i;
     JS_NOT_REACHED("No decommitted arenas found.");
     return -1;
 }
 
 ArenaHeader *
 Chunk::fetchNextDecommittedArena()
 {
     JS_ASSERT(info.numArenasFreeCommitted == 0);
     JS_ASSERT(info.numArenasFree > 0);
 
-    jsuint offset = findDecommittedArenaOffset();
+    unsigned offset = findDecommittedArenaOffset();
     info.lastDecommittedArenaOffset = offset + 1;
     --info.numArenasFree;
     decommittedArenas.unset(offset);
 
     Arena *arena = &arenas[offset];
     MarkPagesInUse(arena, ArenaSize);
     arena->aheader.setAsNotAllocated();
 
@@ -1703,38 +1703,38 @@ ArenaLists::finalizeShapes(JSContext *cx
 
 void
 ArenaLists::finalizeScripts(JSContext *cx)
 {
     finalizeNow(cx, FINALIZE_SCRIPT);
 }
 
 static void
-RunLastDitchGC(JSContext *cx)
+RunLastDitchGC(JSContext *cx, gcreason::Reason reason)
 {
     JSRuntime *rt = cx->runtime;
 
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
-    GC(cx, rt->gcTriggerCompartment, GC_NORMAL, gcreason::LAST_DITCH);
+    GC(cx, rt->gcTriggerCompartment, GC_NORMAL, reason);
 }
 
 /* static */ void *
 ArenaLists::refillFreeList(JSContext *cx, AllocKind thingKind)
 {
     JS_ASSERT(cx->compartment->arenas.freeLists[thingKind].isEmpty());
 
     JSCompartment *comp = cx->compartment;
     JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->gcRunning);
 
     bool runGC = rt->gcIncrementalState != NO_INCREMENTAL && comp->gcBytes > comp->gcTriggerBytes;
     for (;;) {
         if (JS_UNLIKELY(runGC)) {
-            RunLastDitchGC(cx);
+            RunLastDitchGC(cx, gcreason::LAST_DITCH);
 
             /*
              * The JSGC_END callback can legitimately allocate new GC
              * things and populate the free list. If that happens, just
              * return that list head.
              */
             size_t thingSize = Arena::thingSize(thingKind);
             if (void *thing = comp->arenas.allocateFromFreeList(thingKind, thingSize))
@@ -3657,23 +3657,42 @@ GCCycle(JSContext *cx, JSCompartment *co
             JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread);
             cx->gcBackgroundFree = NULL;
             rt->gcHelperThread.startBackgroundSweep(cx, gckind == GC_SHRINK);
         }
     }
 #endif
 }
 
+#ifdef JS_GC_ZEAL
+static bool
+IsDeterministicGCReason(gcreason::Reason reason)
+{
+    if (reason > gcreason::DEBUG_GC && reason != gcreason::CC_FORCED)
+        return false;
+
+    if (reason == gcreason::MAYBEGC)
+        return false;
+
+    return true;
+}
+#endif
+
 static void
 Collect(JSContext *cx, JSCompartment *comp, int64_t budget,
         JSGCInvocationKind gckind, gcreason::Reason reason)
 {
     JSRuntime *rt = cx->runtime;
     JS_AbortIfWrongThread(rt);
 
+#ifdef JS_GC_ZEAL
+    if (rt->gcDeterministicOnly && !IsDeterministicGCReason(reason))
+        return;
+#endif
+
     JS_ASSERT_IF(budget != SliceBudget::Unlimited, JSGC_INCREMENTAL);
 
 #ifdef JS_GC_ZEAL
     struct AutoVerifyBarriers {
         JSContext *cx;
         bool inVerify;
         AutoVerifyBarriers(JSContext *cx) : cx(cx), inVerify(cx->runtime->gcVerifyData) {
             if (inVerify) EndVerifyBarriers(cx);
@@ -3942,17 +3961,26 @@ RunDebugGC(JSContext *cx)
     /*
      * If rt->gcDebugCompartmentGC is true, only GC the current
      * compartment. But don't GC the atoms compartment.
      */
     rt->gcTriggerCompartment = rt->gcDebugCompartmentGC ? cx->compartment : NULL;
     if (rt->gcTriggerCompartment == rt->atomsCompartment)
         rt->gcTriggerCompartment = NULL;
 
-    RunLastDitchGC(cx);
+    RunLastDitchGC(cx, gcreason::DEBUG_GC);
+#endif
+}
+
+void
+SetDeterministicGC(JSContext *cx, bool enabled)
+{
+#ifdef JS_GC_ZEAL
+    JSRuntime *rt = cx->runtime;
+    rt->gcDeterministicOnly = enabled;
 #endif
 }
 
 #if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 
 static void
 CheckStackRoot(JSTracer *trc, uintptr_t *w)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -803,17 +803,17 @@ struct Chunk {
         JS_ASSERT((addr & ChunkMask) == offsetof(Chunk, info.next));
         return reinterpret_cast<Chunk *>(addr - offsetof(Chunk, info.next));
     }
 
   private:
     inline void init();
 
     /* Search for a decommitted arena to allocate. */
-    jsuint findDecommittedArenaOffset();
+    unsigned findDecommittedArenaOffset();
     ArenaHeader* fetchNextDecommittedArena();
 
   public:
     /* Unlink and return the freeArenasHead. */
     inline ArenaHeader* fetchNextFreeArena(JSRuntime *rt);
 
     inline void addArenaToFreeList(JSRuntime *rt, ArenaHeader *aheader);
 };
@@ -1963,16 +1963,19 @@ namespace gc {
 
 JSCompartment *
 NewCompartment(JSContext *cx, JSPrincipals *principals);
 
 /* Tries to run a GC no matter what (used for GC zeal). */
 void
 RunDebugGC(JSContext *cx);
 
+void
+SetDeterministicGC(JSContext *cx, bool enabled);
+
 #if defined(JSGC_ROOT_ANALYSIS) && defined(DEBUG) && !defined(JS_THREADSAFE)
 /* Overwrites stack references to GC things which have not been rooted. */
 void CheckStackRoots(JSContext *cx);
 
 inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); }
 #else
 inline void MaybeCheckStackRoots(JSContext *cx) {}
 #endif
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -121,20 +121,20 @@ MarkInternal(JSTracer *trc, T *thing)
 #define JS_ROOT_MARKING_ASSERT(trc)                                     \
     JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc),                             \
                  trc->runtime->gcIncrementalState == NO_INCREMENTAL ||  \
                  trc->runtime->gcIncrementalState == MARK_ROOTS);
 
 
 template <typename T>
 static void
-MarkUnbarriered(JSTracer *trc, T *thing, const char *name)
+MarkUnbarriered(JSTracer *trc, T **thingp, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
-    MarkInternal(trc, thing);
+    MarkInternal(trc, *thingp);
 }
 
 template <typename T>
 static void
 Mark(JSTracer *trc, HeapPtr<T> *thing, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkInternal(trc, thing->get());
@@ -181,32 +181,33 @@ Mark##base(JSTracer *trc, HeapPtr<type> 
                                                                                                   \
 void                                                                                              \
 Mark##base##Root(JSTracer *trc, type **thingp, const char *name)                                  \
 {                                                                                                 \
     MarkRoot<type>(trc, thingp, name);                                                            \
 }                                                                                                 \
                                                                                                   \
 void                                                                                              \
-Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name)                             \
+Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name)                           \
 {                                                                                                 \
-    MarkUnbarriered<type>(trc, thing, name);                                                      \
+    MarkUnbarriered<type>(trc, thingp, name);                                                     \
 }                                                                                                 \
                                                                                                   \
 void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *vec, const char *name)           \
 {                                                                                                 \
     MarkRange<type>(trc, len, vec, name);                                                         \
 }                                                                                                 \
                                                                                                   \
 void Mark##base##RootRange(JSTracer *trc, size_t len, type **vec, const char *name)               \
 {                                                                                                 \
     MarkRootRange<type>(trc, len, vec, name);                                                     \
 }                                                                                                 \
 
 DeclMarkerImpl(BaseShape, BaseShape)
+DeclMarkerImpl(BaseShape, UnownedBaseShape)
 DeclMarkerImpl(Object, ArgumentsObject)
 DeclMarkerImpl(Object, GlobalObject)
 DeclMarkerImpl(Object, JSObject)
 DeclMarkerImpl(Object, JSFunction)
 DeclMarkerImpl(Script, JSScript)
 DeclMarkerImpl(Shape, Shape)
 DeclMarkerImpl(String, JSAtom)
 DeclMarkerImpl(String, JSString)
@@ -361,25 +362,35 @@ MarkValueRootRange(JSTracer *trc, size_t
 void
 MarkSlot(JSTracer *trc, HeapSlot *s, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkValueInternal(trc, s->unsafeGet());
 }
 
 void
-MarkSlotRange(JSTracer *trc, size_t len, HeapSlot *vec, const char *name)
+MarkArraySlots(JSTracer *trc, size_t len, HeapSlot *vec, const char *name)
 {
     for (size_t i = 0; i < len; ++i) {
         JS_SET_TRACING_INDEX(trc, name, i);
         MarkValueInternal(trc, vec[i].unsafeGet());
     }
 }
 
 void
+MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots)
+{
+    JS_ASSERT(obj->isNative());
+    for (uint32_t i = start; i < (start + nslots); ++i) {
+        JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
+        MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
+    }
+}
+
+void
 MarkCrossCompartmentSlot(JSTracer *trc, HeapSlot *s, const char *name)
 {
     if (s->isMarkable()) {
         Cell *cell = (Cell *)s->toGCThing();
         JSRuntime *rt = trc->runtime;
         if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
             return;
 
@@ -388,25 +399,21 @@ MarkCrossCompartmentSlot(JSTracer *trc, 
             return;
 
         MarkSlot(trc, s, name);
     }
 }
 
 /*** Special Marking ***/
 
-/*
- * The unioned HeapPtr stored in script->globalObj needs special treatment to
- * typecheck correctly.
- */
-static void
-MarkObject(JSTracer *trc, const HeapPtr<GlobalObject, JSScript *> &thing, const char *name)
+void
+MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
-    MarkInternal(trc, thing.get());
+    MarkInternal(trc, thingp->get());
 }
 
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkValueInternal(trc, v);
 }
@@ -639,123 +646,44 @@ PushMarkStack(GCMarker *gcmarker, JSStri
      */
     if (str->markIfUnmarked())
         ScanString(gcmarker, str);
 }
 
 void
 MarkChildren(JSTracer *trc, JSObject *obj)
 {
-    MarkTypeObject(trc, &obj->typeFromGC(), "type");
-
-    Shape *shape = obj->lastProperty();
-    MarkShapeUnbarriered(trc, shape, "shape");
-
-    Class *clasp = shape->getObjectClass();
-    if (clasp->trace)
-        clasp->trace(trc, obj);
-
-    if (shape->isNative()) {
-        uint32_t nslots = obj->slotSpan();
-        for (uint32_t i = 0; i < nslots; i++) {
-            JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
-            MarkValueInternal(trc, obj->nativeGetSlotRef(i).unsafeGet());
-        }
-    }
+    obj->markChildren(trc);
 }
 
 static void
 MarkChildren(JSTracer *trc, JSString *str)
 {
-    /*
-     * We use custom barriers in JSString, so it's safe to use unbarriered
-     * marking here.
-     */
-    if (str->isDependent()) {
-        MarkStringUnbarriered(trc, str->asDependent().base(), "base");
-    } else if (str->isRope()) {
-        JSRope &rope = str->asRope();
-        MarkStringUnbarriered(trc, rope.leftChild(), "left child");
-        MarkStringUnbarriered(trc, rope.rightChild(), "right child");
-    }
+    if (str->isDependent())
+        str->asDependent().markChildren(trc);
+    else if (str->isRope())
+        str->asRope().markChildren(trc);
 }
 
 static void
 MarkChildren(JSTracer *trc, JSScript *script)
 {
-    CheckScript(script, NULL);
-
-    JS_ASSERT_IF(trc->runtime->gcCheckCompartment,
-                 script->compartment() == trc->runtime->gcCheckCompartment);
-
-    for (uint32_t i = 0; i < script->natoms; ++i) {
-        if (JSAtom *p = script->atoms[i])
-            MarkStringUnbarriered(trc, p, "atom");
-    }
-
-    if (JSScript::isValidOffset(script->objectsOffset)) {
-        JSObjectArray *objarray = script->objects();
-        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
-    }
-
-    if (JSScript::isValidOffset(script->regexpsOffset)) {
-        JSObjectArray *objarray = script->regexps();
-        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
-    }
-
-    if (JSScript::isValidOffset(script->constOffset)) {
-        JSConstArray *constarray = script->consts();
-        MarkValueRange(trc, constarray->length, constarray->vector, "consts");
-    }
-
-    if (script->function())
-        MarkObjectUnbarriered(trc, script->function(), "function");
-
-    if (!script->isCachedEval && script->globalObject)
-        MarkObject(trc, script->globalObject, "object");
-
-    if (IS_GC_MARKING_TRACER(trc) && script->filename)
-        js_MarkScriptFilename(script->filename);
-
-    script->bindings.trace(trc);
-
-    if (script->types)
-        script->types->trace(trc);
-
-    if (script->hasAnyBreakpointsOrStepMode())
-        script->markTrapClosures(trc);
+    script->markChildren(trc);
 }
 
 static void
 MarkChildren(JSTracer *trc, Shape *shape)
 {
-    MarkBaseShapeUnbarriered(trc, shape->base(), "base");
-    MarkId(trc, &shape->propidRef(), "propid");
-    if (shape->previous())
-        MarkShape(trc, &shape->previousRef(), "parent");
-}
-
-static inline void
-MarkBaseShapeGetterSetter(JSTracer *trc, BaseShape *base)
-{
-    if (base->hasGetterObject())
-        MarkObjectUnbarriered(trc, base->getterObject(), "getter");
-    if (base->hasSetterObject())
-        MarkObjectUnbarriered(trc, base->setterObject(), "setter");
+    shape->markChildren(trc);
 }
 
 static void
 MarkChildren(JSTracer *trc, BaseShape *base)
 {
-    MarkBaseShapeGetterSetter(trc, base);
-    if (base->isOwned())
-        MarkBaseShapeUnbarriered(trc, base->baseUnowned(), "base");
-
-    if (JSObject *parent = base->getObjectParent())
-        MarkObjectUnbarriered(trc, parent, "parent");
+    base->markChildren(trc);
 }
 
 /*
  * This function is used by the cycle collector to trace through the
  * children of a BaseShape (and its baseUnowned(), if any). The cycle
  * collector does not directly care about BaseShapes, so only the
  * getter, setter, and parent are marked. Furthermore, the parent is
  * marked only if it isn't the same as prevParent, which will be
@@ -768,21 +696,32 @@ MarkCycleCollectorChildren(JSTracer *trc
 
     /*
      * The cycle collector does not need to trace unowned base shapes,
      * as they have the same getter, setter and parent as the original
      * base shape.
      */
     base->assertConsistency();
 
-    MarkBaseShapeGetterSetter(trc, base);
+    if (base->hasGetterObject()) {
+        JSObject *tmp = base->getterObject();
+        MarkObjectUnbarriered(trc, &tmp, "getter");
+        JS_ASSERT(tmp == base->getterObject());
+    }
+
+    if (base->hasSetterObject()) {
+        JSObject *tmp = base->setterObject();
+        MarkObjectUnbarriered(trc, &tmp, "setter");
+        JS_ASSERT(tmp == base->setterObject());
+    }
 
     JSObject *parent = base->getObjectParent();
     if (parent && parent != *prevParent) {
-        MarkObjectUnbarriered(trc, parent, "parent");
+        MarkObjectUnbarriered(trc, &parent, "parent");
+        JS_ASSERT(parent == base->getObjectParent());
         *prevParent = parent;
     }
 }
 
 /*
  * This function is used by the cycle collector to trace through a
  * shape. The cycle collector does not care about shapes or base
  * shapes, so those are not marked. Instead, any shapes or base shapes
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -42,21 +42,22 @@ namespace gc {
  *     are implemented for the given field.
  *
  * Additionally, the functions MarkObjectRange and MarkObjectRootRange are
  * defined for marking arrays of object pointers.
  */
 #define DeclMarker(base, type)                                                                    \
 void Mark##base(JSTracer *trc, HeapPtr<type> *thing, const char *name);                           \
 void Mark##base##Root(JSTracer *trc, type **thingp, const char *name);                            \
-void Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name);                       \
+void Mark##base##Unbarriered(JSTracer *trc, type **thingp, const char *name);                     \
 void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *thing, const char *name);        \
 void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name);
 
 DeclMarker(BaseShape, BaseShape)
+DeclMarker(BaseShape, UnownedBaseShape)
 DeclMarker(Object, ArgumentsObject)
 DeclMarker(Object, GlobalObject)
 DeclMarker(Object, JSObject)
 DeclMarker(Object, JSFunction)
 DeclMarker(Script, JSScript)
 DeclMarker(Shape, Shape)
 DeclMarker(String, JSAtom)
 DeclMarker(String, JSString)
@@ -115,27 +116,38 @@ MarkValueRootRange(JSTracer *trc, Value 
 }
 
 /*** Slot Marking ***/
 
 void
 MarkSlot(JSTracer *trc, HeapSlot *s, const char *name);
 
 void
-MarkSlotRange(JSTracer *trc, size_t len, HeapSlot *vec, const char *name);
+MarkArraySlots(JSTracer *trc, size_t len, HeapSlot *vec, const char *name);
+
+void
+MarkObjectSlots(JSTracer *trc, JSObject *obj, uint32_t start, uint32_t nslots);
 
 /*
  * Mark a value that may be in a different compartment from the compartment
  * being GC'd. (Although it won't be marked if it's in the wrong compartment.)
  */
 void
 MarkCrossCompartmentSlot(JSTracer *trc, HeapSlot *s, const char *name);
 
+
 /*** Special Cases ***/
 
+/*
+ * The unioned HeapPtr stored in script->globalObj needs special treatment to
+ * typecheck correctly.
+ */
+void
+MarkObject(JSTracer *trc, HeapPtr<GlobalObject, JSScript *> *thingp, const char *name);
+
 /* Direct value access used by the write barriers and the methodjit. */
 void
 MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
 
 /*
  * MarkChildren<JSObject> is exposed solely for preWriteBarrier on
  * JSObject::TradeGuts. It should not be considered external interface.
  */
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1307,46 +1307,52 @@ TypeObject::getGlobal()
 inline void
 TypeObject::writeBarrierPre(TypeObject *type)
 {
 #ifdef JSGC_INCREMENTAL
     if (!type)
         return;
 
     JSCompartment *comp = type->compartment();
-    if (comp->needsBarrier())
-        MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "write barrier");
+    if (comp->needsBarrier()) {
+        TypeObject *tmp = type;
+        MarkTypeObjectUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == type);
+    }
 #endif
 }
 
 inline void
 TypeObject::writeBarrierPost(TypeObject *type, void *addr)
 {
 }
 
 inline void
 TypeObject::readBarrier(TypeObject *type)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = type->compartment();
-    if (comp->needsBarrier())
-        MarkTypeObjectUnbarriered(comp->barrierTracer(), type, "read barrier");
+    if (comp->needsBarrier()) {
+        TypeObject *tmp = type;
+        MarkTypeObjectUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == type);
+    }
 #endif
 }
 
 inline void
 TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
 {
 #ifdef JSGC_INCREMENTAL
     if (!newScript)
         return;
 
     JSCompartment *comp = newScript->fun->compartment();
     if (comp->needsBarrier()) {
-        MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier");
+        MarkObject(comp->barrierTracer(), &newScript->fun, "write barrier");
         MarkShape(comp->barrierTracer(), &newScript->shape, "write barrier");
     }
 #endif
 }
 
 inline void
 TypeNewScript::writeBarrierPost(TypeNewScript *newScript, void *addr)
 {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -435,16 +435,31 @@ js::RunScript(JSContext *cx, JSScript *s
     /* FIXME: Once bug 470510 is fixed, make this an assert. */
     if (script->compileAndGo) {
         if (fp->scopeChain().global().isCleared()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CLEARED_SCOPE);
             return false;
         }
     }
 
+#ifdef DEBUG
+    struct CheckStackBalance {
+        JSContext *cx;
+        StackFrame *fp;
+        JSObject *enumerators;
+        CheckStackBalance(JSContext *cx)
+          : cx(cx), fp(cx->fp()), enumerators(cx->enumerators)
+        {}
+        ~CheckStackBalance() {
+            JS_ASSERT(fp == cx->fp());
+            JS_ASSERT_IF(!fp->isGeneratorFrame(), enumerators == cx->enumerators);
+        }
+    } check(cx);
+#endif
+
 #ifdef JS_METHODJIT
     mjit::CompileStatus status;
     status = mjit::CanMethodJIT(cx, script, script->code, fp->isConstructing(),
                                 mjit::CompileRequest_Interpreter);
     if (status == mjit::Compile_Error)
         return false;
 
     if (status == mjit::Compile_Okay)
@@ -506,22 +521,19 @@ js::InvokeKernel(JSContext *cx, CallArgs
         return false;
 
     /* Now that the new frame is rooted, maybe create a call object. */
     StackFrame *fp = ifg.fp();
     if (!fp->functionPrologue(cx))
         return false;
 
     /* Run function until JSOP_STOP, JSOP_RETURN or error. */
-    JSBool ok;
-    {
-        AutoPreserveEnumerators preserve(cx);
-        ok = RunScript(cx, fun->script(), fp);
-    }
-
+    JSBool ok = RunScript(cx, fun->script(), fp);
+
+    /* Propagate the return value out. */
     args.rval() = fp->returnValue();
     JS_ASSERT_IF(ok && construct, !args.rval().isPrimitive());
     return ok;
 }
 
 bool
 js::Invoke(JSContext *cx, const Value &thisv, const Value &fval, unsigned argc, Value *argv,
            Value *rval)
@@ -648,27 +660,27 @@ js::ExecuteKernel(JSContext *cx, JSScrip
     StackFrame *fp = efg.fp();
     if (fp->isStrictEvalFrame() && !CallObject::createForStrictEval(cx, fp))
         return false;
 
     Probes::startExecution(cx, script);
 
     TypeScript::SetThis(cx, script, fp->thisValue());
 
-    AutoPreserveEnumerators preserve(cx);
-    JSBool ok = RunScript(cx, script, fp);
-    if (result && ok)
-        *result = fp->returnValue();
+    bool ok = RunScript(cx, script, fp);
 
     if (fp->isStrictEvalFrame())
         js_PutCallObject(fp);
 
     Probes::stopExecution(cx, script);
 
-    return !!ok;
+    /* Propgate the return value out. */
+    if (result)
+        *result = fp->returnValue();
+    return ok;
 }
 
 bool
 js::Execute(JSContext *cx, JSScript *script, JSObject &scopeChainArg, Value *rval)
 {
     /* The scope chain could be anything, so innerize just in case. */
     JSObject *scopeChain = &scopeChainArg;
     OBJ_TO_INNER_OBJECT(cx, scopeChain);
@@ -972,41 +984,45 @@ js::UnwindScope(JSContext *cx, uint32_t 
         if (scopeChain.isClonedBlock())
             scopeChain.asClonedBlock().put(cx);
         else
             LeaveWith(cx);
     }
 }
 
 /*
- * Find the results of incrementing or decrementing *vp. For pre-increments,
- * both *vp and *vp2 will contain the result on return. For post-increments,
- * vp will contain the original value converted to a number and vp2 will get
- * the result. Both vp and vp2 must be roots.
+ * Increment/decrement the value 'v'. The resulting value is stored in *slot.
+ * The result of the expression (taking into account prefix/postfix) is stored
+ * in *expr.
  */
 static bool
-DoIncDec(JSContext *cx, const JSCodeSpec *cs, Value *vp, Value *vp2)
+DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
 {
-    if (cs->format & JOF_POST) {
-        double d;
-        if (!ToNumber(cx, *vp, &d))
-            return JS_FALSE;
-        vp->setNumber(d);
-        (cs->format & JOF_INC) ? ++d : --d;
-        vp2->setNumber(d);
-        return JS_TRUE;
+    const JSCodeSpec &cs = js_CodeSpec[*pc];
+
+    if (v.isInt32()) {
+        int32_t i = v.toInt32();
+        if (i > JSVAL_INT_MIN && i < JSVAL_INT_MAX) {
+            int32_t sum = i + (cs.format & JOF_INC ? 1 : -1);
+            *slot = Int32Value(sum);
+            *expr = (cs.format & JOF_POST) ? Int32Value(i) : *slot;
+            return true;
+        }
     }
 
     double d;
-    if (!ToNumber(cx, *vp, &d))
-        return JS_FALSE;
-    (cs->format & JOF_INC) ? ++d : --d;
-    vp->setNumber(d);
-    *vp2 = *vp;
-    return JS_TRUE;
+    if (!ToNumber(cx, *slot, &d))
+        return false;
+
+    double sum = d + (cs.format & JOF_INC ? 1 : -1);
+    *slot = NumberValue(sum);
+    *expr = (cs.format & JOF_POST) ? NumberValue(d) : *slot;
+
+    TypeScript::MonitorOverflow(cx, script, pc);
+    return true;
 }
 
 const Value &
 js::GetUpvar(JSContext *cx, unsigned closureLevel, UpvarCookie cookie)
 {
     JS_ASSERT(closureLevel >= cookie.level() && cookie.level() > 0);
     const unsigned targetLevel = closureLevel - cookie.level();
 
@@ -1066,41 +1082,24 @@ js::FindUpvarFrame(JSContext *cx, unsign
             b = vp->toBoolean();                                              \
         } else {                                                              \
             b = !!js_ValueToBoolean(*vp);                                     \
         }                                                                     \
     JS_END_MACRO
 
 #define POP_BOOLEAN(cx, vp, b)   do { VALUE_TO_BOOLEAN(cx, vp, b); regs.sp--; } while(0)
 
-#define VALUE_TO_OBJECT(cx, vp, obj)                                          \
-    JS_BEGIN_MACRO                                                            \
-        if ((vp)->isObject()) {                                               \
-            obj = &(vp)->toObject();                                          \
-        } else {                                                              \
-            obj = js_ValueToNonNullObject(cx, *(vp));                         \
-            if (!obj)                                                         \
-                goto error;                                                   \
-            (vp)->setObject(*obj);                                            \
-        }                                                                     \
-    JS_END_MACRO
-
 #define FETCH_OBJECT(cx, n, obj)                                              \
     JS_BEGIN_MACRO                                                            \
         Value *vp_ = &regs.sp[n];                                             \
-        VALUE_TO_OBJECT(cx, vp_, obj);                                        \
+        obj = ToObject(cx, (vp_));                                            \
+        if (!obj)                                                             \
+            goto error;                                                       \
     JS_END_MACRO
 
-/* Test whether v is an int in the range [-2^31 + 1, 2^31 - 2] */
-static JS_ALWAYS_INLINE bool
-CanIncDecWithoutOverflow(int32_t i)
-{
-    return (i > JSVAL_INT_MIN) && (i < JSVAL_INT_MAX);
-}
-
 /*
  * Threaded interpretation via computed goto appears to be well-supported by
  * GCC 3 and higher.  IBM's C compiler when run with the right options (e.g.,
  * -qlanglvl=extended) also supports threading.  Ditto the SunPro C compiler.
  * Currently it's broken for JS_VERSION < 160, though this isn't worth fixing.
  * Add your compiler support macros here.
  */
 #ifndef JS_THREADED_INTERP
@@ -2494,72 +2493,39 @@ BEGIN_CASE(JSOP_NAMEINC)
 BEGIN_CASE(JSOP_NAMEDEC)
 BEGIN_CASE(JSOP_INCGNAME)
 BEGIN_CASE(JSOP_DECGNAME)
 BEGIN_CASE(JSOP_GNAMEINC)
 BEGIN_CASE(JSOP_GNAMEDEC)
     /* No-op */
 END_CASE(JSOP_INCPROP)
 
-{
-    int incr, incr2;
-    uint32_t slot;
-    Value *vp;
-
-    /* Position cases so the most frequent i++ does not need a jump. */
 BEGIN_CASE(JSOP_DECARG)
-    incr = -1; incr2 = -1; goto do_arg_incop;
 BEGIN_CASE(JSOP_ARGDEC)
-    incr = -1; incr2 =  0; goto do_arg_incop;
 BEGIN_CASE(JSOP_INCARG)
-    incr =  1; incr2 =  1; goto do_arg_incop;
 BEGIN_CASE(JSOP_ARGINC)
-    incr =  1; incr2 =  0;
-
-  do_arg_incop:
-    slot = GET_ARGNO(regs.pc);
-    JS_ASSERT(slot < regs.fp()->numFormalArgs());
-    vp = argv + slot;
-    goto do_int_fast_incop;
+{
+    Value &arg = regs.fp()->formalArg(GET_ARGNO(regs.pc));
+    if (!DoIncDec(cx, script, regs.pc, arg, &arg, &regs.sp[0]))
+        goto error;
+    regs.sp++;
+}
+END_CASE(JSOP_ARGINC);
 
 BEGIN_CASE(JSOP_DECLOCAL)
-    incr = -1; incr2 = -1; goto do_local_incop;
 BEGIN_CASE(JSOP_LOCALDEC)
-    incr = -1; incr2 =  0; goto do_local_incop;
 BEGIN_CASE(JSOP_INCLOCAL)
-    incr =  1; incr2 =  1; goto do_local_incop;
 BEGIN_CASE(JSOP_LOCALINC)
-    incr =  1; incr2 =  0;
-
-  /*
-   * do_local_incop comes right before do_int_fast_incop as we want to
-   * avoid an extra jump for variable cases as local++ is more frequent
-   * than arg++.
-   */
-  do_local_incop:
-    slot = GET_SLOTNO(regs.pc);
-    JS_ASSERT(slot < regs.fp()->numSlots());
-    vp = regs.fp()->slots() + slot;
-
-  do_int_fast_incop:
-    int32_t tmp;
-    if (JS_LIKELY(vp->isInt32() && CanIncDecWithoutOverflow(tmp = vp->toInt32()))) {
-        vp->getInt32Ref() = tmp + incr;
-        JS_ASSERT(JSOP_INCARG_LENGTH == js_CodeSpec[op].length);
-        PUSH_INT32(tmp + incr2);
-    } else {
-        PUSH_COPY(*vp);
-        if (!DoIncDec(cx, &js_CodeSpec[op], &regs.sp[-1], vp))
-            goto error;
-        TypeScript::MonitorOverflow(cx, script, regs.pc);
-    }
-    len = JSOP_INCARG_LENGTH;
-    JS_ASSERT(len == js_CodeSpec[op].length);
-    DO_NEXT_OP(len);
+{
+    Value &local = regs.fp()->localSlot(GET_SLOTNO(regs.pc));
+    if (!DoIncDec(cx, script, regs.pc, local, &local, &regs.sp[0]))
+        goto error;
+    regs.sp++;
 }
+END_CASE(JSOP_LOCALINC)
 
 BEGIN_CASE(JSOP_THIS)
     if (!ComputeThis(cx, regs.fp()))
         goto error;
     PUSH_COPY(regs.fp()->thisValue());
 END_CASE(JSOP_THIS)
 
 BEGIN_CASE(JSOP_GETPROP)
@@ -2910,17 +2876,17 @@ BEGIN_CASE(JSOP_TABLESWITCH)
     }
 
     pc2 += JUMP_OFFSET_LEN;
     int32_t low = GET_JUMP_OFFSET(pc2);
     pc2 += JUMP_OFFSET_LEN;
     int32_t high = GET_JUMP_OFFSET(pc2);
 
     i -= low;
-    if ((jsuint)i < (jsuint)(high - low + 1)) {
+    if ((uint32_t)i < (uint32_t)(high - low + 1)) {
         pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i;
         int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
         if (off)
             len = off;
     }
 }
 END_VARLEN_CASE
 }
@@ -3562,19 +3528,19 @@ BEGIN_CASE(JSOP_INITELEM)
     /*
      * If rref is a hole, do not call JSObject::defineProperty. In this case,
      * obj must be an array, so if the current op is the last element
      * initialiser, set the array length to one greater than id.
      */
     if (rref.isMagic(JS_ARRAY_HOLE)) {
         JS_ASSERT(obj->isArray());
         JS_ASSERT(JSID_IS_INT(id));
-        JS_ASSERT(jsuint(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
+        JS_ASSERT(uint32_t(JSID_TO_INT(id)) < StackSpace::ARGS_LENGTH_MAX);
         if (JSOp(regs.pc[JSOP_INITELEM_LENGTH]) == JSOP_ENDINIT &&
-            !js_SetLengthProperty(cx, obj, (jsuint) (JSID_TO_INT(id) + 1))) {
+            !js_SetLengthProperty(cx, obj, (uint32_t) (JSID_TO_INT(id) + 1))) {
             goto error;
         }
     } else {
         if (!obj->defineGeneric(cx, id, rref, NULL, NULL, JSPROP_ENUMERATE))
             goto error;
     }
     regs.sp -= 2;
 }
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -56,31 +56,16 @@
 #include "jsinferinlines.h"
 #include "jspropertycacheinlines.h"
 #include "jstypedarrayinlines.h"
 
 #include "vm/Stack-inl.h"
 
 namespace js {
 
-class AutoPreserveEnumerators {
-    JSContext *cx;
-    JSObject *enumerators;
-
-  public:
-    AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
-    {
-    }
-
-    ~AutoPreserveEnumerators()
-    {
-        cx->enumerators = enumerators;
-    }
-};
-
 /*
  * Compute the implicit |this| parameter for a call expression where the callee
  * funval was resolved from an unqualified name reference to a property on obj
  * (an object on the scope chain).
  *
  * We can avoid computing |this| eagerly and push the implicit callee-coerced
  * |this| value, undefined, if any of these conditions hold:
  *
@@ -227,17 +212,17 @@ GetPropertyOperation(JSContext *cx, jsby
         }
         if (lval.isMagic(JS_LAZY_ARGUMENTS)) {
             *vp = Int32Value(cx->fp()->numActualArgs());
             return true;
         }
         if (lval.isObject()) {
             JSObject *obj = &lval.toObject();
             if (obj->isArray()) {
-                jsuint length = obj->getArrayLength();
+                uint32_t length = obj->getArrayLength();
                 *vp = NumberValue(length);
                 return true;
             }
 
             if (obj->isArguments()) {
                 ArgumentsObject *argsobj = &obj->asArguments();
                 if (!argsobj->hasOverriddenLength()) {
                     uint32_t length = argsobj->initialLength();
@@ -250,17 +235,17 @@ GetPropertyOperation(JSContext *cx, jsby
             if (js_IsTypedArray(obj)) {
                 JSObject *tarray = TypedArray::getTypedArray(obj);
                 *vp = Int32Value(TypedArray::getLength(tarray));
                 return true;
             }
         }
     }
 
-    JSObject *obj = ValueToObjectOrPrototype(cx, lval);
+    JSObject *obj = ValueToObject(cx, lval);
     if (!obj)
         return false;
 
     unsigned flags = (op == JSOP_CALLPROP)
                   ? JSGET_CACHE_RESULT | JSGET_NO_METHOD_BARRIER
                   : JSGET_CACHE_RESULT | JSGET_METHOD_BARRIER;
 
     PropertyCacheEntry *entry;
@@ -799,23 +784,23 @@ GetElementOperation(JSContext *cx, const
 
 static JS_ALWAYS_INLINE bool
 SetObjectElementOperation(JSContext *cx, JSObject *obj, jsid id, const Value &value, bool strict)
 {
     types::TypeScript::MonitorAssign(cx, obj, id);
 
     do {
         if (obj->isDenseArray() && JSID_IS_INT(id)) {
-            jsuint length = obj->getDenseArrayInitializedLength();
+            uint32_t length = obj->getDenseArrayInitializedLength();
             int32_t i = JSID_TO_INT(id);
-            if ((jsuint)i < length) {
+            if ((uint32_t)i < length) {
                 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
                     if (js_PrototypeHasIndexedProperties(cx, obj))
                         break;
-                    if ((jsuint)i >= obj->getArrayLength())
+                    if ((uint32_t)i >= obj->getArrayLength())
                         obj->setArrayLength(cx, i + 1);
                 }
                 obj->setDenseArrayElementWithType(cx, i, value);
                 return true;
             } else {
                 JSScript *script;
                 jsbytecode *pc;
                 types::TypeScript::GetPcScript(cx, &script, &pc);
--- a/js/src/jsmath.cpp
+++ b/js/src/jsmath.cpp
@@ -430,17 +430,17 @@ js_math_min(JSContext *cx, unsigned argc
     }
     vp->setNumber(z);
     return JS_TRUE;
 }
 
 static double
 powi(double x, int y)
 {
-    jsuint n = (y < 0) ? -y : y;
+    unsigned n = (y < 0) ? -y : y;
     double m = x;
     double p = 1;
     while (true) {
         if ((n & 1) != 0) p *= m;
         n >>= 1;
         if (n == 0) {
             if (y < 0) {
                 // Unfortunately, we have to be careful when p has reached
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -73,21 +73,21 @@
 
 #include "vm/GlobalObject.h"
 #include "vm/MethodGuard.h"
 
 #include "jsatominlines.h"
 #include "jsinferinlines.h"
 #include "jsnuminlines.h"
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/MethodGuard-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/String-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using namespace js::types;
 
 /*
  * If we're accumulating a decimal number and the number is >= 2^53, then the
  * fast result from the loop in GetPrefixInteger may be inaccurate. Call
  * js_strtod_harder to get the correct answer.
@@ -572,37 +572,37 @@ js_IntToString(JSContext *cx, int32_t si
     c->dtoaCache.cache(10, si, str);
     return str;
 }
 
 /* Returns a non-NULL pointer to inside cbuf.  */
 static char *
 IntToCString(ToCStringBuf *cbuf, int i, int base = 10)
 {
-    jsuint u = (i < 0) ? -i : i;
+    unsigned u = (i < 0) ? -i : i;
 
     RangedPtr<char> cp(cbuf->sbuf + cbuf->sbufSize - 1, cbuf->sbuf, cbuf->sbufSize);
     *cp = '\0';
 
     /* Build the string from behind. */
     switch (base) {
     case 10:
       cp = BackfillIndexInCharBuffer(u, cp);
       break;
     case 16:
       do {
-          jsuint newu = u / 16;
+          unsigned newu = u / 16;
           *--cp = "0123456789abcdef"[u - newu * 16];
           u = newu;
       } while (u != 0);
       break;
     default:
       JS_ASSERT(base >= 2 && base <= 36);
       do {
-          jsuint newu = u / base;
+          unsigned newu = u / base;
           *--cp = "0123456789abcdefghijklmnopqrstuvwxyz"[u - newu * base];
           u = newu;
       } while (u != 0);
       break;
     }
     if (i < 0)
         *--cp = '-';
 
@@ -1122,17 +1122,17 @@ js_NumberToStringWithBase(JSContext *cx,
         return NULL;
 
     JSCompartment *c = cx->compartment;
 
     int32_t i;
     if (JSDOUBLE_IS_INT32(d, &i)) {
         if (base == 10 && StaticStrings::hasInt(i))
             return cx->runtime->staticStrings.getInt(i);
-        if (jsuint(i) < jsuint(base)) {
+        if (unsigned(i) < unsigned(base)) {
             if (i < 10)
                 return cx->runtime->staticStrings.getInt(i);
             jschar c = 'a' + i - 10;
             JS_ASSERT(StaticStrings::hasUnit(c));
             return cx->runtime->staticStrings.getUnit(c);
         }
 
         if (JSFlatString *str = c->dtoaCache.lookup(base, d))
@@ -1341,17 +1341,17 @@ ValueToUint16Slow(JSContext *cx, const V
     if ((double)u == d) {
         *out = u;
         return true;
     }
 
     bool neg = (d < 0);
     d = floor(neg ? -d : d);
     d = neg ? -d : d;
-    jsuint m = JS_BIT(16);
+    unsigned m = JS_BIT(16);
     d = fmod(d, (double) m);
     if (d < 0)
         d += m;
     *out = (uint16_t) d;
     return true;
 }
 
 }  /* namespace js */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -84,19 +84,19 @@
 #include "js/MemoryMetrics.h"
 
 #include "jsarrayinlines.h"
 #include "jsatominlines.h"
 #include "jsinterpinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/MethodGuard-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #if JS_HAS_XDR
 #include "jsxdrapi.h"
 #endif
@@ -2189,17 +2189,17 @@ DefinePropertyOnArray(JSContext *cx, JSO
      * the descriptor describes a traditional array property (enumerable,
      * configurable, writable, numeric index or length without altering its
      * attributes).  Such definitions are probably unlikely, so we don't bother
      * for now.
      */
     if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
         return JS_FALSE;
 
-    jsuint oldLen = obj->getArrayLength();
+    uint32_t oldLen = obj->getArrayLength();
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         /*
          * Our optimization of storage of the length property of arrays makes
          * it very difficult to properly implement defining the property.  For
          * now simply throw an exception (NB: not merely Reject) on any attempt
          * to define the "length" property, rather than attempting to implement
          * some difficult-for-authors-to-grasp subset of that functionality.
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -690,17 +690,17 @@ struct JSObject : public js::ObjectImpl
     /*
      * Marks this object as having a singleton type, and leave the type lazy.
      * Constructs a new, unique shape for the object.
      */
     inline bool setSingletonType(JSContext *cx);
 
     inline js::types::TypeObject *getType(JSContext *cx);
 
-    js::HeapPtr<js::types::TypeObject> &typeFromGC() {
+    const js::HeapPtr<js::types::TypeObject> &typeFromGC() const {
         /* Direct field access for use by GC. */
         return type_;
     }
 
     inline void setType(js::types::TypeObject *newType);
 
     js::types::TypeObject *getNewType(JSContext *cx, JSFunction *fun = NULL);
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -647,33 +647,16 @@ inline bool
 JSObject::denseArrayHasInlineSlots() const
 {
     JS_ASSERT(isDenseArray());
     return elements == fixedElements();
 }
 
 namespace js {
 
-inline JSObject *
-ValueToObjectOrPrototype(JSContext *cx, const Value &v)
-{
-    if (v.isObject())
-        return &v.toObject();
-    GlobalObject *global = &cx->fp()->scopeChain().global();
-    if (v.isString())
-        return global->getOrCreateStringPrototype(cx);
-    if (v.isNumber())
-        return global->getOrCreateNumberPrototype(cx);
-    if (v.isBoolean())
-        return global->getOrCreateBooleanPrototype(cx);
-    JS_ASSERT(v.isNull() || v.isUndefined());
-    js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
-    return NULL;
-}
-
 /*
  * Any name atom for a function which will be added as a DeclEnv object to the
  * scope chain above call objects for fun.
  */
 static inline JSAtom *
 CallObjectLambdaName(JSFunction *fun)
 {
     return (fun->flags & JSFUN_LAMBDA) ? fun->atom : NULL;
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -59,19 +59,19 @@
 #include "jsxml.h"
 
 #include "frontend/TokenStream.h"
 
 #include "jsatominlines.h"
 #include "jsboolinlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/Stack-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 Class js::JSONClass = {
     js_JSON_str,
     JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
@@ -495,17 +495,17 @@ JA(JSContext *cx, JSObject *obj, Stringi
     CycleDetector detect(scx, obj);
     if (!detect.init(cx))
         return JS_FALSE;
 
     if (!scx->sb.append('['))
         return JS_FALSE;
 
     /* Step 6. */
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
 
     /* Steps 7-10. */
     if (length != 0) {
         /* Steps 4, 10b(i). */
         if (!WriteIndent(cx, scx, scx->depth))
             return JS_FALSE;
@@ -644,27 +644,27 @@ js_Stringify(JSContext *cx, Value *vp, J
              *            or "Number" then let item be ToString(v).
              *      6. If item is not undefined and item is not currently an
              *         element of PropertyList then,
              *         a. Append item to the end of PropertyList.
              *      7. Let i be i + 1.
              */
 
             /* Step 4b(ii). */
-            jsuint len;
+            uint32_t len;
             JS_ALWAYS_TRUE(js_GetLengthProperty(cx, replacer, &len));
             if (replacer->isDenseArray())
                 len = JS_MIN(len, replacer->getDenseArrayCapacity());
 
             HashSet<jsid> idSet(cx);
             if (!idSet.init(len))
                 return false;
 
             /* Step 4b(iii). */
-            jsuint i = 0;
+            uint32_t i = 0;
 
             /* Step 4b(iv). */
             for (; i < len; i++) {
                 /* Step 4b(iv)(2). */
                 Value v;
                 if (!replacer->getElement(cx, i, &v))
                     return false;
 
--- a/js/src/jsonparser.cpp
+++ b/js/src/jsonparser.cpp
@@ -38,17 +38,18 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsarray.h"
 #include "jsnum.h"
 #include "jsonparser.h"
 
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
+
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 
 void
 JSONParser::error(const char *msg)
 {
     if (errorHandling == RaiseError)
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE, msg);
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -72,21 +72,21 @@
 
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/TokenStream.h"
 #include "vm/Debugger.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 #include "jsopcodeinlines.h"
-#include "jsscriptinlines.h"
 
 #include "jsautooplen.h"
 
 #include "vm/RegExpObject-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 
 /*
  * Index limit must stay within 32 bits.
  */
@@ -1820,30 +1820,30 @@ GetLocal(SprintStack *ss, int i)
         JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length));
 
         if (JSOP_ENTERBLOCK == (JSOp)*pc) {
             JSObject *obj = script->getObject(GET_UINT32_INDEX(pc));
 
             if (obj->isBlock()) {
                 uint32_t depth = obj->asBlock().stackDepth();
                 uint32_t count = obj->asBlock().slotCount();
-                if (jsuint(i - depth) < jsuint(count))
+                if (uint32_t(i - depth) < uint32_t(count))
                     return GetLocalInSlot(ss, i, int(i - depth), obj);
             }
         }
     }
 
     // Iterate over all objects.
     for (jsatomid j = 0, n = script->objects()->length; j != n; j++) {
         JSObject *obj = script->getObject(j);
 
         if (obj->isBlock()) {
             uint32_t depth = obj->asBlock().stackDepth();
             uint32_t count = obj->asBlock().slotCount();
-            if (jsuint(i - depth) < jsuint(count))
+            if (uint32_t(i - depth) < uint32_t(count))
                 return GetLocalInSlot(ss, i, int(i - depth), obj);
         }
     }
 
     return GetStr(ss, i);
 }
 
 #undef LOCAL_ASSERT
@@ -2441,25 +2441,20 @@ SprintNormalFor(JSContext *cx, JSPrinter
     JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_POP);
     pc += JSOP_NOP_LENGTH;
 
     /* Get the cond, next, and loop-closing tail offsets. */
     ptrdiff_t cond = js_GetSrcNoteOffset(sn, 0);
     ptrdiff_t next = js_GetSrcNoteOffset(sn, 1);
     ptrdiff_t tail = js_GetSrcNoteOffset(sn, 2);
 
-    /*
-     * If this loop has a condition, then pc points at a goto
-     * targeting the condition.
-     */
+    /* Find the loop head, skipping over any leading GOTO or NOP. */
     jsbytecode *pc2 = pc;
-    if (cond != tail) {
-        LOCAL_ASSERT(*pc == JSOP_GOTO);
-        pc2 += JSOP_GOTO_LENGTH;
-    }
+    if (*pc == JSOP_GOTO || *pc == JSOP_NOP)
+        pc2 += GetBytecodeLength(pc);
     LOCAL_ASSERT(tail + GET_JUMP_OFFSET(pc + tail) == pc2 - pc);
 
     if (cond != tail) {
         /* Decompile the loop condition. */
         if (!Decompile(ss, pc + cond, tail - cond))
             return -1;
         js_printf(jp, " ");
         jsbytecode *condpc;
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -145,34 +145,37 @@ Shape::removeChild(Shape *child)
 
     KidsHash *hash = kidp->toHash();
     JS_ASSERT(hash->count() >= 2);      /* otherwise kidp->isShape() should be true */
 
     hash->remove(child);
 
     if (hash->count() == 1) {
         /* Convert from HASH form back to SHAPE form. */
-        KidsHash::Range r = hash->all(); 
+        KidsHash::Range r = hash->all();
         Shape *otherChild = r.front();
         JS_ASSERT((r.popFront(), r.empty()));    /* No more elements! */
         kidp->setShape(otherChild);
         js::UnwantedForeground::delete_(hash);
     }
 }
 
 /*
  * We need a read barrier for the shape tree, since these are weak pointers.
  */
 static Shape *
 ReadBarrier(Shape *shape)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = shape->compartment();
-    if (comp->needsBarrier())
-        MarkShapeUnbarriered(comp->barrierTracer(), shape, "read barrier");
+    if (comp->needsBarrier()) {
+        Shape *tmp = shape;
+        MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == shape);
+    }
 #endif
     return shape;
 }
 
 Shape *
 PropertyTree::getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child)
 {
     Shape *shape;
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -489,21 +489,21 @@ static bool
 ArrayToIdVector(JSContext *cx, const Value &array, AutoIdVector &props)
 {
     JS_ASSERT(props.length() == 0);
 
     if (array.isPrimitive())
         return true;
 
     JSObject *obj = &array.toObject();
-    jsuint length;
+    uint32_t length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
 
-    for (jsuint n = 0; n < length; ++n) {
+    for (uint32_t n = 0; n < length; ++n) {
         if (!JS_CHECK_OPERATION_LIMIT(cx))
             return false;
         Value v;
         if (!obj->getElement(cx, n, &v))
             return false;
         jsid id;
         if (!ValueToId(cx, v, &id))
             return false;
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -88,19 +88,16 @@ typedef ptrdiff_t jsid;
 # endif  /* defined(JS_USE_JSID_STRUCT_TYPES) */
 #else  /* defined(__cplusplus) */
 typedef ptrdiff_t jsid;
 # define JSID_BITS(id) (id)
 #endif
 
 JS_BEGIN_EXTERN_C
 
-/* Scalar typedefs. */
-typedef uint32_t  jsuint;
-
 #ifdef WIN32
 typedef wchar_t   jschar;
 #else
 typedef uint16_t  jschar;
 #endif
 
 /*
  * Run-time version enumeration.  See jsversion.h for compile-time counterparts
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -639,17 +639,17 @@ JSObject::addPropertyInternal(JSContext 
 
     JS_ASSERT(!!table == !!spp);
 
     /* Find or create a property tree node labeled by our arguments. */
     Shape *shape;
     {
         shape = self->lastProperty();
 
-        jsuint index;
+        uint32_t index;
         bool indexed = js_IdIsIndex(id, &index);
         UnownedBaseShape *nbase;
         if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
             nbase = shape->base()->unowned();
         } else {
             StackBaseShape base(shape->base());
             base.updateGetterSetter(attrs, getter, setter);
             if (indexed)
@@ -753,17 +753,17 @@ JSObject::putProperty(JSContext *cx, jsi
      */
     bool hadSlot = shape->hasSlot();
     uint32_t oldSlot = shape->maybeSlot();
     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
         slot = oldSlot;
 
     RootedVar<UnownedBaseShape*> nbase(cx);
     {
-        jsuint index;
+        uint32_t index;
         bool indexed = js_IdIsIndex(id, &index);
         StackBaseShape base(self->lastProperty()->base());
         base.updateGetterSetter(attrs, getter, setter);
         if (indexed)
             base.flags |= BaseShape::INDEXED;
         nbase = BaseShape::getUnowned(cx, base);
         if (!nbase)
             return NULL;
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -385,16 +385,18 @@ class BaseShape : public js::gc::Cell
     static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
 
     static inline void writeBarrierPre(BaseShape *shape);
     static inline void writeBarrierPost(BaseShape *shape, void *addr);
     static inline void readBarrier(BaseShape *shape);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
 
+    inline void markChildren(JSTracer *trc);
+
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
     }
 };
 
 class UnownedBaseShape : public BaseShape {};
 
@@ -558,20 +560,16 @@ struct Shape : public js::gc::Cell
         JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
         return !(flags & NON_NATIVE);
     }
 <