initial import
[vuplus_webkit] / 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 "CrossOriginAccessControl.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "Event.h"
31 #include "HTMLNames.h"
32 #include "HTMLObjectElement.h"
33 #include "HTMLParserIdioms.h"
34 #include "RenderImage.h"
35
36 #if ENABLE(SVG)
37 #include "RenderSVGImage.h"
38 #endif
39 #if ENABLE(VIDEO)
40 #include "RenderVideo.h"
41 #endif
42
43 #if !ASSERT_DISABLED
44 // ImageLoader objects are allocated as members of other objects, so generic pointer check would always fail.
45 namespace WTF {
46
47 template<> struct ValueCheck<WebCore::ImageLoader*> {
48     typedef WebCore::ImageLoader* TraitType;
49     static void checkConsistency(const WebCore::ImageLoader* p)
50     {
51         if (!p)
52             return;
53         ASSERT(p->element());
54         ValueCheck<WebCore::Element*>::checkConsistency(p->element());
55     }
56 };
57
58 }
59 #endif
60
61 namespace WebCore {
62
63 class ImageEventSender {
64     WTF_MAKE_NONCOPYABLE(ImageEventSender); WTF_MAKE_FAST_ALLOCATED;
65 public:
66     ImageEventSender(const AtomicString& eventType);
67
68     void dispatchEventSoon(ImageLoader*);
69     void cancelEvent(ImageLoader*);
70
71     void dispatchPendingEvents();
72
73 #ifndef NDEBUG
74     bool hasPendingEvents(ImageLoader* loader) const
75     {
76         return m_dispatchSoonList.find(loader) != notFound || m_dispatchingList.find(loader) != notFound;
77     }
78 #endif
79
80 private:
81     void timerFired(Timer<ImageEventSender>*);
82
83     AtomicString m_eventType;
84     Timer<ImageEventSender> m_timer;
85     Vector<ImageLoader*> m_dispatchSoonList;
86     Vector<ImageLoader*> m_dispatchingList;
87 };
88
89 static ImageEventSender& beforeLoadEventSender()
90 {
91     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
92     return sender;
93 }
94
95 static ImageEventSender& loadEventSender()
96 {
97     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
98     return sender;
99 }
100
101 ImageLoader::ImageLoader(Element* element)
102     : m_element(element)
103     , m_image(0)
104     , m_firedBeforeLoad(true)
105     , m_firedLoad(true)
106     , m_imageComplete(true)
107     , m_loadManually(false)
108 {
109 }
110
111 ImageLoader::~ImageLoader()
112 {
113     if (m_image)
114         m_image->removeClient(this);
115
116     ASSERT(!m_firedBeforeLoad || !beforeLoadEventSender().hasPendingEvents(this));
117     if (!m_firedBeforeLoad)
118         beforeLoadEventSender().cancelEvent(this);
119
120     ASSERT(!m_firedLoad || !loadEventSender().hasPendingEvents(this));
121     if (!m_firedLoad)
122         loadEventSender().cancelEvent(this);
123 }
124
125 void ImageLoader::setImage(CachedImage* newImage)
126 {
127     ASSERT(m_failedLoadURL.isEmpty());
128     CachedImage* oldImage = m_image.get();
129     if (newImage != oldImage) {
130         m_image = newImage;
131         if (!m_firedBeforeLoad) {
132             beforeLoadEventSender().cancelEvent(this);
133             m_firedBeforeLoad = true;
134         }
135         if (!m_firedLoad) {
136             loadEventSender().cancelEvent(this);
137             m_firedLoad = true;
138         }
139         m_imageComplete = true;
140         if (newImage)
141             newImage->addClient(this);
142         if (oldImage)
143             oldImage->removeClient(this);
144     }
145
146     if (RenderImageResource* imageResource = renderImageResource())
147         imageResource->resetAnimation();
148 }
149
150 void ImageLoader::updateFromElement()
151 {
152     // If we're not making renderers for the page, then don't load images.  We don't want to slow
153     // down the raw HTML parsing case by loading images we don't intend to display.
154     Document* document = m_element->document();
155     if (!document->renderer())
156         return;
157
158     AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName());
159
160     if (attr == m_failedLoadURL)
161         return;
162
163     // Do not load any image if the 'src' attribute is missing or if it is
164     // an empty string.
165     CachedImage* newImage = 0;
166     if (!attr.isNull() && !stripLeadingAndTrailingHTMLSpaces(attr).isEmpty()) {
167         ResourceRequest request = ResourceRequest(document->completeURL(sourceURI(attr)));
168
169         String crossOriginMode = m_element->fastGetAttribute(HTMLNames::crossoriginAttr);
170         if (!crossOriginMode.isNull()) {
171             StoredCredentials allowCredentials = equalIgnoringCase(crossOriginMode, "use-credentials") ? AllowStoredCredentials : DoNotAllowStoredCredentials;
172             updateRequestForAccessControl(request, document->securityOrigin(), allowCredentials);
173         }
174
175         if (m_loadManually) {
176             bool autoLoadOtherImages = document->cachedResourceLoader()->autoLoadImages();
177             document->cachedResourceLoader()->setAutoLoadImages(false);
178             newImage = new CachedImage(request);
179             newImage->setLoading(true);
180             newImage->setOwningCachedResourceLoader(document->cachedResourceLoader());
181             document->cachedResourceLoader()->m_documentResources.set(newImage->url(), newImage);
182             document->cachedResourceLoader()->setAutoLoadImages(autoLoadOtherImages);
183         } else
184             newImage = document->cachedResourceLoader()->requestImage(request);
185
186         // If we do not have an image here, it means that a cross-site
187         // violation occurred.
188         m_failedLoadURL = !newImage ? attr : AtomicString();
189     } else if (!attr.isNull()) // Fire an error event if the url is empty.
190         m_element->dispatchEvent(Event::create(eventNames().errorEvent, false, false));
191     
192     CachedImage* oldImage = m_image.get();
193     if (newImage != oldImage) {
194         if (!m_firedBeforeLoad)
195             beforeLoadEventSender().cancelEvent(this);
196         if (!m_firedLoad)
197             loadEventSender().cancelEvent(this);
198
199         m_image = newImage;
200         m_firedBeforeLoad = !newImage;
201         m_firedLoad = !newImage;
202         m_imageComplete = !newImage;
203
204         if (newImage) {
205             newImage->addClient(this);
206             if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER))
207                 dispatchPendingBeforeLoadEvent();
208             else
209                 beforeLoadEventSender().dispatchEventSoon(this);
210         }
211         if (oldImage)
212             oldImage->removeClient(this);
213     }
214
215     if (RenderImageResource* imageResource = renderImageResource())
216         imageResource->resetAnimation();
217 }
218
219 void ImageLoader::updateFromElementIgnoringPreviousError()
220 {
221     // Clear previous error.
222     m_failedLoadURL = AtomicString();
223     updateFromElement();
224 }
225
226 void ImageLoader::notifyFinished(CachedResource* resource)
227 {
228     ASSERT(m_failedLoadURL.isEmpty());
229     ASSERT(resource == m_image.get());
230
231     m_imageComplete = true;
232     if (haveFiredBeforeLoadEvent())
233         updateRenderer();
234
235     if (m_firedLoad)
236         return;
237
238     if (resource->wasCanceled()) {
239         m_firedLoad = true;
240         return;
241     }
242
243     loadEventSender().dispatchEventSoon(this);
244 }
245
246 RenderImageResource* ImageLoader::renderImageResource()
247 {
248     RenderObject* renderer = m_element->renderer();
249
250     if (!renderer)
251         return 0;
252
253     if (renderer->isImage())
254         return toRenderImage(renderer)->imageResource();
255
256 #if ENABLE(SVG)
257     if (renderer->isSVGImage())
258         return toRenderSVGImage(renderer)->imageResource();
259 #endif
260
261 #if ENABLE(VIDEO)
262     if (renderer->isVideo())
263         return toRenderVideo(renderer)->imageResource();
264 #endif
265
266     return 0;
267 }
268
269 void ImageLoader::updateRenderer()
270 {
271     RenderImageResource* imageResource = renderImageResource();
272
273     if (!imageResource)
274         return;
275
276     // Only update the renderer if it doesn't have an image or if what we have
277     // is a complete image.  This prevents flickering in the case where a dynamic
278     // change is happening between two images.
279     CachedImage* cachedImage = imageResource->cachedImage();
280     if (m_image != cachedImage && (m_imageComplete || !cachedImage))
281         imageResource->setCachedImage(m_image.get());
282 }
283
284 void ImageLoader::dispatchPendingBeforeLoadEvent()
285 {
286     if (m_firedBeforeLoad)
287         return;
288     if (!m_image)
289         return;
290     if (!m_element->document()->attached())
291         return;
292     m_firedBeforeLoad = true;
293     if (m_element->dispatchBeforeLoadEvent(m_image->url())) {
294         updateRenderer();
295         return;
296     }
297     if (m_image) {
298         m_image->removeClient(this);
299         m_image = 0;
300     }
301
302     loadEventSender().cancelEvent(this);
303     m_firedLoad = true;
304     
305     if (m_element->hasTagName(HTMLNames::objectTag))
306         static_cast<HTMLObjectElement*>(m_element)->renderFallbackContent();
307 }
308
309 void ImageLoader::dispatchPendingLoadEvent()
310 {
311     if (m_firedLoad)
312         return;
313     if (!m_image)
314         return;
315     if (!m_element->document()->attached())
316         return;
317     m_firedLoad = true;
318     dispatchLoadEvent();
319 }
320
321 void ImageLoader::dispatchPendingBeforeLoadEvents()
322 {
323     beforeLoadEventSender().dispatchPendingEvents();
324 }
325
326 void ImageLoader::dispatchPendingLoadEvents()
327 {
328     loadEventSender().dispatchPendingEvents();
329 }
330
331 void ImageLoader::elementWillMoveToNewOwnerDocument()
332 {
333     setImage(0);
334 }
335
336 ImageEventSender::ImageEventSender(const AtomicString& eventType)
337     : m_eventType(eventType)
338     , m_timer(this, &ImageEventSender::timerFired)
339 {
340 }
341
342 void ImageEventSender::dispatchEventSoon(ImageLoader* loader)
343 {
344     m_dispatchSoonList.append(loader);
345     if (!m_timer.isActive())
346         m_timer.startOneShot(0);
347 }
348
349 void ImageEventSender::cancelEvent(ImageLoader* loader)
350 {
351     // Remove instances of this loader from both lists.
352     // Use loops because we allow multiple instances to get into the lists.
353     size_t size = m_dispatchSoonList.size();
354     for (size_t i = 0; i < size; ++i) {
355         if (m_dispatchSoonList[i] == loader)
356             m_dispatchSoonList[i] = 0;
357     }
358     size = m_dispatchingList.size();
359     for (size_t i = 0; i < size; ++i) {
360         if (m_dispatchingList[i] == loader)
361             m_dispatchingList[i] = 0;
362     }
363     if (m_dispatchSoonList.isEmpty())
364         m_timer.stop();
365 }
366
367 void ImageEventSender::dispatchPendingEvents()
368 {
369     // Need to avoid re-entering this function; if new dispatches are
370     // scheduled before the parent finishes processing the list, they
371     // will set a timer and eventually be processed.
372     if (!m_dispatchingList.isEmpty())
373         return;
374
375     m_timer.stop();
376
377     m_dispatchSoonList.checkConsistency();
378
379     m_dispatchingList.swap(m_dispatchSoonList);
380     size_t size = m_dispatchingList.size();
381     for (size_t i = 0; i < size; ++i) {
382         if (ImageLoader* loader = m_dispatchingList[i]) {
383             m_dispatchingList[i] = 0;
384             if (m_eventType == eventNames().beforeloadEvent)
385                 loader->dispatchPendingBeforeLoadEvent();
386             else
387                 loader->dispatchPendingLoadEvent();
388         }
389     }
390     m_dispatchingList.clear();
391 }
392
393 void ImageEventSender::timerFired(Timer<ImageEventSender>*)
394 {
395     dispatchPendingEvents();
396 }
397
398 }