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