| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) | 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
| 4 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserv
ed. | 4 * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserv
ed. |
| 5 * | 5 * |
| 6 * This library is free software; you can redistribute it and/or | 6 * This library is free software; you can redistribute it and/or |
| 7 * modify it under the terms of the GNU Library General Public | 7 * modify it under the terms of the GNU Library General Public |
| 8 * License as published by the Free Software Foundation; either | 8 * License as published by the Free Software Foundation; either |
| 9 * version 2 of the License, or (at your option) any later version. | 9 * version 2 of the License, or (at your option) any later version. |
| 10 * | 10 * |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 | 23 |
| 24 #include "bindings/core/v8/Microtask.h" | 24 #include "bindings/core/v8/Microtask.h" |
| 25 #include "bindings/core/v8/ScriptController.h" | 25 #include "bindings/core/v8/ScriptController.h" |
| 26 #include "bindings/core/v8/ScriptState.h" | 26 #include "bindings/core/v8/ScriptState.h" |
| 27 #include "bindings/core/v8/V8Binding.h" | 27 #include "bindings/core/v8/V8Binding.h" |
| 28 #include "bindings/core/v8/V8PerIsolateData.h" | 28 #include "bindings/core/v8/V8PerIsolateData.h" |
| 29 #include "core/dom/Document.h" | 29 #include "core/dom/Document.h" |
| 30 #include "core/dom/Element.h" | 30 #include "core/dom/Element.h" |
| 31 #include "core/dom/IncrementLoadEventDelayCount.h" | 31 #include "core/dom/IncrementLoadEventDelayCount.h" |
| 32 #include "core/events/Event.h" | 32 #include "core/events/Event.h" |
| 33 #include "core/events/EventSender.h" | |
| 34 #include "core/fetch/FetchRequest.h" | 33 #include "core/fetch/FetchRequest.h" |
| 35 #include "core/fetch/MemoryCache.h" | 34 #include "core/fetch/MemoryCache.h" |
| 36 #include "core/fetch/ResourceFetcher.h" | 35 #include "core/fetch/ResourceFetcher.h" |
| 37 #include "core/frame/LocalFrame.h" | 36 #include "core/frame/LocalFrame.h" |
| 38 #include "core/frame/Settings.h" | 37 #include "core/frame/Settings.h" |
| 39 #include "core/frame/UseCounter.h" | 38 #include "core/frame/UseCounter.h" |
| 40 #include "core/html/CrossOriginAttribute.h" | 39 #include "core/html/CrossOriginAttribute.h" |
| 41 #include "core/html/HTMLImageElement.h" | 40 #include "core/html/HTMLImageElement.h" |
| 42 #include "core/html/parser/HTMLParserIdioms.h" | 41 #include "core/html/parser/HTMLParserIdioms.h" |
| 43 #include "core/inspector/InspectorInstrumentation.h" | 42 #include "core/inspector/InspectorInstrumentation.h" |
| 44 #include "core/layout/LayoutImage.h" | 43 #include "core/layout/LayoutImage.h" |
| 45 #include "core/layout/LayoutVideo.h" | 44 #include "core/layout/LayoutVideo.h" |
| 46 #include "core/layout/svg/LayoutSVGImage.h" | 45 #include "core/layout/svg/LayoutSVGImage.h" |
| 47 #include "core/svg/graphics/SVGImage.h" | 46 #include "core/svg/graphics/SVGImage.h" |
| 48 #include "platform/Logging.h" | 47 #include "platform/Logging.h" |
| 49 #include "platform/weborigin/SecurityOrigin.h" | 48 #include "platform/weborigin/SecurityOrigin.h" |
| 50 #include "platform/weborigin/SecurityPolicy.h" | 49 #include "platform/weborigin/SecurityPolicy.h" |
| 50 #include "public/platform/Platform.h" |
| 51 #include "public/platform/WebTaskRunner.h" |
| 52 #include "public/platform/WebTraceLocation.h" |
| 51 #include "public/platform/WebURLRequest.h" | 53 #include "public/platform/WebURLRequest.h" |
| 52 | 54 |
| 53 namespace blink { | 55 namespace blink { |
| 54 | 56 |
| 55 static ImageEventSender& loadEventSender() | |
| 56 { | |
| 57 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (ImageEventSender::create(Even
tTypeNames::load))); | |
| 58 return sender; | |
| 59 } | |
| 60 | |
| 61 static ImageEventSender& errorEventSender() | |
| 62 { | |
| 63 DEFINE_STATIC_LOCAL(ImageEventSender, sender, (ImageEventSender::create(Even
tTypeNames::error))); | |
| 64 return sender; | |
| 65 } | |
| 66 | |
| 67 static inline bool pageIsBeingDismissed(Document* document) | 57 static inline bool pageIsBeingDismissed(Document* document) |
| 68 { | 58 { |
| 69 return document->pageDismissalEventBeingDispatched() != Document::NoDismissa
l; | 59 return document->pageDismissalEventBeingDispatched() != Document::NoDismissa
l; |
| 70 } | 60 } |
| 71 | 61 |
| 72 static ImageLoader::BypassMainWorldBehavior shouldBypassMainWorldCSP(ImageLoader
* loader) | 62 static ImageLoader::BypassMainWorldBehavior shouldBypassMainWorldCSP(ImageLoader
* loader) |
| 73 { | 63 { |
| 74 ASSERT(loader); | 64 ASSERT(loader); |
| 75 ASSERT(loader->element()); | 65 ASSERT(loader->element()); |
| 76 if (loader->element()->document().frame() && loader->element()->document().f
rame()->script().shouldBypassMainWorldCSP()) | 66 if (loader->element()->document().frame() && loader->element()->document().f
rame()->script().shouldBypassMainWorldCSP()) |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 142 UpdateFromElementBehavior m_updateBehavior; | 132 UpdateFromElementBehavior m_updateBehavior; |
| 143 RefPtr<ScriptState> m_scriptState; | 133 RefPtr<ScriptState> m_scriptState; |
| 144 WeakPtrFactory<Task> m_weakFactory; | 134 WeakPtrFactory<Task> m_weakFactory; |
| 145 ReferrerPolicy m_referrerPolicy; | 135 ReferrerPolicy m_referrerPolicy; |
| 146 int m_operationId; | 136 int m_operationId; |
| 147 }; | 137 }; |
| 148 | 138 |
| 149 ImageLoader::ImageLoader(Element* element) | 139 ImageLoader::ImageLoader(Element* element) |
| 150 : m_element(element) | 140 : m_element(element) |
| 151 , m_derefElementTimer(this, &ImageLoader::timerFired) | 141 , m_derefElementTimer(this, &ImageLoader::timerFired) |
| 142 , m_finishTask(CancellableTaskFactory::create(this, &ImageLoader::finish)) |
| 143 , m_dispatchErrorEventTask(CancellableTaskFactory::create(this, &ImageLoader
::dispatchErrorEvent)) |
| 152 , m_hasPendingLoadEvent(false) | 144 , m_hasPendingLoadEvent(false) |
| 153 , m_hasPendingErrorEvent(false) | |
| 154 , m_imageComplete(true) | 145 , m_imageComplete(true) |
| 155 , m_loadingImageDocument(false) | 146 , m_loadingImageDocument(false) |
| 156 , m_elementIsProtected(false) | 147 , m_elementIsProtected(false) |
| 157 , m_suppressErrorEvents(false) | 148 , m_suppressErrorEvents(false) |
| 158 { | 149 { |
| 159 WTF_LOG(Timers, "new ImageLoader %p", this); | 150 WTF_LOG(Timers, "new ImageLoader %p", this); |
| 160 #if ENABLE(OILPAN) | 151 #if ENABLE(OILPAN) |
| 161 ThreadState::current()->registerPreFinalizer(this); | 152 ThreadState::current()->registerPreFinalizer(this); |
| 162 #endif | 153 #endif |
| 163 } | 154 } |
| 164 | 155 |
| 165 ImageLoader::~ImageLoader() | 156 ImageLoader::~ImageLoader() |
| 166 { | 157 { |
| 167 #if !ENABLE(OILPAN) | 158 #if !ENABLE(OILPAN) |
| 168 dispose(); | 159 dispose(); |
| 169 #endif | 160 #endif |
| 170 } | 161 } |
| 171 | 162 |
| 163 void ImageLoader::cancelPendingFinish() |
| 164 { |
| 165 if (m_loadDelayCounterForFinish) { |
| 166 m_finishTask->cancel(); |
| 167 m_loadDelayCounterForFinish.clear(); |
| 168 } |
| 169 m_hasPendingLoadEvent = false; |
| 170 } |
| 171 |
| 172 void ImageLoader::cancelPendingErrorEvent() |
| 173 { |
| 174 if (m_loadDelayCounterForError) { |
| 175 m_dispatchErrorEventTask->cancel(); |
| 176 m_loadDelayCounterForError.clear(); |
| 177 } |
| 178 } |
| 179 |
| 172 void ImageLoader::dispose() | 180 void ImageLoader::dispose() |
| 173 { | 181 { |
| 174 WTF_LOG(Timers, "~ImageLoader %p; m_hasPendingLoadEvent=%d, m_hasPendingErro
rEvent=%d", | 182 WTF_LOG(Timers, "~ImageLoader %p; m_hasPendingLoadEvent=%d, hasPendingErrorE
vent=%d", |
| 175 this, m_hasPendingLoadEvent, m_hasPendingErrorEvent); | 183 this, m_hasPendingLoadEvent, hasPendingError()); |
| 176 | 184 |
| 177 #if !ENABLE(OILPAN) | 185 #if !ENABLE(OILPAN) |
| 178 if (m_pendingTask) | 186 if (m_pendingMicrotask) |
| 179 m_pendingTask->clearLoader(); | 187 m_pendingMicrotask->clearLoader(); |
| 180 #endif | 188 #endif |
| 181 | 189 |
| 182 if (m_image) { | 190 if (m_image) { |
| 183 m_image->removeObserver(this); | 191 m_image->removeObserver(this); |
| 184 m_image = nullptr; | 192 m_image = nullptr; |
| 185 } | 193 } |
| 186 | 194 cancelPendingFinish(); |
| 187 #if !ENABLE(OILPAN) | 195 cancelPendingErrorEvent(); |
| 188 ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this)); | |
| 189 if (m_hasPendingLoadEvent) | |
| 190 loadEventSender().cancelEvent(this); | |
| 191 | |
| 192 ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this))
; | |
| 193 if (m_hasPendingErrorEvent) | |
| 194 errorEventSender().cancelEvent(this); | |
| 195 #endif | |
| 196 } | 196 } |
| 197 | 197 |
| 198 DEFINE_TRACE(ImageLoader) | 198 DEFINE_TRACE(ImageLoader) |
| 199 { | 199 { |
| 200 visitor->trace(m_image); | 200 visitor->trace(m_image); |
| 201 visitor->trace(m_element); | 201 visitor->trace(m_element); |
| 202 } | 202 } |
| 203 | 203 |
| 204 void ImageLoader::setImage(ImageResource* newImage) | 204 void ImageLoader::setImage(ImageResource* newImage) |
| 205 { | 205 { |
| 206 setImageWithoutConsideringPendingLoadEvent(newImage); | 206 setImageWithoutConsideringPendingLoadEvent(newImage); |
| 207 | 207 |
| 208 // Only consider updating the protection ref-count of the Element immediatel
y before returning | 208 // Only consider updating the protection ref-count of the Element immediatel
y before returning |
| 209 // from this function as doing so might result in the destruction of this Im
ageLoader. | 209 // from this function as doing so might result in the destruction of this Im
ageLoader. |
| 210 updatedHasPendingEvent(); | 210 updatedHasPendingEvent(); |
| 211 } | 211 } |
| 212 | 212 |
| 213 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newI
mage) | 213 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newI
mage) |
| 214 { | 214 { |
| 215 ASSERT(m_failedLoadURL.isEmpty()); | 215 ASSERT(m_failedLoadURL.isEmpty()); |
| 216 ImageResource* oldImage = m_image.get(); | 216 ImageResource* oldImage = m_image.get(); |
| 217 if (newImage != oldImage) { | 217 if (newImage != oldImage) { |
| 218 m_image = newImage; | 218 m_image = newImage; |
| 219 if (m_hasPendingLoadEvent) { | 219 cancelPendingFinish(); |
| 220 loadEventSender().cancelEvent(this); | 220 cancelPendingErrorEvent(); |
| 221 m_hasPendingLoadEvent = false; | |
| 222 } | |
| 223 if (m_hasPendingErrorEvent) { | |
| 224 errorEventSender().cancelEvent(this); | |
| 225 m_hasPendingErrorEvent = false; | |
| 226 } | |
| 227 m_imageComplete = true; | 221 m_imageComplete = true; |
| 228 if (newImage) { | 222 if (newImage) { |
| 229 newImage->addObserver(this); | 223 newImage->addObserver(this); |
| 230 } | 224 } |
| 231 if (oldImage) { | 225 if (oldImage) { |
| 232 oldImage->removeObserver(this); | 226 oldImage->removeObserver(this); |
| 233 } | 227 } |
| 234 } | 228 } |
| 235 | 229 |
| 236 if (LayoutImageResource* imageResource = layoutImageResource()) | 230 if (LayoutImageResource* imageResource = layoutImageResource()) |
| 237 imageResource->resetAnimation(); | 231 imageResource->resetAnimation(); |
| 238 } | 232 } |
| 239 | 233 |
| 240 static void configureRequest(FetchRequest& request, ImageLoader::BypassMainWorld
Behavior bypassBehavior, Element& element, const ClientHintsPreferences& clientH
intsPreferences) | 234 static void configureRequest(FetchRequest& request, ImageLoader::BypassMainWorld
Behavior bypassBehavior, Element& element, const ClientHintsPreferences& clientH
intsPreferences) |
| 241 { | 235 { |
| 242 if (bypassBehavior == ImageLoader::BypassMainWorldCSP) | 236 if (bypassBehavior == ImageLoader::BypassMainWorldCSP) |
| 243 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); | 237 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); |
| 244 | 238 |
| 245 CrossOriginAttributeValue crossOrigin = crossOriginAttributeValue(element.fa
stGetAttribute(HTMLNames::crossoriginAttr)); | 239 CrossOriginAttributeValue crossOrigin = crossOriginAttributeValue(element.fa
stGetAttribute(HTMLNames::crossoriginAttr)); |
| 246 if (crossOrigin != CrossOriginAttributeNotSet) | 240 if (crossOrigin != CrossOriginAttributeNotSet) |
| 247 request.setCrossOriginAccessControl(element.document().getSecurityOrigin
(), crossOrigin); | 241 request.setCrossOriginAccessControl(element.document().getSecurityOrigin
(), crossOrigin); |
| 248 | 242 |
| 249 if (clientHintsPreferences.shouldSendResourceWidth() && isHTMLImageElement(e
lement)) | 243 if (clientHintsPreferences.shouldSendResourceWidth() && isHTMLImageElement(e
lement)) |
| 250 request.setResourceWidth(toHTMLImageElement(element).getResourceWidth())
; | 244 request.setResourceWidth(toHTMLImageElement(element).getResourceWidth())
; |
| 251 } | 245 } |
| 252 | 246 |
| 253 inline void ImageLoader::dispatchErrorEvent() | 247 inline void ImageLoader::scheduleErrorEvent() |
| 254 { | 248 { |
| 255 m_hasPendingErrorEvent = true; | 249 m_loadDelayCounterForError = IncrementLoadEventDelayCount::create(m_element-
>document()); |
| 256 errorEventSender().dispatchEventSoon(this); | 250 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO
M_HERE, m_dispatchErrorEventTask->cancelAndCreate()); |
| 257 } | 251 } |
| 258 | 252 |
| 259 inline void ImageLoader::crossSiteOrCSPViolationOccurred(AtomicString imageSourc
eURL) | 253 inline void ImageLoader::crossSiteOrCSPViolationOccurred(AtomicString imageSourc
eURL) |
| 260 { | 254 { |
| 261 m_failedLoadURL = imageSourceURL; | 255 m_failedLoadURL = imageSourceURL; |
| 262 } | 256 } |
| 263 | 257 |
| 264 inline void ImageLoader::clearFailedLoadURL() | 258 inline void ImageLoader::clearFailedLoadURL() |
| 265 { | 259 { |
| 266 m_failedLoadURL = AtomicString(); | 260 m_failedLoadURL = AtomicString(); |
| 267 } | 261 } |
| 268 | 262 |
| 269 inline void ImageLoader::enqueueImageLoadingMicroTask(UpdateFromElementBehavior
updateBehavior, ReferrerPolicy referrerPolicy) | 263 inline void ImageLoader::enqueueImageLoadingMicroTask(UpdateFromElementBehavior
updateBehavior, ReferrerPolicy referrerPolicy) |
| 270 { | 264 { |
| 271 OwnPtr<Task> task = Task::create(this, updateBehavior, referrerPolicy); | 265 OwnPtr<Task> task = Task::create(this, updateBehavior, referrerPolicy); |
| 272 m_pendingTask = task->createWeakPtr(); | 266 m_pendingMicrotask = task->createWeakPtr(); |
| 273 Microtask::enqueueMicrotask(task.release()); | 267 Microtask::enqueueMicrotask(task.release()); |
| 274 m_loadDelayCounter = IncrementLoadEventDelayCount::create(m_element->documen
t()); | 268 m_loadDelayCounterForMicrotask = IncrementLoadEventDelayCount::create(m_elem
ent->document()); |
| 275 } | 269 } |
| 276 | 270 |
| 277 void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, Up
dateFromElementBehavior updateBehavior, ReferrerPolicy referrerPolicy) | 271 void ImageLoader::doUpdateFromElement(BypassMainWorldBehavior bypassBehavior, Up
dateFromElementBehavior updateBehavior, ReferrerPolicy referrerPolicy) |
| 278 { | 272 { |
| 279 // FIXME: According to | 273 // FIXME: According to |
| 280 // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-cont
ent.html#the-img-element:the-img-element-55 | 274 // http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-cont
ent.html#the-img-element:the-img-element-55 |
| 281 // When "update image" is called due to environment changes and the load fai
ls, onerror should not be called. | 275 // When "update image" is called due to environment changes and the load fai
ls, onerror should not be called. |
| 282 // That is currently not the case. | 276 // That is currently not the case. |
| 283 // | 277 // |
| 284 // We don't need to call clearLoader here: Either we were called from the | 278 // We don't need to call clearLoader here: Either we were called from the |
| 285 // task, or our caller updateFromElement cleared the task's loader (and set | 279 // task, or our caller updateFromElement cleared the task's loader (and set |
| 286 // m_pendingTask to null). | 280 // m_pendingMicrotask to null). |
| 287 m_pendingTask.clear(); | 281 m_pendingMicrotask.clear(); |
| 288 // Make sure to only decrement the count when we exit this function | 282 // Make sure to only decrement the count when we exit this function |
| 289 OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter; | 283 OwnPtr<IncrementLoadEventDelayCount> loadDelayCounter; |
| 290 loadDelayCounter.swap(m_loadDelayCounter); | 284 loadDelayCounter.swap(m_loadDelayCounterForMicrotask); |
| 291 | 285 |
| 292 Document& document = m_element->document(); | 286 Document& document = m_element->document(); |
| 293 if (!document.isActive()) | 287 if (!document.isActive()) |
| 294 return; | 288 return; |
| 295 | 289 |
| 296 AtomicString imageSourceURL = m_element->imageSourceURL(); | 290 AtomicString imageSourceURL = m_element->imageSourceURL(); |
| 297 KURL url = imageSourceToKURL(imageSourceURL); | 291 KURL url = imageSourceToKURL(imageSourceURL); |
| 298 RawPtr<ImageResource> newImage = nullptr; | 292 RawPtr<ImageResource> newImage = nullptr; |
| 299 RawPtr<Element> protectElement(m_element.get()); | 293 RawPtr<Element> protectElement(m_element.get()); |
| 300 if (!url.isNull()) { | 294 if (!url.isNull()) { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 325 request.setDefer(FetchRequest::DeferredByClient); | 319 request.setDefer(FetchRequest::DeferredByClient); |
| 326 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); | 320 request.setContentSecurityCheck(DoNotCheckContentSecurityPolicy); |
| 327 } | 321 } |
| 328 | 322 |
| 329 newImage = ImageResource::fetch(request, document.fetcher()); | 323 newImage = ImageResource::fetch(request, document.fetcher()); |
| 330 if (m_loadingImageDocument && newImage) | 324 if (m_loadingImageDocument && newImage) |
| 331 newImage->setStatus(Resource::Pending); | 325 newImage->setStatus(Resource::Pending); |
| 332 | 326 |
| 333 if (!newImage && !pageIsBeingDismissed(&document)) { | 327 if (!newImage && !pageIsBeingDismissed(&document)) { |
| 334 crossSiteOrCSPViolationOccurred(imageSourceURL); | 328 crossSiteOrCSPViolationOccurred(imageSourceURL); |
| 335 dispatchErrorEvent(); | 329 scheduleErrorEvent(); |
| 336 } else { | 330 } else { |
| 337 clearFailedLoadURL(); | 331 clearFailedLoadURL(); |
| 338 } | 332 } |
| 339 } else { | 333 } else { |
| 340 if (!imageSourceURL.isNull()) { | 334 if (!imageSourceURL.isNull()) { |
| 341 // Fire an error event if the url string is not empty, but the KURL
is. | 335 // Fire an error event if the url string is not empty, but the KURL
is. |
| 342 dispatchErrorEvent(); | 336 scheduleErrorEvent(); |
| 343 } | 337 } |
| 344 noImageResourceToLoad(); | 338 noImageResourceToLoad(); |
| 345 } | 339 } |
| 346 | 340 |
| 347 RawPtr<ImageResource> oldImage = m_image.get(); | 341 RawPtr<ImageResource> oldImage = m_image.get(); |
| 348 if (updateBehavior == UpdateSizeChanged && m_element->layoutObject() && m_el
ement->layoutObject()->isImage() && newImage == oldImage) { | 342 if (updateBehavior == UpdateSizeChanged && m_element->layoutObject() && m_el
ement->layoutObject()->isImage() && newImage == oldImage) { |
| 349 toLayoutImage(m_element->layoutObject())->intrinsicSizeChanged(); | 343 toLayoutImage(m_element->layoutObject())->intrinsicSizeChanged(); |
| 350 } else { | 344 } else { |
| 351 if (m_hasPendingLoadEvent) { | 345 cancelPendingFinish(); |
| 352 loadEventSender().cancelEvent(this); | |
| 353 m_hasPendingLoadEvent = false; | |
| 354 } | |
| 355 | 346 |
| 356 // Cancel error events that belong to the previous load, which is now ca
ncelled by changing the src attribute. | 347 // Cancel error events that belong to the previous load, which is now ca
ncelled by changing the src attribute. |
| 357 // If newImage is null and m_hasPendingErrorEvent is true, we know the e
rror event has been just posted by | 348 // If newImage is null and hasPendingError() is true, we know the error
event has been just posted by |
| 358 // this load and we should not cancel the event. | 349 // this load and we should not cancel the event. |
| 359 // FIXME: If both previous load and this one got blocked with an error,
we can receive one error event instead of two. | 350 // FIXME: If both previous load and this one got blocked with an error,
we can receive one error event instead of two. |
| 360 if (m_hasPendingErrorEvent && newImage) { | 351 if (newImage) |
| 361 errorEventSender().cancelEvent(this); | 352 cancelPendingErrorEvent(); |
| 362 m_hasPendingErrorEvent = false; | |
| 363 } | |
| 364 | 353 |
| 365 m_image = newImage; | 354 m_image = newImage; |
| 366 m_hasPendingLoadEvent = newImage; | 355 m_hasPendingLoadEvent = newImage; |
| 367 m_imageComplete = !newImage; | 356 m_imageComplete = !newImage; |
| 368 | 357 |
| 369 updateLayoutObject(); | 358 updateLayoutObject(); |
| 370 // If newImage exists and is cached, addObserver() will result in the lo
ad event | 359 // If newImage exists and is cached, addObserver() will result in the lo
ad event |
| 371 // being queued to fire. Ensure this happens after beforeload is dispatc
hed. | 360 // being queued to fire. Ensure this happens after beforeload is dispatc
hed. |
| 372 if (newImage) { | 361 if (newImage) { |
| 373 newImage->addObserver(this); | 362 newImage->addObserver(this); |
| (...skipping 17 matching lines...) Expand all Loading... |
| 391 m_suppressErrorEvents = (updateBehavior == UpdateSizeChanged); | 380 m_suppressErrorEvents = (updateBehavior == UpdateSizeChanged); |
| 392 | 381 |
| 393 if (updateBehavior == UpdateIgnorePreviousError) | 382 if (updateBehavior == UpdateIgnorePreviousError) |
| 394 clearFailedLoadURL(); | 383 clearFailedLoadURL(); |
| 395 | 384 |
| 396 if (!m_failedLoadURL.isEmpty() && imageSourceURL == m_failedLoadURL) | 385 if (!m_failedLoadURL.isEmpty() && imageSourceURL == m_failedLoadURL) |
| 397 return; | 386 return; |
| 398 | 387 |
| 399 // If we have a pending task, we have to clear it -- either we're | 388 // If we have a pending task, we have to clear it -- either we're |
| 400 // now loading immediately, or we need to reset the task's state. | 389 // now loading immediately, or we need to reset the task's state. |
| 401 if (m_pendingTask) { | 390 if (m_pendingMicrotask) { |
| 402 m_pendingTask->clearLoader(); | 391 m_pendingMicrotask->clearLoader(); |
| 403 m_pendingTask.clear(); | 392 m_pendingMicrotask.clear(); |
| 404 } | 393 } |
| 405 | 394 |
| 406 KURL url = imageSourceToKURL(imageSourceURL); | 395 KURL url = imageSourceToKURL(imageSourceURL); |
| 407 if (shouldLoadImmediately(url)) { | 396 if (shouldLoadImmediately(url)) { |
| 408 doUpdateFromElement(DoNotBypassMainWorldCSP, updateBehavior, referrerPol
icy); | 397 doUpdateFromElement(DoNotBypassMainWorldCSP, updateBehavior, referrerPol
icy); |
| 409 return; | 398 return; |
| 410 } | 399 } |
| 411 // Allow the idiom "img.src=''; img.src='.." to clear down the image before | 400 // Allow the idiom "img.src=''; img.src='.." to clear down the image before |
| 412 // an asynchronous load completes. | 401 // an asynchronous load completes. |
| 413 if (imageSourceURL.isEmpty()) { | 402 if (imageSourceURL.isEmpty()) { |
| 414 ImageResource* image = m_image.get(); | 403 ImageResource* image = m_image.get(); |
| 415 if (image) { | 404 if (image) { |
| 416 image->removeObserver(this); | 405 image->removeObserver(this); |
| 417 } | 406 } |
| 418 m_image = nullptr; | 407 m_image = nullptr; |
| 408 cancelPendingFinish(); |
| 409 cancelPendingErrorEvent(); |
| 419 } | 410 } |
| 420 | 411 |
| 421 // Don't load images for inactive documents. We don't want to slow down the | 412 // Don't load images for inactive documents. We don't want to slow down the |
| 422 // raw HTML parsing case by loading images we don't intend to display. | 413 // raw HTML parsing case by loading images we don't intend to display. |
| 423 Document& document = m_element->document(); | 414 Document& document = m_element->document(); |
| 424 if (document.isActive()) | 415 if (document.isActive()) |
| 425 enqueueImageLoadingMicroTask(updateBehavior, referrerPolicy); | 416 enqueueImageLoadingMicroTask(updateBehavior, referrerPolicy); |
| 426 } | 417 } |
| 427 | 418 |
| 428 KURL ImageLoader::imageSourceToKURL(AtomicString imageSourceURL) const | 419 KURL ImageLoader::imageSourceToKURL(AtomicString imageSourceURL) const |
| (...skipping 29 matching lines...) Expand all Loading... |
| 458 } | 449 } |
| 459 | 450 |
| 460 void ImageLoader::imageNotifyFinished(ImageResource* resource) | 451 void ImageLoader::imageNotifyFinished(ImageResource* resource) |
| 461 { | 452 { |
| 462 WTF_LOG(Timers, "ImageLoader::imageNotifyFinished %p; m_hasPendingLoadEvent=
%d", | 453 WTF_LOG(Timers, "ImageLoader::imageNotifyFinished %p; m_hasPendingLoadEvent=
%d", |
| 463 this, m_hasPendingLoadEvent); | 454 this, m_hasPendingLoadEvent); |
| 464 | 455 |
| 465 ASSERT(m_failedLoadURL.isEmpty()); | 456 ASSERT(m_failedLoadURL.isEmpty()); |
| 466 ASSERT(resource == m_image.get()); | 457 ASSERT(resource == m_image.get()); |
| 467 | 458 |
| 459 m_loadDelayCounterForFinish = IncrementLoadEventDelayCount::create(m_element
->document()); |
| 460 Platform::current()->currentThread()->getWebTaskRunner()->postTask(BLINK_FRO
M_HERE, m_finishTask->cancelAndCreate()); |
| 461 } |
| 462 |
| 463 void ImageLoader::finish() |
| 464 { |
| 468 m_imageComplete = true; | 465 m_imageComplete = true; |
| 466 ImageResource* imageResource = m_image.get(); |
| 467 ASSERT(imageResource); |
| 469 | 468 |
| 470 // Update ImageAnimationPolicy for m_image. | 469 // Update ImageAnimationPolicy for |imageResource|. |
| 471 if (m_image) | 470 if (imageResource) |
| 472 m_image->updateImageAnimationPolicy(); | 471 imageResource->updateImageAnimationPolicy(); |
| 473 | 472 |
| 474 updateLayoutObject(); | 473 updateLayoutObject(); |
| 475 | 474 |
| 476 if (m_image && m_image->getImage() && m_image->getImage()->isSVGImage()) | 475 if (imageResource && imageResource->getImage() && imageResource->getImage()-
>isSVGImage()) |
| 477 toSVGImage(m_image->getImage())->updateUseCounters(element()->document()
); | 476 toSVGImage(imageResource->getImage())->updateUseCounters(element()->docu
ment()); |
| 478 | 477 |
| 479 if (!m_hasPendingLoadEvent) | 478 finishInternal(imageResource); |
| 480 return; | |
| 481 | 479 |
| 482 if (resource->errorOccurred()) { | 480 // Dispatches image's load/error event. |
| 483 loadEventSender().cancelEvent(this); | 481 if (m_hasPendingLoadEvent) { |
| 484 m_hasPendingLoadEvent = false; | 482 m_hasPendingLoadEvent = false; |
| 485 | 483 |
| 486 if (resource->resourceError().isAccessCheck()) | 484 if (imageResource->errorOccurred()) { |
| 487 crossSiteOrCSPViolationOccurred(AtomicString(resource->resourceError
().failingURL())); | 485 if (imageResource->resourceError().isAccessCheck()) |
| 486 crossSiteOrCSPViolationOccurred(AtomicString(imageResource->reso
urceError().failingURL())); |
| 488 | 487 |
| 489 // The error event should not fire if the image data update is a result
of environment change. | 488 // The error event should not fire if the image data update is a res
ult of environment change. |
| 490 // https://html.spec.whatwg.org/multipage/embedded-content.html#the-img-
element:the-img-element-55 | 489 // https://html.spec.whatwg.org/multipage/embedded-content.html#the-
img-element:the-img-element-55 |
| 491 if (!m_suppressErrorEvents) | 490 if (!m_suppressErrorEvents) |
| 492 dispatchErrorEvent(); | 491 dispatchErrorEventWithoutUpdatedHasPendingEvent(); |
| 492 } else if (!imageResource->wasCanceled()) { |
| 493 if (element()->document().frame()) |
| 494 dispatchLoadEvent(); |
| 495 } |
| 496 } |
| 493 | 497 |
| 494 // Only consider updating the protection ref-count of the Element immedi
ately before returning | 498 // Dispatches document's load event if applicable. |
| 495 // from this function as doing so might result in the destruction of thi
s ImageLoader. | 499 // Calling checkCompleted() and document load event synchronously here has |
| 496 updatedHasPendingEvent(); | 500 // no problem regarding reentrancy because ImageLoader::finish() is |
| 497 return; | 501 // always executed as a CancellableTask and not called by others. |
| 498 } | 502 m_loadDelayCounterForFinish.clear(); |
| 499 if (resource->wasCanceled()) { | 503 if (element()->document().frame()) |
| 500 m_hasPendingLoadEvent = false; | 504 element()->document().frame()->loader().checkCompleted(); |
| 501 // Only consider updating the protection ref-count of the Element immedi
ately before returning | 505 |
| 502 // from this function as doing so might result in the destruction of thi
s ImageLoader. | 506 // Only consider updating the protection ref-count of the Element immediatel
y before returning |
| 503 updatedHasPendingEvent(); | 507 // from this function as doing so might result in the destruction of this Im
ageLoader. |
| 504 return; | 508 updatedHasPendingEvent(); |
| 505 } | |
| 506 loadEventSender().dispatchEventSoon(this); | |
| 507 } | 509 } |
| 508 | 510 |
| 509 LayoutImageResource* ImageLoader::layoutImageResource() | 511 LayoutImageResource* ImageLoader::layoutImageResource() |
| 510 { | 512 { |
| 511 LayoutObject* layoutObject = m_element->layoutObject(); | 513 LayoutObject* layoutObject = m_element->layoutObject(); |
| 512 | 514 |
| 513 if (!layoutObject) | 515 if (!layoutObject) |
| 514 return 0; | 516 return 0; |
| 515 | 517 |
| 516 // We don't return style generated image because it doesn't belong to the Im
ageLoader. | 518 // We don't return style generated image because it doesn't belong to the Im
ageLoader. |
| (...skipping 25 matching lines...) Expand all Loading... |
| 542 imageResource->setImageResource(m_image.get()); | 544 imageResource->setImageResource(m_image.get()); |
| 543 } | 545 } |
| 544 | 546 |
| 545 void ImageLoader::updatedHasPendingEvent() | 547 void ImageLoader::updatedHasPendingEvent() |
| 546 { | 548 { |
| 547 // If an Element that does image loading is removed from the DOM the load/er
ror event for the image is still observable. | 549 // If an Element that does image loading is removed from the DOM the load/er
ror event for the image is still observable. |
| 548 // As long as the ImageLoader is actively loading, the Element itself needs
to be ref'ed to keep it from being | 550 // As long as the ImageLoader is actively loading, the Element itself needs
to be ref'ed to keep it from being |
| 549 // destroyed by DOM manipulation or garbage collection. | 551 // destroyed by DOM manipulation or garbage collection. |
| 550 // If such an Element wishes for the load to stop when removed from the DOM
it needs to stop the ImageLoader explicitly. | 552 // If such an Element wishes for the load to stop when removed from the DOM
it needs to stop the ImageLoader explicitly. |
| 551 bool wasProtected = m_elementIsProtected; | 553 bool wasProtected = m_elementIsProtected; |
| 552 m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent; | 554 m_elementIsProtected = m_hasPendingLoadEvent || hasPendingError(); |
| 553 if (wasProtected == m_elementIsProtected) | 555 if (wasProtected == m_elementIsProtected) |
| 554 return; | 556 return; |
| 555 | 557 |
| 556 if (m_elementIsProtected) { | 558 if (m_elementIsProtected) { |
| 557 if (m_derefElementTimer.isActive()) | 559 if (m_derefElementTimer.isActive()) |
| 558 m_derefElementTimer.stop(); | 560 m_derefElementTimer.stop(); |
| 559 else | 561 else |
| 560 m_keepAlive = m_element; | 562 m_keepAlive = m_element; |
| 561 } else { | 563 } else { |
| 562 ASSERT(!m_derefElementTimer.isActive()); | 564 ASSERT(!m_derefElementTimer.isActive()); |
| 563 m_derefElementTimer.startOneShot(0, BLINK_FROM_HERE); | 565 m_derefElementTimer.startOneShot(0, BLINK_FROM_HERE); |
| 564 } | 566 } |
| 565 } | 567 } |
| 566 | 568 |
| 567 void ImageLoader::timerFired(Timer<ImageLoader>*) | 569 void ImageLoader::timerFired(Timer<ImageLoader>*) |
| 568 { | 570 { |
| 569 m_keepAlive.clear(); | 571 m_keepAlive.clear(); |
| 570 } | 572 } |
| 571 | 573 |
| 572 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender) | 574 void ImageLoader::dispatchErrorEventWithoutUpdatedHasPendingEvent() |
| 573 { | 575 { |
| 574 WTF_LOG(Timers, "ImageLoader::dispatchPendingEvent %p", this); | 576 if (element()->document().frame()) |
| 575 ASSERT(eventSender == &loadEventSender() || eventSender == &errorEventSender
()); | 577 element()->dispatchEvent(Event::create(EventTypeNames::error)); |
| 576 const AtomicString& eventType = eventSender->eventType(); | 578 m_loadDelayCounterForError.clear(); |
| 577 if (eventType == EventTypeNames::load) | |
| 578 dispatchPendingLoadEvent(); | |
| 579 if (eventType == EventTypeNames::error) | |
| 580 dispatchPendingErrorEvent(); | |
| 581 } | 579 } |
| 582 | 580 |
| 583 void ImageLoader::dispatchPendingLoadEvent() | 581 void ImageLoader::dispatchErrorEvent() |
| 584 { | 582 { |
| 585 if (!m_hasPendingLoadEvent) | 583 dispatchErrorEventWithoutUpdatedHasPendingEvent(); |
| 586 return; | |
| 587 if (!m_image) | |
| 588 return; | |
| 589 m_hasPendingLoadEvent = false; | |
| 590 if (element()->document().frame()) | |
| 591 dispatchLoadEvent(); | |
| 592 | 584 |
| 593 // Only consider updating the protection ref-count of the Element immediatel
y before returning | 585 // Only consider updating the protection ref-count of the Element immediatel
y before returning |
| 594 // from this function as doing so might result in the destruction of this Im
ageLoader. | 586 // from this function as doing so might result in the destruction of this Im
ageLoader. |
| 595 updatedHasPendingEvent(); | |
| 596 } | |
| 597 | |
| 598 void ImageLoader::dispatchPendingErrorEvent() | |
| 599 { | |
| 600 if (!m_hasPendingErrorEvent) | |
| 601 return; | |
| 602 m_hasPendingErrorEvent = false; | |
| 603 | |
| 604 if (element()->document().frame()) | |
| 605 element()->dispatchEvent(Event::create(EventTypeNames::error)); | |
| 606 | |
| 607 // Only consider updating the protection ref-count of the Element immediatel
y before returning | |
| 608 // from this function as doing so might result in the destruction of this Im
ageLoader. | |
| 609 updatedHasPendingEvent(); | 587 updatedHasPendingEvent(); |
| 610 } | 588 } |
| 611 | 589 |
| 612 bool ImageLoader::getImageAnimationPolicy(ImageAnimationPolicy& policy) | 590 bool ImageLoader::getImageAnimationPolicy(ImageAnimationPolicy& policy) |
| 613 { | 591 { |
| 614 if (!element()->document().settings()) | 592 if (!element()->document().settings()) |
| 615 return false; | 593 return false; |
| 616 | 594 |
| 617 policy = element()->document().settings()->imageAnimationPolicy(); | 595 policy = element()->document().settings()->imageAnimationPolicy(); |
| 618 return true; | 596 return true; |
| 619 } | 597 } |
| 620 | 598 |
| 621 void ImageLoader::dispatchPendingLoadEvents() | |
| 622 { | |
| 623 loadEventSender().dispatchPendingEvents(); | |
| 624 } | |
| 625 | |
| 626 void ImageLoader::dispatchPendingErrorEvents() | |
| 627 { | |
| 628 errorEventSender().dispatchPendingEvents(); | |
| 629 } | |
| 630 | |
| 631 void ImageLoader::elementDidMoveToNewDocument() | 599 void ImageLoader::elementDidMoveToNewDocument() |
| 632 { | 600 { |
| 633 if (m_loadDelayCounter) | 601 if (m_loadDelayCounterForMicrotask) |
| 634 m_loadDelayCounter->documentChanged(m_element->document()); | 602 m_loadDelayCounterForMicrotask->documentChanged(m_element->document()); |
| 603 if (m_loadDelayCounterForFinish) |
| 604 m_loadDelayCounterForFinish->documentChanged(m_element->document()); |
| 605 if (m_loadDelayCounterForError) |
| 606 m_loadDelayCounterForError->documentChanged(m_element->document()); |
| 635 clearFailedLoadURL(); | 607 clearFailedLoadURL(); |
| 636 setImage(0); | 608 setImage(0); |
| 637 } | 609 } |
| 638 | 610 |
| 639 } // namespace blink | 611 } // namespace blink |
| OLD | NEW |