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
8 #include "js/JSON.h" // JS_ParseJSON
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(); }
100 * DOMLocalization API
103 void DOMLocalization::ConnectRoot(nsINode
& aNode
, ErrorResult
& aRv
) {
104 nsCOMPtr
<nsIGlobalObject
> global
= aNode
.GetOwnerGlobal();
108 MOZ_ASSERT(global
== mGlobal
,
109 "Cannot add a root that overlaps with existing root.");
112 for (nsINode
* root
: mRoots
) {
114 root
!= &aNode
&& !root
->Contains(&aNode
) && !aNode
.Contains(root
),
115 "Cannot add a root that overlaps with existing root.");
119 mRoots
.Insert(&aNode
);
121 aNode
.AddMutationObserverUnlessExists(mMutations
);
124 void DOMLocalization::DisconnectRoot(nsINode
& aNode
, ErrorResult
& aRv
) {
125 if (mRoots
.Contains(&aNode
)) {
126 aNode
.RemoveMutationObserver(mMutations
);
127 mRoots
.Remove(&aNode
);
131 void DOMLocalization::PauseObserving(ErrorResult
& aRv
) {
132 mMutations
->PauseObserving();
135 void DOMLocalization::ResumeObserving(ErrorResult
& aRv
) {
136 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 (!aElement
.AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datal10nid
, aId
,
144 aElement
.SetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nid
, aId
, true);
147 if (aArgs
.WasPassed() && aArgs
.Value()) {
149 JS::Rooted
<JS::Value
> val(aCx
, JS::ObjectValue(*aArgs
.Value()));
150 if (!nsContentUtils::StringifyJSON(aCx
, &val
, data
)) {
151 aRv
.NoteJSContextException(aCx
);
154 if (!aElement
.AttrValueIs(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
,
156 aElement
.SetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, data
, true);
159 aElement
.UnsetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, true);
163 void DOMLocalization::GetAttributes(Element
& aElement
, L10nIdArgs
& aResult
,
166 nsAutoString l10nArgs
;
168 if (aElement
.GetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nid
, l10nId
)) {
169 CopyUTF16toUTF8(l10nId
, aResult
.mId
);
172 if (aElement
.GetAttr(kNameSpaceID_None
, nsGkAtoms::datal10nargs
, l10nArgs
)) {
173 ConvertStringToL10nArgs(l10nArgs
, aResult
.mArgs
.SetValue(), aRv
);
177 already_AddRefed
<Promise
> DOMLocalization::TranslateFragment(nsINode
& aNode
,
179 Sequence
<OwningNonNull
<Element
>> elements
;
180 GetTranslatables(aNode
, elements
, aRv
);
181 if (NS_WARN_IF(aRv
.Failed())) {
184 return TranslateElements(elements
, aRv
);
188 * A Promise Handler used to apply the result of
189 * a call to Localization::FormatMessages onto the list
190 * of translatable elements.
192 class ElementTranslationHandler
: public PromiseNativeHandler
{
194 explicit ElementTranslationHandler(DOMLocalization
* aDOMLocalization
,
195 nsXULPrototypeDocument
* aProto
)
196 : mDOMLocalization(aDOMLocalization
), mProto(aProto
){};
198 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
199 NS_DECL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler
)
201 nsTArray
<nsCOMPtr
<Element
>>& Elements() { return mElements
; }
203 void SetReturnValuePromise(Promise
* aReturnValuePromise
) {
204 mReturnValuePromise
= aReturnValuePromise
;
207 virtual void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
208 ErrorResult
& aRv
) override
{
211 nsTArray
<Nullable
<L10nMessage
>> l10nData
;
212 if (aValue
.isObject()) {
213 JS::ForOfIterator
iter(aCx
);
214 if (!iter
.init(aValue
, JS::ForOfIterator::AllowNonIterable
)) {
215 mReturnValuePromise
->MaybeRejectWithUndefined();
218 if (!iter
.valueIsIterable()) {
219 mReturnValuePromise
->MaybeRejectWithUndefined();
223 JS::Rooted
<JS::Value
> temp(aCx
);
226 if (!iter
.next(&temp
, &done
)) {
227 mReturnValuePromise
->MaybeRejectWithUndefined();
235 Nullable
<L10nMessage
>* slotPtr
=
236 l10nData
.AppendElement(mozilla::fallible
);
238 mReturnValuePromise
->MaybeRejectWithUndefined();
242 if (!temp
.isNull()) {
243 if (!slotPtr
->SetValue().Init(aCx
, temp
)) {
244 mReturnValuePromise
->MaybeRejectWithUndefined();
252 mDOMLocalization
->ApplyTranslations(mElements
, l10nData
, mProto
, rv
);
253 if (NS_WARN_IF(rv
.Failed()) || !allTranslated
) {
254 mReturnValuePromise
->MaybeRejectWithUndefined();
258 mReturnValuePromise
->MaybeResolveWithUndefined();
261 virtual void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
262 ErrorResult
& aRv
) override
{
263 mReturnValuePromise
->MaybeRejectWithClone(aCx
, aValue
);
267 ~ElementTranslationHandler() = default;
269 nsTArray
<nsCOMPtr
<Element
>> mElements
;
270 RefPtr
<DOMLocalization
> mDOMLocalization
;
271 RefPtr
<Promise
> mReturnValuePromise
;
272 RefPtr
<nsXULPrototypeDocument
> mProto
;
275 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ElementTranslationHandler
)
276 NS_INTERFACE_MAP_ENTRY(nsISupports
)
279 NS_IMPL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler
)
281 NS_IMPL_CYCLE_COLLECTING_ADDREF(ElementTranslationHandler
)
282 NS_IMPL_CYCLE_COLLECTING_RELEASE(ElementTranslationHandler
)
284 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ElementTranslationHandler
)
285 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements
)
286 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMLocalization
)
287 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise
)
288 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProto
)
289 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
291 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ElementTranslationHandler
)
292 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements
)
293 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMLocalization
)
294 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise
)
295 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProto
)
296 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
298 already_AddRefed
<Promise
> DOMLocalization::TranslateElements(
299 const nsTArray
<OwningNonNull
<Element
>>& aElements
, ErrorResult
& aRv
) {
300 return TranslateElements(aElements
, nullptr, aRv
);
303 already_AddRefed
<Promise
> DOMLocalization::TranslateElements(
304 const nsTArray
<OwningNonNull
<Element
>>& aElements
,
305 nsXULPrototypeDocument
* aProto
, ErrorResult
& aRv
) {
306 Sequence
<OwningUTF8StringOrL10nIdArgs
> l10nKeys
;
307 RefPtr
<ElementTranslationHandler
> nativeHandler
=
308 new ElementTranslationHandler(this, aProto
);
309 nsTArray
<nsCOMPtr
<Element
>>& domElements
= nativeHandler
->Elements();
310 domElements
.SetCapacity(aElements
.Length());
313 aRv
.Throw(NS_ERROR_UNEXPECTED
);
317 for (auto& domElement
: aElements
) {
318 if (!domElement
->HasAttr(nsGkAtoms::datal10nid
)) {
322 OwningUTF8StringOrL10nIdArgs
* key
= l10nKeys
.AppendElement(fallible
);
324 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
328 GetAttributes(*domElement
, key
->SetAsL10nIdArgs(), aRv
);
329 if (NS_WARN_IF(aRv
.Failed())) {
333 if (!domElements
.AppendElement(domElement
, fallible
)) {
334 // This can't really happen, we SetCapacity'd above...
335 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
340 RefPtr
<Promise
> promise
= Promise::Create(mGlobal
, aRv
);
341 if (NS_WARN_IF(aRv
.Failed())) {
346 nsTArray
<Nullable
<L10nMessage
>> l10nMessages
;
348 FormatMessagesSync(l10nKeys
, l10nMessages
, aRv
);
351 ApplyTranslations(domElements
, l10nMessages
, aProto
, aRv
);
352 if (NS_WARN_IF(aRv
.Failed()) || !allTranslated
) {
353 promise
->MaybeRejectWithUndefined();
354 return MaybeWrapPromise(promise
);
357 promise
->MaybeResolveWithUndefined();
359 RefPtr
<Promise
> callbackResult
= FormatMessages(l10nKeys
, aRv
);
360 if (NS_WARN_IF(aRv
.Failed())) {
363 nativeHandler
->SetReturnValuePromise(promise
);
364 callbackResult
->AppendNativeHandler(nativeHandler
);
367 return MaybeWrapPromise(promise
);
371 * Promise handler used to set localization data on
372 * roots of elements that got successfully translated.
374 class L10nRootTranslationHandler final
: public PromiseNativeHandler
{
376 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
377 NS_DECL_CYCLE_COLLECTION_CLASS(L10nRootTranslationHandler
)
379 explicit L10nRootTranslationHandler(Element
* aRoot
) : mRoot(aRoot
) {}
381 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
382 ErrorResult
& aRv
) override
{
383 DOMLocalization::SetRootInfo(mRoot
);
386 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
387 ErrorResult
& aRv
) override
{}
390 ~L10nRootTranslationHandler() = default;
392 RefPtr
<Element
> mRoot
;
395 NS_IMPL_CYCLE_COLLECTION(L10nRootTranslationHandler
, mRoot
)
397 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nRootTranslationHandler
)
398 NS_INTERFACE_MAP_ENTRY(nsISupports
)
401 NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nRootTranslationHandler
)
402 NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nRootTranslationHandler
)
404 already_AddRefed
<Promise
> DOMLocalization::TranslateRoots(ErrorResult
& aRv
) {
405 nsTArray
<RefPtr
<Promise
>> promises
;
407 for (nsINode
* root
: mRoots
) {
408 RefPtr
<Promise
> promise
= TranslateFragment(*root
, aRv
);
409 if (MOZ_UNLIKELY(aRv
.Failed())) {
413 // If the root is an element, we'll add a native handler
414 // to set root info (language, direction etc.) on it
415 // once the localization finishes.
416 if (root
->IsElement()) {
417 RefPtr
<L10nRootTranslationHandler
> nativeHandler
=
418 new L10nRootTranslationHandler(root
->AsElement());
419 promise
->AppendNativeHandler(nativeHandler
);
422 promises
.AppendElement(promise
);
424 AutoEntryScript
aes(mGlobal
, "DOMLocalization TranslateRoots");
425 return Promise::All(aes
.cx(), promises
, aRv
);
433 void DOMLocalization::GetTranslatables(
434 nsINode
& aNode
, Sequence
<OwningNonNull
<Element
>>& aElements
,
437 aNode
.IsContent() ? aNode
.AsContent() : aNode
.GetFirstChild();
438 for (; node
; node
= node
->GetNextNode(&aNode
)) {
439 if (!node
->IsElement()) {
443 Element
* domElement
= node
->AsElement();
445 if (!domElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::datal10nid
)) {
449 if (!aElements
.AppendElement(*domElement
, fallible
)) {
450 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
457 void DOMLocalization::SetRootInfo(Element
* aElement
) {
458 nsAutoCString primaryLocale
;
459 LocaleService::GetInstance()->GetAppLocaleAsBCP47(primaryLocale
);
460 aElement
->SetAttr(kNameSpaceID_None
, nsGkAtoms::lang
,
461 NS_ConvertUTF8toUTF16(primaryLocale
), true);
464 if (LocaleService::GetInstance()->IsAppLocaleRTL()) {
465 nsGkAtoms::rtl
->ToString(dir
);
467 nsGkAtoms::ltr
->ToString(dir
);
470 uint32_t nameSpace
= aElement
->GetNameSpaceID();
472 nameSpace
== kNameSpaceID_XUL
? nsGkAtoms::localedir
: nsGkAtoms::dir
;
474 aElement
->SetAttr(kNameSpaceID_None
, dirAtom
, dir
, true);
477 bool DOMLocalization::ApplyTranslations(
478 nsTArray
<nsCOMPtr
<Element
>>& aElements
,
479 nsTArray
<Nullable
<L10nMessage
>>& aTranslations
,
480 nsXULPrototypeDocument
* aProto
, ErrorResult
& aRv
) {
481 if (aElements
.Length() != aTranslations
.Length()) {
482 aRv
.Throw(NS_ERROR_FAILURE
);
487 if (NS_WARN_IF(aRv
.Failed())) {
488 aRv
.Throw(NS_ERROR_FAILURE
);
492 bool hasMissingTranslation
= false;
494 nsTArray
<L10nOverlaysError
> errors
;
495 for (size_t i
= 0; i
< aTranslations
.Length(); ++i
) {
496 nsCOMPtr elem
= aElements
[i
];
497 if (aTranslations
[i
].IsNull()) {
498 hasMissingTranslation
= true;
501 // If we have a proto, we expect all elements are connected up.
502 // If they're not, they may have been removed by earlier translations.
503 // We will have added an error in L10nOverlays in this case.
504 // This is an error in fluent use, but shouldn't be crashing. There's
505 // also no point translating the element - skip it:
506 if (aProto
&& !elem
->IsInComposedDoc()) {
510 // It is possible that someone removed the `data-l10n-id` from the element
511 // before the async translation completed. In that case, skip applying
513 if (!elem
->HasAttr(kNameSpaceID_None
, nsGkAtoms::datal10nid
)) {
516 L10nOverlays::TranslateElement(*elem
, aTranslations
[i
].Value(), errors
,
518 if (NS_WARN_IF(aRv
.Failed())) {
519 hasMissingTranslation
= true;
523 // We only need to rebuild deep if the translation has a value.
524 // Otherwise we'll only rebuild the attributes.
525 aProto
->RebuildL10nPrototype(elem
,
526 !aTranslations
[i
].Value().mValue
.IsVoid());
530 ReportL10nOverlaysErrors(errors
);
532 ResumeObserving(aRv
);
533 if (NS_WARN_IF(aRv
.Failed())) {
534 aRv
.Throw(NS_ERROR_FAILURE
);
538 return !hasMissingTranslation
;
543 void DOMLocalization::OnChange() {
544 Localization::OnChange();
545 RefPtr
<Promise
> promise
= TranslateRoots(IgnoreErrors());
548 void DOMLocalization::DisconnectMutations() {
550 mMutations
->Disconnect();
555 void DOMLocalization::DisconnectRoots() {
556 for (nsINode
* node
: mRoots
) {
557 node
->RemoveMutationObserver(mMutations
);
562 void DOMLocalization::ReportL10nOverlaysErrors(
563 nsTArray
<L10nOverlaysError
>& aErrors
) {
566 for (auto& error
: aErrors
) {
567 if (error
.mCode
.WasPassed()) {
568 msg
= u
"[fluent-dom] "_ns
;
569 switch (error
.mCode
.Value()) {
570 case L10nOverlays_Binding::ERROR_FORBIDDEN_TYPE
:
571 msg
+= u
"An element of forbidden type \""_ns
+
572 error
.mTranslatedElementName
.Value() +
574 u
"\" was found in the translation. Only safe text-level "
575 "elements and elements with data-l10n-name are allowed.");
577 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_MISSING
:
578 msg
+= u
"An element named \""_ns
+ error
.mL10nName
.Value() +
579 u
"\" wasn't found in the source."_ns
;
581 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_TYPE_MISMATCH
:
582 msg
+= u
"An element named \""_ns
+ error
.mL10nName
.Value() +
584 u
"\" was found in the translation but its type ") +
585 error
.mTranslatedElementName
.Value() +
587 u
" didn't match the element found in the source ") +
588 error
.mSourceElementName
.Value() + u
"."_ns
;
590 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISCONNECTED
:
591 msg
+= u
"The element using message \""_ns
+ error
.mL10nName
.Value() +
593 u
"\" was removed from the DOM when translating its \"") +
594 error
.mTranslatedElementName
.Value() + u
"\" parent."_ns
;
596 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISALLOWED_DOM
:
597 msg
+= nsLiteralString(
598 u
"While translating an element with fluent ID \"") +
599 error
.mL10nName
.Value() + u
"\" a child element of type \""_ns
+
600 error
.mTranslatedElementName
.Value() +
602 u
"\" was removed. Either the fluent message "
603 "does not contain markup, or it does not contain markup "
606 case L10nOverlays_Binding::ERROR_UNKNOWN
:
608 msg
+= nsLiteralString(
609 u
"Unknown error happened while translating an element.");
612 nsPIDOMWindowInner
* innerWindow
= GetParentObject()->AsInnerWindow();
613 Document
* doc
= innerWindow
? innerWindow
->GetExtantDoc() : nullptr;
615 nsContentUtils::ReportToConsoleNonLocalized(
616 msg
, nsIScriptError::warningFlag
, "DOM"_ns
, doc
);
618 NS_WARNING("Failed to report l10n DOM Overlay errors to console.");
620 printf_stderr("%s\n", NS_ConvertUTF16toUTF8(msg
).get());
625 void DOMLocalization::ConvertStringToL10nArgs(const nsString
& aInput
,
626 intl::L10nArgs
& aRetVal
,
628 // This method uses a temporary dictionary to automate
629 // converting a JSON string into an IDL Record via a dictionary.
631 // Once we get Record::Init(const nsAString& aJSON), we'll switch to
633 L10nArgsHelperDict helperDict
;
634 if (!helperDict
.Init(u
"{\"args\": "_ns
+ aInput
+ u
"}"_ns
)) {
635 nsTArray
<nsCString
> errors
{
636 "[dom/l10n] Failed to parse l10n-args JSON: "_ns
+
637 NS_ConvertUTF16toUTF8(aInput
),
639 MaybeReportErrorsToGecko(errors
, aRv
, GetParentObject());
642 for (auto& entry
: helperDict
.mArgs
.Entries()) {
643 L10nArgs::EntryType
* newEntry
= aRetVal
.Entries().AppendElement(fallible
);
645 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
648 newEntry
->mKey
= entry
.mKey
;
649 newEntry
->mValue
= entry
.mValue
;