Bug 1627809 - Move Localization.jsm construction to Localization constructor. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D70973
This commit is contained in:
Zibi Braniecki 2020-04-30 18:22:38 +00:00
parent 9211c5cc8a
commit 1427870732
8 changed files with 120 additions and 65 deletions

View file

@ -3841,12 +3841,14 @@ bool Document::GetAllowPlugins() {
void Document::InitializeLocalization(Sequence<nsString>& aResourceIds) { void Document::InitializeLocalization(Sequence<nsString>& aResourceIds) {
MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet"); MOZ_ASSERT(!mDocumentL10n, "mDocumentL10n should not be initialized yet");
RefPtr<DocumentL10n> l10n = new DocumentL10n(this); RefPtr<DocumentL10n> l10n = DocumentL10n::Create(this);
ErrorResult rv; if (NS_WARN_IF(!l10n)) {
l10n->Init(aResourceIds, rv);
if (NS_WARN_IF(rv.Failed())) {
return; return;
} }
if (aResourceIds.Length()) {
l10n->AddResourceIds(aResourceIds);
}
l10n->Activate();
mDocumentL10n = l10n; mDocumentL10n = l10n;
} }

View file

@ -48,12 +48,13 @@ already_AddRefed<DOMLocalization> DOMLocalization::Constructor(
} }
RefPtr<DOMLocalization> domLoc = new DOMLocalization(global); RefPtr<DOMLocalization> domLoc = new DOMLocalization(global);
domLoc->Init(aResourceIds, aSync, aBundleGenerator, aRv);
if (NS_WARN_IF(aRv.Failed())) { if (aResourceIds.Length()) {
return nullptr; domLoc->AddResourceIds(aResourceIds);
} }
domLoc->Activate(aSync, true, aBundleGenerator);
return domLoc.forget(); return domLoc.forget();
} }

View file

@ -29,28 +29,46 @@ NS_IMPL_RELEASE_INHERITED(DocumentL10n, DOMLocalization)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n)
NS_INTERFACE_MAP_END_INHERITING(DOMLocalization) NS_INTERFACE_MAP_END_INHERITING(DOMLocalization)
/* static */
RefPtr<DocumentL10n> DocumentL10n::Create(Document* aDocument) {
RefPtr<DocumentL10n> l10n = new DocumentL10n(aDocument);
if (!l10n->Init()) {
return nullptr;
}
return l10n.forget();
}
DocumentL10n::DocumentL10n(Document* aDocument) DocumentL10n::DocumentL10n(Document* aDocument)
: DOMLocalization(aDocument->GetScopeObject()), : DOMLocalization(aDocument->GetScopeObject()),
mDocument(aDocument), mDocument(aDocument),
mState(DocumentL10nState::Initialized) { mState(DocumentL10nState::Uninitialized) {
mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink()); mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
Element* elem = mDocument->GetDocumentElement();
if (elem) {
mIsSync = elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nsync);
}
} }
void DocumentL10n::Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv) { bool DocumentL10n::Init() {
DOMLocalization::Init(aResourceIds, mIsSync, {}, aRv); ErrorResult rv;
if (NS_WARN_IF(aRv.Failed())) { mReady = Promise::Create(mGlobal, rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
return true;
}
void DocumentL10n::Activate() {
if (mState > DocumentL10nState::Uninitialized) {
return; return;
} }
mReady = Promise::Create(mGlobal, aRv); Element* elem = mDocument->GetDocumentElement();
if (NS_WARN_IF(aRv.Failed())) { bool isSync = false;
return; if (elem) {
isSync = elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nsync);
} }
DOMLocalization::Activate(isSync, true, {});
mState = DocumentL10nState::Activated;
} }
JSObject* DocumentL10n::WrapObject(JSContext* aCx, JSObject* DocumentL10n::WrapObject(JSContext* aCx,

View file

@ -14,9 +14,22 @@ namespace mozilla {
namespace dom { namespace dom {
enum class DocumentL10nState { enum class DocumentL10nState {
Initialized = 0, // State set when the DocumentL10n gets constructed.
Uninitialized = 0,
// State set when the DocumentL10n is activated and ready to be used.
Activated,
// State set when the initial translation got triggered. This happens
// if DocumentL10n was constructed during parsing of the document.
//
// If the DocumentL10n gets constructed later, we'll skip directly to
// Ready state.
InitialTranslationTriggered, InitialTranslationTriggered,
InitialTranslationCompleted
// State set the DocumentL10n has been fully initialized, potentially
// with initial translation being completed.
InitialTranslationCompleted,
}; };
/** /**
@ -34,10 +47,14 @@ class DocumentL10n final : public DOMLocalization {
NS_DECL_ISUPPORTS_INHERITED NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentL10n, DOMLocalization) NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DocumentL10n, DOMLocalization)
explicit DocumentL10n(Document* aDocument); static RefPtr<DocumentL10n> Create(Document* aDocument);
void Init(Sequence<nsString>& aResourceIds, ErrorResult& aRv);
void Activate();
protected: protected:
explicit DocumentL10n(Document* aDocument);
bool Init();
virtual ~DocumentL10n() = default; virtual ~DocumentL10n() = default;
RefPtr<Document> mDocument; RefPtr<Document> mDocument;

View file

@ -43,37 +43,36 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Localization)
NS_INTERFACE_MAP_END NS_INTERFACE_MAP_END
Localization::Localization(nsIGlobalObject* aGlobal) Localization::Localization(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal), mIsSync(false) {} : mGlobal(aGlobal), mIsSync(false) {
void Localization::Init(const Sequence<nsString>& aResourceIds,
const bool aSync,
const BundleGenerator& aBundleGenerator,
ErrorResult& aRv) {
nsCOMPtr<mozILocalizationJSM> jsm = nsCOMPtr<mozILocalizationJSM> jsm =
do_ImportModule("resource://gre/modules/Localization.jsm"); do_ImportModule("resource://gre/modules/Localization.jsm");
MOZ_RELEASE_ASSERT(jsm); MOZ_RELEASE_ASSERT(jsm);
Unused << jsm->GetLocalization(aResourceIds, getter_AddRefs(mLocalization)); Unused << jsm->GetLocalization(getter_AddRefs(mLocalization));
MOZ_RELEASE_ASSERT(mLocalization); MOZ_RELEASE_ASSERT(mLocalization);
mLocalization->SetIsSync(aSync); }
void Localization::Activate(const bool aSync, const bool aEager,
const BundleGenerator& aBundleGenerator) {
AutoJSContext cx; AutoJSContext cx;
JS::Rooted<JS::Value> generateBundlesJS(cx);
JS::Rooted<JS::Value> generateBundlesSyncJS(cx);
if (aBundleGenerator.mGenerateBundles.WasPassed()) { if (aBundleGenerator.mGenerateBundles.WasPassed()) {
GenerateBundles& generateBundles = GenerateBundles& generateBundles =
aBundleGenerator.mGenerateBundles.Value(); aBundleGenerator.mGenerateBundles.Value();
JS::Rooted<JS::Value> generateBundlesJS( generateBundlesJS.set(JS::ObjectValue(*generateBundles.CallbackOrNull()));
cx, JS::ObjectValue(*generateBundles.CallbackOrNull()));
mLocalization->SetGenerateBundles(generateBundlesJS);
} }
if (aBundleGenerator.mGenerateBundlesSync.WasPassed()) { if (aBundleGenerator.mGenerateBundlesSync.WasPassed()) {
GenerateBundlesSync& generateBundlesSync = GenerateBundlesSync& generateBundlesSync =
aBundleGenerator.mGenerateBundlesSync.Value(); aBundleGenerator.mGenerateBundlesSync.Value();
JS::Rooted<JS::Value> generateBundlesSyncJS( generateBundlesSyncJS.set(
cx, JS::ObjectValue(*generateBundlesSync.CallbackOrNull())); JS::ObjectValue(*generateBundlesSync.CallbackOrNull()));
mLocalization->SetGenerateBundlesSync(generateBundlesSyncJS);
} }
mIsSync = aSync;
mLocalization->Init(true); mLocalization->Activate(aSync, aEager, generateBundlesJS,
generateBundlesSyncJS);
RegisterObservers(); RegisterObservers();
} }
@ -90,12 +89,12 @@ already_AddRefed<Localization> Localization::Constructor(
RefPtr<Localization> loc = new Localization(global); RefPtr<Localization> loc = new Localization(global);
loc->Init(aResourceIds, aSync, aBundleGenerator, aRv); if (aResourceIds.Length()) {
loc->AddResourceIds(aResourceIds);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
} }
loc->Activate(aSync, true, aBundleGenerator);
return loc.forget(); return loc.forget();
} }
@ -197,6 +196,7 @@ already_AddRefed<Promise> Localization::FormatValue(
} }
void Localization::SetIsSync(const bool aIsSync) { void Localization::SetIsSync(const bool aIsSync) {
mIsSync = aIsSync;
mLocalization->SetIsSync(aIsSync); mLocalization->SetIsSync(aIsSync);
} }

View file

@ -33,8 +33,8 @@ class Localization : public nsIObserver,
NS_DECL_NSIOBSERVER NS_DECL_NSIOBSERVER
explicit Localization(nsIGlobalObject* aGlobal); explicit Localization(nsIGlobalObject* aGlobal);
void Init(const Sequence<nsString>& aResourceIds, const bool aSync, void Activate(const bool aSync, const bool aEager,
const BundleGenerator& aBundleGenerator, ErrorResult& aRv); const BundleGenerator& aBundleGenerator);
static already_AddRefed<Localization> Constructor( static already_AddRefed<Localization> Constructor(
const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds, const GlobalObject& aGlobal, const Sequence<nsString>& aResourceIds,

View file

@ -211,32 +211,42 @@ function maybeReportErrorToGecko(error) {
*/ */
class Localization { class Localization {
/** /**
* @param {Array<String>} resourceIds - List of resource IDs * `Activate` has to be called for this object to be usable.
* *
* @returns {Localization} * @returns {Localization}
*/ */
constructor(resourceIds = []) { constructor() {
this.resourceIds = resourceIds; this.resourceIds = [];
this.generateBundles = defaultGenerateBundles; this.generateBundles = undefined;
this.generateBundlesSync = defaultGenerateBundlesSync; this.generateBundlesSync = undefined;
this.isSync = undefined;
this.bundles = undefined;
} }
setGenerateBundles(generateBundles) { /**
* Activate the instance of the `Localization` class.
*
* @param {bool} sync - Whether the instance should be
* synchronous.
* @param {bool} eager - Whether the initial bundles should be
* fetched eagerly.
* @param {Function} generateBundles - Custom FluentBundle asynchronous generator.
* @param {Function} generateBundlesSync - Custom FluentBundle generator.
*/
activate(sync, eager, generateBundles = defaultGenerateBundles, generateBundlesSync = defaultGenerateBundlesSync) {
if (this.bundles) {
throw new Error("Attempt to initialize an already initialized instance.");
}
this.generateBundles = generateBundles; this.generateBundles = generateBundles;
}
setGenerateBundlesSync(generateBundlesSync) {
this.generateBundlesSync = generateBundlesSync; this.generateBundlesSync = generateBundlesSync;
this.isSync = sync;
this.regenerateBundles(eager);
} }
setIsSync(isSync) { setIsSync(isSync) {
this.isSync = isSync; this.isSync = isSync;
} }
init(eager = false) {
this.regenerateBundles(eager);
}
cached(iterable) { cached(iterable) {
if (this.isSync) { if (this.isSync) {
return CachedSyncIterable.from(iterable); return CachedSyncIterable.from(iterable);
@ -273,6 +283,9 @@ class Localization {
* @private * @private
*/ */
async formatWithFallback(keys, method) { async formatWithFallback(keys, method) {
if (!this.bundles) {
throw new Error("Attempt to format on an uninitialized instance.");
}
const translations = new Array(keys.length).fill(null); const translations = new Array(keys.length).fill(null);
let hasAtLeastOneBundle = false; let hasAtLeastOneBundle = false;
@ -312,6 +325,10 @@ class Localization {
if (!this.isSync) { if (!this.isSync) {
throw new Error("Can't use sync formatWithFallback when state is async."); throw new Error("Can't use sync formatWithFallback when state is async.");
} }
if (!this.bundles) {
throw new Error("Attempt to format on an uninitialized instance.");
}
const translations = new Array(keys.length).fill(null); const translations = new Array(keys.length).fill(null);
let hasAtLeastOneBundle = false; let hasAtLeastOneBundle = false;
@ -488,8 +505,10 @@ class Localization {
} }
onChange() { onChange() {
if (this.bundles) {
this.regenerateBundles(false); this.regenerateBundles(false);
} }
}
/** /**
* This method should be called when there's a reason to believe * This method should be called when there's a reason to believe
@ -641,8 +660,8 @@ function keysFromBundle(method, bundle, keys, translations) {
* Helper function which allows us to construct a new * Helper function which allows us to construct a new
* Localization from Localization. * Localization from Localization.
*/ */
var getLocalization = (resourceIds) => { var getLocalization = () => {
return new Localization(resourceIds); return new Localization();
}; };
this.Localization = Localization; this.Localization = Localization;

View file

@ -14,11 +14,9 @@
[scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)] [scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)]
interface mozILocalization : nsISupports interface mozILocalization : nsISupports
{ {
void setGenerateBundles(in jsval generateBundles); void activate(in bool aSync, in bool aEager, in jsval aGenerateBundles, in jsval aGenerateBundlesSync);
void setGenerateBundlesSync(in jsval generateBundlesSync);
void setIsSync(in boolean isSync);
void init(in boolean aEager); void setIsSync(in boolean isSync);
unsigned long addResourceIds(in Array<AString> resourceIds); unsigned long addResourceIds(in Array<AString> resourceIds);
unsigned long removeResourceIds(in Array<AString> resourceIds); unsigned long removeResourceIds(in Array<AString> resourceIds);
@ -37,5 +35,5 @@ interface mozILocalization : nsISupports
[scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)] [scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)]
interface mozILocalizationJSM : nsISupports interface mozILocalizationJSM : nsISupports
{ {
mozILocalization getLocalization(in Array<AString> resourceIds); mozILocalization getLocalization();
}; };