2009-11-10 Zoltan Horvath <zoltan@webkit.org>
[WebKit-https.git] / 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 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 "CSSHelper.h"
26 #include "CachedImage.h"
27 #include "DocLoader.h"
28 #include "Document.h"
29 #include "Element.h"
30 #include "RenderImage.h"
31
32 namespace WebCore {
33
34 class ImageEventSender : public Noncopyable {
35 public:
36     ImageEventSender(const AtomicString& eventType);
37
38     void dispatchEventSoon(ImageLoader*);
39     void cancelEvent(ImageLoader*);
40
41     void dispatchPendingEvents();
42
43 private:
44     void timerFired(Timer<ImageEventSender>*);
45
46     AtomicString m_eventType;
47     Timer<ImageEventSender> m_timer;
48     Vector<ImageLoader*> m_dispatchSoonList;
49     Vector<ImageLoader*> m_dispatchingList;
50 };
51
52 static ImageEventSender& beforeLoadEventSender()
53 {
54     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().beforeloadEvent));
55     return sender;
56 }
57
58 static ImageEventSender& loadEventSender()
59 {
60     DEFINE_STATIC_LOCAL(ImageEventSender, sender, (eventNames().loadEvent));
61     return sender;
62 }
63
64 ImageLoader::ImageLoader(Element* element)
65     : m_element(element)
66     , m_image(0)
67     , m_firedBeforeLoad(true)
68     , m_firedLoad(true)
69     , m_imageComplete(true)
70     , m_loadManually(false)
71 {
72 }
73
74 ImageLoader::~ImageLoader()
75 {
76     if (m_image)
77         m_image->removeClient(this);
78     if (!m_firedBeforeLoad)
79         beforeLoadEventSender().cancelEvent(this);
80     if (!m_firedLoad)
81         loadEventSender().cancelEvent(this);
82 }
83
84 void ImageLoader::setImage(CachedImage* newImage)
85 {
86     ASSERT(m_failedLoadURL.isEmpty());
87     CachedImage* oldImage = m_image.get();
88     if (newImage != oldImage) {
89         setLoadingImage(newImage);
90         m_firedBeforeLoad = true;
91         m_firedLoad = true;
92         m_imageComplete = true;
93         if (newImage)
94             newImage->addClient(this);
95         if (oldImage)
96             oldImage->removeClient(this);
97     }
98
99     if (RenderObject* renderer = m_element->renderer()) {
100         if (!renderer->isImage())
101             return;
102         toRenderImage(renderer)->resetAnimation();
103     }
104 }
105
106 void ImageLoader::setLoadingImage(CachedImage* loadingImage)
107 {
108     m_image = loadingImage;
109     m_firedBeforeLoad = !loadingImage;
110     m_firedLoad = !loadingImage;
111     m_imageComplete = !loadingImage;
112 }
113
114 void ImageLoader::updateFromElement()
115 {
116     // If we're not making renderers for the page, then don't load images.  We don't want to slow
117     // down the raw HTML parsing case by loading images we don't intend to display.
118     Document* document = m_element->document();
119     if (!document->renderer())
120         return;
121
122     AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName());
123
124     if (attr == m_failedLoadURL)
125         return;
126
127     // Do not load any image if the 'src' attribute is missing or if it is
128     // an empty string referring to a local file. The latter condition is
129     // a quirk that preserves old behavior that Dashboard widgets
130     // need (<rdar://problem/5994621>).
131     CachedImage* newImage = 0;
132     if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) {
133         if (m_loadManually) {
134             document->docLoader()->setAutoLoadImages(false);
135             newImage = new CachedImage(sourceURI(attr));
136             newImage->setLoading(true);
137             newImage->setDocLoader(document->docLoader());
138             document->docLoader()->m_documentResources.set(newImage->url(), newImage);
139         } else
140             newImage = document->docLoader()->requestImage(sourceURI(attr));
141
142         // If we do not have an image here, it means that a cross-site
143         // violation occurred.
144         m_failedLoadURL = !newImage ? attr : AtomicString();
145     }
146     
147     CachedImage* oldImage = m_image.get();
148     if (newImage != oldImage) {
149         setLoadingImage(newImage);
150         if (newImage) {
151             newImage->addClient(this);
152             if (!m_element->document()->hasListenerType(Document::BEFORELOAD_LISTENER))
153                 dispatchPendingBeforeLoadEvent();
154             else
155                 beforeLoadEventSender().dispatchEventSoon(this);
156         }
157         if (oldImage)
158             oldImage->removeClient(this);
159     }
160
161     if (RenderObject* renderer = m_element->renderer()) {
162         if (!renderer->isImage())
163             return;
164         toRenderImage(renderer)->resetAnimation();
165     }
166 }
167
168 void ImageLoader::updateFromElementIgnoringPreviousError()
169 {
170     // Clear previous error.
171     m_failedLoadURL = AtomicString();
172     updateFromElement();
173 }
174
175 void ImageLoader::notifyFinished(CachedResource*)
176 {
177     ASSERT(m_failedLoadURL.isEmpty());
178
179     m_imageComplete = true;
180     if (haveFiredBeforeLoadEvent())
181         updateRenderer();
182
183     loadEventSender().dispatchEventSoon(this);
184 }
185
186 void ImageLoader::updateRenderer()
187 {
188     if (RenderObject* renderer = m_element->renderer()) {
189         if (!renderer->isImage())
190             return;
191         RenderImage* imageRenderer = toRenderImage(renderer);
192         
193         // Only update the renderer if it doesn't have an image or if what we have
194         // is a complete image.  This prevents flickering in the case where a dynamic
195         // change is happening between two images.
196         CachedImage* cachedImage = imageRenderer->cachedImage();
197         if (m_image != cachedImage && (m_imageComplete || !imageRenderer->cachedImage()))
198             imageRenderer->setCachedImage(m_image.get());
199     }
200 }
201
202 void ImageLoader::dispatchPendingBeforeLoadEvent()
203 {
204     if (m_firedBeforeLoad)
205         return;
206     if (!m_image)
207         return;
208     if (!m_element->document()->attached())
209         return;
210     m_firedBeforeLoad = true;
211     if (m_element->dispatchBeforeLoadEvent(m_image->url())) {
212         updateRenderer();
213         return;
214     }
215     if (m_image) {
216         m_image->removeClient(this);
217         m_image = 0;
218     }
219     loadEventSender().cancelEvent(this);
220 }
221
222 void ImageLoader::dispatchPendingLoadEvent()
223 {
224     if (m_firedLoad)
225         return;
226     if (!m_image)
227         return;
228     if (!m_element->document()->attached())
229         return;
230     m_firedLoad = true;
231     dispatchLoadEvent();
232 }
233
234 void ImageLoader::dispatchPendingEvents()
235 {
236     beforeLoadEventSender().dispatchPendingEvents();
237     loadEventSender().dispatchPendingEvents();
238 }
239
240 ImageEventSender::ImageEventSender(const AtomicString& eventType)
241     : m_eventType(eventType)
242     , m_timer(this, &ImageEventSender::timerFired)
243 {
244 }
245
246 void ImageEventSender::dispatchEventSoon(ImageLoader* loader)
247 {
248     m_dispatchSoonList.append(loader);
249     if (!m_timer.isActive())
250         m_timer.startOneShot(0);
251 }
252
253 void ImageEventSender::cancelEvent(ImageLoader* loader)
254 {
255     // Remove instances of this loader from both lists.
256     // Use loops because we allow multiple instances to get into the lists.
257     size_t size = m_dispatchSoonList.size();
258     for (size_t i = 0; i < size; ++i) {
259         if (m_dispatchSoonList[i] == loader)
260             m_dispatchSoonList[i] = 0;
261     }
262     size = m_dispatchingList.size();
263     for (size_t i = 0; i < size; ++i) {
264         if (m_dispatchingList[i] == loader)
265             m_dispatchingList[i] = 0;
266     }
267     if (m_dispatchSoonList.isEmpty())
268         m_timer.stop();
269 }
270
271 void ImageEventSender::dispatchPendingEvents()
272 {
273     // Need to avoid re-entering this function; if new dispatches are
274     // scheduled before the parent finishes processing the list, they
275     // will set a timer and eventually be processed.
276     if (!m_dispatchingList.isEmpty())
277         return;
278
279     m_timer.stop();
280
281     m_dispatchingList.swap(m_dispatchSoonList);
282     size_t size = m_dispatchingList.size();
283     for (size_t i = 0; i < size; ++i) {
284         if (ImageLoader* loader = m_dispatchingList[i]) {
285             if (m_eventType == eventNames().beforeloadEvent)
286                 loader->dispatchPendingBeforeLoadEvent();
287             else
288                 loader->dispatchPendingLoadEvent();
289         }
290     }
291     m_dispatchingList.clear();
292 }
293
294 void ImageEventSender::timerFired(Timer<ImageEventSender>*)
295 {
296     dispatchPendingEvents();
297 }
298
299 }