3b850fcc193b47908cf46aaea7548dbf93eff0f3
[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-2017 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 "BitmapImage.h"
26 #include "CachedImage.h"
27 #include "CachedResourceLoader.h"
28 #include "CachedResourceRequest.h"
29 #include "CrossOriginAccessControl.h"
30 #include "Document.h"
31 #include "Element.h"
32 #include "Event.h"
33 #include "EventNames.h"
34 #include "EventSender.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "HTMLNames.h"
38 #include "HTMLObjectElement.h"
39 #include "HTMLParserIdioms.h"
40 #include "InspectorInstrumentation.h"
41 #include "Page.h"
42 #include "RenderImage.h"
43 #include "RenderSVGImage.h"
44 #include <wtf/NeverDestroyed.h>
45
46 #if ENABLE(VIDEO)
47 #include "RenderVideo.h"
48 #endif
49
50 #if !ASSERT_DISABLED
51 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail.
52 namespace WTF {
53
54 template<> struct ValueCheck<WebCore::ImageLoader*> {
55     typedef WebCore::ImageLoader* TraitType;
56     static void checkConsistency(const WebCore::ImageLoader* p)
57     {
58         if (!p)
59             return;
60         ValueCheck<WebCore::Element*>::checkConsistency(&p->element());
61     }
62 };
63
64 }
65 #endif
66
67 namespace WebCore {
68
69 static ImageEventSender& beforeLoadEventSender()
70 {
71     static NeverDestroyed<ImageEventSender> sender(eventNames().beforeloadEvent);
72     return sender;
73 }
74
75 static ImageEventSender& loadEventSender()
76 {
77     static NeverDestroyed<ImageEventSender> sender(eventNames().loadEvent);
78     return sender;
79 }
80
81 static ImageEventSender& errorEventSender()
82 {
83     static NeverDestroyed<ImageEventSender> sender(eventNames().errorEvent);
84     return sender;
85 }
86
87 static inline bool pageIsBeingDismissed(Document& document)
88 {
89     Frame* frame = document.frame();
90     return frame && frame->loader().pageDismissalEventBeingDispatched() != FrameLoader::PageDismissalType::None;
91 }
92
93 ImageLoader::ImageLoader(Element& element)
94     : m_element(element)
95     , m_image(nullptr)
96     , m_derefElementTimer(*this, &ImageLoader::timerFired)
97     , m_hasPendingBeforeLoadEvent(false)
98     , m_hasPendingLoadEvent(false)
99     , m_hasPendingErrorEvent(false)
100     , m_imageComplete(true)
101     , m_loadManually(false)
102     , m_elementIsProtected(false)
103 {
104 }
105
106 ImageLoader::~ImageLoader()
107 {
108     if (m_image)
109         m_image->removeClient(*this);
110
111     ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(*this));
112     if (m_hasPendingBeforeLoadEvent)
113         beforeLoadEventSender().cancelEvent(*this);
114
115     ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(*this));
116     if (m_hasPendingLoadEvent)
117         loadEventSender().cancelEvent(*this);
118
119     ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(*this));
120     if (m_hasPendingErrorEvent)
121         errorEventSender().cancelEvent(*this);
122 }
123
124 void ImageLoader::clearImage()
125 {
126     clearImageWithoutConsideringPendingLoadEvent();
127
128     // Only consider updating the protection ref-count of the Element immediately before returning
129     // from this function as doing so might result in the destruction of this ImageLoader.
130     updatedHasPendingEvent();
131 }
132
133 void ImageLoader::clearImageWithoutConsideringPendingLoadEvent()
134 {
135     ASSERT(m_failedLoadURL.isEmpty());
136     CachedImage* oldImage = m_image.get();
137     if (oldImage) {
138         m_image = nullptr;
139         if (m_hasPendingBeforeLoadEvent) {
140             beforeLoadEventSender().cancelEvent(*this);
141             m_hasPendingBeforeLoadEvent = false;
142         }
143         if (m_hasPendingLoadEvent) {
144             loadEventSender().cancelEvent(*this);
145             m_hasPendingLoadEvent = false;
146         }
147         if (m_hasPendingErrorEvent) {
148             errorEventSender().cancelEvent(*this);
149             m_hasPendingErrorEvent = false;
150         }
151         m_imageComplete = true;
152         if (oldImage)
153             oldImage->removeClient(*this);
154     }
155
156     if (RenderImageResource* imageResource = renderImageResource())
157         imageResource->resetAnimation();
158 }
159
160 void ImageLoader::updateFromElement()
161 {
162     // If we're not making renderers for the page, then don't load images. We don't want to slow
163     // down the raw HTML parsing case by loading images we don't intend to display.
164     Document& document = element().document();
165     if (!document.hasLivingRenderTree())
166         return;
167
168     AtomString attr = element().imageSourceURL();
169
170     // Avoid loading a URL we already failed to load.
171     if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL)
172         return;
173
174     // Do not load any image if the 'src' attribute is missing or if it is
175     // an empty string.
176     CachedResourceHandle<CachedImage> newImage = nullptr;
177     if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
178         ResourceLoaderOptions options = CachedResourceLoader::defaultCachedResourceOptions();
179         options.contentSecurityPolicyImposition = element().isInUserAgentShadowTree() ? ContentSecurityPolicyImposition::SkipPolicyCheck : ContentSecurityPolicyImposition::DoPolicyCheck;
180         options.sameOriginDataURLFlag = SameOriginDataURLFlag::Set;
181
182         auto crossOriginAttribute = element().attributeWithoutSynchronization(HTMLNames::crossoriginAttr);
183
184         ResourceRequest resourceRequest(document.completeURL(sourceURI(attr)));
185         resourceRequest.setInspectorInitiatorNodeIdentifier(InspectorInstrumentation::identifierForNode(m_element));
186
187         auto request = createPotentialAccessControlRequest(WTFMove(resourceRequest), document, crossOriginAttribute, WTFMove(options));
188         request.setInitiator(element());
189
190         if (m_loadManually) {
191             bool autoLoadOtherImages = document.cachedResourceLoader().autoLoadImages();
192             document.cachedResourceLoader().setAutoLoadImages(false);
193             auto* page = m_element.document().page();
194             newImage = new CachedImage(WTFMove(request), page->sessionID(), &page->cookieJar());
195             newImage->setStatus(CachedResource::Pending);
196             newImage->setLoading(true);
197             document.cachedResourceLoader().m_documentResources.set(newImage->url(), newImage.get());
198             document.cachedResourceLoader().setAutoLoadImages(autoLoadOtherImages);
199         } else
200             newImage = document.cachedResourceLoader().requestImage(WTFMove(request)).value_or(nullptr);
201
202         // If we do not have an image here, it means that a cross-site
203         // violation occurred, or that the image was blocked via Content
204         // Security Policy, or the page is being dismissed. Trigger an
205         // error event if the page is not being dismissed.
206         if (!newImage && !pageIsBeingDismissed(document)) {
207             m_failedLoadURL = attr;
208             m_hasPendingErrorEvent = true;
209             errorEventSender().dispatchEventSoon(*this);
210         } else
211             clearFailedLoadURL();
212     } else if (!attr.isNull()) {
213         // Fire an error event if the url is empty.
214         m_failedLoadURL = attr;
215         m_hasPendingErrorEvent = true;
216         errorEventSender().dispatchEventSoon(*this);
217     }
218
219     CachedImage* oldImage = m_image.get();
220     if (newImage != oldImage) {
221         if (m_hasPendingBeforeLoadEvent) {
222             beforeLoadEventSender().cancelEvent(*this);
223             m_hasPendingBeforeLoadEvent = false;
224         }
225         if (m_hasPendingLoadEvent) {
226             loadEventSender().cancelEvent(*this);
227             m_hasPendingLoadEvent = false;
228         }
229
230         // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute.
231         // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by
232         // this load and we should not cancel the event.
233         // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two.
234         if (m_hasPendingErrorEvent && newImage) {
235             errorEventSender().cancelEvent(*this);
236             m_hasPendingErrorEvent = false;
237         }
238
239         m_image = newImage;
240         m_hasPendingBeforeLoadEvent = !document.isImageDocument() && newImage;
241         m_hasPendingLoadEvent = newImage;
242         m_imageComplete = !newImage;
243
244         if (newImage) {
245             if (!document.isImageDocument()) {
246                 if (!document.hasListenerType(Document::BEFORELOAD_LISTENER))
247                     dispatchPendingBeforeLoadEvent();
248                 else
249                     beforeLoadEventSender().dispatchEventSoon(*this);
250             } else
251                 updateRenderer();
252
253             // If newImage is cached, addClient() will result in the load event
254             // being queued to fire. Ensure this happens after beforeload is
255             // dispatched.
256             newImage->addClient(*this);
257         }
258         if (oldImage) {
259             oldImage->removeClient(*this);
260             updateRenderer();
261         }
262     }
263
264     if (RenderImageResource* imageResource = renderImageResource())
265         imageResource->resetAnimation();
266
267     // Only consider updating the protection ref-count of the Element immediately before returning
268     // from this function as doing so might result in the destruction of this ImageLoader.
269     updatedHasPendingEvent();
270 }
271
272 void ImageLoader::updateFromElementIgnoringPreviousError()
273 {
274     clearFailedLoadURL();
275     updateFromElement();
276 }
277
278 static inline void resolvePromises(Vector<RefPtr<DeferredPromise>>& promises)
279 {
280     ASSERT(!promises.isEmpty());
281     auto promisesToBeResolved = std::exchange(promises, { });
282     for (auto& promise : promisesToBeResolved)
283         promise->resolve();
284 }
285
286 static inline void rejectPromises(Vector<RefPtr<DeferredPromise>>& promises, const char* message)
287 {
288     ASSERT(!promises.isEmpty());
289     auto promisesToBeRejected = std::exchange(promises, { });
290     for (auto& promise : promisesToBeRejected)
291         promise->reject(Exception { EncodingError, message });
292 }
293
294 inline void ImageLoader::resolveDecodePromises()
295 {
296     resolvePromises(m_decodingPromises);
297 }
298
299 inline void ImageLoader::rejectDecodePromises(const char* message)
300 {
301     rejectPromises(m_decodingPromises, message);
302 }
303
304 void ImageLoader::notifyFinished(CachedResource& resource)
305 {
306     ASSERT(m_failedLoadURL.isEmpty());
307     ASSERT_UNUSED(resource, &resource == m_image.get());
308
309     m_imageComplete = true;
310     if (!hasPendingBeforeLoadEvent())
311         updateRenderer();
312
313     if (!m_hasPendingLoadEvent)
314         return;
315
316     if (m_image->resourceError().isAccessControl()) {
317         URL imageURL = m_image->url();
318
319         clearImageWithoutConsideringPendingLoadEvent();
320
321         m_hasPendingErrorEvent = true;
322         errorEventSender().dispatchEventSoon(*this);
323
324         auto message = makeString("Cannot load image ", imageURL.string(), " due to access control checks.");
325         element().document().addConsoleMessage(MessageSource::Security, MessageLevel::Error, message);
326
327         if (hasPendingDecodePromises())
328             rejectDecodePromises("Access control error.");
329         
330         ASSERT(!m_hasPendingLoadEvent);
331
332         // Only consider updating the protection ref-count of the Element immediately before returning
333         // from this function as doing so might result in the destruction of this ImageLoader.
334         updatedHasPendingEvent();
335         return;
336     }
337
338     if (m_image->wasCanceled()) {
339         if (hasPendingDecodePromises())
340             rejectDecodePromises("Loading was canceled.");
341         m_hasPendingLoadEvent = false;
342         // Only consider updating the protection ref-count of the Element immediately before returning
343         // from this function as doing so might result in the destruction of this ImageLoader.
344         updatedHasPendingEvent();
345         return;
346     }
347
348     if (hasPendingDecodePromises())
349         decode();
350     loadEventSender().dispatchEventSoon(*this);
351 }
352
353 RenderImageResource* ImageLoader::renderImageResource()
354 {
355     auto* renderer = element().renderer();
356     if (!renderer)
357         return nullptr;
358
359     // We don't return style generated image because it doesn't belong to the ImageLoader.
360     // See <https://bugs.webkit.org/show_bug.cgi?id=42840>
361     if (is<RenderImage>(*renderer) && !downcast<RenderImage>(*renderer).isGeneratedContent())
362         return &downcast<RenderImage>(*renderer).imageResource();
363
364     if (is<RenderSVGImage>(*renderer))
365         return &downcast<RenderSVGImage>(*renderer).imageResource();
366
367 #if ENABLE(VIDEO)
368     if (is<RenderVideo>(*renderer))
369         return &downcast<RenderVideo>(*renderer).imageResource();
370 #endif
371
372     return nullptr;
373 }
374
375 void ImageLoader::updateRenderer()
376 {
377     RenderImageResource* imageResource = renderImageResource();
378
379     if (!imageResource)
380         return;
381
382     // Only update the renderer if it doesn't have an image or if what we have
383     // is a complete image. This prevents flickering in the case where a dynamic
384     // change is happening between two images.
385     CachedImage* cachedImage = imageResource->cachedImage();
386     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
387         imageResource->setCachedImage(m_image.get());
388 }
389
390 void ImageLoader::updatedHasPendingEvent()
391 {
392     // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable.
393     // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being
394     // destroyed by DOM manipulation or garbage collection.
395     // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly.
396     bool wasProtected = m_elementIsProtected;
397     m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
398     if (wasProtected == m_elementIsProtected)
399         return;
400
401     if (m_elementIsProtected) {
402         if (m_derefElementTimer.isActive())
403             m_derefElementTimer.stop();
404         else
405             m_protectedElement = &element();
406     } else {
407         ASSERT(!m_derefElementTimer.isActive());
408         m_derefElementTimer.startOneShot(0_s);
409     }   
410 }
411
412 void ImageLoader::decode(Ref<DeferredPromise>&& promise)
413 {
414     m_decodingPromises.append(WTFMove(promise));
415
416     if (!element().document().domWindow()) {
417         rejectDecodePromises("Inactive document.");
418         return;
419     }
420
421     AtomString attr = element().imageSourceURL();
422     if (stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
423         rejectDecodePromises("Missing source URL.");
424         return;
425     }
426
427     if (m_imageComplete)
428         decode();
429 }
430
431 void ImageLoader::decode()
432 {
433     ASSERT(hasPendingDecodePromises());
434     
435     if (!element().document().domWindow()) {
436         rejectDecodePromises("Inactive document.");
437         return;
438     }
439
440     if (!m_image || !m_image->image() || m_image->errorOccurred()) {
441         rejectDecodePromises("Loading error.");
442         return;
443     }
444
445     Image* image = m_image->image();
446     if (!is<BitmapImage>(image)) {
447         resolveDecodePromises();
448         return;
449     }
450
451     auto& bitmapImage = downcast<BitmapImage>(*image);
452     bitmapImage.decode([promises = WTFMove(m_decodingPromises)]() mutable {
453         resolvePromises(promises);
454     });
455 }
456
457 void ImageLoader::timerFired()
458 {
459     m_protectedElement = nullptr;
460 }
461
462 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
463 {
464     ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender());
465     const AtomString& eventType = eventSender->eventType();
466     if (eventType == eventNames().beforeloadEvent)
467         dispatchPendingBeforeLoadEvent();
468     if (eventType == eventNames().loadEvent)
469         dispatchPendingLoadEvent();
470     if (eventType == eventNames().errorEvent)
471         dispatchPendingErrorEvent();
472 }
473
474 void ImageLoader::dispatchPendingBeforeLoadEvent()
475 {
476     if (!m_hasPendingBeforeLoadEvent)
477         return;
478     if (!m_image)
479         return;
480     if (!element().document().hasLivingRenderTree())
481         return;
482     m_hasPendingBeforeLoadEvent = false;
483     Ref<Document> originalDocument = element().document();
484     if (element().dispatchBeforeLoadEvent(m_image->url())) {
485         bool didEventListenerDisconnectThisElement = !element().isConnected() || &element().document() != originalDocument.ptr();
486         if (didEventListenerDisconnectThisElement)
487             return;
488         
489         updateRenderer();
490         return;
491     }
492     if (m_image) {
493         m_image->removeClient(*this);
494         m_image = nullptr;
495     }
496
497     loadEventSender().cancelEvent(*this);
498     m_hasPendingLoadEvent = false;
499     
500     if (is<HTMLObjectElement>(element()))
501         downcast<HTMLObjectElement>(element()).renderFallbackContent();
502
503     // Only consider updating the protection ref-count of the Element immediately before returning
504     // from this function as doing so might result in the destruction of this ImageLoader.
505     updatedHasPendingEvent();
506 }
507
508 void ImageLoader::dispatchPendingLoadEvent()
509 {
510     if (!m_hasPendingLoadEvent)
511         return;
512     if (!m_image)
513         return;
514     m_hasPendingLoadEvent = false;
515     if (element().document().hasLivingRenderTree())
516         dispatchLoadEvent();
517
518     // Only consider updating the protection ref-count of the Element immediately before returning
519     // from this function as doing so might result in the destruction of this ImageLoader.
520     updatedHasPendingEvent();
521 }
522
523 void ImageLoader::dispatchPendingErrorEvent()
524 {
525     if (!m_hasPendingErrorEvent)
526         return;
527     m_hasPendingErrorEvent = false;
528     if (element().document().hasLivingRenderTree())
529         element().dispatchEvent(Event::create(eventNames().errorEvent, Event::CanBubble::No, Event::IsCancelable::No));
530
531     // Only consider updating the protection ref-count of the Element immediately before returning
532     // from this function as doing so might result in the destruction of this ImageLoader.
533     updatedHasPendingEvent();
534 }
535
536 void ImageLoader::dispatchPendingBeforeLoadEvents()
537 {
538     beforeLoadEventSender().dispatchPendingEvents();
539 }
540
541 void ImageLoader::dispatchPendingLoadEvents()
542 {
543     loadEventSender().dispatchPendingEvents();
544 }
545
546 void ImageLoader::dispatchPendingErrorEvents()
547 {
548     errorEventSender().dispatchPendingEvents();
549 }
550
551 void ImageLoader::elementDidMoveToNewDocument()
552 {
553     clearFailedLoadURL();
554     clearImage();
555 }
556
557 inline void ImageLoader::clearFailedLoadURL()
558 {
559     m_failedLoadURL = nullAtom();
560 }
561
562 }