• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 ImageLoadEventSender {
35 public:
36     ImageLoadEventSender();
37 
38     void dispatchLoadEventSoon(ImageLoader*);
39     void cancelLoadEvent(ImageLoader*);
40 
41     void dispatchPendingLoadEvents();
42 
43 private:
44     ~ImageLoadEventSender();
45 
46     void timerFired(Timer<ImageLoadEventSender>*);
47 
48     Timer<ImageLoadEventSender> m_timer;
49     Vector<ImageLoader*> m_dispatchSoonList;
50     Vector<ImageLoader*> m_dispatchingList;
51 };
52 
loadEventSender()53 static ImageLoadEventSender& loadEventSender()
54 {
55     DEFINE_STATIC_LOCAL(ImageLoadEventSender, sender, ());
56     return sender;
57 }
58 
ImageLoader(Element * element)59 ImageLoader::ImageLoader(Element* element)
60     : m_element(element)
61     , m_image(0)
62     , m_firedLoad(true)
63     , m_imageComplete(true)
64     , m_loadManually(false)
65 {
66 }
67 
~ImageLoader()68 ImageLoader::~ImageLoader()
69 {
70     if (m_image)
71         m_image->removeClient(this);
72     loadEventSender().cancelLoadEvent(this);
73 }
74 
setImage(CachedImage * newImage)75 void ImageLoader::setImage(CachedImage* newImage)
76 {
77     ASSERT(m_failedLoadURL.isEmpty());
78     CachedImage* oldImage = m_image.get();
79     if (newImage != oldImage) {
80         setLoadingImage(newImage);
81         m_firedLoad = true;
82         m_imageComplete = true;
83         if (newImage)
84             newImage->addClient(this);
85         if (oldImage)
86             oldImage->removeClient(this);
87     }
88 
89     if (RenderObject* renderer = m_element->renderer()) {
90         if (!renderer->isImage())
91             return;
92 
93         toRenderImage(renderer)->resetAnimation();
94     }
95 }
96 
setLoadingImage(CachedImage * loadingImage)97 void ImageLoader::setLoadingImage(CachedImage* loadingImage)
98 {
99     m_firedLoad = false;
100     m_imageComplete = false;
101     m_image = loadingImage;
102 }
103 
updateFromElement()104 void ImageLoader::updateFromElement()
105 {
106     // If we're not making renderers for the page, then don't load images.  We don't want to slow
107     // down the raw HTML parsing case by loading images we don't intend to display.
108     Document* document = m_element->document();
109     if (!document->renderer())
110         return;
111 
112     AtomicString attr = m_element->getAttribute(m_element->imageSourceAttributeName());
113 
114     if (attr == m_failedLoadURL)
115         return;
116 
117     // Do not load any image if the 'src' attribute is missing or if it is
118     // an empty string referring to a local file. The latter condition is
119     // a quirk that preserves old behavior that Dashboard widgets
120     // need (<rdar://problem/5994621>).
121     CachedImage* newImage = 0;
122     if (!(attr.isNull() || (attr.isEmpty() && document->baseURI().isLocalFile()))) {
123         if (m_loadManually) {
124             document->docLoader()->setAutoLoadImages(false);
125             newImage = new CachedImage(sourceURI(attr));
126             newImage->setLoading(true);
127             newImage->setDocLoader(document->docLoader());
128             document->docLoader()->m_documentResources.set(newImage->url(), newImage);
129         } else
130             newImage = document->docLoader()->requestImage(sourceURI(attr));
131 
132         // If we do not have an image here, it means that a cross-site
133         // violation occurred.
134         m_failedLoadURL = !newImage ? attr : AtomicString();
135     }
136 
137     CachedImage* oldImage = m_image.get();
138     if (newImage != oldImage) {
139         setLoadingImage(newImage);
140         if (newImage)
141             newImage->addClient(this);
142         if (oldImage)
143             oldImage->removeClient(this);
144     }
145 
146     if (RenderObject* renderer = m_element->renderer()) {
147         if (!renderer->isImage())
148             return;
149 
150         toRenderImage(renderer)->resetAnimation();
151     }
152 }
153 
updateFromElementIgnoringPreviousError()154 void ImageLoader::updateFromElementIgnoringPreviousError()
155 {
156     // Clear previous error.
157     m_failedLoadURL = AtomicString();
158     updateFromElement();
159 }
160 
notifyFinished(CachedResource *)161 void ImageLoader::notifyFinished(CachedResource*)
162 {
163     ASSERT(m_failedLoadURL.isEmpty());
164     m_imageComplete = true;
165 
166     loadEventSender().dispatchLoadEventSoon(this);
167 
168     if (RenderObject* renderer = m_element->renderer()) {
169         if (!renderer->isImage())
170             return;
171 
172         toRenderImage(renderer)->setCachedImage(m_image.get());
173     }
174 }
175 
dispatchPendingLoadEvent()176 void ImageLoader::dispatchPendingLoadEvent()
177 {
178     if (m_firedLoad)
179         return;
180     if (!m_image)
181         return;
182     if (!m_element->document()->attached())
183         return;
184     m_firedLoad = true;
185     dispatchLoadEvent();
186 }
187 
dispatchPendingLoadEvents()188 void ImageLoader::dispatchPendingLoadEvents()
189 {
190     loadEventSender().dispatchPendingLoadEvents();
191 }
192 
ImageLoadEventSender()193 ImageLoadEventSender::ImageLoadEventSender()
194     : m_timer(this, &ImageLoadEventSender::timerFired)
195 {
196 }
197 
dispatchLoadEventSoon(ImageLoader * loader)198 void ImageLoadEventSender::dispatchLoadEventSoon(ImageLoader* loader)
199 {
200     m_dispatchSoonList.append(loader);
201     if (!m_timer.isActive())
202         m_timer.startOneShot(0);
203 }
204 
cancelLoadEvent(ImageLoader * loader)205 void ImageLoadEventSender::cancelLoadEvent(ImageLoader* loader)
206 {
207     // Remove instances of this loader from both lists.
208     // Use loops because we allow multiple instances to get into the lists.
209     size_t size = m_dispatchSoonList.size();
210     for (size_t i = 0; i < size; ++i) {
211         if (m_dispatchSoonList[i] == loader)
212             m_dispatchSoonList[i] = 0;
213     }
214     size = m_dispatchingList.size();
215     for (size_t i = 0; i < size; ++i) {
216         if (m_dispatchingList[i] == loader)
217             m_dispatchingList[i] = 0;
218     }
219     if (m_dispatchSoonList.isEmpty())
220         m_timer.stop();
221 }
222 
dispatchPendingLoadEvents()223 void ImageLoadEventSender::dispatchPendingLoadEvents()
224 {
225     // Need to avoid re-entering this function; if new dispatches are
226     // scheduled before the parent finishes processing the list, they
227     // will set a timer and eventually be processed.
228     if (!m_dispatchingList.isEmpty())
229         return;
230 
231     m_timer.stop();
232 
233     m_dispatchingList.swap(m_dispatchSoonList);
234     size_t size = m_dispatchingList.size();
235     for (size_t i = 0; i < size; ++i) {
236         if (ImageLoader* loader = m_dispatchingList[i])
237             loader->dispatchPendingLoadEvent();
238     }
239     m_dispatchingList.clear();
240 }
241 
timerFired(Timer<ImageLoadEventSender> *)242 void ImageLoadEventSender::timerFired(Timer<ImageLoadEventSender>*)
243 {
244     dispatchPendingLoadEvents();
245 }
246 
247 }
248