Merge mc->Maple
authorBenoit Girard <b56girard@gmail.com>
Wed, 07 Mar 2012 11:10:45 -0500
changeset 89306 c7e65c5fa1469a5939cf3f8b236a29b86e786ea0
parent 89304 3be651e9ac2aa795d03c66f5434bf430aca06871 (current diff)
parent 88426 3dcb40ebd4873c77b0708b3089979fef6bd946dd (diff)
child 89307 4012267a4f08f2b689da1bb6e1ee426c4fb3805e
push id22242
push userkgupta@mozilla.com
push dateWed, 14 Mar 2012 15:19:09 +0000
treeherdermozilla-central@936ef50fa498 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge 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());