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, 2009, 2010 Apple Inc. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include "config.h" 23 #include "core/loader/ImageLoader.h" 24 25 #include "bindings/v8/ScriptController.h" 26 #include "core/dom/Document.h" 27 #include "core/dom/Element.h" 28 #include "core/dom/IncrementLoadEventDelayCount.h" 29 #include "core/dom/Microtask.h" 30 #include "core/events/Event.h" 31 #include "core/events/EventSender.h" 32 #include "core/fetch/CrossOriginAccessControl.h" 33 #include "core/fetch/FetchRequest.h" 34 #include "core/fetch/MemoryCache.h" 35 #include "core/fetch/ResourceFetcher.h" 36 #include "core/frame/LocalFrame.h" 37 #include "core/html/HTMLImageElement.h" 38 #include "core/html/parser/HTMLParserIdioms.h" 39 #include "core/rendering/RenderImage.h" 40 #include "core/rendering/RenderVideo.h" 41 #include "core/rendering/svg/RenderSVGImage.h" 42 #include "platform/weborigin/SecurityOrigin.h" 43 44 namespace WebCore { 45 46 static ImageEventSender& loadEventSender() 47 { 48 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::load)); 49 return sender; 50 } 51 52 static ImageEventSender& errorEventSender() 53 { 54 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::error)); 55 return sender; 56 } 57 58 static inline bool pageIsBeingDismissed(Document* document) 59 { 60 return document->pageDismissalEventBeingDispatched() != Document::NoDismissal; 61 } 62 63 class ImageLoader::Task : public blink::WebThread::Task { 64 public: 65 Task(ImageLoader* loader) 66 : m_loader(loader) 67 , m_shouldBypassMainWorldContentSecurityPolicy(false) 68 , m_weakFactory(this) 69 { 70 LocalFrame* frame = loader->m_element->document().frame(); 71 m_shouldBypassMainWorldContentSecurityPolicy = frame->script().shouldBypassMainWorldContentSecurityPolicy(); 72 } 73 74 virtual void run() OVERRIDE 75 { 76 if (m_loader) { 77 m_loader->doUpdateFromElement(m_shouldBypassMainWorldContentSecurityPolicy); 78 } 79 } 80 81 void clearLoader() 82 { 83 m_loader = 0; 84 } 85 86 WeakPtr<Task> createWeakPtr() 87 { 88 return m_weakFactory.createWeakPtr(); 89 } 90 91 private: 92 ImageLoader* m_loader; 93 bool m_shouldBypassMainWorldContentSecurityPolicy; 94 WeakPtrFactory<Task> m_weakFactory; 95 }; 96 97 ImageLoader::ImageLoader(Element* element) 98 : m_element(element) 99 , m_image(0) 100 , m_derefElementTimer(this, &ImageLoader::timerFired) 101 , m_hasPendingLoadEvent(false) 102 , m_hasPendingErrorEvent(false) 103 , m_imageComplete(true) 104 , m_loadManually(false) 105 , m_elementIsProtected(false) 106 , m_highPriorityClientCount(0) 107 { 108 } 109 110 ImageLoader::~ImageLoader() 111 { 112 if (m_pendingTask) 113 m_pendingTask->clearLoader(); 114 115 if (m_image) 116 m_image->removeClient(this); 117 118 ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this)); 119 if (m_hasPendingLoadEvent) 120 loadEventSender().cancelEvent(this); 121 122 ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this)); 123 if (m_hasPendingErrorEvent) 124 errorEventSender().cancelEvent(this); 125 } 126 127 void ImageLoader::trace(Visitor* visitor) 128 { 129 visitor->trace(m_element); 130 } 131 132 void ImageLoader::setImage(ImageResource* newImage) 133 { 134 setImageWithoutConsideringPendingLoadEvent(newImage); 135 136 // Only consider updating the protection ref-count of the Element immediately before returning 137 // from this function as doing so might result in the destruction of this ImageLoader. 138 updatedHasPendingEvent(); 139 } 140 141 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newImage) 142 { 143 ASSERT(m_failedLoadURL.isEmpty()); 144 ImageResource* oldImage = m_image.get(); 145 if (newImage != oldImage) { 146 sourceImageChanged(); 147 m_image = newImage; 148 if (m_hasPendingLoadEvent) { 149 loadEventSender().cancelEvent(this); 150 m_hasPendingLoadEvent = false; 151 } 152 if (m_hasPendingErrorEvent) { 153 errorEventSender().cancelEvent(this); 154 m_hasPendingErrorEvent = false; 155 } 156 m_imageComplete = true; 157 if (newImage) 158 newImage->addClient(this); 159 if (oldImage) 160 oldImage->removeClient(this); 161 } 162 163 if (RenderImageResource* imageResource = renderImageResource()) 164 imageResource->resetAnimation(); 165 } 166 167 void ImageLoader::doUpdateFromElement(bool bypassMainWorldCSP) 168 { 169 // We don't need to call clearLoader here: Either we were called from the 170 // task, or our caller updateFromElement cleared the task's loader (and set 171 // m_pendingTask to null). 172 m_pendingTask.clear(); 173 // Make sure to only decrement the count when we exit this function 174 OwnPtr<IncrementLoadEventDelayCount> delayLoad; 175 delayLoad.swap(m_delayLoad); 176 177 Document& document = m_element->document(); 178 if (!document.isActive()) 179 return; 180 181 AtomicString attr = m_element->imageSourceURL(); 182 183 KURL url = imageURL(); 184 ResourcePtr<ImageResource> newImage = 0; 185 if (!url.isNull()) { 186 FetchRequest request(ResourceRequest(url), element()->localName()); 187 if (bypassMainWorldCSP) 188 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); 189 190 AtomicString crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); 191 if (!crossOriginMode.isNull()) 192 request.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode); 193 194 if (m_loadManually) { 195 bool autoLoadOtherImages = document.fetcher()->autoLoadImages(); 196 document.fetcher()->setAutoLoadImages(false); 197 newImage = new ImageResource(request.resourceRequest()); 198 newImage->setLoading(true); 199 document.fetcher()->m_documentResources.set(newImage->url(), newImage.get()); 200 document.fetcher()->setAutoLoadImages(autoLoadOtherImages); 201 } else { 202 newImage = document.fetcher()->fetchImage(request); 203 } 204 205 // If we do not have an image here, it means that a cross-site 206 // violation occurred, or that the image was blocked via Content 207 // Security Policy, or the page is being dismissed. Trigger an 208 // error event if the page is not being dismissed. 209 if (!newImage && !pageIsBeingDismissed(&document)) { 210 m_failedLoadURL = attr; 211 m_hasPendingErrorEvent = true; 212 errorEventSender().dispatchEventSoon(this); 213 } else 214 clearFailedLoadURL(); 215 } else if (!attr.isNull()) { 216 // Fire an error event if the url is empty. 217 m_hasPendingErrorEvent = true; 218 errorEventSender().dispatchEventSoon(this); 219 } 220 221 ImageResource* oldImage = m_image.get(); 222 if (newImage != oldImage) { 223 sourceImageChanged(); 224 225 if (m_hasPendingLoadEvent) { 226 loadEventSender().cancelEvent(this); 227 m_hasPendingLoadEvent = false; 228 } 229 230 // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. 231 // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by 232 // this load and we should not cancel the event. 233 // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. 234 if (m_hasPendingErrorEvent && newImage) { 235 errorEventSender().cancelEvent(this); 236 m_hasPendingErrorEvent = false; 237 } 238 239 m_image = newImage; 240 m_hasPendingLoadEvent = newImage; 241 m_imageComplete = !newImage; 242 243 if (newImage) { 244 updateRenderer(); 245 246 // If newImage is cached, addClient() will result in the load event 247 // being queued to fire. Ensure this happens after beforeload is 248 // dispatched. 249 newImage->addClient(this); 250 } else { 251 updateRenderer(); 252 } 253 254 if (oldImage) 255 oldImage->removeClient(this); 256 } 257 258 if (RenderImageResource* imageResource = renderImageResource()) 259 imageResource->resetAnimation(); 260 261 // Only consider updating the protection ref-count of the Element immediately before returning 262 // from this function as doing so might result in the destruction of this ImageLoader. 263 updatedHasPendingEvent(); 264 } 265 266 void ImageLoader::updateFromElement(LoadType loadType) 267 { 268 AtomicString attr = m_element->imageSourceURL(); 269 270 if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL) 271 return; 272 273 // If we have a pending task, we have to clear it -- either we're 274 // now loading immediately, or we need to reset the task's state. 275 if (m_pendingTask) { 276 m_pendingTask->clearLoader(); 277 m_pendingTask.clear(); 278 } 279 280 KURL url = imageURL(); 281 if (!attr.isNull() && !url.isNull()) { 282 bool loadImmediately = shouldLoadImmediately(url) || (loadType == ForceLoadImmediately); 283 if (loadImmediately) { 284 doUpdateFromElement(false); 285 } else { 286 OwnPtr<Task> task = adoptPtr(new Task(this)); 287 m_pendingTask = task->createWeakPtr(); 288 Microtask::enqueueMicrotask(task.release()); 289 m_delayLoad = adoptPtr(new IncrementLoadEventDelayCount(m_element->document())); 290 return; 291 } 292 } else { 293 doUpdateFromElement(false); 294 } 295 } 296 297 void ImageLoader::updateFromElementIgnoringPreviousError() 298 { 299 clearFailedLoadURL(); 300 updateFromElement(); 301 } 302 303 KURL ImageLoader::imageURL() const 304 { 305 KURL url; 306 307 // Don't load images for inactive documents. We don't want to slow down the 308 // raw HTML parsing case by loading images we don't intend to display. 309 Document& document = m_element->document(); 310 if (!document.isActive()) 311 return url; 312 313 AtomicString attr = m_element->imageSourceURL(); 314 315 // Do not load any image if the 'src' attribute is missing or if it is 316 // an empty string. 317 if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { 318 url = document.completeURL(sourceURI(attr)); 319 } 320 return url; 321 } 322 323 bool ImageLoader::shouldLoadImmediately(const KURL& url) const 324 { 325 if (m_loadManually) 326 return true; 327 if (isHTMLObjectElement(m_element) || isHTMLEmbedElement(m_element)) 328 return true; 329 330 if (url.protocolIsData()) 331 return true; 332 if (memoryCache()->resourceForURL(url)) 333 return true; 334 return false; 335 } 336 337 void ImageLoader::notifyFinished(Resource* resource) 338 { 339 ASSERT(m_failedLoadURL.isEmpty()); 340 ASSERT(resource == m_image.get()); 341 342 m_imageComplete = true; 343 updateRenderer(); 344 345 if (!m_hasPendingLoadEvent) 346 return; 347 348 if (resource->errorOccurred()) { 349 loadEventSender().cancelEvent(this); 350 m_hasPendingLoadEvent = false; 351 352 m_hasPendingErrorEvent = true; 353 errorEventSender().dispatchEventSoon(this); 354 355 // Only consider updating the protection ref-count of the Element immediately before returning 356 // from this function as doing so might result in the destruction of this ImageLoader. 357 updatedHasPendingEvent(); 358 return; 359 } 360 if (resource->wasCanceled()) { 361 m_hasPendingLoadEvent = false; 362 // Only consider updating the protection ref-count of the Element immediately before returning 363 // from this function as doing so might result in the destruction of this ImageLoader. 364 updatedHasPendingEvent(); 365 return; 366 } 367 loadEventSender().dispatchEventSoon(this); 368 } 369 370 RenderImageResource* ImageLoader::renderImageResource() 371 { 372 RenderObject* renderer = m_element->renderer(); 373 374 if (!renderer) 375 return 0; 376 377 // We don't return style generated image because it doesn't belong to the ImageLoader. 378 // See <https://bugs.webkit.org/show_bug.cgi?id=42840> 379 if (renderer->isImage() && !static_cast<RenderImage*>(renderer)->isGeneratedContent()) 380 return toRenderImage(renderer)->imageResource(); 381 382 if (renderer->isSVGImage()) 383 return toRenderSVGImage(renderer)->imageResource(); 384 385 if (renderer->isVideo()) 386 return toRenderVideo(renderer)->imageResource(); 387 388 return 0; 389 } 390 391 void ImageLoader::updateRenderer() 392 { 393 RenderImageResource* imageResource = renderImageResource(); 394 395 if (!imageResource) 396 return; 397 398 // Only update the renderer if it doesn't have an image or if what we have 399 // is a complete image. This prevents flickering in the case where a dynamic 400 // change is happening between two images. 401 ImageResource* cachedImage = imageResource->cachedImage(); 402 if (m_image != cachedImage && (m_imageComplete || !cachedImage)) 403 imageResource->setImageResource(m_image.get()); 404 } 405 406 void ImageLoader::updatedHasPendingEvent() 407 { 408 // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable. 409 // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being 410 // destroyed by DOM manipulation or garbage collection. 411 // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly. 412 bool wasProtected = m_elementIsProtected; 413 m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent; 414 if (wasProtected == m_elementIsProtected) 415 return; 416 417 if (m_elementIsProtected) { 418 if (m_derefElementTimer.isActive()) 419 m_derefElementTimer.stop(); 420 else 421 m_keepAlive = m_element; 422 } else { 423 ASSERT(!m_derefElementTimer.isActive()); 424 m_derefElementTimer.startOneShot(0, FROM_HERE); 425 } 426 } 427 428 void ImageLoader::timerFired(Timer<ImageLoader>*) 429 { 430 m_keepAlive.clear(); 431 } 432 433 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) 434 { 435 ASSERT(eventSender == &loadEventSender() || eventSender == &errorEventSender()); 436 const AtomicString& eventType = eventSender->eventType(); 437 if (eventType == EventTypeNames::load) 438 dispatchPendingLoadEvent(); 439 if (eventType == EventTypeNames::error) 440 dispatchPendingErrorEvent(); 441 } 442 443 void ImageLoader::dispatchPendingLoadEvent() 444 { 445 if (!m_hasPendingLoadEvent) 446 return; 447 if (!m_image) 448 return; 449 m_hasPendingLoadEvent = false; 450 if (element()->document().frame()) 451 dispatchLoadEvent(); 452 453 // Only consider updating the protection ref-count of the Element immediately before returning 454 // from this function as doing so might result in the destruction of this ImageLoader. 455 updatedHasPendingEvent(); 456 } 457 458 void ImageLoader::dispatchPendingErrorEvent() 459 { 460 if (!m_hasPendingErrorEvent) 461 return; 462 m_hasPendingErrorEvent = false; 463 464 if (element()->document().frame()) 465 element()->dispatchEvent(Event::create(EventTypeNames::error)); 466 467 // Only consider updating the protection ref-count of the Element immediately before returning 468 // from this function as doing so might result in the destruction of this ImageLoader. 469 updatedHasPendingEvent(); 470 } 471 472 void ImageLoader::addClient(ImageLoaderClient* client) 473 { 474 if (client->requestsHighLiveResourceCachePriority()) { 475 if (m_image && !m_highPriorityClientCount++) 476 memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityHigh); 477 } 478 #if ENABLE(OILPAN) 479 m_clients.add(client, adoptPtr(new ImageLoaderClientRemover(*this, *client))); 480 #else 481 m_clients.add(client); 482 #endif 483 } 484 485 void ImageLoader::willRemoveClient(ImageLoaderClient& client) 486 { 487 if (client.requestsHighLiveResourceCachePriority()) { 488 ASSERT(m_highPriorityClientCount); 489 m_highPriorityClientCount--; 490 if (m_image && !m_highPriorityClientCount) 491 memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityLow); 492 } 493 } 494 495 void ImageLoader::removeClient(ImageLoaderClient* client) 496 { 497 willRemoveClient(*client); 498 m_clients.remove(client); 499 } 500 501 void ImageLoader::dispatchPendingLoadEvents() 502 { 503 loadEventSender().dispatchPendingEvents(); 504 } 505 506 void ImageLoader::dispatchPendingErrorEvents() 507 { 508 errorEventSender().dispatchPendingEvents(); 509 } 510 511 void ImageLoader::elementDidMoveToNewDocument() 512 { 513 if (m_delayLoad) { 514 m_delayLoad->documentChanged(m_element->document()); 515 } 516 clearFailedLoadURL(); 517 setImage(0); 518 } 519 520 void ImageLoader::sourceImageChanged() 521 { 522 #if ENABLE(OILPAN) 523 PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator end = m_clients.end(); 524 for (PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator it = m_clients.begin(); it != end; ++it) { 525 it->key->notifyImageSourceChanged(); 526 } 527 #else 528 HashSet<ImageLoaderClient*>::iterator end = m_clients.end(); 529 for (HashSet<ImageLoaderClient*>::iterator it = m_clients.begin(); it != end; ++it) { 530 ImageLoaderClient* handle = *it; 531 handle->notifyImageSourceChanged(); 532 } 533 #endif 534 } 535 536 inline void ImageLoader::clearFailedLoadURL() 537 { 538 m_failedLoadURL = AtomicString(); 539 } 540 541 #if ENABLE(OILPAN) 542 ImageLoader::ImageLoaderClientRemover::~ImageLoaderClientRemover() 543 { 544 m_loader.willRemoveClient(m_client); 545 } 546 #endif 547 548 } 549