627b7b691767a3d547d141117c6c4f1357a4470a
[WebKit-https.git] / Source / WebCore / loader / ImageLoader.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010 Apple Inc. All rights reserved.
5  * Copyright 2014 The Chromium Authors. All rights reserved.
6  * Copyright (C) 2015 Akamai Technologies Inc. All rights reserved.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24 #include "config.h"
25 #include "ImageLoader.h"
26
27 #include "CachedImage.h"
28 #include "CachedResourceLoader.h"
29 #include "CachedResourceRequest.h"
30 #include "CrossOriginAccessControl.h"
31 #include "Document.h"
32 #include "Element.h"
33 #include "Event.h"
34 #include "EventSender.h"
35 #include "Frame.h"
36 #include "HTMLNames.h"
37 #include "HTMLObjectElement.h"
38 #include "HTMLParserIdioms.h"
39 #include "MemoryCache.h"
40 #include "Page.h"
41 #include "RenderImage.h"
42 #include "RenderSVGImage.h"
43 #include "ScriptController.h"
44 #include "SecurityOrigin.h"
45 #include <wtf/NeverDestroyed.h>
46
47 #if ENABLE(VIDEO)
48 #include "RenderVideo.h"
49 #endif
50
51 #if !ASSERT_DISABLED
52 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail.
53 namespace WTF {
54
55 template<> struct ValueCheck<WebCore::ImageLoader*> {
56     typedef WebCore::ImageLoader* TraitType;
57     static void checkConsistency(const WebCore::ImageLoader* p)
58     {
59         if (!p)
60             return;
61         ValueCheck<WebCore::Element*>::checkConsistency(&p->element());
62     }
63 };
64
65 }
66 #endif
67
68 namespace WebCore {
69
70 class ImageLoader::ImageLoaderTask : public MicroTask {
71 public:
72     ImageLoaderTask(WeakPtr<ImageLoader> loader, CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy shouldBypassMainWorldContentSecurityPolicy)
73         : m_loader(loader)
74         , m_shouldBypassMainWorldContentSecurityPolicy(shouldBypassMainWorldContentSecurityPolicy)
75     {
76     }
77
78 private:
79     virtual void run() override
80     {
81         if (m_loader && m_loader->hasPendingTask())
82             m_loader->doUpdateFromElement(m_shouldBypassMainWorldContentSecurityPolicy);
83     }
84
85     WeakPtr<ImageLoader> m_loader;
86     CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy m_shouldBypassMainWorldContentSecurityPolicy;
87 };
88
89 static ImageEventSender& beforeLoadEventSender()
90 {
91     static NeverDestroyed<ImageEventSender> sender(eventNames().beforeloadEvent);
92     return sender;
93 }
94
95 static ImageEventSender& loadEventSender()
96 {
97     static NeverDestroyed<ImageEventSender> sender(eventNames().loadEvent);
98     return sender;
99 }
100
101 static ImageEventSender& errorEventSender()
102 {
103     static NeverDestroyed<ImageEventSender> sender(eventNames().errorEvent);
104     return sender;
105 }
106
107 static inline bool pageIsBeingDismissed(Document& document)
108 {
109     Frame* frame = document.frame();
110     return frame && frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::NoDismissal;
111 }
112
113 ImageLoader::ImageLoader(Element& element)
114     : m_element(element)
115     , m_image(0)
116     , m_derefElementTimer(*this, &ImageLoader::timerFired)
117     , m_weakFactory(this)
118     , m_hasPendingBeforeLoadEvent(false)
119     , m_hasPendingLoadEvent(false)
120     , m_hasPendingErrorEvent(false)
121     , m_imageComplete(true)
122     , m_loadManually(false)
123     , m_elementIsProtected(false)
124     , m_hasPendingTask(false)
125 {
126 }
127
128 ImageLoader::~ImageLoader()
129 {
130     if (m_image)
131         m_image->removeClient(this);
132
133     ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(*this));
134     if (m_hasPendingBeforeLoadEvent)
135         beforeLoadEventSender().cancelEvent(*this);
136
137     ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(*this));
138     if (m_hasPendingLoadEvent)
139         loadEventSender().cancelEvent(*this);
140
141     ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(*this));
142     if (m_hasPendingErrorEvent)
143         errorEventSender().cancelEvent(*this);
144
145     // If the ImageLoader is being destroyed but it is still protecting its image-loading Element,
146     // remove that protection here.
147     if (m_elementIsProtected)
148         element().deref();
149 }
150
151 void ImageLoader::clearImage()
152 {
153     clearImageWithoutConsideringPendingLoadEvent();
154
155     // Only consider updating the protection ref-count of the Element immediately before returning
156     // from this function as doing so might result in the destruction of this ImageLoader.
157     updatedHasPendingEvent();
158 }
159
160 void ImageLoader::clearImageWithoutConsideringPendingLoadEvent()
161 {
162     ASSERT(m_failedLoadURL.isEmpty());
163     CachedImage* oldImage = m_image.get();
164     if (oldImage) {
165         m_image = nullptr;
166         if (m_hasPendingBeforeLoadEvent) {
167             beforeLoadEventSender().cancelEvent(*this);
168             m_hasPendingBeforeLoadEvent = false;
169         }
170         if (m_hasPendingLoadEvent) {
171             loadEventSender().cancelEvent(*this);
172             m_hasPendingLoadEvent = false;
173         }
174         if (m_hasPendingErrorEvent) {
175             errorEventSender().cancelEvent(*this);
176             m_hasPendingErrorEvent = false;
177         }
178         m_imageComplete = true;
179         if (oldImage)
180             oldImage->removeClient(this);
181     }
182
183     if (RenderImageResource* imageResource = renderImageResource())
184         imageResource->resetAnimation();
185 }
186
187 void ImageLoader::doUpdateFromElement(CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy shouldBypassMainWorldContentSecurityPolicy)
188 {
189     m_hasPendingTask = false;
190     Document& document = m_element.document();
191     AtomicString attr = m_element.imageSourceURL();
192
193     String srcURI = sourceURI(attr);
194     URL url;
195     // Set url value only if srcURI is not empty. Otherwise, url will be the URL for the document itself.
196     if (!srcURI.isEmpty())
197         url = document.completeURL(srcURI);
198     // Do not load any image if the 'src' attribute is missing or if it is
199     // an empty string.
200     CachedResourceHandle<CachedImage> newImage = 0;
201     if (!url.isNull()) {
202         ResourceRequest resourceRequest(url);
203         CachedResourceRequest request(resourceRequest);
204         request.setInitiator(&element());
205
206         AtomicString crossOriginMode = m_element.fastGetAttribute(HTMLNames::crossoriginAttr);
207         if (!crossOriginMode.isNull()) {
208             StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
209             updateRequestForAccessControl(request.mutableResourceRequest(), document.securityOrigin(), allowCredentials);
210         }
211
212         if (m_loadManually) {
213             bool autoLoadOtherImages = document.cachedResourceLoader().autoLoadImages();
214             document.cachedResourceLoader().setAutoLoadImages(false);
215             newImage = new CachedImage(request.resourceRequest(), m_element.document().page()->sessionID());
216             newImage->setLoading(true);
217             newImage->setOwningCachedResourceLoader(&document.cachedResourceLoader());
218             document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get());
219             document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages);
220         } else
221             newImage = document.cachedResourceLoader().requestImage(request, shouldBypassMainWorldContentSecurityPolicy);
222
223         // If we do not have an image here, it means that a cross-site
224         // violation occurred, or that the image was blocked via Content
225         // Security Policy, or the page is being dismissed. Trigger an
226         // error event if the page is not being dismissed.
227         if (!newImage && !pageIsBeingDismissed(document)) {
228             m_failedLoadURL = attr;
229             m_hasPendingErrorEvent = true;
230             errorEventSender().dispatchEventSoon(*this);
231         } else
232             clearFailedLoadURL();
233     } else if (!attr.isNull()) {
234         // Fire an error event if the url is empty.
235         m_failedLoadURL = attr;
236         m_hasPendingErrorEvent = true;
237         errorEventSender().dispatchEventSoon(*this);
238     }
239
240     CachedImage* oldImage = m_image.get();
241     if (newImage != oldImage) {
242         if (m_hasPendingBeforeLoadEvent) {
243             beforeLoadEventSender().cancelEvent(*this);
244             m_hasPendingBeforeLoadEvent = false;
245         }
246         if (m_hasPendingLoadEvent) {
247             loadEventSender().cancelEvent(*this);
248             m_hasPendingLoadEvent = false;
249         }
250
251         // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute.
252         // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by
253         // this load and we should not cancel the event.
254         // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two.
255         if (m_hasPendingErrorEvent && newImage) {
256             errorEventSender().cancelEvent(*this);
257             m_hasPendingErrorEvent = false;
258         }
259
260         m_image = newImage;
261         m_hasPendingBeforeLoadEvent = !document.isImageDocument() && newImage;
262         m_hasPendingLoadEvent = newImage;
263         m_imageComplete = !newImage;
264
265         if (newImage) {
266             if (!document.isImageDocument()) {
267                 if (!document.hasListenerType(Document::BEFORELOAD_LISTENER))
268                     dispatchPendingBeforeLoadEvent();
269                 else
270                     beforeLoadEventSender().dispatchEventSoon(*this);
271             } else
272                 updateRenderer();
273
274             // If newImage is cached, addClient() will result in the load event
275             // being queued to fire. Ensure this happens after beforeload is
276             // dispatched.
277             newImage->addClient(this);
278         }
279         if (oldImage) {
280             oldImage->removeClient(this);
281             updateRenderer();
282         }
283     }
284
285     if (RenderImageResource* imageResource = renderImageResource())
286         imageResource->resetAnimation();
287
288     // Only consider updating the protection ref-count of the Element immediately before returning
289     // from this function as doing so might result in the destruction of this ImageLoader.
290     updatedHasPendingEvent();
291     document.decrementLoadEventDelayCount();
292 }
293
294 void ImageLoader::updateFromElement()
295 {
296     AtomicString attribute = m_element.imageSourceURL();
297
298     // Avoid loading a URL we already failed to load.
299     if (!m_failedLoadURL.isEmpty() && attribute == m_failedLoadURL)
300         return;
301
302     // If we're not making renderers for the page, then don't load images. We don't want to slow
303     // down the raw HTML parsing case by loading images we don't intend to display.
304     Document& document = element().document();
305     if (!document.hasLivingRenderTree())
306         return;
307
308     CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy shouldBypassMainWorldContentSecurityPolicy = CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy::No;
309     if (document.frame() && document.frame()->script().shouldBypassMainWorldContentSecurityPolicy())
310         shouldBypassMainWorldContentSecurityPolicy = CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy::Yes;
311
312     if (!m_hasPendingTask) {
313         m_hasPendingTask = true;
314         document.incrementLoadEventDelayCount();
315         if (shouldLoadImmediately(attribute))
316             doUpdateFromElement(CachedResourceLoader::ShouldBypassMainWorldContentSecurityPolicy::No);
317         else
318             MicroTaskQueue::singleton().queueMicroTask(std::make_unique<ImageLoaderTask>(createWeakPtr(), shouldBypassMainWorldContentSecurityPolicy));
319     }
320 }
321
322 bool ImageLoader::shouldLoadImmediately(const AtomicString& attribute) const
323 {
324     String srcURI = sourceURI(attribute);
325     URL url = element().document().completeURL(srcURI);
326     return (srcURI.isEmpty()
327         || url.isEmpty()
328         || m_loadManually
329         || !is<HTMLImageElement>(m_element)
330         || url.protocolIsData()
331         || MemoryCache::singleton().resourceForURL(url));
332 }
333
334 void ImageLoader::updateFromElementIgnoringPreviousError()
335 {
336     clearFailedLoadURL();
337     updateFromElement();
338 }
339
340 void ImageLoader::notifyFinished(CachedResource* resource)
341 {
342     ASSERT(m_failedLoadURL.isEmpty());
343     ASSERT(resource);
344     ASSERT(resource == m_image.get());
345
346     m_imageComplete = true;
347     if (!hasPendingBeforeLoadEvent())
348         updateRenderer();
349
350     if (!m_hasPendingLoadEvent)
351         return;
352
353     ASSERT(image());
354     ASSERT(element().document().securityOrigin());
355
356     if (element().fastHasAttribute(HTMLNames::crossoriginAttr)
357         && !element().document().securityOrigin()->canRequest(image()->response().url())
358         && !resource->passesAccessControlCheck(element().document().securityOrigin())) {
359
360         clearImageWithoutConsideringPendingLoadEvent();
361
362         m_hasPendingErrorEvent = true;
363         errorEventSender().dispatchEventSoon(*this);
364
365         DEPRECATED_DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin image load denied by Cross-Origin Resource Sharing policy.")));
366         element().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, consoleMessage);
367
368         ASSERT(!m_hasPendingLoadEvent);
369
370         // Only consider updating the protection ref-count of the Element immediately before returning
371         // from this function as doing so might result in the destruction of this ImageLoader.
372         updatedHasPendingEvent();
373         return;
374     }
375
376     if (resource->wasCanceled()) {
377         m_hasPendingLoadEvent = false;
378         // Only consider updating the protection ref-count of the Element immediately before returning
379         // from this function as doing so might result in the destruction of this ImageLoader.
380         updatedHasPendingEvent();
381         return;
382     }
383
384     loadEventSender().dispatchEventSoon(*this);
385 }
386
387 RenderImageResource* ImageLoader::renderImageResource()
388 {
389     auto* renderer = element().renderer();
390     if (!renderer)
391         return nullptr;
392
393     // We don't return style generated image because it doesn't belong to the ImageLoader.
394     // See <https://bugs.webkit.org/show_bug.cgi?id=42840>
395     if (is<RenderImage>(*renderer) && !downcast<RenderImage>(*renderer).isGeneratedContent())
396         return &downcast<RenderImage>(*renderer).imageResource();
397
398     if (is<RenderSVGImage>(*renderer))
399         return &downcast<RenderSVGImage>(*renderer).imageResource();
400
401 #if ENABLE(VIDEO)
402     if (is<RenderVideo>(*renderer))
403         return &downcast<RenderVideo>(*renderer).imageResource();
404 #endif
405
406     return nullptr;
407 }
408
409 void ImageLoader::updateRenderer()
410 {
411     RenderImageResource* imageResource = renderImageResource();
412
413     if (!imageResource)
414         return;
415
416     // Only update the renderer if it doesn't have an image or if what we have
417     // is a complete image.  This prevents flickering in the case where a dynamic
418     // change is happening between two images.
419     CachedImage* cachedImage = imageResource->cachedImage();
420     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
421         imageResource->setCachedImage(m_image.get());
422 }
423
424 void ImageLoader::updatedHasPendingEvent()
425 {
426     // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable.
427     // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being
428     // destroyed by DOM manipulation or garbage collection.
429     // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly.
430     bool wasProtected = m_elementIsProtected;
431     m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
432     if (wasProtected == m_elementIsProtected)
433         return;
434
435     if (m_elementIsProtected) {
436         if (m_derefElementTimer.isActive())
437             m_derefElementTimer.stop();
438         else
439             element().ref();
440     } else {
441         ASSERT(!m_derefElementTimer.isActive());
442         m_derefElementTimer.startOneShot(0);
443     }   
444 }
445
446 void ImageLoader::timerFired()
447 {
448     element().deref();
449 }
450
451 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
452 {
453     ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender());
454     const AtomicString& eventType = eventSender->eventType();
455     if (eventType == eventNames().beforeloadEvent)
456         dispatchPendingBeforeLoadEvent();
457     if (eventType == eventNames().loadEvent)
458         dispatchPendingLoadEvent();
459     if (eventType == eventNames().errorEvent)
460         dispatchPendingErrorEvent();
461 }
462
463 void ImageLoader::dispatchPendingBeforeLoadEvent()
464 {
465     if (!m_hasPendingBeforeLoadEvent)
466         return;
467     if (!m_image)
468         return;
469     if (!element().document().hasLivingRenderTree())
470         return;
471     m_hasPendingBeforeLoadEvent = false;
472     if (element().dispatchBeforeLoadEvent(m_image->url())) {
473         updateRenderer();
474         return;
475     }
476     if (m_image) {
477         m_image->removeClient(this);
478         m_image = nullptr;
479     }
480
481     loadEventSender().cancelEvent(*this);
482     m_hasPendingLoadEvent = false;
483     
484     if (is<HTMLObjectElement>(element()))
485         downcast<HTMLObjectElement>(element()).renderFallbackContent();
486
487     // Only consider updating the protection ref-count of the Element immediately before returning
488     // from this function as doing so might result in the destruction of this ImageLoader.
489     updatedHasPendingEvent();
490 }
491
492 void ImageLoader::dispatchPendingLoadEvent()
493 {
494     if (!m_hasPendingLoadEvent)
495         return;
496     if (!m_image)
497         return;
498     m_hasPendingLoadEvent = false;
499     if (element().document().hasLivingRenderTree())
500         dispatchLoadEvent();
501
502     // Only consider updating the protection ref-count of the Element immediately before returning
503     // from this function as doing so might result in the destruction of this ImageLoader.
504     updatedHasPendingEvent();
505 }
506
507 void ImageLoader::dispatchPendingErrorEvent()
508 {
509     if (!m_hasPendingErrorEvent)
510         return;
511     m_hasPendingErrorEvent = false;
512     if (element().document().hasLivingRenderTree())
513         element().dispatchEvent(Event::create(eventNames().errorEvent, false, false));
514
515     // Only consider updating the protection ref-count of the Element immediately before returning
516     // from this function as doing so might result in the destruction of this ImageLoader.
517     updatedHasPendingEvent();
518 }
519
520 void ImageLoader::dispatchPendingBeforeLoadEvents()
521 {
522     beforeLoadEventSender().dispatchPendingEvents();
523 }
524
525 void ImageLoader::dispatchPendingLoadEvents()
526 {
527     loadEventSender().dispatchPendingEvents();
528 }
529
530 void ImageLoader::dispatchPendingErrorEvents()
531 {
532     errorEventSender().dispatchPendingEvents();
533 }
534
535 void ImageLoader::elementDidMoveToNewDocument(Document* oldDocument)
536 {
537     if (m_hasPendingTask) {
538         if (oldDocument)
539             oldDocument->decrementLoadEventDelayCount();
540         m_element.document().incrementLoadEventDelayCount();
541     }
542     clearFailedLoadURL();
543     clearImage();
544 }
545
546 inline void ImageLoader::clearFailedLoadURL()
547 {
548     m_failedLoadURL = AtomicString();
549 }
550
551 }