Tap highlighting: Support better outlines for multiline inlines https://bugs.webkit...
[WebKit-https.git] / Source / WebCore / page / GestureTapHighlighter.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer.
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include "config.h"
31 #include "GestureTapHighlighter.h"
32
33 #include "Element.h"
34 #include "Frame.h"
35 #include "FrameView.h"
36 #include "GraphicsContext.h"
37 #include "GraphicsTypes.h"
38 #include "Node.h"
39 #include "Page.h"
40 #include "RenderBoxModelObject.h"
41 #include "RenderInline.h"
42 #include "RenderLayer.h"
43 #include "RenderObject.h"
44
45 namespace WebCore {
46
47 namespace {
48
49 inline LayoutPoint ownerFrameToMainFrameOffset(const RenderObject* o)
50 {
51     ASSERT(o->node());
52     Frame* containingFrame = o->frame();
53     if (!containingFrame)
54         return LayoutPoint();
55
56     Frame* mainFrame = containingFrame->page()->mainFrame();
57
58     LayoutPoint mainFramePoint = mainFrame->view()->rootViewToContents(containingFrame->view()->contentsToRootView(LayoutPoint()));
59     return mainFramePoint;
60 }
61
62 AffineTransform localToAbsoluteTransform(const RenderObject* o)
63 {
64     AffineTransform transform;
65     LayoutPoint referencePoint;
66
67     while (o) {
68         RenderObject* nextContainer = o->container();
69         if (!nextContainer)
70             break;
71
72         LayoutSize containerOffset = o->offsetFromContainer(nextContainer, referencePoint);
73         TransformationMatrix t;
74         o->getTransformFromContainer(nextContainer, containerOffset, t);
75
76         transform = t.toAffineTransform() * transform;
77         referencePoint.move(containerOffset);
78         o = nextContainer;
79     }
80
81     return transform;
82 }
83
84 Path pathForRenderBox(RenderBox* o)
85 {
86     ASSERT(o);
87     const int rounding = 4;
88
89     LayoutRect contentBox;
90     LayoutRect paddingBox;
91     LayoutRect borderBox;
92
93     contentBox = o->contentBoxRect();
94     paddingBox = LayoutRect(
95             contentBox.x() - o->paddingLeft(),
96             contentBox.y() - o->paddingTop(),
97             contentBox.width() + o->paddingLeft() + o->paddingRight(),
98             contentBox.height() + o->paddingTop() + o->paddingBottom());
99     borderBox = LayoutRect(
100             paddingBox.x() - o->borderLeft(),
101             paddingBox.y() - o->borderTop(),
102             paddingBox.width() + o->borderLeft() + o->borderRight(),
103             paddingBox.height() + o->borderTop() + o->borderBottom());
104
105     FloatRect rect(borderBox);
106     rect.inflate(rounding);
107
108     rect.move(toLayoutSize(ownerFrameToMainFrameOffset(o)));
109
110     Path path;
111     FloatSize rounded(rounding * 1.8, rounding * 1.8);
112     path.addRoundedRect(rect, rounded);
113
114     return path;
115 }
116
117 void addRectWithRoundedCorners(Path& path, const LayoutRect& rect, bool topLeft, bool topRight, bool bottomLeft, bool bottomRight)
118 {
119     const int rounding = 4;
120
121     FloatRect copy(rect);
122     copy.inflateX(rounding);
123     copy.inflateY(rounding / 2);
124
125     FloatSize rounded(rounding * 1.8, rounding * 1.8);
126     FloatSize squared(0, 0);
127
128     path.addBeziersForRoundedRect(copy,
129             topLeft ? rounded : squared, topRight ? rounded : squared,
130             bottomLeft ? rounded : squared, bottomRight ? rounded : squared);
131 }
132
133 inline bool contains(LayoutRect rect, int x)
134 {
135     return !rect.isEmpty() && x >= rect.x() && x <= rect.maxX();
136 }
137
138 Path pathForRenderInline(RenderInline* o)
139 {
140     ASSERT(o);
141     Path path;
142
143     Vector<LayoutRect> rects;
144     o->absoluteRects(rects, /* acc. offset */ ownerFrameToMainFrameOffset(o));
145
146     LayoutRect first = rects.size() ? rects.first() : LayoutRect();
147     LayoutRect last = rects.size() > 1 ? rects.last() : LayoutRect();
148     LayoutRect middle;
149     for (int i = 1; i < rects.size() - 1; ++i)
150         middle.uniteIfNonZero(rects.at(i));
151
152     if (!middle.isEmpty()) {
153         int leftSide = middle.x();
154         int rightSide = middle.maxX();
155
156         if (!first.isEmpty()) {
157             leftSide = std::min(leftSide, first.x());
158             rightSide = std::max(rightSide, first.maxX());
159         }
160         if (!last.isEmpty()) {
161             leftSide = std::min(leftSide, last.x());
162             rightSide = std::max(rightSide, last.maxX());
163         }
164
165         middle.setX(leftSide);
166         middle.setWidth(rightSide - leftSide);
167     }
168
169     if (!first.isEmpty()) {
170         bool roundBottomLeft = !contains(middle, first.x()) && !contains(last, first.x());
171         bool roundBottomRight = !contains(middle, first.maxX()) && !contains(last, first.maxX());
172         addRectWithRoundedCorners(path, first, /* roundTopLeft */ true, /* roundTopRight */ true, roundBottomLeft, roundBottomRight);
173     }
174
175     if (!middle.isEmpty()) {
176         bool roundTopLeft = !contains(first, middle.x());
177         bool roundBottomRight = !contains(last, middle.maxX());
178         addRectWithRoundedCorners(path, middle, roundTopLeft, /* roundTopRight */ false, /* roundBottomLeft */ false, roundBottomRight);
179     }
180
181     if (!last.isEmpty()) {
182         bool roundTopLeft = !contains(middle, last.x()) && !contains(first, last.x());
183         bool roundTopRight = !contains(middle, last.maxX()) && !contains(first, last.maxX());
184         addRectWithRoundedCorners(path, last, roundTopLeft, roundTopRight, /* roundBottomLeft */ true, /* roundBottomRight */ true);
185     }
186
187     return path;
188 }
189
190 } // anonymous namespace
191
192 namespace GestureTapHighlighter {
193
194 Path pathForNodeHighlight(const Node* node)
195 {
196     RenderObject* renderer = node->renderer();
197
198     Path path;
199     if (!renderer || (!renderer->isBox() && !renderer->isRenderInline()))
200         return path;
201
202     if (renderer->isBox())
203         path = pathForRenderBox(toRenderBox(renderer));
204     else
205         path = pathForRenderInline(toRenderInline(renderer));
206
207     path.transform(localToAbsoluteTransform(renderer));
208     return path;
209 }
210
211 } // namespace GestureTapHighlighter
212
213 } // namespace WebCore