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