Index: third_party/WebKit/Source/core/loader/ImageLoader.cpp |
diff --git a/third_party/WebKit/Source/core/loader/ImageLoader.cpp b/third_party/WebKit/Source/core/loader/ImageLoader.cpp |
index 73e2f1916e24f1d0e1af09c5b445cbf6158a5b25..9df7ede1a445b78dde02cdef5b0b94e25b5fa52e 100644 |
--- a/third_party/WebKit/Source/core/loader/ImageLoader.cpp |
+++ b/third_party/WebKit/Source/core/loader/ImageLoader.cpp |
@@ -30,7 +30,6 @@ |
#include "core/dom/Element.h" |
#include "core/dom/IncrementLoadEventDelayCount.h" |
#include "core/events/Event.h" |
-#include "core/events/EventSender.h" |
#include "core/fetch/FetchRequest.h" |
#include "core/fetch/MemoryCache.h" |
#include "core/fetch/ResourceFetcher.h" |
@@ -48,22 +47,13 @@ |
#include "platform/Logging.h" |
#include "platform/weborigin/SecurityOrigin.h" |
#include "platform/weborigin/SecurityPolicy.h" |
+#include "public/platform/Platform.h" |
+#include "public/platform/WebTaskRunner.h" |
+#include "public/platform/WebTraceLocation.h" |
#include "public/platform/WebURLRequest.h" |
namespace blink { |
-static ImageEventSender& loadEventSender() |
-{ |
- DEFINE_STATIC_LOCAL(ImageEventSender, sender, (ImageEventSender::create(EventTypeNames::load))); |
- return sender; |
-} |
- |
-static ImageEventSender& errorEventSender() |
-{ |
- DEFINE_STATIC_LOCAL(ImageEventSender, sender, (ImageEventSender::create(EventTypeNames::error))); |
- return sender; |
-} |
- |
static inline bool pageIsBeingDismissed(Document* document) |
{ |
return document->pageDismissalEventBeingDispatched() != Document::NoDismissal; |
@@ -149,8 +139,9 @@ private: |
ImageLoader::ImageLoader(Element* element) |
: m_element(element) |
, m_derefElementTimer(this, &ImageLoader::timerFired) |
+ , m_finishTask(CancellableTaskFactory::create(this, &ImageLoader::finish)) |
+ , m_dispatchErrorEventTask(CancellableTaskFactory::create(this, &ImageLoader::dispatchErrorEvent)) |
, m_hasPendingLoadEvent(false) |
- , m_hasPendingErrorEvent(false) |
, m_imageComplete(true) |
, m_loadingImageDocument(false) |
, m_elementIsProtected(false) |
@@ -169,30 +160,39 @@ ImageLoader::~ImageLoader() |
#endif |
} |
+void ImageLoader::cancelPendingFinish() |
+{ |
+ if (m_loadDelayCounterForFinish) { |
+ m_finishTask->cancel(); |
+ m_loadDelayCounterForFinish.clear(); |
+ } |
+ m_hasPendingLoadEvent = false; |
+} |
+ |
+void ImageLoader::cancelPendingErrorEvent() |
+{ |
+ if (m_loadDelayCounterForError) { |
+ m_dispatchErrorEventTask->cancel(); |
+ m_loadDelayCounterForError.clear(); |
+ } |
+} |
+ |
void ImageLoader::dispose() |
{ |
- WTF_LOG(Timers, "~ImageLoader %p; m_hasPendingLoadEvent=%d, m_hasPendingErrorEvent=%d", |
- this, m_hasPendingLoadEvent, m_hasPendingErrorEvent); |
+ WTF_LOG(Timers, "~ImageLoader %p; m_hasPendingLoadEvent=%d, hasPendingErrorEvent=%d", |
+ this, m_hasPendingLoadEvent, hasPendingError()); |
#if !ENABLE(OILPAN) |
- if (m_pendingTask) |
- m_pendingTask->clearLoader(); |
+ if (m_pendingMicrotask) |
+ m_pendingMicrotask->clearLoader(); |
#endif |
if (m_image) { |
m_image->removeObserver(this); |
m_image = nullptr; |
} |
- |
-#if !ENABLE(OILPAN) |
- ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this)); |
- if (m_hasPendingLoadEvent) |
- loadEventSender().cancelEvent(this); |
- |
- ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this)); |
- if (m_hasPendingErrorEvent) |
- errorEventSender().cancelEvent(this); |
-#endif |
+ cancelPendingFinish(); |
+ cancelPendingErrorEvent(); |
} |
DEFINE_TRACE(ImageLoader) |
@@ -216,14 +216,8 @@ void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newI |
ImageResource* oldImage = m_image.get(); |
if (newImage != oldImage) { |
m_image = newImage; |
- if (m_hasPendingLoadEvent) { |
- loadEventSender().cancelEvent(this); |
- m_hasPendingLoadEvent = false; |
- } |
- if (m_hasPendingErrorEvent) { |
- errorEventSender().cancelEvent(this); |
- m_hasPendingErrorEvent = false; |
- } |
+ cancelPendingFinish(); |
+ cancelPendingErrorEvent(); |
m_imageComplete = true; |
if (newImage) { |
newImage->addObserver(this); |
@@ -250,10 +244,10 @@ static void configureRequest(FetchRequest& request, ImageLoader::BypassMainWorld |
request.setResourceWidth(toHTMLImageElement(element).getResourceWidth()); |
} |
-inline void ImageLoader::dispatchErrorEvent() |
+inline void ImageLoader::scheduleErrorEvent() |
{ |
- m_hasPendingErrorEvent = true; |
- errorEventSender().dispatchEventSoon(this); |
+ m_loadDelayCounterForError = IncrementLoadEventDelayCount::create(m_element->document()); |
+ Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, m_dispatchErrorEventTask->cancelAndCreate()); |
} |
inline void ImageLoader::crossSiteOrCSPViolationOccurred(AtomicString imageSourceURL) |
@@ -269,9 +263,9 @@ inline void ImageLoader::clearFailedLoadURL() |
inline void ImageLoader::enqueueImageLoadingMicroTask(UpdateFromElementBehavior updateBehavior, ReferrerPolicy referrerPolicy) |
{ |
OwnPtr<Task> task = Task::create(this, updateBehavior, referrerPolicy); |
- m_pendingTask = task->createWeakPtr(); |
+ m_pendingMicrotask = task->createWeakPtr(); |
Microtask::enqueueMicrotask(task.release()); |
- m_loadDelayCounter = IncrementLoadEventDelayCount::create(m_element->document()); |
+ m_loadDelayCounterForMicrotask = IncrementLoadEventDelayCount::create(m_element->document()); |
} |
void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, UpdateFromElementBehavior updateBehavior, ReferrerPolicy referrerPolicy) |
@@ -283,11 +277,11 @@ void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, Up |
// |
// 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(); |
+ // m_pendingMicrotask to null). |
+ m_pendingMicrotask.clear(); |
// Make sure to only decrement the count when we exit this function |
OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter; |
- loadDelayCounter.swap(m_loadDelayCounter); |
+ loadDelayCounter.swap(m_loadDelayCounterForMicrotask); |
Document& document = m_element->document(); |
if (!document.isActive()) |
@@ -332,14 +326,14 @@ void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, Up |
if (!newImage && !pageIsBeingDismissed(&document)) { |
crossSiteOrCSPViolationOccurred(imageSourceURL); |
- dispatchErrorEvent(); |
+ scheduleErrorEvent(); |
} else { |
clearFailedLoadURL(); |
} |
} else { |
if (!imageSourceURL.isNull()) { |
// Fire an error event if the url string is not empty, but the KURL is. |
- dispatchErrorEvent(); |
+ scheduleErrorEvent(); |
} |
noImageResourceToLoad(); |
} |
@@ -348,19 +342,14 @@ void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, Up |
if (updateBehavior == UpdateSizeChanged && m_element->layoutObject() && m_element->layoutObject()->isImage() && newImage == oldImage) { |
toLayoutImage(m_element->layoutObject())->intrinsicSizeChanged(); |
} else { |
- if (m_hasPendingLoadEvent) { |
- loadEventSender().cancelEvent(this); |
- m_hasPendingLoadEvent = false; |
- } |
+ cancelPendingFinish(); |
// Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute. |
- // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by |
+ // If newImage is null and hasPendingError() is true, we know the error event has been just posted by |
// this load and we should not cancel the event. |
// FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two. |
- if (m_hasPendingErrorEvent && newImage) { |
- errorEventSender().cancelEvent(this); |
- m_hasPendingErrorEvent = false; |
- } |
+ if (newImage) |
+ cancelPendingErrorEvent(); |
m_image = newImage; |
m_hasPendingLoadEvent = newImage; |
@@ -398,9 +387,9 @@ void ImageLoader::updateFromElement(UpdateFromElementBehavior updateBehavior, Re |
// 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(); |
+ if (m_pendingMicrotask) { |
+ m_pendingMicrotask->clearLoader(); |
+ m_pendingMicrotask.clear(); |
} |
KURL url = imageSourceToKURL(imageSourceURL); |
@@ -416,6 +405,8 @@ void ImageLoader::updateFromElement(UpdateFromElementBehavior updateBehavior, Re |
image->removeObserver(this); |
} |
m_image = nullptr; |
+ cancelPendingFinish(); |
+ cancelPendingErrorEvent(); |
} |
// Don't load images for inactive documents. We don't want to slow down the |
@@ -465,45 +456,56 @@ void ImageLoader::imageNotifyFinished(ImageResource* resource) |
ASSERT(m_failedLoadURL.isEmpty()); |
ASSERT(resource == m_image.get()); |
+ m_loadDelayCounterForFinish = IncrementLoadEventDelayCount::create(m_element->document()); |
+ Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FROM_HERE, m_finishTask->cancelAndCreate()); |
+} |
+ |
+void ImageLoader::finish() |
+{ |
m_imageComplete = true; |
+ ImageResource* imageResource = m_image.get(); |
+ ASSERT(imageResource); |
- // Update ImageAnimationPolicy for m_image. |
- if (m_image) |
- m_image->updateImageAnimationPolicy(); |
+ // Update ImageAnimationPolicy for |imageResource|. |
+ if (imageResource) |
+ imageResource->updateImageAnimationPolicy(); |
updateLayoutObject(); |
- if (m_image && m_image->getImage() && m_image->getImage()->isSVGImage()) |
- toSVGImage(m_image->getImage())->updateUseCounters(element()->document()); |
+ if (imageResource && imageResource->getImage() && imageResource->getImage()->isSVGImage()) |
+ toSVGImage(imageResource->getImage())->updateUseCounters(element()->document()); |
- if (!m_hasPendingLoadEvent) |
- return; |
+ finishInternal(imageResource); |
- if (resource->errorOccurred()) { |
- loadEventSender().cancelEvent(this); |
+ // Dispatches image's load/error event. |
+ if (m_hasPendingLoadEvent) { |
m_hasPendingLoadEvent = false; |
- if (resource->resourceError().isAccessCheck()) |
- crossSiteOrCSPViolationOccurred(AtomicString(resource->resourceError().failingURL())); |
+ if (imageResource->errorOccurred()) { |
+ if (imageResource->resourceError().isAccessCheck()) |
+ crossSiteOrCSPViolationOccurred(AtomicString(imageResource->resourceError().failingURL())); |
+ |
+ // The error event should not fire if the image data update is a result of environment change. |
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element:the-img-element-55 |
+ if (!m_suppressErrorEvents) |
+ dispatchErrorEventWithoutUpdatedHasPendingEvent(); |
+ } else if (!imageResource->wasCanceled()) { |
+ if (element()->document().frame()) |
+ dispatchLoadEvent(); |
+ } |
+ } |
- // The error event should not fire if the image data update is a result of environment change. |
- // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-element:the-img-element-55 |
- if (!m_suppressErrorEvents) |
- dispatchErrorEvent(); |
+ // Dispatches document's load event if applicable. |
+ // Calling checkCompleted() and document load event synchronously here has |
+ // no problem regarding reentrancy because ImageLoader::finish() is |
+ // always executed as a CancellableTask and not called by others. |
+ m_loadDelayCounterForFinish.clear(); |
+ if (element()->document().frame()) |
+ element()->document().frame()->loader().checkCompleted(); |
- // 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 |
- // from this function as doing so might result in the destruction of this ImageLoader. |
- updatedHasPendingEvent(); |
- return; |
- } |
- loadEventSender().dispatchEventSoon(this); |
+ // 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(); |
} |
LayoutImageResource* ImageLoader::layoutImageResource() |
@@ -549,7 +551,7 @@ void ImageLoader::updatedHasPendingEvent() |
// destroyed by DOM manipulation or garbage collection. |
// If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly. |
bool wasProtected = m_elementIsProtected; |
- m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent; |
+ m_elementIsProtected = m_hasPendingLoadEvent || hasPendingError(); |
if (wasProtected == m_elementIsProtected) |
return; |
@@ -569,40 +571,16 @@ void ImageLoader::timerFired(Timer<ImageLoader>*) |
m_keepAlive.clear(); |
} |
-void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) |
+void ImageLoader::dispatchErrorEventWithoutUpdatedHasPendingEvent() |
{ |
- WTF_LOG(Timers, "ImageLoader::dispatchPendingEvent %p", this); |
- ASSERT(eventSender == &loadEventSender() || eventSender == &errorEventSender()); |
- const AtomicString& eventType = eventSender->eventType(); |
- if (eventType == EventTypeNames::load) |
- dispatchPendingLoadEvent(); |
- if (eventType == EventTypeNames::error) |
- dispatchPendingErrorEvent(); |
-} |
- |
-void ImageLoader::dispatchPendingLoadEvent() |
-{ |
- if (!m_hasPendingLoadEvent) |
- return; |
- if (!m_image) |
- return; |
- m_hasPendingLoadEvent = false; |
if (element()->document().frame()) |
- dispatchLoadEvent(); |
- |
- // 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(); |
+ element()->dispatchEvent(Event::create(EventTypeNames::error)); |
+ m_loadDelayCounterForError.clear(); |
} |
-void ImageLoader::dispatchPendingErrorEvent() |
+void ImageLoader::dispatchErrorEvent() |
{ |
- if (!m_hasPendingErrorEvent) |
- return; |
- m_hasPendingErrorEvent = false; |
- |
- if (element()->document().frame()) |
- element()->dispatchEvent(Event::create(EventTypeNames::error)); |
+ dispatchErrorEventWithoutUpdatedHasPendingEvent(); |
// 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. |
@@ -618,20 +596,14 @@ bool ImageLoader::getImageAnimationPolicy(ImageAnimationPolicy& policy) |
return true; |
} |
-void ImageLoader::dispatchPendingLoadEvents() |
-{ |
- loadEventSender().dispatchPendingEvents(); |
-} |
- |
-void ImageLoader::dispatchPendingErrorEvents() |
-{ |
- errorEventSender().dispatchPendingEvents(); |
-} |
- |
void ImageLoader::elementDidMoveToNewDocument() |
{ |
- if (m_loadDelayCounter) |
- m_loadDelayCounter->documentChanged(m_element->document()); |
+ if (m_loadDelayCounterForMicrotask) |
+ m_loadDelayCounterForMicrotask->documentChanged(m_element->document()); |
+ if (m_loadDelayCounterForFinish) |
+ m_loadDelayCounterForFinish->documentChanged(m_element->document()); |
+ if (m_loadDelayCounterForError) |
+ m_loadDelayCounterForError->documentChanged(m_element->document()); |
clearFailedLoadURL(); |
setImage(0); |
} |