tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

Localization.cpp (18323B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "Localization.h"
      8 #include "nsIObserverService.h"
      9 #include "xpcpublic.h"
     10 #include "mozilla/BasePrincipal.h"
     11 #include "mozilla/Preferences.h"
     12 #include "mozilla/Services.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/PromiseNativeHandler.h"
     15 
     16 #define INTL_APP_LOCALES_CHANGED "intl:app-locales-changed"
     17 #define L10N_PSEUDO_PREF "intl.l10n.pseudo"
     18 
     19 using namespace mozilla;
     20 using namespace mozilla::dom;
     21 using namespace mozilla::intl;
     22 
     23 static const char* kObservedPrefs[] = {L10N_PSEUDO_PREF, nullptr};
     24 
     25 static nsTArray<ffi::L10nKey> ConvertFromL10nKeys(
     26    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys) {
     27  nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
     28 
     29  for (const auto& entry : aKeys) {
     30    if (entry.IsUTF8String()) {
     31      const auto& id = entry.GetAsUTF8String();
     32      ffi::L10nKey* key = l10nKeys.AppendElement();
     33      key->id = &id;
     34    } else {
     35      const auto& e = entry.GetAsL10nIdArgs();
     36      ffi::L10nKey* key = l10nKeys.AppendElement();
     37      key->id = &e.mId;
     38      if (!e.mArgs.IsNull()) {
     39        FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
     40      }
     41    }
     42  }
     43 
     44  return l10nKeys;
     45 }
     46 
     47 [[nodiscard]] static bool ConvertToAttributeNameValue(
     48    const nsTArray<ffi::L10nAttribute>& aAttributes,
     49    FallibleTArray<AttributeNameValue>& aValues) {
     50  if (!aValues.SetCapacity(aAttributes.Length(), fallible)) {
     51    return false;
     52  }
     53  for (const auto& attr : aAttributes) {
     54    auto* cvtAttr = aValues.AppendElement(fallible);
     55    MOZ_ASSERT(cvtAttr, "SetCapacity didn't set enough capacity somehow?");
     56    cvtAttr->mName = attr.name;
     57    cvtAttr->mValue = attr.value;
     58  }
     59  return true;
     60 }
     61 
     62 [[nodiscard]] static bool ConvertToL10nMessages(
     63    const nsTArray<ffi::OptionalL10nMessage>& aMessages,
     64    nsTArray<Nullable<L10nMessage>>& aOut) {
     65  if (!aOut.SetCapacity(aMessages.Length(), fallible)) {
     66    return false;
     67  }
     68 
     69  for (const auto& entry : aMessages) {
     70    Nullable<L10nMessage>* msg = aOut.AppendElement(fallible);
     71    MOZ_ASSERT(msg, "SetCapacity didn't set enough capacity somehow?");
     72 
     73    if (!entry.is_present) {
     74      continue;
     75    }
     76 
     77    L10nMessage& m = msg->SetValue();
     78    if (!entry.message.value.IsVoid()) {
     79      m.mValue = entry.message.value;
     80    }
     81    if (!entry.message.attributes.IsEmpty()) {
     82      auto& value = m.mAttributes.SetValue();
     83      if (!ConvertToAttributeNameValue(entry.message.attributes, value)) {
     84        return false;
     85      }
     86    }
     87  }
     88 
     89  return true;
     90 }
     91 
     92 NS_IMPL_CYCLE_COLLECTING_ADDREF(Localization)
     93 NS_IMPL_CYCLE_COLLECTING_RELEASE(Localization)
     94 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Localization)
     95  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
     96  NS_INTERFACE_MAP_ENTRY(nsIObserver)
     97  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
     98  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
     99 NS_INTERFACE_MAP_END
    100 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK(Localization, mGlobal)
    101 
    102 /* static */
    103 already_AddRefed<Localization> Localization::Create(
    104    const nsTArray<nsCString>& aResourceIds, bool aIsSync) {
    105  return MakeAndAddRef<Localization>(aResourceIds, aIsSync);
    106 }
    107 
    108 /* static */
    109 already_AddRefed<Localization> Localization::Create(
    110    const nsTArray<nsCString>& aResourceIds, bool aIsSync,
    111    const nsTArray<nsCString>& aLocales) {
    112  return MakeAndAddRef<Localization>(aResourceIds, aIsSync, aLocales);
    113 }
    114 
    115 /* static */
    116 already_AddRefed<Localization> Localization::Create(
    117    const nsTArray<ffi::GeckoResourceId>& aResourceIds, bool aIsSync) {
    118  return MakeAndAddRef<Localization>(aResourceIds, aIsSync);
    119 }
    120 
    121 Localization::Localization(const nsTArray<nsCString>& aResIds, bool aIsSync) {
    122  auto ffiResourceIds{L10nRegistry::ResourceIdsToFFI(aResIds)};
    123  ffi::localization_new(&ffiResourceIds, aIsSync, nullptr,
    124                        getter_AddRefs(mRaw));
    125 
    126  RegisterObservers();
    127 }
    128 
    129 Localization::Localization(const nsTArray<nsCString>& aResIds, bool aIsSync,
    130                           const nsTArray<nsCString>& aLocales) {
    131  auto ffiResourceIds{L10nRegistry::ResourceIdsToFFI(aResIds)};
    132  ffi::localization_new_with_locales(&ffiResourceIds, aIsSync, nullptr,
    133                                     &aLocales, getter_AddRefs(mRaw));
    134 }
    135 
    136 Localization::Localization(const nsTArray<ffi::GeckoResourceId>& aResIds,
    137                           bool aIsSync) {
    138  ffi::localization_new(&aResIds, aIsSync, nullptr, getter_AddRefs(mRaw));
    139 
    140  RegisterObservers();
    141 }
    142 
    143 Localization::Localization(nsIGlobalObject* aGlobal,
    144                           const nsTArray<nsCString>& aResIds, bool aIsSync)
    145    : mGlobal(aGlobal) {
    146  nsTArray<ffi::GeckoResourceId> resourceIds{
    147      L10nRegistry::ResourceIdsToFFI(aResIds)};
    148  ffi::localization_new(&resourceIds, aIsSync, nullptr, getter_AddRefs(mRaw));
    149 
    150  RegisterObservers();
    151 }
    152 
    153 Localization::Localization(nsIGlobalObject* aGlobal, bool aIsSync)
    154    : mGlobal(aGlobal) {
    155  nsTArray<ffi::GeckoResourceId> resIds;
    156  ffi::localization_new(&resIds, aIsSync, nullptr, getter_AddRefs(mRaw));
    157 
    158  RegisterObservers();
    159 }
    160 
    161 Localization::Localization(nsIGlobalObject* aGlobal, bool aIsSync,
    162                           const ffi::LocalizationRc* aRaw)
    163    : mGlobal(aGlobal), mRaw(aRaw) {
    164  RegisterObservers();
    165 }
    166 
    167 Localization::Localization(nsIGlobalObject* aGlobal, bool aIsSync,
    168                           const nsTArray<nsCString>& aLocales)
    169    : mGlobal(aGlobal) {
    170  nsTArray<ffi::GeckoResourceId> resIds;
    171  ffi::localization_new_with_locales(&resIds, aIsSync, nullptr, &aLocales,
    172                                     getter_AddRefs(mRaw));
    173 }
    174 
    175 /* static */
    176 bool Localization::IsAPIEnabled(JSContext* aCx, JSObject* aObject) {
    177  JS::Rooted<JSObject*> obj(aCx, aObject);
    178  return Document::DocumentSupportsL10n(aCx, obj) ||
    179         IsChromeOrUAWidget(aCx, obj);
    180 }
    181 
    182 already_AddRefed<Localization> Localization::Constructor(
    183    const GlobalObject& aGlobal,
    184    const Sequence<OwningUTF8StringOrResourceId>& aResourceIds, bool aIsSync,
    185    const Optional<NonNull<L10nRegistry>>& aRegistry,
    186    const Optional<Sequence<nsCString>>& aLocales, ErrorResult& aRv) {
    187  auto ffiResourceIds{L10nRegistry::ResourceIdsToFFI(aResourceIds)};
    188  Maybe<nsTArray<nsCString>> locales;
    189 
    190  if (aLocales.WasPassed()) {
    191    locales.emplace();
    192    locales->SetCapacity(aLocales.Value().Length());
    193    for (const auto& locale : aLocales.Value()) {
    194      locales->AppendElement(locale);
    195    }
    196  }
    197 
    198  RefPtr<const ffi::LocalizationRc> raw;
    199 
    200  bool result = ffi::localization_new_with_locales(
    201      &ffiResourceIds, aIsSync,
    202      aRegistry.WasPassed() ? aRegistry.Value().Raw() : nullptr,
    203      locales.ptrOr(nullptr), getter_AddRefs(raw));
    204 
    205  if (!result) {
    206    aRv.ThrowInvalidStateError(
    207        "Failed to create the Localization. Check the locales arguments.");
    208    return nullptr;
    209  }
    210 
    211  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    212 
    213  return do_AddRef(new Localization(global, aIsSync, raw));
    214 }
    215 
    216 JSObject* Localization::WrapObject(JSContext* aCx,
    217                                   JS::Handle<JSObject*> aGivenProto) {
    218  return Localization_Binding::Wrap(aCx, this, aGivenProto);
    219 }
    220 
    221 Localization::~Localization() = default;
    222 
    223 NS_IMETHODIMP
    224 Localization::Observe(nsISupports* aSubject, const char* aTopic,
    225                      const char16_t* aData) {
    226  if (!strcmp(aTopic, INTL_APP_LOCALES_CHANGED)) {
    227    OnChange();
    228  } else {
    229    MOZ_ASSERT(!strcmp("nsPref:changed", aTopic));
    230    nsDependentString pref(aData);
    231    if (pref.EqualsLiteral(L10N_PSEUDO_PREF)) {
    232      OnChange();
    233    }
    234  }
    235 
    236  return NS_OK;
    237 }
    238 
    239 void Localization::RegisterObservers() {
    240  DebugOnly<nsresult> rv = Preferences::AddWeakObservers(this, kObservedPrefs);
    241  MOZ_ASSERT(NS_SUCCEEDED(rv), "Adding observers failed.");
    242 
    243  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    244  if (obs) {
    245    obs->AddObserver(this, INTL_APP_LOCALES_CHANGED, true);
    246  }
    247 }
    248 
    249 void Localization::OnChange() { ffi::localization_on_change(mRaw.get()); }
    250 
    251 void Localization::AddResourceId(const ffi::GeckoResourceId& aResourceId) {
    252  ffi::localization_add_res_id(mRaw.get(), &aResourceId);
    253 }
    254 void Localization::AddResourceId(const nsCString& aResourceId) {
    255  auto ffiResourceId{L10nRegistry::ResourceIdToFFI(aResourceId)};
    256  AddResourceId(ffiResourceId);
    257 }
    258 void Localization::AddResourceId(
    259    const dom::OwningUTF8StringOrResourceId& aResourceId) {
    260  auto ffiResourceId{L10nRegistry::ResourceIdToFFI(aResourceId)};
    261  AddResourceId(ffiResourceId);
    262 }
    263 
    264 uint32_t Localization::RemoveResourceId(
    265    const ffi::GeckoResourceId& aResourceId) {
    266  return ffi::localization_remove_res_id(mRaw.get(), &aResourceId);
    267 }
    268 uint32_t Localization::RemoveResourceId(const nsCString& aResourceId) {
    269  auto ffiResourceId{L10nRegistry::ResourceIdToFFI(aResourceId)};
    270  return RemoveResourceId(ffiResourceId);
    271 }
    272 uint32_t Localization::RemoveResourceId(
    273    const dom::OwningUTF8StringOrResourceId& aResourceId) {
    274  auto ffiResourceId{L10nRegistry::ResourceIdToFFI(aResourceId)};
    275  return RemoveResourceId(ffiResourceId);
    276 }
    277 
    278 void Localization::AddResourceIds(
    279    const nsTArray<dom::OwningUTF8StringOrResourceId>& aResourceIds) {
    280  auto ffiResourceIds{L10nRegistry::ResourceIdsToFFI(aResourceIds)};
    281  ffi::localization_add_res_ids(mRaw.get(), &ffiResourceIds);
    282 }
    283 
    284 uint32_t Localization::RemoveResourceIds(
    285    const nsTArray<dom::OwningUTF8StringOrResourceId>& aResourceIds) {
    286  auto ffiResourceIds{L10nRegistry::ResourceIdsToFFI(aResourceIds)};
    287  return ffi::localization_remove_res_ids(mRaw.get(), &ffiResourceIds);
    288 }
    289 
    290 already_AddRefed<Promise> Localization::FormatValue(
    291    const nsACString& aId, const Optional<L10nArgs>& aArgs, ErrorResult& aRv) {
    292  nsTArray<ffi::L10nArg> l10nArgs;
    293  nsTArray<nsCString> errors;
    294 
    295  if (aArgs.WasPassed()) {
    296    const L10nArgs& args = aArgs.Value();
    297    FluentBundle::ConvertArgs(args, l10nArgs);
    298  }
    299  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    300 
    301  ffi::localization_format_value(
    302      mRaw.get(), &aId, &l10nArgs, promise,
    303      [](const Promise* aPromise, const nsACString* aValue,
    304         const nsTArray<nsCString>* aErrors) {
    305        Promise* promise = const_cast<Promise*>(aPromise);
    306 
    307        ErrorResult rv;
    308        if (MaybeReportErrorsToGecko(*aErrors, rv,
    309                                     promise->GetParentObject())) {
    310          promise->MaybeReject(std::move(rv));
    311        } else {
    312          promise->MaybeResolve(aValue);
    313        }
    314      });
    315 
    316  return MaybeWrapPromise(promise);
    317 }
    318 
    319 already_AddRefed<Promise> Localization::FormatValues(
    320    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys, ErrorResult& aRv) {
    321  nsTArray<ffi::L10nKey> l10nKeys = ConvertFromL10nKeys(aKeys);
    322 
    323  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    324  if (aRv.Failed()) {
    325    return nullptr;
    326  }
    327 
    328  ffi::localization_format_values(
    329      mRaw.get(), &l10nKeys, promise,
    330      // callback function which will be invoked by the rust code, passing the
    331      // promise back in.
    332      [](const Promise* aPromise, const nsTArray<nsCString>* aValues,
    333         const nsTArray<nsCString>* aErrors) {
    334        Promise* promise = const_cast<Promise*>(aPromise);
    335 
    336        ErrorResult rv;
    337        if (MaybeReportErrorsToGecko(*aErrors, rv,
    338                                     promise->GetParentObject())) {
    339          promise->MaybeReject(std::move(rv));
    340        } else {
    341          promise->MaybeResolve(*aValues);
    342        }
    343      });
    344 
    345  return MaybeWrapPromise(promise);
    346 }
    347 
    348 already_AddRefed<Promise> Localization::FormatMessages(
    349    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys, ErrorResult& aRv) {
    350  auto l10nKeys = ConvertFromL10nKeys(aKeys);
    351 
    352  RefPtr<Promise> promise = Promise::Create(mGlobal, aRv);
    353  if (aRv.Failed()) {
    354    return nullptr;
    355  }
    356 
    357  ffi::localization_format_messages(
    358      mRaw.get(), &l10nKeys, promise,
    359      // callback function which will be invoked by the rust code, passing the
    360      // promise back in.
    361      [](const Promise* aPromise,
    362         const nsTArray<ffi::OptionalL10nMessage>* aRaw,
    363         const nsTArray<nsCString>* aErrors) {
    364        Promise* promise = const_cast<Promise*>(aPromise);
    365 
    366        ErrorResult rv;
    367        if (MaybeReportErrorsToGecko(*aErrors, rv,
    368                                     promise->GetParentObject())) {
    369          promise->MaybeReject(std::move(rv));
    370        } else {
    371          nsTArray<Nullable<L10nMessage>> messages;
    372          if (!ConvertToL10nMessages(*aRaw, messages)) {
    373            promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
    374          } else {
    375            promise->MaybeResolve(std::move(messages));
    376          }
    377        }
    378      });
    379 
    380  return MaybeWrapPromise(promise);
    381 }
    382 
    383 void Localization::FormatValueSync(const nsACString& aId,
    384                                   const Optional<L10nArgs>& aArgs,
    385                                   nsACString& aRetVal, ErrorResult& aRv) {
    386  nsTArray<ffi::L10nArg> l10nArgs;
    387  nsTArray<nsCString> errors;
    388 
    389  if (aArgs.WasPassed()) {
    390    const L10nArgs& args = aArgs.Value();
    391    FluentBundle::ConvertArgs(args, l10nArgs);
    392  }
    393 
    394  bool rv = ffi::localization_format_value_sync(mRaw.get(), &aId, &l10nArgs,
    395                                                &aRetVal, &errors);
    396 
    397  if (rv) {
    398    MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
    399  } else {
    400    aRv.ThrowInvalidStateError(
    401        "Can't use formatValueSync when state is async.");
    402  }
    403 }
    404 
    405 void Localization::FormatValuesSync(
    406    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
    407    nsTArray<nsCString>& aRetVal, ErrorResult& aRv) {
    408  nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
    409  nsTArray<nsCString> errors;
    410 
    411  for (const auto& entry : aKeys) {
    412    if (entry.IsUTF8String()) {
    413      const auto& id = entry.GetAsUTF8String();
    414      nsTArray<ffi::L10nArg> l10nArgs;
    415      ffi::L10nKey* key = l10nKeys.AppendElement();
    416      key->id = &id;
    417    } else {
    418      const auto& e = entry.GetAsL10nIdArgs();
    419      nsTArray<ffi::L10nArg> l10nArgs;
    420      ffi::L10nKey* key = l10nKeys.AppendElement();
    421      key->id = &e.mId;
    422      if (!e.mArgs.IsNull()) {
    423        FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
    424      }
    425    }
    426  }
    427 
    428  bool rv = ffi::localization_format_values_sync(mRaw.get(), &l10nKeys,
    429                                                 &aRetVal, &errors);
    430 
    431  if (rv) {
    432    MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
    433  } else {
    434    aRv.ThrowInvalidStateError(
    435        "Can't use formatValuesSync when state is async.");
    436  }
    437 }
    438 
    439 void Localization::FormatMessagesSync(
    440    const Sequence<OwningUTF8StringOrL10nIdArgs>& aKeys,
    441    nsTArray<Nullable<L10nMessage>>& aRetVal, ErrorResult& aRv) {
    442  nsTArray<ffi::L10nKey> l10nKeys(aKeys.Length());
    443  nsTArray<nsCString> errors;
    444 
    445  for (const auto& entry : aKeys) {
    446    if (entry.IsUTF8String()) {
    447      const auto& id = entry.GetAsUTF8String();
    448      nsTArray<ffi::L10nArg> l10nArgs;
    449      ffi::L10nKey* key = l10nKeys.AppendElement();
    450      key->id = &id;
    451    } else {
    452      const auto& e = entry.GetAsL10nIdArgs();
    453      nsTArray<ffi::L10nArg> l10nArgs;
    454      ffi::L10nKey* key = l10nKeys.AppendElement();
    455      key->id = &e.mId;
    456      if (!e.mArgs.IsNull()) {
    457        FluentBundle::ConvertArgs(e.mArgs.Value(), key->args);
    458      }
    459    }
    460  }
    461 
    462  nsTArray<ffi::OptionalL10nMessage> result(l10nKeys.Length());
    463 
    464  bool rv = ffi::localization_format_messages_sync(mRaw.get(), &l10nKeys,
    465                                                   &result, &errors);
    466 
    467  if (!rv) {
    468    return aRv.ThrowInvalidStateError(
    469        "Can't use formatMessagesSync when state is async.");
    470  }
    471  MaybeReportErrorsToGecko(errors, aRv, GetParentObject());
    472  if (aRv.Failed()) {
    473    return;
    474  }
    475  if (!ConvertToL10nMessages(result, aRetVal)) {
    476    return aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
    477  }
    478 }
    479 
    480 void Localization::SetAsync() { ffi::localization_set_async(mRaw.get()); }
    481 bool Localization::IsSync() { return ffi::localization_is_sync(mRaw.get()); }
    482 
    483 /**
    484 * PromiseResolver is a PromiseNativeHandler used
    485 * by MaybeWrapPromise method.
    486 */
    487 class PromiseResolver final : public PromiseNativeHandler {
    488 public:
    489  NS_DECL_ISUPPORTS
    490 
    491  explicit PromiseResolver(Promise* aPromise) : mPromise(aPromise) {}
    492  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    493                        ErrorResult& aRv) override;
    494  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    495                        ErrorResult& aRv) override;
    496 
    497 protected:
    498  virtual ~PromiseResolver();
    499 
    500  RefPtr<Promise> mPromise;
    501 };
    502 
    503 NS_INTERFACE_MAP_BEGIN(PromiseResolver)
    504  NS_INTERFACE_MAP_ENTRY(nsISupports)
    505 NS_INTERFACE_MAP_END
    506 
    507 NS_IMPL_ADDREF(PromiseResolver)
    508 NS_IMPL_RELEASE(PromiseResolver)
    509 
    510 void PromiseResolver::ResolvedCallback(JSContext* aCx,
    511                                       JS::Handle<JS::Value> aValue,
    512                                       ErrorResult& aRv) {
    513  mPromise->MaybeResolveWithClone(aCx, aValue);
    514 }
    515 
    516 void PromiseResolver::RejectedCallback(JSContext* aCx,
    517                                       JS::Handle<JS::Value> aValue,
    518                                       ErrorResult& aRv) {
    519  mPromise->MaybeRejectWithClone(aCx, aValue);
    520 }
    521 
    522 PromiseResolver::~PromiseResolver() { mPromise = nullptr; }
    523 
    524 /**
    525 * MaybeWrapPromise is a helper method used by Localization
    526 * API methods to clone the value returned by a promise
    527 * into a new context.
    528 *
    529 * This allows for a promise from a privileged context
    530 * to be returned into an unprivileged document.
    531 *
    532 * This method is only used for promises that carry values.
    533 */
    534 already_AddRefed<Promise> Localization::MaybeWrapPromise(
    535    Promise* aInnerPromise) {
    536  MOZ_ASSERT(aInnerPromise->State() == Promise::PromiseState::Pending);
    537  // For system principal we don't need to wrap the
    538  // result promise at all.
    539  nsIPrincipal* principal = mGlobal->PrincipalOrNull();
    540  if (principal && principal->IsSystemPrincipal()) {
    541    return do_AddRef(aInnerPromise);
    542  }
    543 
    544  IgnoredErrorResult result;
    545  RefPtr<Promise> docPromise = Promise::Create(mGlobal, result);
    546  if (NS_WARN_IF(result.Failed())) {
    547    return nullptr;
    548  }
    549 
    550  auto resolver = MakeRefPtr<PromiseResolver>(docPromise);
    551  aInnerPromise->AppendNativeHandler(resolver);
    552  return docPromise.forget();
    553 }