When showing a document viewer, don't start layout on the documnt unless it's already had layout started once. Otherwise, just wait for the sink, or whoever is responsible for it, to start layout once they're ready. Bug 404470, r+sr=jst
authorbzbarsky@mit.edu
Sun, 20 Jan 2008 10:02:02 -0800
changeset 10474 9d954b90d921610e7baa45d99a79f91c79f738b3
parent 10473 79ad141c879162e8875ea8441041dcfb6cfc3d2c
child 10475 5c0342e2cf4d433c0e8f60b7ca22f3dd56119f3c
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherderautoland@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs404470
milestone1.9b3pre
When showing a document viewer, don't start layout on the documnt unless it's already had layout started once. Otherwise, just wait for the sink, or whoever is responsible for it, to start layout once they're ready. Bug 404470, r+sr=jst
content/base/public/nsIDocument.h
content/base/src/nsContentSink.cpp
content/base/src/nsDocument.cpp
content/html/document/src/nsMediaDocument.cpp
content/xul/document/src/nsXULDocument.cpp
layout/base/nsDocumentViewer.cpp
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -91,19 +91,18 @@ template<class E> class nsCOMArray;
 class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 
 // IID for the nsIDocument interface
 #define NS_IDOCUMENT_IID      \
-{ 0xed21686d, 0x4e2f, 0x41f5, \
-  { 0x94, 0xaa, 0xcc, 0x1f, 0xbd, 0xfa, 0x1f, 0x84 } }
-
+{ 0x626d86d2, 0x615f, 0x4a12, \
+ { 0x94, 0xd8, 0xe3, 0xdb, 0x3a, 0x29, 0x83, 0x72 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 //----------------------------------------------------------------------
 
 // Document interface.  This is implemented by all document objects in
 // Gecko.
@@ -116,16 +115,17 @@ public:
 #ifdef MOZILLA_INTERNAL_API
   nsIDocument()
     : nsINode(nsnull),
       mCharacterSet(NS_LITERAL_CSTRING("ISO-8859-1")),
       mBindingManager(nsnull),
       mNodeInfoManager(nsnull),
       mCompatMode(eCompatibility_FullStandards),
       mIsInitialDocumentInWindow(PR_FALSE),
+      mMayStartLayout(PR_TRUE),
       mPartID(0),
       mJSObject(nsnull)
   {
     mParentPtrBits |= PARENT_BIT_INDOCUMENT;
   }
 #endif
   
   /**
@@ -146,16 +146,21 @@ public:
    *                           documents.
    * @param aReset whether the document should call Reset() on itself.  If this
    *               is false, the document will NOT set its principal to the
    *               channel's owner, will not clear any event listeners that are
    *               already set on it, etc.
    * @param aSink The content sink to use for the data.  If this is null and
    *              the document needs a content sink, it will create one based
    *              on whatever it knows about the data it's going to load.
+   *
+   * Once this has been called, the document will return false for
+   * MayStartLayout() until SetMayStartLayout(PR_TRUE) is called on it.  Making
+   * sure this happens is the responsibility of the caller of
+   * StartDocumentLoad().
    */  
   virtual nsresult StartDocumentLoad(const char* aCommand,
                                      nsIChannel* aChannel,
                                      nsILoadGroup* aLoadGroup,
                                      nsISupports* aContainer,
                                      nsIStreamListener **aDocListener,
                                      PRBool aReset,
                                      nsIContentSink* aSink = nsnull) = 0;
@@ -354,17 +359,19 @@ public:
    * sources, like HTML META tags).
    */
   virtual void GetHeaderData(nsIAtom* aHeaderField, nsAString& aData) const = 0;
   virtual void SetHeaderData(nsIAtom* aheaderField, const nsAString& aData) = 0;
 
   /**
    * Create a new presentation shell that will use aContext for its
    * presentation context (presentation contexts <b>must not</b> be
-   * shared among multiple presentation shells).
+   * shared among multiple presentation shells). The caller of this
+   * method is responsible for calling BeginObservingDocument() on the
+   * presshell if the presshell should observe document mutations.
    */
   virtual nsresult CreateShell(nsPresContext* aContext,
                                nsIViewManager* aViewManager,
                                nsStyleSet* aStyleSet,
                                nsIPresShell** aInstancePtrResult) = 0;
   virtual PRBool DeleteShell(nsIPresShell* aShell) = 0;
   virtual nsIPresShell *GetPrimaryShell() const = 0;
   void SetShellsHidden(PRBool aHide) { mShellsAreHidden = aHide; }
@@ -909,16 +916,26 @@ public:
     return mMarkedCCGeneration;
   }
 
   PRBool IsLoadedAsData()
   {
     return mLoadedAsData;
   }
 
+  PRBool MayStartLayout()
+  {
+    return mMayStartLayout;
+  }
+
+  void SetMayStartLayout(PRBool aMayStartLayout)
+  {
+    mMayStartLayout = aMayStartLayout;
+  }
+
   JSObject* GetJSObject() const
   {
     return mJSObject;
   }
 
   void SetJSObject(JSObject *aJSObject)
   {
     mJSObject = aJSObject;
@@ -988,16 +1005,20 @@ protected:
   PRPackedBool mIsInitialDocumentInWindow;
 
   PRPackedBool mShellsAreHidden;
 
   // True if we're loaded as data and therefor has any dangerous stuff, such
   // as scripts and plugins, disabled.
   PRPackedBool mLoadedAsData;
 
+  // If true, whoever is creating the document has gotten it to the
+  // point where it's safe to start layout on it.
+  PRPackedBool mMayStartLayout;
+
   // The bidi options for this document.  What this bitfield means is
   // defined in nsBidiUtils.h
   PRUint32 mBidiOptions;
 
   nsCString mContentLanguage;
   nsCString mContentType;
 
   // The document's security info
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -970,16 +970,17 @@ nsContentSink::StartLayout(PRBool aIgnor
   // have right now).  If some of them _have_ started layout, we want to make
   // sure to flush tags instead of just calling UpdateChildCounts() after we
   // loop over the shells.
   FlushTags();
 
   mLayoutStarted = PR_TRUE;
   mLastNotificationTime = PR_Now();
 
+  mDocument->SetMayStartLayout(PR_TRUE);
   nsPresShellIterator iter(mDocument);
   nsCOMPtr<nsIPresShell> shell;
   while ((shell = iter.GetNextShell())) {
     // Make sure we don't call InitialReflow() for a shell that has
     // already called it. This can happen when the layout frame for
     // an iframe is constructed *between* the Embed() call for the
     // docshell in the iframe, and the content sink's call to OpenBody().
     // (Bug 153815)
@@ -990,20 +991,16 @@ nsContentSink::StartLayout(PRBool aIgnor
       // XXX: The assumption here is that if something already
       // called InitialReflow() on this shell, it also did some of
       // the setup below, so we do nothing and just move on to the
       // next shell in the list.
 
       continue;
     }
 
-    // Make shell an observer for next time
-    shell->BeginObservingDocument();
-
-    // Resize-reflow this time
     nsRect r = shell->GetPresContext()->GetVisibleArea();
     nsCOMPtr<nsIPresShell> shellGrip = shell;
     nsresult rv = shell->InitialReflow(r.width, r.height);
     if (NS_FAILED(rv)) {
       return;
     }
 
     // Now trigger a refresh
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1448,16 +1448,18 @@ nsDocument::StartDocumentLoad(const char
 
     // Do not load/process scripts when loading as data
     ScriptLoader()->SetEnabled(PR_FALSE);
 
     // styles
     CSSLoader()->SetEnabled(PR_FALSE); // Do not load/process styles when loading as data
   }
 
+  mMayStartLayout = PR_FALSE;
+
   if (aReset) {
     Reset(aChannel, aLoadGroup);
   }
 
   nsCAutoString contentType;
   if (NS_SUCCEEDED(aChannel->GetContentType(contentType))) {
     // XXX this is only necessary for viewsource:
     nsACString::const_iterator start, end, semicolon;
--- a/content/html/document/src/nsMediaDocument.cpp
+++ b/content/html/document/src/nsMediaDocument.cpp
@@ -260,23 +260,20 @@ nsMediaDocument::CreateSyntheticDocument
   root->AppendChildTo(body, PR_FALSE);
 
   return NS_OK;
 }
 
 nsresult
 nsMediaDocument::StartLayout()
 {
+  mMayStartLayout = PR_TRUE;
   nsPresShellIterator iter(this);
   nsCOMPtr<nsIPresShell> shell;
   while ((shell = iter.GetNextShell())) {
-    // Make shell an observer for next time.
-    shell->BeginObservingDocument();
-
-    // Initial-reflow this time.
     nsRect visibleArea = shell->GetPresContext()->GetVisibleArea();
     nsCOMPtr<nsIPresShell> shellGrip = shell;
     nsresult rv = shell->InitialReflow(visibleArea.width, visibleArea.height);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Now trigger a refresh.  vm might be null if the presshell got
     // Destroy() called already.
     nsIViewManager* vm = shell->GetViewManager();
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -414,16 +414,17 @@ nsXULDocument::StartDocumentLoad(const c
                                  nsILoadGroup* aLoadGroup,
                                  nsISupports* aContainer,
                                  nsIStreamListener **aDocListener,
                                  PRBool aReset, nsIContentSink* aSink)
 {
     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
     // we'll possibly need to reset our content type afterwards.
     mStillWalking = PR_TRUE;
+    mMayStartLayout = PR_FALSE;
     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
 
     mDocumentTitle.SetIsVoid(PR_TRUE);
 
     mChannel = aChannel;
 
     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
     nsresult rv =
@@ -1927,29 +1928,23 @@ nsXULDocument::StartLayout(void)
                 PRBool enabled;
                 contentViewer->GetEnableRendering(&enabled);
                 if (enabled) {
                     vm->EnableRefresh(NS_VMREFRESH_IMMEDIATE);
                 }
             }
         }
 
+        mMayStartLayout = PR_TRUE;
+        
         // Make sure we're holding a strong ref to |shell| before we call
         // InitialReflow()
         nsCOMPtr<nsIPresShell> shellGrip = shell;
         rv = shell->InitialReflow(r.width, r.height);
         NS_ENSURE_SUCCESS(rv, rv);
-
-        // Start observing the document _after_ we do the initial
-        // reflow. Otherwise, we'll get into an trouble trying to
-        // create kids before the root frame is established.
-        // XXXbz why is that an issue here and not in nsContentSink or
-        // nsDocumentViewer?  Perhaps we should just flush the way
-        // nsDocumentViewer does?
-        shell->BeginObservingDocument();
     }
 
     return NS_OK;
 }
 
 /* static */
 PRBool
 nsXULDocument::MatchAttribute(nsIContent* aContent,
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1912,17 +1912,18 @@ DocumentViewerImpl::Show(void)
       }
 
       mPresContext->SetContainer(base_win);
     }
 
     if (mPresContext) {
       Hide();
 
-      rv = InitPresentationStuff(PR_TRUE, PR_TRUE);
+      rv = InitPresentationStuff(mDocument->MayStartLayout(),
+                                 mDocument->MayStartLayout());
     }
 
     // If we get here the document load has already started and the
     // window is shown because some JS on the page caused it to be
     // shown...
 
     nsCOMPtr<nsIPresShell> shellDeathGrip(mPresShell); // bug 378682
     mPresShell->UnsuppressPainting();