1/*
2 * Copyright (C) 2006, 2008, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20*/
21
22#include "config.h"
23#include "HitTestLocation.h"
24
25#include "CachedImage.h"
26#include "DocumentMarkerController.h"
27#include "Frame.h"
28#include "FrameSelection.h"
29#include "FrameTree.h"
30#include "HTMLAnchorElement.h"
31#include "HTMLImageElement.h"
32#include "HTMLInputElement.h"
33#include "HTMLMediaElement.h"
34#include "HTMLNames.h"
35#include "HTMLParserIdioms.h"
36#include "HTMLPlugInImageElement.h"
37#include "HTMLVideoElement.h"
38#include "RenderBlock.h"
39#include "RenderImage.h"
40#include "RenderInline.h"
41#include "Scrollbar.h"
42
43#if ENABLE(SVG)
44#include "SVGNames.h"
45#include "XLinkNames.h"
46#endif
47
48namespace WebCore {
49
50using namespace HTMLNames;
51
52HitTestLocation::HitTestLocation()
53    : m_region(0)
54    , m_isRectBased(false)
55    , m_isRectilinear(true)
56{
57}
58
59HitTestLocation::HitTestLocation(const LayoutPoint& point)
60    : m_point(point)
61    , m_boundingBox(rectForPoint(point, 0, 0, 0, 0))
62    , m_transformedPoint(point)
63    , m_transformedRect(m_boundingBox)
64    , m_region(0)
65    , m_isRectBased(false)
66    , m_isRectilinear(true)
67{
68}
69
70HitTestLocation::HitTestLocation(const FloatPoint& point)
71    : m_point(flooredLayoutPoint(point))
72    , m_boundingBox(rectForPoint(m_point, 0, 0, 0, 0))
73    , m_transformedPoint(point)
74    , m_transformedRect(m_boundingBox)
75    , m_region(0)
76    , m_isRectBased(false)
77    , m_isRectilinear(true)
78{
79}
80
81HitTestLocation::HitTestLocation(const FloatPoint& point, const FloatQuad& quad)
82    : m_transformedPoint(point)
83    , m_transformedRect(quad)
84    , m_region(0)
85    , m_isRectBased(true)
86{
87    m_point = flooredLayoutPoint(point);
88    m_boundingBox = enclosingIntRect(quad.boundingBox());
89    m_isRectilinear = quad.isRectilinear();
90}
91
92HitTestLocation::HitTestLocation(const LayoutPoint& centerPoint, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
93    : m_point(centerPoint)
94    , m_boundingBox(rectForPoint(centerPoint, topPadding, rightPadding, bottomPadding, leftPadding))
95    , m_transformedPoint(centerPoint)
96    , m_region(0)
97    , m_isRectBased(topPadding || rightPadding || bottomPadding || leftPadding)
98    , m_isRectilinear(true)
99{
100    m_transformedRect = FloatQuad(m_boundingBox);
101}
102
103HitTestLocation::HitTestLocation(const HitTestLocation& other, const LayoutSize& offset, RenderRegion* region)
104    : m_point(other.m_point)
105    , m_boundingBox(other.m_boundingBox)
106    , m_transformedPoint(other.m_transformedPoint)
107    , m_transformedRect(other.m_transformedRect)
108    , m_region(region ? region : other.m_region)
109    , m_isRectBased(other.m_isRectBased)
110    , m_isRectilinear(other.m_isRectilinear)
111{
112    move(offset);
113}
114
115HitTestLocation::HitTestLocation(const HitTestLocation& other)
116    : m_point(other.m_point)
117    , m_boundingBox(other.m_boundingBox)
118    , m_transformedPoint(other.m_transformedPoint)
119    , m_transformedRect(other.m_transformedRect)
120    , m_region(other.m_region)
121    , m_isRectBased(other.m_isRectBased)
122    , m_isRectilinear(other.m_isRectilinear)
123{
124}
125
126HitTestLocation::~HitTestLocation()
127{
128}
129
130HitTestLocation& HitTestLocation::operator=(const HitTestLocation& other)
131{
132    m_point = other.m_point;
133    m_boundingBox = other.m_boundingBox;
134    m_transformedPoint = other.m_transformedPoint;
135    m_transformedRect = other.m_transformedRect;
136    m_region = other.m_region;
137    m_isRectBased = other.m_isRectBased;
138    m_isRectilinear = other.m_isRectilinear;
139
140    return *this;
141}
142
143void HitTestLocation::move(const LayoutSize& offset)
144{
145    m_point.move(offset);
146    m_transformedPoint.move(offset);
147    m_transformedRect.move(offset);
148    m_boundingBox = enclosingIntRect(m_transformedRect.boundingBox());
149}
150
151template<typename RectType>
152bool HitTestLocation::intersectsRect(const RectType& rect) const
153{
154    // FIXME: When the hit test is not rect based we should use rect.contains(m_point).
155    // That does change some corner case tests though.
156
157    // First check if rect even intersects our bounding box.
158    if (!rect.intersects(m_boundingBox))
159        return false;
160
161    // If the transformed rect is rectilinear the bounding box intersection was accurate.
162    if (m_isRectilinear)
163        return true;
164
165    // If rect fully contains our bounding box, we are also sure of an intersection.
166    if (rect.contains(m_boundingBox))
167        return true;
168
169    // Otherwise we need to do a slower quad based intersection test.
170    return m_transformedRect.intersectsRect(rect);
171}
172
173bool HitTestLocation::intersects(const LayoutRect& rect) const
174{
175    return intersectsRect(rect);
176}
177
178bool HitTestLocation::intersects(const FloatRect& rect) const
179{
180    return intersectsRect(rect);
181}
182
183bool HitTestLocation::intersects(const RoundedRect& rect) const
184{
185    return rect.intersectsQuad(m_transformedRect);
186}
187
188IntRect HitTestLocation::rectForPoint(const LayoutPoint& point, unsigned topPadding, unsigned rightPadding, unsigned bottomPadding, unsigned leftPadding)
189{
190    IntPoint actualPoint(flooredIntPoint(point));
191    actualPoint -= IntSize(leftPadding, topPadding);
192
193    IntSize actualPadding(leftPadding + rightPadding, topPadding + bottomPadding);
194    // As IntRect is left inclusive and right exclusive (seeing IntRect::contains(x, y)), adding "1".
195    // FIXME: Remove this once non-rect based hit-detection stops using IntRect:intersects.
196    actualPadding += IntSize(1, 1);
197
198    return IntRect(actualPoint, actualPadding);
199}
200
201} // namespace WebCore
202