Don't start layout for XML just because the root has been opened. And don't
Don't start layout for XML just because the root has been opened. And don't
start layout until <html:head> closes if <html:head> gets opened. For XHTML
documents, this should prevent flashes of unstyled content.
Also, don't flush tags unless forced to if a sheet is pending.
Bug 380028, r+sr=sicking
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -264,24 +264,30 @@ NS_IMETHODIMP
nsContentSink::StyleSheetLoaded(nsICSSStyleSheet* aSheet,
PRBool aWasAlternate,
nsresult aStatus)
{
if (!aWasAlternate) {
NS_ASSERTION(mPendingSheetCount > 0, "How'd that happen?");
--mPendingSheetCount;
- if (mPendingSheetCount == 0 && mDeferredLayoutStart) {
- // We might not have really started layout, since this sheet was still
- // loading. Do it now. Probably doesn't matter whether we do this
- // before or after we unblock scripts, but before feels saner. Note that
- // if mDeferredLayoutStart is true, that means any subclass StartLayout()
- // stuff that needs to happen has already happened, so we don't need to
- // worry about it.
- StartLayout(PR_FALSE);
+ if (mPendingSheetCount == 0 &&
+ (mDeferredLayoutStart || mDeferredFlushTags)) {
+ if (mDeferredFlushTags) {
+ FlushTags();
+ }
+ if (mDeferredLayoutStart) {
+ // We might not have really started layout, since this sheet was still
+ // loading. Do it now. Probably doesn't matter whether we do this
+ // before or after we unblock scripts, but before feels saner. Note
+ // that if mDeferredLayoutStart is true, that means any subclass
+ // StartLayout() stuff that needs to happen has already happened, so we
+ // don't need to worry about it.
+ StartLayout(PR_FALSE);
+ }
// Go ahead and try to scroll to our ref if we have one
TryToScrollToRef();
}
mScriptLoader->RemoveExecuteBlocker();
}
@@ -1014,17 +1020,17 @@ nsContentSink::StartLayout(PRBool aIgnor
{
if (mLayoutStarted) {
// Nothing to do here
return;
}
mDeferredLayoutStart = PR_TRUE;
- if (!aIgnorePendingSheets && mPendingSheetCount > 0) {
+ if (!aIgnorePendingSheets && WaitForPendingSheets()) {
// Bail out; we'll start layout when the sheets load
return;
}
mDeferredLayoutStart = PR_FALSE;
// Notify on all our content. If none of our presshells have started layout
// yet it'll be a no-op except for updating our data structures, a la
@@ -1167,35 +1173,45 @@ nsContentSink::Notify(nsITimer *timer)
mBackoffCount--;
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_REFLOW,
("nsContentSink::Notify: reflow on a timer: %d milliseconds "
"late, backoff count: %d", delay, mBackoffCount));
}
#endif
- FlushTags();
+ if (WaitForPendingSheets()) {
+ mDeferredFlushTags = PR_TRUE;
+ } else {
+ FlushTags();
- // Now try and scroll to the reference
- // XXX Should we scroll unconditionally for history loads??
- TryToScrollToRef();
+ // Now try and scroll to the reference
+ // XXX Should we scroll unconditionally for history loads??
+ TryToScrollToRef();
+ }
+
mNotificationTimer = nsnull;
MOZ_TIMER_DEBUGLOG(("Stop: nsHTMLContentSink::Notify()\n"));
MOZ_TIMER_STOP(mWatch);
return NS_OK;
}
PRBool
nsContentSink::IsTimeToNotify()
{
if (!mNotifyOnTimer || !mLayoutStarted || !mBackoffCount ||
mInMonolithicContainer) {
return PR_FALSE;
}
+ if (WaitForPendingSheets()) {
+ mDeferredFlushTags = PR_TRUE;
+ return PR_FALSE;
+ }
+
PRTime now = PR_Now();
PRInt64 interval, diff;
LL_I2L(interval, GetNotificationInterval());
LL_SUB(diff, now, mLastNotificationTime);
if (LL_CMP(diff, >, interval)) {
mBackoffCount--;
@@ -1208,17 +1224,19 @@ nsContentSink::IsTimeToNotify()
nsresult
nsContentSink::WillInterruptImpl()
{
nsresult result = NS_OK;
SINK_TRACE(gContentSinkLogModuleInfo, SINK_TRACE_CALLS,
("nsContentSink::WillInterrupt: this=%p", this));
#ifndef SINK_NO_INCREMENTAL
- if (mNotifyOnTimer && mLayoutStarted) {
+ if (WaitForPendingSheets()) {
+ mDeferredFlushTags = PR_TRUE;
+ } else if (mNotifyOnTimer && mLayoutStarted) {
if (mBackoffCount && !mInMonolithicContainer) {
PRInt64 now = PR_Now();
PRInt64 interval = GetNotificationInterval();
PRInt64 diff = now - mLastNotificationTime;
// If it's already time for us to have a notification
if (diff > interval || mDroppedTimer) {
mBackoffCount--;
--- a/content/base/src/nsContentSink.h
+++ b/content/base/src/nsContentSink.h
@@ -206,16 +206,20 @@ protected:
// Overridable hooks into script evaluation
virtual void PreEvaluateScript() {return;}
virtual void PostEvaluateScript(nsIScriptElement *aElement) {return;}
virtual nsresult FlushTags() = 0;
void TryToScrollToRef();
+ // Later on we might want to make this more involved somehow
+ // (e.g. stop waiting after some timeout or whatnot).
+ PRBool WaitForPendingSheets() { return mPendingSheetCount > 0; }
+
private:
// People shouldn't be allocating this class directly. All subclasses should
// be allocated using a zeroing operator new.
void* operator new(size_t sz) CPP_THROW_NEW; // Not to be implemented
protected:
void ContinueInterruptedParsingAsync();
@@ -271,17 +275,19 @@ protected:
PRUint8 mInTitle : 1;
PRUint8 mChangeScrollPosWhenScrollingToRef : 1;
// If true, we deferred starting layout until sheets load
PRUint8 mDeferredLayoutStart : 1;
// true if an <link rel="offline-resource"> nodes have been encountered.
PRUint8 mHaveOfflineResources : 1;
// true if offline-resource links should be saved to the offline cache
PRUint8 mSaveOfflineResources : 1;
-
+ // If true, we deferred notifications until sheets load
+ PRUint8 mDeferredFlushTags : 1;
+
// -- Can interrupt parsing members --
PRUint32 mDelayTimerStart;
// Interrupt parsing during token procesing after # of microseconds
PRInt32 mMaxTokenProcessingTime;
// Switch between intervals when time is exceeded
PRInt32 mDynamicIntervalSwitchThreshold;
--- a/content/html/document/src/nsHTMLContentSink.cpp
+++ b/content/html/document/src/nsHTMLContentSink.cpp
@@ -1335,16 +1335,17 @@ SinkContext::AddText(const nsAString& aT
* they are visible in the tree. Specifically, make sure
* that they are all added to their respective parents.
* Also, do notification at the top for all content that
* has been newly added so that the frame tree is complete.
*/
nsresult
SinkContext::FlushTags()
{
+ mSink->mDeferredFlushTags = PR_FALSE;
PRBool oldBeganUpdate = mSink->mBeganUpdate;
PRUint32 oldUpdates = mSink->mUpdatesInNotification;
++(mSink->mInNotification);
mSink->mUpdatesInNotification = 0;
{
// Scope so we call EndUpdate before we decrease mInNotification
mozAutoDocUpdate updateBatch(mSink->mDocument, UPDATE_CONTENT_MODEL,
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -1061,27 +1061,34 @@ nsXMLContentSink::HandleStartElement(con
NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
parent->AppendChildTo(content, PR_FALSE);
}
}
// Some HTML nodes need DoneCreatingElement() called to initialize
// properly (eg form state restoration).
- if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML &&
- (nodeInfo->NameAtom() == nsGkAtoms::input ||
- nodeInfo->NameAtom() == nsGkAtoms::button)) {
- content->DoneCreatingElement();
+ if (nodeInfo->NamespaceID() == kNameSpaceID_XHTML) {
+ if (nodeInfo->NameAtom() == nsGkAtoms::input ||
+ nodeInfo->NameAtom() == nsGkAtoms::button) {
+ content->DoneCreatingElement();
+ } else if (nodeInfo->NameAtom() == nsGkAtoms::head && !mCurrentHead) {
+ mCurrentHead = content;
+ }
}
if (IsMonolithicContainer(nodeInfo)) {
mInMonolithicContainer++;
}
- MaybeStartLayout(PR_FALSE);
+ if (content != mDocElement && !mCurrentHead) {
+ // This isn't the root and we're not inside an XHTML <head>.
+ // Might need to start layout
+ MaybeStartLayout(PR_FALSE);
+ }
return aInterruptable && NS_SUCCEEDED(result) ? DidProcessATokenImpl() :
result;
}
NS_IMETHODIMP
nsXMLContentSink::HandleEndElement(const PRUnichar *aName)
{
@@ -1117,20 +1124,27 @@ nsXMLContentSink::HandleEndElement(const
getter_AddRefs(debugTagAtom),
&debugNameSpaceID);
NS_ASSERTION(content->NodeInfo()->Equals(debugTagAtom, debugNameSpaceID),
"Wrong element being closed");
#endif
result = CloseElement(content);
+ if (mCurrentHead == content) {
+ mCurrentHead = nsnull;
+ }
+
if (mDocElement == content) {
// XXXbz for roots that don't want to be appended on open, we
// probably need to deal here.... (and stop appending them on open).
mState = eXMLContentSinkState_InEpilog;
+
+ // We might have had no occasion to start layout yet. Do so now.
+ MaybeStartLayout(PR_FALSE);
}
PRInt32 stackLen = mContentStack.Length();
if (mNotifyLevel >= stackLen) {
if (numFlushed < content->GetChildCount()) {
NotifyAppend(content, numFlushed);
}
mNotifyLevel = stackLen - 1;
@@ -1586,16 +1600,17 @@ nsXMLContentSink::FlushPendingNotificati
* they are visible in the tree. Specifically, make sure
* that they are all added to their respective parents.
* Also, do notification at the top for all content that
* has been newly added so that the frame tree is complete.
*/
nsresult
nsXMLContentSink::FlushTags()
{
+ mDeferredFlushTags = PR_FALSE;
PRBool oldBeganUpdate = mBeganUpdate;
PRUint32 oldUpdates = mUpdatesInNotification;
mUpdatesInNotification = 0;
++mInNotification;
{
// Scope so we call EndUpdate before we decrease mInNotification
mozAutoDocUpdate updateBatch(mDocument, UPDATE_CONTENT_MODEL, PR_TRUE);
--- a/content/xml/document/src/nsXMLContentSink.h
+++ b/content/xml/document/src/nsXMLContentSink.h
@@ -178,16 +178,17 @@ protected:
PRUint32 aAttsCount, PRInt32 aIndex,
PRUint32 aLineNumber,
PRBool aInterruptable);
nsresult HandleEndElement(const PRUnichar *aName, PRBool aInterruptable);
nsresult HandleCharacterData(const PRUnichar *aData, PRUint32 aLength,
PRBool aInterruptable);
nsIContent* mDocElement;
+ nsCOMPtr<nsIContent> mCurrentHead; // When set, we're in an XHTML <haed>
PRUnichar* mText;
XMLContentSinkState mState;
nsString mTitleText;
PRInt32 mTextLength;
PRInt32 mTextSize;