1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2004, 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
23#ifndef ImageLoader_h
24#define ImageLoader_h
25
26#include "core/fetch/ImageResource.h"
27#include "core/fetch/ImageResourceClient.h"
28#include "core/fetch/ResourcePtr.h"
29#include "platform/heap/Handle.h"
30#include "wtf/HashSet.h"
31#include "wtf/WeakPtr.h"
32#include "wtf/text/AtomicString.h"
33
34namespace blink {
35
36class IncrementLoadEventDelayCount;
37class FetchRequest;
38class Document;
39
40class ImageLoaderClient : public WillBeGarbageCollectedMixin {
41public:
42    virtual void notifyImageSourceChanged() = 0;
43
44    // Determines whether the observed ImageResource should have higher priority in the decoded resources cache.
45    virtual bool requestsHighLiveResourceCachePriority() { return false; }
46
47    virtual void trace(Visitor*) { }
48
49protected:
50    ImageLoaderClient() { }
51};
52
53class Element;
54class ImageLoader;
55class RenderImageResource;
56
57template<typename T> class EventSender;
58typedef EventSender<ImageLoader> ImageEventSender;
59
60class ImageLoader : public NoBaseWillBeGarbageCollectedFinalized<ImageLoader>, public ImageResourceClient {
61public:
62    explicit ImageLoader(Element*);
63    virtual ~ImageLoader();
64    void trace(Visitor*);
65
66    enum LoadType {
67        LoadNormally,
68        ForceLoadImmediately
69    };
70
71    enum UpdateFromElementBehavior {
72        // This should be the update behavior when the element is attached to a document, or when DOM mutations trigger a new load.
73        // Starts loading if a load hasn't already been started.
74        UpdateNormal,
75        // This should be the update behavior when the resource was changed (via 'src', 'srcset' or 'sizes').
76        // Starts a new load even if a previous load of the same resource have failed, to match Firefox's behavior.
77        // FIXME - Verify that this is the right behavior according to the spec.
78        UpdateIgnorePreviousError,
79        // This forces the image to update its intrinsic size, even if the image source has not changed.
80        UpdateSizeChanged
81    };
82
83    enum BypassMainWorldBehavior {
84        BypassMainWorldCSP,
85        DoNotBypassMainWorldCSP
86    };
87
88    void updateFromElement(UpdateFromElementBehavior = UpdateNormal, LoadType = LoadNormally);
89
90    void elementDidMoveToNewDocument();
91
92    Element* element() const { return m_element; }
93    bool imageComplete() const
94    {
95        return m_imageComplete && !m_pendingTask;
96    }
97
98    ImageResource* image() const { return m_image.get(); }
99    void setImage(ImageResource*); // Cancels pending load events, and doesn't dispatch new ones.
100
101    void setLoadingImageDocument() { m_loadingImageDocument = true; }
102
103    bool hasPendingActivity() const
104    {
105        return m_hasPendingLoadEvent || m_hasPendingErrorEvent || m_pendingTask;
106    }
107
108    void dispatchPendingEvent(ImageEventSender*);
109
110    static void dispatchPendingLoadEvents();
111    static void dispatchPendingErrorEvents();
112
113    void addClient(ImageLoaderClient*);
114    void removeClient(ImageLoaderClient*);
115
116protected:
117    virtual void notifyFinished(Resource*) OVERRIDE;
118
119private:
120    class Task;
121
122    // Called from the task or from updateFromElement to initiate the load.
123    void doUpdateFromElement(BypassMainWorldBehavior, UpdateFromElementBehavior);
124
125    virtual void dispatchLoadEvent() = 0;
126    virtual String sourceURI(const AtomicString&) const = 0;
127
128    void updatedHasPendingEvent();
129
130    void dispatchPendingLoadEvent();
131    void dispatchPendingErrorEvent();
132
133    RenderImageResource* renderImageResource();
134    void updateRenderer();
135
136    void setImageWithoutConsideringPendingLoadEvent(ImageResource*);
137    void sourceImageChanged();
138    void clearFailedLoadURL();
139    void crossSiteOrCSPViolationOccured(AtomicString);
140    void enqueueImageLoadingMicroTask(UpdateFromElementBehavior);
141    static ResourcePtr<ImageResource> createImageResourceForImageDocument(Document&, FetchRequest&);
142
143    void timerFired(Timer<ImageLoader>*);
144
145    KURL imageSourceToKURL(AtomicString) const;
146
147    // Used to determine whether to immediately initiate the load
148    // or to schedule a microtask.
149    bool shouldLoadImmediately(const KURL&, LoadType) const;
150
151    void willRemoveClient(ImageLoaderClient&);
152
153    RawPtrWillBeMember<Element> m_element;
154    ResourcePtr<ImageResource> m_image;
155    // FIXME: Oilpan: We might be able to remove this Persistent hack when
156    // ImageResourceClient is traceable.
157    GC_PLUGIN_IGNORE("http://crbug.com/383741")
158    RefPtrWillBePersistent<Element> m_keepAlive;
159#if ENABLE(OILPAN)
160    class ImageLoaderClientRemover {
161    public:
162        ImageLoaderClientRemover(ImageLoader& loader, ImageLoaderClient& client) : m_loader(loader), m_client(client) { }
163        ~ImageLoaderClientRemover();
164
165    private:
166        ImageLoader& m_loader;
167        ImageLoaderClient& m_client;
168    };
169    friend class ImageLoaderClientRemover;
170    // Oilpan: This ImageLoader object must outlive its clients because they
171    // need to call ImageLoader::willRemoveClient before they
172    // die. Non-Persistent HeapHashMap doesn't work well because weak processing
173    // for HeapHashMap is not triggered when both of ImageLoader and
174    // ImageLoaderClient are unreachable.
175    GC_PLUGIN_IGNORE("http://crbug.com/383742")
176    PersistentHeapHashMap<WeakMember<ImageLoaderClient>, OwnPtr<ImageLoaderClientRemover> > m_clients;
177#else
178    HashSet<ImageLoaderClient*> m_clients;
179#endif
180    Timer<ImageLoader> m_derefElementTimer;
181    AtomicString m_failedLoadURL;
182    WeakPtr<Task> m_pendingTask; // owned by Microtask
183    OwnPtr<IncrementLoadEventDelayCount> m_loadDelayCounter;
184    bool m_hasPendingLoadEvent : 1;
185    bool m_hasPendingErrorEvent : 1;
186    bool m_imageComplete : 1;
187    bool m_loadingImageDocument : 1;
188    bool m_elementIsProtected : 1;
189    unsigned m_highPriorityClientCount;
190};
191
192}
193
194#endif
195