diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 05f6735ad78f7..8bd150c6cceab 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -8691,6 +8691,9 @@ The following plug-in is unresponsive: <ph name="PLUGIN_NAME">$1
       <message name="IDS_PRINT_PREVIEW_NO_DESTS_PROMO_NOT_NOW_BUTTON_LABEL" desc="Label of button to cancel the Google Cloud Print promotion.">
         Not now
       </message>
+      <message name="IDS_PRINT_PREVIEW_COULD_NOT_PRINT" desc="Error message when printing fails in the print preview">
+        Printing failed. Please check your printer and try again.
+      </message>
 
       <!-- Load State -->
       <message name="IDS_LOAD_STATE_WAITING_FOR_SOCKET_SLOT" desc="Status text when Chrome is at its connection limit, and is waiting for a URL request to complete to free up a connection.">
diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
index 13330d406b969..58a45e843131c 100644
--- a/chrome/browser/resources/print_preview/data/destination_store.js
+++ b/chrome/browser/resources/print_preview/data/destination_store.js
@@ -680,7 +680,8 @@ cr.define('print_preview', function() {
       dest.capabilities = event.capabilities;
 
       this.updateDestination(dest);
-      if (this.selectedDestination_ == dest) {
+      if (this.selectedDestination_.isPrivet &&
+          this.selectedDestination_.id == dest.id) {
         cr.dispatchSimpleEvent(
             this,
             DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
diff --git a/chrome/browser/resources/print_preview/data/print_ticket_store.js b/chrome/browser/resources/print_preview/data/print_ticket_store.js
index 1db226010cca6..e99d4a8116b9d 100644
--- a/chrome/browser/resources/print_preview/data/print_ticket_store.js
+++ b/chrome/browser/resources/print_preview/data/print_ticket_store.js
@@ -337,9 +337,10 @@ cr.define('print_preview', function() {
      * @return {!Object} Google Cloud Print print ticket.
      */
     createPrintTicket: function(destination) {
-      assert(!destination.isLocal,
+      assert(!destination.isLocal || destination.isPrivet,
              'Trying to create a Google Cloud Print print ticket for a local ' +
-                 'destination');
+                 ' non-privet destination');
+
       assert(destination.capabilities,
              'Trying to create a Google Cloud Print print ticket for a ' +
                  'destination with no print capabilities');
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 7a2fa366a6517..112b573f36ee2 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -48,6 +48,7 @@ cr.define('print_preview', function() {
       this.onPrivetPrinterSearchDone_.bind(this);
     global['onPrivetCapabilitiesSet'] =
       this.onPrivetCapabilitiesSet_.bind(this);
+    global['onPrivetPrintFailed'] = this.onPrivetPrintFailed_.bind(this);
   };
 
   /**
@@ -81,7 +82,8 @@ cr.define('print_preview', function() {
     PRIVET_PRINTER_SEARCH_DONE:
         'print_preview.NativeLayer.PRIVET_PRINTER_SEARCH_DONE',
     PRIVET_CAPABILITIES_SET:
-        'print_preview.NativeLayer.PRIVET_CAPABILITIES_SET'
+        'print_preview.NativeLayer.PRIVET_CAPABILITIES_SET',
+    PRIVET_PRINT_FAILED: 'print_preview.NativeLayer.PRIVET_PRINT_FAILED'
   };
 
   /**
@@ -273,6 +275,7 @@ cr.define('print_preview', function() {
         'printToPDF': destination.id ==
             print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
         'printWithCloudPrint': !destination.isLocal,
+        'printWithPrivet': destination.isPrivet,
         'deviceName': destination.id,
         'isFirstRequest': false,
         'requestID': -1,
@@ -300,6 +303,10 @@ cr.define('print_preview', function() {
         };
       }
 
+      if (destination.isPrivet) {
+        ticket['ticket'] = printTicketStore.createPrintTicket(destination);
+      }
+
       if (opt_isOpenPdfInPreview) {
         ticket['OpenPDFInPreview'] = true;
       }
@@ -654,8 +661,8 @@ cr.define('print_preview', function() {
      * @private
      */
     onPrivetPrinterSearchDone_: function() {
-      var privetPrinterSearchDoneEvent = new Event(
-          NativeLayer.EventType.PRIVET_PRINTER_SEARCH_DONE);
+      var privetPrinterSearchDoneEvent =
+            new Event(NativeLayer.EventType.PRIVET_PRINTER_SEARCH_DONE);
       this.dispatchEvent(privetPrinterSearchDoneEvent);
     },
 
@@ -665,11 +672,23 @@ cr.define('print_preview', function() {
      * @private
      */
     onPrivetCapabilitiesSet_: function(printer, capabilities) {
-      var privetCapabilitiesSetEvent = new Event(
-        NativeLayer.EventType.PRIVET_CAPABILITIES_SET);
+      var privetCapabilitiesSetEvent =
+            new Event(NativeLayer.EventType.PRIVET_CAPABILITIES_SET);
       privetCapabilitiesSetEvent.printer = printer;
       privetCapabilitiesSetEvent.capabilities = capabilities;
       this.dispatchEvent(privetCapabilitiesSetEvent);
+    },
+
+    /**
+     * @param {string} http_error The HTTP response code or -1 if not an HTTP
+     *    error.
+     * @private
+     */
+    onPrivetPrintFailed_: function(http_error) {
+      var privetPrintFailedEvent =
+            new Event(NativeLayer.EventType.PRIVET_PRINT_FAILED);
+      privetPrintFailedEvent.httpError = http_error;
+      this.dispatchEvent(privetPrintFailedEvent);
     }
   };
 
diff --git a/chrome/browser/resources/print_preview/print_preview.js b/chrome/browser/resources/print_preview/print_preview.js
index ec356b864b3c4..c26a584986d37 100644
--- a/chrome/browser/resources/print_preview/print_preview.js
+++ b/chrome/browser/resources/print_preview/print_preview.js
@@ -260,6 +260,11 @@ cr.define('print_preview', function() {
           this.nativeLayer_,
           print_preview.NativeLayer.EventType.DISABLE_SCALING,
           this.onDisableScaling_.bind(this));
+      this.tracker.add(
+          this.nativeLayer_,
+          print_preview.NativeLayer.EventType.PRIVET_PRINT_FAILED,
+          this.onPrivetPrintFailed_.bind(this));
+
 
       this.tracker.add(
           $('system-dialog-link'),
@@ -406,6 +411,7 @@ cr.define('print_preview', function() {
       this.setIsEnabled_(false);
       if (this.printIfReady_() &&
           ((this.destinationStore_.selectedDestination.isLocal &&
+            !this.destinationStore_.selectedDestination.isPrivet &&
             this.destinationStore_.selectedDestination.id !=
                 print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) ||
            this.uiState_ == PrintPreview.UiState_.OPENING_PDF_PREVIEW)) {
@@ -802,6 +808,18 @@ cr.define('print_preview', function() {
       this.documentInfo_.updateIsScalingDisabled(true);
     },
 
+    /**
+     * Called when privet printing fails.
+     * @param {Event} event Event object representing the failure.
+     * @private
+     */
+    onPrivetPrintFailed_: function(event) {
+      console.error('Privet printing failed with error code ' +
+                    event.httpError);
+      this.printHeader_.setErrorMessage(
+        localStrings.getString('couldNotPrint'));
+    },
+
     /**
      * Called when the open-cloud-print-dialog link is clicked. Opens the Google
      * Cloud Print web dialog.
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 aadc478dd0ca3..8dcfd8cdbbb40 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -37,6 +37,9 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/signin/profile_oauth2_token_service.h"
 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
+#include "chrome/browser/signin/signin_manager.h"
+#include "chrome/browser/signin/signin_manager_base.h"
+#include "chrome/browser/signin/signin_manager_factory.h"
 #include "chrome/browser/ui/browser_finder.h"
 #include "chrome/browser/ui/browser_tabstrip.h"
 #include "chrome/browser/ui/chrome_select_file_policy.h"
@@ -88,6 +91,7 @@ enum UserActionBuckets {
   INITIATOR_CRASHED,  // UNUSED
   INITIATOR_CLOSED,
   PRINT_WITH_CLOUD_PRINT,
+  PRINT_WITH_PRIVET,
   USERACTION_BUCKET_BOUNDARY
 };
 
@@ -602,24 +606,10 @@ void PrintPreviewHandler::HandleGetPrivetPrinterCapabilities(
   bool success = args->GetString(0, &name);
   DCHECK(success);
 
-  const local_discovery::DeviceDescription* device_description =
-      printer_lister_->GetDeviceDescription(name);
-
-  if (!device_description) {
-    SendPrivetCapabilitiesError(name);
-    return;
-  }
-
-  privet_http_factory_ =
-      local_discovery::PrivetHTTPAsynchronousFactory::CreateInstance(
-      service_discovery_client_,
-      Profile::FromWebUI(web_ui())->GetRequestContext());
-  privet_http_resolution_ = privet_http_factory_->CreatePrivetHTTP(
+  CreatePrivetHTTP(
       name,
-      device_description->address,
-      base::Bind(&PrintPreviewHandler::StartPrivetCapabilities,
+      base::Bind(&PrintPreviewHandler::PrivetCapabilitiesUpdateClient,
                  base::Unretained(this)));
-  privet_http_resolution_->Start();
 #endif
 }
 
@@ -718,6 +708,7 @@ void PrintPreviewHandler::HandlePrint(const ListValue* args) {
 
   bool print_to_pdf = false;
   bool is_cloud_printer = false;
+  bool print_with_privet = false;
 
   bool open_pdf_in_preview = false;
 #if defined(OS_MACOSX)
@@ -726,6 +717,7 @@ void PrintPreviewHandler::HandlePrint(const ListValue* args) {
 
   if (!open_pdf_in_preview) {
     settings->GetBoolean(printing::kSettingPrintToPDF, &print_to_pdf);
+    settings->GetBoolean(printing::kSettingPrintWithPrivet, &print_with_privet);
     is_cloud_printer = settings->HasKey(printing::kSettingCloudPrintId);
   }
 
@@ -739,6 +731,26 @@ void PrintPreviewHandler::HandlePrint(const ListValue* args) {
     return;
   }
 
+#if defined(ENABLE_MDNS)
+  if (print_with_privet && PrivetPrintingEnabled()) {
+    std::string printer_name;
+    std::string print_ticket;
+    UMA_HISTOGRAM_COUNTS("PrintPreview.PageCount.PrintWithPrivet",
+                         page_count);
+    ReportUserActionHistogram(PRINT_WITH_PRIVET);
+
+    bool success = settings->GetString(printing::kSettingDeviceName,
+                                       &printer_name);
+    DCHECK(success);
+    success = settings->GetString(printing::kSettingTicket,
+                                  &print_ticket);
+    DCHECK(success);
+
+    PrintToPrivetPrinter(printer_name, print_ticket);
+    return;
+  }
+#endif
+
   scoped_refptr<base::RefCountedBytes> data;
   string16 title;
   if (!GetPreviewDataAndTitle(&data, &title)) {
@@ -1366,22 +1378,68 @@ void PrintPreviewHandler::StopPrivetPrinterSearch() {
   web_ui()->CallJavascriptFunction("onPrivetPrinterSearchDone");
 }
 
-void PrintPreviewHandler::StartPrivetCapabilities(
+void PrintPreviewHandler::PrivetCapabilitiesUpdateClient(
+    scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
+  if (!PrivetUpdateClient(http_client.Pass()))
+    return;
+
+  privet_capabilities_operation_ =
+      privet_http_client_->CreateCapabilitiesOperation(
+          this);
+  privet_capabilities_operation_->Start();
+}
+
+bool PrintPreviewHandler::PrivetUpdateClient(
     scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
   if (!http_client) {
     SendPrivetCapabilitiesError(privet_http_resolution_->GetName());
     privet_http_resolution_.reset();
-    return;
+    return false;
   }
 
+  privet_local_print_operation_.reset();
+  privet_capabilities_operation_.reset();
   privet_http_client_ = http_client.Pass();
-  privet_capabilities_operation_ =
-      privet_http_client_->CreateCapabilitiesOperation(
-          this);
+
   privet_http_resolution_.reset();
-  privet_capabilities_operation_->Start();
+
+  return true;
 }
 
+void PrintPreviewHandler::PrivetLocalPrintUpdateClient(
+    std::string print_ticket,
+    scoped_ptr<local_discovery::PrivetHTTPClient> http_client) {
+  if (!PrivetUpdateClient(http_client.Pass()))
+    return;
+
+  StartPrivetLocalPrint(print_ticket);
+}
+
+void PrintPreviewHandler::StartPrivetLocalPrint(
+    const std::string& print_ticket) {
+  privet_local_print_operation_ =
+      privet_http_client_->CreateLocalPrintOperation(this);
+
+  privet_local_print_operation_->SetTicket(print_ticket);
+
+  PrintPreviewUI* print_preview_ui = static_cast<PrintPreviewUI*>(
+      web_ui()->GetController());
+  privet_local_print_operation_->SetJobname(
+      base::UTF16ToUTF8(print_preview_ui->initiator_title()));
+
+  Profile* profile = Profile::FromWebUI(web_ui());
+  SigninManagerBase* signin_manager =
+      SigninManagerFactory::GetForProfileIfExists(profile);
+
+  if (signin_manager) {
+    privet_local_print_operation_->SetUsername(
+        signin_manager->GetAuthenticatedUsername());
+  }
+
+  privet_local_print_operation_->Start();
+}
+
+
 void PrintPreviewHandler::OnPrivetCapabilities(
     local_discovery::PrivetCapabilitiesOperation* capabilities_operation,
     int http_error,
@@ -1420,6 +1478,75 @@ void PrintPreviewHandler::SendPrivetCapabilitiesError(
       name_value);
 }
 
+void PrintPreviewHandler::PrintToPrivetPrinter(
+    const std::string& device_name,
+    const std::string& ticket) {
+    CreatePrivetHTTP(
+        device_name,
+        base::Bind(&PrintPreviewHandler::PrivetLocalPrintUpdateClient,
+                   base::Unretained(this),
+                   ticket));
+}
+
+bool PrintPreviewHandler::CreatePrivetHTTP(
+    const std::string& name,
+    const local_discovery::PrivetHTTPAsynchronousFactory::ResultCallback&
+    callback) {
+  const local_discovery::DeviceDescription* device_description =
+      printer_lister_->GetDeviceDescription(name);
+
+  if (!device_description) {
+    SendPrivetCapabilitiesError(name);
+    return false;
+  }
+
+  privet_http_factory_ =
+      local_discovery::PrivetHTTPAsynchronousFactory::CreateInstance(
+      service_discovery_client_,
+      Profile::FromWebUI(web_ui())->GetRequestContext());
+  privet_http_resolution_ = privet_http_factory_->CreatePrivetHTTP(
+      name,
+      device_description->address,
+      callback);
+  privet_http_resolution_->Start();
+
+  return true;
+}
+
+void PrintPreviewHandler::OnPrivetPrintingRequestPDF(
+    const local_discovery::PrivetLocalPrintOperation* print_operation) {
+  scoped_refptr<base::RefCountedBytes> data;
+  string16 title;
+
+  if (!GetPreviewDataAndTitle(&data, &title)) {
+    base::FundamentalValue http_code_value(-1);
+    web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value);
+    return;
+  }
+
+  // TODO(noamsml): Move data into request without copying it?
+  std::string data_str((const char*)data->front(), data->size());
+
+  privet_local_print_operation_->SendData(data_str);
+}
+
+void PrintPreviewHandler::OnPrivetPrintingRequestPWGRaster(
+    const local_discovery::PrivetLocalPrintOperation* print_operation) {
+  NOTIMPLEMENTED();
+}
+
+void PrintPreviewHandler::OnPrivetPrintingDone(
+    const local_discovery::PrivetLocalPrintOperation* print_operation) {
+  ClosePreviewDialog();
+}
+
+void PrintPreviewHandler::OnPrivetPrintingError(
+    const local_discovery::PrivetLocalPrintOperation* print_operation,
+    int http_code) {
+  base::FundamentalValue http_code_value(http_code);
+  web_ui()->CallJavascriptFunction("onPrivetPrintFailed", http_code_value);
+}
+
 void PrintPreviewHandler::FillPrinterDescription(
     const std::string& name,
     const local_discovery::DeviceDescription& description,
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 c6ee255bacc85..98926988f4ffb 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -42,6 +42,7 @@ class PrintPreviewHandler
 #if defined(ENABLE_MDNS)
       public local_discovery::PrivetLocalPrinterLister::Delegate,
       public local_discovery::PrivetCapabilitiesOperation::Delegate,
+      public local_discovery::PrivetLocalPrintOperation::Delegate,
 #endif
       public ui::SelectFileDialog::Listener,
       public printing::PrintViewManagerObserver
@@ -91,6 +92,20 @@ class PrintPreviewHandler
       local_discovery::PrivetCapabilitiesOperation* capabilities_operation,
       int http_error,
       const base::DictionaryValue* capabilities) OVERRIDE;
+
+  // PrivetLocalPrintOperation::Delegate implementation.
+  virtual void OnPrivetPrintingRequestPDF(
+      const local_discovery::PrivetLocalPrintOperation*
+      print_operation) OVERRIDE;
+  virtual void OnPrivetPrintingRequestPWGRaster(
+      const local_discovery::PrivetLocalPrintOperation*
+      print_operation) OVERRIDE;
+  virtual void OnPrivetPrintingDone(
+      const local_discovery::PrivetLocalPrintOperation*
+      print_operation) OVERRIDE;
+  virtual void OnPrivetPrintingError(
+      const local_discovery::PrivetLocalPrintOperation* print_operation,
+        int http_code) OVERRIDE;
 #endif  // ENABLE_MDNS
 
  private:
@@ -237,9 +252,21 @@ class PrintPreviewHandler
 
 #if defined(ENABLE_MDNS)
   void StopPrivetPrinterSearch();
-  void StartPrivetCapabilities(
+  void PrivetCapabilitiesUpdateClient(
       scoped_ptr<local_discovery::PrivetHTTPClient> http_client);
+  void PrivetLocalPrintUpdateClient(
+      std::string printTicket,
+      scoped_ptr<local_discovery::PrivetHTTPClient> http_client);
+  bool PrivetUpdateClient(
+      scoped_ptr<local_discovery::PrivetHTTPClient> http_client);
+  void StartPrivetLocalPrint(const std::string& print_ticket);
   void SendPrivetCapabilitiesError(const std::string& id);
+  void PrintToPrivetPrinter(const std::string& printer_name,
+                            const std::string& print_ticket);
+  bool CreatePrivetHTTP(
+      const std::string& name,
+      const local_discovery::PrivetHTTPAsynchronousFactory::ResultCallback&
+      callback);
   void FillPrinterDescription(
       const std::string& name,
       const local_discovery::DeviceDescription& description,
@@ -289,6 +316,8 @@ class PrintPreviewHandler
   scoped_ptr<local_discovery::PrivetHTTPClient> privet_http_client_;
   scoped_ptr<local_discovery::PrivetCapabilitiesOperation>
       privet_capabilities_operation_;
+  scoped_ptr<local_discovery::PrivetLocalPrintOperation>
+      privet_local_print_operation_;
 #endif
 
   base::WeakPtrFactory<PrintPreviewHandler> weak_factory_;
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 e927c4d5b3b55..5e9989995daa3 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_ui.cc
@@ -320,6 +320,8 @@ content::WebUIDataSource* CreatePrintPreviewUISource() {
   source->AddLocalizedString(
       "noDestsPromoNotNowButtonLabel",
       IDS_PRINT_PREVIEW_NO_DESTS_PROMO_NOT_NOW_BUTTON_LABEL);
+  source->AddLocalizedString("couldNotPrint",
+                             IDS_PRINT_PREVIEW_COULD_NOT_PRINT);
 
   source->SetJsonPath("strings.js");
   source->AddResourcePath("print_preview.js", IDR_PRINT_PREVIEW_JS);
diff --git a/cloud_print/gcp20/prototype/print_job_handler.cc b/cloud_print/gcp20/prototype/print_job_handler.cc
index a82f26f88873d..5666143bebae5 100644
--- a/cloud_print/gcp20/prototype/print_job_handler.cc
+++ b/cloud_print/gcp20/prototype/print_job_handler.cc
@@ -23,7 +23,7 @@ const int kLocalPrintJobExpirationSec = 20;
 const int kErrorTimeoutSec = 30;
 
 // Errors simulation constants:
-const double kPaperJamProbability = 0.2;
+const double kPaperJamProbability = 1.0;
 const int kTooManyDraftsTimeout = 10;
 const size_t kMaxDrafts = 5;
 
@@ -321,4 +321,3 @@ void PrintJobHandler::ForgetDraft(const std::string& id) {
 void PrintJobHandler::ForgetLocalJob(const std::string& id) {
   jobs.erase(id);
 }
-
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index b2fefa86a721f..25b5b499bdf94 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -119,6 +119,13 @@ const char kSettingPrinterName[] = "printerName";
 // Print to PDF option: true if selected, false if not.
 const char kSettingPrintToPDF[] = "printToPDF";
 
+// Print using Privet option: true if destination is a Privet printer, false if
+// not.
+const char kSettingPrintWithPrivet[] = "printWithPrivet";
+
+// Ticket option. Contains the ticket in CJT format.
+const char kSettingTicket[] = "ticket";
+
 // Whether to print CSS backgrounds.
 const char kSettingShouldPrintBackgrounds[] = "shouldPrintBackgrounds";
 
diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h
index 526f8ee34f8bf..4fd55d4468414 100644
--- a/printing/print_job_constants.h
+++ b/printing/print_job_constants.h
@@ -48,6 +48,8 @@ PRINTING_EXPORT extern const char kSettingPrintableAreaWidth[];
 PRINTING_EXPORT extern const char kSettingPrintableAreaHeight[];
 PRINTING_EXPORT extern const char kSettingPrinterName[];
 PRINTING_EXPORT extern const char kSettingPrintToPDF[];
+PRINTING_EXPORT extern const char kSettingPrintWithPrivet[];
+PRINTING_EXPORT extern const char kSettingTicket[];
 PRINTING_EXPORT extern const char kSettingShouldPrintBackgrounds[];
 PRINTING_EXPORT extern const char kSettingShouldPrintSelectionOnly[];