blob: 68362a9545c29a51205b5022dfbd77426d3476f1 [file] [log] [blame]
// Copyright 2016 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.
#include "core/paint/SVGInlineTextBoxPainter.h"
#include "core/dom/Document.h"
#include "core/dom/Range.h"
#include "core/editing/DOMSelection.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/layout/LayoutView.h"
#include "core/layout/line/InlineTextBox.h"
#include "core/layout/svg/LayoutSVGInlineText.h"
#include "core/layout/svg/LayoutSVGText.h"
#include "core/paint/PaintControllerPaintTest.h"
#include "core/paint/PaintLayer.h"
#include "platform/geometry/IntRectOutsets.h"
#include "platform/graphics/GraphicsLayer.h"
#include "platform/graphics/paint/DisplayItemList.h"
#include "platform/graphics/paint/DrawingDisplayItem.h"
#include "platform/graphics/paint/PaintController.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
class SVGInlineTextBoxPainterTest : public PaintControllerPaintTest {
public:
const DrawingDisplayItem* GetDrawingForSVGTextById(const char* element_name) {
// Look up the inline text box that serves as the display item client for
// the painted text.
LayoutSVGText* target_svg_text =
ToLayoutSVGText(GetDocument()
.getElementById(AtomicString(element_name))
->GetLayoutObject());
LayoutSVGInlineText* target_inline_text =
target_svg_text->DescendantTextNodes()[0];
const DisplayItemClient* target_client =
static_cast<const DisplayItemClient*>(
target_inline_text->FirstTextBox());
// Find the appropriate drawing in the display item list.
const DisplayItemList& display_item_list =
RootPaintController().GetDisplayItemList();
for (size_t i = 0; i < display_item_list.size(); i++) {
if (display_item_list[i].Client() == *target_client)
return static_cast<const DrawingDisplayItem*>(&display_item_list[i]);
}
return nullptr;
}
void SelectAllText() {
Range* range = GetDocument().createRange();
range->selectNode(GetDocument().documentElement());
LocalDOMWindow* window = GetDocument().domWindow();
DOMSelection* selection = window->getSelection();
selection->removeAllRanges();
selection->addRange(range);
}
private:
void SetUp() override {
PaintControllerPaintTest::SetUp();
EnableCompositing();
}
};
static void AssertTextDrawingEquals(
const DrawingDisplayItem* drawing_display_item,
const char* str) {
ASSERT_EQ(str,
static_cast<const InlineTextBox*>(&drawing_display_item->Client())
->GetText());
}
bool ApproximatelyEqual(int a, int b, int delta) {
return abs(a - b) <= delta;
}
const static int kPixelDelta = 4;
#define EXPECT_RECT_EQ(expected, actual) \
do { \
const FloatRect& actual_rect = actual; \
EXPECT_TRUE( \
ApproximatelyEqual(expected.X(), actual_rect.X(), kPixelDelta)) \
<< "actual: " << actual_rect.X() << ", expected: " << expected.X(); \
EXPECT_TRUE( \
ApproximatelyEqual(expected.Y(), actual_rect.Y(), kPixelDelta)) \
<< "actual: " << actual_rect.Y() << ", expected: " << expected.Y(); \
EXPECT_TRUE(ApproximatelyEqual(expected.Width(), actual_rect.Width(), \
kPixelDelta)) \
<< "actual: " << actual_rect.Width() \
<< ", expected: " << expected.Width(); \
EXPECT_TRUE(ApproximatelyEqual(expected.Height(), actual_rect.Height(), \
kPixelDelta)) \
<< "actual: " << actual_rect.Height() \
<< ", expected: " << expected.Height(); \
} while (false)
TEST_F(SVGInlineTextBoxPainterTest, TextCullRect_DefaultWritingMode) {
SetBodyInnerHTML(
"<svg width='400px' height='400px' font-family='Arial' font-size='30'>"
"<text id='target' x='50' y='30'>x</text>"
"</svg>");
GetDocument().View()->UpdateAllLifecyclePhases();
const DrawingDisplayItem* drawing_display_item =
GetDrawingForSVGTextById("target");
AssertTextDrawingEquals(drawing_display_item, "x");
EXPECT_RECT_EQ(IntRect(50, 3, 15, 33),
drawing_display_item->GetPaintRecordBounds());
SelectAllText();
GetDocument().View()->UpdateAllLifecyclePhases();
drawing_display_item = GetDrawingForSVGTextById("target");
AssertTextDrawingEquals(drawing_display_item, "x");
EXPECT_RECT_EQ(IntRect(50, 3, 15, 33),
drawing_display_item->GetPaintRecordBounds());
}
TEST_F(SVGInlineTextBoxPainterTest, TextCullRect_WritingModeTopToBottom) {
SetBodyInnerHTML(
"<svg width='400px' height='400px' font-family='Arial' font-size='30'>"
"<text id='target' x='50' y='30' writing-mode='tb'>x</text>"
"</svg>");
GetDocument().View()->UpdateAllLifecyclePhases();
const DrawingDisplayItem* drawing_display_item =
GetDrawingForSVGTextById("target");
AssertTextDrawingEquals(drawing_display_item, "x");
EXPECT_RECT_EQ(IntRect(33, 30, 34, 15),
drawing_display_item->GetPaintRecordBounds());
SelectAllText();
GetDocument().View()->UpdateAllLifecyclePhases();
// The selection rect is one pixel taller due to sub-pixel difference
// between the text bounds and selection bounds in combination with use of
// enclosingIntRect() in SVGInlineTextBox::localSelectionRect().
drawing_display_item = GetDrawingForSVGTextById("target");
AssertTextDrawingEquals(drawing_display_item, "x");
EXPECT_RECT_EQ(IntRect(33, 30, 34, 16),
drawing_display_item->GetPaintRecordBounds());
}
TEST_F(SVGInlineTextBoxPainterTest, TextCullRect_TextShadow) {
SetBodyInnerHTML(
"<svg width='400px' height='400px' font-family='Arial' font-size='30'>"
"<text id='target' x='50' y='30' style='text-shadow: 200px 200px 5px "
"red'>x</text>"
"</svg>");
GetDocument().View()->UpdateAllLifecyclePhases();
const DrawingDisplayItem* drawing_display_item =
GetDrawingForSVGTextById("target");
AssertTextDrawingEquals(drawing_display_item, "x");
EXPECT_RECT_EQ(IntRect(50, 3, 220, 238),
drawing_display_item->GetPaintRecordBounds());
}
} // namespace
} // namespace blink