Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / 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 "core/loader/ImageLoader.h"
24
25 #include "HTMLNames.h"
26 #include "core/dom/Document.h"
27 #include "core/dom/Element.h"
28 #include "core/events/Event.h"
29 #include "core/events/EventSender.h"
30 #include "core/fetch/CrossOriginAccessControl.h"
31 #include "core/fetch/FetchRequest.h"
32 #include "core/fetch/ResourceFetcher.h"
33 #include "core/html/HTMLObjectElement.h"
34 #include "core/html/parser/HTMLParserIdioms.h"
35 #include "core/rendering/RenderImage.h"
36 #include "core/rendering/RenderVideo.h"
37 #include "core/rendering/svg/RenderSVGImage.h"
38 #include "platform/weborigin/SecurityOrigin.h"
39
40 namespace WebCore {
41
42 static ImageEventSender& beforeLoadEventSender()
43 {
44     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::beforeload));
45     return sender;
46 }
47
48 static ImageEventSender& loadEventSender()
49 {
50     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::load));
51     return sender;
52 }
53
54 static ImageEventSender& errorEventSender()
55 {
56     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (EventTypeNames::error));
57     return sender;
58 }
59
60 static inline bool pageIsBeingDismissed(Document* document)
61 {
62     return document->pageDismissalEventBeingDispatched() != Document::NoDismissal;
63 }
64
65 ImageLoader::ImageLoader(Element* element)
66     : m_element(element)
67     , m_image(0)
68     , m_derefElementTimer(this, &ImageLoader::timerFired)
69     , m_hasPendingBeforeLoadEvent(false)
70     , m_hasPendingLoadEvent(false)
71     , m_hasPendingErrorEvent(false)
72     , m_imageComplete(true)
73     , m_loadManually(false)
74     , m_elementIsProtected(false)
75     , m_highPriorityClientCount(0)
76 {
77 }
78
79 ImageLoader::~ImageLoader()
80 {
81     if (m_image)
82         m_image->removeClient(this);
83
84     ASSERT(m_hasPendingBeforeLoadEvent || !beforeLoadEventSender().hasPendingEvents(this));
85     if (m_hasPendingBeforeLoadEvent)
86         beforeLoadEventSender().cancelEvent(this);
87
88     ASSERT(m_hasPendingLoadEvent || !loadEventSender().hasPendingEvents(this));
89     if (m_hasPendingLoadEvent)
90         loadEventSender().cancelEvent(this);
91
92     ASSERT(m_hasPendingErrorEvent || !errorEventSender().hasPendingEvents(this));
93     if (m_hasPendingErrorEvent)
94         errorEventSender().cancelEvent(this);
95
96     // If the ImageLoader is being destroyed but it is still protecting its image-loading Element,
97     // remove that protection here.
98     if (m_elementIsProtected)
99         m_element->deref();
100 }
101
102 void ImageLoader::setImage(ImageResource* newImage)
103 {
104     setImageWithoutConsideringPendingLoadEvent(newImage);
105
106     // Only consider updating the protection ref-count of the Element immediately before returning
107     // from this function as doing so might result in the destruction of this ImageLoader.
108     updatedHasPendingEvent();
109 }
110
111 void ImageLoader::setImageWithoutConsideringPendingLoadEvent(ImageResource* newImage)
112 {
113     ASSERT(m_failedLoadURL.isEmpty());
114     ImageResource* oldImage = m_image.get();
115     if (newImage != oldImage) {
116         sourceImageChanged();
117         m_image = newImage;
118         if (m_hasPendingBeforeLoadEvent) {
119             beforeLoadEventSender().cancelEvent(this);
120             m_hasPendingBeforeLoadEvent = false;
121         }
122         if (m_hasPendingLoadEvent) {
123             loadEventSender().cancelEvent(this);
124             m_hasPendingLoadEvent = false;
125         }
126         if (m_hasPendingErrorEvent) {
127             errorEventSender().cancelEvent(this);
128             m_hasPendingErrorEvent = false;
129         }
130         m_imageComplete = true;
131         if (newImage)
132             newImage->addClient(this);
133         if (oldImage)
134             oldImage->removeClient(this);
135     }
136
137     if (RenderImageResource* imageResource = renderImageResource())
138         imageResource->resetAnimation();
139 }
140
141 void ImageLoader::updateFromElement()
142 {
143     // Don't load images for inactive documents. We don't want to slow down the
144     // raw HTML parsing case by loading images we don't intend to display.
145     Document& document = m_element->document();
146     if (!document.isActive())
147         return;
148
149     AtomicString attr = m_element->imageSourceURL();
150
151     if (!m_failedLoadURL.isEmpty() && attr == m_failedLoadURL)
152         return;
153
154     // Do not load any image if the 'src' attribute is missing or if it is
155     // an empty string.
156     ResourcePtr<ImageResource> newImage = 0;
157     if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
158         FetchRequest request(ResourceRequest(document.completeURL(sourceURI(attr))), element()->localName());
159
160         AtomicString crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr);
161         if (!crossOriginMode.isNull())
162             request.setCrossOriginAccessControl(document.securityOrigin(), crossOriginMode);
163
164         if (m_loadManually) {
165             bool autoLoadOtherImages = document.fetcher()->autoLoadImages();
166             document.fetcher()->setAutoLoadImages(false);
167             newImage = new ImageResource(request.resourceRequest());
168             newImage->setLoading(true);
169             document.fetcher()->m_documentResources.set(newImage->url(), newImage.get());
170             document.fetcher()->setAutoLoadImages(autoLoadOtherImages);
171         } else {
172             newImage = document.fetcher()->fetchImage(request);
173         }
174
175         // If we do not have an image here, it means that a cross-site
176         // violation occurred, or that the image was blocked via Content
177         // Security Policy, or the page is being dismissed. Trigger an
178         // error event if the page is not being dismissed.
179         if (!newImage && !pageIsBeingDismissed(&document)) {
180             m_failedLoadURL = attr;
181             m_hasPendingErrorEvent = true;
182             errorEventSender().dispatchEventSoon(this);
183         } else
184             clearFailedLoadURL();
185     } else if (!attr.isNull()) {
186         // Fire an error event if the url is empty.
187         m_hasPendingErrorEvent = true;
188         errorEventSender().dispatchEventSoon(this);
189     }
190
191     ImageResource* oldImage = m_image.get();
192     if (newImage != oldImage) {
193         sourceImageChanged();
194
195         if (m_hasPendingBeforeLoadEvent) {
196             beforeLoadEventSender().cancelEvent(this);
197             m_hasPendingBeforeLoadEvent = false;
198         }
199         if (m_hasPendingLoadEvent) {
200             loadEventSender().cancelEvent(this);
201             m_hasPendingLoadEvent = false;
202         }
203
204         // Cancel error events that belong to the previous load, which is now cancelled by changing the src attribute.
205         // If newImage is null and m_hasPendingErrorEvent is true, we know the error event has been just posted by
206         // this load and we should not cancel the event.
207         // FIXME: If both previous load and this one got blocked with an error, we can receive one error event instead of two.
208         if (m_hasPendingErrorEvent && newImage) {
209             errorEventSender().cancelEvent(this);
210             m_hasPendingErrorEvent = false;
211         }
212
213         m_image = newImage;
214         m_hasPendingBeforeLoadEvent = !m_element->document().isImageDocument() && newImage;
215         m_hasPendingLoadEvent = newImage;
216         m_imageComplete = !newImage;
217
218         if (newImage) {
219             if (!m_element->document().isImageDocument()) {
220                 if (!m_element->document().hasListenerType(Document::BEFORELOAD_LISTENER))
221                     dispatchPendingBeforeLoadEvent();
222                 else
223                     beforeLoadEventSender().dispatchEventSoon(this);
224             } else
225                 updateRenderer();
226
227             // If newImage is cached, addClient() will result in the load event
228             // being queued to fire. Ensure this happens after beforeload is
229             // dispatched.
230             newImage->addClient(this);
231         } else {
232             updateRenderer();
233         }
234
235         if (oldImage)
236             oldImage->removeClient(this);
237     }
238
239     if (RenderImageResource* imageResource = renderImageResource())
240         imageResource->resetAnimation();
241
242     // Only consider updating the protection ref-count of the Element immediately before returning
243     // from this function as doing so might result in the destruction of this ImageLoader.
244     updatedHasPendingEvent();
245 }
246
247 void ImageLoader::updateFromElementIgnoringPreviousError()
248 {
249     clearFailedLoadURL();
250     updateFromElement();
251 }
252
253 void ImageLoader::notifyFinished(Resource* resource)
254 {
255     ASSERT(m_failedLoadURL.isEmpty());
256     ASSERT(resource == m_image.get());
257
258     m_imageComplete = true;
259     if (!hasPendingBeforeLoadEvent())
260         updateRenderer();
261
262     if (!m_hasPendingLoadEvent)
263         return;
264
265     if (resource->errorOccurred()) {
266         loadEventSender().cancelEvent(this);
267         m_hasPendingLoadEvent = false;
268
269         m_hasPendingErrorEvent = true;
270         errorEventSender().dispatchEventSoon(this);
271
272         // Only consider updating the protection ref-count of the Element immediately before returning
273         // from this function as doing so might result in the destruction of this ImageLoader.
274         updatedHasPendingEvent();
275         return;
276     }
277     if (resource->wasCanceled()) {
278         m_hasPendingLoadEvent = false;
279         // Only consider updating the protection ref-count of the Element immediately before returning
280         // from this function as doing so might result in the destruction of this ImageLoader.
281         updatedHasPendingEvent();
282         return;
283     }
284     loadEventSender().dispatchEventSoon(this);
285 }
286
287 RenderImageResource* ImageLoader::renderImageResource()
288 {
289     RenderObject* renderer = m_element->renderer();
290
291     if (!renderer)
292         return 0;
293
294     // We don't return style generated image because it doesn't belong to the ImageLoader.
295     // See <https://bugs.webkit.org/show_bug.cgi?id=42840>
296     if (renderer->isImage() && !static_cast<RenderImage*>(renderer)->isGeneratedContent())
297         return toRenderImage(renderer)->imageResource();
298
299     if (renderer->isSVGImage())
300         return toRenderSVGImage(renderer)->imageResource();
301
302     if (renderer->isVideo())
303         return toRenderVideo(renderer)->imageResource();
304
305     return 0;
306 }
307
308 void ImageLoader::updateRenderer()
309 {
310     RenderImageResource* imageResource = renderImageResource();
311
312     if (!imageResource)
313         return;
314
315     // Only update the renderer if it doesn't have an image or if what we have
316     // is a complete image.  This prevents flickering in the case where a dynamic
317     // change is happening between two images.
318     ImageResource* cachedImage = imageResource->cachedImage();
319     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
320         imageResource->setImageResource(m_image.get());
321 }
322
323 void ImageLoader::updatedHasPendingEvent()
324 {
325     // If an Element that does image loading is removed from the DOM the load/error event for the image is still observable.
326     // As long as the ImageLoader is actively loading, the Element itself needs to be ref'ed to keep it from being
327     // destroyed by DOM manipulation or garbage collection.
328     // If such an Element wishes for the load to stop when removed from the DOM it needs to stop the ImageLoader explicitly.
329     bool wasProtected = m_elementIsProtected;
330     m_elementIsProtected = m_hasPendingLoadEvent || m_hasPendingErrorEvent;
331     if (wasProtected == m_elementIsProtected)
332         return;
333
334     if (m_elementIsProtected) {
335         if (m_derefElementTimer.isActive())
336             m_derefElementTimer.stop();
337         else
338             m_element->ref();
339     } else {
340         ASSERT(!m_derefElementTimer.isActive());
341         m_derefElementTimer.startOneShot(0);
342     }
343 }
344
345 void ImageLoader::timerFired(Timer<ImageLoader>*)
346 {
347     m_element->deref();
348 }
349
350 void ImageLoader::dispatchPendingEvent(ImageEventSender* eventSender)
351 {
352     ASSERT(eventSender == &beforeLoadEventSender() || eventSender == &loadEventSender() || eventSender == &errorEventSender());
353     const AtomicString& eventType = eventSender->eventType();
354     if (eventType == EventTypeNames::beforeload)
355         dispatchPendingBeforeLoadEvent();
356     if (eventType == EventTypeNames::load)
357         dispatchPendingLoadEvent();
358     if (eventType == EventTypeNames::error)
359         dispatchPendingErrorEvent();
360 }
361
362 void ImageLoader::dispatchPendingBeforeLoadEvent()
363 {
364     if (!m_hasPendingBeforeLoadEvent)
365         return;
366     if (!m_image)
367         return;
368     if (!m_element->document().frame())
369         return;
370     m_hasPendingBeforeLoadEvent = false;
371     if (m_element->dispatchBeforeLoadEvent(m_image->url().string())) {
372         updateRenderer();
373         return;
374     }
375     if (m_image) {
376         m_image->removeClient(this);
377         m_image = 0;
378     }
379
380     loadEventSender().cancelEvent(this);
381     m_hasPendingLoadEvent = false;
382
383     if (m_element->hasTagName(HTMLNames::objectTag))
384         toHTMLObjectElement(m_element)->renderFallbackContent();
385
386     // Only consider updating the protection ref-count of the Element immediately before returning
387     // from this function as doing so might result in the destruction of this ImageLoader.
388     updatedHasPendingEvent();
389 }
390
391 void ImageLoader::dispatchPendingLoadEvent()
392 {
393     if (!m_hasPendingLoadEvent)
394         return;
395     if (!m_image)
396         return;
397     m_hasPendingLoadEvent = false;
398     if (element()->document().frame())
399         dispatchLoadEvent();
400
401     // Only consider updating the protection ref-count of the Element immediately before returning
402     // from this function as doing so might result in the destruction of this ImageLoader.
403     updatedHasPendingEvent();
404 }
405
406 void ImageLoader::dispatchPendingErrorEvent()
407 {
408     if (!m_hasPendingErrorEvent)
409         return;
410     m_hasPendingErrorEvent = false;
411     if (element()->document().frame())
412         element()->dispatchEvent(Event::create(EventTypeNames::error));
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::addClient(ImageLoaderClient* client)
420 {
421     if (client->requestsHighLiveResourceCachePriority()) {
422         if (m_image && !m_highPriorityClientCount++)
423             m_image->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityHigh);
424     }
425     m_clients.add(client);
426 }
427 void ImageLoader::removeClient(ImageLoaderClient* client)
428 {
429     if (client->requestsHighLiveResourceCachePriority()) {
430         ASSERT(m_highPriorityClientCount);
431         m_highPriorityClientCount--;
432         if (m_image && !m_highPriorityClientCount)
433             m_image->setCacheLiveResourcePriority(Resource::CacheLiveResourcePriorityLow);
434     }
435     m_clients.remove(client);
436 }
437
438 void ImageLoader::dispatchPendingBeforeLoadEvents()
439 {
440     beforeLoadEventSender().dispatchPendingEvents();
441 }
442
443 void ImageLoader::dispatchPendingLoadEvents()
444 {
445     loadEventSender().dispatchPendingEvents();
446 }
447
448 void ImageLoader::dispatchPendingErrorEvents()
449 {
450     errorEventSender().dispatchPendingEvents();
451 }
452
453 void ImageLoader::elementDidMoveToNewDocument()
454 {
455     clearFailedLoadURL();
456     setImage(0);
457 }
458
459 void ImageLoader::sourceImageChanged()
460 {
461     HashSet<ImageLoaderClient*>::iterator end = m_clients.end();
462     for (HashSet<ImageLoaderClient*>::iterator it = m_clients.begin(); it != end; ++it) {
463         ImageLoaderClient* handle = *it;
464         handle->notifyImageSourceChanged();
465     }
466 }
467
468 inline void ImageLoader::clearFailedLoadURL()
469 {
470     m_failedLoadURL = AtomicString();
471 }
472
473 }