| /* |
| * Copyright (C) 2014-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 "DownloadClient.h" |
| |
| #import "AuthenticationChallengeDisposition.h" |
| #import "AuthenticationChallengeProxy.h" |
| #import "AuthenticationDecisionListener.h" |
| #import "CompletionHandlerCallChecker.h" |
| #import "DownloadProxy.h" |
| #import "Logging.h" |
| #import "SystemPreviewController.h" |
| #import "WKNSURLAuthenticationChallenge.h" |
| #import "WKNSURLExtras.h" |
| #import "WebCredential.h" |
| #import "WebPageProxy.h" |
| #import "WebProcessProxy.h" |
| #import "_WKDownloadDelegate.h" |
| #import "_WKDownloadInternal.h" |
| #import <WebCore/ResourceError.h> |
| #import <WebCore/ResourceResponse.h> |
| #import <wtf/BlockPtr.h> |
| #import <wtf/FileSystem.h> |
| |
| namespace WebKit { |
| |
| DownloadClient::DownloadClient(id <_WKDownloadDelegate> delegate) |
| : m_delegate(delegate) |
| { |
| m_delegateMethods.downloadDidStart = [delegate respondsToSelector:@selector(_downloadDidStart:)]; |
| m_delegateMethods.downloadDidReceiveResponse = [delegate respondsToSelector:@selector(_download:didReceiveResponse:)]; |
| m_delegateMethods.downloadDidReceiveData = [delegate respondsToSelector:@selector(_download:didReceiveData:)]; |
| m_delegateMethods.downloadDecideDestinationWithSuggestedFilenameAllowOverwrite = [delegate respondsToSelector:@selector(_download:decideDestinationWithSuggestedFilename:allowOverwrite:)]; |
| m_delegateMethods.downloadDecideDestinationWithSuggestedFilenameCompletionHandler = [delegate respondsToSelector:@selector(_download:decideDestinationWithSuggestedFilename:completionHandler:)]; |
| m_delegateMethods.downloadDidFinish = [delegate respondsToSelector:@selector(_downloadDidFinish:)]; |
| m_delegateMethods.downloadDidFail = [delegate respondsToSelector:@selector(_download:didFailWithError:)]; |
| m_delegateMethods.downloadDidCancel = [delegate respondsToSelector:@selector(_downloadDidCancel:)]; |
| m_delegateMethods.downloadDidReceiveServerRedirectToURL = [delegate respondsToSelector:@selector(_download:didReceiveServerRedirectToURL:)]; |
| m_delegateMethods.downloadDidReceiveAuthenticationChallengeCompletionHandler = [delegate respondsToSelector:@selector(_download:didReceiveAuthenticationChallenge:completionHandler:)]; |
| m_delegateMethods.downloadShouldDecodeSourceDataOfMIMEType = [delegate respondsToSelector:@selector(_download:shouldDecodeSourceDataOfMIMEType:)]; |
| m_delegateMethods.downloadDidCreateDestination = [delegate respondsToSelector:@selector(_download:didCreateDestination:)]; |
| m_delegateMethods.downloadProcessDidCrash = [delegate respondsToSelector:@selector(_downloadProcessDidCrash:)]; |
| } |
| |
| void DownloadClient::didStart(DownloadProxy& downloadProxy) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| if (auto* webPage = downloadProxy.originatingPage()) { |
| // FIXME: Update the MIME-type once it is known in the ResourceResponse. |
| webPage->systemPreviewController()->start(URL(URL(), webPage->currentURL()), "application/octet-stream"_s, downloadProxy.systemPreviewDownloadInfo()); |
| } |
| takeActivityToken(downloadProxy); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidStart) |
| [m_delegate _downloadDidStart:wrapper(downloadProxy)]; |
| } |
| |
| void DownloadClient::didReceiveResponse(DownloadProxy& downloadProxy, const WebCore::ResourceResponse& response) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload() && response.isSuccessful()) { |
| downloadProxy.setExpectedContentLength(response.expectedContentLength()); |
| downloadProxy.setBytesLoaded(0); |
| if (auto* webPage = downloadProxy.originatingPage()) |
| webPage->systemPreviewController()->updateProgress(0); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidReceiveResponse) |
| [m_delegate _download:wrapper(downloadProxy) didReceiveResponse:response.nsURLResponse()]; |
| } |
| |
| void DownloadClient::didReceiveData(DownloadProxy& downloadProxy, uint64_t length) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| downloadProxy.setBytesLoaded(downloadProxy.bytesLoaded() + length); |
| if (auto* webPage = downloadProxy.originatingPage()) |
| webPage->systemPreviewController()->updateProgress(static_cast<float>(downloadProxy.bytesLoaded()) / downloadProxy.expectedContentLength()); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidReceiveData) |
| [m_delegate _download:wrapper(downloadProxy) didReceiveData:length]; |
| } |
| |
| void DownloadClient::didReceiveAuthenticationChallenge(DownloadProxy& downloadProxy, AuthenticationChallengeProxy& authenticationChallenge) |
| { |
| // FIXME: System Preview needs code here. |
| if (!m_delegateMethods.downloadDidReceiveAuthenticationChallengeCompletionHandler) { |
| authenticationChallenge.listener().completeChallenge(WebKit::AuthenticationChallengeDisposition::PerformDefaultHandling); |
| return; |
| } |
| |
| [m_delegate _download:wrapper(downloadProxy) didReceiveAuthenticationChallenge:wrapper(authenticationChallenge) completionHandler:makeBlockPtr([authenticationChallenge = makeRef(authenticationChallenge), checker = CompletionHandlerCallChecker::create(m_delegate.get().get(), @selector(_download:didReceiveAuthenticationChallenge:completionHandler:))] (NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential) { |
| if (checker->completionHandlerHasBeenCalled()) |
| return; |
| checker->didCallCompletionHandler(); |
| switch (disposition) { |
| case NSURLSessionAuthChallengeUseCredential: |
| authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::UseCredential, credential ? WebCore::Credential(credential) : WebCore::Credential()); |
| break; |
| case NSURLSessionAuthChallengePerformDefaultHandling: |
| authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::PerformDefaultHandling); |
| break; |
| |
| case NSURLSessionAuthChallengeCancelAuthenticationChallenge: |
| authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::Cancel); |
| break; |
| |
| case NSURLSessionAuthChallengeRejectProtectionSpace: |
| authenticationChallenge->listener().completeChallenge(AuthenticationChallengeDisposition::RejectProtectionSpaceAndContinue); |
| break; |
| |
| default: |
| [NSException raise:NSInvalidArgumentException format:@"Invalid NSURLSessionAuthChallengeDisposition (%ld)", (long)disposition]; |
| } |
| }).get()]; |
| } |
| |
| void DownloadClient::didCreateDestination(DownloadProxy& downloadProxy, const String& destination) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| downloadProxy.setDestinationFilename(destination); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidCreateDestination) |
| [m_delegate _download:wrapper(downloadProxy) didCreateDestination:destination]; |
| } |
| |
| void DownloadClient::processDidCrash(DownloadProxy& downloadProxy) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| if (auto* webPage = downloadProxy.originatingPage()) |
| webPage->systemPreviewController()->cancel(); |
| releaseActivityTokenIfNecessary(downloadProxy); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadProcessDidCrash) |
| [m_delegate _downloadProcessDidCrash:wrapper(downloadProxy)]; |
| } |
| |
| void DownloadClient::decideDestinationWithSuggestedFilename(DownloadProxy& downloadProxy, const String& filename, Function<void(AllowOverwrite, String)>&& completionHandler) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| NSString *temporaryDirectory = FileSystem::createTemporaryDirectory(@"SystemPreviews"); |
| NSString *destination = [temporaryDirectory stringByAppendingPathComponent:filename]; |
| completionHandler(AllowOverwrite::Yes, destination); |
| return; |
| } |
| #endif |
| |
| if (!m_delegateMethods.downloadDecideDestinationWithSuggestedFilenameAllowOverwrite && !m_delegateMethods.downloadDecideDestinationWithSuggestedFilenameCompletionHandler) |
| return completionHandler(AllowOverwrite::No, { }); |
| |
| if (m_delegateMethods.downloadDecideDestinationWithSuggestedFilenameAllowOverwrite) { |
| BOOL allowOverwrite = NO; |
| ALLOW_DEPRECATED_DECLARATIONS_BEGIN |
| NSString *destination = [m_delegate _download:wrapper(downloadProxy) decideDestinationWithSuggestedFilename:filename allowOverwrite:&allowOverwrite]; |
| ALLOW_DEPRECATED_DECLARATIONS_END |
| completionHandler(allowOverwrite ? AllowOverwrite::Yes : AllowOverwrite::No, destination); |
| } else { |
| [m_delegate _download:wrapper(downloadProxy) decideDestinationWithSuggestedFilename:filename completionHandler:makeBlockPtr([checker = CompletionHandlerCallChecker::create(m_delegate.get().get(), @selector(_download:decideDestinationWithSuggestedFilename:completionHandler:)), completionHandler = WTFMove(completionHandler)] (BOOL allowOverwrite, NSString *destination) { |
| if (checker->completionHandlerHasBeenCalled()) |
| return; |
| checker->didCallCompletionHandler(); |
| completionHandler(allowOverwrite ? AllowOverwrite::Yes : AllowOverwrite::No, destination); |
| }).get()]; |
| } |
| } |
| |
| void DownloadClient::didFinish(DownloadProxy& downloadProxy) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| if (auto* webPage = downloadProxy.originatingPage()) { |
| WTF::URL destinationURL = WTF::URL::fileURLWithFileSystemPath(downloadProxy.destinationFilename()); |
| if (!destinationURL.fragmentIdentifier().length()) |
| destinationURL.setFragmentIdentifier(downloadProxy.request().url().fragmentIdentifier()); |
| webPage->systemPreviewController()->finish(destinationURL); |
| } |
| releaseActivityTokenIfNecessary(downloadProxy); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidFinish) |
| [m_delegate _downloadDidFinish:wrapper(downloadProxy)]; |
| } |
| |
| void DownloadClient::didFail(DownloadProxy& downloadProxy, const WebCore::ResourceError& error) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| if (auto* webPage = downloadProxy.originatingPage()) |
| webPage->systemPreviewController()->fail(error); |
| releaseActivityTokenIfNecessary(downloadProxy); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidFail) |
| [m_delegate _download:wrapper(downloadProxy) didFailWithError:error.nsError()]; |
| } |
| |
| void DownloadClient::didCancel(DownloadProxy& downloadProxy) |
| { |
| #if USE(SYSTEM_PREVIEW) |
| if (downloadProxy.isSystemPreviewDownload()) { |
| if (auto* webPage = downloadProxy.originatingPage()) |
| webPage->systemPreviewController()->cancel(); |
| releaseActivityTokenIfNecessary(downloadProxy); |
| return; |
| } |
| #endif |
| |
| if (m_delegateMethods.downloadDidCancel) |
| [m_delegate _downloadDidCancel:wrapper(downloadProxy)]; |
| } |
| |
| void DownloadClient::willSendRequest(DownloadProxy& downloadProxy, WebCore::ResourceRequest&& request, const WebCore::ResourceResponse&, CompletionHandler<void(WebCore::ResourceRequest&&)>&& completionHandler) |
| { |
| if (m_delegateMethods.downloadDidReceiveServerRedirectToURL) |
| [m_delegate _download:wrapper(downloadProxy) didReceiveServerRedirectToURL:[NSURL _web_URLWithWTFString:request.url().string()]]; |
| |
| completionHandler(WTFMove(request)); |
| } |
| |
| #if USE(SYSTEM_PREVIEW) |
| void DownloadClient::takeActivityToken(DownloadProxy& downloadProxy) |
| { |
| #if PLATFORM(IOS_FAMILY) |
| if (auto* webPage = downloadProxy.originatingPage()) { |
| RELEASE_LOG_IF(webPage->isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p - UIProcess is taking a background assertion because it is downloading a system preview", this); |
| ASSERT(!m_activity); |
| m_activity = webPage->process().throttler().backgroundActivity("System preview download"_s).moveToUniquePtr(); |
| } |
| #else |
| UNUSED_PARAM(downloadProxy); |
| #endif |
| } |
| |
| void DownloadClient::releaseActivityTokenIfNecessary(DownloadProxy& downloadProxy) |
| { |
| #if PLATFORM(IOS_FAMILY) |
| if (m_activity) { |
| RELEASE_LOG_IF(downloadProxy.originatingPage()->isAlwaysOnLoggingAllowed(), ProcessSuspension, "%p UIProcess is releasing a background assertion because a system preview download completed", this); |
| m_activity = nullptr; |
| } |
| #else |
| UNUSED_PARAM(downloadProxy); |
| #endif |
| } |
| #endif |
| |
| } // namespace WebKit |