Print directly to CUPS using the IPP APIs

Implements printing to CUPS with paper size, color, and duplexing
configurable.

Existing bug where the printer does not initialize the first time it is
selected.

BUG=607668

TEST=Enable --enable-native-cups.  Print a webpage to a desired CUPS
printer.

Review-Url: https://codereview.chromium.org/2117713002
Cr-Commit-Position: refs/heads/master@{#408179}
diff --git a/chrome/browser/printing/pdf_to_emf_converter.cc b/chrome/browser/printing/pdf_to_emf_converter.cc
index fed753ae..09e4b66 100644
--- a/chrome/browser/printing/pdf_to_emf_converter.cc
+++ b/chrome/browser/printing/pdf_to_emf_converter.cc
@@ -9,6 +9,7 @@
 #include <memory>
 #include <queue>
 #include <utility>
+#include <vector>
 
 #include "base/files/file.h"
 #include "base/files/file_util.h"
@@ -72,6 +73,7 @@
   ~LazyEmf() override { Close(); }
 
   bool SafePlayback(HDC hdc) const override;
+  bool GetDataAsVector(std::vector<char>* buffer) const override;
   bool SaveTo(base::File* file) const override;
 
  private:
@@ -258,6 +260,11 @@
   return result;
 }
 
+bool LazyEmf::GetDataAsVector(std::vector<char>* buffer) const {
+  NOTREACHED();
+  return false;
+}
+
 bool LazyEmf::SaveTo(base::File* file) const {
   Emf emf;
   return LoadEmf(&emf) && emf.SaveTo(file);
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 26524b9..e41f4f9 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -58,7 +58,6 @@
     "print_settings_initializer_win.h",
     "printed_document.cc",
     "printed_document.h",
-    "printed_document_linux.cc",
     "printed_document_mac.cc",
     "printed_document_win.cc",
     "printed_page.cc",
@@ -181,6 +180,8 @@
         "backend/cups_printer.h",
         "backend/print_backend_cups_ipp.cc",
         "backend/print_backend_cups_ipp.h",
+        "printing_context_chromeos.cc",
+        "printing_context_chromeos.h",
       ]
     } else {
       sources += [
@@ -197,11 +198,13 @@
 
     sources += [
       "backend/print_backend_chromeos.cc",
+      "printed_document_chromeos.cc",
       "printing_context_no_system_dialog.cc",
       "printing_context_no_system_dialog.h",
     ]
   } else if (is_linux) {  # Non-ChromeOS Linux.
     sources += [
+      "printed_document_linux.cc",
       "printing_context_linux.cc",
       "printing_context_linux.h",
     ]
diff --git a/printing/backend/cups_deleters.cc b/printing/backend/cups_deleters.cc
index c37366c..2dccc5b 100644
--- a/printing/backend/cups_deleters.cc
+++ b/printing/backend/cups_deleters.cc
@@ -18,4 +18,9 @@
   cupsFreeDestInfo(info);
 }
 
+void OptionDeleter::operator()(cups_option_t* option) const {
+  // Frees the name and value buffers then the struct itself
+  cupsFreeOptions(1, option);
+}
+
 }  // namespace printing
diff --git a/printing/backend/cups_deleters.h b/printing/backend/cups_deleters.h
index ac4ef4d..004dc06b 100644
--- a/printing/backend/cups_deleters.h
+++ b/printing/backend/cups_deleters.h
@@ -24,6 +24,11 @@
   void operator()(cups_dinfo_t* info) const;
 };
 
+struct OptionDeleter {
+ public:
+  void operator()(cups_option_t* option) const;
+};
+
 }  // namespace printing
 
 #endif  // PRINTING_BACKEND_CUPS_DELETERS_H_
diff --git a/printing/backend/cups_ipp_util.cc b/printing/backend/cups_ipp_util.cc
index 49f02d0..eb5976d 100644
--- a/printing/backend/cups_ipp_util.cc
+++ b/printing/backend/cups_ipp_util.cc
@@ -22,15 +22,18 @@
 
 namespace printing {
 
-namespace {
-
+// property names
 const char kIppCollate[] = "sheet-collate";  // RFC 3381
 const char kIppCopies[] = CUPS_COPIES;
 const char kIppColor[] = CUPS_PRINT_COLOR_MODE;
 const char kIppMedia[] = CUPS_MEDIA;
 const char kIppDuplex[] = CUPS_SIDES;
 
+// collation values
 const char kCollated[] = "collated";
+const char kUncollated[] = "uncollated";
+
+namespace {
 
 const int kMicronsPerMM = 1000;
 const double kMMPerInch = 25.4;
diff --git a/printing/backend/cups_ipp_util.h b/printing/backend/cups_ipp_util.h
index 9887ca5..670a9e3 100644
--- a/printing/backend/cups_ipp_util.h
+++ b/printing/backend/cups_ipp_util.h
@@ -13,6 +13,15 @@
 
 namespace printing {
 
+extern const char kIppCollate[];
+extern const char kIppCopies[];
+extern const char kIppColor[];
+extern const char kIppMedia[];
+extern const char kIppDuplex[];
+
+extern const char kCollated[];
+extern const char kUncollated[];
+
 // Returns the default ColorModel for |printer|.
 ColorModel DefaultColorModel(const CupsOptionProvider& printer);
 
diff --git a/printing/metafile.h b/printing/metafile.h
index e93ffd1..4621fbf 100644
--- a/printing/metafile.h
+++ b/printing/metafile.h
@@ -86,6 +86,10 @@
                           const MacRenderPageParams& params) const = 0;
 #endif  // if defined(OS_WIN)
 
+  // Populates the buffer with the underlying data. This function should ONLY be
+  // called after the metafile is closed. Returns true if writing succeeded.
+  virtual bool GetDataAsVector(std::vector<char>* buffer) const = 0;
+
   // Saves the underlying data to the given file. This function should ONLY be
   // called after the metafile is closed. Returns true if writing succeeded.
   virtual bool SaveTo(base::File* file) const = 0;
@@ -155,8 +159,8 @@
                         const RECT* rect) const = 0;
 #endif  // OS_WIN
 
-  bool GetDataAsVector(std::vector<char>* buffer) const;
-
+  // MetfilePlayer
+  bool GetDataAsVector(std::vector<char>* buffer) const override;
   bool SaveTo(base::File* file) const override;
 
  private:
diff --git a/printing/printed_document.cc b/printing/printed_document.cc
index f77b135..047d2db 100644
--- a/printing/printed_document.cc
+++ b/printing/printed_document.cc
@@ -263,10 +263,11 @@
 PrintedDocument::Immutable::~Immutable() {
 }
 
-#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
-// This function is not used on aura linux/chromeos or android.
+#if defined(OS_ANDROID)
+// This function is not used on android.
 void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
                                         PrintingContext* context) const {
+  NOTREACHED();
 }
 #endif
 
diff --git a/printing/printed_document_chromeos.cc b/printing/printed_document_chromeos.cc
new file mode 100644
index 0000000..aac115f
--- /dev/null
+++ b/printing/printed_document_chromeos.cc
@@ -0,0 +1,47 @@
+// Copyright 2016 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 "printing/printed_document.h"
+
+#include "base/logging.h"
+#include "printing/page_number.h"
+#include "printing/printed_page.h"
+
+#if defined(USE_CUPS)
+#include "printing/printing_context_chromeos.h"
+#endif
+
+namespace printing {
+
+void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
+                                        PrintingContext* context) const {
+#if defined(USE_CUPS)
+#if defined(NDEBUG)
+  {
+    // Make sure the page is from our list.
+    base::AutoLock lock(lock_);
+    DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
+  }
+#endif  // defined(NDEBUG)
+
+  DCHECK(context);
+
+  {
+    base::AutoLock lock(lock_);
+    if (page.page_number() - 1 == mutable_.first_page) {
+      std::vector<char> buffer;
+
+      if (page.metafile()->GetDataAsVector(&buffer)) {
+        static_cast<PrintingContextChromeos*>(context)->StreamData(buffer);
+      } else {
+        LOG(WARNING) << "Failed to read data from metafile";
+      }
+    }
+  }
+#else
+  NOTREACHED();
+#endif  // defined(USE_CUPS)
+}
+
+}  // namespace printing
diff --git a/printing/printed_document_linux.cc b/printing/printed_document_linux.cc
index a104171..74120ba0 100644
--- a/printing/printed_document_linux.cc
+++ b/printing/printed_document_linux.cc
@@ -11,7 +11,7 @@
 
 namespace printing {
 
-#if !defined(OS_CHROMEOS) && !defined(OS_ANDROID)
+#if !defined(OS_ANDROID)
 void PrintedDocument::RenderPrintedPage(
     const PrintedPage& page, PrintingContext* context) const {
 #ifndef NDEBUG
@@ -32,6 +32,6 @@
     }
   }
 }
-#endif  // !OS_CHROMEOS && !OS_ANDROID
+#endif  // !OS_ANDROID
 
 }  // namespace printing
diff --git a/printing/printing.gyp b/printing/printing.gyp
index 80b83c5..c77febb 100644
--- a/printing/printing.gyp
+++ b/printing/printing.gyp
@@ -129,6 +129,7 @@
         }],
         ['chromeos==1',{
           'sources': [
+            'printed_document_chromeos.cc',
             'printing_context_no_system_dialog.cc',
             'printing_context_no_system_dialog.h',
           ],
@@ -179,6 +180,8 @@
                 'backend/cups_printer.h',
                 'backend/print_backend_cups_ipp.cc',
                 'backend/print_backend_cups_ipp.h',
+                'printing_context_chromeos.cc',
+                'printing_context_chromeos.h',
               ],
             }, { # chromeos==0
               'sources': [
@@ -202,6 +205,7 @@
           ],
           'sources': [
             'backend/print_backend_chromeos.cc',
+            'printed_document_chromeos.cc',
           ],
         }],
         ['OS=="linux" and chromeos==0', {
diff --git a/printing/printing_context_chromeos.cc b/printing/printing_context_chromeos.cc
new file mode 100644
index 0000000..35f1587f
--- /dev/null
+++ b/printing/printing_context_chromeos.cc
@@ -0,0 +1,401 @@
+// Copyright 2016 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 "printing/printing_context_chromeos.h"
+
+#include <cups/cups.h>
+#include <stdint.h>
+#include <unicode/ulocdata.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/ptr_util.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+#include "base/strings/utf_string_conversions.h"
+#include "printing/backend/cups_connection.h"
+#include "printing/backend/cups_ipp_util.h"
+#include "printing/backend/cups_printer.h"
+#include "printing/metafile.h"
+#include "printing/print_job_constants.h"
+#include "printing/print_settings.h"
+#include "printing/printing_context_no_system_dialog.h"
+#include "printing/units.h"
+
+namespace printing {
+
+namespace {
+
+using ScopedCupsOption = std::unique_ptr<cups_option_t, OptionDeleter>;
+
+// convert from a ColorMode setting to a print-color-mode value from PWG 5100.13
+const char* GetColorModelForMode(int color_mode) {
+  const char* mode_string;
+  switch (color_mode) {
+    case COLOR:
+    case CMYK:
+    case CMY:
+    case KCMY:
+    case CMY_K:
+    case RGB:
+    case RGB16:
+    case RGBA:
+    case COLORMODE_COLOR:
+    case HP_COLOR_COLOR:
+    case PRINTOUTMODE_NORMAL:
+    case PROCESSCOLORMODEL_CMYK:
+    case PROCESSCOLORMODEL_RGB:
+      mode_string = CUPS_PRINT_COLOR_MODE_COLOR;
+      break;
+    case GRAY:
+    case BLACK:
+    case GRAYSCALE:
+    case COLORMODE_MONOCHROME:
+    case HP_COLOR_BLACK:
+    case PRINTOUTMODE_NORMAL_GRAY:
+    case PROCESSCOLORMODEL_GREYSCALE:
+      mode_string = CUPS_PRINT_COLOR_MODE_MONOCHROME;
+      break;
+    default:
+      mode_string = nullptr;
+      LOG(WARNING) << "Unrecognized color mode";
+      break;
+  }
+
+  return mode_string;
+}
+
+// Returns a new char buffer which is a null-terminated copy of |value|.  The
+// caller owns the returned string.
+char* DuplicateString(const base::StringPiece value) {
+  char* dst = new char[value.size() + 1];
+  value.copy(dst, value.size());
+  dst[value.size()] = '\0';
+  return dst;
+}
+
+ScopedCupsOption ConstructOption(const base::StringPiece name,
+                                 const base::StringPiece value) {
+  // ScopedCupsOption frees the name and value buffers on deletion
+  ScopedCupsOption option = ScopedCupsOption(new cups_option_t);
+  option->name = DuplicateString(name);
+  option->value = DuplicateString(value);
+  return option;
+}
+
+base::StringPiece GetCollateString(bool collate) {
+  return collate ? kCollated : kUncollated;
+}
+
+std::vector<ScopedCupsOption> SettingsToCupsOptions(
+    const PrintSettings& settings) {
+  const char* sides = nullptr;
+  switch (settings.duplex_mode()) {
+    case SIMPLEX:
+      sides = CUPS_SIDES_ONE_SIDED;
+      break;
+    case LONG_EDGE:
+      sides = CUPS_SIDES_TWO_SIDED_PORTRAIT;
+      break;
+    case SHORT_EDGE:
+      sides = CUPS_SIDES_TWO_SIDED_LANDSCAPE;
+      break;
+    default:
+      NOTREACHED();
+  }
+
+  std::vector<ScopedCupsOption> options;
+  options.push_back(
+      ConstructOption(kIppColor,
+                      GetColorModelForMode(settings.color())));  // color
+  options.push_back(ConstructOption(kIppDuplex, sides));         // duplexing
+  options.push_back(
+      ConstructOption(kIppMedia,
+                      settings.requested_media().vendor_id));  // paper size
+  options.push_back(
+      ConstructOption(kIppCopies,
+                      base::IntToString(settings.copies())));  // copies
+  options.push_back(
+      ConstructOption(kIppCollate,
+                      GetCollateString(settings.collate())));  // collate
+
+  return options;
+}
+
+void SetPrintableArea(PrintSettings* settings,
+                      const PrintSettings::RequestedMedia& media,
+                      bool flip) {
+  if (!media.size_microns.IsEmpty()) {
+    float deviceMicronsPerDeviceUnit =
+        (kHundrethsMMPerInch * 10.0f) / settings->device_units_per_inch();
+    gfx::Size paper_size =
+        gfx::Size(media.size_microns.width() / deviceMicronsPerDeviceUnit,
+                  media.size_microns.height() / deviceMicronsPerDeviceUnit);
+
+    gfx::Rect paper_rect(0, 0, paper_size.width(), paper_size.height());
+    settings->SetPrinterPrintableArea(paper_size, paper_rect, flip);
+  }
+}
+
+}  // namespace
+
+// static
+std::unique_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) {
+  if (PrintBackend::GetNativeCupsEnabled())
+    return base::MakeUnique<PrintingContextChromeos>(delegate);
+
+  return base::MakeUnique<PrintingContextNoSystemDialog>(delegate);
+}
+
+PrintingContextChromeos::PrintingContextChromeos(Delegate* delegate)
+    : PrintingContext(delegate),
+      connection_(GURL(), HTTP_ENCRYPT_NEVER, true) {}
+
+PrintingContextChromeos::~PrintingContextChromeos() {
+  ReleaseContext();
+}
+
+void PrintingContextChromeos::AskUserForSettings(
+    int max_pages,
+    bool has_selection,
+    bool is_scripted,
+    const PrintSettingsCallback& callback) {
+  // We don't want to bring up a dialog here.  Ever.  Just signal the callback.
+  callback.Run(OK);
+}
+
+PrintingContext::Result PrintingContextChromeos::UseDefaultSettings() {
+  DCHECK(!in_print_job_);
+
+  ResetSettings();
+
+  std::string device_name = base::UTF16ToUTF8(settings_.device_name());
+  if (device_name.empty())
+    return OnError();
+
+  // TODO(skau): https://crbug.com/613779. See UpdatePrinterSettings for more
+  // info.
+  if (settings_.dpi() == 0) {
+    DVLOG(1) << "Using Default DPI";
+    settings_.set_dpi(kDefaultPdfDpi);
+  }
+
+  // Retrieve device information and set it
+  if (InitializeDevice(device_name) != OK) {
+    LOG(ERROR) << "Could not initialize printer";
+    return OnError();
+  }
+
+  // Set printable area
+  DCHECK(printer_);
+  PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
+
+  PrintSettings::RequestedMedia media;
+  media.vendor_id = paper.vendor_id;
+  media.size_microns = paper.size_um;
+  settings_.set_requested_media(media);
+
+  SetPrintableArea(&settings_, media, true /* flip landscape */);
+
+  return OK;
+}
+
+gfx::Size PrintingContextChromeos::GetPdfPaperSizeDeviceUnits() {
+  int32_t width = 0;
+  int32_t height = 0;
+  UErrorCode error = U_ZERO_ERROR;
+  ulocdata_getPaperSize(delegate_->GetAppLocale().c_str(), &height, &width,
+                        &error);
+  if (error > U_ZERO_ERROR) {
+    // If the call failed, assume a paper size of 8.5 x 11 inches.
+    LOG(WARNING) << "ulocdata_getPaperSize failed, using 8.5 x 11, error: "
+                 << error;
+    width =
+        static_cast<int>(kLetterWidthInch * settings_.device_units_per_inch());
+    height =
+        static_cast<int>(kLetterHeightInch * settings_.device_units_per_inch());
+  } else {
+    // ulocdata_getPaperSize returns the width and height in mm.
+    // Convert this to pixels based on the dpi.
+    float multiplier = 100 * settings_.device_units_per_inch();
+    multiplier /= kHundrethsMMPerInch;
+    width *= multiplier;
+    height *= multiplier;
+  }
+  return gfx::Size(width, height);
+}
+
+PrintingContext::Result PrintingContextChromeos::UpdatePrinterSettings(
+    bool external_preview,
+    bool show_system_dialog,
+    int page_count) {
+  DCHECK(!show_system_dialog);
+
+  if (InitializeDevice(base::UTF16ToUTF8(settings_.device_name())) != OK)
+    return OnError();
+
+  // TODO(skau): Convert to DCHECK when https://crbug.com/613779 is resolved
+  // Print quality suffers when this is set to the resolution reported by the
+  // printer but print quality is fine at this resolution. UseDefaultSettings
+  // exhibits the same problem.
+  if (settings_.dpi() == 0) {
+    DVLOG(1) << "Using Default DPI";
+    settings_.set_dpi(kDefaultPdfDpi);
+  }
+
+  // compute paper size
+  PrintSettings::RequestedMedia media = settings_.requested_media();
+
+  if (media.IsDefault()) {
+    DCHECK(printer_);
+    PrinterSemanticCapsAndDefaults::Paper paper = DefaultPaper(*printer_);
+
+    media.vendor_id = paper.vendor_id;
+    media.size_microns = paper.size_um;
+    settings_.set_requested_media(media);
+  }
+
+  SetPrintableArea(&settings_, media, true);
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::InitializeDevice(
+    const std::string& device) {
+  DCHECK(!in_print_job_);
+
+  std::unique_ptr<CupsPrinter> printer = connection_.GetPrinter(device);
+  if (!printer) {
+    LOG(WARNING) << "Could not initialize device";
+    return OnError();
+  }
+
+  printer_ = std::move(printer);
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::InitWithSettings(
+    const PrintSettings& settings) {
+  DCHECK(!in_print_job_);
+
+  settings_ = settings;
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::NewDocument(
+    const base::string16& document_name) {
+  DCHECK(!in_print_job_);
+  in_print_job_ = true;
+
+  std::string converted_name = base::UTF16ToUTF8(document_name);
+  std::string title = base::UTF16ToUTF8(settings_.title());
+  std::vector<ScopedCupsOption> cups_options = SettingsToCupsOptions(settings_);
+
+  std::vector<cups_option_t> options;
+  for (const ScopedCupsOption& option : cups_options) {
+    if (printer_->CheckOptionSupported(option->name, option->value)) {
+      options.push_back(*(option.get()));
+    } else {
+      DVLOG(1) << "Unsupported option skipped " << option->name << ", "
+               << option->value;
+    }
+  }
+
+  ipp_status_t create_status = printer_->CreateJob(&job_id_, title, options);
+
+  if (job_id_ == 0) {
+    DLOG(WARNING) << "Creating cups job failed"
+                  << ippErrorString(create_status);
+    return OnError();
+  }
+
+  // we only send one document, so it's always the last one
+  if (!printer_->StartDocument(job_id_, converted_name, true, options)) {
+    LOG(ERROR) << "Starting document failed";
+    return OnError();
+  }
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::NewPage() {
+  if (abort_printing_)
+    return CANCEL;
+
+  DCHECK(in_print_job_);
+
+  // Intentional No-op.
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::PageDone() {
+  if (abort_printing_)
+    return CANCEL;
+
+  DCHECK(in_print_job_);
+
+  // Intentional No-op.
+
+  return OK;
+}
+
+PrintingContext::Result PrintingContextChromeos::DocumentDone() {
+  if (abort_printing_)
+    return CANCEL;
+
+  DCHECK(in_print_job_);
+
+  if (!printer_->FinishDocument()) {
+    LOG(WARNING) << "Finishing document failed";
+    return OnError();
+  }
+
+  ipp_status_t job_status = printer_->CloseJob(job_id_);
+  job_id_ = 0;
+
+  if (job_status != IPP_STATUS_OK) {
+    LOG(WARNING) << "Closing job failed";
+    return OnError();
+  }
+
+  ResetSettings();
+  return OK;
+}
+
+void PrintingContextChromeos::Cancel() {
+  abort_printing_ = true;
+  in_print_job_ = false;
+}
+
+void PrintingContextChromeos::ReleaseContext() {
+  printer_.reset();
+}
+
+gfx::NativeDrawingContext PrintingContextChromeos::context() const {
+  // Intentional No-op.
+  return nullptr;
+}
+
+PrintingContext::Result PrintingContextChromeos::StreamData(
+    const std::vector<char>& buffer) {
+  if (abort_printing_)
+    return CANCEL;
+
+  DCHECK(in_print_job_);
+  DCHECK(printer_);
+
+  if (!printer_->StreamData(buffer))
+    return OnError();
+
+  return OK;
+}
+
+}  // namespace printing
diff --git a/printing/printing_context_chromeos.h b/printing/printing_context_chromeos.h
new file mode 100644
index 0000000..baa4af2
--- /dev/null
+++ b/printing/printing_context_chromeos.h
@@ -0,0 +1,64 @@
+// Copyright 2016 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 PRINTING_PRINTING_CONTEXT_CHROMEOS_H_
+#define PRINTING_PRINTING_CONTEXT_CHROMEOS_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/macros.h"
+#include "printing/backend/cups_connection.h"
+#include "printing/backend/cups_printer.h"
+#include "printing/printing_context.h"
+
+namespace base {
+class DictionaryValue;
+}
+
+namespace printing {
+
+class PRINTING_EXPORT PrintingContextChromeos : public PrintingContext {
+ public:
+  explicit PrintingContextChromeos(Delegate* delegate);
+  ~PrintingContextChromeos() override;
+
+  // PrintingContext implementation.
+  void AskUserForSettings(int max_pages,
+                          bool has_selection,
+                          bool is_scripted,
+                          const PrintSettingsCallback& callback) override;
+  Result UseDefaultSettings() override;
+  gfx::Size GetPdfPaperSizeDeviceUnits() override;
+  Result UpdatePrinterSettings(bool external_preview,
+                               bool show_system_dialog,
+                               int page_count) override;
+  Result InitWithSettings(const PrintSettings& settings) override;
+  Result NewDocument(const base::string16& document_name) override;
+  Result NewPage() override;
+  Result PageDone() override;
+  Result DocumentDone() override;
+  void Cancel() override;
+  void ReleaseContext() override;
+  gfx::NativeDrawingContext context() const override;
+
+  Result StreamData(const std::vector<char>& buffer);
+
+ private:
+  // Lazily initializes |printer_|.
+  Result InitializeDevice(const std::string& device);
+
+  // id for ongoing print job.  0 if no job is active.
+  int job_id_;
+
+  CupsConnection connection_;
+  std::unique_ptr<CupsPrinter> printer_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrintingContextChromeos);
+};
+
+}  // namespace printing
+
+#endif  // PRINTING_PRINTING_CONTEXT_CHROMEOS_H_
diff --git a/printing/printing_context_no_system_dialog.cc b/printing/printing_context_no_system_dialog.cc
index 5f8db1a..bd5e180 100644
--- a/printing/printing_context_no_system_dialog.cc
+++ b/printing/printing_context_no_system_dialog.cc
@@ -7,6 +7,8 @@
 #include <stdint.h>
 #include <unicode/ulocdata.h>
 
+#include <memory>
+
 #include "base/logging.h"
 #include "base/memory/ptr_util.h"
 #include "base/values.h"
@@ -16,10 +18,12 @@
 
 namespace printing {
 
+#if !defined(USE_CUPS)
 // static
 std::unique_ptr<PrintingContext> PrintingContext::Create(Delegate* delegate) {
-  return base::WrapUnique(new PrintingContextNoSystemDialog(delegate));
+  return base::MakeUnique<PrintingContextNoSystemDialog>(delegate);
 }
+#endif  // !defined(USE_CUPS)
 
 PrintingContextNoSystemDialog::PrintingContextNoSystemDialog(Delegate* delegate)
     : PrintingContext(delegate) {
@@ -145,7 +149,7 @@
 
 gfx::NativeDrawingContext PrintingContextNoSystemDialog::context() const {
   // Intentional No-op.
-  return NULL;
+  return nullptr;
 }
 
 }  // namespace printing