| 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);
|
| }
|
|
|