Unreviewed, rolling out r134442.
[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 "ElementShadow.h"
32 #include "Event.h"
33 #include "EventSender.h"
34 #include "HTMLNames.h"
35 #include "HTMLObjectElement.h"
36 #include "HTMLParserIdioms.h"
37 #include "ImageLoaderClient.h"
38 #include "RenderImage.h"
39 #include "ScriptCallStack.h"
40 #include "SecurityOrigin.h"
41
42 #if ENABLE(SVG)
43 #include "RenderSVGImage.h"
44 #endif
45 #if ENABLE(VIDEO)
46 #include "RenderVideo.h"
47 #endif
48
49 #if !ASSERT_DISABLED
50 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail.
51 namespace WTF {
52
53 template<> struct ValueCheck<WebCore::ImageLoader*> {
54     typedef WebCore::ImageLoader* TraitType;
55     static void checkConsistency(const WebCore::ImageLoader* p)
56     {
57         if (!p)
58             return;
59         ASSERT(p->client()->imageElement());
60         ValueCheck<WebCore::Element*>::checkConsistency(p->client()->imageElement());
61     }
62 };
63
64 }
65 #endif
66
67 namespace WebCore {
68
69 static ImageEventSender& beforeLoadEventSender()
70 {
71     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
72     return sender;
73 }
74
75 static ImageEventSender& loadEventSender()
76 {
77     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
78     return sender;
79 }
80
81 static ImageEventSender& errorEventSender()
82 {
83     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().errorEvent));
84     return sender;
85 }
86
87 ImageLoader::ImageLoader(ImageLoaderClient* client)
88     : m_client(client)
89     , m_image(0)
90     , m_hasPendingBeforeLoadEvent(false)
91     , m_hasPendingLoadEvent(false)
92     , m_hasPendingErrorEvent(false)
93     , m_imageComplete(true)
94     , m_loadManually(false)
95     , m_elementIsProtected(false)
96 {
97 }
98
99 ImageLoader::~ImageLoader()
100 {
101     if (m_image)
102         m_image->removeClient(this);
103
104     ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(this));
105     if (m_hasPendingBeforeLoadEvent)
106         beforeLoadEventSender().cancelEvent(this);
107
108     ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this));
109     if (m_hasPendingLoadEvent)
110         loadEventSender().cancelEvent(this);
111
112     ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this));
113     if (m_hasPendingErrorEvent)
114         errorEventSender().cancelEvent(this);
115
116     // If the ImageLoader is being destroyed but it is still protecting its image-loading Element,
117     // remove that protection here.
118     if (m_elementIsProtected)
119         client()->derefSourceElement();
120 }
121
122 inline Document* ImageLoader::document()
123 {
124     return client()->sourceElement()->document();
125 }
126
127 void ImageLoader::setImage(CachedImage* newImage)
128 {
129     setImageWithoutConsideringPendingLoadEvent(newImage);
130
131     // Only consider updating the protection ref-count of the Element immediately before returning
132     // from this function as doing so might result in the destruction of this ImageLoader.
133     updatedHasPendingEvent();
134 }
135
136 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(CachedImage* newImage)
137 {
138     ASSERT(m_failedLoadURL.isEmpty());
139     CachedImage* oldImage = m_image.get();
140     if (newImage != oldImage) {
141         m_image = newImage;
142         if (m_hasPendingBeforeLoadEvent) {
143             beforeLoadEventSender().cancelEvent(this);
144             m_hasPendingBeforeLoadEvent = false;
145         }
146         if (m_hasPendingLoadEvent) {
147             loadEventSender().cancelEvent(this);
148             m_hasPendingLoadEvent = false;
149         }
150         if (m_hasPendingErrorEvent) {
151             errorEventSender().cancelEvent(this);
152             m_hasPendingErrorEvent = false;
153         }
154         m_imageComplete = true;
155         if (newImage)
156             newImage->addClient(this);
157         if (oldImage)
158             oldImage->removeClient(this);
159     }
160
161     if (RenderImageResource* imageResource = renderImageResource())
162         imageResource->resetAnimation();
163 }
164
165 void ImageLoader::updateFromElement()
166 {
167     // If we're not making renderers for the page, then don't load images.  We don't want to slow
168     // down the raw HTML parsing case by loading images we don't intend to display.
169     if (!document()->renderer())
170         return;
171
172     AtomicString attr = client()->sourceElement()->getAttribute(client()->sourceElement()->imageSourceAttributeName());
173
174     if (attr == m_failedLoadURL)
175         return;
176
177     // Do not load any image if the 'src' attribute is missing or if it is
178     // an empty string.
179     CachedResourceHandle<CachedImage> newImage = 0;
180     if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
181         CachedResourceRequest request(ResourceRequest(document()->completeURL(sourceURI(attr))));
182
183         String crossOriginMode = client()->sourceElement()->fastGetAttribute(HTMLNames::crossoriginAttr);
184         if (!crossOriginMode.isNull()) {
185             StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
186             updateRequestForAccessControl(request.mutableResourceRequest(), document()->securityOrigin(), allowCredentials);
187         }
188
189         if (m_loadManually) {
190             bool autoLoadOtherImages = document()->cachedResourceLoader()->autoLoadImages();
191             document()->cachedResourceLoader()->setAutoLoadImages(false);
192             newImage = new CachedImage(request.resourceRequest());
193             newImage->setLoading(true);
194             newImage->setOwningCachedResourceLoader(document()->cachedResourceLoader());
195             document()->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage.get());
196             document()->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages);
197         } else
198             newImage = document()->cachedResourceLoader()->requestImage(request);
199
200         // If we do not have an image here, it means that a cross-site
201         // violation occurred, or that the image was blocked via Content
202         // Security Policy. Either way, trigger an error event.
203         if (!newImage) {
204             m_failedLoadURL = attr;
205             m_hasPendingErrorEvent = true;
206             errorEventSender().dispatchEventSoon(this);
207         } else
208             m_failedLoadURL = AtomicString();
209     } else if (!attr.isNull()) {
210         // Fire an error event if the url is empty.
211         // FIXME: Should we fire this event asynchronoulsy via errorEventSender()?
212         client()->imageElement()->dispatchEvent(Event::create(eventNames().errorEvent, false, false));
213     }
214     
215     CachedImage* oldImage = m_image.get();
216     if (newImage != oldImage) {
217         if (m_hasPendingBeforeLoadEvent)
218             beforeLoadEventSender().cancelEvent(this);
219         if (m_hasPendingLoadEvent)
220             loadEventSender().cancelEvent(this);
221         if (m_hasPendingErrorEvent)
222             errorEventSender().cancelEvent(this);
223
224         m_image = newImage;
225         m_hasPendingBeforeLoadEvent = !document()->isImageDocument() && newImage;
226         m_hasPendingLoadEvent = newImage;
227         m_imageComplete = !newImage;
228
229         if (newImage) {
230             if (!document()->isImageDocument()) {
231                 if (!document()->hasListenerType(Document::BEFORELOAD_LISTENER))
232                     dispatchPendingBeforeLoadEvent();
233                 else
234                     beforeLoadEventSender().dispatchEventSoon(this);
235             } else
236                 updateRenderer();
237
238             // If newImage is cached, addClient() will result in the load event
239             // being queued to fire. Ensure this happens after beforeload is
240             // dispatched.
241             newImage->addClient(this);
242         }
243         if (oldImage)
244             oldImage->removeClient(this);
245     }
246
247     if (RenderImageResource* imageResource = renderImageResource())
248         imageResource->resetAnimation();
249
250     // Only consider updating the protection ref-count of the Element immediately before returning
251     // from this function as doing so might result in the destruction of this ImageLoader.
252     updatedHasPendingEvent();
253 }
254
255 void ImageLoader::updateFromElementIgnoringPreviousError()
256 {
257     // Clear previous error.
258     m_failedLoadURL = AtomicString();
259     updateFromElement();
260 }
261
262 void ImageLoader::notifyFinished(CachedResource* resource)
263 {
264     ASSERT(m_failedLoadURL.isEmpty());
265     ASSERT(resource == m_image.get());
266
267     m_imageComplete = true;
268     if (!hasPendingBeforeLoadEvent())
269         updateRenderer();
270
271     if (!m_hasPendingLoadEvent)
272         return;
273
274     if (client()->sourceElement()->fastHasAttribute(HTMLNames::crossoriginAttr)
275         && !document()->securityOrigin()->canRequest(image()->response().url())
276         && !resource->passesAccessControlCheck(document()->securityOrigin())) {
277
278         setImageWithoutConsideringPendingLoadEvent(0);
279
280         m_hasPendingErrorEvent = true;
281         errorEventSender().dispatchEventSoon(this);
282
283         DEFINE_STATIC_LOCAL(String, consoleMessage, (ASCIILiteral("Cross-origin image load denied by Cross-Origin Resource Sharing policy.")));
284         document()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage);
285
286         ASSERT(!m_hasPendingLoadEvent);
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         return;
292     }
293
294     if (resource->wasCanceled()) {
295         m_hasPendingLoadEvent = false;
296         // Only consider updating the protection ref-count of the Element immediately before returning
297         // from this function as doing so might result in the destruction of this ImageLoader.
298         updatedHasPendingEvent();
299         return;
300     }
301
302     loadEventSender().dispatchEventSoon(this);
303 }
304
305 RenderImageResource* ImageLoader::renderImageResource()
306 {
307     RenderObject* renderer = client()->imageElement()->renderer();
308
309     if (!renderer)
310         return 0;
311
312     // We don't return style generated image because it doesn't belong to the ImageLoader.
313     // See <https://bugs.webkit.org/show_bug.cgi?id=42840>
314     if (renderer->isImage() && !static_cast<RenderImage*>(renderer)->isGeneratedContent())
315         return toRenderImage(renderer)->imageResource();
316
317 #if ENABLE(SVG)
318     if (renderer->isSVGImage())
319         return toRenderSVGImage(renderer)->imageResource();
320 #endif
321
322 #if ENABLE(VIDEO)
323     if (renderer->isVideo())
324         return toRenderVideo(renderer)->imageResource();
325 #endif
326
327     return 0;
328 }
329
330 void ImageLoader::updateRenderer()
331 {
332     RenderImageResource* imageResource = renderImageResource();
333
334     if (!imageResource)
335         return;
336
337     // Only update the renderer if it doesn't have an image or if what we have
338     // is a complete image.  This prevents flickering in the case where a dynamic
339     // change is happening between two images.
340     CachedImage* cachedImage = imageResource->cachedImage();
341     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
342         imageResource->setCachedImage(m_image.get());
343 }
344
345 void ImageLoader::updatedHasPendingEvent()
346 {
347     // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable.
348     // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being
349     // destroyed by DOM manipulation or garbage collection.
350     // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly.
351     bool wasProtected = m_elementIsProtected;
352     m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
353     if (wasProtected == m_elementIsProtected)
354         return;
355
356     if (m_elementIsProtected)
357         client()->refSourceElement();
358     else
359         client()->derefSourceElement();
360 }
361
362 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
363 {
364     ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender());
365     const AtomicString& eventType = eventSender->eventType();
366     if (eventType == eventNames().beforeloadEvent)
367         dispatchPendingBeforeLoadEvent();
368     if (eventType == eventNames().loadEvent)
369         dispatchPendingLoadEvent();
370     if (eventType == eventNames().errorEvent)
371         dispatchPendingErrorEvent();
372 }
373
374 void ImageLoader::dispatchPendingBeforeLoadEvent()
375 {
376     if (!m_hasPendingBeforeLoadEvent)
377         return;
378     if (!m_image)
379         return;
380     if (!document()->attached())
381         return;
382     m_hasPendingBeforeLoadEvent = false;
383     if (client()->sourceElement()->dispatchBeforeLoadEvent(m_image->url())) {
384         updateRenderer();
385         return;
386     }
387     if (m_image) {
388         m_image->removeClient(this);
389         m_image = 0;
390     }
391
392     loadEventSender().cancelEvent(this);
393     m_hasPendingLoadEvent = false;
394
395     if (client()->sourceElement()->hasTagName(HTMLNames::objectTag))
396         static_cast<HTMLObjectElement*>(client()->sourceElement())->renderFallbackContent();
397
398     // Only consider updating the protection ref-count of the Element immediately before returning
399     // from this function as doing so might result in the destruction of this ImageLoader.
400     updatedHasPendingEvent();
401 }
402
403 void ImageLoader::dispatchPendingLoadEvent()
404 {
405     if (!m_hasPendingLoadEvent)
406         return;
407     if (!m_image)
408         return;
409     if (!document()->attached())
410         return;
411     m_hasPendingLoadEvent = false;
412     dispatchLoadEvent();
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::dispatchPendingErrorEvent()
420 {
421     if (!m_hasPendingErrorEvent)
422         return;
423     if (!document()->attached())
424         return;
425     m_hasPendingErrorEvent = false;
426     client()->imageElement()->dispatchEvent(Event::create(eventNames().errorEvent, false, false));
427 }
428
429 void ImageLoader::dispatchPendingBeforeLoadEvents()
430 {
431     beforeLoadEventSender().dispatchPendingEvents();
432 }
433
434 void ImageLoader::dispatchPendingLoadEvents()
435 {
436     loadEventSender().dispatchPendingEvents();
437 }
438
439 void ImageLoader::dispatchPendingErrorEvents()
440 {
441     errorEventSender().dispatchPendingEvents();
442 }
443
444 void ImageLoader::elementDidMoveToNewDocument()
445 {
446     setImage(0);
447 }
448
449 }