XPCLocale.cpp (4421B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/Assertions.h" 8 9 #include "js/LocaleSensitive.h" 10 11 #include "nsIObserver.h" 12 #include "nsIObserverService.h" 13 #include "nsComponentManagerUtils.h" 14 #include "nsIPrefService.h" 15 #include "nsServiceManagerUtils.h" 16 #include "mozilla/Services.h" 17 #include "mozilla/CycleCollectedJSRuntime.h" 18 #include "mozilla/CycleCollectedJSContext.h" 19 #include "mozilla/intl/LocaleService.h" 20 #include "mozilla/Preferences.h" 21 22 #include "xpcpublic.h" 23 #include "xpcprivate.h" 24 25 using namespace mozilla; 26 using mozilla::intl::LocaleService; 27 28 class XPCLocaleObserver : public nsIObserver { 29 public: 30 NS_DECL_ISUPPORTS 31 NS_DECL_NSIOBSERVER 32 33 void Init(); 34 35 private: 36 virtual ~XPCLocaleObserver() = default; 37 }; 38 39 NS_IMPL_ISUPPORTS(XPCLocaleObserver, nsIObserver); 40 41 void XPCLocaleObserver::Init() { 42 nsCOMPtr<nsIObserverService> observerService = 43 mozilla::services::GetObserverService(); 44 45 observerService->AddObserver(this, "intl:app-locales-changed", false); 46 } 47 48 NS_IMETHODIMP 49 XPCLocaleObserver::Observe(nsISupports* aSubject, const char* aTopic, 50 const char16_t* aData) { 51 if (!strcmp(aTopic, "intl:app-locales-changed")) { 52 JSRuntime* rt = CycleCollectedJSRuntime::Get()->Runtime(); 53 if (!xpc_LocalizeRuntime(rt)) { 54 return NS_ERROR_OUT_OF_MEMORY; 55 } 56 return NS_OK; 57 } 58 59 return NS_ERROR_UNEXPECTED; 60 } 61 62 /** 63 * JS locale callbacks implemented by XPCOM modules. These are theoretically 64 * safe for use on multiple threads. Unfortunately, the intl code underlying 65 * these XPCOM modules doesn't yet support this, so in practice 66 * XPCLocaleCallbacks are limited to the main thread. 67 */ 68 struct XPCLocaleCallbacks : public JSLocaleCallbacks { 69 XPCLocaleCallbacks() { 70 MOZ_COUNT_CTOR(XPCLocaleCallbacks); 71 72 // Disable the toLocaleUpper/Lower case hooks to use the standard, 73 // locale-insensitive definition from String.prototype. (These hooks are 74 // only consulted when JS_HAS_INTL_API is not set.) Since JS_HAS_INTL_API 75 // is always set, these hooks should be disabled. 76 localeToUpperCase = nullptr; 77 localeToLowerCase = nullptr; 78 localeCompare = nullptr; 79 localeToUnicode = nullptr; 80 81 // It's going to be retained by the ObserverService. 82 RefPtr<XPCLocaleObserver> locObs = new XPCLocaleObserver(); 83 locObs->Init(); 84 } 85 86 ~XPCLocaleCallbacks() { 87 AssertThreadSafety(); 88 MOZ_COUNT_DTOR(XPCLocaleCallbacks); 89 } 90 91 /** 92 * Return the XPCLocaleCallbacks that's hidden away in |rt|. (This impl uses 93 * the locale callbacks struct to store away its per-context data.) 94 */ 95 static XPCLocaleCallbacks* This(JSRuntime* rt) { 96 // Locale information for |cx| was associated using xpc_LocalizeContext; 97 // assert and double-check this. 98 const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt); 99 MOZ_ASSERT(lc); 100 MOZ_ASSERT(lc->localeToUpperCase == nullptr); 101 MOZ_ASSERT(lc->localeToLowerCase == nullptr); 102 MOZ_ASSERT(lc->localeCompare == nullptr); 103 MOZ_ASSERT(lc->localeToUnicode == nullptr); 104 105 const XPCLocaleCallbacks* ths = static_cast<const XPCLocaleCallbacks*>(lc); 106 ths->AssertThreadSafety(); 107 return const_cast<XPCLocaleCallbacks*>(ths); 108 } 109 110 private: 111 void AssertThreadSafety() const { 112 NS_ASSERT_OWNINGTHREAD(XPCLocaleCallbacks); 113 } 114 115 NS_DECL_OWNINGTHREAD 116 }; 117 118 bool xpc_LocalizeRuntime(JSRuntime* rt) { 119 // We want to assign the locale callbacks only the first time we 120 // localize the context. 121 // All consequent calls to this function are result of language changes 122 // and should not assign it again. 123 const JSLocaleCallbacks* lc = JS_GetLocaleCallbacks(rt); 124 if (!lc) { 125 JS_SetLocaleCallbacks(rt, new XPCLocaleCallbacks()); 126 } 127 128 // Set the default locale from the regional prefs locales. 129 AutoTArray<nsCString, 10> rpLocales; 130 LocaleService::GetInstance()->GetRegionalPrefsLocales(rpLocales); 131 132 MOZ_ASSERT(rpLocales.Length() > 0); 133 return JS_SetDefaultLocale(rt, rpLocales[0].get()); 134 } 135 136 void xpc_DelocalizeRuntime(JSRuntime* rt) { 137 const XPCLocaleCallbacks* lc = XPCLocaleCallbacks::This(rt); 138 JS_SetLocaleCallbacks(rt, nullptr); 139 delete lc; 140 }