diff --git a/Source/WebKit/NetworkProcess/Downloads/Download.h b/Source/WebKit/NetworkProcess/Downloads/Download.h index 8693167981db7..46624f548d53e 100644 --- a/Source/WebKit/NetworkProcess/Downloads/Download.h +++ b/Source/WebKit/NetworkProcess/Downloads/Download.h @@ -31,6 +31,7 @@ #include "MessageSender.h" #include "NetworkDataTask.h" #include "SandboxExtension.h" +#include "UseDownloadPlaceholder.h" #include #include #include @@ -90,7 +91,7 @@ class Download : public IPC::MessageSender, public CanMakeWeakPtr, pub #endif #if HAVE(MODERN_DOWNLOADPROGRESS) - void publishProgress(const URL&, std::span); + void publishProgress(const URL&, std::span, WebKit::UseDownloadPlaceholder); #endif DownloadID downloadID() const { return m_downloadID; } diff --git a/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp b/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp index 005b9c36c3e75..5c55ea51cec35 100644 --- a/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp +++ b/Source/WebKit/NetworkProcess/Downloads/DownloadManager.cpp @@ -129,12 +129,12 @@ void DownloadManager::cancelDownload(DownloadID downloadID, CompletionHandler bookmarkData) +void DownloadManager::publishDownloadProgress(DownloadID downloadID, const URL& url, std::span bookmarkData, WebKit::UseDownloadPlaceholder useDownloadPlaceholder) { if (auto* download = m_downloads.get(downloadID)) - download->publishProgress(url, bookmarkData); + download->publishProgress(url, bookmarkData, useDownloadPlaceholder); else if (auto* pendingDownload = m_pendingDownloads.get(downloadID)) - pendingDownload->publishProgress(url, bookmarkData); + pendingDownload->publishProgress(url, bookmarkData, useDownloadPlaceholder); } #else void DownloadManager::publishDownloadProgress(DownloadID downloadID, const URL& url, SandboxExtension::Handle&& sandboxExtensionHandle) diff --git a/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h b/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h index ffa871eb88802..4ad623d6b0247 100644 --- a/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h +++ b/Source/WebKit/NetworkProcess/Downloads/DownloadManager.h @@ -31,6 +31,7 @@ #include "PendingDownload.h" #include "PolicyDecision.h" #include "SandboxExtension.h" +#include "UseDownloadPlaceholder.h" #include #include #include @@ -101,7 +102,7 @@ class DownloadManager : public CanMakeCheckedPtr { void cancelDownload(DownloadID, CompletionHandler)>&&); #if PLATFORM(COCOA) #if HAVE(MODERN_DOWNLOADPROGRESS) - void publishDownloadProgress(DownloadID, const URL&, std::span bookmarkData); + void publishDownloadProgress(DownloadID, const URL&, std::span bookmarkData, WebKit::UseDownloadPlaceholder); #else void publishDownloadProgress(DownloadID, const URL&, SandboxExtension::Handle&&); #endif diff --git a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp index 950f09ed5e466..7db1d44333cff 100644 --- a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp +++ b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.cpp @@ -81,11 +81,12 @@ void PendingDownload::cancel(CompletionHandler)>&& #if PLATFORM(COCOA) #if HAVE(MODERN_DOWNLOADPROGRESS) -void PendingDownload::publishProgress(const URL& url, std::span bookmarkData) +void PendingDownload::publishProgress(const URL& url, std::span bookmarkData, UseDownloadPlaceholder useDownloadPlaceholder) { ASSERT(!m_progressURL.isValid()); m_progressURL = url; m_bookmarkData = bookmarkData; + m_useDownloadPlaceholder = useDownloadPlaceholder; } #else void PendingDownload::publishProgress(const URL& url, SandboxExtension::Handle&& sandboxExtension) @@ -101,7 +102,7 @@ void PendingDownload::didBecomeDownload(const std::unique_ptr& downloa if (!m_progressURL.isValid()) return; #if HAVE(MODERN_DOWNLOADPROGRESS) - download->publishProgress(m_progressURL, m_bookmarkData); + download->publishProgress(m_progressURL, m_bookmarkData, m_useDownloadPlaceholder); #else download->publishProgress(m_progressURL, WTFMove(m_progressSandboxExtension)); #endif diff --git a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h index 1d9bc9178e26d..355efe36187ea 100644 --- a/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h +++ b/Source/WebKit/NetworkProcess/Downloads/PendingDownload.h @@ -29,6 +29,7 @@ #include "MessageSender.h" #include "NetworkLoadClient.h" #include "SandboxExtension.h" +#include "UseDownloadPlaceholder.h" #include namespace WebKit { @@ -66,7 +67,7 @@ class PendingDownload : public NetworkLoadClient, public IPC::MessageSender, pub #if PLATFORM(COCOA) #if HAVE(MODERN_DOWNLOADPROGRESS) - void publishProgress(const URL&, std::span); + void publishProgress(const URL&, std::span, UseDownloadPlaceholder); #else void publishProgress(const URL&, SandboxExtension::Handle&&); #endif @@ -97,6 +98,7 @@ class PendingDownload : public NetworkLoadClient, public IPC::MessageSender, pub URL m_progressURL; #if HAVE(MODERN_DOWNLOADPROGRESS) Vector m_bookmarkData; + UseDownloadPlaceholder m_useDownloadPlaceholder { UseDownloadPlaceholder::No }; #else SandboxExtension::Handle m_progressSandboxExtension; #endif diff --git a/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm b/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm index bf104edf9baf8..65cbf30d4d9ad 100644 --- a/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm +++ b/Source/WebKit/NetworkProcess/Downloads/cocoa/DownloadCocoa.mm @@ -90,7 +90,7 @@ } #if HAVE(MODERN_DOWNLOADPROGRESS) -void Download::publishProgress(const URL& url, std::span bookmarkData) +void Download::publishProgress(const URL& url, std::span bookmarkData, UseDownloadPlaceholder useDownloadPlaceholder) { if (m_progress) { RELEASE_LOG(Network, "Progress is already being published for download."); @@ -110,7 +110,8 @@ bool shouldEnableModernDownloadProgress = CFPreferencesGetAppBooleanValue(CFSTR("EnableModernDownloadProgress"), CFSTR("com.apple.WebKit"), NULL); if (shouldEnableModernDownloadProgress) { - m_progress = adoptNS([[WKModernDownloadProgress alloc] initWithDownloadTask:m_downloadTask.get() download:*this URL:(NSURL *)url]); + NSData *accessToken = [NSData data]; // FIXME: replace with actual access token + m_progress = adoptNS([[WKModernDownloadProgress alloc] initWithDownloadTask:m_downloadTask.get() download:*this URL:(NSURL *)url useDownloadPlaceholder:useDownloadPlaceholder == WebKit::UseDownloadPlaceholder::Yes liveActivityAccessToken:accessToken]); [m_progress publish]; } else { m_progress = adoptNS([[WKDownloadProgress alloc] initWithDownloadTask:m_downloadTask.get() download:*this URL:(NSURL *)url sandboxExtension:nullptr]); diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp index 131b888ff51fc..73199cde54beb 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp +++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp @@ -2141,9 +2141,9 @@ void NetworkProcess::cancelDownload(DownloadID downloadID, CompletionHandler bookmarkData) +void NetworkProcess::publishDownloadProgress(DownloadID downloadID, const URL& url, std::span bookmarkData, WebKit::UseDownloadPlaceholder useDownloadPlaceholder) { - downloadManager().publishDownloadProgress(downloadID, url, bookmarkData); + downloadManager().publishDownloadProgress(downloadID, url, bookmarkData, useDownloadPlaceholder); } #else void NetworkProcess::publishDownloadProgress(DownloadID downloadID, const URL& url, SandboxExtension::Handle&& sandboxExtensionHandle) @@ -2157,7 +2157,7 @@ void NetworkProcess::findPendingDownloadLocation(NetworkDataTask& networkDataTas { String suggestedFilename = networkDataTask.suggestedFilename(); - downloadProxyConnection()->sendWithAsyncReply(Messages::DownloadProxy::DecideDestinationWithSuggestedFilename(response, suggestedFilename), [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler), networkDataTask = Ref { networkDataTask }] (String&& destination, SandboxExtension::Handle&& sandboxExtensionHandle, AllowOverwrite allowOverwrite) mutable { + downloadProxyConnection()->sendWithAsyncReply(Messages::DownloadProxy::DecideDestinationWithSuggestedFilename(response, suggestedFilename), [this, protectedThis = Ref { *this }, completionHandler = WTFMove(completionHandler), networkDataTask = Ref { networkDataTask }] (String&& destination, SandboxExtension::Handle&& sandboxExtensionHandle, AllowOverwrite allowOverwrite, WebKit::UseDownloadPlaceholder usePlaceholder) mutable { auto downloadID = *networkDataTask->pendingDownloadID(); if (destination.isEmpty()) return completionHandler(PolicyAction::Ignore); @@ -2166,6 +2166,14 @@ void NetworkProcess::findPendingDownloadLocation(NetworkDataTask& networkDataTas if (networkDataTask->state() == NetworkDataTask::State::Canceling || networkDataTask->state() == NetworkDataTask::State::Completed) return; +#if HAVE(MODERN_DOWNLOADPROGRESS) + bool shouldEnableModernDownloadProgress = CFPreferencesGetAppBooleanValue(CFSTR("EnableModernDownloadProgress"), CFSTR("com.apple.WebKit"), nullptr); + if (shouldEnableModernDownloadProgress) + publishDownloadProgress(downloadID, URL::fileURLWithFileSystemPath(destination), std::span(), usePlaceholder); +#else + UNUSED_PARAM(usePlaceholder); +#endif + if (downloadManager().download(downloadID)) { // The completion handler already called dataTaskBecameDownloadTask(). return; diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h index fb1687978260d..6c985c9be7b4e 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.h +++ b/Source/WebKit/NetworkProcess/NetworkProcess.h @@ -33,6 +33,7 @@ #include "DownloadManager.h" #include "NetworkContentRuleListManager.h" #include "QuotaIncreaseRequestIdentifier.h" +#include "UseDownloadPlaceholder.h" #include "WebPageProxyIdentifier.h" #include "WebResourceLoadStatisticsStore.h" #include "WebsiteData.h" @@ -483,7 +484,7 @@ class NetworkProcess final : public AuxiliaryProcess, private DownloadManager::C void cancelDownload(DownloadID, CompletionHandler)>&&); #if PLATFORM(COCOA) #if HAVE(MODERN_DOWNLOADPROGRESS) - void publishDownloadProgress(DownloadID, const URL&, std::span bookmarkData); + void publishDownloadProgress(DownloadID, const URL&, std::span bookmarkData, WebKit::UseDownloadPlaceholder); #else void publishDownloadProgress(DownloadID, const URL&, SandboxExtensionHandle&&); #endif diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in index 40a427dca2ace..a9b36cf601fff 100644 --- a/Source/WebKit/NetworkProcess/NetworkProcess.messages.in +++ b/Source/WebKit/NetworkProcess/NetworkProcess.messages.in @@ -52,9 +52,6 @@ messages -> NetworkProcess LegacyReceiver { DownloadRequest(PAL::SessionID sessionID, WebKit::DownloadID downloadID, WebCore::ResourceRequest request, std::optional topOrigin, std::optional isNavigatingToAppBoundDomain, String suggestedFilename) ResumeDownload(PAL::SessionID sessionID, WebKit::DownloadID downloadID, std::span resumeData, String path, WebKit::SandboxExtensionHandle sandboxExtensionHandle, enum:bool WebKit::CallDownloadDidStart callDownloadDidStart) CancelDownload(WebKit::DownloadID downloadID) -> (std::span resumeData) -#if HAVE(MODERN_DOWNLOADPROGRESS) - PublishDownloadProgress(WebKit::DownloadID downloadID, URL url, std::span bookmarkData) -#endif #if PLATFORM(COCOA) && !HAVE(MODERN_DOWNLOADPROGRESS) PublishDownloadProgress(WebKit::DownloadID downloadID, URL url, WebKit::SandboxExtensionHandle sandboxExtensionHandle) #endif diff --git a/Source/WebKit/Shared/API/Cocoa/WebKitPrivate.h b/Source/WebKit/Shared/API/Cocoa/WebKitPrivate.h index 656ce1f8c9f7c..a16bb3462ce6e 100644 --- a/Source/WebKit/Shared/API/Cocoa/WebKitPrivate.h +++ b/Source/WebKit/Shared/API/Cocoa/WebKitPrivate.h @@ -23,6 +23,7 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#import #import #import #import diff --git a/Source/WebKit/UIProcess/API/APIDownloadClient.h b/Source/WebKit/UIProcess/API/APIDownloadClient.h index 36e1adcfaceda..f5a1b3b546dad 100644 --- a/Source/WebKit/UIProcess/API/APIDownloadClient.h +++ b/Source/WebKit/UIProcess/API/APIDownloadClient.h @@ -29,6 +29,7 @@ #include "AuthenticationChallengeProxy.h" #include "AuthenticationDecisionListener.h" #include "DownloadID.h" +#include "DownloadProxy.h" #include #include #include @@ -41,7 +42,6 @@ class ResourceResponse; namespace WebKit { class AuthenticationChallengeProxy; -class DownloadProxy; class WebsiteDataStore; class WebProtectionSpace; @@ -60,6 +60,9 @@ class DownloadClient : public RefCounted { virtual void legacyDidStart(WebKit::DownloadProxy&) { } virtual void didReceiveAuthenticationChallenge(WebKit::DownloadProxy&, WebKit::AuthenticationChallengeProxy& challenge) { challenge.listener().completeChallenge(WebKit::AuthenticationChallengeDisposition::Cancel); } virtual void didReceiveData(WebKit::DownloadProxy&, uint64_t, uint64_t, uint64_t) { } +#if HAVE(MODERN_DOWNLOADPROGRESS) + virtual void decidePlaceholderPolicy(WebKit::DownloadProxy&, CompletionHandler&& completionHandler) { completionHandler(WebKit::UseDownloadPlaceholder::No); } +#endif virtual void decideDestinationWithSuggestedFilename(WebKit::DownloadProxy&, const WebCore::ResourceResponse&, const WTF::String&, CompletionHandler&& completionHandler) { completionHandler(WebKit::AllowOverwrite::No, { }); } virtual void didCreateDestination(WebKit::DownloadProxy&, const WTF::String&) { } virtual void didFinish(WebKit::DownloadProxy&) { } diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKDownload.mm b/Source/WebKit/UIProcess/API/Cocoa/WKDownload.mm index 9c459085069f2..f551a8048c882 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/WKDownload.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/WKDownload.mm @@ -30,6 +30,7 @@ #import "CompletionHandlerCallChecker.h" #import "DownloadProxy.h" #import "WKDownloadDelegate.h" +#import "WKDownloadDelegatePrivate.h" #import "WKFrameInfoInternal.h" #import "WKNSData.h" #import "WKNSURLAuthenticationChallenge.h" @@ -41,12 +42,16 @@ class DownloadClient final : public API::DownloadClient { public: - explicit DownloadClient(id delegate) + explicit DownloadClient(id delegate) : m_delegate(delegate) , m_respondsToWillPerformHTTPRedirection([delegate respondsToSelector:@selector(download:willPerformHTTPRedirection:newRequest:decisionHandler:)]) , m_respondsToDidReceiveAuthenticationChallenge([delegate respondsToSelector:@selector(download:didReceiveAuthenticationChallenge:completionHandler:)]) , m_respondsToDidFinish([m_delegate respondsToSelector:@selector(downloadDidFinish:)]) , m_respondsToDidFailWithError([delegate respondsToSelector:@selector(download:didFailWithError:resumeData:)]) +#if HAVE(MODERN_DOWNLOADPROGRESS) + , m_respondsToDecidePlaceholderPolicy([delegate respondsToSelector:@selector(_download:decidePlaceholderPolicy:)]) +#endif + { ASSERT([delegate respondsToSelector:@selector(download:decideDestinationUsingResponse:suggestedFilename:completionHandler:)]); } @@ -142,6 +147,30 @@ void decideDestinationWithSuggestedFilename(WebKit::DownloadProxy& download, con }).get()]; } +#if HAVE(MODERN_DOWNLOADPROGRESS) + void decidePlaceholderPolicy(WebKit::DownloadProxy& download, CompletionHandler&& completionHandler) + { + if (!m_respondsToDecidePlaceholderPolicy) { + completionHandler(WebKit::UseDownloadPlaceholder::No); + return; + } + [m_delegate _download:wrapper(download) decidePlaceholderPolicy:makeBlockPtr([completionHandler = WTFMove(completionHandler)] (_WKPlaceholderPolicy policy) mutable { + switch (policy) { + case _WKPlaceholderPolicyDisable: { + completionHandler(WebKit::UseDownloadPlaceholder::No); + break; + } + case _WKPlaceholderPolicyEnable: { + completionHandler(WebKit::UseDownloadPlaceholder::Yes); + break; + } + default: + [NSException raise:NSInvalidArgumentException format:@"Invalid WKPlaceholderPolicy (%ld)", (long)policy]; + } + }).get()]; + } +#endif + void didReceiveData(WebKit::DownloadProxy& download, uint64_t, uint64_t totalBytesWritten, uint64_t totalBytesExpectedToWrite) final { NSProgress *progress = wrapper(download).progress; @@ -173,12 +202,15 @@ void processDidCrash(WebKit::DownloadProxy& download) final [m_delegate download:wrapper(download) didFailWithError:[NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorNetworkConnectionLost userInfo:nil] resumeData:nil]; } - WeakObjCPtr > m_delegate; + WeakObjCPtr> m_delegate; bool m_respondsToWillPerformHTTPRedirection : 1; bool m_respondsToDidReceiveAuthenticationChallenge : 1; bool m_respondsToDidFinish : 1; bool m_respondsToDidFailWithError : 1; +#if HAVE(MODERN_DOWNLOADPROGRESS) + bool m_respondsToDecidePlaceholderPolicy : 1; +#endif }; @implementation WKDownload @@ -219,7 +251,7 @@ - (WKFrameInfo *)originatingFrame return _delegate.get().get(); } -- (void)setDelegate:(id )delegate +- (void)setDelegate:(id)delegate { _delegate = delegate; _download->setClient(adoptRef(*new DownloadClient(delegate))); diff --git a/Source/WebKit/UIProcess/API/Cocoa/WKDownloadDelegatePrivate.h b/Source/WebKit/UIProcess/API/Cocoa/WKDownloadDelegatePrivate.h new file mode 100644 index 0000000000000..79322c1b283e1 --- /dev/null +++ b/Source/WebKit/UIProcess/API/Cocoa/WKDownloadDelegatePrivate.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2024 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 +#import +#import + +@class WKDownload; + +/* @enum _WKPlaceholderPolicy + @abstract The policy for creating a placeholder file in the Downloads directory during downloads. + @constant _WKPlaceholderPolicyDisable Do not create a placeholder file. + @constant _WKPlaceholderPolicyEnable Create a placeholder file. + */ +typedef NS_ENUM(NSInteger, _WKPlaceholderPolicy) { + _WKPlaceholderPolicyDisable, + _WKPlaceholderPolicyEnable, +} NS_SWIFT_NAME(WKDownload.PlaceholderPolicy) WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); + +NS_ASSUME_NONNULL_BEGIN + +WK_SWIFT_UI_ACTOR +@protocol WKDownloadDelegatePrivate + +@optional + +/* @abstract Invoked when the download needs a placeholder policy from the client. + @param download The download for which we need a placeholder policy + @param completionHandler The completion handler that should be invoked with the chosen policy + @discussion The placeholder policy specifies whether a placeholder file should be created in + the Downloads directory when the download is in progress. + */ +- (void)_download:(WKDownload *)download decidePlaceholderPolicy:(void (^)(_WKPlaceholderPolicy))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA)); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm b/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm index 338348dfa061a..54c50c9a8cb9f 100644 --- a/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm +++ b/Source/WebKit/UIProcess/API/Cocoa/_WKDownload.mm @@ -79,7 +79,9 @@ - (void)cancel - (void)publishProgressAtURL:(NSURL *)URL { +#if !HAVE(MODERN_DOWNLOADPROGRESS) _download->_download->publishProgress(URL); +#endif } - (NSURLRequest *)request diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp index 4d2991f887d8e..17b0f4d068b10 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.cpp @@ -141,7 +141,7 @@ void DownloadProxy::didReceiveData(uint64_t bytesWritten, uint64_t totalBytesWri m_client->didReceiveData(*this, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite); } -void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse& response, String&& suggestedFilename, CompletionHandler&& completionHandler) +void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse& response, String&& suggestedFilename, DecideDestinationCallback&& completionHandler) { RELEASE_LOG_INFO_IF(!response.expectedContentLength(), Network, "DownloadProxy::decideDestinationWithSuggestedFilename expectedContentLength is null"); @@ -161,12 +161,13 @@ void DownloadProxy::decideDestinationWithSuggestedFilename(const WebCore::Resour } setDestinationFilename(destination); - completionHandler(destination, WTFMove(sandboxExtensionHandle), allowOverwrite); #if HAVE(MODERN_DOWNLOADPROGRESS) - bool shouldEnableModernDownloadProgress = CFPreferencesGetAppBooleanValue(CFSTR("EnableModernDownloadProgress"), CFSTR("com.apple.WebKit"), nullptr); - if (!destination.isEmpty() && shouldEnableModernDownloadProgress) - publishProgress(URL::fileURLWithFileSystemPath(destination)); + m_client->decidePlaceholderPolicy(*this, [completionHandler = WTFMove(completionHandler), destination = WTFMove(destination), sandboxExtensionHandle = WTFMove(sandboxExtensionHandle), allowOverwrite] (WebKit::UseDownloadPlaceholder usePlaceholder) mutable { + completionHandler(destination, WTFMove(sandboxExtensionHandle), allowOverwrite, usePlaceholder); + }); +#else + completionHandler(destination, WTFMove(sandboxExtensionHandle), allowOverwrite, WebKit::UseDownloadPlaceholder::No); #endif }); } diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h index 76852723ff17f..4c83db3523e25 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.h +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.h @@ -30,6 +30,7 @@ #include "DownloadID.h" #include "IdentifierTypes.h" #include "SandboxExtension.h" +#include "UseDownloadPlaceholder.h" #include #include #include @@ -63,6 +64,7 @@ struct FrameInfoData; class DownloadProxy : public API::ObjectImpl, public IPC::MessageReceiver { public: + using DecideDestinationCallback = CompletionHandler; template static Ref create(Args&&... args) { @@ -94,7 +96,11 @@ class DownloadProxy : public API::ObjectImpl, publi void setDestinationFilename(const String& d) { m_destinationFilename = d; } #if PLATFORM(COCOA) +#if HAVE(MODERN_DOWNLOADPROGRESS) + void publishProgress(const URL&, UseDownloadPlaceholder = UseDownloadPlaceholder::No); +#else void publishProgress(const URL&); +#endif void setProgress(NSProgress *progress) { m_progress = progress; } NSProgress *progress() const { return m_progress.get(); } #endif @@ -117,7 +123,7 @@ class DownloadProxy : public API::ObjectImpl, publi void didFinish(); void didFail(const WebCore::ResourceError&, std::span resumeData); void willSendRequest(WebCore::ResourceRequest&& redirectRequest, const WebCore::ResourceResponse& redirectResponse, CompletionHandler&&); - void decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse&, String&& suggestedFilename, CompletionHandler&&); + void decideDestinationWithSuggestedFilename(const WebCore::ResourceResponse&, String&& suggestedFilename, DecideDestinationCallback&&); private: explicit DownloadProxy(DownloadProxyMap&, WebsiteDataStore&, API::DownloadClient&, const WebCore::ResourceRequest&, const FrameInfoData&, WebPageProxy*); diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxy.messages.in b/Source/WebKit/UIProcess/Downloads/DownloadProxy.messages.in index 24c73deb132cb..1d49b7f54ebc2 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxy.messages.in +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxy.messages.in @@ -24,8 +24,7 @@ messages -> DownloadProxy { DidStart(WebCore::ResourceRequest request, String suggestedFilename) DidReceiveAuthenticationChallenge(WebCore::AuthenticationChallenge challenge, WebKit::AuthenticationChallengeIdentifier challengeID) WillSendRequest(WebCore::ResourceRequest redirectRequest, WebCore::ResourceResponse redirectResponse) -> (WebCore::ResourceRequest newRequest) - DecideDestinationWithSuggestedFilename(WebCore::ResourceResponse response, String suggestedFilename) -> (String filename, WebKit::SandboxExtensionHandle handle, enum:bool WebKit::AllowOverwrite allowOverwrite) - + DecideDestinationWithSuggestedFilename(WebCore::ResourceResponse response, String suggestedFilename) -> (String filename, WebKit::SandboxExtensionHandle handle, enum:bool WebKit::AllowOverwrite allowOverwrite, enum:bool WebKit::UseDownloadPlaceholder useDownloadPlaceholder) DidReceiveData(uint64_t bytesWritten, uint64_t totalBytesWritten, uint64_t totalBytesExpectedToWrite) DidCreateDestination(String path) DidFinish() diff --git a/Source/WebKit/UIProcess/Downloads/DownloadProxyCocoa.mm b/Source/WebKit/UIProcess/Downloads/DownloadProxyCocoa.mm index 10aa7e757c25c..51dab2f911c9d 100644 --- a/Source/WebKit/UIProcess/Downloads/DownloadProxyCocoa.mm +++ b/Source/WebKit/UIProcess/Downloads/DownloadProxyCocoa.mm @@ -34,24 +34,19 @@ namespace WebKit { +#if !HAVE(MODERN_DOWNLOADPROGRESS) void DownloadProxy::publishProgress(const URL& url) { if (!m_dataStore) return; -#if HAVE(MODERN_DOWNLOADPROGRESS) - RetainPtr localURL = adoptNS([[NSURL alloc] initFileURLWithPath:url.fileSystemPath() relativeToURL:nil]); - NSError *error = nil; - RetainPtr bookmark = [localURL bookmarkDataWithOptions:NSURLBookmarkCreationMinimalBookmark includingResourceValuesForKeys:nil relativeToURL:nil error:&error]; - m_dataStore->networkProcess().send(Messages::NetworkProcess::PublishDownloadProgress(m_downloadID, url, span(bookmark.get())), 0); -#else auto handle = SandboxExtension::createHandle(url.fileSystemPath(), SandboxExtension::Type::ReadWrite); ASSERT(handle); if (!handle) return; m_dataStore->networkProcess().send(Messages::NetworkProcess::PublishDownloadProgress(m_downloadID, url, WTFMove(*handle)), 0); + } #endif -} } diff --git a/Source/WebKit/UIProcess/Downloads/UseDownloadPlaceholder.h b/Source/WebKit/UIProcess/Downloads/UseDownloadPlaceholder.h new file mode 100644 index 0000000000000..7e094e6f8e410 --- /dev/null +++ b/Source/WebKit/UIProcess/Downloads/UseDownloadPlaceholder.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2024 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 + +namespace WebKit { + +enum class UseDownloadPlaceholder : bool { + No, + Yes, +}; + +} // namespace WebKit diff --git a/Source/WebKit/WebKit.xcodeproj/project.pbxproj b/Source/WebKit/WebKit.xcodeproj/project.pbxproj index 1c92e01f649d6..0135df0bdc786 100644 --- a/Source/WebKit/WebKit.xcodeproj/project.pbxproj +++ b/Source/WebKit/WebKit.xcodeproj/project.pbxproj @@ -2438,8 +2438,10 @@ E31869C42B1A7C2400571519 /* WKProcessExtension.mm in Sources */ = {isa = PBXBuildFile; fileRef = E31869C22B1A7C2400571519 /* WKProcessExtension.mm */; }; E31869C52B1A7C2400571519 /* WKProcessExtension.h in Headers */ = {isa = PBXBuildFile; fileRef = E31869C32B1A7C2400571519 /* WKProcessExtension.h */; }; E326E357284E580E00157372 /* AuxiliaryProcessProxyCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E326E356284E580E00157372 /* AuxiliaryProcessProxyCocoa.mm */; }; + E33E8FFD2C7FD2980002BEB3 /* UseDownloadPlaceholder.h in Headers */ = {isa = PBXBuildFile; fileRef = E33E8FFC2C7FD2980002BEB3 /* UseDownloadPlaceholder.h */; }; E34DAD392B753FA700FABEE2 /* ExtensionProcess.mm in Sources */ = {isa = PBXBuildFile; fileRef = E34DAD372B753FA700FABEE2 /* ExtensionProcess.mm */; }; E34DAD3A2B753FA700FABEE2 /* ExtensionProcess.h in Headers */ = {isa = PBXBuildFile; fileRef = E34DAD382B753FA700FABEE2 /* ExtensionProcess.h */; }; + E3607DEA2C7FE92200956766 /* WKDownloadDelegatePrivate.h in Headers */ = {isa = PBXBuildFile; fileRef = E3607DE92C7FE92200956766 /* WKDownloadDelegatePrivate.h */; settings = {ATTRIBUTES = (Private, ); }; }; E36A00E329CF7AC000AC4E8A /* TextTrackRepresentationCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = E36A00E229CF7AC000AC4E8A /* TextTrackRepresentationCocoa.mm */; }; E36FF00327F36FBD004BE21A /* SandboxStateVariables.h in Headers */ = {isa = PBXBuildFile; fileRef = E36FF00127F36FBD004BE21A /* SandboxStateVariables.h */; }; E3816B3D27E2463A005EAFC0 /* WebMockContentFilterManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E3816B3B27E24639005EAFC0 /* WebMockContentFilterManager.cpp */; }; @@ -7979,6 +7981,7 @@ E31869C22B1A7C2400571519 /* WKProcessExtension.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKProcessExtension.mm; sourceTree = ""; }; E31869C32B1A7C2400571519 /* WKProcessExtension.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WKProcessExtension.h; sourceTree = ""; }; E326E356284E580E00157372 /* AuxiliaryProcessProxyCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AuxiliaryProcessProxyCocoa.mm; sourceTree = ""; }; + E33E8FFC2C7FD2980002BEB3 /* UseDownloadPlaceholder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UseDownloadPlaceholder.h; sourceTree = ""; }; E3439B632345463A0011DE0B /* NetworkProcessConnectionInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = NetworkProcessConnectionInfo.h; path = Network/NetworkProcessConnectionInfo.h; sourceTree = ""; }; E34B110C27C46BC6006D2F2E /* libWebCoreTestShim.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libWebCoreTestShim.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; E34B110F27C46D09006D2F2E /* libWebCoreTestSupport.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libWebCoreTestSupport.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -7987,6 +7990,7 @@ E350A7C52934F1C100A06C29 /* common.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = common.sb; sourceTree = ""; }; E350A7C82934F75F00A06C29 /* common.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = common.sb; sourceTree = ""; }; E350A7DF29364D3800A06C29 /* util.sb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = util.sb; sourceTree = ""; }; + E3607DE92C7FE92200956766 /* WKDownloadDelegatePrivate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WKDownloadDelegatePrivate.h; sourceTree = ""; }; E36A00E129CF4EBA00AC4E8A /* TextTrackRepresentationCocoa.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TextTrackRepresentationCocoa.h; sourceTree = ""; }; E36A00E229CF7AC000AC4E8A /* TextTrackRepresentationCocoa.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = TextTrackRepresentationCocoa.mm; sourceTree = ""; }; E36D701A27B709ED006531B7 /* WebAttachmentElementClient.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAttachmentElementClient.h; sourceTree = ""; }; @@ -9493,6 +9497,7 @@ E382D57E2C21D500005F7653 /* DownloadProxyCocoa.mm */, 1AD25E93167AB08100EA9BCD /* DownloadProxyMap.cpp */, 1AD25E94167AB08100EA9BCD /* DownloadProxyMap.h */, + E33E8FFC2C7FD2980002BEB3 /* UseDownloadPlaceholder.h */, ); path = Downloads; sourceTree = ""; @@ -11585,6 +11590,7 @@ DF0C5F24252ECB8D00D921DB /* WKDownload.h */, DF0C5F23252ECB8D00D921DB /* WKDownload.mm */, DF0C5F26252ECB8E00D921DB /* WKDownloadDelegate.h */, + E3607DE92C7FE92200956766 /* WKDownloadDelegatePrivate.h */, DF0C5F25252ECB8E00D921DB /* WKDownloadInternal.h */, 1AF4592D19464B2000F9D4A2 /* WKError.h */, 1AF4592C19464B2000F9D4A2 /* WKError.mm */, @@ -16820,6 +16826,7 @@ 93D1EEF529669D74009B31D6 /* UnifiedOriginStorageLevel.h in Headers */, 1A64245E12DE29A100CAAE2C /* UpdateInfo.h in Headers */, 5C19A5201FD0B29500EEA323 /* URLSchemeTaskParameters.h in Headers */, + E33E8FFD2C7FD2980002BEB3 /* UseDownloadPlaceholder.h in Headers */, 1AC1336818565B5700F3EC05 /* UserData.h in Headers */, CD491B081E70D05F00009066 /* UserMediaCaptureManager.h in Headers */, CD491B0E1E732E4D00009066 /* UserMediaCaptureManagerMessages.h in Headers */, @@ -17327,6 +17334,7 @@ DF0C5F28252ECB8E00D921DB /* WKDownload.h in Headers */, 5C2EBDFA2564380B00D55B05 /* WKDownloadClient.h in Headers */, DF0C5F2A252ECB8E00D921DB /* WKDownloadDelegate.h in Headers */, + E3607DEA2C7FE92200956766 /* WKDownloadDelegatePrivate.h in Headers */, DF0C5F2B252ED44000D921DB /* WKDownloadInternal.h in Headers */, 637281A221ADC744009E0DE6 /* WKDownloadProgress.h in Headers */, 1AB7D78D1288CD9A00CFD08C /* WKDownloadRef.h in Headers */, diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm index aa4c63ac0a6e9..84f4f8a0692e2 100644 --- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm +++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/Download.mm @@ -2247,6 +2247,39 @@ HTTPServer server({ }); } +#if HAVE(MODERN_DOWNLOADPROGRESS) +TEST(WKDownload, DecidePlaceholderPolicy) +{ + HTTPServer server({ + { "/"_s, { 404, { }, "http body"_s } } + }); + NSURL *expectedDownloadFile = tempFileThatDoesNotExist(); + auto delegate = adoptNS([TestDownloadDelegate new]); + auto webView = adoptNS([WKWebView new]); + [webView setNavigationDelegate:delegate.get()]; + + __block bool didFinish = false; + [webView startDownloadUsingRequest:server.request() completionHandler:^(WKDownload *download) { + download.delegate = delegate.get(); + delegate.get().decideDestinationUsingResponse = ^(WKDownload *, NSURLResponse *, NSString *, void (^completionHandler)(NSURL *)) { + completionHandler(expectedDownloadFile); + }; + delegate.get().downloadDidFinish = ^(WKDownload *download) { + didFinish = true; + }; + }]; + Util::run(&didFinish); + + checkFileContents(expectedDownloadFile, "http body"_s); + + checkCallbackRecord(delegate.get(), { + DownloadCallback::DecideDestination, + DownloadCallback::DecidePlaceholderPolicy, + DownloadCallback::DidFinish, + }); +} +#endif + TEST(WKDownload, NetworkProcessCrash) { auto server = downloadTestServer(); diff --git a/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h b/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h index 37a6598589fd1..184f5f700523d 100644 --- a/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h +++ b/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.h @@ -23,12 +23,16 @@ * THE POSSIBILITY OF SUCH DAMAGE. */ +#import #import enum class DownloadCallback : uint8_t { WillRedirect, AuthenticationChallenge, DecideDestination, +#if HAVE(MODERN_DOWNLOADPROGRESS) + DecidePlaceholderPolicy, +#endif DidFinish, DidFailWithError, NavigationActionBecameDownload, @@ -37,7 +41,7 @@ enum class DownloadCallback : uint8_t { NavigationResponse, }; -@interface TestDownloadDelegate : NSObject +@interface TestDownloadDelegate : NSObject @property (nonatomic, copy) void (^willPerformHTTPRedirection)(WKDownload *, NSHTTPURLResponse *, NSURLRequest *, void (^)(WKDownloadRedirectPolicy)); @property (nonatomic, copy) void (^didReceiveAuthenticationChallenge)(WKDownload *, NSURLAuthenticationChallenge *, void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential*)); diff --git a/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm b/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm index 783cae42321f4..6b75be5b1ec03 100644 --- a/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm +++ b/Tools/TestWebKitAPI/cocoa/TestDownloadDelegate.mm @@ -58,6 +58,14 @@ - (void)download:(WKDownload *)download didReceiveAuthenticationChallenge:(NSURL completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); } +#if HAVE(MODERN_DOWNLOADPROGRESS) +- (void)_download:(WKDownload *)download decidePlaceholderPolicy:(void (^)(_WKPlaceholderPolicy))completionHandler +{ + _callbackRecord.append(DownloadCallback::DecidePlaceholderPolicy); + completionHandler(_WKPlaceholderPolicyDisable); +} +#endif + - (void)downloadDidFinish:(WKDownload *)download { _callbackRecord.append(DownloadCallback::DidFinish);