tor-browser

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

L10nRegistry.cpp (14779B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include "L10nRegistry.h"
      6 #include "mozilla/RefPtr.h"
      7 #include "mozilla/URLPreloader.h"
      8 #include "nsIChannel.h"
      9 #include "nsILoadInfo.h"
     10 #include "nsNetUtil.h"
     11 #include "nsString.h"
     12 #include "nsContentUtils.h"
     13 #include "FluentResource.h"
     14 #include "FileSource.h"
     15 #include "nsICategoryManager.h"
     16 #include "mozilla/SimpleEnumerator.h"
     17 #include "mozilla/dom/Promise.h"
     18 #include "mozilla/dom/PContent.h"
     19 #include "mozilla/dom/ContentParent.h"
     20 #include "mozilla/Preferences.h"
     21 
     22 using namespace mozilla;
     23 using namespace mozilla::dom;
     24 
     25 namespace mozilla::intl {
     26 
     27 /* FluentBundleIterator */
     28 
     29 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FluentBundleIterator, mGlobal)
     30 
     31 FluentBundleIterator::FluentBundleIterator(
     32    nsIGlobalObject* aGlobal, UniquePtr<ffi::GeckoFluentBundleIterator> aRaw)
     33    : mGlobal(aGlobal), mRaw(std::move(aRaw)) {}
     34 
     35 JSObject* FluentBundleIterator::WrapObject(JSContext* aCx,
     36                                           JS::Handle<JSObject*> aGivenProto) {
     37  return FluentBundleIterator_Binding::Wrap(aCx, this, aGivenProto);
     38 }
     39 
     40 void FluentBundleIterator::Next(FluentBundleIteratorResult& aResult) {
     41  UniquePtr<ffi::FluentBundleRc> raw(
     42      ffi::fluent_bundle_iterator_next(mRaw.get()));
     43  if (!raw) {
     44    aResult.mDone = true;
     45    return;
     46  }
     47  aResult.mDone = false;
     48  aResult.mValue = new FluentBundle(mGlobal, std::move(raw));
     49 }
     50 
     51 already_AddRefed<FluentBundleIterator> FluentBundleIterator::Values() {
     52  return do_AddRef(this);
     53 }
     54 
     55 /* FluentBundleAsyncIterator */
     56 
     57 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FluentBundleAsyncIterator, mGlobal)
     58 
     59 FluentBundleAsyncIterator::FluentBundleAsyncIterator(
     60    nsIGlobalObject* aGlobal,
     61    UniquePtr<ffi::GeckoFluentBundleAsyncIteratorWrapper> aRaw)
     62    : mGlobal(aGlobal), mRaw(std::move(aRaw)) {}
     63 
     64 JSObject* FluentBundleAsyncIterator::WrapObject(
     65    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
     66  return FluentBundleAsyncIterator_Binding::Wrap(aCx, this, aGivenProto);
     67 }
     68 
     69 already_AddRefed<Promise> FluentBundleAsyncIterator::Next(ErrorResult& aError) {
     70  RefPtr<Promise> promise = Promise::Create(mGlobal, aError);
     71  if (aError.Failed()) {
     72    return nullptr;
     73  }
     74 
     75  ffi::fluent_bundle_async_iterator_next(
     76      mRaw.get(), promise,
     77      // callback function which will be invoked by the rust code, passing the
     78      // promise back in.
     79      [](auto* aPromise, ffi::FluentBundleRc* aBundle) {
     80        Promise* promise = const_cast<Promise*>(aPromise);
     81 
     82        FluentBundleIteratorResult res;
     83 
     84        if (aBundle) {
     85          // The Rust caller will transfer the ownership to us.
     86          UniquePtr<ffi::FluentBundleRc> b(aBundle);
     87          nsIGlobalObject* global = promise->GetGlobalObject();
     88          res.mValue = new FluentBundle(global, std::move(b));
     89          res.mDone = false;
     90        } else {
     91          res.mDone = true;
     92        }
     93        promise->MaybeResolve(res);
     94      });
     95 
     96  return promise.forget();
     97 }
     98 
     99 already_AddRefed<FluentBundleAsyncIterator>
    100 FluentBundleAsyncIterator::Values() {
    101  return do_AddRef(this);
    102 }
    103 
    104 /* L10nRegistry */
    105 
    106 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(L10nRegistry, mGlobal)
    107 
    108 L10nRegistry::L10nRegistry(nsIGlobalObject* aGlobal, bool aUseIsolating)
    109    : mGlobal(aGlobal),
    110      mRaw(dont_AddRef(ffi::l10nregistry_new(aUseIsolating))) {}
    111 
    112 L10nRegistry::L10nRegistry(nsIGlobalObject* aGlobal,
    113                           RefPtr<const ffi::GeckoL10nRegistry> aRaw)
    114    : mGlobal(aGlobal), mRaw(std::move(aRaw)) {}
    115 
    116 /* static */
    117 already_AddRefed<L10nRegistry> L10nRegistry::Constructor(
    118    const GlobalObject& aGlobal, const L10nRegistryOptions& aOptions) {
    119  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    120  return MakeAndAddRef<L10nRegistry>(global,
    121                                     aOptions.mBundleOptions.mUseIsolating);
    122 }
    123 
    124 /* static */
    125 already_AddRefed<L10nRegistry> L10nRegistry::GetInstance(
    126    const GlobalObject& aGlobal) {
    127  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
    128  return MakeAndAddRef<L10nRegistry>(
    129      global, dont_AddRef(ffi::l10nregistry_instance_get()));
    130 }
    131 
    132 JSObject* L10nRegistry::WrapObject(JSContext* aCx,
    133                                   JS::Handle<JSObject*> aGivenProto) {
    134  return L10nRegistry_Binding::Wrap(aCx, this, aGivenProto);
    135 }
    136 
    137 void L10nRegistry::GetAvailableLocales(nsTArray<nsCString>& aRetVal) {
    138  ffi::l10nregistry_get_available_locales(mRaw.get(), &aRetVal);
    139 }
    140 
    141 void L10nRegistry::RegisterSources(
    142    const Sequence<OwningNonNull<L10nFileSource>>& aSources) {
    143  nsTArray<const ffi::FileSource*> sources(aSources.Length());
    144  for (const auto& source : aSources) {
    145    sources.AppendElement(source->Raw());
    146  }
    147 
    148  ffi::l10nregistry_register_sources(mRaw.get(), &sources);
    149 }
    150 
    151 void L10nRegistry::UpdateSources(
    152    const Sequence<OwningNonNull<L10nFileSource>>& aSources) {
    153  nsTArray<const ffi::FileSource*> sources(aSources.Length());
    154  for (const auto& source : aSources) {
    155    sources.AppendElement(source->Raw());
    156  }
    157 
    158  ffi::l10nregistry_update_sources(mRaw.get(), &sources);
    159 }
    160 
    161 void L10nRegistry::RemoveSources(const Sequence<nsCString>& aSources) {
    162  ffi::l10nregistry_remove_sources(mRaw.get(), aSources.Elements(),
    163                                   aSources.Length());
    164 }
    165 
    166 bool L10nRegistry::HasSource(const nsACString& aName, ErrorResult& aRv) {
    167  ffi::L10nRegistryStatus status;
    168 
    169  bool result = ffi::l10nregistry_has_source(mRaw.get(), &aName, &status);
    170  PopulateError(aRv, status);
    171  return result;
    172 }
    173 
    174 already_AddRefed<L10nFileSource> L10nRegistry::GetSource(
    175    const nsACString& aName, ErrorResult& aRv) {
    176  ffi::L10nRegistryStatus status;
    177 
    178  RefPtr<const ffi::FileSource> raw(
    179      dont_AddRef(ffi::l10nregistry_get_source(mRaw.get(), &aName, &status)));
    180  if (PopulateError(aRv, status)) {
    181    return nullptr;
    182  }
    183 
    184  return MakeAndAddRef<L10nFileSource>(std::move(raw));
    185 }
    186 
    187 void L10nRegistry::GetSourceNames(nsTArray<nsCString>& aRetVal) {
    188  ffi::l10nregistry_get_source_names(mRaw.get(), &aRetVal);
    189 }
    190 
    191 void L10nRegistry::ClearSources() {
    192  ffi::l10nregistry_clear_sources(mRaw.get());
    193 }
    194 
    195 /* static */
    196 ffi::GeckoResourceId L10nRegistry::ResourceIdToFFI(
    197    const nsCString& aResourceId) {
    198  return ffi::GeckoResourceId{
    199      aResourceId,
    200      ffi::GeckoResourceType::Required,
    201  };
    202 }
    203 
    204 /* static */
    205 ffi::GeckoResourceId L10nRegistry::ResourceIdToFFI(
    206    const dom::OwningUTF8StringOrResourceId& aResourceId) {
    207  if (aResourceId.IsUTF8String()) {
    208    return ffi::GeckoResourceId{
    209        aResourceId.GetAsUTF8String(),
    210        ffi::GeckoResourceType::Required,
    211    };
    212  }
    213  return ffi::GeckoResourceId{
    214      aResourceId.GetAsResourceId().mPath,
    215      aResourceId.GetAsResourceId().mOptional
    216          ? ffi::GeckoResourceType::Optional
    217          : ffi::GeckoResourceType::Required,
    218  };
    219 }
    220 
    221 /* static */
    222 nsTArray<ffi::GeckoResourceId> L10nRegistry::ResourceIdsToFFI(
    223    const nsTArray<nsCString>& aResourceIds) {
    224  nsTArray<ffi::GeckoResourceId> ffiResourceIds;
    225  for (const auto& resourceId : aResourceIds) {
    226    ffiResourceIds.EmplaceBack(ResourceIdToFFI(resourceId));
    227  }
    228  return ffiResourceIds;
    229 }
    230 
    231 /* static */
    232 nsTArray<ffi::GeckoResourceId> L10nRegistry::ResourceIdsToFFI(
    233    const nsTArray<dom::OwningUTF8StringOrResourceId>& aResourceIds) {
    234  nsTArray<ffi::GeckoResourceId> ffiResourceIds;
    235  for (const auto& resourceId : aResourceIds) {
    236    ffiResourceIds.EmplaceBack(ResourceIdToFFI(resourceId));
    237  }
    238  return ffiResourceIds;
    239 }
    240 
    241 already_AddRefed<FluentBundleIterator> L10nRegistry::GenerateBundlesSync(
    242    const nsTArray<nsCString>& aLocales,
    243    const nsTArray<ffi::GeckoResourceId>& aResourceIds, ErrorResult& aRv) {
    244  ffi::L10nRegistryStatus status;
    245  UniquePtr<ffi::GeckoFluentBundleIterator> iter(
    246      ffi::l10nregistry_generate_bundles_sync(
    247          mRaw, aLocales.Elements(), aLocales.Length(), aResourceIds.Elements(),
    248          aResourceIds.Length(), &status));
    249 
    250  if (PopulateError(aRv, status) || !iter) {
    251    return nullptr;
    252  }
    253 
    254  return do_AddRef(new FluentBundleIterator(mGlobal, std::move(iter)));
    255 }
    256 
    257 already_AddRefed<FluentBundleIterator> L10nRegistry::GenerateBundlesSync(
    258    const dom::Sequence<nsCString>& aLocales,
    259    const dom::Sequence<dom::OwningUTF8StringOrResourceId>& aResourceIds,
    260    ErrorResult& aRv) {
    261  auto ffiResourceIds{ResourceIdsToFFI(aResourceIds)};
    262  return GenerateBundlesSync(aLocales, ffiResourceIds, aRv);
    263 }
    264 
    265 already_AddRefed<FluentBundleAsyncIterator> L10nRegistry::GenerateBundles(
    266    const nsTArray<nsCString>& aLocales,
    267    const nsTArray<ffi::GeckoResourceId>& aResourceIds, ErrorResult& aRv) {
    268  ffi::L10nRegistryStatus status;
    269  UniquePtr<ffi::GeckoFluentBundleAsyncIteratorWrapper> iter(
    270      ffi::l10nregistry_generate_bundles(
    271          mRaw, aLocales.Elements(), aLocales.Length(), aResourceIds.Elements(),
    272          aResourceIds.Length(), &status));
    273  if (PopulateError(aRv, status) || !iter) {
    274    return nullptr;
    275  }
    276 
    277  return do_AddRef(new FluentBundleAsyncIterator(mGlobal, std::move(iter)));
    278 }
    279 
    280 already_AddRefed<FluentBundleAsyncIterator> L10nRegistry::GenerateBundles(
    281    const dom::Sequence<nsCString>& aLocales,
    282    const dom::Sequence<dom::OwningUTF8StringOrResourceId>& aResourceIds,
    283    ErrorResult& aRv) {
    284  nsTArray<ffi::GeckoResourceId> resourceIds;
    285  for (const auto& resourceId : aResourceIds) {
    286    resourceIds.EmplaceBack(ResourceIdToFFI(resourceId));
    287  }
    288  return GenerateBundles(aLocales, resourceIds, aRv);
    289 }
    290 
    291 /* static */
    292 void L10nRegistry::GetParentProcessFileSourceDescriptors(
    293    nsTArray<L10nFileSourceDescriptor>& aRetVal) {
    294  MOZ_ASSERT(XRE_IsParentProcess());
    295  nsTArray<ffi::L10nFileSourceDescriptor> sources;
    296  ffi::l10nregistry_get_parent_process_sources(&sources);
    297  for (const auto& source : sources) {
    298    auto descriptor = aRetVal.AppendElement();
    299    descriptor->name() = source.name;
    300    descriptor->metasource() = source.metasource;
    301    descriptor->locales().AppendElements(std::move(source.locales));
    302    descriptor->prePath() = source.pre_path;
    303    descriptor->index().AppendElements(std::move(source.index));
    304  }
    305 }
    306 
    307 /* static */
    308 void L10nRegistry::RegisterFileSourcesFromParentProcess(
    309    const nsTArray<L10nFileSourceDescriptor>& aDescriptors) {
    310  // This means that in content processes the L10nRegistry
    311  // service instance is created eagerly, not lazily.
    312  // It is necessary so that the instance can store the sources
    313  // provided in the IPC init, which, in turn, is necessary
    314  // for the service to be avialable for sync bundle generation.
    315  //
    316  // L10nRegistry is lightweight and performs no operations, so
    317  // we believe this behavior to be acceptable.
    318  MOZ_ASSERT(XRE_IsContentProcess());
    319  nsTArray<ffi::L10nFileSourceDescriptor> sources;
    320  for (const auto& desc : aDescriptors) {
    321    auto source = sources.AppendElement();
    322    source->name = desc.name();
    323    source->metasource = desc.metasource();
    324    source->locales.AppendElements(desc.locales());
    325    source->pre_path = desc.prePath();
    326    source->index.AppendElements(desc.index());
    327  }
    328  ffi::l10nregistry_register_parent_process_sources(&sources);
    329 }
    330 
    331 /* static */
    332 nsresult L10nRegistry::Load(const nsACString& aPath,
    333                            nsIStreamLoaderObserver* aObserver) {
    334  nsCOMPtr<nsIURI> uri;
    335  nsresult rv = NS_NewURI(getter_AddRefs(uri), aPath);
    336  NS_ENSURE_SUCCESS(rv, rv);
    337  NS_ENSURE_TRUE(uri, NS_ERROR_INVALID_ARG);
    338 
    339  RefPtr<nsIStreamLoader> loader;
    340  rv = NS_NewStreamLoader(
    341      getter_AddRefs(loader), uri, aObserver,
    342      nsContentUtils::GetSystemPrincipal(),
    343      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    344      nsIContentPolicy::TYPE_OTHER);
    345 
    346  return rv;
    347 }
    348 
    349 /* static */
    350 nsresult L10nRegistry::LoadSync(const nsACString& aPath, void** aData,
    351                                uint64_t* aSize) {
    352  nsCOMPtr<nsIURI> uri;
    353 
    354  nsresult rv = NS_NewURI(getter_AddRefs(uri), aPath);
    355  NS_ENSURE_SUCCESS(rv, rv);
    356 
    357  NS_ENSURE_TRUE(uri, NS_ERROR_INVALID_ARG);
    358 
    359  auto result = URLPreloader::ReadURI(uri);
    360  if (result.isOk()) {
    361    auto uri = result.unwrap();
    362    *aData = ToNewCString(uri);
    363    *aSize = uri.Length();
    364    return NS_OK;
    365  }
    366 
    367  auto err = result.unwrapErr();
    368  if (err != NS_ERROR_INVALID_ARG && err != NS_ERROR_NOT_INITIALIZED) {
    369    return err;
    370  }
    371 
    372  nsCOMPtr<nsIChannel> channel;
    373  rv = NS_NewChannel(
    374      getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
    375      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
    376      nsIContentPolicy::TYPE_OTHER, nullptr, /* nsICookieJarSettings */
    377      nullptr,                               /* aPerformanceStorage */
    378      nullptr,                               /* aLoadGroup */
    379      nullptr,                               /* aCallbacks */
    380      nsIRequest::LOAD_NORMAL);
    381  NS_ENSURE_SUCCESS(rv, rv);
    382 
    383  // Don't warn on failure here, because it is triggered very frequently for
    384  // necko.ftl which first tries and fails loading a resource://app/ URI before
    385  // succeeding with a resource://gre/ URI.
    386  nsCOMPtr<nsIInputStream> input;
    387  if (NS_FAILED(channel->Open(getter_AddRefs(input)))) {
    388    return NS_ERROR_INVALID_ARG;
    389  }
    390 
    391  return NS_ReadInputStreamToBuffer(input, aData, -1, aSize);
    392 }
    393 
    394 /* static */
    395 bool L10nRegistry::PopulateError(ErrorResult& aError,
    396                                 ffi::L10nRegistryStatus& aStatus) {
    397  switch (aStatus) {
    398    case ffi::L10nRegistryStatus::InvalidLocaleCode:
    399      aError.ThrowTypeError("Invalid locale code");
    400      return true;
    401    case ffi::L10nRegistryStatus::EmptyName:
    402      aError.ThrowTypeError("Name cannot be empty.");
    403      return true;
    404 
    405    case ffi::L10nRegistryStatus::None:
    406      return false;
    407  }
    408  MOZ_ASSERT_UNREACHABLE("Unknown status");
    409  return false;
    410 }
    411 
    412 extern "C" {
    413 nsresult L10nRegistryLoad(const nsACString* aPath,
    414                          const nsIStreamLoaderObserver* aObserver) {
    415  if (!aPath || !aObserver) {
    416    return NS_ERROR_INVALID_ARG;
    417  }
    418 
    419  return mozilla::intl::L10nRegistry::Load(
    420      *aPath, const_cast<nsIStreamLoaderObserver*>(aObserver));
    421 }
    422 
    423 nsresult L10nRegistryLoadSync(const nsACString* aPath, void** aData,
    424                              uint64_t* aSize) {
    425  if (!aPath || !aData || !aSize) {
    426    return NS_ERROR_INVALID_ARG;
    427  }
    428 
    429  return mozilla::intl::L10nRegistry::LoadSync(*aPath, aData, aSize);
    430 }
    431 
    432 void L10nRegistrySendUpdateL10nFileSources() {
    433  MOZ_ASSERT(XRE_IsParentProcess());
    434  nsTArray<L10nFileSourceDescriptor> sources;
    435  L10nRegistry::GetParentProcessFileSourceDescriptors(sources);
    436 
    437  nsTArray<ContentParent*> parents;
    438  ContentParent::GetAll(parents);
    439  for (ContentParent* parent : parents) {
    440    (void)parent->SendUpdateL10nFileSources(sources);
    441  }
    442 }
    443 
    444 }  // extern "C"
    445 
    446 }  // namespace mozilla::intl