1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2004, 2005, 2006, 2009, 2011 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22 #include "config.h"
23 #include "HTMLAreaElement.h"
24
25 #include "AffineTransform.h"
26 #include "Attribute.h"
27 #include "Frame.h"
28 #include "HTMLImageElement.h"
29 #include "HTMLMapElement.h"
30 #include "HTMLNames.h"
31 #include "HitTestResult.h"
32 #include "Path.h"
33 #include "RenderImage.h"
34
35 using namespace std;
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
HTMLAreaElement(const QualifiedName & tagName,Document * document)41 inline HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document* document)
42 : HTMLAnchorElement(tagName, document)
43 , m_coordsLen(0)
44 , m_lastSize(-1, -1)
45 , m_shape(Unknown)
46 {
47 ASSERT(hasTagName(areaTag));
48 }
49
create(const QualifiedName & tagName,Document * document)50 PassRefPtr<HTMLAreaElement> HTMLAreaElement::create(const QualifiedName& tagName, Document* document)
51 {
52 return adoptRef(new HTMLAreaElement(tagName, document));
53 }
54
parseMappedAttribute(Attribute * attr)55 void HTMLAreaElement::parseMappedAttribute(Attribute* attr)
56 {
57 if (attr->name() == shapeAttr) {
58 if (equalIgnoringCase(attr->value(), "default"))
59 m_shape = Default;
60 else if (equalIgnoringCase(attr->value(), "circle"))
61 m_shape = Circle;
62 else if (equalIgnoringCase(attr->value(), "poly"))
63 m_shape = Poly;
64 else if (equalIgnoringCase(attr->value(), "rect"))
65 m_shape = Rect;
66 } else if (attr->name() == coordsAttr) {
67 m_coords = newCoordsArray(attr->value().string(), m_coordsLen);
68 } else if (attr->name() == altAttr || attr->name() == accesskeyAttr) {
69 // Do nothing.
70 } else
71 HTMLAnchorElement::parseMappedAttribute(attr);
72 }
73
mapMouseEvent(int x,int y,const IntSize & size,HitTestResult & result)74 bool HTMLAreaElement::mapMouseEvent(int x, int y, const IntSize& size, HitTestResult& result)
75 {
76 if (m_lastSize != size) {
77 m_region = adoptPtr(new Path(getRegion(size)));
78 m_lastSize = size;
79 }
80
81 if (!m_region->contains(IntPoint(x, y)))
82 return false;
83
84 result.setInnerNode(this);
85 result.setURLElement(this);
86 return true;
87 }
88
computePath(RenderObject * obj) const89 Path HTMLAreaElement::computePath(RenderObject* obj) const
90 {
91 if (!obj)
92 return Path();
93
94 // FIXME: This doesn't work correctly with transforms.
95 FloatPoint absPos = obj->localToAbsolute();
96
97 // Default should default to the size of the containing object.
98 IntSize size = m_lastSize;
99 if (m_shape == Default)
100 size = obj->absoluteOutlineBounds().size();
101
102 Path p = getRegion(size);
103 float zoomFactor = document()->frame()->pageZoomFactor();
104 if (zoomFactor != 1.0f) {
105 AffineTransform zoomTransform;
106 zoomTransform.scale(zoomFactor);
107 p.transform(zoomTransform);
108 }
109
110 p.translate(absPos - FloatPoint());
111 return p;
112 }
113
computeRect(RenderObject * obj) const114 IntRect HTMLAreaElement::computeRect(RenderObject* obj) const
115 {
116 return enclosingIntRect(computePath(obj).boundingRect());
117 }
118
getRegion(const IntSize & size) const119 Path HTMLAreaElement::getRegion(const IntSize& size) const
120 {
121 if (!m_coords && m_shape != Default)
122 return Path();
123
124 int width = size.width();
125 int height = size.height();
126
127 // If element omits the shape attribute, select shape based on number of coordinates.
128 Shape shape = m_shape;
129 if (shape == Unknown) {
130 if (m_coordsLen == 3)
131 shape = Circle;
132 else if (m_coordsLen == 4)
133 shape = Rect;
134 else if (m_coordsLen >= 6)
135 shape = Poly;
136 }
137
138 Path path;
139 switch (shape) {
140 case Poly:
141 if (m_coordsLen >= 6) {
142 int numPoints = m_coordsLen / 2;
143 path.moveTo(FloatPoint(m_coords[0].calcMinValue(width), m_coords[1].calcMinValue(height)));
144 for (int i = 1; i < numPoints; ++i)
145 path.addLineTo(FloatPoint(m_coords[i * 2].calcMinValue(width), m_coords[i * 2 + 1].calcMinValue(height)));
146 path.closeSubpath();
147 }
148 break;
149 case Circle:
150 if (m_coordsLen >= 3) {
151 Length radius = m_coords[2];
152 int r = min(radius.calcMinValue(width), radius.calcMinValue(height));
153 path.addEllipse(FloatRect(m_coords[0].calcMinValue(width) - r, m_coords[1].calcMinValue(height) - r, 2 * r, 2 * r));
154 }
155 break;
156 case Rect:
157 if (m_coordsLen >= 4) {
158 int x0 = m_coords[0].calcMinValue(width);
159 int y0 = m_coords[1].calcMinValue(height);
160 int x1 = m_coords[2].calcMinValue(width);
161 int y1 = m_coords[3].calcMinValue(height);
162 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0));
163 }
164 break;
165 case Default:
166 path.addRect(FloatRect(0, 0, width, height));
167 break;
168 case Unknown:
169 break;
170 }
171
172 return path;
173 }
174
imageElement() const175 HTMLImageElement* HTMLAreaElement::imageElement() const
176 {
177 Node* mapElement = parentNode();
178 if (!mapElement || !mapElement->hasTagName(mapTag))
179 return 0;
180
181 return static_cast<HTMLMapElement*>(mapElement)->imageElement();
182 }
183
isKeyboardFocusable(KeyboardEvent *) const184 bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const
185 {
186 return isFocusable();
187 }
188
isMouseFocusable() const189 bool HTMLAreaElement::isMouseFocusable() const
190 {
191 return isFocusable();
192 }
193
isFocusable() const194 bool HTMLAreaElement::isFocusable() const
195 {
196 return supportsFocus() && Element::tabIndex() >= 0;
197 }
198
setFocus(bool shouldBeFocused)199 void HTMLAreaElement::setFocus(bool shouldBeFocused)
200 {
201 if (focused() == shouldBeFocused)
202 return;
203
204 HTMLAnchorElement::setFocus(shouldBeFocused);
205
206 HTMLImageElement* imageElement = this->imageElement();
207 if (!imageElement)
208 return;
209
210 RenderObject* renderer = imageElement->renderer();
211 if (!renderer || !renderer->isImage())
212 return;
213
214 toRenderImage(renderer)->areaElementFocusChanged(this);
215 }
216
updateFocusAppearance(bool restorePreviousSelection)217 void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection)
218 {
219 if (!isFocusable())
220 return;
221
222 HTMLImageElement* imageElement = this->imageElement();
223 if (!imageElement)
224 return;
225
226 imageElement->updateFocusAppearance(restorePreviousSelection);
227 }
228
supportsFocus() const229 bool HTMLAreaElement::supportsFocus() const
230 {
231 // If the AREA element was a link, it should support focus.
232 // The inherited method is not used because it assumes that a render object must exist
233 // for the element to support focus. AREA elements do not have render objects.
234 return isLink();
235 }
236
target() const237 String HTMLAreaElement::target() const
238 {
239 return getAttribute(targetAttr);
240 }
241
242 }
243