| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ |
| #define WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ |
| |
| #include <map> |
| #include <vector> |
| |
| #include "base/basictypes.h" |
| #include "base/compiler_specific.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "net/base/completion_callback.h" |
| #include "webkit/browser/appcache/appcache_working_set.h" |
| #include "webkit/browser/webkit_storage_browser_export.h" |
| |
| class GURL; |
| |
| namespace content { |
| FORWARD_DECLARE_TEST(AppCacheStorageTest, DelegateReferences); |
| FORWARD_DECLARE_TEST(AppCacheStorageTest, UsageMap); |
| class AppCacheQuotaClientTest; |
| class AppCacheResponseTest; |
| class AppCacheStorageTest; |
| } |
| |
| namespace appcache { |
| |
| class AppCache; |
| class AppCacheEntry; |
| class AppCacheGroup; |
| class AppCacheResponseReader; |
| class AppCacheResponseWriter; |
| class AppCacheServiceImpl; |
| struct AppCacheInfoCollection; |
| struct HttpResponseInfoIOBuffer; |
| |
| class WEBKIT_STORAGE_BROWSER_EXPORT AppCacheStorage { |
| public: |
| typedef std::map<GURL, int64> UsageMap; |
| |
| class WEBKIT_STORAGE_BROWSER_EXPORT Delegate { |
| public: |
| // If retrieval fails, 'collection' will be NULL. |
| virtual void OnAllInfo(AppCacheInfoCollection* collection) {} |
| |
| // If a load fails the 'cache' will be NULL. |
| virtual void OnCacheLoaded(AppCache* cache, int64 cache_id) {} |
| |
| // If a load fails the 'group' will be NULL. |
| virtual void OnGroupLoaded( |
| AppCacheGroup* group, const GURL& manifest_url) {} |
| |
| // If successfully stored 'success' will be true. |
| virtual void OnGroupAndNewestCacheStored( |
| AppCacheGroup* group, AppCache* newest_cache, bool success, |
| bool would_exceed_quota) {} |
| |
| // If the operation fails, success will be false. |
| virtual void OnGroupMadeObsolete(AppCacheGroup* group, |
| bool success, |
| int response_code) {} |
| |
| // If a load fails the 'response_info' will be NULL. |
| virtual void OnResponseInfoLoaded( |
| AppCacheResponseInfo* response_info, int64 response_id) {} |
| |
| // If no response is found, entry.response_id() and |
| // fallback_entry.response_id() will be kAppCacheNoResponseId. |
| // If the response is the entry for an intercept or fallback |
| // namespace, the url of the namespece entry is returned. |
| // If a response is found, the cache id and manifest url of the |
| // containing cache and group are also returned. |
| virtual void 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) {} |
| |
| protected: |
| virtual ~Delegate() {} |
| }; |
| |
| explicit AppCacheStorage(AppCacheServiceImpl* service); |
| virtual ~AppCacheStorage(); |
| |
| // Schedules a task to retrieve basic info about all groups and caches |
| // stored in the system. Upon completion the delegate will be called |
| // with the results. |
| virtual void GetAllInfo(Delegate* delegate) = 0; |
| |
| // Schedules a cache to be loaded from storage. Upon load completion |
| // the delegate will be called back. If the cache already resides in |
| // memory, the delegate will be called back immediately without returning |
| // to the message loop. If the load fails, the delegate will be called |
| // back with a NULL cache pointer. |
| virtual void LoadCache(int64 id, Delegate* delegate) = 0; |
| |
| // Schedules a group and its newest cache, if any, to be loaded from storage. |
| // Upon load completion the delegate will be called back. If the group |
| // and newest cache already reside in memory, the delegate will be called |
| // back immediately without returning to the message loop. If the load fails, |
| // the delegate will be called back with a NULL group pointer. |
| virtual void LoadOrCreateGroup( |
| const GURL& manifest_url, Delegate* delegate) = 0; |
| |
| // Schedules response info to be loaded from storage. |
| // Upon load completion the delegate will be called back. If the data |
| // already resides in memory, the delegate will be called back |
| // immediately without returning to the message loop. If the load fails, |
| // the delegate will be called back with a NULL pointer. |
| virtual void LoadResponseInfo( |
| const GURL& manifest_url, int64 group_id, int64 response_id, |
| Delegate* delegate); |
| |
| // Schedules a group and its newest complete cache to be initially stored or |
| // incrementally updated with new changes. Upon completion the delegate |
| // will be called back. A group without a newest cache cannot be stored. |
| // It's a programming error to call this method without a newest cache. A |
| // side effect of storing a new newest cache is the removal of the group's |
| // old caches and responses from persistent storage (although they may still |
| // linger in the in-memory working set until no longer needed). The new |
| // cache will be added as the group's newest complete cache only if storage |
| // succeeds. |
| virtual void StoreGroupAndNewestCache( |
| AppCacheGroup* group, AppCache* newest_cache, Delegate* delegate) = 0; |
| |
| // Schedules a query to identify a response for a main request. Upon |
| // completion the delegate will be called back. |
| virtual void FindResponseForMainRequest( |
| const GURL& url, |
| const GURL& preferred_manifest_url, |
| Delegate* delegate) = 0; |
| |
| // Performs an immediate lookup of the in-memory cache to |
| // identify a response for a sub resource request. |
| virtual void FindResponseForSubRequest( |
| AppCache* cache, const GURL& url, |
| AppCacheEntry* found_entry, AppCacheEntry* found_fallback_entry, |
| bool* found_network_namespace) = 0; |
| |
| // Immediately updates in-memory storage, if the cache is in memory, |
| // and schedules a task to update persistent storage. If the cache is |
| // already scheduled to be loaded, upon loading completion the entry |
| // will be marked. There is no delegate completion callback. |
| virtual void MarkEntryAsForeign(const GURL& entry_url, int64 cache_id) = 0; |
| |
| // Schedules a task to update persistent storage and doom the group and all |
| // related caches and responses for deletion. Upon completion the in-memory |
| // instance is marked as obsolete and the delegate callback is called. |
| virtual void MakeGroupObsolete(AppCacheGroup* group, |
| Delegate* delegate, |
| int response_code) = 0; |
| |
| // Cancels all pending callbacks for the delegate. The delegate callbacks |
| // will not be invoked after, however any scheduled operations will still |
| // take place. The callbacks for subsequently scheduled operations are |
| // unaffected. |
| void CancelDelegateCallbacks(Delegate* delegate) { |
| DelegateReference* delegate_reference = GetDelegateReference(delegate); |
| if (delegate_reference) |
| delegate_reference->CancelReference(); |
| } |
| |
| // Creates a reader to read a response from storage. |
| virtual AppCacheResponseReader* CreateResponseReader( |
| const GURL& manifest_url, int64 group_id, int64 response_id) = 0; |
| |
| // Creates a writer to write a new response to storage. This call |
| // establishes a new response id. |
| virtual AppCacheResponseWriter* CreateResponseWriter( |
| const GURL& manifest_url, int64 group_id) = 0; |
| |
| // Schedules the lazy deletion of responses and saves the ids |
| // persistently such that the responses will be deleted upon restart |
| // if they aren't deleted prior to shutdown. |
| virtual void DoomResponses( |
| const GURL& manifest_url, const std::vector<int64>& response_ids) = 0; |
| |
| // Schedules the lazy deletion of responses without persistently saving |
| // the response ids. |
| virtual void DeleteResponses( |
| const GURL& manifest_url, const std::vector<int64>& response_ids) = 0; |
| |
| // Generates unique storage ids for different object types. |
| int64 NewCacheId() { |
| return ++last_cache_id_; |
| } |
| int64 NewGroupId() { |
| return ++last_group_id_; |
| } |
| |
| // The working set of object instances currently in memory. |
| AppCacheWorkingSet* working_set() { return &working_set_; } |
| |
| // A map of origins to usage. |
| const UsageMap* usage_map() { return &usage_map_; } |
| |
| // Simple ptr back to the service object that owns us. |
| AppCacheServiceImpl* service() { return service_; } |
| |
| protected: |
| friend class content::AppCacheQuotaClientTest; |
| friend class content::AppCacheResponseTest; |
| friend class content::AppCacheStorageTest; |
| |
| // Helper to call a collection of delegates. |
| #define FOR_EACH_DELEGATE(delegates, func_and_args) \ |
| do { \ |
| for (DelegateReferenceVector::iterator it = delegates.begin(); \ |
| it != delegates.end(); ++it) { \ |
| if (it->get()->delegate) \ |
| it->get()->delegate->func_and_args; \ |
| } \ |
| } while (0) |
| |
| // Helper used to manage multiple references to a 'delegate' and to |
| // allow all pending callbacks to that delegate to be easily cancelled. |
| struct DelegateReference : public base::RefCounted<DelegateReference> { |
| Delegate* delegate; |
| AppCacheStorage* storage; |
| |
| DelegateReference(Delegate* delegate, AppCacheStorage* storage); |
| |
| void CancelReference() { |
| storage->delegate_references_.erase(delegate); |
| storage = NULL; |
| delegate = NULL; |
| } |
| |
| private: |
| friend class base::RefCounted<DelegateReference>; |
| |
| virtual ~DelegateReference(); |
| }; |
| typedef std::map<Delegate*, DelegateReference*> DelegateReferenceMap; |
| typedef std::vector<scoped_refptr<DelegateReference> > |
| DelegateReferenceVector; |
| |
| // Helper used to manage an async LoadResponseInfo calls on behalf of |
| // multiple callers. |
| class ResponseInfoLoadTask { |
| public: |
| ResponseInfoLoadTask(const GURL& manifest_url, int64 group_id, |
| int64 response_id, AppCacheStorage* storage); |
| ~ResponseInfoLoadTask(); |
| |
| int64 response_id() const { return response_id_; } |
| const GURL& manifest_url() const { return manifest_url_; } |
| int64 group_id() const { return group_id_; } |
| |
| void AddDelegate(DelegateReference* delegate_reference) { |
| delegates_.push_back(delegate_reference); |
| } |
| |
| void StartIfNeeded(); |
| |
| private: |
| void OnReadComplete(int result); |
| |
| AppCacheStorage* storage_; |
| GURL manifest_url_; |
| int64 group_id_; |
| int64 response_id_; |
| scoped_ptr<AppCacheResponseReader> reader_; |
| DelegateReferenceVector delegates_; |
| scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_; |
| }; |
| |
| typedef std::map<int64, ResponseInfoLoadTask*> PendingResponseInfoLoads; |
| |
| DelegateReference* GetDelegateReference(Delegate* delegate) { |
| DelegateReferenceMap::iterator iter = |
| delegate_references_.find(delegate); |
| if (iter != delegate_references_.end()) |
| return iter->second; |
| return NULL; |
| } |
| |
| DelegateReference* GetOrCreateDelegateReference(Delegate* delegate) { |
| DelegateReference* reference = GetDelegateReference(delegate); |
| if (reference) |
| return reference; |
| return new DelegateReference(delegate, this); |
| } |
| |
| ResponseInfoLoadTask* GetOrCreateResponseInfoLoadTask( |
| const GURL& manifest_url, int64 group_id, int64 response_id) { |
| PendingResponseInfoLoads::iterator iter = |
| pending_info_loads_.find(response_id); |
| if (iter != pending_info_loads_.end()) |
| return iter->second; |
| return new ResponseInfoLoadTask(manifest_url, group_id, response_id, this); |
| } |
| |
| // Should only be called when creating a new response writer. |
| int64 NewResponseId() { |
| return ++last_response_id_; |
| } |
| |
| // Helpers to query and notify the QuotaManager. |
| void UpdateUsageMapAndNotify(const GURL& origin, int64 new_usage); |
| void ClearUsageMapAndNotify(); |
| void NotifyStorageAccessed(const GURL& origin); |
| |
| // The last storage id used for different object types. |
| int64 last_cache_id_; |
| int64 last_group_id_; |
| int64 last_response_id_; |
| |
| UsageMap usage_map_; // maps origin to usage |
| AppCacheWorkingSet working_set_; |
| AppCacheServiceImpl* service_; |
| DelegateReferenceMap delegate_references_; |
| PendingResponseInfoLoads pending_info_loads_; |
| |
| // The set of last ids must be retrieved from storage prior to being used. |
| static const int64 kUnitializedId; |
| |
| FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, DelegateReferences); |
| FRIEND_TEST_ALL_PREFIXES(content::AppCacheStorageTest, UsageMap); |
| |
| DISALLOW_COPY_AND_ASSIGN(AppCacheStorage); |
| }; |
| |
| } // namespace appcache |
| |
| #endif // WEBKIT_BROWSER_APPCACHE_APPCACHE_STORAGE_H_ |