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