Commit 255540f5 authored by Hiroshi Ichikawa's avatar Hiroshi Ichikawa Committed by Commit Bot

Implement download API.

Bug: 884055
Change-Id: Ib5a93bf85d877dfdaef7491a99295be305107398
Reviewed-on: https://chromium-review.googlesource.com/c/1318514
Commit-Queue: Hiroshi Ichikawa <ichikawa@chromium.org>
Reviewed-by: default avatarEugene But <eugenebut@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605905}
parent 24ffcf2e
......@@ -65,6 +65,7 @@ ios_web_view_public_headers = [
"public/cwv_user_script.h",
"public/cwv_web_view.h",
"public/cwv_web_view_configuration.h",
"public/cwv_download_task.h",
]
if (ios_web_view_enable_sync) {
ios_web_view_public_headers += [
......@@ -215,6 +216,10 @@ ios_web_view_sources = [
"internal/web_view_web_state_policy_decider.mm",
"internal/webdata_services/web_view_web_data_service_wrapper_factory.cc",
"internal/webdata_services/web_view_web_data_service_wrapper_factory.h",
"internal/web_view_download_manager.h",
"internal/web_view_download_manager.mm",
"internal/cwv_download_task_internal.h",
"internal/cwv_download_task.mm",
]
ios_web_view_sources += ios_web_view_public_headers
if (ios_web_view_enable_sync) {
......@@ -382,6 +387,7 @@ test("ios_web_view_unittests") {
"internal/autofill/cwv_autofill_suggestion_unittest.mm",
"internal/autofill/cwv_credit_card_unittest.mm",
"internal/autofill/cwv_credit_card_verifier_unittest.mm",
"internal/cwv_download_task_unittest.mm",
"internal/cwv_favicon_unittest.mm",
"internal/cwv_html_element_unittest.mm",
"internal/cwv_preferences_unittest.mm",
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web_view/internal/cwv_download_task_internal.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/sys_string_conversions.h"
#include "base/task/post_task.h"
#include "base/task/task_traits.h"
#import "ios/web/public/download/download_task.h"
#include "ios/web/public/download/download_task_observer.h"
#include "ios/web_view/internal/cwv_web_view_internal.h"
#include "net/base/mac/url_conversions.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_fetcher_response_writer.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
int64_t const CWVDownloadSizeUnknown = -1;
NSErrorDomain const CWVDownloadErrorDomain =
@"org.chromium.chromewebview.DownloadErrorDomain";
NSInteger const CWVDownloadErrorUnknown = -100;
@interface CWVDownloadTask ()
// Called when the download task has started, downloaded a chunk of data or
// the download has been completed.
- (void)downloadWasUpdated;
@end
namespace {
// Bridges C++ observer method calls to Objective-C.
class DownloadTaskObserverBridge : public web::DownloadTaskObserver {
public:
explicit DownloadTaskObserverBridge(CWVDownloadTask* task) : task_(task) {}
void OnDownloadUpdated(web::DownloadTask* task) override {
[task_ downloadWasUpdated];
}
private:
__weak CWVDownloadTask* task_ = nil;
};
} // namespace
@implementation CWVDownloadTask {
std::unique_ptr<DownloadTaskObserverBridge> _observerBridge;
std::unique_ptr<web::DownloadTask> _internalTask;
}
@synthesize delegate = _delegate;
- (NSString*)suggestedFileName {
return base::SysUTF16ToNSString(_internalTask->GetSuggestedFilename());
}
- (NSString*)MIMEType {
return base::SysUTF8ToNSString(_internalTask->GetMimeType());
}
- (NSURL*)originalURL {
return net::NSURLWithGURL(_internalTask->GetOriginalUrl());
}
- (int64_t)totalBytes {
return _internalTask->GetTotalBytes();
}
- (int64_t)receivedBytes {
return _internalTask->GetReceivedBytes();
}
- (double)progress {
int percent = _internalTask->GetPercentComplete();
// percent == -1 means unknown.
return percent == -1 ? NAN : percent / 100.0;
}
- (instancetype)initWithInternalTask:
(std::unique_ptr<web::DownloadTask>)internalTask {
self = [super init];
if (self) {
_observerBridge = std::make_unique<DownloadTaskObserverBridge>(self);
_internalTask = std::move(internalTask);
_internalTask->AddObserver(_observerBridge.get());
}
return self;
}
- (void)dealloc {
_internalTask->RemoveObserver(_observerBridge.get());
}
- (void)startDownloadToLocalFileAtPath:(NSString*)path {
scoped_refptr<base::SequencedTaskRunner> taskRunner =
base::CreateSequencedTaskRunnerWithTraits(
{base::MayBlock(), base::TaskPriority::BEST_EFFORT});
__block auto writer = std::make_unique<net::URLFetcherFileWriter>(
taskRunner, base::FilePath(base::SysNSStringToUTF8(path)));
__weak CWVDownloadTask* weakSelf = self;
int errorCode = writer->Initialize(base::BindOnce(^(int blockErrorCode) {
[weakSelf startTaskWithWriter:std::move(writer) errorCode:blockErrorCode];
}));
// When |errorCode| is net::ERR_IO_PENDING, the callback above will be run
// later with the result.
if (errorCode != net::ERR_IO_PENDING) {
[self startTaskWithWriter:std::move(writer) errorCode:errorCode];
}
}
- (void)cancel {
_internalTask->Cancel();
}
#pragma mark - Private
- (void)startTaskWithWriter:(std::unique_ptr<net::URLFetcherFileWriter>)writer
errorCode:(int)errorCode {
if (errorCode == net::OK) {
_internalTask->Start(std::move(writer));
} else {
[self notifyFinishWithErrorCode:errorCode];
}
}
- (void)downloadWasUpdated {
if (_internalTask->IsDone()) {
int errorCode = _internalTask->GetErrorCode();
if (errorCode == net::OK) {
// The writer deletes the file on its destructor by default. This prevents
// the deletion.
_internalTask->GetResponseWriter()->AsFileWriter()->DisownFile();
}
[self notifyFinishWithErrorCode:errorCode];
} else {
if ([_delegate
respondsToSelector:@selector(downloadTaskProgressDidChange:)]) {
[_delegate downloadTaskProgressDidChange:self];
}
}
}
- (void)notifyFinishWithErrorCode:(int)errorCode {
NSError* error = nil;
if (errorCode != net::OK) {
NSString* errorDescription =
base::SysUTF8ToNSString(net::ErrorToShortString(errorCode));
// Always use CWVDownloadErrorUnknown so far because a detailed error code
// is likely not very useful. Text representation of the error is still
// available via error.localizedDescription.
error = [NSError
errorWithDomain:CWVDownloadErrorDomain
code:CWVDownloadErrorUnknown
userInfo:@{NSLocalizedDescriptionKey : errorDescription}];
}
if ([_delegate
respondsToSelector:@selector(downloadTask:didFinishWithError:)]) {
[_delegate downloadTask:self didFinishWithError:error];
}
}
@end
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_INTERNAL_CWV_DOWNLOAD_TASK_INTERNAL_H_
#define IOS_WEB_VIEW_INTERNAL_CWV_DOWNLOAD_TASK_INTERNAL_H_
#include <memory>
#import "ios/web_view/public/cwv_download_task.h"
NS_ASSUME_NONNULL_BEGIN
namespace web {
class DownloadTask;
}
@interface CWVDownloadTask ()
- (instancetype)initWithInternalTask:
(std::unique_ptr<web::DownloadTask>)internalTask;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_INTERNAL_CWV_DOWNLOAD_TASK_INTERNAL_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "ios/web_view/internal/cwv_download_task_internal.h"
#import <Foundation/Foundation.h>
#include "base/files/file_path.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#import "base/test/ios/wait_util.h"
#include "base/test/scoped_task_environment.h"
#import "ios/web/public/test/fakes/fake_download_task.h"
#include "net/base/net_errors.h"
#include "net/url_request/url_fetcher_response_writer.h"
#include "testing/gtest/include/gtest/gtest.h"
#import "testing/gtest_mac.h"
#include "testing/platform_test.h"
#import "third_party/ocmock/OCMock/OCMock.h"
#import "third_party/ocmock/gtest_support.h"
using base::test::ios::WaitUntilConditionOrTimeout;
using base::test::ios::kWaitForFileOperationTimeout;
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ios_web_view {
class CWVDownloadTaskTest : public PlatformTest {
public:
CWVDownloadTaskTest()
: valid_local_file_path_(testing::TempDir() + "/foo.txt") {
auto task_ptr = std::make_unique<web::FakeDownloadTask>(
GURL("http://example.com/foo.txt"), "text/plain");
fake_internal_task_ = task_ptr.get();
cwv_task_ =
[[CWVDownloadTask alloc] initWithInternalTask:std::move(task_ptr)];
mock_delegate_ = OCMProtocolMock(@protocol(CWVDownloadTaskDelegate));
cwv_task_.delegate = mock_delegate_;
}
protected:
std::string valid_local_file_path_;
base::test::ScopedTaskEnvironment scoped_task_environment_;
web::FakeDownloadTask* fake_internal_task_ = nullptr;
id<CWVDownloadTaskDelegate> mock_delegate_ = nil;
CWVDownloadTask* cwv_task_ = nil;
// Waits until fake_internal_task_->Start() is called.
bool WaitUntilTaskStarts() WARN_UNUSED_RESULT {
return WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
scoped_task_environment_.RunUntilIdle();
return fake_internal_task_->GetState() ==
web::DownloadTask::State::kInProgress;
});
}
// Finishes the response writer and waits for its completion.
bool FinishResponseWriter() WARN_UNUSED_RESULT {
__block int error_code = net::ERR_IO_PENDING;
error_code = fake_internal_task_->GetResponseWriter()->Finish(
net::OK, base::BindOnce(^(int block_error_code) {
error_code = block_error_code;
}));
return WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
scoped_task_environment_.RunUntilIdle();
return error_code == net::OK;
});
}
};
// Tests a flow where the download starts and finishes successfully.
TEST_F(CWVDownloadTaskTest, SuccessfulFlow) {
OCMExpect([mock_delegate_ downloadTaskProgressDidChange:cwv_task_]);
[cwv_task_ startDownloadToLocalFileAtPath:base::SysUTF8ToNSString(
valid_local_file_path_)];
ASSERT_TRUE(WaitUntilTaskStarts());
EXPECT_OCMOCK_VERIFY((id)mock_delegate_);
EXPECT_EQ(
base::FilePath(valid_local_file_path_),
fake_internal_task_->GetResponseWriter()->AsFileWriter()->file_path());
OCMExpect([mock_delegate_ downloadTaskProgressDidChange:cwv_task_]);
fake_internal_task_->SetPercentComplete(50);
EXPECT_OCMOCK_VERIFY((id)mock_delegate_);
OCMExpect([mock_delegate_ downloadTask:cwv_task_ didFinishWithError:nil]);
ASSERT_TRUE(FinishResponseWriter());
fake_internal_task_->SetDone(true);
EXPECT_OCMOCK_VERIFY((id)mock_delegate_);
}
// Tests a flow where the download finishes with an error.
TEST_F(CWVDownloadTaskTest, FailedFlow) {
[cwv_task_ startDownloadToLocalFileAtPath:base::SysUTF8ToNSString(
valid_local_file_path_)];
ASSERT_TRUE(WaitUntilTaskStarts());
OCMExpect([mock_delegate_ downloadTask:cwv_task_
didFinishWithError:[OCMArg isNotNil]]);
ASSERT_TRUE(FinishResponseWriter());
fake_internal_task_->SetErrorCode(net::ERR_FAILED);
fake_internal_task_->SetDone(true);
EXPECT_OCMOCK_VERIFY((id)mock_delegate_);
}
// Tests a flow where the download is cancelled.
TEST_F(CWVDownloadTaskTest, CancelledFlow) {
[cwv_task_ startDownloadToLocalFileAtPath:base::SysUTF8ToNSString(
valid_local_file_path_)];
ASSERT_TRUE(WaitUntilTaskStarts());
ASSERT_TRUE(FinishResponseWriter());
[cwv_task_ cancel];
EXPECT_EQ(web::DownloadTask::State::kCancelled,
fake_internal_task_->GetState());
}
// Tests a case when it fails to write to the specified local file path.
TEST_F(CWVDownloadTaskTest, WriteFailure) {
__block bool did_finish_called = false;
OCMStub([mock_delegate_ downloadTask:cwv_task_
didFinishWithError:[OCMArg isNotNil]])
.andDo(^(NSInvocation*) {
did_finish_called = true;
});
NSString* path =
base::SysUTF8ToNSString(testing::TempDir() + "/non_existent_dir/foo.txt");
[cwv_task_ startDownloadToLocalFileAtPath:path];
EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^{
scoped_task_environment_.RunUntilIdle();
return did_finish_called;
}));
}
// Tests properties of CWVDownloadTask.
TEST_F(CWVDownloadTaskTest, Properties) {
// Specified in the constructor.
EXPECT_NSEQ([NSURL URLWithString:@"http://example.com/foo.txt"],
cwv_task_.originalURL);
EXPECT_NSEQ(@"text/plain", cwv_task_.MIMEType);
fake_internal_task_->SetSuggestedFilename(base::UTF8ToUTF16("foo.txt"));
EXPECT_NSEQ(@"foo.txt", cwv_task_.suggestedFileName);
fake_internal_task_->SetTotalBytes(1024);
EXPECT_EQ(1024, cwv_task_.totalBytes);
fake_internal_task_->SetTotalBytes(-1); // Unknown
EXPECT_EQ(CWVDownloadSizeUnknown, cwv_task_.totalBytes);
fake_internal_task_->SetReceivedBytes(512);
EXPECT_EQ(512, cwv_task_.receivedBytes);
fake_internal_task_->SetPercentComplete(50);
EXPECT_FLOAT_EQ(0.5, cwv_task_.progress);
fake_internal_task_->SetPercentComplete(-1); // Unknown
EXPECT_TRUE(isnan(cwv_task_.progress));
}
} // namespace ios_web_view
......@@ -20,6 +20,7 @@ class PrefRegistrySyncable;
namespace ios_web_view {
class WebViewURLRequestContextGetter;
class WebViewDownloadManager;
// WebView implementation of BrowserState. Can only be used only on the UI
// thread.
......@@ -65,6 +66,9 @@ class WebViewBrowserState : public web::BrowserState {
// The recording browser state associated with this browser state.
WebViewBrowserState* recording_browser_state_;
// Handles browser downloads.
std::unique_ptr<WebViewDownloadManager> download_manager_;
DISALLOW_COPY_AND_ASSIGN(WebViewBrowserState);
};
......
......@@ -52,6 +52,7 @@
#import "ios/web_view/internal/sync/web_view_profile_sync_service_factory.h"
#include "ios/web_view/internal/translate/web_view_translate_accept_languages_factory.h"
#include "ios/web_view/internal/translate/web_view_translate_ranker_factory.h"
#include "ios/web_view/internal/web_view_download_manager.h"
#include "ios/web_view/internal/web_view_url_request_context_getter.h"
#include "ios/web_view/internal/webdata_services/web_view_web_data_service_wrapper_factory.h"
#include "ui/base/l10n/l10n_util_mac.h"
......@@ -69,7 +70,9 @@ namespace ios_web_view {
WebViewBrowserState::WebViewBrowserState(
bool off_the_record,
WebViewBrowserState* recording_browser_state /* = nullptr */)
: web::BrowserState(), off_the_record_(off_the_record) {
: web::BrowserState(),
off_the_record_(off_the_record),
download_manager_(std::make_unique<WebViewDownloadManager>(this)) {
// A recording browser state must not be associated with another recording
// browser state. An off the record browser state must be associated with
// a recording browser state.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_INTERNAL_WEB_VIEW_DOWNLOAD_MANAGER_H_
#define IOS_WEB_VIEW_INTERNAL_WEB_VIEW_DOWNLOAD_MANAGER_H_
#include <memory>
#include "ios/web/public/download/download_controller_delegate.h"
namespace web {
class BrowserState;
class DownloadTask;
class WebState;
} // namespace web
namespace ios_web_view {
// A class to handle browser downloads in //ios/web_view.
class WebViewDownloadManager : public web::DownloadControllerDelegate {
public:
explicit WebViewDownloadManager(web::BrowserState* browser_state);
~WebViewDownloadManager() override;
void OnDownloadCreated(web::DownloadController* download_controller,
web::WebState* web_state,
std::unique_ptr<web::DownloadTask> task) override;
void OnDownloadControllerDestroyed(
web::DownloadController* download_controller) override;
private:
web::BrowserState* browser_state_ = nullptr;
web::DownloadController* download_controller_ = nullptr;
};
} // namespace ios_web_view
#endif // IOS_WEB_VIEW_INTERNAL_WEB_VIEW_DOWNLOAD_MANAGER_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ios/web_view/internal/web_view_download_manager.h"
#include "ios/web/public/download/download_controller.h"
#import "ios/web/public/download/download_task.h"
#import "ios/web_view/internal/cwv_download_task_internal.h"
#import "ios/web_view/internal/cwv_web_view_internal.h"
#import "ios/web_view/public/cwv_navigation_delegate.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace ios_web_view {
WebViewDownloadManager::WebViewDownloadManager(web::BrowserState* browser_state)
: browser_state_(browser_state),
download_controller_(
web::DownloadController::FromBrowserState(browser_state_)) {
download_controller_->SetDelegate(this);
}
WebViewDownloadManager::~WebViewDownloadManager() {
if (download_controller_) {
download_controller_->SetDelegate(nullptr);
}
}
void WebViewDownloadManager::OnDownloadCreated(
web::DownloadController*,
web::WebState* web_state,
std::unique_ptr<web::DownloadTask> task) {
CWVWebView* web_view = [CWVWebView webViewForWebState:web_state];
if ([web_view.navigationDelegate
respondsToSelector:@selector(webView:didRequestDownloadWithTask:)]) {
CWVDownloadTask* cwv_task =
[[CWVDownloadTask alloc] initWithInternalTask:std::move(task)];
[web_view.navigationDelegate webView:web_view
didRequestDownloadWithTask:cwv_task];
}
}
void WebViewDownloadManager::OnDownloadControllerDestroyed(
web::DownloadController*) {
download_controller_->SetDelegate(nullptr);
download_controller_ = nullptr;
}
} // namespace ios_web_view
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef IOS_WEB_VIEW_PUBLIC_CWV_DOWNLOAD_TASK_H_
#define IOS_WEB_VIEW_PUBLIC_CWV_DOWNLOAD_TASK_H_
#import <Foundation/Foundation.h>
#import "cwv_export.h"
NS_ASSUME_NONNULL_BEGIN
@protocol CWVDownloadTaskDelegate;
// Indicates that a file size is unknown.
FOUNDATION_EXPORT CWV_EXPORT int64_t const CWVDownloadSizeUnknown;
// The error domain for download errors.
FOUNDATION_EXPORT CWV_EXPORT NSErrorDomain const CWVDownloadErrorDomain;
// An error code for CWVDownloadErrorDomain which doesn't indicate the cause.
FOUNDATION_EXPORT CWV_EXPORT NSInteger const CWVDownloadErrorUnknown;
// Represents a single browser download task.
CWV_EXPORT
@interface CWVDownloadTask : NSObject
// Suggested name for the downloaded file including an extension.
@property(nonatomic, readonly) NSString* suggestedFileName;
// Effective MIME type of downloaded content.
@property(nonatomic, readonly) NSString* MIMEType;
// The URL that the download request originally attempted to fetch. This may
// differ from the final download URL if there were redirects.
@property(nonatomic, readonly) NSURL* originalURL;
// Total number of expected bytes (a best-guess upper-bound). Returns
// CWVDownloadSizeUnknown if the total size is unknown.
@property(nonatomic, readonly) int64_t totalBytes;
// Total number of bytes that have been received.
@property(nonatomic, readonly) int64_t receivedBytes;
// Rough progress of download, between 0.0 and 1.0.
// It is NAN when the progress is unknown.
@property(nonatomic, readonly) double progress;
@property(nonatomic, weak, nullable) id<CWVDownloadTaskDelegate> delegate;
- (instancetype)init NS_UNAVAILABLE;
// Starts to download the file to a local file at |path|.
//
// This can be only called once. The local file is not deleted automatically. It
// is the caller's responsibility to delete it when it is unnecessary.
- (void)startDownloadToLocalFileAtPath:(NSString*)path;
// Cancels the download. Once cancelled, it cannot be restarted.
- (void)cancel;
@end
// Delegate to observe updates to CWVDownloadTask.
@protocol CWVDownloadTaskDelegate<NSObject>
@optional
// Called when the download has finished. |error| is nil when it has completed
// successfully. |error| represents the error when the download has failed
// e.g., due to network errors. |error| contains a description which describes
// the type of an error.
- (void)downloadTask:(CWVDownloadTask*)downloadTask
didFinishWithError:(nullable NSError*)error;
// Called when the progress of the download has changed. Refer to task.progress
// to check the progress.
- (void)downloadTaskProgressDidChange:(CWVDownloadTask*)downloadTask;
@end
NS_ASSUME_NONNULL_END
#endif // IOS_WEB_VIEW_PUBLIC_CWV_DOWNLOAD_TASK_H_
......@@ -10,6 +10,7 @@
#import "cwv_navigation_type.h"
@protocol CRIWVTranslateDelegate;
@class CWVDownloadTask;
@class CWVSSLStatus;
@class CWVWebView;
......@@ -72,6 +73,20 @@ typedef NS_ENUM(NSInteger, CWVSSLErrorDecision) {
decisionHandler:
(void (^)(CWVSSLErrorDecision))decisionHandler;
// Called when the web view requests to start downloading a file.
//
// The delegate can either:
// - call [task startDownloadToLocalFileWithPath:] to start download
// immediately. - call [task startDownloadToLocalFileWithPath:] later. - do
// nothing in the method, to ignore the request.
// It does nothing when the method is not implemented.
//
// The delegate must retain a strong reference to |task| until it completes
// downloading or is cancelled. Otherwise it is deallocated immediately after
// exiting this method.
- (void)webView:(CWVWebView*)webView
didRequestDownloadWithTask:(CWVDownloadTask*)task;
// Notifies the delegate that web view process was terminated
// (usually by crashing, though possibly by other means).
- (void)webViewWebContentProcessDidTerminate:(CWVWebView*)webView;
......
......@@ -20,7 +20,8 @@ NSString* const kWebViewShellAddressFieldAccessibilityLabel = @"Address field";
NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
@"WebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier";
@interface ShellViewController ()<CWVNavigationDelegate,
@interface ShellViewController ()<CWVDownloadTaskDelegate,
CWVNavigationDelegate,
CWVUIDelegate,
CWVScriptCommandHandler,
UITextFieldDelegate>
......@@ -38,6 +39,13 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
@property(nonatomic, strong) ShellAutofillDelegate* autofillDelegate;
// Handles the translation of the content displayed in |webView|.
@property(nonatomic, strong) ShellTranslationDelegate* translationDelegate;
// The on-going download task if any.
@property(nonatomic, strong, nullable) CWVDownloadTask* downloadTask;
// The path to a local file which the download task is writing to.
@property(nonatomic, strong, nullable) NSString* downloadFilePath;
// A controller to show a "Share" menu for the downloaded file.
@property(nonatomic, strong, nullable)
UIDocumentInteractionController* documentInteractionController;
- (void)back;
- (void)forward;
......@@ -58,6 +66,9 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
@synthesize toolbar = _toolbar;
@synthesize webView = _webView;
@synthesize translationDelegate = _translationDelegate;
@synthesize downloadTask = _downloadTask;
@synthesize downloadFilePath = _downloadFilePath;
@synthesize documentInteractionController = _documentInteractionController;
- (void)viewDidLoad {
[super viewDidLoad];
......@@ -518,6 +529,18 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
decisionHandler(CWVSSLErrorDecisionDoNothing);
}
- (void)webView:(CWVWebView*)webView
didRequestDownloadWithTask:(CWVDownloadTask*)task {
NSLog(@"%@", NSStringFromSelector(_cmd));
self.downloadTask = task;
NSString* documentDirectoryPath = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES)[0];
self.downloadFilePath = [documentDirectoryPath
stringByAppendingPathComponent:task.suggestedFileName];
task.delegate = self;
[task startDownloadToLocalFileAtPath:self.downloadFilePath];
}
#pragma mark CWVScriptCommandHandler
- (BOOL)webView:(CWVWebView*)webView
......@@ -527,4 +550,25 @@ NSString* const kWebViewShellJavaScriptDialogTextFieldAccessibiltyIdentifier =
return YES;
}
#pragma mark CWVDownloadTaskDelegate
- (void)downloadTask:(CWVDownloadTask*)downloadTask
didFinishWithError:(nullable NSError*)error {
NSLog(@"%@", NSStringFromSelector(_cmd));
if (!error) {
NSURL* url = [NSURL fileURLWithPath:self.downloadFilePath];
self.documentInteractionController =
[UIDocumentInteractionController interactionControllerWithURL:url];
[self.documentInteractionController presentOptionsMenuFromRect:CGRectZero
inView:self.view
animated:YES];
}
self.downloadTask = nil;
self.downloadFilePath = nil;
}
- (void)downloadTaskProgressDidChange:(CWVDownloadTask*)downloadTask {
NSLog(@"%@", NSStringFromSelector(_cmd));
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment