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