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, 2007, 2008, 2010 Apple Inc. All rights reserved.
      5  * Copyright (C) 2010 Google Inc. All rights reserved.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  */
     22 
     23 #include "config.h"
     24 #include "HTMLImageElement.h"
     25 
     26 #include "Attribute.h"
     27 #include "CSSPropertyNames.h"
     28 #include "CSSValueKeywords.h"
     29 #include "EventNames.h"
     30 #include "FrameView.h"
     31 #include "HTMLDocument.h"
     32 #include "HTMLFormElement.h"
     33 #include "HTMLNames.h"
     34 #include "HTMLParserIdioms.h"
     35 #include "RenderImage.h"
     36 #include "ScriptEventListener.h"
     37 
     38 using namespace std;
     39 
     40 namespace WebCore {
     41 
     42 using namespace HTMLNames;
     43 
     44 HTMLImageElement::HTMLImageElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     45     : HTMLElement(tagName, document)
     46     , m_imageLoader(this)
     47     , ismap(false)
     48     , m_form(form)
     49     , m_compositeOperator(CompositeSourceOver)
     50 {
     51     ASSERT(hasTagName(imgTag));
     52     if (form)
     53         form->registerImgElement(this);
     54 }
     55 
     56 PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document* document)
     57 {
     58     return adoptRef(new HTMLImageElement(imgTag, document));
     59 }
     60 
     61 PassRefPtr<HTMLImageElement> HTMLImageElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     62 {
     63     return adoptRef(new HTMLImageElement(tagName, document, form));
     64 }
     65 
     66 HTMLImageElement::~HTMLImageElement()
     67 {
     68     if (m_form)
     69         m_form->removeImgElement(this);
     70 }
     71 
     72 PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document* document, const int* optionalWidth, const int* optionalHeight)
     73 {
     74     RefPtr<HTMLImageElement> image = adoptRef(new HTMLImageElement(imgTag, document));
     75     if (optionalWidth)
     76         image->setWidth(*optionalWidth);
     77     if (optionalHeight > 0)
     78         image->setHeight(*optionalHeight);
     79     return image.release();
     80 }
     81 
     82 bool HTMLImageElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
     83 {
     84     if (attrName == widthAttr ||
     85         attrName == heightAttr ||
     86         attrName == vspaceAttr ||
     87         attrName == hspaceAttr ||
     88         attrName == valignAttr) {
     89         result = eUniversal;
     90         return false;
     91     }
     92 
     93     if (attrName == borderAttr || attrName == alignAttr) {
     94         result = eReplaced; // Shared with embed and iframe elements.
     95         return false;
     96     }
     97 
     98     return HTMLElement::mapToEntry(attrName, result);
     99 }
    100 
    101 void HTMLImageElement::parseMappedAttribute(Attribute* attr)
    102 {
    103     const QualifiedName& attrName = attr->name();
    104     if (attrName == altAttr) {
    105         if (renderer() && renderer()->isImage())
    106             toRenderImage(renderer())->updateAltText();
    107     } else if (attrName == srcAttr)
    108         m_imageLoader.updateFromElementIgnoringPreviousError();
    109     else if (attrName == widthAttr)
    110         addCSSLength(attr, CSSPropertyWidth, attr->value());
    111     else if (attrName == heightAttr)
    112         addCSSLength(attr, CSSPropertyHeight, attr->value());
    113     else if (attrName == borderAttr) {
    114         // border="noborder" -> border="0"
    115         addCSSLength(attr, CSSPropertyBorderWidth, attr->value().toInt() ? attr->value() : "0");
    116         addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid);
    117         addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid);
    118         addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid);
    119         addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid);
    120     } else if (attrName == vspaceAttr) {
    121         addCSSLength(attr, CSSPropertyMarginTop, attr->value());
    122         addCSSLength(attr, CSSPropertyMarginBottom, attr->value());
    123     } else if (attrName == hspaceAttr) {
    124         addCSSLength(attr, CSSPropertyMarginLeft, attr->value());
    125         addCSSLength(attr, CSSPropertyMarginRight, attr->value());
    126     } else if (attrName == alignAttr)
    127         addHTMLAlignment(attr);
    128     else if (attrName == valignAttr)
    129         addCSSProperty(attr, CSSPropertyVerticalAlign, attr->value());
    130     else if (attrName == usemapAttr) {
    131         if (attr->value().string()[0] == '#')
    132             usemap = attr->value();
    133         else
    134             usemap = document()->completeURL(stripLeadingAndTrailingHTMLSpaces(attr->value())).string();
    135         setIsLink(!attr->isNull());
    136     } else if (attrName == ismapAttr)
    137         ismap = true;
    138     else if (attrName == onabortAttr)
    139         setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr));
    140     else if (attrName == onloadAttr)
    141         setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr));
    142     else if (attrName == onbeforeloadAttr)
    143         setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr));
    144     else if (attrName == compositeAttr) {
    145         if (!parseCompositeOperator(attr->value(), m_compositeOperator))
    146             m_compositeOperator = CompositeSourceOver;
    147     } else if (attrName == nameAttr) {
    148         const AtomicString& newName = attr->value();
    149         if (inDocument() && document()->isHTMLDocument()) {
    150             HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
    151             document->removeNamedItem(m_name);
    152             document->addNamedItem(newName);
    153         }
    154         m_name = newName;
    155     } else if (isIdAttributeName(attr->name())) {
    156         const AtomicString& newId = attr->value();
    157         if (inDocument() && document()->isHTMLDocument()) {
    158             HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
    159             document->removeExtraNamedItem(m_id);
    160             document->addExtraNamedItem(newId);
    161         }
    162         m_id = newId;
    163         // also call superclass
    164         HTMLElement::parseMappedAttribute(attr);
    165     } else
    166         HTMLElement::parseMappedAttribute(attr);
    167 }
    168 
    169 String HTMLImageElement::altText() const
    170 {
    171     // lets figure out the alt text.. magic stuff
    172     // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
    173     // also heavily discussed by Hixie on bugzilla
    174     String alt = getAttribute(altAttr);
    175     // fall back to title attribute
    176     if (alt.isNull())
    177         alt = getAttribute(titleAttr);
    178     return alt;
    179 }
    180 
    181 RenderObject* HTMLImageElement::createRenderer(RenderArena* arena, RenderStyle* style)
    182 {
    183     if (style->contentData())
    184         return RenderObject::createObject(this, style);
    185 
    186     RenderImage* image = new (arena) RenderImage(this);
    187     image->setImageResource(RenderImageResource::create());
    188     return image;
    189 }
    190 
    191 void HTMLImageElement::attach()
    192 {
    193     HTMLElement::attach();
    194 
    195     if (renderer() && renderer()->isImage() && m_imageLoader.haveFiredBeforeLoadEvent()) {
    196         RenderImage* renderImage = toRenderImage(renderer());
    197         RenderImageResource* renderImageResource = renderImage->imageResource();
    198         if (renderImageResource->hasImage())
    199             return;
    200         renderImageResource->setCachedImage(m_imageLoader.image());
    201 
    202         // If we have no image at all because we have no src attribute, set
    203         // image height and width for the alt text instead.
    204         if (!m_imageLoader.image() && !renderImageResource->cachedImage())
    205             renderImage->setImageSizeForAltText();
    206     }
    207 }
    208 
    209 void HTMLImageElement::insertedIntoDocument()
    210 {
    211     if (document()->isHTMLDocument()) {
    212         HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
    213         document->addNamedItem(m_name);
    214         document->addExtraNamedItem(m_id);
    215     }
    216 
    217     // If we have been inserted from a renderer-less document,
    218     // our loader may have not fetched the image, so do it now.
    219     if (!m_imageLoader.image())
    220         m_imageLoader.updateFromElement();
    221 
    222     HTMLElement::insertedIntoDocument();
    223 }
    224 
    225 void HTMLImageElement::removedFromDocument()
    226 {
    227     if (document()->isHTMLDocument()) {
    228         HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
    229         document->removeNamedItem(m_name);
    230         document->removeExtraNamedItem(m_id);
    231     }
    232 
    233     HTMLElement::removedFromDocument();
    234 }
    235 
    236 void HTMLImageElement::insertedIntoTree(bool deep)
    237 {
    238     if (!m_form) {
    239         // m_form can be non-null if it was set in constructor.
    240         for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) {
    241             if (ancestor->hasTagName(formTag)) {
    242                 m_form = static_cast<HTMLFormElement*>(ancestor);
    243                 m_form->registerImgElement(this);
    244                 break;
    245             }
    246         }
    247     }
    248 
    249     HTMLElement::insertedIntoTree(deep);
    250 }
    251 
    252 void HTMLImageElement::removedFromTree(bool deep)
    253 {
    254     if (m_form)
    255         m_form->removeImgElement(this);
    256     m_form = 0;
    257     HTMLElement::removedFromTree(deep);
    258 }
    259 
    260 int HTMLImageElement::width(bool ignorePendingStylesheets) const
    261 {
    262     if (!renderer()) {
    263         // check the attribute first for an explicit pixel value
    264         bool ok;
    265         int width = getAttribute(widthAttr).toInt(&ok);
    266         if (ok)
    267             return width;
    268 
    269         // if the image is available, use its width
    270         if (m_imageLoader.image())
    271             return m_imageLoader.image()->imageSize(1.0f).width();
    272     }
    273 
    274     if (ignorePendingStylesheets)
    275         document()->updateLayoutIgnorePendingStylesheets();
    276     else
    277         document()->updateLayout();
    278 
    279     RenderBox* box = renderBox();
    280     return box ? adjustForAbsoluteZoom(box->contentWidth(), box) : 0;
    281 }
    282 
    283 int HTMLImageElement::height(bool ignorePendingStylesheets) const
    284 {
    285     if (!renderer()) {
    286         // check the attribute first for an explicit pixel value
    287         bool ok;
    288         int height = getAttribute(heightAttr).toInt(&ok);
    289         if (ok)
    290             return height;
    291 
    292         // if the image is available, use its height
    293         if (m_imageLoader.image())
    294             return m_imageLoader.image()->imageSize(1.0f).height();
    295     }
    296 
    297     if (ignorePendingStylesheets)
    298         document()->updateLayoutIgnorePendingStylesheets();
    299     else
    300         document()->updateLayout();
    301 
    302     RenderBox* box = renderBox();
    303     return box ? adjustForAbsoluteZoom(box->contentHeight(), box) : 0;
    304 }
    305 
    306 int HTMLImageElement::naturalWidth() const
    307 {
    308     if (!m_imageLoader.image())
    309         return 0;
    310 
    311     return m_imageLoader.image()->imageSize(1.0f).width();
    312 }
    313 
    314 int HTMLImageElement::naturalHeight() const
    315 {
    316     if (!m_imageLoader.image())
    317         return 0;
    318 
    319     return m_imageLoader.image()->imageSize(1.0f).height();
    320 }
    321 
    322 bool HTMLImageElement::isURLAttribute(Attribute* attr) const
    323 {
    324     return attr->name() == srcAttr
    325         || attr->name() == lowsrcAttr
    326         || attr->name() == longdescAttr
    327         || (attr->name() == usemapAttr && attr->value().string()[0] != '#');
    328 }
    329 
    330 const AtomicString& HTMLImageElement::alt() const
    331 {
    332     return getAttribute(altAttr);
    333 }
    334 
    335 bool HTMLImageElement::draggable() const
    336 {
    337     // Image elements are draggable by default.
    338     return !equalIgnoringCase(getAttribute(draggableAttr), "false");
    339 }
    340 
    341 void HTMLImageElement::setHeight(int value)
    342 {
    343     setAttribute(heightAttr, String::number(value));
    344 }
    345 
    346 KURL HTMLImageElement::src() const
    347 {
    348     return document()->completeURL(getAttribute(srcAttr));
    349 }
    350 
    351 void HTMLImageElement::setSrc(const String& value)
    352 {
    353     setAttribute(srcAttr, value);
    354 }
    355 
    356 void HTMLImageElement::setWidth(int value)
    357 {
    358     setAttribute(widthAttr, String::number(value));
    359 }
    360 
    361 int HTMLImageElement::x() const
    362 {
    363     RenderObject* r = renderer();
    364     if (!r)
    365         return 0;
    366 
    367     // FIXME: This doesn't work correctly with transforms.
    368     FloatPoint absPos = r->localToAbsolute();
    369     return absPos.x();
    370 }
    371 
    372 int HTMLImageElement::y() const
    373 {
    374     RenderObject* r = renderer();
    375     if (!r)
    376         return 0;
    377 
    378     // FIXME: This doesn't work correctly with transforms.
    379     FloatPoint absPos = r->localToAbsolute();
    380     return absPos.y();
    381 }
    382 
    383 bool HTMLImageElement::complete() const
    384 {
    385     return m_imageLoader.imageComplete();
    386 }
    387 
    388 void HTMLImageElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    389 {
    390     HTMLElement::addSubresourceAttributeURLs(urls);
    391 
    392     addSubresourceURL(urls, src());
    393     // FIXME: What about when the usemap attribute begins with "#"?
    394     addSubresourceURL(urls, document()->completeURL(getAttribute(usemapAttr)));
    395 }
    396 
    397 void HTMLImageElement::willMoveToNewOwnerDocument()
    398 {
    399     m_imageLoader.elementWillMoveToNewOwnerDocument();
    400     HTMLElement::willMoveToNewOwnerDocument();
    401 }
    402 
    403 }
    404