Bug 1700051: part 26) Correct typo in comment of `mozInlineSpellWordUtil::BuildSoftTe...
[gecko.git] / dom / l10n / DOMLocalization.cpp
blobbeb02bb64dd9e7318cebfade9a1d490e1bc4464a
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/LocaleService.h"
13 #include "mozilla/dom/AutoEntryScript.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/L10nOverlays.h"
17 using namespace mozilla;
18 using namespace mozilla::dom;
19 using namespace mozilla::intl;
21 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMLocalization)
22 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMLocalization, Localization)
23 tmp->DisconnectMutations();
24 NS_IMPL_CYCLE_COLLECTION_UNLINK(mMutations)
25 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoots)
26 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
27 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
28 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMLocalization, Localization)
29 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMutations)
30 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoots)
31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
33 NS_IMPL_ADDREF_INHERITED(DOMLocalization, Localization)
34 NS_IMPL_RELEASE_INHERITED(DOMLocalization, Localization)
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMLocalization)
36 NS_INTERFACE_MAP_END_INHERITING(Localization)
38 /* static */
39 already_AddRefed<DOMLocalization> DOMLocalization::Create(
40 nsIGlobalObject* aGlobal, const bool aSync,
41 const BundleGenerator& aBundleGenerator) {
42 RefPtr<DOMLocalization> domLoc =
43 new DOMLocalization(aGlobal, aSync, aBundleGenerator);
45 domLoc->Init();
47 return domLoc.forget();
50 DOMLocalization::DOMLocalization(nsIGlobalObject* aGlobal, const bool aSync,
51 const BundleGenerator& aBundleGenerator)
52 : Localization(aGlobal, aSync, aBundleGenerator) {
53 mMutations = new L10nMutations(this);
56 already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
57 const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,
58 const bool aSync, const BundleGenerator& aBundleGenerator,
59 ErrorResult& aRv) {
60 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
61 if (!global) {
62 aRv.Throw(NS_ERROR_FAILURE);
63 return nullptr;
66 RefPtr<DOMLocalization> domLoc =
67 DOMLocalization::Create(global, aSync, aBundleGenerator);
69 if (aResourceIds.Length()) {
70 domLoc->AddResourceIds(aResourceIds);
73 domLoc->Activate(true);
75 return domLoc.forget();
78 JSObject* DOMLocalization::WrapObject(JSContext* aCx,
79 JS::Handle<JSObject*> aGivenProto) {
80 return DOMLocalization_Binding::Wrap(aCx, this, aGivenProto);
83 void DOMLocalization::Destroy() { DisconnectMutations(); }
85 DOMLocalization::~DOMLocalization() { Destroy(); }
87 /**
88 * DOMLocalization API
91 void DOMLocalization::ConnectRoot(nsINode& aNode, ErrorResult& aRv) {
92 nsCOMPtr<nsIGlobalObject> global = aNode.GetOwnerGlobal();
93 if (!global) {
94 return;
96 MOZ_ASSERT(global == mGlobal,
97 "Cannot add a root that overlaps with existing root.");
99 #ifdef DEBUG
100 for (nsINode* root : mRoots) {
101 MOZ_ASSERT(
102 root != &aNode && !root->Contains(&aNode) && !aNode.Contains(root),
103 "Cannot add a root that overlaps with existing root.");
105 #endif
107 mRoots.Insert(&aNode);
109 aNode.AddMutationObserverUnlessExists(mMutations);
112 void DOMLocalization::DisconnectRoot(nsINode& aNode, ErrorResult& aRv) {
113 if (mRoots.Contains(&aNode)) {
114 aNode.RemoveMutationObserver(mMutations);
115 mRoots.Remove(&aNode);
119 void DOMLocalization::PauseObserving(ErrorResult& aRv) {
120 mMutations->PauseObserving();
123 void DOMLocalization::ResumeObserving(ErrorResult& aRv) {
124 mMutations->ResumeObserving();
127 void DOMLocalization::SetAttributes(
128 JSContext* aCx, Element& aElement, const nsAString& aId,
129 const Optional<JS::Handle<JSObject*>>& aArgs, ErrorResult& aRv) {
130 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::datal10nid, aId,
131 eCaseMatters)) {
132 aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::datal10nid, aId, true);
135 if (aArgs.WasPassed() && aArgs.Value()) {
136 nsAutoString data;
137 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*aArgs.Value()));
138 if (!nsContentUtils::StringifyJSON(aCx, &val, data)) {
139 aRv.NoteJSContextException(aCx);
140 return;
142 if (!aElement.AttrValueIs(kNameSpaceID_None, nsGkAtoms::datal10nargs, data,
143 eCaseMatters)) {
144 aElement.SetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, data, true);
146 } else {
147 aElement.UnsetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, true);
151 void DOMLocalization::GetAttributes(Element& aElement, L10nIdArgs& aResult,
152 ErrorResult& aRv) {
153 nsAutoString l10nId;
154 nsAutoString l10nArgs;
156 if (aElement.GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nid, l10nId)) {
157 CopyUTF16toUTF8(l10nId, aResult.mId);
160 if (aElement.GetAttr(kNameSpaceID_None, nsGkAtoms::datal10nargs, l10nArgs)) {
161 ConvertStringToL10nArgs(l10nArgs, aResult.mArgs.SetValue(), aRv);
165 already_AddRefed<Promise> DOMLocalization::TranslateFragment(nsINode& aNode,
166 ErrorResult& aRv) {
167 Sequence<OwningNonNull<Element>> elements;
169 GetTranslatables(aNode, elements, aRv);
171 return TranslateElements(elements, aRv);
175 * A Promise Handler used to apply the result of
176 * a call to Localization::FormatMessages onto the list
177 * of translatable elements.
179 class ElementTranslationHandler : public PromiseNativeHandler {
180 public:
181 explicit ElementTranslationHandler(DOMLocalization* aDOMLocalization,
182 nsXULPrototypeDocument* aProto)
183 : mDOMLocalization(aDOMLocalization), mProto(aProto){};
185 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
186 NS_DECL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler)
188 nsTArray<nsCOMPtr<Element>>& Elements() { return mElements; }
190 void SetReturnValuePromise(Promise* aReturnValuePromise) {
191 mReturnValuePromise = aReturnValuePromise;
194 virtual void ResolvedCallback(JSContext* aCx,
195 JS::Handle<JS::Value> aValue) override {
196 ErrorResult rv;
198 nsTArray<Nullable<L10nMessage>> l10nData;
199 if (aValue.isObject()) {
200 JS::ForOfIterator iter(aCx);
201 if (!iter.init(aValue, JS::ForOfIterator::AllowNonIterable)) {
202 mReturnValuePromise->MaybeRejectWithUndefined();
203 return;
205 if (!iter.valueIsIterable()) {
206 mReturnValuePromise->MaybeRejectWithUndefined();
207 return;
210 JS::Rooted<JS::Value> temp(aCx);
211 while (true) {
212 bool done;
213 if (!iter.next(&temp, &done)) {
214 mReturnValuePromise->MaybeRejectWithUndefined();
215 return;
218 if (done) {
219 break;
222 Nullable<L10nMessage>* slotPtr =
223 l10nData.AppendElement(mozilla::fallible);
224 if (!slotPtr) {
225 mReturnValuePromise->MaybeRejectWithUndefined();
226 return;
229 if (!temp.isNull()) {
230 if (!slotPtr->SetValue().Init(aCx, temp)) {
231 mReturnValuePromise->MaybeRejectWithUndefined();
232 return;
238 bool allTranslated =
239 mDOMLocalization->ApplyTranslations(mElements, l10nData, mProto, rv);
240 if (NS_WARN_IF(rv.Failed()) || !allTranslated) {
241 mReturnValuePromise->MaybeRejectWithUndefined();
242 return;
245 mReturnValuePromise->MaybeResolveWithUndefined();
248 virtual void RejectedCallback(JSContext* aCx,
249 JS::Handle<JS::Value> aValue) override {
250 mReturnValuePromise->MaybeRejectWithClone(aCx, aValue);
253 private:
254 ~ElementTranslationHandler() = default;
256 nsTArray<nsCOMPtr<Element>> mElements;
257 RefPtr<DOMLocalization> mDOMLocalization;
258 RefPtr<Promise> mReturnValuePromise;
259 RefPtr<nsXULPrototypeDocument> mProto;
262 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ElementTranslationHandler)
263 NS_INTERFACE_MAP_ENTRY(nsISupports)
264 NS_INTERFACE_MAP_END
266 NS_IMPL_CYCLE_COLLECTION_CLASS(ElementTranslationHandler)
268 NS_IMPL_CYCLE_COLLECTING_ADDREF(ElementTranslationHandler)
269 NS_IMPL_CYCLE_COLLECTING_RELEASE(ElementTranslationHandler)
271 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ElementTranslationHandler)
272 NS_IMPL_CYCLE_COLLECTION_UNLINK(mElements)
273 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMLocalization)
274 NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValuePromise)
275 NS_IMPL_CYCLE_COLLECTION_UNLINK(mProto)
276 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
278 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ElementTranslationHandler)
279 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElements)
280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMLocalization)
281 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValuePromise)
282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProto)
283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
285 already_AddRefed<Promise> DOMLocalization::TranslateElements(
286 const Sequence<OwningNonNull<Element>>& aElements, ErrorResult& aRv) {
287 return TranslateElements(aElements, nullptr, aRv);
290 already_AddRefed<Promise> DOMLocalization::TranslateElements(
291 const Sequence<OwningNonNull<Element>>& aElements,
292 nsXULPrototypeDocument* aProto, ErrorResult& aRv) {
293 JS::RootingContext* rcx = RootingCx();
294 Sequence<OwningUTF8StringOrL10nIdArgs> l10nKeys;
295 SequenceRooter<OwningUTF8StringOrL10nIdArgs> rooter(rcx, &l10nKeys);
296 RefPtr<ElementTranslationHandler> nativeHandler =
297 new ElementTranslationHandler(this, aProto);
298 nsTArray<nsCOMPtr<Element>>& domElements = nativeHandler->Elements();
299 domElements.SetCapacity(aElements.Length());
301 if (!mGlobal) {
302 return nullptr;
305 AutoEntryScript aes(mGlobal, "DOMLocalization TranslateElements");
306 JSContext* cx = aes.cx();
308 for (auto& domElement : aElements) {
309 if (!domElement->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
310 continue;
313 OwningUTF8StringOrL10nIdArgs* key = l10nKeys.AppendElement(fallible);
314 if (!key) {
315 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
316 return nullptr;
319 GetAttributes(*domElement, key->SetAsL10nIdArgs(), aRv);
320 if (NS_WARN_IF(aRv.Failed())) {
321 return nullptr;
324 if (!domElements.AppendElement(domElement, fallible)) {
325 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
326 return nullptr;
330 RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
331 if (NS_WARN_IF(aRv.Failed())) {
332 return nullptr;
335 if (mIsSync) {
336 nsTArray<Nullable<L10nMessage>> l10nMessages;
338 FormatMessagesSync(cx, l10nKeys, l10nMessages, aRv);
340 bool allTranslated =
341 ApplyTranslations(domElements, l10nMessages, aProto, aRv);
342 if (NS_WARN_IF(aRv.Failed()) || !allTranslated) {
343 promise->MaybeRejectWithUndefined();
344 return MaybeWrapPromise(promise);
347 promise->MaybeResolveWithUndefined();
348 } else {
349 RefPtr<Promise> callbackResult = FormatMessages(cx, l10nKeys, aRv);
350 if (NS_WARN_IF(aRv.Failed())) {
351 return nullptr;
353 nativeHandler->SetReturnValuePromise(promise);
354 callbackResult->AppendNativeHandler(nativeHandler);
357 return MaybeWrapPromise(promise);
361 * Promise handler used to set localization data on
362 * roots of elements that got successfully translated.
364 class L10nRootTranslationHandler final : public PromiseNativeHandler {
365 public:
366 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
367 NS_DECL_CYCLE_COLLECTION_CLASS(L10nRootTranslationHandler)
369 explicit L10nRootTranslationHandler(Element* aRoot) : mRoot(aRoot) {}
371 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
372 DOMLocalization::SetRootInfo(mRoot);
375 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
378 private:
379 ~L10nRootTranslationHandler() = default;
381 RefPtr<Element> mRoot;
384 NS_IMPL_CYCLE_COLLECTION(L10nRootTranslationHandler, mRoot)
386 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(L10nRootTranslationHandler)
387 NS_INTERFACE_MAP_ENTRY(nsISupports)
388 NS_INTERFACE_MAP_END
390 NS_IMPL_CYCLE_COLLECTING_ADDREF(L10nRootTranslationHandler)
391 NS_IMPL_CYCLE_COLLECTING_RELEASE(L10nRootTranslationHandler)
393 already_AddRefed<Promise> DOMLocalization::TranslateRoots(ErrorResult& aRv) {
394 nsTArray<RefPtr<Promise>> promises;
396 for (nsINode* root : mRoots) {
397 RefPtr<Promise> promise = TranslateFragment(*root, aRv);
399 // If the root is an element, we'll add a native handler
400 // to set root info (language, direction etc.) on it
401 // once the localization finishes.
402 if (root->IsElement()) {
403 RefPtr<L10nRootTranslationHandler> nativeHandler =
404 new L10nRootTranslationHandler(root->AsElement());
405 promise->AppendNativeHandler(nativeHandler);
408 promises.AppendElement(promise);
410 AutoEntryScript aes(mGlobal, "DOMLocalization TranslateRoots");
411 return Promise::All(aes.cx(), promises, aRv);
415 * Helper methods
418 /* static */
419 void DOMLocalization::GetTranslatables(
420 nsINode& aNode, Sequence<OwningNonNull<Element>>& aElements,
421 ErrorResult& aRv) {
422 nsIContent* node =
423 aNode.IsContent() ? aNode.AsContent() : aNode.GetFirstChild();
424 for (; node; node = node->GetNextNode(&aNode)) {
425 if (!node->IsElement()) {
426 continue;
429 Element* domElement = node->AsElement();
431 if (!domElement->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nid)) {
432 continue;
435 if (!aElements.AppendElement(*domElement, fallible)) {
436 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
437 return;
442 /* static */
443 void DOMLocalization::SetRootInfo(Element* aElement) {
444 nsAutoCString primaryLocale;
445 LocaleService::GetInstance()->GetAppLocaleAsBCP47(primaryLocale);
446 aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::lang,
447 NS_ConvertUTF8toUTF16(primaryLocale), true);
449 nsAutoString dir;
450 if (LocaleService::GetInstance()->IsAppLocaleRTL()) {
451 nsGkAtoms::rtl->ToString(dir);
452 } else {
453 nsGkAtoms::ltr->ToString(dir);
456 uint32_t nameSpace = aElement->GetNameSpaceID();
457 nsAtom* dirAtom =
458 nameSpace == kNameSpaceID_XUL ? nsGkAtoms::localedir : nsGkAtoms::dir;
460 aElement->SetAttr(kNameSpaceID_None, dirAtom, dir, true);
463 bool DOMLocalization::ApplyTranslations(
464 nsTArray<nsCOMPtr<Element>>& aElements,
465 nsTArray<Nullable<L10nMessage>>& aTranslations,
466 nsXULPrototypeDocument* aProto, ErrorResult& aRv) {
467 if (aElements.Length() != aTranslations.Length()) {
468 aRv.Throw(NS_ERROR_FAILURE);
469 return false;
472 PauseObserving(aRv);
473 if (NS_WARN_IF(aRv.Failed())) {
474 aRv.Throw(NS_ERROR_FAILURE);
475 return false;
478 bool hasMissingTranslation = false;
480 nsTArray<L10nOverlaysError> errors;
481 for (size_t i = 0; i < aTranslations.Length(); ++i) {
482 Element* elem = aElements[i];
483 if (aTranslations[i].IsNull()) {
484 hasMissingTranslation = true;
485 continue;
487 // If we have a proto, we expect all elements are connected up.
488 // If they're not, they may have been removed by earlier translations.
489 // We will have added an error in L10nOverlays in this case.
490 // This is an error in fluent use, but shouldn't be crashing. There's
491 // also no point translating the element - skip it:
492 if (aProto && !elem->IsInComposedDoc()) {
493 continue;
495 L10nOverlays::TranslateElement(*elem, aTranslations[i].Value(), errors,
496 aRv);
497 if (NS_WARN_IF(aRv.Failed())) {
498 hasMissingTranslation = true;
499 continue;
501 if (aProto) {
502 // We only need to rebuild deep if the translation has a value.
503 // Otherwise we'll only rebuild the attributes.
504 aProto->RebuildL10nPrototype(elem,
505 !aTranslations[i].Value().mValue.IsVoid());
509 ReportL10nOverlaysErrors(errors);
511 ResumeObserving(aRv);
512 if (NS_WARN_IF(aRv.Failed())) {
513 aRv.Throw(NS_ERROR_FAILURE);
514 return false;
517 return !hasMissingTranslation;
520 /* Protected */
522 void DOMLocalization::OnChange() {
523 Localization::OnChange();
524 if (mLocalization && !mResourceIds.IsEmpty()) {
525 ErrorResult rv;
526 RefPtr<Promise> promise = TranslateRoots(rv);
530 void DOMLocalization::DisconnectMutations() {
531 if (mMutations) {
532 mMutations->Disconnect();
533 DisconnectRoots();
537 void DOMLocalization::DisconnectRoots() {
538 for (nsINode* node : mRoots) {
539 node->RemoveMutationObserver(mMutations);
541 mRoots.Clear();
544 void DOMLocalization::ReportL10nOverlaysErrors(
545 nsTArray<L10nOverlaysError>& aErrors) {
546 nsAutoString msg;
548 for (auto& error : aErrors) {
549 if (error.mCode.WasPassed()) {
550 msg = u"[fluent-dom] "_ns;
551 switch (error.mCode.Value()) {
552 case L10nOverlays_Binding::ERROR_FORBIDDEN_TYPE:
553 msg += u"An element of forbidden type \""_ns +
554 error.mTranslatedElementName.Value() +
555 nsLiteralString(
556 u"\" was found in the translation. Only safe text-level "
557 "elements and elements with data-l10n-name are allowed.");
558 break;
559 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_MISSING:
560 msg += u"An element named \""_ns + error.mL10nName.Value() +
561 u"\" wasn't found in the source."_ns;
562 break;
563 case L10nOverlays_Binding::ERROR_NAMED_ELEMENT_TYPE_MISMATCH:
564 msg += u"An element named \""_ns + error.mL10nName.Value() +
565 nsLiteralString(
566 u"\" was found in the translation but its type ") +
567 error.mTranslatedElementName.Value() +
568 nsLiteralString(
569 u" didn't match the element found in the source ") +
570 error.mSourceElementName.Value() + u"."_ns;
571 break;
572 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISCONNECTED:
573 msg += u"The element using message \""_ns + error.mL10nName.Value() +
574 nsLiteralString(
575 u"\" was removed from the DOM when translating its \"") +
576 error.mTranslatedElementName.Value() + u"\" parent."_ns;
577 break;
578 case L10nOverlays_Binding::ERROR_TRANSLATED_ELEMENT_DISALLOWED_DOM:
579 msg += nsLiteralString(
580 u"While translating an element with fluent ID \"") +
581 error.mL10nName.Value() + u"\" a child element of type \""_ns +
582 error.mTranslatedElementName.Value() +
583 nsLiteralString(
584 u"\" was removed. Either the fluent message "
585 "does not contain markup, or it does not contain markup "
586 "of this type.");
587 break;
588 case L10nOverlays_Binding::ERROR_UNKNOWN:
589 default:
590 msg += nsLiteralString(
591 u"Unknown error happened while translating an element.");
592 break;
594 nsPIDOMWindowInner* innerWindow = GetParentObject()->AsInnerWindow();
595 Document* doc = innerWindow ? innerWindow->GetExtantDoc() : nullptr;
596 if (doc) {
597 nsContentUtils::ReportToConsoleNonLocalized(
598 msg, nsIScriptError::warningFlag, "DOM"_ns, doc);
599 } else {
600 NS_WARNING("Failed to report l10n DOM Overlay errors to console.");
606 void DOMLocalization::ConvertStringToL10nArgs(const nsString& aInput,
607 intl::L10nArgs& aRetVal,
608 ErrorResult& aRv) {
609 // This method uses a temporary dictionary to automate
610 // converting a JSON string into an IDL Record via a dictionary.
612 // Once we get Record::Init(const nsAString& aJSON), we'll switch to
613 // that.
614 L10nArgsHelperDict helperDict;
615 if (!helperDict.Init(u"{\"args\": "_ns + aInput + u"}"_ns)) {
616 aRv.Throw(NS_ERROR_UNEXPECTED);
617 return;
619 for (auto& entry : helperDict.mArgs.Entries()) {
620 L10nArgs::EntryType* newEntry = aRetVal.Entries().AppendElement(fallible);
621 if (!newEntry) {
622 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
623 return;
625 newEntry->mKey = entry.mKey;
626 newEntry->mValue = entry.mValue;