2
* Copyright (C) 2011 Google Inc. All rights reserved.
3
* Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
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.
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.
31
#include "GestureTapHighlighter.h"
35
#include "FrameView.h"
36
#include "GraphicsContext.h"
37
#include "GraphicsTypes.h"
40
#include "RenderBoxModelObject.h"
41
#include "RenderInline.h"
42
#include "RenderLayer.h"
43
#include "RenderObject.h"
49
inline LayoutPoint ownerFrameToMainFrameOffset(const RenderObject* o)
52
Frame* containingFrame = o->frame();
56
Frame* mainFrame = containingFrame->page()->mainFrame();
58
LayoutPoint mainFramePoint = mainFrame->view()->windowToContents(containingFrame->view()->contentsToWindow(IntPoint()));
59
return mainFramePoint;
62
AffineTransform localToAbsoluteTransform(const RenderObject* o)
64
AffineTransform transform;
65
LayoutPoint referencePoint;
68
RenderObject* nextContainer = o->container();
72
LayoutSize containerOffset = o->offsetFromContainer(nextContainer, referencePoint);
73
TransformationMatrix t;
74
o->getTransformFromContainer(nextContainer, containerOffset, t);
76
transform = t.toAffineTransform() * transform;
77
referencePoint.move(containerOffset);
84
inline bool contains(const LayoutRect& rect, int x)
86
return !rect.isEmpty() && x >= rect.x() && x <= rect.maxX();
89
inline bool strikes(const LayoutRect& a, const LayoutRect& b)
91
return !a.isEmpty() && !b.isEmpty()
92
&& a.x() <= b.maxX() && b.x() <= a.maxX()
93
&& a.y() <= b.maxY() && b.y() <= a.maxY();
96
inline void shiftXEdgesToContainIfStrikes(LayoutRect& rect, LayoutRect& other, bool isFirst)
101
if (other.isEmpty() || !strikes(rect, other))
104
LayoutUnit leftSide = std::min(rect.x(), other.x());
105
LayoutUnit rightSide = std::max(rect.maxX(), other.maxX());
107
rect.shiftXEdgeTo(leftSide);
108
rect.shiftMaxXEdgeTo(rightSide);
111
other.shiftMaxXEdgeTo(rightSide);
113
other.shiftXEdgeTo(leftSide);
116
inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next)
118
// The rounding check depends on the rects not intersecting eachother,
119
// or being contained for that matter.
120
ASSERT(!rect.intersects(prev));
121
ASSERT(!rect.intersects(next));
126
const int rounding = 4;
128
FloatRect copy(rect);
129
copy.inflateX(rounding);
130
copy.inflateY(rounding / 2);
132
FloatSize rounded(rounding * 1.8, rounding * 1.8);
133
FloatSize squared(0, 0);
135
path.addBeziersForRoundedRect(copy,
136
contains(prev, rect.x()) ? squared : rounded,
137
contains(prev, rect.maxX()) ? squared : rounded,
138
contains(next, rect.x()) ? squared : rounded,
139
contains(next, rect.maxX()) ? squared : rounded);
142
Path absolutePathForRenderer(RenderObject* const o)
146
Vector<IntRect> rects;
147
LayoutPoint frameOffset = ownerFrameToMainFrameOffset(o);
148
o->addFocusRingRects(rects, frameOffset);
153
// The basic idea is to allow up to three different boxes in order to highlight
154
// text with line breaks more nicer than using a bounding box.
156
// Merge all center boxes (all but the first and the last).
159
// Set the end value to integer. It ensures that no unsigned int overflow occurs
160
// in the test expression, in case of empty rects vector.
161
int end = rects.size() - 1;
162
for (int i = 1; i < end; ++i)
163
mid.uniteIfNonZero(rects.at(i));
168
// Add the first box, but merge it with the center boxes if it intersects or if the center box is empty.
169
if (rects.size() && !rects.first().isEmpty()) {
170
// If the mid box is empty at this point, unite it with the first box. This allows the first box to be
171
// united with the last box if they intersect in the following check for last. Not uniting them would
172
// trigger in assert in addHighlighRect due to the first and the last box intersecting, but being passed
173
// as two separate boxes.
174
if (mid.isEmpty() || mid.intersects(rects.first()))
175
mid.unite(rects.first());
177
first = rects.first();
178
shiftXEdgesToContainIfStrikes(mid, first, /* isFirst */ true);
182
// Add the last box, but merge it with the center boxes if it intersects.
183
if (rects.size() > 1 && !rects.last().isEmpty()) {
184
// Adjust center boxes to boundary of last
185
if (mid.intersects(rects.last()))
186
mid.unite(rects.last());
189
shiftXEdgesToContainIfStrikes(mid, last, /* isFirst */ false);
193
Vector<LayoutRect> drawableRects;
194
if (!first.isEmpty())
195
drawableRects.append(first);
197
drawableRects.append(mid);
199
drawableRects.append(last);
201
// Clip the overflow rects if needed, before the ring path is formed to
202
// ensure rounded highlight rects. This clipping has the problem with nested
203
// divs with transforms, which could be resolved by proper Path::intersecting.
204
for (int i = drawableRects.size() - 1; i >= 0; --i) {
205
LayoutRect& ringRect = drawableRects.at(i);
206
LayoutPoint ringRectLocation = ringRect.location();
208
ringRect.moveBy(-frameOffset);
210
RenderLayer* layer = o->enclosingLayer();
211
RenderObject* currentRenderer = o;
213
// Check ancestor layers for overflow clip and intersect them.
214
for (; layer; layer = layer->parent()) {
215
RenderLayerModelObject* layerRenderer = layer->renderer();
217
if (layerRenderer->hasOverflowClip() && layerRenderer != currentRenderer) {
218
bool containerSkipped = false;
219
// Skip ancestor layers that are not containers for the current renderer.
220
currentRenderer->container(layerRenderer, &containerSkipped);
221
if (containerSkipped)
223
ringRect.move(currentRenderer->offsetFromAncestorContainer(layerRenderer));
224
currentRenderer = layerRenderer;
226
ASSERT(layerRenderer->isBox());
227
ringRect.intersect(toRenderBox(layerRenderer)->borderBoxRect());
229
if (ringRect.isEmpty())
234
if (ringRect.isEmpty()) {
235
drawableRects.remove(i);
238
// After clipping, reset the original position so that parents' transforms apply correctly.
239
ringRect.setLocation(ringRectLocation);
243
for (size_t i = 0; i < drawableRects.size(); ++i) {
244
LayoutRect prev = i ? drawableRects.at(i - 1) : LayoutRect();
245
LayoutRect next = i < (drawableRects.size() - 1) ? drawableRects.at(i + 1) : LayoutRect();
246
addHighlightRect(path, drawableRects.at(i), prev, next);
249
path.transform(localToAbsoluteTransform(o));
253
} // anonymous namespace
255
namespace GestureTapHighlighter {
257
Path pathForNodeHighlight(const Node* node)
259
RenderObject* renderer = node->renderer();
261
if (!renderer || (!renderer->isBox() && !renderer->isRenderInline()))
264
return absolutePathForRenderer(renderer);
267
} // namespace GestureTapHighlighter
269
} // namespace WebCore