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 "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 38using namespace std; 39 40namespace WebCore { 41 42using namespace HTMLNames; 43 44HTMLImageElement::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 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 > 0) 78 image->setHeight(*optionalHeight); 79 return image.release(); 80} 81 82bool 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 101void 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 169String 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 181RenderObject* 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 191void 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 209void 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 225void 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 236void 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 252void HTMLImageElement::removedFromTree(bool deep) 253{ 254 if (m_form) 255 m_form->removeImgElement(this); 256 m_form = 0; 257 HTMLElement::removedFromTree(deep); 258} 259 260int 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 283int 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 306int HTMLImageElement::naturalWidth() const 307{ 308 if (!m_imageLoader.image()) 309 return 0; 310 311 return m_imageLoader.image()->imageSize(1.0f).width(); 312} 313 314int HTMLImageElement::naturalHeight() const 315{ 316 if (!m_imageLoader.image()) 317 return 0; 318 319 return m_imageLoader.image()->imageSize(1.0f).height(); 320} 321 322bool 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 330const AtomicString& HTMLImageElement::alt() const 331{ 332 return getAttribute(altAttr); 333} 334 335bool HTMLImageElement::draggable() const 336{ 337 // Image elements are draggable by default. 338 return !equalIgnoringCase(getAttribute(draggableAttr), "false"); 339} 340 341void HTMLImageElement::setHeight(int value) 342{ 343 setAttribute(heightAttr, String::number(value)); 344} 345 346KURL HTMLImageElement::src() const 347{ 348 return document()->completeURL(getAttribute(srcAttr)); 349} 350 351void HTMLImageElement::setSrc(const String& value) 352{ 353 setAttribute(srcAttr, value); 354} 355 356void HTMLImageElement::setWidth(int value) 357{ 358 setAttribute(widthAttr, String::number(value)); 359} 360 361int 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 372int 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 383bool HTMLImageElement::complete() const 384{ 385 return m_imageLoader.imageComplete(); 386} 387 388void 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 397void HTMLImageElement::willMoveToNewOwnerDocument() 398{ 399 m_imageLoader.elementWillMoveToNewOwnerDocument(); 400 HTMLElement::willMoveToNewOwnerDocument(); 401} 402 403} 404