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.
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.
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.
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.
23 #include "HTMLAreaElement.h"
25 #include "AffineTransform.h"
26 #include "Attribute.h"
28 #include "HTMLImageElement.h"
29 #include "HTMLMapElement.h"
30 #include "HTMLNames.h"
31 #include "HitTestResult.h"
33 #include "RenderImage.h"
39 using namespace HTMLNames;
41 inline HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document* document)
42 : HTMLAnchorElement(tagName, document)
47 ASSERT(hasTagName(areaTag));
50 PassRefPtr<HTMLAreaElement> HTMLAreaElement::create(const QualifiedName& tagName, Document* document)
52 return adoptRef(new HTMLAreaElement(tagName, document));
55 void HTMLAreaElement::parseMappedAttribute(Attribute* attr)
57 if (attr->name() == shapeAttr) {
58 if (equalIgnoringCase(attr->value(), "default"))
60 else if (equalIgnoringCase(attr->value(), "circle"))
62 else if (equalIgnoringCase(attr->value(), "poly"))
64 else if (equalIgnoringCase(attr->value(), "rect"))
66 invalidateCachedRegion();
67 } else if (attr->name() == coordsAttr) {
68 m_coords = newCoordsArray(attr->value().string(), m_coordsLen);
69 invalidateCachedRegion();
70 } else if (attr->name() == altAttr || attr->name() == accesskeyAttr) {
73 HTMLAnchorElement::parseMappedAttribute(attr);
76 void HTMLAreaElement::invalidateCachedRegion()
78 m_lastSize = LayoutSize(-1, -1);
81 bool HTMLAreaElement::mapMouseEvent(LayoutPoint location, const LayoutSize& size, HitTestResult& result)
83 if (m_lastSize != size) {
84 m_region = adoptPtr(new Path(getRegion(size)));
88 if (!m_region->contains(location))
91 result.setInnerNode(this);
92 result.setURLElement(this);
96 Path HTMLAreaElement::computePath(RenderObject* obj) const
101 // FIXME: This doesn't work correctly with transforms.
102 FloatPoint absPos = obj->localToAbsolute();
104 // Default should default to the size of the containing object.
105 LayoutSize size = m_lastSize;
106 if (m_shape == Default)
107 size = obj->absoluteOutlineBounds().size();
109 Path p = getRegion(size);
110 float zoomFactor = document()->frame()->pageZoomFactor();
111 if (zoomFactor != 1.0f) {
112 AffineTransform zoomTransform;
113 zoomTransform.scale(zoomFactor);
114 p.transform(zoomTransform);
117 p.translate(absPos - FloatPoint());
121 LayoutRect HTMLAreaElement::computeRect(RenderObject* obj) const
123 return enclosingLayoutRect(computePath(obj).boundingRect());
126 Path HTMLAreaElement::getRegion(const LayoutSize& size) const
128 if (!m_coords && m_shape != Default)
131 LayoutUnit width = size.width();
132 LayoutUnit height = size.height();
134 // If element omits the shape attribute, select shape based on number of coordinates.
135 Shape shape = m_shape;
136 if (shape == Unknown) {
137 if (m_coordsLen == 3)
139 else if (m_coordsLen == 4)
141 else if (m_coordsLen >= 6)
148 if (m_coordsLen >= 6) {
149 int numPoints = m_coordsLen / 2;
150 path.moveTo(FloatPoint(m_coords[0].calcMinValue(width), m_coords[1].calcMinValue(height)));
151 for (int i = 1; i < numPoints; ++i)
152 path.addLineTo(FloatPoint(m_coords[i * 2].calcMinValue(width), m_coords[i * 2 + 1].calcMinValue(height)));
157 if (m_coordsLen >= 3) {
158 Length radius = m_coords[2];
159 int r = min(radius.calcMinValue(width), radius.calcMinValue(height));
160 path.addEllipse(FloatRect(m_coords[0].calcMinValue(width) - r, m_coords[1].calcMinValue(height) - r, 2 * r, 2 * r));
164 if (m_coordsLen >= 4) {
165 int x0 = m_coords[0].calcMinValue(width);
166 int y0 = m_coords[1].calcMinValue(height);
167 int x1 = m_coords[2].calcMinValue(width);
168 int y1 = m_coords[3].calcMinValue(height);
169 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0));
173 path.addRect(FloatRect(0, 0, width, height));
182 HTMLImageElement* HTMLAreaElement::imageElement()
184 Node* mapElement = parentNode();
185 if (!mapElement || !mapElement->hasTagName(mapTag))
188 return static_cast<HTMLMapElement*>(mapElement)->imageElement();
191 bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const
193 return isFocusable();
196 bool HTMLAreaElement::isMouseFocusable() const
198 return isFocusable();
201 bool HTMLAreaElement::isFocusable() const
203 return supportsFocus() && Element::tabIndex() >= 0;
206 void HTMLAreaElement::setFocus(bool shouldBeFocused)
208 if (focused() == shouldBeFocused)
211 HTMLAnchorElement::setFocus(shouldBeFocused);
213 HTMLImageElement* imageElement = this->imageElement();
217 RenderObject* renderer = imageElement->renderer();
218 if (!renderer || !renderer->isImage())
221 toRenderImage(renderer)->areaElementFocusChanged(this);
224 void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection)
229 HTMLImageElement* imageElement = this->imageElement();
233 imageElement->updateFocusAppearance(restorePreviousSelection);
236 bool HTMLAreaElement::supportsFocus() const
238 // If the AREA element was a link, it should support focus.
239 // The inherited method is not used because it assumes that a render object must exist
240 // for the element to support focus. AREA elements do not have render objects.
244 String HTMLAreaElement::target() const
246 return getAttribute(targetAttr);