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