diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp | 270 |
1 files changed, 167 insertions, 103 deletions
diff --git a/chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp b/chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp index a8177ba48d5..fd13c3f47eb 100644 --- a/chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp +++ b/chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp @@ -22,15 +22,19 @@ #include "config.h" #include "core/loader/ImageLoader.h" -#include "HTMLNames.h" +#include "bindings/v8/ScriptController.h" #include "core/dom/Document.h" #include "core/dom/Element.h" +#include "core/dom/IncrementLoadEventDelayCount.h" +#include "core/dom/Microtask.h" #include "core/events/Event.h" #include "core/events/EventSender.h" #include "core/fetch/CrossOriginAccessControl.h" #include "core/fetch/FetchRequest.h" +#include "core/fetch/MemoryCache.h" #include "core/fetch/ResourceFetcher.h" -#include "core/html/HTMLObjectElement.h" +#include "core/frame/LocalFrame.h" +#include "core/html/HTMLImageElement.h" #include "core/html/parser/HTMLParserIdioms.h" #include "core/rendering/RenderImage.h" #include "core/rendering/RenderVideo.h" @@ -39,12 +43,6 @@ namespace WebCore { -static ImageEventSender& beforeLoadEventSender() -{ - DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::beforeload)); - return sender; -} - static ImageEventSender& loadEventSender() { DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::load)); @@ -62,11 +60,44 @@ static inline bool pageIsBeingDismissed(Document* document) return document->pageDismissalEventBeingDispatched() != Document::NoDismissal; } +class ImageLoader::Task : public blink::WebThread::Task { +public: + Task(ImageLoader* loader) + : m_loader(loader) + , m_shouldBypassMainWorldContentSecurityPolicy(false) + , m_weakFactory(this) + { + LocalFrame* frame = loader->m_element->document().frame(); + m_shouldBypassMainWorldContentSecurityPolicy = frame->script().shouldBypassMainWorldContentSecurityPolicy(); + } + + virtual void run() OVERRIDE + { + if (m_loader) { + m_loader->doUpdateFromElement(m_shouldBypassMainWorldContentSecurityPolicy); + } + } + + void clearLoader() + { + m_loader = 0; + } + + WeakPtr<Task> createWeakPtr() + { + return m_weakFactory.createWeakPtr(); + } + +private: + ImageLoader* m_loader; + bool m_shouldBypassMainWorldContentSecurityPolicy; + WeakPtrFactory<Task> m_weakFactory; +}; + ImageLoader::ImageLoader(Element* element) : m_element(element) , m_image(0) , m_derefElementTimer(this, &ImageLoader::timerFired) - , m_hasPendingBeforeLoadEvent(false) , m_hasPendingLoadEvent(false) , m_hasPendingErrorEvent(false) , m_imageComplete(true) @@ -78,13 +109,12 @@ ImageLoader::ImageLoader(Element* element) ImageLoader::~ImageLoader() { + if (m_pendingTask) + m_pendingTask->clearLoader(); + if (m_image) m_image->removeClient(this); - ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(this)); - if (m_hasPendingBeforeLoadEvent) - beforeLoadEventSender().cancelEvent(this); - ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this)); if (m_hasPendingLoadEvent) loadEventSender().cancelEvent(this); @@ -92,11 +122,11 @@ ImageLoader::~ImageLoader() ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this)); if (m_hasPendingErrorEvent) errorEventSender().cancelEvent(this); +} - // If the ImageLoader is being destroyed but it is still protecting its image-loading Element, - // remove that protection here. - if (m_elementIsProtected) - m_element->deref(); +void ImageLoader::trace(Visitor* visitor) +{ + visitor->trace(m_element); } void ImageLoader::setImage(ImageResource* newImage) @@ -115,10 +145,6 @@ void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newI if (newImage != oldImage) { sourceImageChanged(); m_image = newImage; - if (m_hasPendingBeforeLoadEvent) { - beforeLoadEventSender().cancelEvent(this); - m_hasPendingBeforeLoadEvent = false; - } if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; @@ -138,30 +164,32 @@ void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newI imageResource->resetAnimation(); } -void ImageLoader::updateFromElement() +void ImageLoader::doUpdateFromElement(bool bypassMainWorldCSP) { - // Don't load images for inactive documents. We don't want to slow down the - // raw HTML parsing case by loading images we don't intend to display. + // We don't need to call clearLoader here: Either we were called from the + // task, or our caller updateFromElement cleared the task's loader (and set + // m_pendingTask to null). + m_pendingTask.clear(); + // Make sure to only decrement the count when we exit this function + OwnPtr<IncrementLoadEventDelayCount> delayLoad; + delayLoad.swap(m_delayLoad); + Document& document = m_element->document(); if (!document.isActive()) return; AtomicString attr = m_element->imageSourceURL(); - if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL) - return; - - // Do not load any image if the 'src' attribute is missing or if it is - // an empty string. + KURL url = imageURL(); ResourcePtr<ImageResource> newImage = 0; - if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { - FetchRequest request(ResourceRequest(document.completeURL(sourceURI(attr))), element()->localName()); + if (!url.isNull()) { + FetchRequest request(ResourceRequest(url), element()->localName()); + if (bypassMainWorldCSP) + request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); AtomicString crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr); - if (!crossOriginMode.isNull()) { - StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials; - updateRequestForAccessControl(request.mutableResourceRequest(), document.securityOrigin(), allowCredentials); - } + if (!crossOriginMode.isNull()) + request.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode); if (m_loadManually) { bool autoLoadOtherImages = document.fetcher()->autoLoadImages(); @@ -194,10 +222,6 @@ void ImageLoader::updateFromElement() if (newImage != oldImage) { sourceImageChanged(); - if (m_hasPendingBeforeLoadEvent) { - beforeLoadEventSender().cancelEvent(this); - m_hasPendingBeforeLoadEvent = false; - } if (m_hasPendingLoadEvent) { loadEventSender().cancelEvent(this); m_hasPendingLoadEvent = false; @@ -213,18 +237,11 @@ void ImageLoader::updateFromElement() } m_image = newImage; - m_hasPendingBeforeLoadEvent = !m_element->document().isImageDocument() && newImage; m_hasPendingLoadEvent = newImage; m_imageComplete = !newImage; if (newImage) { - if (!m_element->document().isImageDocument()) { - if (!m_element->document().hasListenerType(Document::BEFORELOAD_LISTENER)) - dispatchPendingBeforeLoadEvent(); - else - beforeLoadEventSender().dispatchEventSoon(this); - } else - updateRenderer(); + updateRenderer(); // If newImage is cached, addClient() will result in the load event // being queued to fire. Ensure this happens after beforeload is @@ -246,44 +263,100 @@ void ImageLoader::updateFromElement() updatedHasPendingEvent(); } +void ImageLoader::updateFromElement(LoadType loadType) +{ + AtomicString attr = m_element->imageSourceURL(); + + if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL) + return; + + // If we have a pending task, we have to clear it -- either we're + // now loading immediately, or we need to reset the task's state. + if (m_pendingTask) { + m_pendingTask->clearLoader(); + m_pendingTask.clear(); + } + + KURL url = imageURL(); + if (!attr.isNull() && !url.isNull()) { + bool loadImmediately = shouldLoadImmediately(url) || (loadType == ForceLoadImmediately); + if (loadImmediately) { + doUpdateFromElement(false); + } else { + OwnPtr<Task> task = adoptPtr(new Task(this)); + m_pendingTask = task->createWeakPtr(); + Microtask::enqueueMicrotask(task.release()); + m_delayLoad = adoptPtr(new IncrementLoadEventDelayCount(m_element->document())); + return; + } + } else { + doUpdateFromElement(false); + } +} + void ImageLoader::updateFromElementIgnoringPreviousError() { clearFailedLoadURL(); updateFromElement(); } +KURL ImageLoader::imageURL() const +{ + KURL url; + + // Don't load images for inactive documents. We don't want to slow down the + // raw HTML parsing case by loading images we don't intend to display. + Document& document = m_element->document(); + if (!document.isActive()) + return url; + + AtomicString attr = m_element->imageSourceURL(); + + // Do not load any image if the 'src' attribute is missing or if it is + // an empty string. + if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) { + url = document.completeURL(sourceURI(attr)); + } + return url; +} + +bool ImageLoader::shouldLoadImmediately(const KURL& url) const +{ + if (m_loadManually) + return true; + if (isHTMLObjectElement(m_element) || isHTMLEmbedElement(m_element)) + return true; + + if (url.protocolIsData()) + return true; + if (memoryCache()->resourceForURL(url)) + return true; + return false; +} + void ImageLoader::notifyFinished(Resource* resource) { ASSERT(m_failedLoadURL.isEmpty()); ASSERT(resource == m_image.get()); m_imageComplete = true; - if (!hasPendingBeforeLoadEvent()) - updateRenderer(); + updateRenderer(); if (!m_hasPendingLoadEvent) return; - if (m_element->fastHasAttribute(HTMLNames::crossoriginAttr) - && !m_element->document().securityOrigin()->canRequest(image()->response().url()) - && !resource->passesAccessControlCheck(m_element->document().securityOrigin())) { - - setImageWithoutConsideringPendingLoadEvent(0); + if (resource->errorOccurred()) { + loadEventSender().cancelEvent(this); + m_hasPendingLoadEvent = false; m_hasPendingErrorEvent = true; errorEventSender().dispatchEventSoon(this); - DEFINE_STATIC_LOCAL(String, consoleMessage, ("Cross-origin image load denied by Cross-Origin Resource Sharing policy.")); - m_element->document().addConsoleMessage(SecurityMessageSource, ErrorMessageLevel, consoleMessage); - - ASSERT(!m_hasPendingLoadEvent); - // Only consider updating the protection ref-count of the Element immediately before returning // from this function as doing so might result in the destruction of this ImageLoader. updatedHasPendingEvent(); return; } - if (resource->wasCanceled()) { m_hasPendingLoadEvent = false; // Only consider updating the protection ref-count of the Element immediately before returning @@ -291,7 +364,6 @@ void ImageLoader::notifyFinished(Resource* resource) updatedHasPendingEvent(); return; } - loadEventSender().dispatchEventSoon(this); } @@ -346,59 +418,28 @@ void ImageLoader::updatedHasPendingEvent() if (m_derefElementTimer.isActive()) m_derefElementTimer.stop(); else - m_element->ref(); + m_keepAlive = m_element; } else { ASSERT(!m_derefElementTimer.isActive()); - m_derefElementTimer.startOneShot(0); + m_derefElementTimer.startOneShot(0, FROM_HERE); } } void ImageLoader::timerFired(Timer<ImageLoader>*) { - m_element->deref(); + m_keepAlive.clear(); } void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) { - ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender()); + ASSERT(eventSender == &loadEventSender() || eventSender == &errorEventSender()); const AtomicString& eventType = eventSender->eventType(); - if (eventType == EventTypeNames::beforeload) - dispatchPendingBeforeLoadEvent(); if (eventType == EventTypeNames::load) dispatchPendingLoadEvent(); if (eventType == EventTypeNames::error) dispatchPendingErrorEvent(); } -void ImageLoader::dispatchPendingBeforeLoadEvent() -{ - if (!m_hasPendingBeforeLoadEvent) - return; - if (!m_image) - return; - if (!m_element->document().frame()) - return; - m_hasPendingBeforeLoadEvent = false; - if (m_element->dispatchBeforeLoadEvent(m_image->url().string())) { - updateRenderer(); - return; - } - if (m_image) { - m_image->removeClient(this); - m_image = 0; - } - - loadEventSender().cancelEvent(this); - m_hasPendingLoadEvent = false; - - if (m_element->hasTagName(HTMLNames::objectTag)) - toHTMLObjectElement(m_element)->renderFallbackContent(); - - // Only consider updating the protection ref-count of the Element immediately before returning - // from this function as doing so might result in the destruction of this ImageLoader. - updatedHasPendingEvent(); -} - void ImageLoader::dispatchPendingLoadEvent() { if (!m_hasPendingLoadEvent) @@ -419,6 +460,7 @@ void ImageLoader::dispatchPendingErrorEvent() if (!m_hasPendingErrorEvent) return; m_hasPendingErrorEvent = false; + if (element()->document().frame()) element()->dispatchEvent(Event::create(EventTypeNames::error)); @@ -431,24 +473,29 @@ void ImageLoader::addClient(ImageLoaderClient* client) { if (client->requestsHighLiveResourceCachePriority()) { if (m_image && !m_highPriorityClientCount++) - m_image->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh); + memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityHigh); } +#if ENABLE(OILPAN) + m_clients.add(client, adoptPtr(new ImageLoaderClientRemover(*this, *client))); +#else m_clients.add(client); +#endif } -void ImageLoader::removeClient(ImageLoaderClient* client) + +void ImageLoader::willRemoveClient(ImageLoaderClient& client) { - if (client->requestsHighLiveResourceCachePriority()) { + if (client.requestsHighLiveResourceCachePriority()) { ASSERT(m_highPriorityClientCount); m_highPriorityClientCount--; if (m_image && !m_highPriorityClientCount) - m_image->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow); + memoryCache()->updateDecodedResource(m_image.get(), UpdateForPropertyChange, MemoryCacheLiveResourcePriorityLow); } - m_clients.remove(client); } -void ImageLoader::dispatchPendingBeforeLoadEvents() +void ImageLoader::removeClient(ImageLoaderClient* client) { - beforeLoadEventSender().dispatchPendingEvents(); + willRemoveClient(*client); + m_clients.remove(client); } void ImageLoader::dispatchPendingLoadEvents() @@ -463,17 +510,27 @@ void ImageLoader::dispatchPendingErrorEvents() void ImageLoader::elementDidMoveToNewDocument() { + if (m_delayLoad) { + m_delayLoad->documentChanged(m_element->document()); + } clearFailedLoadURL(); setImage(0); } void ImageLoader::sourceImageChanged() { +#if ENABLE(OILPAN) + PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator end = m_clients.end(); + for (PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> >::iterator it = m_clients.begin(); it != end; ++it) { + it->key->notifyImageSourceChanged(); + } +#else HashSet<ImageLoaderClient*>::iterator end = m_clients.end(); for (HashSet<ImageLoaderClient*>::iterator it = m_clients.begin(); it != end; ++it) { ImageLoaderClient* handle = *it; handle->notifyImageSourceChanged(); } +#endif } inline void ImageLoader::clearFailedLoadURL() @@ -481,4 +538,11 @@ inline void ImageLoader::clearFailedLoadURL() m_failedLoadURL = AtomicString(); } +#if ENABLE(OILPAN) +ImageLoader::ImageLoaderClientRemover::~ImageLoaderClientRemover() +{ + m_loader.willRemoveClient(m_client); +} +#endif + } |