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