Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(334)

Unified Diff: ios/chrome/browser/ui/print/print_controller.mm

Issue 2589803002: Upstream Chrome on iOS source code [6/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/print/print_controller.mm
diff --git a/ios/chrome/browser/ui/print/print_controller.mm b/ios/chrome/browser/ui/print/print_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..c5576d30a898cde2c86e3754d30c63828d643b7b
--- /dev/null
+++ b/ios/chrome/browser/ui/print/print_controller.mm
@@ -0,0 +1,365 @@
+// Copyright 2012 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/chrome/browser/ui/print/print_controller.h"
+
+#import <MobileCoreServices/UTType.h>
+#import <Webkit/Webkit.h>
+
+#include <memory>
+
+#include "base/callback_helpers.h"
+#import "base/ios/ios_util.h"
+#import "base/ios/weak_nsobject.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/mac/foundation_util.h"
+#import "base/mac/scoped_nsobject.h"
+#include "base/memory/ref_counted.h"
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "components/strings/grit/components_strings.h"
+#import "ios/chrome/browser/ui/alert_coordinator/alert_coordinator.h"
+#import "ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ios/web/public/web_thread.h"
+#import "net/base/mac/url_conversions.h"
+#include "net/http/http_response_headers.h"
+#include "net/url_request/url_fetcher.h"
+#include "net/url_request/url_fetcher_delegate.h"
+#include "net/url_request/url_request_context_getter.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+using net::URLFetcher;
+using net::URLFetcherDelegate;
+using net::URLRequestContextGetter;
+
+@interface PrintController ()
+
+// Presents a UIPrintInteractionController with a default completion handler.
+// |isPDF| indicates if |printInteractionController| is being presented to print
+// a PDF.
++ (void)displayPrintInteractionController:
+ (UIPrintInteractionController*)printInteractionController
+ forPDF:(BOOL)isPDF;
+
+// Shows a dialog on |viewController| indicating that the print preview is being
+// prepared. The dialog will appear only if the download has not completed or
+// been cancelled |kPDFDownloadDialogDelay| seconds after this method is called.
+- (void)showPDFDownloadingDialog:(UIViewController*)viewController;
+
+// Dismisses the dialog which indicates that the print preview is being
+// prepared.
+- (void)dismissPDFDownloadingDialog;
+
+// Shows an dialog on |viewController| indicating that there was an error when
+// preparing the print preview, and providing the ability to retry. |URL| is the
+// URL of the PDF which had an error.
+- (void)showPDFDownloadErrorWithURL:(const GURL)URL
+ viewController:(UIViewController*)viewController;
+
+// Handles downloading the file at |URL| and presents dialogs on
+// |viewController| if necessary.
+- (void)downloadPDFFileWithURL:(const GURL&)URL
+ viewController:(UIViewController*)viewController;
+
+// Accesses the result of the PDF download, and if successful, presents the
+// AirPrint menu with the downloaded PDF. It also ensures that the
+// PDFDownloadingDialog is dismissed, and presents an error dialog on
+// |viewController| if the download fails. This method should be called only by
+// the URLFetcherDelegate.
+- (void)finishedDownloadingPDF:(UIViewController*)viewController;
+
+@end
+
+namespace {
+
+// The MIME type of a PDF file contained in a Web view.
+const char kPDFMimeType[] = "application/pdf";
+
+// Delay after downloading begins that the |_PDFDownloadingDialog| appears.
+const int64_t kPDFDownloadDialogDelay = 1;
+
+// A delegate for the URLFetcher to tell owning PrintController that the
+// download is complete.
+class PrintPDFFetcherDelegate : public URLFetcherDelegate {
+ public:
+ explicit PrintPDFFetcherDelegate(PrintController* owner) : owner_(owner) {}
+ void OnURLFetchComplete(const URLFetcher* source) override {
+ DCHECK(view_controller_);
+ [owner_ finishedDownloadingPDF:view_controller_];
+ }
+
+ // The ViewController used to display an error if the download failed.
+ void SetViewController(UIViewController* view_controller) {
+ view_controller_ = view_controller;
+ }
+
+ private:
+ __unsafe_unretained PrintController* owner_;
+ __unsafe_unretained UIViewController* view_controller_;
+ DISALLOW_COPY_AND_ASSIGN(PrintPDFFetcherDelegate);
+};
+} // namespace
+
+@implementation PrintController {
+ // URLFetcher to download the PDF pointed to by the WKWebView.
+ std::unique_ptr<URLFetcher> _fetcher;
+
+ // A delegate to bridge between PrintController and the URLFetcher callback.
+ std::unique_ptr<PrintPDFFetcherDelegate> _fetcherDelegate;
+
+ // Context getter required by the URLFetcher.
+ scoped_refptr<URLRequestContextGetter> _requestContextGetter;
+
+ // A dialog which indicates that the print preview is being prepared. It
+ // offers a cancel button which will cancel the download. It is created when
+ // downloading begins and is released when downloading ends (either due to
+ // cancellation or completion).
+ base::scoped_nsobject<LoadingAlertCoordinator> _PDFDownloadingDialog;
+
+ // A dialog which indicates that the print preview failed.
+ base::scoped_nsobject<AlertCoordinator> _PDFDownloadingErrorDialog;
+}
+
+#pragma mark - Class methods.
+
++ (void)displayPrintInteractionController:
+ (UIPrintInteractionController*)printInteractionController
+ forPDF:(BOOL)isPDF {
+ void (^completionHandler)(UIPrintInteractionController*, BOOL, NSError*) = ^(
+ UIPrintInteractionController* printInteractionController, BOOL completed,
+ NSError* error) {
+ if (error)
+ DLOG(ERROR) << "Air printing error: " << error.description;
+
+ // When printing a NSData object given to the
+ // UIPrintInteractionController's |printingItem| object, a PDF file
+ // representing the NSData object is created in the app's tmp directory
+ // by the OS and never deleted. So, this workaround deletes PDF files in
+ // tmp now that printing is done. When iOS9 is deprecated, this can
+ // be removed since PDFs will no longer need to be downloaded to print,
+ // and |printingItem| will no longer be used.
+ if (!base::ios::IsRunningOnIOS10OrLater() && isPDF) {
+ web::WebThread::PostBlockingPoolTask(
+ FROM_HERE, base::BindBlock(^{
+ NSFileManager* manager = [NSFileManager defaultManager];
+ NSString* tempDir = NSTemporaryDirectory();
+ NSError* tempDirError = nil;
+
+ // Iterate over files in tmp directory.
+ for (NSString* file in
+ [manager contentsOfDirectoryAtPath:tempDir
+ error:&tempDirError]) {
+ // If the file is a PDF file, delete it.
+ if ([[file pathExtension] isEqualToString:@"pdf"]) {
+ NSError* deletionError = nil;
+ NSString* fullFilePath =
+ [tempDir stringByAppendingPathComponent:file];
+ BOOL success = [manager removeItemAtPath:fullFilePath
+ error:&deletionError];
+ if (!success) {
+ DLOG(ERROR) << "AirPrint unable to remove tmp file:" << file
+ << " error: " << deletionError.description;
+ }
+ }
+ }
+ if (tempDirError) {
+ DLOG(ERROR) << "AirPrint tmp dir access error:"
+ << tempDirError.description;
+ }
+ }));
+ }
+ };
+ [printInteractionController presentAnimated:YES
+ completionHandler:completionHandler];
+}
+
+#pragma mark - Public Methods
+
+- (instancetype)initWithContextGetter:
+ (scoped_refptr<net::URLRequestContextGetter>)getter {
+ self = [super init];
+ if (self) {
+ _requestContextGetter = std::move(getter);
+ _fetcherDelegate.reset(new PrintPDFFetcherDelegate(self));
+ }
+ return self;
+}
+
+- (instancetype)init {
+ NOTREACHED();
+ return nil;
+}
+
+- (void)printView:(UIView*)view
+ withTitle:(NSString*)title
+ viewController:(UIViewController*)viewController {
+ base::RecordAction(base::UserMetricsAction("MobilePrintMenuAirPrint"));
+ UIPrintInteractionController* printInteractionController =
+ [UIPrintInteractionController sharedPrintController];
+ UIPrintInfo* printInfo = [UIPrintInfo printInfo];
+ printInfo.outputType = UIPrintInfoOutputGeneral;
+ printInfo.jobName = title;
+ printInteractionController.printInfo = printInfo;
+ printInteractionController.showsPageRange = YES;
+
+ // Print Formatters do not work for PDFs in iOS9 WKWebView, but do in iOS10.
+ // Instead, download the PDF and (eventually) pass it to the
+ // UIPrintInteractionController. Remove this workaround and all associated PDF
+ // specific code when iOS9 is deprecated.
+ BOOL isPDFURL = NO;
+ if (!base::ios::IsRunningOnIOS10OrLater() &&
+ [view isMemberOfClass:[WKWebView class]]) {
+ WKWebView* webView = base::mac::ObjCCastStrict<WKWebView>(view);
+ NSURL* URL = webView.URL;
+ CFStringRef UTI = UTTypeCreatePreferredIdentifierForTag(
+ kUTTagClassFilenameExtension,
+ (__bridge CFStringRef)[[URL path] pathExtension], NULL);
+ if (UTI) {
+ CFStringRef MIMEType =
+ UTTypeCopyPreferredTagWithClass(UTI, kUTTagClassMIMEType);
+ if (MIMEType) {
+ isPDFURL =
+ [@(kPDFMimeType) isEqualToString:(__bridge NSString*)MIMEType];
+ if (isPDFURL) {
+ [self downloadPDFFileWithURL:net::GURLWithNSURL(URL)
+ viewController:viewController];
+ }
+ CFRelease(MIMEType);
+ }
+ CFRelease(UTI);
+ }
+ }
+
+ if (!isPDFURL) {
+ base::scoped_nsobject<UIPrintPageRenderer> renderer(
+ [[UIPrintPageRenderer alloc] init]);
+ [renderer addPrintFormatter:[view viewPrintFormatter]
+ startingAtPageAtIndex:0];
+ printInteractionController.printPageRenderer = renderer;
+ [PrintController
+ displayPrintInteractionController:printInteractionController
+ forPDF:NO];
+ }
+}
+
+- (void)dismissAnimated:(BOOL)animated {
+ _fetcher.reset();
+ [self dismissPDFDownloadingDialog];
+ [_PDFDownloadingErrorDialog stop];
+ _PDFDownloadingErrorDialog.reset();
+ [[UIPrintInteractionController sharedPrintController]
+ dismissAnimated:animated];
+}
+
+#pragma mark - Private Methods
+
+- (void)showPDFDownloadingDialog:(UIViewController*)viewController {
+ if (_PDFDownloadingDialog)
+ return;
+
+ NSString* title = l10n_util::GetNSString(IDS_IOS_PRINT_PDF_PREPARATION);
+
+ base::WeakNSObject<PrintController> weakSelf(self);
+ ProceduralBlock cancelHandler = ^{
+ base::scoped_nsobject<PrintController> strongSelf([weakSelf retain]);
+ if (strongSelf)
+ strongSelf.get()->_fetcher.reset();
+ };
+
+ _PDFDownloadingDialog.reset([[LoadingAlertCoordinator alloc]
+ initWithBaseViewController:viewController
+ title:title
+ cancelHandler:cancelHandler]);
+
+ dispatch_after(
+ dispatch_time(DISPATCH_TIME_NOW, kPDFDownloadDialogDelay * NSEC_PER_SEC),
+ dispatch_get_main_queue(), ^{
+ base::scoped_nsobject<PrintController> strongSelf([weakSelf retain]);
+ if (!strongSelf)
+ return;
+ [strongSelf.get()->_PDFDownloadingDialog start];
+ });
+}
+
+- (void)dismissPDFDownloadingDialog {
+ [_PDFDownloadingDialog stop];
+ _PDFDownloadingDialog.reset();
+}
+
+- (void)showPDFDownloadErrorWithURL:(const GURL)URL
+ viewController:(UIViewController*)viewController {
+ NSString* title = l10n_util::GetNSString(IDS_IOS_PRINT_PDF_ERROR_TITLE);
+ NSString* message = l10n_util::GetNSString(IDS_IOS_PRINT_PDF_ERROR_SUBTITLE);
+
+ _PDFDownloadingErrorDialog.reset([[AlertCoordinator alloc]
+ initWithBaseViewController:viewController
+ title:title
+ message:message]);
+
+ base::WeakNSObject<PrintController> weakSelf(self);
+
+ [_PDFDownloadingErrorDialog
+ addItemWithTitle:l10n_util::GetNSString(IDS_IOS_PRINT_PDF_TRY_AGAIN)
+ action:^{
+ [weakSelf downloadPDFFileWithURL:URL
+ viewController:viewController];
+ }
+ style:UIAlertActionStyleDefault];
+
+ [_PDFDownloadingErrorDialog
+ addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
+ action:nil
+ style:UIAlertActionStyleCancel];
+
+ [_PDFDownloadingErrorDialog start];
+}
+
+- (void)downloadPDFFileWithURL:(const GURL&)URL
+ viewController:(UIViewController*)viewController {
+ DCHECK(!_fetcher);
+ _fetcherDelegate->SetViewController(viewController);
+ _fetcher = URLFetcher::Create(URL, URLFetcher::GET, _fetcherDelegate.get());
+ _fetcher->SetRequestContext(_requestContextGetter.get());
+ _fetcher->Start();
+ [self showPDFDownloadingDialog:viewController];
+}
+
+- (void)finishedDownloadingPDF:(UIViewController*)viewController {
+ [self dismissPDFDownloadingDialog];
+ DCHECK(_fetcher);
+ base::ScopedClosureRunner fetcherResetter(base::BindBlock(^{
+ _fetcher.reset();
+ }));
+ int responseCode = _fetcher->GetResponseCode();
+ std::string response;
+ std::string MIMEType;
+ // If the request is not successful or does not match a PDF
+ // MIME type, show an error.
+ if (!_fetcher->GetStatus().is_success() || responseCode != 200 ||
+ !_fetcher->GetResponseAsString(&response) ||
+ !_fetcher->GetResponseHeaders()->GetMimeType(&MIMEType) ||
+ MIMEType != kPDFMimeType) {
+ [self showPDFDownloadErrorWithURL:_fetcher->GetOriginalURL()
+ viewController:viewController];
+ return;
+ }
+ NSData* data =
+ [NSData dataWithBytes:response.c_str() length:response.length()];
+ // If the data cannot be printed, show an error.
+ if (![UIPrintInteractionController canPrintData:data]) {
+ [self showPDFDownloadErrorWithURL:_fetcher->GetOriginalURL()
+ viewController:viewController];
+ return;
+ }
+ UIPrintInteractionController* printInteractionController =
+ [UIPrintInteractionController sharedPrintController];
+ printInteractionController.printingItem = data;
+ [PrintController displayPrintInteractionController:printInteractionController
+ forPDF:YES];
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/ui/print/print_controller.h ('k') | ios/chrome/browser/ui/print/print_controller_egtest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698