3c11db17d8bf50cc31fc089aeaf0a1c79d2e40c2
[WebKit-https.git] / Source / WebCore / rendering / EllipsisBox.cpp
1 /**
2  * Copyright (C) 2003, 2006 Apple Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #include "config.h"
21 #include "EllipsisBox.h"
22
23 #include "Document.h"
24 #include "FontCascade.h"
25 #include "GraphicsContext.h"
26 #include "HitTestResult.h"
27 #include "InlineTextBox.h"
28 #include "PaintInfo.h"
29 #include "RootInlineBox.h"
30 #include "TextRun.h"
31 #include <wtf/IsoMallocInlines.h>
32
33 namespace WebCore {
34
35 WTF_MAKE_ISO_ALLOCATED_IMPL(EllipsisBox);
36
37 EllipsisBox::EllipsisBox(RenderBlockFlow& renderer, const AtomicString& ellipsisStr, InlineFlowBox* parent, int width, int height, int y, bool firstLine, bool isHorizontal, InlineBox* markupBox)
38     : InlineElementBox(renderer, FloatPoint(0, y), width, firstLine, true, false, false, isHorizontal, 0, 0, parent)
39     , m_shouldPaintMarkupBox(markupBox)
40     , m_height(height)
41     , m_str(ellipsisStr)
42     , m_selectionState(RenderObject::SelectionNone)
43 {
44 #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
45     m_isEverInChildList = false;
46 #endif
47 }
48
49 void EllipsisBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom)
50 {
51     GraphicsContext& context = paintInfo.context();
52     const RenderStyle& lineStyle = this->lineStyle();
53     Color textColor = lineStyle.visitedDependentColor(CSSPropertyWebkitTextFillColor);
54     if (textColor != context.fillColor())
55         context.setFillColor(textColor);
56     bool setShadow = false;
57     if (lineStyle.textShadow()) {
58         context.setShadow(LayoutSize(lineStyle.textShadow()->x(), lineStyle.textShadow()->y()),
59             lineStyle.textShadow()->radius(), lineStyle.textShadow()->color());
60         setShadow = true;
61     }
62
63     const FontCascade& font = lineStyle.fontCascade();
64     if (selectionState() != RenderObject::SelectionNone) {
65         paintSelection(context, paintOffset, lineStyle, font);
66
67         // Select the correct color for painting the text.
68         Color foreground = paintInfo.forceTextColor() ? paintInfo.forcedTextColor() : blockFlow().selectionForegroundColor();
69         if (foreground.isValid() && foreground != textColor)
70             context.setFillColor(foreground);
71     }
72
73     // FIXME: Why is this always LTR? Fix by passing correct text run flags below.
74     context.drawText(font, RenderBlock::constructTextRun(m_str, lineStyle, AllowTrailingExpansion), LayoutPoint(x() + paintOffset.x(), y() + paintOffset.y() + lineStyle.fontMetrics().ascent()));
75
76     // Restore the regular fill color.
77     if (textColor != context.fillColor())
78         context.setFillColor(textColor);
79
80     if (setShadow)
81         context.clearShadow();
82
83     paintMarkupBox(paintInfo, paintOffset, lineTop, lineBottom, lineStyle);
84 }
85
86 InlineBox* EllipsisBox::markupBox() const
87 {
88     if (!m_shouldPaintMarkupBox)
89         return 0;
90
91     RootInlineBox* lastLine = blockFlow().lineAtIndex(blockFlow().lineCount() - 1);
92     if (!lastLine)
93         return 0;
94
95     // If the last line-box on the last line of a block is a link, -webkit-line-clamp paints that box after the ellipsis.
96     // It does not actually move the link.
97     InlineBox* anchorBox = lastLine->lastChild();
98     if (!anchorBox || !anchorBox->renderer().style().isLink())
99         return 0;
100
101     return anchorBox;
102 }
103
104 void EllipsisBox::paintMarkupBox(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom, const RenderStyle& style)
105 {
106     InlineBox* markupBox = this->markupBox();
107     if (!markupBox)
108         return;
109
110     LayoutPoint adjustedPaintOffset = paintOffset;
111     adjustedPaintOffset.move(x() + m_logicalWidth - markupBox->x(),
112         y() + style.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent()));
113     markupBox->paint(paintInfo, adjustedPaintOffset, lineTop, lineBottom);
114 }
115
116 IntRect EllipsisBox::selectionRect()
117 {
118     const RenderStyle& lineStyle = this->lineStyle();
119     const FontCascade& font = lineStyle.fontCascade();
120     const RootInlineBox& rootBox = root();
121     // FIXME: Why is this always LTR? Fix by passing correct text run flags below.
122     LayoutRect selectionRect = LayoutRect(x(), y() + rootBox.selectionTopAdjustedForPrecedingBlock(), 0, rootBox.selectionHeightAdjustedForPrecedingBlock());
123     font.adjustSelectionRectForText(RenderBlock::constructTextRun(m_str, lineStyle, AllowTrailingExpansion), selectionRect);
124     // FIXME: use directional pixel snapping instead.
125     return enclosingIntRect(selectionRect);
126 }
127
128 void EllipsisBox::paintSelection(GraphicsContext& context, const LayoutPoint& paintOffset, const RenderStyle& style, const FontCascade& font)
129 {
130     Color textColor = style.visitedDependentColor(CSSPropertyColor);
131     Color c = blockFlow().selectionBackgroundColor();
132     if (!c.isVisible())
133         return;
134
135     // If the text color ends up being the same as the selection background, invert the selection
136     // background.
137     if (textColor == c)
138         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
139
140     const RootInlineBox& rootBox = root();
141     GraphicsContextStateSaver stateSaver(context);
142     // FIXME: Why is this always LTR? Fix by passing correct text run flags below.
143     LayoutRect selectionRect = LayoutRect(x() + paintOffset.x(), y() + paintOffset.y() + rootBox.selectionTop(), 0, rootBox.selectionHeight());
144     TextRun run = RenderBlock::constructTextRun(m_str, style, AllowTrailingExpansion);
145     font.adjustSelectionRectForText(run, selectionRect);
146     context.fillRect(snapRectToDevicePixelsWithWritingDirection(selectionRect, renderer().document().deviceScaleFactor(), run.ltr()), c);
147 }
148
149 bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction hitTestAction)
150 {
151     LayoutPoint adjustedLocation = accumulatedOffset + LayoutPoint(topLeft());
152
153     // Hit test the markup box.
154     if (InlineBox* markupBox = this->markupBox()) {
155         const RenderStyle& lineStyle = this->lineStyle();
156         LayoutUnit mtx = adjustedLocation.x() + m_logicalWidth - markupBox->x();
157         LayoutUnit mty = adjustedLocation.y() + lineStyle.fontMetrics().ascent() - (markupBox->y() + markupBox->lineStyle().fontMetrics().ascent());
158         if (markupBox->nodeAtPoint(request, result, locationInContainer, LayoutPoint(mtx, mty), lineTop, lineBottom, hitTestAction)) {
159             blockFlow().updateHitTestResult(result, locationInContainer.point() - LayoutSize(mtx, mty));
160             return true;
161         }
162     }
163
164     LayoutRect boundsRect(adjustedLocation, LayoutSize(m_logicalWidth, m_height));
165     if (visibleToHitTesting() && boundsRect.intersects(HitTestLocation::rectForPoint(locationInContainer.point(), 0, 0, 0, 0))) {
166         blockFlow().updateHitTestResult(result, locationInContainer.point() - toLayoutSize(adjustedLocation));
167         if (result.addNodeToListBasedTestResult(blockFlow().element(), request, locationInContainer, boundsRect) == HitTestProgress::Stop)
168             return true;
169     }
170
171     return false;
172 }
173
174 } // namespace WebCore