Bug 826009 - Move locale callback info, default locale, etc. data and APIs to be JSRuntime-centered. f=bholley, r=jorendorff

--HG--
extra : rebase_source : 35317f4c29e9dd70e7d4fd1292027cfd51ce2675
This commit is contained in:
Jeff Walden 2013-02-07 18:04:11 -08:00
parent 7d776dcf48
commit 0e302a5094
18 changed files with 228 additions and 295 deletions

View File

@ -1133,8 +1133,6 @@ nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope)
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;

View File

@ -1114,8 +1114,6 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime, bool aGCOnDestruction,
js_options_dot_str, this);
::JS_SetOperationCallback(mContext, DOMOperationCallback);
xpc_LocalizeContext(mContext);
}
mIsInitialized = false;
mTerminations = nullptr;

View File

@ -1004,8 +1004,6 @@ XPCShellEnvironment::Init()
return false;
}
xpc_LocalizeContext(cx);
nsRefPtr<FullTrustSecMan> secman(new FullTrustSecMan());
xpc->SetSecurityManagerForJSContext(cx, secman, 0xFFFF);

View File

@ -248,7 +248,7 @@ 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")

View File

@ -723,6 +723,8 @@ js::PerThreadData::PerThreadData(JSRuntime *runtime)
JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads)
: mainThread(this),
atomsCompartment(NULL),
localeCallbacks(NULL),
defaultLocale(NULL),
#ifdef JS_THREADSAFE
ownerThread_(NULL),
#endif
@ -1135,6 +1137,7 @@ JS_PUBLIC_API(void)
JS_DestroyRuntime(JSRuntime *rt)
{
Probes::destroyRuntime(rt);
js_free(rt->defaultLocale);
js_delete(rt);
}
@ -2277,6 +2280,17 @@ JS_strdup(JSContext *cx, const char *s)
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)
@ -6754,31 +6768,31 @@ JS_GetRegExpSource(JSContext *cx, JSObject *objArg)
/************************************************************************/
JS_PUBLIC_API(JSBool)
JS_SetDefaultLocale(JSContext *cx, const char *locale)
JS_SetDefaultLocale(JSRuntime *rt, const char *locale)
{
AssertHeapIsIdle(cx);
return cx->setDefaultLocale(locale);
AssertHeapIsIdle(rt);
return rt->setDefaultLocale(locale);
}
JS_PUBLIC_API(void)
JS_ResetDefaultLocale(JSContext *cx)
JS_ResetDefaultLocale(JSRuntime *rt)
{
AssertHeapIsIdle(cx);
cx->resetDefaultLocale();
AssertHeapIsIdle(rt);
rt->resetDefaultLocale();
}
JS_PUBLIC_API(void)
JS_SetLocaleCallbacks(JSContext *cx, JSLocaleCallbacks *callbacks)
JS_SetLocaleCallbacks(JSRuntime *rt, JSLocaleCallbacks *callbacks)
{
AssertHeapIsIdle(cx);
cx->localeCallbacks = 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;
}
/************************************************************************/

View File

@ -2498,6 +2498,10 @@ 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
@ -4675,13 +4679,13 @@ JS_WriteTypedArray(JSStructuredCloneWriter *w, jsval v);
* 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.
@ -4696,17 +4700,17 @@ struct JSLocaleCallbacks {
/*
* 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);
/************************************************************************/

View File

@ -1169,9 +1169,7 @@ JSContext::JSContext(JSRuntime *rt)
throwing(false),
exception(UndefinedValue()),
options_(0),
defaultLocale(NULL),
reportGranularity(JS_DEFAULT_JITREPORT_GRANULARITY),
localeCallbacks(NULL),
resolvingList(NULL),
generatingError(false),
enterCompartmentDepth_(0),
@ -1215,7 +1213,6 @@ JSContext::JSContext(JSRuntime *rt)
JSContext::~JSContext()
{
/* Free the stuff hanging off of cx. */
js_free(defaultLocale);
if (parseMapPool_)
js_delete(parseMapPool_);
@ -1223,7 +1220,7 @@ JSContext::~JSContext()
}
bool
JSContext::setDefaultLocale(const char *locale)
JSRuntime::setDefaultLocale(const char *locale)
{
if (!locale)
return false;
@ -1233,14 +1230,14 @@ JSContext::setDefaultLocale(const char *locale)
}
void
JSContext::resetDefaultLocale()
JSRuntime::resetDefaultLocale()
{
js_free(defaultLocale);
defaultLocale = NULL;
}
const char *
JSContext::getDefaultLocale()
JSRuntime::getDefaultLocale()
{
if (defaultLocale)
return defaultLocale;
@ -1253,7 +1250,7 @@ JSContext::getDefaultLocale()
#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;

View File

@ -593,6 +593,12 @@ struct JSRuntime : js::RuntimeFriendFields,
/* 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:
@ -676,6 +682,10 @@ struct JSRuntime : js::RuntimeFriendFields,
return ionRuntime_ ? ionRuntime_ : createIonRuntime(cx);
}
//-------------------------------------------------------------------------
// Self-hosting support
//-------------------------------------------------------------------------
bool initSelfHosting(JSContext *cx);
void markSelfHostingGlobal(JSTracer *trc);
bool isSelfHostingGlobal(js::HandleObject global) {
@ -686,6 +696,25 @@ struct JSRuntime : js::RuntimeFriendFields,
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;
@ -1369,30 +1398,9 @@ struct JSContext : js::ContextFriendFields,
/* 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. */

View File

@ -2761,8 +2761,8 @@ ToLocaleHelper(JSContext *cx, HandleObject obj, const char *format, MutableHandl
}
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)

View File

@ -866,8 +866,8 @@ js_GetLocalizedErrorMessage(JSContext* cx, void *userRef, const char *locale,
{
const JSErrorFormatString *errorString = NULL;
if (cx->localeCallbacks && cx->localeCallbacks->localeGetErrorMessage) {
errorString = cx->localeCallbacks
if (cx->runtime->localeCallbacks && cx->runtime->localeCallbacks->localeGetErrorMessage) {
errorString = cx->runtime->localeCallbacks
->localeGetErrorMessage(userRef, locale, errorNumber);
}
if (!errorString)

View File

@ -720,9 +720,9 @@ num_toLocaleString_impl(JSContext *cx, CallArgs args)
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);

View File

@ -679,13 +679,13 @@ str_toLocaleLowerCase(JSContext *cx, unsigned argc, Value *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);
@ -746,13 +746,13 @@ str_toLocaleUpperCase(JSContext *cx, unsigned argc, Value *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);
@ -775,11 +775,11 @@ str_localeCompare(JSContext *cx, unsigned argc, Value *vp)
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);

View File

@ -284,9 +284,11 @@ 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)

View File

@ -495,9 +495,6 @@ mozJSComponentLoader::ReallyInit()
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

View File

@ -1825,8 +1825,6 @@ main(int argc, char **argv, char **envp)
argv++;
ProcessArgsForCompartment(cx, argv, argc);
xpc_LocalizeContext(cx);
nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
if (!xpc) {
printf("failed to get nsXPConnect service!\n");

View File

@ -1224,6 +1224,8 @@ XPCJSRuntime::~XPCJSRuntime()
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
@ -2598,6 +2600,12 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
// 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));

View File

@ -5,7 +5,8 @@
* 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"
@ -22,41 +23,79 @@
#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
{
/**
* 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.
*/
static XPCLocaleCallbacks*
MaybeThis(JSContext* cx)
XPCLocaleCallbacks()
#ifdef DEBUG
: mThread(PR_GetCurrentThread())
#endif
{
JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(cx);
return (lc &&
lc->localeToUpperCase == LocaleToUpperCase &&
lc->localeToLowerCase == LocaleToLowerCase &&
lc->localeCompare == LocaleCompare &&
lc->localeToUnicode == LocaleToUnicode) ? This(cx) : nullptr;
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 |rt|. (This impl uses
* the locale callbacks struct to store away its per-runtime data.)
*/
static XPCLocaleCallbacks*
This(JSRuntime *rt)
{
// 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&))
@ -76,70 +115,54 @@ struct XPCLocaleCallbacks : public JSLocaleCallbacks
}
*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;
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));
}
}
}
static JSBool
LocaleToLowerCase(JSContext *cx, JSString *src, jsval *rval)
{
return ChangeCase(cx, src, rval, ToLowerCase);
if (NS_FAILED(rv)) {
xpc::Throw(cx, rv);
return false;
}
}
/**
* 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;
nsDependentJSString depStr1, depStr2;
if (!depStr1.init(cx, src1) || !depStr2.init(cx, src2)) {
return false;
}
static JSBool
LocaleToUnicode(JSContext* cx, const char* src, jsval* rval)
{
return This(cx)->ToUnicode(cx, src, rval);
int32_t result;
rv = mCollation->CompareString(nsICollation::kCollationStrengthDefault,
depStr1, depStr2, &result);
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);
}
XPCLocaleCallbacks()
#ifdef DEBUG
: mThread(nullptr)
#endif
{
MOZ_COUNT_CTOR(XPCLocaleCallbacks);
localeToUpperCase = LocaleToUpperCase;
localeToLowerCase = LocaleToLowerCase;
localeCompare = LocaleCompare;
localeToUnicode = LocaleToUnicode;
localeGetErrorMessage = nullptr;
}
~XPCLocaleCallbacks()
{
MOZ_COUNT_DTOR(XPCLocaleCallbacks);
AssertThreadSafety();
*rval = INT_TO_JSVAL(result);
return true;
}
JSBool
@ -217,161 +240,47 @@ struct XPCLocaleCallbacks : public JSLocaleCallbacks
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
};
/**
* 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_EXPORT_(bool)
xpc_LocalizeRuntime(JSRuntime *rt)
{
NS_ABORT_IF_FALSE(JS_GetRuntime(cx) == sHookedRuntime, "unknown runtime!");
JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks());
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;
}
}
nsCOMPtr<nsILocale> appLocale;
nsresult rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
if (NS_FAILED(rv))
return false;
return ok;
}
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);
static PRStatus
HookRuntime(void* arg)
{
JSRuntime* rt = static_cast<JSRuntime*>(arg);
NS_ABORT_IF_FALSE(!sHookedRuntime && !sOldContextCallback,
"PRCallOnce called twice?");
// 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;
}

View File

@ -62,9 +62,11 @@ GetXBLScope(JSContext *cx, JSObject *contentScope);
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);