Bug 489259. Grab the parent chain for our OnStateChange calls before we fire onload and possibly change the docshell tree. r=jst
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 08 Sep 2011 23:19:45 -0400
changeset 76823 b201507b95c7fb0e3c0e026f5520118af526d425
parent 76822 68b5bfaebb24e31f6c52bb4b2660c814eba09ac3
child 76824 7a8399ef753513fd59f0753d08fc2ef5d1405b1c
push id21141
push usereakhgari@mozilla.com
push dateFri, 09 Sep 2011 14:06:30 +0000
treeherdermozilla-central@694520af9b18 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs489259
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 489259. Grab the parent chain for our OnStateChange calls before we fire onload and possibly change the docshell tree. r=jst
uriloader/base/nsDocLoader.cpp
uriloader/base/nsDocLoader.h
--- a/uriloader/base/nsDocLoader.cpp
+++ b/uriloader/base/nsDocLoader.cpp
@@ -941,36 +941,43 @@ void nsDocLoader::doStopDocumentLoad(nsI
 
   GetURIStringFromRequest(request, buffer);
   PR_LOG(gDocLoaderLog, PR_LOG_DEBUG, 
          ("DocLoader:%p: ++ Firing OnStateChange for end document load (...)."
          "\tURI: %s Status=%x\n",
           this, buffer.get(), aStatus));
 #endif /* DEBUG */
 
+  // Firing STATE_STOP|STATE_IS_DOCUMENT will fire onload handlers.
+  // Grab our parent chain before doing that so we can still dispatch
+  // STATE_STOP|STATE_IS_WINDW_STATE_IS_NETWORK to them all, even if
+  // the onload handlers rearrange the docshell tree.
+  WebProgressList list;
+  GatherAncestorWebProgresses(list);
+  
   //
   // Fire an OnStateChange(...) notification indicating the the
   // current document has finished loading...
   //
-  FireOnStateChange(this,
-                    request,
-                    nsIWebProgressListener::STATE_STOP |
-                    nsIWebProgressListener::STATE_IS_DOCUMENT,
-                    aStatus);
+  PRInt32 flags = nsIWebProgressListener::STATE_STOP |
+                  nsIWebProgressListener::STATE_IS_DOCUMENT;
+  for (PRUint32 i = 0; i < list.Length(); ++i) {
+    list[i]->DoFireOnStateChange(this, request, flags, aStatus);
+  }
 
   //
   // Fire a final OnStateChange(...) notification indicating the the
   // current document has finished loading...
   //
-  FireOnStateChange(this,
-                    request,
-                    nsIWebProgressListener::STATE_STOP |
-                    nsIWebProgressListener::STATE_IS_WINDOW |
-                    nsIWebProgressListener::STATE_IS_NETWORK,
-                    aStatus);
+  flags = nsIWebProgressListener::STATE_STOP |
+          nsIWebProgressListener::STATE_IS_WINDOW |
+          nsIWebProgressListener::STATE_IS_NETWORK;
+  for (PRUint32 i = 0; i < list.Length(); ++i) {
+    list[i]->DoFireOnStateChange(this, request, flags, aStatus);
+  }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////
 // The following section contains support for nsIWebProgress and related stuff
 ////////////////////////////////////////////////////////////////////////////////////
 
 NS_IMETHODIMP
 nsDocLoader::AddProgressListener(nsIWebProgressListener *aListener,
@@ -1290,22 +1297,40 @@ void nsDocLoader::FireOnProgressChange(n
   if (mParent) {
     mParent->FireOnProgressChange(aLoadInitiator, request,
                                   aProgress, aProgressMax,
                                   aProgressDelta,
                                   aTotalProgress, aMaxTotalProgress);
   }
 }
 
+void nsDocLoader::GatherAncestorWebProgresses(WebProgressList& aList)
+{
+  for (nsDocLoader* loader = this; loader; loader = loader->mParent) {
+    aList.AppendElement(loader);
+  }
+}
 
 void nsDocLoader::FireOnStateChange(nsIWebProgress *aProgress,
                                     nsIRequest *aRequest,
                                     PRInt32 aStateFlags,
                                     nsresult aStatus)
 {
+  WebProgressList list;
+  GatherAncestorWebProgresses(list);
+  for (PRUint32 i = 0; i < list.Length(); ++i) {
+    list[i]->DoFireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
+  }
+}
+
+void nsDocLoader::DoFireOnStateChange(nsIWebProgress * const aProgress,
+                                      nsIRequest * const aRequest,
+                                      PRInt32 &aStateFlags,
+                                      const nsresult aStatus)
+{
   //
   // Remove the STATE_IS_NETWORK bit if necessary.
   //
   // The rule is to remove this bit, if the notification has been passed
   // up from a child WebProgress, and the current WebProgress is already
   // active...
   //
   if (mIsLoadingDocument &&
@@ -1354,21 +1379,16 @@ void nsDocLoader::FireOnStateChange(nsIW
       delete info;
       continue;
     }
 
     listener->OnStateChange(aProgress, aRequest, aStateFlags, aStatus);
   }
 
   mListenerInfoList.Compact();
-
-  // Pass the notification up to the parent...
-  if (mParent) {
-    mParent->FireOnStateChange(aProgress, aRequest, aStateFlags, aStatus);
-  }
 }
 
 
 
 void
 nsDocLoader::FireOnLocationChange(nsIWebProgress* aWebProgress,
                                   nsIRequest* aRequest,
                                   nsIURI *aUri)
--- a/uriloader/base/nsDocLoader.h
+++ b/uriloader/base/nsDocLoader.h
@@ -55,16 +55,17 @@
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIChannelEventSink.h"
 #include "nsISecurityEventSink.h"
 #include "nsISupportsPriority.h"
 #include "nsCOMPtr.h"
 #include "pldhash.h"
 #include "prclist.h"
+#include "nsAutoPtr.h"
 
 struct nsRequestInfo;
 struct nsListenerInfo;
 
 /****************************************************************************
  * nsDocLoader implementation...
  ****************************************************************************/
 
@@ -149,21 +150,39 @@ protected:
     void FireOnProgressChange(nsDocLoader* aLoadInitiator,
                               nsIRequest *request,
                               PRInt64 aProgress,
                               PRInt64 aProgressMax,
                               PRInt64 aProgressDelta,
                               PRInt64 aTotalProgress,
                               PRInt64 aMaxTotalProgress);
 
+    // This should be at least 2 long since we'll generally always
+    // have the current page and the global docloader on the ancestor
+    // list.  But to deal with frames it's better to make it a bit
+    // longer, and it's always a stack temporary so there's no real
+    // reason not to.
+    typedef nsAutoTArray<nsRefPtr<nsDocLoader>, 8> WebProgressList;
+    void GatherAncestorWebProgresses(WebProgressList& aList);
+
     void FireOnStateChange(nsIWebProgress *aProgress,
                            nsIRequest* request,
                            PRInt32 aStateFlags,
                            nsresult aStatus);
 
+    // The guts of FireOnStateChange, but does not call itself on our ancestors.
+    // The arguments that are const are const so that we can detect cases when
+    // DoFireOnStateChange wants to propagate changes to the next web progress
+    // at compile time.  The ones that are not, are references so that such
+    // changes can be propagated.
+    void DoFireOnStateChange(nsIWebProgress * const aProgress,
+                             nsIRequest* const request,
+                             PRInt32 &aStateFlags,
+                             const nsresult aStatus);
+
     void FireOnStatusChange(nsIWebProgress *aWebProgress,
                             nsIRequest *aRequest,
                             nsresult aStatus,
                             const PRUnichar* aMessage);
 
     void FireOnLocationChange(nsIWebProgress* aWebProgress,
                               nsIRequest* aRequest,
                               nsIURI *aUri);