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, 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 49namespace blink { 50 51using namespace HTMLNames; 52 53class HTMLImageElement::ViewportChangeListener FINAL : public MediaQueryListListener { 54public: 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 } 74private: 75 explicit ViewportChangeListener(HTMLImageElement* element) : m_element(element) { } 76 RawPtrWillBeMember<HTMLImageElement> m_element; 77}; 78 79HTMLImageElement::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 101PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::create(Document& document) 102{ 103 return adoptRefWillBeNoop(new HTMLImageElement(document)); 104} 105 106PassRefPtrWillBeRawPtr<HTMLImageElement> HTMLImageElement::create(Document& document, HTMLFormElement* form, bool createdByParser) 107{ 108 return adoptRefWillBeNoop(new HTMLImageElement(document, form, createdByParser)); 109} 110 111HTMLImageElement::~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 123void 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 131void 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 139PassRefPtrWillBeRawPtr<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 150bool 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 157void 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 179const AtomicString HTMLImageElement::imageSourceURL() const 180{ 181 return m_bestFitImageURL.isNull() ? fastGetAttribute(srcAttr) : m_bestFitImageURL; 182} 183 184HTMLFormElement* HTMLImageElement::formOwner() const 185{ 186 return m_form.get(); 187} 188 189void HTMLImageElement::formRemovedFromTree(const Node& formRoot) 190{ 191 ASSERT(m_form); 192 if (NodeTraversal::highestAncestorOrSelf(*this) != formRoot) 193 resetFormOwner(); 194} 195 196void 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 221void 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 237void 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 257const 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 269static bool supportedImageType(const String& type) 270{ 271 return MIMETypeRegistry::isSupportedImagePrefixedMIMEType(type); 272} 273 274// http://picture.responsiveimages.org/#update-source-set 275ImageCandidate 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 315RenderObject* 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 326bool HTMLImageElement::canStartSelection() const 327{ 328 if (shadow()) 329 return HTMLElement::canStartSelection(); 330 331 return false; 332} 333 334void 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 354Node::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 378void 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 387int 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 410int 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 433int 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 441int 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 449const 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 461bool 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 470bool HTMLImageElement::hasLegalLinkAttribute(const QualifiedName& name) const 471{ 472 return name == srcAttr || HTMLElement::hasLegalLinkAttribute(name); 473} 474 475const QualifiedName& HTMLImageElement::subResourceAttributeName() const 476{ 477 return srcAttr; 478} 479 480bool HTMLImageElement::draggable() const 481{ 482 // Image elements are draggable by default. 483 return !equalIgnoringCase(getAttribute(draggableAttr), "false"); 484} 485 486void HTMLImageElement::setHeight(int value) 487{ 488 setIntegralAttribute(heightAttr, value); 489} 490 491KURL HTMLImageElement::src() const 492{ 493 return document().completeURL(getAttribute(srcAttr)); 494} 495 496void HTMLImageElement::setSrc(const String& value) 497{ 498 setAttribute(srcAttr, AtomicString(value)); 499} 500 501void HTMLImageElement::setWidth(int value) 502{ 503 setIntegralAttribute(widthAttr, value); 504} 505 506int 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 518int 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 530bool HTMLImageElement::complete() const 531{ 532 return imageLoader().imageComplete(); 533} 534 535void HTMLImageElement::didMoveToNewDocument(Document& oldDocument) 536{ 537 imageLoader().elementDidMoveToNewDocument(); 538 HTMLElement::didMoveToNewDocument(oldDocument); 539} 540 541bool 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 555Image* HTMLImageElement::imageContents() 556{ 557 if (!imageLoader().imageComplete()) 558 return 0; 559 560 return imageLoader().image()->image(); 561} 562 563bool HTMLImageElement::isInteractiveContent() const 564{ 565 return fastHasAttribute(usemapAttr); 566} 567 568PassRefPtr<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 590bool HTMLImageElement::wouldTaintOrigin(SecurityOrigin* destinationSecurityOrigin) const 591{ 592 ImageResource* image = cachedImage(); 593 if (!image) 594 return false; 595 return !image->isAccessAllowed(destinationSecurityOrigin); 596} 597 598FloatSize 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 609FloatSize 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 621void 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 652const KURL& HTMLImageElement::sourceURL() const 653{ 654 return cachedImage()->response().url(); 655} 656 657} 658