Diff
Modified: trunk/Source/WebCore/PAL/ChangeLog (238709 => 238710)
--- trunk/Source/WebCore/PAL/ChangeLog 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebCore/PAL/ChangeLog 2018-11-30 00:38:29 UTC (rev 238710)
@@ -1,3 +1,19 @@
+2018-11-29 David Quesada <[email protected]>
+
+ Add SPI to publish NSProgress on active downloads
+ https://bugs.webkit.org/show_bug.cgi?id=192021
+ rdar://problem/44405661
+
+ Reviewed by Alex Christensen.
+
+ Add an SPI header for NSProgress's publishing and unpublishing methods. In older
+ SDKs, these methods are explicitly unavailable on platforms other than macOS,
+ with underscore-prefixed versions available as SPI. In newer SDKs, the unprefixed
+ versions are SPI and the prefixed versions are deprecated.
+
+ * PAL.xcodeproj/project.pbxproj:
+ * pal/spi/cocoa/NSProgressSPI.h:
+
2018-11-29 Megan Gardner <[email protected]>
Move Lookup Code for better cross platform usage
Modified: trunk/Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj (238709 => 238710)
--- trunk/Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebCore/PAL/PAL.xcodeproj/project.pbxproj 2018-11-30 00:38:29 UTC (rev 238710)
@@ -114,6 +114,7 @@
442956CD218A72DF0080DB54 /* RevealSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 442956CC218A72DE0080DB54 /* RevealSPI.h */; };
570AB8F120AE2E8D00B8BE87 /* SecKeyProxySPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 570AB8F020AE2E8D00B8BE87 /* SecKeyProxySPI.h */; };
570AB8F920AF6E3D00B8BE87 /* NSXPCConnectionSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 570AB8F820AF6E3D00B8BE87 /* NSXPCConnectionSPI.h */; };
+ 63C7EDC721AFAE04006A7B99 /* NSProgressSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 63E369F921AFA83F001C14BC /* NSProgressSPI.h */; };
7A1656441F97B2B900BA3CE4 /* NSKeyedArchiverSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */; };
7A3A6A8020CADB4700317AAE /* NSImageSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A3A6A7F20CADB4600317AAE /* NSImageSPI.h */; };
A10265891F56747A00B4C844 /* HIToolboxSPI.h in Headers */ = {isa = PBXBuildFile; fileRef = A10265881F56747A00B4C844 /* HIToolboxSPI.h */; };
@@ -271,6 +272,7 @@
442956CC218A72DE0080DB54 /* RevealSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RevealSPI.h; sourceTree = "<group>"; };
570AB8F020AE2E8D00B8BE87 /* SecKeyProxySPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SecKeyProxySPI.h; sourceTree = "<group>"; };
570AB8F820AF6E3D00B8BE87 /* NSXPCConnectionSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSXPCConnectionSPI.h; sourceTree = "<group>"; };
+ 63E369F921AFA83F001C14BC /* NSProgressSPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSProgressSPI.h; sourceTree = "<group>"; };
7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSKeyedArchiverSPI.h; sourceTree = "<group>"; };
7A3A6A7F20CADB4600317AAE /* NSImageSPI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NSImageSPI.h; sourceTree = "<group>"; };
93E5909C1F93BF1E0067F8CF /* UnencodableHandling.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UnencodableHandling.h; sourceTree = "<group>"; };
@@ -397,6 +399,7 @@
0C2DA1331F3BEB4900DBC317 /* NSFileManagerSPI.h */,
F442915D1FA52473002CC93E /* NSFileSizeFormatterSPI.h */,
7A1656431F97B2B800BA3CE4 /* NSKeyedArchiverSPI.h */,
+ 63E369F921AFA83F001C14BC /* NSProgressSPI.h */,
0C2DA1341F3BEB4900DBC317 /* NSStringSPI.h */,
0C2DA1351F3BEB4900DBC317 /* NSTouchBarSPI.h */,
0C2DA1361F3BEB4900DBC317 /* NSURLConnectionSPI.h */,
@@ -743,6 +746,7 @@
0C77859F1F45130F00F4EBB6 /* QTKitSPI.h in Headers */,
0C2DA1571F3BEB4900DBC317 /* QuartzCoreSPI.h in Headers */,
A102658B1F56748C00B4C844 /* QuickDrawSPI.h in Headers */,
+ 63C7EDC721AFAE04006A7B99 /* NSProgressSPI.h in Headers */,
0C7785A01F45130F00F4EBB6 /* QuickLookMacSPI.h in Headers */,
0C5AF9201F43A4C7002EAC02 /* QuickLookSPI.h in Headers */,
442956CD218A72DF0080DB54 /* RevealSPI.h in Headers */,
Copied: trunk/Source/WebCore/PAL/pal/spi/cocoa/NSProgressSPI.h (from rev 238708, trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h) (0 => 238710)
--- trunk/Source/WebCore/PAL/pal/spi/cocoa/NSProgressSPI.h (rev 0)
+++ trunk/Source/WebCore/PAL/pal/spi/cocoa/NSProgressSPI.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#define USE_NSPROGRESS_PUBLISHING_SPI ((PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000) || (PLATFORM(WATCHOS) && __WATCH_OS_VERSION_MIN_REQUIRED < 60000) || (PLATFORM(TVOS) && __TV_OS_VERSION_MIN_REQUIRED < 130000))
+
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+
+#if USE(APPLE_INTERNAL_SDK)
+
+#import <Foundation/NSProgress_Private.h>
+
+#else
+
+@interface NSProgress ()
+
+- (void)_publish;
+- (void)_unpublish;
++ (id)_addSubscriberForFileURL:(NSURL *)inURL withPublishingHandler:(NSProgressPublishingHandler)inPublishingHandler;
++ (void)_removeSubscriber:(id)inSubscriber;
+
+@end
+
+#endif // not USE(APPLE_INTERNAL_SDK)
+
+#endif // USE(NSPROGRESS_PUBLISHING_SPI)
Modified: trunk/Source/WebKit/ChangeLog (238709 => 238710)
--- trunk/Source/WebKit/ChangeLog 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/ChangeLog 2018-11-30 00:38:29 UTC (rev 238710)
@@ -1,3 +1,86 @@
+2018-11-29 David Quesada <[email protected]>
+
+ Add SPI to publish NSProgress on active downloads
+ https://bugs.webkit.org/show_bug.cgi?id=192021
+ rdar://problem/44405661
+
+ Reviewed by Alex Christensen.
+
+ Make it possible for clients to allow other processes to monitor the state of active
+ downloads. On Cocoa platforms, this can be done by creating an NSProgress, publishing
+ it on an appropriate file URL (potentially a different file URL than where the download
+ data is being written), updating properties on it as the download makes progress, and
+ wiring up a cancellation handler that allows it to be remotely canceled. Interested
+ clients can then subscribe to progress on that URL and receive a proxy to the progress
+ that WebKit publishes.
+
+ * NetworkProcess/Downloads/Download.cpp:
+ (WebKit::Download::~Download):
+ (WebKit::Download::platformDestroyDownload):
+ Add a platform-customizable hook for destructing the Download. DownloadCocoa.mm
+ will interact with its Objective-C NSProgress instance at this point.
+
+ * NetworkProcess/Downloads/Download.h:
+ * NetworkProcess/Downloads/DownloadManager.cpp:
+ (WebKit::DownloadManager::dataTaskBecameDownloadTask):
+ See comments for publishDownloadProgress().
+ (WebKit::DownloadManager::publishDownloadProgress):
+ If the provided downloadID corresponds to a non-Pending Download, hand the URL
+ and a matching sandbox extension to the Download so it can create its progress.
+ Otherwise, store the URL and sandbox extension on the PendingDownload to be used
+ later when the full Download is created. When this happens, dataTaskBecameDownloadTask()
+ will tell the PendingDownload about the Download it has become. The PendingDownload
+ will then relay the progress URL and sandbox extension to the Download.
+
+ * NetworkProcess/Downloads/DownloadManager.h:
+ * NetworkProcess/Downloads/PendingDownload.cpp:
+ (WebKit::PendingDownload::publishProgress):
+ Store the progress info for later use, when the proper Download is created.
+ (WebKit::PendingDownload::didBecomeDownload):
+ If there was a progress URL provided earlier, tell the Download corresponding to this
+ PendingDownload to publish its progress using that URL.
+
+ * NetworkProcess/Downloads/PendingDownload.h:
+ * NetworkProcess/Downloads/cocoa/DownloadCocoa.mm:
+ (WebKit::Download::platformDestroyDownload):
+ When the Download is destroyed (i.e. when the download succeeds, fails, or is canceled),
+ unpublish the progress, since there is no longer any activity to report.
+ (WebKit::Download::publishProgress):
+ Resolve the sandbox extension, create a progress configured to reflect the progress of
+ this Download's NSURLSessionDownloadTask, and publish it at the given file URL.
+
+ * NetworkProcess/Downloads/cocoa/WKDownloadProgress.h: Copied from Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h.
+ * NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm: Added.
+ (-[WKDownloadProgress initWithDownloadTask:download:URL:sandboxExtension:]):
+ Configure this progress, start observing properties on the download task that this
+ progress will reflect, and connect the cancellation handler of the progress to cancel
+ the corresponding Download.
+ (-[WKDownloadProgress publish]):
+ (-[WKDownloadProgress unpublish]):
+ Override these methods to consume and revoke the sandbox extension to make sure the
+ process has access to the progress file while the progress will be published.
+ (-[WKDownloadProgress dealloc]):
+ (-[WKDownloadProgress observeValueForKeyPath:ofObject:change:context:]):
+ When either byte count (number received, or number expected to receive) of the download
+ task changes, update this progress to reflect that.
+
+ * NetworkProcess/NetworkProcess.cpp:
+ (WebKit::NetworkProcess::publishDownloadProgress):
+ * NetworkProcess/NetworkProcess.h:
+ * NetworkProcess/NetworkProcess.messages.in:
+
+ * NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in:
+ * Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb:
+ Allow looking up the services that manages published NSProgresses.
+
+ * UIProcess/API/Cocoa/_WKDownload.h:
+ * UIProcess/API/Cocoa/_WKDownload.mm:
+ (-[_WKDownload publishProgressAtURL:]):
+ * UIProcess/Downloads/DownloadProxy.cpp:
+ (WebKit::DownloadProxy::publishProgress):
+ * UIProcess/Downloads/DownloadProxy.h:
+ * WebKit.xcodeproj/project.pbxproj:
+
2018-11-29 Tim Horton <[email protected]>
Make drawing tools available when an editable image is focused
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/Download.cpp 2018-11-30 00:38:29 UTC (rev 238710)
@@ -77,6 +77,7 @@
Download::~Download()
{
+ platformDestroyDownload();
m_downloadManager.didDestroyDownload();
}
@@ -179,6 +180,10 @@
void Download::platformCancelNetworkLoad()
{
}
+
+void Download::platformDestroyDownload()
+{
+}
#endif
} // namespace WebKit
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/Download.h (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/Download.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/Download.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -37,6 +37,7 @@
#include <wtf/RetainPtr.h>
#if PLATFORM(COCOA)
+OBJC_CLASS NSProgress;
OBJC_CLASS NSURLSessionDownloadTask;
#endif
@@ -73,6 +74,9 @@
void resume(const IPC::DataReference& resumeData, const String& path, SandboxExtension::Handle&&);
void cancel();
+#if PLATFORM(COCOA)
+ void publishProgress(const WebCore::URL&, SandboxExtension::Handle&&);
+#endif
DownloadID downloadID() const { return m_downloadID; }
const String& suggestedName() const { return m_suggestedName; }
@@ -91,6 +95,7 @@
uint64_t messageSenderDestinationID() override;
void platformCancelNetworkLoad();
+ void platformDestroyDownload();
bool isAlwaysOnLoggingAllowed() const;
@@ -103,6 +108,7 @@
RefPtr<NetworkDataTask> m_download;
#if PLATFORM(COCOA)
RetainPtr<NSURLSessionDownloadTask> m_downloadTask;
+ RetainPtr<NSProgress> m_progress;
#endif
PAL::SessionID m_sessionID;
String m_suggestedName;
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp 2018-11-30 00:38:29 UTC (rev 238710)
@@ -64,7 +64,11 @@
void DownloadManager::dataTaskBecameDownloadTask(DownloadID downloadID, std::unique_ptr<Download>&& download)
{
ASSERT(m_pendingDownloads.contains(downloadID));
- m_pendingDownloads.remove(downloadID);
+ if (auto pendingDownload = m_pendingDownloads.take(downloadID)) {
+#if PLATFORM(COCOA)
+ pendingDownload->didBecomeDownload(download);
+#endif
+ }
ASSERT(!m_downloads.contains(downloadID));
m_downloadsAfterDestinationDecided.remove(downloadID);
m_downloads.add(downloadID, WTFMove(download));
@@ -155,6 +159,16 @@
pendingDownload->cancel();
}
+#if PLATFORM(COCOA)
+void DownloadManager::publishDownloadProgress(DownloadID downloadID, const WebCore::URL& url, SandboxExtension::Handle&& sandboxExtensionHandle)
+{
+ if (auto* download = m_downloads.get(downloadID))
+ download->publishProgress(url, WTFMove(sandboxExtensionHandle));
+ else if (auto* pendingDownload = m_pendingDownloads.get(downloadID))
+ pendingDownload->publishProgress(url, WTFMove(sandboxExtensionHandle));
+}
+#endif // PLATFORM(COCOA)
+
void DownloadManager::downloadFinished(Download* download)
{
ASSERT(m_downloads.contains(download->downloadID()));
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -85,6 +85,9 @@
void resumeDownload(PAL::SessionID, DownloadID, const IPC::DataReference& resumeData, const String& path, SandboxExtension::Handle&&);
void cancelDownload(DownloadID);
+#if PLATFORM(COCOA)
+ void publishDownloadProgress(DownloadID, const WebCore::URL&, SandboxExtension::Handle&&);
+#endif
Download* download(DownloadID downloadID) { return m_downloads.get(downloadID); }
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp 2018-11-30 00:38:29 UTC (rev 238710)
@@ -75,6 +75,21 @@
send(Messages::DownloadProxy::DidCancel({ }));
}
+#if PLATFORM(COCOA)
+void PendingDownload::publishProgress(const WebCore::URL& url, SandboxExtension::Handle&& sandboxExtension)
+{
+ ASSERT(!m_progressURL.isValid());
+ m_progressURL = url;
+ m_progressSandboxExtension = WTFMove(sandboxExtension);
+}
+
+void PendingDownload::didBecomeDownload(const std::unique_ptr<Download>& download)
+{
+ if (m_progressURL.isValid())
+ download->publishProgress(m_progressURL, WTFMove(m_progressSandboxExtension));
+}
+#endif // PLATFORM(COCOA)
+
void PendingDownload::didFailLoading(const WebCore::ResourceError& error)
{
send(Messages::DownloadProxy::DidFail(error, { }));
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -34,6 +34,7 @@
namespace WebKit {
+class Download;
class DownloadID;
class NetworkLoad;
class NetworkLoadParameters;
@@ -48,6 +49,11 @@
void continueWillSendRequest(WebCore::ResourceRequest&&);
void cancel();
+#if PLATFORM(COCOA)
+ void publishProgress(const WebCore::URL&, SandboxExtension::Handle&&);
+ void didBecomeDownload(const std::unique_ptr<Download>&);
+#endif
+
private:
// NetworkLoadClient.
void didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent) override { }
@@ -66,6 +72,11 @@
private:
std::unique_ptr<NetworkLoad> m_networkLoad;
bool m_isAllowedToAskUserForCredentials;
+
+#if PLATFORM(COCOA)
+ WebCore::URL m_progressURL;
+ SandboxExtension::Handle m_progressSandboxExtension;
+#endif
};
}
Modified: trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm 2018-11-30 00:38:29 UTC (rev 238710)
@@ -29,7 +29,9 @@
#import "DataReference.h"
#import "NetworkSessionCocoa.h"
#import "SessionTracker.h"
+#import "WKDownloadProgress.h"
#import <pal/spi/cf/CFNetworkSPI.h>
+#import <pal/spi/cocoa/NSProgressSPI.h>
namespace WebKit {
@@ -87,4 +89,36 @@
}];
}
+void Download::platformDestroyDownload()
+{
+ if (m_progress)
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ [m_progress _unpublish];
+#else
+ [m_progress unpublish];
+#endif
}
+
+void Download::publishProgress(const WebCore::URL& url, SandboxExtension::Handle&& sandboxExtensionHandle)
+{
+#if WK_API_ENABLED
+ ASSERT(!m_progress);
+ ASSERT(url.isValid());
+
+ auto sandboxExtension = SandboxExtension::create(WTFMove(sandboxExtensionHandle));
+
+ ASSERT(sandboxExtension);
+
+ m_progress = adoptNS([[WKDownloadProgress alloc] initWithDownloadTask:m_downloadTask.get() download:this URL:(NSURL *)url sandboxExtension:sandboxExtension]);
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ [m_progress _publish];
+#else
+ [m_progress publish];
+#endif
+#else
+ UNUSED_PARAM(url);
+ UNUSED_PARAM(sandboxExtensionHandle);
+#endif // not WK_API_ENABLED
+}
+
+}
Copied: trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.h (from rev 238708, trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h) (0 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.h (rev 0)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <WebKit/WKFoundation.h>
+
+#if WK_API_ENABLED
+
+#import <Foundation/NSProgress.h>
+#import <wtf/RefPtr.h>
+
+namespace WebKit {
+
+class Download;
+class SandboxExtension;
+
+}
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface WKDownloadProgress : NSProgress
+
+- (instancetype)initWithDownloadTask:(NSURLSessionDownloadTask *)task download:(WebKit::Download*)download URL:(NSURL *)fileURL sandboxExtension:(RefPtr<WebKit::SandboxExtension>)sandboxExtension;
+
+@end
+
+NS_ASSUME_NONNULL_END
+
+#endif // WK_API_ENABLED
Added: trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm (0 => 238710)
--- trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm (rev 0)
+++ trunk/Source/WebKit/NetworkProcess/Downloads/cocoa/WKDownloadProgress.mm 2018-11-30 00:38:29 UTC (rev 238710)
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "WKDownloadProgress.h"
+
+#if WK_API_ENABLED
+
+#import "Download.h"
+#import <pal/spi/cocoa/NSProgressSPI.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/WeakObjCPtr.h>
+
+static void* WKDownloadProgressBytesExpectedToReceiveCountContext = &WKDownloadProgressBytesExpectedToReceiveCountContext;
+static void* WKDownloadProgressBytesReceivedContext = &WKDownloadProgressBytesReceivedContext;
+
+static NSString * const countOfBytesExpectedToReceiveKeyPath = @"countOfBytesExpectedToReceive";
+static NSString * const countOfBytesReceivedKeyPath = @"countOfBytesReceived";
+
+@implementation WKDownloadProgress {
+ RetainPtr<NSURLSessionDownloadTask> m_task;
+ WebKit::Download* m_download;
+ RefPtr<WebKit::SandboxExtension> m_sandboxExtension;
+}
+
+- (instancetype)initWithDownloadTask:(NSURLSessionDownloadTask *)task download:(WebKit::Download*)download URL:(NSURL *)fileURL sandboxExtension:(RefPtr<WebKit::SandboxExtension>)sandboxExtension
+{
+ if (!(self = [self initWithParent:nil userInfo:nil]))
+ return nil;
+
+ m_task = task;
+ m_download = download;
+
+ [task addObserver:self forKeyPath:countOfBytesExpectedToReceiveKeyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:WKDownloadProgressBytesExpectedToReceiveCountContext];
+ [task addObserver:self forKeyPath:countOfBytesReceivedKeyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial context:WKDownloadProgressBytesReceivedContext];
+
+ self.kind = NSProgressKindFile;
+#if !PLATFORM(MAC) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
+ self.fileOperationKind = NSProgressFileOperationKindDownloading;
+ self.fileURL = fileURL;
+#else
+ [self setUserInfoObject:NSProgressFileOperationKindDownloading forKey:NSProgressFileOperationKindKey];
+ [self setUserInfoObject:fileURL forKey:NSProgressFileURLKey];
+#endif
+ m_sandboxExtension = sandboxExtension;
+
+ self.cancellable = YES;
+ self.cancellationHandler = BlockPtr<void()>::fromCallable([weakSelf = WeakObjCPtr<WKDownloadProgress> { self }] {
+ auto strongSelf = weakSelf.get();
+ if (!strongSelf)
+ return;
+
+ if (auto* download = strongSelf.get()->m_download)
+ download->cancel();
+ }).get();
+
+ return self;
+}
+
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+- (void)_publish
+#else
+- (void)publish
+#endif
+{
+ BOOL consumedExtension = m_sandboxExtension->consume();
+ ASSERT_UNUSED(consumedExtension, consumedExtension);
+
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ [super _publish];
+#else
+ [super publish];
+#endif
+}
+
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+- (void)_unpublish
+#else
+- (void)unpublish
+#endif
+{
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ [super _unpublish];
+#else
+ [super unpublish];
+#endif
+
+ m_sandboxExtension->revoke();
+ m_sandboxExtension = nullptr;
+}
+
+- (void)dealloc
+{
+ [m_task.get() removeObserver:self forKeyPath:countOfBytesExpectedToReceiveKeyPath];
+ [m_task.get() removeObserver:self forKeyPath:countOfBytesReceivedKeyPath];
+
+ ASSERT(!m_sandboxExtension);
+
+ [super dealloc];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+ if (context == WKDownloadProgressBytesExpectedToReceiveCountContext) {
+ RetainPtr<NSNumber> value = static_cast<NSNumber *>(change[NSKeyValueChangeNewKey]);
+ ASSERT([value isKindOfClass:[NSNumber class]]);
+ int64_t expectedByteCount = value.get().longLongValue;
+ self.totalUnitCount = (expectedByteCount <= 0) ? -1 : expectedByteCount;
+ } else if (context == WKDownloadProgressBytesReceivedContext) {
+ RetainPtr<NSNumber> value = static_cast<NSNumber *>(change[NSKeyValueChangeNewKey]);
+ ASSERT([value isKindOfClass:[NSNumber class]]);
+ self.completedUnitCount = value.get().longLongValue;
+ } else
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+@end
+
+#endif // WK_API_ENABLED
Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp 2018-11-30 00:38:29 UTC (rev 238710)
@@ -819,6 +819,13 @@
downloadManager().cancelDownload(downloadID);
}
+#if PLATFORM(COCOA)
+void NetworkProcess::publishDownloadProgress(DownloadID downloadID, const WebCore::URL& url, SandboxExtension::Handle&& sandboxExtensionHandle)
+{
+ downloadManager().publishDownloadProgress(downloadID, url, WTFMove(sandboxExtensionHandle));
+}
+#endif
+
void NetworkProcess::continueWillSendRequest(DownloadID downloadID, WebCore::ResourceRequest&& request)
{
downloadManager().continueWillSendRequest(downloadID, WTFMove(request));
Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -274,6 +274,9 @@
void downloadRequest(PAL::SessionID, DownloadID, const WebCore::ResourceRequest&, const String& suggestedFilename);
void resumeDownload(PAL::SessionID, DownloadID, const IPC::DataReference& resumeData, const String& path, SandboxExtension::Handle&&);
void cancelDownload(DownloadID);
+#if PLATFORM(COCOA)
+ void publishDownloadProgress(DownloadID, const WebCore::URL&, SandboxExtension::Handle&&);
+#endif
void continueWillSendRequest(DownloadID, WebCore::ResourceRequest&&);
void continueDecidePendingDownloadDestination(DownloadID, String destination, SandboxExtension::Handle&&, bool allowOverwrite);
Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.messages.in 2018-11-30 00:38:29 UTC (rev 238710)
@@ -49,6 +49,9 @@
DownloadRequest(PAL::SessionID sessionID, WebKit::DownloadID downloadID, WebCore::ResourceRequest request, String suggestedFilename)
ResumeDownload(PAL::SessionID sessionID, WebKit::DownloadID downloadID, IPC::DataReference resumeData, String path, WebKit::SandboxExtension::Handle sandboxExtensionHandle)
CancelDownload(WebKit::DownloadID downloadID)
+#if PLATFORM(COCOA)
+ PublishDownloadProgress(WebKit::DownloadID downloadID, WebCore::URL url, WebKit::SandboxExtension::Handle sandboxExtensionHandle)
+#endif
ContinueWillSendRequest(WebKit::DownloadID downloadID, WebCore::ResourceRequest request)
ContinueDecidePendingDownloadDestination(WebKit::DownloadID downloadID, String destination, WebKit::SandboxExtension::Handle sandboxExtensionHandle, bool allowOverwrite)
Modified: trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in (238709 => 238710)
--- trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/NetworkProcess/mac/com.apple.WebKit.NetworkProcess.sb.in 2018-11-30 00:38:29 UTC (rev 238710)
@@ -290,3 +290,7 @@
(local tcp)
(remote tcp))
+;; For reporting progress for active downloads <rdar://problem/44405661>
+(allow mach-lookup
+ (global-name "com.apple.ProgressReporting"))
+
Modified: trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb (238709 => 238710)
--- trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/Resources/SandboxProfiles/ios/com.apple.WebKit.Networking.sb 2018-11-30 00:38:29 UTC (rev 238710)
@@ -94,3 +94,7 @@
(global-name "com.apple.WirelessCoexManager")
(global-name "com.apple.wifi.manager")
(global-name "com.apple.identityservicesd.embedded.auth"))
+
+;; For reporting progress for active downloads <rdar://problem/44405661>
+(allow mach-lookup
+ (global-name "com.apple.ProgressReporting"))
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h (238709 => 238710)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -36,6 +36,8 @@
- (void)cancel;
+- (void)publishProgressAtURL:(NSURL *)URL WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
@property (nonatomic, readonly) NSURLRequest *request;
@property (nonatomic, readonly, weak) WKWebView *originatingWebView;
@property (nonatomic, readonly, copy) NSArray<NSURL *> *redirectChain WK_API_AVAILABLE(macosx(10.13.4), ios(11.3));
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm (238709 => 238710)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm 2018-11-30 00:38:29 UTC (rev 238710)
@@ -49,6 +49,11 @@
_download->cancel();
}
+- (void)publishProgressAtURL:(NSURL *)URL
+{
+ _download->publishProgress(URL);
+}
+
- (NSURLRequest *)request
{
return _download->request().nsURLRequest(WebCore::HTTPBodyUpdatePolicy::DoNotUpdateHTTPBody);
Modified: trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp (238709 => 238710)
--- trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp 2018-11-30 00:38:29 UTC (rev 238710)
@@ -101,6 +101,21 @@
m_originatingPage = makeWeakPtr(page);
}
+#if PLATFORM(COCOA)
+void DownloadProxy::publishProgress(const WebCore::URL& URL)
+{
+ if (!m_processPool)
+ return;
+
+ if (auto* networkProcess = m_processPool->networkProcess()) {
+ SandboxExtension::Handle handle;
+ bool createdSandboxExtension = SandboxExtension::createHandle(URL.fileSystemPath(), SandboxExtension::Type::ReadWrite, handle);
+ ASSERT_UNUSED(createdSandboxExtension, createdSandboxExtension);
+ networkProcess->send(Messages::NetworkProcess::PublishDownloadProgress(m_downloadID, URL, handle), 0);
+ }
+}
+#endif // PLATFORM(COCOA)
+
void DownloadProxy::didStart(const ResourceRequest& request, const String& suggestedFilename)
{
m_request = request;
Modified: trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.h (238709 => 238710)
--- trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.h 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/UIProcess/Downloads/DownloadProxy.h 2018-11-30 00:38:29 UTC (rev 238710)
@@ -95,6 +95,10 @@
const WebCore::IntRect& systemPreviewDownloadRect() const { return request().systemPreviewRect(); }
#endif
+#if PLATFORM(COCOA)
+ void publishProgress(const WebCore::URL&);
+#endif
+
private:
explicit DownloadProxy(DownloadProxyMap&, WebProcessPool&, const WebCore::ResourceRequest&);
Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (238709 => 238710)
--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj 2018-11-30 00:38:29 UTC (rev 238710)
@@ -1083,6 +1083,8 @@
63108F991F9671F700A0DB84 /* _WKApplicationManifestInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 63108F981F9671F700A0DB84 /* _WKApplicationManifestInternal.h */; };
634842511FB26E7100946E3C /* APIApplicationManifest.h in Headers */ = {isa = PBXBuildFile; fileRef = 6348424F1FB26E7100946E3C /* APIApplicationManifest.h */; };
636353A51E9858DF0009F8AF /* _WKGeolocationCoreLocationProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 63FABE191E970D65003011D5 /* _WKGeolocationCoreLocationProvider.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 637281A221ADC744009E0DE6 /* WKDownloadProgress.h in Headers */ = {isa = PBXBuildFile; fileRef = 637281A021ADC744009E0DE6 /* WKDownloadProgress.h */; };
+ 637281A321ADC744009E0DE6 /* WKDownloadProgress.mm in Sources */ = {isa = PBXBuildFile; fileRef = 637281A121ADC744009E0DE6 /* WKDownloadProgress.mm */; };
63C32C261E9810D900699BD0 /* _WKGeolocationPosition.h in Headers */ = {isa = PBXBuildFile; fileRef = 63C32C241E9810D900699BD0 /* _WKGeolocationPosition.h */; settings = {ATTRIBUTES = (Private, ); }; };
63C32C281E98119000699BD0 /* _WKGeolocationPositionInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 63C32C271E98119000699BD0 /* _WKGeolocationPositionInternal.h */; };
65B86F1E12F11DE300B7DD8A /* WKBundleInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = 65B86F1812F11D7B00B7DD8A /* WKBundleInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
@@ -3499,6 +3501,8 @@
63108F951F96719C00A0DB84 /* _WKApplicationManifest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKApplicationManifest.mm; sourceTree = "<group>"; };
63108F981F9671F700A0DB84 /* _WKApplicationManifestInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = _WKApplicationManifestInternal.h; sourceTree = "<group>"; };
6348424F1FB26E7100946E3C /* APIApplicationManifest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = APIApplicationManifest.h; sourceTree = "<group>"; };
+ 637281A021ADC744009E0DE6 /* WKDownloadProgress.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKDownloadProgress.h; sourceTree = "<group>"; };
+ 637281A121ADC744009E0DE6 /* WKDownloadProgress.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WKDownloadProgress.mm; sourceTree = "<group>"; };
63C32C231E9810D900699BD0 /* _WKGeolocationPosition.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = _WKGeolocationPosition.mm; sourceTree = "<group>"; };
63C32C241E9810D900699BD0 /* _WKGeolocationPosition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKGeolocationPosition.h; sourceTree = "<group>"; };
63C32C271E98119000699BD0 /* _WKGeolocationPositionInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _WKGeolocationPositionInternal.h; sourceTree = "<group>"; };
@@ -6824,6 +6828,8 @@
isa = PBXGroup;
children = (
5C1427091C23F86900D41183 /* DownloadCocoa.mm */,
+ 637281A021ADC744009E0DE6 /* WKDownloadProgress.h */,
+ 637281A121ADC744009E0DE6 /* WKDownloadProgress.mm */,
);
path = cocoa;
sourceTree = "<group>";
@@ -9106,6 +9112,7 @@
C0E3AA7C1209E83C00A49D01 /* Module.h in Headers */,
2D50366B1BCDE17900E20BB3 /* NativeWebGestureEvent.h in Headers */,
263172CF18B469490065B9C3 /* NativeWebTouchEvent.h in Headers */,
+ 637281A221ADC744009E0DE6 /* WKDownloadProgress.h in Headers */,
1ADCB86B189831B30022EE5A /* NavigationActionData.h in Headers */,
1ABC3DF61899E437004F0626 /* NavigationState.h in Headers */,
1A6FBA2A11E6862700DB1371 /* NetscapeBrowserFuncs.h in Headers */,
@@ -10785,6 +10792,7 @@
2D92A794212B6AD400F493FD /* NPVariantData.cpp in Sources */,
CDA29A281CBEB67A00901CCF /* PlaybackSessionManagerMessageReceiver.cpp in Sources */,
CDA29A2A1CBEB67A00901CCF /* PlaybackSessionManagerProxyMessageReceiver.cpp in Sources */,
+ 637281A321ADC744009E0DE6 /* WKDownloadProgress.mm in Sources */,
2D913449212CF9F000128AFD /* Plugin.cpp in Sources */,
1A8EF4CC1252403700F7067F /* PluginControllerProxy.cpp in Sources */,
1A2D91A61281D739001EB962 /* PluginControllerProxyMac.mm in Sources */,
Modified: trunk/Tools/ChangeLog (238709 => 238710)
--- trunk/Tools/ChangeLog 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Tools/ChangeLog 2018-11-30 00:38:29 UTC (rev 238710)
@@ -1,3 +1,50 @@
+2018-11-29 David Quesada <[email protected]>
+
+ Add SPI to publish NSProgress on active downloads
+ https://bugs.webkit.org/show_bug.cgi?id=192021
+ rdar://problem/44405661
+
+ Reviewed by Alex Christensen.
+
+ Add API tests to exercise the progress-publishing functionality in a variety of scenarios.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebKitCocoa/DownloadProgress.mm: Added.
+ (+[DownloadProgressTestProtocol registerProtocolForTestRunner:]):
+ (+[DownloadProgressTestProtocol unregisterProtocol]):
+ (+[DownloadProgressTestProtocol canInitWithRequest:]):
+ (+[DownloadProgressTestProtocol canonicalRequestForRequest:]):
+ (+[DownloadProgressTestProtocol requestIsCacheEquivalent:toRequest:]):
+ (-[DownloadProgressTestProtocol startLoading]):
+ (-[DownloadProgressTestProtocol stopLoading]):
+ (-[DownloadProgressTestRunner init]):
+ (-[DownloadProgressTestRunner startLoadingWithProtocol:]):
+ (-[DownloadProgressTestRunner tearDown]):
+ (-[DownloadProgressTestRunner _didGetProgress:]):
+ (-[DownloadProgressTestRunner _didLoseProgress]):
+ (-[DownloadProgressTestRunner subscribeAndWaitForProgress]):
+ (-[DownloadProgressTestRunner waitToLoseProgress]):
+ (-[DownloadProgressTestRunner startDownload:expectedLength:]):
+ (-[DownloadProgressTestRunner publishProgress]):
+ (-[DownloadProgressTestRunner receiveData:]):
+ (-[DownloadProgressTestRunner finishDownloadTask]):
+ (-[DownloadProgressTestRunner failDownloadTask]):
+ (-[DownloadProgressTestRunner waitForDownloadDidCreateDestination]):
+ (-[DownloadProgressTestRunner waitForDownloadFinished]):
+ (-[DownloadProgressTestRunner waitForDownloadCanceled]):
+ (-[DownloadProgressTestRunner waitForDownloadFailed]):
+ (-[DownloadProgressTestRunner waitForUpdatedCompletedUnitCount]):
+ (-[DownloadProgressTestRunner observeValueForKeyPath:ofObject:change:context:]):
+ (-[DownloadProgressTestRunner webView:decidePolicyForNavigationResponse:decisionHandler:]):
+ (-[DownloadProgressTestRunner webView:decidePolicyForNavigationAction:decisionHandler:]):
+ (-[DownloadProgressTestRunner _downloadDidStart:]):
+ (-[DownloadProgressTestRunner _download:didCreateDestination:]):
+ (-[DownloadProgressTestRunner _downloadDidFinish:]):
+ (-[DownloadProgressTestRunner _downloadDidCancel:]):
+ (-[DownloadProgressTestRunner _download:didFailWithError:]):
+ (-[DownloadProgressTestRunner _download:decideDestinationWithSuggestedFilename:completionHandler:]):
+ (TEST):
+
2018-11-29 Alexey Proskuryakov <[email protected]>
Exception in bot watcher's dashboard if a hidden platform in no longer configured for display
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (238709 => 238710)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-11-30 00:34:37 UTC (rev 238709)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2018-11-30 00:38:29 UTC (rev 238710)
@@ -317,6 +317,7 @@
6354F4D11F7C3AB500D89DF3 /* ApplicationManifestParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6354F4D01F7C3AB500D89DF3 /* ApplicationManifestParser.cpp */; };
6356FB221EC4E0BA0044BF18 /* VisibleContentRect.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6356FB211EC4E0BA0044BF18 /* VisibleContentRect.mm */; };
636353A71E98665D0009F8AF /* GeolocationGetCurrentPositionResult.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 636353A61E9861940009F8AF /* GeolocationGetCurrentPositionResult.html */; };
+ 637281A721AE1386009E0DE6 /* DownloadProgress.mm in Sources */ = {isa = PBXBuildFile; fileRef = 637281A621AE1386009E0DE6 /* DownloadProgress.mm */; };
63A61B8B1FAD251100F06885 /* display-mode.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 63A61B8A1FAD204D00F06885 /* display-mode.html */; };
63F668221F97F7F90032EE51 /* ApplicationManifest.mm in Sources */ = {isa = PBXBuildFile; fileRef = 63F668201F97C3AA0032EE51 /* ApplicationManifest.mm */; };
6B306106218A372900F5A802 /* ClosingWebView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6B306105218A372900F5A802 /* ClosingWebView.mm */; };
@@ -1654,6 +1655,7 @@
6354F4D01F7C3AB500D89DF3 /* ApplicationManifestParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ApplicationManifestParser.cpp; sourceTree = "<group>"; };
6356FB211EC4E0BA0044BF18 /* VisibleContentRect.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = VisibleContentRect.mm; sourceTree = "<group>"; };
636353A61E9861940009F8AF /* GeolocationGetCurrentPositionResult.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = GeolocationGetCurrentPositionResult.html; sourceTree = "<group>"; };
+ 637281A621AE1386009E0DE6 /* DownloadProgress.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DownloadProgress.mm; sourceTree = "<group>"; };
63A61B8A1FAD204D00F06885 /* display-mode.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "display-mode.html"; sourceTree = "<group>"; };
63F668201F97C3AA0032EE51 /* ApplicationManifest.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ApplicationManifest.mm; sourceTree = "<group>"; };
6B306105218A372900F5A802 /* ClosingWebView.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ClosingWebView.mm; sourceTree = "<group>"; };
@@ -2412,6 +2414,7 @@
518EE51620A78CDF00E024F3 /* DoubleDefersLoading.mm */,
518EE51720A78CDF00E024F3 /* DoubleDefersLoadingPlugin.mm */,
A1A4FE5D18DD3DB700B5EA8A /* Download.mm */,
+ 637281A621AE1386009E0DE6 /* DownloadProgress.mm */,
F46128D6211E489C00D9FADB /* DragAndDropTests.mm */,
A15502281E05020B00A24C57 /* DuplicateCompletionHandlerCalls.mm */,
F44D06461F395C4D001A0E29 /* EditorStateTests.mm */,
@@ -3792,6 +3795,7 @@
F45B63FE1F19D410009D38B9 /* ActionSheetTests.mm in Sources */,
37E7DD641EA06FF2009B396D /* AdditionalReadAccessAllowedURLs.mm in Sources */,
55A817FC218100E00004A39A /* AdditionalSupportedImageTypes.mm in Sources */,
+ 637281A721AE1386009E0DE6 /* DownloadProgress.mm in Sources */,
7A909A7D1D877480007E10F8 /* AffineTransform.cpp in Sources */,
A1DF74321C41B65800A2F4D0 /* AlwaysRevalidatedURLSchemes.mm in Sources */,
2DE71AFE1D49C0BD00904094 /* AnimatedResize.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DownloadProgress.mm (0 => 238710)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DownloadProgress.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/DownloadProgress.mm 2018-11-30 00:38:29 UTC (rev 238710)
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+
+#import "TestWKWebView.h"
+#import "Utilities.h"
+#import <Foundation/NSProgress.h>
+#import <WebCore/FileSystem.h>
+#import <WebKit/WKBrowsingContextController.h>
+#import <WebKit/WKNavigationDelegatePrivate.h>
+#import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKWebViewConfiguration.h>
+#import <WebKit/_WKDownload.h>
+#import <WebKit/_WKDownloadDelegate.h>
+#import <pal/spi/cocoa/NSProgressSPI.h>
+#import <wtf/BlockPtr.h>
+#import <wtf/RetainPtr.h>
+#import <wtf/WeakObjCPtr.h>
+
+#if WK_API_ENABLED
+
+@class DownloadProgressTestProtocol;
+
+enum class DownloadStartType {
+ ConvertLoadToDownload,
+ StartFromNavigationAction,
+ StartInProcessPool,
+};
+
+@interface DownloadProgressTestRunner : NSObject <WKNavigationDelegate, _WKDownloadDelegate>
+
+@property (nonatomic, readonly) _WKDownload *download;
+@property (nonatomic, readonly) NSProgress *progress;
+
+- (void)startLoadingWithProtocol:(DownloadProgressTestProtocol *)protocol;
+
+@end
+
+@interface DownloadProgressTestProtocol : NSURLProtocol
+@end
+
+@implementation DownloadProgressTestProtocol
+
+static DownloadProgressTestRunner *currentTestRunner;
+
++ (void)registerProtocolForTestRunner:(DownloadProgressTestRunner *)testRunner
+{
+ currentTestRunner = testRunner;
+ [NSURLProtocol registerClass:self];
+ [WKBrowsingContextController registerSchemeForCustomProtocol:@"http"];
+}
+
++ (void)unregisterProtocol
+{
+ currentTestRunner = nullptr;
+ [WKBrowsingContextController unregisterSchemeForCustomProtocol:@"http"];
+ [NSURLProtocol unregisterClass:self];
+}
+
+// MARK: NSURLProtocol Methods
+
++ (BOOL)canInitWithRequest:(NSURLRequest *)request
+{
+ return [request.URL.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame;
+}
+
++ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
+{
+ return request;
+}
+
++ (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b
+{
+ return NO;
+}
+
+- (void)startLoading
+{
+ [currentTestRunner startLoadingWithProtocol:self];
+}
+
+- (void)stopLoading
+{
+}
+
+@end
+
+static void* progressObservingContext = &progressObservingContext;
+
+@implementation DownloadProgressTestRunner {
+ RetainPtr<NSURL> m_progressURL;
+ RetainPtr<TestWKWebView> m_webView;
+ RetainPtr<id> m_progressSubscriber;
+ RetainPtr<NSProgress> m_progress;
+ RetainPtr<_WKDownload> m_download;
+ RetainPtr<DownloadProgressTestProtocol> m_protocol;
+ BlockPtr<void(void)> m_unpublishingBlock;
+ DownloadStartType m_startType;
+ NSInteger m_expectedLength;
+ bool m_hasProgress;
+ bool m_lostProgress;
+ bool m_downloadStarted;
+ bool m_downloadDidCreateDestination;
+ bool m_downloadFinished;
+ bool m_downloadCanceled;
+ bool m_downloadFailed;
+ bool m_hasUpdatedCompletedUnitCount;
+}
+
+- (instancetype)init
+{
+ self = [super init];
+
+ [DownloadProgressTestProtocol registerProtocolForTestRunner:self];
+
+ NSString *fileName = [NSString stringWithFormat:@"download-progress-%@", [NSUUID UUID].UUIDString];
+ m_progressURL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:fileName] isDirectory:NO];
+
+ currentTestRunner = self;
+
+ m_unpublishingBlock = BlockPtr<void(void)>::fromCallable([self] {
+ [self _didLoseProgress];
+ }).get();
+
+ return self;
+}
+
+- (_WKDownload *)download
+{
+ return m_download.get();
+}
+
+- (NSProgress *)progress
+{
+ return m_progress.get();
+}
+
+- (void)startLoadingWithProtocol:(DownloadProgressTestProtocol *)protocol
+{
+ m_protocol = protocol;
+
+ auto response = adoptNS([[NSURLResponse alloc] initWithURL:protocol.request.URL MIMEType:@"application/x-test-file" expectedContentLength:m_expectedLength textEncodingName:nullptr]);
+ [m_protocol.get().client URLProtocol:m_protocol.get() didReceiveResponse:response.get() cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+}
+
+- (void)tearDown
+{
+ if (m_webView) {
+ m_webView.get().configuration.processPool._downloadDelegate = nullptr;
+ [m_webView.get() removeFromSuperview];
+ m_webView = nullptr;
+ }
+
+ if (m_progressSubscriber) {
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ [NSProgress _removeSubscriber:m_progressSubscriber.get()];
+#else
+ [NSProgress removeSubscriber:m_progressSubscriber.get()];
+#endif
+ m_progressSubscriber = nullptr;
+ }
+
+ m_progress = nullptr;
+ m_download = nullptr;
+ m_protocol = nullptr;
+ m_unpublishingBlock = nullptr;
+
+ [DownloadProgressTestProtocol unregisterProtocol];
+}
+
+- (void)_didGetProgress:(NSProgress *)progress
+{
+ ASSERT(!m_progress);
+ m_progress = progress;
+ [progress addObserver:self forKeyPath:@"completedUnitCount" options:NSKeyValueObservingOptionNew context:progressObservingContext];
+ m_hasProgress = true;
+}
+
+- (void)_didLoseProgress
+{
+ ASSERT(m_progress);
+ [m_progress.get() removeObserver:self forKeyPath:@"completedUnitCount"];
+ m_progress = nullptr;
+ m_lostProgress = true;
+}
+
+- (void)subscribeAndWaitForProgress
+{
+ if (!m_progressSubscriber) {
+ auto publishingHandler = BlockPtr<NSProgressUnpublishingHandler(NSProgress *)>::fromCallable([weakSelf = WeakObjCPtr<DownloadProgressTestRunner> { self }](NSProgress *progress) {
+ if (auto strongSelf = weakSelf.get()) {
+ [strongSelf.get() _didGetProgress:progress];
+ return strongSelf->m_unpublishingBlock.get();
+ }
+ return static_cast<NSProgressUnpublishingHandler>(nil);
+ });
+
+#if USE(NSPROGRESS_PUBLISHING_SPI)
+ m_progressSubscriber = [NSProgress _addSubscriberForFileURL:m_progressURL.get() withPublishingHandler:publishingHandler.get()];
+#else
+ m_progressSubscriber = [NSProgress addSubscriberForFileURL:m_progressURL.get() withPublishingHandler:publishingHandler.get()];
+#endif
+ }
+ TestWebKitAPI::Util::run(&m_hasProgress);
+}
+
+- (void)waitToLoseProgress
+{
+ TestWebKitAPI::Util::run(&m_lostProgress);
+}
+
+- (void)startDownload:(DownloadStartType)startType expectedLength:(NSInteger)expectedLength
+{
+ m_startType = startType;
+ m_expectedLength = expectedLength;
+
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ m_webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
+ m_webView.get().navigationDelegate = self;
+ m_webView.get().configuration.processPool._downloadDelegate = self;
+
+ auto request = adoptNS([[NSURLRequest alloc] initWithURL:[NSURL URLWithString:@"http://file"]]);
+
+ switch (startType) {
+ case DownloadStartType::ConvertLoadToDownload:
+ case DownloadStartType::StartFromNavigationAction:
+ [m_webView loadRequest:request.get()];
+ break;
+ case DownloadStartType::StartInProcessPool:
+ [m_webView.get().configuration.processPool _downloadURLRequest:request.get()];
+ break;
+ }
+
+ TestWebKitAPI::Util::run(&m_downloadStarted);
+}
+
+- (void)publishProgress
+{
+ ASSERT(m_download);
+
+ [m_download.get() publishProgressAtURL:m_progressURL.get()];
+}
+
+- (void)receiveData:(NSInteger)length
+{
+ auto data = "" alloc] init]);
+ while (length-- > 0) {
+ const char byte = 'A';
+ [data.get() appendBytes:static_cast<const void*>(&byte) length:1];
+ }
+
+ [m_protocol.get().client URLProtocol:m_protocol.get() didLoadData:data.get()];
+}
+
+- (void)finishDownloadTask
+{
+ [m_protocol.get().client URLProtocolDidFinishLoading:m_protocol.get()];
+}
+
+- (void)failDownloadTask
+{
+ [m_protocol.get().client URLProtocol:m_protocol.get() didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorNetworkConnectionLost userInfo:nullptr]];
+}
+
+- (void)waitForDownloadDidCreateDestination
+{
+ TestWebKitAPI::Util::run(&m_downloadDidCreateDestination);
+}
+
+- (void)waitForDownloadFinished
+{
+ TestWebKitAPI::Util::run(&m_downloadFinished);
+}
+
+- (void)waitForDownloadCanceled
+{
+ TestWebKitAPI::Util::run(&m_downloadCanceled);
+}
+
+- (void)waitForDownloadFailed
+{
+ TestWebKitAPI::Util::run(&m_downloadFailed);
+}
+
+- (int64_t)waitForUpdatedCompletedUnitCount
+{
+ TestWebKitAPI::Util::run(&m_hasUpdatedCompletedUnitCount);
+ m_hasUpdatedCompletedUnitCount = false;
+
+ return m_progress.get().completedUnitCount;
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
+{
+ if (context == progressObservingContext) {
+ EXPECT_EQ(object, m_progress.get());
+ EXPECT_STREQ(keyPath.UTF8String, "completedUnitCount");
+ m_hasUpdatedCompletedUnitCount = true;
+ } else
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
+}
+
+// MARK: <WKNavigationDelegate> Methods
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+ if (m_startType == DownloadStartType::ConvertLoadToDownload)
+ decisionHandler(_WKNavigationResponsePolicyBecomeDownload);
+ else
+ decisionHandler(WKNavigationResponsePolicyAllow);
+}
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
+{
+ if (m_startType == DownloadStartType::StartFromNavigationAction)
+ decisionHandler(_WKNavigationActionPolicyDownload);
+ else
+ decisionHandler(WKNavigationActionPolicyAllow);
+}
+
+// MARK: <_WKDownloadDelegate> Methods
+
+- (void)_downloadDidStart:(_WKDownload *)download
+{
+ ASSERT(!m_downloadStarted);
+ ASSERT(!m_download);
+
+ m_download = download;
+ m_downloadStarted = true;
+}
+
+- (void)_download:(_WKDownload *)download didCreateDestination:(NSString *)destination
+{
+ EXPECT_EQ(download, m_download.get());
+ m_downloadDidCreateDestination = true;
+}
+
+- (void)_downloadDidFinish:(_WKDownload *)download
+{
+ EXPECT_EQ(download, m_download.get());
+ m_downloadFinished = true;
+}
+
+- (void)_downloadDidCancel:(_WKDownload *)download
+{
+ EXPECT_EQ(download, m_download.get());
+ m_downloadCanceled = true;
+}
+
+- (void)_download:(_WKDownload *)download didFailWithError:(NSError *)error
+{
+ EXPECT_EQ(download, m_download.get());
+ m_downloadFailed = true;
+}
+
+- (void)_download:(_WKDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename completionHandler:(void (^)(BOOL allowOverwrite, NSString *destination))completionHandler
+{
+ EXPECT_EQ(download, m_download.get());
+
+ WebCore::FileSystem::PlatformFileHandle fileHandle;
+ RetainPtr<NSString *> path = (NSString *)WebCore::FileSystem::openTemporaryFile("TestWebKitAPI", fileHandle);
+ EXPECT_TRUE(fileHandle != WebCore::FileSystem::invalidPlatformFileHandle);
+ WebCore::FileSystem::closeFile(fileHandle);
+
+ completionHandler(YES, path.get());
+}
+
+@end
+
+// End-to-end test of subscribing to progress on a successful download. The client
+// should be able to receive an NSProgress that is updated as the download makes
+// progress, and the NSProgress should be unpublished when the download finishes.
+TEST(DownloadProgress, BasicSubscriptionAndProgressUpdates)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+
+ [testRunner.get() receiveData:50];
+ EXPECT_EQ([testRunner.get() waitForUpdatedCompletedUnitCount], 50);
+ EXPECT_EQ(testRunner.get().progress.fractionCompleted, .5);
+
+ [testRunner.get() receiveData:50];
+ EXPECT_EQ([testRunner.get() waitForUpdatedCompletedUnitCount], 100);
+ EXPECT_EQ(testRunner.get().progress.fractionCompleted, 1);
+
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Similar test as before, but initiating the download before receiving its response.
+TEST(DownloadProgress, StartDownloadFromNavigationAction)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::StartFromNavigationAction expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ [testRunner.get() receiveData:100];
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+TEST(DownloadProgress, StartDownloadInProcessPool)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::StartInProcessPool expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ [testRunner.get() receiveData:100];
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// If the download is canceled, the progress should be unpublished.
+TEST(DownloadProgress, LoseProgressWhenDownloadIsCanceled)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ [testRunner.get() receiveData:50];
+ [testRunner.get().download cancel];
+ [testRunner.get() waitForDownloadCanceled];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// If the download fails, the progress should be unpublished.
+TEST(DownloadProgress, LoseProgressWhenDownloadFails)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ [testRunner.get() receiveData:50];
+ [testRunner.get() failDownloadTask];
+ [testRunner.get() waitForDownloadFailed];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Canceling the progress should cancel the download.
+TEST(DownloadProgress, CancelDownloadWhenProgressIsCanceled)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ [testRunner.get() receiveData:50];
+ [testRunner.get().progress cancel];
+ [testRunner.get() waitForDownloadCanceled];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Publishing progress on a download after it has finished should be a safe no-op.
+TEST(DownloadProgress, PublishProgressAfterDownloadFinished)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() receiveData:100];
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() publishProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Test the behavior of a download of unknown length.
+TEST(DownloadProgress, IndeterminateDownloadSize)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:NSURLResponseUnknownLength];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ EXPECT_EQ(testRunner.get().progress.totalUnitCount, -1);
+
+ [testRunner.get() receiveData:50];
+ EXPECT_EQ([testRunner.get() waitForUpdatedCompletedUnitCount], 50);
+ EXPECT_EQ(testRunner.get().progress.fractionCompleted, 0);
+ EXPECT_EQ(testRunner.get().progress.totalUnitCount, -1);
+
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Test the behavior when a download continues returning data beyond its expected length.
+TEST(DownloadProgress, ExtraData)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+
+ [testRunner.get() receiveData:150];
+ EXPECT_EQ([testRunner.get() waitForUpdatedCompletedUnitCount], 150);
+ EXPECT_EQ(testRunner.get().progress.fractionCompleted, 1.5);
+
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+// Clients should be able to publish progress on a download that has already started.
+TEST(DownloadProgress, PublishProgressOnPartialDownload)
+{
+ auto testRunner = adoptNS([[DownloadProgressTestRunner alloc] init]);
+
+ [testRunner.get() startDownload:DownloadStartType::ConvertLoadToDownload expectedLength:100];
+ [testRunner.get() receiveData:50];
+
+ // Ensure that the the data task has become a download task so that we test
+ // telling a live Download, not a PendingDownload, to publish its progress.
+ [testRunner.get() waitForDownloadDidCreateDestination];
+
+ [testRunner.get() publishProgress];
+ [testRunner.get() subscribeAndWaitForProgress];
+ EXPECT_EQ(testRunner.get().progress.completedUnitCount, 50);
+
+ [testRunner.get() receiveData:50];
+ EXPECT_EQ([testRunner.get() waitForUpdatedCompletedUnitCount], 100);
+
+ [testRunner.get() finishDownloadTask];
+ [testRunner.get() waitForDownloadFinished];
+ [testRunner.get() waitToLoseProgress];
+
+ [testRunner.get() tearDown];
+}
+
+#endif // WK_API_ENABLED