| 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 | #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 | AtomicString 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 | AtomicString 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 | |