~ubuntu-branches/ubuntu/raring/qtwebkit-source/raring-proposed

« back to all changes in this revision

Viewing changes to Source/WebCore/page/GestureTapHighlighter.cpp

  • Committer: Package Import Robot
  • Author(s): Jonathan Riddell
  • Date: 2013-02-18 14:24:18 UTC
  • Revision ID: package-import@ubuntu.com-20130218142418-eon0jmjg3nj438uy
Tags: upstream-2.3
ImportĀ upstreamĀ versionĀ 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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()->windowToContents(containingFrame->view()->contentsToWindow(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, LayoutRect& other, bool isFirst)
 
97
{
 
98
    if (rect.isEmpty())
 
99
        return;
 
100
 
 
101
    if (other.isEmpty() || !strikes(rect, other))
 
102
        return;
 
103
 
 
104
    LayoutUnit leftSide = std::min(rect.x(), other.x());
 
105
    LayoutUnit rightSide = std::max(rect.maxX(), other.maxX());
 
106
 
 
107
    rect.shiftXEdgeTo(leftSide);
 
108
    rect.shiftMaxXEdgeTo(rightSide);
 
109
 
 
110
    if (isFirst)
 
111
        other.shiftMaxXEdgeTo(rightSide);
 
112
    else
 
113
        other.shiftXEdgeTo(leftSide);
 
114
}
 
115
 
 
116
inline void addHighlightRect(Path& path, const LayoutRect& rect, const LayoutRect& prev, const LayoutRect& next)
 
117
{
 
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));
 
122
 
 
123
    if (rect.isEmpty())
 
124
        return;
 
125
 
 
126
    const int rounding = 4;
 
127
 
 
128
    FloatRect copy(rect);
 
129
    copy.inflateX(rounding);
 
130
    copy.inflateY(rounding / 2);
 
131
 
 
132
    FloatSize rounded(rounding * 1.8, rounding * 1.8);
 
133
    FloatSize squared(0, 0);
 
134
 
 
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);
 
140
}
 
141
 
 
142
Path absolutePathForRenderer(RenderObject* const o)
 
143
{
 
144
    ASSERT(o);
 
145
 
 
146
    Vector<IntRect> rects;
 
147
    LayoutPoint frameOffset = ownerFrameToMainFrameOffset(o);
 
148
    o->addFocusRingRects(rects, frameOffset);
 
149
 
 
150
    if (rects.isEmpty())
 
151
        return Path();
 
152
 
 
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.
 
155
 
 
156
    // Merge all center boxes (all but the first and the last).
 
157
    LayoutRect mid;
 
158
 
 
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));
 
164
 
 
165
    LayoutRect first;
 
166
    LayoutRect last;
 
167
 
 
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());
 
176
        else {
 
177
            first = rects.first();
 
178
            shiftXEdgesToContainIfStrikes(mid, first, /* isFirst */ true);
 
179
        }
 
180
    }
 
181
 
 
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());
 
187
        else {
 
188
            last = rects.last();
 
189
            shiftXEdgesToContainIfStrikes(mid, last, /* isFirst */ false);
 
190
        }
 
191
    }
 
192
 
 
193
    Vector<LayoutRect> drawableRects;
 
194
    if (!first.isEmpty())
 
195
        drawableRects.append(first);
 
196
    if (!mid.isEmpty())
 
197
        drawableRects.append(mid);
 
198
    if (!last.isEmpty())
 
199
        drawableRects.append(last);
 
200
 
 
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();
 
207
 
 
208
        ringRect.moveBy(-frameOffset);
 
209
 
 
210
        RenderLayer* layer = o->enclosingLayer();
 
211
        RenderObject* currentRenderer = o;
 
212
 
 
213
        // Check ancestor layers for overflow clip and intersect them.
 
214
        for (; layer; layer = layer->parent()) {
 
215
            RenderLayerModelObject* layerRenderer = layer->renderer();
 
216
 
 
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)
 
222
                    continue;
 
223
                ringRect.move(currentRenderer->offsetFromAncestorContainer(layerRenderer));
 
224
                currentRenderer = layerRenderer;
 
225
 
 
226
                ASSERT(layerRenderer->isBox());
 
227
                ringRect.intersect(toRenderBox(layerRenderer)->borderBoxRect());
 
228
 
 
229
                if (ringRect.isEmpty())
 
230
                    break;
 
231
            }
 
232
        }
 
233
 
 
234
        if (ringRect.isEmpty()) {
 
235
            drawableRects.remove(i);
 
236
            continue;
 
237
        }
 
238
        // After clipping, reset the original position so that parents' transforms apply correctly.
 
239
        ringRect.setLocation(ringRectLocation);
 
240
    }
 
241
 
 
242
    Path path;
 
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);
 
247
    }
 
248
 
 
249
    path.transform(localToAbsoluteTransform(o));
 
250
    return path;
 
251
}
 
252
 
 
253
} // anonymous namespace
 
254
 
 
255
namespace GestureTapHighlighter {
 
256
 
 
257
Path pathForNodeHighlight(const Node* node)
 
258
{
 
259
    RenderObject* renderer = node->renderer();
 
260
 
 
261
    if (!renderer || (!renderer->isBox() && !renderer->isRenderInline()))
 
262
        return Path();
 
263
 
 
264
    return absolutePathForRenderer(renderer);
 
265
}
 
266
 
 
267
} // namespace GestureTapHighlighter
 
268
 
 
269
} // namespace WebCore