Title: [218896] trunk
Revision
218896
Author
beid...@apple.com
Date
2017-06-28 15:40:10 -0700 (Wed, 28 Jun 2017)

Log Message

DocumentLoader should always notify the client if there are pending icon loads when the load is stopped.
https://bugs.webkit.org/show_bug.cgi?id=173874

Reviewed by Alex Christensen.

Source/WebCore:

Covered by API tests.

Patch started by Carlos Garcia Campos, finished by me.

* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::stopLoading): Make all of the callbacks for cancelled IconLoaders.
(WebCore::DocumentLoader::didGetLoadDecisionForIcon): Make the callback even if there's no IconLoader.
(WebCore::DocumentLoader::finishedLoadingIcon):
(WebCore::DocumentLoader::notifyFinishedLoadingIcon):
* loader/DocumentLoader.h:

Tools:

* TestWebKitAPI/Tests/WebKit2Cocoa/IconLoadingDelegate.mm:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (218895 => 218896)


--- trunk/Source/WebCore/ChangeLog	2017-06-28 22:27:28 UTC (rev 218895)
+++ trunk/Source/WebCore/ChangeLog	2017-06-28 22:40:10 UTC (rev 218896)
@@ -1,3 +1,21 @@
+2017-06-28  Brady Eidson  <beid...@apple.com>
+
+        DocumentLoader should always notify the client if there are pending icon loads when the load is stopped.
+        https://bugs.webkit.org/show_bug.cgi?id=173874
+
+        Reviewed by Alex Christensen.
+
+        Covered by API tests.
+
+        Patch started by Carlos Garcia Campos, finished by me.
+        
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::stopLoading): Make all of the callbacks for cancelled IconLoaders.
+        (WebCore::DocumentLoader::didGetLoadDecisionForIcon): Make the callback even if there's no IconLoader.
+        (WebCore::DocumentLoader::finishedLoadingIcon):
+        (WebCore::DocumentLoader::notifyFinishedLoadingIcon):
+        * loader/DocumentLoader.h:
+
 2017-06-28  Antoine Quint  <grao...@apple.com>
 
         Volume controls should be hidden when AirPlay is active

Modified: trunk/Source/WebCore/loader/DocumentLoader.cpp (218895 => 218896)


--- trunk/Source/WebCore/loader/DocumentLoader.cpp	2017-06-28 22:27:28 UTC (rev 218895)
+++ trunk/Source/WebCore/loader/DocumentLoader.cpp	2017-06-28 22:40:10 UTC (rev 218896)
@@ -282,8 +282,10 @@
             m_frame->loader().stopLoading(UnloadEventPolicyNone);
     }
 
+    for (auto callbackIdentifier : m_iconLoaders.values())
+        notifyFinishedLoadingIcon(callbackIdentifier, nullptr);
+    m_iconLoaders.clear();
     m_iconsPendingLoadDecision.clear();
-    m_iconLoaders.clear();
 
     // Always cancel multipart loaders
     cancelAll(m_multipartSubresourceLoaders);
@@ -1680,9 +1682,19 @@
 void DocumentLoader::didGetLoadDecisionForIcon(bool decision, uint64_t loadIdentifier, uint64_t newCallbackID)
 {
     auto icon = m_iconsPendingLoadDecision.take(loadIdentifier);
-    if (!decision || icon.url.isEmpty() || !m_frame)
+
+    // If the LinkIcon we just took is empty, then the DocumentLoader had all of its loaders stopped
+    // while this icon load decision was pending.
+    // In this case we need to notify the client that the icon finished loading with empty data.
+    if (icon.url.isEmpty()) {
+        notifyFinishedLoadingIcon(newCallbackID, nullptr);
         return;
+    }
 
+    // If the decision was not to load or this DocumentLoader is already detached, there is no load to perform.
+    if (!decision || !m_frame)
+        return;
+
     auto iconLoader = std::make_unique<IconLoader>(*this, icon.url);
     auto* rawIconLoader = iconLoader.get();
     m_iconLoaders.set(WTFMove(iconLoader), newCallbackID);
@@ -1698,9 +1710,15 @@
     auto callbackIdentifier = m_iconLoaders.take(&loader);
     RELEASE_ASSERT(callbackIdentifier);
 
-    m_frame->loader().client().finishedLoadingIcon(callbackIdentifier, buffer);
+    notifyFinishedLoadingIcon(callbackIdentifier, buffer);
 }
 
+void DocumentLoader::notifyFinishedLoadingIcon(uint64_t callbackIdentifier, SharedBuffer* buffer)
+{
+    if (m_frame)
+        m_frame->loader().client().finishedLoadingIcon(callbackIdentifier, buffer);
+}
+
 void DocumentLoader::dispatchOnloadEvents()
 {
     m_wasOnloadDispatched = true;

Modified: trunk/Source/WebCore/loader/DocumentLoader.h (218895 => 218896)


--- trunk/Source/WebCore/loader/DocumentLoader.h	2017-06-28 22:27:28 UTC (rev 218895)
+++ trunk/Source/WebCore/loader/DocumentLoader.h	2017-06-28 22:40:10 UTC (rev 218896)
@@ -368,6 +368,8 @@
     void cancelPolicyCheckIfNeeded();
     void becomeMainResourceClient();
 
+    void notifyFinishedLoadingIcon(uint64_t callbackIdentifier, SharedBuffer*);
+
     Frame* m_frame { nullptr };
     Ref<CachedResourceLoader> m_cachedResourceLoader;
 

Modified: trunk/Tools/ChangeLog (218895 => 218896)


--- trunk/Tools/ChangeLog	2017-06-28 22:27:28 UTC (rev 218895)
+++ trunk/Tools/ChangeLog	2017-06-28 22:40:10 UTC (rev 218896)
@@ -1,3 +1,12 @@
+2017-06-28  Brady Eidson  <beid...@apple.com>
+
+        DocumentLoader should always notify the client if there are pending icon loads when the load is stopped.
+        https://bugs.webkit.org/show_bug.cgi?id=173874
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/Tests/WebKit2Cocoa/IconLoadingDelegate.mm:
+
 2017-06-28  Don Olmstead  <don.olmst...@sony.com>
 
         Unreviewed, adding Don Olmstead to contributors.json

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/IconLoadingDelegate.mm (218895 => 218896)


--- trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/IconLoadingDelegate.mm	2017-06-28 22:27:28 UTC (rev 218895)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKit2Cocoa/IconLoadingDelegate.mm	2017-06-28 22:40:10 UTC (rev 218896)
@@ -39,11 +39,16 @@
 #if WK_API_ENABLED
 
 static bool doneWithIcons;
-static bool receivedFaviconDataCallback;
 static bool alreadyProvidedIconData;
-static RetainPtr<NSData> receivedFaviconData;
 
-@interface IconLoadingDelegate : NSObject <_WKIconLoadingDelegate>
+@interface IconLoadingDelegate : NSObject <_WKIconLoadingDelegate> {
+    @public
+    RetainPtr<NSData> receivedFaviconData;
+    bool receivedFaviconDataCallback;
+    bool shouldSaveCallback;
+    bool didSaveCallback;
+    void (^savedCallback)(void (^)(NSData*));
+}
 @end
 
 @implementation IconLoadingDelegate {
@@ -70,7 +75,13 @@
         doneWithIcons = true;
 
     if (parameters.iconType == WKLinkIconTypeFavicon) {
-        completionHandler([](NSData *iconData) {
+        if (shouldSaveCallback) {
+            savedCallback = [completionHandler retain];
+            didSaveCallback = true;
+            return;
+        }
+
+        completionHandler([self](NSData *iconData) {
             receivedFaviconData = iconData;
             receivedFaviconDataCallback = true;
         });
@@ -80,7 +91,12 @@
 
 @end
 
-@interface IconLoadingSchemeHandler : NSObject <WKURLSchemeHandler>
+@interface IconLoadingSchemeHandler : NSObject <WKURLSchemeHandler> {
+    @public
+    bool shouldIgnoreFaviconTask;
+    bool receivedFaviconTask;
+    bool faviconTaskStopped;
+}
 - (instancetype)initWithData:(NSData *)data;
 - (void)setFaviconData:(NSData *)data;
 @end
@@ -113,6 +129,10 @@
 
     if ([[task.request.URL absoluteString] isEqual:@"testing:///favicon.ico"]) {
         EXPECT_FALSE(alreadyProvidedIconData);
+        if (shouldIgnoreFaviconTask) {
+            receivedFaviconTask = true;
+            return;
+        }
         response = adoptNS([[NSURLResponse alloc] initWithURL:task.request.URL MIMEType:@"image/png" expectedContentLength:1 textEncodingName:nil]);
         data = ""
         alreadyProvidedIconData = true;
@@ -128,6 +148,8 @@
 
 - (void)webView:(WKWebView *)webView stopURLSchemeTask:(id <WKURLSchemeTask>)task
 {
+    if ([[task.request.URL absoluteString] isEqual:@"testing:///favicon.ico"])
+        faviconTaskStopped = true;
 }
 
 @end
@@ -154,6 +176,7 @@
     [webView loadRequest:request];
 
     TestWebKitAPI::Util::run(&doneWithIcons);
+    TestWebKitAPI::Util::run(&iconDelegate.get()->receivedFaviconDataCallback);
 }
 
 static const char mainBytes2[] =
@@ -180,20 +203,86 @@
     NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"testing:///main"]];
     [webView loadRequest:request];
 
-    TestWebKitAPI::Util::run(&receivedFaviconDataCallback);
+    TestWebKitAPI::Util::run(&iconDelegate.get()->receivedFaviconDataCallback);
 
-    EXPECT_TRUE([iconDataFromDisk.get() isEqual:receivedFaviconData.get()]);
+    EXPECT_TRUE([iconDataFromDisk.get() isEqual:iconDelegate.get()->receivedFaviconData.get()]);
 
-    receivedFaviconDataCallback = false;
-    receivedFaviconData = nil;
+    iconDelegate.get()->receivedFaviconDataCallback = false;
+    iconDelegate.get()->receivedFaviconData = nil;
 
     // Load another main resource that results in the same icon being loaded (which should come from the memory cache).
     request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"testing:///main2"]];
     [webView loadRequest:request];
 
-    TestWebKitAPI::Util::run(&receivedFaviconDataCallback);
+    TestWebKitAPI::Util::run(&iconDelegate.get()->receivedFaviconDataCallback);
 
-    EXPECT_TRUE([iconDataFromDisk.get() isEqual:receivedFaviconData.get()]);
+    EXPECT_TRUE([iconDataFromDisk.get() isEqual:iconDelegate.get()->receivedFaviconData.get()]);
 }
 
+TEST(IconLoading, IconLoadCancelledCallback)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    NSData *mainData = [NSData dataWithBytesNoCopy:(void*)mainBytes length:sizeof(mainBytes) freeWhenDone:NO];
+    RetainPtr<IconLoadingSchemeHandler> handler = adoptNS([[IconLoadingSchemeHandler alloc] initWithData:mainData]);
+    handler.get()->shouldIgnoreFaviconTask = true;
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"testing"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    RetainPtr<IconLoadingDelegate> iconDelegate = adoptNS([[IconLoadingDelegate alloc] init]);
+    webView.get()._iconLoadingDelegate = iconDelegate.get();
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"testing:///main"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&handler.get()->receivedFaviconTask);
+
+    // Our scheme handler never replies to the favicon task, so our icon delegate load callback is still pending.
+    // Stop the documentloader's loading and verify the icon delegate callback is called.
+    [webView stopLoading];
+
+    // Wait until the data callback is called, *and* the task is stopped
+    TestWebKitAPI::Util::run(&handler.get()->faviconTaskStopped);
+    TestWebKitAPI::Util::run(&iconDelegate.get()->receivedFaviconDataCallback);
+
+    EXPECT_EQ(iconDelegate.get()->receivedFaviconData.get().length, (unsigned long)0);
+}
+
+TEST(IconLoading, IconLoadCancelledCallback2)
+{
+    RetainPtr<WKWebViewConfiguration> configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+
+    NSData *mainData = [NSData dataWithBytesNoCopy:(void*)mainBytes length:sizeof(mainBytes) freeWhenDone:NO];
+    RetainPtr<IconLoadingSchemeHandler> handler = adoptNS([[IconLoadingSchemeHandler alloc] initWithData:mainData]);
+    [configuration setURLSchemeHandler:handler.get() forURLScheme:@"testing"];
+
+    RetainPtr<WKWebView> webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    RetainPtr<IconLoadingDelegate> iconDelegate = adoptNS([[IconLoadingDelegate alloc] init]);
+    iconDelegate.get()->shouldSaveCallback = true;
+    webView.get()._iconLoadingDelegate = iconDelegate.get();
+
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"testing:///main"]];
+    [webView loadRequest:request];
+
+    TestWebKitAPI::Util::run(&iconDelegate.get()->didSaveCallback);
+
+    // Our scheme handler never replies to the favicon task, so our icon delegate load callback is still pending.
+    // Stop the documentloader's loading and verify the icon delegate callback is called.
+    [webView stopLoading];
+
+
+    // Even though loading has already been stopped (and therefore IconLoaders were cancelled),
+    // we should still get the callback.
+    static bool iconCallbackCalled;
+    iconDelegate.get()->savedCallback([iconCallbackCalled = &iconCallbackCalled](NSData *data) {
+        EXPECT_EQ(data.length, (unsigned long)0);
+
+        *iconCallbackCalled = true;
+    });
+
+    TestWebKitAPI::Util::run(&iconCallbackCalled);
+}
+
 #endif
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to