|  | // Copyright 2014 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. | 
|  |  | 
|  | #include "content/browser/appcache/appcache_host.h" | 
|  |  | 
|  | #include <stdint.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/bind_helpers.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "content/browser/appcache/appcache.h" | 
|  | #include "content/browser/appcache/appcache_backend_impl.h" | 
|  | #include "content/browser/appcache/appcache_group.h" | 
|  | #include "content/browser/appcache/mock_appcache_policy.h" | 
|  | #include "content/browser/appcache/mock_appcache_service.h" | 
|  | #include "net/url_request/url_request.h" | 
|  | #include "storage/browser/quota/quota_manager.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | class AppCacheHostTest : public testing::Test { | 
|  | public: | 
|  | AppCacheHostTest() { | 
|  | get_status_callback_ = | 
|  | base::Bind(&AppCacheHostTest::GetStatusCallback, | 
|  | base::Unretained(this)); | 
|  | start_update_callback_ = | 
|  | base::Bind(&AppCacheHostTest::StartUpdateCallback, | 
|  | base::Unretained(this)); | 
|  | swap_cache_callback_ = | 
|  | base::Bind(&AppCacheHostTest::SwapCacheCallback, | 
|  | base::Unretained(this)); | 
|  | } | 
|  |  | 
|  | class MockFrontend : public AppCacheFrontend { | 
|  | public: | 
|  | MockFrontend() | 
|  | : last_host_id_(-222), last_cache_id_(-222), | 
|  | last_status_(APPCACHE_STATUS_OBSOLETE), | 
|  | last_status_changed_(APPCACHE_STATUS_OBSOLETE), | 
|  | last_event_id_(APPCACHE_OBSOLETE_EVENT), | 
|  | content_blocked_(false) { | 
|  | } | 
|  |  | 
|  | void OnCacheSelected(int host_id, const AppCacheInfo& info) override { | 
|  | last_host_id_ = host_id; | 
|  | last_cache_id_ = info.cache_id; | 
|  | last_status_ = info.status; | 
|  | } | 
|  |  | 
|  | void OnStatusChanged(const std::vector<int>& host_ids, | 
|  | AppCacheStatus status) override { | 
|  | last_status_changed_ = status; | 
|  | } | 
|  |  | 
|  | void OnEventRaised(const std::vector<int>& host_ids, | 
|  | AppCacheEventID event_id) override { | 
|  | last_event_id_ = event_id; | 
|  | } | 
|  |  | 
|  | void OnErrorEventRaised(const std::vector<int>& host_ids, | 
|  | const AppCacheErrorDetails& details) override { | 
|  | last_event_id_ = APPCACHE_ERROR_EVENT; | 
|  | } | 
|  |  | 
|  | void OnProgressEventRaised(const std::vector<int>& host_ids, | 
|  | const GURL& url, | 
|  | int num_total, | 
|  | int num_complete) override { | 
|  | last_event_id_ = APPCACHE_PROGRESS_EVENT; | 
|  | } | 
|  |  | 
|  | void OnLogMessage(int host_id, | 
|  | AppCacheLogLevel log_level, | 
|  | const std::string& message) override {} | 
|  |  | 
|  | void OnContentBlocked(int host_id, const GURL& manifest_url) override { | 
|  | content_blocked_ = true; | 
|  | } | 
|  |  | 
|  | int last_host_id_; | 
|  | int64_t last_cache_id_; | 
|  | AppCacheStatus last_status_; | 
|  | AppCacheStatus last_status_changed_; | 
|  | AppCacheEventID last_event_id_; | 
|  | bool content_blocked_; | 
|  | }; | 
|  |  | 
|  | class MockQuotaManagerProxy : public storage::QuotaManagerProxy { | 
|  | public: | 
|  | MockQuotaManagerProxy() : QuotaManagerProxy(NULL, NULL) {} | 
|  |  | 
|  | // Not needed for our tests. | 
|  | void RegisterClient(storage::QuotaClient* client) override {} | 
|  | void NotifyStorageAccessed(storage::QuotaClient::ID client_id, | 
|  | const GURL& origin, | 
|  | storage::StorageType type) override {} | 
|  | void NotifyStorageModified(storage::QuotaClient::ID client_id, | 
|  | const GURL& origin, | 
|  | storage::StorageType type, | 
|  | int64_t delta) override {} | 
|  | void SetUsageCacheEnabled(storage::QuotaClient::ID client_id, | 
|  | const GURL& origin, | 
|  | storage::StorageType type, | 
|  | bool enabled) override {} | 
|  | void GetUsageAndQuota(base::SequencedTaskRunner* original_task_runner, | 
|  | const GURL& origin, | 
|  | storage::StorageType type, | 
|  | const GetUsageAndQuotaCallback& callback) override {} | 
|  |  | 
|  | void NotifyOriginInUse(const GURL& origin) override { inuse_[origin] += 1; } | 
|  |  | 
|  | void NotifyOriginNoLongerInUse(const GURL& origin) override { | 
|  | inuse_[origin] -= 1; | 
|  | } | 
|  |  | 
|  | int GetInUseCount(const GURL& origin) { | 
|  | return inuse_[origin]; | 
|  | } | 
|  |  | 
|  | void reset() { | 
|  | inuse_.clear(); | 
|  | } | 
|  |  | 
|  | // Map from origin to count of inuse notifications. | 
|  | std::map<GURL, int> inuse_; | 
|  |  | 
|  | protected: | 
|  | ~MockQuotaManagerProxy() override {} | 
|  | }; | 
|  |  | 
|  | void GetStatusCallback(AppCacheStatus status, void* param) { | 
|  | last_status_result_ = status; | 
|  | last_callback_param_ = param; | 
|  | } | 
|  |  | 
|  | void StartUpdateCallback(bool result, void* param) { | 
|  | last_start_result_ = result; | 
|  | last_callback_param_ = param; | 
|  | } | 
|  |  | 
|  | void SwapCacheCallback(bool result, void* param) { | 
|  | last_swap_result_ = result; | 
|  | last_callback_param_ = param; | 
|  | } | 
|  |  | 
|  | base::MessageLoop message_loop_; | 
|  |  | 
|  | // Mock classes for the 'host' to work with | 
|  | MockAppCacheService service_; | 
|  | MockFrontend mock_frontend_; | 
|  |  | 
|  | // Mock callbacks we expect to receive from the 'host' | 
|  | content::GetStatusCallback get_status_callback_; | 
|  | content::StartUpdateCallback start_update_callback_; | 
|  | content::SwapCacheCallback swap_cache_callback_; | 
|  |  | 
|  | AppCacheStatus last_status_result_; | 
|  | bool last_swap_result_; | 
|  | bool last_start_result_; | 
|  | void* last_callback_param_; | 
|  | }; | 
|  |  | 
|  | TEST_F(AppCacheHostTest, Basic) { | 
|  | // Construct a host and test what state it appears to be in. | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | EXPECT_EQ(1, host.host_id()); | 
|  | EXPECT_EQ(&service_, host.service()); | 
|  | EXPECT_EQ(&mock_frontend_, host.frontend()); | 
|  | EXPECT_EQ(NULL, host.associated_cache()); | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  |  | 
|  | // See that the callbacks are delivered immediately | 
|  | // and respond as if there is no cache selected. | 
|  | last_status_result_ = APPCACHE_STATUS_OBSOLETE; | 
|  | host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1)); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_); | 
|  |  | 
|  | last_start_result_ = true; | 
|  | host.StartUpdateWithCallback(start_update_callback_, | 
|  | reinterpret_cast<void*>(2)); | 
|  | EXPECT_FALSE(last_start_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(2), last_callback_param_); | 
|  |  | 
|  | last_swap_result_ = true; | 
|  | host.SwapCacheWithCallback(swap_cache_callback_, reinterpret_cast<void*>(3)); | 
|  | EXPECT_FALSE(last_swap_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(3), last_callback_param_); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, SelectNoCache) { | 
|  | scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy( | 
|  | new MockQuotaManagerProxy); | 
|  | service_.set_quota_manager_proxy(mock_quota_proxy.get()); | 
|  |  | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  |  | 
|  | const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin()); | 
|  | { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL()); | 
|  | EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  |  | 
|  | // We should have received an OnCacheSelected msg | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // Otherwise, see that it respond as if there is no cache selected. | 
|  | EXPECT_EQ(1, host.host_id()); | 
|  | EXPECT_EQ(&service_, host.service()); | 
|  | EXPECT_EQ(&mock_frontend_, host.frontend()); | 
|  | EXPECT_EQ(NULL, host.associated_cache()); | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  | EXPECT_TRUE(host.preferred_manifest_url().is_empty()); | 
|  | } | 
|  | EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  | service_.set_quota_manager_proxy(NULL); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, ForeignEntry) { | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  |  | 
|  | // Precondition, a cache with an entry that is not marked as foreign. | 
|  | const int kCacheId = 22; | 
|  | const GURL kDocumentURL("http://origin/document"); | 
|  | scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId); | 
|  | cache->AddEntry(kDocumentURL, AppCacheEntry(AppCacheEntry::EXPLICIT)); | 
|  |  | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.MarkAsForeignEntry(kDocumentURL, kCacheId); | 
|  |  | 
|  | // We should have received an OnCacheSelected msg for kAppCacheNoCacheId. | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // See that it respond as if there is no cache selected. | 
|  | EXPECT_EQ(1, host.host_id()); | 
|  | EXPECT_EQ(&service_, host.service()); | 
|  | EXPECT_EQ(&mock_frontend_, host.frontend()); | 
|  | EXPECT_EQ(NULL, host.associated_cache()); | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  |  | 
|  | // See that the entry was marked as foreign. | 
|  | EXPECT_TRUE(cache->GetEntry(kDocumentURL)->IsForeign()); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, ForeignFallbackEntry) { | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  |  | 
|  | // Precondition, a cache with a fallback entry that is not marked as foreign. | 
|  | const int kCacheId = 22; | 
|  | const GURL kFallbackURL("http://origin/fallback_resource"); | 
|  | scoped_refptr<AppCache> cache = new AppCache(service_.storage(), kCacheId); | 
|  | cache->AddEntry(kFallbackURL, AppCacheEntry(AppCacheEntry::FALLBACK)); | 
|  |  | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.NotifyMainResourceIsNamespaceEntry(kFallbackURL); | 
|  | host.MarkAsForeignEntry(GURL("http://origin/missing_document"), kCacheId); | 
|  |  | 
|  | // We should have received an OnCacheSelected msg for kAppCacheNoCacheId. | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // See that the fallback entry was marked as foreign. | 
|  | EXPECT_TRUE(cache->GetEntry(kFallbackURL)->IsForeign()); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, FailedCacheLoad) { | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  |  | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  |  | 
|  | const int kMockCacheId = 333; | 
|  |  | 
|  | // Put it in a state where we're waiting on a cache | 
|  | // load prior to finishing cache selection. | 
|  | host.pending_selected_cache_id_ = kMockCacheId; | 
|  | EXPECT_TRUE(host.is_selection_pending()); | 
|  |  | 
|  | // The callback should not occur until we finish cache selection. | 
|  | last_status_result_ = APPCACHE_STATUS_OBSOLETE; | 
|  | last_callback_param_ = reinterpret_cast<void*>(-1); | 
|  | host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1)); | 
|  | EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_); | 
|  |  | 
|  | // Satisfy the load with NULL, a failure. | 
|  | host.OnCacheLoaded(NULL, kMockCacheId); | 
|  |  | 
|  | // Cache selection should have finished | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // Callback should have fired upon completing the cache load too. | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, FailedGroupLoad) { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  |  | 
|  | const GURL kMockManifestUrl("http://foo.bar/baz"); | 
|  |  | 
|  | // Put it in a state where we're waiting on a cache | 
|  | // load prior to finishing cache selection. | 
|  | host.pending_selected_manifest_url_ = kMockManifestUrl; | 
|  | EXPECT_TRUE(host.is_selection_pending()); | 
|  |  | 
|  | // The callback should not occur until we finish cache selection. | 
|  | last_status_result_ = APPCACHE_STATUS_OBSOLETE; | 
|  | last_callback_param_ = reinterpret_cast<void*>(-1); | 
|  | host.GetStatusWithCallback(get_status_callback_, reinterpret_cast<void*>(1)); | 
|  | EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, last_status_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(-1), last_callback_param_); | 
|  |  | 
|  | // Satisfy the load will NULL, a failure. | 
|  | host.OnGroupLoaded(NULL, kMockManifestUrl); | 
|  |  | 
|  | // Cache selection should have finished | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // Callback should have fired upon completing the group load. | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, last_status_result_); | 
|  | EXPECT_EQ(reinterpret_cast<void*>(1), last_callback_param_); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, SetSwappableCache) { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.SetSwappableCache(NULL); | 
|  | EXPECT_FALSE(host.swappable_cache_.get()); | 
|  |  | 
|  | scoped_refptr<AppCacheGroup> group1(new AppCacheGroup( | 
|  | service_.storage(), GURL(), service_.storage()->NewGroupId())); | 
|  | host.SetSwappableCache(group1.get()); | 
|  | EXPECT_FALSE(host.swappable_cache_.get()); | 
|  |  | 
|  | AppCache* cache1 = new AppCache(service_.storage(), 111); | 
|  | cache1->set_complete(true); | 
|  | group1->AddCache(cache1); | 
|  | host.SetSwappableCache(group1.get()); | 
|  | EXPECT_EQ(cache1, host.swappable_cache_.get()); | 
|  |  | 
|  | mock_frontend_.last_host_id_ = -222;  // to verify we received OnCacheSelected | 
|  |  | 
|  | host.AssociateCompleteCache(cache1); | 
|  | EXPECT_FALSE(host.swappable_cache_.get());  // was same as associated cache | 
|  | EXPECT_EQ(APPCACHE_STATUS_IDLE, host.GetStatus()); | 
|  | // verify OnCacheSelected was called | 
|  | EXPECT_EQ(host.host_id(), mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(cache1->cache_id(), mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_IDLE, mock_frontend_.last_status_); | 
|  |  | 
|  | AppCache* cache2 = new AppCache(service_.storage(), 222); | 
|  | cache2->set_complete(true); | 
|  | group1->AddCache(cache2); | 
|  | EXPECT_EQ(cache2, host.swappable_cache_.get());  // updated to newest | 
|  |  | 
|  | scoped_refptr<AppCacheGroup> group2( | 
|  | new AppCacheGroup(service_.storage(), GURL("http://foo.com"), | 
|  | service_.storage()->NewGroupId())); | 
|  | AppCache* cache3 = new AppCache(service_.storage(), 333); | 
|  | cache3->set_complete(true); | 
|  | group2->AddCache(cache3); | 
|  |  | 
|  | AppCache* cache4 = new AppCache(service_.storage(), 444); | 
|  | cache4->set_complete(true); | 
|  | group2->AddCache(cache4); | 
|  | EXPECT_EQ(cache2, host.swappable_cache_.get());  // unchanged | 
|  |  | 
|  | host.AssociateCompleteCache(cache3); | 
|  | EXPECT_EQ(cache4, host.swappable_cache_.get());  // newest cache in group2 | 
|  | EXPECT_FALSE(group1->HasCache());  // both caches in group1 have refcount 0 | 
|  |  | 
|  | host.AssociateNoCache(GURL()); | 
|  | EXPECT_FALSE(host.swappable_cache_.get()); | 
|  | EXPECT_FALSE(group2->HasCache());  // both caches in group2 have refcount 0 | 
|  |  | 
|  | // Host adds reference to newest cache when an update is complete. | 
|  | AppCache* cache5 = new AppCache(service_.storage(), 555); | 
|  | cache5->set_complete(true); | 
|  | group2->AddCache(cache5); | 
|  | host.group_being_updated_ = group2; | 
|  | host.OnUpdateComplete(group2.get()); | 
|  | EXPECT_FALSE(host.group_being_updated_.get()); | 
|  | EXPECT_EQ(cache5, host.swappable_cache_.get()); | 
|  |  | 
|  | group2->RemoveCache(cache5); | 
|  | EXPECT_FALSE(group2->HasCache()); | 
|  | host.group_being_updated_ = group2; | 
|  | host.OnUpdateComplete(group2.get()); | 
|  | EXPECT_FALSE(host.group_being_updated_.get()); | 
|  | EXPECT_FALSE(host.swappable_cache_.get());  // group2 had no newest cache | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, ForDedicatedWorker) { | 
|  | const int kMockProcessId = 1; | 
|  | const int kParentHostId = 1; | 
|  | const int kWorkerHostId = 2; | 
|  |  | 
|  | AppCacheBackendImpl backend_impl; | 
|  | backend_impl.Initialize(&service_, &mock_frontend_, kMockProcessId); | 
|  | backend_impl.RegisterHost(kParentHostId); | 
|  | backend_impl.RegisterHost(kWorkerHostId); | 
|  |  | 
|  | AppCacheHost* parent_host = backend_impl.GetHost(kParentHostId); | 
|  | EXPECT_FALSE(parent_host->is_for_dedicated_worker()); | 
|  |  | 
|  | AppCacheHost* worker_host = backend_impl.GetHost(kWorkerHostId); | 
|  | worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId); | 
|  | EXPECT_TRUE(worker_host->is_for_dedicated_worker()); | 
|  | EXPECT_EQ(parent_host, worker_host->GetParentAppCacheHost()); | 
|  |  | 
|  | // We should have received an OnCacheSelected msg for the worker_host. | 
|  | // The host for workers always indicates 'no cache selected' regardless | 
|  | // of its parent's state. This is OK because the worker cannot access | 
|  | // the scriptable interface, the only function available is resource | 
|  | // loading (see appcache_request_handler_unittests those tests). | 
|  | EXPECT_EQ(kWorkerHostId, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // Simulate the parent being torn down. | 
|  | backend_impl.UnregisterHost(kParentHostId); | 
|  | parent_host = NULL; | 
|  | EXPECT_EQ(NULL, backend_impl.GetHost(kParentHostId)); | 
|  | EXPECT_EQ(NULL, worker_host->GetParentAppCacheHost()); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, SelectCacheAllowed) { | 
|  | scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy( | 
|  | new MockQuotaManagerProxy); | 
|  | MockAppCachePolicy mock_appcache_policy; | 
|  | mock_appcache_policy.can_create_return_value_ = true; | 
|  | service_.set_quota_manager_proxy(mock_quota_proxy.get()); | 
|  | service_.set_appcache_policy(&mock_appcache_policy); | 
|  |  | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  | mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT; | 
|  | mock_frontend_.content_blocked_ = false; | 
|  |  | 
|  | const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin()); | 
|  | const GURL kManifestUrl(GURL("http://whatever/cache.manifest")); | 
|  | { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.first_party_url_ = kDocAndOriginUrl; | 
|  | host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl); | 
|  | EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  |  | 
|  | // MockAppCacheService::LoadOrCreateGroup is asynchronous, so we shouldn't | 
|  | // have received an OnCacheSelected msg yet. | 
|  | EXPECT_EQ(-333, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(-333, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_OBSOLETE, mock_frontend_.last_status_); | 
|  | // No error events either | 
|  | EXPECT_EQ(APPCACHE_OBSOLETE_EVENT, mock_frontend_.last_event_id_); | 
|  | EXPECT_FALSE(mock_frontend_.content_blocked_); | 
|  |  | 
|  | EXPECT_TRUE(host.is_selection_pending()); | 
|  | } | 
|  | EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  | service_.set_quota_manager_proxy(NULL); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, SelectCacheBlocked) { | 
|  | scoped_refptr<MockQuotaManagerProxy> mock_quota_proxy( | 
|  | new MockQuotaManagerProxy); | 
|  | MockAppCachePolicy mock_appcache_policy; | 
|  | mock_appcache_policy.can_create_return_value_ = false; | 
|  | service_.set_quota_manager_proxy(mock_quota_proxy.get()); | 
|  | service_.set_appcache_policy(&mock_appcache_policy); | 
|  |  | 
|  | // Reset our mock frontend | 
|  | mock_frontend_.last_cache_id_ = -333; | 
|  | mock_frontend_.last_host_id_ = -333; | 
|  | mock_frontend_.last_status_ = APPCACHE_STATUS_OBSOLETE; | 
|  | mock_frontend_.last_event_id_ = APPCACHE_OBSOLETE_EVENT; | 
|  | mock_frontend_.content_blocked_ = false; | 
|  |  | 
|  | const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin()); | 
|  | const GURL kManifestUrl(GURL("http://whatever/cache.manifest")); | 
|  | { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | host.first_party_url_ = kDocAndOriginUrl; | 
|  | host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, kManifestUrl); | 
|  | EXPECT_EQ(1, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  |  | 
|  | // We should have received an OnCacheSelected msg | 
|  | EXPECT_EQ(1, mock_frontend_.last_host_id_); | 
|  | EXPECT_EQ(kAppCacheNoCacheId, mock_frontend_.last_cache_id_); | 
|  | EXPECT_EQ(APPCACHE_STATUS_UNCACHED, mock_frontend_.last_status_); | 
|  |  | 
|  | // Also, an error event was raised | 
|  | EXPECT_EQ(APPCACHE_ERROR_EVENT, mock_frontend_.last_event_id_); | 
|  | EXPECT_TRUE(mock_frontend_.content_blocked_); | 
|  |  | 
|  | // Otherwise, see that it respond as if there is no cache selected. | 
|  | EXPECT_EQ(1, host.host_id()); | 
|  | EXPECT_EQ(&service_, host.service()); | 
|  | EXPECT_EQ(&mock_frontend_, host.frontend()); | 
|  | EXPECT_EQ(NULL, host.associated_cache()); | 
|  | EXPECT_FALSE(host.is_selection_pending()); | 
|  | EXPECT_TRUE(host.preferred_manifest_url().is_empty()); | 
|  | } | 
|  | EXPECT_EQ(0, mock_quota_proxy->GetInUseCount(kDocAndOriginUrl)); | 
|  | service_.set_quota_manager_proxy(NULL); | 
|  | } | 
|  |  | 
|  | TEST_F(AppCacheHostTest, SelectCacheTwice) { | 
|  | AppCacheHost host(1, &mock_frontend_, &service_); | 
|  | const GURL kDocAndOriginUrl(GURL("http://whatever/").GetOrigin()); | 
|  |  | 
|  | EXPECT_TRUE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL())); | 
|  |  | 
|  | // Select methods should bail if cache has already been selected. | 
|  | EXPECT_FALSE(host.SelectCache(kDocAndOriginUrl, kAppCacheNoCacheId, GURL())); | 
|  | EXPECT_FALSE(host.SelectCacheForWorker(0, 0)); | 
|  | EXPECT_FALSE(host.SelectCacheForSharedWorker(kAppCacheNoCacheId)); | 
|  | EXPECT_FALSE(host.MarkAsForeignEntry(kDocAndOriginUrl, kAppCacheNoCacheId)); | 
|  | } | 
|  |  | 
|  | }  // namespace content |