summaryrefslogtreecommitdiffstats
path: root/chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp')
-rw-r--r--chromium/third_party/WebKit/Source/core/loader/ImageLoader.cpp270
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
+
}