print_compositor_impl.cc 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/services/print_compositor/print_compositor_impl.h"
  5. #include <algorithm>
  6. #include <tuple>
  7. #include <utility>
  8. #include "base/containers/contains.h"
  9. #include "base/logging.h"
  10. #include "base/memory/discardable_memory.h"
  11. #include "base/task/single_thread_task_runner.h"
  12. #include "base/trace_event/trace_event.h"
  13. #include "build/build_config.h"
  14. #include "components/crash/core/common/crash_key.h"
  15. #include "components/discardable_memory/client/client_discardable_shared_memory_manager.h"
  16. #include "components/services/print_compositor/public/cpp/print_service_mojo_types.h"
  17. #include "content/public/utility/utility_thread.h"
  18. #include "mojo/public/cpp/bindings/pending_remote.h"
  19. #include "mojo/public/cpp/system/platform_handle.h"
  20. #include "printing/common/metafile_utils.h"
  21. #include "third_party/blink/public/platform/web_image_generator.h"
  22. #include "third_party/skia/include/core/SkCanvas.h"
  23. #include "third_party/skia/include/core/SkDocument.h"
  24. #include "third_party/skia/include/core/SkGraphics.h"
  25. #include "third_party/skia/include/core/SkSerialProcs.h"
  26. #include "third_party/skia/src/utils/SkMultiPictureDocument.h"
  27. #include "ui/accessibility/ax_tree_update.h"
  28. #if BUILDFLAG(IS_WIN)
  29. #include "content/public/child/dwrite_font_proxy_init_win.h"
  30. #elif BUILDFLAG(IS_APPLE)
  31. #include "third_party/blink/public/platform/platform.h"
  32. #include "third_party/skia/include/core/SkFontMgr.h"
  33. #elif BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
  34. #include "third_party/blink/public/platform/platform.h"
  35. #endif
  36. namespace printing {
  37. PrintCompositorImpl::PrintCompositorImpl(
  38. mojo::PendingReceiver<mojom::PrintCompositor> receiver,
  39. bool initialize_environment,
  40. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
  41. : io_task_runner_(std::move(io_task_runner)) {
  42. if (receiver)
  43. receiver_.Bind(std::move(receiver));
  44. if (!initialize_environment)
  45. return;
  46. #if BUILDFLAG(IS_WIN)
  47. // Initialize direct write font proxy so skia can use it.
  48. content::InitializeDWriteFontProxy();
  49. #endif
  50. // Hook up blink's codecs so skia can call them.
  51. SkGraphics::SetImageGeneratorFromEncodedDataFactory(
  52. blink::WebImageGenerator::CreateAsSkImageGenerator);
  53. #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
  54. content::UtilityThread::Get()->EnsureBlinkInitializedWithSandboxSupport();
  55. // Check that we have sandbox support on this platform.
  56. DCHECK(blink::Platform::Current()->GetSandboxSupport());
  57. #else
  58. content::UtilityThread::Get()->EnsureBlinkInitialized();
  59. #endif
  60. #if BUILDFLAG(IS_APPLE)
  61. // Check that font access is granted.
  62. // This doesn't do comprehensive tests to make sure fonts can work properly.
  63. // It is just a quick and simple check to catch things like improper sandbox
  64. // policy setup.
  65. DCHECK(SkFontMgr::RefDefault()->countFamilies());
  66. #endif
  67. }
  68. PrintCompositorImpl::~PrintCompositorImpl() {
  69. #if BUILDFLAG(IS_WIN)
  70. content::UninitializeDWriteFontProxy();
  71. #endif
  72. }
  73. void PrintCompositorImpl::SetDiscardableSharedMemoryManager(
  74. mojo::PendingRemote<
  75. discardable_memory::mojom::DiscardableSharedMemoryManager> manager) {
  76. // Set up discardable memory manager.
  77. mojo::PendingRemote<discardable_memory::mojom::DiscardableSharedMemoryManager>
  78. manager_remote(std::move(manager));
  79. discardable_shared_memory_manager_ = base::MakeRefCounted<
  80. discardable_memory::ClientDiscardableSharedMemoryManager>(
  81. std::move(manager_remote), io_task_runner_);
  82. base::DiscardableMemoryAllocator::SetInstance(
  83. discardable_shared_memory_manager_.get());
  84. }
  85. void PrintCompositorImpl::NotifyUnavailableSubframe(uint64_t frame_guid) {
  86. // Add this frame into the map.
  87. DCHECK(!base::Contains(frame_info_map_, frame_guid));
  88. auto& frame_info =
  89. frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>())
  90. .first->second;
  91. frame_info->composited = true;
  92. // Set content to be nullptr so it will be replaced by an empty picture during
  93. // deserialization of its parent.
  94. frame_info->content = nullptr;
  95. // Update the requests in case any of them might be waiting for this frame.
  96. UpdateRequestsWithSubframeInfo(frame_guid, std::vector<uint64_t>());
  97. }
  98. void PrintCompositorImpl::AddSubframeContent(
  99. uint64_t frame_guid,
  100. base::ReadOnlySharedMemoryRegion serialized_content,
  101. const ContentToFrameMap& subframe_content_map) {
  102. base::ReadOnlySharedMemoryMapping mapping = serialized_content.Map();
  103. if (!mapping.IsValid()) {
  104. NotifyUnavailableSubframe(frame_guid);
  105. return;
  106. }
  107. // Add this frame and its serialized content.
  108. DCHECK(!base::Contains(frame_info_map_, frame_guid));
  109. frame_info_map_.emplace(frame_guid, std::make_unique<FrameInfo>(
  110. mapping.GetMemoryAsSpan<uint8_t>(),
  111. subframe_content_map));
  112. // If there is no request, we do nothing more.
  113. // Otherwise, we need to check whether any request actually waits on this
  114. // frame content.
  115. if (requests_.empty())
  116. return;
  117. // Get the pending list which is a list of subframes this frame needs
  118. // but are still unavailable.
  119. std::vector<uint64_t> pending_subframes;
  120. for (auto& subframe_content : subframe_content_map) {
  121. auto subframe_guid = subframe_content.second;
  122. if (!base::Contains(frame_info_map_, subframe_guid))
  123. pending_subframes.push_back(subframe_guid);
  124. }
  125. // Update the requests in case any of them is waiting for this frame.
  126. UpdateRequestsWithSubframeInfo(frame_guid, pending_subframes);
  127. }
  128. #if BUILDFLAG(ENABLE_TAGGED_PDF)
  129. void PrintCompositorImpl::SetAccessibilityTree(
  130. const ui::AXTreeUpdate& accessibility_tree) {
  131. accessibility_tree_ = accessibility_tree;
  132. }
  133. #endif
  134. void PrintCompositorImpl::CompositePageToPdf(
  135. uint64_t frame_guid,
  136. base::ReadOnlySharedMemoryRegion serialized_content,
  137. const ContentToFrameMap& subframe_content_map,
  138. mojom::PrintCompositor::CompositePageToPdfCallback callback) {
  139. TRACE_EVENT0("print", "PrintCompositorImpl::CompositePageToPdf");
  140. if (docinfo_)
  141. docinfo_->pages_provided++;
  142. HandleCompositionRequest(frame_guid, std::move(serialized_content),
  143. subframe_content_map, std::move(callback));
  144. }
  145. void PrintCompositorImpl::CompositeDocumentToPdf(
  146. uint64_t frame_guid,
  147. base::ReadOnlySharedMemoryRegion serialized_content,
  148. const ContentToFrameMap& subframe_content_map,
  149. mojom::PrintCompositor::CompositeDocumentToPdfCallback callback) {
  150. TRACE_EVENT0("print", "PrintCompositorImpl::CompositeDocumentToPdf");
  151. DCHECK(!docinfo_);
  152. HandleCompositionRequest(frame_guid, std::move(serialized_content),
  153. subframe_content_map, std::move(callback));
  154. }
  155. void PrintCompositorImpl::PrepareForDocumentToPdf(
  156. mojom::PrintCompositor::PrepareForDocumentToPdfCallback callback) {
  157. DCHECK(!docinfo_);
  158. docinfo_ = std::make_unique<DocumentInfo>();
  159. std::move(callback).Run(mojom::PrintCompositor::Status::kSuccess);
  160. }
  161. void PrintCompositorImpl::CompleteDocumentToPdf(
  162. uint32_t page_count,
  163. mojom::PrintCompositor::CompleteDocumentToPdfCallback callback) {
  164. DCHECK(docinfo_);
  165. DCHECK_GT(page_count, 0U);
  166. docinfo_->page_count = page_count;
  167. docinfo_->callback = std::move(callback);
  168. if (!docinfo_->doc) {
  169. docinfo_->doc = MakePdfDocument(creator_, accessibility_tree_,
  170. &docinfo_->compositor_stream);
  171. }
  172. HandleDocumentCompletionRequest();
  173. }
  174. void PrintCompositorImpl::SetWebContentsURL(const GURL& url) {
  175. // Record the most recent url we tried to print. This should be sufficient
  176. // for users using print preview by default.
  177. static crash_reporter::CrashKeyString<1024> crash_key("main-frame-url");
  178. crash_key.Set(url.spec());
  179. }
  180. void PrintCompositorImpl::SetUserAgent(const std::string& user_agent) {
  181. if (!user_agent.empty())
  182. creator_ = user_agent;
  183. }
  184. void PrintCompositorImpl::UpdateRequestsWithSubframeInfo(
  185. uint64_t frame_guid,
  186. const std::vector<uint64_t>& pending_subframes) {
  187. // Check for each request's pending list.
  188. for (auto it = requests_.begin(); it != requests_.end();) {
  189. auto& request = *it;
  190. // If the request needs this frame, we can remove the dependency, but
  191. // update with this frame's pending list.
  192. auto& pending_list = request->pending_subframes;
  193. if (pending_list.erase(frame_guid)) {
  194. std::copy(pending_subframes.begin(), pending_subframes.end(),
  195. std::inserter(pending_list, pending_list.end()));
  196. }
  197. // If the request still has pending frames, or isn't at the front of the
  198. // request queue (and thus could be dependent upon content from their
  199. // data stream), then keep waiting.
  200. const bool fulfill_request =
  201. it == requests_.begin() && pending_list.empty();
  202. if (!fulfill_request) {
  203. ++it;
  204. continue;
  205. }
  206. // Fulfill the request now.
  207. FulfillRequest(request->serialized_content, request->subframe_content_map,
  208. std::move(request->callback));
  209. // Check for a collected print preview document that was waiting on
  210. // this page to finish.
  211. if (docinfo_) {
  212. if (docinfo_->page_count &&
  213. (docinfo_->pages_written == docinfo_->page_count)) {
  214. CompleteDocumentRequest(std::move(docinfo_->callback));
  215. }
  216. }
  217. it = requests_.erase(it);
  218. }
  219. }
  220. bool PrintCompositorImpl::IsReadyToComposite(
  221. uint64_t frame_guid,
  222. const ContentToFrameMap& subframe_content_map,
  223. base::flat_set<uint64_t>* pending_subframes) const {
  224. pending_subframes->clear();
  225. base::flat_set<uint64_t> visited_frames = {frame_guid};
  226. CheckFramesForReadiness(subframe_content_map, pending_subframes,
  227. &visited_frames);
  228. return pending_subframes->empty();
  229. }
  230. void PrintCompositorImpl::CheckFramesForReadiness(
  231. const ContentToFrameMap& subframe_content_map,
  232. base::flat_set<uint64_t>* pending_subframes,
  233. base::flat_set<uint64_t>* visited) const {
  234. for (auto& subframe_content : subframe_content_map) {
  235. auto subframe_guid = subframe_content.second;
  236. // If this frame has been checked, skip it.
  237. if (!visited->insert(subframe_guid).second)
  238. continue;
  239. auto iter = frame_info_map_.find(subframe_guid);
  240. if (iter == frame_info_map_.end()) {
  241. pending_subframes->insert(subframe_guid);
  242. } else {
  243. CheckFramesForReadiness(iter->second->subframe_content_map,
  244. pending_subframes, visited);
  245. }
  246. }
  247. }
  248. void PrintCompositorImpl::HandleCompositionRequest(
  249. uint64_t frame_guid,
  250. base::ReadOnlySharedMemoryRegion serialized_content,
  251. const ContentToFrameMap& subframe_content_map,
  252. CompositeToPdfCallback callback) {
  253. base::ReadOnlySharedMemoryMapping mapping = serialized_content.Map();
  254. if (!mapping.IsValid()) {
  255. DLOG(ERROR) << "HandleCompositionRequest: Cannot map input.";
  256. std::move(callback).Run(mojom::PrintCompositor::Status::kHandleMapError,
  257. base::ReadOnlySharedMemoryRegion());
  258. return;
  259. }
  260. base::flat_set<uint64_t> pending_subframes;
  261. if (IsReadyToComposite(frame_guid, subframe_content_map,
  262. &pending_subframes)) {
  263. // This request has all the necessary subframes.
  264. // Due to typeface serialization caching, need to ensure that previous
  265. // requests have already been processed, otherwise this request could
  266. // fail by trying to use a typeface which hasn't been deserialized yet.
  267. if (requests_.empty()) {
  268. FulfillRequest(mapping.GetMemoryAsSpan<uint8_t>(), subframe_content_map,
  269. std::move(callback));
  270. return;
  271. }
  272. }
  273. // When it is not ready yet, keep its information and
  274. // wait until all dependent subframes are ready.
  275. auto iter = frame_info_map_.find(frame_guid);
  276. if (iter == frame_info_map_.end())
  277. frame_info_map_[frame_guid] = std::make_unique<FrameInfo>();
  278. requests_.push_back(std::make_unique<RequestInfo>(
  279. mapping.GetMemoryAsSpan<uint8_t>(), subframe_content_map,
  280. std::move(pending_subframes), std::move(callback)));
  281. }
  282. void PrintCompositorImpl::HandleDocumentCompletionRequest() {
  283. if (docinfo_->pages_written == docinfo_->page_count) {
  284. CompleteDocumentRequest(std::move(docinfo_->callback));
  285. return;
  286. }
  287. // Just need to wait on pages to percolate through processing, callback will
  288. // be handled from UpdateRequestsWithSubframeInfo() once the pending requests
  289. // have finished.
  290. }
  291. mojom::PrintCompositor::Status PrintCompositorImpl::CompositeToPdf(
  292. base::span<const uint8_t> serialized_content,
  293. const ContentToFrameMap& subframe_content_map,
  294. base::ReadOnlySharedMemoryRegion* region) {
  295. TRACE_EVENT0("print", "PrintCompositorImpl::CompositeToPdf");
  296. PictureDeserializationContext subframes =
  297. GetPictureDeserializationContext(subframe_content_map);
  298. // Read in content and convert it into pdf.
  299. SkMemoryStream stream(serialized_content.data(), serialized_content.size());
  300. int page_count = SkMultiPictureDocumentReadPageCount(&stream);
  301. if (!page_count) {
  302. DLOG(ERROR) << "CompositeToPdf: No page is read.";
  303. return mojom::PrintCompositor::Status::kContentFormatError;
  304. }
  305. std::vector<SkDocumentPage> pages(page_count);
  306. SkDeserialProcs procs = DeserializationProcs(&subframes, &typefaces_);
  307. if (!SkMultiPictureDocumentRead(&stream, pages.data(), page_count, &procs)) {
  308. DLOG(ERROR) << "CompositeToPdf: Page reading failed.";
  309. return mojom::PrintCompositor::Status::kContentFormatError;
  310. }
  311. SkDynamicMemoryWStream wstream;
  312. sk_sp<SkDocument> doc =
  313. MakePdfDocument(creator_, ui::AXTreeUpdate(), &wstream);
  314. for (const auto& page : pages) {
  315. TRACE_EVENT0("print", "PrintCompositorImpl::CompositeToPdf draw page");
  316. SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height());
  317. canvas->drawPicture(page.fPicture);
  318. doc->endPage();
  319. if (docinfo_) {
  320. // Create document PDF if needed.
  321. if (!docinfo_->doc) {
  322. docinfo_->doc = MakePdfDocument(creator_, accessibility_tree_,
  323. &docinfo_->compositor_stream);
  324. }
  325. // Collect this page into document PDF.
  326. SkCanvas* canvas_doc =
  327. docinfo_->doc->beginPage(page.fSize.width(), page.fSize.height());
  328. canvas_doc->drawPicture(page.fPicture);
  329. docinfo_->doc->endPage();
  330. docinfo_->pages_written++;
  331. }
  332. }
  333. doc->close();
  334. base::MappedReadOnlyRegion region_mapping =
  335. base::ReadOnlySharedMemoryRegion::Create(wstream.bytesWritten());
  336. if (!region_mapping.IsValid()) {
  337. DLOG(ERROR) << "CompositeToPdf: Cannot create new shared memory region.";
  338. return mojom::PrintCompositor::Status::kHandleMapError;
  339. }
  340. wstream.copyToAndReset(region_mapping.mapping.memory());
  341. *region = std::move(region_mapping.region);
  342. return mojom::PrintCompositor::Status::kSuccess;
  343. }
  344. void PrintCompositorImpl::CompositeSubframe(FrameInfo* frame_info) {
  345. frame_info->composited = true;
  346. // Composite subframes first.
  347. PictureDeserializationContext subframes =
  348. GetPictureDeserializationContext(frame_info->subframe_content_map);
  349. // Composite the entire frame.
  350. SkMemoryStream stream(frame_info->serialized_content.data(),
  351. frame_info->serialized_content.size());
  352. SkDeserialProcs procs =
  353. DeserializationProcs(&subframes, &frame_info->typefaces);
  354. frame_info->content = SkPicture::MakeFromStream(&stream, &procs);
  355. }
  356. PrintCompositorImpl::PictureDeserializationContext
  357. PrintCompositorImpl::GetPictureDeserializationContext(
  358. const ContentToFrameMap& subframe_content_map) {
  359. PictureDeserializationContext subframes;
  360. for (auto& content_info : subframe_content_map) {
  361. uint32_t content_id = content_info.first;
  362. uint64_t frame_guid = content_info.second;
  363. auto iter = frame_info_map_.find(frame_guid);
  364. if (iter == frame_info_map_.end())
  365. continue;
  366. FrameInfo* frame_info = iter->second.get();
  367. if (!frame_info->composited)
  368. CompositeSubframe(frame_info);
  369. subframes[content_id] = frame_info->content;
  370. }
  371. return subframes;
  372. }
  373. void PrintCompositorImpl::FulfillRequest(
  374. base::span<const uint8_t> serialized_content,
  375. const ContentToFrameMap& subframe_content_map,
  376. CompositeToPdfCallback callback) {
  377. base::ReadOnlySharedMemoryRegion region;
  378. auto status =
  379. CompositeToPdf(serialized_content, subframe_content_map, &region);
  380. std::move(callback).Run(status, std::move(region));
  381. }
  382. void PrintCompositorImpl::CompleteDocumentRequest(
  383. CompleteDocumentToPdfCallback callback) {
  384. mojom::PrintCompositor::Status status;
  385. base::ReadOnlySharedMemoryRegion region;
  386. docinfo_->doc->close();
  387. base::MappedReadOnlyRegion region_mapping =
  388. base::ReadOnlySharedMemoryRegion::Create(
  389. docinfo_->compositor_stream.bytesWritten());
  390. if (region_mapping.IsValid()) {
  391. docinfo_->compositor_stream.copyToAndReset(region_mapping.mapping.memory());
  392. region = std::move(region_mapping.region);
  393. status = mojom::PrintCompositor::Status::kSuccess;
  394. } else {
  395. DLOG(ERROR) << "CompleteDocumentRequest: "
  396. << "Cannot create new shared memory region.";
  397. status = mojom::PrintCompositor::Status::kHandleMapError;
  398. }
  399. std::move(callback).Run(status, std::move(region));
  400. }
  401. PrintCompositorImpl::FrameContentInfo::FrameContentInfo(
  402. base::span<const uint8_t> content,
  403. const ContentToFrameMap& map)
  404. : serialized_content(content.begin(), content.end()),
  405. subframe_content_map(map) {}
  406. PrintCompositorImpl::FrameContentInfo::FrameContentInfo() = default;
  407. PrintCompositorImpl::FrameContentInfo::~FrameContentInfo() = default;
  408. PrintCompositorImpl::DocumentInfo::DocumentInfo() = default;
  409. PrintCompositorImpl::DocumentInfo::~DocumentInfo() = default;
  410. PrintCompositorImpl::RequestInfo::RequestInfo(
  411. base::span<const uint8_t> content,
  412. const ContentToFrameMap& content_info,
  413. const base::flat_set<uint64_t>& pending_subframes,
  414. mojom::PrintCompositor::CompositePageToPdfCallback callback)
  415. : FrameContentInfo(content, content_info),
  416. pending_subframes(pending_subframes),
  417. callback(std::move(callback)) {}
  418. PrintCompositorImpl::RequestInfo::~RequestInfo() = default;
  419. } // namespace printing