Bug 1518252 - Block layout on Fluent. r=smaug

Differential Revision: https://phabricator.services.mozilla.com/D17334

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Zibi Braniecki 2019-01-23 21:29:15 +00:00
parent 9359df8494
commit 5e38c8a5a5
12 changed files with 105 additions and 53 deletions

View File

@ -1339,7 +1339,8 @@ Document::Document(const char* aContentType)
mThrowOnDynamicMarkupInsertionCounter(0),
mIgnoreOpensDuringUnloadCounter(0),
mDocLWTheme(Doc_Theme_Uninitialized),
mSavedResolution(1.0f) {
mSavedResolution(1.0f),
mPendingInitialTranslation(false) {
MOZ_LOG(gDocumentLeakPRLog, LogLevel::Debug, ("DOCUMENT %p created", this));
SetIsInDocument();
@ -3131,6 +3132,8 @@ void Document::LocalizationLinkAdded(Element* aLinkElement) {
// will be resolved once the end of l10n resource
// container is reached.
mL10nResources.AppendElement(href);
mPendingInitialTranslation = true;
}
}
@ -3173,11 +3176,30 @@ void Document::OnL10nResourceContainerParsed() {
}
void Document::TriggerInitialDocumentTranslation() {
// Let's call it again, in case the resource
// container has not been closed, and only
// now we're closing the document.
OnL10nResourceContainerParsed();
if (mDocumentL10n) {
mDocumentL10n->TriggerInitialDocumentTranslation();
}
}
void Document::InitialDocumentTranslationCompleted() {
mPendingInitialTranslation = false;
nsCOMPtr<nsIContentSink> sink;
if (mParser) {
sink = mParser->GetContentSink();
} else {
sink = do_QueryReferent(mWeakSink);
}
if (sink) {
sink->InitialDocumentTranslationCompleted();
}
}
bool Document::IsWebAnimationsEnabled(JSContext* aCx, JSObject* /*unused*/) {
MOZ_ASSERT(NS_IsMainThread());
@ -8141,7 +8163,6 @@ void Document::SetReadyStateInternal(ReadyState rs) {
mXULPersist->Init();
}
}
TriggerInitialDocumentTranslation();
}
RecordNavigationTiming(rs);

View File

@ -3425,13 +3425,12 @@ class Document : public nsINode,
*/
void LocalizationLinkRemoved(Element* aLinkElement);
protected:
/**
* This method should be collect as soon as the
* This method should be called as soon as the
* parsing of the document is completed.
*
* In HTML this happens when readyState becomes
* `interactive`.
* In HTML/XHTML this happens when we finish parsing
* the document element.
* In XUL it happens at `DoneWalking`, during
* `MozBeforeInitialXULLayout`.
*
@ -3440,6 +3439,18 @@ class Document : public nsINode,
*/
void TriggerInitialDocumentTranslation();
/**
* This method is called when the initial translation
* of the document is completed.
*
* It unblocks the layout.
*
* This method is virtual so that XULDocument can
* override it.
*/
virtual void InitialDocumentTranslationCompleted();
protected:
RefPtr<mozilla::dom::DocumentL10n> mDocumentL10n;
private:
@ -4513,9 +4524,13 @@ class Document : public nsINode,
// Pres shell resolution saved before entering fullscreen mode.
float mSavedResolution;
bool mPendingInitialTranslation;
public:
// Needs to be public because the bindings code pokes at it.
js::ExpandoAndGeneration mExpandoAndGeneration;
bool HasPendingInitialTranslation() { return mPendingInitialTranslation; }
};
NS_DEFINE_STATIC_IID_ACCESSOR(Document, NS_IDOCUMENT_IID)

View File

@ -1170,8 +1170,9 @@ void nsContentSink::StartLayout(bool aIgnorePendingSheets) {
mDeferredLayoutStart = true;
if (!aIgnorePendingSheets && WaitForPendingSheets()) {
// Bail out; we'll start layout when the sheets load
if (!aIgnorePendingSheets &&
(WaitForPendingSheets() || mDocument->HasPendingInitialTranslation())) {
// Bail out; we'll start layout when the sheets and l10n load
return;
}

View File

@ -1073,6 +1073,8 @@ nsresult nsXMLContentSink::HandleEndElement(const char16_t* aName,
// probably need to deal here.... (and stop appending them on open).
mState = eXMLContentSinkState_InEpilog;
mDocument->TriggerInitialDocumentTranslation();
// We might have had no occasion to start layout yet. Do so now.
MaybeStartLayout(false);
}
@ -1407,6 +1409,10 @@ nsresult nsXMLContentSink::AddText(const char16_t* aText, int32_t aLength) {
return NS_OK;
}
void nsXMLContentSink::InitialDocumentTranslationCompleted() {
StartLayout(false);
}
void nsXMLContentSink::FlushPendingNotifications(FlushType aType) {
// Only flush tags if we're not doing the notification ourselves
// (since we aren't reentrant)

View File

@ -67,6 +67,7 @@ class nsXMLContentSink : public nsContentSink,
NS_IMETHOD WillInterrupt(void) override;
NS_IMETHOD WillResume(void) override;
NS_IMETHOD SetParser(nsParserBase* aParser) override;
virtual void InitialDocumentTranslationCompleted() override;
virtual void FlushPendingNotifications(mozilla::FlushType aType) override;
virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override;
virtual nsISupports* GetTarget() override;

View File

@ -449,6 +449,11 @@ void XULDocument::AddElementToDocumentPost(Element* aElement) {
}
}
void XULDocument::InitialDocumentTranslationCompleted() {
mPendingInitialTranslation = false;
MaybeDoneWalking();
}
void XULDocument::AddSubtreeToDocument(nsIContent* aContent) {
MOZ_ASSERT(aContent->GetComposedDoc() == this, "Element not in doc!");
@ -958,15 +963,26 @@ nsresult XULDocument::ResumeWalk() {
mXULPersist->Init();
mStillWalking = false;
if (mPendingSheets == 0) {
rv = DoneWalking();
return MaybeDoneWalking();
}
nsresult XULDocument::MaybeDoneWalking() {
if (mPendingSheets > 0 || mStillWalking) {
return NS_OK;
}
return rv;
if (mPendingInitialTranslation) {
TriggerInitialDocumentTranslation();
return NS_OK;
}
return DoneWalking();
}
nsresult XULDocument::DoneWalking() {
MOZ_ASSERT(mPendingSheets == 0, "there are sheets to be loaded");
MOZ_ASSERT(!mStillWalking, "walk not done");
MOZ_ASSERT(!mPendingInitialTranslation, "translation pending");
// XXXldb This is where we should really be setting the chromehidden
// attribute.
@ -983,18 +999,10 @@ nsresult XULDocument::DoneWalking() {
NotifyPossibleTitleChange(false);
// For performance reasons, we want to trigger the DocumentL10n's
// `TriggerInitialDocumentTranslation` within the same microtask that will
// be created for a `MozBeforeInitialXULLayout` event listener.
AddEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
mDocumentL10n, true, false);
nsContentUtils::DispatchTrustedEvent(
this, ToSupports(this), NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
CanBubble::eYes, Cancelable::eNo);
RemoveEventListener(NS_LITERAL_STRING("MozBeforeInitialXULLayout"),
mDocumentL10n, true);
// Before starting layout, check whether we're a toplevel chrome
// window. If we are, setup some state so that we don't have to restyle
@ -1039,9 +1047,7 @@ XULDocument::StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred,
--mPendingSheets;
if (!mStillWalking && mPendingSheets == 0) {
return DoneWalking();
}
return MaybeDoneWalking();
}
return NS_OK;

View File

@ -76,6 +76,8 @@ class XULDocument final : public XMLDocument,
virtual void EndLoad() override;
virtual void InitialDocumentTranslationCompleted() override;
// nsIMutationObserver interface
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
@ -296,7 +298,15 @@ class XULDocument final : public XMLDocument,
nsresult ResumeWalk();
/**
* Called at the end of ResumeWalk() and from StyleSheetLoaded().
* Called at the end of ResumeWalk(), from StyleSheetLoaded(),
* and from DocumentL10n.
* If walking, stylesheets and l10n are not blocking, it
* will trigger `DoneWalking()`.
*/
nsresult MaybeDoneWalking();
/**
* Called from `MaybeDoneWalking()`.
* Expects that both the prototype document walk is complete and
* all referenced stylesheets finished loading.
*/

View File

@ -8,7 +8,6 @@
#include "mozilla/dom/DocumentL10n.h"
#include "mozilla/dom/DocumentL10nBinding.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/PromiseNativeHandler.h"
#include "nsQueryObject.h"
@ -60,18 +59,12 @@ void PromiseResolver::RejectedCallback(JSContext* aCx,
PromiseResolver::~PromiseResolver() { mPromise = nullptr; }
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DocumentL10n)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(DocumentL10n)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DocumentL10n)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DocumentL10n, mDocument, mDOMLocalization,
mReady)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DocumentL10n, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DocumentL10n, Release)
DocumentL10n::DocumentL10n(Document* aDocument)
: mDocument(aDocument), mState(DocumentL10nState::Initialized) {}
@ -140,19 +133,6 @@ already_AddRefed<Promise> DocumentL10n::MaybeWrapPromise(
return docPromise.forget();
}
NS_IMETHODIMP
DocumentL10n::HandleEvent(Event* aEvent) {
#ifdef DEBUG
nsAutoString eventType;
aEvent->GetType(eventType);
MOZ_ASSERT(eventType.EqualsLiteral("MozBeforeInitialXULLayout"));
#endif
TriggerInitialDocumentTranslation();
return NS_OK;
}
uint32_t DocumentL10n::AddResourceIds(nsTArray<nsString>& aResourceIds) {
uint32_t ret = 0;
mDOMLocalization->AddResourceIds(aResourceIds, false, &ret);
@ -312,13 +292,16 @@ class L10nReadyHandler final : public PromiseNativeHandler {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(L10nReadyHandler)
explicit L10nReadyHandler(Promise* aPromise) : mPromise(aPromise) {}
explicit L10nReadyHandler(Promise* aPromise, Document* aDocument)
: mPromise(aPromise), mDocument(aDocument) {}
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
mDocument->InitialDocumentTranslationCompleted();
mPromise->MaybeResolveWithUndefined();
}
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override {
mDocument->InitialDocumentTranslationCompleted();
mPromise->MaybeRejectWithUndefined();
}
@ -326,6 +309,7 @@ class L10nReadyHandler final : public PromiseNativeHandler {
~L10nReadyHandler() = default;
RefPtr<Promise> mPromise;
RefPtr<Document> mDocument;
};
NS_IMPL_CYCLE_COLLECTION(L10nReadyHandler, mPromise)
@ -351,7 +335,8 @@ void DocumentL10n::TriggerInitialDocumentTranslation() {
RefPtr<Promise> promise;
mDOMLocalization->TranslateRoots(getter_AddRefs(promise));
RefPtr<PromiseNativeHandler> l10nReadyHandler = new L10nReadyHandler(mReady);
RefPtr<PromiseNativeHandler> l10nReadyHandler =
new L10nReadyHandler(mReady, mDocument);
promise->AppendNativeHandler(l10nReadyHandler);
}

View File

@ -11,7 +11,6 @@
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "nsIDOMEventListener.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/Document.h"
@ -53,11 +52,10 @@ enum class DocumentL10nState { Initialized = 0, InitialTranslationTriggered };
* instance of mozIDOMLocalization and maintains a single promise
* which gets resolved the first time the document gets translated.
*/
class DocumentL10n final : public nsIDOMEventListener, public nsWrapperCache {
class DocumentL10n final : public nsWrapperCache {
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DocumentL10n)
NS_DECL_NSIDOMEVENTLISTENER
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(DocumentL10n)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(DocumentL10n)
public:
explicit DocumentL10n(Document* aDocument);

View File

@ -170,6 +170,7 @@ nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated) {
}
if (!destroying) {
mDocument->TriggerInitialDocumentTranslation();
nsContentSink::StartLayout(false);
}
}
@ -228,6 +229,10 @@ nsHtml5TreeOpExecutor::SetParser(nsParserBase* aParser) {
return NS_OK;
}
void nsHtml5TreeOpExecutor::InitialDocumentTranslationCompleted() {
nsContentSink::StartLayout(false);
}
void nsHtml5TreeOpExecutor::FlushPendingNotifications(FlushType aType) {
if (aType >= FlushType::EnsurePresShellInitAndFrames) {
// Bug 577508 / 253951

View File

@ -136,6 +136,8 @@ class nsHtml5TreeOpExecutor final
*/
NS_IMETHOD WillResume() override;
virtual void InitialDocumentTranslationCompleted() override;
/**
* Sets the parser.
*/

View File

@ -130,6 +130,8 @@ class nsIContentSink : public nsISupports {
* Posts a runnable that continues parsing.
*/
virtual void ContinueInterruptedParsingAsync() {}
virtual void InitialDocumentTranslationCompleted() {}
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentSink, NS_ICONTENT_SINK_IID)