mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1613705 - [localization] part2: Switch Localization class to use localization-ffi. r=emilio,nika
Depends on D104788 Differential Revision: https://phabricator.services.mozilla.com/D104789
This commit is contained in:
parent
05db766a87
commit
9f3aa2521f
@ -712,7 +712,7 @@ size_t IdentifierMapEntry::SizeOfExcludingThis(
|
||||
class SubDocMapEntry : public PLDHashEntryHdr {
|
||||
public:
|
||||
// Both of these are strong references
|
||||
Element* mKey; // must be first, to look like PLDHashEntryStub
|
||||
dom::Element* mKey; // must be first, to look like PLDHashEntryStub
|
||||
dom::Document* mSubDocument;
|
||||
};
|
||||
|
||||
@ -4140,18 +4140,6 @@ bool Document::GetAllowPlugins() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Document::EnsureL10n() {
|
||||
if (!mDocumentL10n) {
|
||||
Element* elem = GetDocumentElement();
|
||||
if (NS_WARN_IF(!elem)) {
|
||||
return;
|
||||
}
|
||||
bool isSync = elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nsync);
|
||||
mDocumentL10n = DocumentL10n::Create(this, isSync);
|
||||
MOZ_ASSERT(mDocumentL10n);
|
||||
}
|
||||
}
|
||||
|
||||
bool Document::HasPendingInitialTranslation() {
|
||||
return mDocumentL10n && mDocumentL10n->GetState() != DocumentL10nState::Ready;
|
||||
}
|
||||
@ -4174,15 +4162,20 @@ void Document::LocalizationLinkAdded(Element* aLinkElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureL10n();
|
||||
|
||||
nsAutoString href;
|
||||
aLinkElement->GetAttr(kNameSpaceID_None, nsGkAtoms::href, href);
|
||||
|
||||
if (!mDocumentL10n) {
|
||||
Element* elem = GetDocumentElement();
|
||||
MOZ_DIAGNOSTIC_ASSERT(elem);
|
||||
|
||||
bool isSync = elem->HasAttr(nsGkAtoms::datal10nsync);
|
||||
mDocumentL10n = DocumentL10n::Create(this, isSync);
|
||||
MOZ_ASSERT(mDocumentL10n);
|
||||
}
|
||||
mDocumentL10n->AddResourceId(href);
|
||||
|
||||
if (mReadyState >= READYSTATE_INTERACTIVE) {
|
||||
mDocumentL10n->Activate(true);
|
||||
mDocumentL10n->TriggerInitialTranslation();
|
||||
} else {
|
||||
if (!mDocumentL10n->mBlockingLayout) {
|
||||
@ -4227,9 +4220,8 @@ void Document::LocalizationLinkRemoved(Element* aLinkElement) {
|
||||
* collected.
|
||||
*/
|
||||
void Document::OnL10nResourceContainerParsed() {
|
||||
if (mDocumentL10n) {
|
||||
mDocumentL10n->Activate(false);
|
||||
}
|
||||
// XXX: This is a scaffolding for where we might inject prefetch
|
||||
// in bug 1717241.
|
||||
}
|
||||
|
||||
void Document::OnParsingCompleted() {
|
||||
|
@ -3854,8 +3854,6 @@ class Document : public nsINode,
|
||||
private:
|
||||
bool IsErrorPage() const;
|
||||
|
||||
void EnsureL10n();
|
||||
|
||||
// Takes the bits from mStyleUseCounters if appropriate, and sets them in
|
||||
// mUseCounters.
|
||||
void SetCssUseCounterBits();
|
||||
|
@ -465,7 +465,6 @@ DOMInterfaces = {
|
||||
},
|
||||
|
||||
'Localization': {
|
||||
'implicitJSContext': [ 'formatValue', 'formatValues', 'formatMessages', 'formatValueSync', 'formatValuesSync', 'formatMessagesSync' ],
|
||||
'nativeType': 'mozilla::intl::Localization',
|
||||
},
|
||||
|
||||
|
@ -435,6 +435,17 @@ template <typename K, typename V>
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] bool ToJSValue(JSContext* aCx, const Nullable<T>& aArgument,
|
||||
JS::MutableHandle<JS::Value> aValue) {
|
||||
if (aArgument.IsNull()) {
|
||||
aValue.setNull();
|
||||
return true;
|
||||
}
|
||||
|
||||
return ToJSValue(aCx, aArgument.Value(), aValue);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -42,14 +42,12 @@ already_AddRefed<DOMLocalization> DOMLocalization::Create(
|
||||
RefPtr<DOMLocalization> domLoc =
|
||||
new DOMLocalization(aGlobal, aSync, aBundleGenerator);
|
||||
|
||||
domLoc->Init();
|
||||
|
||||
return domLoc.forget();
|
||||
}
|
||||
|
||||
DOMLocalization::DOMLocalization(nsIGlobalObject* aGlobal, const bool aSync,
|
||||
const BundleGenerator& aBundleGenerator)
|
||||
: Localization(aGlobal, aSync, aBundleGenerator) {
|
||||
: Localization(aGlobal, aSync) {
|
||||
mMutations = new L10nMutations(this);
|
||||
}
|
||||
|
||||
@ -70,8 +68,6 @@ already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
|
||||
domLoc->AddResourceIds(aResourceIds);
|
||||
}
|
||||
|
||||
domLoc->Activate(true);
|
||||
|
||||
return domLoc.forget();
|
||||
}
|
||||
|
||||
@ -302,9 +298,6 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AutoEntryScript aes(mGlobal, "DOMLocalization TranslateElements");
|
||||
JSContext* cx = aes.cx();
|
||||
|
||||
for (auto& domElement : aElements) {
|
||||
if (!domElement->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
|
||||
continue;
|
||||
@ -335,7 +328,7 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||
if (mIsSync) {
|
||||
nsTArray<Nullable<L10nMessage>> l10nMessages;
|
||||
|
||||
FormatMessagesSync(cx, l10nKeys, l10nMessages, aRv);
|
||||
FormatMessagesSync(l10nKeys, l10nMessages, aRv);
|
||||
|
||||
bool allTranslated =
|
||||
ApplyTranslations(domElements, l10nMessages, aProto, aRv);
|
||||
@ -346,7 +339,7 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||
|
||||
promise->MaybeResolveWithUndefined();
|
||||
} else {
|
||||
RefPtr<Promise> callbackResult = FormatMessages(cx, l10nKeys, aRv);
|
||||
RefPtr<Promise> callbackResult = FormatMessages(l10nKeys, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -528,10 +521,7 @@ bool DOMLocalization::ApplyTranslations(
|
||||
|
||||
void DOMLocalization::OnChange() {
|
||||
Localization::OnChange();
|
||||
if (mLocalization && !mResourceIds.IsEmpty()) {
|
||||
ErrorResult rv;
|
||||
RefPtr<Promise> promise = TranslateRoots(rv);
|
||||
}
|
||||
RefPtr<Promise> promise = TranslateRoots(IgnoreErrors());
|
||||
}
|
||||
|
||||
void DOMLocalization::DisconnectMutations() {
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "mozilla/dom/L10nMutations.h"
|
||||
#include "mozilla/dom/L10nOverlaysBinding.h"
|
||||
#include "mozilla/dom/LocalizationBinding.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
|
||||
// XXX Avoid including this here by moving function bodies to the cpp file
|
||||
#include "nsINode.h"
|
||||
|
@ -35,33 +35,25 @@ NS_INTERFACE_MAP_END_INHERITING(DOMLocalization)
|
||||
bool DocumentL10n::mIsFirstBrowserWindow = true;
|
||||
|
||||
/* static */
|
||||
RefPtr<DocumentL10n> DocumentL10n::Create(Document* aDocument,
|
||||
const bool aSync) {
|
||||
RefPtr<DocumentL10n> DocumentL10n::Create(Document* aDocument, bool aSync) {
|
||||
RefPtr<DocumentL10n> l10n = new DocumentL10n(aDocument, aSync);
|
||||
|
||||
if (!l10n->Init()) {
|
||||
IgnoredErrorResult rv;
|
||||
l10n->mReady = Promise::Create(l10n->mGlobal, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return l10n.forget();
|
||||
}
|
||||
|
||||
DocumentL10n::DocumentL10n(Document* aDocument, const bool aSync)
|
||||
DocumentL10n::DocumentL10n(Document* aDocument, bool aSync)
|
||||
: DOMLocalization(aDocument->GetScopeObject(), aSync, {}),
|
||||
mDocument(aDocument),
|
||||
mState(DocumentL10nState::Constructed) {
|
||||
mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
|
||||
}
|
||||
|
||||
bool DocumentL10n::Init() {
|
||||
DOMLocalization::Init();
|
||||
ErrorResult rv;
|
||||
mReady = Promise::Create(mGlobal, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* DocumentL10n::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return DocumentL10n_Binding::Wrap(aCx, this, aGivenProto);
|
||||
|
@ -47,12 +47,10 @@ class DocumentL10n final : public DOMLocalization {
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentL10n, DOMLocalization)
|
||||
|
||||
static RefPtr<DocumentL10n> Create(Document* aDocument, const bool aSync);
|
||||
static RefPtr<DocumentL10n> Create(Document* aDocument, bool aSync);
|
||||
|
||||
protected:
|
||||
explicit DocumentL10n(Document* aDocument, const bool aSync);
|
||||
bool Init() override;
|
||||
|
||||
explicit DocumentL10n(Document* aDocument, bool aSync);
|
||||
virtual ~DocumentL10n() = default;
|
||||
|
||||
RefPtr<Document> mDocument;
|
||||
|
@ -516,7 +516,7 @@ void L10nOverlays::TranslateElement(Element& aElement,
|
||||
DocumentFragment(aElement.OwnerDoc()->NodeInfoManager());
|
||||
nsContentUtils::ParseFragmentHTML(
|
||||
NS_ConvertUTF8toUTF16(aTranslation.mValue), fragment,
|
||||
nsGkAtoms::_template, kNameSpaceID_XHTML, false, true);
|
||||
nsGkAtoms::_template, kNameSpaceID_XHTML, false, true, 0);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
@ -114,18 +114,15 @@ void MediaControlService::Init() {
|
||||
mControllerManager = MakeUnique<ControllerManager>(this);
|
||||
|
||||
// Initialize the fallback title
|
||||
nsCOMPtr<nsIGlobalObject> global =
|
||||
xpc::NativeGlobal(xpc::PrivilegedJunkScope());
|
||||
RefPtr<Localization> l10n = Localization::Create(global, true, {});
|
||||
l10n->AddResourceId(u"branding/brand.ftl"_ns);
|
||||
l10n->AddResourceId(u"dom/media.ftl"_ns);
|
||||
nsTArray<nsCString> resIds{
|
||||
"branding/brand.ftl"_ns,
|
||||
"dom/media.ftl"_ns,
|
||||
};
|
||||
RefPtr<Localization> l10n = Localization::Create(resIds, true);
|
||||
{
|
||||
AutoSafeJSContext cx;
|
||||
|
||||
nsAutoCString translation;
|
||||
ErrorResult rv;
|
||||
l10n->FormatValueSync(cx, "mediastatus-fallback-title"_ns, {}, translation,
|
||||
rv);
|
||||
IgnoredErrorResult rv;
|
||||
l10n->FormatValueSync("mediastatus-fallback-title"_ns, {}, translation, rv);
|
||||
if (!rv.Failed()) {
|
||||
mFallbackTitle = NS_ConvertUTF8toUTF16(translation);
|
||||
}
|
||||
|
@ -92,16 +92,13 @@ interface Localization {
|
||||
* default generators provided by Gecko.
|
||||
*/
|
||||
[Throws]
|
||||
constructor(sequence<DOMString> aResourceIds,
|
||||
optional boolean aSync = false,
|
||||
optional BundleGenerator aBundleGenerator = {});
|
||||
constructor(sequence<UTF8String> aResourceIds,
|
||||
optional boolean aSync = false);
|
||||
|
||||
/**
|
||||
* A method for adding resources to the localization context.
|
||||
*
|
||||
* Returns a new count of resources used by the context.
|
||||
*/
|
||||
unsigned long addResourceIds(sequence<DOMString> aResourceIds);
|
||||
void addResourceIds(sequence<DOMString> aResourceIds);
|
||||
|
||||
/**
|
||||
* A method for removing resources from the localization context.
|
||||
|
@ -188,8 +188,10 @@ bool extendJSArrayWithErrors(JSContext* aCx, JS::Handle<JSObject*> aErrors,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ConvertArgs(const L10nArgs& aArgs,
|
||||
nsTArray<ffi::L10nArg>& aRetVal) {
|
||||
/* static */
|
||||
void FluentBundle::ConvertArgs(const L10nArgs& aArgs,
|
||||
nsTArray<ffi::L10nArg>& aRetVal) {
|
||||
aRetVal.SetCapacity(aArgs.Entries().Length());
|
||||
for (const auto& entry : aArgs.Entries()) {
|
||||
if (!entry.mValue.IsNull()) {
|
||||
const auto& value = entry.mValue.Value();
|
||||
|
@ -81,6 +81,9 @@ class FluentBundle final : public nsWrapperCache {
|
||||
const dom::Optional<JS::Handle<JSObject*>>& aErrors,
|
||||
nsACString& aRetVal, ErrorResult& aRv);
|
||||
|
||||
static void ConvertArgs(const L10nArgs& aArgs,
|
||||
nsTArray<ffi::L10nArg>& aRetVal);
|
||||
|
||||
protected:
|
||||
virtual ~FluentBundle();
|
||||
|
||||
|
@ -5,162 +5,137 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Localization.h"
|
||||
#include "nsImportModule.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/HoldDropJSObjects.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "js/PropertyAndElement.h" // JS_GetProperty
|
||||
|
||||
#define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed"
|
||||
#define L10N_PSEUDO_PREF "intl.l10n.pseudo"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::intl;
|
||||
|
||||
static const char* kObservedPrefs[] = {L10N_PSEUDO_PREF, nullptr};
|
||||
|
||||
using namespace mozilla::intl;
|
||||
using namespace mozilla::dom;
|
||||
static nsTArray<ffi::L10nKey> ConvertFromL10nKeys(
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys) {
|
||||
nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_MULTI_ZONE_JSHOLDER_CLASS(Localization)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Localization)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalization)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
|
||||
tmp->Destroy();
|
||||
mozilla::DropJSObjects(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Localization)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocalization)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
for (const auto& entry : aKeys) {
|
||||
if (entry.IsUTF8String()) {
|
||||
const auto& id = entry.GetAsUTF8String();
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &id;
|
||||
} else {
|
||||
const auto& e = entry.GetAsL10nIdArgs();
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &e.mId;
|
||||
if (!e.mArgs.IsNull()) {
|
||||
FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Localization)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGenerateBundles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGenerateBundlesSync)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mBundles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
return l10nKeys;
|
||||
}
|
||||
|
||||
static FallibleTArray<AttributeNameValue> ConvertToAttributeNameValue(
|
||||
const nsTArray<ffi::L10nAttribute>& aAttributes, OOMReporter& aError) {
|
||||
FallibleTArray<AttributeNameValue> result(aAttributes.Length());
|
||||
for (const auto& attr : aAttributes) {
|
||||
auto cvtAttr = AttributeNameValue();
|
||||
cvtAttr.mName = attr.name;
|
||||
cvtAttr.mValue = attr.value;
|
||||
if (!result.AppendElement(std::move(cvtAttr), fallible)) {
|
||||
result.Clear();
|
||||
aError.ReportOOM();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static FallibleTArray<Nullable<L10nMessage>> ConvertToL10nMessages(
|
||||
const nsTArray<ffi::OptionalL10nMessage>& aMessages, ErrorResult& aError) {
|
||||
FallibleTArray<Nullable<L10nMessage>> l10nMessages(aMessages.Length());
|
||||
|
||||
for (const auto& entry : aMessages) {
|
||||
Nullable<L10nMessage> msg;
|
||||
if (entry.is_present) {
|
||||
L10nMessage& m = msg.SetValue();
|
||||
if (!entry.message.value.IsVoid()) {
|
||||
m.mValue = entry.message.value;
|
||||
}
|
||||
if (!entry.message.attributes.IsEmpty()) {
|
||||
m.mAttributes.SetValue(
|
||||
ConvertToAttributeNameValue(entry.message.attributes, aError));
|
||||
}
|
||||
}
|
||||
|
||||
if (!l10nMessages.AppendElement(std::move(msg), fallible)) {
|
||||
l10nMessages.Clear();
|
||||
aError.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return l10nMessages;
|
||||
}
|
||||
}
|
||||
|
||||
return l10nMessages;
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Localization, mGlobal)
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(Localization)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(Localization)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Localization)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
/* static */
|
||||
already_AddRefed<Localization> Localization::Create(
|
||||
nsIGlobalObject* aGlobal, const bool aSync,
|
||||
const BundleGenerator& aBundleGenerator) {
|
||||
RefPtr<Localization> loc = new Localization(aGlobal, aSync, aBundleGenerator);
|
||||
|
||||
loc->Init();
|
||||
|
||||
return loc.forget();
|
||||
const nsTArray<nsCString>& aResourceIds, bool aIsSync) {
|
||||
return MakeAndAddRef<Localization>(aResourceIds, aIsSync);
|
||||
}
|
||||
|
||||
Localization::Localization(nsIGlobalObject* aGlobal, const bool aSync,
|
||||
const BundleGenerator& aBundleGenerator)
|
||||
: mGlobal(aGlobal), mIsSync(aSync) {
|
||||
if (aBundleGenerator.mGenerateBundles.WasPassed()) {
|
||||
GenerateBundles& generateBundles =
|
||||
aBundleGenerator.mGenerateBundles.Value();
|
||||
mGenerateBundles.setObject(*generateBundles.CallbackOrNull());
|
||||
}
|
||||
if (aBundleGenerator.mGenerateBundlesSync.WasPassed()) {
|
||||
GenerateBundlesSync& generateBundlesSync =
|
||||
aBundleGenerator.mGenerateBundlesSync.Value();
|
||||
mGenerateBundlesSync.setObject(*generateBundlesSync.CallbackOrNull());
|
||||
}
|
||||
mIsSync = aSync;
|
||||
Localization::Localization(const nsTArray<nsCString>& aResIds, bool aIsSync)
|
||||
: mIsSync(aIsSync) {
|
||||
ffi::localization_new(&aResIds, mIsSync, getter_AddRefs(mRaw));
|
||||
}
|
||||
|
||||
bool Localization::Init() {
|
||||
RegisterObservers();
|
||||
|
||||
return true;
|
||||
Localization::Localization(nsIGlobalObject* aGlobal,
|
||||
const nsTArray<nsCString>& aResIds, bool aIsSync)
|
||||
: mGlobal(aGlobal), mIsSync(aIsSync) {
|
||||
ffi::localization_new(&aResIds, mIsSync, getter_AddRefs(mRaw));
|
||||
}
|
||||
|
||||
void Localization::Activate(const bool aEager) {
|
||||
mLocalization = do_ImportModule("resource://gre/modules/Localization.jsm",
|
||||
"Localization");
|
||||
Localization::Localization(nsIGlobalObject* aGlobal, bool aIsSync)
|
||||
: mGlobal(aGlobal), mIsSync(aIsSync) {
|
||||
nsTArray<nsCString> resIds;
|
||||
ffi::localization_new(&resIds, mIsSync, getter_AddRefs(mRaw));
|
||||
}
|
||||
|
||||
AutoJSContext cx;
|
||||
|
||||
JS::Rooted<JS::Value> generateBundlesJS(cx, mGenerateBundles);
|
||||
JS::Rooted<JS::Value> generateBundlesSyncJS(cx, mGenerateBundlesSync);
|
||||
JS::Rooted<JS::Value> bundlesJS(cx);
|
||||
mLocalization->GenerateBundles(mResourceIds, mIsSync, aEager,
|
||||
generateBundlesJS, generateBundlesSyncJS,
|
||||
&bundlesJS);
|
||||
mBundles.set(bundlesJS);
|
||||
|
||||
mozilla::HoldJSObjects(this);
|
||||
Localization::Localization(nsIGlobalObject* aGlobal)
|
||||
: mGlobal(aGlobal), mIsSync(false) {
|
||||
nsTArray<nsCString> resIds;
|
||||
ffi::localization_new(&resIds, mIsSync, getter_AddRefs(mRaw));
|
||||
}
|
||||
|
||||
already_AddRefed<Localization> Localization::Constructor(
|
||||
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
|
||||
const bool aSync, const BundleGenerator& aBundleGenerator,
|
||||
ErrorResult& aRv) {
|
||||
const GlobalObject& aGlobal, const Sequence<nsCString>& aResourceIds,
|
||||
bool aIsSync, ErrorResult& aRv) {
|
||||
nsTArray<nsCString> resIds = ToTArray<nsTArray<nsCString>>(aResourceIds);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!global) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Localization> loc =
|
||||
Localization::Create(global, aSync, aBundleGenerator);
|
||||
|
||||
if (aResourceIds.Length()) {
|
||||
loc->AddResourceIds(aResourceIds);
|
||||
}
|
||||
|
||||
loc->Activate(true);
|
||||
|
||||
return loc.forget();
|
||||
return do_AddRef(new Localization(global, resIds, aIsSync));
|
||||
}
|
||||
|
||||
nsIGlobalObject* Localization::GetParentObject() const { return mGlobal; }
|
||||
|
||||
JSObject* Localization::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) {
|
||||
return Localization_Binding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
Localization::~Localization() {
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->RemoveObserver(this, INTL_APP_LOCALES_CHANGED);
|
||||
}
|
||||
|
||||
Preferences::RemoveObservers(this, kObservedPrefs);
|
||||
|
||||
Destroy();
|
||||
mozilla::DropJSObjects(this);
|
||||
}
|
||||
|
||||
void Localization::Destroy() {
|
||||
mGenerateBundles.setUndefined();
|
||||
mGenerateBundlesSync.setUndefined();
|
||||
mBundles.setUndefined();
|
||||
}
|
||||
|
||||
/* Protected */
|
||||
|
||||
void Localization::RegisterObservers() {
|
||||
DebugOnly<nsresult> rv = Preferences::AddWeakObservers(this, kObservedPrefs);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true);
|
||||
}
|
||||
}
|
||||
Localization::~Localization() = default;
|
||||
|
||||
NS_IMETHODIMP
|
||||
Localization::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
@ -178,261 +153,218 @@ Localization::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Localization::OnChange() {
|
||||
if (mLocalization) {
|
||||
AutoJSContext cx;
|
||||
JS::Rooted<JS::Value> generateBundlesJS(cx, mGenerateBundles);
|
||||
JS::Rooted<JS::Value> generateBundlesSyncJS(cx, mGenerateBundlesSync);
|
||||
JS::Rooted<JS::Value> bundlesJS(cx);
|
||||
mLocalization->GenerateBundles(mResourceIds, mIsSync, false,
|
||||
generateBundlesJS, generateBundlesSyncJS,
|
||||
&bundlesJS);
|
||||
mBundles.set(bundlesJS);
|
||||
}
|
||||
void Localization::OnChange() {}
|
||||
|
||||
void Localization::SetIsSync(bool aIsSync) {
|
||||
MOZ_ASSERT(!aIsSync, "We should only move from sync to async!");
|
||||
mIsSync = aIsSync;
|
||||
Upgrade();
|
||||
}
|
||||
|
||||
uint32_t Localization::AddResourceId(const nsAString& aResourceId) {
|
||||
if (!mResourceIds.Contains(aResourceId)) {
|
||||
mResourceIds.AppendElement(aResourceId);
|
||||
Localization::OnChange();
|
||||
}
|
||||
return mResourceIds.Length();
|
||||
void Localization::AddResourceId(const nsAString& aResourceId) {
|
||||
NS_ConvertUTF16toUTF8 resId(aResourceId);
|
||||
ffi::localization_add_res_id(mRaw.get(), &resId);
|
||||
}
|
||||
|
||||
uint32_t Localization::RemoveResourceId(const nsAString& aResourceId) {
|
||||
if (mResourceIds.RemoveElement(aResourceId)) {
|
||||
Localization::OnChange();
|
||||
}
|
||||
return mResourceIds.Length();
|
||||
NS_ConvertUTF16toUTF8 resId(aResourceId);
|
||||
return ffi::localization_remove_res_id(mRaw.get(), &resId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Localization API
|
||||
*/
|
||||
|
||||
uint32_t Localization::AddResourceIds(const nsTArray<nsString>& aResourceIds) {
|
||||
bool added = false;
|
||||
|
||||
void Localization::AddResourceIds(const nsTArray<nsString>& aResourceIds) {
|
||||
nsTArray<nsCString> resIds(aResourceIds.Length());
|
||||
for (const auto& resId : aResourceIds) {
|
||||
if (!mResourceIds.Contains(resId)) {
|
||||
mResourceIds.AppendElement(resId);
|
||||
added = true;
|
||||
}
|
||||
resIds.AppendElement(NS_ConvertUTF16toUTF8(resId));
|
||||
}
|
||||
if (added) {
|
||||
Localization::OnChange();
|
||||
}
|
||||
return mResourceIds.Length();
|
||||
ffi::localization_add_res_ids(mRaw.get(), &resIds);
|
||||
}
|
||||
|
||||
uint32_t Localization::RemoveResourceIds(
|
||||
const nsTArray<nsString>& aResourceIds) {
|
||||
bool removed = false;
|
||||
|
||||
nsTArray<nsCString> resIds(aResourceIds.Length());
|
||||
for (const auto& resId : aResourceIds) {
|
||||
if (mResourceIds.RemoveElement(resId)) {
|
||||
removed = true;
|
||||
}
|
||||
resIds.AppendElement(NS_ConvertUTF16toUTF8(resId));
|
||||
}
|
||||
if (removed) {
|
||||
Localization::OnChange();
|
||||
}
|
||||
return mResourceIds.Length();
|
||||
return ffi::localization_remove_res_ids(mRaw.get(), &resIds);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Localization::FormatValue(
|
||||
JSContext* aCx, const nsACString& aId, const Optional<L10nArgs>& aArgs,
|
||||
ErrorResult& aRv) {
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
JS::Rooted<JS::Value> args(aCx);
|
||||
const nsACString& aId, const Optional<L10nArgs>& aArgs, ErrorResult& aRv) {
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
nsTArray<nsCString> errors;
|
||||
|
||||
if (aArgs.WasPassed()) {
|
||||
ConvertL10nArgsToJSValue(aCx, aArgs.Value(), &args, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
args = JS::UndefinedValue();
|
||||
const L10nArgs& args = aArgs.Value();
|
||||
FluentBundle::ConvertArgs(args, l10nArgs);
|
||||
}
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
|
||||
|
||||
ffi::localization_format_value(
|
||||
mRaw.get(), &aId, &l10nArgs, promise,
|
||||
[](const Promise* aPromise, const nsACString* aValue,
|
||||
const nsTArray<nsCString>* aErrors) {
|
||||
Promise* promise = const_cast<Promise*>(aPromise);
|
||||
|
||||
if (!aErrors->IsEmpty()) {
|
||||
ErrorResult rv;
|
||||
rv.ThrowInvalidStateError(aErrors->ElementAt(0));
|
||||
promise->MaybeReject(std::move(rv));
|
||||
} else {
|
||||
promise->MaybeResolve(aValue);
|
||||
}
|
||||
});
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
nsresult rv = mLocalization->FormatValue(mResourceIds, bundlesJS, aId, args,
|
||||
getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
return MaybeWrapPromise(promise);
|
||||
}
|
||||
|
||||
void Localization::SetIsSync(const bool aIsSync) { mIsSync = aIsSync; }
|
||||
|
||||
already_AddRefed<Promise> Localization::FormatValues(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv) {
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
nsTArray<JS::Value> jsKeys;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
|
||||
for (auto& key : aKeys) {
|
||||
JS::RootedValue jsKey(aCx);
|
||||
if (!ToJSValue(aCx, key, &jsKey)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
jsKeys.AppendElement(jsKey);
|
||||
}
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys, ErrorResult& aRv) {
|
||||
nsTArray<ffi::L10nKey> l10nKeys = ConvertFromL10nKeys(aKeys);
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
aRv = mLocalization->FormatValues(mResourceIds, bundlesJS, jsKeys,
|
||||
getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ffi::localization_format_values(
|
||||
mRaw.get(), &l10nKeys, promise,
|
||||
// callback function which will be invoked by the rust code, passing the
|
||||
// promise back in.
|
||||
[](const Promise* aPromise, const nsTArray<nsCString>* aRaw) {
|
||||
Promise* promise = const_cast<Promise*>(aPromise);
|
||||
|
||||
promise->MaybeResolve(*aRaw);
|
||||
});
|
||||
|
||||
return MaybeWrapPromise(promise);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise> Localization::FormatMessages(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv) {
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
nsTArray<JS::Value> jsKeys;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
|
||||
for (auto& key : aKeys) {
|
||||
JS::RootedValue jsKey(aCx);
|
||||
if (!ToJSValue(aCx, key, &jsKey)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return nullptr;
|
||||
}
|
||||
jsKeys.AppendElement(jsKey);
|
||||
}
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys, ErrorResult& aRv) {
|
||||
auto l10nKeys = ConvertFromL10nKeys(aKeys);
|
||||
|
||||
RefPtr<Promise> promise;
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
aRv = mLocalization->FormatMessages(mResourceIds, bundlesJS, jsKeys,
|
||||
getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ffi::localization_format_messages(
|
||||
mRaw.get(), &l10nKeys, promise,
|
||||
// callback function which will be invoked by the rust code, passing the
|
||||
// promise back in.
|
||||
[](const Promise* aPromise,
|
||||
const nsTArray<ffi::OptionalL10nMessage>* aRaw,
|
||||
const nsTArray<nsCString>* aErrors) {
|
||||
Promise* promise = const_cast<Promise*>(aPromise);
|
||||
|
||||
if (!aErrors->IsEmpty()) {
|
||||
// XXX: We should consider throwing an array of errors here.
|
||||
promise->MaybeRejectWithInvalidStateError(aErrors->ElementAt(0));
|
||||
} else {
|
||||
ErrorResult rv;
|
||||
FallibleTArray<Nullable<L10nMessage>> messages;
|
||||
if (aRaw) {
|
||||
messages = ConvertToL10nMessages(*aRaw, rv);
|
||||
}
|
||||
if (rv.Failed()) {
|
||||
promise->MaybeReject(std::move(rv));
|
||||
} else {
|
||||
promise->MaybeResolve(messages);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return MaybeWrapPromise(promise);
|
||||
}
|
||||
|
||||
void Localization::FormatValueSync(JSContext* aCx, const nsACString& aId,
|
||||
void Localization::FormatValueSync(const nsACString& aId,
|
||||
const Optional<L10nArgs>& aArgs,
|
||||
nsACString& aRetVal, ErrorResult& aRv) {
|
||||
if (!mIsSync) {
|
||||
aRv.ThrowInvalidStateError(
|
||||
"Can't use formatValueSync when state is async.");
|
||||
return;
|
||||
}
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
JS::Rooted<JS::Value> args(aCx);
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
nsTArray<nsCString> errors;
|
||||
|
||||
if (aArgs.WasPassed()) {
|
||||
ConvertL10nArgsToJSValue(aCx, aArgs.Value(), &args, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
args = JS::UndefinedValue();
|
||||
const L10nArgs& args = aArgs.Value();
|
||||
FluentBundle::ConvertArgs(args, l10nArgs);
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
aRv = mLocalization->FormatValueSync(mResourceIds, bundlesJS, aId, args,
|
||||
aRetVal);
|
||||
ffi::localization_format_value_sync(mRaw.get(), &aId, &l10nArgs, &aRetVal,
|
||||
&errors);
|
||||
|
||||
if (!errors.IsEmpty()) {
|
||||
aRv.ThrowInvalidStateError(errors.ElementAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
void Localization::FormatValuesSync(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<nsCString>& aRetVal, ErrorResult& aRv) {
|
||||
if (!mIsSync) {
|
||||
aRv.ThrowInvalidStateError(
|
||||
"Can't use formatValuesSync when state is async.");
|
||||
return;
|
||||
}
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
nsTArray<JS::Value> jsKeys;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
|
||||
for (auto& key : aKeys) {
|
||||
JS::RootedValue jsKey(aCx);
|
||||
if (!ToJSValue(aCx, key, &jsKey)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
jsKeys.AppendElement(jsKey);
|
||||
}
|
||||
nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
|
||||
nsTArray<nsCString> errors;
|
||||
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
aRv =
|
||||
mLocalization->FormatValuesSync(mResourceIds, bundlesJS, jsKeys, aRetVal);
|
||||
}
|
||||
|
||||
void Localization::FormatMessagesSync(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<Nullable<L10nMessage>>& aRetVal, ErrorResult& aRv) {
|
||||
if (!mIsSync) {
|
||||
aRv.ThrowInvalidStateError(
|
||||
"Can't use formatMessagesSync when state is async.");
|
||||
return;
|
||||
}
|
||||
if (!mLocalization) {
|
||||
Activate(false);
|
||||
}
|
||||
nsTArray<JS::Value> jsKeys;
|
||||
SequenceRooter<JS::Value> rooter(aCx, &jsKeys);
|
||||
for (auto& key : aKeys) {
|
||||
JS::RootedValue jsKey(aCx);
|
||||
if (!ToJSValue(aCx, key, &jsKey)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
jsKeys.AppendElement(jsKey);
|
||||
}
|
||||
|
||||
nsTArray<JS::Value> messages;
|
||||
|
||||
SequenceRooter<JS::Value> messagesRooter(aCx, &messages);
|
||||
JS::Rooted<JS::Value> bundlesJS(aCx, mBundles);
|
||||
aRv = mLocalization->FormatMessagesSync(mResourceIds, bundlesJS, jsKeys,
|
||||
messages);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
JS::Rooted<JS::Value> rootedMsg(aCx);
|
||||
for (auto& msg : messages) {
|
||||
rootedMsg.set(msg);
|
||||
Nullable<L10nMessage>* slotPtr = aRetVal.AppendElement(mozilla::fallible);
|
||||
if (!slotPtr) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootedMsg.isNull()) {
|
||||
slotPtr->SetNull();
|
||||
for (const auto& entry : aKeys) {
|
||||
if (entry.IsUTF8String()) {
|
||||
const auto& id = entry.GetAsUTF8String();
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &id;
|
||||
} else {
|
||||
JS_WrapValue(aCx, &rootedMsg);
|
||||
if (!slotPtr->SetValue().Init(aCx, rootedMsg)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
const auto& e = entry.GetAsL10nIdArgs();
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &e.mId;
|
||||
if (!e.mArgs.IsNull()) {
|
||||
FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ffi::localization_format_values_sync(mRaw.get(), &l10nKeys, &aRetVal,
|
||||
&errors);
|
||||
|
||||
if (!errors.IsEmpty()) {
|
||||
aRv.ThrowInvalidStateError(errors.ElementAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
void Localization::FormatMessagesSync(
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<Nullable<L10nMessage>>& aRetVal, ErrorResult& aRv) {
|
||||
nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
|
||||
nsTArray<nsCString> errors;
|
||||
|
||||
for (const auto& entry : aKeys) {
|
||||
if (entry.IsUTF8String()) {
|
||||
const auto& id = entry.GetAsUTF8String();
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &id;
|
||||
} else {
|
||||
const auto& e = entry.GetAsL10nIdArgs();
|
||||
nsTArray<ffi::L10nArg> l10nArgs;
|
||||
ffi::L10nKey* key = l10nKeys.AppendElement();
|
||||
key->id = &e.mId;
|
||||
if (!e.mArgs.IsNull()) {
|
||||
FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsTArray<ffi::OptionalL10nMessage> result(l10nKeys.Length());
|
||||
|
||||
ffi::localization_format_messages_sync(mRaw.get(), &l10nKeys, &result,
|
||||
&errors);
|
||||
|
||||
if (!errors.IsEmpty()) {
|
||||
aRv.ThrowInvalidStateError(errors.ElementAt(0));
|
||||
return;
|
||||
}
|
||||
|
||||
aRetVal = ConvertToL10nMessages(result, aRv);
|
||||
}
|
||||
|
||||
void Localization::Upgrade() { ffi::localization_upgrade(mRaw.get()); }
|
||||
|
||||
/**
|
||||
* PromiseResolver is a PromiseNativeHandler used
|
||||
* by MaybeWrapPromise method.
|
||||
@ -501,33 +433,3 @@ already_AddRefed<Promise> Localization::MaybeWrapPromise(
|
||||
aInnerPromise->AppendNativeHandler(resolver);
|
||||
return docPromise.forget();
|
||||
}
|
||||
|
||||
void Localization::ConvertL10nArgsToJSValue(
|
||||
JSContext* aCx, const L10nArgs& aArgs, JS::MutableHandle<JS::Value> aRetVal,
|
||||
ErrorResult& aRv) {
|
||||
// This method uses a temporary dictionary to automate
|
||||
// converting an IDL Record to a JS Value via a dictionary.
|
||||
//
|
||||
// Once we get ToJSValue for Record, we'll switch to that.
|
||||
L10nArgsHelperDict helperDict;
|
||||
for (auto& entry : aArgs.Entries()) {
|
||||
L10nArgs::EntryType* newEntry =
|
||||
helperDict.mArgs.Entries().AppendElement(fallible);
|
||||
if (!newEntry) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
newEntry->mKey = entry.mKey;
|
||||
newEntry->mValue = entry.mValue;
|
||||
}
|
||||
JS::Rooted<JS::Value> jsVal(aCx);
|
||||
if (!ToJSValue(aCx, helperDict, &jsVal)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
JS::Rooted<JSObject*> jsObj(aCx, &jsVal.toObject());
|
||||
if (!JS_GetProperty(aCx, jsObj, "args", aRetVal)) {
|
||||
aRv.Throw(NS_ERROR_UNEXPECTED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -7,109 +7,83 @@
|
||||
#ifndef mozilla_intl_l10n_Localization_h
|
||||
#define mozilla_intl_l10n_Localization_h
|
||||
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "mozILocalization.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/LocalizationBinding.h"
|
||||
#include "mozilla/dom/PromiseNativeHandler.h"
|
||||
|
||||
class nsIGlobalObject;
|
||||
|
||||
using namespace mozilla::dom;
|
||||
#include "mozilla/intl/LocalizationBindings.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
|
||||
namespace intl {
|
||||
|
||||
typedef Record<nsCString, Nullable<OwningUTF8StringOrDouble>> L10nArgs;
|
||||
class Localization : public nsIObserver, public nsWrapperCache {
|
||||
template <typename T, typename... Args>
|
||||
friend already_AddRefed<T> mozilla::MakeAndAddRef(Args&&... aArgs);
|
||||
|
||||
class Localization : public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
public nsWrapperCache {
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Localization,
|
||||
nsIObserver)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Localization)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static already_AddRefed<Localization> Create(
|
||||
nsIGlobalObject* aGlobal, const bool aSync,
|
||||
const BundleGenerator& aBundleGenerator);
|
||||
|
||||
void Activate(const bool aEager);
|
||||
|
||||
void Destroy();
|
||||
|
||||
static already_AddRefed<Localization> Constructor(
|
||||
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
|
||||
const bool aSync, const BundleGenerator& aBundleGenerator,
|
||||
const dom::GlobalObject& aGlobal,
|
||||
const dom::Sequence<nsCString>& aResourceIds, bool aIsSync,
|
||||
ErrorResult& aRv);
|
||||
static already_AddRefed<Localization> Create(
|
||||
const nsTArray<nsCString>& aResourceIds, bool aIsSync);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
nsIGlobalObject* GetParentObject() const { return mGlobal; }
|
||||
|
||||
void SetIsSync(bool aIsSync);
|
||||
|
||||
already_AddRefed<dom::Promise> FormatValue(
|
||||
const nsACString& aId, const dom::Optional<L10nArgs>& aArgs,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsIGlobalObject* GetParentObject() const;
|
||||
already_AddRefed<dom::Promise> FormatValues(
|
||||
const dom::Sequence<dom::OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv);
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
already_AddRefed<dom::Promise> FormatMessages(
|
||||
const dom::Sequence<dom::OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv);
|
||||
|
||||
uint32_t AddResourceId(const nsAString& aResourceId);
|
||||
void FormatValueSync(const nsACString& aId,
|
||||
const dom::Optional<L10nArgs>& aArgs,
|
||||
nsACString& aRetVal, ErrorResult& aRv);
|
||||
void FormatValuesSync(
|
||||
const dom::Sequence<dom::OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<nsCString>& aRetVal, ErrorResult& aRv);
|
||||
void FormatMessagesSync(
|
||||
const dom::Sequence<dom::OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<dom::Nullable<dom::L10nMessage>>& aRetVal, ErrorResult& aRv);
|
||||
|
||||
void AddResourceId(const nsAString& aResourceId);
|
||||
uint32_t RemoveResourceId(const nsAString& aResourceId);
|
||||
|
||||
/**
|
||||
* Localization API
|
||||
*
|
||||
* Methods documentation in Localization.webidl
|
||||
*/
|
||||
uint32_t AddResourceIds(const nsTArray<nsString>& aResourceIds);
|
||||
|
||||
void AddResourceIds(const nsTArray<nsString>& aResourceIds);
|
||||
uint32_t RemoveResourceIds(const nsTArray<nsString>& aResourceIds);
|
||||
|
||||
already_AddRefed<Promise> FormatValue(JSContext* aCx, const nsACString& aId,
|
||||
const Optional<L10nArgs>& aArgs,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> FormatValues(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise> FormatMessages(
|
||||
JSContext* aCx, const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void SetIsSync(const bool aIsSync);
|
||||
|
||||
void FormatValueSync(JSContext* aCx, const nsACString& aId,
|
||||
const Optional<L10nArgs>& aArgs, nsACString& aRetVal,
|
||||
ErrorResult& aRv);
|
||||
void FormatValuesSync(JSContext* aCx,
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<nsCString>& aRetVal, ErrorResult& aRv);
|
||||
void FormatMessagesSync(JSContext* aCx,
|
||||
const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
|
||||
nsTArray<Nullable<L10nMessage>>& aRetVal,
|
||||
ErrorResult& aRv);
|
||||
void Upgrade();
|
||||
|
||||
protected:
|
||||
Localization(nsIGlobalObject* aGlobal, const bool aSync,
|
||||
const BundleGenerator& aBundleGenerator);
|
||||
virtual bool Init();
|
||||
|
||||
Localization(const nsTArray<nsCString>& aResIds, bool aIsSync);
|
||||
Localization(nsIGlobalObject* aGlobal, const nsTArray<nsCString>& aResIds,
|
||||
bool aIsSync);
|
||||
explicit Localization(nsIGlobalObject* aGlobal);
|
||||
Localization(nsIGlobalObject* aGlobal, bool aIsSync);
|
||||
virtual ~Localization();
|
||||
void RegisterObservers();
|
||||
|
||||
virtual void OnChange();
|
||||
already_AddRefed<Promise> MaybeWrapPromise(Promise* aInnerPromise);
|
||||
void ConvertL10nArgsToJSValue(JSContext* aCx, const L10nArgs& aArgs,
|
||||
JS::MutableHandle<JS::Value> aRetVal,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<dom::Promise> MaybeWrapPromise(dom::Promise* aInnerPromise);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||
nsCOMPtr<mozILocalization> mLocalization;
|
||||
|
||||
RefPtr<const ffi::LocalizationRc> mRaw;
|
||||
bool mIsSync;
|
||||
nsTArray<nsString> mResourceIds;
|
||||
|
||||
JS::Heap<JS::Value> mBundles;
|
||||
JS::Heap<JS::Value> mGenerateBundles;
|
||||
JS::Heap<JS::Value> mGenerateBundlesSync;
|
||||
};
|
||||
|
||||
} // namespace intl
|
||||
|
@ -301,7 +301,7 @@ impl LocalizationRc {
|
||||
&self,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
|
||||
callback: extern "C" fn(&xpcom::Promise, &ThinVec<nsCString>),
|
||||
) {
|
||||
let bundles = self.inner.borrow().bundles().clone();
|
||||
|
||||
@ -326,7 +326,7 @@ impl LocalizationRc {
|
||||
})
|
||||
.collect::<ThinVec<_>>();
|
||||
|
||||
callback(&strong_promise, Some(&ret_val));
|
||||
callback(&strong_promise, &ret_val);
|
||||
})
|
||||
.expect("Failed to spawn future");
|
||||
}
|
||||
@ -337,7 +337,7 @@ impl LocalizationRc {
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(
|
||||
&xpcom::Promise,
|
||||
Option<&ThinVec<OptionalL10nMessage>>,
|
||||
&ThinVec<OptionalL10nMessage>,
|
||||
&ThinVec<nsCString>,
|
||||
),
|
||||
) {
|
||||
@ -375,7 +375,7 @@ impl LocalizationRc {
|
||||
.map(|err| err.to_string().into())
|
||||
.collect();
|
||||
|
||||
callback(&strong_promise, Some(&ret_val), &errors);
|
||||
callback(&strong_promise, &ret_val, &errors);
|
||||
})
|
||||
.expect("Failed to spawn future");
|
||||
}
|
||||
@ -483,7 +483,7 @@ pub extern "C" fn localization_format_values(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(&xpcom::Promise, Option<&ThinVec<nsCString>>),
|
||||
callback: extern "C" fn(&xpcom::Promise, &ThinVec<nsCString>),
|
||||
) {
|
||||
loc.format_values(keys, promise, callback);
|
||||
}
|
||||
@ -493,11 +493,7 @@ pub extern "C" fn localization_format_messages(
|
||||
loc: &LocalizationRc,
|
||||
keys: &ThinVec<L10nKey>,
|
||||
promise: &xpcom::Promise,
|
||||
callback: extern "C" fn(
|
||||
&xpcom::Promise,
|
||||
Option<&ThinVec<OptionalL10nMessage>>,
|
||||
&ThinVec<nsCString>,
|
||||
),
|
||||
callback: extern "C" fn(&xpcom::Promise, &ThinVec<OptionalL10nMessage>, &ThinVec<nsCString>),
|
||||
) {
|
||||
loc.format_messages(keys, promise, callback);
|
||||
}
|
||||
|
@ -133,9 +133,10 @@ void nsPrinterListBase::EnsureCommonPaperInfo(JSContext* aCx) {
|
||||
// available (otherwise leave them as the internal keys, which are at least
|
||||
// somewhat recognizable).
|
||||
IgnoredErrorResult rv;
|
||||
nsCOMPtr<nsIGlobalObject> global = xpc::CurrentNativeGlobal(aCx);
|
||||
RefPtr<Localization> l10n = Localization::Create(global, true, {});
|
||||
l10n->AddResourceId(u"toolkit/printing/printUI.ftl"_ns);
|
||||
nsTArray<nsCString> resIds = {
|
||||
"toolkit/printing/printUI.ftl"_ns,
|
||||
};
|
||||
RefPtr<Localization> l10n = Localization::Create(resIds, true);
|
||||
|
||||
for (auto i : IntegerRange(nsPaper::kNumCommonPaperSizes)) {
|
||||
const CommonPaperSize& size = nsPaper::kCommonPaperSizes[i];
|
||||
@ -144,7 +145,7 @@ void nsPrinterListBase::EnsureCommonPaperInfo(JSContext* aCx) {
|
||||
nsAutoCString key{"printui-paper-"};
|
||||
key.Append(size.mLocalizableNameKey);
|
||||
nsAutoCString name;
|
||||
l10n->FormatValueSync(aCx, key, {}, name, rv);
|
||||
l10n->FormatValueSync(key, {}, name, rv);
|
||||
|
||||
// Fill out the info with our PWG size and the localized name.
|
||||
info.mId = size.mPWGName;
|
||||
|
Loading…
Reference in New Issue
Block a user