summaryrefslogtreecommitdiff
path: root/chromium/components/services/pdf_compositor
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/components/services/pdf_compositor')
-rw-r--r--chromium/components/services/pdf_compositor/BUILD.gn75
-rw-r--r--chromium/components/services/pdf_compositor/DEPS17
-rw-r--r--chromium/components/services/pdf_compositor/OWNERS7
-rw-r--r--chromium/components/services/pdf_compositor/README.md4
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_impl.cc323
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_impl.h191
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc375
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_manifest.json18
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_service.cc122
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_service.h55
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_service_unittest.cc246
-rw-r--r--chromium/components/services/pdf_compositor/pdf_compositor_service_unittest_manifest.json17
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/BUILD.gn34
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc28
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.h20
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h17
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc53
-rw-r--r--chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h28
-rw-r--r--chromium/components/services/pdf_compositor/public/interfaces/BUILD.gn15
-rw-r--r--chromium/components/services/pdf_compositor/public/interfaces/OWNERS2
-rw-r--r--chromium/components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom62
21 files changed, 1709 insertions, 0 deletions
diff --git a/chromium/components/services/pdf_compositor/BUILD.gn b/chromium/components/services/pdf_compositor/BUILD.gn
new file mode 100644
index 00000000000..32b899386c2
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/BUILD.gn
@@ -0,0 +1,75 @@
+# 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("//printing/buildflags/buildflags.gni")
+import("//services/service_manager/public/cpp/service.gni")
+import("//services/service_manager/public/service_manifest.gni")
+import("//services/service_manager/public/tools/test/service_test.gni")
+
+static_library("pdf_compositor") {
+ sources = [
+ "pdf_compositor_impl.cc",
+ "pdf_compositor_impl.h",
+ "pdf_compositor_service.cc",
+ "pdf_compositor_service.h",
+ ]
+
+ deps = [
+ "//base",
+ "//components/crash/core/common:crash_key",
+ "//components/discardable_memory/client",
+ "//components/discardable_memory/public/interfaces",
+ "//content/public/child",
+ "//content/public/common:service_names",
+ "//content/public/utility",
+ "//printing/common",
+ "//services/ui/public/interfaces",
+ "//skia",
+ "//third_party/blink/public:blink_headers",
+ ]
+
+ public_deps = [
+ "//components/services/pdf_compositor/public/cpp:utils",
+ "//components/services/pdf_compositor/public/interfaces",
+ "//services/service_manager/public/cpp",
+ ]
+}
+
+service_manifest("pdf_compositor_manifest") {
+ name = "pdf_compositor"
+ source = "pdf_compositor_manifest.json"
+}
+
+if (enable_basic_printing) {
+ source_set("unit_tests") {
+ testonly = true
+ sources = [
+ "pdf_compositor_impl_unittest.cc",
+ "pdf_compositor_service_unittest.cc",
+ ]
+
+ include_dirs = [
+ "//skia/config",
+ "//testing/gmock/include",
+ "//third_party/skia/include/core",
+ ]
+ deps = [
+ ":pdf_compositor",
+ "//base/test:test_support",
+ "//cc/paint:paint",
+ "//components/crash/core/common:crash_key",
+ "//components/services/pdf_compositor/public/interfaces",
+ "//services/service_manager/public/cpp:service_test_support",
+ "//skia",
+ "//testing/gmock",
+ "//testing/gtest",
+ ]
+ }
+
+ service_manifest("pdf_compositor_service_unittest_manifest") {
+ name = "pdf_compositor_service_unittest"
+ source = "pdf_compositor_service_unittest_manifest.json"
+ packaged_services = [ ":pdf_compositor_manifest" ]
+ }
+}
diff --git a/chromium/components/services/pdf_compositor/DEPS b/chromium/components/services/pdf_compositor/DEPS
new file mode 100644
index 00000000000..952a2798ac8
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/DEPS
@@ -0,0 +1,17 @@
+include_rules = [
+ "+cc/paint",
+ "+components/crash/core/common/crash_key.h",
+ "+components/discardable_memory/client",
+ "+content/public/child", # Windows direct write proxy access.
+ "+content/public/common",
+ "+content/public/utility",
+ "+mojo/public/cpp",
+ "+printing/common",
+ "+services/service_manager/public/cpp",
+ "+services/service_manager/public/mojom",
+ "+services/ui/public/interfaces/constants.mojom.h", # UI service name.
+ "+skia",
+ "+third_party/skia",
+ "+third_party/blink/public/platform", # Test web sandbox support.
+ "+ui/base/ui_base_features.h", # UI features.
+]
diff --git a/chromium/components/services/pdf_compositor/OWNERS b/chromium/components/services/pdf_compositor/OWNERS
new file mode 100644
index 00000000000..3fb8863170f
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/OWNERS
@@ -0,0 +1,7 @@
+file://printing/OWNERS
+
+per-file pdf_compositor_manifest.json=set noparent
+per-file pdf_compositor_manifest.json=file://ipc/SECURITY_OWNERS
+
+per-file pdf_compositor_service_unittest_manifest.json=set noparent
+per-file pdf_compositor_service_unittest_manifest.json=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/services/pdf_compositor/README.md b/chromium/components/services/pdf_compositor/README.md
new file mode 100644
index 00000000000..6ce9c170a85
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/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/chromium/components/services/pdf_compositor/pdf_compositor_impl.cc b/chromium/components/services/pdf_compositor/pdf_compositor_impl.cc
new file mode 100644
index 00000000000..072a78b90ec
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_impl.cc
@@ -0,0 +1,323 @@
+// 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/services/pdf_compositor/pdf_compositor_impl.h"
+
+#include <algorithm>
+#include <tuple>
+#include <utility>
+
+#include "base/logging.h"
+#include "base/memory/shared_memory_handle.h"
+#include "base/stl_util.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.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/include/core/SkSerialProcs.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::NotifyUnavailableSubframe(uint64_t frame_guid) {
+ // Add this frame into the map.
+ DCHECK(!base::ContainsKey(frame_info_map_, frame_guid));
+ auto& frame_info =
+ frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>())
+ .first->second;
+ frame_info->composited = true;
+ // Set content to be nullptr so it will be replaced by an empty picture during
+ // deserialization of its parent.
+ frame_info->content = nullptr;
+
+ // Update the requests in case any of them might be waiting for this frame.
+ UpdateRequestsWithSubframeInfo(frame_guid, std::vector<uint64_t>());
+}
+
+void PdfCompositorImpl::AddSubframeContent(
+ uint64_t frame_guid,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map) {
+ // Add this frame and its serialized content.
+ DCHECK(!base::ContainsKey(frame_info_map_, frame_guid));
+ auto& frame_info =
+ frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>())
+ .first->second;
+ frame_info->serialized_content =
+ GetShmFromMojoHandle(std::move(serialized_content));
+
+ // Copy the subframe content information.
+ frame_info->subframe_content_map = subframe_content_map;
+
+ // If there is no request, we do nothing more.
+ // Otherwise, we need to check whether any request actually waits on this
+ // frame content.
+ if (requests_.empty())
+ return;
+
+ // Get the pending list which is a list of subframes this frame needs
+ // but are still unavailable.
+ std::vector<uint64_t> pending_subframes;
+ for (auto& subframe_content : subframe_content_map) {
+ auto subframe_guid = subframe_content.second;
+ if (!base::ContainsKey(frame_info_map_, subframe_guid))
+ pending_subframes.push_back(subframe_guid);
+ }
+
+ // Update the requests in case any of them is waiting for this frame.
+ UpdateRequestsWithSubframeInfo(frame_guid, pending_subframes);
+}
+
+void PdfCompositorImpl::CompositePageToPdf(
+ uint64_t frame_guid,
+ uint32_t page_num,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ mojom::PdfCompositor::CompositePageToPdfCallback callback) {
+ HandleCompositionRequest(frame_guid, page_num, std::move(serialized_content),
+ subframe_content_map, std::move(callback));
+}
+
+void PdfCompositorImpl::CompositeDocumentToPdf(
+ uint64_t frame_guid,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) {
+ HandleCompositionRequest(frame_guid, base::nullopt,
+ std::move(serialized_content), subframe_content_map,
+ std::move(callback));
+}
+
+void PdfCompositorImpl::SetWebContentsURL(const GURL& url) {
+ // Record the most recent url we tried to print. This should be sufficient
+ // for users using print preview by default.
+ static crash_reporter::CrashKeyString<1024> crash_key("main-frame-url");
+ crash_key.Set(url.spec());
+}
+
+void PdfCompositorImpl::UpdateRequestsWithSubframeInfo(
+ uint64_t frame_guid,
+ const std::vector<uint64_t>& pending_subframes) {
+ // Check for each request's pending list.
+ for (auto it = requests_.begin(); it != requests_.end();) {
+ auto& request = *it;
+ // If the request needs this frame, we can remove the dependency, but
+ // update with this frame's pending list.
+ auto& pending_list = request->pending_subframes;
+ if (pending_list.erase(frame_guid)) {
+ std::copy(pending_subframes.begin(), pending_subframes.end(),
+ std::inserter(pending_list, pending_list.end()));
+ if (pending_list.empty()) {
+ // If the request isn't waiting on any subframes then it is ready.
+ // Fulfill the request now.
+ FulfillRequest(request->frame_guid, request->page_number,
+ std::move(request->serialized_content),
+ request->subframe_content_map,
+ std::move(request->callback));
+ it = requests_.erase(it);
+ continue;
+ }
+ }
+ // If the request still has pending frames, keep waiting.
+ ++it;
+ }
+}
+
+bool PdfCompositorImpl::IsReadyToComposite(
+ uint64_t frame_guid,
+ const ContentToFrameMap& subframe_content_map,
+ base::flat_set<uint64_t>* pending_subframes) {
+ pending_subframes->clear();
+ base::flat_set<uint64_t> visited_frames;
+ visited_frames.insert(frame_guid);
+ CheckFramesForReadiness(frame_guid, subframe_content_map, pending_subframes,
+ &visited_frames);
+ return pending_subframes->empty();
+}
+
+void PdfCompositorImpl::CheckFramesForReadiness(
+ uint64_t frame_guid,
+ const ContentToFrameMap& subframe_content_map,
+ base::flat_set<uint64_t>* pending_subframes,
+ base::flat_set<uint64_t>* visited) {
+ for (auto& subframe_content : subframe_content_map) {
+ auto subframe_guid = subframe_content.second;
+ // If this frame has been checked, skip it.
+ auto result = visited->insert(subframe_guid);
+ if (!result.second)
+ continue;
+
+ auto iter = frame_info_map_.find(subframe_guid);
+ if (iter == frame_info_map_.end()) {
+ pending_subframes->insert(subframe_guid);
+ } else {
+ CheckFramesForReadiness(subframe_guid, iter->second->subframe_content_map,
+ pending_subframes, visited);
+ }
+ }
+}
+
+void PdfCompositorImpl::HandleCompositionRequest(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ CompositeToPdfCallback callback) {
+ base::flat_set<uint64_t> pending_subframes;
+ if (IsReadyToComposite(frame_guid, subframe_content_map,
+ &pending_subframes)) {
+ FulfillRequest(frame_guid, page_num,
+ GetShmFromMojoHandle(std::move(serialized_content)),
+ subframe_content_map, std::move(callback));
+ return;
+ }
+
+ // When it is not ready yet, keep its information and
+ // wait until all dependent subframes are ready.
+ auto iter = frame_info_map_.find(frame_guid);
+ if (iter == frame_info_map_.end())
+ frame_info_map_[frame_guid] = std::make_unique<FrameInfo>();
+
+ requests_.push_back(std::make_unique<RequestInfo>(
+ frame_guid, page_num, GetShmFromMojoHandle(std::move(serialized_content)),
+ subframe_content_map, std::move(pending_subframes), std::move(callback)));
+}
+
+mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> shared_mem,
+ const ContentToFrameMap& subframe_content_map,
+ base::ReadOnlySharedMemoryRegion* region) {
+ DeserializationContext subframes =
+ GetDeserializationContext(subframe_content_map);
+
+ // Read in content and convert it into pdf.
+ SkMemoryStream stream(shared_mem->memory(), shared_mem->mapped_size());
+ int page_count = SkMultiPictureDocumentReadPageCount(&stream);
+ if (!page_count) {
+ DLOG(ERROR) << "CompositeToPdf: No page is read.";
+ return mojom::PdfCompositor::Status::CONTENT_FORMAT_ERROR;
+ }
+
+ std::vector<SkDocumentPage> pages(page_count);
+ SkDeserialProcs procs = DeserializationProcs(&subframes);
+ if (!SkMultiPictureDocumentRead(&stream, pages.data(), page_count, &procs)) {
+ DLOG(ERROR) << "CompositeToPdf: Page reading failed.";
+ return mojom::PdfCompositor::Status::CONTENT_FORMAT_ERROR;
+ }
+
+ 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();
+
+ base::MappedReadOnlyRegion region_mapping =
+ CreateReadOnlySharedMemoryRegion(wstream.bytesWritten());
+ if (!region_mapping.IsValid()) {
+ DLOG(ERROR) << "CompositeToPdf: Cannot create new shared memory region.";
+ return mojom::PdfCompositor::Status::HANDLE_MAP_ERROR;
+ }
+
+ wstream.copyToAndReset(region_mapping.mapping.memory());
+ *region = std::move(region_mapping.region);
+ return mojom::PdfCompositor::Status::SUCCESS;
+}
+
+sk_sp<SkPicture> PdfCompositorImpl::CompositeSubframe(uint64_t frame_guid) {
+ // The content of this frame should be available.
+ auto iter = frame_info_map_.find(frame_guid);
+ DCHECK(iter != frame_info_map_.end());
+
+ std::unique_ptr<FrameInfo>& frame_info = iter->second;
+ frame_info->composited = true;
+
+ // Composite subframes first.
+ DeserializationContext subframes =
+ GetDeserializationContext(frame_info->subframe_content_map);
+
+ // Composite the entire frame.
+ SkMemoryStream stream(iter->second->serialized_content->memory(),
+ iter->second->serialized_content->mapped_size());
+ SkDeserialProcs procs = DeserializationProcs(&subframes);
+ iter->second->content = SkPicture::MakeFromStream(&stream, &procs);
+ return iter->second->content;
+}
+
+PdfCompositorImpl::DeserializationContext
+PdfCompositorImpl::GetDeserializationContext(
+ const ContentToFrameMap& subframe_content_map) {
+ DeserializationContext subframes;
+ for (auto& content_info : subframe_content_map) {
+ uint32_t content_id = content_info.first;
+ uint64_t frame_guid = content_info.second;
+ auto frame_iter = frame_info_map_.find(frame_guid);
+ if (frame_iter == frame_info_map_.end())
+ continue;
+
+ if (frame_iter->second->composited)
+ subframes[content_id] = frame_iter->second->content;
+ else
+ subframes[content_id] = CompositeSubframe(frame_iter->first);
+ }
+ return subframes;
+}
+
+void PdfCompositorImpl::FulfillRequest(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ CompositeToPdfCallback callback) {
+ base::ReadOnlySharedMemoryRegion region;
+ auto status =
+ CompositeToPdf(frame_guid, page_num, std::move(serialized_content),
+ subframe_content_map, &region);
+ std::move(callback).Run(status, std::move(region));
+}
+
+PdfCompositorImpl::FrameContentInfo::FrameContentInfo(
+ std::unique_ptr<base::SharedMemory> content,
+ const ContentToFrameMap& map)
+ : serialized_content(std::move(content)), subframe_content_map(map) {}
+
+PdfCompositorImpl::FrameContentInfo::FrameContentInfo() {}
+
+PdfCompositorImpl::FrameContentInfo::~FrameContentInfo() {}
+
+PdfCompositorImpl::FrameInfo::FrameInfo() {}
+
+PdfCompositorImpl::FrameInfo::~FrameInfo() {}
+
+PdfCompositorImpl::RequestInfo::RequestInfo(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> content,
+ const ContentToFrameMap& content_info,
+ const base::flat_set<uint64_t>& pending_subframes,
+ mojom::PdfCompositor::CompositePageToPdfCallback callback)
+ : FrameContentInfo(std::move(content), content_info),
+ frame_guid(frame_guid),
+ page_number(page_num),
+ pending_subframes(pending_subframes),
+ callback(std::move(callback)) {}
+
+PdfCompositorImpl::RequestInfo::~RequestInfo() {}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_impl.h b/chromium/components/services/pdf_compositor/pdf_compositor_impl.h
new file mode 100644
index 00000000000..4fe2d80a040
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_impl.h
@@ -0,0 +1,191 @@
+// 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_SERVICES_PDF_COMPOSITOR_PDF_COMPOSITOR_IMPL_H_
+#define COMPONENTS_SERVICES_PDF_COMPOSITOR_PDF_COMPOSITOR_IMPL_H_
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/containers/flat_map.h"
+#include "base/containers/flat_set.h"
+#include "base/macros.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/memory/shared_memory.h"
+#include "base/optional.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
+#include "services/service_manager/public/cpp/service_context_ref.h"
+#include "third_party/skia/include/core/SkPicture.h"
+#include "third_party/skia/include/core/SkRefCnt.h"
+
+namespace printing {
+
+class PdfCompositorImpl : public mojom::PdfCompositor {
+ public:
+ PdfCompositorImpl(
+ const std::string& creator,
+ std::unique_ptr<service_manager::ServiceContextRef> service_ref);
+ ~PdfCompositorImpl() override;
+
+ // mojom::PdfCompositor
+ void NotifyUnavailableSubframe(uint64_t frame_guid) override;
+ void AddSubframeContent(
+ uint64_t frame_guid,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map) override;
+ void CompositePageToPdf(
+ uint64_t frame_guid,
+ uint32_t page_num,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ mojom::PdfCompositor::CompositePageToPdfCallback callback) override;
+ void CompositeDocumentToPdf(
+ uint64_t frame_guid,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ mojom::PdfCompositor::CompositeDocumentToPdfCallback callback) override;
+ void SetWebContentsURL(const GURL& url) override;
+
+ protected:
+ // This is the uniform underlying type for both
+ // mojom::PdfCompositor::CompositePageToPdfCallback and
+ // mojom::PdfCompositor::CompositeDocumentToPdfCallback.
+ using CompositeToPdfCallback =
+ base::OnceCallback<void(PdfCompositor::Status,
+ base::ReadOnlySharedMemoryRegion)>;
+
+ // Make this function virtual so tests can override it.
+ virtual void FulfillRequest(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ CompositeToPdfCallback callback);
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, IsReadyToComposite);
+ FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, MultiLayerDependency);
+ FRIEND_TEST_ALL_PREFIXES(PdfCompositorImplTest, DependencyLoop);
+
+ // The map needed during content deserialization. It stores the mapping
+ // between content id and its actual content.
+ using DeserializationContext = base::flat_map<uint32_t, sk_sp<SkPicture>>;
+
+ // Base structure to store a frame's content and its subframe
+ // content information.
+ struct FrameContentInfo {
+ FrameContentInfo(std::unique_ptr<base::SharedMemory> content,
+ const ContentToFrameMap& map);
+ FrameContentInfo();
+ ~FrameContentInfo();
+
+ // Serialized SkPicture content of this frame.
+ std::unique_ptr<base::SharedMemory> serialized_content;
+
+ // Frame content after composition with subframe content.
+ sk_sp<SkPicture> content;
+
+ // Subframe content id and its corresponding frame guid.
+ ContentToFrameMap subframe_content_map;
+ };
+
+ // Other than content, it also stores the status during frame composition.
+ struct FrameInfo : public FrameContentInfo {
+ FrameInfo();
+ ~FrameInfo();
+
+ // The following fields are used for storing composition status.
+ // Set to true when this frame's |serialized_content| is composed with
+ // subframe content and the final result is stored in |content|.
+ bool composited = false;
+ };
+
+ // Stores the mapping between frame's global unique ids and their
+ // corresponding frame information.
+ using FrameMap = base::flat_map<uint64_t, std::unique_ptr<FrameInfo>>;
+
+ // Stores the page or document's request information.
+ struct RequestInfo : public FrameContentInfo {
+ RequestInfo(uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> content,
+ const ContentToFrameMap& content_info,
+ const base::flat_set<uint64_t>& pending_subframes,
+ CompositeToPdfCallback callback);
+ ~RequestInfo();
+
+ uint64_t frame_guid;
+ base::Optional<uint32_t> page_number;
+
+ // All pending frame ids whose content is not available but needed
+ // for composition.
+ base::flat_set<uint64_t> pending_subframes;
+
+ CompositeToPdfCallback callback;
+ };
+
+ // Check whether any request is waiting for the specific subframe, if so,
+ // update its dependecy with the subframe's pending child frames.
+ void UpdateRequestsWithSubframeInfo(
+ uint64_t frame_guid,
+ const std::vector<uint64_t>& pending_subframes);
+
+ // Check whether the frame with a list of subframe content is ready to
+ // composite. If not, return all unavailable frames' ids in
+ // |pending_subframes|.
+ bool IsReadyToComposite(uint64_t frame_guid,
+ const ContentToFrameMap& subframe_content_map,
+ base::flat_set<uint64_t>* pending_subframes);
+
+ // Recursive check all the frames |frame_guid| depends on and put those
+ // not ready in |pending_subframes|.
+ void CheckFramesForReadiness(uint64_t frame_guid,
+ const ContentToFrameMap& subframe_content_map,
+ base::flat_set<uint64_t>* pending_subframes,
+ base::flat_set<uint64_t>* visited);
+
+ // The internal implementation for handling page and documentation composition
+ // requests.
+ void HandleCompositionRequest(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ mojo::ScopedSharedBufferHandle serialized_content,
+ const ContentToFrameMap& subframe_content_ids,
+ CompositeToPdfCallback callback);
+
+ // The core function for content composition and conversion to a pdf file.
+ mojom::PdfCompositor::Status CompositeToPdf(
+ uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> shared_mem,
+ const ContentToFrameMap& subframe_content_map,
+ base::ReadOnlySharedMemoryRegion* region);
+
+ // Composite the content of a subframe.
+ sk_sp<SkPicture> CompositeSubframe(uint64_t frame_guid);
+
+ bool CheckForPendingFrame(uint64_t frame_guid,
+ base::flat_set<uint64_t> visited_frames);
+
+ DeserializationContext GetDeserializationContext(
+ const ContentToFrameMap& subframe_content_map);
+
+ const std::unique_ptr<service_manager::ServiceContextRef> service_ref_;
+ const std::string creator_;
+
+ // Keep track of all frames' information indexed by frame id.
+ FrameMap frame_info_map_;
+
+ std::vector<std::unique_ptr<RequestInfo>> requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(PdfCompositorImpl);
+};
+
+} // namespace printing
+
+#endif // COMPONENTS_SERVICES_PDF_COMPOSITOR_PDF_COMPOSITOR_IMPL_H_
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc b/chromium/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc
new file mode 100644
index 00000000000..cc68640562c
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_impl_unittest.cc
@@ -0,0 +1,375 @@
+// 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 <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/run_loop.h"
+#include "base/test/scoped_task_environment.h"
+#include "components/crash/core/common/crash_key.h"
+#include "components/services/pdf_compositor/pdf_compositor_impl.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace printing {
+
+class MockPdfCompositorImpl : public PdfCompositorImpl {
+ public:
+ MockPdfCompositorImpl() : PdfCompositorImpl("unittest", nullptr) {}
+ ~MockPdfCompositorImpl() override {}
+
+ MOCK_METHOD2(OnFulfillRequest, void(uint64_t, int));
+
+ protected:
+ void FulfillRequest(uint64_t frame_guid,
+ base::Optional<uint32_t> page_num,
+ std::unique_ptr<base::SharedMemory> serialized_content,
+ const ContentToFrameMap& subframe_content_map,
+ CompositeToPdfCallback callback) override {
+ OnFulfillRequest(frame_guid, page_num.has_value() ? page_num.value() : -1);
+ }
+};
+
+class PdfCompositorImplTest : public testing::Test {
+ public:
+ PdfCompositorImplTest()
+ : task_environment_(
+ base::test::ScopedTaskEnvironment::MainThreadType::IO),
+ run_loop_(std::make_unique<base::RunLoop>()),
+ is_ready_(false) {}
+
+ void OnIsReadyToCompositeCallback(bool is_ready) {
+ is_ready_ = is_ready;
+ run_loop_->Quit();
+ }
+
+ bool ResultFromCallback() {
+ run_loop_->Run();
+ run_loop_ = std::make_unique<base::RunLoop>();
+ return is_ready_;
+ }
+
+ void OnCompositeToPdfCallback(mojom::PdfCompositor::Status status,
+ base::ReadOnlySharedMemoryRegion region) {
+ // A stub for testing, no implementation.
+ }
+
+ protected:
+ base::test::ScopedTaskEnvironment task_environment_;
+ std::unique_ptr<base::RunLoop> run_loop_;
+ bool is_ready_;
+};
+
+class PdfCompositorImplCrashKeyTest : public PdfCompositorImplTest {
+ public:
+ PdfCompositorImplCrashKeyTest() {}
+ ~PdfCompositorImplCrashKeyTest() override {}
+
+ void SetUp() override {
+ crash_reporter::ResetCrashKeysForTesting();
+ crash_reporter::InitializeCrashKeys();
+ }
+
+ void TearDown() override { crash_reporter::ResetCrashKeysForTesting(); }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PdfCompositorImplCrashKeyTest);
+};
+
+TEST_F(PdfCompositorImplTest, IsReadyToComposite) {
+ PdfCompositorImpl impl("unittest", nullptr);
+ // Frame 2 and 3 are painted.
+ impl.AddSubframeContent(2u, mojo::SharedBufferHandle::Create(10),
+ ContentToFrameMap());
+ impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
+ ContentToFrameMap());
+
+ // Frame 1 contains content 3 which corresponds to frame 2.
+ // Frame 1 should be ready as frame 2 is ready.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[3u] = 2u;
+ base::flat_set<uint64_t> pending_subframes;
+ bool is_ready = impl.IsReadyToComposite(1u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+
+ // If another page of frame 1 needs content 2 which corresponds to frame 3.
+ // This page is ready since frame 3 was painted also.
+ subframe_content_map.clear();
+ subframe_content_map[2u] = 3u;
+ is_ready = impl.IsReadyToComposite(1u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+
+ // Frame 1 with content 1, 2 and 3 should not be ready since content 1's
+ // content in frame 4 is not painted yet.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 4u;
+ subframe_content_map[2u] = 3u;
+ subframe_content_map[3u] = 2u;
+ is_ready = impl.IsReadyToComposite(1u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_FALSE(is_ready);
+ ASSERT_EQ(pending_subframes.size(), 1u);
+ EXPECT_EQ(*pending_subframes.begin(), 4u);
+
+ // Add content of frame 4. Now it is ready for composition.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 4u;
+ subframe_content_map[2u] = 3u;
+ subframe_content_map[3u] = 2u;
+ impl.AddSubframeContent(4u, mojo::SharedBufferHandle::Create(10),
+ ContentToFrameMap());
+ is_ready = impl.IsReadyToComposite(1u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+}
+
+TEST_F(PdfCompositorImplTest, MultiLayerDependency) {
+ PdfCompositorImpl impl("unittest", nullptr);
+ // Frame 3 has content 1 which refers to subframe 1.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[1u] = 1u;
+ impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+
+ // Frame 5 has content 3 which refers to subframe 3.
+ // Although frame 3's content is added, its subframe 1's content is not added.
+ // So frame 5 is not ready.
+ subframe_content_map.clear();
+ subframe_content_map[3u] = 3u;
+ base::flat_set<uint64_t> pending_subframes;
+ bool is_ready = impl.IsReadyToComposite(5u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_FALSE(is_ready);
+ ASSERT_EQ(pending_subframes.size(), 1u);
+ EXPECT_EQ(*pending_subframes.begin(), 1u);
+
+ // Frame 6 is not ready either since it needs frame 5 to be ready.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 5u;
+ is_ready = impl.IsReadyToComposite(6u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_FALSE(is_ready);
+ ASSERT_EQ(pending_subframes.size(), 1u);
+ EXPECT_EQ(*pending_subframes.begin(), 5u);
+
+ // When frame 1's content is added, frame 5 is ready.
+ impl.AddSubframeContent(1u, mojo::SharedBufferHandle::Create(10),
+ ContentToFrameMap());
+ subframe_content_map.clear();
+ subframe_content_map[3u] = 3u;
+ is_ready = impl.IsReadyToComposite(5u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+
+ // Add frame 5's content.
+ subframe_content_map.clear();
+ subframe_content_map[3u] = 3u;
+ impl.AddSubframeContent(5u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+
+ // Frame 6 is ready too.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 5u;
+ is_ready = impl.IsReadyToComposite(6u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+}
+
+TEST_F(PdfCompositorImplTest, DependencyLoop) {
+ PdfCompositorImpl impl("unittest", nullptr);
+ // Frame 3 has content 1, which refers to frame 1.
+ // Frame 1 has content 3, which refers to frame 3.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[3u] = 3u;
+ impl.AddSubframeContent(1u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 1u;
+ impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+
+ // Both frame 1 and 3 are painted, frame 5 should be ready.
+ base::flat_set<uint64_t> pending_subframes;
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 3u;
+ bool is_ready = impl.IsReadyToComposite(5u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+
+ // Frame 6 has content 7, which refers to frame 7.
+ subframe_content_map.clear();
+ subframe_content_map[7u] = 7u;
+ impl.AddSubframeContent(6, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+ // Frame 7 should be ready since frame 6's own content is added and it only
+ // depends on frame 7.
+ subframe_content_map.clear();
+ subframe_content_map[6u] = 6u;
+ is_ready = impl.IsReadyToComposite(7u, std::move(subframe_content_map),
+ &pending_subframes);
+ EXPECT_TRUE(is_ready);
+ EXPECT_TRUE(pending_subframes.empty());
+}
+
+TEST_F(PdfCompositorImplTest, MultiRequestsBasic) {
+ MockPdfCompositorImpl impl;
+ // Page 0 with frame 3 has content 1, which refers to frame 8.
+ // When the content is not available, the request is not fulfilled.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[1u] = 8u;
+ EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
+ impl.CompositePageToPdf(
+ 3u, 0, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ testing::Mock::VerifyAndClearExpectations(&impl);
+
+ // When frame 8's content is ready, the previous request should be fulfilled.
+ EXPECT_CALL(impl, OnFulfillRequest(3u, 0)).Times(1);
+ impl.AddSubframeContent(8u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+ testing::Mock::VerifyAndClearExpectations(&impl);
+
+ // The following requests which only depends on frame 8 should be
+ // immediately fulfilled.
+ EXPECT_CALL(impl, OnFulfillRequest(3u, 1)).Times(1);
+ EXPECT_CALL(impl, OnFulfillRequest(3u, -1)).Times(1);
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 8u;
+ impl.CompositePageToPdf(
+ 3u, 1, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 8u;
+ impl.CompositeDocumentToPdf(
+ 3u, mojo::SharedBufferHandle::Create(10), std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+}
+
+TEST_F(PdfCompositorImplTest, MultiRequestsOrder) {
+ MockPdfCompositorImpl impl;
+ // Page 0 with frame 3 has content 1, which refers to frame 8.
+ // When the content is not available, the request is not fulfilled.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[1u] = 8u;
+ EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
+ impl.CompositePageToPdf(
+ 3u, 0, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+
+ // The following requests which only depends on frame 8 should be
+ // immediately fulfilled.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 8u;
+ impl.CompositePageToPdf(
+ 3u, 1, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 8u;
+ impl.CompositeDocumentToPdf(
+ 3u, mojo::SharedBufferHandle::Create(10), std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ testing::Mock::VerifyAndClearExpectations(&impl);
+
+ // When frame 8's content is ready, the previous request should be
+ // fulfilled.
+ EXPECT_CALL(impl, OnFulfillRequest(3u, 0)).Times(1);
+ EXPECT_CALL(impl, OnFulfillRequest(3u, 1)).Times(1);
+ EXPECT_CALL(impl, OnFulfillRequest(3u, -1)).Times(1);
+ subframe_content_map.clear();
+ impl.AddSubframeContent(8u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+}
+
+TEST_F(PdfCompositorImplTest, MultiRequestsDepOrder) {
+ MockPdfCompositorImpl impl;
+ // Page 0 with frame 1 has content 1, which refers to frame
+ // 2. When the content is not available, the request is not
+ // fulfilled.
+ EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[1u] = 2u;
+ impl.CompositePageToPdf(
+ 1u, 0, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+
+ // Page 1 with frame 1 has content 1, which refers to frame
+ // 3. When the content is not available, the request is not
+ // fulfilled either.
+ subframe_content_map.clear();
+ subframe_content_map[1u] = 3u;
+ impl.CompositePageToPdf(
+ 1u, 1, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ testing::Mock::VerifyAndClearExpectations(&impl);
+
+ // When frame 3 and 2 become available, the pending requests should be
+ // satisfied, thus be fulfilled in order.
+ testing::Sequence order;
+ EXPECT_CALL(impl, OnFulfillRequest(1, 1)).Times(1).InSequence(order);
+ EXPECT_CALL(impl, OnFulfillRequest(1, 0)).Times(1).InSequence(order);
+ subframe_content_map.clear();
+ impl.AddSubframeContent(3u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+ impl.AddSubframeContent(2u, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map));
+}
+
+TEST_F(PdfCompositorImplTest, NotifyUnavailableSubframe) {
+ MockPdfCompositorImpl impl;
+ // Page 0 with frame 3 has content 1, which refers to frame 8.
+ // When the content is not available, the request is not fulfilled.
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[1u] = 8u;
+ EXPECT_CALL(impl, OnFulfillRequest(testing::_, testing::_)).Times(0);
+ impl.CompositePageToPdf(
+ 3u, 0, mojo::SharedBufferHandle::Create(10),
+ std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorImplTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ testing::Mock::VerifyAndClearExpectations(&impl);
+
+ // Notifies that frame 8's unavailable, the previous request should be
+ // fulfilled.
+ EXPECT_CALL(impl, OnFulfillRequest(3u, 0)).Times(1);
+ impl.NotifyUnavailableSubframe(8u);
+ testing::Mock::VerifyAndClearExpectations(&impl);
+}
+
+TEST_F(PdfCompositorImplCrashKeyTest, SetCrashKey) {
+ PdfCompositorImpl impl("unittest", nullptr);
+ std::string url_str("https://www.example.com/");
+ GURL url(url_str);
+ impl.SetWebContentsURL(url);
+
+ EXPECT_EQ(crash_reporter::GetCrashKeyValue("main-frame-url"), url_str);
+}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_manifest.json b/chromium/components/services/pdf_compositor/pdf_compositor_manifest.json
new file mode 100644
index 00000000000..ecbad4346d6
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_manifest.json
@@ -0,0 +1,18 @@
+{
+ "name": "pdf_compositor",
+ "display_name": "PDF Compositor Service",
+ "sandbox_type": "pdf_compositor",
+ "interface_provider_specs": {
+ "service_manager:connector": {
+ "provides": {
+ "compositor": [ "printing.mojom.PdfCompositor" ]
+ },
+ "requires": {
+ "*": [ "app" ],
+ "content_browser": [ "font_loader" ],
+ "service_manager": [ "service_manager:all_users" ],
+ "ui": [ "discardable_memory" ]
+ }
+ }
+ }
+}
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_service.cc b/chromium/components/services/pdf_compositor/pdf_compositor_service.cc
new file mode 100644
index 00000000000..e71b72ab482
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_service.cc
@@ -0,0 +1,122 @@
+// 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/services/pdf_compositor/pdf_compositor_service.h"
+
+#include <utility>
+
+#include <memory>
+
+#include "base/lazy_instance.h"
+#include "base/memory/discardable_memory.h"
+#include "build/build_config.h"
+#include "components/services/pdf_compositor/pdf_compositor_impl.h"
+#include "components/services/pdf_compositor/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"
+#include "services/ui/public/interfaces/constants.mojom.h"
+#include "ui/base/ui_base_features.h"
+
+#if defined(OS_WIN)
+#include "content/public/child/dwrite_font_proxy_init_win.h"
+#elif defined(OS_MACOSX)
+#include "third_party/blink/public/platform/platform.h"
+#include "third_party/skia/include/ports/SkFontMgr.h"
+#elif defined(OS_POSIX) && !defined(OS_ANDROID)
+#include "third_party/blink/public/platform/platform.h"
+#endif
+
+namespace {
+
+void OnPdfCompositorRequest(
+ const std::string& creator,
+ service_manager::ServiceContextRefFactory* ref_factory,
+ printing::mojom::PdfCompositorRequest request) {
+ mojo::MakeStrongBinding(std::make_unique<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() {
+#if defined(OS_WIN)
+ content::UninitializeDWriteFontProxy();
+#endif
+}
+
+// static
+std::unique_ptr<service_manager::Service> PdfCompositorService::Create(
+ const std::string& creator) {
+#if defined(OS_WIN)
+ // Initialize direct write font proxy so skia can use it.
+ content::InitializeDWriteFontProxy();
+#endif
+ return std::make_unique<printing::PdfCompositorService>(creator);
+}
+
+void PdfCompositorService::PrepareToStart() {
+ // Set up discardable memory manager.
+ discardable_memory::mojom::DiscardableSharedMemoryManagerPtr manager_ptr;
+ if (features::IsMashEnabled()) {
+#if defined(USE_AURA)
+ context()->connector()->BindInterface(ui::mojom::kServiceName,
+ &manager_ptr);
+#else
+ NOTREACHED();
+#endif
+ } else {
+ context()->connector()->BindInterface(content::mojom::kBrowserServiceName,
+ &manager_ptr);
+ }
+ discardable_shared_memory_manager_ = std::make_unique<
+ discardable_memory::ClientDiscardableSharedMemoryManager>(
+ std::move(manager_ptr), content::UtilityThread::Get()->GetIOTaskRunner());
+ DCHECK(discardable_shared_memory_manager_);
+ base::DiscardableMemoryAllocator::SetInstance(
+ discardable_shared_memory_manager_.get());
+
+#if defined(OS_POSIX) && !defined(OS_ANDROID)
+ // Check that we have sandbox support on this platform.
+ DCHECK(blink::Platform::Current()->GetSandboxSupport());
+#endif
+
+#if defined(OS_MACOSX)
+ // Check that font access is granted.
+ // This doesn't do comprehensive tests to make sure fonts can work properly.
+ // It is just a quick and simple check to catch things like improper sandbox
+ // policy setup.
+ DCHECK(SkFontMgr::RefDefault()->countFamilies());
+
+ // Initialize a connection to FontLoaderMac service so blink platform's web
+ // sandbox support can communicate with it to load font.
+ content::UtilityThread::Get()->InitializeFontLoaderMac(
+ context()->connector());
+#endif
+}
+
+void PdfCompositorService::OnStart() {
+ PrepareToStart();
+
+ ref_factory_ = std::make_unique<service_manager::ServiceContextRefFactory>(
+ context()->CreateQuitClosure());
+ 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(interface_name, std::move(interface_pipe));
+}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_service.h b/chromium/components/services/pdf_compositor/pdf_compositor_service.h
new file mode 100644
index 00000000000..48ec8fb848d
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_service.h
@@ -0,0 +1,55 @@
+// 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_SERVICES_PDF_COMPOSITOR_PDF_COMPOSITOR_SERVICE_H_
+#define COMPONENTS_SERVICES_PDF_COMPOSITOR_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/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.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;
+
+ virtual void PrepareToStart();
+
+ 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_SERVICES_PDF_COMPOSITOR_PDF_COMPOSITOR_SERVICE_H_
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest.cc b/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest.cc
new file mode 100644
index 00000000000..ccdc45dd33d
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest.cc
@@ -0,0 +1,246 @@
+// 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 <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/files/file_util.h"
+#include "base/memory/read_only_shared_memory_region.h"
+#include "base/path_service.h"
+#include "base/run_loop.h"
+#include "base/test/test_discardable_memory_allocator.h"
+#include "cc/paint/paint_flags.h"
+#include "cc/paint/skia_paint_canvas.h"
+#include "components/services/pdf_compositor/pdf_compositor_service.h"
+#include "components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h"
+#include "components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom.h"
+#include "mojo/public/cpp/bindings/binding_set.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+#include "services/service_manager/public/cpp/binder_registry.h"
+#include "services/service_manager/public/cpp/service_context.h"
+#include "services/service_manager/public/cpp/service_test.h"
+#include "services/service_manager/public/mojom/service_factory.mojom.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkStream.h"
+#include "third_party/skia/src/utils/SkMultiPictureDocument.h"
+
+namespace printing {
+
+// In order to test PdfCompositorService, this class overrides PrepareToStart()
+// to do nothing. So the test discardable memory allocator set up by
+// PdfCompositorServiceTest will be used. Also checks for the service setup are
+// skipped since we don't have those setups in unit tests.
+class PdfCompositorTestService : public printing::PdfCompositorService {
+ public:
+ explicit PdfCompositorTestService(const std::string& creator)
+ : PdfCompositorService(creator) {}
+ ~PdfCompositorTestService() override {}
+
+ // PdfCompositorService:
+ void PrepareToStart() override {}
+};
+
+class PdfServiceTestClient : public service_manager::test::ServiceTestClient,
+ public service_manager::mojom::ServiceFactory {
+ public:
+ explicit PdfServiceTestClient(service_manager::test::ServiceTest* test)
+ : service_manager::test::ServiceTestClient(test) {
+ registry_.AddInterface<service_manager::mojom::ServiceFactory>(
+ base::Bind(&PdfServiceTestClient::Create, base::Unretained(this)));
+ }
+ ~PdfServiceTestClient() override {}
+
+ // service_manager::Service
+ void OnBindInterface(const service_manager::BindSourceInfo& source_info,
+ const std::string& interface_name,
+ mojo::ScopedMessagePipeHandle interface_pipe) override {
+ registry_.BindInterface(interface_name, std::move(interface_pipe));
+ }
+
+ // service_manager::mojom::ServiceFactory
+ void CreateService(
+ service_manager::mojom::ServiceRequest request,
+ const std::string& name,
+ service_manager::mojom::PIDReceiverPtr pid_receiver) override {
+ if (!name.compare(mojom::kServiceName)) {
+ service_context_ = std::make_unique<service_manager::ServiceContext>(
+ std::make_unique<PdfCompositorTestService>("pdf_compositor_unittest"),
+ std::move(request));
+ }
+ }
+
+ void Create(service_manager::mojom::ServiceFactoryRequest request) {
+ service_factory_bindings_.AddBinding(this, std::move(request));
+ }
+
+ private:
+ service_manager::BinderRegistry registry_;
+ mojo::BindingSet<service_manager::mojom::ServiceFactory>
+ service_factory_bindings_;
+ std::unique_ptr<service_manager::ServiceContext> service_context_;
+};
+
+class PdfCompositorServiceTest : public service_manager::test::ServiceTest {
+ public:
+ PdfCompositorServiceTest() : ServiceTest("pdf_compositor_service_unittest") {}
+ ~PdfCompositorServiceTest() override {}
+
+ MOCK_METHOD1(CallbackOnCompositeSuccess,
+ void(const base::ReadOnlySharedMemoryRegion&));
+ MOCK_METHOD1(CallbackOnCompositeStatus, void(mojom::PdfCompositor::Status));
+ void OnCompositeToPdfCallback(mojom::PdfCompositor::Status status,
+ base::ReadOnlySharedMemoryRegion region) {
+ if (status == mojom::PdfCompositor::Status::SUCCESS)
+ CallbackOnCompositeSuccess(region);
+ else
+ CallbackOnCompositeStatus(status);
+ run_loop_->Quit();
+ }
+
+ MOCK_METHOD0(ConnectionClosed, void());
+
+ protected:
+ // service_manager::test::ServiceTest:
+ void SetUp() override {
+ base::DiscardableMemoryAllocator::SetInstance(
+ &discardable_memory_allocator_);
+ ServiceTest::SetUp();
+
+ ASSERT_FALSE(compositor_);
+ connector()->BindInterface(mojom::kServiceName, &compositor_);
+ ASSERT_TRUE(compositor_);
+
+ run_loop_ = std::make_unique<base::RunLoop>();
+ }
+
+ void TearDown() override {
+ // Clean up
+ compositor_.reset();
+ base::DiscardableMemoryAllocator::SetInstance(nullptr);
+ }
+
+ std::unique_ptr<service_manager::Service> CreateService() override {
+ return std::make_unique<PdfServiceTestClient>(this);
+ }
+
+ mojo::ScopedSharedBufferHandle CreateMSKP() {
+ SkDynamicMemoryWStream stream;
+ sk_sp<SkDocument> doc = SkMakeMultiPictureDocument(&stream);
+ cc::SkiaPaintCanvas canvas(doc->beginPage(800, 600));
+ SkRect rect = SkRect::MakeXYWH(10, 10, 250, 250);
+ cc::PaintFlags flags;
+ flags.setAntiAlias(false);
+ flags.setColor(SK_ColorRED);
+ flags.setStyle(cc::PaintFlags::kFill_Style);
+ canvas.drawRect(rect, flags);
+ doc->endPage();
+ doc->close();
+
+ size_t len = stream.bytesWritten();
+ base::MappedReadOnlyRegion memory =
+ base::ReadOnlySharedMemoryRegion::Create(len);
+ CHECK(memory.IsValid());
+ stream.copyTo(memory.mapping.memory());
+ return mojo::WrapReadOnlySharedMemoryRegion(std::move(memory.region));
+ }
+
+ void CallCompositorWithSuccess(mojom::PdfCompositorPtr ptr) {
+ static constexpr uint64_t kFrameGuid = 1234;
+ auto handle = CreateMSKP();
+ ASSERT_TRUE(handle->is_valid());
+ EXPECT_CALL(*this, CallbackOnCompositeSuccess(testing::_)).Times(1);
+ ptr->CompositeDocumentToPdf(
+ kFrameGuid, std::move(handle), ContentToFrameMap(),
+ base::BindOnce(&PdfCompositorServiceTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ run_loop_->Run();
+ }
+
+ std::unique_ptr<base::RunLoop> run_loop_;
+ mojom::PdfCompositorPtr compositor_;
+ base::TestDiscardableMemoryAllocator discardable_memory_allocator_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(PdfCompositorServiceTest);
+};
+
+// Test callback function is called on error conditions in service.
+TEST_F(PdfCompositorServiceTest, InvokeCallbackOnContentError) {
+ EXPECT_CALL(*this, CallbackOnCompositeStatus(
+ mojom::PdfCompositor::Status::CONTENT_FORMAT_ERROR))
+ .Times(1);
+ compositor_->CompositeDocumentToPdf(
+ 5u, mojo::SharedBufferHandle::Create(10), ContentToFrameMap(),
+ base::BindOnce(&PdfCompositorServiceTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ run_loop_->Run();
+}
+
+// Test callback function is called upon success.
+TEST_F(PdfCompositorServiceTest, InvokeCallbackOnSuccess) {
+ CallCompositorWithSuccess(std::move(compositor_));
+}
+
+// Test coexistence of multiple service instances.
+TEST_F(PdfCompositorServiceTest, MultipleServiceInstances) {
+ // One service can bind multiple interfaces.
+ mojom::PdfCompositorPtr another_compositor;
+ ASSERT_FALSE(another_compositor);
+ connector()->BindInterface(mojom::kServiceName, &another_compositor);
+ ASSERT_TRUE(another_compositor);
+ ASSERT_NE(compositor_.get(), another_compositor.get());
+
+ // Terminating one interface won't affect another.
+ compositor_.reset();
+ CallCompositorWithSuccess(std::move(another_compositor));
+}
+
+// Test data structures and content of multiple service instances
+// are independent from each other.
+TEST_F(PdfCompositorServiceTest, IndependentServiceInstances) {
+ // Create a new connection 2.
+ mojom::PdfCompositorPtr compositor2;
+ ASSERT_FALSE(compositor2);
+ connector()->BindInterface(mojom::kServiceName, &compositor2);
+ ASSERT_TRUE(compositor2);
+
+ // In original connection, add frame 4 with content 2 referring
+ // to subframe 1.
+ compositor_->AddSubframeContent(1u, CreateMSKP(), ContentToFrameMap());
+
+ // Original connection can use this subframe 1.
+ EXPECT_CALL(*this, CallbackOnCompositeSuccess(testing::_)).Times(1);
+ ContentToFrameMap subframe_content_map;
+ subframe_content_map[2u] = 1u;
+
+ compositor_->CompositeDocumentToPdf(
+ 4u, CreateMSKP(), std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorServiceTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ run_loop_->Run();
+ testing::Mock::VerifyAndClearExpectations(this);
+
+ // Connection 2 doesn't know about subframe 1.
+ subframe_content_map.clear();
+ subframe_content_map[2u] = 1u;
+ EXPECT_CALL(*this, CallbackOnCompositeSuccess(testing::_)).Times(0);
+ compositor2->CompositeDocumentToPdf(
+ 4u, CreateMSKP(), std::move(subframe_content_map),
+ base::BindOnce(&PdfCompositorServiceTest::OnCompositeToPdfCallback,
+ base::Unretained(this)));
+ testing::Mock::VerifyAndClearExpectations(this);
+
+ // Add info about subframe 1 to connection 2 so it can use it.
+ EXPECT_CALL(*this, CallbackOnCompositeSuccess(testing::_)).Times(1);
+ // Add subframe 1's content.
+ // Now all content needed for previous request is ready.
+ compositor2->AddSubframeContent(1u, CreateMSKP(), ContentToFrameMap());
+ run_loop_ = std::make_unique<base::RunLoop>();
+ run_loop_->Run();
+}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest_manifest.json b/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest_manifest.json
new file mode 100644
index 00000000000..19a847f67ce
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/pdf_compositor_service_unittest_manifest.json
@@ -0,0 +1,17 @@
+{
+ "name": "pdf_compositor_service_unittest",
+ "display_name": "Pdf Compositor Service Unittest",
+ "interface_provider_specs": {
+ "service_manager:connector": {
+ "provides": {
+ "service_manager:service_factory": [
+ "service_manager.mojom.ServiceFactory"
+ ]
+ },
+ "requires": {
+ "pdf_compositor": [ "compositor" ],
+ "service_manager": [ "service_manager:service_manager" ]
+ }
+ }
+ }
+}
diff --git a/chromium/components/services/pdf_compositor/public/cpp/BUILD.gn b/chromium/components/services/pdf_compositor/public/cpp/BUILD.gn
new file mode 100644
index 00000000000..414da314966
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/cpp/BUILD.gn
@@ -0,0 +1,34 @@
+# 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("factory") {
+ sources = [
+ "pdf_compositor_service_factory.cc",
+ "pdf_compositor_service_factory.h",
+ ]
+
+ deps = [
+ "//components/services/pdf_compositor/",
+ "//content/public/common",
+ "//content/public/utility",
+ ]
+
+ public_deps = [
+ "//components/services/pdf_compositor/public/interfaces",
+ "//services/service_manager/public/cpp",
+ ]
+}
+
+source_set("utils") {
+ sources = [
+ "pdf_service_mojo_utils.cc",
+ "pdf_service_mojo_utils.h",
+ ]
+
+ public_deps = [
+ "//mojo/public/cpp/system:system",
+ ]
+}
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc b/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc
new file mode 100644
index 00000000000..0af223b62b1
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.cc
@@ -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.
+
+#include "components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.h"
+
+#include "build/build_config.h"
+#include "components/services/pdf_compositor/pdf_compositor_service.h"
+#include "content/public/utility/utility_thread.h"
+#include "third_party/blink/public/platform/web_image_generator.h"
+#include "third_party/skia/include/core/SkGraphics.h"
+
+namespace printing {
+
+std::unique_ptr<service_manager::Service> CreatePdfCompositorService(
+ const std::string& creator) {
+#if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_FUCHSIA)
+ content::UtilityThread::Get()->EnsureBlinkInitializedWithSandboxSupport();
+#else
+ content::UtilityThread::Get()->EnsureBlinkInitialized();
+#endif
+ // Hook up blink's codecs so skia can call them.
+ SkGraphics::SetImageGeneratorFromEncodedDataFactory(
+ blink::WebImageGenerator::CreateAsSkImageGenerator);
+ return printing::PdfCompositorService::Create(creator);
+}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.h b/chromium/components/services/pdf_compositor/public/cpp/pdf_compositor_service_factory.h
new file mode 100644
index 00000000000..371f47294a0
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/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_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_COMPOSITOR_SERVICE_FACTORY_H_
+#define COMPONENTS_SERVICES_PDF_COMPOSITOR_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_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_COMPOSITOR_SERVICE_FACTORY_H_
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h
new file mode 100644
index 00000000000..dfc3b5f8e15
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_types.h
@@ -0,0 +1,17 @@
+// Copyright 2018 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_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_TYPES_H_
+#define COMPONENTS_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_TYPES_H_
+
+#include "base/containers/flat_map.h"
+
+namespace printing {
+
+// Create an alias for map<uint32, uint64> type.
+using ContentToFrameMap = base::flat_map<uint32_t, uint64_t>;
+
+} // namespace printing
+
+#endif // COMPONENTS_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_TYPES_H_
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc
new file mode 100644
index 00000000000..b814cf60dfe
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.cc
@@ -0,0 +1,53 @@
+// 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/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h"
+
+#include <utility>
+
+#include "base/memory/shared_memory.h"
+#include "base/memory/writable_shared_memory_region.h"
+#include "mojo/public/cpp/system/platform_handle.h"
+
+namespace printing {
+
+base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size) {
+ mojo::ScopedSharedBufferHandle handle =
+ mojo::SharedBufferHandle::Create(size);
+ base::WritableSharedMemoryRegion writable_region =
+ UnwrapWritableSharedMemoryRegion(std::move(handle));
+ base::WritableSharedMemoryMapping mapping = writable_region.Map();
+ if (!mapping.IsValid())
+ return {};
+
+ base::ReadOnlySharedMemoryRegion readonly_region =
+ base::WritableSharedMemoryRegion::ConvertToReadOnly(
+ std::move(writable_region));
+ return {std::move(readonly_region), std::move(mapping)};
+}
+
+std::unique_ptr<base::SharedMemory> GetShmFromMojoHandle(
+ mojo::ScopedSharedBufferHandle handle) {
+ base::SharedMemoryHandle memory_handle;
+ size_t memory_size = 0;
+ mojo::UnwrappedSharedMemoryHandleProtection protection;
+
+ const MojoResult result = mojo::UnwrapSharedMemoryHandle(
+ std::move(handle), &memory_handle, &memory_size, &protection);
+ if (result != MOJO_RESULT_OK)
+ return nullptr;
+
+ DCHECK_GT(memory_size, 0u);
+ const bool read_only =
+ protection == mojo::UnwrappedSharedMemoryHandleProtection::kReadOnly;
+ std::unique_ptr<base::SharedMemory> shm =
+ std::make_unique<base::SharedMemory>(memory_handle, read_only);
+ if (!shm->Map(memory_size)) {
+ DLOG(ERROR) << "Map shared memory failed.";
+ return nullptr;
+ }
+ return shm;
+}
+
+} // namespace printing
diff --git a/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_utils.h
new file mode 100644
index 00000000000..1e0bd92fe1e
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/cpp/pdf_service_mojo_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 COMPONENTS_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_UTILS_H_
+#define COMPONENTS_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_UTILS_H_
+
+#include <memory>
+
+#include "base/memory/read_only_shared_memory_region.h"
+#include "mojo/public/cpp/system/buffer.h"
+
+namespace base {
+class SharedMemory;
+} // namespace base
+
+namespace printing {
+
+// Similar to base::ReadOnlySharedMemoryRegion::Create(), except it works inside
+// sandboxed environments.
+base::MappedReadOnlyRegion CreateReadOnlySharedMemoryRegion(size_t size);
+
+std::unique_ptr<base::SharedMemory> GetShmFromMojoHandle(
+ mojo::ScopedSharedBufferHandle handle);
+
+} // namespace printing
+
+#endif // COMPONENTS_SERVICES_PDF_COMPOSITOR_PUBLIC_CPP_PDF_SERVICE_MOJO_UTILS_H_
diff --git a/chromium/components/services/pdf_compositor/public/interfaces/BUILD.gn b/chromium/components/services/pdf_compositor/public/interfaces/BUILD.gn
new file mode 100644
index 00000000000..804ccb4a828
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/interfaces/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.
+
+import("//mojo/public/tools/bindings/mojom.gni")
+
+mojom("interfaces") {
+ sources = [
+ "pdf_compositor.mojom",
+ ]
+ public_deps = [
+ "//mojo/public/mojom/base",
+ "//url/mojom:url_mojom_gurl",
+ ]
+}
diff --git a/chromium/components/services/pdf_compositor/public/interfaces/OWNERS b/chromium/components/services/pdf_compositor/public/interfaces/OWNERS
new file mode 100644
index 00000000000..08850f42120
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/interfaces/OWNERS
@@ -0,0 +1,2 @@
+per-file *.mojom=set noparent
+per-file *.mojom=file://ipc/SECURITY_OWNERS
diff --git a/chromium/components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom b/chromium/components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom
new file mode 100644
index 00000000000..934b04e83ae
--- /dev/null
+++ b/chromium/components/services/pdf_compositor/public/interfaces/pdf_compositor.mojom
@@ -0,0 +1,62 @@
+// 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;
+
+import "mojo/public/mojom/base/shared_memory.mojom";
+import "url/mojom/url.mojom";
+
+const string kServiceName = "pdf_compositor";
+
+interface PdfCompositor {
+ // The status of composition and conversion execution.
+ // These values are persisted to logs. Entries should not be renumbered and
+ // numeric values should never be reused.
+ enum Status {
+ SUCCESS = 0,
+ HANDLE_MAP_ERROR = 1,
+ CONTENT_FORMAT_ERROR = 2,
+ COMPOSTING_FAILURE = 3,
+ };
+
+ // Notifies that a subframe is unavailable, such as the render frame process
+ // hosting it crashed or terminated. The subframe will be composited with no
+ // content in the composited result.
+ // |frame_guid| is this subframe's global unique id.
+ NotifyUnavailableSubframe(uint64 frame_guid);
+
+ // Adds the content of a subframe for composition.
+ // |frame_guid| is this subframe's global unique id.
+ // |serialized_content| points to a buffer of a serialized Skia picture which
+ // has the painted content of this frame.
+ // |subframe_content_map| records content id and its corresponding frame's
+ // global unique id.
+ AddSubframeContent(uint64 frame_guid, handle<shared_buffer> serialized_content,
+ map<uint32, uint64> subframe_content_info);
+
+ // Requests to composite a page and convert it into a PDF file.
+ // |frame_guid| is the global unique id of the frame to be composited.
+ // |page_num| is zero-based sequence number of page.
+ // |sk_handle| points to a buffer of a Skia MultiPictureDocument which has
+ // the drawing content of this frame or a page of this frame.
+ // |subframe_content_map| records content id and its corresponding frame's
+ // global unique id.
+ CompositePageToPdf(uint64 frame_guid, uint32 page_num,
+ handle<shared_buffer> sk_handle,
+ map<uint32, uint64> subframe_content_info)
+ => (Status status,
+ mojo_base.mojom.ReadOnlySharedMemoryRegion? pdf_region);
+
+ // Requests to composite the entire document and convert it into a PDF file.
+ // All the arguments carry the same meaning as CompositePageToPdf() above,
+ // except this call doesn't have |page_num|.
+ CompositeDocumentToPdf(uint64 frame_guid, handle<shared_buffer> sk_handle,
+ map<uint32, uint64> subframe_content_info)
+ => (Status status,
+ mojo_base.mojom.ReadOnlySharedMemoryRegion? pdf_region);
+
+ // Sets the URL which is committed in the main frame of the WebContents,
+ // for use in crash diagnosis.
+ SetWebContentsURL(url.mojom.Url url);
+};