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 "core/html/HTMLImageElement.h"
     25 
     26 #include "CSSPropertyNames.h"
     27 #include "HTMLNames.h"
     28 #include "bindings/v8/ScriptEventListener.h"
     29 #include "core/dom/Attribute.h"
     30 #include "core/dom/EventNames.h"
     31 #include "core/html/HTMLFormElement.h"
     32 #include "core/html/parser/HTMLParserIdioms.h"
     33 #include "core/loader/cache/ImageResource.h"
     34 #include "core/rendering/RenderImage.h"
     35 
     36 using namespace std;
     37 
     38 namespace WebCore {
     39 
     40 using namespace HTMLNames;
     41 
     42 HTMLImageElement::HTMLImageElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     43     : HTMLElement(tagName, document)
     44     , m_imageLoader(this)
     45     , m_form(form)
     46     , m_compositeOperator(CompositeSourceOver)
     47 {
     48     ASSERT(hasTagName(imgTag));
     49     ScriptWrappable::init(this);
     50     if (form)
     51         form->registerImgElement(this);
     52 }
     53 
     54 PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document* document)
     55 {
     56     return adoptRef(new HTMLImageElement(imgTag, document));
     57 }
     58 
     59 PassRefPtr<HTMLImageElement> HTMLImageElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form)
     60 {
     61     return adoptRef(new HTMLImageElement(tagName, document, form));
     62 }
     63 
     64 HTMLImageElement::~HTMLImageElement()
     65 {
     66     if (m_form)
     67         m_form->removeImgElement(this);
     68 }
     69 
     70 PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document* document, const int* optionalWidth, const int* optionalHeight)
     71 {
     72     RefPtr<HTMLImageElement> image = adoptRef(new HTMLImageElement(imgTag, document));
     73     if (optionalWidth)
     74         image->setWidth(*optionalWidth);
     75     if (optionalHeight)
     76         image->setHeight(*optionalHeight);
     77     return image.release();
     78 }
     79 
     80 bool HTMLImageElement::isPresentationAttribute(const QualifiedName& name) const
     81 {
     82     if (name == widthAttr || name == heightAttr || name == borderAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == valignAttr)
     83         return true;
     84     return HTMLElement::isPresentationAttribute(name);
     85 }
     86 
     87 void HTMLImageElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
     88 {
     89     if (name == widthAttr)
     90         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
     91     else if (name == heightAttr)
     92         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
     93     else if (name == borderAttr)
     94         applyBorderAttributeToStyle(value, style);
     95     else if (name == vspaceAttr) {
     96         addHTMLLengthToStyle(style, CSSPropertyMarginTop, value);
     97         addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value);
     98     } else if (name == hspaceAttr) {
     99         addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value);
    100         addHTMLLengthToStyle(style, CSSPropertyMarginRight, value);
    101     } else if (name == alignAttr)
    102         applyAlignmentAttributeToStyle(value, style);
    103     else if (name == valignAttr)
    104         addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, value);
    105     else
    106         HTMLElement::collectStyleForPresentationAttribute(name, value, style);
    107 }
    108 
    109 void HTMLImageElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
    110 {
    111     if (name == altAttr) {
    112         if (renderer() && renderer()->isImage())
    113             toRenderImage(renderer())->updateAltText();
    114     } else if (name == srcAttr)
    115         m_imageLoader.updateFromElementIgnoringPreviousError();
    116     else if (name == usemapAttr)
    117         setIsLink(!value.isNull());
    118     else if (name == onbeforeloadAttr)
    119         setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, name, value));
    120     else if (name == compositeAttr) {
    121         // FIXME: images don't support blend modes in their compositing attribute.
    122         BlendMode blendOp = BlendModeNormal;
    123         if (!parseCompositeAndBlendOperator(value, m_compositeOperator, blendOp))
    124             m_compositeOperator = CompositeSourceOver;
    125     } else
    126         HTMLElement::parseAttribute(name, value);
    127 }
    128 
    129 String HTMLImageElement::altText() const
    130 {
    131     // lets figure out the alt text.. magic stuff
    132     // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
    133     // also heavily discussed by Hixie on bugzilla
    134     String alt = getAttribute(altAttr);
    135     // fall back to title attribute
    136     if (alt.isNull())
    137         alt = getAttribute(titleAttr);
    138     return alt;
    139 }
    140 
    141 RenderObject* HTMLImageElement::createRenderer(RenderStyle* style)
    142 {
    143     if (style->hasContent())
    144         return RenderObject::createObject(this, style);
    145 
    146     RenderImage* image = new RenderImage(this);
    147     image->setImageResource(RenderImageResource::create());
    148     return image;
    149 }
    150 
    151 bool HTMLImageElement::canStartSelection() const
    152 {
    153     if (shadow())
    154         return HTMLElement::canStartSelection();
    155 
    156     return false;
    157 }
    158 
    159 void HTMLImageElement::attach(const AttachContext& context)
    160 {
    161     HTMLElement::attach(context);
    162 
    163     if (renderer() && renderer()->isImage() && !m_imageLoader.hasPendingBeforeLoadEvent()) {
    164         RenderImage* renderImage = toRenderImage(renderer());
    165         RenderImageResource* renderImageResource = renderImage->imageResource();
    166         if (renderImageResource->hasImage())
    167             return;
    168 
    169         // If we have no image at all because we have no src attribute, set
    170         // image height and width for the alt text instead.
    171         if (!m_imageLoader.image() && !renderImageResource->cachedImage())
    172             renderImage->setImageSizeForAltText();
    173         else
    174             renderImageResource->setImageResource(m_imageLoader.image());
    175 
    176     }
    177 }
    178 
    179 Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint)
    180 {
    181     // m_form can be non-null if it was set in constructor.
    182     if (m_form && insertionPoint->highestAncestor() != m_form->highestAncestor()) {
    183         m_form->removeImgElement(this);
    184         m_form = 0;
    185     }
    186 
    187     if (!m_form) {
    188         m_form = findFormAncestor();
    189         if (m_form)
    190             m_form->registerImgElement(this);
    191     }
    192 
    193     // If we have been inserted from a renderer-less document,
    194     // our loader may have not fetched the image, so do it now.
    195     if (insertionPoint->inDocument() && !m_imageLoader.image())
    196         m_imageLoader.updateFromElement();
    197 
    198     return HTMLElement::insertedInto(insertionPoint);
    199 }
    200 
    201 void HTMLImageElement::removedFrom(ContainerNode* insertionPoint)
    202 {
    203     if (m_form)
    204         m_form->removeImgElement(this);
    205     m_form = 0;
    206     HTMLElement::removedFrom(insertionPoint);
    207 }
    208 
    209 int HTMLImageElement::width(bool ignorePendingStylesheets)
    210 {
    211     if (!renderer()) {
    212         // check the attribute first for an explicit pixel value
    213         bool ok;
    214         int width = getAttribute(widthAttr).toInt(&ok);
    215         if (ok)
    216             return width;
    217 
    218         // if the image is available, use its width
    219         if (m_imageLoader.image())
    220             return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width();
    221     }
    222 
    223     if (ignorePendingStylesheets)
    224         document()->updateLayoutIgnorePendingStylesheets();
    225     else
    226         document()->updateLayout();
    227 
    228     RenderBox* box = renderBox();
    229     return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedWidth(), box) : 0;
    230 }
    231 
    232 int HTMLImageElement::height(bool ignorePendingStylesheets)
    233 {
    234     if (!renderer()) {
    235         // check the attribute first for an explicit pixel value
    236         bool ok;
    237         int height = getAttribute(heightAttr).toInt(&ok);
    238         if (ok)
    239             return height;
    240 
    241         // if the image is available, use its height
    242         if (m_imageLoader.image())
    243             return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height();
    244     }
    245 
    246     if (ignorePendingStylesheets)
    247         document()->updateLayoutIgnorePendingStylesheets();
    248     else
    249         document()->updateLayout();
    250 
    251     RenderBox* box = renderBox();
    252     return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedHeight(), box) : 0;
    253 }
    254 
    255 int HTMLImageElement::naturalWidth() const
    256 {
    257     if (!m_imageLoader.image())
    258         return 0;
    259 
    260     return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width();
    261 }
    262 
    263 int HTMLImageElement::naturalHeight() const
    264 {
    265     if (!m_imageLoader.image())
    266         return 0;
    267 
    268     return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height();
    269 }
    270 
    271 bool HTMLImageElement::isURLAttribute(const Attribute& attribute) const
    272 {
    273     return attribute.name() == srcAttr
    274         || attribute.name() == lowsrcAttr
    275         || attribute.name() == longdescAttr
    276         || (attribute.name() == usemapAttr && attribute.value().string()[0] != '#')
    277         || HTMLElement::isURLAttribute(attribute);
    278 }
    279 
    280 const AtomicString& HTMLImageElement::alt() const
    281 {
    282     return getAttribute(altAttr);
    283 }
    284 
    285 bool HTMLImageElement::draggable() const
    286 {
    287     // Image elements are draggable by default.
    288     return !equalIgnoringCase(getAttribute(draggableAttr), "false");
    289 }
    290 
    291 void HTMLImageElement::setHeight(int value)
    292 {
    293     setAttribute(heightAttr, String::number(value));
    294 }
    295 
    296 KURL HTMLImageElement::src() const
    297 {
    298     return document()->completeURL(getAttribute(srcAttr));
    299 }
    300 
    301 void HTMLImageElement::setSrc(const String& value)
    302 {
    303     setAttribute(srcAttr, value);
    304 }
    305 
    306 void HTMLImageElement::setWidth(int value)
    307 {
    308     setAttribute(widthAttr, String::number(value));
    309 }
    310 
    311 int HTMLImageElement::x() const
    312 {
    313     RenderObject* r = renderer();
    314     if (!r)
    315         return 0;
    316 
    317     // FIXME: This doesn't work correctly with transforms.
    318     FloatPoint absPos = r->localToAbsolute();
    319     return absPos.x();
    320 }
    321 
    322 int HTMLImageElement::y() const
    323 {
    324     RenderObject* r = renderer();
    325     if (!r)
    326         return 0;
    327 
    328     // FIXME: This doesn't work correctly with transforms.
    329     FloatPoint absPos = r->localToAbsolute();
    330     return absPos.y();
    331 }
    332 
    333 bool HTMLImageElement::complete() const
    334 {
    335     return m_imageLoader.imageComplete();
    336 }
    337 
    338 void HTMLImageElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
    339 {
    340     HTMLElement::addSubresourceAttributeURLs(urls);
    341 
    342     addSubresourceURL(urls, src());
    343     // FIXME: What about when the usemap attribute begins with "#"?
    344     addSubresourceURL(urls, document()->completeURL(getAttribute(usemapAttr)));
    345 }
    346 
    347 void HTMLImageElement::didMoveToNewDocument(Document* oldDocument)
    348 {
    349     m_imageLoader.elementDidMoveToNewDocument();
    350     HTMLElement::didMoveToNewDocument(oldDocument);
    351 }
    352 
    353 bool HTMLImageElement::isServerMap() const
    354 {
    355     if (!fastHasAttribute(ismapAttr))
    356         return false;
    357 
    358     const AtomicString& usemap = fastGetAttribute(usemapAttr);
    359 
    360     // If the usemap attribute starts with '#', it refers to a map element in the document.
    361     if (usemap.string()[0] == '#')
    362         return false;
    363 
    364     return document()->completeURL(stripLeadingAndTrailingHTMLSpaces(usemap)).isEmpty();
    365 }
    366 
    367 Image* HTMLImageElement::imageContents()
    368 {
    369     if (!m_imageLoader.imageComplete())
    370         return 0;
    371 
    372     return m_imageLoader.image()->image();
    373 }
    374 
    375 }
    376