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#include "RenderView.h" 35 36using namespace std; 37 38namespace WebCore { 39 40using namespace HTMLNames; 41 42inline HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document* document) 43 : HTMLAnchorElement(tagName, document) 44 , m_coordsLen(0) 45 , m_lastSize(-1, -1) 46 , m_shape(Unknown) 47{ 48 ASSERT(hasTagName(areaTag)); 49} 50 51PassRefPtr<HTMLAreaElement> HTMLAreaElement::create(const QualifiedName& tagName, Document* document) 52{ 53 return adoptRef(new HTMLAreaElement(tagName, document)); 54} 55 56void HTMLAreaElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 57{ 58 if (name == shapeAttr) { 59 if (equalIgnoringCase(value, "default")) 60 m_shape = Default; 61 else if (equalIgnoringCase(value, "circle")) 62 m_shape = Circle; 63 else if (equalIgnoringCase(value, "poly")) 64 m_shape = Poly; 65 else if (equalIgnoringCase(value, "rect")) 66 m_shape = Rect; 67 invalidateCachedRegion(); 68 } else if (name == coordsAttr) { 69 m_coords = newCoordsArray(value.string(), m_coordsLen); 70 invalidateCachedRegion(); 71 } else if (name == altAttr || name == accesskeyAttr) { 72 // Do nothing. 73 } else 74 HTMLAnchorElement::parseAttribute(name, value); 75} 76 77void HTMLAreaElement::invalidateCachedRegion() 78{ 79 m_lastSize = LayoutSize(-1, -1); 80} 81 82bool HTMLAreaElement::mapMouseEvent(LayoutPoint location, const LayoutSize& size, HitTestResult& result) 83{ 84 if (m_lastSize != size) { 85 m_region = adoptPtr(new Path(getRegion(size))); 86 m_lastSize = size; 87 } 88 89 if (!m_region->contains(location)) 90 return false; 91 92 result.setInnerNode(this); 93 result.setURLElement(this); 94 return true; 95} 96 97Path HTMLAreaElement::computePath(RenderObject* obj) const 98{ 99 if (!obj) 100 return Path(); 101 102 // FIXME: This doesn't work correctly with transforms. 103 FloatPoint absPos = obj->localToAbsolute(); 104 105 // Default should default to the size of the containing object. 106 LayoutSize size = m_lastSize; 107 if (m_shape == Default) 108 size = obj->absoluteOutlineBounds().size(); 109 110 Path p = getRegion(size); 111 float zoomFactor = obj->style()->effectiveZoom(); 112 if (zoomFactor != 1.0f) { 113 AffineTransform zoomTransform; 114 zoomTransform.scale(zoomFactor); 115 p.transform(zoomTransform); 116 } 117 118 p.translate(toFloatSize(absPos)); 119 return p; 120} 121 122LayoutRect HTMLAreaElement::computeRect(RenderObject* obj) const 123{ 124 return enclosingLayoutRect(computePath(obj).fastBoundingRect()); 125} 126 127Path HTMLAreaElement::getRegion(const LayoutSize& size) const 128{ 129 if (!m_coords && m_shape != Default) 130 return Path(); 131 132 LayoutUnit width = size.width(); 133 LayoutUnit height = size.height(); 134 135 // If element omits the shape attribute, select shape based on number of coordinates. 136 Shape shape = m_shape; 137 if (shape == Unknown) { 138 if (m_coordsLen == 3) 139 shape = Circle; 140 else if (m_coordsLen == 4) 141 shape = Rect; 142 else if (m_coordsLen >= 6) 143 shape = Poly; 144 } 145 146 Path path; 147 RenderView* renderView = document()->renderView(); 148 switch (shape) { 149 case Poly: 150 if (m_coordsLen >= 6) { 151 int numPoints = m_coordsLen / 2; 152 path.moveTo(FloatPoint(minimumValueForLength(m_coords[0], width, renderView), minimumValueForLength(m_coords[1], height, renderView))); 153 for (int i = 1; i < numPoints; ++i) 154 path.addLineTo(FloatPoint(minimumValueForLength(m_coords[i * 2], width, renderView), minimumValueForLength(m_coords[i * 2 + 1], height, renderView))); 155 path.closeSubpath(); 156 } 157 break; 158 case Circle: 159 if (m_coordsLen >= 3) { 160 Length radius = m_coords[2]; 161 int r = min(minimumValueForLength(radius, width, renderView), minimumValueForLength(radius, height, renderView)); 162 path.addEllipse(FloatRect(minimumValueForLength(m_coords[0], width, renderView) - r, minimumValueForLength(m_coords[1], height, renderView) - r, 2 * r, 2 * r)); 163 } 164 break; 165 case Rect: 166 if (m_coordsLen >= 4) { 167 int x0 = minimumValueForLength(m_coords[0], width, renderView); 168 int y0 = minimumValueForLength(m_coords[1], height, renderView); 169 int x1 = minimumValueForLength(m_coords[2], width, renderView); 170 int y1 = minimumValueForLength(m_coords[3], height, renderView); 171 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0)); 172 } 173 break; 174 case Default: 175 path.addRect(FloatRect(0, 0, width, height)); 176 break; 177 case Unknown: 178 break; 179 } 180 181 return path; 182} 183 184HTMLImageElement* HTMLAreaElement::imageElement() const 185{ 186 Node* mapElement = parentNode(); 187 if (!mapElement || !mapElement->hasTagName(mapTag)) 188 return 0; 189 190 return static_cast<HTMLMapElement*>(mapElement)->imageElement(); 191} 192 193bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const 194{ 195 return isFocusable(); 196} 197 198bool HTMLAreaElement::isMouseFocusable() const 199{ 200 return isFocusable(); 201} 202 203bool HTMLAreaElement::isFocusable() const 204{ 205 HTMLImageElement* image = imageElement(); 206 if (!image || !image->renderer() || image->renderer()->style()->visibility() != VISIBLE) 207 return false; 208 209 return supportsFocus() && Element::tabIndex() >= 0; 210} 211 212void HTMLAreaElement::setFocus(bool shouldBeFocused) 213{ 214 if (focused() == shouldBeFocused) 215 return; 216 217 HTMLAnchorElement::setFocus(shouldBeFocused); 218 219 HTMLImageElement* imageElement = this->imageElement(); 220 if (!imageElement) 221 return; 222 223 RenderObject* renderer = imageElement->renderer(); 224 if (!renderer || !renderer->isRenderImage()) 225 return; 226 227 toRenderImage(renderer)->areaElementFocusChanged(this); 228} 229 230void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection) 231{ 232 if (!isFocusable()) 233 return; 234 235 HTMLImageElement* imageElement = this->imageElement(); 236 if (!imageElement) 237 return; 238 239 imageElement->updateFocusAppearance(restorePreviousSelection); 240} 241 242bool HTMLAreaElement::supportsFocus() const 243{ 244 // If the AREA element was a link, it should support focus. 245 // The inherited method is not used because it assumes that a render object must exist 246 // for the element to support focus. AREA elements do not have render objects. 247 return isLink(); 248} 249 250String HTMLAreaElement::target() const 251{ 252 return getAttribute(targetAttr); 253} 254 255#if ENABLE(MICRODATA) 256String HTMLAreaElement::itemValueText() const 257{ 258 return getURLAttribute(hrefAttr); 259} 260 261void HTMLAreaElement::setItemValueText(const String& value, ExceptionCode&) 262{ 263 setAttribute(hrefAttr, value); 264} 265#endif 266 267} 268