Title: [86720] trunk/Source/WebCore
Revision
86720
Author
[email protected]
Date
2011-05-17 16:42:36 -0700 (Tue, 17 May 2011)

Log Message

2011-05-17  Brady Eidson  <[email protected]>

        Reviewed by Darin Adler.

        <rdar://problem/9366728> and https://webkit.org/b/60796
        Crash when code inside a ResourceLoadDelegate method calls [WebView stopLoading:]

        Break up ResourceLoader::didCancel() into willCancel() and didCancel(), and making them pure virtual.
        This change has the following benefits:
          - Managing ResourceLoader state can be in the base class; Subclasses no longer need to protect
            themselves, check these variables as often, or ASSERT them.
          - ResourceLoader subclasses no longer have to call the base class ::didCancel
          - ResourceLoader::cancel becomes more capable of handling reentrancy with the design that the
            cancellation is completed inside the last call.

        No new tests - No change in behavior for previous tests, and new test would require API usage outside
        the scope of DumpRenderTree.

        * loader/ResourceLoader.cpp:
        (WebCore::ResourceLoader::ResourceLoader):
        (WebCore::ResourceLoader::cancel): Moved from ResourceLoader::didCancel, and does all of that same work
          except it interposes calls to "willCancel" and "didCancel" as required to maintain the same behavior.
        * loader/ResourceLoader.h: Added pure virtual didCancel() and willCancel().

        Split-up into willCancel() and didCancel(), based on when the base class didCancel() used to be called:
        * loader/MainResourceLoader.cpp:
        (WebCore::MainResourceLoader::willCancel):
        (WebCore::MainResourceLoader::didCancel):
        * loader/MainResourceLoader.h:

        Split-up into willCancel() and didCancel(), based on when the "reached terminal state" flag used to be checked:
        * loader/NetscapePlugInStreamLoader.cpp:
        (WebCore::NetscapePlugInStreamLoader::didReceiveResponse): Call the entry point cancel() instead of the old didCancel()
        (WebCore::NetscapePlugInStreamLoader::willCancel):
        (WebCore::NetscapePlugInStreamLoader::didCancel):
        * loader/NetscapePlugInStreamLoader.h:

        Split-up into willCancel() and didCancel(), based on when the "reached terminal state" flag used to be checked:
        * loader/SubresourceLoader.cpp:
        (WebCore::SubresourceLoader::willCancel):
        (WebCore::SubresourceLoader::didCancel):
        * loader/SubresourceLoader.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (86719 => 86720)


--- trunk/Source/WebCore/ChangeLog	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/ChangeLog	2011-05-17 23:42:36 UTC (rev 86720)
@@ -1,3 +1,46 @@
+2011-05-17  Brady Eidson  <[email protected]>
+
+        Reviewed by Darin Adler.
+
+        <rdar://problem/9366728> and https://webkit.org/b/60796
+        Crash when code inside a ResourceLoadDelegate method calls [WebView stopLoading:]
+
+        Break up ResourceLoader::didCancel() into willCancel() and didCancel(), and making them pure virtual.
+        This change has the following benefits:
+          - Managing ResourceLoader state can be in the base class; Subclasses no longer need to protect
+            themselves, check these variables as often, or ASSERT them.
+          - ResourceLoader subclasses no longer have to call the base class ::didCancel
+          - ResourceLoader::cancel becomes more capable of handling reentrancy with the design that the 
+            cancellation is completed inside the last call.
+
+        No new tests - No change in behavior for previous tests, and new test would require API usage outside
+        the scope of DumpRenderTree.
+
+        * loader/ResourceLoader.cpp:
+        (WebCore::ResourceLoader::ResourceLoader):
+        (WebCore::ResourceLoader::cancel): Moved from ResourceLoader::didCancel, and does all of that same work
+          except it interposes calls to "willCancel" and "didCancel" as required to maintain the same behavior.
+        * loader/ResourceLoader.h: Added pure virtual didCancel() and willCancel().
+
+        Split-up into willCancel() and didCancel(), based on when the base class didCancel() used to be called:
+        * loader/MainResourceLoader.cpp:
+        (WebCore::MainResourceLoader::willCancel):
+        (WebCore::MainResourceLoader::didCancel):
+        * loader/MainResourceLoader.h:
+
+        Split-up into willCancel() and didCancel(), based on when the "reached terminal state" flag used to be checked:
+        * loader/NetscapePlugInStreamLoader.cpp:
+        (WebCore::NetscapePlugInStreamLoader::didReceiveResponse): Call the entry point cancel() instead of the old didCancel()
+        (WebCore::NetscapePlugInStreamLoader::willCancel):
+        (WebCore::NetscapePlugInStreamLoader::didCancel):
+        * loader/NetscapePlugInStreamLoader.h:
+
+        Split-up into willCancel() and didCancel(), based on when the "reached terminal state" flag used to be checked:
+        * loader/SubresourceLoader.cpp:
+        (WebCore::SubresourceLoader::willCancel):
+        (WebCore::SubresourceLoader::didCancel):
+        * loader/SubresourceLoader.h:
+
 2011-05-17  Nat Duca  <[email protected]>
 
         Reviewed by James Robinson.

Modified: trunk/Source/WebCore/loader/MainResourceLoader.cpp (86719 => 86720)


--- trunk/Source/WebCore/loader/MainResourceLoader.cpp	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/MainResourceLoader.cpp	2011-05-17 23:42:36 UTC (rev 86720)
@@ -99,24 +99,23 @@
     ASSERT(reachedTerminalState());
 }
 
-void MainResourceLoader::didCancel(const ResourceError& error)
+void MainResourceLoader::willCancel(const ResourceError&)
 {
     m_dataLoadTimer.stop();
 
-    // FrameLoader won't be reachable after calling ResourceLoader::didCancel().
-    FrameLoader* frameLoader = this->frameLoader();
-
     if (m_waitingForContentPolicy) {
-        frameLoader->policyChecker()->cancelCheck();
+        frameLoader()->policyChecker()->cancelCheck();
         ASSERT(m_waitingForContentPolicy);
         m_waitingForContentPolicy = false;
         deref(); // balances ref in didReceiveResponse
     }
-    ResourceLoader::didCancel(error);
+}
 
+void MainResourceLoader::didCancel(const ResourceError& error)
+{
     // We should notify the frame loader after fully canceling the load, because it can do complicated work
     // like calling DOMWindow::print(), during which a half-canceled load could try to finish.
-    frameLoader->receivedMainResourceError(error, true);
+    frameLoader()->receivedMainResourceError(error, true);
 }
 
 ResourceError MainResourceLoader::interruptionForPolicyChangeError() const

Modified: trunk/Source/WebCore/loader/MainResourceLoader.h (86719 => 86720)


--- trunk/Source/WebCore/loader/MainResourceLoader.h	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/MainResourceLoader.h	2011-05-17 23:42:36 UTC (rev 86720)
@@ -74,6 +74,7 @@
     private:
         MainResourceLoader(Frame*);
 
+        virtual void willCancel(const ResourceError&);
         virtual void didCancel(const ResourceError&);
 
         bool loadNow(ResourceRequest&);

Modified: trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp (86719 => 86720)


--- trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.cpp	2011-05-17 23:42:36 UTC (rev 86720)
@@ -89,7 +89,7 @@
         return;
     
     if (response.httpStatusCode() < 100 || response.httpStatusCode() >= 400)
-        didCancel(frameLoader()->fileDoesNotExistError(response));
+        cancel(frameLoader()->fileDoesNotExistError(response));
 }
 
 void NetscapePlugInStreamLoader::didReceiveData(const char* data, int length, long long encodedDataLength, bool allAtOnce)
@@ -119,23 +119,17 @@
     ResourceLoader::didFail(error);
 }
 
-void NetscapePlugInStreamLoader::didCancel(const ResourceError& error)
+void NetscapePlugInStreamLoader::willCancel(const ResourceError& error)
 {
-    RefPtr<NetscapePlugInStreamLoader> protect(this);
-
     m_client->didFail(this, error);
+}
 
-    // If calling didFail spins the run loop the stream loader can reach the terminal state.
-    // If that's the case we just return early.
-    if (reachedTerminalState())
-        return;
-    
+void NetscapePlugInStreamLoader::didCancel(const ResourceError&)
+{
     // We need to remove the stream loader after the call to didFail, since didFail can 
     // spawn a new run loop and if the loader has been removed it won't be deferred when
     // the document loader is asked to defer loading.
     m_documentLoader->removePlugInStreamLoader(this);
-
-    ResourceLoader::didCancel(error);
 }
 
 }

Modified: trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.h (86719 => 86720)


--- trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.h	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/NetscapePlugInStreamLoader.h	2011-05-17 23:42:36 UTC (rev 86720)
@@ -65,7 +65,8 @@
 
         NetscapePlugInStreamLoader(Frame*, NetscapePlugInStreamLoaderClient*);
 
-        virtual void didCancel(const ResourceError& error);
+        virtual void willCancel(const ResourceError&);
+        virtual void didCancel(const ResourceError&);
 
         NetscapePlugInStreamLoaderClient* m_client;
     };

Modified: trunk/Source/WebCore/loader/ResourceLoader.cpp (86719 => 86720)


--- trunk/Source/WebCore/loader/ResourceLoader.cpp	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/ResourceLoader.cpp	2011-05-17 23:42:36 UTC (rev 86720)
@@ -63,6 +63,7 @@
     , m_documentLoader(frame->loader()->activeDocumentLoader())
     , m_identifier(0)
     , m_reachedTerminalState(false)
+    , m_calledWillCancel(false)
     , m_cancelled(false)
     , m_calledDidFinishLoad(false)
     , m_sendResourceLoadCallbacks(sendResourceLoadCallbacks)
@@ -337,35 +338,6 @@
     releaseResources();
 }
 
-void ResourceLoader::didCancel(const ResourceError& error)
-{
-    ASSERT(!m_cancelled);
-    ASSERT(!m_reachedTerminalState);
-
-    if (FormData* data = ""
-        data->removeGeneratedFilesIfNeeded();
-
-    // This flag prevents bad behavior when loads that finish cause the
-    // load itself to be cancelled (which could happen with a _javascript_ that 
-    // changes the window location). This is used to prevent both the body
-    // of this method and the body of connectionDidFinishLoading: running
-    // for a single delegate. Canceling wins.
-    m_cancelled = true;
-    
-    if (m_handle)
-        m_handle->clearAuthentication();
-
-    m_documentLoader->cancelPendingSubstituteLoad(this);
-    if (m_handle) {
-        m_handle->cancel();
-        m_handle = 0;
-    }
-    if (m_sendResourceLoadCallbacks && m_identifier && !m_calledDidFinishLoad)
-        frameLoader()->notifier()->didFailToLoad(this, error);
-
-    releaseResources();
-}
-
 void ResourceLoader::cancel()
 {
     cancel(ResourceError());
@@ -373,12 +345,53 @@
 
 void ResourceLoader::cancel(const ResourceError& error)
 {
+    // If the load has already completed - succeeded, failed, or previously cancelled - do nothing.
     if (m_reachedTerminalState)
         return;
-    if (!error.isNull())
-        didCancel(error);
-    else
-        didCancel(cancelledError());
+       
+    ResourceError nonNullError = error.isNull() ? cancelledError() : error;
+    
+    // willCancel() and didFailToLoad() both call out to clients that might do 
+    // something causing the last reference to this object to go away.
+    RefPtr<ResourceLoader> protector(this);
+    
+    // If we re-enter cancel() from inside willCancel(), we want to pick up from where we left 
+    // off without re-running willCancel()
+    if (!m_calledWillCancel) {
+        m_calledWillCancel = true;
+        
+        willCancel(nonNullError);
+    }
+
+    // If we re-enter cancel() from inside didFailToLoad(), we want to pick up from where we 
+    // left off without redoing any of this work.
+    if (!m_cancelled) {
+        m_cancelled = true;
+        
+        if (FormData* data = ""
+            data->removeGeneratedFilesIfNeeded();
+
+        if (m_handle)
+            m_handle->clearAuthentication();
+
+        m_documentLoader->cancelPendingSubstituteLoad(this);
+        if (m_handle) {
+            m_handle->cancel();
+            m_handle = 0;
+        }
+
+        if (m_sendResourceLoadCallbacks && m_identifier && !m_calledDidFinishLoad)
+            frameLoader()->notifier()->didFailToLoad(this, nonNullError);
+    }
+
+    // If cancel() completed from within the call to willCancel() or didFailToLoad(),
+    // we don't want to redo didCancel() or releasesResources().
+    if (m_reachedTerminalState)
+        return;
+
+    didCancel(nonNullError);
+            
+    releaseResources();
 }
 
 const ResourceResponse& ResourceLoader::response() const

Modified: trunk/Source/WebCore/loader/ResourceLoader.h (86719 => 86720)


--- trunk/Source/WebCore/loader/ResourceLoader.h	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/ResourceLoader.h	2011-05-17 23:42:36 UTC (rev 86720)
@@ -150,7 +150,6 @@
         // deferred) and should only be called by ResourceLoadScheduler or setDefersLoading().
         void start();
         
-        virtual void didCancel(const ResourceError&);
         void didFinishLoadingOnePart(double finishTime);
 
         const ResourceRequest& request() const { return m_request; }
@@ -163,12 +162,16 @@
         ResourceResponse m_response;
         
     private:
+        virtual void willCancel(const ResourceError&) = 0;
+        virtual void didCancel(const ResourceError&) = 0;
+
         ResourceRequest m_request;
         RefPtr<SharedBuffer> m_resourceData;
         
         unsigned long m_identifier;
 
         bool m_reachedTerminalState;
+        bool m_calledWillCancel;
         bool m_cancelled;
         bool m_calledDidFinishLoad;
 

Modified: trunk/Source/WebCore/loader/SubresourceLoader.cpp (86719 => 86720)


--- trunk/Source/WebCore/loader/SubresourceLoader.cpp	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/SubresourceLoader.cpp	2011-05-17 23:42:36 UTC (rev 86720)
@@ -224,27 +224,15 @@
     ResourceLoader::didFail(error);
 }
 
-void SubresourceLoader::didCancel(const ResourceError& error)
+void SubresourceLoader::willCancel(const ResourceError& error)
 {
-    ASSERT(!reachedTerminalState());
-
-    // Calling removeSubresourceLoader will likely result in a call to deref, so we must protect ourselves.
-    RefPtr<SubresourceLoader> protect(this);
-
     if (m_client)
         m_client->didFail(this, error);
-    
-    if (cancelled())
-        return;
-    
-    // The only way the subresource loader can reach the terminal state here is if the run loop spins when calling
-    // m_client->didFail. This should in theory not happen which is why the assert is here. 
-    ASSERT(!reachedTerminalState());
-    if (reachedTerminalState())
-        return;
-    
+}
+
+void SubresourceLoader::didCancel(const ResourceError&)
+{
     m_documentLoader->removeSubresourceLoader(this);
-    ResourceLoader::didCancel(error);
 }
 
 bool SubresourceLoader::shouldUseCredentialStorage()

Modified: trunk/Source/WebCore/loader/SubresourceLoader.h (86719 => 86720)


--- trunk/Source/WebCore/loader/SubresourceLoader.h	2011-05-17 23:39:57 UTC (rev 86719)
+++ trunk/Source/WebCore/loader/SubresourceLoader.h	2011-05-17 23:42:36 UTC (rev 86720)
@@ -59,6 +59,7 @@
         virtual bool shouldUseCredentialStorage();
         virtual void didReceiveAuthenticationChallenge(const AuthenticationChallenge&);
         virtual void receivedCancellation(const AuthenticationChallenge&);        
+        virtual void willCancel(const ResourceError&);
         virtual void didCancel(const ResourceError&);
 
 #if HAVE(CFNETWORK_DATA_ARRAY_CALLBACK)
_______________________________________________
webkit-changes mailing list
[email protected]
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to