1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ 6 #define WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ 7 8 #include <map> 9 #include <vector> 10 11 #include "base/basictypes.h" 12 #include "base/compiler_specific.h" 13 #include "base/gtest_prod_util.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "net/base/completion_callback.h" 17 #include "webkit/browser/appcache/appcache_working_set.h" 18 #include "webkit/browser/webkit_storage_browser_export.h" 19 20 class GURL; 21 22 namespace content { 23 FORWARD_DECLARE_TEST(AppCacheStorageTest, DelegateReferences); 24 FORWARD_DECLARE_TEST(AppCacheStorageTest, UsageMap); 25 class AppCacheQuotaClientTest; 26 class AppCacheResponseTest; 27 class AppCacheStorageTest; 28 } 29 30 namespace appcache { 31 32 class AppCache; 33 class AppCacheEntry; 34 class AppCacheGroup; 35 class AppCacheResponseReader; 36 class AppCacheResponseWriter; 37 class AppCacheServiceImpl; 38 struct AppCacheInfoCollection; 39 struct HttpResponseInfoIOBuffer; 40 41 class WEBKIT_STORAGE_BROWSER_EXPORT AppCacheStorage { 42 public: 43 typedef std::map<GURL, int64> UsageMap; 44 45 class WEBKIT_STORAGE_BROWSER_EXPORT Delegate { 46 public: 47 // If retrieval fails, 'collection' will be NULL. OnAllInfo(AppCacheInfoCollection * collection)48 virtual void OnAllInfo(AppCacheInfoCollection* collection) {} 49 50 // If a load fails the 'cache' will be NULL. OnCacheLoaded(AppCache * cache,int64 cache_id)51 virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) {} 52 53 // If a load fails the 'group' will be NULL. OnGroupLoaded(AppCacheGroup * group,const GURL & manifest_url)54 virtual void OnGroupLoaded( 55 AppCacheGroup* group, const GURL& manifest_url) {} 56 57 // If successfully stored 'success' will be true. OnGroupAndNewestCacheStored(AppCacheGroup * group,AppCache * newest_cache,bool success,bool would_exceed_quota)58 virtual void OnGroupAndNewestCacheStored( 59 AppCacheGroup* group, AppCache* newest_cache, bool success, 60 bool would_exceed_quota) {} 61 62 // If the operation fails, success will be false. OnGroupMadeObsolete(AppCacheGroup * group,bool success,int response_code)63 virtual void OnGroupMadeObsolete(AppCacheGroup* group, 64 bool success, 65 int response_code) {} 66 67 // If a load fails the 'response_info' will be NULL. OnResponseInfoLoaded(AppCacheResponseInfo * response_info,int64 response_id)68 virtual void OnResponseInfoLoaded( 69 AppCacheResponseInfo* response_info, int64 response_id) {} 70 71 // If no response is found, entry.response_id() and 72 // fallback_entry.response_id() will be kAppCacheNoResponseId. 73 // If the response is the entry for an intercept or fallback 74 // namespace, the url of the namespece entry is returned. 75 // If a response is found, the cache id and manifest url of the 76 // containing cache and group are also returned. OnMainResponseFound(const GURL & url,const AppCacheEntry & entry,const GURL & namespace_entry_url,const AppCacheEntry & fallback_entry,int64 cache_id,int64 group_id,const GURL & mainfest_url)77 virtual void OnMainResponseFound( 78 const GURL& url, const AppCacheEntry& entry, 79 const GURL& namespace_entry_url, const AppCacheEntry& fallback_entry, 80 int64 cache_id, int64 group_id, const GURL& mainfest_url) {} 81 82 protected: ~Delegate()83 virtual ~Delegate() {} 84 }; 85 86 explicit AppCacheStorage(AppCacheServiceImpl* service); 87 virtual ~AppCacheStorage(); 88 89 // Schedules a task to retrieve basic info about all groups and caches 90 // stored in the system. Upon completion the delegate will be called 91 // with the results. 92 virtual void GetAllInfo(Delegate* delegate) = 0; 93 94 // Schedules a cache to be loaded from storage. Upon load completion 95 // the delegate will be called back. If the cache already resides in 96 // memory, the delegate will be called back immediately without returning 97 // to the message loop. If the load fails, the delegate will be called 98 // back with a NULL cache pointer. 99 virtual void LoadCache(int64 id, Delegate* delegate) = 0; 100 101 // Schedules a group and its newest cache, if any, to be loaded from storage. 102 // Upon load completion the delegate will be called back. If the group 103 // and newest cache already reside in memory, the delegate will be called 104 // back immediately without returning to the message loop. If the load fails, 105 // the delegate will be called back with a NULL group pointer. 106 virtual void LoadOrCreateGroup( 107 const GURL& manifest_url, Delegate* delegate) = 0; 108 109 // Schedules response info to be loaded from storage. 110 // Upon load completion the delegate will be called back. If the data 111 // already resides in memory, the delegate will be called back 112 // immediately without returning to the message loop. If the load fails, 113 // the delegate will be called back with a NULL pointer. 114 virtual void LoadResponseInfo( 115 const GURL& manifest_url, int64 group_id, int64 response_id, 116 Delegate* delegate); 117 118 // Schedules a group and its newest complete cache to be initially stored or 119 // incrementally updated with new changes. Upon completion the delegate 120 // will be called back. A group without a newest cache cannot be stored. 121 // It's a programming error to call this method without a newest cache. A 122 // side effect of storing a new newest cache is the removal of the group's 123 // old caches and responses from persistent storage (although they may still 124 // linger in the in-memory working set until no longer needed). The new 125 // cache will be added as the group's newest complete cache only if storage 126 // succeeds. 127 virtual void StoreGroupAndNewestCache( 128 AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) = 0; 129 130 // Schedules a query to identify a response for a main request. Upon 131 // completion the delegate will be called back. 132 virtual void FindResponseForMainRequest( 133 const GURL& url, 134 const GURL& preferred_manifest_url, 135 Delegate* delegate) = 0; 136 137 // Performs an immediate lookup of the in-memory cache to 138 // identify a response for a sub resource request. 139 virtual void FindResponseForSubRequest( 140 AppCache* cache, const GURL& url, 141 AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry, 142 bool* found_network_namespace) = 0; 143 144 // Immediately updates in-memory storage, if the cache is in memory, 145 // and schedules a task to update persistent storage. If the cache is 146 // already scheduled to be loaded, upon loading completion the entry 147 // will be marked. There is no delegate completion callback. 148 virtual void MarkEntryAsForeign(const GURL& entry_url, int64 cache_id) = 0; 149 150 // Schedules a task to update persistent storage and doom the group and all 151 // related caches and responses for deletion. Upon completion the in-memory 152 // instance is marked as obsolete and the delegate callback is called. 153 virtual void MakeGroupObsolete(AppCacheGroup* group, 154 Delegate* delegate, 155 int response_code) = 0; 156 157 // Cancels all pending callbacks for the delegate. The delegate callbacks 158 // will not be invoked after, however any scheduled operations will still 159 // take place. The callbacks for subsequently scheduled operations are 160 // unaffected. CancelDelegateCallbacks(Delegate * delegate)161 void CancelDelegateCallbacks(Delegate* delegate) { 162 DelegateReference* delegate_reference = GetDelegateReference(delegate); 163 if (delegate_reference) 164 delegate_reference->CancelReference(); 165 } 166 167 // Creates a reader to read a response from storage. 168 virtual AppCacheResponseReader* CreateResponseReader( 169 const GURL& manifest_url, int64 group_id, int64 response_id) = 0; 170 171 // Creates a writer to write a new response to storage. This call 172 // establishes a new response id. 173 virtual AppCacheResponseWriter* CreateResponseWriter( 174 const GURL& manifest_url, int64 group_id) = 0; 175 176 // Schedules the lazy deletion of responses and saves the ids 177 // persistently such that the responses will be deleted upon restart 178 // if they aren't deleted prior to shutdown. 179 virtual void DoomResponses( 180 const GURL& manifest_url, const std::vector<int64>& response_ids) = 0; 181 182 // Schedules the lazy deletion of responses without persistently saving 183 // the response ids. 184 virtual void DeleteResponses( 185 const GURL& manifest_url, const std::vector<int64>& response_ids) = 0; 186 187 // Generates unique storage ids for different object types. NewCacheId()188 int64 NewCacheId() { 189 return ++last_cache_id_; 190 } NewGroupId()191 int64 NewGroupId() { 192 return ++last_group_id_; 193 } 194 195 // The working set of object instances currently in memory. working_set()196 AppCacheWorkingSet* working_set() { return &working_set_; } 197 198 // A map of origins to usage. usage_map()199 const UsageMap* usage_map() { return &usage_map_; } 200 201 // Simple ptr back to the service object that owns us. service()202 AppCacheServiceImpl* service() { return service_; } 203 204 protected: 205 friend class content::AppCacheQuotaClientTest; 206 friend class content::AppCacheResponseTest; 207 friend class content::AppCacheStorageTest; 208 209 // Helper to call a collection of delegates. 210 #define FOR_EACH_DELEGATE(delegates, func_and_args) \ 211 do { \ 212 for (DelegateReferenceVector::iterator it = delegates.begin(); \ 213 it != delegates.end(); ++it) { \ 214 if (it->get()->delegate) \ 215 it->get()->delegate->func_and_args; \ 216 } \ 217 } while (0) 218 219 // Helper used to manage multiple references to a 'delegate' and to 220 // allow all pending callbacks to that delegate to be easily cancelled. 221 struct DelegateReference : public base::RefCounted<DelegateReference> { 222 Delegate* delegate; 223 AppCacheStorage* storage; 224 225 DelegateReference(Delegate* delegate, AppCacheStorage* storage); 226 CancelReferenceDelegateReference227 void CancelReference() { 228 storage->delegate_references_.erase(delegate); 229 storage = NULL; 230 delegate = NULL; 231 } 232 233 private: 234 friend class base::RefCounted<DelegateReference>; 235 236 virtual ~DelegateReference(); 237 }; 238 typedef std::map<Delegate*, DelegateReference*> DelegateReferenceMap; 239 typedef std::vector<scoped_refptr<DelegateReference> > 240 DelegateReferenceVector; 241 242 // Helper used to manage an async LoadResponseInfo calls on behalf of 243 // multiple callers. 244 class ResponseInfoLoadTask { 245 public: 246 ResponseInfoLoadTask(const GURL& manifest_url, int64 group_id, 247 int64 response_id, AppCacheStorage* storage); 248 ~ResponseInfoLoadTask(); 249 response_id()250 int64 response_id() const { return response_id_; } manifest_url()251 const GURL& manifest_url() const { return manifest_url_; } group_id()252 int64 group_id() const { return group_id_; } 253 AddDelegate(DelegateReference * delegate_reference)254 void AddDelegate(DelegateReference* delegate_reference) { 255 delegates_.push_back(delegate_reference); 256 } 257 258 void StartIfNeeded(); 259 260 private: 261 void OnReadComplete(int result); 262 263 AppCacheStorage* storage_; 264 GURL manifest_url_; 265 int64 group_id_; 266 int64 response_id_; 267 scoped_ptr<AppCacheResponseReader> reader_; 268 DelegateReferenceVector delegates_; 269 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_; 270 }; 271 272 typedef std::map<int64, ResponseInfoLoadTask*> PendingResponseInfoLoads; 273 GetDelegateReference(Delegate * delegate)274 DelegateReference* GetDelegateReference(Delegate* delegate) { 275 DelegateReferenceMap::iterator iter = 276 delegate_references_.find(delegate); 277 if (iter != delegate_references_.end()) 278 return iter->second; 279 return NULL; 280 } 281 GetOrCreateDelegateReference(Delegate * delegate)282 DelegateReference* GetOrCreateDelegateReference(Delegate* delegate) { 283 DelegateReference* reference = GetDelegateReference(delegate); 284 if (reference) 285 return reference; 286 return new DelegateReference(delegate, this); 287 } 288 GetOrCreateResponseInfoLoadTask(const GURL & manifest_url,int64 group_id,int64 response_id)289 ResponseInfoLoadTask* GetOrCreateResponseInfoLoadTask( 290 const GURL& manifest_url, int64 group_id, int64 response_id) { 291 PendingResponseInfoLoads::iterator iter = 292 pending_info_loads_.find(response_id); 293 if (iter != pending_info_loads_.end()) 294 return iter->second; 295 return new ResponseInfoLoadTask(manifest_url, group_id, response_id, this); 296 } 297 298 // Should only be called when creating a new response writer. NewResponseId()299 int64 NewResponseId() { 300 return ++last_response_id_; 301 } 302 303 // Helpers to query and notify the QuotaManager. 304 void UpdateUsageMapAndNotify(const GURL& origin, int64 new_usage); 305 void ClearUsageMapAndNotify(); 306 void NotifyStorageAccessed(const GURL& origin); 307 308 // The last storage id used for different object types. 309 int64 last_cache_id_; 310 int64 last_group_id_; 311 int64 last_response_id_; 312 313 UsageMap usage_map_; // maps origin to usage 314 AppCacheWorkingSet working_set_; 315 AppCacheServiceImpl* service_; 316 DelegateReferenceMap delegate_references_; 317 PendingResponseInfoLoads pending_info_loads_; 318 319 // The set of last ids must be retrieved from storage prior to being used. 320 static const int64 kUnitializedId; 321 322 FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, DelegateReferences); 323 FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, UsageMap); 324 325 DISALLOW_COPY_AND_ASSIGN(AppCacheStorage); 326 }; 327 328 } // namespace appcache 329 330 #endif // WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ 331