Home | History | Annotate | Download | only in page
      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