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