Title: [246568] trunk/Source/WebKit
Revision
246568
Author
beid...@apple.com
Date
2019-06-18 14:30:18 -0700 (Tue, 18 Jun 2019)

Log Message

Handle NSProgress calling our cancellation handler on background threads (and calling it more than once).
<rdar://problem/51392926> and https://bugs.webkit.org/show_bug.cgi?id=198945

Reviewed by Alex Christensen.

If you have a download in progress and quickly tap the button to cancel it multiple times, then:
    - NSProgress calls our cancellation handler on a non-main thread, which we can't handle.
    - They do it more than once, which is also bad.
    - They might even do it multiple times concurrently (on different background dispatch queues)

Let's work around these.

* NetworkProcess/Downloads/Download.cpp:
(WebKit::Download::cancel): Double check we're on the main thread, and handle being called twice.

* NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm:
(-[WKDownloadProgress performCancel]): Actually cancel the WebKit::Download if we still have one.
(-[WKDownloadProgress progressCancelled]): Called when NSProgress calls the cancellation handler, no matter
  which thread it does it on. By leveraging std::call_once we handle multiple calls as well as being called
  concurrently from different threads. call_once punts the *actual* cancel operation off to the main thread.
(-[WKDownloadProgress initWithDownloadTask:download:URL:sandboxExtension:]): The cancellation handler is
  now simply calling 'progressCancelled' on self, assuming the weak pointer for self is still valid.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (246567 => 246568)


--- trunk/Source/WebKit/ChangeLog	2019-06-18 21:21:48 UTC (rev 246567)
+++ trunk/Source/WebKit/ChangeLog	2019-06-18 21:30:18 UTC (rev 246568)
@@ -1,3 +1,28 @@
+2019-06-18  Brady Eidson  <beid...@apple.com>
+
+        Handle NSProgress calling our cancellation handler on background threads (and calling it more than once).
+        <rdar://problem/51392926> and https://bugs.webkit.org/show_bug.cgi?id=198945
+
+        Reviewed by Alex Christensen.
+
+        If you have a download in progress and quickly tap the button to cancel it multiple times, then:
+            - NSProgress calls our cancellation handler on a non-main thread, which we can't handle.
+            - They do it more than once, which is also bad.
+            - They might even do it multiple times concurrently (on different background dispatch queues)
+        
+        Let's work around these.
+
+        * NetworkProcess/Downloads/Download.cpp:
+        (WebKit::Download::cancel): Double check we're on the main thread, and handle being called twice.
+
+        * NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm:
+        (-[WKDownloadProgress performCancel]): Actually cancel the WebKit::Download if we still have one.
+        (-[WKDownloadProgress progressCancelled]): Called when NSProgress calls the cancellation handler, no matter
+          which thread it does it on. By leveraging std::call_once we handle multiple calls as well as being called
+          concurrently from different threads. call_once punts the *actual* cancel operation off to the main thread.
+        (-[WKDownloadProgress initWithDownloadTask:download:URL:sandboxExtension:]): The cancellation handler is
+          now simply calling 'progressCancelled' on self, assuming the weak pointer for self is still valid.
+
 2019-06-18  Daniel Bates  <daba...@apple.com>
 
         [iOS] Pressing key while holding Command should not insert character

Modified: trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp (246567 => 246568)


--- trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp	2019-06-18 21:21:48 UTC (rev 246567)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp	2019-06-18 21:30:18 UTC (rev 246568)
@@ -86,7 +86,12 @@
 
 void Download::cancel()
 {
+    RELEASE_ASSERT(isMainThread());
+
+    if (m_wasCanceled)
+        return;
     m_wasCanceled = true;
+
     if (m_download) {
         m_download->cancel();
         didCancel({ });

Modified: trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm (246567 => 246568)


--- trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm	2019-06-18 21:21:48 UTC (rev 246567)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm	2019-06-18 21:30:18 UTC (rev 246568)
@@ -41,8 +41,27 @@
     RetainPtr<NSURLSessionDownloadTask> m_task;
     WebKit::Download* m_download;
     RefPtr<WebKit::SandboxExtension> m_sandboxExtension;
+    std::once_flag m_progressCancelOnce;
 }
 
+- (void)performCancel
+{
+    if (m_download)
+        m_download->cancel();
+}
+
+- (void)progressCancelled
+{
+    std::call_once(m_progressCancelOnce, [self] {
+        if (!isMainThread()) {
+            [self performSelectorOnMainThread:@selector(performCancel) withObject:nil waitUntilDone:NO];
+            return;
+        }
+
+        [self performCancel];
+    });
+}
+
 - (instancetype)initWithDownloadTask:(NSURLSessionDownloadTask *)task download:(WebKit::Download*)download URL:(NSURL *)fileURL sandboxExtension:(RefPtr<WebKit::SandboxExtension>)sandboxExtension
 {
     if (!(self = [self initWithParent:nil userInfo:nil]))
@@ -66,12 +85,7 @@
 
     self.cancellable = YES;
     self.cancellationHandler = makeBlockPtr([weakSelf = WeakObjCPtr<WKDownloadProgress> { self }] {
-        auto strongSelf = weakSelf.get();
-        if (!strongSelf)
-            return;
-
-        if (auto* download = strongSelf.get()->m_download)
-            download->cancel();
+        [weakSelf.get() progressCancelled];
     }).get();
 
     return self;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to