Bug 826009 - Move locale callback info, default locale, etc. data and APIs to be JSRuntime-centered. f=bholley, r=jorendorff
authorJeff Walden <jwalden@mit.edu>
Thu, 07 Feb 2013 18:04:11 -0800
changeset 121570 bb8d5da0bf70c3d8647d308359a5d996409eface
parent 121569 b8c19a26a1d2a5410698b2a2cabbab93ffe6e7e2
child 121571 66b99572ab2665a8afca73b988fc083a755f12cc
push id24296
push useremorley@mozilla.com
push dateTue, 12 Feb 2013 14:43:19 +0000
treeherdermozilla-central@860d7a47b675 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs826009
milestone21.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
Bug 826009 - Move locale callback info, default locale, etc. data and APIs to be JSRuntime-centered. f=bholley, r=jorendorff
content/base/src/nsFrameMessageManager.cpp
dom/base/nsJSEnvironment.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/js.msg
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdate.cpp
js/src/jsexn.cpp
js/src/jsnum.cpp
js/src/jsstr.cpp
js/src/vm/SelfHosting.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCLocale.cpp
js/xpconnect/src/xpcpublic.h
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -1128,18 +1128,16 @@ nsFrameScriptExecutor::InitTabChildGloba
   mCx = cx;
 
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
-  xpc_LocalizeContext(cx);
-
   JSAutoRequest ar(cx);
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const uint32_t flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES;
 
   
   JS_SetContextPrivate(cx, aScope);
 
   nsresult rv =
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1109,18 +1109,16 @@ nsJSContext::nsJSContext(JSRuntime *aRun
     // Make sure the new context gets the default context options
     ::JS_SetOptions(mContext, mDefaultJSOptions);
 
     // Watch for the JS boolean options
     Preferences::RegisterCallback(JSOptionChangedCallback,
                                   js_options_dot_str, this);
 
     ::JS_SetOperationCallback(mContext, DOMOperationCallback);
-
-    xpc_LocalizeContext(mContext);
   }
   mIsInitialized = false;
   mTerminations = nullptr;
   mScriptsEnabled = true;
   mOperationCallbackTime = 0;
   mModalStateTime = 0;
   mModalStateDepth = 0;
   mProcessingScriptTag = false;
--- a/ipc/testshell/XPCShellEnvironment.cpp
+++ b/ipc/testshell/XPCShellEnvironment.cpp
@@ -999,18 +999,16 @@ XPCShellEnvironment::Init()
 
     nsCOMPtr<nsIXPConnect> xpc =
       do_GetService(nsIXPConnect::GetCID());
     if (!xpc) {
         NS_ERROR("failed to get nsXPConnect service!");
         return false;
     }
 
-    xpc_LocalizeContext(cx);
-
     nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan());
     xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF);
 
     nsCOMPtr<nsIPrincipal> principal;
 
     nsCOMPtr<nsIScriptSecurityManager> securityManager =
         do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     if (NS_SUCCEEDED(rv) && securityManager) {
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -243,17 +243,17 @@ MSG_DEF(JSMSG_UNUSED189,              18
 MSG_DEF(JSMSG_UNUSED190,              190, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED191,              191, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED192,              192, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,      193, 0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_UNUSED194,              194, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED195,              195, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED196,              196, 0, JSEXN_NONE, "")
 MSG_DEF(JSMSG_UNUSED197,              197, 0, JSEXN_NONE, "")
-MSG_DEF(JSMSG_UNUSED198,              198, 0, JSEXN_NONE, "")
+MSG_DEF(JSMSG_DEFAULT_LOCALE_ERROR,   198, 0, JSEXN_ERR, "internal error getting the default locale")
 MSG_DEF(JSMSG_TOO_MANY_LOCALS,        199, 0, JSEXN_SYNTAXERR, "too many local variables")
 MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG,     200, 0, JSEXN_INTERNALERR, "array initialiser too large")
 MSG_DEF(JSMSG_REGEXP_TOO_COMPLEX,     201, 0, JSEXN_INTERNALERR, "regular expression too complex")
 MSG_DEF(JSMSG_BUFFER_TOO_SMALL,       202, 0, JSEXN_INTERNALERR, "buffer too small")
 MSG_DEF(JSMSG_BAD_SURROGATE_CHAR,     203, 1, JSEXN_TYPEERR, "bad surrogate character {0}")
 MSG_DEF(JSMSG_UTF8_CHAR_TOO_LARGE,    204, 1, JSEXN_TYPEERR, "UTF-8 character {0} too large")
 MSG_DEF(JSMSG_MALFORMED_UTF8_CHAR,    205, 1, JSEXN_TYPEERR, "malformed UTF-8 character sequence at offset {0}")
 MSG_DEF(JSMSG_USER_DEFINED_ERROR,     206, 0, JSEXN_ERR, "JS_ReportError was called")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -718,16 +718,18 @@ js::PerThreadData::PerThreadData(JSRunti
     ionStackLimit(0),
     ionActivation(NULL),
     suppressGC(0)
 {}
 
 JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
   : mainThread(this),
     atomsCompartment(NULL),
+    localeCallbacks(NULL),
+    defaultLocale(NULL),
 #ifdef JS_THREADSAFE
     ownerThread_(NULL),
 #endif
     tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     freeLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
     execAlloc_(NULL),
     bumpAlloc_(NULL),
 #ifdef JS_METHODJIT
@@ -1130,16 +1132,17 @@ JS_NewRuntime(uint32_t maxbytes, JSUseHe
     Probes::createRuntime(rt);
     return rt;
 }
 
 JS_PUBLIC_API(void)
 JS_DestroyRuntime(JSRuntime *rt)
 {
     Probes::destroyRuntime(rt);
+    js_free(rt->defaultLocale);
     js_delete(rt);
 }
 
 JS_PUBLIC_API(void)
 JS_ShutDown(void)
 {
     Probes::shutdown();
     PRMJ_NowShutdown();
@@ -2272,16 +2275,27 @@ JS_strdup(JSContext *cx, const char *s)
     AssertHeapIsIdle(cx);
     size_t n = strlen(s) + 1;
     void *p = cx->malloc_(n);
     if (!p)
         return NULL;
     return (char *)js_memcpy(p, s, n);
 }
 
+JS_PUBLIC_API(char *)
+JS_strdup(JSRuntime *rt, const char *s)
+{
+    AssertHeapIsIdle(rt);
+    size_t n = strlen(s) + 1;
+    void *p = rt->malloc_(n);
+    if (!p)
+        return NULL;
+    return static_cast<char*>(js_memcpy(p, s, n));
+}
+
 #undef JS_AddRoot
 
 JS_PUBLIC_API(JSBool)
 JS_AddValueRoot(JSContext *cx, jsval *vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return AddValueRoot(cx, vp, NULL);
@@ -6749,41 +6763,41 @@ JS_GetRegExpSource(JSContext *cx, JSObje
     CHECK_REQUEST(cx);
 
     return obj->asRegExp().getSource();
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSBool)
-JS_SetDefaultLocale(JSContext *cx, const char *locale)
-{
-    AssertHeapIsIdle(cx);
-    return cx->setDefaultLocale(locale);
+JS_SetDefaultLocale(JSRuntime *rt, const char *locale)
+{
+    AssertHeapIsIdle(rt);
+    return rt->setDefaultLocale(locale);
 }
 
 JS_PUBLIC_API(void)
-JS_ResetDefaultLocale(JSContext *cx)
-{
-    AssertHeapIsIdle(cx);
-    cx->resetDefaultLocale();
+JS_ResetDefaultLocale(JSRuntime *rt)
+{
+    AssertHeapIsIdle(rt);
+    rt->resetDefaultLocale();
 }
 
 JS_PUBLIC_API(void)
-JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
-{
-    AssertHeapIsIdle(cx);
-    cx->localeCallbacks = callbacks;
+JS_SetLocaleCallbacks(JSRuntime *rt, JSLocaleCallbacks *callbacks)
+{
+    AssertHeapIsIdle(rt);
+    rt->localeCallbacks = callbacks;
 }
 
 JS_PUBLIC_API(JSLocaleCallbacks *)
-JS_GetLocaleCallbacks(JSContext *cx)
+JS_GetLocaleCallbacks(JSRuntime *rt)
 {
     /* This function can be called by a finalizer. */
-    return cx->localeCallbacks;
+    return rt->localeCallbacks;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(JSBool)
 JS_IsExceptionPending(JSContext *cx)
 {
     /* This function can be called by a finalizer. */
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2493,16 +2493,20 @@ extern JS_PUBLIC_API(JSFreeOp *)
 JS_GetDefaultFreeOp(JSRuntime *rt);
 
 extern JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s);
 
+/* Duplicate a string.  Does not report an error on failure. */
+extern JS_PUBLIC_API(char *)
+JS_strdup(JSRuntime *rt, const char *s);
+
 
 /*
  * A GC root is a pointer to a jsval, JSObject * or JSString * that itself
  * points into the GC heap. JS_AddValueRoot takes a pointer to a jsval and
  * JS_AddGCThingRoot takes a pointer to a JSObject * or JString *.
  *
  * Note that, since JS_Add*Root stores the address of a variable (of type
  * jsval, JSString *, or JSObject *), that variable must live until
@@ -4670,48 +4674,48 @@ JS_WriteTypedArray(JSStructuredCloneWrit
 /*
  * The default locale for the ECMAScript Internationalization API
  * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
  * Note that the Internationalization API encourages clients to
  * specify their own locales.
  * The locale string remains owned by the caller.
  */
 extern JS_PUBLIC_API(JSBool)
-JS_SetDefaultLocale(JSContext *cx, const char *locale);
+JS_SetDefaultLocale(JSRuntime *rt, const char *locale);
 
 /*
  * Reset the default locale to OS defaults.
  */
 extern JS_PUBLIC_API(void)
-JS_ResetDefaultLocale(JSContext *cx);
+JS_ResetDefaultLocale(JSRuntime *rt);
 
 /*
  * Locale specific string conversion and error message callbacks.
  */
 struct JSLocaleCallbacks {
     JSLocaleToUpperCase     localeToUpperCase;
     JSLocaleToLowerCase     localeToLowerCase;
     JSLocaleCompare         localeCompare;
     JSLocaleToUnicode       localeToUnicode;
     JSErrorCallback         localeGetErrorMessage;
 };
 
 /*
  * Establish locale callbacks. The pointer must persist as long as the
- * JSContext.  Passing NULL restores the default behaviour.
+ * JSRuntime.  Passing NULL restores the default behaviour.
  */
 extern JS_PUBLIC_API(void)
-JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks);
+JS_SetLocaleCallbacks(JSRuntime *rt, JSLocaleCallbacks *callbacks);
 
 /*
  * Return the address of the current locale callbacks struct, which may
  * be NULL.
  */
 extern JS_PUBLIC_API(JSLocaleCallbacks *)
-JS_GetLocaleCallbacks(JSContext *cx);
+JS_GetLocaleCallbacks(JSRuntime *rt);
 
 /************************************************************************/
 
 /*
  * Error reporting.
  */
 
 /*
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1164,19 +1164,17 @@ js_GetCurrentBytecodePC(JSContext* cx)
 
 JSContext::JSContext(JSRuntime *rt)
   : ContextFriendFields(rt),
     defaultVersion(JSVERSION_DEFAULT),
     hasVersionOverride(false),
     throwing(false),
     exception(UndefinedValue()),
     options_(0),
-    defaultLocale(NULL),
     reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
-    localeCallbacks(NULL),
     resolvingList(NULL),
     generatingError(false),
     enterCompartmentDepth_(0),
     savedFrameChains_(),
     defaultCompartmentObject_(NULL),
     stack(thisDuringConstruction()),
     parseMapPool_(NULL),
     cycleDetectorSet(thisDuringConstruction()),
@@ -1210,55 +1208,54 @@ JSContext::JSContext(JSRuntime *rt)
     skipGCRooters = NULL;
 #endif
 #endif
 }
 
 JSContext::~JSContext()
 {
     /* Free the stuff hanging off of cx. */
-    js_free(defaultLocale);
     if (parseMapPool_)
         js_delete(parseMapPool_);
 
     JS_ASSERT(!resolvingList);
 }
 
 bool
-JSContext::setDefaultLocale(const char *locale)
+JSRuntime::setDefaultLocale(const char *locale)
 {
     if (!locale)
         return false;
     resetDefaultLocale();
     defaultLocale = JS_strdup(this, locale);
     return defaultLocale != NULL;
 }
 
 void
-JSContext::resetDefaultLocale()
+JSRuntime::resetDefaultLocale()
 {
     js_free(defaultLocale);
     defaultLocale = NULL;
 }
 
 const char *
-JSContext::getDefaultLocale()
+JSRuntime::getDefaultLocale()
 {
     if (defaultLocale)
         return defaultLocale;
 
     char *locale, *lang, *p;
 #ifdef HAVE_SETLOCALE
     locale = setlocale(LC_ALL, NULL);
 #else
     locale = getenv("LANG");
 #endif
     // convert to a well-formed BCP 47 language tag
     if (!locale || !strcmp(locale, "C"))
-        locale = (char *) "und";
+        locale = const_cast<char*>("und");
     lang = JS_strdup(this, locale);
     if (!lang)
         return NULL;
     if ((p = strchr(lang, '.')))
         *p = '\0';
     while ((p = strchr(lang, '_')))
         *p = '-';
 
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -588,16 +588,22 @@ struct JSRuntime : js::RuntimeFriendFiel
     js::PerThreadData   mainThread;
 
     /* Default compartment. */
     JSCompartment       *atomsCompartment;
 
     /* List of compartments (protected by the GC lock). */
     js::CompartmentVector compartments;
 
+    /* Locale-specific callbacks for string conversion. */
+    JSLocaleCallbacks *localeCallbacks;
+
+    /* Default locale for Internationalization API */
+    char *defaultLocale;
+
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
 #ifdef JS_THREADSAFE
   public:
     void *ownerThread() const { return ownerThread_; }
     void clearOwnerThread();
     void setOwnerThread();
     JS_FRIEND_API(void) abortIfWrongThread() const;
 #ifdef DEBUG
@@ -671,26 +677,49 @@ struct JSRuntime : js::RuntimeFriendFiel
         JS_ASSERT(hasJaegerRuntime());
         return *jaegerRuntime_;
     }
 #endif
     js::ion::IonRuntime *getIonRuntime(JSContext *cx) {
         return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx);
     }
 
+    //-------------------------------------------------------------------------
+    // Self-hosting support
+    //-------------------------------------------------------------------------
+
     bool initSelfHosting(JSContext *cx);
     void markSelfHostingGlobal(JSTracer *trc);
     bool isSelfHostingGlobal(js::HandleObject global) {
         return global == selfHostingGlobal_;
     }
     bool cloneSelfHostedFunctionScript(JSContext *cx, js::Handle<js::PropertyName*> name,
                                        js::Handle<JSFunction*> targetFun);
     bool cloneSelfHostedValue(JSContext *cx, js::Handle<js::PropertyName*> name,
                               js::MutableHandleValue vp);
 
+    //-------------------------------------------------------------------------
+    // Locale information
+    //-------------------------------------------------------------------------
+
+    /*
+     * Set the default locale for the ECMAScript Internationalization API
+     * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
+     * Note that the Internationalization API encourages clients to
+     * specify their own locales.
+     * The locale string remains owned by the caller.
+     */
+    bool setDefaultLocale(const char *locale);
+
+    /* Reset the default locale to OS defaults. */
+    void resetDefaultLocale();
+
+    /* Gets current default locale. String remains owned by context. */
+    const char *getDefaultLocale();
+
     /* Base address of the native stack for the current thread. */
     uintptr_t           nativeStackBase;
 
     /* The native stack size limit that runtime should not exceed. */
     size_t              nativeStackQuota;
 
     /*
      * Frames currently running in js::Interpret. See InterpreterFrames for
@@ -1364,40 +1393,19 @@ struct JSContext : js::ContextFriendFiel
 
     /* Exception state -- the exception member is a GC root by definition. */
     bool                throwing;            /* is there a pending exception? */
     js::Value           exception;           /* most-recently-thrown exception */
 
     /* Per-context options. */
     unsigned            options_;            /* see jsapi.h for JSOPTION_* */
 
-    /* Default locale for Internationalization API */
-    char                *defaultLocale;
-
   public:
     int32_t             reportGranularity;  /* see jsprobes.h */
 
-    /*
-     * Set the default locale for the ECMAScript Internationalization API
-     * (Intl.Collator, Intl.NumberFormat, Intl.DateTimeFormat).
-     * Note that the Internationalization API encourages clients to
-     * specify their own locales.
-     * The locale string remains owned by the caller.
-     */
-    bool setDefaultLocale(const char *locale);
-
-    /* Reset the default locale to OS defaults. */
-    void resetDefaultLocale();
-
-    /* Gets current default locale. String remains owned by context. */
-    const char *getDefaultLocale();
-
-    /* Locale specific callbacks for string conversion. */
-    JSLocaleCallbacks   *localeCallbacks;
-
     js::AutoResolving   *resolvingList;
 
     /* True if generating an error, to prevent runaway recursion. */
     bool                generatingError;
 
     inline void setCompartment(JSCompartment *c) { compartment = c; }
 
     /*
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2756,18 +2756,18 @@ ToLocaleHelper(JSContext *cx, HandleObje
             !(isdigit(buf[0]) && isdigit(buf[1]) &&
               isdigit(buf[2]) && isdigit(buf[3]))) {
             JS_snprintf(buf + (result_len - 2), (sizeof buf) - (result_len - 2),
                         "%d", js_DateGetYear(cx, obj));
         }
 
     }
 
-    if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode)
-        return cx->localeCallbacks->localeToUnicode(cx, buf, rval.address());
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeToUnicode)
+        return cx->runtime->localeCallbacks->localeToUnicode(cx, buf, rval.address());
 
     UnrootedString str = JS_NewStringCopyZ(cx, buf);
     if (!str)
         return false;
     rval.setString(str);
     return true;
 }
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -861,19 +861,19 @@ js_InitExceptionClasses(JSContext *cx, H
 }
 
 const JSErrorFormatString*
 js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
                             const unsigned errorNumber)
 {
     const JSErrorFormatString *errorString = NULL;
 
-    if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
-        errorString = cx->localeCallbacks
-                        ->localeGetErrorMessage(userRef, locale, errorNumber);
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeGetErrorMessage) {
+        errorString = cx->runtime->localeCallbacks
+                                 ->localeGetErrorMessage(userRef, locale, errorNumber);
     }
     if (!errorString)
         errorString = js_GetErrorMessage(userRef, locale, errorNumber);
     return errorString;
 }
 
 JS_FRIEND_API(const jschar*)
 js::GetErrorTypeName(JSContext* cx, int16_t exnType)
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -715,19 +715,19 @@ num_toLocaleString_impl(JSContext *cx, C
         tmpDest += decimalLength;
         JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
         strcpy(tmpDest, nint + 1);
     } else {
         JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
         strcpy(tmpDest, nint);
     }
 
-    if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) {
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeToUnicode) {
         Rooted<Value> v(cx, StringValue(str));
-        bool ok = !!cx->localeCallbacks->localeToUnicode(cx, buf, v.address());
+        bool ok = !!cx->runtime->localeCallbacks->localeToUnicode(cx, buf, v.address());
         if (ok)
             args.rval().set(v);
         js_free(buf);
         return ok;
     }
 
     str = js_NewStringCopyN<CanGC>(cx, buf, buflen);
     js_free(buf);
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -674,23 +674,23 @@ static JSBool
 str_toLocaleLowerCase(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /*
      * Forcefully ignore the first (or any) argument and return toLowerCase(),
      * ECMA has reserved that argument, presumably for defining the locale.
      */
-    if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeToLowerCase) {
         RootedString str(cx, ThisToStringForStringProto(cx, args));
         if (!str)
             return false;
 
         Value result;
-        if (!cx->localeCallbacks->localeToLowerCase(cx, str, &result))
+        if (!cx->runtime->localeCallbacks->localeToLowerCase(cx, str, &result))
             return false;
 
         args.rval().set(result);
         return true;
     }
 
     return ToLowerCaseHelper(cx, args);
 }
@@ -741,23 +741,23 @@ static JSBool
 str_toLocaleUpperCase(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /*
      * Forcefully ignore the first (or any) argument and return toUpperCase(),
      * ECMA has reserved that argument, presumably for defining the locale.
      */
-    if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeToUpperCase) {
         RootedString str(cx, ThisToStringForStringProto(cx, args));
         if (!str)
             return false;
 
         Value result;
-        if (!cx->localeCallbacks->localeToUpperCase(cx, str, &result))
+        if (!cx->runtime->localeCallbacks->localeToUpperCase(cx, str, &result))
             return false;
 
         args.rval().set(result);
         return true;
     }
 
     return ToUpperCaseHelper(cx, args);
 }
@@ -770,21 +770,21 @@ str_localeCompare(JSContext *cx, unsigne
     if (!str)
         return false;
 
     Value thatValue = args.length() > 0 ? args[0] : UndefinedValue();
     RootedString thatStr(cx, ToString<CanGC>(cx, thatValue));
     if (!thatStr)
         return false;
 
-    if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
+    if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeCompare) {
         args[0].setString(thatStr);
 
         Value result;
-        if (!cx->localeCallbacks->localeCompare(cx, str, thatStr, &result))
+        if (!cx->runtime->localeCallbacks->localeCompare(cx, str, thatStr, &result))
             return false;
 
         args.rval().set(result);
         return true;
     }
 
     int32_t result;
     if (!CompareStrings(cx, str, thatStr, &result))
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -279,19 +279,21 @@ js::intrinsic_UnsafeSetElement(JSContext
  * Returns the default locale as a well-formed, but not necessarily canonicalized,
  * BCP-47 language tag.
  */
 static JSBool
 intrinsic_RuntimeDefaultLocale(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    const char *locale = cx->getDefaultLocale();
-    if (!locale)
+    const char *locale = cx->runtime->getDefaultLocale();
+    if (!locale) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEFAULT_LOCALE_ERROR);
         return false;
+    }
 
     RootedString jslocale(cx, JS_NewStringCopyZ(cx, locale));
     if (!jslocale)
         return false;
 
     args.rval().setString(jslocale);
     return true;
 }
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -490,19 +490,16 @@ mozJSComponentLoader::ReallyInit()
 
     nsCOMPtr<nsIObserverService> obsSvc =
         do_GetService(kObserverServiceContractID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = obsSvc->AddObserver(this, "xpcom-shutdown-loaders", false);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // Set up localized comparison and string conversion
-    xpc_LocalizeContext(mContext);
-
 #ifdef DEBUG_shaver_off
     fprintf(stderr, "mJCL: ReallyInit success!\n");
 #endif
     mInitialized = true;
 
     return NS_OK;
 }
 
--- a/js/xpconnect/shell/xpcshell.cpp
+++ b/js/xpconnect/shell/xpcshell.cpp
@@ -1820,18 +1820,16 @@ main(int argc, char **argv, char **envp)
             printf("JS_NewContext failed!\n");
             return 1;
         }
 
         argc--;
         argv++;
         ProcessArgsForCompartment(cx, argv, argc);
 
-        xpc_LocalizeContext(cx);
-
         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
         if (!xpc) {
             printf("failed to get nsXPConnect service!\n");
             return 1;
         }
 
         // Since the caps security system might set a default security manager
         // we will be sure that the secman on this context gives full trust.
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1219,16 +1219,18 @@ XPCJSRuntime::GetJSCycleCollectionContex
 }
 
 XPCJSRuntime::~XPCJSRuntime()
 {
     MOZ_ASSERT(!mReleaseRunnable);
 
     js::SetGCSliceCallback(mJSRuntime, mPrevGCSliceCallback);
 
+    xpc_DelocalizeRuntime(mJSRuntime);
+
     if (mWatchdogWakeup) {
         // If the watchdog thread is running, tell it to terminate waking it
         // up if necessary and wait until it signals that it finished. As we
         // must release the lock before calling PR_DestroyCondVar, we use an
         // extra block here.
         {
             AutoLockWatchdog lock(this);
             if (mWatchdogThread) {
@@ -2593,16 +2595,22 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
     // function compiled with LAZY_SOURCE, it calls SourceHook to load it.
     ///
     // Note we do have to retain the source code in memory for scripts compiled in
     // compileAndGo mode and compiled function bodies (from
     // JS_CompileFunction*). In practice, this means content scripts and event
     // handlers.
     JS_SetSourceHook(mJSRuntime, SourceHook);
 
+    // Set up locale information and callbacks for the newly-created runtime so
+    // that the various toLocaleString() methods, localeCompare(), and other
+    // internationalization APIs work as desired.
+    if (!xpc_LocalizeRuntime(mJSRuntime))
+        NS_RUNTIMEABORT("xpc_LocalizeRuntime failed.");
+
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSSystemCompartmentCount));
     NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSUserCompartmentCount));
     NS_RegisterMemoryMultiReporter(new JSCompartmentsMultiReporter);
 
     mJSHolders.Init(512);
 
     // Install a JavaScript 'debugger' keyword handler in debug builds only
--- a/js/xpconnect/src/XPCLocale.cpp
+++ b/js/xpconnect/src/XPCLocale.cpp
@@ -1,68 +1,107 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: sw=2 ts=8 et :
  */
 /* 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 "prinit.h"
+#include "mozilla/Assertions.h"
+
 #include "plstr.h"
 
 #include "jsapi.h"
 
 #include "nsCollationCID.h"
 #include "nsJSUtils.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIPlatformCharset.h"
 #include "nsILocaleService.h"
 #include "nsICollation.h"
 #include "nsIServiceManager.h"
 #include "nsUnicharUtils.h"
 
 #include "xpcpublic.h"
 
 /**
- * JS locale callbacks implemented by XPCOM modules.  This
- * implementation is "safe" up to the following restrictions
- *
- * - All JSContexts for which xpc_LocalizeContext() is called belong
- *   to the same JSRuntime
- *
- * - Each JSContext is destroyed on the thread on which its locale
- *   functions are called.
- *
- * Unfortunately, the intl code underlying these XPCOM modules doesn't
- * yet support this model, so in practice XPCLocaleCallbacks are
- * limited to the main thread.
+ * JS locale callbacks implemented by XPCOM modules.  These are theoretically
+ * safe for use on multiple threads.  Unfortunately, the intl code underlying
+ * these XPCOM modules doesn't yet support this, so in practice
+ * XPCLocaleCallbacks are limited to the main thread.
  */
 struct XPCLocaleCallbacks : public JSLocaleCallbacks
 {
+  XPCLocaleCallbacks()
+#ifdef DEBUG
+    : mThread(PR_GetCurrentThread())
+#endif
+  {
+    MOZ_COUNT_CTOR(XPCLocaleCallbacks);
+
+    localeToUpperCase = LocaleToUpperCase;
+    localeToLowerCase = LocaleToLowerCase;
+    localeCompare = LocaleCompare;
+    localeToUnicode = LocaleToUnicode;
+    localeGetErrorMessage = nullptr;
+  }
+
+  ~XPCLocaleCallbacks()
+  {
+    AssertThreadSafety();
+    MOZ_COUNT_DTOR(XPCLocaleCallbacks);
+  }
+
   /**
-   * Return the XPCLocaleCallbacks that's hidden away in |cx|, or null
-   * if there isn't one. (This impl uses the locale callbacks struct
-   * to store away its per-context data.)
-   *
-   * NB: If the returned XPCLocaleCallbacks hasn't yet been bound to a
-   * thread, then a side effect of calling MaybeThis() is to bind it
-   * to the calling thread.
+   * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses
+   * the locale callbacks struct to store away its per-runtime data.)
    */
   static XPCLocaleCallbacks*
-  MaybeThis(JSContext* cx)
+  This(JSRuntime *rt)
   {
-    JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
-    return (lc &&
-            lc->localeToUpperCase == LocaleToUpperCase &&
-            lc->localeToLowerCase == LocaleToLowerCase &&
-            lc->localeCompare == LocaleCompare &&
-            lc->localeToUnicode == LocaleToUnicode) ? This(cx) : nullptr;
+    // Locale information for |rt| was associated using xpc_LocalizeRuntime;
+    // assert and double-check this.
+    JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt);
+    MOZ_ASSERT(lc);
+    MOZ_ASSERT(lc->localeToUpperCase == LocaleToUpperCase);
+    MOZ_ASSERT(lc->localeToLowerCase == LocaleToLowerCase);
+    MOZ_ASSERT(lc->localeCompare == LocaleCompare);
+    MOZ_ASSERT(lc->localeToUnicode == LocaleToUnicode);
+
+    XPCLocaleCallbacks* ths = static_cast<XPCLocaleCallbacks*>(lc);
+    ths->AssertThreadSafety();
+    return ths;
   }
 
   static JSBool
+  LocaleToUpperCase(JSContext *cx, JSString *src, jsval *rval)
+  {
+    return ChangeCase(cx, src, rval, ToUpperCase);
+  }
+
+  static JSBool
+  LocaleToLowerCase(JSContext *cx, JSString *src, jsval *rval)
+  {
+    return ChangeCase(cx, src, rval, ToLowerCase);
+  }
+
+  static JSBool
+  LocaleToUnicode(JSContext* cx, const char* src, jsval* rval)
+  {
+    return This(JS_GetRuntime(cx))->ToUnicode(cx, src, rval);
+  }
+
+  static JSBool
+  LocaleCompare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval)
+  {
+    return This(JS_GetRuntime(cx))->Compare(cx, src1, src2, rval);
+  }
+
+private:
+  static JSBool
   ChangeCase(JSContext* cx, JSString* src, jsval* rval,
              void(*changeCaseFnc)(const nsAString&, nsAString&))
   {
     nsDependentJSString depStr;
     if (!depStr.init(cx, src)) {
       return false;
     }
 
@@ -71,80 +110,64 @@ struct XPCLocaleCallbacks : public JSLoc
 
     JSString *ucstr =
       JS_NewUCStringCopyN(cx, (jschar*)result.get(), result.Length());
     if (!ucstr) {
       return false;
     }
 
     *rval = STRING_TO_JSVAL(ucstr);
-
     return true;
   }
 
-  static JSBool
-  LocaleToUpperCase(JSContext *cx, JSString *src, jsval *rval)
+  JSBool
+  Compare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval)
   {
-    return ChangeCase(cx, src, rval, ToUpperCase);
-  }
+    nsresult rv;
 
-  static JSBool
-  LocaleToLowerCase(JSContext *cx, JSString *src, jsval *rval)
-  {
-    return ChangeCase(cx, src, rval, ToLowerCase);
-  }
+    if (!mCollation) {
+      nsCOMPtr<nsILocaleService> localeService =
+        do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
 
-  /**
-   * Return an XPCLocaleCallbacks out of |cx|.  Callers must know that
-   * |cx| has an XPCLocaleCallbacks; i.e., the checks in MaybeThis()
-   * would be pointless to run from the calling context.
-   *
-   * NB: If the returned XPCLocaleCallbacks hasn't yet been bound to a
-   * thread, then a side effect of calling This() is to bind it to the
-   * calling thread.
-   */
-  static XPCLocaleCallbacks*
-  This(JSContext* cx)
-  {
-    XPCLocaleCallbacks* ths =
-      static_cast<XPCLocaleCallbacks*>(JS_GetLocaleCallbacks(cx));
-    ths->AssertThreadSafety();
-    return ths;
-  }
+      if (NS_SUCCEEDED(rv)) {
+        nsCOMPtr<nsILocale> locale;
+        rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
+
+        if (NS_SUCCEEDED(rv)) {
+          nsCOMPtr<nsICollationFactory> colFactory =
+            do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
+
+          if (NS_SUCCEEDED(rv)) {
+            rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
+          }
+        }
+      }
 
-  static JSBool
-  LocaleToUnicode(JSContext* cx, const char* src, jsval* rval)
-  {
-    return This(cx)->ToUnicode(cx, src, rval);
-  }
+      if (NS_FAILED(rv)) {
+        xpc::Throw(cx, rv);
+        return false;
+      }
+    }
 
-  static JSBool
-  LocaleCompare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval)
-  {
-    return This(cx)->Compare(cx, src1, src2, rval);
-  }
+    nsDependentJSString depStr1, depStr2;
+    if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) {
+      return false;
+    }
 
-  XPCLocaleCallbacks()
-#ifdef DEBUG
-    : mThread(nullptr)
-#endif
-  {
-    MOZ_COUNT_CTOR(XPCLocaleCallbacks);
+    int32_t result;
+    rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
+                                   depStr1, depStr2, &result);
 
-    localeToUpperCase = LocaleToUpperCase;
-    localeToLowerCase = LocaleToLowerCase;
-    localeCompare = LocaleCompare;
-    localeToUnicode = LocaleToUnicode;
-    localeGetErrorMessage = nullptr;
-  }
+    if (NS_FAILED(rv)) {
+      xpc::Throw(cx, rv);
+      return false;
+    }
 
-  ~XPCLocaleCallbacks()
-  {
-    MOZ_COUNT_DTOR(XPCLocaleCallbacks);
-    AssertThreadSafety();
+    *rval = INT_TO_JSVAL(result);
+    return true;
   }
 
   JSBool
   ToUnicode(JSContext* cx, const char* src, jsval* rval)
   {
     nsresult rv;
 
     if (!mDecoder) {
@@ -212,166 +235,52 @@ struct XPCLocaleCallbacks : public JSLoc
       xpc::Throw(cx, NS_ERROR_OUT_OF_MEMORY);
       return false;
     }
 
     *rval = STRING_TO_JSVAL(str);
     return true;
   }
 
-  JSBool
-  Compare(JSContext *cx, JSString *src1, JSString *src2, jsval *rval)
+  void AssertThreadSafety()
   {
-    nsresult rv;
-
-    if (!mCollation) {
-      nsCOMPtr<nsILocaleService> localeService =
-        do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-
-      if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsILocale> locale;
-        rv = localeService->GetApplicationLocale(getter_AddRefs(locale));
-
-        if (NS_SUCCEEDED(rv)) {
-          nsCOMPtr<nsICollationFactory> colFactory =
-            do_CreateInstance(NS_COLLATIONFACTORY_CONTRACTID, &rv);
-
-          if (NS_SUCCEEDED(rv)) {
-            rv = colFactory->CreateCollation(locale, getter_AddRefs(mCollation));
-          }
-        }
-      }
-
-      if (NS_FAILED(rv)) {
-        xpc::Throw(cx, rv);
-
-        return false;
-      }
-    }
-
-    nsDependentJSString depStr1, depStr2;
-    if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) {
-      return false;
-    }
-
-    int32_t result;
-    rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
-                                   depStr1, depStr2, &result);
-
-    if (NS_FAILED(rv)) {
-      xpc::Throw(cx, rv);
-
-      return false;
-    }
-
-    *rval = INT_TO_JSVAL(result);
-
-    return true;
+    MOZ_ASSERT(mThread == PR_GetCurrentThread(),
+               "XPCLocaleCallbacks used unsafely!");
   }
 
   nsCOMPtr<nsICollation> mCollation;
   nsCOMPtr<nsIUnicodeDecoder> mDecoder;
-
 #ifdef DEBUG
   PRThread* mThread;
-
-  // Assert that |this| being used in a way consistent with its
-  // restrictions.  If |this| hasn't been bound to a thread yet, then
-  // it will be bound to calling thread.
-  void AssertThreadSafety()
-  {
-    NS_ABORT_IF_FALSE(!mThread || mThread == PR_GetCurrentThread(),
-                      "XPCLocaleCallbacks used unsafely!");
-    if (!mThread) {
-      mThread = PR_GetCurrentThread();
-    }
-  }
-#else
-    void AssertThreadSafety() { }
-#endif  // DEBUG
+#endif
 };
 
+NS_EXPORT_(bool)
+xpc_LocalizeRuntime(JSRuntime *rt)
+{
+  JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks());
 
-/**
- * There can only be one JSRuntime in which JSContexts are hooked with
- * XPCLocaleCallbacks.  |sHookedRuntime| is it.
- *
- * Initializing the JSContextCallback must be thread safe.
- * |sOldContextCallback| and |sHookedRuntime| are protected by
- * |sHookRuntime|.  After that, however, the context callback itself
- * doesn't need to be thread safe, since it operates on
- * JSContext-local data.
- */
-static PRCallOnceType sHookRuntime;
-static JSContextCallback sOldContextCallback;
-#ifdef DEBUG
-static JSRuntime* sHookedRuntime;
-#endif  // DEBUG
-
-static JSBool
-DelocalizeContextCallback(JSContext *cx, unsigned contextOp)
-{
-  NS_ABORT_IF_FALSE(JS_GetRuntime(cx) == sHookedRuntime, "unknown runtime!");
-
-  JSBool ok = true;
-  if (sOldContextCallback && !sOldContextCallback(cx, contextOp)) {
-    ok = false;
-    // Even if the old callback fails, we still have to march on or
-    // else we might leak the intl stuff hooked onto |cx|
-  }
+  // Set the default locale.
+  nsCOMPtr<nsILocaleService> localeService =
+    do_GetService(NS_LOCALESERVICE_CONTRACTID);
+  if (!localeService)
+    return false;
 
-  if (contextOp == JSCONTEXT_DESTROY) {
-    if (XPCLocaleCallbacks* lc = XPCLocaleCallbacks::MaybeThis(cx)) {
-      // This is a JSContext for which xpc_LocalizeContext() was called.
-      JS_SetLocaleCallbacks(cx, nullptr);
-      delete lc;
-    }
-  }
-
-  return ok;
-}
+  nsCOMPtr<nsILocale> appLocale;
+  nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+  if (NS_FAILED(rv))
+    return false;
 
-static PRStatus
-HookRuntime(void* arg)
-{
-  JSRuntime* rt = static_cast<JSRuntime*>(arg);
-
-  NS_ABORT_IF_FALSE(!sHookedRuntime && !sOldContextCallback,
-                    "PRCallOnce called twice?");
+  nsAutoString localeStr;
+  rv = appLocale->GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
+  NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info");
+  NS_LossyConvertUTF16toASCII locale(localeStr);
 
-  // XXX it appears that in practice we only have to worry about
-  // xpconnect's context hook, and it chains properly.  However, it
-  // *will* stomp our callback on shutdown.
-  sOldContextCallback = JS_SetContextCallback(rt, DelocalizeContextCallback);
-#ifdef DEBUG
-  sHookedRuntime = rt;
-#endif
-
-  return PR_SUCCESS;
+  return !!JS_SetDefaultLocale(rt, locale.get());
 }
 
 NS_EXPORT_(void)
-xpc_LocalizeContext(JSContext *cx)
+xpc_DelocalizeRuntime(JSRuntime *rt)
 {
-  JSRuntime* rt = JS_GetRuntime(cx);
-  PR_CallOnceWithArg(&sHookRuntime, HookRuntime, rt);
-
-  NS_ABORT_IF_FALSE(sHookedRuntime == rt, "created multiple JSRuntimes?");
-
-  JS_SetLocaleCallbacks(cx, new XPCLocaleCallbacks());
-
-  // set the context's default locale
-  nsresult rv;
-  nsCOMPtr<nsILocaleService> localeService =
-    do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
-  if (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsILocale> appLocale;
-    rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
-    if (NS_SUCCEEDED(rv)) {
-      nsAutoString localeStr;
-      rv = appLocale->
-           GetCategory(NS_LITERAL_STRING(NSILOCALE_TIME), localeStr);
-      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to get app locale info");
-      NS_LossyConvertUTF16toASCII locale(localeStr);
-      JS_SetDefaultLocale(cx, locale.get());
-    }
-  }
+  XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt);
+  JS_SetLocaleCallbacks(rt, nullptr);
+  delete lc;
 }
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -57,19 +57,21 @@ GetXBLScope(JSContext *cx, JSObject *con
 #define XPCONNECT_GLOBAL_FLAGS                                                \
     JSCLASS_DOM_GLOBAL | JSCLASS_HAS_PRIVATE |                                \
     JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS |            \
     JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(2)
 
 void
 TraceXPCGlobal(JSTracer *trc, JSObject *obj);
 
-// XXX where should this live?
+// XXX These should be moved into XPCJSRuntime!
+NS_EXPORT_(bool)
+xpc_LocalizeRuntime(JSRuntime *rt);
 NS_EXPORT_(void)
-xpc_LocalizeContext(JSContext *cx);
+xpc_DelocalizeRuntime(JSRuntime *rt);
 
 nsresult
 xpc_MorphSlimWrapper(JSContext *cx, nsISupports *tomorph);
 
 static inline bool IS_WRAPPER_CLASS(js::Class* clazz)
 {
     return clazz->ext.isWrappedNative;
 }