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