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