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 "core/html/HTMLAreaElement.h" 24 25 #include "core/HTMLNames.h" 26 #include "core/html/HTMLImageElement.h" 27 #include "core/html/HTMLMapElement.h" 28 #include "core/rendering/HitTestResult.h" 29 #include "core/rendering/RenderImage.h" 30 #include "core/rendering/RenderView.h" 31 #include "platform/LengthFunctions.h" 32 #include "platform/graphics/Path.h" 33 #include "platform/transforms/AffineTransform.h" 34 35 namespace WebCore { 36 37 using namespace HTMLNames; 38 39 inline HTMLAreaElement::HTMLAreaElement(Document& document) 40 : HTMLAnchorElement(areaTag, document) 41 , m_lastSize(-1, -1) 42 , m_shape(Unknown) 43 { 44 ScriptWrappable::init(this); 45 } 46 47 DEFINE_NODE_FACTORY(HTMLAreaElement) 48 49 void HTMLAreaElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 50 { 51 if (name == shapeAttr) { 52 if (equalIgnoringCase(value, "default")) 53 m_shape = Default; 54 else if (equalIgnoringCase(value, "circle")) 55 m_shape = Circle; 56 else if (equalIgnoringCase(value, "poly")) 57 m_shape = Poly; 58 else if (equalIgnoringCase(value, "rect")) 59 m_shape = Rect; 60 invalidateCachedRegion(); 61 } else if (name == coordsAttr) { 62 m_coords = parseHTMLAreaElementCoords(value.string()); 63 invalidateCachedRegion(); 64 } else if (name == altAttr || name == accesskeyAttr) { 65 // Do nothing. 66 } else 67 HTMLAnchorElement::parseAttribute(name, value); 68 } 69 70 void HTMLAreaElement::invalidateCachedRegion() 71 { 72 m_lastSize = LayoutSize(-1, -1); 73 } 74 75 bool HTMLAreaElement::mapMouseEvent(LayoutPoint location, const LayoutSize& size, HitTestResult& result) 76 { 77 if (m_lastSize != size) { 78 m_region = adoptPtr(new Path(getRegion(size))); 79 m_lastSize = size; 80 } 81 82 if (!m_region->contains(location)) 83 return false; 84 85 result.setInnerNode(this); 86 result.setURLElement(this); 87 return true; 88 } 89 90 Path HTMLAreaElement::computePath(RenderObject* obj) const 91 { 92 if (!obj) 93 return Path(); 94 95 // FIXME: This doesn't work correctly with transforms. 96 FloatPoint absPos = obj->localToAbsolute(); 97 98 // Default should default to the size of the containing object. 99 LayoutSize size = m_lastSize; 100 if (m_shape == Default) 101 size = obj->absoluteClippedOverflowRect().size(); 102 103 Path p = getRegion(size); 104 float zoomFactor = obj->style()->effectiveZoom(); 105 if (zoomFactor != 1.0f) { 106 AffineTransform zoomTransform; 107 zoomTransform.scale(zoomFactor); 108 p.transform(zoomTransform); 109 } 110 111 p.translate(toFloatSize(absPos)); 112 return p; 113 } 114 115 LayoutRect HTMLAreaElement::computeRect(RenderObject* obj) const 116 { 117 return enclosingLayoutRect(computePath(obj).boundingRect()); 118 } 119 120 Path HTMLAreaElement::getRegion(const LayoutSize& size) const 121 { 122 if (m_coords.isEmpty() && m_shape != Default) 123 return Path(); 124 125 LayoutUnit width = size.width(); 126 LayoutUnit height = size.height(); 127 128 // If element omits the shape attribute, select shape based on number of coordinates. 129 Shape shape = m_shape; 130 if (shape == Unknown) { 131 if (m_coords.size() == 3) 132 shape = Circle; 133 else if (m_coords.size() == 4) 134 shape = Rect; 135 else if (m_coords.size() >= 6) 136 shape = Poly; 137 } 138 139 Path path; 140 switch (shape) { 141 case Poly: 142 if (m_coords.size() >= 6) { 143 int numPoints = m_coords.size() / 2; 144 path.moveTo(FloatPoint(minimumValueForLength(m_coords[0], width).toFloat(), minimumValueForLength(m_coords[1], height).toFloat())); 145 for (int i = 1; i < numPoints; ++i) 146 path.addLineTo(FloatPoint(minimumValueForLength(m_coords[i * 2], width).toFloat(), minimumValueForLength(m_coords[i * 2 + 1], height).toFloat())); 147 path.closeSubpath(); 148 } 149 break; 150 case Circle: 151 if (m_coords.size() >= 3) { 152 Length radius = m_coords[2]; 153 float r = std::min(minimumValueForLength(radius, width).toFloat(), minimumValueForLength(radius, height).toFloat()); 154 path.addEllipse(FloatRect(minimumValueForLength(m_coords[0], width).toFloat() - r, minimumValueForLength(m_coords[1], height).toFloat() - r, 2 * r, 2 * r)); 155 } 156 break; 157 case Rect: 158 if (m_coords.size() >= 4) { 159 float x0 = minimumValueForLength(m_coords[0], width).toFloat(); 160 float y0 = minimumValueForLength(m_coords[1], height).toFloat(); 161 float x1 = minimumValueForLength(m_coords[2], width).toFloat(); 162 float y1 = minimumValueForLength(m_coords[3], height).toFloat(); 163 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0)); 164 } 165 break; 166 case Default: 167 path.addRect(FloatRect(0, 0, width.toFloat(), height.toFloat())); 168 break; 169 case Unknown: 170 break; 171 } 172 173 return path; 174 } 175 176 HTMLImageElement* HTMLAreaElement::imageElement() const 177 { 178 Element* mapElement = parentElement(); 179 while (mapElement && !isHTMLMapElement(*mapElement)) 180 mapElement = mapElement->parentElement(); 181 182 if (!mapElement) 183 return 0; 184 185 return toHTMLMapElement(*mapElement).imageElement(); 186 } 187 188 bool HTMLAreaElement::isKeyboardFocusable() const 189 { 190 return isFocusable(); 191 } 192 193 bool HTMLAreaElement::isMouseFocusable() const 194 { 195 return isFocusable(); 196 } 197 198 bool HTMLAreaElement::rendererIsFocusable() const 199 { 200 HTMLImageElement* image = imageElement(); 201 if (!image || !image->renderer() || image->renderer()->style()->visibility() != VISIBLE) 202 return false; 203 204 return supportsFocus() && Element::tabIndex() >= 0; 205 } 206 207 void HTMLAreaElement::setFocus(bool shouldBeFocused) 208 { 209 if (focused() == shouldBeFocused) 210 return; 211 212 HTMLAnchorElement::setFocus(shouldBeFocused); 213 214 HTMLImageElement* imageElement = this->imageElement(); 215 if (!imageElement) 216 return; 217 218 RenderObject* renderer = imageElement->renderer(); 219 if (!renderer || !renderer->isImage()) 220 return; 221 222 toRenderImage(renderer)->areaElementFocusChanged(this); 223 } 224 225 void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection) 226 { 227 if (!isFocusable()) 228 return; 229 230 HTMLImageElement* imageElement = this->imageElement(); 231 if (!imageElement) 232 return; 233 234 imageElement->updateFocusAppearance(restorePreviousSelection); 235 } 236 237 } 238