[Qt] Assert in GestureTapHighlighter.
[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(IntPoint()));
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 inline bool contains(const LayoutRect& rect, int x)
85 {
86     return !rect.isEmpty() && x >= rect.x() && x <= rect.maxX();
87 }
88
89 inline bool strikes(const LayoutRect& a, const LayoutRect& b)
90 {
91     return !a.isEmpty() && !b.isEmpty()
92         && a.x() <= b.maxX() && b.x() <= a.maxX()
93         && a.y() <= b.maxY() && b.y() <= a.maxY();
94 }
95
96 inline void shiftXEdgesToContainIfStrikes(LayoutRect& rect, const LayoutRect& other)
97 {
98     if (rect.isEmpty())
99         return;
100     LayoutUnit leftSide = rect.x();
101     LayoutUnit rightSide = rect.maxX();
102
103     if (!other.isEmpty() && strikes(rect, other)) {
104         leftSide = std::min(leftSide, other.x());
105         rightSide = std::max(rightSide, other.maxX());
106     }
107
108     rect.setX(leftSide);
109     rect.setWidth(rightSide - leftSide);
110 }
111
112 inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next)
113 {
114     // The rounding check depends on the rects not intersecting eachother,
115     // or being contained for that matter.
116     ASSERT(!rect.intersects(prev));
117     ASSERT(!rect.intersects(next));
118
119     if (rect.isEmpty())
120         return;
121
122     const int rounding = 4;
123
124     FloatRect copy(rect);
125     copy.inflateX(rounding);
126     copy.inflateY(rounding / 2);
127
128     FloatSize rounded(rounding * 1.8, rounding * 1.8);
129     FloatSize squared(0, 0);
130
131     path.addBeziersForRoundedRect(copy,
132             contains(prev, rect.x()) ? squared : rounded,
133             contains(prev, rect.maxX()) ? squared : rounded,
134             contains(next, rect.x()) ? squared : rounded,
135             contains(next, rect.maxX()) ? squared : rounded);
136 }
137
138 Path pathForRenderer(RenderObject* o)
139 {
140     ASSERT(o);
141     Path path;
142
143     Vector<IntRect> rects;
144     o->addFocusRingRects(rects, /* acc. offset */ ownerFrameToMainFrameOffset(o));
145
146     // The basic idea is to allow up to three different boxes in order to highlight
147     // text with line breaks more nicer than using a bounding box.
148
149     // Merge all center boxes (all but the first and the last).
150     LayoutRect mid;
151     for (size_t i = 1; i < rects.size() - 1; ++i)
152         mid.uniteIfNonZero(rects.at(i));
153
154     Vector<LayoutRect> drawableRects;
155
156     if (!mid.isEmpty())
157         drawableRects.append(mid);
158
159     // Add the first box, but merge it with the center boxes if it intersects.
160     if (rects.size() && !rects.first().isEmpty()) {
161         // Adjust center boxes to boundary of first
162         if (drawableRects.size())
163             shiftXEdgesToContainIfStrikes(drawableRects.last(), rects.first());
164         if (drawableRects.size() && drawableRects.last().intersects(rects.first()))
165             drawableRects.last().unite(rects.first());
166         else
167             drawableRects.prepend(rects.first());
168     }
169
170     // Add the last box, but merge it with the center boxes if it intersects.
171     if (rects.size() > 1 && !rects.last().isEmpty()) {
172         // Adjust center boxes to boundary of last
173         if (drawableRects.size())
174             shiftXEdgesToContainIfStrikes(drawableRects.last(), rects.last());
175         if (drawableRects.size() && drawableRects.last().intersects(rects.last()))
176             drawableRects.last().unite(rects.last());
177         else
178             drawableRects.append(rects.last());
179     }
180
181     for (size_t i = 0; i < drawableRects.size(); ++i) {
182         LayoutRect prev = i ? drawableRects.at(i - 1) : LayoutRect();
183         LayoutRect next = i < (drawableRects.size() - 1) ? drawableRects.at(i + 1) : LayoutRect();
184         addHighlightRect(path, drawableRects.at(i), prev, next);
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     path = pathForRenderer(renderer);
203     path.transform(localToAbsoluteTransform(renderer));
204
205     return path;
206 }
207
208 } // namespace GestureTapHighlighter
209
210 } // namespace WebCore