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