Chromium Code Reviews| Index: webkit/appcache/mock_appcache_storage.cc |
| =================================================================== |
| --- webkit/appcache/mock_appcache_storage.cc (revision 29670) |
| +++ webkit/appcache/mock_appcache_storage.cc (working copy) |
| @@ -5,86 +5,89 @@ |
| #include "webkit/appcache/mock_appcache_storage.h" |
| #include "base/logging.h" |
| +#include "base/message_loop.h" |
| #include "base/ref_counted.h" |
| +#include "base/stl_util-inl.h" |
| #include "webkit/appcache/appcache.h" |
| #include "webkit/appcache/appcache_entry.h" |
| #include "webkit/appcache/appcache_group.h" |
| #include "webkit/appcache/appcache_response.h" |
| +// This is a quick and easy 'mock' implementation of the storage interface |
| +// that doesn't put anything to disk. |
| +// |
| +// We simply add an extra reference to objects when they're put in storage, |
| +// and remove the extra reference when they are removed from storage. |
| +// Responses are never really removed from the in-memory disk cache. |
| +// Delegate callbacks are made asyncly to appropiately mimic what will |
| +// happen with a real disk-backed storage impl that involves IO on a |
| +// background thread. |
| + |
| namespace appcache { |
| MockAppCacheStorage::MockAppCacheStorage(AppCacheService* service) |
| - : AppCacheStorage(service) { |
| + : AppCacheStorage(service), |
| + ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| last_cache_id_ = 0; |
| last_entry_id_ = 0; |
| last_group_id_ = 0; |
| last_response_id_ = 0; |
| } |
| +MockAppCacheStorage::~MockAppCacheStorage() { |
| + STLDeleteElements(&pending_tasks_); |
| +} |
| + |
| void MockAppCacheStorage::LoadCache(int64 id, Delegate* delegate) { |
| + DCHECK(delegate); |
| AppCache* cache = working_set_.GetCache(id); |
| - if (cache->HasOneRef()) { |
| - // TODO(michaeln): make the load look async |
| + if (ShouldCacheLoadAppearAsync(cache)) { |
| + ScheduleTask(method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::ProcessLoadCache, |
| + id, GetOrCreateDelegateReference(delegate))); |
| + return; |
| } |
| - delegate->OnCacheLoaded(cache, id); |
| + ProcessLoadCache(id, GetOrCreateDelegateReference(delegate)); |
| } |
| void MockAppCacheStorage::LoadOrCreateGroup( |
| const GURL& manifest_url, Delegate* delegate) { |
| - scoped_refptr<AppCacheGroup> group = working_set_.GetGroup(manifest_url); |
| - if (!group.get()) { |
| - group = new AppCacheGroup(service_, manifest_url); |
| - DCHECK(working_set_.GetGroup(manifest_url)); |
| + DCHECK(delegate); |
| + AppCacheGroup* group = working_set_.GetGroup(manifest_url); |
| + if (ShouldGroupLoadAppearAsync(group)) { |
| + ScheduleTask(method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::ProcessLoadOrCreateGroup, |
| + manifest_url, GetOrCreateDelegateReference(delegate))); |
| + return; |
| } |
| - // TODO(michaeln): make the load look async if all of the groups |
| - // caches only have one ref |
| - delegate->OnGroupLoaded(group.get(), manifest_url); |
| + ProcessLoadOrCreateGroup( |
| + manifest_url, GetOrCreateDelegateReference(delegate)); |
| } |
| void MockAppCacheStorage::StoreGroupAndNewestCache( |
| AppCacheGroup* group, Delegate* delegate) { |
| + DCHECK(group && delegate); |
| DCHECK(group->newest_complete_cache()); |
| - // TODO(michaeln): write me |
| - // 'store' the group + newest |
| - // 'unstore' the old caches |
| - // figure out which responses can be doomed and doom them |
| - // OldRepsonses - NewestResponse == ToBeDoomed |
| - |
| - // TODO(michaeln): Make this appear async |
| - //AddStoredGroup(group); |
| - //AddStoredCache(group->newest_complete_cache()); |
| - // |
| - //foreach(group->old_caches()) |
| - // RemoveStoredCache(old_cache); |
| - //std::set<int64> doomed_responses_ = responses from old caches |
| - //std::set<int64> needed_responses_ = responses from newest cache |
| - //foreach(needed_responses_) |
| - // doomed_responses_.remove(needed_response_); |
| - //DoomResponses(group->manifest_url(), doomed_responses_); |
| - |
| - delegate->OnGroupAndNewestCacheStored(group, false); |
| + // Always make this operation look async. |
| + ScheduleTask(method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::ProcessStoreGroupAndNewestCache, |
| + group, group->newest_complete_cache(), |
| + GetOrCreateDelegateReference(delegate))); |
| } |
| void MockAppCacheStorage::FindResponseForMainRequest( |
| const GURL& url, Delegate* delegate) { |
| - // TODO(michaeln): write me |
| - // |
| - //foreach(stored_group) { |
| - // if (group->manifest_url()->origin() != url.GetOrigin()) |
| - // continue; |
| - // |
| - // look for an entry |
| - // look for a fallback namespace |
| - // look for a online namespace |
| - //} |
| - delegate->OnMainResponseFound( |
| - url, kNoResponseId, false, kNoCacheId, GURL::EmptyGURL()); |
| + DCHECK(delegate); |
| + |
| + // Always make this operation look async. |
| + ScheduleTask(method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::ProcessFindResponseForMainRequest, |
| + url, GetOrCreateDelegateReference(delegate))); |
| } |
| void MockAppCacheStorage::MarkEntryAsForeign( |
| const GURL& entry_url, int64 cache_id) { |
| - // Update the working set. |
| AppCache* cache = working_set_.GetCache(cache_id); |
| if (cache) { |
| AppCacheEntry* entry = cache->GetEntry(entry_url); |
| @@ -96,14 +99,14 @@ |
| // being loaded be sure to update the memory cache upon load completion. |
| } |
| -void MockAppCacheStorage::MarkGroupAsObsolete( |
| +void MockAppCacheStorage::MakeGroupObsolete( |
| AppCacheGroup* group, Delegate* delegate) { |
| - // TODO(michaeln): write me |
| - // remove from working_set |
| - // remove from storage |
| - // doom things |
| - // group->set_obsolete(true); |
| - // TODO(michaeln): Make this appear async |
| + DCHECK(group && delegate); |
| + |
| + // Always make this method look async. |
| + ScheduleTask(method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::ProcessMakeGroupObsolete, |
| + group, GetOrCreateDelegateReference(delegate))); |
| } |
| AppCacheResponseReader* MockAppCacheStorage::CreateResponseReader( |
| @@ -118,31 +121,185 @@ |
| void MockAppCacheStorage::DoomResponses( |
| const GURL& manifest_url, const std::vector<int64>& response_ids) { |
| - // We don't bother with deleting responses from mock storage. |
| + // We don't bother with actually removing responses from the disk-cache, |
| + // just keep track of which ids have been doomed. |
| + std::vector<int64>::const_iterator it = response_ids.begin(); |
| + while (it != response_ids.end()) { |
| + doomed_response_ids_.insert(*it); |
| + ++it; |
| + } |
| } |
| +void MockAppCacheStorage::ProcessLoadCache( |
| + int64 id, scoped_refptr<DelegateReference> delegate_ref) { |
| + AppCache* cache = working_set_.GetCache(id); |
| + if (delegate_ref->delegate) |
| + delegate_ref->delegate->OnCacheLoaded(cache, id); |
| +} |
| + |
| +void MockAppCacheStorage::ProcessLoadOrCreateGroup( |
| + const GURL& manifest_url, scoped_refptr<DelegateReference> delegate_ref) { |
| + scoped_refptr<AppCacheGroup> group = working_set_.GetGroup(manifest_url); |
| + |
| + // Newly created groups are not put in the stored_groups collection |
| + // until StoreGroupAndNewestCache is called. |
| + if (!group) |
| + group = new AppCacheGroup(service_, manifest_url); |
| + |
| + if (delegate_ref->delegate) |
| + delegate_ref->delegate->OnGroupLoaded(group, manifest_url); |
| +} |
| + |
| +void MockAppCacheStorage::ProcessStoreGroupAndNewestCache( |
| + scoped_refptr<AppCacheGroup> group, |
| + scoped_refptr<AppCache> newest_cache, |
| + scoped_refptr<DelegateReference> delegate_ref) { |
| + DCHECK(group->newest_complete_cache() == newest_cache.get()); |
| + |
| + AddStoredGroup(group); |
| + AddStoredCache(group->newest_complete_cache()); |
| + |
| + // Copy the collection prior to removal, on final release |
| + // of a cache the group's collection will change. |
| + AppCacheGroup::Caches copy = group->old_caches(); |
| + RemoveStoredCaches(copy); |
| + |
| + if (delegate_ref->delegate) |
| + delegate_ref->delegate->OnGroupAndNewestCacheStored(group, true); |
| + |
| + // We don't bother with removing responses from 'mock' storage |
| + // TODO(michaeln): for 'real' storage... |
| + // std::set<int64> doomed_responses_ = responses from old caches |
| + // std::set<int64> needed_responses_ = responses from newest cache |
| + // foreach(needed_responses_) |
| + // doomed_responses_.remove(needed_response_); |
| + // DoomResponses(group->manifest_url(), doomed_responses_); |
| +} |
| + |
| +void MockAppCacheStorage::ProcessFindResponseForMainRequest( |
| + const GURL& url, scoped_refptr<DelegateReference> delegate_ref) { |
| + // TODO(michaeln): write me when doing AppCacheRequestHandler |
| + // foreach(stored_group) { |
| + // if (group->manifest_url()->origin() != url.GetOrigin()) |
| + // continue; |
| + // look for an entry |
| + // look for a fallback namespace |
| + // look for a online namespace |
| + // } |
| + if (delegate_ref->delegate) { |
| + delegate_ref->delegate->OnMainResponseFound( |
| + url, AppCacheEntry(), kNoCacheId, GURL()); |
| + } |
| +} |
| + |
| +void MockAppCacheStorage::ProcessMakeGroupObsolete( |
| + scoped_refptr<AppCacheGroup> group, |
| + scoped_refptr<DelegateReference> delegate_ref) { |
| + RemoveStoredGroup(group); |
| + if (group->newest_complete_cache()) |
| + RemoveStoredCache(group->newest_complete_cache()); |
| + |
| + // Copy the collection prior to removal, on final release |
| + // of a cache the group's collection will change. |
| + AppCacheGroup::Caches copy = group->old_caches(); |
| + RemoveStoredCaches(copy); |
| + |
| + group->set_obsolete(true); |
| + |
| + if (delegate_ref->delegate) |
| + delegate_ref->delegate->OnGroupMadeObsolete(group, true); |
| +} |
| + |
| +void MockAppCacheStorage::ScheduleTask(Task* task) { |
| + pending_tasks_.push_back(task); |
| + MessageLoop::current()->PostTask(FROM_HERE, |
| + method_factory_.NewRunnableMethod( |
| + &MockAppCacheStorage::RunOnePendingTask)); |
| +} |
| + |
| +void MockAppCacheStorage::RunOnePendingTask() { |
| + DCHECK(!pending_tasks_.empty()); |
| + Task* task = pending_tasks_.front(); |
| + pending_tasks_.pop_front(); |
| + task->Run(); |
| + delete task; |
| +} |
| + |
| void MockAppCacheStorage::AddStoredCache(AppCache* cache) { |
| -// cache->set_is_stored(true); |
| int64 cache_id = cache->cache_id(); |
| if (stored_caches_.find(cache_id) == stored_caches_.end()) |
| stored_caches_.insert(StoredCacheMap::value_type(cache_id, cache)); |
| } |
| void MockAppCacheStorage::RemoveStoredCache(AppCache* cache) { |
| -// cache->set_is_stored(false); |
| + // Do not remove from the working set, active caches are still usable |
| + // and may be looked up by id until they fall out of use. |
| stored_caches_.erase(cache->cache_id()); |
| } |
| +void MockAppCacheStorage::RemoveStoredCaches( |
| + const AppCacheGroup::Caches& caches) { |
| + AppCacheGroup::Caches::const_iterator it = caches.begin(); |
| + while (it != caches.end()) { |
| + RemoveStoredCache(*it); |
| + ++it; |
| + } |
| +} |
| + |
| void MockAppCacheStorage::AddStoredGroup(AppCacheGroup* group) { |
| -// group->set_is_stored(true); |
| const GURL& url = group->manifest_url(); |
| if (stored_groups_.find(url) == stored_groups_.end()) |
| stored_groups_.insert(StoredGroupMap::value_type(url, group)); |
| } |
| void MockAppCacheStorage::RemoveStoredGroup(AppCacheGroup* group) { |
| -// group->set_is_stored(false); |
| + // Also remove from the working set, caches for an 'obsolete' group |
| + // may linger in use, but the group itself cannot be looked up by |
| + // 'manifest_url' in the working set any longer. |
| + working_set()->RemoveGroup(group); |
| stored_groups_.erase(group->manifest_url()); |
| } |
| +bool MockAppCacheStorage::ShouldGroupLoadAppearAsync( |
| + const AppCacheGroup* group) { |
| + // We'll have to query the database to see if a group for the |
| + // manifest_url exists on disk. So return true for async. |
| + if (!group) |
| + return true; |
| + |
| + // Groups without a newest cache can't have been put to disk yet, so |
| + // we can synchronously return a reference we have in the working set. |
| + if (!group->newest_complete_cache()) |
| + return false; |
| + |
| + // The LoadGroup interface implies also loading the newest cache, so |
| + // if loading the newest cache should appear async, so to must this |
|
michaeln
2009/10/24 00:36:06
changed locally to... "so too must the"
|
| + // loading of this group. |
| + if (ShouldCacheLoadAppearAsync(group->newest_complete_cache())) |
| + return true; |
| + |
| + |
| + // If any of the old caches are "in use", then the group must also |
| + // be memory resident and not require async loading. |
| + const AppCacheGroup::Caches& old_caches = group->old_caches(); |
| + AppCacheGroup::Caches::const_iterator it = old_caches.begin(); |
| + while (it != old_caches.end()) { |
| + // "in use" caches don't require async loading |
| + if (!ShouldCacheLoadAppearAsync(*it)) |
| + return false; |
| + ++it; |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool MockAppCacheStorage::ShouldCacheLoadAppearAsync(const AppCache* cache) { |
| + if (!cache) |
| + return true; |
| + |
| + // If the 'stored' ref is the only ref, real storage will have to load from |
| + // the database. |
| + return IsCacheStored(cache) && cache->HasOneRef(); |
| +} |
| + |
| } // namespace appcache |