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 "config.h" 6 #include "core/page/PrintContext.h" 7 8 #include "core/dom/Document.h" 9 #include "core/html/HTMLElement.h" 10 #include "core/testing/DummyPageHolder.h" 11 #include "platform/graphics/GraphicsContext.h" 12 #include "platform/testing/SkiaForCoreTesting.h" 13 #include "platform/text/TextStream.h" 14 #include <gtest/gtest.h> 15 16 namespace blink { 17 18 const int kPageWidth = 800; 19 const int kPageHeight = 600; 20 21 class MockPrintContext : public PrintContext { 22 public: 23 MockPrintContext(LocalFrame* frame) : PrintContext(frame) { } 24 25 void outputLinkAndLinkedDestinations(GraphicsContext& context, Node* node, const IntRect& pageRect) 26 { 27 PrintContext::outputLinkAndLinkedDestinations(context, node, pageRect); 28 } 29 }; 30 31 class MockCanvas : public SkCanvas { 32 public: 33 enum OperationType { 34 DrawRect, 35 DrawPoint 36 }; 37 38 struct Operation { 39 OperationType type; 40 SkRect rect; 41 }; 42 43 MockCanvas() : SkCanvas(kPageWidth, kPageHeight) { } 44 45 virtual void drawRect(const SkRect& rect, const SkPaint& paint) OVERRIDE 46 { 47 ASSERT_TRUE(paint.getAnnotation()); 48 Operation operation = { DrawRect, rect }; 49 m_recordedOperations.append(operation); 50 } 51 52 virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) OVERRIDE 53 { 54 ASSERT_EQ(1u, count); // Only called from drawPoint(). 55 ASSERT_TRUE(paint.getAnnotation()); 56 Operation operation = { DrawPoint, SkRect::MakeXYWH(pts[0].x(), pts[0].y(), 0, 0) }; 57 m_recordedOperations.append(operation); 58 } 59 60 const Vector<Operation>& recordedOperations() const { return m_recordedOperations; } 61 62 private: 63 Vector<Operation> m_recordedOperations; 64 }; 65 66 class PrintContextTest : public testing::Test { 67 protected: 68 PrintContextTest() 69 : m_pageHolder(DummyPageHolder::create(IntSize(kPageWidth, kPageHeight))) 70 , m_printContext(adoptPtrWillBeNoop(new MockPrintContext(document().frame()))) { } 71 72 Document& document() const { return m_pageHolder->document(); } 73 MockPrintContext& printContext() { return *m_printContext.get(); } 74 75 void setBodyInnerHTML(String bodyContent) 76 { 77 document().body()->setInnerHTML(bodyContent, ASSERT_NO_EXCEPTION); 78 } 79 80 void printSinglePage(SkCanvas& canvas) 81 { 82 IntRect pageRect(0, 0, kPageWidth, kPageHeight); 83 GraphicsContext context(&canvas); 84 printContext().begin(kPageWidth, kPageHeight); 85 printContext().outputLinkAndLinkedDestinations(context, &document(), pageRect); 86 printContext().end(); 87 } 88 89 static String htmlForLink(int x, int y, int width, int height, const char* url) 90 { 91 TextStream ts; 92 ts << "<a style='position: absolute; left: " << x << "px; top: " << y << "px; width: " << width << "px; height: " << height 93 << "px' href='" << url << "'>" << url << "</a>"; 94 return ts.release(); 95 } 96 97 static String htmlForAnchor(int x, int y, const char* name) 98 { 99 TextStream ts; 100 ts << "<a name='" << name << "' style='position: absolute; left: " << x << "px; top: " << y << "px'>" << name << "</a>"; 101 return ts.release(); 102 } 103 104 private: 105 OwnPtr<DummyPageHolder> m_pageHolder; 106 OwnPtrWillBePersistent<MockPrintContext> m_printContext; 107 }; 108 109 #define EXPECT_SKRECT_EQ(expectedX, expectedY, expectedWidth, expectedHeight, actualRect) \ 110 EXPECT_EQ(expectedX, actualRect.x()); \ 111 EXPECT_EQ(expectedY, actualRect.y()); \ 112 EXPECT_EQ(expectedWidth, actualRect.width()); \ 113 EXPECT_EQ(expectedHeight, actualRect.height()); 114 115 TEST_F(PrintContextTest, LinkTarget) 116 { 117 MockCanvas canvas; 118 setBodyInnerHTML(htmlForLink(50, 60, 70, 80, "http://www.google.com") 119 + htmlForLink(150, 160, 170, 180, "http://www.google.com#fragment")); 120 printSinglePage(canvas); 121 122 const Vector<MockCanvas::Operation>& operations = canvas.recordedOperations(); 123 ASSERT_EQ(2u, operations.size()); 124 125 // The items in the result can be in any sequence. 126 size_t firstIndex = operations[0].rect.x() == 50 ? 0 : 1; 127 EXPECT_EQ(MockCanvas::DrawRect, operations[firstIndex].type); 128 EXPECT_SKRECT_EQ(50, 60, 70, 80, operations[firstIndex].rect); 129 // We should also check if the annotation is correct but Skia doesn't export 130 // SkAnnotation API. 131 132 size_t secondIndex = firstIndex == 0 ? 1 : 0; 133 EXPECT_EQ(MockCanvas::DrawRect, operations[secondIndex].type); 134 EXPECT_SKRECT_EQ(150, 160, 170, 180, operations[secondIndex].rect); 135 } 136 137 TEST_F(PrintContextTest, LinkedTarget) 138 { 139 MockCanvas canvas; 140 document().setBaseURLOverride(KURL(ParsedURLString, "http://a.com/")); 141 setBodyInnerHTML(htmlForLink(50, 60, 70, 80, "#fragment") // Generates a Link_Named_Dest_Key annotation 142 + htmlForLink(150, 160, 170, 180, "#not-found") // Generates no annotation 143 + htmlForAnchor(250, 260, "fragment") // Generates a Define_Named_Dest_Key annotation 144 + htmlForAnchor(350, 360, "fragment-not-used")); // Generates no annotation 145 printSinglePage(canvas); 146 147 const Vector<MockCanvas::Operation>& operations = canvas.recordedOperations(); 148 ASSERT_EQ(2u, operations.size()); 149 150 size_t firstIndex = operations[0].rect.x() == 50 ? 0 : 1; 151 EXPECT_EQ(MockCanvas::DrawRect, operations[firstIndex].type); 152 EXPECT_SKRECT_EQ(50, 60, 70, 80, operations[firstIndex].rect); 153 154 size_t secondIndex = firstIndex == 0 ? 1 : 0; 155 EXPECT_EQ(MockCanvas::DrawPoint, operations[secondIndex].type); 156 EXPECT_SKRECT_EQ(250, 260, 0, 0, operations[secondIndex].rect); 157 } 158 159 } // namespace blink 160