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 "bindings/core/v8/ScriptEventListener.h" 27 #include "core/CSSPropertyNames.h" 28 #include "core/HTMLNames.h" 29 #include "core/MediaTypeNames.h" 30 #include "core/css/MediaQueryMatcher.h" 31 #include "core/css/MediaValuesDynamic.h" 32 #include "core/css/parser/SizesAttributeParser.h" 33 #include "core/dom/Attribute.h" 34 #include "core/dom/NodeTraversal.h" 35 #include "core/fetch/ImageResource.h" 36 #include "core/frame/UseCounter.h" 37 #include "core/html/HTMLAnchorElement.h" 38 #include "core/html/HTMLCanvasElement.h" 39 #include "core/html/HTMLFormElement.h" 40 #include "core/html/HTMLSourceElement.h" 41 #include "core/html/canvas/CanvasRenderingContext.h" 42 #include "core/html/parser/HTMLParserIdioms.h" 43 #include "core/html/parser/HTMLSrcsetParser.h" 44 #include "core/inspector/ConsoleMessage.h" 45 #include "core/rendering/RenderImage.h" 46 #include "platform/MIMETypeRegistry.h" 47 #include "platform/RuntimeEnabledFeatures.h" 48 49 namespace blink { 50 51 using namespace HTMLNames; 52 53 class HTMLImageElement::ViewportChangeListener FINAL : public MediaQueryListListener { 54 public: 55 static RefPtrWillBeRawPtr<ViewportChangeListener> create(HTMLImageElement* element) 56 { 57 return adoptRefWillBeNoop(new ViewportChangeListener(element)); 58 } 59 60 virtual void notifyMediaQueryChanged() OVERRIDE 61 { 62 if (m_element) 63 m_element->notifyViewportChanged(); 64 } 65 66 #if !ENABLE(OILPAN) 67 void clearElement() { m_element = nullptr; } 68 #endif 69 virtual void trace(Visitor* visitor) OVERRIDE 70 { 71 visitor->trace(m_element); 72 MediaQueryListListener::trace(visitor); 73 } 74 private: 75 explicit ViewportChangeListener(HTMLImageElement* element) : m_element(element) { } 76 RawPtrWillBeMember<HTMLImageElement> m_element; 77 }; 78 79 HTMLImageElement::HTMLImageElement(Document& document, HTMLFormElement* form, bool createdByParser) 80 : HTMLElement(imgTag, document) 81 , m_imageLoader(HTMLImageLoader::create(this)) 82 , m_compositeOperator(CompositeSourceOver) 83 , m_imageDevicePixelRatio(1.0f) 84 , m_formWasSetByParser(false) 85 , m_elementCreatedByParser(createdByParser) 86 , m_intrinsicSizingViewportDependant(false) 87 , m_effectiveSizeViewportDependant(false) 88 { 89 if (form && form->inDocument()) { 90 #if ENABLE(OILPAN) 91 m_form = form; 92 #else 93 m_form = form->createWeakPtr(); 94 #endif 95 m_formWasSetByParser = true; 96 m_form->associate(*this); 97 m_form->didAssociateByParser(); 98 } 99 } 100 101 PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::create(Document& document) 102 { 103 return adoptRefWillBeNoop(new HTMLImageElement(document)); 104 } 105 106 PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::create(Document& document, HTMLFormElement* form, bool createdByParser) 107 { 108 return adoptRefWillBeNoop(new HTMLImageElement(document, form, createdByParser)); 109 } 110 111 HTMLImageElement::~HTMLImageElement() 112 { 113 #if !ENABLE(OILPAN) 114 if (m_listener) { 115 document().mediaQueryMatcher().removeViewportListener(m_listener.get()); 116 m_listener->clearElement(); 117 } 118 if (m_form) 119 m_form->disassociate(*this); 120 #endif 121 } 122 123 void HTMLImageElement::trace(Visitor* visitor) 124 { 125 visitor->trace(m_imageLoader); 126 visitor->trace(m_listener); 127 visitor->trace(m_form); 128 HTMLElement::trace(visitor); 129 } 130 131 void HTMLImageElement::notifyViewportChanged() 132 { 133 // Re-selecting the source URL in order to pick a more fitting resource 134 // And update the image's intrinsic dimensions when the viewport changes. 135 // Picking of a better fitting resource is UA dependant, not spec required. 136 selectSourceURL(ImageLoader::UpdateSizeChanged); 137 } 138 139 PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document& document, int width, int height) 140 { 141 RefPtrWillBeRawPtr<HTMLImageElement> image = adoptRefWillBeNoop(new HTMLImageElement(document)); 142 if (width) 143 image->setWidth(width); 144 if (height) 145 image->setHeight(height); 146 image->m_elementCreatedByParser = false; 147 return image.release(); 148 } 149 150 bool HTMLImageElement::isPresentationAttribute(const QualifiedName& name) const 151 { 152 if (name == widthAttr || name == heightAttr || name == borderAttr || name == vspaceAttr || name == hspaceAttr || name == alignAttr || name == valignAttr) 153 return true; 154 return HTMLElement::isPresentationAttribute(name); 155 } 156 157 void HTMLImageElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style) 158 { 159 if (name == widthAttr) 160 addHTMLLengthToStyle(style, CSSPropertyWidth, value); 161 else if (name == heightAttr) 162 addHTMLLengthToStyle(style, CSSPropertyHeight, value); 163 else if (name == borderAttr) 164 applyBorderAttributeToStyle(value, style); 165 else if (name == vspaceAttr) { 166 addHTMLLengthToStyle(style, CSSPropertyMarginTop, value); 167 addHTMLLengthToStyle(style, CSSPropertyMarginBottom, value); 168 } else if (name == hspaceAttr) { 169 addHTMLLengthToStyle(style, CSSPropertyMarginLeft, value); 170 addHTMLLengthToStyle(style, CSSPropertyMarginRight, value); 171 } else if (name == alignAttr) 172 applyAlignmentAttributeToStyle(value, style); 173 else if (name == valignAttr) 174 addPropertyToPresentationAttributeStyle(style, CSSPropertyVerticalAlign, value); 175 else 176 HTMLElement::collectStyleForPresentationAttribute(name, value, style); 177 } 178 179 const AtomicString HTMLImageElement::imageSourceURL() const 180 { 181 return m_bestFitImageURL.isNull() ? fastGetAttribute(srcAttr) : m_bestFitImageURL; 182 } 183 184 HTMLFormElement* HTMLImageElement::formOwner() const 185 { 186 return m_form.get(); 187 } 188 189 void HTMLImageElement::formRemovedFromTree(const Node& formRoot) 190 { 191 ASSERT(m_form); 192 if (NodeTraversal::highestAncestorOrSelf(*this) != formRoot) 193 resetFormOwner(); 194 } 195 196 void HTMLImageElement::resetFormOwner() 197 { 198 m_formWasSetByParser = false; 199 HTMLFormElement* nearestForm = findFormAncestor(); 200 if (m_form) { 201 if (nearestForm == m_form.get()) 202 return; 203 m_form->disassociate(*this); 204 } 205 if (nearestForm) { 206 #if ENABLE(OILPAN) 207 m_form = nearestForm; 208 #else 209 m_form = nearestForm->createWeakPtr(); 210 #endif 211 m_form->associate(*this); 212 } else { 213 #if ENABLE(OILPAN) 214 m_form = nullptr; 215 #else 216 m_form = WeakPtr<HTMLFormElement>(); 217 #endif 218 } 219 } 220 221 void HTMLImageElement::setBestFitURLAndDPRFromImageCandidate(const ImageCandidate& candidate) 222 { 223 m_bestFitImageURL = candidate.url(); 224 float candidateDensity = candidate.density(); 225 if (candidateDensity >= 0) 226 m_imageDevicePixelRatio = 1.0 / candidateDensity; 227 if (candidate.resourceWidth() > 0) { 228 m_intrinsicSizingViewportDependant = true; 229 UseCounter::count(document(), UseCounter::SrcsetWDescriptor); 230 } else if (!candidate.srcOrigin()) { 231 UseCounter::count(document(), UseCounter::SrcsetXDescriptor); 232 } 233 if (renderer() && renderer()->isImage()) 234 toRenderImage(renderer())->setImageDevicePixelRatio(m_imageDevicePixelRatio); 235 } 236 237 void HTMLImageElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 238 { 239 if (name == altAttr) { 240 if (renderer() && renderer()->isImage()) 241 toRenderImage(renderer())->updateAltText(); 242 } else if (name == srcAttr || name == srcsetAttr || name == sizesAttr) { 243 selectSourceURL(ImageLoader::UpdateIgnorePreviousError); 244 } else if (name == usemapAttr) { 245 setIsLink(!value.isNull()); 246 } else if (name == compositeAttr) { 247 blink::WebBlendMode blendOp = blink::WebBlendModeNormal; 248 if (!parseCompositeAndBlendOperator(value, m_compositeOperator, blendOp)) 249 m_compositeOperator = CompositeSourceOver; 250 else if (m_compositeOperator != CompositeSourceOver) 251 UseCounter::count(document(), UseCounter::HTMLImageElementComposite); 252 } else { 253 HTMLElement::parseAttribute(name, value); 254 } 255 } 256 257 const AtomicString& HTMLImageElement::altText() const 258 { 259 // lets figure out the alt text.. magic stuff 260 // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen 261 // also heavily discussed by Hixie on bugzilla 262 const AtomicString& alt = fastGetAttribute(altAttr); 263 if (!alt.isNull()) 264 return alt; 265 // fall back to title attribute 266 return fastGetAttribute(titleAttr); 267 } 268 269 static bool supportedImageType(const String& type) 270 { 271 return MIMETypeRegistry::isSupportedImagePrefixedMIMEType(type); 272 } 273 274 // http://picture.responsiveimages.org/#update-source-set 275 ImageCandidate HTMLImageElement::findBestFitImageFromPictureParent() 276 { 277 ASSERT(isMainThread()); 278 Node* parent = parentNode(); 279 if (!parent || !isHTMLPictureElement(*parent)) 280 return ImageCandidate(); 281 for (Node* child = parent->firstChild(); child; child = child->nextSibling()) { 282 if (child == this) 283 return ImageCandidate(); 284 285 if (!isHTMLSourceElement(*child)) 286 continue; 287 288 HTMLSourceElement* source = toHTMLSourceElement(child); 289 if (!source->fastGetAttribute(srcAttr).isNull()) 290 UseCounter::countDeprecation(document(), UseCounter::PictureSourceSrc); 291 String srcset = source->fastGetAttribute(srcsetAttr); 292 if (srcset.isEmpty()) 293 continue; 294 String type = source->fastGetAttribute(typeAttr); 295 if (!type.isEmpty() && !supportedImageType(type)) 296 continue; 297 298 if (!source->mediaQueryMatches()) 299 continue; 300 301 String sizes = source->fastGetAttribute(sizesAttr); 302 if (!sizes.isNull()) 303 UseCounter::count(document(), UseCounter::Sizes); 304 SizesAttributeParser parser = SizesAttributeParser(MediaValuesDynamic::create(document()), sizes); 305 unsigned effectiveSize = parser.length(); 306 m_effectiveSizeViewportDependant = parser.viewportDependant(); 307 ImageCandidate candidate = bestFitSourceForSrcsetAttribute(document().devicePixelRatio(), effectiveSize, source->fastGetAttribute(srcsetAttr)); 308 if (candidate.isEmpty()) 309 continue; 310 return candidate; 311 } 312 return ImageCandidate(); 313 } 314 315 RenderObject* HTMLImageElement::createRenderer(RenderStyle* style) 316 { 317 if (style->hasContent()) 318 return RenderObject::createObject(this, style); 319 320 RenderImage* image = new RenderImage(this); 321 image->setImageResource(RenderImageResource::create()); 322 image->setImageDevicePixelRatio(m_imageDevicePixelRatio); 323 return image; 324 } 325 326 bool HTMLImageElement::canStartSelection() const 327 { 328 if (shadow()) 329 return HTMLElement::canStartSelection(); 330 331 return false; 332 } 333 334 void HTMLImageElement::attach(const AttachContext& context) 335 { 336 HTMLElement::attach(context); 337 338 if (renderer() && renderer()->isImage()) { 339 RenderImage* renderImage = toRenderImage(renderer()); 340 RenderImageResource* renderImageResource = renderImage->imageResource(); 341 if (renderImageResource->hasImage()) 342 return; 343 344 // If we have no image at all because we have no src attribute, set 345 // image height and width for the alt text instead. 346 if (!imageLoader().image() && !renderImageResource->cachedImage()) 347 renderImage->setImageSizeForAltText(); 348 else 349 renderImageResource->setImageResource(imageLoader().image()); 350 351 } 352 } 353 354 Node::InsertionNotificationRequest HTMLImageElement::insertedInto(ContainerNode* insertionPoint) 355 { 356 if (!m_formWasSetByParser || NodeTraversal::highestAncestorOrSelf(*insertionPoint) != NodeTraversal::highestAncestorOrSelf(*m_form.get())) 357 resetFormOwner(); 358 if (m_listener) 359 document().mediaQueryMatcher().addViewportListener(m_listener); 360 361 bool imageWasModified = false; 362 if (RuntimeEnabledFeatures::pictureEnabled()) { 363 ImageCandidate candidate = findBestFitImageFromPictureParent(); 364 if (!candidate.isEmpty()) { 365 setBestFitURLAndDPRFromImageCandidate(candidate); 366 imageWasModified = true; 367 } 368 } 369 370 // If we have been inserted from a renderer-less document, 371 // our loader may have not fetched the image, so do it now. 372 if ((insertionPoint->inDocument() && !imageLoader().image()) || imageWasModified) 373 imageLoader().updateFromElement(ImageLoader::UpdateNormal, m_elementCreatedByParser ? ImageLoader::ForceLoadImmediately : ImageLoader::LoadNormally); 374 375 return HTMLElement::insertedInto(insertionPoint); 376 } 377 378 void HTMLImageElement::removedFrom(ContainerNode* insertionPoint) 379 { 380 if (!m_form || NodeTraversal::highestAncestorOrSelf(*m_form.get()) != NodeTraversal::highestAncestorOrSelf(*this)) 381 resetFormOwner(); 382 if (m_listener) 383 document().mediaQueryMatcher().removeViewportListener(m_listener); 384 HTMLElement::removedFrom(insertionPoint); 385 } 386 387 int HTMLImageElement::width(bool ignorePendingStylesheets) 388 { 389 if (!renderer()) { 390 // check the attribute first for an explicit pixel value 391 bool ok; 392 int width = getAttribute(widthAttr).toInt(&ok); 393 if (ok) 394 return width; 395 396 // if the image is available, use its width 397 if (imageLoader().image()) 398 return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f).width(); 399 } 400 401 if (ignorePendingStylesheets) 402 document().updateLayoutIgnorePendingStylesheets(); 403 else 404 document().updateLayout(); 405 406 RenderBox* box = renderBox(); 407 return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedWidth(), box) : 0; 408 } 409 410 int HTMLImageElement::height(bool ignorePendingStylesheets) 411 { 412 if (!renderer()) { 413 // check the attribute first for an explicit pixel value 414 bool ok; 415 int height = getAttribute(heightAttr).toInt(&ok); 416 if (ok) 417 return height; 418 419 // if the image is available, use its height 420 if (imageLoader().image()) 421 return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f).height(); 422 } 423 424 if (ignorePendingStylesheets) 425 document().updateLayoutIgnorePendingStylesheets(); 426 else 427 document().updateLayout(); 428 429 RenderBox* box = renderBox(); 430 return box ? adjustForAbsoluteZoom(box->contentBoxRect().pixelSnappedHeight(), box) : 0; 431 } 432 433 int HTMLImageElement::naturalWidth() const 434 { 435 if (!imageLoader().image()) 436 return 0; 437 438 return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f, ImageResource::IntrinsicSize).width(); 439 } 440 441 int HTMLImageElement::naturalHeight() const 442 { 443 if (!imageLoader().image()) 444 return 0; 445 446 return imageLoader().image()->imageSizeForRenderer(renderer(), 1.0f, ImageResource::IntrinsicSize).height(); 447 } 448 449 const String& HTMLImageElement::currentSrc() const 450 { 451 // http://www.whatwg.org/specs/web-apps/current-work/multipage/edits.html#dom-img-currentsrc 452 // The currentSrc IDL attribute must return the img element's current request's current URL. 453 // Initially, the pending request turns into current request when it is either available or broken. 454 // We use the image's dimensions as a proxy to it being in any of these states. 455 if (!imageLoader().image() || !imageLoader().image()->image() || !imageLoader().image()->image()->width()) 456 return emptyAtom; 457 458 return imageLoader().image()->url().string(); 459 } 460 461 bool HTMLImageElement::isURLAttribute(const Attribute& attribute) const 462 { 463 return attribute.name() == srcAttr 464 || attribute.name() == lowsrcAttr 465 || attribute.name() == longdescAttr 466 || (attribute.name() == usemapAttr && attribute.value()[0] != '#') 467 || HTMLElement::isURLAttribute(attribute); 468 } 469 470 bool HTMLImageElement::hasLegalLinkAttribute(const QualifiedName& name) const 471 { 472 return name == srcAttr || HTMLElement::hasLegalLinkAttribute(name); 473 } 474 475 const QualifiedName& HTMLImageElement::subResourceAttributeName() const 476 { 477 return srcAttr; 478 } 479 480 bool HTMLImageElement::draggable() const 481 { 482 // Image elements are draggable by default. 483 return !equalIgnoringCase(getAttribute(draggableAttr), "false"); 484 } 485 486 void HTMLImageElement::setHeight(int value) 487 { 488 setIntegralAttribute(heightAttr, value); 489 } 490 491 KURL HTMLImageElement::src() const 492 { 493 return document().completeURL(getAttribute(srcAttr)); 494 } 495 496 void HTMLImageElement::setSrc(const String& value) 497 { 498 setAttribute(srcAttr, AtomicString(value)); 499 } 500 501 void HTMLImageElement::setWidth(int value) 502 { 503 setIntegralAttribute(widthAttr, value); 504 } 505 506 int HTMLImageElement::x() const 507 { 508 document().updateLayoutIgnorePendingStylesheets(); 509 RenderObject* r = renderer(); 510 if (!r) 511 return 0; 512 513 // FIXME: This doesn't work correctly with transforms. 514 FloatPoint absPos = r->localToAbsolute(); 515 return absPos.x(); 516 } 517 518 int HTMLImageElement::y() const 519 { 520 document().updateLayoutIgnorePendingStylesheets(); 521 RenderObject* r = renderer(); 522 if (!r) 523 return 0; 524 525 // FIXME: This doesn't work correctly with transforms. 526 FloatPoint absPos = r->localToAbsolute(); 527 return absPos.y(); 528 } 529 530 bool HTMLImageElement::complete() const 531 { 532 return imageLoader().imageComplete(); 533 } 534 535 void HTMLImageElement::didMoveToNewDocument(Document& oldDocument) 536 { 537 imageLoader().elementDidMoveToNewDocument(); 538 HTMLElement::didMoveToNewDocument(oldDocument); 539 } 540 541 bool HTMLImageElement::isServerMap() const 542 { 543 if (!fastHasAttribute(ismapAttr)) 544 return false; 545 546 const AtomicString& usemap = fastGetAttribute(usemapAttr); 547 548 // If the usemap attribute starts with '#', it refers to a map element in the document. 549 if (usemap[0] == '#') 550 return false; 551 552 return document().completeURL(stripLeadingAndTrailingHTMLSpaces(usemap)).isEmpty(); 553 } 554 555 Image* HTMLImageElement::imageContents() 556 { 557 if (!imageLoader().imageComplete()) 558 return 0; 559 560 return imageLoader().image()->image(); 561 } 562 563 bool HTMLImageElement::isInteractiveContent() const 564 { 565 return fastHasAttribute(usemapAttr); 566 } 567 568 PassRefPtr<Image> HTMLImageElement::getSourceImageForCanvas(SourceImageMode, SourceImageStatus* status) const 569 { 570 if (!complete() || !cachedImage()) { 571 *status = IncompleteSourceImageStatus; 572 return nullptr; 573 } 574 575 if (cachedImage()->errorOccurred()) { 576 *status = UndecodableSourceImageStatus; 577 return nullptr; 578 } 579 580 RefPtr<Image> sourceImage = cachedImage()->imageForRenderer(renderer()); 581 582 // We need to synthesize a container size if a renderer is not available to provide one. 583 if (!renderer() && sourceImage->usesContainerSize()) 584 sourceImage->setContainerSize(sourceImage->size()); 585 586 *status = NormalSourceImageStatus; 587 return sourceImage->imageForDefaultFrame(); 588 } 589 590 bool HTMLImageElement::wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const 591 { 592 ImageResource* image = cachedImage(); 593 if (!image) 594 return false; 595 return !image->isAccessAllowed(destinationSecurityOrigin); 596 } 597 598 FloatSize HTMLImageElement::sourceSize() const 599 { 600 ImageResource* image = cachedImage(); 601 if (!image) 602 return FloatSize(); 603 LayoutSize size; 604 size = image->imageSizeForRenderer(renderer(), 1.0f); // FIXME: Not sure about this. 605 606 return size; 607 } 608 609 FloatSize HTMLImageElement::defaultDestinationSize() const 610 { 611 ImageResource* image = cachedImage(); 612 if (!image) 613 return FloatSize(); 614 LayoutSize size; 615 size = image->imageSizeForRenderer(renderer(), 1.0f); // FIXME: Not sure about this. 616 if (renderer() && renderer()->isRenderImage() && image->image() && !image->image()->hasRelativeWidth()) 617 size.scale(toRenderImage(renderer())->imageDevicePixelRatio()); 618 return size; 619 } 620 621 void HTMLImageElement::selectSourceURL(ImageLoader::UpdateFromElementBehavior behavior) 622 { 623 bool foundURL = false; 624 if (RuntimeEnabledFeatures::pictureEnabled()) { 625 ImageCandidate candidate = findBestFitImageFromPictureParent(); 626 if (!candidate.isEmpty()) { 627 setBestFitURLAndDPRFromImageCandidate(candidate); 628 foundURL = true; 629 } 630 } 631 632 if (!foundURL) { 633 unsigned effectiveSize = 0; 634 if (RuntimeEnabledFeatures::pictureSizesEnabled()) { 635 String sizes = fastGetAttribute(sizesAttr); 636 if (!sizes.isNull()) 637 UseCounter::count(document(), UseCounter::Sizes); 638 SizesAttributeParser parser = SizesAttributeParser(MediaValuesDynamic::create(document()), sizes); 639 effectiveSize = parser.length(); 640 m_effectiveSizeViewportDependant = parser.viewportDependant(); 641 } 642 ImageCandidate candidate = bestFitSourceForImageAttributes(document().devicePixelRatio(), effectiveSize, fastGetAttribute(srcAttr), fastGetAttribute(srcsetAttr)); 643 setBestFitURLAndDPRFromImageCandidate(candidate); 644 } 645 if (m_intrinsicSizingViewportDependant && m_effectiveSizeViewportDependant && !m_listener) { 646 m_listener = ViewportChangeListener::create(this); 647 document().mediaQueryMatcher().addViewportListener(m_listener); 648 } 649 imageLoader().updateFromElement(behavior); 650 } 651 652 const KURL& HTMLImageElement::sourceURL() const 653 { 654 return cachedImage()->response().url(); 655 } 656 657 } 658