1/*
2    Copyright (C) 1998 Lars Knoll (knoll@mpi-hd.mpg.de)
3    Copyright (C) 2001 Dirk Mueller <mueller@kde.org>
4    Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
5    Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
6
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Library General Public
9    License as published by the Free Software Foundation; either
10    version 2 of the License, or (at your option) any later version.
11
12    This library is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Library General Public License for more details.
16
17    You should have received a copy of the GNU Library General Public License
18    along with this library; see the file COPYING.LIB.  If not, write to
19    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20    Boston, MA 02110-1301, USA.
21*/
22
23#ifndef CachedResource_h
24#define CachedResource_h
25
26#include "CachePolicy.h"
27#include "FrameLoaderTypes.h"
28#include "PurgePriority.h"
29#include "ResourceError.h"
30#include "ResourceLoadPriority.h"
31#include "ResourceLoaderOptions.h"
32#include "ResourceRequest.h"
33#include "ResourceResponse.h"
34#include "SessionID.h"
35#include "Timer.h"
36#include <time.h>
37#include <wtf/HashCountedSet.h>
38#include <wtf/HashSet.h>
39#include <wtf/OwnPtr.h>
40#include <wtf/Vector.h>
41#include <wtf/text/WTFString.h>
42
43namespace WebCore {
44
45class MemoryCache;
46class CachedResourceClient;
47class CachedResourceHandleBase;
48class CachedResourceLoader;
49class InspectorResource;
50class PurgeableBuffer;
51class ResourceBuffer;
52class SecurityOrigin;
53class SharedBuffer;
54class SubresourceLoader;
55
56// A resource that is held in the cache. Classes who want to use this object should derive
57// from CachedResourceClient, to get the function calls in case the requested data has arrived.
58// This class also does the actual communication with the loader to obtain the resource from the network.
59class CachedResource {
60    WTF_MAKE_NONCOPYABLE(CachedResource); WTF_MAKE_FAST_ALLOCATED;
61    friend class MemoryCache;
62    friend class InspectorResource;
63
64public:
65    enum Type {
66        MainResource,
67        ImageResource,
68        CSSStyleSheet,
69        Script,
70        FontResource,
71        RawResource,
72        SVGDocumentResource
73#if ENABLE(XSLT)
74        , XSLStyleSheet
75#endif
76#if ENABLE(LINK_PREFETCH)
77        , LinkPrefetch
78        , LinkSubresource
79#endif
80#if ENABLE(VIDEO_TRACK)
81        , TextTrackResource
82#endif
83    };
84
85    enum Status {
86        Unknown,      // let cache decide what to do with it
87        Pending,      // only partially loaded
88        Cached,       // regular case
89        LoadError,
90        DecodeError
91    };
92
93    CachedResource(const ResourceRequest&, Type, SessionID);
94    virtual ~CachedResource();
95
96    virtual void load(CachedResourceLoader*, const ResourceLoaderOptions&);
97
98    virtual void setEncoding(const String&) { }
99    virtual String encoding() const { return String(); }
100    virtual void addDataBuffer(ResourceBuffer*);
101    virtual void addData(const char* data, unsigned length);
102    virtual void finishLoading(ResourceBuffer*);
103    virtual void error(CachedResource::Status);
104
105    void setResourceError(const ResourceError& error) { m_error = error; }
106    const ResourceError& resourceError() const { return m_error; }
107
108    virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; }
109
110    ResourceRequest& resourceRequest() { return m_resourceRequest; }
111    const URL& url() const { return m_resourceRequest.url();}
112#if ENABLE(CACHE_PARTITIONING)
113    const String& cachePartition() const { return m_resourceRequest.cachePartition(); }
114#endif
115    SessionID sessionID() const { return m_sessionID; }
116    Type type() const { return static_cast<Type>(m_type); }
117
118    ResourceLoadPriority loadPriority() const { return m_loadPriority; }
119    void setLoadPriority(ResourceLoadPriority);
120
121    void addClient(CachedResourceClient*);
122    void removeClient(CachedResourceClient*);
123    bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); }
124    bool hasClient(CachedResourceClient* client) { return m_clients.contains(client) || m_clientsAwaitingCallback.contains(client); }
125    bool deleteIfPossible();
126
127    enum PreloadResult {
128        PreloadNotReferenced,
129        PreloadReferenced,
130        PreloadReferencedWhileLoading,
131        PreloadReferencedWhileComplete
132    };
133    PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); }
134
135    virtual void didAddClient(CachedResourceClient*);
136    virtual void didRemoveClient(CachedResourceClient*) { }
137    virtual void allClientsRemoved() { }
138    void destroyDecodedDataIfNeeded();
139
140    unsigned count() const { return m_clients.size(); }
141
142    Status status() const { return static_cast<Status>(m_status); }
143    void setStatus(Status status) { m_status = status; }
144
145    unsigned size() const { return encodedSize() + decodedSize() + overheadSize(); }
146    unsigned encodedSize() const { return m_encodedSize; }
147    unsigned decodedSize() const { return m_decodedSize; }
148    unsigned overheadSize() const;
149
150    bool isLoaded() const { return !m_loading; } // FIXME. Method name is inaccurate. Loading might not have started yet.
151
152    bool isLoading() const { return m_loading; }
153    void setLoading(bool b) { m_loading = b; }
154    virtual bool stillNeedsLoad() const { return false; }
155
156    SubresourceLoader* loader() { return m_loader.get(); }
157
158    bool isImage() const { return type() == ImageResource; }
159    // FIXME: CachedRawResource could be either a main resource or a raw XHR resource.
160    bool isMainOrRawResource() const { return type() == MainResource || type() == RawResource; }
161    bool ignoreForRequestCount() const
162    {
163        return type() == MainResource
164#if ENABLE(LINK_PREFETCH)
165            || type() == LinkPrefetch
166            || type() == LinkSubresource
167#endif
168            || type() == RawResource;
169    }
170
171    unsigned accessCount() const { return m_accessCount; }
172    void increaseAccessCount() { m_accessCount++; }
173
174    // Computes the status of an object after loading.
175    // Updates the expire date on the cache entry file
176    void finish();
177
178    bool passesAccessControlCheck(SecurityOrigin*);
179
180    // Called by the cache if the object has been removed from the cache
181    // while still being referenced. This means the object should delete itself
182    // if the number of clients observing it ever drops to 0.
183    // The resource can be brought back to cache after successful revalidation.
184    void setInCache(bool inCache) { m_inCache = inCache; }
185    bool inCache() const { return m_inCache; }
186
187    bool inLiveDecodedResourcesList() { return m_inLiveDecodedResourcesList; }
188
189    void clearLoader();
190
191    ResourceBuffer* resourceBuffer() const { ASSERT(!m_purgeableData); return m_data.get(); }
192
193    virtual void willSendRequest(ResourceRequest&, const ResourceResponse&) { m_requestedFromNetworkingLayer = true; }
194    virtual void responseReceived(const ResourceResponse&);
195    void setResponse(const ResourceResponse& response) { m_response = response; }
196    const ResourceResponse& response() const { return m_response; }
197
198    bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; }
199    bool hasOneHandle() const { return m_handleCount == 1; }
200
201    bool isExpired() const;
202
203    // List of acceptable MIME types separated by ",".
204    // A MIME type may contain a wildcard, e.g. "text/*".
205    String accept() const { return m_accept; }
206    void setAccept(const String& accept) { m_accept = accept; }
207
208    void cancelLoad();
209    bool wasCanceled() const { return m_error.isCancellation(); }
210    bool errorOccurred() const { return m_status == LoadError || m_status == DecodeError; }
211    bool loadFailedOrCanceled() { return !m_error.isNull(); }
212
213    bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks() == SendCallbacks; }
214    DataBufferingPolicy dataBufferingPolicy() const { return m_options.dataBufferingPolicy(); }
215
216    virtual void destroyDecodedData() { }
217
218    void setOwningCachedResourceLoader(CachedResourceLoader* cachedResourceLoader) { m_owningCachedResourceLoader = cachedResourceLoader; }
219
220    bool isPreloaded() const { return m_preloadCount; }
221    void increasePreloadCount() { ++m_preloadCount; }
222    void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; }
223
224    void registerHandle(CachedResourceHandleBase* h);
225    void unregisterHandle(CachedResourceHandleBase* h);
226
227    bool canUseCacheValidator() const;
228
229    virtual bool mustRevalidateDueToCacheHeaders(CachePolicy) const;
230
231    bool isCacheValidator() const { return m_resourceToRevalidate; }
232    CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; }
233
234    bool isPurgeable() const;
235    bool wasPurged() const;
236
237    // This is used by the archive machinery to get at a purged resource without
238    // triggering a load. We should make it protected again if we can find a
239    // better way to handle the archive case.
240    bool makePurgeable(bool purgeable);
241
242    // HTTP revalidation support methods for CachedResourceLoader.
243    void setResourceToRevalidate(CachedResource*);
244    virtual void switchClientsToRevalidatedResource();
245    void clearResourceToRevalidate();
246    void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse);
247
248    virtual void didSendData(unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { }
249
250    void setLoadFinishTime(double finishTime) { m_loadFinishTime = finishTime; }
251    double loadFinishTime() const { return m_loadFinishTime; }
252
253#if ENABLE(DISK_IMAGE_CACHE)
254    bool isUsingDiskImageCache() const;
255    virtual bool canUseDiskImageCache() const { return false; }
256    virtual void useDiskImageCache() { ASSERT(canUseDiskImageCache()); }
257#endif
258
259    virtual bool canReuse(const ResourceRequest&) const { return true; }
260
261#if USE(FOUNDATION)
262    void tryReplaceEncodedData(PassRefPtr<SharedBuffer>);
263#endif
264
265#if USE(SOUP)
266    virtual char* getOrCreateReadBuffer(size_t /* requestedSize */, size_t& /* actualSize */) { return 0; }
267#endif
268
269protected:
270    virtual void checkNotify();
271
272    void setEncodedSize(unsigned);
273    void setDecodedSize(unsigned);
274    void didAccessDecodedData(double timeStamp);
275
276    bool isSafeToMakePurgeable() const;
277
278    HashCountedSet<CachedResourceClient*> m_clients;
279
280    class CachedResourceCallback {
281    public:
282        static PassOwnPtr<CachedResourceCallback> schedule(CachedResource* resource, CachedResourceClient* client) { return adoptPtr(new CachedResourceCallback(resource, client)); }
283        void cancel();
284    private:
285        CachedResourceCallback(CachedResource*, CachedResourceClient*);
286        void timerFired(Timer<CachedResourceCallback>&);
287
288        CachedResource* m_resource;
289        CachedResourceClient* m_client;
290        Timer<CachedResourceCallback> m_callbackTimer;
291    };
292    HashMap<CachedResourceClient*, OwnPtr<CachedResourceCallback>> m_clientsAwaitingCallback;
293
294    ResourceRequest m_resourceRequest;
295    SessionID m_sessionID;
296    String m_accept;
297    RefPtr<SubresourceLoader> m_loader;
298    ResourceLoaderOptions m_options;
299    ResourceLoadPriority m_loadPriority;
300
301    ResourceResponse m_response;
302    double m_responseTimestamp;
303
304    RefPtr<ResourceBuffer> m_data;
305    OwnPtr<PurgeableBuffer> m_purgeableData;
306    DeferrableOneShotTimer m_decodedDataDeletionTimer;
307
308private:
309    bool addClientToSet(CachedResourceClient*);
310
311    void decodedDataDeletionTimerFired();
312
313    virtual PurgePriority purgePriority() const { return PurgeDefault; }
314    virtual bool mayTryReplaceEncodedData() const { return false; }
315
316    double currentAge() const;
317    double freshnessLifetime() const;
318
319    void addAdditionalRequestHeaders(CachedResourceLoader*);
320    void failBeforeStarting();
321
322    String m_fragmentIdentifierForRequest;
323
324    ResourceError m_error;
325
326    double m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache
327    double m_loadFinishTime;
328
329    unsigned m_encodedSize;
330    unsigned m_decodedSize;
331    unsigned m_accessCount;
332    unsigned m_handleCount;
333    unsigned m_preloadCount;
334
335    unsigned m_preloadResult : 2; // PreloadResult
336
337    bool m_inLiveDecodedResourcesList : 1;
338    bool m_requestedFromNetworkingLayer : 1;
339
340    bool m_inCache : 1;
341    bool m_loading : 1;
342
343    bool m_switchingClientsToRevalidatedResource : 1;
344
345    unsigned m_type : 4; // Type
346    unsigned m_status : 3; // Status
347
348#ifndef NDEBUG
349    bool m_deleted;
350    unsigned m_lruIndex;
351#endif
352
353    CachedResource* m_nextInAllResourcesList;
354    CachedResource* m_prevInAllResourcesList;
355
356    CachedResource* m_nextInLiveResourcesList;
357    CachedResource* m_prevInLiveResourcesList;
358
359    CachedResourceLoader* m_owningCachedResourceLoader; // only non-0 for resources that are not in the cache
360
361    // If this field is non-null we are using the resource as a proxy for checking whether an existing resource is still up to date
362    // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved
363    // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this
364    // resources becomes normal resource load.
365    CachedResource* m_resourceToRevalidate;
366
367    // If this field is non-null, the resource has a proxy for checking whether it is still up to date (see m_resourceToRevalidate).
368    CachedResource* m_proxyResource;
369
370    // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response.
371    HashSet<CachedResourceHandleBase*> m_handlesToRevalidate;
372};
373
374#define CACHED_RESOURCE_TYPE_CASTS(ToClassName, FromClassName, CachedResourceType) \
375    TYPE_CASTS_BASE(ToClassName, FromClassName, resource, resource->type() == CachedResourceType, resource.type() == CachedResourceType)
376
377}
378
379#endif
380