Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Tue, 06 Mar 2012 14:08:51 -0800
changeset 112241 1edb33d3750780b9130f196ef6b5e2e2bed14368
parent 112240 9c866973398c487baf4771af084ffc1c9cfd29f2 (current diff)
parent 91210 c1e5daa36ab201d3008b953ed09f4c542c8b956a (diff)
child 112242 dce272d6ef91bbdccfe7a5dab86b7cde61e2cddf
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [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 from mozilla-central.
configure.in
content/base/src/nsFrameMessageManager.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsJSEnvironment.cpp
gfx/layers/basic/BasicLayers.cpp
gfx/layers/ipc/ShadowLayersParent.cpp
gfx/layers/opengl/LayerManagerOGL.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/Makefile.in
js/src/frontend/BytecodeEmitter.cpp
js/src/ion/Ion.cpp
js/src/ion/arm/Assembler-arm.cpp
js/src/ion/shared/Assembler-x86-shared.cpp
js/src/ion/x64/Assembler-x64.cpp
js/src/ion/x86/Assembler-x86.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsbool.cpp
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsgcmark.cpp
js/src/jsgcmark.h
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/vm/Debugger.cpp
js/src/vm/RegExpObject.cpp
js/src/vm/Stack.cpp
js/src/vm/String.cpp
js/src/vm/String.h
js/xpconnect/idl/nsIXPConnect.idl
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCConvert.cpp
js/xpconnect/src/XPCInlines.h
js/xpconnect/src/XPCJSID.cpp
js/xpconnect/src/XPCMaps.cpp
js/xpconnect/src/XPCMaps.h
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCRuntimeService.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
memory/jemalloc/jemalloc.c
mobile/android/base/GeckoApp.java
services/sync/tests/unit/xpcshell.ini
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/mozapps/readstrings/Makefile.in
toolkit/mozapps/readstrings/errors.h
toolkit/mozapps/readstrings/readstrings.cpp
toolkit/mozapps/readstrings/readstrings.h
toolkit/mozapps/update/nsUpdateService.js
xpcom/base/nsCycleCollector.cpp
xpcom/base/nsStackWalk.cpp
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -379,24 +379,21 @@
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gInitQueue = null;
     function initTests()
     {
       if (SEAMONKEY) {
         todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237)");
+        shutdownAutoComplete();
         SimpleTest.finish();
         return;
       }
 
-      // register 'test-a11y-search' autocomplete search
-      initAutoComplete([ "hello", "hi" ],
-                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
-
       gInitQueue = new eventQueue();
       gInitQueue.push(new loadFormAutoComplete("iframe"));
       gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
       gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
       gInitQueue.push(new loadHTML5ListAutoComplete("iframe2"));
       gInitQueue.onFinish = function initQueue_onFinish()
       {
         SimpleTest.executeSoon(doTests);
@@ -456,16 +453,22 @@
       {
         // unregister 'test-a11y-search' autocomplete search
         shutdownAutoComplete();
       }
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
+
+    // Register 'test-a11y-search' autocomplete search.
+    // XPFE AutoComplete needs to register early.
+    initAutoComplete([ "hello", "hi" ],
+                     [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
     addA11yLoadEvent(initTests);
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=383759"
--- a/accessible/tests/mochitest/states/test_expandable.xul
+++ b/accessible/tests/mochitest/states/test_expandable.xul
@@ -30,20 +30,16 @@
 
   <script type="application/javascript">
   <![CDATA[
     //gA11yEventDumpToConsole = true; // debuggin
 
     var gQueue = null;
     function doTest()
     {
-      // register 'test-a11y-search' autocomplete search
-      initAutoComplete([ "hello", "hi" ],
-                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
-
       gQueue = new eventQueue();
 
       gQueue.push(new openCombobox("menulist"));
       gQueue.push(new closeCombobox("menulist"));
 
       todo(false, "Autocompletes don't fire expanded state change events when popup open. See bug 688480!");
       //gQueue.push(new openCombobox("autocomplete"));
       //gQueue.push(new closeCombobox("autocomplete"));
@@ -68,16 +64,22 @@
     function getBrowser()
     {
       return {
         mCurrentBrowser: { engines: new Array() }
       };
     }
 
     SimpleTest.waitForExplicitFinish();
+
+    // Register 'test-a11y-search' autocomplete search.
+    // XPFE AutoComplete needs to register early.
+    initAutoComplete([ "hello", "hi" ],
+                     [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <hbox style="overflow: auto;" flex="1">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=467057"
--- a/browser/base/content/test/browser_aboutSyncProgress.js
+++ b/browser/base/content/test/browser_aboutSyncProgress.js
@@ -1,14 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-sync/main.js");
 
 let gTests = [ {
   desc: "Makes sure the progress bar appears if firstSync pref is set",
   setup: function () {
     Services.prefs.setCharPref("services.sync.firstSync", "newAccount");
   },
   run: function () {
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -1219,17 +1219,17 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   margin: 1px 3px;
   padding: 0;
   background-clip: padding-box;
   border: 1px solid ThreeDShadow;
-  border-radius: 2.5px;
+  border-radius: 2px;
 }
 
 #urlbar {
   width: 7em;
   min-width: 7em;
   -moz-padding-end: 2px;
 }
 
--- a/browser/themes/winstripe/places/organizer-aero.css
+++ b/browser/themes/winstripe/places/organizer-aero.css
@@ -69,11 +69,11 @@
   }
 
   #searchFilter {
     -moz-appearance: none;
     padding: 2px;
     -moz-padding-start: 4px;
     background-clip: padding-box;
     border: 1px solid rgba(0,0,0,.32);
-    border-radius: 2.5px;
+    border-radius: 2px;
   }
 }
--- a/configure.in
+++ b/configure.in
@@ -9207,17 +9207,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/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/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/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/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -68,16 +68,17 @@ EXPORTS = \
         ReadbackLayer.h \
         LayerSorter.h \
         $(NULL)
 
 CPPSRCS = \
         BasicImages.cpp \
         BasicLayers.cpp \
         Layers.cpp \
+        RenderTrace.cpp \
         ReadbackProcessor.cpp \
         ThebesLayerBuffer.cpp \
         CanvasLayerOGL.cpp \
         ColorLayerOGL.cpp \
         ContainerLayerOGL.cpp \
         ImageLayerOGL.cpp \
         LayerManagerOGL.cpp \
         ThebesLayerOGL.cpp \
new file mode 100644
--- /dev/null
+++ b/gfx/layers/RenderTrace.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "Layers.h"
+#include "RenderTrace.h"
+
+// If rendertrace is off let's no compile this code
+#ifdef MOZ_RENDERTRACE
+
+
+namespace mozilla {
+namespace layers {
+
+static int colorId = 0;
+
+// This should be done in the printf but android's printf is buggy
+const char* colors[] = {
+    "00", "01", "02", "03", "04", "05", "06", "07", "08", "09",
+    "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"
+    };
+
+static gfx3DMatrix GetRootTransform(Layer *aLayer) {
+  gfx3DMatrix layerTrans = aLayer->GetTransform().ProjectTo2D();
+  if (aLayer->GetParent() != NULL) {
+    return GetRootTransform(aLayer->GetParent()) * layerTrans;
+  }
+  return layerTrans;
+}
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, const gfx3DMatrix aRootTransform, bool aReset) {
+  if (!aLayer)
+    return;
+
+  gfx3DMatrix trans = aRootTransform * aLayer->GetTransform().ProjectTo2D();
+  nsIntRect clipRect = aLayer->GetEffectiveVisibleRegion().GetBounds();
+  gfxRect rect(clipRect.x, clipRect.y, clipRect.width, clipRect.height);
+  trans.TransformBounds(rect);
+
+  printf_stderr("%s RENDERTRACE %u rect #%02X%s %i %i %i %i\n",
+    aLayer->Name(), (int)PR_IntervalNow(),
+    colorId, aColor,
+    (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+
+  colorId++;
+
+  for (Layer* child = aLayer->GetFirstChild();
+        child; child = child->GetNextSibling()) {
+    RenderTraceLayers(child, aColor, aRootTransform, false);
+  }
+
+  if (aReset) colorId = 0;
+}
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const nsIntRect aRect) {
+  gfx3DMatrix trans = GetRootTransform(aLayer);
+  gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height);
+  trans.TransformBounds(rect);
+
+  printf_stderr("%s RENDERTRACE %u fillrect #%s %i %i %i %i\n",
+    aLayer->Name(), (int)PR_IntervalNow(),
+    aColor,
+    (int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+}
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor) {
+  // Clear with an empty rect
+  RenderTraceInvalidateStart(aLayer, aColor, nsIntRect());
+}
+
+}
+}
+
+#endif
+
new file mode 100644
--- /dev/null
+++ b/gfx/layers/RenderTrace.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Benoit Girard <bgirard@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 is a general tool that will let you visualize platform operation.
+// Currently used for the layer system, the general syntax allows this
+// tools to be adapted to trace other operations.
+//
+// For the front end see: https://github.com/staktrace/rendertrace
+
+// Uncomment this line to enable RENDERTRACE
+//#define MOZ_RENDERTRACE
+#ifdef MOZ_RENDERTRACE
+
+#include "gfx3DMatrix.h"
+#include "nsRect.h"
+
+#ifndef GFX_RENDERTRACE_H
+#define GFX_RENDERTRACE_H
+
+namespace mozilla {
+namespace layers {
+
+class Layer;
+
+void RenderTraceLayers(Layer *aLayer, const char *aColor, gfx3DMatrix aRootTransform = gfx3DMatrix(), bool aReset = true);
+
+void RenderTraceInvalidateStart(Layer *aLayer, const char *aColor, const nsIntRect aRect);
+void RenderTraceInvalidateEnd(Layer *aLayer, const char *aColor);
+
+}
+}
+
+#endif //GFX_RENDERTRACE_H
+
+#endif // MOZ_RENDERTRACE
--- a/gfx/layers/basic/BasicLayers.cpp
+++ b/gfx/layers/basic/BasicLayers.cpp
@@ -44,16 +44,17 @@
 #include "mozilla/layers/PLayersChild.h"
 #include "mozilla/layers/PLayersParent.h"
 #include "mozilla/gfx/2D.h"
 
 #include "ipc/ShadowLayerChild.h"
 
 #include "BasicLayers.h"
 #include "ImageLayers.h"
+#include "RenderTrace.h"
 
 #include "prprf.h"
 #include "nsTArray.h"
 #include "nsGUIEvent.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxPattern.h"
 #include "gfxPlatform.h"
@@ -683,16 +684,21 @@ BasicThebesLayer::PaintThebes(gfxContext
        (mContentFlags & CONTENT_COMPONENT_ALPHA) &&
        !MustRetainContent())) {
     NS_ASSERTION(readbackUpdates.IsEmpty(), "Can't do readback for non-retained layer");
 
     mValidRegion.SetEmpty();
     mBuffer.Clear();
 
     nsIntRegion toDraw = IntersectWithClip(GetEffectiveVisibleRegion(), aContext);
+
+#ifdef MOZ_RENDERTRACE
+    RenderTraceInvalidateStart(this, "FFFF00", toDraw.GetBounds());
+#endif
+
     if (!toDraw.IsEmpty() && !IsHidden()) {
       if (!aCallback) {
         BasicManager()->SetTransactionIncomplete();
         return;
       }
 
       aContext->Save();
 
@@ -718,16 +724,20 @@ BasicThebesLayer::PaintThebes(gfxContext
           gfxUtils::ClipToRegion(aContext, toDraw);
         }
         AutoSetOperator setOperator(aContext, GetOperator());
         aContext->Paint(opacity);
       }
 
       aContext->Restore();
     }
+
+#ifdef MOZ_RENDERTRACE
+    RenderTraceInvalidateEnd(this, "FFFF00");
+#endif
     return;
   }
 
   {
     PRUint32 flags = 0;
 #ifndef MOZ_GFX_OPTIMIZE_MOBILE
     gfxMatrix transform;
     if (!GetEffectiveTransform().CanDraw2D(&transform) ||
@@ -743,21 +753,30 @@ BasicThebesLayer::PaintThebes(gfxContext
       // The area that became invalid and is visible needs to be repainted
       // (this could be the whole visible area if our buffer switched
       // from RGB to RGBA, because we might need to repaint with
       // subpixel AA)
       state.mRegionToInvalidate.And(state.mRegionToInvalidate,
                                     GetEffectiveVisibleRegion());
       nsIntRegion extendedDrawRegion = state.mRegionToDraw;
       SetAntialiasingFlags(this, state.mContext);
+
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateStart(this, "FFFF00", state.mRegionToDraw.GetBounds());
+#endif
+
       PaintBuffer(state.mContext,
                   state.mRegionToDraw, extendedDrawRegion, state.mRegionToInvalidate,
                   state.mDidSelfCopy,
                   aCallback, aCallbackData);
       Mutated();
+
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateEnd(this, "FFFF00");
+#endif
     } else {
       // It's possible that state.mRegionToInvalidate is nonempty here,
       // if we are shrinking the valid region to nothing.
       NS_ASSERTION(state.mRegionToDraw.IsEmpty(),
                    "If we need to draw, we should have a context");
     }
   }
 
@@ -1595,16 +1614,21 @@ BasicLayerManager::EndTransactionInterna
   Log();
 #endif
 
   NS_ASSERTION(InConstruction(), "Should be in construction phase");
 #ifdef DEBUG
   mPhase = PHASE_DRAWING;
 #endif
 
+#ifdef MOZ_RENDERTRACE
+  Layer* aLayer = GetRoot();
+  RenderTraceLayers(aLayer, "FF00");
+#endif
+
   mTransactionIncomplete = false;
 
   if (mTarget && mRoot && !(aFlags & END_NO_IMMEDIATE_REDRAW)) {
     nsIntRect clipRect;
     if (HasShadowManager()) {
       // If this has a shadow manager, the clip extents of mTarget are meaningless.
       // So instead just use the root layer's visible region bounds.
       const nsIntRect& bounds = mRoot->GetVisibleRegion().GetBounds();
@@ -1815,16 +1839,18 @@ Transform3D(gfxASurface* aSource, gfxCon
   }
 
   // If we haven't actually drawn to aDest then return our temporary image so that
   // the caller can do this.
   aDrawOffset = destRect.TopLeft();
   return destImage.forget(); 
 }
 
+
+
 void
 BasicLayerManager::PaintLayer(gfxContext* aTarget,
                               Layer* aLayer,
                               DrawThebesLayerCallback aCallback,
                               void* aCallbackData,
                               ReadbackProcessor* aReadback)
 {
   const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
--- 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"
 
 namespace mozilla {
 namespace layers {
 
 CompositorParent::CompositorParent(nsIWidget* aWidget)
@@ -75,16 +76,22 @@ CompositorParent::RecvStop()
   return true;
 }
 
 void
 CompositorParent::ScheduleComposition()
 {
   CancelableTask *composeTask = NewRunnableMethod(this, &CompositorParent::Composite);
   MessageLoop::current()->PostTask(FROM_HERE, composeTask);
+
+#ifdef MOZ_RENDERTRACE
+  Layer* aLayer = mLayerManager->GetRoot();
+  mozilla::layers::RenderTraceLayers(aLayer, "0000");
+#endif
+
 }
 
 void
 CompositorParent::Composite()
 {
   if (mStopped || !mLayerManager) {
     return;
   }
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -38,16 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <vector>
 
 #include "ShadowLayersParent.h"
 #include "ShadowLayerParent.h"
 #include "ShadowLayers.h"
+#include "RenderTrace.h"
 
 #include "mozilla/unused.h"
 
 #include "mozilla/layout/RenderFrameParent.h"
 #include "CompositorParent.h"
 
 #include "gfxSharedImageSurface.h"
 
@@ -313,61 +314,83 @@ ShadowLayersParent::RecvUpdate(const Inf
       MOZ_LAYERS_LOG(("[ParentSide] Paint ThebesLayer"));
 
       const OpPaintThebesBuffer& op = edit.get_OpPaintThebesBuffer();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowThebesLayer* thebes =
         static_cast<ShadowThebesLayer*>(shadow->AsLayer());
       const ThebesBuffer& newFront = op.newFrontBuffer();
 
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateStart(thebes, "FF00FF", op.updatedRegion().GetBounds());
+#endif
+
       OptionalThebesBuffer newBack;
       nsIntRegion newValidRegion;
       OptionalThebesBuffer readonlyFront;
       nsIntRegion frontUpdatedRegion;
       thebes->Swap(newFront, op.updatedRegion(),
                    &newBack, &newValidRegion,
                    &readonlyFront, &frontUpdatedRegion);
       replyv.push_back(
         OpThebesBufferSwap(
           shadow, NULL,
           newBack, newValidRegion,
           readonlyFront, frontUpdatedRegion));
+
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateEnd(thebes, "FF00FF");
+#endif
       break;
     }
     case Edit::TOpPaintCanvas: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint CanvasLayer"));
 
       const OpPaintCanvas& op = edit.get_OpPaintCanvas();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowCanvasLayer* canvas =
         static_cast<ShadowCanvasLayer*>(shadow->AsLayer());
 
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateStart(canvas, "FF00FF", canvas->GetVisibleRegion().GetBounds());
+#endif
+
       canvas->SetAllocator(this);
       CanvasSurface newBack;
       canvas->Swap(op.newFrontBuffer(), op.needYFlip(), &newBack);
       canvas->Updated();
       replyv.push_back(OpBufferSwap(shadow, NULL,
                                     newBack));
 
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateEnd(canvas, "FF00FF");
+#endif
       break;
     }
     case Edit::TOpPaintImage: {
       MOZ_LAYERS_LOG(("[ParentSide] Paint ImageLayer"));
 
       const OpPaintImage& op = edit.get_OpPaintImage();
       ShadowLayerParent* shadow = AsShadowLayer(op);
       ShadowImageLayer* image =
         static_cast<ShadowImageLayer*>(shadow->AsLayer());
 
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateStart(image, "FF00FF", image->GetVisibleRegion().GetBounds());
+#endif
+
       image->SetAllocator(this);
       SharedImage newBack;
       image->Swap(op.newFrontBuffer(), &newBack);
       replyv.push_back(OpImageSwap(shadow, NULL,
                                    newBack));
 
+#ifdef MOZ_RENDERTRACE
+      RenderTraceInvalidateEnd(image, "FF00FF");
+#endif
       break;
     }
 
     default:
       NS_RUNTIMEABORT("not reached");
     }
   }
 
--- a/gfx/layers/opengl/LayerManagerOGL.cpp
+++ b/gfx/layers/opengl/LayerManagerOGL.cpp
@@ -362,17 +362,30 @@ LayerManagerOGL::Initialize(nsRefPtr<GLC
     msg += NS_LITERAL_STRING("\nFBO Texture Target: ");
     if (mFBOTextureTarget == LOCAL_GL_TEXTURE_2D)
       msg += NS_LITERAL_STRING("TEXTURE_2D");
     else
       msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
     console->LogStringMessage(msg.get());
   }
 
-  Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
+  if (NS_IsMainThread()) {
+    Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
+  } else {
+    // We have to dispatch an event to the main thread to read the pref.
+    class ReadDrawFPSPref : public nsRunnable {
+    public:
+      NS_IMETHOD Run()
+      {
+        Preferences::AddBoolVarCache(&sDrawFPS, "layers.acceleration.draw-fps");
+        return NS_OK;
+      }
+    };
+    NS_DispatchToMainThread(new ReadDrawFPSPref());
+  }
 
   reporter.SetSuccessful();
   return true;
 }
 
 void
 LayerManagerOGL::SetClippingRegion(const nsIntRegion& aClippingRegion)
 {
--- 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/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -168,16 +168,17 @@ CPPSRCS		= \
 		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;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -4931,16 +4931,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/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/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -343,30 +343,30 @@ void
 IonCode::readBarrier(IonCode *code)
 {
 #ifdef JSGC_INCREMENTAL
     if (!code)
         return;
 
     JSCompartment *comp = code->compartment();
     if (comp->needsBarrier())
-        MarkIonCodeUnbarriered(comp->barrierTracer(), code, "ioncode read barrier");
+        MarkIonCodeUnbarriered(comp->barrierTracer(), &code, "ioncode read barrier");
 #endif
 }
 
 void
 IonCode::writeBarrierPre(IonCode *code)
 {
 #ifdef JSGC_INCREMENTAL
     if (!code)
         return;
 
     JSCompartment *comp = code->compartment();
     if (comp->needsBarrier())
-        MarkIonCodeUnbarriered(comp->barrierTracer(), code, "ioncode write barrier");
+        MarkIonCodeUnbarriered(comp->barrierTracer(), &code, "ioncode write barrier");
 #endif
 }
 
 void
 IonCode::writeBarrierPost(IonCode *code, void *addr)
 {
 #ifdef JSGC_INCREMENTAL
     // Nothing to do.
--- a/js/src/ion/arm/Assembler-arm.cpp
+++ b/js/src/ion/arm/Assembler-arm.cpp
@@ -544,17 +544,18 @@ CodeFromJump(Instruction *jump)
 }
 
 void
 Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     RelocationIterator iter(reader);
     while (iter.read()) {
         IonCode *child = CodeFromJump((Instruction *) (code->raw() + iter.offset()));
-        MarkIonCodeUnbarriered(trc, child, "rel32");
+        MarkIonCodeUnbarriered(trc, &child, "rel32");
+        JS_ASSERT(child == CodeFromJump((Instruction *) (code->raw() + iter.offset())));
     };
 }
 
 static void
 TraceDataRelocations(JSTracer *trc, uint8 *buffer, CompactBufferReader &reader)
 {
     while (reader.more()) {
         size_t offset = reader.readUnsigned();
@@ -593,18 +594,21 @@ Assembler::copyDataRelocationTable(uint8
         memcpy(dest, dataRelocations_.buffer(), dataRelocations_.length());
 }
 
 void
 Assembler::trace(JSTracer *trc)
 {
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch &rp = jumps_[i];
-        if (rp.kind == Relocation::IONCODE)
-            MarkIonCodeUnbarriered(trc, IonCode::FromExecutable((uint8*)rp.target), "masmrel32");
+        if (rp.kind == Relocation::IONCODE) {
+            IonCode *code = IonCode::FromExecutable((uint8*)rp.target);
+            MarkIonCodeUnbarriered(trc, &code, "masmrel32");
+            JS_ASSERT(code == IonCode::FromExecutable((uint8*)rp.target));
+        }
     }
     if (tmpDataRelocations_.length()) {
         CompactBufferReader reader(dataRelocations_);
         ::TraceDataRelocations(trc, &m_buffer, reader);
     }
 }
 
 void
--- a/js/src/ion/shared/Assembler-x86-shared.cpp
+++ b/js/src/ion/shared/Assembler-x86-shared.cpp
@@ -75,18 +75,21 @@ AssemblerX86Shared::TraceDataRelocations
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
 
 void
 AssemblerX86Shared::trace(JSTracer *trc)
 {
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch &rp = jumps_[i];
-        if (rp.kind == Relocation::IONCODE)
-            MarkIonCodeUnbarriered(trc, IonCode::FromExecutable((uint8 *)rp.target), "masmrel32");
+        if (rp.kind == Relocation::IONCODE) {
+            IonCode *code = IonCode::FromExecutable((uint8 *)rp.target);
+            MarkIonCodeUnbarriered(trc, &code, "masmrel32");
+            JS_ASSERT(code == IonCode::FromExecutable((uint8 *)rp.target));
+        }
     }
     if (dataRelocations_.length()) {
         CompactBufferReader reader(dataRelocations_);
         ::TraceDataRelocations(trc, masm.buffer(), reader);
     }
 }
 
 void
--- a/js/src/ion/x64/Assembler-x64.cpp
+++ b/js/src/ion/x64/Assembler-x64.cpp
@@ -214,12 +214,13 @@ Assembler::CodeFromJump(IonCode *code, u
 }
 
 void
 Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     RelocationIterator iter(reader);
     while (iter.read()) {
         IonCode *child = CodeFromJump(code, code->raw() + iter.offset());
-        MarkIonCodeUnbarriered(trc, child, "rel32");
+        MarkIonCodeUnbarriered(trc, &child, "rel32");
+        JS_ASSERT(child == CodeFromJump(code, code->raw() + iter.offset()));
     }
 }
 
--- a/js/src/ion/x86/Assembler-x86.cpp
+++ b/js/src/ion/x86/Assembler-x86.cpp
@@ -86,12 +86,13 @@ CodeFromJump(uint8 *jump)
 }
 
 void
 Assembler::TraceJumpRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader)
 {
     RelocationIterator iter(reader);
     while (iter.read()) {
         IonCode *child = CodeFromJump(code->raw() + iter.offset());
-        MarkIonCodeUnbarriered(trc, child, "rel32");
+        MarkIonCodeUnbarriered(trc, &child, "rel32");
+        JS_ASSERT(child == CodeFromJump(code->raw() + iter.offset()));
     }
 }
 
--- 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"
@@ -4278,17 +4279,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 = {
--- 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"
@@ -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());
 
     /*
--- 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/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
--- 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);
 }
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -123,20 +123,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());
@@ -183,32 +183,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)
@@ -367,25 +368,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;
 
@@ -394,25 +405,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);
 }
@@ -676,128 +683,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);
-
-#ifdef JS_ION
-    if (script->ion)
-	    ion::IonScript::Trace(trc, script->ion);
-#endif
-
-    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
@@ -810,21 +733,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
@@ -44,21 +44,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)
@@ -121,27 +122,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
@@ -1314,46 +1314,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
@@ -1098,32 +1098,22 @@ 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);
 }
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -250,17 +250,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;
--- 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.
--- 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
--- 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),
--- 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.
  */
@@ -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/jsscope.h
+++ b/js/src/jsscope.h
@@ -385,16 +385,18 @@ class BaseShape : public js::gc::Cell
     static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
 
     static inline void writeBarrierPre(BaseShape *shape);
     static inline void writeBarrierPost(BaseShape *shape, void *addr);
     static inline void readBarrier(BaseShape *shape);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_BASE_SHAPE; }
 
+    inline void markChildren(JSTracer *trc);
+
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(BaseShape, clasp) == offsetof(js::shadow::BaseShape, clasp));
     }
 };
 
 class UnownedBaseShape : public BaseShape {};
 
@@ -558,20 +560,16 @@ struct Shape : public js::gc::Cell
         JS_ASSERT(!(flags & NON_NATIVE) == getObjectClass()->isNative());
         return !(flags & NON_NATIVE);
     }
 
     const HeapPtrShape &previous() const {
         return parent;
     }
 
-    HeapPtrShape &previousRef() {
-        return parent;
-    }
-
     class Range {
       protected:
         friend struct Shape;
         const Shape *cursor;
 
       public:
         Range(const Shape *shape) : cursor(shape) { }
 
@@ -905,16 +903,18 @@ struct Shape : public js::gc::Cell
      * All weak references need a read barrier for incremental GC. This getter
      * method implements the read barrier. It's used to obtain initial shapes
      * from the compartment.
      */
     static inline void readBarrier(const Shape *shape);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_SHAPE; }
 
+    inline void markChildren(JSTracer *trc);
+
     /* For JIT usage */
     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
 
   private:
     static void staticAsserts() {
         JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
         JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
         JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -389,59 +389,96 @@ EmptyShape::EmptyShape(UnownedBaseShape 
 inline void
 Shape::writeBarrierPre(const js::Shape *shape)
 {
 #ifdef JSGC_INCREMENTAL
     if (!shape)
         return;
 
     JSCompartment *comp = shape->compartment();
-    if (comp->needsBarrier())
-        MarkShapeUnbarriered(comp->barrierTracer(), const_cast<Shape *>(shape), "write barrier");
+    if (comp->needsBarrier()) {
+        Shape *tmp = const_cast<Shape *>(shape);
+        MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == shape);
+    }
 #endif
 }
 
 inline void
 Shape::writeBarrierPost(const js::Shape *shape, void *addr)
 {
 }
 
 inline void
 Shape::readBarrier(const Shape *shape)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = shape->compartment();
-    if (comp->needsBarrier())
-        MarkShapeUnbarriered(comp->barrierTracer(), const_cast<Shape *>(shape), "read barrier");
+    if (comp->needsBarrier()) {
+        Shape *tmp = const_cast<Shape *>(shape);
+        MarkShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == shape);
+    }
 #endif
 }
 
 inline void
+Shape::markChildren(JSTracer *trc)
+{
+    MarkBaseShape(trc, &base_, "base");
+    gc::MarkId(trc, &propidRef(), "propid");
+    if (parent)
+        MarkShape(trc, &parent, "parent");
+}
+
+inline void
 BaseShape::writeBarrierPre(BaseShape *base)
 {
 #ifdef JSGC_INCREMENTAL
     if (!base)
         return;
 
     JSCompartment *comp = base->compartment();
-    if (comp->needsBarrier())
-        MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "write barrier");
+    if (comp->needsBarrier()) {
+        BaseShape *tmp = base;
+        MarkBaseShapeUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == base);
+    }
 #endif
 }
 
 inline void
 BaseShape::writeBarrierPost(BaseShape *shape, void *addr)
 {
 }
 
 inline void
 BaseShape::readBarrier(BaseShape *base)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = base->compartment();
-    if (comp->needsBarrier())
-        MarkBaseShapeUnbarriered(comp->barrierTracer(), base, "read barrier");
+    if (comp->needsBarrier()) {
+        BaseShape *tmp = base;
+        MarkBaseShapeUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == base);
+    }
 #endif
 }
 
+inline void
+BaseShape::markChildren(JSTracer *trc)
+{
+    if (hasGetterObject())
+        MarkObjectUnbarriered(trc, &getterObj, "getter");
+
+    if (hasSetterObject())
+        MarkObjectUnbarriered(trc, &setterObj, "setter");
+
+    if (isOwned())
+        MarkBaseShape(trc, &unowned_, "base");
+
+    if (parent)
+        MarkObject(trc, &parent, "parent");
+}
+
 } /* namespace js */
 
 #endif /* jsscopeinlines_h___ */
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -318,30 +318,16 @@ Bindings::makeImmutable()
 
 void
 Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, &lastBinding, "shape");
 }
 
-#ifdef JS_CRASH_DIAGNOSTICS
-
-void
-CheckScript(JSScript *script, JSScript *prev)
-{
-    if (script->cookie1[0] != JS_SCRIPT_COOKIE || script->cookie2[0] != JS_SCRIPT_COOKIE) {
-        crash::StackBuffer<sizeof(JSScript), 0x87> buf1(script);
-        crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
-        JS_OPT_ASSERT(false);
-    }
-}
-
-#endif /* JS_CRASH_DIAGNOSTICS */
-
 #if JS_HAS_XDR
 
 static bool
 XDRScriptConst(JSXDRState *xdr, HeapValue *vp)
 {
     /*
      * A script constant can be an arbitrary primitive value as they are used
      * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see
@@ -1449,20 +1435,34 @@ js_CallDestroyScriptHook(JSContext *cx, 
         return;
 
     if (JSDestroyScriptHook hook = cx->runtime->debugHooks.destroyScriptHook)
         hook(cx, script, cx->runtime->debugHooks.destroyScriptHookData);
     script->callDestroyHook = false;
     JS_ClearScriptTraps(cx, script);
 }
 
+#ifdef JS_CRASH_DIAGNOSTICS
+
+void
+JSScript::CheckScript(JSScript *prev)
+{
+    if (cookie1[0] != JS_SCRIPT_COOKIE || cookie2[0] != JS_SCRIPT_COOKIE) {
+        crash::StackBuffer<sizeof(JSScript), 0x87> buf1(this);
+        crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
+        JS_OPT_ASSERT(false);
+    }
+}
+
+#endif /* JS_CRASH_DIAGNOSTICS */
+
 void
 JSScript::finalize(JSContext *cx, bool background)
 {
-    CheckScript(this, NULL);
+    CheckScript(NULL);
 
     js_CallDestroyScriptHook(cx, this);
 
     JS_ASSERT_IF(principals, originPrincipals);
     if (principals)
         JSPRINCIPALS_DROP(cx, principals);
     if (originPrincipals)
         JSPRINCIPALS_DROP(cx, originPrincipals);
@@ -1945,18 +1945,57 @@ JSScript::clearTraps(JSContext *cx)
     for (jsbytecode *pc = code; pc < end; pc++) {
         BreakpointSite *site = getBreakpointSite(pc);
         if (site)
             site->clearTrap(cx);
     }
 }
 
 void
-JSScript::markTrapClosures(JSTracer *trc)
+JSScript::markChildren(JSTracer *trc)
 {
-    JS_ASSERT(hasAnyBreakpointsOrStepMode());
+    CheckScript(NULL);
+
+    JS_ASSERT_IF(trc->runtime->gcCheckCompartment,
+                 compartment() == trc->runtime->gcCheckCompartment);
+
+    for (uint32_t i = 0; i < natoms; ++i) {
+        if (atoms[i])
+            MarkStringUnbarriered(trc, &atoms[i], "atom");
+    }
+
+    if (JSScript::isValidOffset(objectsOffset)) {
+        JSObjectArray *objarray = objects();
+        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
+    }
+
+    if (JSScript::isValidOffset(regexpsOffset)) {
+        JSObjectArray *objarray = regexps();
+        MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
+    }
 
-    for (unsigned i = 0; i < length; i++) {
-        BreakpointSite *site = debug->breakpoints[i];
-        if (site && site->trapHandler)
-            MarkValue(trc, &site->trapClosure, "trap closure");
+    if (JSScript::isValidOffset(constOffset)) {
+        JSConstArray *constarray = consts();
+        MarkValueRange(trc, constarray->length, constarray->vector, "consts");
+    }
+
+    if (function())
+        MarkObject(trc, &function_, "function");
+
+    if (!isCachedEval && globalObject)
+        MarkObject(trc, &globalObject, "object");
+
+    if (IS_GC_MARKING_TRACER(trc) && filename)
+        js_MarkScriptFilename(filename);
+
+    bindings.trace(trc);
+
+    if (types)
+        types->trace(trc);
+
+    if (hasAnyBreakpointsOrStepMode()) {
+        for (unsigned i = 0; i < length; i++) {
+            BreakpointSite *site = debug->breakpoints[i];
+            if (site && site->trapHandler)
+                MarkValue(trc, &site->trapClosure, "trap closure");
+        }
     }
 }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -569,17 +569,21 @@ struct JSScript : public js::gc::Cell {
      * NULL for global and eval scripts.
      */
     JSFunction *function() const { return function_; }
     void setFunction(JSFunction *fun);
 
 #ifdef JS_CRASH_DIAGNOSTICS
     /* All diagnostic fields must be multiples of Cell::CellSize. */
     uint32_t        cookie2[Cell::CellSize / sizeof(uint32_t)];
-#endif
+
+    void CheckScript(JSScript *prev);
+#else
+    void CheckScript(JSScript *prev) {}
+#endif /* !JS_CRASH_DIAGNOSTICS */
 
 #ifdef DEBUG
     /*
      * Unique identifier within the compartment for this script, used for
      * printing analysis information.
      */
     uint32_t id_;
     uint32_t idpad;
@@ -843,16 +847,18 @@ struct JSScript : public js::gc::Cell {
     static inline void writeBarrierPost(JSScript *script, void *addr);
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
 
     static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
                                                    JSPrincipals *originPrincipals) {
         return originPrincipals ? originPrincipals : principals;
     }
+
+    void markChildren(JSTracer *trc);
 };
 
 /* If this fails, padding_ can be removed or added. */
 JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::Cell::CellSize == 0);
 
 static JS_INLINE unsigned
 StackDepth(JSScript *script)
 {
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -240,17 +240,19 @@ JSScript::writeBarrierPre(JSScript *scri
 {
 #ifdef JSGC_INCREMENTAL
     if (!script)
         return;
 
     JSCompartment *comp = script->compartment();
     if (comp->needsBarrier()) {
         JS_ASSERT(!comp->rt->gcRunning);
-        MarkScriptUnbarriered(comp->barrierTracer(), script, "write barrier");
+        JSScript *tmp = script;
+        MarkScriptUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == script);
     }
 #endif
 }
 
 inline void
 JSScript::writeBarrierPost(JSScript *script, void *addr)
 {
 }
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -74,23 +74,25 @@
 #include "jsversion.h"
 
 #include "builtin/RegExp.h"
 #include "vm/GlobalObject.h"
 #include "vm/RegExpObject.h"
 
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
+#include "jsstrinlines.h"
 #include "jsautooplen.h"        // generated headers last
 
 #include "vm/MethodGuard-inl.h"
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
 #include "vm/StringObject-inl.h"
 #include "vm/String-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 using namespace js::unicode;
 
 static JSLinearString *
 ArgToRootedString(JSContext *cx, CallArgs &args, unsigned argno)
@@ -3107,38 +3109,16 @@ JSFixedString *
 js_NewString(JSContext *cx, jschar *chars, size_t length)
 {
     JSFixedString *s = JSFixedString::new_(cx, chars, length);
     if (s)
         Probes::createString(cx, s, length);
     return s;
 }
 
-static JS_ALWAYS_INLINE JSFixedString *
-NewShortString(JSContext *cx, const jschar *chars, size_t length)
-{
-    /*
-     * Don't bother trying to find a static atom; measurement shows that not
-     * many get here (for one, Atomize is catching them).
-     */
-
-    JS_ASSERT(JSShortString::lengthFits(length));
-    JSInlineString *str = JSInlineString::lengthFits(length)
-                          ? JSInlineString::new_(cx)
-                          : JSShortString::new_(cx);
-    if (!str)
-        return NULL;
-
-    jschar *storage = str->init(length);
-    PodCopy(storage, chars, length);
-    storage[length] = 0;
-    Probes::createString(cx, str, length);
-    return str;
-}
-
 static JSInlineString *
 NewShortString(JSContext *cx, const char *chars, size_t length)
 {
     JS_ASSERT(JSShortString::lengthFits(length));
     JSInlineString *str = JSInlineString::lengthFits(length)
                           ? JSInlineString::new_(cx)
                           : JSShortString::new_(cx);
     if (!str)
@@ -3160,84 +3140,16 @@ NewShortString(JSContext *cx, const char
         while (n--)
             *p++ = (unsigned char)*chars++;
         *p = 0;
     }
     Probes::createString(cx, str, length);
     return str;
 }
 
-jschar *
-StringBuffer::extractWellSized()
-{
-    size_t capacity = cb.capacity();
-    size_t length = cb.length();
-
-    jschar *buf = cb.extractRawBuffer();
-    if (!buf)
-        return NULL;
-
-    /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
-    JS_ASSERT(capacity >= length);
-    if (length > CharBuffer::sMaxInlineStorage && capacity - length > length / 4) {
-        size_t bytes = sizeof(jschar) * (length + 1);
-        JSContext *cx = context();
-        jschar *tmp = (jschar *)cx->realloc_(buf, bytes);
-        if (!tmp) {
-            cx->free_(buf);
-            return NULL;
-        }
-        buf = tmp;
-    }
-
-    return buf;
-}
-
-JSFixedString *
-StringBuffer::finishString()
-{
-    JSContext *cx = context();
-    if (cb.empty())
-        return cx->runtime->atomState.emptyAtom;
-
-    size_t length = cb.length();
-    if (!checkLength(length))
-        return NULL;
-
-    JS_STATIC_ASSERT(JSShortString::MAX_SHORT_LENGTH < CharBuffer::InlineLength);
-    if (JSShortString::lengthFits(length))
-        return NewShortString(cx, cb.begin(), length);
-
-    if (!cb.append('\0'))
-        return NULL;
-
-    jschar *buf = extractWellSized();
-    if (!buf)
-        return NULL;
-
-    JSFixedString *str = js_NewString(cx, buf, length);
-    if (!str)
-        cx->free_(buf);
-    return str;
-}
-
-JSAtom *
-StringBuffer::finishAtom()
-{
-    JSContext *cx = context();
-
-    size_t length = cb.length();
-    if (length == 0)
-        return cx->runtime->atomState.emptyAtom;
-
-    JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length);
-    cb.clear();
-    return atom;
-}
-
 JSLinearString *
 js_NewDependentString(JSContext *cx, JSString *baseArg, size_t start, size_t length)
 {
     if (length == 0)
         return cx->runtime->emptyString;
 
     JSLinearString *base = baseArg->ensureLinear(cx);
     if (!base)
--- a/js/src/jsstrinlines.h
+++ b/js/src/jsstrinlines.h
@@ -46,207 +46,16 @@
 #include "jsstr.h"
 
 #include "jscntxtinlines.h"
 #include "jsgcinlines.h"
 #include "vm/String-inl.h"
 
 namespace js {
 
-/*
- * String builder that eagerly checks for over-allocation past the maximum
- * string length.
- *
- * Any operation which would exceed the maximum string length causes an
- * exception report on the context and results in a failed return value.
- *
- * Well-sized extractions (which waste no more than 1/4 of their char
- * buffer space) are guaranteed for strings built by this interface.
- * See |extractWellSized|.
- *
- * Note: over-allocation is not checked for when using the infallible
- * |replaceRawBuffer|, so the implementation of |finishString| also must check
- * for over-allocation.
- */
-class StringBuffer
-{
-    /* cb's buffer is taken by the new string so use ContextAllocPolicy. */
-    typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
-
-    CharBuffer cb;
-
-    static inline bool checkLength(JSContext *cx, size_t length);
-    inline bool checkLength(size_t length);
-    JSContext *context() const { return cb.allocPolicy().context(); }
-    jschar *extractWellSized();
-
-    StringBuffer(const StringBuffer &other) MOZ_DELETE;
-    void operator=(const StringBuffer &other) MOZ_DELETE;
-
-  public:
-    explicit inline StringBuffer(JSContext *cx);
-    bool reserve(size_t len);
-    bool resize(size_t len);
-    bool append(const jschar c);
-    bool append(const jschar *chars, size_t len);
-    bool append(const jschar *begin, const jschar *end);
-    bool append(JSString *str);
-    bool append(JSLinearString *str);
-    bool appendN(const jschar c, size_t n);
-    bool appendInflated(const char *cstr, size_t len);
-
-    /* Infallible variants usable when the corresponding space is reserved. */
-    void infallibleAppend(const jschar c) {
-        cb.infallibleAppend(c);
-    }
-    void infallibleAppend(const jschar *chars, size_t len) {
-        cb.infallibleAppend(chars, len);
-    }
-    void infallibleAppend(const jschar *begin, const jschar *end) {
-        cb.infallibleAppend(begin, end);
-    }
-    void infallibleAppendN(const jschar c, size_t n) {
-        cb.infallibleAppendN(c, n);
-    }
-
-    JSAtom *atomize(unsigned flags = 0);
-    static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, unsigned flags = 0);
-    static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, unsigned flags = 0);
-
-    void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); }
-    jschar *begin() { return cb.begin(); }
-    jschar *end() { return cb.end(); }
-    const jschar *begin() const { return cb.begin(); }
-    const jschar *end() const { return cb.end(); }
-    bool empty() const { return cb.empty(); }
-    inline int length() const;
-
-    /*
-     * Creates a string from the characters in this buffer, then (regardless
-     * whether string creation succeeded or failed) empties the buffer.
-     */
-    JSFixedString *finishString();
-
-    /* Identical to finishString() except that an atom is created. */
-    JSAtom *finishAtom();
-
-    template <size_t ArrayLength>
-    bool append(const char (&array)[ArrayLength]) {
-        return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
-    }
-};
-
-inline
-StringBuffer::StringBuffer(JSContext *cx)
-  : cb(cx)
-{}
-
-inline bool
-StringBuffer::reserve(size_t len)
-{
-    if (!checkLength(len))
-        return false;
-    return cb.reserve(len);
-}
-
-inline bool
-StringBuffer::resize(size_t len)
-{
-    if (!checkLength(len))
-        return false;
-    return cb.resize(len);
-}
-
-inline bool
-StringBuffer::append(const jschar c)
-{
-    if (!checkLength(cb.length() + 1))
-        return false;
-    return cb.append(c);
-}
-
-inline bool
-StringBuffer::append(const jschar *chars, size_t len)
-{
-    if (!checkLength(cb.length() + len))
-        return false;
-    return cb.append(chars, len);
-}
-
-inline bool
-StringBuffer::append(const jschar *begin, const jschar *end)
-{
-    if (!checkLength(cb.length() + (end - begin)))
-        return false;
-    return cb.append(begin, end);
-}
-
-inline bool
-StringBuffer::append(JSString *str)
-{
-    JSLinearString *linear = str->ensureLinear(context());
-    if (!linear)
-        return false;
-    return append(linear);
-}
-
-inline bool
-StringBuffer::append(JSLinearString *str)
-{
-    JS::Anchor<JSString *> anch(str);
-    return cb.append(str->chars(), str->length());
-}
-
-inline bool
-StringBuffer::appendN(const jschar c, size_t n)
-{
-    if (!checkLength(cb.length() + n))
-        return false;
-    return cb.appendN(c, n);
-}
-
-inline bool
-StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
-{
-    size_t lengthBefore = length();
-    if (!cb.growByUninitialized(cstrlen))
-        return false;
-    DebugOnly<size_t> oldcstrlen = cstrlen;
-    DebugOnly<bool> ok = InflateStringToBuffer(context(), cstr, cstrlen,
-                                               begin() + lengthBefore, &cstrlen);
-    JS_ASSERT(ok && oldcstrlen == cstrlen);
-    return true;
-}
-
-inline int
-StringBuffer::length() const
-{
-    JS_STATIC_ASSERT(int(JSString::MAX_LENGTH) == JSString::MAX_LENGTH);
-    JS_ASSERT(cb.length() <= JSString::MAX_LENGTH);
-    return int(cb.length());
-}
-
-inline bool
-StringBuffer::checkLength(size_t length)
-{
-    return JSString::validateLength(context(), length);
-}
-
-extern bool
-ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb);
-
-inline bool
-ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
-{
-    if (v.isString())
-        return sb.append(v.toString());
-
-    return ValueToStringBufferSlow(cx, v, sb);
-}
-
 class RopeBuilder {
     JSContext *cx;
     JSString *res;
 
     RopeBuilder(const RopeBuilder &other) MOZ_DELETE;
     void operator=(const RopeBuilder &other) MOZ_DELETE;
 
   public:
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -325,18 +325,20 @@ ArrayBuffer::~ArrayBuffer()
 void
 ArrayBuffer::obj_trace(JSTracer *trc, JSObject *obj)
 {
     /*
      * If this object changes, it will get marked via the private data barrier,
      * so it's safe to leave it Unbarriered.
      */
     JSObject *delegate = static_cast<JSObject*>(obj->getPrivate());
-    if (delegate)
-        MarkObjectUnbarriered(trc, delegate, "arraybuffer.delegate");
+    if (delegate) {
+        MarkObjectUnbarriered(trc, &delegate, "arraybuffer.delegate");
+        obj->setPrivate(delegate);
+    }
 }
 
 static JSProperty * const PROPERTY_FOUND = reinterpret_cast<JSProperty *>(1);
 
 JSBool
 ArrayBuffer::obj_lookupGeneric(JSContext *cx, JSObject *obj, jsid id,
                                JSObject **objp, JSProperty **propp)
 {
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -70,20 +70,20 @@
 #include "frontend/Parser.h"
 #include "frontend/TokenStream.h"
 #include "vm/GlobalObject.h"
 #include "vm/MethodGuard.h"
 
 #include "jsatominlines.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"
 
 #ifdef DEBUG
 #include <string.h>     /* for #ifdef DEBUG memset calls */
 #endif
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
@@ -5155,18 +5155,20 @@ xml_hasInstance(JSContext *cx, JSObject 
 static void
 xml_trace(JSTracer *trc, JSObject *obj)
 {
     JSXML *xml = (JSXML *) obj->getPrivate();
     /*
      * This is safe to leave Unbarriered for incremental GC, but we'll need
      * to fix somehow for generational.
      */
-    if (xml)
-        MarkXMLUnbarriered(trc, xml, "private");
+    if (xml) {
+        MarkXMLUnbarriered(trc, &xml, "private");
+        JS_ASSERT(xml == obj->getPrivate());
+    }
 }
 
 static JSBool
 xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
 {
     JS_ASSERT(obj->isExtensible());
     *success = false;
     return true;
@@ -7303,18 +7305,21 @@ js_NewXML(JSContext *cx, JSXMLClass xml_
 void
 JSXML::writeBarrierPre(JSXML *xml)
 {
 #ifdef JSGC_INCREMENTAL
     if (!xml)
         return;
 
     JSCompartment *comp = xml->compartment();
-    if (comp->needsBarrier())
-        MarkXMLUnbarriered(comp->barrierTracer(), xml, "write barrier");
+    if (comp->needsBarrier()) {
+        JSXML *tmp = xml;
+        MarkXMLUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == xml);
+    }
 #endif
 }
 
 void
 JSXML::writeBarrierPost(JSXML *xml, void *addr)
 {
 }
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -136,17 +136,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
          cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS))) {
         inlining_ = true;
     }
 }
 
 CompileStatus
 mjit::Compiler::compile()
 {
-    JS_ASSERT(!outerChunk.chunk);
+    JS_ASSERT(!outerChunkRef().chunk);
 
     void **checkAddr = isConstructing
                        ? &outerScript->jitArityCheckCtor
                        : &outerScript->jitArityCheckNormal;
 
     CompileStatus status = performCompilation();
     if (status != Compile_Okay && status != Compile_Retry) {
         *checkAddr = JS_UNJITTABLE_SCRIPT;
@@ -552,18 +552,18 @@ mjit::Compiler::performCompilation()
     }
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
     JaegerSpew(JSpew_Prof, "compilation took %d us\n", prof.time_us());
 #endif
 
     JaegerSpew(JSpew_Scripts, "successfully compiled (code \"%p\") (size \"%u\")\n",
-               outerChunk.chunk->code.m_code.executableAddress(),
-               unsigned(outerChunk.chunk->code.m_size));
+               outerChunkRef().chunk->code.m_code.executableAddress(),
+               unsigned(outerChunkRef().chunk->code.m_size));
 
     return Compile_Okay;
 }
 
 #undef CHECK_STATUS
 
 mjit::JSActiveFrame::JSActiveFrame()
     : parent(NULL), parentPC(NULL), script(NULL), inlineIndex(UINT32_MAX)
@@ -1775,17 +1775,17 @@ mjit::Compiler::finishThisUp()
     JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
 
     Probes::registerMJITCode(cx, jit,
                              a,
                              (JSActiveFrame**) inlineFrames.begin(),
                              result, masm.size(),
                              result + masm.size(), stubcc.size());
 
-    outerChunk.chunk = chunk;
+    outerChunkRef().chunk = chunk;
 
     /* Patch all incoming and outgoing cross-chunk jumps. */
     CrossChunkEdge *crossEdges = jit->edges();
     for (unsigned i = 0; i < jit->nedges; i++) {
         CrossChunkEdge &edge = crossEdges[i];
         if (bytecodeInChunk(outerScript->code + edge.source)) {
             JS_ASSERT(!edge.sourceJump1 && !edge.sourceJump2);
             void *label = edge.targetLabel ? edge.targetLabel : edge.shimLabel;
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -393,17 +393,17 @@ class Compiler : public BaseCompiler
         uint32_t slot;
         VarType vt;
         SlotType(uint32_t slot, VarType vt) : slot(slot), vt(vt) {}
     };
 
     JSScript *outerScript;
     unsigned chunkIndex;
     bool isConstructing;
-    ChunkDescriptor &outerChunk;
+    ChunkDescriptor outerChunk;
 
     /* SSA information for the outer script and all frames we will be inlining. */
     analyze::CrossScriptSSA ssa;
 
     GlobalObject *globalObj;
     const HeapSlot *globalSlots;  /* Original slots pointer. */
 
     Assembler masm;
@@ -520,16 +520,20 @@ private:
             scan = static_cast<ActiveFrame *>(scan->parent);
         return scan->parentPC;
     }
 
     JITScript *outerJIT() {
         return outerScript->getJIT(isConstructing);
     }
 
+    ChunkDescriptor &outerChunkRef() {
+        return outerJIT()->chunkDescriptor(chunkIndex);
+    }
+
     bool bytecodeInChunk(jsbytecode *pc) {
         return (unsigned(pc - outerScript->code) >= outerChunk.begin)
             && (unsigned(pc - outerScript->code) < outerChunk.end);
     }
 
     jsbytecode *inlinePC() { return PC; }
     uint32_t inlineIndex() { return a->inlineIndex; }
 
--- a/js/src/tests/ecma_5/Expressions/jstests.list
+++ b/js/src/tests/ecma_5/Expressions/jstests.list
@@ -1,7 +1,8 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Expressions/
 script 11.1.5-01.js
 script named-accessor-function.js
 script nested-delete-name-in-evalcode.js
 script object-literal-accessor-arguments.js
 script object-literal-accessor-property-name.js
+script primitive-this-boxing-behavior.js
 script string-literal-escape-sequences.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Expressions/primitive-this-boxing-behavior.js
@@ -0,0 +1,106 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 732669;
+var summary = "Primitive values don't box correctly";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var t;
+function returnThis() { return this; }
+
+// Boolean
+
+Boolean.prototype.method = returnThis;
+t = true.method();
+assertEq(t !== Boolean.prototype, true);
+assertEq(t.toString(), "true");
+
+Object.defineProperty(Boolean.prototype, "property", { get: returnThis, configurable: true });
+t = false.property;
+assertEq(t !== Boolean.prototype, true);
+assertEq(t.toString(), "false");
+
+delete Boolean.prototype.method;
+delete Boolean.prototype.property;
+
+
+// Number
+
+Number.prototype.method = returnThis;
+t = 5..method();
+assertEq(t !== Number.prototype, true);
+assertEq(t.toString(), "5");
+
+Object.defineProperty(Number.prototype, "property", { get: returnThis, configurable: true });
+t = 17..property;
+assertEq(t !== Number.prototype, true);
+assertEq(t.toString(), "17");
+
+delete Number.prototype.method;
+delete Number.prototype.property;
+
+
+// String
+
+String.prototype.method = returnThis;
+t = "foo".method();
+assertEq(t !== String.prototype, true);
+assertEq(t.toString(), "foo");
+
+Object.defineProperty(String.prototype, "property", { get: returnThis, configurable: true });
+t = "bar".property;
+assertEq(t !== String.prototype, true);
+assertEq(t.toString(), "bar");
+
+delete String.prototype.method;
+delete String.prototype.property;
+
+
+// Object
+
+Object.prototype.method = returnThis;
+
+t = true.method();
+assertEq(t !== Object.prototype, true);
+assertEq(t !== Boolean.prototype, true);
+assertEq(t.toString(), "true");
+
+t = 42..method();
+assertEq(t !== Object.prototype, true);
+assertEq(t !== Number.prototype, true);
+assertEq(t.toString(), "42");
+
+t = "foo".method();
+assertEq(t !== Object.prototype, true);
+assertEq(t !== String.prototype, true);
+assertEq(t.toString(), "foo");
+
+Object.defineProperty(Object.prototype, "property", { get: returnThis, configurable: true });
+
+t = false.property;
+assertEq(t !== Object.prototype, true);
+assertEq(t !== Boolean.prototype, true);
+assertEq(t.toString(), "false");
+
+t = 8675309..property;
+assertEq(t !== Object.prototype, true);
+assertEq(t !== Number.prototype, true);
+assertEq(t.toString(), "8675309");
+
+t = "bar".property;
+assertEq(t !== Object.prototype, true);
+assertEq(t !== String.prototype, true);
+assertEq(t.toString(), "bar");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2061,24 +2061,32 @@ JSFunctionSpec Debugger::methods[] = {
 
 static inline JSScript *
 GetScriptReferent(JSObject *obj)
 {
     JS_ASSERT(obj->getClass() == &DebuggerScript_class);
     return static_cast<JSScript *>(obj->getPrivate());
 }
 
+static inline void
+SetScriptReferent(JSObject *obj, JSScript *script)
+{
+    JS_ASSERT(obj->getClass() == &DebuggerScript_class);
+    obj->setPrivate(script);
+}
+
 static void
 DebuggerScript_trace(JSTracer *trc, JSObject *obj)
 {
     if (!trc->runtime->gcCurrentCompartment) {
         /* This comes from a private pointer, so no barrier needed. */
-        if (JSScript *script = GetScriptReferent(obj))
-            MarkScriptUnbarriered(trc, script, "Debugger.Script referent");
-
+        if (JSScript *script = GetScriptReferent(obj)) {
+            MarkScriptUnbarriered(trc, &script, "Debugger.Script referent");
+            SetScriptReferent(obj, script);
+        }
     }
 }
 
 Class DebuggerScript_class = {
     "Script",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
@@ -3200,18 +3208,20 @@ static JSFunctionSpec DebuggerFrame_meth
 static void
 DebuggerObject_trace(JSTracer *trc, JSObject *obj)
 {
     if (!trc->runtime->gcCurrentCompartment) {
         /*
          * There is a barrier on private pointers, so the Unbarriered marking
          * is okay.
          */
-        if (JSObject *referent = (JSObject *) obj->getPrivate())
-            MarkObjectUnbarriered(trc, referent, "Debugger.Object referent");
+        if (JSObject *referent = (JSObject *) obj->getPrivate()) {
+            MarkObjectUnbarriered(trc, &referent, "Debugger.Object referent");
+            obj->setPrivate(referent);
+        }
     }
 }
 
 Class DebuggerObject_class = {
     "Object",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
@@ -3842,18 +3852,20 @@ static JSFunctionSpec DebuggerObject_met
 static void
 DebuggerEnv_trace(JSTracer *trc, JSObject *obj)
 {
     if (!trc->runtime->gcCurrentCompartment) {
         /*
          * There is a barrier on private pointers, so the Unbarriered marking
          * is okay.
          */
-        if (Env *referent = (JSObject *) obj->getPrivate())
-            MarkObjectUnbarriered(trc, referent, "Debugger.Environment referent");
+        if (Env *referent = (JSObject *) obj->getPrivate()) {
+            MarkObjectUnbarriered(trc, &referent, "Debugger.Environment referent");
+            obj->setPrivate(referent);
+        }
     }
 }
 
 Class DebuggerEnv_class = {
     "Environment",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
--- a/js/src/vm/ObjectImpl-inl.h
+++ b/js/src/vm/ObjectImpl-inl.h
@@ -83,17 +83,19 @@ js::ObjectImpl::sizeOfThis() const
 
 /* static */ inline void
 js::ObjectImpl::readBarrier(ObjectImpl *obj)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = obj->compartment();
     if (comp->needsBarrier()) {
         MOZ_ASSERT(!comp->rt->gcRunning);
-        MarkObjectUnbarriered(comp->barrierTracer(), obj->asObjectPtr(), "read barrier");
+        JSObject *tmp = obj->asObjectPtr();
+        MarkObjectUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == obj->asObjectPtr());
     }
 #endif
 }
 
 inline void
 js::ObjectImpl::privateWriteBarrierPre(void **old)
 {
 #ifdef JSGC_INCREMENTAL
@@ -119,17 +121,19 @@ js::ObjectImpl::writeBarrierPre(ObjectIm
      * special value.
      */
     if (uintptr_t(obj) < 32)
         return;
 
     JSCompartment *comp = obj->compartment();
     if (comp->needsBarrier()) {
         MOZ_ASSERT(!comp->rt->gcRunning);
-        MarkObjectUnbarriered(comp->barrierTracer(), obj->asObjectPtr(), "write barrier");
+        JSObject *tmp = obj->asObjectPtr();
+        MarkObjectUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == obj->asObjectPtr());
     }
 #endif
 }
 
 /* static */ inline void
 js::ObjectImpl::writeBarrierPost(ObjectImpl *obj, void *addr)
 {
 }
--- a/js/src/vm/ObjectImpl.cpp
+++ b/js/src/vm/ObjectImpl.cpp
@@ -4,16 +4,17 @@
  * 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 "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 
 #include "jsscope.h"
+#include "jsobjinlines.h"
 
 #include "ObjectImpl.h"
 
 #include "ObjectImpl-inl.h"
 
 using namespace js;
 
 static ObjectElements emptyElementsHeader(0, 0);
@@ -32,8 +33,24 @@ MOZ_NEVER_INLINE
 #endif
 const Shape *
 js::ObjectImpl::nativeLookup(JSContext *cx, jsid id)
 {
     MOZ_ASSERT(isNative());
     Shape **spp;
     return Shape::search(cx, lastProperty(), id, &spp);
 }
+
+void
+js::ObjectImpl::markChildren(JSTracer *trc)
+{
+    MarkTypeObject(trc, &type_, "type");
+
+    MarkShape(trc, &shape_, "shape");
+
+    Class *clasp = shape_->getObjectClass();
+    JSObject *obj = asObjectPtr();
+    if (clasp->trace)
+        clasp->trace(trc, obj);
+
+    if (shape_->isNative())
+        MarkObjectSlots(trc, obj, 0, obj->slotSpan());
+}
--- a/js/src/vm/ObjectImpl.h
+++ b/js/src/vm/ObjectImpl.h
@@ -274,22 +274,23 @@ class ObjectImpl : public gc::Cell
          * a spurious 'true' result, if the end of this object is exactly
          * aligned with the end of its arena and dynamic slots are allocated
          * immediately afterwards. Such cases cannot occur for dense arrays
          * (which have at least two fixed slots) and can only result in a leak.
          */
         return elements != emptyObjectElements && elements != fixedElements();
     }
 
-    /* Write barrier support. */
+    /* GC support. */
     static inline void readBarrier(ObjectImpl *obj);
     static inline void writeBarrierPre(ObjectImpl *obj);
     static inline void writeBarrierPost(ObjectImpl *obj, void *addr);
     inline void privateWriteBarrierPre(void **oldval);
     inline void privateWriteBarrierPost(void **oldval);
+    void markChildren(JSTracer *trc);
 
     /* JIT Accessors */
     static size_t offsetOfShape() { return offsetof(ObjectImpl, shape_); }
     HeapPtrShape *addressOfShape() { return &shape_; }
 
     static size_t offsetOfType() { return offsetof(ObjectImpl, type_); }
     HeapPtrTypeObject *addressOfType() { return &type_; }
 
--- a/js/src/vm/RegExpObject.cpp
+++ b/js/src/vm/RegExpObject.cpp
@@ -38,20 +38,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "frontend/TokenStream.h"
 #include "vm/RegExpStatics.h"
 #include "vm/MatchPairs.h"
 
 #include "jsobjinlines.h"
-#include "jsstrinlines.h"
 
 #include "vm/RegExpObject-inl.h"
 #include "vm/RegExpStatics-inl.h"
+#include "vm/StringBuffer-inl.h"
 
 using namespace js;
 using js::detail::RegExpCode;
 
 JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
 JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
 JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
 JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -257,27 +257,28 @@ StackFrame::pcQuadratic(const ContextSta
 void
 StackFrame::mark(JSTracer *trc)
 {
     /*
      * Normally we would use MarkRoot here, except that generators also take
      * this path. However, generators use a special write barrier when the stack
      * frame is copied to the floating frame. Therefore, no barrier is needed.
      */
-    gc::MarkObjectUnbarriered(trc, &scopeChain(), "scope chain");
+    if (flags_ & HAS_SCOPECHAIN)
+        gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain");
     if (isDummyFrame())
         return;
     if (hasArgsObj())
-        gc::MarkObjectUnbarriered(trc, &argsObj(), "arguments");
+        gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments");
     if (isFunctionFrame()) {
-        gc::MarkObjectUnbarriered(trc, fun(), "fun");
+        gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
         if (isEvalFrame())
-            gc::MarkScriptUnbarriered(trc, script(), "eval script");
+            gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
     } else {
-        gc::MarkScriptUnbarriered(trc, script(), "script");
+        gc::MarkScriptUnbarriered(trc, &exec.script, "script");
     }
     if (IS_GC_MARKING_TRACER(trc))
         script()->compartment()->active = true;
     gc::MarkValueUnbarriered(trc, &returnValue(), "rval");
 }
 
 /*****************************************************************************/
 
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -36,33 +36,37 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef String_inl_h__
 #define String_inl_h__
 
-#include "String.h"
-
 #include "jscntxt.h"
 #include "jsgcmark.h"
+#include "jsprobes.h"
+
+#include "String.h"
 
 #include "jsgcinlines.h"
 
 inline void
 JSString::writeBarrierPre(JSString *str)
 {
 #ifdef JSGC_INCREMENTAL
     if (!str)
         return;
 
     JSCompartment *comp = str->compartment();
-    if (comp->needsBarrier())
-        MarkStringUnbarriered(comp->barrierTracer(), str, "write barrier");
+    if (comp->needsBarrier()) {
+        JSString *tmp = str;
+        MarkStringUnbarriered(comp->barrierTracer(), &tmp, "write barrier");
+        JS_ASSERT(tmp == str);
+    }
 #endif
 }
 
 inline void
 JSString::writeBarrierPost(JSString *str, void *addr)
 {
 }
 
@@ -76,18 +80,21 @@ JSString::needWriteBarrierPre(JSCompartm
 #endif
 }
 
 inline void
 JSString::readBarrier(JSString *str)
 {
 #ifdef JSGC_INCREMENTAL
     JSCompartment *comp = str->compartment();
-    if (comp->needsBarrier())
-        MarkStringUnbarriered(comp->barrierTracer(), str, "read barrier");
+    if (comp->needsBarrier()) {
+        JSString *tmp = str;
+        MarkStringUnbarriered(comp->barrierTracer(), &tmp, "read barrier");
+        JS_ASSERT(tmp == str);
+    }
 #endif
 }
 
 JS_ALWAYS_INLINE bool
 JSString::validateLength(JSContext *cx, size_t length)
 {
     if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
         js_ReportAllocationOverflow(cx);
@@ -114,16 +121,23 @@ JSRope::new_(JSContext *cx, JSString *le
         return NULL;
     JSRope *str = (JSRope *)js_NewGCString(cx);
     if (!str)
         return NULL;
     str->init(left, right, length);
     return str;
 }
 
+inline void
+JSRope::markChildren(JSTracer *trc)
+{
+    js::gc::MarkStringUnbarriered(trc, &d.u1.left, "left child");
+    js::gc::MarkStringUnbarriered(trc, &d.s.u2.right, "right child");
+}
+
 JS_ALWAYS_INLINE void
 JSDependentString::init(JSLinearString *base, const jschar *chars, size_t length)
 {
     d.lengthAndFlags = buildLengthAndFlags(length, DEPENDENT_BIT);
     d.u1.chars = chars;
     d.s.u2.base = base;
     JSString::writeBarrierPost(d.s.u2.base, &d.s.u2.base);
 }
@@ -141,16 +155,22 @@ JSDependentString::new_(JSContext *cx, J
 
     JSDependentString *str = (JSDependentString *)js_NewGCString(cx);
     if (!str)
         return NULL;
     str->init(base, chars, length);
     return str;
 }
 
+inline void
+JSDependentString::markChildren(JSTracer *trc)
+{
+    js::gc::MarkStringUnbarriered(trc, &d.s.u2.base, "base");
+}
+
 inline js::PropertyName *
 JSFlatString::toPropertyName(JSContext *cx)
 {
 #ifdef DEBUG
     uint32_t dummy;
     JS_ASSERT(!isIndex(&dummy));
 #endif
     if (isAtom())
@@ -414,9 +434,34 @@ JSExternalString::finalize(JSContext *cx
 
 inline void
 JSExternalString::finalize()
 {
     const JSStringFinalizer *fin = externalFinalizer();
     fin->finalize(fin, const_cast<jschar *>(chars()));
 }
 
+namespace js {
+
+static JS_ALWAYS_INLINE JSFixedString *
+NewShortString(JSContext *cx, const jschar *chars, size_t length)
+{
+    /*
+     * Don't bother trying to find a static atom; measurement shows that not
+     * many get here (for one, Atomize is catching them).
+     */
+    JS_ASSERT(JSShortString::lengthFits(length));
+    JSInlineString *str = JSInlineString::lengthFits(length)
+                          ? JSInlineString::new_(cx)
+                          : JSShortString::new_(cx);
+    if (!str)
+        return NULL;
+
+    jschar *storage = str->init(length);
+    PodCopy(storage, chars, length);
+    storage[length] = 0;
+    Probes::createString(cx, str, length);
+    return str;
+}
+
+} /* namespace js */
+
 #endif
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -495,29 +495,29 @@ StaticStrings::init(JSContext *cx)
 }
 
 void
 StaticStrings::trace(JSTracer *trc)
 {
     /* These strings never change, so barriers are not needed. */
 
     for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
-        if (JSAtom *atom = unitStaticTable[i])
-            MarkStringUnbarriered(trc, atom, "unit-static-string");
+        if (unitStaticTable[i])
+            MarkStringUnbarriered(trc, &unitStaticTable[i], "unit-static-string");
     }
 
     for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
-        if (JSAtom *atom = length2StaticTable[i])
-            MarkStringUnbarriered(trc, atom, "length2-static-string");
+        if (length2StaticTable[i])
+            MarkStringUnbarriered(trc, &length2StaticTable[i], "length2-static-string");
     }
 
     /* This may mark some strings more than once, but so be it. */
     for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
-        if (JSAtom *atom = intStaticTable[i])
-            MarkStringUnbarriered(trc, atom, "int-static-string");
+        if (intStaticTable[i])
+            MarkStringUnbarriered(trc, &intStaticTable[i], "int-static-string");
     }
 }
 
 bool
 StaticStrings::isStatic(JSAtom *atom)
 {
     const jschar *chars = atom->chars();
     switch (atom->length()) {
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -443,16 +443,18 @@ class JSRope : public JSString
         JS_ASSERT(isRope());
         return d.u1.left;
     }
 
     inline JSString *rightChild() const {
         JS_ASSERT(isRope());
         return d.s.u2.right;
     }
+
+    inline void markChildren(JSTracer *trc);
 };
 
 JS_STATIC_ASSERT(sizeof(JSRope) == sizeof(JSString));
 
 class JSLinearString : public JSString
 {
     friend class JSString;
 
@@ -485,16 +487,18 @@ class JSDependentString : public JSLinea
   public:
     static inline JSDependentString *new_(JSContext *cx, JSLinearString *base,
                                           const jschar *chars, size_t length);
 
     JSLinearString *base() const {
         JS_ASSERT(JSString::isDependent());
         return d.s.u2.base;
     }
+
+    inline void markChildren(JSTracer *trc);
 };
 
 JS_STATIC_ASSERT(sizeof(JSDependentString) == sizeof(JSString));
 
 class JSFlatString : public JSLinearString
 {
     friend class JSRope;
     void morphExtensibleIntoDependent(JSLinearString *base) {
new file mode 100644
--- /dev/null
+++ b/js/src/vm/StringBuffer-inl.h
@@ -0,0 +1,84 @@
+/* -*- 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 StringBuffer_inl_h___
+#define StringBuffer_inl_h___
+
+#include "vm/StringBuffer.h"
+
+#include "vm/String-inl.h"
+
+namespace js {
+
+inline bool
+StringBuffer::checkLength(size_t length)
+{
+    return JSString::validateLength(context(), length);
+}
+
+inline bool
+StringBuffer::reserve(size_t len)
+{
+    if (!checkLength(len))
+        return false;
+    return cb.reserve(len);
+}
+
+inline bool
+StringBuffer::resize(size_t len)
+{
+    if (!checkLength(len))
+        return false;
+    return cb.resize(len);
+}
+
+inline bool
+StringBuffer::append(const jschar c)
+{
+    if (!checkLength(cb.length() + 1))
+        return false;
+    return cb.append(c);
+}
+
+inline bool
+StringBuffer::append(const jschar *chars, size_t len)
+{
+    if (!checkLength(cb.length() + len))
+        return false;
+    return cb.append(chars, len);
+}
+
+inline bool
+StringBuffer::append(const jschar *begin, const jschar *end)
+{
+    if (!checkLength(cb.length() + (end - begin)))
+        return false;
+    return cb.append(begin, end);
+}
+
+inline bool
+StringBuffer::appendN(const jschar c, size_t n)
+{
+    if (!checkLength(cb.length() + n))
+        return false;
+    return cb.appendN(c, n);
+}
+
+extern bool
+ValueToStringBufferSlow(JSContext *cx, const Value &v, StringBuffer &sb);
+
+inline bool
+ValueToStringBuffer(JSContext *cx, const Value &v, StringBuffer &sb)
+{
+    if (v.isString())
+        return sb.append(v.toString());
+
+    return ValueToStringBufferSlow(cx, v, sb);
+}
+
+}  /* namespace js */
+
+#endif /* StringBuffer_inl_h__ */
new file mode 100644
--- /dev/null
+++ b/js/src/vm/StringBuffer.cpp
@@ -0,0 +1,80 @@
+/* -*- 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 "vm/StringBuffer.h"
+
+#include "vm/String-inl.h"
+#include "vm/StringBuffer-inl.h"
+
+using namespace js;
+
+jschar *
+StringBuffer::extractWellSized()
+{
+    size_t capacity = cb.capacity();
+    size_t length = cb.length();
+
+    jschar *buf = cb.extractRawBuffer();
+    if (!buf)
+        return NULL;
+
+    /* For medium/big buffers, avoid wasting more than 1/4 of the memory. */
+    JS_ASSERT(capacity >= length);
+    if (length > CharBuffer::sMaxInlineStorage && capacity - length > length / 4) {
+        size_t bytes = sizeof(jschar) * (length + 1);
+        JSContext *cx = context();
+        jschar *tmp = (jschar *)cx->realloc_(buf, bytes);
+        if (!tmp) {
+            cx->free_(buf);
+            return NULL;
+        }
+        buf = tmp;
+    }
+
+    return buf;
+}
+
+JSFixedString *
+StringBuffer::finishString()
+{
+    JSContext *cx = context();
+    if (cb.empty())
+        return cx->runtime->atomState.emptyAtom;
+
+    size_t length = cb.length();
+    if (!checkLength(length))
+        return NULL;
+
+    JS_STATIC_ASSERT(JSShortString::MAX_SHORT_LENGTH < CharBuffer::InlineLength);
+    if (JSShortString::lengthFits(length))
+        return NewShortString(cx, cb.begin(), length);
+
+    if (!cb.append('\0'))
+        return NULL;
+
+    jschar *buf = extractWellSized();
+    if (!buf)
+        return NULL;
+
+    JSFixedString *str = js_NewString(cx, buf, length);
+    if (!str)
+        cx->free_(buf);
+    return str;
+}
+
+JSAtom *
+StringBuffer::finishAtom()
+{
+    JSContext *cx = context();
+
+    size_t length = cb.length();
+    if (length == 0)
+        return cx->runtime->atomState.emptyAtom;
+
+    JSAtom *atom = js_AtomizeChars(cx, cb.begin(), length);
+    cb.clear();
+    return atom;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/StringBuffer.h
@@ -0,0 +1,145 @@
+/* -*- 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 StringBuffer_h___
+#define StringBuffer_h___
+
+#include "mozilla/Attributes.h"
+
+#include "jscntxt.h"
+#include "jspubtd.h"
+
+#include "js/Vector.h"
+
+namespace js {
+
+/*
+ * String builder that eagerly checks for over-allocation past the maximum
+ * string length.
+ *
+ * Any operation which would exceed the maximum string length causes an
+ * exception report on the context and results in a failed return value.
+ *
+ * Well-sized extractions (which waste no more than 1/4 of their char
+ * buffer space) are guaranteed for strings built by this interface.
+ * See |extractWellSized|.
+ *
+ * Note: over-allocation is not checked for when using the infallible
+ * |replaceRawBuffer|, so the implementation of |finishString| also must check
+ * for over-allocation.
+ */
+class StringBuffer
+{
+    /* cb's buffer is taken by the new string so use ContextAllocPolicy. */
+    typedef Vector<jschar, 32, ContextAllocPolicy> CharBuffer;
+
+    CharBuffer cb;
+
+    static inline bool checkLength(JSContext *cx, size_t length);
+    inline bool checkLength(size_t length);
+    JSContext *context() const { return cb.allocPolicy().context(); }
+    jschar *extractWellSized();
+
+    StringBuffer(const StringBuffer &other) MOZ_DELETE;
+    void operator=(const StringBuffer &other) MOZ_DELETE;
+
+  public:
+    explicit inline StringBuffer(JSContext *cx);
+    inline bool reserve(size_t len);
+    inline bool resize(size_t len);
+    inline bool append(const jschar c);
+    inline bool append(const jschar *chars, size_t len);
+    inline bool append(const jschar *begin, const jschar *end);
+    inline bool append(JSString *str);
+    inline bool append(JSLinearString *str);
+    inline bool appendN(const jschar c, size_t n);
+    inline bool appendInflated(const char *cstr, size_t len);
+
+    /* Infallible variants usable when the corresponding space is reserved. */
+    void infallibleAppend(const jschar c) {
+        cb.infallibleAppend(c);
+    }
+    void infallibleAppend(const jschar *chars, size_t len) {
+        cb.infallibleAppend(chars, len);
+    }
+    void infallibleAppend(const jschar *begin, const jschar *end) {
+        cb.infallibleAppend(begin, end);
+    }
+    void infallibleAppendN(const jschar c, size_t n) {
+        cb.infallibleAppendN(c, n);
+    }
+
+    JSAtom *atomize(unsigned flags = 0);
+    static JSAtom *atomize(JSContext *cx, const CharBuffer &cb, unsigned flags = 0);
+    static JSAtom *atomize(JSContext *cx, const jschar *begin, size_t length, unsigned flags = 0);
+
+    void replaceRawBuffer(jschar *chars, size_t len) { cb.replaceRawBuffer(chars, len); }
+    jschar *begin() { return cb.begin(); }
+    jschar *end() { return cb.end(); }
+    const jschar *begin() const { return cb.begin(); }
+    const jschar *end() const { return cb.end(); }
+    bool empty() const { return cb.empty(); }
+    inline size_t length() const;
+
+    /*
+     * Creates a string from the characters in this buffer, then (regardless
+     * whether string creation succeeded or failed) empties the buffer.
+     */
+    JSFixedString *finishString();
+
+    /* Identical to finishString() except that an atom is created. */
+    JSAtom *finishAtom();
+
+    template <size_t ArrayLength>
+    bool append(const char (&array)[ArrayLength]) {
+        return cb.append(array, array + ArrayLength - 1); /* No trailing '\0'. */
+    }
+};
+
+inline
+StringBuffer::StringBuffer(JSContext *cx)
+  : cb(cx)
+{}
+
+inline bool
+StringBuffer::append(JSString *str)
+{
+    JSLinearString *linear = str->ensureLinear(context());
+    if (!linear)
+        return false;
+    return append(linear);
+}
+
+inline bool
+StringBuffer::append(JSLinearString *str)
+{
+    JS::Anchor<JSString *> anch(str);
+    return cb.append(str->chars(), str->length());
+}
+
+inline size_t
+StringBuffer::length() const
+{
+    JS_ASSERT(cb.length() <= JSString::MAX_LENGTH);
+    return cb.length();
+}
+
+inline bool
+StringBuffer::appendInflated(const char *cstr, size_t cstrlen)
+{
+    size_t lengthBefore = length();
+    if (!cb.growByUninitialized(cstrlen))
+        return false;
+    DebugOnly<size_t> oldcstrlen = cstrlen;
+    DebugOnly<bool> ok = InflateStringToBuffer(context(), cstr, cstrlen,
+                                               begin() + lengthBefore, &cstrlen);
+    JS_ASSERT(ok && oldcstrlen == cstrlen);
+    return true;
+}
+
+}  /* namespace js */
+
+#endif /* StringBuffer_h___ */
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -87,25 +87,27 @@ interface nsIXPCScriptable : nsISupports
     const PRUint32 WANT_NEWENUMERATE                = 1 <<  8;
     const PRUint32 WANT_NEWRESOLVE                  = 1 <<  9;
     const PRUint32 WANT_CONVERT                     = 1 << 10;
     const PRUint32 WANT_FINALIZE                    = 1 << 11;
     const PRUint32 WANT_CHECKACCESS                 = 1 << 12;
     const PRUint32 WANT_CALL                        = 1 << 13;
     const PRUint32 WANT_CONSTRUCT                   = 1 << 14;
     const PRUint32 WANT_HASINSTANCE                 = 1 << 15;
+    // Unused bit here!
     const PRUint32 USE_JSSTUB_FOR_ADDPROPERTY       = 1 << 17;
     const PRUint32 USE_JSSTUB_FOR_DELPROPERTY       = 1 << 18;
     const PRUint32 USE_JSSTUB_FOR_SETPROPERTY       = 1 << 19;
     const PRUint32 DONT_ENUM_STATIC_PROPS           = 1 << 20;
     const PRUint32 DONT_ENUM_QUERY_INTERFACE        = 1 << 21;
     const PRUint32 DONT_ASK_INSTANCE_FOR_SCRIPTABLE = 1 << 22;
     const PRUint32 CLASSINFO_INTERFACES_ONLY        = 1 << 23;
     const PRUint32 ALLOW_PROP_MODS_DURING_RESOLVE   = 1 << 24;
     const PRUint32 ALLOW_PROP_MODS_TO_PROTOTYPE     = 1 << 25;
+    const PRUint32 IS_GLOBAL_OBJECT                 = 1 << 26;
     const PRUint32 DONT_REFLECT_INTERFACE_NAMES     = 1 << 27;
     const PRUint32 WANT_EQUALITY                    = 1 << 28;
     const PRUint32 WANT_OUTER_OBJECT                = 1 << 29;
     const PRUint32 USE_STUB_EQUALITY_HOOK           = 1 << 30;
 
     // The high order bit is RESERVED for consumers of these flags. 
     // No implementor of this interface should ever return flags 
     // with this bit set.
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -175,17 +175,24 @@ interface nsIXPConnectWrappedNative : ns
      */
 
     readonly attribute nsIXPConnect XPConnect;
     nsIInterfaceInfo FindInterfaceWithMember(in jsid nameID);
     nsIInterfaceInfo FindInterfaceWithName(in jsid nameID);
 
     void debugDump(in short depth);
 
-    void refreshPrototype();
+    /*
+     * This finishes initializing a wrapped global, doing the parts that we
+     * couldn't do while the global and window were being simultaneously
+     * bootstrapped. This should be called exactly once, and only for wrapped
+     * globals.
+     */
+    void finishInitForWrappedGlobal();
+
     /* 
      * This returns a pointer into the instance and care should be taken
      * to make sure the pointer is not kept past the life time of the
      * object it points into.
      */
     voidPtrPtr GetSecurityInfoAddress();
 
     /*
@@ -393,17 +400,17 @@ interface nsIXPCFunctionThisTranslator :
 
 enum nsGCType {
     nsGCNormal,
     nsGCShrinking,
     nsGCIncremental
 };
 %}
 
-[uuid(e92bf5e0-494c-11e1-b86c-0800200c9a66)]
+[uuid(0213cb40-2dd5-4ac8-a9d3-157bd53c3824)]
 interface nsIXPConnect : nsISupports
 {
 %{ C++
   NS_DEFINE_STATIC_CID_ACCESSOR(NS_XPCONNECT_CID)
 %}
 
     /**
      * Initializes classes on a global object that has already been created.
@@ -416,32 +423,26 @@ interface nsIXPConnect : nsISupports
      * Creates a new global object using the given aCOMObj as the global
      * object. The object will be set up according to the flags (defined
      * below). If you do not pass INIT_JS_STANDARD_CLASSES, then aCOMObj
      * must implement nsIXPCScriptable so it can resolve the standard
      * classes when asked by the JS engine.
      *
      * @param aJSContext the context to use while creating the global object.
      * @param aCOMObj the native object that represents the global object.
-     * @param aIID the IID used to wrap the global object.
      * @param aPrincipal the principal of the code that will run in this
      *                   compartment. Can be null if not on the main thread.
-     * @param aExtraPtr must be passed if aPrincipal is null. Used to separate
-     *                  code from the same principal into different
-     *                  compartments, as for sandboxes.
      * @param aFlags one of the flags below specifying what options this
      *               global object wants.
      */
     nsIXPConnectJSObjectHolder
     initClassesWithNewWrappedGlobal(
                   in JSContextPtr aJSContext,
                   in nsISupports  aCOMObj,
-                  in nsIIDRef     aIID,
                   in nsIPrincipal aPrincipal,
-                  in nsISupports  aExtraPtr,
                   in PRUint32     aFlags);
 
     const PRUint32 INIT_JS_STANDARD_CLASSES  = 1 << 0;
     const PRUint32 FLAG_SYSTEM_GLOBAL_OBJECT = 1 << 1;
     const PRUint32 OMIT_COMPONENTS_OBJECT    = 1 << 2;
 
     /**
     * wrapNative will create a new JSObject or return an existing one.
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -689,19 +689,17 @@ mozJSComponentLoader::GlobalForLocation(
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Make sure InitClassesWithNewWrappedGlobal() installs the
     // backstage pass as the global in our compilation context.
     JS_SetGlobalObject(cx, nsnull);
 
     nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
     rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
-                                              NS_GET_IID(nsISupports),
                                               mSystemPrincipal,
-                                              nsnull,
                                               nsIXPConnect::
                                               FLAG_SYSTEM_GLOBAL_OBJECT,
                                               getter_AddRefs(holder));
     NS_ENSURE_SUCCESS(rv, rv);
 
     JSObject *global;
     rv = holder->GetJSObject(&global);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -1943,19 +1943,17 @@ main(int argc, char **argv, char **envp)
         if (NS_FAILED(rv)) {
             fprintf(gErrFile, "+++ Failed to get backstage pass from rtsvc: %8x\n",
                     rv);
             return 1;
         }
 
         nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
         rv = xpc->InitClassesWithNewWrappedGlobal(cx, backstagePass,
-                                                  NS_GET_IID(nsISupports),
                                                   systemprincipal,
-                                                  nsnull,
                                                   nsIXPConnect::
                                                   FLAG_SYSTEM_GLOBAL_OBJECT,
                                                   getter_AddRefs(holder));
         if (NS_FAILED(rv))
             return 1;
 
         rv = holder->GetJSObject(&glob);
         if (NS_FAILED(rv)) {
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -4218,18 +4218,17 @@ nsXPCComponents::AttachNewComponentsObje
     AutoMarkingNativeInterfacePtr iface(ccx);
     iface = XPCNativeInterface::GetNewOrUsed(ccx, &NS_GET_IID(nsIXPCComponents));
 
     if (!iface)
         return false;
 
     nsCOMPtr<XPCWrappedNative> wrapper;
     xpcObjectHelper helper(cholder);
-    XPCWrappedNative::GetNewOrUsed(ccx, helper, aScope, iface,
-                                   OBJ_IS_NOT_GLOBAL, getter_AddRefs(wrapper));
+    XPCWrappedNative::GetNewOrUsed(ccx, helper, aScope, iface, getter_AddRefs(wrapper));
     if (!wrapper)
         return false;
 
     aScope->SetComponents(components);
 
     jsid id = ccx.GetRuntime()->GetStringID(XPCJSRuntime::IDX_COMPONENTS);
     JSObject* obj;
 
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -343,26 +343,19 @@ XPCConvert::NativeData2JS(XPCLazyCallCon
                         nsCOMPtr<nsIVariant> variant = do_QueryInterface(iface);
                         if (!variant)
                             return false;
 
                         return XPCVariant::VariantDataToJS(lccx, variant,
                                                            pErr, d);
                     }
                     // else...
-
-                    // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In
-                    // fact, this code is depending on the fact that the
-                    // global object will not have been collected, and
-                    // therefore this NativeInterface2JSObject will not end up
-                    // creating a new XPCNativeScriptableShared.
                     xpcObjectHelper helper(iface);
                     if (!NativeInterface2JSObject(lccx, d, nsnull, helper, iid,
-                                                  nsnull, true,
-                                                  OBJ_IS_NOT_GLOBAL, pErr))
+                                                  nsnull, true, pErr))
                         return false;
 
 #ifdef DEBUG
                     JSObject* jsobj = JSVAL_TO_OBJECT(*d);
                     if (jsobj && !js::GetObjectParent(jsobj))
                         NS_ASSERTION(js::GetObjectClass(jsobj)->flags & JSCLASS_IS_GLOBAL,
                                      "Why did we recreate this wrapper?");
 #endif
@@ -871,17 +864,16 @@ CreateHolderIfNeeded(XPCCallContext& ccx
 JSBool
 XPCConvert::NativeInterface2JSObject(XPCLazyCallContext& lccx,
                                      jsval* d,
                                      nsIXPConnectJSObjectHolder** dest,
                                      xpcObjectHelper& aHelper,
                                      const nsID* iid,
                                      XPCNativeInterface** Interface,
                                      bool allowNativeWrapper,
-                                     bool isGlobal,
                                      nsresult* pErr)
 {
     NS_ASSERTION(!Interface || iid,
                  "Need the iid if you pass in an XPCNativeInterface cache.");
 
     *d = JSVAL_NULL;
     if (dest)
         *dest = nsnull;
@@ -1011,17 +1003,16 @@ XPCConvert::NativeInterface2JSObject(XPC
     XPCWrappedNative* wrapper;
     nsRefPtr<XPCWrappedNative> strongWrapper;
     if (!flat) {
         XPCCallContext &ccx = lccx.GetXPCCallContext();
         if (!ccx.IsValid())
             return false;
 
         rv = XPCWrappedNative::GetNewOrUsed(ccx, aHelper, xpcscope, iface,
-                                            isGlobal,
                                             getter_AddRefs(strongWrapper));
 
         wrapper = strongWrapper;
     } else if (IS_WN_WRAPPER_OBJECT(flat)) {
         wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(flat));
 
         // If asked to return the wrapper we'll return a strong reference,
         // otherwise we'll just return its JSObject in d (which should be
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -653,24 +653,21 @@ xpc_ForcePropertyResolve(JSContext* cx, 
         return false;
     return true;
 }
 
 inline JSObject*
 xpc_NewSystemInheritingJSObject(JSContext *cx, JSClass *clasp, JSObject *proto,
                                 bool uniqueType, JSObject *parent)
 {
+    // Global creation should go through XPCWrappedNative::WrapNewGlobal().
+    MOZ_ASSERT(!(clasp->flags & JSCLASS_IS_GLOBAL));
+
     JSObject *obj;
-    if (clasp->flags & JSCLASS_IS_GLOBAL) {
-        obj = JS_NewGlobalObject(cx, clasp);
-        if (obj && proto) {
-            if (!JS_SplicePrototype(cx, obj, proto))
-                obj = NULL;
-        }
-    } else if (uniqueType) {
+    if (uniqueType) {
         obj = JS_NewObjectWithUniqueType(cx, clasp, proto, parent);
     } else {
         obj = JS_NewObject(cx, clasp, proto, parent);
     }
     if (obj && JS_IsSystemObject(cx, parent) && !JS_MakeSystemObject(cx, obj))
         obj = NULL;
     return obj;
 }
--- a/js/xpconnect/src/XPCJSID.cpp
+++ b/js/xpconnect/src/XPCJSID.cpp
@@ -255,59 +255,62 @@ NS_IMPL_THREADSAFE_RELEASE(SharedScripta
 // The nsIXPCScriptable map declaration that will generate stubs for us...
 #define XPC_MAP_CLASSNAME           SharedScriptableHelperForJSIID
 #define XPC_MAP_QUOTED_CLASSNAME   "JSIID"
 #define XPC_MAP_FLAGS               nsIXPCScriptable::DONT_ENUM_STATIC_PROPS |\
                                     nsIXPCScriptable::ALLOW_PROP_MODS_DURING_RESOLVE
 #include "xpc_map_end.h" /* This will #undef the above */
 
 static nsIXPCScriptable* gSharedScriptableHelperForJSIID;
+static bool gClassObjectsWereInited = false;
+
+static void EnsureClassObjectsInitialized()
+{
+    if (!gClassObjectsWereInited) {
+        gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
+        NS_ADDREF(gSharedScriptableHelperForJSIID);
+
+        gClassObjectsWereInited = true;
+    }
+}
 
 NS_METHOD GetSharedScriptableHelperForJSIID(PRUint32 language,
                                             nsISupports **helper)
 {
+    EnsureClassObjectsInitialized();
     if (language == nsIProgrammingLanguage::JAVASCRIPT) {
         NS_IF_ADDREF(gSharedScriptableHelperForJSIID);
         *helper = gSharedScriptableHelperForJSIID;
     } else
         *helper = nsnull;
     return NS_OK;
 }
 
 /******************************************************/
 
-static JSBool gClassObjectsWereInited = false;
-
 #define NULL_CID                                                              \
 { 0x00000000, 0x0000, 0x0000,                                                 \
   { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }
 
 NS_DECL_CI_INTERFACE_GETTER(nsJSIID)
 NS_IMPL_CLASSINFO(nsJSIID, GetSharedScriptableHelperForJSIID,
                   nsIClassInfo::THREADSAFE, NULL_CID)
 
 NS_DECL_CI_INTERFACE_GETTER(nsJSCID)
 NS_IMPL_CLASSINFO(nsJSCID, NULL, nsIClassInfo::THREADSAFE, NULL_CID)
 
-void xpc_InitJSxIDClassObjects()
-{
-    if (!gClassObjectsWereInited) {
-        gSharedScriptableHelperForJSIID = new SharedScriptableHelperForJSIID();
-        NS_ADDREF(gSharedScriptableHelperForJSIID);
-    }
-    gClassObjectsWereInited = true;
-}
-
 void xpc_DestroyJSxIDClassObjects()
 {
-    NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID));
-    NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID));
-    NS_IF_RELEASE(gSharedScriptableHelperForJSIID);
+    if (gClassObjectsWereInited) {
+        NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSIID));
+        NS_IF_RELEASE(NS_CLASSINFO_NAME(nsJSCID));
+        NS_IF_RELEASE(gSharedScriptableHelperForJSIID);
 
-    gClassObjectsWereInited = false;
+        gClassObjectsWereInited = false;
+    }
 }
 
 /***************************************************************************/
 
 NS_INTERFACE_MAP_BEGIN(nsJSIID)
   NS_INTERFACE_MAP_ENTRY(nsIJSID)
   NS_INTERFACE_MAP_ENTRY(nsIJSIID)
   NS_INTERFACE_MAP_ENTRY(nsIXPCScriptable)
--- a/js/xpconnect/src/XPCMaps.cpp
+++ b/js/xpconnect/src/XPCMaps.cpp
@@ -617,17 +617,16 @@ XPCNativeScriptableSharedMap::~XPCNative
 {
     if (mTable)
         JS_DHashTableDestroy(mTable);
 }
 
 JSBool
 XPCNativeScriptableSharedMap::GetNewOrUsed(uint32_t flags,
                                            char* name,
-                                           bool isGlobal,
                                            PRUint32 interfacesBitmap,
                                            XPCNativeScriptableInfo* si)
 {
     NS_PRECONDITION(name,"bad param");
     NS_PRECONDITION(si,"bad param");
 
     XPCNativeScriptableShared key(flags, name, interfacesBitmap);
     Entry* entry = (Entry*)
@@ -638,17 +637,17 @@ XPCNativeScriptableSharedMap::GetNewOrUs
     XPCNativeScriptableShared* shared = entry->key;
 
     if (!shared) {
         entry->key = shared =
             new XPCNativeScriptableShared(flags, key.TransferNameOwnership(),
                                           interfacesBitmap);
         if (!shared)
             return false;
-        shared->PopulateJSClass(isGlobal);
+        shared->PopulateJSClass();
     }
     si->SetScriptableShared(shared);
     return true;
 }
 
 /***************************************************************************/
 // implement XPCWrappedNativeProtoMap...
 
--- a/js/xpconnect/src/XPCMaps.h
+++ b/js/xpconnect/src/XPCMaps.h
@@ -575,18 +575,18 @@ public:
               const JSDHashEntryHdr *entry,
               const void *key);
 
         static struct JSDHashTableOps sOps;
     };
 
     static XPCNativeScriptableSharedMap* newMap(int size);
 
-    JSBool GetNewOrUsed(uint32_t flags, char* name, bool isGlobal,
-                        PRUint32 interfacesBitmap, XPCNativeScriptableInfo* si);
+    JSBool GetNewOrUsed(uint32_t flags, char* name, PRUint32 interfacesBitmap,
+                        XPCNativeScriptableInfo* si);
 
     inline uint32_t Count() {return mTable->entryCount;}
     inline uint32_t Enumerate(JSDHashEnumerator f, void *arg)
         {return JS_DHashTableEnumerate(mTable, f, arg);}
 
     ~XPCNativeScriptableSharedMap();
 private:
     XPCNativeScriptableSharedMap();    // no implementation
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -1106,26 +1106,20 @@ xpc_qsXPCOMObjectToJsval(XPCLazyCallCont
 {
     NS_PRECONDITION(iface, "Who did that and why?");
 
     // From the T_INTERFACE case in XPCConvert::NativeData2JS.
     // This is one of the slowest things quick stubs do.
 
     JSContext *cx = lccx.GetJSContext();
 
-    // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In
-    // fact, this code is depending on the fact that the
-    // global object will not have been collected, and
-    // therefore this NativeInterface2JSObject will not end up
-    // creating a new XPCNativeScriptableShared.
-
     nsresult rv;
     if (!XPCConvert::NativeInterface2JSObject(lccx, rval, nsnull,
                                               aHelper, iid, iface,
-                                              true, OBJ_IS_NOT_GLOBAL, &rv)) {
+                                              true, &rv)) {
         // I can't tell if NativeInterface2JSObject throws JS exceptions
         // or not.  This is a sloppy stab at the right semantics; the
         // method really ought to be fixed to behave consistently.
         if (!JS_IsExceptionPending(cx))
             xpc_qsThrow(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
         return false;
     }
 
--- a/js/xpconnect/src/XPCRuntimeService.cpp
+++ b/js/xpconnect/src/XPCRuntimeService.cpp
@@ -58,16 +58,17 @@ NS_IMPL_THREADSAFE_RELEASE(BackstagePass
 #define XPC_MAP_CLASSNAME           BackstagePass
 #define XPC_MAP_QUOTED_CLASSNAME   "BackstagePass"
 #define                             XPC_MAP_WANT_NEWRESOLVE
 #define XPC_MAP_FLAGS       nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY   |  \
                             nsIXPCScriptable::USE_JSSTUB_FOR_DELPROPERTY   |  \
                             nsIXPCScriptable::USE_JSSTUB_FOR_SETPROPERTY   |  \
                             nsIXPCScriptable::DONT_ENUM_STATIC_PROPS       |  \
                             nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE    |  \
+                            nsIXPCScriptable::IS_GLOBAL_OBJECT             |  \
                             nsIXPCScriptable::DONT_REFLECT_INTERFACE_NAMES
 #include "xpc_map_end.h" /* This will #undef the above */
 
 /* bool newResolve (in nsIXPConnectWrappedNative wrapper, in JSContextPtr cx, in JSObjectPtr obj, in jsval id, in PRUint32 flags, out JSObjectPtr objp); */
 NS_IMETHODIMP
 BackstagePass::NewResolve(nsIXPConnectWrappedNative *wrapper,
                           JSContext * cx, JSObject * obj,
                           jsid id, PRUint32 flags,
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -1310,18 +1310,17 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
                                 goto pre_call_clean_up;
                             }
                             if (newThis) {
                                 jsval v;
                                 xpcObjectHelper helper(newThis);
                                 JSBool ok =
                                   XPCConvert::NativeInterface2JSObject(ccx,
                                                                        &v, nsnull, helper, newWrapperIID,
-                                                                       nsnull, false, false,
-                                                                       nsnull);
+                                                                       nsnull, false, nsnull);
                                 if (newWrapperIID)
                                     nsMemory::Free(newWrapperIID);
                                 if (!ok) {
                                     goto pre_call_clean_up;
                                 }
                                 thisObj = JSVAL_TO_OBJECT(v);
                                 if (!JS_WrapObject(cx, &thisObj))
                                     goto pre_call_clean_up;
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -317,26 +317,171 @@ static void DEBUG_TrackShutdownWrapper(X
 #endif
 
 /***************************************************************************/
 static nsresult
 FinishCreate(XPCCallContext& ccx,
              XPCWrappedNativeScope* Scope,
              XPCNativeInterface* Interface,
              nsWrapperCache *cache,
-             XPCWrappedNative* wrapper,
+             XPCWrappedNative* inWrapper,
              XPCWrappedNative** resultWrapper);
 
 // static
+//
+// This method handles the special case of wrapping a new global object.
+//
+// The normal code path for wrapping natives goes through
+// XPCConvert::NativeInterface2JSObject, XPCWrappedNative::GetNewOrUsed,
+// and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
+// very early on that we have an XPCWrappedNativeScope and corresponding global
+// JS object, which are the very things we need to create here. So we special-
+// case the logic and do some things in a different order.
+nsresult
+XPCWrappedNative::WrapNewGlobal(XPCCallContext &ccx, xpcObjectHelper &nativeHelper,
+                                nsIPrincipal *principal, bool initStandardClasses,
+                                XPCWrappedNative **wrappedGlobal)
+{
+    bool success;
+    nsresult rv;
+    nsISupports *identity = nativeHelper.GetCanonical();
+
+    // The object should specify that it's meant to be global.
+    MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+
+    // We shouldn't be reusing globals.
+    MOZ_ASSERT(!nativeHelper.GetWrapperCache() ||
+               !nativeHelper.GetWrapperCache()->GetWrapperPreserveColor());
+
+    // Put together the ScriptableCreateInfo...
+    XPCNativeScriptableCreateInfo sciProto;
+    XPCNativeScriptableCreateInfo sciMaybe;
+    const XPCNativeScriptableCreateInfo& sciWrapper =
+        GatherScriptableCreateInfo(identity, nativeHelper.GetClassInfo(),
+                                   sciProto, sciMaybe);
+
+    // ...and then ScriptableInfo. We need all this stuff now because it's going
+    // to tell us the JSClass of the object we're going to create.
+    XPCNativeScriptableInfo *si = XPCNativeScriptableInfo::Construct(ccx, &sciWrapper);
+    MOZ_ASSERT(si);
+
+    // Finally, we get to the JSClass.
+    JSClass *clasp = si->GetJSClass();
+    MOZ_ASSERT(clasp->flags & JSCLASS_IS_GLOBAL);
+
+    // Create the global.
+    JSObject *global;
+    JSCompartment *compartment;
+    rv = xpc_CreateGlobalObject(ccx, clasp, principal, nsnull, false,
+                                &global, &compartment);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Immediately enter the global's compartment, so that everything else we
+    // create ends up there.
+    JSAutoEnterCompartment ac;
+    success = ac.enter(ccx, global);
+    MOZ_ASSERT(success);
+
+    // If requested, immediately initialize the standard classes on the global.
+    // We need to do this before creating a scope, because
+    // XPCWrappedNativeScope::SetGlobal resolves |Object| via
+    // JS_ResolveStandardClass. JS_InitStandardClasses asserts if any of the
+    // standard classes are already initialized, so this is a problem.
+    if (initStandardClasses && ! JS_InitStandardClasses(ccx, global))
+        return NS_ERROR_FAILURE;
+
+    // Create a scope, but don't do any extra stuff like initializing |Components|.
+    // All of that stuff happens in the caller.
+    XPCWrappedNativeScope *scope = XPCWrappedNativeScope::GetNewOrUsed(ccx, global, identity);
+    MOZ_ASSERT(scope);
+
+    // Make a proto.
+    XPCWrappedNativeProto *proto =
+        XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, nativeHelper.GetClassInfo(), &sciProto,
+                                            UNKNOWN_OFFSETS, /* callPostCreatePrototype = */ false);
+    if (!proto)
+        return NS_ERROR_FAILURE;
+    proto->CacheOffsets(identity);
+
+    // Set up the prototype on the global.
+    MOZ_ASSERT(proto->GetJSProtoObject());
+    success = JS_SplicePrototype(ccx, global, proto->GetJSProtoObject());
+    if (!success)
+        return NS_ERROR_FAILURE;
+
+    // Construct the wrapper.
+    nsRefPtr<XPCWrappedNative> wrapper = new XPCWrappedNative(identity, proto);
+
+    // The wrapper takes over the strong reference to the native object.
+    nativeHelper.forgetCanonical();
+
+    //
+    // We don't call ::Init() on this wrapper, because our setup requirements
+    // are different for globals. We do our setup inline here, instead.
+    //
+
+    // Share mScriptableInfo with the proto.
+    //
+    // This is probably more trouble than it's worth, since we've already created
+    // an XPCNativeScriptableInfo for ourselves. Moreover, most of that class is
+    // shared internally via XPCNativeScriptableInfoShared, so the memory
+    // savings are negligible. Nevertheless, this is what ::Init() does, and we
+    // want to be as consistent as possible with that code.
+    XPCNativeScriptableInfo* siProto = proto->GetScriptableInfo();
+    if (siProto && siProto->GetCallback() == sciWrapper.GetCallback()) {
+        wrapper->mScriptableInfo = siProto;
+        delete si;
+    } else {
+        wrapper->mScriptableInfo = si;
+    }
+
+    // Set the JS object to the global we already created.
+    wrapper->mFlatJSObject = global;
+
+    // Set the private to the XPCWrappedNative.
+    JS_SetPrivate(global, wrapper);
+
+    // There are dire comments elsewhere in the code about how a GC can
+    // happen somewhere after wrapper initialization but before the wrapper is
+    // added to the hashtable in FinishCreate(). It's not clear if that can
+    // happen here, but let's just be safe for now.
+    AutoMarkingWrappedNativePtr wrapperMarker(ccx, wrapper);
+
+    // Call the common Init finish routine. This mainly just does an AddRef
+    // on behalf of XPConnect (the corresponding Release is in the finalizer
+    // hook), but it does some other miscellaneous things too, so we don't
+    // inline it.
+    success = wrapper->FinishInit(ccx);
+    MOZ_ASSERT(success);
+
+    // Go through some extra work to find the tearoff. This is kind of silly
+    // on a conceptual level: the point of tearoffs is to cache the results
+    // of QI-ing mIdentity to different interfaces, and we don't need that
+    // since we're dealing with nsISupports. But lots of code expects tearoffs
+    // to exist for everything, so we just follow along.
+    XPCNativeInterface* iface = XPCNativeInterface::GetNewOrUsed(ccx, &NS_GET_IID(nsISupports));
+    MOZ_ASSERT(iface);
+    nsresult status;
+    success = wrapper->FindTearOff(ccx, iface, false, &status);
+    if (!success)
+        return status;
+
+    // Call the common creation finish routine. This does all of the bookkeeping
+    // like inserting the wrapper into the wrapper map and setting up the wrapper
+    // cache.
+    return FinishCreate(ccx, scope, iface, nativeHelper.GetWrapperCache(),
+                        wrapper, wrappedGlobal);
+}
+
+// static
 nsresult
 XPCWrappedNative::GetNewOrUsed(XPCCallContext& ccx,
                                xpcObjectHelper& helper,
                                XPCWrappedNativeScope* Scope,
                                XPCNativeInterface* Interface,
-                               JSBool isGlobal,
                                XPCWrappedNative** resultWrapper)
 {
     nsWrapperCache *cache = helper.GetWrapperCache();
 
     NS_ASSERTION(!cache || !cache->GetWrapperPreserveColor(),
                  "We assume the caller already checked if it could get the "
                  "wrapper from the cache.");
 
@@ -349,40 +494,33 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 
     if (!identity) {
         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
         return NS_ERROR_FAILURE;
     }
 
     XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
 
-    // We use an AutoMarkingPtr here because it is possible for JS gc to happen
-    // after we have Init'd the wrapper but *before* we add it to the hashtable.
-    // This would cause the mSet to get collected and we'd later crash. I've
-    // *seen* this happen.
-    AutoMarkingWrappedNativePtr wrapper(ccx);
+    nsRefPtr<XPCWrappedNative> wrapper;
 
     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
     if (!cache) {
         {   // scoped lock
             XPCAutoLock lock(mapLock);
             wrapper = map->Find(identity);
-            if (wrapper)
-                wrapper->AddRef();
         }
 
         if (wrapper) {
             if (Interface &&
                 !wrapper->FindTearOff(ccx, Interface, false, &rv)) {
-                NS_RELEASE(wrapper);
                 NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
                 return rv;
             }
             DEBUG_CheckWrapperThreadSafety(wrapper);
-            *resultWrapper = wrapper;
+            *resultWrapper = wrapper.forget().get();
             return NS_OK;
         }
     }
 #ifdef DEBUG
     else if (!cache->GetWrapperPreserveColor())
     {   // scoped lock
         XPCAutoLock lock(mapLock);
         NS_ASSERTION(!map->Find(identity),
@@ -447,59 +585,49 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 
         if (!ac.enter(ccx, parent))
             return NS_ERROR_FAILURE;
 
         if (parent != plannedParent) {
             XPCWrappedNativeScope* betterScope =
                 XPCWrappedNativeScope::FindInJSObjectScope(ccx, parent);
             if (betterScope != Scope)
-                return GetNewOrUsed(ccx, helper, betterScope, Interface,
-                                    isGlobal, resultWrapper);
+                return GetNewOrUsed(ccx, helper, betterScope, Interface, resultWrapper);
 
             newParentVal = OBJECT_TO_JSVAL(parent);
         }
 
         // Take the performance hit of checking the hashtable again in case
         // the preCreate call caused the wrapper to get created through some
         // interesting path (the DOM code tends to make this happen sometimes).
 
         if (cache) {
             JSObject *cached = cache->GetWrapper();
             if (cached) {
                 if (IS_SLIM_WRAPPER_OBJECT(cached)) {
-                    nsRefPtr<XPCWrappedNative> morphed;
                     if (!XPCWrappedNative::Morph(ccx, cached, Interface, cache,
-                                                 getter_AddRefs(morphed)))
+                                                 getter_AddRefs(wrapper)))
                         return NS_ERROR_FAILURE;
-
-                    wrapper = morphed.forget().get();
                 } else {
-                    wrapper =
-                        static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(cached));
-                    if (wrapper)
-                        wrapper->AddRef();
+                    wrapper = static_cast<XPCWrappedNative*>(xpc_GetJSPrivate(cached));
                 }
             }
-        } else
-        {   // scoped lock
+        } else {
+            // scoped lock
             XPCAutoLock lock(mapLock);
             wrapper = map->Find(identity);
-            if (wrapper)
-                wrapper->AddRef();
         }
 
         if (wrapper) {
             if (Interface && !wrapper->FindTearOff(ccx, Interface, false, &rv)) {
-                NS_RELEASE(wrapper);
                 NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
                 return rv;
             }
             DEBUG_CheckWrapperThreadSafety(wrapper);
-            *resultWrapper = wrapper;
+            *resultWrapper = wrapper.forget().get();
             return NS_OK;
         }
     } else {
         if (!ac.enter(ccx, parent))
             return NS_ERROR_FAILURE;
 
         nsISupports *Object = helper.Object();
         if (nsXPCWrappedJSClass::IsWrappedJS(Object)) {
@@ -517,17 +645,17 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 
     // If there is ClassInfo (and we are not building a wrapper for the
     // nsIClassInfo interface) then we use a wrapper that needs a prototype.
 
     // Note that the security check happens inside FindTearOff - after the
     // wrapper is actually created, but before JS code can see it.
 
     if (info && !isClassInfo) {
-        proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, Scope, info, &sciProto, isGlobal);
+        proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, Scope, info, &sciProto);
         if (!proto)
             return NS_ERROR_FAILURE;
 
         proto->CacheOffsets(identity);
 
         wrapper = new XPCWrappedNative(identity, proto);
         if (!wrapper)
             return NS_ERROR_FAILURE;
@@ -548,29 +676,29 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
 
         DEBUG_ReportShadowedMembers(set, wrapper, nsnull);
     }
 
     // The strong reference was taken over by the wrapper, so make the nsCOMPtr
     // forget about it.
     helper.forgetCanonical();
 
-    NS_ADDREF(wrapper);
-
     NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(parent),
                  "Xray wrapper being used to parent XPCWrappedNative?");
 
-    if (!wrapper->Init(ccx, parent, isGlobal, &sciWrapper)) {
-        NS_RELEASE(wrapper);
+    // We use an AutoMarkingPtr here because it is possible for JS gc to happen
+    // after we have Init'd the wrapper but *before* we add it to the hashtable.
+    // This would cause the mSet to get collected and we'd later crash. I've
+    // *seen* this happen.
+    AutoMarkingWrappedNativePtr wrapperMarker(ccx, wrapper);
+
+    if (!wrapper->Init(ccx, parent, &sciWrapper))
         return NS_ERROR_FAILURE;
-    }
 
     if (Interface && !wrapper->FindTearOff(ccx, Interface, false, &rv)) {
-        // Second reference will be released by the FlatJSObject's finalizer.
-        wrapper->Release();
         NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
         return rv;
     }
 
     if (needsSOW)
         wrapper->SetNeedsSOW();
     if (needsCOW)
         wrapper->SetNeedsCOW();
@@ -578,57 +706,49 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
     return FinishCreate(ccx, Scope, Interface, cache, wrapper, resultWrapper);
 }
 
 static nsresult
 FinishCreate(XPCCallContext& ccx,
              XPCWrappedNativeScope* Scope,
              XPCNativeInterface* Interface,
              nsWrapperCache *cache,
-             XPCWrappedNative* wrapper,
+             XPCWrappedNative* inWrapper,
              XPCWrappedNative** resultWrapper)
 {
+    MOZ_ASSERT(inWrapper);
+
 #if DEBUG_xpc_leaks
     {
         char* s = wrapper->ToString(ccx);
         NS_ASSERTION(wrapper->IsValid(), "eh?");
         printf("Created wrapped native %s, flat JSObject is %p\n",
                s, (void*)wrapper->GetFlatJSObjectNoMark());
         if (s)
             JS_smprintf_free(s);
     }
 #endif
 
     XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
 
-    // Redundant wrapper must be killed outside of the map lock.
-    XPCWrappedNative* wrapperToKill = nsnull;
-
+    nsRefPtr<XPCWrappedNative> wrapper;
     {   // scoped lock
-        XPCAutoLock lock(mapLock);
 
         // Deal with the case where the wrapper got created as a side effect
-        // of one of our calls out of this code (or on another thread).
-        XPCWrappedNative* wrapper2 = map->Add(wrapper);
-        if (!wrapper2) {
-            NS_ERROR("failed to add our wrapper!");
-            wrapperToKill = wrapper;
-            wrapper = nsnull;
-        } else if (wrapper2 != wrapper) {
-            NS_ADDREF(wrapper2);
-            wrapperToKill = wrapper;
-            wrapper = wrapper2;
-        }
+        // of one of our calls out of this code (or on another thread). Add()
+        // returns the (possibly pre-existing) wrapper that ultimately ends up
+        // in the map, which is what we want.
+        XPCAutoLock lock(mapLock);
+        wrapper = map->Add(inWrapper);
+        if (!wrapper)
+            return NS_ERROR_FAILURE;
     }
 
-    if (wrapperToKill) {
-        // Second reference will be released by the FlatJSObject's finializer.
-        wrapperToKill->Release();
-    } else if (wrapper) {
+    if (wrapper == inWrapper) {
         JSObject *flat = wrapper->GetFlatJSObject();
         NS_ASSERTION(!cache || !cache->GetWrapperPreserveColor() ||
                      flat == cache->GetWrapperPreserveColor(),
                      "This object has a cached wrapper that's different from "
                      "the JSObject held by its native wrapper?");
 
         if (cache && !cache->GetWrapperPreserveColor())
             cache->SetWrapper(flat);
@@ -663,21 +783,18 @@ FinishCreate(XPCCallContext& ccx,
                 if (cache)
                     cache->ClearWrapper();
                 wrapper->Release();
                 return rv;
             }
         }
     }
 
-    if (!wrapper)
-        return NS_ERROR_FAILURE;
-
     DEBUG_CheckClassInfoClaims(wrapper);
-    *resultWrapper = wrapper;
+    *resultWrapper = wrapper.forget().get();
     return NS_OK;
 }
 
 // static
 nsresult
 XPCWrappedNative::Morph(XPCCallContext& ccx,
                         JSObject* existingJSObject,
                         XPCNativeInterface* Interface,
@@ -686,22 +803,16 @@ XPCWrappedNative::Morph(XPCCallContext& 
 {
     NS_ASSERTION(IS_SLIM_WRAPPER(existingJSObject),
                  "Trying to morph a JSObject that's not a slim wrapper?");
 
     nsISupports *identity =
         static_cast<nsISupports*>(xpc_GetJSPrivate(existingJSObject));
     XPCWrappedNativeProto *proto = GetSlimWrapperProto(existingJSObject);
 
-    // We use an AutoMarkingPtr here because it is possible for JS gc to happen
-    // after we have Init'd the wrapper but *before* we add it to the hashtable.
-    // This would cause the mSet to get collected and we'd later crash. I've
-    // *seen* this happen.
-    AutoMarkingWrappedNativePtr wrapper(ccx);
-
 #if DEBUG
     // FIXME Can't assert this until
     //       https://bugzilla.mozilla.org/show_bug.cgi?id=343141 is fixed.
 #if 0
     if (proto->GetScriptableInfo()->GetFlags().WantPreCreate()) {
         JSObject* parent = JS_GetParent(existingJSObject);
         JSObject* plannedParent = parent;
         nsresult rv =
@@ -712,41 +823,40 @@ XPCWrappedNative::Morph(XPCCallContext& 
             return rv;
 
         NS_ASSERTION(parent == plannedParent,
                      "PreCreate returned a different parent");
     }
 #endif
 #endif
 
-    wrapper = new XPCWrappedNative(dont_AddRef(identity), proto);
+    nsRefPtr<XPCWrappedNative> wrapper = new XPCWrappedNative(dont_AddRef(identity), proto);
     if (!wrapper)
         return NS_ERROR_FAILURE;
 
-    NS_ADDREF(wrapper);
-
     NS_ASSERTION(!xpc::WrapperFactory::IsXrayWrapper(js::GetObjectParent(existingJSObject)),
                  "Xray wrapper being used to parent XPCWrappedNative?");
 
+    // We use an AutoMarkingPtr here because it is possible for JS gc to happen
+    // after we have Init'd the wrapper but *before* we add it to the hashtable.
+    // This would cause the mSet to get collected and we'd later crash. I've
+    // *seen* this happen.
+    AutoMarkingWrappedNativePtr wrapperMarker(ccx, wrapper);
+
     JSAutoEnterCompartment ac;
-    if (!ac.enter(ccx, existingJSObject) || !wrapper->Init(ccx, existingJSObject)) {
-        NS_RELEASE(wrapper);
+    if (!ac.enter(ccx, existingJSObject) || !wrapper->Init(ccx, existingJSObject))
         return NS_ERROR_FAILURE;
-    }
 
     nsresult rv;
     if (Interface && !wrapper->FindTearOff(ccx, Interface, false, &rv)) {
-        // Second reference will be released by the FlatJSObject's finalizer.
-        wrapper->Release();
         NS_ASSERTION(NS_FAILED(rv), "returning NS_OK on failure");
         return rv;
     }
 
-    return FinishCreate(ccx, wrapper->GetScope(), Interface, cache, wrapper,
-                        resultWrapper);
+    return FinishCreate(ccx, wrapper->GetScope(), Interface, cache, wrapper, resultWrapper);
 }
 
 // static
 nsresult
 XPCWrappedNative::GetUsedOnly(XPCCallContext& ccx,
                               nsISupports* Object,
                               XPCWrappedNativeScope* Scope,
                               XPCNativeInterface* Interface,
@@ -1057,51 +1167,43 @@ XPCWrappedNative::GatherScriptableCreate
     return sciProto;
 }
 
 #ifdef DEBUG_slimwrappers
 static PRUint32 sMorphedSlimWrappers;
 #endif
 
 JSBool
-XPCWrappedNative::Init(XPCCallContext& ccx,
-                       JSObject* parent, JSBool isGlobal,
+XPCWrappedNative::Init(XPCCallContext& ccx, JSObject* parent,
                        const XPCNativeScriptableCreateInfo* sci)
 {
     // setup our scriptable info...
 
     if (sci->GetCallback()) {
         if (HasProto()) {
             XPCNativeScriptableInfo* siProto = GetProto()->GetScriptableInfo();
             if (siProto && siProto->GetCallback() == sci->GetCallback())
                 mScriptableInfo = siProto;
         }
         if (!mScriptableInfo) {
             mScriptableInfo =
-                XPCNativeScriptableInfo::Construct(ccx, isGlobal, sci);
+                XPCNativeScriptableInfo::Construct(ccx, sci);
 
             if (!mScriptableInfo)
                 return false;
         }
     }
     XPCNativeScriptableInfo* si = mScriptableInfo;
 
     // create our flatJSObject
 
     JSClass* jsclazz = si ? si->GetJSClass() : Jsvalify(&XPC_WN_NoHelper_JSClass.base);
 
-    if (isGlobal) {
-        // Resolving a global object's class can cause us to create a global's
-        // JS class without the proper global flags. Notice that here and fix
-        // the problem.
-        if (!(jsclazz->flags & JSCLASS_IS_GLOBAL))
-            jsclazz->flags |= XPCONNECT_GLOBAL_FLAGS;
-    } else
-        NS_ASSERTION(!(jsclazz->flags & JSCLASS_IS_GLOBAL),
-                     "Non-global object has the wrong flags");
+    // We should have the global jsclass flag if and only if we're a global.
+    MOZ_ASSERT_IF(si, !!si->GetFlags().IsGlobalObject() == !!(jsclazz->flags & JSCLASS_IS_GLOBAL));
 
     NS_ASSERTION(jsclazz &&
                  jsclazz->name &&
                  jsclazz->flags &&
                  jsclazz->addProperty &&
                  jsclazz->delProperty &&
                  jsclazz->getProperty &&
                  jsclazz->setProperty &&
@@ -1453,19 +1555,17 @@ XPCWrappedNative::ReparentWrapperIfFound
             oldProto = wrapper->GetProto();
 
         if (oldProto) {
             XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
             XPCNativeScriptableCreateInfo ci(*info);
             newProto =
                 XPCWrappedNativeProto::GetNewOrUsed(ccx, aNewScope,
                                                     oldProto->GetClassInfo(),
-                                                    &ci,
-                                                    (info->GetJSClass()->flags & JSCLASS_IS_GLOBAL),
-                                                    oldProto->GetOffsetsMasked());
+                                                    &ci, oldProto->GetOffsetsMasked());
             if (!newProto) {
                 return NS_ERROR_FAILURE;
             }
         }
 
         if (wrapper) {
             Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
             Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
@@ -2969,60 +3069,33 @@ NS_IMETHODIMP XPCWrappedNative::FindInte
 }
 
 inline nsresult UnexpectedFailure(nsresult rv)
 {
     NS_ERROR("This is not supposed to fail!");
     return rv;
 }
 
-/* void refreshPrototype (); */
-NS_IMETHODIMP XPCWrappedNative::RefreshPrototype()
+/* void finishInitForWrappedGlobal (); */
+NS_IMETHODIMP XPCWrappedNative::FinishInitForWrappedGlobal()
 {
+    // We can only be called under certain conditions.
+    MOZ_ASSERT(mScriptableInfo);
+    MOZ_ASSERT(mScriptableInfo->GetFlags().IsGlobalObject());
+    MOZ_ASSERT(HasProto());
+
+    // Build a CCX.
     XPCCallContext ccx(NATIVE_CALLER);
     if (!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
-    if (!HasProto())
-        return NS_OK;
-
-    if (!mFlatJSObject)
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    JSAutoEnterCompartment ac;
-    if (!ac.enter(ccx, GetFlatJSObject()))
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    AutoMarkingWrappedNativeProtoPtr oldProto(ccx);
-    AutoMarkingWrappedNativeProtoPtr newProto(ccx);
-
-    oldProto = GetProto();
-
-    XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
-    XPCNativeScriptableCreateInfo ci(*info);
-    newProto = XPCWrappedNativeProto::GetNewOrUsed(ccx, oldProto->GetScope(),
-                                                   oldProto->GetClassInfo(),
-                                                   &ci,
-                                                   (info->GetJSClass()->flags & JSCLASS_IS_GLOBAL),
-                                                   oldProto->GetOffsetsMasked());
-    if (!newProto)
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    // If nothing needs to change then we're done.
-
-    if (newProto.get() == oldProto.get())
-        return NS_OK;
-
-    if (!JS_SplicePrototype(ccx, GetFlatJSObject(), newProto->GetJSProtoObject()))
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    SetProto(newProto);
-
-    if (mScriptableInfo == oldProto->GetScriptableInfo())
-        UpdateScriptableInfo(newProto->GetScriptableInfo());
+    // Call PostCreateProrotype.
+    bool success = GetProto()->CallPostCreatePrototype(ccx);
+    if (!success)
+        return NS_ERROR_FAILURE;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP XPCWrappedNative::GetSecurityInfoAddress(void*** securityInfoAddrPtr)
 {
     NS_ENSURE_ARG_POINTER(securityInfoAddrPtr);
     *securityInfoAddrPtr = GetSecurityInfoAddr();
@@ -3681,20 +3754,18 @@ ConstructSlimWrapper(XPCCallContext &ccx
         return true;
     }
 
     PRUint32 interfacesBitmap = classInfoHelper->GetInterfacesBitmap();
     XPCNativeScriptableCreateInfo
         sciProto(aHelper.forgetXPCClassInfo(), flags, interfacesBitmap);
 
     AutoMarkingWrappedNativeProtoPtr xpcproto(ccx);
-    JSBool isGlobal = false;
     xpcproto = XPCWrappedNativeProto::GetNewOrUsed(ccx, xpcScope,
-                                                   classInfoHelper, &sciProto,
-                                                   isGlobal);
+                                                   classInfoHelper, &sciProto);
     if (!xpcproto)
         return false;
 
     xpcproto->CacheOffsets(identityObj);
 
     XPCNativeScriptableInfo* si = xpcproto->GetScriptableInfo();
     JSClass* jsclazz = si->GetSlimJSClass();
     if (!jsclazz)
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -1344,17 +1344,16 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JS
     return JS_ObjectToOuterObject(cx, obj);
 }
 
 /***************************************************************************/
 
 // static
 XPCNativeScriptableInfo*
 XPCNativeScriptableInfo::Construct(XPCCallContext& ccx,
-                                   JSBool isGlobal,
                                    const XPCNativeScriptableCreateInfo* sci)
 {
     NS_ASSERTION(sci, "bad param");
     NS_ASSERTION(sci->GetCallback(), "bad param");
 
     XPCNativeScriptableInfo* newObj =
         new XPCNativeScriptableInfo(sci->GetCallback());
     if (!newObj)
@@ -1367,38 +1366,38 @@ XPCNativeScriptableInfo::Construct(XPCCa
     }
 
     JSBool success;
 
     XPCJSRuntime* rt = ccx.GetRuntime();
     XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap();
     {   // scoped lock
         XPCAutoLock lock(rt->GetMapLock());
-        success = map->GetNewOrUsed(sci->GetFlags(), name, isGlobal,
+        success = map->GetNewOrUsed(sci->GetFlags(), name,
                                     sci->GetInterfacesBitmap(), newObj);
     }
 
     if (!success) {
         delete newObj;
         return nsnull;
     }
 
     return newObj;
 }
 
 void
-XPCNativeScriptableShared::PopulateJSClass(JSBool isGlobal)
+XPCNativeScriptableShared::PopulateJSClass()
 {
     NS_ASSERTION(mJSClass.base.name, "bad state!");
 
     mJSClass.base.flags = WRAPPER_SLOTS |
                           JSCLASS_PRIVATE_IS_NSISUPPORTS |
                           JSCLASS_NEW_RESOLVE;
 
-    if (isGlobal)
+    if (mFlags.IsGlobalObject())
         mJSClass.base.flags |= XPCONNECT_GLOBAL_FLAGS;
 
     JSPropertyOp addProperty;
     if (mFlags.WantAddProperty())
         addProperty = XPC_WN_Helper_AddProperty;
     else if (mFlags.UseJSStubForAddProperty())
         addProperty = JS_PropertyStub;
     else if (mFlags.AllowPropModsDuringResolve())
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -84,25 +84,25 @@ XPCWrappedNativeProto::~XPCWrappedNative
 
     XPCNativeSet::ClearCacheEntryForClassInfo(mClassInfo);
 
     delete mScriptableInfo;
 }
 
 JSBool
 XPCWrappedNativeProto::Init(XPCCallContext& ccx,
-                            JSBool isGlobal,
-                            const XPCNativeScriptableCreateInfo* scriptableCreateInfo)
+                            const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
+                            bool callPostCreatePrototype)
 {
     nsIXPCScriptable *callback = scriptableCreateInfo ?
                                  scriptableCreateInfo->GetCallback() :
                                  nsnull;
     if (callback) {
         mScriptableInfo =
-            XPCNativeScriptableInfo::Construct(ccx, isGlobal, scriptableCreateInfo);
+            XPCNativeScriptableInfo::Construct(ccx, scriptableCreateInfo);
         if (!mScriptableInfo)
             return false;
     }
 
     js::Class* jsclazz;
 
 
     if (mScriptableInfo) {
@@ -123,34 +123,48 @@ XPCWrappedNativeProto::Init(XPCCallConte
 
     JSObject *parent = mScope->GetGlobalJSObject();
 
     mJSProtoObject =
         xpc_NewSystemInheritingJSObject(ccx, js::Jsvalify(jsclazz),
                                         mScope->GetPrototypeJSObject(),
                                         true, parent);
 
-    JSBool ok = !!mJSProtoObject;
-
-    if (ok) {
+    bool success = !!mJSProtoObject;
+    if (success) {
         JS_SetPrivate(mJSProtoObject, this);
-        if (callback) {
-            nsresult rv = callback->PostCreatePrototype(ccx, mJSProtoObject);
-            if (NS_FAILED(rv)) {
-                JS_SetPrivate(mJSProtoObject, nsnull);
-                mJSProtoObject = nsnull;
-                XPCThrower::Throw(rv, ccx);
-                return false;
-            }
-        }
+        if (callPostCreatePrototype)
+            success = CallPostCreatePrototype(ccx);
     }
 
     DEBUG_ReportShadowedMembers(mSet, nsnull, this);
 
-    return ok;
+    return success;
+}
+
+bool
+XPCWrappedNativeProto::CallPostCreatePrototype(XPCCallContext& ccx)
+{
+    // Nothing to do if we don't have a scriptable callback.
+    nsIXPCScriptable *callback = mScriptableInfo ? mScriptableInfo->GetCallback()
+                                                 : nsnull;
+    if (!callback)
+        return true;
+
+    // Call the helper. This can handle being called if it's not implemented,
+    // so we don't have to check any sort of "want" here. See xpc_map_end.h.
+    nsresult rv = callback->PostCreatePrototype(ccx, mJSProtoObject);
+    if (NS_FAILED(rv)) {
+        JS_SetPrivate(mJSProtoObject, nsnull);
+        mJSProtoObject = nsnull;
+        XPCThrower::Throw(rv, ccx);
+        return false;
+    }
+
+    return true;
 }
 
 void
 XPCWrappedNativeProto::JSProtoObjectFinalized(JSContext *cx, JSObject *obj)
 {
     NS_ASSERTION(obj == mJSProtoObject, "huh?");
 
     // Map locking is not necessary since we are running gc.
@@ -190,18 +204,18 @@ XPCWrappedNativeProto::SystemIsBeingShut
 }
 
 // static
 XPCWrappedNativeProto*
 XPCWrappedNativeProto::GetNewOrUsed(XPCCallContext& ccx,
                                     XPCWrappedNativeScope* scope,
                                     nsIClassInfo* classInfo,
                                     const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
-                                    JSBool isGlobal,
-                                    QITableEntry* offsets)
+                                    QITableEntry* offsets,
+                                    bool callPostCreatePrototype)
 {
     NS_ASSERTION(scope, "bad param");
     NS_ASSERTION(classInfo, "bad param");
 
     AutoMarkingWrappedNativeProtoPtr proto(ccx);
     ClassInfo2WrappedNativeProtoMap* map = nsnull;
     XPCLock* lock = nsnull;
 
@@ -221,17 +235,17 @@ XPCWrappedNativeProto::GetNewOrUsed(XPCC
 
     AutoMarkingNativeSetPtr set(ccx);
     set = XPCNativeSet::GetNewOrUsed(ccx, classInfo);
     if (!set)
         return nsnull;
 
     proto = new XPCWrappedNativeProto(scope, classInfo, ciFlags, set, offsets);
 
-    if (!proto || !proto->Init(ccx, isGlobal, scriptableCreateInfo)) {
+    if (!proto || !proto->Init(ccx, scriptableCreateInfo, callPostCreatePrototype)) {
         delete proto.get();
         return nsnull;
     }
 
     {   // scoped lock
         XPCAutoLock al(lock);
         map->Add(classInfo, proto);
     }
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -107,39 +107,40 @@ static void DEBUG_TrackScopeShutdown()
 
 /***************************************************************************/
 
 XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nsnull;
 XPCWrappedNativeScope* XPCWrappedNativeScope::gDyingScopes = nsnull;
 
 // static
 XPCWrappedNativeScope*
-XPCWrappedNativeScope::GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal)
+XPCWrappedNativeScope::GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative)
 {
 
     XPCWrappedNativeScope* scope = FindInJSObjectScope(ccx, aGlobal, true);
     if (!scope)
-        scope = new XPCWrappedNativeScope(ccx, aGlobal);
+        scope = new XPCWrappedNativeScope(ccx, aGlobal, aNative);
     else {
         // We need to call SetGlobal in order to refresh our cached
         // mPrototypeJSObject and to clear mPrototypeNoHelper (so we get a new
         // new one if requested in the new scope) in the case where the global
         // object is being reused (JS_ClearScope has been called).  NOTE: We are
         // only called by nsXPConnect::InitClasses.
-        scope->SetGlobal(ccx, aGlobal);
+        scope->SetGlobal(ccx, aGlobal, aNative);
     }
     if (js::GetObjectClass(aGlobal)->flags & JSCLASS_XPCONNECT_GLOBAL)
         JS_SetReservedSlot(aGlobal,
                            JSCLASS_GLOBAL_SLOT_COUNT,
                            PRIVATE_TO_JSVAL(scope));
     return scope;
 }
 
 XPCWrappedNativeScope::XPCWrappedNativeScope(XPCCallContext& ccx,
-                                             JSObject* aGlobal)
+                                             JSObject* aGlobal,
+                                             nsISupports* aNative)
     :   mRuntime(ccx.GetRuntime()),
         mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
         mMainThreadWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
         mComponents(nsnull),
         mNext(nsnull),
         mGlobalJSObject(nsnull),
         mPrototypeJSObject(nsnull),
@@ -160,17 +161,17 @@ XPCWrappedNativeScope::XPCWrappedNativeS
         gScopes = this;
 
         // Grab the XPCContext associated with our context.
         mContext = XPCContext::GetXPCContext(ccx.GetJSContext());
         mContext->AddScope(this);
     }
 
     if (aGlobal)
-        SetGlobal(ccx, aGlobal);
+        SetGlobal(ccx, aGlobal, aNative);
 
     DEBUG_TrackNewScope(this);
     MOZ_COUNT_CTOR(XPCWrappedNativeScope);
 }
 
 // static
 JSBool
 XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope *scope)
@@ -221,52 +222,60 @@ js::Class XPC_WN_NoHelper_Proto_JSClass 
     nsnull,                         // trace;
 
     JS_NULL_CLASS_EXT,
     XPC_WN_NoCall_ObjectOps
 };
 
 
 void
-XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal)
+XPCWrappedNativeScope::SetGlobal(XPCCallContext& ccx, JSObject* aGlobal,
+                                 nsISupports* aNative)
 {
     // We allow for calling this more than once. This feature is used by
     // nsXPConnect::InitClassesWithNewWrappedGlobal.
 
     mGlobalJSObject = aGlobal;
     mScriptObjectPrincipal = nsnull;
-    // Now init our script object principal, if the new global has one
 
-    const JSClass* jsClass = js::GetObjectJSClass(aGlobal);
-    if (!(~jsClass->flags & (JSCLASS_HAS_PRIVATE |
-                             JSCLASS_PRIVATE_IS_NSISUPPORTS))) {
-        // Our global has an nsISupports native pointer.  Let's
-        // see whether it's what we want.
-        nsISupports* priv = (nsISupports*)xpc_GetJSPrivate(aGlobal);
-        nsCOMPtr<nsIXPConnectWrappedNative> native =
-            do_QueryInterface(priv);
-        nsCOMPtr<nsIScriptObjectPrincipal> sop;
-        if (native) {
-            sop = do_QueryWrappedNative(native);
-        }
-        if (!sop) {
-            sop = do_QueryInterface(priv);
-        }
-        mScriptObjectPrincipal = sop;
+    // Try to find the native global object. If we didn't receive it explicitly,
+    // we might be able to find it in the private slot.
+    nsISupports* native = aNative;
+    if (!native &&
+        !(~js::GetObjectJSClass(aGlobal)->flags & (JSCLASS_HAS_PRIVATE |
+                                                   JSCLASS_PRIVATE_IS_NSISUPPORTS)))
+    {
+        // Get the private. It might be a WN, in which case we dig deeper.
+        native = (nsISupports*)xpc_GetJSPrivate(aGlobal);
+        nsCOMPtr<nsIXPConnectWrappedNative> wn = do_QueryInterface(native);
+        if (wn)
+            native = static_cast<XPCWrappedNative*>(native)->GetIdentityObject();
     }
 
+    // Now init our script object principal, if the new global has one.
+    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(native);
+    mScriptObjectPrincipal = sop;
+
     // Lookup 'globalObject.Object.prototype' for our wrapper's proto
     {
         AutoJSErrorAndExceptionEater eater(ccx); // scoped error eater
 
         jsval val;
         jsid idObj = mRuntime->GetStringID(XPCJSRuntime::IDX_OBJECT);
         jsid idProto = mRuntime->GetStringID(XPCJSRuntime::IDX_PROTOTYPE);
 
-        if (JS_GetPropertyById(ccx, aGlobal, idObj, &val) &&
+        // When creating a new scope to boostrap a new global, we don't yet have
+        // an XPCWrappedNative associated with the global object. However, the
+        // resolve hook on the JSClass assumes there is one. So we need to avoid
+        // resolving anything on the global object until things get a bit further
+        // along. As such, we manually resolve |Object| before accessing it below.
+        JSBool didResolve;
+
+        if (JS_ResolveStandardClass(ccx, aGlobal, idObj, &didResolve) &&
+            JS_GetPropertyById(ccx, aGlobal, idObj, &val) &&
             !JSVAL_IS_PRIMITIVE(val) &&
             JS_GetPropertyById(ccx, JSVAL_TO_OBJECT(val), idProto, &val) &&
             !JSVAL_IS_PRIMITIVE(val)) {
             mPrototypeJSObject = JSVAL_TO_OBJECT(val);
         } else {
             NS_ERROR("Can't get globalObject.Object.prototype");
         }
     }
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -101,27 +101,21 @@ Throw(JSContext *cx, nsresult rv)
 
 
 // Only set allowNativeWrapper to false if you really know you need it, if in
 // doubt use true. Setting it to false disables security wrappers.
 static bool
 XPCOMObjectToJsval(JSContext *cx, JSObject *scope, xpcObjectHelper &helper,
                    bool allowNativeWrapper, jsval *rval)
 {
-    // XXX The OBJ_IS_NOT_GLOBAL here is not really right. In
-    // fact, this code is depending on the fact that the
-    // global object will not have been collected, and
-    // therefore this NativeInterface2JSObject will not end up
-    // creating a new XPCNativeScriptableShared.
-
     XPCLazyCallContext lccx(JS_CALLER, cx, scope);
 
     nsresult rv;
     if (!XPCConvert::NativeInterface2JSObject(lccx, rval, NULL, helper, NULL, NULL,
-                                              allowNativeWrapper, OBJ_IS_NOT_GLOBAL, &rv)) {
+                                              allowNativeWrapper, &rv)) {
         // I can't tell if NativeInterface2JSObject throws JS exceptions
         // or not.  This is a sloppy stab at the right semantics; the
         // method really ought to be fixed to behave consistently.
         if (!JS_IsExceptionPending(cx))
             Throw(cx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
         return false;
     }
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1078,18 +1078,16 @@ nsXPConnect::InitClasses(JSContext * aJS
     XPCCallContext ccx(NATIVE_CALLER, aJSContext);
     if (!ccx.IsValid())
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     JSAutoEnterCompartment ac;
     if (!ac.enter(ccx, aGlobalJSObj))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
-    xpc_InitJSxIDClassObjects();
-
     XPCWrappedNativeScope* scope =
         XPCWrappedNativeScope::GetNewOrUsed(ccx, aGlobalJSObj);
 
     if (!scope)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     scope->RemoveWrappedNativeProtos();
 
@@ -1099,30 +1097,16 @@ nsXPConnect::InitClasses(JSContext * aJS
     if (XPCPerThreadData::IsMainThread(ccx)) {
         if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, aGlobalJSObj))
             return UnexpectedFailure(NS_ERROR_FAILURE);
     }
 
     return NS_OK;
 }
 
-static JSBool
-TempGlobalResolve(JSContext *aJSContext, JSObject *obj, jsid id)
-{
-    JSBool resolved;
-    return JS_ResolveStandardClass(aJSContext, obj, id, &resolved);
-}
-
-static JSClass xpcTempGlobalClass = {
-    "xpcTempGlobalClass", JSCLASS_GLOBAL_FLAGS,
-    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_StrictPropertyStub,
-    JS_EnumerateStub, TempGlobalResolve, JS_ConvertStub,   nsnull,
-    JSCLASS_NO_OPTIONAL_MEMBERS
-};
-
 static bool
 CreateNewCompartment(JSContext *cx, JSClass *clasp, nsIPrincipal *principal,
                      xpc::CompartmentPrivate *priv, JSObject **global,
                      JSCompartment **compartment)
 {
     // We take ownership of |priv|. Ensure that either we free it in the case
     // of failure or give ownership to the compartment in case of success (in
     // that case it will be free'd in CompartmentCallback during GC).
@@ -1201,145 +1185,85 @@ xpc_CreateGlobalObject(JSContext *cx, JS
 
         JSObject *tempGlobal = JS_NewGlobalObject(cx, clasp);
         if (!tempGlobal)
             return UnexpectedFailure(NS_ERROR_FAILURE);
         *global = tempGlobal;
     }
 
 #ifdef DEBUG
-    if (clasp->flags & JSCLASS_XPCONNECT_GLOBAL) {
+    // Verify that the right trace hook is called. Note that this doesn't
+    // work right for wrapped globals, since the tracing situation there is
+    // more complicated. Manual inspection shows that they do the right thing.
+    if (clasp->flags & JSCLASS_XPCONNECT_GLOBAL &&
+        !((js::Class*)clasp)->ext.isWrappedNative)
+    {
         VerifyTraceXPCGlobalCalledTracer trc;
         JS_TracerInit(&trc.base, JS_GetRuntime(cx), VerifyTraceXPCGlobalCalled);
         trc.ok = false;
         JS_TraceChildren(&trc.base, *global, JSTRACE_OBJECT);
         NS_ABORT_IF_FALSE(trc.ok, "Trace hook needs to call TraceXPCGlobal if JSCLASS_XPCONNECT_GLOBAL is set.");
     }
 #endif
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
                                              nsISupports *aCOMObj,
-                                             const nsIID & aIID,
                                              nsIPrincipal * aPrincipal,
-                                             nsISupports * aExtraPtr,
                                              PRUint32 aFlags,
                                              nsIXPConnectJSObjectHolder **_retval)
 {
     NS_ASSERTION(aJSContext, "bad param");
     NS_ASSERTION(aCOMObj, "bad param");
     NS_ASSERTION(_retval, "bad param");
-    NS_ASSERTION(aPrincipal, "must be able to find a compartment");
-
-    // XXX This is not pretty. We make a temporary global object and
-    // init it with all the Components object junk just so we have a
-    // parent with an xpc scope to use when wrapping the object that will
-    // become the 'real' global.
+
+    // We pass null for the 'extra' pointer during global object creation, so
+    // we need to have a principal.
+    MOZ_ASSERT(aPrincipal);
 
     XPCCallContext ccx(NATIVE_CALLER, aJSContext);
 
-    JSCompartment* compartment;
-    JSObject* tempGlobal;
-
-    nsresult rv = xpc_CreateGlobalObject(ccx, &xpcTempGlobalClass, aPrincipal,
-                                         aExtraPtr, false, &tempGlobal, &compartment);
+    // Call into XPCWrappedNative to make a new global object, scope, and global
+    // prototype.
+    xpcObjectHelper helper(aCOMObj);
+    MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
+    nsRefPtr<XPCWrappedNative> wrappedGlobal;
+    nsresult rv =
+        XPCWrappedNative::WrapNewGlobal(ccx, helper, aPrincipal,
+                                        aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES,
+                                        getter_AddRefs(wrappedGlobal));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // Grab a copy of the global and enter its compartment.
+    JSObject *global = wrappedGlobal->GetFlatJSObject();
+    MOZ_ASSERT(!js::GetObjectParent(global));
     JSAutoEnterCompartment ac;
-    if (!ac.enter(ccx, tempGlobal))
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-    ccx.SetScopeForNewJSObjects(tempGlobal);
-
+    if (!ac.enter(ccx, global))
+        return NS_ERROR_UNEXPECTED;
+
+    // Apply the system flag, if requested.
     bool system = (aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT) != 0;
-    if (system && !JS_MakeSystemObject(aJSContext, tempGlobal))
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    jsval v;
-    nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
-    {
-        // Scope for our auto-marker; it just needs to keep tempGlobal alive
-        // long enough for InitClasses and WrapNative to do their work
-        AUTO_MARK_JSVAL(ccx, OBJECT_TO_JSVAL(tempGlobal));
-
-        if (NS_FAILED(InitClasses(aJSContext, tempGlobal)))
-            return UnexpectedFailure(NS_ERROR_FAILURE);
-
-        nsresult rv;
-        xpcObjectHelper helper(aCOMObj);
-        if (!XPCConvert::NativeInterface2JSObject(ccx, &v,
-                                                  getter_AddRefs(holder),
-                                                  helper, &aIID, nsnull,
-                                                  false, OBJ_IS_GLOBAL, &rv))
-            return UnexpectedFailure(rv);
-
-        NS_ASSERTION(NS_SUCCEEDED(rv) && holder, "Didn't wrap properly");
-    }
-
-    JSObject* globalJSObj = JSVAL_TO_OBJECT(v);
-    if (!globalJSObj)
+    if (system && !JS_MakeSystemObject(aJSContext, global))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
-    if (aFlags & nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT)
-        NS_ASSERTION(JS_IsSystemObject(aJSContext, globalJSObj), "huh?!");
-
-    // voodoo to fixup scoping and parenting...
-
-    MOZ_ASSERT(!js::GetObjectParent(globalJSObj));
-
-    JSObject* oldGlobal = JS_GetGlobalObject(aJSContext);
-    if (!oldGlobal || oldGlobal == tempGlobal)
-        JS_SetGlobalObject(aJSContext, globalJSObj);
-
-    if ((aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES) &&
-        !JS_InitStandardClasses(aJSContext, globalJSObj))
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    XPCWrappedNative* wrapper =
-        reinterpret_cast<XPCWrappedNative*>(holder.get());
-    XPCWrappedNativeScope* scope = wrapper->GetScope();
-
-    if (!scope)
-        return UnexpectedFailure(NS_ERROR_FAILURE);
-
-    // Note: This call cooperates with a call to wrapper->RefreshPrototype()
-    // in nsJSEnvironment::SetOuterObject in order to ensure that the
-    // prototype defines its constructor on the right global object.
-    if (wrapper->GetProto()->GetScriptableInfo())
-        scope->RemoveWrappedNativeProtos();
-
-    NS_ASSERTION(scope->GetGlobalJSObject() == tempGlobal, "stealing scope!");
-
-    scope->SetGlobal(ccx, globalJSObj);
-
-    JSObject* protoJSObject = wrapper->HasProto() ?
-                                    wrapper->GetProto()->GetJSProtoObject() :
-                                    globalJSObj;
-    if (protoJSObject) {
-        if (protoJSObject != globalJSObj)
-            JS_SetParent(aJSContext, protoJSObject, globalJSObj);
-        if (!JS_SplicePrototype(aJSContext, protoJSObject, scope->GetPrototypeJSObject()))
-            return UnexpectedFailure(NS_ERROR_FAILURE);
-    }
-
     if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) {
         // XPCCallContext gives us an active request needed to save/restore.
-        if (!nsXPCComponents::AttachNewComponentsObject(ccx, scope, globalJSObj))
+        if (!nsXPCComponents::AttachNewComponentsObject(ccx, wrappedGlobal->GetScope(), global))
             return UnexpectedFailure(NS_ERROR_FAILURE);
 
         if (XPCPerThreadData::IsMainThread(ccx)) {
-            if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, globalJSObj))
+            if (!XPCNativeWrapper::AttachNewConstructorObject(ccx, global))
                 return UnexpectedFailure(NS_ERROR_FAILURE);
         }
     }
 
-    NS_ADDREF(*_retval = holder);
-
+    *_retval = wrappedGlobal.forget().get();
     return NS_OK;
 }
 
 nsresult
 xpc_MorphSlimWrapper(JSContext *cx, nsISupports *tomorph)
 {
     nsWrapperCache *cache;
     CallQueryInterface(tomorph, &cache);
@@ -1366,18 +1290,17 @@ NativeInterface2JSObject(XPCLazyCallCont
     if (!ac.enter(lccx.GetJSContext(), aScope))
         return NS_ERROR_OUT_OF_MEMORY;
 
     lccx.SetScopeForNewJSObjects(aScope);
 
     nsresult rv;
     xpcObjectHelper helper(aCOMObj, aCache);
     if (!XPCConvert::NativeInterface2JSObject(lccx, aVal, aHolder, helper, aIID,
-                                              nsnull, aAllowWrapping,
-                                              OBJ_IS_NOT_GLOBAL, &rv))
+                                              nsnull, aAllowWrapping, &rv))
         return rv;
 
 #ifdef DEBUG
     NS_ASSERTION(aAllowWrapping ||
                  !xpc::WrapperFactory::IsXrayWrapper(JSVAL_TO_OBJECT(*aVal)),
                  "Shouldn't be returning a xray wrapper here");
 #endif
 
@@ -2114,18 +2037,17 @@ nsXPConnect::GetWrappedNativePrototype(J
         XPCWrappedNativeScope::FindInJSObjectScope(ccx, aScope);
     if (!scope)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     XPCNativeScriptableCreateInfo sciProto;
     XPCWrappedNative::GatherProtoScriptableCreateInfo(aClassInfo, sciProto);
 
     AutoMarkingWrappedNativeProtoPtr proto(ccx);
-    proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, aClassInfo,
-                                                &sciProto, OBJ_IS_NOT_GLOBAL);
+    proto = XPCWrappedNativeProto::GetNewOrUsed(ccx, scope, aClassInfo, &sciProto);
     if (!proto)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     nsIXPConnectJSObjectHolder* holder;
     *_retval = holder = XPCJSObjectHolder::newHolder(ccx,
                                                      proto->GetJSProtoObject());
     if (!holder)
         return UnexpectedFailure(NS_ERROR_FAILURE);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -43,16 +43,17 @@
 
 /* All the XPConnect private declarations - only include locally. */
 
 #ifndef xpcprivate_h___
 #define xpcprivate_h___
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
+#include "mozilla/Util.h"
 
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
 #include <math.h>
 #include "xpcpublic.h"
 #include "jsapi.h"
 #include "jsdhash.h"
@@ -453,19 +454,16 @@ private:
 // JSTRACE_XML can recursively hold on to more JSTRACE_XML objects, adding it to
 // the cycle collector avoids stack overflow.
 inline bool
 AddToCCKind(JSGCTraceKind kind)
 {
     return kind == JSTRACE_OBJECT || kind == JSTRACE_XML || kind == JSTRACE_SCRIPT;
 }
 
-const bool OBJ_IS_GLOBAL = true;
-const bool OBJ_IS_NOT_GLOBAL = false;
-
 class nsXPConnect : public nsIXPConnect,
                     public nsIThreadObserver,
                     public nsSupportsWeakReference,
                     public nsCycleCollectionJSRuntime,
                     public nsCycleCollectionParticipant,
                     public nsIJSRuntimeService,
                     public nsIThreadJSContextStack,
                     public nsIJSEngineTelemetryStats
@@ -1496,17 +1494,17 @@ enum WrapperType {
 /***************************************************************************/
 // XPCWrappedNativeScope is one-to-one with a JS global object.
 
 class XPCWrappedNativeScope : public PRCList
 {
 public:
 
     static XPCWrappedNativeScope*
-    GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal);
+    GetNewOrUsed(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative = nsnull);
 
     XPCJSRuntime*
     GetRuntime() const {return mRuntime;}
 
     Native2WrappedNativeMap*
     GetWrappedNativeMap() const {return mWrappedNativeMap;}
 
     ClassInfo2WrappedNativeProtoMap*
@@ -1592,17 +1590,17 @@ public:
 
     JSBool
     IsValid() const {return mRuntime != nsnull;}
 
     static JSBool
     IsDyingScope(XPCWrappedNativeScope *scope);
 
     void SetComponents(nsXPCComponents* aComponents);
-    void SetGlobal(XPCCallContext& ccx, JSObject* aGlobal);
+    void SetGlobal(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative);
 
     static void InitStatics() { gScopes = nsnull; gDyingScopes = nsnull; }
 
     XPCContext *GetContext() { return mContext; }
     void SetContext(XPCContext *xpcc) { mContext = nsnull; }
 
     nsDataHashtable<nsDepCharHashKey, JSObject*>& GetCachedDOMPrototypes()
     {
@@ -1621,17 +1619,17 @@ public:
     void TraceDOMPrototypes(JSTracer *trc);
 
     JSBool NewDOMBindingsEnabled()
     {
         return mNewDOMBindingsEnabled;
     }
 
 protected:
-    XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal);
+    XPCWrappedNativeScope(XPCCallContext& ccx, JSObject* aGlobal, nsISupports* aNative);
     virtual ~XPCWrappedNativeScope();
 
     static void KillDyingScopes();
 
     XPCWrappedNativeScope(); // not implemented
 
 private:
     static XPCWrappedNativeScope* gScopes;
@@ -2016,16 +2014,17 @@ public:
     JSBool UseJSStubForDelProperty()      GET_IT(USE_JSSTUB_FOR_DELPROPERTY)
     JSBool UseJSStubForSetProperty()      GET_IT(USE_JSSTUB_FOR_SETPROPERTY)
     JSBool DontEnumStaticProps()          GET_IT(DONT_ENUM_STATIC_PROPS)
     JSBool DontEnumQueryInterface()       GET_IT(DONT_ENUM_QUERY_INTERFACE)
     JSBool DontAskInstanceForScriptable() GET_IT(DONT_ASK_INSTANCE_FOR_SCRIPTABLE)
     JSBool ClassInfoInterfacesOnly()      GET_IT(CLASSINFO_INTERFACES_ONLY)
     JSBool AllowPropModsDuringResolve()   GET_IT(ALLOW_PROP_MODS_DURING_RESOLVE)
     JSBool AllowPropModsToPrototype()     GET_IT(ALLOW_PROP_MODS_TO_PROTOTYPE)
+    JSBool IsGlobalObject()               GET_IT(IS_GLOBAL_OBJECT)
     JSBool DontReflectInterfaceNames()    GET_IT(DONT_REFLECT_INTERFACE_NAMES)
     JSBool UseStubEqualityHook()          GET_IT(USE_STUB_EQUALITY_HOOK)
 
 #undef GET_IT
 };
 
 /***************************************************************************/
 
@@ -2069,17 +2068,17 @@ public:
     ~XPCNativeScriptableShared()
         {if (mJSClass.base.name)nsMemory::Free((void*)mJSClass.base.name);
          MOZ_COUNT_DTOR(XPCNativeScriptableShared);}
 
     char* TransferNameOwnership()
         {char* name=(char*)mJSClass.base.name; mJSClass.base.name = nsnull;
         return name;}
 
-    void PopulateJSClass(JSBool isGlobal);
+    void PopulateJSClass();
 
     void Mark()       {mFlags.Mark();}
     void Unmark()     {mFlags.Unmark();}
     JSBool IsMarked() const {return mFlags.IsMarked();}
 
 private:
     XPCNativeScriptableFlags mFlags;
     XPCWrappedNativeJSClass  mJSClass;
@@ -2089,18 +2088,17 @@ private:
 /***************************************************************************/
 // XPCNativeScriptableInfo is used to hold the nsIXPCScriptable state for a
 // given class or instance.
 
 class XPCNativeScriptableInfo
 {
 public:
     static XPCNativeScriptableInfo*
-    Construct(XPCCallContext& ccx, JSBool isGlobal,
-              const XPCNativeScriptableCreateInfo* sci);
+    Construct(XPCCallContext& ccx, const XPCNativeScriptableCreateInfo* sci);
 
     nsIXPCScriptable*
     GetCallback() const {return mCallback;}
 
     const XPCNativeScriptableFlags&
     GetFlags() const      {return mShared->GetFlags();}
 
     PRUint32
@@ -2201,18 +2199,18 @@ private:
 class XPCWrappedNativeProto
 {
 public:
     static XPCWrappedNativeProto*
     GetNewOrUsed(XPCCallContext& ccx,
                  XPCWrappedNativeScope* scope,
                  nsIClassInfo* classInfo,
                  const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
-                 JSBool isGlobal,
-                 QITableEntry* offsets = UNKNOWN_OFFSETS);
+                 QITableEntry* offsets = UNKNOWN_OFFSETS,
+                 bool callPostCreatePrototype = true);
 
     XPCWrappedNativeScope*
     GetScope()   const {return mScope;}
 
     XPCJSRuntime*
     GetRuntime() const {return mScope->GetRuntime();}
 
     JSObject*
@@ -2281,16 +2279,17 @@ public:
 #undef GET_IT
 
     XPCLock* GetLock() const
         {return ClassIsThreadSafe() ? GetRuntime()->GetMapLock() : nsnull;}
 
     void SetScriptableInfo(XPCNativeScriptableInfo* si)
         {NS_ASSERTION(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
 
+    bool CallPostCreatePrototype(XPCCallContext& ccx);
     void JSProtoObjectFinalized(JSContext *cx, JSObject *obj);
 
     void SystemIsBeingShutDown();
 
     void DebugDump(PRInt16 depth);
 
     void TraceJS(JSTracer* trc)
     {
@@ -2329,18 +2328,19 @@ protected:
 
     // hide ctor
     XPCWrappedNativeProto(XPCWrappedNativeScope* Scope,
                           nsIClassInfo* ClassInfo,
                           PRUint32 ClassInfoFlags,
                           XPCNativeSet* Set,
                           QITableEntry* offsets);
 
-    JSBool Init(XPCCallContext& ccx, JSBool isGlobal,
-                const XPCNativeScriptableCreateInfo* scriptableCreateInfo);
+    JSBool Init(XPCCallContext& ccx,
+                const XPCNativeScriptableCreateInfo* scriptableCreateInfo,
+                bool callPostCreatePrototype);
 
 private:
 #if defined(DEBUG_xpc_hacker) || defined(DEBUG)
     static PRInt32 gDEBUG_LiveProtoCount;
 #endif
 
 private:
     bool
@@ -2579,21 +2579,25 @@ public:
                                   (!HasProto() ||
                                    GetSet() != GetProto()->GetSet());}
 
     XPCJSRuntime*
     GetRuntime() const {XPCWrappedNativeScope* scope = GetScope();
                         return scope ? scope->GetRuntime() : nsnull;}
 
     static nsresult
+    WrapNewGlobal(XPCCallContext &ccx, xpcObjectHelper &nativeHelper,
+                  nsIPrincipal *principal, bool initStandardClasses,
+                  XPCWrappedNative **wrappedGlobal);
+
+    static nsresult
     GetNewOrUsed(XPCCallContext& ccx,
                  xpcObjectHelper& helper,
                  XPCWrappedNativeScope* Scope,
                  XPCNativeInterface* Interface,
-                 JSBool isGlobal,
                  XPCWrappedNative** wrapper);
 
     static nsresult
     Morph(XPCCallContext& ccx,
           JSObject* existingJSObject,
           XPCNativeInterface* Interface,
           nsWrapperCache *cache,
           XPCWrappedNative** resultWrapper);
@@ -2676,17 +2680,18 @@ public:
         if (mScriptableInfo && JS_IsGCMarkingTracer(trc))
             mScriptableInfo->Mark();
         if (HasProto()) GetProto()->TraceJS(trc);
         JSObject* wrapper = GetWrapperPreserveColor();
         if (wrapper)
             JS_CALL_OBJECT_TRACER(trc, wrapper, "XPCWrappedNative::mWrapper");
         if (mScriptableInfo &&
             (mScriptableInfo->GetJSClass()->flags & JSCLASS_XPCONNECT_GLOBAL))
-            GetScope()->TraceDOMPrototypes(trc);
+            TraceXPCGlobal(trc, mFlatJSObject);
+
     }
 
     inline void AutoTrace(JSTracer* trc)
     {
         // If this got called, we're being kept alive by someone who really
         // needs us alive and whole.  Do not let our mFlatJSObject go away.
         // This is the only time we should be tracing our mFlatJSObject,
         // normally somebody else is doing that. Be careful not to trace the
@@ -2784,18 +2789,17 @@ private:
         NEEDS_SOW = JS_BIT(0),
         NEEDS_COW = JS_BIT(1),
         MIGHT_HAVE_EXPANDO = JS_BIT(2),
         FLAG_MASK = JS_BITMASK(3)
     };
 
 private:
 
-    JSBool Init(XPCCallContext& ccx, JSObject* parent, JSBool isGlobal,
-                const XPCNativeScriptableCreateInfo* sci);
+    JSBool Init(XPCCallContext& ccx, JSObject* parent, const XPCNativeScriptableCreateInfo* sci);
     JSBool Init(XPCCallContext &ccx, JSObject *existingJSObject);
     JSBool FinishInit(XPCCallContext &ccx);
 
     JSBool ExtendSet(XPCCallContext& ccx, XPCNativeInterface* aInterface);
 
     nsresult InitTearOff(XPCCallContext& ccx,
                          XPCWrappedNativeTearOff* aTearOff,
                          XPCNativeInterface* aInterface,
@@ -3174,16 +3178,37 @@ public:
 
     already_AddRefed<nsXPCClassInfo> forgetXPCClassInfo()
     {
         GetXPCClassInfo();
 
         return mXPCClassInfo.forget();
     }
 
+    // We assert that we can reach an nsIXPCScriptable somehow.
+    PRUint32 GetScriptableFlags()
+    {
+        // Try getting an nsXPCClassInfo - this handles DOM scriptable helpers.
+        nsCOMPtr<nsIXPCScriptable> sinfo = GetXPCClassInfo();
+
+        // If that didn't work, try just QI-ing. This handles BackstagePass.
+        if (!sinfo)
+            sinfo = do_QueryInterface(GetCanonical());
+
+        // We should have something by now.
+        MOZ_ASSERT(sinfo);
+
+        // Grab the flags. This should not fail.
+        PRUint32 flags;
+        mozilla::DebugOnly<nsresult> rv = sinfo->GetScriptableFlags(&flags);
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+        return flags;
+    }
+
     nsWrapperCache *GetWrapperCache()
     {
         return mCache;
     }
 
 protected:
     xpcObjectHelper(nsISupports *aObject, nsISupports *aCanonical,
                     nsWrapperCache *aCache, bool aIsNode)
@@ -3250,43 +3275,40 @@ public:
      * @param dest [out] the resulting JSObject
      * @param src the native object we're working with
      * @param iid the interface of src that we want (may be null)
      * @param Interface the interface of src that we want
      * @param cache the wrapper cache for src (may be null, in which case src
      *              will be QI'ed to get the cache)
      * @param allowNativeWrapper if true, this method may wrap the resulting
      *        JSObject in an XPCNativeWrapper and return that, as needed.
-     * @param isGlobal
      * @param pErr [out] relevant error code, if any.
      * @param src_is_identity optional performance hint. Set to true only
      *                        if src is the identity pointer.
      */
     static JSBool NativeInterface2JSObject(XPCCallContext& ccx,
                                            jsval* d,
                                            nsIXPConnectJSObjectHolder** dest,
                                            xpcObjectHelper& aHelper,
                                            const nsID* iid,
                                            XPCNativeInterface** Interface,
                                            bool allowNativeWrapper,
-                                           bool isGlobal,
                                            nsresult* pErr)
     {
         XPCLazyCallContext lccx(ccx);
         return NativeInterface2JSObject(lccx, d, dest, aHelper, iid, Interface,
-                                        allowNativeWrapper, isGlobal, pErr);
+                                        allowNativeWrapper, pErr);
     }
     static JSBool NativeInterface2JSObject(XPCLazyCallContext& lccx,
                                            jsval* d,
                                            nsIXPConnectJSObjectHolder** dest,
                                            xpcObjectHelper& aHelper,
                                            const nsID* iid,
                                            XPCNativeInterface** Interface,
                                            bool allowNativeWrapper,
-                                           bool isGlobal,
                                            nsresult* pErr);
 
     static JSBool GetNativeInterfaceFromJSObject(XPCCallContext& ccx,
                                                  void** dest, JSObject* src,
                                                  const nsID* iid,
                                                  nsresult* pErr);
     static JSBool JSObject2NativeInterface(XPCCallContext& ccx,
                                            void** dest, JSObject* src,
@@ -3475,20 +3497,20 @@ private:
 };
 
 /***************************************************************************/
 /*
 * nsJSID implements nsIJSID. It is also used by nsJSIID and nsJSCID as a
 * member (as a hidden implementaion detail) to which they delegate many calls.
 */
 
-extern void xpc_InitJSxIDClassObjects();
+// Initialization is done on demand, and calling the destructor below is always
+// safe.
 extern void xpc_DestroyJSxIDClassObjects();
 
-
 class nsJSID : public nsIJSID
 {
 public:
     NS_DEFINE_STATIC_CID_ACCESSOR(NS_JS_ID_CID)
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIJSID
 
--- a/memory/jemalloc/jemalloc.c
+++ b/memory/jemalloc/jemalloc.c
@@ -984,20 +984,20 @@ struct arena_bin_s {
 
 #ifdef MALLOC_STATS
 	/* Bin statistics. */
 	malloc_bin_stats_t stats;
 #endif
 };
 
 struct arena_s {
-	/* For bug 703087, we're temporarily adding arena.magic to release
-	   builds. */
+#ifdef MALLOC_DEBUG
 	uint32_t		magic;
 #  define ARENA_MAGIC 0x947d3d24
+#endif
 
 	/* All operations on this arena require that lock be locked. */
 #ifdef MOZ_MEMORY
 	malloc_spinlock_t	lock;
 #else
 	pthread_mutex_t		lock;
 #endif
 
@@ -4408,25 +4408,17 @@ isalloc_validate(const void *ptr)
 	chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(ptr);
 	if (chunk == NULL)
 		return (0);
 
 	if (malloc_rtree_get(chunk_rtree, (uintptr_t)chunk) == NULL)
 		return (0);
 
 	if (chunk != ptr) {
-		/* For bug 703087, we've temporarily made what is normally a
-		   debug-only assertion here into a fatal assertion. */
-		if (chunk->arena->magic != ARENA_MAGIC) {
-			char* boom = (char*) 0;
-			_malloc_message("isalloc_validate called with invalid pointer. "
-			                "Crashing...\n", "", "", "");
-			*boom = 1;
-		}
-
+		assert(chunk->arena->magic == ARENA_MAGIC);
 		return (arena_salloc(ptr));
 	} else {
 		size_t ret;
 		extent_node_t *node;
 		extent_node_t key;
 
 		/* Chunk. */
 		key.addr = (void *)chunk;
@@ -4926,19 +4918,20 @@ arena_new(arena_t *arena)
 
 		prev_run_size = arena_bin_run_size_calc(bin, prev_run_size);
 
 #ifdef MALLOC_STATS
 		memset(&bin->stats, 0, sizeof(malloc_bin_stats_t));
 #endif
 	}
 
-        /* For bug 703087, we're temporarily adding arena->magic for release
-           builds. */
+#ifdef MALLOC_DEBUG
 	arena->magic = ARENA_MAGIC;
+#endif
+
 	return (false);
 }
 
 /* Create a new arena and insert it into the arenas array at index ind. */
 static arena_t *
 arenas_extend(unsigned ind)
 {
 	arena_t *ret;
--- a/mobile/android/base/AwesomeBar.java
+++ b/mobile/android/base/AwesomeBar.java
@@ -92,16 +92,17 @@ public class AwesomeBar extends Activity
     static final String USER_ENTERED_KEY = "user_entered";
     static enum Type { ADD, EDIT };
 
     private String mType;
     private AwesomeBarTabs mAwesomeTabs;
     private AwesomeBarEditText mText;
     private ImageButton mGoButton;
     private ContentResolver mResolver;
+    private ContextMenuSubject mContextMenuSubject;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Log.d(LOGTAG, "creating awesomebar");
 
         mResolver = Tabs.getInstance().getContentResolver();
@@ -405,114 +406,106 @@ public class AwesomeBar extends Activity
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         mAwesomeTabs.destroy();
         GeckoAppShell.unregisterGeckoEventListener("SearchEngines:Data", this);
     }
 
-    private Object mContextMenuSubject = null;
+    private class ContextMenuSubject {
+        public int id;
+        public String url;
+        public byte[] favicon;
+        public String title;
+
+        public ContextMenuSubject(int id, String url, byte[] favicon, String title) {
+            this.id = id;
+            this.url = url;
+            this.favicon = favicon;
+            this.title = title;
+        }
+    };
 
     @Override
     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
         super.onCreateContextMenu(menu, view, menuInfo);
         ListView list = (ListView) view;
-        Object selectedItem = null;
-        String title = "";
+        mContextMenuSubject = null;
 
         if (list == findViewById(R.id.history_list)) {
             if (!(menuInfo instanceof ExpandableListView.ExpandableListContextMenuInfo)) {
                 Log.e(LOGTAG, "menuInfo is not ExpandableListContextMenuInfo");
                 return;
             }
 
             ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;
             int childPosition = ExpandableListView.getPackedPositionChild(info.packedPosition);
             int groupPosition = ExpandableListView.getPackedPositionGroup(info.packedPosition);
 
             // Check if long tap is on a header row
             if (groupPosition < 0 || childPosition < 0)
                 return;
 
             ExpandableListView exList = (ExpandableListView) list;
-            selectedItem = exList.getExpandableListAdapter().getChild(groupPosition, childPosition);
 
             // The history list is backed by a SimpleExpandableListAdapter
             @SuppressWarnings("rawtypes")
-            Map map = (Map) selectedItem;
-            title = (String) map.get(URLColumns.TITLE);
+            Map map = (Map) exList.getExpandableListAdapter().getChild(groupPosition, childPosition);
+            mContextMenuSubject = new ContextMenuSubject(-1, (String)map.get(URLColumns.URL),
+                    (byte[]) map.get(URLColumns.FAVICON), (String)map.get(URLColumns.TITLE));
         } else {
             if (!(menuInfo instanceof AdapterView.AdapterContextMenuInfo)) {
                 Log.e(LOGTAG, "menuInfo is not AdapterContextMenuInfo");
                 return;
             }
 
             AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
-            selectedItem = list.getItemAtPosition(info.position);
+            Object selectedItem = list.getItemAtPosition(info.position);
 
             if (!(selectedItem instanceof Cursor)) {
                 Log.e(LOGTAG, "item at " + info.position + " is not a Cursor");
                 return;
             }
 
             Cursor cursor = (Cursor) selectedItem;
-            title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
 
             // Don't show the context menu for folders
-            if (list == findViewById(R.id.bookmarks_list) &&
-                cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.IS_FOLDER)) == 1) {
-                selectedItem = null;
+            if (!(list == findViewById(R.id.bookmarks_list) && cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks.IS_FOLDER)) == 1)) {
+                mContextMenuSubject = new ContextMenuSubject(cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID)),
+                                                             cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL)),
+                                                             cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON)),
+                                                             cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE))
+                );
             }
         }
 
-        if (selectedItem == null || !((selectedItem instanceof Cursor) || (selectedItem instanceof Map))) {
-            mContextMenuSubject = null;
+        if (mContextMenuSubject == null)
             return;
-        }
-
-        mContextMenuSubject = selectedItem;
 
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.awesomebar_contextmenu, menu);
         
         if (list != findViewById(R.id.bookmarks_list)) {
             MenuItem removeBookmarkItem = menu.findItem(R.id.remove_bookmark);
             removeBookmarkItem.setVisible(false);
         }
 
-        menu.setHeaderTitle(title);
+        menu.setHeaderTitle(mContextMenuSubject.title);
     }
 
     @Override
     public boolean onContextItemSelected(MenuItem item) {
         if (mContextMenuSubject == null)
             return false;
 
-        final int id;
-        final String url;
-        byte[] b = null;
-        String title = "";
-        if (mContextMenuSubject instanceof Cursor) {
-            Cursor cursor = (Cursor)mContextMenuSubject;
-            id = cursor.getInt(cursor.getColumnIndexOrThrow(Bookmarks._ID));
-            url = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.URL));
-            b = cursor.getBlob(cursor.getColumnIndexOrThrow(URLColumns.FAVICON));
-            title = cursor.getString(cursor.getColumnIndexOrThrow(URLColumns.TITLE));
-        } else if (mContextMenuSubject instanceof Map) {
-            @SuppressWarnings("rawtypes") Map map = (Map)mContextMenuSubject;
-            id = -1;
-            url = (String)map.get(URLColumns.URL);
-            b = (byte[]) map.get(URLColumns.FAVICON);
-            title = (String)map.get(URLColumns.TITLE);
-        } else {
-            return false;
-        }
-
-        mContextMenuSubject = null;
+        final int id = mContextMenuSubject.id;
+        final String url = mContextMenuSubject.url;
+        final byte[] b = mContextMenuSubject.favicon;
+        final String title = mContextMenuSubject.title;
 
         switch (item.getItemId()) {
             case R.id.open_new_tab: {
                 GeckoApp.mAppContext.loadUrl(url, AwesomeBar.Type.ADD);
                 Toast.makeText(this, R.string.new_tab_opened, Toast.LENGTH_SHORT).show();
                 break;
             }
             case R.id.remove_bookmark: {
--- a/mobile/android/base/BrowserToolbar.java
+++ b/mobile/android/base/BrowserToolbar.java
@@ -289,17 +289,17 @@ public class BrowserToolbar extends Line
     public void setShadowVisibility(boolean visible) {
         mShadow.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     public void setTitle(CharSequence title) {
         Tab tab = Tabs.getInstance().getSelectedTab();
         // Setting a null title for about:home will ensure we just see
         // the "Enter Search or Address" placeholder text
-        if (tab != null && tab.getURL().equals("about:home"))
+        if (tab != null && "about:home".equals(tab.getURL()))
             title = null;
         mAwesomeBar.setText(title);
     }
 
     public void setFavicon(Drawable image) {
         if (Tabs.getInstance().getSelectedTab().isLoading())
             return;
 
@@ -334,17 +334,18 @@ public class BrowserToolbar extends Line
             GeckoActionBar.hide(GeckoApp.mAppContext);
         else
             setVisibility(View.GONE);
     }
 
     public void refresh() {
         Tab tab = Tabs.getInstance().getSelectedTab();
         if (tab != null) {
+            String url = tab.getURL();
             setTitle(tab.getDisplayTitle());
             setFavicon(tab.getFavicon());
             setSecurityMode(tab.getSecurityMode());
             setProgressVisibility(tab.isLoading());
-            setShadowVisibility(!(tab.getURL().startsWith("about:")));
+            setShadowVisibility((url == null) || !url.startsWith("about:"));
             updateTabCount(Tabs.getInstance().getCount());
         }
     }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -427,17 +427,17 @@ abstract public class GeckoApp
 
         Tab tab = Tabs.getInstance().getSelectedTab();
         MenuItem bookmark = aMenu.findItem(R.id.bookmark);
         MenuItem forward = aMenu.findItem(R.id.forward);
         MenuItem share = aMenu.findItem(R.id.share);
         MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
         MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
 
-        if (tab == null) {
+        if (tab == null || tab.getURL() == null) {
             bookmark.setEnabled(false);
             forward.setEnabled(false);
             share.setEnabled(false);
             saveAsPDF.setEnabled(false);
             return true;
         }
         
         bookmark.setEnabled(true);
@@ -450,19 +450,18 @@ abstract public class GeckoApp
             bookmark.setChecked(false);
             bookmark.setIcon(R.drawable.ic_menu_bookmark_add);
         }
 
         forward.setEnabled(tab.canDoForward());
 
         // Disable share menuitem for about:, chrome: and file: URIs
         String scheme = Uri.parse(tab.getURL()).getScheme();
-        boolean enabled = scheme != null && !(scheme.equals("about") || scheme.equals("chrome") ||
-                                              scheme.equals("file"));
-        share.setEnabled(enabled);
+        share.setEnabled(!(scheme.equals("about") || scheme.equals("chrome") ||
+                           scheme.equals("file")));
 
         // Disable save as PDF for about:home and xul pages
         saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
                                tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
 
         charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
 
         return true;
@@ -495,18 +494,22 @@ abstract public class GeckoApp
                         Toast.makeText(this, R.string.bookmark_added, Toast.LENGTH_SHORT).show();
                         item.setIcon(R.drawable.ic_menu_bookmark_remove);
                     }
                 }
                 return true;
             case R.id.share:
                 tab = Tabs.getInstance().getSelectedTab();
                 if (tab != null) {
-                  GeckoAppShell.openUriExternal(tab.getURL(), "text/plain", "", "",
-                                                Intent.ACTION_SEND, tab.getTitle());
+                    String url = tab.getURL();
+                    if (url == null)
+                        return false;
+
+                    GeckoAppShell.openUriExternal(url, "text/plain", "", "",
+                                                  Intent.ACTION_SEND, tab.getTitle());
                 }
                 return true;
             case R.id.reload:
                 doReload();
                 return true;
             case R.id.forward:
                 doForward();
                 return true;
--- a/mobile/android/base/Tab.java
+++ b/mobile/android/base/Tab.java
@@ -97,20 +97,16 @@ public final class Tab {
         public String mTitle;       // must never be null
 
         public HistoryEntry(String uri, String title) {
             mUri = uri;
             mTitle = title;
         }
     }
 
-    public Tab() {
-        this(-1, "", false, -1, "");
-    }
-
     public Tab(int id, String url, boolean external, int parentId, String title) {
         mId = id;
         mUrl = url;
         mExternal = external;
         mParentId = parentId;
         mTitle = title;
         mFavicon = null;
         mFaviconUrl = null;
@@ -143,16 +139,17 @@ public final class Tab {
     public int getId() {
         return mId;
     }
 
     public int getParentId() {
         return mParentId;
     }
 
+    // may be null if user-entered query hasn't yet been resolved to a URI
     public String getURL() {
         return mUrl;
     }
 
     public String getTitle() {
         return mTitle;
     }
 
@@ -365,34 +362,46 @@ public final class Tab {
     }
 
     private void updateBookmark() {
         GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
             public void run() {
                 if (mCheckBookmarkTask != null)
                     mCheckBookmarkTask.cancel(false);
 
-                mCheckBookmarkTask = new CheckBookmarkTask(getURL());
+                String url = getURL();
+                if (url == null)
+                    return;
+
+                mCheckBookmarkTask = new CheckBookmarkTask(url);
                 mCheckBookmarkTask.execute();
             }
         });
     }
 
     public void addBookmark() {
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
-                BrowserDB.addBookmark(mContentResolver, getTitle(), getURL());
+                String url = getURL();
+                if (url == null)
+                    return;
+
+                BrowserDB.addBookmark(mContentResolver, getTitle(), url);
             }
         });
     }
 
     public void removeBookmark() {
         GeckoAppShell.getHandler().post(new Runnable() {
             public void run() {
-                BrowserDB.removeBookmarksWithURL(mContentResolver, getURL());
+                String url = getURL();
+                if (url == null)
+                    return;
+
+                BrowserDB.removeBookmarksWithURL(mContentResolver, url);
             }
         });
     }
 
     public boolean doReload() {
         if (mHistory.isEmpty())
             return false;
         GeckoEvent e = GeckoEvent.createBroadcastEvent("Session:Reload", "");
@@ -536,17 +545,21 @@ public final class Tab {
                     mBookmark = isBookmark.booleanValue();
                 }
             });
         }
     }
 
     private void saveThumbnailToDB(BitmapDrawable thumbnail) {
         try {
-            BrowserDB.updateThumbnailForUrl(mContentResolver, getURL(), thumbnail);
+            String url = getURL();
+            if (url == null)
+                return;
+
+            BrowserDB.updateThumbnailForUrl(mContentResolver, url, thumbnail);
         } catch (Exception e) {
             // ignore
         }
     }
 
     public void addPluginView(View view) {
         mPluginViews.add(view);
     }
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -76,34 +76,35 @@ public class Tabs implements GeckoEventL
         return tabs.size();
     }
 
     public Tab addTab(JSONObject params) throws JSONException {
         int id = params.getInt("tabID");
         if (tabs.containsKey(id))
            return tabs.get(id);
 
-        String url = params.getString("uri");
+        // null strings return "null" (http://code.google.com/p/android/issues/detail?id=13830)
+        String url = params.isNull("uri") ? null : params.getString("uri");
         Boolean external = params.getBoolean("external");
         int parentId = params.getInt("parentId");
         String title = params.getString("title");
 
         Tab tab = new Tab(id, url, external, parentId, title);
         tabs.put(id, tab);
         order.add(tab);
 
         if (!mRestoringSession) {
             GeckoApp.mAppContext.mMainHandler.post(new Runnable() {
                 public void run() {
                     GeckoApp.mBrowserToolbar.updateTabCountAndAnimate(getCount());
                 }
             });
         }
 
-        Log.i(LOGTAG, "Added a tab with id: " + id + ", url: " + url);
+        Log.i(LOGTAG, "Added a tab with id: " + id);
         return tab;
     }
 
     public void removeTab(int id) {
         if (tabs.containsKey(id)) {
             order.remove(getTab(id));
             tabs.remove(id);
             Log.i(LOGTAG, "Removed a tab with id: " + id);
@@ -116,32 +117,33 @@ public class Tabs implements GeckoEventL
 
         final Tab oldTab = getSelectedTab();
         final Tab tab = tabs.get(id);
         // This avoids a NPE below, but callers need to be careful to
         // handle this case
         if (tab == null)
             return null;
 
-        if (tab.getURL().equals("about:home"))
+        if ("about:home".equals(tab.getURL()))
             GeckoApp.mAppContext.showAboutHome();
         else
             GeckoApp.mAppContext.hideAboutHome();
 
         GeckoApp.mAppContext.mMainHandler.post(new Runnable() { 
             public void run() {
                 GeckoApp.mAutoCompletePopup.hide();
                 // Do we need to do this check?
                 if (isSelectedTab(tab)) {
+                    String url = tab.getURL();
                     GeckoApp.mBrowserToolbar.setTitle(tab.getDisplayTitle());
                     GeckoApp.mBrowserToolbar.setFavicon(tab.getFavicon());
                     GeckoApp.mBrowserToolbar.setSecurityMode(tab.getSecurityMode());
                     GeckoApp.mBrowserToolbar.setProgressVisibility(tab.isLoading());
                     GeckoApp.mDoorHangerPopup.updatePopup();
-                    GeckoApp.mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:")));
+                    GeckoApp.mBrowserToolbar.setShadowVisibility((url == null) || !url.startsWith("about:"));
                     notifyListeners(tab, TabEvents.SELECTED);
 
                     if (oldTab != null)
                         GeckoApp.mAppContext.hidePlugins(oldTab, true);
                 }
             }
         });
 
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -133,16 +133,21 @@ XPCOMUtils.defineLazyServiceGetter(this,
 #endif
 
 XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
   let ContentAreaUtils = {};
   Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
   return ContentAreaUtils;
 });
 
+XPCOMUtils.defineLazyGetter(this, "Rect", function() {
+  Cu.import("resource://gre/modules/Geometry.jsm");
+  return Rect;
+});
+
 function resolveGeckoURI(aURI) {
   if (aURI.indexOf("chrome://") == 0) {
     let registry = Cc['@mozilla.org/chrome/chrome-registry;1'].getService(Ci["nsIChromeRegistry"]);
     return registry.convertChromeURL(Services.io.newURI(aURI, null, null)).spec;
   } else if (aURI.indexOf("resource://") == 0) {
     let handler = Services.io.getProtocolHandler("resource").QueryInterface(Ci.nsIResProtocolHandler);
     return handler.resolveURI(Services.io.newURI(aURI, null, null));
   }
@@ -1518,27 +1523,33 @@ Tab.prototype = {
     this.vbox.appendChild(this.browser);
 
     this.browser.stop();
 
     // Turn off clipping so we can buffer areas outside of the browser element.
     let frameLoader = this.browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
     frameLoader.clipSubdocument = false;
 
+    // only set tab uri if uri is valid
+    let uri = null;
+    try {
+      uri = Services.io.newURI(aURL, null, null).spec;
+    } catch (e) {}
+
     this.id = ++gTabIDFactory;
 
     let message = {
       gecko: {
         type: "Tab:Added",
         tabID: this.id,
-        uri: aURL,
+        uri: uri,
         parentId: ("parentId" in aParams) ? aParams.parentId : -1,
         external: ("external" in aParams) ? aParams.external : false,
         selected: ("selected" in aParams) ? aParams.selected : true,
-        title: aParams.title || "",
+        title: aParams.title || aURL,
         delayLoad: aParams.delayLoad || false
       }
     };
     sendMessageToJava(message);
 
     let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
                 Ci.nsIWebProgress.NOTIFY_LOCATION |
                 Ci.nsIWebProgress.NOTIFY_SECURITY;
@@ -2311,58 +2322,75 @@ var BrowserEventHandler = {
           type: "Tab:HasTouchListener",
           tabID: tab.id
         }
       });
     }
   },
  
   _zoomOut: function() {
-    this._zoomedToElement = null;
-    // zoom out, try to keep the center in the center of the page
-    setTimeout(function() {
-      sendMessageToJava({ gecko: { type: "Browser:ZoomToPageWidth"} });
-    }, 0);    
+    sendMessageToJava({ gecko: { type: "Browser:ZoomToPageWidth"} });
   },
 
   onDoubleTap: function(aData) {
     let data = JSON.parse(aData);
 
-    let rect = {};
     let win = BrowserApp.selectedBrowser.contentWindow;
     
     let zoom = BrowserApp.selectedTab._viewport.zoom;
     let element = ElementTouchHelper.anyElementFromPoint(win, data.x, data.y);
     if (!element) {
       this._zoomOut();
       return;
     }
 
     win = element.ownerDocument.defaultView;
     while (element && win.getComputedStyle(element,null).display == "inline")
       element = element.parentNode;
-    if (!element || element == this._zoomedToElement) {
+
+    if (!element) {
       this._zoomOut();
-    } else if (element) {
+    } else {
       const margin = 15;
-      this._zoomedToElement = element;
-      rect = ElementTouchHelper.getBoundingContentRect(element);
-
-      let zoom = BrowserApp.selectedTab.viewport.zoom;
-      rect.x *= zoom;
-      rect.y *= zoom;
-      rect.w *= zoom;
-      rect.h *= zoom;
-
-      setTimeout(function() {
-        rect.type = "Browser:ZoomToRect";
-        rect.x -= margin;
-        rect.w += 2*margin;
-        sendMessageToJava({ gecko: rect });
-      }, 0);
+      const minDifference = -20;
+      const maxDifference = 20;
+      let rect = ElementTouchHelper.getBoundingContentRect(element);
+
+      let viewport = BrowserApp.selectedTab.viewport;
+      let vRect = new Rect(viewport.x, viewport.y, viewport.width, viewport.height);
+
+      let zoom = viewport.zoom;
+      let bRect = new Rect(Math.max(0,rect.x - margin),
+                           rect.y,
+                           rect.w + 2*margin,
+                           rect.h);
+      // constrict the rect to the screen width
+      bRect.width = Math.min(bRect.width, viewport.pageWidth/zoom - bRect.x);
+      bRect.scale(zoom, zoom);
+
+      let overlap = vRect.intersect(bRect);
+      let overlapArea = overlap.width*overlap.height;
+      // we want to know if the area of the element showing is near the max we can show
+      // on the screen at any time and if its already stretching the width of the screen
+      let availHeight = Math.min(bRect.width*vRect.height/vRect.width, bRect.height);
+      let showing = overlapArea/(bRect.width*availHeight);
+      let dw = (bRect.width - vRect.width)/zoom;
+      let dx = (bRect.x - vRect.x)/zoom;
+
+      if (showing > 0.9 &&
+          dx > minDifference && dx < maxDifference &&
+          dw > minDifference && dw < maxDifference) {
+            this._zoomOut();
+            return;
+      }
+
+      rect.type = "Browser:ZoomToRect";
+      rect.x = bRect.x; rect.y = bRect.y;
+      rect.w = bRect.width; rect.h = availHeight;
+      sendMessageToJava({ gecko: rect });
     }
   },
 
   _firstScrollEvent: false,
 
   _scrollableElement: null,
 
   _highlightElement: null,
--- a/modules/libmar/tests/unit/test_sign_verify.js
+++ b/modules/libmar/tests/unit/test_sign_verify.js
@@ -52,18 +52,17 @@ function run_test() {
 
     let DERFile = do_get_file("data/mycert.der");
 
     // Will reference the arguments to use for verification in signmar
     let args;
 
     // The XPCShell test wiki indicates this is the preferred way for 
     // Windows detection.
-    var isWindows = ("@mozilla.org/windows-registry-key;1" 
-                     in Cc);
+    var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
 
     // Setup the command line arguments to create the MAR.
     // Windows vs. Linux/Mac/... have different command line for verification 
     // since  on Windows we verify with CryptoAPI and on all other platforms 
     // we verify with NSS. So on Windows we use an exported DER file and on 
     // other platforms we use the NSS config db.
     if (isWindows) {
       args = ["-D", DERFile.path, "-v", signedMAR.path];
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -750,16 +750,140 @@ let Utils = {
    * Take a base64-encoded 128-bit AES key, returning it as five groups of five
    * uppercase alphanumeric characters, separated by hyphens.
    * A.K.A. base64-to-base32 encoding.
    */
   presentEncodedKeyAsSyncKey : function presentEncodedKeyAsSyncKey(encodedKey) {
     return Utils.encodeKeyBase32(atob(encodedKey));
   },
 
+  /**
+   * Compute the HTTP MAC SHA-1 for an HTTP request.
+   *
+   * @param  identifier
+   *         (string) MAC Key Identifier.
+   * @param  key
+   *         (string) MAC Key.
+   * @param  method
+   *         (string) HTTP request method.
+   * @param  URI
+   *         (nsIURI) HTTP request URI.
+   * @param  extra
+   *         (object) Optional extra parameters. Valid keys are:
+   *           nonce_bytes - How many bytes the nonce should be. This defaults
+   *             to 8. Note that this many bytes are Base64 encoded, so the
+   *             string length of the nonce will be longer than this value.
+   *           ts - Timestamp to use. Should only be defined for testing.
+   *           nonce - String nonce. Should only be defined for testing as this
+   *             function will generate a cryptographically secure random one
+   *             if not defined.
+   *           ext - Extra string to be included in MAC. Per the HTTP MAC spec,
+   *             the format is undefined and thus application specific.
+   * @returns
+   *         (object) Contains results of operation and input arguments (for
+   *           symmetry). The object has the following keys:
+   *
+   *           identifier - (string) MAC Key Identifier (from arguments).
+   *           key - (string) MAC Key (from arguments).
+   *           method - (string) HTTP request method (from arguments).
+   *           hostname - (string) HTTP hostname used (derived from arguments).
+   *           port - (string) HTTP port number used (derived from arguments).
+   *           mac - (string) Raw HMAC digest bytes.
+   *           getHeader - (function) Call to obtain the string Authorization
+   *             header value for this invocation.
+   *           nonce - (string) Nonce value used.
+   *           ts - (number) Integer seconds since Unix epoch that was used.
+   */
+  computeHTTPMACSHA1: function computeHTTPMACSHA1(identifier, key, method,
+                                                  uri, extra) {
+    let ts = (extra && extra.ts) ? extra.ts : Math.floor(Date.now() / 1000);
+    let nonce_bytes = (extra && extra.nonce_bytes > 0) ? extra.nonce_bytes : 8;
+
+    // We are allowed to use more than the Base64 alphabet if we want.
+    let nonce = (extra && extra.nonce)
+                ? extra.nonce
+                : btoa(Utils.generateRandomBytes(nonce_bytes));
+
+    let host = uri.asciiHost;
+    let port;
+    let usedMethod = method.toUpperCase();
+
+    if (uri.port != -1) {
+      port = uri.port;
+    } else if (uri.scheme == "http") {
+      port = "80";
+    } else if (uri.scheme == "https") {
+      port = "443";
+    } else {
+      throw new Error("Unsupported URI scheme: " + uri.scheme);
+    }
+
+    let ext = (extra && extra.ext) ? extra.ext : "";
+
+    let requestString = ts.toString(10) + "\n" +
+                        nonce           + "\n" +
+                        usedMethod      + "\n" +
+                        uri.path        + "\n" +
+                        host            + "\n" +
+                        port            + "\n" +
+                        ext             + "\n";
+
+    let hasher = Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA1,
+                                      Utils.makeHMACKey(key));
+    let mac = Utils.digestBytes(requestString, hasher);
+
+    function getHeader() {
+      return Utils.getHTTPMACSHA1Header(this.identifier, this.ts, this.nonce,
+                                        this.mac, this.ext);
+    }
+
+    return {
+      identifier: identifier,
+      key:        key,
+      method:     usedMethod,
+      hostname:   host,
+      port:       port,
+      mac:        mac,
+      nonce:      nonce,
+      ts:         ts,
+      ext:        ext,
+      getHeader:  getHeader
+    };
+  },
+
+  /**
+   * Obtain the HTTP MAC Authorization header value from fields.
+   *
+   * @param  identifier
+   *         (string) MAC key identifier.
+   * @param  ts
+   *         (number) Integer seconds since Unix epoch.
+   * @param  nonce
+   *         (string) Nonce value.
+   * @param  mac
+   *         (string) Computed HMAC digest (raw bytes).
+   * @param  ext
+   *         (optional) (string) Extra string content.
+   * @returns
+   *         (string) Value to put in Authorization header.
+   */
+  getHTTPMACSHA1Header: function getHTTPMACSHA1Header(identifier, ts, nonce,
+                                                      mac, ext) {
+    let header ='MAC id="' + identifier + '", ' +
+                'ts="'     + ts         + '", ' +
+                'nonce="'  + nonce      + '", ' +
+                'mac="'    + btoa(mac)  + '"';
+
+    if (!ext) {
+      return header;
+    }
+
+    return header += ', ext="' + ext +'"';
+  },
+
   makeURI: function Weave_makeURI(URIString) {
     if (!URIString)
       return null;
     try {
       return Services.io.newURI(URIString, null, null);
     } catch (e) {
       let log = Log4Moz.repository.getLogger("Sync.Utils");
       log.debug("Could not create URI: " + Utils.exceptionStr(e));
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 Cu.import("resource://services-sync/async.js");
 Cu.import("resource://services-sync/util.js");
 Cu.import("resource://services-sync/record.js");
 Cu.import("resource://services-sync/engines.js");
-var btoa;
+let btoa;
+let atob;
 
 let provider = {
   getFile: function(prop, persistent) {
     persistent.value = true;
     switch (prop) {
       case "ExtPrefDL":
         return [Services.dirsvc.get("CurProcD", Ci.nsIFile)];
       default:
@@ -34,16 +35,17 @@ function waitForZeroTimer(callback) {
       return;
     }
     callback();
   }
   timer = Utils.namedTimer(wait, 150, {}, "timer");
 }
 
 btoa = Cu.import("resource://services-sync/log4moz.js").btoa;
+atob = Cu.import("resource://services-sync/log4moz.js").atob;
 function getTestLogger(component) {
   return Log4Moz.repository.getLogger("Testing");
 }
 
 function initTestLogging(level) {
   function LogStats() {
     this.errorsLogged = 0;
   }
--- a/services/sync/tests/unit/test_load_modules.js
+++ b/services/sync/tests/unit/test_load_modules.js
@@ -1,27 +1,34 @@
 const modules = [
+                 "addonsreconciler.js",
+                 "async.js",
                  "constants.js",
+                 "engines/addons.js",
                  "engines/bookmarks.js",
                  "engines/clients.js",
                  "engines/forms.js",
                  "engines/history.js",
                  "engines/passwords.js",
                  "engines/prefs.js",
                  "engines/tabs.js",
                  "engines.js",
                  "ext/Observers.js",
                  "ext/Preferences.js",
                  "identity.js",
+                 "jpakeclient.js",
                  "log4moz.js",
                  "main.js",
                  "notifications.js",
+                 "policies.js",
                  "record.js",
                  "resource.js",
+                 "rest.js",
                  "service.js",
+                 "status.js",
                  "util.js",
 ];
 
 function run_test() {
   for each (let m in modules) {
     _("Attempting to load resource://services-sync/" + m);
     Cu.import("resource://services-sync/" + m, {});
   }
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_utils_httpmac.js
@@ -0,0 +1,69 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://services-sync/util.js");
+
+function run_test() {
+  initTestLogging();
+
+  run_next_test();
+}
+
+add_test(function test_sha1() {
+  _("Ensure HTTP MAC SHA1 generation works as expected.");
+
+  let id = "vmo1txkttblmn51u2p3zk2xiy16hgvm5ok8qiv1yyi86ffjzy9zj0ez9x6wnvbx7";
+  let key = "b8u1cc5iiio5o319og7hh8faf2gi5ym4aq0zwf112cv1287an65fudu5zj7zo7dz";
+  let ts = 1329181221;
+  let method = "GET";
+  let nonce = "wGX71";
+  let uri = Utils.makeURI("http://10.250.2.176/alias/");
+
+  let result = Utils.computeHTTPMACSHA1(id, key, method, uri, {ts: ts,
+                                                               nonce: nonce});
+
+  do_check_eq(btoa(result.mac), "jzh5chjQc2zFEvLbyHnPdX11Yck=");
+
+  do_check_eq(result.getHeader(),
+              'MAC id="vmo1txkttblmn51u2p3zk2xiy16hgvm5ok8qiv1yyi86ffjzy9zj0ez9x6wnvbx7", ' +
+              'ts="1329181221", nonce="wGX71", mac="jzh5chjQc2zFEvLbyHnPdX11Yck="');
+
+  let ext = "EXTRA DATA; foo,bar=1";
+
+  let result = Utils.computeHTTPMACSHA1(id, key, method, uri, {ts: ts,
+                                                               nonce: nonce,
+                                                               ext: ext});
+  do_check_eq(btoa(result.mac), "bNf4Fnt5k6DnhmyipLPkuZroH68=");
+  do_check_eq(result.getHeader(),
+              'MAC id="vmo1txkttblmn51u2p3zk2xiy16hgvm5ok8qiv1yyi86ffjzy9zj0ez9x6wnvbx7", ' +
+              'ts="1329181221", nonce="wGX71", mac="bNf4Fnt5k6DnhmyipLPkuZroH68=", ' +
+              'ext="EXTRA DATA; foo,bar=1"');
+
+  run_next_test();
+});
+
+add_test(function test_nonce_length() {
+  _("Ensure custom nonce lengths are honoured.");
+
+  function get_mac(length) {
+    let uri = Utils.makeURI("http://example.com/");
+    return Utils.computeHTTPMACSHA1("foo", "bar", "GET", uri, {
+      nonce_bytes: length
+    });
+  }
+
+  let result = get_mac(12);
+  do_check_eq(12, atob(result.nonce).length);
+
+  let result = get_mac(2);
+  do_check_eq(2, atob(result.nonce).length);
+
+  let result = get_mac(0);
+  do_check_eq(8, atob(result.nonce).length);
+
+  let result = get_mac(-1);
+  do_check_eq(8, atob(result.nonce).length);
+
+  run_next_test();
+});
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
 head = head_appinfo.js head_helpers.js head_http_server.js
-tail = 
+tail =
+
+[test_load_modules.js]
 
 [test_Observers.js]
 [test_Preferences.js]
 [test_addons_engine.js]
 [test_addons_reconciler.js]
 [test_addons_store.js]
 [test_addons_tracker.js]
 [test_async_chain.js]
@@ -44,17 +46,16 @@ skip-if = os == "android"
 [test_hmac_error.js]
 [test_httpd_sync_server.js]
 [test_interval_triggers.js]
 [test_jpakeclient.js]
 # Bug 618233: this test produces random failures on Windows 7.
 # Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
 skip-if = os == "win" || os == "android"
 [test_keys.js]
-[test_load_modules.js]
 [test_log4moz.js]
 [test_node_reassignment.js]
 [test_notifications.js]
 [test_password_store.js]
 [test_password_tracker.js]
 [test_places_guid_downgrade.js]
 [test_prefs_store.js]
 [test_prefs_tracker.js]
@@ -110,16 +111,17 @@ skip-if = os == "android"
 [test_utils_deepEquals.js]
 [test_utils_deferGetSet.js]
 [test_utils_deriveKey.js]
 [test_utils_encodeBase32.js]
 [test_utils_ensureOneOpen.js]
 [test_utils_getErrorString.js]
 [test_utils_getIcon.js]
 [test_utils_hkdfExpand.js]
+[test_utils_httpmac.js]
 [test_utils_json.js]
 [test_utils_lazyStrings.js]
 [test_utils_lock.js]
 [test_utils_makeGUID.js]
 [test_utils_makeURI.js]
 [test_utils_namedTimer.js]
 [test_utils_notify.js]
 [test_utils_passphrase.js]
--- a/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
+++ b/testing/mochitest/tests/SimpleTest/specialpowersAPI.js
@@ -112,38 +112,30 @@ function isXrayWrapper(x) {
 }
 
 // We can't call apply() directy on Xray-wrapped functions, so we have to be
 // clever.
 function doApply(fun, invocant, args) {
   return Function.prototype.apply.call(fun, invocant, args);
 }
 
-// Use a weak map to cache wrappers. This allows the wrappers to preserve identity.
-var wrapperCache = WeakMap();
-
 function wrapPrivileged(obj) {
 
   // Primitives pass straight through.
   if (!isWrappable(obj))
     return obj;
 
   // No double wrapping.
   if (isWrapper(obj))
     throw "Trying to double-wrap object!";
 
-  // Try the cache.
-  if (wrapperCache.has(obj))
-    return wrapperCache.get(obj);
-
   // Make our core wrapper object.
   var handler = new SpecialPowersHandler(obj);
 
   // If the object is callable, make a function proxy.
-  var wrapper;
   if (typeof obj === "function") {
     var callTrap = function() {
       // The invocant and arguments may or may not be wrappers. Unwrap them if necessary.
       var invocant = unwrapIfWrapped(this);
       var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
 
       return wrapPrivileged(doApply(obj, invocant, unwrappedArgs));
     };
@@ -157,26 +149,21 @@ function wrapPrivileged(obj) {
       var FakeConstructor = function() {
         doApply(obj, this, unwrappedArgs);
       };
       FakeConstructor.prototype = obj.prototype;
 
       return wrapPrivileged(new FakeConstructor());
     };
 
-    wrapper = Proxy.createFunction(handler, callTrap, constructTrap);
-  }
-  // Otherwise, just make a regular object proxy.
-  else {
-    wrapper = Proxy.create(handler);
+    return Proxy.createFunction(handler, callTrap, constructTrap);
   }
 
-  // Cache the wrapper and return it.
-  wrapperCache.set(obj, wrapper);
-  return wrapper;
+  // Otherwise, just make a regular object proxy.
+  return Proxy.create(handler);
 };
 
 function unwrapPrivileged(x) {
 
   // We don't wrap primitives, so sometimes we have a primitive where we'd
   // expect to have a wrapper. The proxy pretends to be the type that it's
   // emulating, so we can just as easily check isWrappable() on a proxy as
   // we can on an unwrapped object.
@@ -377,16 +364,19 @@ SpecialPowersAPI.prototype = {
    * object containing a reference to the underlying object, where all method
    * calls and property accesses are transparently performed with the System
    * Principal. Moreover, objects obtained from the wrapper (including properties
    * and method return values) are wrapped automatically. Thus, after a single
    * call to SpecialPowers.wrap(), the wrapper layer is transitively maintained.
    *
    * Known Issues:
    *
+   *  - The wrapping function does not preserve identity, so
+   *    SpecialPowers.wrap(foo) !== SpecialPowers.wrap(foo). See bug 718543.
+   *
    *  - The wrapper cannot see expando properties on unprivileged DOM objects.
    *    That is to say, the wrapper uses Xray delegation.
    *
    *  - The wrapper sometimes guesses certain ES5 attributes for returned
    *    properties. This is explained in a comment in the wrapper code above,
    *    and shouldn't be a problem.
    */
   wrap: wrapPrivileged,
--- a/testing/mochitest/tests/test_SpecialPowersExtension.html
+++ b/testing/mochitest/tests/test_SpecialPowersExtension.html
@@ -129,23 +129,16 @@ function starttest(){
   noxray.__proto__ = noxray_proto;
   var noxray_wrapper = SpecialPowers.wrap(noxray);
   is(noxray_wrapper.c, 32, "Regular properties should work.");
   is(noxray_wrapper.a, 5, "Shadow properties should work.");
   is(noxray_wrapper.b, 12, "Proto properties should work.");
   noxray.b = 122;
   is(noxray_wrapper.b, 122, "Should be able to shadow.");
 
-  // Check that the wrapper preserves identity.
-  var someIdentityObject = {a: 2};
-  ok(SpecialPowers.wrap(someIdentityObject) === SpecialPowers.wrap(someIdentityObject),
-     "SpecialPowers wrapping should preserve identity!");
-  ok(webnav === SpecialPowers.wrap(SpecialPowers.unwrap(webnav)),
-     "SpecialPowers wrapping should preserve identity!");
-
   info("\nProfile::SpecialPowersRunTime: " + (new Date() - startTime) + "\n");
   SimpleTest.finish();
 }
 </script>
 </pre>
 </body>
 </html>
 
--- a/toolkit/components/maintenanceservice/Makefile.in
+++ b/toolkit/components/maintenanceservice/Makefile.in
@@ -59,17 +59,16 @@ DIST_PROGRAM = maintenanceservice$(BIN_S
 
 # Don't link the maintenanceservice against mozglue.dll. See bug 687139 and
 # bug 725876
 MOZ_GLUE_LDFLAGS =
 MOZ_GLUE_PROGRAM_LDFLAGS =
 
 LIBS += \
   ../../mozapps/update/common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
-  ../../mozapps/readstrings/$(LIB_PREFIX)readstrings.$(LIB_SUFFIX) \
   $(NULL)
 
 USE_STATIC_LIBS = 1
 HAVE_PROGRESSUI = 1
 RCINCLUDE = maintenanceservice.rc
 
 OS_LIBS += $(call EXPAND_LIBNAME,comctl32 ws2_32 shell32)
 DEFINES += -DUNICODE -D_UNICODE
@@ -93,10 +92,9 @@ DEFINES += -DNS_NO_XPCOM
 
 ifdef _MSC_VER
 WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup
 endif
 
 # Pick up nsWindowsRestart.cpp
 LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre \
   -I$(topsrcdir)/toolkit/mozapps/update/common \
-  -I$(topsrcdir)/toolkit/mozapps/readstrings \
   $(NULL)
--- a/toolkit/components/maintenanceservice/workmonitor.cpp
+++ b/toolkit/components/maintenanceservice/workmonitor.cpp
@@ -51,36 +51,27 @@
 #include "nsAutoPtr.h"
 
 #include "workmonitor.h"
 #include "serviceinstall.h"
 #include "servicebase.h"
 #include "registrycertificates.h"
 #include "uachelper.h"
 #include "updatehelper.h"
+#include "errors.h"
 
 // Wait 15 minutes for an update operation to run at most.
 // Updates usually take less than a minute so this seems like a 
 // significantly large and safe amount of time to wait.
 static const int TIME_TO_WAIT_ON_UPDATER = 15 * 60 * 1000;
 PRUnichar* MakeCommandLine(int argc, PRUnichar **argv);
 BOOL WriteStatusFailure(LPCWSTR updateDirPath, int errorCode);
 BOOL PathGetSiblingFilePath(LPWSTR destinationBuffer,  LPCWSTR siblingFilePath, 
                             LPCWSTR newFileName);
 
-// The error codes start from 16000 since Windows system error 
-// codes only go up to 15999
-const int SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
-const int SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
-const int SERVICE_UPDATER_SIGN_ERROR = 16002;
-const int SERVICE_UPDATER_COMPARE_ERROR = 16003;
-const int SERVICE_UPDATER_IDENTITY_ERROR = 16004;
-const int SERVICE_STILL_APPLYING_ON_SUCCESS = 16005;
-const int SERVICE_STILL_APPLYING_ON_FAILURE = 16006;
-
 /* 
  * Read the update.status file and sets isApplying to true if
  * the status is set to applying
  *
  * @param  updateDirPath The directory where update.status is stored
  * @param  isApplying Out parameter for specifying if the status
  *         is set to applying or not.
  * @return TRUE if the information was filled.
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -326,17 +326,17 @@ HISTOGRAM(PLACES_ANNOS_BOOKMARKS_COUNT, 
 HISTOGRAM(PLACES_ANNOS_BOOKMARKS_SIZE_KB, 10, 10000, 10, EXPONENTIAL, "PLACES: Size of bookmarks annotations (KB)")
 HISTOGRAM(PLACES_ANNOS_PAGES_COUNT, 50, 5000, 10, EXPONENTIAL, "PLACES: Number of pages annotations")
 HISTOGRAM(PLACES_ANNOS_PAGES_SIZE_KB, 10, 10000, 10, EXPONENTIAL, "PLACES: Size of pages annotations (KB)")
 HISTOGRAM(PLACES_FRECENCY_CALC_TIME_MS, 1, 100, 10, EXPONENTIAL, "PLACES: Time to calculate frecency of a page (ms)")
 
 /**
  * Updater telemetry.
  */
-HISTOGRAM(UPDATE_STATUS, 1, 16006, 27, LINEAR, "Updater: the status of the latest update performed")
+HISTOGRAM(UPDATER_STATUS_CODES, 1, 50, 51, LINEAR, "Updater: the status of the latest update performed")
 
 /**
  * Thunderbird-specific telemetry.
  */
 // Disable this application-specific #ifdef ftb. (See bug 710562)
 // #ifdef MOZ_THUNDERBIRD
 HISTOGRAM(THUNDERBIRD_GLODA_SIZE_MB, 1, 1000, 40, LINEAR, "Gloda: size of global-messages-db.sqlite (MB)")
 // THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS is used by Thunderbird Conversations add-on.
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_bug638292.js
@@ -8,19 +8,23 @@ function test() {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
     waitForFocus(page_loaded, gBrowser.contentWindow);
   }, true);
   gBrowser.loadURI(TESTROOT + "bug638292.html");
 }
 
 function check_load(aCallback) {
   gBrowser.addEventListener("load", function(aEvent) {
-    // SeaMonkey needs to deal with intermediate "about:blank" document(s).
-    if (!aEvent.target.location) {
-      info("Ignoring about:blank load. (Expected (a few times) on SeaMonkey only.)");
+    if (!gBrowser.browsers[2] ||
+        aEvent.target != gBrowser.browsers[2].contentDocument) {
+      // SeaMonkey tabbrowser needs to deal with additional loads.
+      if (navigator.userAgent.match(/ SeaMonkey\//))
+        info("Ignoring unrelated load on SeaMonkey. (Expected 2-3 times.)");
+      else
+        ok(false, "Ignoring unrelated load on Firefox. (Should never happen!)");
       return;
     }
 
     gBrowser.removeEventListener("load", arguments.callee, true);
 
     // Let the load handler complete
     executeSoon(function() {
       var doc = gBrowser.browsers[2].contentDocument;
deleted file mode 100644
--- a/toolkit/mozapps/readstrings/Makefile.in
+++ /dev/null
@@ -1,54 +0,0 @@
-# ***** 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 readstrings
-#
-# The Initial Developer of the Original Code is The Mozilla Foundation.
-#
-# Portions created by the Initial Developer are Copyright (C) 2009
-# the Mozilla Foundation <http://www.mozilla.org/>. All Rights Reserved.
-#
-# Contributor(s): 
-#   Brad Lassey <blassey@mozilla.com> (original author)
-#
-# 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 *****
-
-DEPTH     = ../../..
-topsrcdir = @top_srcdir@
-srcdir    = @srcdir@
-VPATH     = @srcdir@
-
-include $(DEPTH)/config/autoconf.mk
-
-MODULE  = readstrings
-LIBRARY_NAME = readstrings
-FORCE_STATIC_LIB = 1
-export NO_SHUNT = 1
-USE_STATIC_LIBS = 1
-
-CPPSRCS = readstrings.cpp
-EXPORTS = readstrings.h
-
-include $(topsrcdir)/config/rules.mk
--- a/toolkit/mozapps/update/Makefile.in
+++ b/toolkit/mozapps/update/Makefile.in
@@ -44,18 +44,16 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = update
 
 XPIDLSRCS = nsIUpdateTimerManager.idl
 
 EXTRA_PP_COMPONENTS = nsUpdateTimerManager.js nsUpdateTimerManager.manifest
 
 ifdef MOZ_UPDATER
-DIRS = ../readstrings
-
 ifneq ($(OS_TARGET),Android)
 DIRS += common
 DIRS += updater
 endif
 
 XPIDLSRCS += nsIUpdateService.idl
 
 EXTRA_PP_COMPONENTS += \
@@ -65,17 +63,16 @@ EXTRA_PP_COMPONENTS += \
   $(NULL)
 else
 
 # If only the maintenance service is installed and not
 # the updater, then the maintenance service may still be
 # used for other things.  We need to build update/common
 # which the maintenance service uses.
 ifdef MOZ_MAINTENANCE_SERVICE
-DIRS = ../readstrings
 ifneq ($(OS_TARGET),Android)
 DIRS += common
 endif
 endif
 endif
 
 TEST_DIRS += test_timermanager
 # Update tests require the updater binary
--- a/toolkit/mozapps/update/common/Makefile.in
+++ b/toolkit/mozapps/update/common/Makefile.in
@@ -47,20 +47,22 @@ LIBRARY_NAME = updatecommon
 FORCE_STATIC_LIB =1
 LIBXUL_LIBRARY = 1
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 endif
 
 CPPSRCS = \
   updatelogging.cpp \
+  readstrings.cpp \
   $(NULL)
 
 EXPORTS = updatelogging.h \
   updatedefines.h \
+  readstrings.h \
   $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 CPPSRCS += updatehelper.cpp \
   uachelper.cpp \
   pathhash.cpp \
   $(NULL)
 
rename from toolkit/mozapps/readstrings/errors.h
rename to toolkit/mozapps/update/common/errors.h
--- a/toolkit/mozapps/readstrings/errors.h
+++ b/toolkit/mozapps/update/common/errors.h
@@ -35,40 +35,57 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef Errors_h__
 #define Errors_h__
 
 #define OK 0
-//#define MEM_ERROR 1  // Replaced with errors 10-16 (inclusive)
+
+// Old unused error codes:
+// #define MEM_ERROR 1  // Replaced with errors 10-16 (inclusive)
 // #define IO_ERROR 2  // Use READ_ERROR or WRITE_ERROR instead
+
+// Error codes 3-16 are for general update problems.
 #define USAGE_ERROR 3
 #define CRC_ERROR 4
 #define PARSE_ERROR 5
 #define READ_ERROR 6
 #define WRITE_ERROR 7
 #define UNEXPECTED_ERROR 8
 #define ELEVATION_CANCELED 9
 #define READ_STRINGS_MEM_ERROR 10
 #define ARCHIVE_READER_MEM_ERROR 11
 #define BSPATCH_MEM_ERROR 12
 #define UPDATER_MEM_ERROR 13
 #define UPDATER_QUOTED_PATH_MEM_ERROR 14
 #define BAD_ACTION_ERROR 15
 #define STRING_CONVERSION_ERROR 16
+
+// Error codes 17-23 are related to security tasks for MAR
+// signing and MAR protection.
 #define CERT_LOAD_ERROR 17
 #define CERT_HANDLING_ERROR 18
 #define CERT_VERIFY_ERROR 19
 #define ARCHIVE_NOT_OPEN 20
 #define COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR 21
 #define MAR_CHANNEL_MISMATCH_ERROR 22
 #define VERSION_DOWNGRADE_ERROR 23
 
+// Error codes 24-30 are related to the maintenance service
+// and so are Windows only
+#define SERVICE_UPDATER_COULD_NOT_BE_STARTED 24
+#define SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS 25
+#define SERVICE_UPDATER_SIGN_ERROR 26
+#define SERVICE_UPDATER_COMPARE_ERROR 27
+#define SERVICE_UPDATER_IDENTITY_ERROR 28
+#define SERVICE_STILL_APPLYING_ON_SUCCESS 29
+#define SERVICE_STILL_APPLYING_ON_FAILURE 30
+
 // The following error codes are only used by updater.exe
 // when a fallback key exists and XPCShell tests are being run.
 #define FALLBACKKEY_UNKNOWN_ERROR 100
 #define FALLBACKKEY_REGPATH_ERROR 101
 #define FALLBACKKEY_NOKEY_ERROR 102
 #define FALLBACKKEY_SERVICE_NO_STOP_ERROR 103
 #define FALLBACKKEY_LAUNCH_ERROR 104
 
rename from toolkit/mozapps/readstrings/readstrings.cpp
rename to toolkit/mozapps/update/common/readstrings.cpp
rename from toolkit/mozapps/readstrings/readstrings.h
rename to toolkit/mozapps/update/common/readstrings.h
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -138,32 +138,23 @@ const STATE_DOWNLOAD_FAILED = "download-
 const STATE_FAILED          = "failed";
 
 // From updater/errors.h:
 const WRITE_ERROR        = 7;
 const UNEXPECTED_ERROR   = 8;
 const ELEVATION_CANCELED = 9;
 
 // Windows service specific errors
-const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 16000;
-const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 16001;
-const SERVICE_UPDATER_SIGN_ERROR           = 16002;
-const SERVICE_UPDATER_COMPARE_ERROR        = 16003;
-const SERVICE_UPDATER_IDENTITY_ERROR       = 16004;
-const SERVICE_STILL_APPLYING_ON_SUCCESS    = 16005;
-const SERVICE_STILL_APPLYING_ON_FAILURE    = 16006;
-
-// Updater MAR security errors
-const CERT_LOAD_ERROR                         = 17;
-const CERT_HANDLING_ERROR                     = 18;
-const CERT_VERIFY_ERROR                       = 19;
-const ARCHIVE_NOT_OPEN                        = 20;
-const COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR = 21;
-const MAR_CHANNEL_MISMATCH_ERROR              = 22;
-const VERSION_DOWNGRADE_ERROR                 = 23;
+const SERVICE_UPDATER_COULD_NOT_BE_STARTED = 24;
+const SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS = 25;
+const SERVICE_UPDATER_SIGN_ERROR           = 26;
+const SERVICE_UPDATER_COMPARE_ERROR        = 27;
+const SERVICE_UPDATER_IDENTITY_ERROR       = 28;
+const SERVICE_STILL_APPLYING_ON_SUCCESS    = 29;
+const SERVICE_STILL_APPLYING_ON_FAILURE    = 30;
 
 const CERT_ATTR_CHECK_FAILED_NO_UPDATE  = 100;
 const CERT_ATTR_CHECK_FAILED_HAS_UPDATE = 101;
 const BACKGROUNDCHECK_MULTIPLE_FAILURES = 110;
 
 const DOWNLOAD_CHUNK_SIZE           = 300000; // bytes
 const DOWNLOAD_BACKGROUND_INTERVAL  = 600;    // seconds
 const DOWNLOAD_FOREGROUND_INTERVAL  = 0;
@@ -1419,24 +1410,17 @@ UpdateService.prototype = {
         }
 
         if (update.errorCode == SERVICE_UPDATER_COULD_NOT_BE_STARTED ||
             update.errorCode == SERVICE_NOT_ENOUGH_COMMAND_LINE_ARGS ||
             update.errorCode == SERVICE_UPDATER_SIGN_ERROR ||
             update.errorCode == SERVICE_UPDATER_COMPARE_ERROR ||
             update.errorCode == SERVICE_UPDATER_IDENTITY_ERROR ||
             update.errorCode == SERVICE_STILL_APPLYING_ON_SUCCESS ||
-            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE ||
-            update.errorCode == CERT_LOAD_ERROR ||
-            update.errorCode == CERT_HANDLING_ERROR ||
-            update.errorCode == CERT_VERIFY_ERROR ||
-            update.errorCode == ARCHIVE_NOT_OPEN ||
-            update.errorCode == COULD_NOT_READ_PRODUCT_INFO_BLOCK_ERROR ||
-            update.errorCode == MAR_CHANNEL_MISMATCH_ERROR ||
-            update.errorCode == VERSION_DOWNGRADE_ERROR) {
+            update.errorCode == SERVICE_STILL_APPLYING_ON_FAILURE) {
           var failCount = getPref("getIntPref", 
                                   PREF_APP_UPDATE_SERVICE_ERRORS, 0);
           var maxFail = getPref("getIntPref", 
                                 PREF_APP_UPDATE_SERVICE_MAX_ERRORS, 
                                 DEFAULT_SERVICE_MAX_ERRORS);
 
           // As a safety, when the service reaches maximum failures, it will
           // disable itself and fallback to using the normal update mechanism
--- a/toolkit/mozapps/update/test/Makefile.in
+++ b/toolkit/mozapps/update/test/Makefile.in
@@ -79,17 +79,17 @@ LOCAL_INCLUDES += \
   -I$(srcdir) \
   -I$(topsrcdir)/toolkit/mozapps/update \
   -I$(topsrcdir)/toolkit/mozapps/update/common \
   $(NULL)
 
 MOZ_WINCONSOLE = 1
 
 LIBS += \
-  ../../readstrings/$(LIB_PREFIX)readstrings.$(LIB_SUFFIX) \
+  ../common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \
   $(NULL)
 
 ifeq ($(OS_ARCH),WINNT)
 USE_STATIC_LIBS = 1
 DEFINES += -DUNICODE -D_UNICODE
 endif
 endif # Not Android
 
--- a/toolkit/mozapps/update/test/TestAUSHelper.cpp
+++ b/toolkit/mozapps/update/test/TestAUSHelper.cpp
@@ -26,16 +26,19 @@
   }
 # define NS_taccess _waccess
 # define NS_tchdir _wchdir
 # define NS_tfopen _wfopen
 # define NS_tstrcmp wcscmp
 # define NS_ttoi _wtoi
 # define NS_tstat _wstat
 # define LOG_S "%S"
+
+#include "../common/updatehelper.h"
+
 #else
 # include <unistd.h>
 # define NS_main main
   typedef char NS_tchar;
 # define NS_T(str) str
 # define NS_tsnprintf snprintf
 # define NS_taccess access
 # define NS_tchdir chdir
@@ -148,224 +151,16 @@ VerifyCertificateTrustForFile(LPCWSTR fi
   trustData.dwUIContext = 0;
   trustData.pFile = &fileToCheck;
 
   GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
   // Check if the file is signed by something that is trusted.
   return WinVerifyTrust(NULL, &policyGUID, &trustData);
 }
 
-/**
- * Waits for a service to enter a stopped state.
- * This function does not stop the service, it just blocks until the service
- * is stopped.
- *
- * @param  serviceName     The service to wait for.