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