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 blink { 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 } 45 46 DEFINE_NODE_FACTORY(HTMLAreaElement) 47 48 void HTMLAreaElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 49 { 50 if (name == shapeAttr) { 51 if (equalIgnoringCase(value, "default")) 52 m_shape = Default; 53 else if (equalIgnoringCase(value, "circle")) 54 m_shape = Circle; 55 else if (equalIgnoringCase(value, "poly")) 56 m_shape = Poly; 57 else if (equalIgnoringCase(value, "rect")) 58 m_shape = Rect; 59 invalidateCachedRegion(); 60 } else if (name == coordsAttr) { 61 m_coords = parseHTMLAreaElementCoords(value.string()); 62 invalidateCachedRegion(); 63 } else if (name == altAttr || name == accesskeyAttr) { 64 // Do nothing. 65 } else 66 HTMLAnchorElement::parseAttribute(name, value); 67 } 68 69 void HTMLAreaElement::invalidateCachedRegion() 70 { 71 m_lastSize = LayoutSize(-1, -1); 72 } 73 74 bool HTMLAreaElement::mapMouseEvent(LayoutPoint location, const LayoutSize& 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(location)) 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 LayoutSize size = m_lastSize; 99 if (m_shape == Default) 100 size = obj->absoluteClippedOverflowRect().size(); 101 102 Path p = getRegion(size); 103 float zoomFactor = obj->style()->effectiveZoom(); 104 if (zoomFactor != 1.0f) { 105 AffineTransform zoomTransform; 106 zoomTransform.scale(zoomFactor); 107 p.transform(zoomTransform); 108 } 109 110 p.translate(toFloatSize(absPos)); 111 return p; 112 } 113 114 LayoutRect HTMLAreaElement::computeRect(RenderObject* obj) const 115 { 116 return enclosingLayoutRect(computePath(obj).boundingRect()); 117 } 118 119 Path HTMLAreaElement::getRegion(const LayoutSize& size) const 120 { 121 if (m_coords.isEmpty() && m_shape != Default) 122 return Path(); 123 124 LayoutUnit width = size.width(); 125 LayoutUnit 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_coords.size() == 3) 131 shape = Circle; 132 else if (m_coords.size() == 4) 133 shape = Rect; 134 else if (m_coords.size() >= 6) 135 shape = Poly; 136 } 137 138 Path path; 139 switch (shape) { 140 case Poly: 141 if (m_coords.size() >= 6) { 142 int numPoints = m_coords.size() / 2; 143 path.moveTo(FloatPoint(minimumValueForLength(m_coords[0], width).toFloat(), minimumValueForLength(m_coords[1], height).toFloat())); 144 for (int i = 1; i < numPoints; ++i) 145 path.addLineTo(FloatPoint(minimumValueForLength(m_coords[i * 2], width).toFloat(), minimumValueForLength(m_coords[i * 2 + 1], height).toFloat())); 146 path.closeSubpath(); 147 } 148 break; 149 case Circle: 150 if (m_coords.size() >= 3) { 151 Length radius = m_coords[2]; 152 float r = std::min(minimumValueForLength(radius, width).toFloat(), minimumValueForLength(radius, height).toFloat()); 153 path.addEllipse(FloatRect(minimumValueForLength(m_coords[0], width).toFloat() - r, minimumValueForLength(m_coords[1], height).toFloat() - r, 2 * r, 2 * r)); 154 } 155 break; 156 case Rect: 157 if (m_coords.size() >= 4) { 158 float x0 = minimumValueForLength(m_coords[0], width).toFloat(); 159 float y0 = minimumValueForLength(m_coords[1], height).toFloat(); 160 float x1 = minimumValueForLength(m_coords[2], width).toFloat(); 161 float y1 = minimumValueForLength(m_coords[3], height).toFloat(); 162 path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0)); 163 } 164 break; 165 case Default: 166 path.addRect(FloatRect(0, 0, width.toFloat(), height.toFloat())); 167 break; 168 case Unknown: 169 break; 170 } 171 172 return path; 173 } 174 175 HTMLImageElement* HTMLAreaElement::imageElement() const 176 { 177 if (HTMLMapElement* mapElement = Traversal<HTMLMapElement>::firstAncestor(*this)) 178 return mapElement->imageElement(); 179 return 0; 180 } 181 182 bool HTMLAreaElement::isKeyboardFocusable() const 183 { 184 return isFocusable(); 185 } 186 187 bool HTMLAreaElement::isMouseFocusable() const 188 { 189 return isFocusable(); 190 } 191 192 bool HTMLAreaElement::rendererIsFocusable() const 193 { 194 HTMLImageElement* image = imageElement(); 195 if (!image || !image->renderer() || image->renderer()->style()->visibility() != VISIBLE) 196 return false; 197 198 return supportsFocus() && Element::tabIndex() >= 0; 199 } 200 201 void HTMLAreaElement::setFocus(bool shouldBeFocused) 202 { 203 if (focused() == shouldBeFocused) 204 return; 205 206 HTMLAnchorElement::setFocus(shouldBeFocused); 207 208 HTMLImageElement* imageElement = this->imageElement(); 209 if (!imageElement) 210 return; 211 212 RenderObject* renderer = imageElement->renderer(); 213 if (!renderer || !renderer->isImage()) 214 return; 215 216 toRenderImage(renderer)->areaElementFocusChanged(this); 217 } 218 219 void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection) 220 { 221 if (!isFocusable()) 222 return; 223 224 HTMLImageElement* imageElement = this->imageElement(); 225 if (!imageElement) 226 return; 227 228 imageElement->updateFocusAppearance(restorePreviousSelection); 229 } 230 231 } 232