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