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