1 /* 2 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 3 * (C) 1999 Antti Koivisto (koivisto (at) 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 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 50 PassRefPtr<HTMLAreaElement> HTMLAreaElement::create(const QualifiedName& tagName, Document* document) 51 { 52 return adoptRef(new HTMLAreaElement(tagName, document)); 53 } 54 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 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 89 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 114 IntRect HTMLAreaElement::computeRect(RenderObject* obj) const 115 { 116 return enclosingIntRect(computePath(obj).boundingRect()); 117 } 118 119 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 175 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 184 bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const 185 { 186 return isFocusable(); 187 } 188 189 bool HTMLAreaElement::isMouseFocusable() const 190 { 191 return isFocusable(); 192 } 193 194 bool HTMLAreaElement::isFocusable() const 195 { 196 return supportsFocus() && Element::tabIndex() >= 0; 197 } 198 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 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 229 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 237 String HTMLAreaElement::target() const 238 { 239 return getAttribute(targetAttr); 240 } 241 242 } 243