/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_intl_l10n_Localization_h #define mozilla_intl_l10n_Localization_h #include "nsCycleCollectionParticipant.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include "nsWrapperCache.h" #include "nsWeakReference.h" #include "nsIScriptError.h" #include "nsContentUtils.h" #include "nsPIDOMWindow.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/BindingDeclarations.h" #include "mozilla/dom/LocalizationBinding.h" #include "mozilla/intl/LocalizationBindings.h" #include "mozilla/intl/L10nRegistry.h" namespace mozilla { namespace intl { // The state where the application contains incomplete localization resources // is much more common than for other types of core resources. // // In result, our localization is designed to handle missing resources // gracefully, and we need a more fine-tuned way to communicate those problems // to developers. // // In particular, we want developers and early adopters to be able to reason // about missing translations, without bothering end user in production, where // the user cannot react to that. // // We currently differentiate between nightly/dev-edition builds or automation // where we report the errors, and beta/release, where we silence them. // // A side effect of the conditional model of strict vs loose error handling is // that we don't have a way to write integration tests for behavior we expect // out of production environment. See bug 1741430. [[maybe_unused]] static bool MaybeReportErrorsToGecko( const nsTArray& aErrors, ErrorResult& aRv, nsIGlobalObject* aGlobal) { if (!aErrors.IsEmpty()) { if (xpc::IsInAutomation()) { aRv.ThrowInvalidStateError(aErrors.ElementAt(0)); return true; } #if defined(NIGHTLY_BUILD) || defined(MOZ_DEV_EDITION) || defined(DEBUG) dom::Document* doc = nullptr; if (aGlobal) { nsPIDOMWindowInner* innerWindow = aGlobal->AsInnerWindow(); if (innerWindow) { doc = innerWindow->GetExtantDoc(); } } for (const auto& error : aErrors) { nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16(error), nsIScriptError::warningFlag, "l10n"_ns, doc); printf_stderr("%s\n", error.get()); } #endif } return false; } class Localization : public nsIObserver, public nsWrapperCache, public nsSupportsWeakReference { template friend already_AddRefed mozilla::MakeAndAddRef(Args&&... aArgs); public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS_AMBIGUOUS(Localization, nsIObserver) NS_DECL_NSIOBSERVER static already_AddRefed Constructor( const dom::GlobalObject& aGlobal, const dom::Sequence& aResourceIds, bool aIsSync, const dom::Optional>& aRegistry, const dom::Optional>& aLocales, ErrorResult& aRv); static already_AddRefed Create( const nsTArray& aResourceIds, bool aIsSync); static already_AddRefed Create( const nsTArray& aResourceIds, bool aIsSync); JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; nsIGlobalObject* GetParentObject() const { return mGlobal; } void SetIsSync(bool aIsSync); already_AddRefed FormatValue( const nsACString& aId, const dom::Optional& aArgs, ErrorResult& aRv); already_AddRefed FormatValues( const dom::Sequence& aKeys, ErrorResult& aRv); already_AddRefed FormatMessages( const dom::Sequence& aKeys, ErrorResult& aRv); void FormatValueSync(const nsACString& aId, const dom::Optional& aArgs, nsACString& aRetVal, ErrorResult& aRv); void FormatValuesSync( const dom::Sequence& aKeys, nsTArray& aRetVal, ErrorResult& aRv); void FormatMessagesSync( const dom::Sequence& aKeys, nsTArray>& aRetVal, ErrorResult& aRv); void AddResourceId(const ffi::GeckoResourceId& aResourceId); void AddResourceId(const nsCString& aResourceId); void AddResourceId(const dom::OwningUTF8StringOrResourceId& aResourceId); uint32_t RemoveResourceId(const ffi::GeckoResourceId& aResourceId); uint32_t RemoveResourceId(const nsCString& aResourceId); uint32_t RemoveResourceId( const dom::OwningUTF8StringOrResourceId& aResourceId); void AddResourceIds( const nsTArray& aResourceIds); uint32_t RemoveResourceIds( const nsTArray& aResourceIds); void SetAsync(); bool IsSync(); protected: Localization(const nsTArray& aResIds, bool aIsSync); Localization(const nsTArray& aResIds, bool aIsSync); Localization(nsIGlobalObject* aGlobal, bool aIsSync); Localization(nsIGlobalObject* aGlobal, const nsTArray& aResIds, bool aIsSync); Localization(nsIGlobalObject* aGlobal, bool aIsSync, const ffi::LocalizationRc* aRaw); virtual ~Localization(); void RegisterObservers(); virtual void OnChange(); already_AddRefed MaybeWrapPromise(dom::Promise* aInnerPromise); nsCOMPtr mGlobal; RefPtr mRaw; }; } // namespace intl } // namespace mozilla #endif