From 0a39059c4ca72bb1446231f0b40360cb2d389c02 Mon Sep 17 00:00:00 2001 From: Dominic Mazzoni Date: Wed, 8 Jan 2020 19:30:45 +0000 Subject: [PATCH] Support exporting tagged PDFs from Chrome Headless When the --export-tagged-pdf flag is enabled and when exporting a PDF using Chrome Headless, generate a tagged (accessible) PDF instead of an untagged one. The details are described in the design doc: https://docs.google.com/document/d/1ku6QNtAHEqVnRGqFzpmlMe1fowkjCX9miHj8Kx1cFlk In a nutshell, PrintRenderFrameHelper captures a snapshot of the accessibility tree for the page and returns it in the metafile. This snapshot includes blink DOMNodeIds. In addition, we ensure that all text drawing commands captured for printing also include DOMNodeIds (this was originally added to support content capture). Finally, printing::MakePdfDocument is modified to convert the accessibility tree into a structure tree and pass this through to Skia. A follow-up change adds an end-to-end test of generating a tagged PDF using Chrome Headless: http://crrev.com/c/1970744 Bug: 607777 Change-Id: Ib2bfa2de94f622c766c4b4c73c013437e404bbde Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1970742 Commit-Queue: Dominic Mazzoni Reviewed-by: Lei Zhang Reviewed-by: Philip Rogers Reviewed-by: Eric Seckler Cr-Commit-Position: refs/heads/master@{#729428} --- cc/paint/paint_op_buffer.cc | 3 + cc/paint/skia_paint_canvas.cc | 3 + .../renderer/print_render_frame_helper.cc | 16 +++ .../renderer/print_render_frame_helper.h | 4 + components/services/pdf_compositor/BUILD.gn | 1 + components/services/pdf_compositor/DEPS | 1 + .../pdf_compositor/pdf_compositor_impl.cc | 6 +- .../render_accessibility_impl.cc | 1 + headless/app/headless_shell_switches.cc | 5 + headless/app/headless_shell_switches.h | 1 + .../headless_content_browser_client.cc | 3 + ...less_print_render_frame_helper_delegate.cc | 7 ++ ...dless_print_render_frame_helper_delegate.h | 1 + printing/BUILD.gn | 1 + printing/DEPS | 1 + printing/common/BUILD.gn | 1 + printing/common/metafile_utils.cc | 99 ++++++++++++++++++- printing/common/metafile_utils.h | 2 + printing/metafile_skia.cc | 2 +- printing/metafile_skia.h | 8 ++ .../content_capture_manager.cc | 4 +- .../content_capture/content_capture_manager.h | 7 +- .../content_capture/content_capture_test.cc | 7 +- .../blink/renderer/core/layout/layout_text.cc | 10 +- .../renderer/core/page/print_context_test.cc | 4 + ui/accessibility/BUILD.gn | 1 + ui/accessibility/ax_tree_update_forward.h | 18 ++++ 27 files changed, 201 insertions(+), 16 deletions(-) create mode 100644 ui/accessibility/ax_tree_update_forward.h diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc index 20afc9bc84f62b..97a182902ff584 100644 --- a/cc/paint/paint_op_buffer.cc +++ b/cc/paint/paint_op_buffer.cc @@ -16,6 +16,7 @@ #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkRegion.h" #include "third_party/skia/include/core/SkSerialProcs.h" +#include "third_party/skia/include/docs/SkPDFDocument.h" #include "third_party/skia/include/gpu/GrContext.h" namespace cc { @@ -1395,9 +1396,11 @@ void DrawTextBlobOp::RasterWithFlags(const DrawTextBlobOp* op, const PaintFlags* flags, SkCanvas* canvas, const PlaybackParams& params) { + SkPDF::SetNodeId(canvas, op->node_id); flags->DrawToSk(canvas, [op](SkCanvas* c, const SkPaint& p) { c->drawTextBlob(op->blob.get(), op->x, op->y, p); }); + SkPDF::SetNodeId(canvas, 0); } void RestoreOp::Raster(const RestoreOp* op, diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc index f143d9ea15d7ad..e71b9a77c6eedc 100644 --- a/cc/paint/skia_paint_canvas.cc +++ b/cc/paint/skia_paint_canvas.cc @@ -9,6 +9,7 @@ #include "cc/paint/paint_recorder.h" #include "cc/paint/scoped_raster_flags.h" #include "third_party/skia/include/core/SkAnnotation.h" +#include "third_party/skia/include/docs/SkPDFDocument.h" namespace cc { SkiaPaintCanvas::ContextFlushes::ContextFlushes() @@ -317,7 +318,9 @@ void SkiaPaintCanvas::drawTextBlob(sk_sp blob, SkScalar y, NodeId node_id, const PaintFlags& flags) { + SkPDF::SetNodeId(canvas_, node_id); drawTextBlob(blob, x, y, flags); + SkPDF::SetNodeId(canvas_, 0); } void SkiaPaintCanvas::drawPicture(sk_sp record) { diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc index 35bcee6547ef87..d3f4055902c759 100644 --- a/components/printing/renderer/print_render_frame_helper.cc +++ b/components/printing/renderer/print_render_frame_helper.cc @@ -1043,6 +1043,10 @@ bool PrintRenderFrameHelper::Delegate::IsScriptedPrintEnabled() { return true; } +bool PrintRenderFrameHelper::Delegate::ShouldGenerateTaggedPDF() { + return false; +} + PrintRenderFrameHelper::PrintRenderFrameHelper( content::RenderFrame* render_frame, std::unique_ptr delegate) @@ -1865,6 +1869,18 @@ bool PrintRenderFrameHelper::PrintPagesNative(blink::WebLocalFrame* frame, print_params.document_cookie); CHECK(metafile.Init()); + // If tagged PDF exporting is enabled, we also need to capture an + // accessibility tree and store it in the metafile. AXTreeSnapshotter + // should stay alive through the end of this function, because text + // drawing commands are only annotated with a DOMNodeId if accessibility + // is enabled. + std::unique_ptr snapshotter; + if (delegate_->ShouldGenerateTaggedPDF()) { + snapshotter = render_frame()->CreateAXTreeSnapshotter(); + snapshotter->Snapshot(ui::kAXModeComplete, 0, + &metafile.accessibility_tree()); + } + PrintHostMsg_DidPrintDocument_Params page_params; gfx::Size* page_size_in_dpi; gfx::Rect* content_area_in_dpi; diff --git a/components/printing/renderer/print_render_frame_helper.h b/components/printing/renderer/print_render_frame_helper.h index bd00b17898627b..0778deefa7af69 100644 --- a/components/printing/renderer/print_render_frame_helper.h +++ b/components/printing/renderer/print_render_frame_helper.h @@ -106,6 +106,10 @@ class PrintRenderFrameHelper // The default implementation returns |true|. virtual bool IsScriptedPrintEnabled(); + // Whether we should send extra metadata necessary to produce a tagged + // (accessible) PDF. + virtual bool ShouldGenerateTaggedPDF(); + // Returns true if printing is overridden and the default behavior should be // skipped for |frame|. virtual bool OverridePrint(blink::WebLocalFrame* frame) = 0; diff --git a/components/services/pdf_compositor/BUILD.gn b/components/services/pdf_compositor/BUILD.gn index ccc4858e0ef979..2e0e99a4139daf 100644 --- a/components/services/pdf_compositor/BUILD.gn +++ b/components/services/pdf_compositor/BUILD.gn @@ -20,6 +20,7 @@ static_library("pdf_compositor") { "//printing/common", "//skia", "//third_party/blink/public:blink_headers", + "//ui/accessibility", ] if (is_win) { diff --git a/components/services/pdf_compositor/DEPS b/components/services/pdf_compositor/DEPS index 3dec59eb9f24df..980abab857c10b 100644 --- a/components/services/pdf_compositor/DEPS +++ b/components/services/pdf_compositor/DEPS @@ -11,4 +11,5 @@ include_rules = [ "+third_party/skia", "+third_party/blink/public/platform", # Test web sandbox support. "+ui/base/ui_base_features.h", # UI features. + "+ui/accessibility", # Tagged PDF exporting. ] diff --git a/components/services/pdf_compositor/pdf_compositor_impl.cc b/components/services/pdf_compositor/pdf_compositor_impl.cc index e3692fb1fc2ccf..5f1bca07a4197c 100644 --- a/components/services/pdf_compositor/pdf_compositor_impl.cc +++ b/components/services/pdf_compositor/pdf_compositor_impl.cc @@ -27,6 +27,7 @@ #include "third_party/skia/include/core/SkGraphics.h" #include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/src/utils/SkMultiPictureDocument.h" +#include "ui/accessibility/ax_tree_update.h" #if defined(OS_WIN) #include "content/public/child/dwrite_font_proxy_init_win.h" @@ -336,7 +337,8 @@ mojom::PdfCompositor::Status PdfCompositorImpl::CompositeToPdf( } SkDynamicMemoryWStream wstream; - sk_sp doc = MakePdfDocument(creator_, &wstream); + sk_sp doc = + MakePdfDocument(creator_, ui::AXTreeUpdate(), &wstream); for (const auto& page : pages) { SkCanvas* canvas = doc->beginPage(page.fSize.width(), page.fSize.height()); @@ -447,7 +449,7 @@ PdfCompositorImpl::FrameInfo::FrameInfo() = default; PdfCompositorImpl::FrameInfo::~FrameInfo() = default; PdfCompositorImpl::DocumentInfo::DocumentInfo(const std::string& creator) - : doc(MakePdfDocument(creator, &compositor_stream)) {} + : doc(MakePdfDocument(creator, ui::AXTreeUpdate(), &compositor_stream)) {} PdfCompositorImpl::DocumentInfo::~DocumentInfo() = default; diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc index 132bf220b8c98b..586c311073421f 100644 --- a/content/renderer/accessibility/render_accessibility_impl.cc +++ b/content/renderer/accessibility/render_accessibility_impl.cc @@ -134,6 +134,7 @@ void AXTreeSnapshotterImpl::SnapshotContentTree(ui::AXMode ax_mode, BlinkAXTreeSource tree_source(render_frame_, ax_mode); tree_source.SetRoot(root); + tree_source.EnableDOMNodeIDs(); ScopedFreezeBlinkAXTreeSource freeze(&tree_source); // The serializer returns an AXContentTreeUpdate, which can store a complete diff --git a/headless/app/headless_shell_switches.cc b/headless/app/headless_shell_switches.cc index 6b2c5f44911945..c91c5fbc03f66f 100644 --- a/headless/app/headless_shell_switches.cc +++ b/headless/app/headless_shell_switches.cc @@ -22,6 +22,11 @@ const char kEnableBeginFrameControl[] = "enable-begin-frame-control"; // Enable crash reporter for headless. const char kEnableCrashReporter[] = "enable-crash-reporter"; +// If enabled, generate a tagged (accessible) file when printing to PDF. +// The plan is for this to go away once tagged PDFs become the default. +// See https://crbug.com/607777 +const char kExportTaggedPDF[] = "export-tagged-pdf"; + // Disable crash reporter for headless. It is enabled by default in official // builds. const char kDisableCrashReporter[] = "disable-crash-reporter"; diff --git a/headless/app/headless_shell_switches.h b/headless/app/headless_shell_switches.h index 1e69ba64d97799..aeb3a14fcdfcf9 100644 --- a/headless/app/headless_shell_switches.h +++ b/headless/app/headless_shell_switches.h @@ -20,6 +20,7 @@ HEADLESS_EXPORT extern const char kDiskCacheDir[]; HEADLESS_EXPORT extern const char kDumpDom[]; HEADLESS_EXPORT extern const char kEnableBeginFrameControl[]; HEADLESS_EXPORT extern const char kEnableCrashReporter[]; +HEADLESS_EXPORT extern const char kExportTaggedPDF[]; HEADLESS_EXPORT extern const char kHideScrollbars[]; HEADLESS_EXPORT extern const char kPasswordStore[]; HEADLESS_EXPORT extern const char kPrintToPDF[]; diff --git a/headless/lib/browser/headless_content_browser_client.cc b/headless/lib/browser/headless_content_browser_client.cc index aac56725f11071..3612acbb692478 100644 --- a/headless/lib/browser/headless_content_browser_client.cc +++ b/headless/lib/browser/headless_content_browser_client.cc @@ -190,6 +190,9 @@ void HeadlessContentBrowserClient::AppendExtraCommandLineSwitches( command_line->AppendSwitch(::switches::kEnableCrashReporter); #endif // defined(HEADLESS_USE_BREAKPAD) + if (old_command_line.HasSwitch(switches::kExportTaggedPDF)) + command_line->AppendSwitch(switches::kExportTaggedPDF); + // If we're spawning a renderer, then override the language switch. std::string process_type = command_line->GetSwitchValueASCII(::switches::kProcessType); diff --git a/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc b/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc index 7d8ab3f36308ad..253e6857d413df 100644 --- a/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc +++ b/headless/lib/renderer/headless_print_render_frame_helper_delegate.cc @@ -3,6 +3,8 @@ // found in the LICENSE file. #include "headless/lib/renderer/headless_print_render_frame_helper_delegate.h" +#include "base/command_line.h" +#include "headless/app/headless_shell_switches.h" #include "third_party/blink/public/web/web_element.h" @@ -23,6 +25,11 @@ bool HeadlessPrintRenderFrameHelperDelegate::IsPrintPreviewEnabled() { return false; } +bool HeadlessPrintRenderFrameHelperDelegate::ShouldGenerateTaggedPDF() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + headless::switches::kExportTaggedPDF); +} + bool HeadlessPrintRenderFrameHelperDelegate::OverridePrint( blink::WebLocalFrame* frame) { return false; diff --git a/headless/lib/renderer/headless_print_render_frame_helper_delegate.h b/headless/lib/renderer/headless_print_render_frame_helper_delegate.h index c5c070dce15f67..707e849c413870 100644 --- a/headless/lib/renderer/headless_print_render_frame_helper_delegate.h +++ b/headless/lib/renderer/headless_print_render_frame_helper_delegate.h @@ -19,6 +19,7 @@ class HeadlessPrintRenderFrameHelperDelegate private: // printing::PrintRenderFrameHelper::Delegate: bool IsPrintPreviewEnabled() override; + bool ShouldGenerateTaggedPDF() override; bool OverridePrint(blink::WebLocalFrame* frame) override; blink::WebElement GetPdfElement(blink::WebLocalFrame* frame) override; diff --git a/printing/BUILD.gn b/printing/BUILD.gn index 890028484cf403..1491e109522052 100644 --- a/printing/BUILD.gn +++ b/printing/BUILD.gn @@ -117,6 +117,7 @@ component("printing") { "//printing/common", "//skia", "//third_party/icu", + "//ui/accessibility", "//ui/gfx", "//ui/gfx/geometry", "//url", diff --git a/printing/DEPS b/printing/DEPS index 41732d72750d4c..71708c22400a56 100644 --- a/printing/DEPS +++ b/printing/DEPS @@ -5,6 +5,7 @@ include_rules = [ "+third_party/icu/source/common/unicode", "+third_party/icu/source/i18n/unicode", "+third_party/skia", + "+ui/accessibility", "+ui/android", "+ui/aura", "+ui/base/resource", diff --git a/printing/common/BUILD.gn b/printing/common/BUILD.gn index 4143ca728c7a56..029a42e5daa2d3 100644 --- a/printing/common/BUILD.gn +++ b/printing/common/BUILD.gn @@ -14,6 +14,7 @@ source_set("common") { "//base", "//printing:printing_export", "//skia", + "//ui/accessibility", ] defines = [ "PRINTING_IMPLEMENTATION" ] diff --git a/printing/common/metafile_utils.cc b/printing/common/metafile_utils.cc index eae01fb862e426..24278c28b9b793 100644 --- a/printing/common/metafile_utils.cc +++ b/printing/common/metafile_utils.cc @@ -11,6 +11,10 @@ #include "third_party/skia/include/core/SkPictureRecorder.h" #include "third_party/skia/include/core/SkTime.h" #include "third_party/skia/include/docs/SkPDFDocument.h" +#include "ui/accessibility/ax_node.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_tree.h" +#include "ui/accessibility/ax_tree_update.h" namespace { @@ -38,11 +42,94 @@ sk_sp GetEmptyPicture() { return rec.finishRecordingAsPicture(); } +// Convert an AXNode into a SkPDF::StructureElementNode in order to make a +// tagged (accessible) PDF. Returns true on success and false if we don't +// have enough data to build a valid tree. +bool RecursiveBuildStructureTree(const ui::AXNode* ax_node, + SkPDF::StructureElementNode* tag) { + bool valid = false; + + tag->fNodeId = ax_node->GetIntAttribute(ax::mojom::IntAttribute::kDOMNodeId); + switch (ax_node->data().role) { + case ax::mojom::Role::kRootWebArea: + tag->fType = SkPDF::DocumentStructureType::kDocument; + break; + case ax::mojom::Role::kParagraph: + tag->fType = SkPDF::DocumentStructureType::kP; + break; + case ax::mojom::Role::kGenericContainer: + tag->fType = SkPDF::DocumentStructureType::kDiv; + break; + case ax::mojom::Role::kHeading: + // TODO(dmazzoni): heading levels. https://crbug.com/1039816 + tag->fType = SkPDF::DocumentStructureType::kH; + break; + case ax::mojom::Role::kList: + tag->fType = SkPDF::DocumentStructureType::kL; + break; + case ax::mojom::Role::kListMarker: + tag->fType = SkPDF::DocumentStructureType::kLbl; + break; + case ax::mojom::Role::kListItem: + tag->fType = SkPDF::DocumentStructureType::kLI; + break; + case ax::mojom::Role::kTable: + tag->fType = SkPDF::DocumentStructureType::kTable; + break; + case ax::mojom::Role::kRow: + tag->fType = SkPDF::DocumentStructureType::kTR; + break; + case ax::mojom::Role::kColumnHeader: + case ax::mojom::Role::kRowHeader: + tag->fType = SkPDF::DocumentStructureType::kTH; + break; + case ax::mojom::Role::kCell: + tag->fType = SkPDF::DocumentStructureType::kTD; + break; + case ax::mojom::Role::kFigure: + case ax::mojom::Role::kImage: + tag->fType = SkPDF::DocumentStructureType::kFigure; + break; + case ax::mojom::Role::kStaticText: + // Currently we're only marking text content, so we can't generate + // a nonempty structure tree unless we have at least one kStaticText + // node in the tree. + tag->fType = SkPDF::DocumentStructureType::kNonStruct; + valid = true; + break; + default: + tag->fType = SkPDF::DocumentStructureType::kNonStruct; + } + + tag->fChildCount = ax_node->GetUnignoredChildCount(); + // Allocated here, cleaned up in DestroyStructureElementNodeTree(). + SkPDF::StructureElementNode* children = + new SkPDF::StructureElementNode[tag->fChildCount]; + tag->fChildren = children; + for (size_t i = 0; i < tag->fChildCount; i++) { + bool success = RecursiveBuildStructureTree( + ax_node->GetUnignoredChildAtIndex(i), &children[i]); + if (success) + valid = true; + } + + return valid; +} + +void DestroyStructureElementNodeTree(SkPDF::StructureElementNode* node) { + for (size_t i = 0; i < node->fChildCount; i++) { + DestroyStructureElementNodeTree( + const_cast(&node->fChildren[i])); + } + delete[] node->fChildren; +} + } // namespace namespace printing { sk_sp MakePdfDocument(const std::string& creator, + const ui::AXTreeUpdate& accessibility_tree, SkWStream* stream) { SkPDF::Metadata metadata; SkTime::DateTime now = TimeToSkTime(base::Time::Now()); @@ -56,7 +143,17 @@ sk_sp MakePdfDocument(const std::string& creator, base::FeatureList::IsEnabled(printing::features::kHarfBuzzPDFSubsetter) ? SkPDF::Metadata::kHarfbuzz_Subsetter : SkPDF::Metadata::kSfntly_Subsetter; - return SkPDF::MakeDocument(stream, metadata); + + SkPDF::StructureElementNode tag_root = {}; + if (!accessibility_tree.nodes.empty()) { + ui::AXTree tree(accessibility_tree); + if (RecursiveBuildStructureTree(tree.root(), &tag_root)) + metadata.fStructureElementTreeRoot = &tag_root; + } + + sk_sp document = SkPDF::MakeDocument(stream, metadata); + DestroyStructureElementNodeTree(&tag_root); + return document; } sk_sp SerializeOopPicture(SkPicture* pic, void* ctx) { diff --git a/printing/common/metafile_utils.h b/printing/common/metafile_utils.h index efda8507bd9f91..5796754fdd5c48 100644 --- a/printing/common/metafile_utils.h +++ b/printing/common/metafile_utils.h @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkRefCnt.h" #include "third_party/skia/include/core/SkSerialProcs.h" #include "third_party/skia/include/core/SkStream.h" +#include "ui/accessibility/ax_tree_update_forward.h" namespace printing { @@ -34,6 +35,7 @@ using DeserializationContext = base::flat_map>; using SerializationContext = ContentToProxyIdMap; sk_sp MakePdfDocument(const std::string& creator, + const ui::AXTreeUpdate& accessibility_tree, SkWStream* stream); SkSerialProcs SerializationProcs(SerializationContext* ctx); diff --git a/printing/metafile_skia.cc b/printing/metafile_skia.cc index e14acb47f11ca3..8e2a9a713e7216 100644 --- a/printing/metafile_skia.cc +++ b/printing/metafile_skia.cc @@ -178,7 +178,7 @@ bool MetafileSkia::FinishDocument() { cc::PlaybackParams::CustomDataRasterCallback custom_callback; switch (data_->type) { case SkiaDocumentType::PDF: - doc = MakePdfDocument(printing::GetAgent(), &stream); + doc = MakePdfDocument(printing::GetAgent(), accessibility_tree_, &stream); break; case SkiaDocumentType::MSKP: SkSerialProcs procs = SerializationProcs(&data_->subframe_content_info); diff --git a/printing/metafile_skia.h b/printing/metafile_skia.h index 26b9049163f2c5..f2fb34f6284610 100644 --- a/printing/metafile_skia.h +++ b/printing/metafile_skia.h @@ -16,6 +16,7 @@ #include "printing/common/metafile_utils.h" #include "printing/metafile.h" #include "skia/ext/platform_canvas.h" +#include "ui/accessibility/ax_tree_update.h" #if defined(OS_WIN) #include @@ -95,6 +96,11 @@ class PRINTING_EXPORT MetafileSkia : public Metafile { int GetDocumentCookie() const; const ContentToProxyIdMap& GetSubframeContentInfo() const; + const ui::AXTreeUpdate& accessibility_tree() const { + return accessibility_tree_; + } + ui::AXTreeUpdate& accessibility_tree() { return accessibility_tree_; } + private: FRIEND_TEST_ALL_PREFIXES(MetafileSkiaTest, TestFrameContent); @@ -111,6 +117,8 @@ class PRINTING_EXPORT MetafileSkia : public Metafile { std::unique_ptr data_; + ui::AXTreeUpdate accessibility_tree_; + DISALLOW_COPY_AND_ASSIGN(MetafileSkia); }; diff --git a/third_party/blink/renderer/core/content_capture/content_capture_manager.cc b/third_party/blink/renderer/core/content_capture/content_capture_manager.cc index c0e166ff3a2140..d403eced1fe306 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_manager.cc +++ b/third_party/blink/renderer/core/content_capture/content_capture_manager.cc @@ -5,7 +5,6 @@ #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" #include "third_party/blink/renderer/core/content_capture/sent_nodes.h" -#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/frame/local_frame.h" #include "third_party/blink/renderer/core/layout/layout_text.h" @@ -20,14 +19,13 @@ ContentCaptureManager::ContentCaptureManager(LocalFrame& local_frame_root) ContentCaptureManager::~ContentCaptureManager() = default; -DOMNodeId ContentCaptureManager::GetNodeId(Node& node) { +void ContentCaptureManager::ScheduleTaskIfNeeded() { if (first_node_holder_created_) { ScheduleTask(ContentCaptureTask::ScheduleReason::kContentChange); } else { ScheduleTask(ContentCaptureTask::ScheduleReason::kFirstContentChange); first_node_holder_created_ = true; } - return DOMNodeIds::IdForNode(&node); } void ContentCaptureManager::ScheduleTask( diff --git a/third_party/blink/renderer/core/content_capture/content_capture_manager.h b/third_party/blink/renderer/core/content_capture/content_capture_manager.h index 9d374aa9777d3a..00a2e59655ece6 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_manager.h +++ b/third_party/blink/renderer/core/content_capture/content_capture_manager.h @@ -24,11 +24,8 @@ class CORE_EXPORT ContentCaptureManager explicit ContentCaptureManager(LocalFrame& local_frame_root); virtual ~ContentCaptureManager(); - // Creates and returns NodeHolder for the given |node|, and schedules - // ContentCaptureTask if it isn't already scheduled. - // Can't use const Node& for parameter, because |node| is passed to - // DOMNodeIds::IdForNode(Node*). - DOMNodeId GetNodeId(Node& node); + // Schedules ContentCaptureTask if it isn't already scheduled. + void ScheduleTaskIfNeeded(); // Invokes when the |node_holder| asscociated LayoutText will be destroyed. void OnLayoutTextWillBeDestroyed(const Node& node); diff --git a/third_party/blink/renderer/core/content_capture/content_capture_test.cc b/third_party/blink/renderer/core/content_capture/content_capture_test.cc index 930c04892ea99e..614eee7a6dd78f 100644 --- a/third_party/blink/renderer/core/content_capture/content_capture_test.cc +++ b/third_party/blink/renderer/core/content_capture/content_capture_test.cc @@ -8,6 +8,7 @@ #include "third_party/blink/public/web/web_content_capture_client.h" #include "third_party/blink/public/web/web_content_holder.h" #include "third_party/blink/renderer/bindings/core/v8/v8_gc_controller.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/element.h" #include "third_party/blink/renderer/core/dom/node.h" #include "third_party/blink/renderer/core/frame/web_local_frame_impl.h" @@ -192,7 +193,8 @@ class ContentCaptureTest : public PageTestBase { Element* div_element = GetElementById("d1"); div_element->appendChild(element); UpdateAllLifecyclePhasesForTest(); - created_node_id_ = GetContentCaptureManager()->GetNodeId(*node); + GetContentCaptureManager()->ScheduleTaskIfNeeded(); + created_node_id_ = DOMNodeIds::IdForNode(node); Vector captured_content{created_node_id_}; content_capture_manager_->GetContentCaptureTask() ->SetCapturedContentForTesting(captured_content); @@ -268,7 +270,8 @@ class ContentCaptureTest : public PageTestBase { CHECK(layout_object); CHECK(layout_object->IsText()); nodes_.push_back(node); - node_ids_.push_back(GetContentCaptureManager()->GetNodeId(*node)); + GetContentCaptureManager()->ScheduleTaskIfNeeded(); + node_ids_.push_back(DOMNodeIds::IdForNode(node)); } } diff --git a/third_party/blink/renderer/core/layout/layout_text.cc b/third_party/blink/renderer/core/layout/layout_text.cc index 722664cf247d5c..e9082e4cd0f50a 100644 --- a/third_party/blink/renderer/core/layout/layout_text.cc +++ b/third_party/blink/renderer/core/layout/layout_text.cc @@ -29,6 +29,7 @@ #include "third_party/blink/public/platform/task_type.h" #include "third_party/blink/renderer/core/accessibility/ax_object_cache.h" #include "third_party/blink/renderer/core/content_capture/content_capture_manager.h" +#include "third_party/blink/renderer/core/dom/dom_node_ids.h" #include "third_party/blink/renderer/core/dom/text.h" #include "third_party/blink/renderer/core/editing/ephemeral_range.h" #include "third_party/blink/renderer/core/editing/frame_selection.h" @@ -2471,8 +2472,13 @@ PhysicalRect LayoutText::DebugRect() const { DOMNodeId LayoutText::EnsureNodeId() { if (node_id_ == kInvalidDOMNodeId) { - if (auto* content_capture_manager = GetContentCaptureManager()) - node_id_ = content_capture_manager->GetNodeId(*GetNode()); + auto* content_capture_manager = GetContentCaptureManager(); + if (content_capture_manager) + content_capture_manager->ScheduleTaskIfNeeded(); + + // If either content capture or accessibility are enabled, store a node ID. + if (content_capture_manager || GetDocument().ExistingAXObjectCache()) + node_id_ = DOMNodeIds::IdForNode(GetNode()); } return node_id_; } diff --git a/third_party/blink/renderer/core/page/print_context_test.cc b/third_party/blink/renderer/core/page/print_context_test.cc index cd38040c55b12e..7c9e9d9505dede 100644 --- a/third_party/blink/renderer/core/page/print_context_test.cc +++ b/third_party/blink/renderer/core/page/print_context_test.cc @@ -48,6 +48,10 @@ class MockPageContextCanvas : public SkCanvas { void onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) override { + // Ignore PDF node key annotations, defined in SkPDFDocument.cpp. + if (0 == strcmp(key, "PDF_Node_Key")) + return; + if (rect.width() == 0 && rect.height() == 0) { SkPoint point = getTotalMatrix().mapXY(rect.x(), rect.y()); Operation operation = {kDrawPoint, diff --git a/ui/accessibility/BUILD.gn b/ui/accessibility/BUILD.gn index a51cbdce4c3373..efc671b8d4a08b 100644 --- a/ui/accessibility/BUILD.gn +++ b/ui/accessibility/BUILD.gn @@ -108,6 +108,7 @@ jumbo_component("accessibility") { "ax_tree_source.h", "ax_tree_source_checker.h", "ax_tree_update.h", + "ax_tree_update_forward.h", "null_ax_action_target.cc", "null_ax_action_target.h", "platform/ax_android_constants.cc", diff --git a/ui/accessibility/ax_tree_update_forward.h b/ui/accessibility/ax_tree_update_forward.h new file mode 100644 index 00000000000000..c59dfaaad7398d --- /dev/null +++ b/ui/accessibility/ax_tree_update_forward.h @@ -0,0 +1,18 @@ +// Copyright 2019 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 UI_ACCESSIBILITY_AX_TREE_UPDATE_FORWARD_H_ +#define UI_ACCESSIBILITY_AX_TREE_UPDATE_FORWARD_H_ + +namespace ui { + +struct AXNodeData; +struct AXTreeData; +template +struct AXTreeUpdateBase; +using AXTreeUpdate = AXTreeUpdateBase; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_TREE_UPDATE_FORWARD_H_