1 | /* |
2 | Copyright (C) 1998 Lars Knoll ([email protected]) |
3 | Copyright (C) 2001 Dirk Mueller <[email protected]> |
4 | Copyright (C) 2006 Samuel Weinig ([email protected]) |
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 | #pragma once |
24 | |
25 | #include "CachePolicy.h" |
26 | #include "CacheValidation.h" |
27 | #include "FrameLoaderTypes.h" |
28 | #include "ResourceError.h" |
29 | #include "ResourceLoadPriority.h" |
30 | #include "ResourceLoaderOptions.h" |
31 | #include "ResourceRequest.h" |
32 | #include "ResourceResponse.h" |
33 | #include "Timer.h" |
34 | #include <pal/SessionID.h> |
35 | #include <time.h> |
36 | #include <wtf/HashCountedSet.h> |
37 | #include <wtf/HashSet.h> |
38 | #include <wtf/TypeCasts.h> |
39 | #include <wtf/Vector.h> |
40 | #include <wtf/text/WTFString.h> |
41 | |
42 | namespace WebCore { |
43 | |
44 | class CachedResourceClient; |
45 | class CachedResourceHandleBase; |
46 | class CachedResourceLoader; |
47 | class CachedResourceRequest; |
48 | class CookieJar; |
49 | class LoadTiming; |
50 | class MemoryCache; |
51 | class SecurityOrigin; |
52 | class SharedBuffer; |
53 | class SubresourceLoader; |
54 | class TextResourceDecoder; |
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. |
59 | class CachedResource { |
60 | WTF_MAKE_NONCOPYABLE(CachedResource); WTF_MAKE_FAST_ALLOCATED; |
61 | friend class MemoryCache; |
62 | |
63 | public: |
64 | enum class Type : uint8_t { |
65 | MainResource, |
66 | ImageResource, |
67 | CSSStyleSheet, |
68 | Script, |
69 | FontResource, |
70 | #if ENABLE(SVG_FONTS) |
71 | SVGFontResource, |
72 | #endif |
73 | MediaResource, |
74 | RawResource, |
75 | Icon, |
76 | Beacon, |
77 | Ping, |
78 | SVGDocumentResource |
79 | #if ENABLE(XSLT) |
80 | , XSLStyleSheet |
81 | #endif |
82 | , LinkPrefetch |
83 | #if ENABLE(VIDEO_TRACK) |
84 | , TextTrackResource |
85 | #endif |
86 | #if ENABLE(APPLICATION_MANIFEST) |
87 | , ApplicationManifest |
88 | #endif |
89 | }; |
90 | |
91 | enum Status { |
92 | Unknown, // let cache decide what to do with it |
93 | Pending, // only partially loaded |
94 | Cached, // regular case |
95 | LoadError, |
96 | DecodeError |
97 | }; |
98 | |
99 | CachedResource(CachedResourceRequest&&, Type, const PAL::SessionID&, const CookieJar*); |
100 | virtual ~CachedResource(); |
101 | |
102 | virtual void load(CachedResourceLoader&); |
103 | |
104 | virtual void setEncoding(const String&) { } |
105 | virtual String encoding() const { return String(); } |
106 | virtual const TextResourceDecoder* textResourceDecoder() const { return nullptr; } |
107 | virtual void updateBuffer(SharedBuffer&); |
108 | virtual void updateData(const char* data, unsigned length); |
109 | virtual void finishLoading(SharedBuffer*); |
110 | virtual void error(CachedResource::Status); |
111 | |
112 | void setResourceError(const ResourceError& error) { m_error = error; } |
113 | const ResourceError& resourceError() const { return m_error; } |
114 | |
115 | virtual bool shouldIgnoreHTTPStatusCodeErrors() const { return false; } |
116 | |
117 | const ResourceRequest& resourceRequest() const { return m_resourceRequest; } |
118 | const URL& url() const { return m_resourceRequest.url();} |
119 | const String& cachePartition() const { return m_resourceRequest.cachePartition(); } |
120 | PAL::SessionID sessionID() const { return m_sessionID; } |
121 | const CookieJar* cookieJar() const { return m_cookieJar.get(); } |
122 | Type type() const { return m_type; } |
123 | String mimeType() const { return m_response.mimeType(); } |
124 | long long expectedContentLength() const { return m_response.expectedContentLength(); } |
125 | |
126 | static bool shouldUsePingLoad(Type type) { return type == Type::Beacon || type == Type::Ping; } |
127 | |
128 | ResourceLoadPriority loadPriority() const { return m_loadPriority; } |
129 | void setLoadPriority(const Optional<ResourceLoadPriority>&); |
130 | |
131 | WEBCORE_EXPORT void addClient(CachedResourceClient&); |
132 | WEBCORE_EXPORT void removeClient(CachedResourceClient&); |
133 | bool hasClients() const { return !m_clients.isEmpty() || !m_clientsAwaitingCallback.isEmpty(); } |
134 | bool hasClient(CachedResourceClient& client) { return m_clients.contains(&client) || m_clientsAwaitingCallback.contains(&client); } |
135 | bool deleteIfPossible(); |
136 | |
137 | enum class PreloadResult : uint8_t { |
138 | PreloadNotReferenced, |
139 | PreloadReferenced, |
140 | PreloadReferencedWhileLoading, |
141 | PreloadReferencedWhileComplete |
142 | }; |
143 | PreloadResult preloadResult() const { return static_cast<PreloadResult>(m_preloadResult); } |
144 | |
145 | virtual void didAddClient(CachedResourceClient&); |
146 | virtual void didRemoveClient(CachedResourceClient&) { } |
147 | virtual void allClientsRemoved(); |
148 | void destroyDecodedDataIfNeeded(); |
149 | |
150 | unsigned numberOfClients() const { return m_clients.size(); } |
151 | |
152 | Status status() const { return static_cast<Status>(m_status); } |
153 | void setStatus(Status status) { m_status = status; } |
154 | |
155 | unsigned size() const { return encodedSize() + decodedSize() + overheadSize(); } |
156 | unsigned encodedSize() const { return m_encodedSize; } |
157 | unsigned decodedSize() const { return m_decodedSize; } |
158 | unsigned overheadSize() const; |
159 | |
160 | bool isLoaded() const { return !m_loading; } // FIXME. Method name is inaccurate. Loading might not have started yet. |
161 | |
162 | bool isLoading() const { return m_loading; } |
163 | void setLoading(bool b) { m_loading = b; } |
164 | virtual bool stillNeedsLoad() const { return false; } |
165 | |
166 | SubresourceLoader* loader() { return m_loader.get(); } |
167 | |
168 | bool areAllClientsXMLHttpRequests() const; |
169 | |
170 | bool isImage() const { return type() == Type::ImageResource; } |
171 | // FIXME: CachedRawResource could be a main resource, an audio/video resource, or a raw XHR/icon resource. |
172 | bool isMainOrMediaOrIconOrRawResource() const { return type() == Type::MainResource || type() == Type::MediaResource || type() == Type::Icon || type() == Type::RawResource || type() == Type::Beacon || type() == Type::Ping; } |
173 | |
174 | // Whether this request should impact request counting and delay window.onload. |
175 | bool ignoreForRequestCount() const |
176 | { |
177 | return m_ignoreForRequestCount |
178 | || type() == Type::MainResource |
179 | || type() == Type::LinkPrefetch |
180 | || type() == Type::Beacon |
181 | || type() == Type::Ping |
182 | || type() == Type::Icon |
183 | || type() == Type::RawResource; |
184 | } |
185 | |
186 | void setIgnoreForRequestCount(bool ignoreForRequestCount) { m_ignoreForRequestCount = ignoreForRequestCount; } |
187 | |
188 | unsigned accessCount() const { return m_accessCount; } |
189 | void increaseAccessCount() { m_accessCount++; } |
190 | |
191 | // Computes the status of an object after loading. |
192 | // Updates the expire date on the cache entry file |
193 | void finish(); |
194 | |
195 | // Called by the cache if the object has been removed from the cache |
196 | // while still being referenced. This means the object should delete itself |
197 | // if the number of clients observing it ever drops to 0. |
198 | // The resource can be brought back to cache after successful revalidation. |
199 | void setInCache(bool inCache) { m_inCache = inCache; } |
200 | bool inCache() const { return m_inCache; } |
201 | |
202 | void clearLoader(); |
203 | |
204 | SharedBuffer* resourceBuffer() const { return m_data.get(); } |
205 | |
206 | virtual void redirectReceived(ResourceRequest&&, const ResourceResponse&, CompletionHandler<void(ResourceRequest&&)>&&); |
207 | virtual void responseReceived(const ResourceResponse&); |
208 | virtual bool shouldCacheResponse(const ResourceResponse&) { return true; } |
209 | void setResponse(const ResourceResponse&); |
210 | const ResourceResponse& response() const { return m_response; } |
211 | |
212 | void setCrossOrigin(); |
213 | bool isCrossOrigin() const; |
214 | bool () const; |
215 | ResourceResponse::Tainting responseTainting() const { return m_responseTainting; } |
216 | |
217 | void loadFrom(const CachedResource&); |
218 | |
219 | SecurityOrigin* origin() const { return m_origin.get(); } |
220 | AtomString initiatorName() const { return m_initiatorName; } |
221 | |
222 | bool canDelete() const { return !hasClients() && !m_loader && !m_preloadCount && !m_handleCount && !m_resourceToRevalidate && !m_proxyResource; } |
223 | bool hasOneHandle() const { return m_handleCount == 1; } |
224 | |
225 | bool isExpired() const; |
226 | |
227 | void cancelLoad(); |
228 | bool wasCanceled() const { return m_error.isCancellation(); } |
229 | bool errorOccurred() const { return m_status == LoadError || m_status == DecodeError; } |
230 | bool loadFailedOrCanceled() const { return !m_error.isNull(); } |
231 | |
232 | bool shouldSendResourceLoadCallbacks() const { return m_options.sendLoadCallbacks == SendCallbackPolicy::SendCallbacks; } |
233 | DataBufferingPolicy dataBufferingPolicy() const { return m_options.dataBufferingPolicy; } |
234 | |
235 | bool allowsCaching() const { return m_options.cachingPolicy == CachingPolicy::AllowCaching; } |
236 | const ResourceLoaderOptions& options() const { return m_options; } |
237 | |
238 | virtual void destroyDecodedData() { } |
239 | |
240 | bool isPreloaded() const { return m_preloadCount; } |
241 | void increasePreloadCount() { ++m_preloadCount; } |
242 | void decreasePreloadCount() { ASSERT(m_preloadCount); --m_preloadCount; } |
243 | bool isLinkPreload() { return m_isLinkPreload; } |
244 | void setLinkPreload() { m_isLinkPreload = true; } |
245 | bool hasUnknownEncoding() { return m_hasUnknownEncoding; } |
246 | void setHasUnknownEncoding(bool hasUnknownEncoding) { m_hasUnknownEncoding = hasUnknownEncoding; } |
247 | |
248 | void registerHandle(CachedResourceHandleBase*); |
249 | WEBCORE_EXPORT void unregisterHandle(CachedResourceHandleBase*); |
250 | |
251 | bool canUseCacheValidator() const; |
252 | |
253 | enum class RevalidationDecision { No, YesDueToCachePolicy, YesDueToNoStore, YesDueToNoCache, YesDueToExpired }; |
254 | virtual RevalidationDecision makeRevalidationDecision(CachePolicy) const; |
255 | bool redirectChainAllowsReuse(ReuseExpiredRedirectionOrNot) const; |
256 | bool hasRedirections() const { return m_redirectChainCacheStatus.status != RedirectChainCacheStatus::Status::NoRedirection; } |
257 | |
258 | bool (const ResourceRequest&); |
259 | |
260 | bool isCacheValidator() const { return m_resourceToRevalidate; } |
261 | CachedResource* resourceToRevalidate() const { return m_resourceToRevalidate; } |
262 | |
263 | // HTTP revalidation support methods for CachedResourceLoader. |
264 | void setResourceToRevalidate(CachedResource*); |
265 | virtual void switchClientsToRevalidatedResource(); |
266 | void clearResourceToRevalidate(); |
267 | void updateResponseAfterRevalidation(const ResourceResponse& validatingResponse); |
268 | bool validationInProgress() const { return m_proxyResource; } |
269 | bool validationCompleting() const { return m_proxyResource && m_proxyResource->m_switchingClientsToRevalidatedResource; } |
270 | |
271 | virtual void didSendData(unsigned long long /* bytesSent */, unsigned long long /* totalBytesToBeSent */) { } |
272 | |
273 | #if USE(FOUNDATION) || USE(SOUP) |
274 | WEBCORE_EXPORT void tryReplaceEncodedData(SharedBuffer&); |
275 | #endif |
276 | |
277 | unsigned long identifierForLoadWithoutResourceLoader() const { return m_identifierForLoadWithoutResourceLoader; } |
278 | static ResourceLoadPriority defaultPriorityForResourceType(Type); |
279 | |
280 | void setOriginalRequest(std::unique_ptr<ResourceRequest>&& originalRequest) { m_originalRequest = WTFMove(originalRequest); } |
281 | const std::unique_ptr<ResourceRequest>& originalRequest() const { return m_originalRequest; } |
282 | |
283 | protected: |
284 | // CachedResource constructor that may be used when the CachedResource can already be filled with response data. |
285 | CachedResource(const URL&, Type, const PAL::SessionID&, const CookieJar*); |
286 | |
287 | void setEncodedSize(unsigned); |
288 | void setDecodedSize(unsigned); |
289 | void didAccessDecodedData(MonotonicTime timeStamp); |
290 | |
291 | virtual void didReplaceSharedBufferContents() { } |
292 | |
293 | virtual void setBodyDataFrom(const CachedResource&); |
294 | |
295 | private: |
296 | class Callback; |
297 | |
298 | bool addClientToSet(CachedResourceClient&); |
299 | |
300 | void decodedDataDeletionTimerFired(); |
301 | |
302 | virtual void checkNotify(); |
303 | virtual bool mayTryReplaceEncodedData() const { return false; } |
304 | |
305 | Seconds freshnessLifetime(const ResourceResponse&) const; |
306 | |
307 | void (CachedResourceLoader&); |
308 | void failBeforeStarting(); |
309 | |
310 | protected: |
311 | ResourceLoaderOptions m_options; |
312 | ResourceRequest m_resourceRequest; |
313 | ResourceResponse m_response; |
314 | |
315 | DeferrableOneShotTimer m_decodedDataDeletionTimer; |
316 | |
317 | // FIXME: Make the rest of these data members private and use functions in derived classes instead. |
318 | HashCountedSet<CachedResourceClient*> m_clients; |
319 | std::unique_ptr<ResourceRequest> m_originalRequest; // Needed by Ping loads. |
320 | RefPtr<SubresourceLoader> m_loader; |
321 | RefPtr<SharedBuffer> m_data; |
322 | |
323 | private: |
324 | MonotonicTime m_lastDecodedAccessTime; // Used as a "thrash guard" in the cache |
325 | PAL::SessionID m_sessionID; |
326 | RefPtr<const CookieJar> m_cookieJar; |
327 | WallTime m_responseTimestamp; |
328 | unsigned long m_identifierForLoadWithoutResourceLoader { 0 }; |
329 | |
330 | HashMap<CachedResourceClient*, std::unique_ptr<Callback>> m_clientsAwaitingCallback; |
331 | |
332 | // These handles will need to be updated to point to the m_resourceToRevalidate in case we get 304 response. |
333 | HashSet<CachedResourceHandleBase*> m_handlesToRevalidate; |
334 | |
335 | Vector<std::pair<String, String>> ; |
336 | |
337 | // 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 |
338 | // using HTTP If-Modified-Since/If-None-Match headers. If the response is 304 all clients of this resource are moved |
339 | // to to be clients of m_resourceToRevalidate and the resource is deleted. If not, the field is zeroed and this |
340 | // resources becomes normal resource load. |
341 | CachedResource* m_resourceToRevalidate { nullptr }; |
342 | |
343 | // If this field is non-null, the resource has a proxy for checking whether it is still up to date (see m_resourceToRevalidate). |
344 | CachedResource* m_proxyResource { nullptr }; |
345 | |
346 | String m_fragmentIdentifierForRequest; |
347 | |
348 | ResourceError m_error; |
349 | RefPtr<SecurityOrigin> m_origin; |
350 | AtomString m_initiatorName; |
351 | |
352 | RedirectChainCacheStatus m_redirectChainCacheStatus; |
353 | |
354 | unsigned m_encodedSize { 0 }; |
355 | unsigned m_decodedSize { 0 }; |
356 | unsigned m_accessCount { 0 }; |
357 | unsigned m_handleCount { 0 }; |
358 | unsigned m_preloadCount { 0 }; |
359 | |
360 | unsigned m_status { Pending }; // Status |
361 | |
362 | PreloadResult m_preloadResult { PreloadResult::PreloadNotReferenced }; |
363 | |
364 | ResourceResponse::Tainting m_responseTainting { ResourceResponse::Tainting::Basic }; |
365 | ResourceLoadPriority m_loadPriority; |
366 | |
367 | Type m_type; // Type |
368 | |
369 | bool m_requestedFromNetworkingLayer { false }; |
370 | bool m_inCache { false }; |
371 | bool m_loading { false }; |
372 | bool m_isLinkPreload { false }; |
373 | bool m_hasUnknownEncoding { false }; |
374 | bool m_switchingClientsToRevalidatedResource { false }; |
375 | bool m_ignoreForRequestCount { false }; |
376 | |
377 | #ifndef NDEBUG |
378 | bool m_deleted { false }; |
379 | unsigned m_lruIndex { 0 }; |
380 | #endif |
381 | }; |
382 | |
383 | class CachedResource::Callback { |
384 | #if !COMPILER(MSVC) |
385 | WTF_MAKE_FAST_ALLOCATED; |
386 | #endif |
387 | public: |
388 | Callback(CachedResource&, CachedResourceClient&); |
389 | |
390 | void cancel(); |
391 | |
392 | private: |
393 | void timerFired(); |
394 | |
395 | CachedResource& m_resource; |
396 | CachedResourceClient& m_client; |
397 | Timer m_timer; |
398 | }; |
399 | |
400 | } // namespace WebCore |
401 | |
402 | #define SPECIALIZE_TYPE_TRAITS_CACHED_RESOURCE(ToClassName, CachedResourceType) \ |
403 | SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToClassName) \ |
404 | static bool isType(const WebCore::CachedResource& resource) { return resource.type() == WebCore::CachedResourceType; } \ |
405 | SPECIALIZE_TYPE_TRAITS_END() |
406 | |