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/. */
7 #include "js/ForOfIterator.h" // JS::ForOfIterator
9 #include "nsContentUtils.h"
10 #include "nsIScriptError.h"
11 #include "DOMLocalization.h"
12 #include "mozilla/intl/L10nRegistry.h"
13 #include "mozilla/intl/LocaleService.h"
14 #include "mozilla/dom/AutoEntryScript.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/L10nOverlays.h"
18 using namespace mozilla
;
19 using namespace mozilla::dom
;
20 using namespace mozilla::intl
;
22 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMLocalization
)
23 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMLocalization
, Localization
)
24 tmp
->DisconnectMutations();
25 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMutations
)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoots
)
27 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
28 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMLocalization
, Localization
)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutations
)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoots
)
32 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
34 NS_IMPL_ADDREF_INHERITED(DOMLocalization
, Localization
)
35 NS_IMPL_RELEASE_INHERITED(DOMLocalization
, Localization
)
36 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMLocalization
)
37 NS_INTERFACE_MAP_END_INHERITING(Localization
)
39 DOMLocalization::DOMLocalization(nsIGlobalObject
* aGlobal
, bool aSync
)
40 : Localization(aGlobal
, aSync
) {
41 mMutations
= new L10nMutations(this);
44 DOMLocalization::DOMLocalization(nsIGlobalObject
* aGlobal
, bool aIsSync
,
45 const ffi::LocalizationRc
* aRaw
)
46 : Localization(aGlobal
, aIsSync
, aRaw
) {
47 mMutations
= new L10nMutations(this);
50 already_AddRefed
<DOMLocalization
> DOMLocalization::Constructor(
51 const GlobalObject
& aGlobal
,
52 const Sequence
<dom::OwningUTF8StringOrResourceId
>& aResourceIds
,
53 bool aIsSync
, const Optional
<NonNull
<L10nRegistry
>>& aRegistry
,
54 const Optional
<Sequence
<nsCString
>>& aLocales
, ErrorResult
& aRv
) {
55 auto ffiResourceIds
{L10nRegistry::ResourceIdsToFFI(aResourceIds
)};
56 Maybe
<nsTArray
<nsCString
>> locales
;
58 if (aLocales
.WasPassed()) {
60 locales
->SetCapacity(aLocales
.Value().Length());
61 for (const auto& locale
: aLocales
.Value()) {
62 locales
->AppendElement(locale
);
66 RefPtr
<const ffi::LocalizationRc
> raw
;
69 if (aRegistry
.WasPassed()) {
70 result
= ffi::localization_new_with_locales(
71 &ffiResourceIds
, aIsSync
, aRegistry
.Value().Raw(),
72 locales
.ptrOr(nullptr), getter_AddRefs(raw
));
74 result
= ffi::localization_new_with_locales(&ffiResourceIds
, aIsSync
,
75 nullptr, locales
.ptrOr(nullptr),
80 nsCOMPtr
<nsIGlobalObject
> global
=
81 do_QueryInterface(aGlobal
.GetAsSupports());
83 return do_AddRef(new DOMLocalization(global
, aIsSync
, raw
));
85 aRv
.ThrowInvalidStateError(
86 "Failed to create the Localization. Check the locales arguments.");
90 JSObject
* DOMLocalization::WrapObject(JSContext
* aCx
,
91 JS::Handle
<JSObject
*> aGivenProto
) {
92 return DOMLocalization_Binding::Wrap(aCx
, this, aGivenProto
);
95 void DOMLocalization::Destroy() { DisconnectMutations(); }
97 DOMLocalization::~DOMLocalization() { Destroy(); }
99 bool DOMLocalization::HasPendingMutations() const {
100 return mMutations
&& mMutations
->HasPendingMutations();
104 * DOMLocalization API
107 void DOMLocalization::ConnectRoot(nsINode
& aNode
) {
108 nsCOMPtr
<nsIGlobalObject
> global
= aNode
.GetOwnerGlobal();
112 MOZ_ASSERT(global
== mGlobal
,
113 "Cannot add a root that overlaps with existing root.");
116 for (nsINode
* root
: mRoots
) {
118 root
!= &aNode
&& !root
->Contains(&aNode
) && !aNode
.Contains(root
),
119 "Cannot add a root that overlaps with existing root.");
123 mRoots
.Insert(&aNode
);
125 aNode
.AddMutationObserverUnlessExists(mMutations
);
128 void DOMLocalization::DisconnectRoot(nsINode
& aNode
) {
129 if (mRoots
.Contains(&aNode
)) {
130 aNode
.RemoveMutationObserver(mMutations
);
131 mRoots
.Remove(&aNode
);
135 void DOMLocalization::PauseObserving() { mMutations
->PauseObserving(); }
137 void DOMLocalization::ResumeObserving() { mMutations
->ResumeObserving(); }
139 void DOMLocalization::SetAttributes(
140 JSContext
* aCx
, Element
& aElement
, const nsAString
& aId
,
141 const Optional
<JS::Handle
<JSObject
*>>& aArgs
, ErrorResult
& aRv
) {
142 if (aArgs
.WasPassed() && aArgs
.Value()) {
144 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*aArgs
.Value()));
145 if (!nsContentUtils::StringifyJSON(aCx
, val
, data
,
146 UndefinedIsNullStringLiteral
)) {
147 aRv
.NoteJSContextException(aCx
);
150 if (!aElement
.AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
,
152 aElement
.SetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
, true);
155 aElement
.UnsetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, true);
158 if (!aElement
.AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datal10nid
, aId
,
160 aElement
.SetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nid
, aId
, true);
164 void DOMLocalization::GetAttributes(Element
& aElement
, L10nIdArgs
& aResult
,
167 nsAutoString l10nArgs
;
169 if (aElement
.GetAttr(nsGkAtoms::datal10nid
, l10nId
)) {
170 CopyUTF16toUTF8(l10nId
, aResult
.mId
);
173 if (aElement
.GetAttr(nsGkAtoms::datal10nargs
, l10nArgs
)) {
174 ConvertStringToL10nArgs(l10nArgs
, aResult
.mArgs
.SetValue(), aRv
);
178 void DOMLocalization::SetArgs(JSContext
* aCx
, Element
& aElement
,
179 const Optional
<JS::Handle
<JSObject
*>>& aArgs
,
181 if (aArgs
.WasPassed() && aArgs
.Value()) {
183 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*aArgs
.Value()));
184 if (!nsContentUtils::StringifyJSON(aCx
, val
, data
,
185 UndefinedIsNullStringLiteral
)) {
186 aRv
.NoteJSContextException(aCx
);
189 if (!aElement
.AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
,
191 aElement
.SetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
, true);
194 aElement
.UnsetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, true);
198 already_AddRefed
<Promise
> DOMLocalization::TranslateFragment(nsINode
& aNode
,
200 Sequence
<OwningNonNull
<Element
>> elements
;
201 GetTranslatables(aNode
, elements
, aRv
);
202 if (NS_WARN_IF(aRv
.Failed())) {
205 return TranslateElements(elements
, aRv
);
209 * A Promise Handler used to apply the result of
210 * a call to Localization::FormatMessages onto the list
211 * of translatable elements.
213 class ElementTranslationHandler
: public PromiseNativeHandler
{
215 explicit ElementTranslationHandler(DOMLocalization
* aDOMLocalization
,
216 nsXULPrototypeDocument
* aProto
)
217 : mDOMLocalization(aDOMLocalization
), mProto(aProto
){};
219 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
220 NS_DECL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler
)
222 nsTArray
<nsCOMPtr
<Element
>>& Elements() { return mElements
; }
224 void SetReturnValuePromise(Promise
* aReturnValuePromise
) {
225 mReturnValuePromise
= aReturnValuePromise
;
228 virtual void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
229 ErrorResult
& aRv
) override
{
232 nsTArray
<Nullable
<L10nMessage
>> l10nData
;
233 if (aValue
.isObject()) {
234 JS::ForOfIterator
iter(aCx
);
235 if (!iter
.init(aValue
, JS::ForOfIterator::AllowNonIterable
)) {
236 mReturnValuePromise
->MaybeRejectWithUndefined();
239 if (!iter
.valueIsIterable()) {
240 mReturnValuePromise
->MaybeRejectWithUndefined();
244 JS::Rooted
<JS::Value
> temp(aCx
);
247 if (!iter
.next(&temp
, &done
)) {
248 mReturnValuePromise
->MaybeRejectWithUndefined();
256 Nullable
<L10nMessage
>* slotPtr
=
257 l10nData
.AppendElement(mozilla::fallible
);
259 mReturnValuePromise
->MaybeRejectWithUndefined();
263 if (!temp
.isNull()) {
264 if (!slotPtr
->SetValue().Init(aCx
, temp
)) {
265 mReturnValuePromise
->MaybeRejectWithUndefined();
273 mDOMLocalization
->ApplyTranslations(mElements
, l10nData
, mProto
, rv
);
274 if (NS_WARN_IF(rv
.Failed()) || !allTranslated
) {
275 mReturnValuePromise
->MaybeRejectWithUndefined();
279 mReturnValuePromise
->MaybeResolveWithUndefined();
282 virtual void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
283 ErrorResult
& aRv
) override
{
284 mReturnValuePromise
->MaybeRejectWithClone(aCx
, aValue
);
288 ~ElementTranslationHandler() = default;
290 nsTArray
<nsCOMPtr
<Element
>> mElements
;
291 RefPtr
<DOMLocalization
> mDOMLocalization
;
292 RefPtr
<Promise
> mReturnValuePromise
;
293 RefPtr
<nsXULPrototypeDocument
> mProto
;
296 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ElementTranslationHandler
)
297 NS_INTERFACE_MAP_ENTRY(nsISupports
)
300 NS_IMPL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler
)
302 NS_IMPL_CYCLE_COLLECTING_ADDREF(ElementTranslationHandler
)
303 NS_IMPL_CYCLE_COLLECTING_RELEASE(ElementTranslationHandler
)
305 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ElementTranslationHandler
)
306 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements
)
307 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMLocalization
)
308 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise
)
309 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProto
)
310 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
312 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ElementTranslationHandler
)
313 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements
)
314 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMLocalization
)
315 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise
)
316 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProto
)
317 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
319 already_AddRefed
<Promise
> DOMLocalization::TranslateElements(
320 const nsTArray
<OwningNonNull
<Element
>>& aElements
, ErrorResult
& aRv
) {
321 return TranslateElements(aElements
, nullptr, aRv
);
324 already_AddRefed
<Promise
> DOMLocalization::TranslateElements(
325 const nsTArray
<OwningNonNull
<Element
>>& aElements
,
326 nsXULPrototypeDocument
* aProto
, ErrorResult
& aRv
) {
327 Sequence
<OwningUTF8StringOrL10nIdArgs
> l10nKeys
;
328 RefPtr
<ElementTranslationHandler
> nativeHandler
=
329 new ElementTranslationHandler(this, aProto
);
330 nsTArray
<nsCOMPtr
<Element
>>& domElements
= nativeHandler
->Elements();
331 domElements
.SetCapacity(aElements
.Length());
334 aRv
.Throw(NS_ERROR_UNEXPECTED
);
338 for (auto& domElement
: aElements
) {
339 if (!domElement
->HasAttr(nsGkAtoms::datal10nid
)) {
343 OwningUTF8StringOrL10nIdArgs
* key
= l10nKeys
.AppendElement(fallible
);
345 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
349 GetAttributes(*domElement
, key
->SetAsL10nIdArgs(), aRv
);
350 if (NS_WARN_IF(aRv
.Failed())) {
354 if (!domElements
.AppendElement(domElement
, fallible
)) {
355 // This can't really happen, we SetCapacity'd above...
356 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
361 RefPtr
<Promise
> promise
= Promise::Create(mGlobal
, aRv
);
362 if (NS_WARN_IF(aRv
.Failed())) {
367 nsTArray
<Nullable
<L10nMessage
>> l10nMessages
;
369 FormatMessagesSync(l10nKeys
, l10nMessages
, aRv
);
371 if (NS_WARN_IF(aRv
.Failed())) {
372 promise
->MaybeRejectWithUndefined();
373 return promise
.forget();
377 ApplyTranslations(domElements
, l10nMessages
, aProto
, aRv
);
378 if (NS_WARN_IF(aRv
.Failed()) || !allTranslated
) {
379 promise
->MaybeRejectWithUndefined();
380 return promise
.forget();
383 promise
->MaybeResolveWithUndefined();
384 return promise
.forget();
386 RefPtr
<Promise
> callbackResult
= FormatMessages(l10nKeys
, aRv
);
387 if (NS_WARN_IF(aRv
.Failed())) {
390 nativeHandler
->SetReturnValuePromise(promise
);
391 callbackResult
->AppendNativeHandler(nativeHandler
);
392 return MaybeWrapPromise(promise
);
396 * Promise handler used to set localization data on
397 * roots of elements that got successfully translated.
399 class L10nRootTranslationHandler final
: public PromiseNativeHandler
{
401 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
402 NS_DECL_CYCLE_COLLECTION_CLASS(L10nRootTranslationHandler
)
404 explicit L10nRootTranslationHandler(Element
* aRoot
) : mRoot(aRoot
) {}
406 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
407 ErrorResult
& aRv
) override
{
408 DOMLocalization::SetRootInfo(mRoot
);
411 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
412 ErrorResult
& aRv
) override
{}
415 ~L10nRootTranslationHandler() = default;
417 RefPtr
<Element
> mRoot
;
420 NS_IMPL_CYCLE_COLLECTION(L10nRootTranslationHandler
, mRoot
)
422 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nRootTranslationHandler
)
423 NS_INTERFACE_MAP_ENTRY(nsISupports
)
426 NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nRootTranslationHandler
)
427 NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nRootTranslationHandler
)
429 already_AddRefed
<Promise
> DOMLocalization::TranslateRoots(ErrorResult
& aRv
) {
430 nsTArray
<RefPtr
<Promise
>> promises
;
432 for (nsINode
* root
: mRoots
) {
433 RefPtr
<Promise
> promise
= TranslateFragment(*root
, aRv
);
434 if (MOZ_UNLIKELY(aRv
.Failed())) {
438 // If the root is an element, we'll add a native handler
439 // to set root info (language, direction etc.) on it
440 // once the localization finishes.
441 if (root
->IsElement()) {
442 RefPtr
<L10nRootTranslationHandler
> nativeHandler
=
443 new L10nRootTranslationHandler(root
->AsElement());
444 promise
->AppendNativeHandler(nativeHandler
);
447 promises
.AppendElement(promise
);
449 AutoEntryScript
aes(mGlobal
, "DOMLocalization TranslateRoots");
450 return Promise::All(aes
.cx(), promises
, aRv
);
458 void DOMLocalization::GetTranslatables(
459 nsINode
& aNode
, Sequence
<OwningNonNull
<Element
>>& aElements
,
462 aNode
.IsContent() ? aNode
.AsContent() : aNode
.GetFirstChild();
463 for (; node
; node
= node
->GetNextNode(&aNode
)) {
464 if (!node
->IsElement()) {
468 Element
* domElement
= node
->AsElement();
470 if (!domElement
->HasAttr(nsGkAtoms::datal10nid
)) {
474 if (!aElements
.AppendElement(*domElement
, fallible
)) {
475 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
482 void DOMLocalization::SetRootInfo(Element
* aElement
) {
483 nsAutoCString primaryLocale
;
484 LocaleService::GetInstance()->GetAppLocaleAsBCP47(primaryLocale
);
485 aElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::lang
,
486 NS_ConvertUTF8toUTF16(primaryLocale
), true);
489 if (LocaleService::GetInstance()->IsAppLocaleRTL()) {
490 nsGkAtoms::rtl
->ToString(dir
);
492 nsGkAtoms::ltr
->ToString(dir
);
495 uint32_t nameSpace
= aElement
->GetNameSpaceID();
497 nameSpace
== kNameSpaceID_XUL
? nsGkAtoms::localedir
: nsGkAtoms::dir
;
499 aElement
->SetAttr(kNameSpaceID_None
, dirAtom
, dir
, true);
502 bool DOMLocalization::ApplyTranslations(
503 nsTArray
<nsCOMPtr
<Element
>>& aElements
,
504 nsTArray
<Nullable
<L10nMessage
>>& aTranslations
,
505 nsXULPrototypeDocument
* aProto
, ErrorResult
& aRv
) {
506 if (aElements
.Length() != aTranslations
.Length()) {
507 aRv
.Throw(NS_ERROR_FAILURE
);
513 bool hasMissingTranslation
= false;
515 nsTArray
<L10nOverlaysError
> errors
;
516 for (size_t i
= 0; i
< aTranslations
.Length(); ++i
) {
517 nsCOMPtr elem
= aElements
[i
];
518 if (aTranslations
[i
].IsNull()) {
519 hasMissingTranslation
= true;
522 // If we have a proto, we expect all elements are connected up.
523 // If they're not, they may have been removed by earlier translations.
524 // We will have added an error in L10nOverlays in this case.
525 // This is an error in fluent use, but shouldn't be crashing. There's
526 // also no point translating the element - skip it:
527 if (aProto
&& !elem
->IsInComposedDoc()) {
531 // It is possible that someone removed the `data-l10n-id` from the element
532 // before the async translation completed. In that case, skip applying
534 if (!elem
->HasAttr(nsGkAtoms::datal10nid
)) {
537 L10nOverlays::TranslateElement(*elem
, aTranslations
[i
].Value(), errors
,
539 if (NS_WARN_IF(aRv
.Failed())) {
540 hasMissingTranslation
= true;
544 // We only need to rebuild deep if the translation has a value.
545 // Otherwise we'll only rebuild the attributes.
546 aProto
->RebuildL10nPrototype(elem
,
547 !aTranslations
[i
].Value().mValue
.IsVoid());
551 ReportL10nOverlaysErrors(errors
);
555 return !hasMissingTranslation
;
560 void DOMLocalization::OnChange() {
561 Localization::OnChange();
562 RefPtr
<Promise
> promise
= TranslateRoots(IgnoreErrors());
565 void DOMLocalization::DisconnectMutations() {
567 mMutations
->Disconnect();
572 void DOMLocalization::DisconnectRoots() {
573 for (nsINode
* node
: mRoots
) {
574 node
->RemoveMutationObserver(mMutations
);
579 void DOMLocalization::ReportL10nOverlaysErrors(
580 nsTArray
<L10nOverlaysError
>& aErrors
) {
583 for (auto& error
: aErrors
) {
584 if (error
.mCode
.WasPassed()) {
585 msg
= u
"[fluent-dom] "_ns
;
586 switch (error
.mCode
.Value()) {
587 case L10nOverlays_Binding::ERROR_FORBIDDEN_TYPE
:
588 msg
+= u
"An element of forbidden type \""_ns
+
589 error
.mTranslatedElementName
.Value() +
591 u
"\" was found in the translation. Only safe text-level "
592 "elements and elements with data-l10n-name are allowed.");
594 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_MISSING
:
595 msg
+= u
"An element named \""_ns
+ error
.mL10nName
.Value() +
596 u
"\" wasn't found in the source."_ns
;
598 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_TYPE_MISMATCH
:
599 msg
+= u
"An element named \""_ns
+ error
.mL10nName
.Value() +
601 u
"\" was found in the translation but its type ") +
602 error
.mTranslatedElementName
.Value() +
604 u
" didn't match the element found in the source ") +
605 error
.mSourceElementName
.Value() + u
"."_ns
;
607 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISCONNECTED
:
608 msg
+= u
"The element using message \""_ns
+ error
.mL10nName
.Value() +
610 u
"\" was removed from the DOM when translating its \"") +
611 error
.mTranslatedElementName
.Value() + u
"\" parent."_ns
;
613 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISALLOWED_DOM
:
614 msg
+= nsLiteralString(
615 u
"While translating an element with fluent ID \"") +
616 error
.mL10nName
.Value() + u
"\" a child element of type \""_ns
+
617 error
.mTranslatedElementName
.Value() +
619 u
"\" was removed. Either the fluent message "
620 "does not contain markup, or it does not contain markup "
623 case L10nOverlays_Binding::ERROR_UNKNOWN
:
625 msg
+= nsLiteralString(
626 u
"Unknown error happened while translating an element.");
629 nsPIDOMWindowInner
* innerWindow
= GetParentObject()->GetAsInnerWindow();
630 Document
* doc
= innerWindow
? innerWindow
->GetExtantDoc() : nullptr;
632 nsContentUtils::ReportToConsoleNonLocalized(
633 msg
, nsIScriptError::warningFlag
, "DOM"_ns
, doc
);
635 NS_WARNING("Failed to report l10n DOM Overlay errors to console.");
637 printf_stderr("%s\n", NS_ConvertUTF16toUTF8(msg
).get());
642 void DOMLocalization::ConvertStringToL10nArgs(const nsString
& aInput
,
643 intl::L10nArgs
& aRetVal
,
645 if (aInput
.IsEmpty()) {
646 // There are no properties.
651 Json::Reader jsonReader
;
653 if (!jsonReader
.parse(NS_ConvertUTF16toUTF8(aInput
).get(), args
, false)) {
654 nsTArray
<nsCString
> errors
{
655 "[dom/l10n] Failed to parse l10n-args JSON: "_ns
+
656 NS_ConvertUTF16toUTF8(aInput
),
658 MaybeReportErrorsToGecko(errors
, aRv
, GetParentObject());
662 if (!args
.isObject()) {
663 nsTArray
<nsCString
> errors
{
664 "[dom/l10n] Failed to parse l10n-args JSON: "_ns
+
665 NS_ConvertUTF16toUTF8(aInput
),
667 MaybeReportErrorsToGecko(errors
, aRv
, GetParentObject());
671 for (Json::ValueConstIterator iter
= args
.begin(); iter
!= args
.end();
673 L10nArgs::EntryType
* newEntry
= aRetVal
.Entries().AppendElement(fallible
);
675 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
678 newEntry
->mKey
= iter
.name().c_str();
679 if (iter
->isString()) {
680 newEntry
->mValue
.SetValue().RawSetAsUTF8String().Assign(
681 iter
->asString().c_str(), iter
->asString().length());
682 } else if (iter
->isDouble()) {
683 newEntry
->mValue
.SetValue().RawSetAsDouble() = iter
->asDouble();
684 } else if (iter
->isBool()) {
685 if (iter
->asBool()) {
686 newEntry
->mValue
.SetValue().RawSetAsUTF8String().Assign("true");
688 newEntry
->mValue
.SetValue().RawSetAsUTF8String().Assign("false");
690 } else if (iter
->isNull()) {
691 newEntry
->mValue
.SetNull();
693 nsTArray
<nsCString
> errors
{
694 "[dom/l10n] Failed to convert l10n-args JSON: "_ns
+
695 NS_ConvertUTF16toUTF8(aInput
),
697 MaybeReportErrorsToGecko(errors
, aRv
, GetParentObject());