Print Preview: Change print to cr.sendWithPromise

Change the print message in print preview to send with promise, and use
this to eliminate some global JS functions/CallJavascriptFunctionUnsafe:
- onFileSelectionCompleted
- onFileSelectionCancelled
- printToCloud

Also remove the print-failed webui event.

BUG=717296
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:closure_compilation

Review-Url: https://codereview.chromium.org/2948723002
Cr-Commit-Position: refs/heads/master@{#482095}
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index f84f893c..1a2b0e6 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -88,9 +88,6 @@
     // Bind global handlers
     global.setUseCloudPrint = this.onSetUseCloudPrint_.bind(this);
     global.reloadPrintersList = this.onReloadPrintersList_.bind(this);
-    global.printToCloud = this.onPrintToCloud_.bind(this);
-    global.fileSelectionCancelled = this.onFileSelectionCancelled_.bind(this);
-    global.fileSelectionCompleted = this.onFileSelectionCompleted_.bind(this);
     global.printPreviewFailed = this.onPrintPreviewFailed_.bind(this);
     global.invalidPrinterSettings = this.onInvalidPrinterSettings_.bind(this);
     global.onDidGetDefaultPageLayout =
@@ -137,9 +134,6 @@
     CLOUD_PRINT_ENABLE: 'print_preview.NativeLayer.CLOUD_PRINT_ENABLE',
     DESTINATIONS_RELOAD: 'print_preview.NativeLayer.DESTINATIONS_RELOAD',
     DISABLE_SCALING: 'print_preview.NativeLayer.DISABLE_SCALING',
-    FILE_SELECTION_CANCEL: 'print_preview.NativeLayer.FILE_SELECTION_CANCEL',
-    FILE_SELECTION_COMPLETE:
-        'print_preview.NativeLayer.FILE_SELECTION_COMPLETE',
     MANIPULATE_SETTINGS_FOR_TEST:
         'print_preview.NativeLayer.MANIPULATE_SETTINGS_FOR_TEST',
     PAGE_COUNT_READY: 'print_preview.NativeLayer.PAGE_COUNT_READY',
@@ -149,7 +143,6 @@
         'print_preview.NativeLayer.PREVIEW_GENERATION_DONE',
     PREVIEW_GENERATION_FAIL:
         'print_preview.NativeLayer.PREVIEW_GENERATION_FAIL',
-    PRINT_TO_CLOUD: 'print_preview.NativeLayer.PRINT_TO_CLOUD',
     SETTINGS_INVALID: 'print_preview.NativeLayer.SETTINGS_INVALID',
     PRINT_PRESET_OPTIONS: 'print_preview.NativeLayer.PRINT_PRESET_OPTIONS',
   };
@@ -420,8 +413,10 @@
      *     system's preview application.
      * @param {boolean=} opt_showSystemDialog Whether to open system dialog for
      *     advanced settings.
+     * @return {!Promise} Promise that will resolve when the print request is
+     *     finished or rejected.
      */
-    startPrint: function(
+    print: function(
         destination, printTicketStore, cloudPrintInterface, documentInfo,
         opt_isOpenPdfInPreview, opt_showSystemDialog) {
       assert(
@@ -497,7 +492,7 @@
         ticket['OpenPDFInPreview'] = true;
       }
 
-      chrome.send('print', [JSON.stringify(ticket)]);
+      return cr.sendWithPromise('print', JSON.stringify(ticket));
     },
 
     /** Requests that the current pending print request be cancelled. */
@@ -511,9 +506,16 @@
       chrome.send('showSystemDialog');
     },
 
-    /** Closes the print preview dialog. */
-    startCloseDialog: function() {
-      chrome.send('closePrintPreviewDialog');
+    /**
+     * Closes the print preview dialog.
+     * If |isCancel| is true, also sends a message to Print Preview Handler in
+     * order to update UMA statistics.
+     * @param {boolean} isCancel whether this was called due to the user
+     *     closing the dialog without printing.
+     */
+    startCloseDialog: function(isCancel) {
+      if (isCancel)
+        chrome.send('closePrintPreviewDialog');
       chrome.send('dialogClose');
     },
 
@@ -573,41 +575,6 @@
     },
 
     /**
-     * Called from the C++ layer.
-     * Take the PDF data handed to us and submit it to the cloud, closing the
-     * print preview dialog once the upload is successful.
-     * @param {string} data Data to send as the print job.
-     * @private
-     */
-    onPrintToCloud_: function(data) {
-      var printToCloudEvent = new Event(NativeLayer.EventType.PRINT_TO_CLOUD);
-      printToCloudEvent.data = data;
-      this.eventTarget_.dispatchEvent(printToCloudEvent);
-    },
-
-    /**
-     * Called from PrintPreviewUI::OnFileSelectionCancelled to notify the print
-     * preview dialog regarding the file selection cancel event.
-     * @private
-     */
-    onFileSelectionCancelled_: function() {
-      cr.dispatchSimpleEvent(
-          this.eventTarget_, NativeLayer.EventType.FILE_SELECTION_CANCEL);
-    },
-
-    /**
-     * Called from PrintPreviewUI::OnFileSelectionCompleted to notify the print
-     * preview dialog regarding the file selection completed event.
-     * @private
-     */
-    onFileSelectionCompleted_: function() {
-      // If the file selection is completed and the dialog is not already closed
-      // it means that a pending print to pdf request exists.
-      cr.dispatchSimpleEvent(
-          this.eventTarget_, NativeLayer.EventType.FILE_SELECTION_COMPLETE);
-    },
-
-    /**
      * Display an error message when print preview fails.
      * Called from PrintPreviewMessageHandler::OnPrintPreviewFailed().
      * @private
diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js
index 1162a515..741a50e 100644
--- a/chrome/browser/resources/print_preview/print_preview.js
+++ b/chrome/browser/resources/print_preview/print_preview.js
@@ -352,18 +352,6 @@
           this.onCloudPrintEnable_.bind(this));
       this.tracker.add(
           nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.PRINT_TO_CLOUD,
-          this.onPrintToCloud_.bind(this));
-      this.tracker.add(
-          nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.FILE_SELECTION_CANCEL,
-          this.onFileSelectionCancel_.bind(this));
-      this.tracker.add(
-          nativeLayerEventTarget,
-          print_preview.NativeLayer.EventType.FILE_SELECTION_COMPLETE,
-          this.onFileSelectionComplete_.bind(this));
-      this.tracker.add(
-          nativeLayerEventTarget,
           print_preview.NativeLayer.EventType.SETTINGS_INVALID,
           this.onSettingsInvalid_.bind(this));
       this.tracker.add(
@@ -544,9 +532,8 @@
       this.setIsEnabled_(false);
       this.printHeader_.isCancelButtonEnabled = true;
       var printAttemptResult = this.printIfReady_();
-      if (printAttemptResult == print_preview.PrintAttemptResult_.PRINTED ||
-          printAttemptResult ==
-              print_preview.PrintAttemptResult_.READY_WAITING_FOR_PREVIEW) {
+      if (printAttemptResult ==
+          print_preview.PrintAttemptResult_.READY_WAITING_FOR_PREVIEW) {
         if ((this.destinationStore_.selectedDestination.isLocal &&
              !this.destinationStore_.selectedDestination.isPrivet &&
              !this.destinationStore_.selectedDestination.isExtension &&
@@ -590,23 +577,54 @@
                 print_preview.Metrics.PrintSettingsUiBucket
                     .PRINT_WITH_SETTINGS_COLLAPSED);
       }
-      this.nativeLayer_.startPrint(
-          assert(this.destinationStore_.selectedDestination),
-          this.printTicketStore_, this.cloudPrintInterface_, this.documentInfo_,
+      var destination = assert(this.destinationStore_.selectedDestination);
+      var whenPrintDone = this.nativeLayer_.print(
+          destination, this.printTicketStore_, this.cloudPrintInterface_,
+          this.documentInfo_,
           this.uiState_ == PrintPreviewUiState_.OPENING_PDF_PREVIEW,
           this.showSystemDialogBeforeNextPrint_);
+
+      if (!destination.isLocal) {
+        // Cloud print resolves when print data is returned to submit to cloud
+        // print, or if setings are invalid.
+        whenPrintDone.then(
+            this.onPrintToCloud_.bind(this),
+            this.onSettingsInvalid_.bind(this));
+      } else if (destination.isPrivet || destination.isExtension) {
+        // Privet and extension resolve when printing is complete or if there
+        // is an error printing.
+        whenPrintDone.then(
+            this.close_.bind(this, false),
+            this.onPrintFailed_.bind(this));
+      } else if (
+          destination.id ==
+          print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
+        // Save as PDF resolves when file selection is completed or cancelled.
+        whenPrintDone.then(
+            this.onFileSelectionComplete_.bind(this),
+            this.onFileSelectionCancel_.bind(this));
+      } else {  // standard local printer
+        var boundHideDialog = function () {
+          this.nativeLayer_.startHideDialog();
+        }.bind(this);
+        // Local printers resolve when print is started. Hide the dialog.
+        whenPrintDone.then(boundHideDialog, boundHideDialog);
+      }
+
       this.showSystemDialogBeforeNextPrint_ = false;
       return print_preview.PrintAttemptResult_.PRINTED;
     },
 
     /**
      * Closes the print preview.
+     * @param {boolean} isCancel Whether this was called due to the user
+     *     closing the dialog without printing.
      * @private
      */
-    close_: function() {
+    close_: function(isCancel) {
       this.exitDocument();
       this.uiState_ = PrintPreviewUiState_.CLOSING;
-      this.nativeLayer_.startCloseDialog();
+      this.nativeLayer_.startCloseDialog(isCancel);
     },
 
     /**
@@ -707,10 +725,10 @@
 
     /**
      * Called from the native layer when ready to print to Google Cloud Print.
-     * @param {Event} event Contains the body to send in the HTTP request.
+     * @param {string} data The body to send in the HTTP request.
      * @private
      */
-    onPrintToCloud_: function(event) {
+    onPrintToCloud_: function(data) {
       assert(
           this.uiState_ == PrintPreviewUiState_.PRINTING,
           'Document ready to be sent to the cloud when not in printing ' +
@@ -721,7 +739,7 @@
       assert(this.destinationStore_.selectedDestination != null);
       this.cloudPrintInterface_.submit(
           this.destinationStore_.selectedDestination, this.printTicketStore_,
-          this.documentInfo_, event.data);
+          this.documentInfo_, data);
     },
 
     /**
@@ -762,7 +780,7 @@
           this.uiState_ == PrintPreviewUiState_.PRINTING,
           'Submited job to Google Cloud Print but not in printing state ' +
               this.uiState_);
-      this.close_();
+      this.close_(false);
     },
 
     /**
@@ -857,7 +875,7 @@
      * @private
      */
     onCancelButtonClick_: function() {
-      this.close_();
+      this.close_(true);
     },
 
     /**
@@ -885,7 +903,7 @@
         // On non-mac with toolkit-views, ESC key is handled by C++-side instead
         // of JS-side.
         if (cr.isMac) {
-          this.close_();
+          this.close_(true);
           e.preventDefault();
         }
         return;
@@ -893,7 +911,7 @@
 
       // On Mac, Cmd-. should close the print dialog.
       if (cr.isMac && e.keyCode == 190 && e.metaKey) {
-        this.close_();
+        this.close_(true);
         e.preventDefault();
         return;
       }
@@ -1027,8 +1045,8 @@
 
     /**
      * Called when printing to a privet or extension printer fails.
-     * @param {number | string} httpError The HTTP error code, or -1 if not an
-     *     HTTP error.
+     * @param {*} httpError The HTTP error code, or -1 or a string describing
+     *     the error, if not an HTTP error.
      * @private
      */
     onPrintFailed_: function(httpError) {
@@ -1256,7 +1274,7 @@
       window.open(
           this.cloudPrintInterface_.baseUrl +
           '?user=' + this.userInfo_.activeUser + '#printers');
-      this.close_();
+      this.close_(false);
     }
   };
 
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index bbc00684..1f29ef4 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -202,15 +202,10 @@
 // Timeout for searching for privet printers, in seconds.
 const int kPrivetTimeoutSec = 5;
 
-// Get the print job settings dictionary from |args|. The caller takes
-// ownership of the returned DictionaryValue. Returns NULL on failure.
+// Get the print job settings dictionary from |json_str|. Returns NULL on
+// failure.
 std::unique_ptr<base::DictionaryValue> GetSettingsDictionary(
-    const base::ListValue* args) {
-  std::string json_str;
-  if (!args->GetString(0, &json_str)) {
-    NOTREACHED() << "Could not read JSON argument";
-    return NULL;
-  }
+    const std::string& json_str) {
   if (json_str.empty()) {
     NOTREACHED() << "Empty print job settings";
     return NULL;
@@ -698,8 +693,8 @@
   using local_discovery::ServiceDiscoverySharedClient;
   scoped_refptr<ServiceDiscoverySharedClient> service_discovery =
       ServiceDiscoverySharedClient::GetInstance();
-  DCHECK(privet_callback_id_.empty());
-  privet_callback_id_ = callback_id;
+  DCHECK(privet_search_callback_id_.empty());
+  privet_search_callback_id_ = callback_id;
   StartPrivetLister(service_discovery);
 #endif
 }
@@ -710,8 +705,9 @@
   if (PrivetPrintingEnabled() && printer_lister_) {
     printer_lister_->Stop();
   }
-  ResolveJavascriptCallback(base::Value(privet_callback_id_), base::Value());
-  privet_callback_id_ = "";
+  ResolveJavascriptCallback(base::Value(privet_search_callback_id_),
+                            base::Value());
+  privet_search_callback_id_.clear();
 #endif
 }
 
@@ -789,7 +785,11 @@
 
 void PrintPreviewHandler::HandleGetPreview(const base::ListValue* args) {
   DCHECK_EQ(2U, args->GetSize());
-  std::unique_ptr<base::DictionaryValue> settings = GetSettingsDictionary(args);
+  std::string json_str;
+  if (!args->GetString(0, &json_str))
+    return;
+  std::unique_ptr<base::DictionaryValue> settings =
+      GetSettingsDictionary(json_str);
   if (!settings)
     return;
   int request_id = -1;
@@ -875,9 +875,19 @@
   UMA_HISTOGRAM_COUNTS("PrintPreview.RegeneratePreviewRequest.BeforePrint",
                        regenerate_preview_request_count_);
 
-  std::unique_ptr<base::DictionaryValue> settings = GetSettingsDictionary(args);
+  AllowJavascript();
+
+  std::string callback_id;
+  CHECK(args->GetString(0, &callback_id));
+  CHECK(!callback_id.empty());
+  std::string json_str;
+  if (!args->GetString(1, &json_str))
+    RejectJavascriptCallback(base::Value(callback_id), base::Value(-1));
+
+  std::unique_ptr<base::DictionaryValue> settings =
+      GetSettingsDictionary(json_str);
   if (!settings)
-    return;
+    RejectJavascriptCallback(base::Value(callback_id), base::Value(-1));
 
   ReportPrintSettingsStats(*settings);
 
@@ -909,6 +919,8 @@
   if (print_to_pdf) {
     UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToPDF", page_count);
     ReportUserActionHistogram(PRINT_TO_PDF);
+    DCHECK(pdf_callback_id_.empty());
+    pdf_callback_id_ = callback_id;
     PrintToPdf();
     return;
   }
@@ -930,12 +942,14 @@
         !settings->GetInteger(printing::kSettingPageHeight, &height) ||
         width <= 0 || height <= 0) {
       NOTREACHED();
-      FireWebUIListener("print-failed", base::Value(-1));
+      RejectJavascriptCallback(base::Value(callback_id), base::Value(-1));
       return;
     }
 
-    PrintToPrivetPrinter(
-        printer_name, print_ticket, capabilities, gfx::Size(width, height));
+    DCHECK(privet_print_callback_id_.empty());
+    privet_print_callback_id_ = callback_id;
+    PrintToPrivetPrinter(callback_id, printer_name, print_ticket, capabilities,
+                         gfx::Size(width, height));
     return;
   }
 #endif
@@ -957,7 +971,7 @@
         !settings->GetInteger(printing::kSettingPageHeight, &height) ||
         width <= 0 || height <= 0) {
       NOTREACHED();
-      OnExtensionPrintResult(false, "FAILED");
+      RejectJavascriptCallback(base::Value(callback_id), base::Value("FAILED"));
       return;
     }
 
@@ -965,7 +979,8 @@
     scoped_refptr<base::RefCountedBytes> data;
     if (!GetPreviewDataAndTitle(&data, &title)) {
       LOG(ERROR) << "Nothing to print; no preview available.";
-      OnExtensionPrintResult(false, "NO_DATA");
+      RejectJavascriptCallback(base::Value(callback_id),
+                               base::Value("NO_DATA"));
       return;
     }
 
@@ -974,7 +989,7 @@
         destination_id, capabilities, title, print_ticket,
         gfx::Size(width, height), data,
         base::Bind(&PrintPreviewHandler::OnExtensionPrintResult,
-                   weak_factory_.GetWeakPtr()));
+                   weak_factory_.GetWeakPtr(), callback_id));
     return;
   }
 
@@ -982,6 +997,7 @@
   base::string16 title;
   if (!GetPreviewDataAndTitle(&data, &title)) {
     // Nothing to print, no preview available.
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
 
@@ -989,7 +1005,7 @@
     UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintToCloudPrint",
                          page_count);
     ReportUserActionHistogram(PRINT_WITH_CLOUD_PRINT);
-    SendCloudPrintJob(data.get());
+    SendCloudPrintJob(callback_id, data.get());
     return;
   }
 
@@ -1004,13 +1020,6 @@
     ReportUserActionHistogram(PRINT_TO_PRINTER);
   }
 
-  // This tries to activate the initiator as well, so do not clear the
-  // association with the initiator yet.
-  print_preview_ui()->OnHidePreviewDialog();
-
-  // Grab the current initiator before calling ClearInitiatorDetails() below.
-  // Otherwise calling GetInitiator() later will return the wrong WebContents.
-  // https://crbug.com/407080
   WebContents* initiator = GetInitiator();
   if (initiator) {
     // Save initiator IDs. PrintMsg_PrintForPrintPreview below should cause
@@ -1023,24 +1032,24 @@
                          main_render_frame->GetRoutingID());
   }
 
-  // Do this so the initiator can open a new print preview dialog, while the
-  // current print preview dialog is still handling its print job.
-  ClearInitiatorDetails();
-
   // Set ID to know whether printing is for preview.
   settings->SetInteger(printing::kPreviewUIID,
                        print_preview_ui()->GetIDForPrintPreviewUI());
   RenderFrameHost* rfh = preview_web_contents()->GetMainFrame();
   rfh->Send(new PrintMsg_PrintForPrintPreview(rfh->GetRoutingID(), *settings));
 
-  // For all other cases above, the preview dialog will stay open until the
-  // printing has finished. Then the dialog closes and PrintPreviewDone() gets
-  // called. In the case below, since the preview dialog will be hidden and
-  // not closed, we need to make this call.
-  if (initiator) {
-    auto* print_view_manager = PrintViewManager::FromWebContents(initiator);
-    print_view_manager->PrintPreviewDone();
-  }
+  // Set this so when print preview sends "hidePreviewDialog" we clear the
+  // initiator and call PrintPreviewDone(). In the cases above, the preview
+  // dialog stays open until printing is finished and we do this when the
+  // dialog is closed. In this case, we set this so that these tasks are
+  // done in HandleHidePreview().
+  printing_started_ = true;
+
+  // This will ultimately try to activate the initiator as well, so do not
+  // clear the association with the initiator until "hidePreviewDialog" is
+  // received from JS.
+  ResolveJavascriptCallback(base::Value(callback_id), base::Value());
+
 #else
   NOTREACHED();
 #endif   // BUILDFLAG(ENABLE_BASIC_PRINTING)
@@ -1049,6 +1058,8 @@
 void PrintPreviewHandler::PrintToPdf() {
   if (!print_to_pdf_path_.empty()) {
     // User has already selected a path, no need to show the dialog again.
+    ResolveJavascriptCallback(base::Value(pdf_callback_id_), base::Value());
+    pdf_callback_id_.clear();
     PostPrintToPdfTask();
   } else if (!select_file_dialog_.get() ||
              !select_file_dialog_->IsRunning(platform_util::GetTopLevel(
@@ -1076,6 +1087,24 @@
 }
 
 void PrintPreviewHandler::HandleHidePreview(const base::ListValue* /*args*/) {
+  if (printing_started_) {
+    // Printing has started, so clear the initiator so that it can open a new
+    // print preview dialog, while the current print preview dialog is still
+    // handling its print job.
+    WebContents* initiator = GetInitiator();
+    ClearInitiatorDetails();
+
+    // Since the preview dialog will be hidden and not closed, we need to make
+    // this call.
+    if (initiator) {
+      auto* print_view_manager = PrintViewManager::FromWebContents(initiator);
+      print_view_manager->PrintPreviewDone();
+    }
+
+    // Since the initiator is cleared, only want to do this once.
+    printing_started_ = false;
+  }
+
   print_preview_ui()->OnHidePreviewDialog();
 }
 
@@ -1412,15 +1441,15 @@
   }
 }
 
-void PrintPreviewHandler::SendCloudPrintJob(const base::RefCountedBytes* data) {
+void PrintPreviewHandler::SendCloudPrintJob(const std::string& callback_id,
+                                            const base::RefCountedBytes* data) {
   // BASE64 encode the job data.
   const base::StringPiece raw_data(reinterpret_cast<const char*>(data->front()),
                                    data->size());
   std::string base64_data;
   base::Base64Encode(raw_data, &base64_data);
-  base::Value data_value(base64_data);
 
-  web_ui()->CallJavascriptFunctionUnsafe("printToCloud", data_value);
+  ResolveJavascriptCallback(base::Value(callback_id), base::Value(base64_data));
 }
 
 WebContents* PrintPreviewHandler::GetInitiator() const {
@@ -1443,7 +1472,9 @@
     ChromeSelectFilePolicy policy(GetInitiator());
     if (!policy.CanOpenSelectFileDialog()) {
       policy.SelectFileDenied();
-      return ClosePreviewDialog();
+      RejectJavascriptCallback(base::Value(pdf_callback_id_), base::Value());
+      pdf_callback_id_.clear();
+      return;
     }
   }
 
@@ -1526,7 +1557,8 @@
   sticky_settings->SaveInPrefs(
       Profile::FromBrowserContext(preview_web_contents()->GetBrowserContext())
           ->GetPrefs());
-  web_ui()->CallJavascriptFunctionUnsafe("fileSelectionCompleted");
+  ResolveJavascriptCallback(base::Value(pdf_callback_id_), base::Value());
+  pdf_callback_id_.clear();
   print_to_pdf_path_ = path;
   PostPrintToPdfTask();
 }
@@ -1548,7 +1580,8 @@
 }
 
 void PrintPreviewHandler::FileSelectionCanceled(void* params) {
-  print_preview_ui()->OnFileSelectionCancelled();
+  RejectJavascriptCallback(base::Value(pdf_callback_id_), base::Value());
+  pdf_callback_id_.clear();
 }
 
 void PrintPreviewHandler::ClearInitiatorDetails() {
@@ -1636,13 +1669,10 @@
     const std::string& callback_id,
     std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
   if (!http_client) {
-    if (callback_id.empty()) {
-      // This was an attempt to print to a privet printer and has failed.
-      FireWebUIListener("print-failed", base::Value(-1));
-    } else {  // Capabilities update failed
-      RejectJavascriptCallback(base::Value(callback_id), base::Value());
-    }
+    RejectJavascriptCallback(base::Value(callback_id), base::Value());
     privet_http_resolution_.reset();
+    if (callback_id == privet_print_callback_id_)
+      privet_print_callback_id_.clear();
     return false;
   }
 
@@ -1657,11 +1687,12 @@
 }
 
 void PrintPreviewHandler::PrivetLocalPrintUpdateClient(
+    const std::string& callback_id,
     std::string print_ticket,
     std::string capabilities,
     gfx::Size page_size,
     std::unique_ptr<cloud_print::PrivetHTTPClient> http_client) {
-  if (!PrivetUpdateClient("", std::move(http_client)))
+  if (!PrivetUpdateClient(callback_id, std::move(http_client)))
     return;
 
   StartPrivetLocalPrint(print_ticket, capabilities, page_size);
@@ -1680,7 +1711,9 @@
   base::string16 title;
 
   if (!GetPreviewDataAndTitle(&data, &title)) {
-    FireWebUIListener("print-failed", base::Value(-1));
+    RejectJavascriptCallback(base::Value(privet_print_callback_id_),
+                             base::Value(-1));
+    privet_print_callback_id_.clear();
     return;
   }
 
@@ -1733,16 +1766,19 @@
   privet_capabilities_operation_.reset();
 }
 
-void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& device_name,
+void PrintPreviewHandler::PrintToPrivetPrinter(const std::string& callback_id,
+                                               const std::string& device_name,
                                                const std::string& ticket,
                                                const std::string& capabilities,
                                                const gfx::Size& page_size) {
   if (!CreatePrivetHTTP(
           device_name,
           base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
-                     weak_factory_.GetWeakPtr(), ticket, capabilities,
-                     page_size))) {
-    FireWebUIListener("print-failed", base::Value(-1));
+                     weak_factory_.GetWeakPtr(), callback_id, ticket,
+                     capabilities, page_size))) {
+    RejectJavascriptCallback(base::Value(privet_print_callback_id_),
+                             base::Value(-1));
+    privet_print_callback_id_.clear();
   }
 }
 
@@ -1767,13 +1803,17 @@
 
 void PrintPreviewHandler::OnPrivetPrintingDone(
     const cloud_print::PrivetLocalPrintOperation* print_operation) {
-  ClosePreviewDialog();
+  ResolveJavascriptCallback(base::Value(privet_print_callback_id_),
+                            base::Value());
+  privet_print_callback_id_.clear();
 }
 
 void PrintPreviewHandler::OnPrivetPrintingError(
     const cloud_print::PrivetLocalPrintOperation* print_operation,
     int http_code) {
-  FireWebUIListener("print-failed", base::Value(http_code));
+  RejectJavascriptCallback(base::Value(privet_print_callback_id_),
+                           base::Value(http_code));
+  privet_print_callback_id_.clear();
 }
 
 void PrintPreviewHandler::FillPrinterDescription(
@@ -1834,13 +1874,14 @@
   ResolveJavascriptCallback(base::Value(callback_id), capabilities);
 }
 
-void PrintPreviewHandler::OnExtensionPrintResult(bool success,
+void PrintPreviewHandler::OnExtensionPrintResult(const std::string& callback_id,
+                                                 bool success,
                                                  const std::string& status) {
   if (success) {
-    ClosePreviewDialog();
+    ResolveJavascriptCallback(base::Value(callback_id), base::Value());
     return;
   }
-  FireWebUIListener("print-failed", base::Value(status));
+  RejectJavascriptCallback(base::Value(callback_id), base::Value(status));
 }
 
 void PrintPreviewHandler::RegisterForGaiaCookieChanges() {
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index 80446ab..35b50cf 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -253,7 +253,8 @@
   void SendCloudPrintEnabled();
 
   // Send the PDF data to the cloud to print.
-  void SendCloudPrintJob(const base::RefCountedBytes* data);
+  void SendCloudPrintJob(const std::string& callback_id,
+                         const base::RefCountedBytes* data);
 
   // Gets the initiator for the print preview dialog.
   content::WebContents* GetInitiator() const;
@@ -301,6 +302,7 @@
       const std::string& callback_id,
       std::unique_ptr<cloud_print::PrivetHTTPClient> http_client);
   void PrivetLocalPrintUpdateClient(
+      const std::string& callback_id,
       std::string print_ticket,
       std::string capabilities,
       gfx::Size page_size,
@@ -312,7 +314,8 @@
                              const std::string& capabilities,
                              const gfx::Size& page_size);
   void SendPrivetCapabilitiesError(const std::string& id);
-  void PrintToPrivetPrinter(const std::string& printer_name,
+  void PrintToPrivetPrinter(const std::string& callback_id,
+                            const std::string& printer_name,
                             const std::string& print_ticket,
                             const std::string& capabilities,
                             const gfx::Size& page_size);
@@ -357,10 +360,13 @@
       const base::DictionaryValue& capabilities);
 
   // Called when an extension print job is completed.
+  // |callback_id|: The javascript callback to run.
   // |success|: Whether the job succeeded.
   // |status|: The returned print job status. Useful for reporting a specific
   //     error.
-  void OnExtensionPrintResult(bool success, const std::string& status);
+  void OnExtensionPrintResult(const std::string& callback_id,
+                              bool success,
+                              const std::string& status);
 
   // Register/unregister from notifications of changes done to the GAIA
   // cookie.
@@ -415,8 +421,18 @@
   // notify the test if it was a successful save, only that it was attempted.
   base::Closure pdf_file_saved_closure_;
 
+#if BUILDFLAG(ENABLE_SERVICE_DISCOVERY)
   // Callback ID to be used to notify UI that privet search is finished.
-  std::string privet_callback_id_ = "";
+  std::string privet_search_callback_id_;
+
+  // Callback ID to be used to notify UI that privet printing is finished.
+  std::string privet_print_callback_id_;
+#endif
+
+  // Callback ID to be used to notify UI that PDF file selection has finished.
+  std::string pdf_callback_id_;
+
+  bool printing_started_ = false;
 
   // Proxy for calls to the print backend.  Lazily initialized since web_ui() is
   // not available at construction time.
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
index ee92bb87..83086734 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -5,6 +5,7 @@
 #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
 
 #include <map>
+#include <memory>
 #include <utility>
 #include <vector>
 
@@ -624,10 +625,6 @@
                                          ui_preview_request_id);
 }
 
-void PrintPreviewUI::OnFileSelectionCancelled() {
-  web_ui()->CallJavascriptFunctionUnsafe("fileSelectionCancelled");
-}
-
 void PrintPreviewUI::OnCancelPendingPreviewRequest() {
   g_print_preview_request_id_map.Get().Set(id_, -1);
 }
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_ui.h b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
index 360a1cd2..2abe24e 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.h
@@ -119,9 +119,6 @@
   // controls that need the initiator for generating the preview data.
   void OnInitiatorClosed();
 
-  // Notifies the Web UI renderer that file selection has been cancelled.
-  void OnFileSelectionCancelled();
-
   // Notifies the Web UI that the printer is unavailable or its settings are
   // invalid.
   void OnInvalidPrinterSettings();
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index 73ba1244..23e223e 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -15,6 +15,7 @@
         'getExtensionPrinters',
         'getPrivetPrinters',
         'getPrinterCapabilities',
+        'print',
         'setupPrinter'
       ]);
 
@@ -24,9 +25,6 @@
      */
     this.eventTarget_ = new cr.EventTarget();
 
-    /** @private {boolean} Whether the native layer has sent a print message. */
-    this.printStarted_ = false;
-
     /**
      * @private {boolean} Whether the native layer has set the generate draft
      *      parameter when requesting an updated preview.
@@ -100,6 +98,12 @@
     },
 
     /** @override */
+    print: function() {
+      this.methodCalled('print');
+      return Promise.resolve();
+    },
+
+    /** @override */
     setupPrinter: function(printerId) {
       this.methodCalled('setupPrinter', printerId);
       return this.shouldRejectPrinterSetup_ ?
@@ -114,7 +118,6 @@
                               generateDraft, requestId) {
       this.generateDraft_ = generateDraft;
     },
-    startPrint: function () { this.printStarted_ = true; },
     startHideDialog: function () {},
 
     /** @return {!cr.EventTarget} The native layer event target. */
@@ -128,9 +131,6 @@
     /** @return {boolean} Whether a new draft was requested for preview. */
     generateDraft: function() { return this.generateDraft_; },
 
-    /** @return {boolean} Whether a print request has been issued. */
-    isPrintStarted: function() { return this.printStarted_; },
-
     /**
      * @param {!print_preview.NativeInitialSettings} settings The settings
      *     to return as a response to |getInitialSettings|.
diff --git a/chrome/test/data/webui/print_preview/print_preview_tests.js b/chrome/test/data/webui/print_preview/print_preview_tests.js
index fa0892e..9af4ebd 100644
--- a/chrome/test/data/webui/print_preview/print_preview_tests.js
+++ b/chrome/test/data/webui/print_preview/print_preview_tests.js
@@ -1307,9 +1307,9 @@
               // Has active print button and successfully 'prints', indicating
               // recovery from error state.
               expectFalse(printButton.disabled);
-              expectFalse(nativeLayer.isPrintStarted());
               printButton.click();
-              expectTrue(nativeLayer.isPrintStarted());
+              // This should result in a call to print.
+              return nativeLayer.whenCalled('print');
             });
       });
     });