1 // Copyright 2014 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 
5 #include "third_party/blink/renderer/core/page/print_context.h"
6 
7 #include <memory>
8 
9 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/renderer/core/dom/document.h"
12 #include "third_party/blink/renderer/core/events/before_print_event.h"
13 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
14 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
15 #include "third_party/blink/renderer/core/html/html_element.h"
16 #include "third_party/blink/renderer/core/layout/layout_view.h"
17 #include "third_party/blink/renderer/core/paint/paint_layer.h"
18 #include "third_party/blink/renderer/core/paint/paint_layer_painter.h"
19 #include "third_party/blink/renderer/core/scroll/scrollbar_theme.h"
20 #include "third_party/blink/renderer/core/testing/core_unit_test_helper.h"
21 #include "third_party/blink/renderer/core/testing/dummy_page_holder.h"
22 #include "third_party/blink/renderer/platform/graphics/graphics_context.h"
23 #include "third_party/blink/renderer/platform/graphics/paint/drawing_recorder.h"
24 #include "third_party/blink/renderer/platform/graphics/paint/paint_record_builder.h"
25 #include "third_party/blink/renderer/platform/testing/paint_test_configurations.h"
26 #include "third_party/blink/renderer/platform/wtf/text/text_stream.h"
27 #include "third_party/skia/include/core/SkCanvas.h"
28 
29 using testing::_;
30 
31 namespace blink {
32 
33 const int kPageWidth = 800;
34 const int kPageHeight = 600;
35 
36 class MockPageContextCanvas : public SkCanvas {
37  public:
38   enum OperationType { kDrawRect, kDrawPoint };
39 
40   struct Operation {
41     OperationType type;
42     SkRect rect;
43   };
44 
MockPageContextCanvas()45   MockPageContextCanvas() : SkCanvas(kPageWidth, kPageHeight) {}
46   ~MockPageContextCanvas() override = default;
47 
onDrawAnnotation(const SkRect & rect,const char key[],SkData * value)48   void onDrawAnnotation(const SkRect& rect,
49                         const char key[],
50                         SkData* value) override {
51     // Ignore PDF node key annotations, defined in SkPDFDocument.cpp.
52     if (0 == strcmp(key, "PDF_Node_Key"))
53       return;
54 
55     if (rect.width() == 0 && rect.height() == 0) {
56       SkPoint point = getTotalMatrix().mapXY(rect.x(), rect.y());
57       Operation operation = {kDrawPoint,
58                              SkRect::MakeXYWH(point.x(), point.y(), 0, 0)};
59       recorded_operations_.push_back(operation);
60     } else {
61       Operation operation = {kDrawRect, rect};
62       getTotalMatrix().mapRect(&operation.rect);
63       recorded_operations_.push_back(operation);
64     }
65   }
66 
RecordedOperations() const67   const Vector<Operation>& RecordedOperations() const {
68     return recorded_operations_;
69   }
70 
71   MOCK_METHOD2(onDrawRect, void(const SkRect&, const SkPaint&));
72   MOCK_METHOD1(DrawPicture, void(const SkPicture*));
73   MOCK_METHOD1(OnDrawPicture, void(const SkPicture*));
74   MOCK_METHOD3(OnDrawPicture,
75                void(const SkPicture*, const SkMatrix*, const SkPaint*));
76   MOCK_METHOD3(DrawPicture,
77                void(const SkPicture*, const SkMatrix*, const SkPaint*));
78   MOCK_METHOD4(onDrawImage,
79                void(const SkImage*, SkScalar, SkScalar, const SkPaint*));
80   MOCK_METHOD5(onDrawImageRect,
81                void(const SkImage*,
82                     const SkRect*,
83                     const SkRect&,
84                     const SkPaint*,
85                     SrcRectConstraint));
86 
87  private:
88   Vector<Operation> recorded_operations_;
89 };
90 
91 class PrintContextTest : public PaintTestConfigurations, public RenderingTest {
92  protected:
PrintContextTest(LocalFrameClient * local_frame_client=nullptr)93   explicit PrintContextTest(LocalFrameClient* local_frame_client = nullptr)
94       : RenderingTest(local_frame_client) {}
95   ~PrintContextTest() override = default;
96 
SetUp()97   void SetUp() override {
98     RenderingTest::SetUp();
99     print_context_ =
100         MakeGarbageCollected<PrintContext>(GetDocument().GetFrame(),
101                                            /*use_printing_layout=*/true);
102   }
103 
GetPrintContext()104   PrintContext& GetPrintContext() { return *print_context_.Get(); }
105 
SetBodyInnerHTML(String body_content)106   void SetBodyInnerHTML(String body_content) {
107     GetDocument().body()->setAttribute(html_names::kStyleAttr, "margin: 0");
108     GetDocument().body()->setInnerHTML(body_content);
109   }
110 
PrintSinglePage(SkCanvas & canvas)111   void PrintSinglePage(SkCanvas& canvas) {
112     IntRect page_rect(0, 0, kPageWidth, kPageHeight);
113     GetDocument().SetPrinting(Document::kBeforePrinting);
114     Event* event = MakeGarbageCollected<BeforePrintEvent>();
115     GetPrintContext().GetFrame()->DomWindow()->DispatchEvent(*event);
116     GetPrintContext().BeginPrintMode(page_rect.Width(), page_rect.Height());
117     UpdateAllLifecyclePhasesForTest();
118     PaintRecordBuilder builder;
119     GraphicsContext& context = builder.Context();
120     context.SetPrinting(true);
121     GetDocument().View()->PaintContentsOutsideOfLifecycle(
122         context, kGlobalPaintPrinting | kGlobalPaintAddUrlMetadata,
123         CullRect(page_rect));
124     {
125       DrawingRecorder recorder(
126           context, *GetDocument().GetLayoutView(),
127           DisplayItem::kPrintedContentDestinationLocations);
128       GetPrintContext().OutputLinkedDestinations(context, page_rect);
129     }
130     builder.EndRecording()->Playback(&canvas);
131     GetPrintContext().EndPrintMode();
132   }
133 
AbsoluteBlockHtmlForLink(int x,int y,int width,int height,String url,String children=String ())134   static String AbsoluteBlockHtmlForLink(int x,
135                                          int y,
136                                          int width,
137                                          int height,
138                                          String url,
139                                          String children = String()) {
140     WTF::TextStream ts;
141     ts << "<a style='position: absolute; left: " << x << "px; top: " << y
142        << "px; width: " << width << "px; height: " << height << "px' href='"
143        << url << "'>" << (children ? children : url) << "</a>";
144     return ts.Release();
145   }
146 
InlineHtmlForLink(String url,String children=String ())147   static String InlineHtmlForLink(String url, String children = String()) {
148     WTF::TextStream ts;
149     ts << "<a href='" << url << "'>" << (children ? children : url) << "</a>";
150     return ts.Release();
151   }
152 
HtmlForAnchor(int x,int y,String name,String text_content)153   static String HtmlForAnchor(int x, int y, String name, String text_content) {
154     WTF::TextStream ts;
155     ts << "<a name='" << name << "' style='position: absolute; left: " << x
156        << "px; top: " << y << "px'>" << text_content << "</a>";
157     return ts.Release();
158   }
159 
160  private:
161   std::unique_ptr<DummyPageHolder> page_holder_;
162   Persistent<PrintContext> print_context_;
163 };
164 
165 class PrintContextFrameTest : public PrintContextTest {
166  public:
PrintContextFrameTest()167   PrintContextFrameTest()
168       : PrintContextTest(MakeGarbageCollected<SingleChildLocalFrameClient>()) {}
169 };
170 
171 #define EXPECT_SKRECT_EQ(expectedX, expectedY, expectedWidth, expectedHeight, \
172                          actualRect)                                          \
173   EXPECT_EQ(expectedX, actualRect.x());                                       \
174   EXPECT_EQ(expectedY, actualRect.y());                                       \
175   EXPECT_EQ(expectedWidth, actualRect.width());                               \
176   EXPECT_EQ(expectedHeight, actualRect.height());
177 
178 INSTANTIATE_PAINT_TEST_SUITE_P(PrintContextTest);
179 
TEST_P(PrintContextTest,LinkTarget)180 TEST_P(PrintContextTest, LinkTarget) {
181   MockPageContextCanvas canvas;
182   SetBodyInnerHTML(
183       AbsoluteBlockHtmlForLink(50, 60, 70, 80, "http://www.google.com") +
184       AbsoluteBlockHtmlForLink(150, 160, 170, 180,
185                                "http://www.google.com#fragment"));
186   PrintSinglePage(canvas);
187 
188   const Vector<MockPageContextCanvas::Operation>& operations =
189       canvas.RecordedOperations();
190   ASSERT_EQ(2u, operations.size());
191   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
192   EXPECT_SKRECT_EQ(50, 60, 70, 80, operations[0].rect);
193   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
194   EXPECT_SKRECT_EQ(150, 160, 170, 180, operations[1].rect);
195 }
196 
TEST_P(PrintContextTest,LinkTargetUnderAnonymousBlockBeforeBlock)197 TEST_P(PrintContextTest, LinkTargetUnderAnonymousBlockBeforeBlock) {
198   GetDocument().SetCompatibilityMode(Document::kQuirksMode);
199   MockPageContextCanvas canvas;
200   SetBodyInnerHTML("<div style='padding-top: 50px'>" +
201                    InlineHtmlForLink("http://www.google.com",
202                                      "<img style='width: 111; height: 10'>") +
203                    "<div> " +
204                    InlineHtmlForLink("http://www.google1.com",
205                                      "<img style='width: 122; height: 20'>") +
206                    "</div>" + "</div>");
207   PrintSinglePage(canvas);
208   const Vector<MockPageContextCanvas::Operation>& operations =
209       canvas.RecordedOperations();
210   ASSERT_EQ(2u, operations.size());
211   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
212   EXPECT_SKRECT_EQ(0, 50, 111, 10, operations[0].rect);
213   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
214   EXPECT_SKRECT_EQ(0, 60, 122, 20, operations[1].rect);
215 }
216 
TEST_P(PrintContextTest,LinkTargetContainingABlock)217 TEST_P(PrintContextTest, LinkTargetContainingABlock) {
218   GetDocument().SetCompatibilityMode(Document::kQuirksMode);
219   MockPageContextCanvas canvas;
220   SetBodyInnerHTML(
221       "<div style='padding-top: 50px'>" +
222       InlineHtmlForLink("http://www.google2.com",
223                         "<div style='width:133; height: 30'>BLOCK</div>") +
224       "</div>");
225   PrintSinglePage(canvas);
226   const Vector<MockPageContextCanvas::Operation>& operations =
227       canvas.RecordedOperations();
228   ASSERT_EQ(1u, operations.size());
229   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
230   EXPECT_SKRECT_EQ(0, 50, 133, 30, operations[0].rect);
231 }
232 
TEST_P(PrintContextTest,LinkTargetUnderInInlines)233 TEST_P(PrintContextTest, LinkTargetUnderInInlines) {
234   MockPageContextCanvas canvas;
235   SetBodyInnerHTML(
236       "<span><b><i><img style='width: 40px; height: 40px'><br>" +
237       InlineHtmlForLink("http://www.google3.com",
238                         "<img style='width: 144px; height: 40px'>") +
239       "</i></b></span>");
240   PrintSinglePage(canvas);
241   const Vector<MockPageContextCanvas::Operation>& operations =
242       canvas.RecordedOperations();
243   ASSERT_EQ(1u, operations.size());
244   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
245   EXPECT_SKRECT_EQ(0, 40, 144, 40, operations[0].rect);
246 }
247 
TEST_P(PrintContextTest,LinkTargetUnderRelativelyPositionedInline)248 TEST_P(PrintContextTest, LinkTargetUnderRelativelyPositionedInline) {
249   MockPageContextCanvas canvas;
250   SetBodyInnerHTML(
251         + "<span style='position: relative; top: 50px; left: 50px'><b><i><img style='width: 1px; height: 40px'><br>"
252         + InlineHtmlForLink("http://www.google3.com", "<img style='width: 155px; height: 50px'>")
253         + "</i></b></span>");
254   PrintSinglePage(canvas);
255   const Vector<MockPageContextCanvas::Operation>& operations =
256       canvas.RecordedOperations();
257   ASSERT_EQ(1u, operations.size());
258   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
259   EXPECT_SKRECT_EQ(50, 90, 155, 50, operations[0].rect);
260 }
261 
TEST_P(PrintContextTest,LinkTargetSvg)262 TEST_P(PrintContextTest, LinkTargetSvg) {
263   MockPageContextCanvas canvas;
264   SetBodyInnerHTML(R"HTML(
265     <svg width='100' height='100'>
266     <a xlink:href='http://www.w3.org'><rect x='20' y='20' width='50'
267     height='50'/></a>
268     <text x='10' y='90'><a
269     xlink:href='http://www.google.com'><tspan>google</tspan></a></text>
270     </svg>
271   )HTML");
272   PrintSinglePage(canvas);
273 
274   const Vector<MockPageContextCanvas::Operation>& operations =
275       canvas.RecordedOperations();
276   ASSERT_EQ(2u, operations.size());
277   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
278   EXPECT_SKRECT_EQ(20, 20, 50, 50, operations[0].rect);
279   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
280   EXPECT_EQ(10, operations[1].rect.x());
281   EXPECT_GE(90, operations[1].rect.y());
282 }
283 
TEST_P(PrintContextTest,LinkedTarget)284 TEST_P(PrintContextTest, LinkedTarget) {
285   MockPageContextCanvas canvas;
286   GetDocument().SetBaseURLOverride(KURL("http://a.com/"));
287   // Careful about locations, the page is 800x600 and only one page is printed.
288   SetBodyInnerHTML(
289       AbsoluteBlockHtmlForLink(
290           50, 60, 10, 10,
291           "#fragment")  // Generates a Link_Named_Dest_Key annotation
292       + AbsoluteBlockHtmlForLink(50, 160, 10, 10,
293                                  "#not-found")  // Generates no annotation
294       + AbsoluteBlockHtmlForLink(
295             50, 260, 10, 10,
296             u"#\u00F6")  // Generates a Link_Named_Dest_Key annotation
297       + AbsoluteBlockHtmlForLink(
298             50, 360, 10, 10,
299             "#")  // Generates a Link_Named_Dest_Key annotation
300       + AbsoluteBlockHtmlForLink(
301             50, 460, 10, 10,
302             "#t%6Fp")  // Generates a Link_Named_Dest_Key annotation
303       +
304       HtmlForAnchor(450, 60, "fragment",
305                     "fragment")  // Generates a Define_Named_Dest_Key annotation
306       + HtmlForAnchor(450, 160, "fragment-not-used",
307                       "fragment-not-used")  // Generates no annotation
308       + HtmlForAnchor(450, 260, u"\u00F6",
309                       "O")  // Generates a Define_Named_Dest_Key annotation
310       // TODO(1117212): The escaped version currently takes precedence.
311       //+ HtmlForAnchor(450, 360, "%C3%B6",
312       //                "O2")  // Generates a Define_Named_Dest_Key annotation
313   );
314   PrintSinglePage(canvas);
315 
316   const Vector<MockPageContextCanvas::Operation>& operations =
317       canvas.RecordedOperations();
318   for (const auto& operation : operations) {
319     LOG(INFO) << (operation.type ? "Point" : "Rect") << operation.rect;
320   }
321   ASSERT_EQ(8u, operations.size());
322   // The DrawRect operations come from a stable iterator.
323   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
324   EXPECT_SKRECT_EQ(50, 60, 10, 10, operations[0].rect);
325   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
326   EXPECT_SKRECT_EQ(50, 260, 10, 10, operations[1].rect);
327   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[2].type);
328   EXPECT_SKRECT_EQ(50, 360, 10, 10, operations[2].rect);
329   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[3].type);
330   EXPECT_SKRECT_EQ(50, 460, 10, 10, operations[3].rect);
331 
332   // The DrawPoint operations come from an unstable iterator.
333   EXPECT_EQ(MockPageContextCanvas::kDrawPoint, operations[4].type);
334   EXPECT_SKRECT_EQ(450, 260, 0, 0, operations[4].rect);
335   EXPECT_EQ(MockPageContextCanvas::kDrawPoint, operations[5].type);
336   EXPECT_SKRECT_EQ(0, 0, 0, 0, operations[5].rect);
337   EXPECT_EQ(MockPageContextCanvas::kDrawPoint, operations[6].type);
338   EXPECT_SKRECT_EQ(0, 0, 0, 0, operations[6].rect);
339   EXPECT_EQ(MockPageContextCanvas::kDrawPoint, operations[7].type);
340   EXPECT_SKRECT_EQ(450, 60, 0, 0, operations[7].rect);
341 }
342 
TEST_P(PrintContextTest,EmptyLinkedTarget)343 TEST_P(PrintContextTest, EmptyLinkedTarget) {
344   MockPageContextCanvas canvas;
345   GetDocument().SetBaseURLOverride(KURL("http://a.com/"));
346   SetBodyInnerHTML(AbsoluteBlockHtmlForLink(50, 60, 70, 80, "#fragment") +
347                    HtmlForAnchor(250, 260, "fragment", ""));
348   PrintSinglePage(canvas);
349 
350   const Vector<MockPageContextCanvas::Operation>& operations =
351       canvas.RecordedOperations();
352   ASSERT_EQ(2u, operations.size());
353   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
354   EXPECT_SKRECT_EQ(50, 60, 70, 80, operations[0].rect);
355   EXPECT_EQ(MockPageContextCanvas::kDrawPoint, operations[1].type);
356   EXPECT_SKRECT_EQ(250, 260, 0, 0, operations[1].rect);
357 }
358 
TEST_P(PrintContextTest,LinkTargetBoundingBox)359 TEST_P(PrintContextTest, LinkTargetBoundingBox) {
360   MockPageContextCanvas canvas;
361   SetBodyInnerHTML(
362       AbsoluteBlockHtmlForLink(50, 60, 70, 20, "http://www.google.com",
363                                "<img style='width: 200px; height: 100px'>"));
364   PrintSinglePage(canvas);
365 
366   const Vector<MockPageContextCanvas::Operation>& operations =
367       canvas.RecordedOperations();
368   ASSERT_EQ(1u, operations.size());
369   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
370   EXPECT_SKRECT_EQ(50, 60, 200, 100, operations[0].rect);
371 }
372 
373 INSTANTIATE_PAINT_TEST_SUITE_P(PrintContextFrameTest);
374 
TEST_P(PrintContextFrameTest,WithSubframe)375 TEST_P(PrintContextFrameTest, WithSubframe) {
376   GetDocument().SetBaseURLOverride(KURL("http://a.com/"));
377   SetBodyInnerHTML(R"HTML(
378     <style>::-webkit-scrollbar { display: none }</style>
379     <iframe src='http://b.com/' width='500' height='500'
380      style='border-width: 5px; margin: 5px; position: absolute; top: 90px;
381     left: 90px'></iframe>
382   )HTML");
383   SetChildFrameHTML(
384       AbsoluteBlockHtmlForLink(50, 60, 70, 80, "#fragment") +
385       AbsoluteBlockHtmlForLink(150, 160, 170, 180, "http://www.google.com") +
386       AbsoluteBlockHtmlForLink(250, 260, 270, 280,
387                                "http://www.google.com#fragment"));
388 
389   MockPageContextCanvas canvas;
390   PrintSinglePage(canvas);
391 
392   const Vector<MockPageContextCanvas::Operation>& operations =
393       canvas.RecordedOperations();
394   ASSERT_EQ(2u, operations.size());
395   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
396   EXPECT_SKRECT_EQ(250, 260, 170, 180, operations[0].rect);
397   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
398   EXPECT_SKRECT_EQ(350, 360, 270, 280, operations[1].rect);
399 }
400 
TEST_P(PrintContextFrameTest,WithScrolledSubframe)401 TEST_P(PrintContextFrameTest, WithScrolledSubframe) {
402   GetDocument().SetBaseURLOverride(KURL("http://a.com/"));
403   SetBodyInnerHTML(R"HTML(
404     <style>::-webkit-scrollbar { display: none }</style>
405     <iframe src='http://b.com/' width='500' height='500'
406      style='border-width: 5px; margin: 5px; position: absolute; top: 90px;
407     left: 90px'></iframe>
408   )HTML");
409   SetChildFrameHTML(
410       AbsoluteBlockHtmlForLink(10, 10, 20, 20, "http://invisible.com") +
411       AbsoluteBlockHtmlForLink(50, 60, 70, 80, "http://partly.visible.com") +
412       AbsoluteBlockHtmlForLink(150, 160, 170, 180, "http://www.google.com") +
413       AbsoluteBlockHtmlForLink(250, 260, 270, 280,
414                                "http://www.google.com#fragment") +
415       AbsoluteBlockHtmlForLink(850, 860, 70, 80,
416                                "http://another.invisible.com"));
417 
418   ChildDocument().domWindow()->scrollTo(100, 100);
419 
420   MockPageContextCanvas canvas;
421   PrintSinglePage(canvas);
422 
423   const Vector<MockPageContextCanvas::Operation>& operations =
424       canvas.RecordedOperations();
425   ASSERT_EQ(3u, operations.size());
426   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[0].type);
427   EXPECT_SKRECT_EQ(50, 60, 70, 80,
428                    operations[0].rect);  // FIXME: the rect should be clipped.
429   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[1].type);
430   EXPECT_SKRECT_EQ(150, 160, 170, 180, operations[1].rect);
431   EXPECT_EQ(MockPageContextCanvas::kDrawRect, operations[2].type);
432   EXPECT_SKRECT_EQ(250, 260, 270, 280, operations[2].rect);
433 }
434 
435 // This tests that we properly resize and re-layout pages for printing.
TEST_P(PrintContextFrameTest,BasicPrintPageLayout)436 TEST_P(PrintContextFrameTest, BasicPrintPageLayout) {
437   FloatSize page_size(400, 400);
438   float maximum_shrink_ratio = 1.1;
439   auto* node = GetDocument().documentElement();
440 
441   GetDocument().GetFrame()->StartPrinting(page_size, page_size,
442                                           maximum_shrink_ratio);
443   EXPECT_EQ(node->OffsetWidth(), 400);
444   GetDocument().GetFrame()->EndPrinting();
445   EXPECT_EQ(node->OffsetWidth(), 800);
446 
447   SetBodyInnerHTML(R"HTML(
448       <div style='border: 0px; margin: 0px; background-color: #0000FF;
449       width:800px; height:400px'></div>)HTML");
450   GetDocument().GetFrame()->StartPrinting(page_size, page_size,
451                                           maximum_shrink_ratio);
452   EXPECT_EQ(node->OffsetWidth(), 440);
453   GetDocument().GetFrame()->EndPrinting();
454   EXPECT_EQ(node->OffsetWidth(), 800);
455 }
456 
TEST_P(PrintContextTest,Canvas2DBeforePrint)457 TEST_P(PrintContextTest, Canvas2DBeforePrint) {
458   MockPageContextCanvas canvas;
459   SetBodyInnerHTML("<canvas id='c' width=100 height=100></canvas>");
460   GetDocument().GetSettings()->SetScriptEnabled(true);
461   Element* const script_element =
462       GetDocument().CreateRawElement(html_names::kScriptTag);
463   script_element->setTextContent(
464       "window.addEventListener('beforeprint', (ev) => {"
465       "const ctx = document.getElementById('c').getContext('2d');"
466       "ctx.fillRect(0, 0, 10, 10);"
467       "ctx.fillRect(50, 50, 10, 10);"
468       "});");
469   GetDocument().body()->AppendChild(script_element);
470 
471   EXPECT_CALL(canvas, onDrawRect(_, _)).Times(testing::AtLeast(2));
472 
473   PrintSinglePage(canvas);
474 }
475 
TEST_P(PrintContextTest,Canvas2DPixelated)476 TEST_P(PrintContextTest, Canvas2DPixelated) {
477   MockPageContextCanvas canvas;
478   SetBodyInnerHTML(
479       "<canvas id='c' style='image-rendering: pixelated' "
480       "width=100 height=100></canvas>");
481   GetDocument().GetSettings()->SetScriptEnabled(true);
482   Element* const script_element =
483       GetDocument().CreateRawElement(html_names::kScriptTag);
484   script_element->setTextContent(
485       "window.addEventListener('beforeprint', (ev) => {"
486       "const ctx = document.getElementById('c').getContext('2d');"
487       "ctx.fillRect(0, 0, 10, 10);"
488       "ctx.fillRect(50, 50, 10, 10);"
489       "});");
490   GetDocument().body()->AppendChild(script_element);
491 
492   EXPECT_CALL(canvas, onDrawImageRect(_, _, _, _, _));
493 
494   PrintSinglePage(canvas);
495 }
496 
497 // This tests that we don't resize or re-layout subframes in printed content.
498 // TODO(weili): This test fails when the iframe isn't the root scroller - e.g.
499 // Adding ScopedImplicitRootScrollerForTest disabler(false);
500 // https://crbug.com/841602.
TEST_P(PrintContextFrameTest,DISABLED_SubframePrintPageLayout)501 TEST_P(PrintContextFrameTest, DISABLED_SubframePrintPageLayout) {
502   SetBodyInnerHTML(R"HTML(
503       <div style='border: 0px; margin: 0px; background-color: #0000FF;
504       width:800px; height:400px'></div>
505       <iframe id="target" src='http://b.com/' width='100%' height='100%'
506       style='border: 0px; margin: 0px; position: absolute; top: 0px;
507       left: 0px'></iframe>)HTML");
508   FloatSize page_size(400, 400);
509   float maximum_shrink_ratio = 1.1;
510   auto* parent = GetDocument().documentElement();
511   // The child document element inside iframe.
512   auto* child = ChildDocument().documentElement();
513   // The iframe element in the document.
514   auto* target = GetDocument().getElementById("target");
515 
516   GetDocument().GetFrame()->StartPrinting(page_size, page_size,
517                                           maximum_shrink_ratio);
518   EXPECT_EQ(parent->OffsetWidth(), 440);
519   EXPECT_EQ(child->OffsetWidth(), 800);
520   EXPECT_EQ(target->OffsetWidth(), 440);
521   GetDocument().GetFrame()->EndPrinting();
522   EXPECT_EQ(parent->OffsetWidth(), 800);
523   EXPECT_EQ(child->OffsetWidth(), 800);
524   EXPECT_EQ(target->OffsetWidth(), 800);
525 
526   GetDocument().GetFrame()->StartPrinting();
527   EXPECT_EQ(parent->OffsetWidth(), 800);
528   EXPECT_EQ(child->OffsetWidth(), 800);
529   EXPECT_EQ(target->OffsetWidth(), 800);
530   GetDocument().GetFrame()->EndPrinting();
531   EXPECT_EQ(parent->OffsetWidth(), 800);
532   EXPECT_EQ(child->OffsetWidth(), 800);
533   EXPECT_EQ(target->OffsetWidth(), 800);
534 
535   ASSERT_TRUE(ChildDocument() != GetDocument());
536   ChildDocument().GetFrame()->StartPrinting(page_size, page_size,
537                                             maximum_shrink_ratio);
538   EXPECT_EQ(parent->OffsetWidth(), 800);
539   EXPECT_EQ(child->OffsetWidth(), 400);
540   EXPECT_EQ(target->OffsetWidth(), 800);
541   GetDocument().GetFrame()->EndPrinting();
542   EXPECT_EQ(parent->OffsetWidth(), 800);
543   //  The child frame should return to the original size.
544   EXPECT_EQ(child->OffsetWidth(), 800);
545   EXPECT_EQ(target->OffsetWidth(), 800);
546 }
547 
548 }  // namespace blink
549