Add PDF compositor service

Currently, the service converts one or multiple pages encapsulated in a
SkMultiPictureDocument to a PDF file. It runs in a sandboxed utility process.

This allows Chromium to move PDF generation code into a separate
process, and eventually can support compositing content generated from
multiple processes.

BUG=455764

Review-Url: https://codereview.chromium.org/2832633002
Cr-Commit-Position: refs/heads/master@{#473644}
diff --git a/chrome/app/BUILD.gn b/chrome/app/BUILD.gn
index 2daa56c..7e40b44 100644
--- a/chrome/app/BUILD.gn
+++ b/chrome/app/BUILD.gn
@@ -382,6 +382,11 @@
 
 chrome_packaged_services = [ ":chrome_manifest" ]
 
+if (enable_basic_printing || enable_print_preview) {
+  chrome_packaged_services +=
+      [ "//components/printing/service:pdf_compositor_manifest" ]
+}
+
 if (is_chromeos) {
   chrome_packaged_services +=
       [ "//chrome/browser:preferences_forwarder_manifest" ]
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 7df13cf0..c637153b 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -2581,6 +2581,7 @@
     ]
     deps += [
       "//components/printing/browser",
+      "//components/printing/service/public/interfaces",
       "//printing",
     ]
 
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index 10854ff..77382fa 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -143,6 +143,7 @@
       </if>
       <if expr="enable_printing">
         <include name="IDR_CLOUDPRINT_MANIFEST" file="resources\cloud_print_app\manifest.json" type="BINDATA" />
+        <include name="IDR_PDF_COMPOSITOR_MANIFEST" file="..\..\components\printing\service\pdf_compositor_manifest.json" type="BINDATA" />
       </if>
       <if expr="not is_ios">
         <include name="IDR_DEVTOOLS_DISCOVERY_PAGE_HTML" file="devtools\frontend\devtools_discovery_page.html" type="BINDATA"/>
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 6936ca0..13bf33d 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -379,6 +379,10 @@
 #include "chrome/browser/media/cast_remoting_connector.h"
 #endif
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
+#endif
+
 #if BUILDFLAG(ENABLE_WAYLAND_SERVER)
 #include "chrome/browser/chrome_browser_main_extra_parts_exo.h"
 #endif
@@ -3273,9 +3277,13 @@
 
 void ChromeContentBrowserClient::RegisterOutOfProcessServices(
       OutOfProcessServiceMap* services) {
-#if BUILDFLAG(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS)
-  services->insert(std::make_pair("media",
-                                  base::ASCIIToUTF16("Media Service")));
+#if defined(ENABLE_MOJO_MEDIA_IN_UTILITY_PROCESS)
+  services->emplace("media", base::ASCIIToUTF16("Media Service"));
+#endif
+
+#if BUILDFLAG(ENABLE_PRINTING)
+  services->emplace(printing::mojom::kServiceName,
+                    base::ASCIIToUTF16("PDF Compositor Service"));
 #endif
 }
 
@@ -3312,6 +3320,9 @@
         {nacl::kNaClBrokerServiceName, IDR_NACL_BROKER_MANIFEST},
 #endif  // defined(OS_WIN)
 #endif  // !defined(DISABLE_NACL)
+#if BUILDFLAG(ENABLE_PRINTING)
+        {printing::mojom::kServiceName, IDR_PDF_COMPOSITOR_MANIFEST},
+#endif
   });
 }
 
diff --git a/chrome/utility/BUILD.gn b/chrome/utility/BUILD.gn
index c8482f9..f190c82 100644
--- a/chrome/utility/BUILD.gn
+++ b/chrome/utility/BUILD.gn
@@ -175,6 +175,13 @@
     ]
   }
 
+  if (enable_basic_printing || enable_print_preview) {
+    deps += [
+      "//components/printing/service/public/cpp:factory",
+      "//components/printing/service/public/interfaces",
+    ]
+  }
+
   if (is_mac && safe_browsing_mode == 1) {
     deps += [ "//chrome/utility/safe_browsing/mac" ]
   }
diff --git a/chrome/utility/DEPS b/chrome/utility/DEPS
index 17f0adb..32c697e 100644
--- a/chrome/utility/DEPS
+++ b/chrome/utility/DEPS
@@ -2,6 +2,8 @@
   "+chrome/grit",
   "+chrome/installer/util",
   "+components/payments/content/utility",
+  "+components/printing/service/public/cpp",
+  "+components/printing/service/public/interfaces",
   "+components/safe_json",
   "+components/wifi",
   "+content/public/child",
diff --git a/chrome/utility/chrome_content_utility_client.cc b/chrome/utility/chrome_content_utility_client.cc
index 3a028e7..763c5d8 100644
--- a/chrome/utility/chrome_content_utility_client.cc
+++ b/chrome/utility/chrome_content_utility_client.cc
@@ -56,6 +56,12 @@
 #include "chrome/utility/printing_handler.h"
 #endif
 
+#if BUILDFLAG(ENABLE_PRINTING)
+#include "chrome/common/chrome_content_client.h"
+#include "components/printing/service/public/cpp/pdf_compositor_service_factory.h"
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"  // nogncheck
+#endif
+
 #if defined(FULL_SAFE_BROWSING)
 #include "chrome/common/safe_archive_analyzer.mojom.h"
 #include "chrome/common/safe_browsing/zip_analyzer.h"
@@ -320,6 +326,16 @@
   return false;
 }
 
+void ChromeContentUtilityClient::RegisterServices(
+    ChromeContentUtilityClient::StaticServiceMap* services) {
+#if BUILDFLAG(ENABLE_PRINTING)
+  content::ServiceInfo pdf_compositor_info;
+  pdf_compositor_info.factory =
+      base::Bind(&printing::CreatePdfCompositorService, GetUserAgent());
+  services->emplace(printing::mojom::kServiceName, pdf_compositor_info);
+#endif
+}
+
 // static
 void ChromeContentUtilityClient::PreSandboxStartup() {
 #if BUILDFLAG(ENABLE_EXTENSIONS)
diff --git a/chrome/utility/chrome_content_utility_client.h b/chrome/utility/chrome_content_utility_client.h
index 31a1ca66..70c4b0a 100644
--- a/chrome/utility/chrome_content_utility_client.h
+++ b/chrome/utility/chrome_content_utility_client.h
@@ -21,6 +21,7 @@
   // content::ContentUtilityClient:
   void UtilityThreadStarted() override;
   bool OnMessageReceived(const IPC::Message& message) override;
+  void RegisterServices(StaticServiceMap* services) override;
 
   static void PreSandboxStartup();
 
diff --git a/components/printing/service/BUILD.gn b/components/printing/service/BUILD.gn
new file mode 100644
index 0000000..077ee28
--- /dev/null
+++ b/components/printing/service/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2017 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("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+
+static_library("service") {
+  sources = [
+    "pdf_compositor_impl.cc",
+    "pdf_compositor_impl.h",
+    "pdf_compositor_service.cc",
+    "pdf_compositor_service.h",
+  ]
+
+  deps = [
+    "//base",
+    "//components/discardable_memory/client",
+    "//components/discardable_memory/public/interfaces",
+    "//content/public/common:service_names",
+    "//content/public/utility",
+    "//printing/common",
+    "//skia",
+  ]
+
+  public_deps = [
+    "//components/printing/service/public/interfaces",
+    "//services/service_manager/public/cpp",
+  ]
+}
+
+service_manifest("pdf_compositor_manifest") {
+  name = "pdf_compositor"
+  source = "pdf_compositor_manifest.json"
+}
diff --git a/components/printing/service/DEPS b/components/printing/service/DEPS
new file mode 100644
index 0000000..8391279
--- /dev/null
+++ b/components/printing/service/DEPS
@@ -0,0 +1,10 @@
+include_rules = [
+  "+components/discardable_memory/client",
+  "+content/public/common",
+  "+content/public/utility",
+  "+mojo/public/cpp",
+  "+printing/common",
+  "+services/service_manager/public/cpp",
+  "+skia",
+  "+third_party/skia",
+]
diff --git a/components/printing/service/README.md b/components/printing/service/README.md
new file mode 100644
index 0000000..6ce9c17
--- /dev/null
+++ b/components/printing/service/README.md
@@ -0,0 +1,4 @@
+The pdf_compositor service should composite multiple raw pictures from different
+frames into a complete one, then converts it into a pdf file within an isolated
+sandboxed process. Currently, it has no compositing functionality, just convert
+a set of raw pictures into a pdf file within the sandboxed process.
diff --git a/components/printing/service/pdf_compositor_impl.cc b/components/printing/service/pdf_compositor_impl.cc
new file mode 100644
index 0000000..c27e466
--- /dev/null
+++ b/components/printing/service/pdf_compositor_impl.cc
@@ -0,0 +1,86 @@
+// Copyright 2017 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 "components/printing/service/pdf_compositor_impl.h"
+
+#include <utility>
+#include <vector>
+
+#include "base/logging.h"
+#include "base/memory/shared_memory.h"
+#include "base/memory/shared_memory_handle.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "printing/common/pdf_metafile_utils.h"
+#include "third_party/skia/include/core/SkCanvas.h"
+#include "third_party/skia/include/core/SkDocument.h"
+#include "third_party/skia/src/utils/SkMultiPictureDocument.h"
+
+namespace printing {
+
+PdfCompositorImpl::PdfCompositorImpl(
+    const std::string& creator,
+    std::unique_ptr<service_manager::ServiceContextRef> service_ref)
+    : service_ref_(std::move(service_ref)), creator_(creator) {}
+
+PdfCompositorImpl::~PdfCompositorImpl() = default;
+
+void PdfCompositorImpl::CompositePdf(
+    mojo::ScopedSharedBufferHandle sk_handle,
+    mojom::PdfCompositor::CompositePdfCallback callback) {
+  DCHECK(sk_handle.is_valid());
+
+  base::SharedMemoryHandle memory_handle;
+  size_t memory_size = 0;
+  bool read_only_flag = false;
+
+  const MojoResult result = mojo::UnwrapSharedMemoryHandle(
+      std::move(sk_handle), &memory_handle, &memory_size, &read_only_flag);
+  DCHECK_EQ(MOJO_RESULT_OK, result);
+  DCHECK_GT(memory_size, 0u);
+
+  std::unique_ptr<base::SharedMemory> shm =
+      base::MakeUnique<base::SharedMemory>(memory_handle, true);
+  if (!shm->Map(memory_size)) {
+    DLOG(ERROR) << "CompositePdf: Shared memory map failed.";
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle());
+    return;
+  }
+
+  SkMemoryStream stream(shm->memory(), memory_size);
+  int page_count = SkMultiPictureDocumentReadPageCount(&stream);
+  if (!page_count) {
+    DLOG(ERROR) << "CompositePdf: No page is read.";
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle());
+    return;
+  }
+
+  std::vector<SkDocumentPage> pages(page_count);
+  if (!SkMultiPictureDocumentRead(&stream, pages.data(), page_count)) {
+    DLOG(ERROR) << "CompositePdf: Page reading failed.";
+    std::move(callback).Run(mojo::ScopedSharedBufferHandle());
+    return;
+  }
+
+  SkDynamicMemoryWStream wstream;
+  sk_sp<SkDocument> doc = MakePdfDocument(creator_, &wstream);
+
+  for (const auto& page : pages) {
+    SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height());
+    canvas->drawPicture(page.fPicture);
+    doc->endPage();
+  }
+  doc->close();
+
+  mojo::ScopedSharedBufferHandle buffer =
+      mojo::SharedBufferHandle::Create(wstream.bytesWritten());
+  DCHECK(buffer.is_valid());
+
+  mojo::ScopedSharedBufferMapping mapping = buffer->Map(wstream.bytesWritten());
+  DCHECK(mapping);
+  wstream.copyToAndReset(mapping.get());
+
+  std::move(callback).Run(std::move(buffer));
+}
+
+}  // namespace printing
diff --git a/components/printing/service/pdf_compositor_impl.h b/components/printing/service/pdf_compositor_impl.h
new file mode 100644
index 0000000..b7890fa
--- /dev/null
+++ b/components/printing/service/pdf_compositor_impl.h
@@ -0,0 +1,38 @@
+// Copyright 2017 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 COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_IMPL_H_
+#define COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_IMPL_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
+#include "mojo/public/cpp/system/buffer.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace printing {
+
+class PdfCompositorImpl : public mojom::PdfCompositor {
+ public:
+  PdfCompositorImpl(
+      const std::string& creator,
+      std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+  ~PdfCompositorImpl() override;
+
+  void CompositePdf(
+      mojo::ScopedSharedBufferHandle sk_handle,
+      mojom::PdfCompositor::CompositePdfCallback callback) override;
+
+ private:
+  const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+  const std::string creator_;
+
+  DISALLOW_COPY_AND_ASSIGN(PdfCompositorImpl);
+};
+
+}  // namespace printing
+
+#endif  // COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_IMPL_H_
diff --git a/components/printing/service/pdf_compositor_manifest.json b/components/printing/service/pdf_compositor_manifest.json
new file mode 100644
index 0000000..9e9b059
--- /dev/null
+++ b/components/printing/service/pdf_compositor_manifest.json
@@ -0,0 +1,15 @@
+{
+  "name": "pdf_compositor",
+  "display_name": "PDF Compositor Service",
+  "interface_provider_specs": {
+    "service_manager:connector": {
+      "provides": {
+        "composite": [ "printing::mojom::PdfCompositor" ]
+       },
+       "requires": {
+         "service_manager": [ "service_manager:all_users" ],
+         "content_browser": [ "utility" ]
+       }
+     }
+  }
+}
diff --git a/components/printing/service/pdf_compositor_service.cc b/components/printing/service/pdf_compositor_service.cc
new file mode 100644
index 0000000..d00f3ed
--- /dev/null
+++ b/components/printing/service/pdf_compositor_service.cc
@@ -0,0 +1,73 @@
+// Copyright 2017 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 "components/printing/service/pdf_compositor_service.h"
+
+#include <utility>
+
+#include "base/lazy_instance.h"
+#include "base/memory/discardable_memory.h"
+#include "base/memory/ptr_util.h"
+#include "components/printing/service/pdf_compositor_impl.h"
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
+#include "content/public/common/service_names.mojom.h"
+#include "content/public/utility/utility_thread.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "services/service_manager/public/cpp/connector.h"
+#include "services/service_manager/public/cpp/service_context.h"
+
+namespace {
+
+void OnPdfCompositorRequest(
+    const std::string& creator,
+    service_manager::ServiceContextRefFactory* ref_factory,
+    const service_manager::BindSourceInfo& source_info,
+    printing::mojom::PdfCompositorRequest request) {
+  mojo::MakeStrongBinding(base::MakeUnique<printing::PdfCompositorImpl>(
+                              creator, ref_factory->CreateRef()),
+                          std::move(request));
+}
+}  // namespace
+
+namespace printing {
+
+PdfCompositorService::PdfCompositorService(const std::string& creator)
+    : creator_(creator.empty() ? "Chromium" : creator), weak_factory_(this) {}
+
+PdfCompositorService::~PdfCompositorService() = default;
+
+// static
+std::unique_ptr<service_manager::Service> PdfCompositorService::Create(
+    const std::string& creator) {
+  return base::MakeUnique<printing::PdfCompositorService>(creator);
+}
+
+void PdfCompositorService::OnStart() {
+  // Set up discardable memory manager.
+  discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
+  context()->connector()->BindInterface(content::mojom::kBrowserServiceName,
+                                        &manager_ptr);
+  discardable_shared_memory_manager_ = base::MakeUnique<
+      discardable_memory::ClientDiscardableSharedMemoryManager>(
+      std::move(manager_ptr), content::UtilityThread::Get()->GetIOTaskRunner());
+  DCHECK(discardable_shared_memory_manager_);
+  base::DiscardableMemoryAllocator::SetInstance(
+      discardable_shared_memory_manager_.get());
+
+  ref_factory_ = base::MakeUnique<service_manager::ServiceContextRefFactory>(
+      base::Bind(&service_manager::ServiceContext::RequestQuit,
+                 base::Unretained(context())));
+  registry_.AddInterface(
+      base::Bind(&OnPdfCompositorRequest, creator_, ref_factory_.get()));
+}
+
+void PdfCompositorService::OnBindInterface(
+    const service_manager::BindSourceInfo& source_info,
+    const std::string& interface_name,
+    mojo::ScopedMessagePipeHandle interface_pipe) {
+  registry_.BindInterface(source_info, interface_name,
+                          std::move(interface_pipe));
+}
+
+}  // namespace printing
diff --git a/components/printing/service/pdf_compositor_service.h b/components/printing/service/pdf_compositor_service.h
new file mode 100644
index 0000000..31672988
--- /dev/null
+++ b/components/printing/service/pdf_compositor_service.h
@@ -0,0 +1,54 @@
+// Copyright 2017 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 COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_SERVICE_H_
+#define COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_SERVICE_H_
+
+#include <memory>
+#include <string>
+
+#include "base/macros.h"
+#include "base/memory/weak_ptr.h"
+#include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
+#include "services/service_manager/public/cpp/bind_source_info.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+
+namespace printing {
+
+class PdfCompositorService : public service_manager::Service {
+ public:
+  explicit PdfCompositorService(const std::string& creator);
+  ~PdfCompositorService() override;
+
+  // Factory function for use as an embedded service.
+  static std::unique_ptr<service_manager::Service> Create(
+      const std::string& creator);
+
+  // service_manager::Service:
+  void OnStart() override;
+  void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+                       const std::string& interface_name,
+                       mojo::ScopedMessagePipeHandle interface_pipe) override;
+
+ private:
+  // The creator of this service.
+  // Currently contains the service creator's user agent string if given,
+  // otherwise just use string "Chromium".
+  const std::string creator_;
+
+  std::unique_ptr<discardable_memory::ClientDiscardableSharedMemoryManager>
+      discardable_shared_memory_manager_;
+  std::unique_ptr<service_manager::ServiceContextRefFactory> ref_factory_;
+  service_manager::BinderRegistry registry_;
+  base::WeakPtrFactory<PdfCompositorService> weak_factory_;
+
+  DISALLOW_COPY_AND_ASSIGN(PdfCompositorService);
+};
+
+}  // namespace printing
+
+#endif  // COMPONENTS_PRINTING_SERVICE_PDF_COMPOSITOR_SERVICE_H_
diff --git a/components/printing/service/public/cpp/BUILD.gn b/components/printing/service/public/cpp/BUILD.gn
new file mode 100644
index 0000000..f0c9b84
--- /dev/null
+++ b/components/printing/service/public/cpp/BUILD.gn
@@ -0,0 +1,35 @@
+# Copyright 2017 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("//mojo/public/tools/bindings/mojom.gni")
+
+source_set("client") {
+  sources = [
+    "pdf_compositor_client.cc",
+    "pdf_compositor_client.h",
+  ]
+
+  public_deps = [
+    "//components/printing/service/public/interfaces",
+    "//services/service_manager/public/cpp",
+  ]
+}
+
+source_set("factory") {
+  sources = [
+    "pdf_compositor_service_factory.cc",
+    "pdf_compositor_service_factory.h",
+  ]
+
+  deps = [
+    "//components/printing/service/",
+    "//content/public/common",
+    "//content/public/utility",
+  ]
+
+  public_deps = [
+    "//components/printing/service/public/interfaces",
+    "//services/service_manager/public/cpp",
+  ]
+}
diff --git a/components/printing/service/public/cpp/pdf_compositor_client.cc b/components/printing/service/public/cpp/pdf_compositor_client.cc
new file mode 100644
index 0000000..51aaa56
--- /dev/null
+++ b/components/printing/service/public/cpp/pdf_compositor_client.cc
@@ -0,0 +1,58 @@
+// Copyright 2017 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 "components/printing/service/public/cpp/pdf_compositor_client.h"
+
+#include <utility>
+
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace printing {
+
+namespace {
+
+// Helper callback which owns an PdfCompositorPtr until invoked. This keeps the
+// PdfCompositor pipe open just long enough to dispatch a reply, at which point
+// the reply is forwarded to the wrapped |callback|.
+void OnCompositePdf(
+    printing::mojom::PdfCompositorPtr compositor,
+    printing::mojom::PdfCompositor::CompositePdfCallback callback,
+    scoped_refptr<base::SequencedTaskRunner> task_runner,
+    mojo::ScopedSharedBufferHandle pdf_handle) {
+  task_runner->PostTask(FROM_HERE, base::BindOnce(std::move(callback),
+                                                  base::Passed(&pdf_handle)));
+}
+
+}  // namespace
+
+PdfCompositorClient::PdfCompositorClient() : compositor_(nullptr) {}
+
+PdfCompositorClient::~PdfCompositorClient() {}
+
+void PdfCompositorClient::Connect(service_manager::Connector* connector) {
+  DCHECK(!compositor_.is_bound());
+  connector->BindInterface(mojom::kServiceName, &compositor_);
+}
+
+void PdfCompositorClient::Composite(
+    service_manager::Connector* connector,
+    base::SharedMemoryHandle handle,
+    size_t data_size,
+    mojom::PdfCompositor::CompositePdfCallback callback,
+    scoped_refptr<base::SequencedTaskRunner> callback_task_runner) {
+  DCHECK(data_size);
+
+  if (!compositor_)
+    Connect(connector);
+
+  mojo::ScopedSharedBufferHandle buffer_handle =
+      mojo::WrapSharedMemoryHandle(handle, data_size, true);
+
+  compositor_->CompositePdf(
+      std::move(buffer_handle),
+      base::BindOnce(&OnCompositePdf, base::Passed(&compositor_),
+                     std::move(callback), callback_task_runner));
+}
+
+}  // namespace printing
diff --git a/components/printing/service/public/cpp/pdf_compositor_client.h b/components/printing/service/public/cpp/pdf_compositor_client.h
new file mode 100644
index 0000000..d3d8c29
--- /dev/null
+++ b/components/printing/service/public/cpp/pdf_compositor_client.h
@@ -0,0 +1,38 @@
+// Copyright 2017 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 COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_CLIENT_H_
+#define COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_CLIENT_H_
+
+#include "base/memory/shared_memory_handle.h"
+#include "components/printing/service/public/interfaces/pdf_compositor.mojom.h"
+#include "services/service_manager/public/cpp/connector.h"
+
+namespace printing {
+
+// Helper class to composite a pdf via the pdf_compositor service.
+class PdfCompositorClient {
+ public:
+  PdfCompositorClient();
+  ~PdfCompositorClient();
+
+  // Composite the final picture and convert into a PDF file.
+  void Composite(service_manager::Connector* connector,
+                 base::SharedMemoryHandle handle,
+                 size_t data_size,
+                 mojom::PdfCompositor::CompositePdfCallback callback,
+                 scoped_refptr<base::SequencedTaskRunner> callback_task_runner);
+
+ private:
+  // Connect to the service.
+  void Connect(service_manager::Connector* connector);
+
+  mojom::PdfCompositorPtr compositor_;
+
+  DISALLOW_COPY_AND_ASSIGN(PdfCompositorClient);
+};
+
+}  // namespace printing
+
+#endif  // COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_CLIENT_H_
diff --git a/components/printing/service/public/cpp/pdf_compositor_service_factory.cc b/components/printing/service/public/cpp/pdf_compositor_service_factory.cc
new file mode 100644
index 0000000..f832f86
--- /dev/null
+++ b/components/printing/service/public/cpp/pdf_compositor_service_factory.cc
@@ -0,0 +1,23 @@
+// Copyright 2017 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 "components/printing/service/public/cpp/pdf_compositor_service_factory.h"
+
+#include "components/printing/service/pdf_compositor_service.h"
+#include "content/public/utility/utility_thread.h"
+#include "third_party/WebKit/public/platform/WebImageGenerator.h"
+#include "third_party/skia/include/core/SkGraphics.h"
+
+namespace printing {
+
+std::unique_ptr<service_manager::Service> CreatePdfCompositorService(
+    const std::string& creator) {
+  content::UtilityThread::Get()->EnsureBlinkInitialized();
+  // Hook up blink's codecs so skia can call them.
+  SkGraphics::SetImageGeneratorFromEncodedDataFactory(
+      blink::WebImageGenerator::Create);
+  return printing::PdfCompositorService::Create(creator);
+}
+
+}  // namespace printing
diff --git a/components/printing/service/public/cpp/pdf_compositor_service_factory.h b/components/printing/service/public/cpp/pdf_compositor_service_factory.h
new file mode 100644
index 0000000..10d06ba
--- /dev/null
+++ b/components/printing/service/public/cpp/pdf_compositor_service_factory.h
@@ -0,0 +1,20 @@
+// Copyright 2017 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 COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_SERVICE_FACTORY_H_
+#define COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_SERVICE_FACTORY_H_
+
+#include <memory>
+#include <string>
+
+#include "services/service_manager/public/cpp/service.h"
+
+namespace printing {
+
+std::unique_ptr<service_manager::Service> CreatePdfCompositorService(
+    const std::string& creator);
+
+}  // namespace printing
+
+#endif  // COMPONENTS_PRINTING_SERVICE_PUBLIC_CPP_PDF_COMPOSITOR_SERVICE_FACTORY_H_
diff --git a/components/printing/service/public/interfaces/BUILD.gn b/components/printing/service/public/interfaces/BUILD.gn
new file mode 100644
index 0000000..1efdb19
--- /dev/null
+++ b/components/printing/service/public/interfaces/BUILD.gn
@@ -0,0 +1,11 @@
+# Copyright 2017 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("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+  sources = [
+    "pdf_compositor.mojom",
+  ]
+}
diff --git a/components/printing/service/public/interfaces/OWNERS b/components/printing/service/public/interfaces/OWNERS
new file mode 100644
index 0000000..08850f4
--- /dev/null
+++ b/components/printing/service/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/components/printing/service/public/interfaces/pdf_compositor.mojom b/components/printing/service/public/interfaces/pdf_compositor.mojom
new file mode 100644
index 0000000..67f9448
--- /dev/null
+++ b/components/printing/service/public/interfaces/pdf_compositor.mojom
@@ -0,0 +1,16 @@
+// Copyright 2017 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.
+
+module printing.mojom;
+
+const string kServiceName = "pdf_compositor";
+
+// TODO(weili): Add support for printing frames from different processes.
+interface PdfCompositor {
+  // Currently directly convert passed in page data to a PDF file.
+  // |sk_handle| points to a buffer of a Skia MultiPictureDocument.
+  // |pdf_handle| points to the generated PDF file buffer.
+  CompositePdf(handle<shared_buffer> sk_handle)
+      => (handle<shared_buffer> pdf_handle);
+};
diff --git a/content/child/child_thread_impl.h b/content/child/child_thread_impl.h
index e9883a2..a7e2cd2 100644
--- a/content/child/child_thread_impl.h
+++ b/content/child/child_thread_impl.h
@@ -95,6 +95,7 @@
   void RecordComputedAction(const std::string& action) override;
   ServiceManagerConnection* GetServiceManagerConnection() override;
   service_manager::Connector* GetConnector() override;
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override;
 
   IPC::SyncChannel* channel() { return channel_.get(); }
 
@@ -192,7 +193,6 @@
   void OnChannelError() override;
 
   bool IsInBrowserProcess() const;
-  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner();
 
  private:
   class ChildThreadMessageRouter : public IPC::MessageRouter {
diff --git a/content/public/app/mojo/content_browser_manifest.json b/content/public/app/mojo/content_browser_manifest.json
index ea5984a..6f051c5 100644
--- a/content/public/app/mojo/content_browser_manifest.json
+++ b/content/public/app/mojo/content_browser_manifest.json
@@ -48,6 +48,10 @@
         ],
         "service_manager:service_factory": [
           "service_manager::mojom::ServiceFactory"
+        ],
+        "utility": [
+          "discardable_memory::mojom::DiscardableSharedMemoryManager",
+          "memory_instrumentation::mojom::Coordinator"
         ]
       },
       "requires": {
diff --git a/content/public/child/child_thread.h b/content/public/child/child_thread.h
index 575ee80..950200b 100644
--- a/content/public/child/child_thread.h
+++ b/content/public/child/child_thread.h
@@ -7,7 +7,7 @@
 
 #include <string>
 
-#include "base/logging.h"
+#include "base/memory/ref_counted.h"
 #include "build/build_config.h"
 #include "content/common/content_export.h"
 #include "ipc/ipc_sender.h"
@@ -17,6 +17,7 @@
 #endif
 
 namespace base {
+class SingleThreadTaskRunner;
 struct UserMetricsAction;
 }
 
@@ -73,6 +74,8 @@
   // Returns a connector that can be used to bind interfaces exposed by other
   // services.
   virtual service_manager::Connector* GetConnector() = 0;
+
+  virtual scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/renderer/render_thread.h b/content/public/renderer/render_thread.h
index d58570b..d5d1329 100644
--- a/content/public/renderer/render_thread.h
+++ b/content/public/renderer/render_thread.h
@@ -53,7 +53,6 @@
   virtual IPC::SyncChannel* GetChannel() = 0;
   virtual std::string GetLocale() = 0;
   virtual IPC::SyncMessageFilter* GetSyncMessageFilter() = 0;
-  virtual scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() = 0;
 
   // Called to add or remove a listener for a particular message routing ID.
   // These methods normally get delegated to a MessageRouter.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index da073cb..f191e4f 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1019,11 +1019,6 @@
   return sync_message_filter();
 }
 
-scoped_refptr<base::SingleThreadTaskRunner>
-RenderThreadImpl::GetIOTaskRunner() {
-  return ChildProcess::current()->io_task_runner();
-}
-
 void RenderThreadImpl::AddRoute(int32_t routing_id, IPC::Listener* listener) {
   ChildThreadImpl::GetRouter()->AddRoute(routing_id, listener);
   auto it = pending_frame_creates_.find(routing_id);
@@ -1537,6 +1532,11 @@
     ChildThreadImpl::OnAssociatedInterfaceRequest(name, std::move(handle));
 }
 
+scoped_refptr<base::SingleThreadTaskRunner>
+RenderThreadImpl::GetIOTaskRunner() {
+  return ChildProcess::current()->io_task_runner();
+}
+
 bool RenderThreadImpl::IsGpuRasterizationForced() {
   return is_gpu_rasterization_forced_;
 }
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index 0240ef3..deb435f 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -193,7 +193,6 @@
   IPC::SyncChannel* GetChannel() override;
   std::string GetLocale() override;
   IPC::SyncMessageFilter* GetSyncMessageFilter() override;
-  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override;
   void AddRoute(int32_t routing_id, IPC::Listener* listener) override;
   void RemoveRoute(int32_t routing_id) override;
   int GenerateRoutingID() override;
@@ -226,6 +225,9 @@
       const std::string& name,
       mojo::ScopedInterfaceEndpointHandle handle) override;
 
+  // ChildThread implementation via ChildThreadImpl:
+  scoped_refptr<base::SingleThreadTaskRunner> GetIOTaskRunner() override;
+
   // CompositorDependencies implementation.
   bool IsGpuRasterizationForced() override;
   bool IsAsyncWorkerContextEnabled() override;
diff --git a/printing/BUILD.gn b/printing/BUILD.gn
index 98bc611..eb1fb5a 100644
--- a/printing/BUILD.gn
+++ b/printing/BUILD.gn
@@ -95,6 +95,7 @@
     "//base:i18n",
     "//base/third_party/dynamic_annotations",
     "//cc/paint",
+    "//printing/common",
     "//skia",
     "//third_party/icu",
     "//ui/gfx",
diff --git a/printing/common/BUILD.gn b/printing/common/BUILD.gn
new file mode 100644
index 0000000..d1b74bb
--- /dev/null
+++ b/printing/common/BUILD.gn
@@ -0,0 +1,15 @@
+# Copyright 2017 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.
+
+source_set("common") {
+  sources = [
+    "pdf_metafile_utils.cc",
+    "pdf_metafile_utils.h",
+  ]
+
+  deps = [
+    "//base",
+    "//skia",
+  ]
+}
diff --git a/printing/common/pdf_metafile_utils.cc b/printing/common/pdf_metafile_utils.cc
new file mode 100644
index 0000000..3a30ad0
--- /dev/null
+++ b/printing/common/pdf_metafile_utils.cc
@@ -0,0 +1,46 @@
+// Copyright 2017 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/common/pdf_metafile_utils.h"
+
+#include "base/time/time.h"
+#include "third_party/skia/include/core/SkTime.h"
+
+namespace {
+
+SkTime::DateTime TimeToSkTime(base::Time time) {
+  base::Time::Exploded exploded;
+  time.UTCExplode(&exploded);
+  SkTime::DateTime skdate;
+  skdate.fTimeZoneMinutes = 0;
+  skdate.fYear = exploded.year;
+  skdate.fMonth = exploded.month;
+  skdate.fDayOfWeek = exploded.day_of_week;
+  skdate.fDay = exploded.day_of_month;
+  skdate.fHour = exploded.hour;
+  skdate.fMinute = exploded.minute;
+  skdate.fSecond = exploded.second;
+  return skdate;
+}
+
+}  // namespace
+
+namespace printing {
+
+sk_sp<SkDocument> MakePdfDocument(const std::string& creator,
+                                  SkWStream* stream) {
+  SkDocument::PDFMetadata metadata;
+  SkTime::DateTime now = TimeToSkTime(base::Time::Now());
+  metadata.fCreation.fEnabled = true;
+  metadata.fCreation.fDateTime = now;
+  metadata.fModified.fEnabled = true;
+  metadata.fModified.fDateTime = now;
+  metadata.fCreator = creator.empty()
+                          ? SkString("Chromium")
+                          : SkString(creator.c_str(), creator.size());
+  return SkDocument::MakePDF(stream, SK_ScalarDefaultRasterDPI, metadata,
+                             nullptr, false);
+}
+
+}  // namespace printing
diff --git a/printing/common/pdf_metafile_utils.h b/printing/common/pdf_metafile_utils.h
new file mode 100644
index 0000000..78d2b53
--- /dev/null
+++ b/printing/common/pdf_metafile_utils.h
@@ -0,0 +1,28 @@
+// Copyright 2017 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_COMMON_PDF_METAFILE_UTILS_H_
+#define PRINTING_COMMON_PDF_METAFILE_UTILS_H_
+
+#include <string>
+
+#include "skia/ext/platform_canvas.h"
+#include "third_party/skia/include/core/SkDocument.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+#include "third_party/skia/include/core/SkStream.h"
+
+namespace printing {
+
+enum SkiaDocumentType {
+  PDF_SKIA_DOCUMENT_TYPE,
+  // MSKP is an experimental, fragile, and diagnostic-only document type.
+  MSKP_SKIA_DOCUMENT_TYPE,
+};
+
+sk_sp<SkDocument> MakePdfDocument(const std::string& creator,
+                                  SkWStream* stream);
+
+}  // namespace printing
+
+#endif  // PRINTING_COMMON_PDF_METAFILE_UTILS_H_
diff --git a/printing/pdf_metafile_skia.cc b/printing/pdf_metafile_skia.cc
index 4e15484..2e3b07b 100644
--- a/printing/pdf_metafile_skia.cc
+++ b/printing/pdf_metafile_skia.cc
@@ -16,7 +16,6 @@
 #include "cc/paint/paint_recorder.h"
 #include "cc/paint/skia_paint_canvas.h"
 #include "printing/print_settings.h"
-#include "third_party/skia/include/core/SkDocument.h"
 #include "third_party/skia/include/core/SkStream.h"
 // Note that headers in third_party/skia/src are fragile.  This is
 // an experimental, fragile, and diagnostic-only document type.
@@ -45,35 +44,6 @@
   return (length == assetCopy->read(buffer, length));
 }
 
-SkTime::DateTime TimeToSkTime(base::Time time) {
-  base::Time::Exploded exploded;
-  time.UTCExplode(&exploded);
-  SkTime::DateTime skdate;
-  skdate.fTimeZoneMinutes = 0;
-  skdate.fYear = exploded.year;
-  skdate.fMonth = exploded.month;
-  skdate.fDayOfWeek = exploded.day_of_week;
-  skdate.fDay = exploded.day_of_month;
-  skdate.fHour = exploded.hour;
-  skdate.fMinute = exploded.minute;
-  skdate.fSecond = exploded.second;
-  return skdate;
-}
-
-sk_sp<SkDocument> MakePdfDocument(SkWStream* wStream) {
-  SkDocument::PDFMetadata metadata;
-  SkTime::DateTime now = TimeToSkTime(base::Time::Now());
-  metadata.fCreation.fEnabled = true;
-  metadata.fCreation.fDateTime = now;
-  metadata.fModified.fEnabled = true;
-  metadata.fModified.fDateTime = now;
-  const std::string& agent = printing::GetAgent();
-  metadata.fCreator = agent.empty() ? SkString("Chromium")
-                                    : SkString(agent.c_str(), agent.size());
-  return SkDocument::MakePDF(wStream, SK_ScalarDefaultRasterDPI, metadata,
-                             nullptr, false);
-}
-
 }  // namespace
 
 namespace printing {
@@ -192,7 +162,7 @@
   sk_sp<SkDocument> doc;
   switch (data_->type_) {
     case PDF_SKIA_DOCUMENT_TYPE:
-      doc = MakePdfDocument(&stream);
+      doc = MakePdfDocument(printing::GetAgent(), &stream);
       break;
     case MSKP_SKIA_DOCUMENT_TYPE:
       doc = SkMakeMultiPictureDocument(&stream);
diff --git a/printing/pdf_metafile_skia.h b/printing/pdf_metafile_skia.h
index 948e27f..dacd1a7 100644
--- a/printing/pdf_metafile_skia.h
+++ b/printing/pdf_metafile_skia.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "build/build_config.h"
 #include "cc/paint/paint_canvas.h"
+#include "printing/common/pdf_metafile_utils.h"
 #include "printing/metafile.h"
 #include "skia/ext/platform_canvas.h"
 
@@ -21,12 +22,6 @@
 
 namespace printing {
 
-enum SkiaDocumentType {
-  PDF_SKIA_DOCUMENT_TYPE,
-  // MSKP is an experimental, fragile, and diagnostic-only document type.
-  MSKP_SKIA_DOCUMENT_TYPE,
-};
-
 struct PdfMetafileSkiaData;
 
 // This class uses Skia graphics library to generate a PDF document.