1 // Copyright 2014 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 #include <stdint.h>
6 
7 #include <string>
8 
9 #include "content/browser/appcache/appcache.h"
10 #include "content/browser/appcache/appcache_group.h"
11 #include "content/browser/appcache/appcache_host.h"
12 #include "content/browser/appcache/appcache_update_job.h"
13 #include "content/browser/appcache/mock_appcache_service.h"
14 #include "content/public/test/browser_task_environment.h"
15 #include "mojo/public/cpp/bindings/pending_remote.h"
16 #include "mojo/public/cpp/bindings/receiver_set.h"
17 #include "mojo/public/cpp/bindings/remote.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/blink/public/mojom/appcache/appcache.mojom.h"
20 #include "third_party/blink/public/mojom/appcache/appcache_info.mojom.h"
21 #include "third_party/blink/public/mojom/devtools/console_message.mojom.h"
22 
23 namespace {
24 
25 class TestAppCacheFrontend : public blink::mojom::AppCacheFrontend {
26  public:
TestAppCacheFrontend()27   TestAppCacheFrontend()
28       : last_cache_id_(-1),
29         last_status_(blink::mojom::AppCacheStatus::APPCACHE_STATUS_OBSOLETE) {}
30 
CacheSelected(blink::mojom::AppCacheInfoPtr info)31   void CacheSelected(blink::mojom::AppCacheInfoPtr info) override {
32     last_host_id_ = receivers_.current_context();
33     last_cache_id_ = info->cache_id;
34     last_status_ = info->status;
35   }
36 
EventRaised(blink::mojom::AppCacheEventID event_id)37   void EventRaised(blink::mojom::AppCacheEventID event_id) override {}
38 
ErrorEventRaised(blink::mojom::AppCacheErrorDetailsPtr details)39   void ErrorEventRaised(
40       blink::mojom::AppCacheErrorDetailsPtr details) override {}
41 
ProgressEventRaised(const GURL & url,int32_t num_total,int32_t num_complete)42   void ProgressEventRaised(const GURL& url,
43                            int32_t num_total,
44                            int32_t num_complete) override {}
45 
LogMessage(blink::mojom::ConsoleMessageLevel log_level,const std::string & message)46   void LogMessage(blink::mojom::ConsoleMessageLevel log_level,
47                   const std::string& message) override {}
48 
SetSubresourceFactory(mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory)49   void SetSubresourceFactory(
50       mojo::PendingRemote<network::mojom::URLLoaderFactory> url_loader_factory)
51       override {}
52 
Bind(const base::UnguessableToken & host_id)53   mojo::PendingRemote<blink::mojom::AppCacheFrontend> Bind(
54       const base::UnguessableToken& host_id) {
55     mojo::PendingRemote<blink::mojom::AppCacheFrontend> result;
56     receivers_.Add(this, result.InitWithNewPipeAndPassReceiver(), host_id);
57     return result;
58   }
59 
60   base::UnguessableToken last_host_id_;
61   int64_t last_cache_id_;
62   blink::mojom::AppCacheStatus last_status_;
63   mojo::ReceiverSet<blink::mojom::AppCacheFrontend, base::UnguessableToken>
64       receivers_;
65 };
66 
67 }  // namespace
68 
69 namespace content {
70 
71 class TestUpdateObserver : public AppCacheGroup::UpdateObserver {
72  public:
TestUpdateObserver()73   TestUpdateObserver() : update_completed_(false), group_has_cache_(false) {}
74 
OnUpdateComplete(AppCacheGroup * group)75   void OnUpdateComplete(AppCacheGroup* group) override {
76     update_completed_ = true;
77     group_has_cache_ = group->HasCache();
78   }
79 
OnContentBlocked(AppCacheGroup * group)80   virtual void OnContentBlocked(AppCacheGroup* group) {}
81 
82   bool update_completed_;
83   bool group_has_cache_;
84 };
85 
86 class TestAppCacheHost : public AppCacheHost {
87  public:
TestAppCacheHost(const base::UnguessableToken & host_id,TestAppCacheFrontend * frontend,AppCacheServiceImpl * service)88   TestAppCacheHost(const base::UnguessableToken& host_id,
89                    TestAppCacheFrontend* frontend,
90                    AppCacheServiceImpl* service)
91       : AppCacheHost(
92             host_id,
93             /*process_id=*/456,
94             /*render_frame_id=*/789,
95             ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
96                 /*process_id=*/456),
97             frontend->Bind(host_id),
98             service),
99         update_completed_(false) {}
100 
OnUpdateComplete(AppCacheGroup * group)101   void OnUpdateComplete(AppCacheGroup* group) override {
102     update_completed_ = true;
103   }
104 
105   bool update_completed_;
106 };
107 
108 class AppCacheGroupTest : public testing::Test {
109  private:
110   BrowserTaskEnvironment task_environment_;
111 };
112 
TEST_F(AppCacheGroupTest,AddRemoveCache)113 TEST_F(AppCacheGroupTest, AddRemoveCache) {
114   MockAppCacheService service;
115   auto group = base::MakeRefCounted<AppCacheGroup>(service.storage(),
116                                                    GURL("http://foo.com"), 111);
117 
118   base::Time now = base::Time::Now();
119 
120   auto cache1 = base::MakeRefCounted<AppCache>(service.storage(), 111);
121   cache1->set_complete(true);
122   cache1->set_update_time(now);
123   group->AddCache(cache1.get());
124   EXPECT_EQ(cache1.get(), group->newest_complete_cache());
125 
126   // Adding older cache does not change newest complete cache.
127   auto cache2 = base::MakeRefCounted<AppCache>(service.storage(), 222);
128   cache2->set_complete(true);
129   cache2->set_update_time(now - base::TimeDelta::FromDays(1));
130   group->AddCache(cache2.get());
131   EXPECT_EQ(cache1.get(), group->newest_complete_cache());
132 
133   // Adding newer cache does change newest complete cache.
134   auto cache3 = base::MakeRefCounted<AppCache>(service.storage(), 333);
135   cache3->set_complete(true);
136   cache3->set_update_time(now + base::TimeDelta::FromDays(1));
137   group->AddCache(cache3.get());
138   EXPECT_EQ(cache3.get(), group->newest_complete_cache());
139 
140   // Adding cache with same update time uses one with larger ID.
141   auto cache4 = base::MakeRefCounted<AppCache>(service.storage(), 444);
142   cache4->set_complete(true);
143   cache4->set_update_time(now + base::TimeDelta::FromDays(1));  // same as 3
144   group->AddCache(cache4.get());
145   EXPECT_EQ(cache4.get(), group->newest_complete_cache());
146 
147   // smaller id
148   auto cache5 = base::MakeRefCounted<AppCache>(service.storage(), 55);
149   cache5->set_complete(true);
150   cache5->set_update_time(now + base::TimeDelta::FromDays(1));  // same as 4
151   group->AddCache(cache5.get());
152   EXPECT_EQ(cache4.get(), group->newest_complete_cache());  // no change
153 
154   // Old caches can always be removed.
155   group->RemoveCache(cache1.get());
156   EXPECT_FALSE(cache1->owning_group());
157   EXPECT_EQ(cache4.get(), group->newest_complete_cache());  // newest unchanged
158 
159   // Remove rest of caches.
160   group->RemoveCache(cache2.get());
161   EXPECT_FALSE(cache2->owning_group());
162   EXPECT_EQ(cache4.get(), group->newest_complete_cache());  // newest unchanged
163   group->RemoveCache(cache3.get());
164   EXPECT_FALSE(cache3->owning_group());
165   EXPECT_EQ(cache4.get(), group->newest_complete_cache());  // newest unchanged
166   group->RemoveCache(cache5.get());
167   EXPECT_FALSE(cache5->owning_group());
168   EXPECT_EQ(cache4.get(), group->newest_complete_cache());  // newest unchanged
169   group->RemoveCache(cache4.get());                         // newest removed
170   EXPECT_FALSE(cache4->owning_group());
171   EXPECT_FALSE(group->newest_complete_cache());  // no more newest cache
172 
173   // Can remove newest cache if there are older caches.
174   group->AddCache(cache1.get());
175   EXPECT_EQ(cache1.get(), group->newest_complete_cache());
176   group->AddCache(cache4.get());
177   EXPECT_EQ(cache4.get(), group->newest_complete_cache());
178   group->RemoveCache(cache4.get());  // remove newest
179   EXPECT_FALSE(cache4->owning_group());
180   EXPECT_FALSE(group->newest_complete_cache());  // newest removed
181 }
182 
TEST_F(AppCacheGroupTest,CleanupUnusedGroup)183 TEST_F(AppCacheGroupTest, CleanupUnusedGroup) {
184   MockAppCacheService service;
185   TestAppCacheFrontend frontend;
186   AppCacheGroup* group =
187       new AppCacheGroup(service.storage(), GURL("http://foo.com"), 111);
188 
189   auto host1_id = base::UnguessableToken::Create();
190   const int kMockProcessId1 = 1;
191   const int kMockProcessId2 = 2;
192   AppCacheHost host1(
193       host1_id, kMockProcessId1, /*render_frame_id=*/1,
194       ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
195           kMockProcessId1),
196       frontend.Bind(host1_id), &service);
197   auto host2_id = base::UnguessableToken::Create();
198   AppCacheHost host2(
199       host2_id, kMockProcessId2, /*render_frame_id=*/2,
200       ChildProcessSecurityPolicyImpl::GetInstance()->CreateHandle(
201           kMockProcessId2),
202       frontend.Bind(host2_id), &service);
203 
204   base::Time now = base::Time::Now();
205 
206   AppCache* cache1 = new AppCache(service.storage(), 111);
207   cache1->set_complete(true);
208   cache1->set_update_time(now);
209   group->AddCache(cache1);
210   EXPECT_EQ(cache1, group->newest_complete_cache());
211 
212   host1.AssociateCompleteCache(cache1);
213   base::RunLoop().RunUntilIdle();
214   EXPECT_EQ(frontend.last_host_id_, host1.host_id());
215   EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
216   EXPECT_EQ(frontend.last_status_,
217             blink::mojom::AppCacheStatus::APPCACHE_STATUS_IDLE);
218 
219   host2.AssociateCompleteCache(cache1);
220   base::RunLoop().RunUntilIdle();
221   EXPECT_EQ(frontend.last_host_id_, host2.host_id());
222   EXPECT_EQ(frontend.last_cache_id_, cache1->cache_id());
223   EXPECT_EQ(frontend.last_status_,
224             blink::mojom::AppCacheStatus::APPCACHE_STATUS_IDLE);
225 
226   AppCache* cache2 = new AppCache(service.storage(), 222);
227   cache2->set_complete(true);
228   cache2->set_update_time(now + base::TimeDelta::FromDays(1));
229   group->AddCache(cache2);
230   EXPECT_EQ(cache2, group->newest_complete_cache());
231 
232   // Unassociate all hosts from older cache.
233   host1.AssociateNoCache(GURL());
234   host2.AssociateNoCache(GURL());
235   base::RunLoop().RunUntilIdle();
236   EXPECT_EQ(frontend.last_host_id_, host2.host_id());
237   EXPECT_EQ(frontend.last_cache_id_, blink::mojom::kAppCacheNoCacheId);
238   EXPECT_EQ(frontend.last_status_,
239             blink::mojom::AppCacheStatus::APPCACHE_STATUS_UNCACHED);
240 }
241 
TEST_F(AppCacheGroupTest,StartUpdate)242 TEST_F(AppCacheGroupTest, StartUpdate) {
243   MockAppCacheService service;
244   auto group = base::MakeRefCounted<AppCacheGroup>(service.storage(),
245                                                    GURL("http://foo.com"), 111);
246 
247   // Set state to checking to prevent update job from executing fetches.
248   group->update_status_ = AppCacheGroup::CHECKING;
249   group->StartUpdate();
250   AppCacheUpdateJob* update = group->update_job_;
251   EXPECT_TRUE(update != nullptr);
252 
253   // Start another update, check that same update job is in use.
254   group->StartUpdateWithHost(nullptr);
255   EXPECT_EQ(update, group->update_job_);
256 
257   // Deleting the update should restore the group to AppCacheGroup::IDLE.
258   delete update;
259   EXPECT_TRUE(group->update_job_ == nullptr);
260   EXPECT_EQ(AppCacheGroup::IDLE, group->update_status());
261 }
262 
TEST_F(AppCacheGroupTest,CancelUpdate)263 TEST_F(AppCacheGroupTest, CancelUpdate) {
264   MockAppCacheService service;
265   auto group = base::MakeRefCounted<AppCacheGroup>(service.storage(),
266                                                    GURL("http://foo.com"), 111);
267 
268   // Set state to checking to prevent update job from executing fetches.
269   group->update_status_ = AppCacheGroup::CHECKING;
270   group->StartUpdate();
271   AppCacheUpdateJob* update = group->update_job_;
272   EXPECT_TRUE(update != nullptr);
273 
274   // Deleting the group should cancel the update.
275   TestUpdateObserver observer;
276   group->AddUpdateObserver(&observer);
277   group = nullptr;  // causes group to be deleted
278   EXPECT_TRUE(observer.update_completed_);
279   EXPECT_FALSE(observer.group_has_cache_);
280 }
281 
TEST_F(AppCacheGroupTest,QueueUpdate)282 TEST_F(AppCacheGroupTest, QueueUpdate) {
283   MockAppCacheService service;
284   auto group = base::MakeRefCounted<AppCacheGroup>(service.storage(),
285                                                    GURL("http://foo.com"), 111);
286 
287   // Set state to checking to prevent update job from executing fetches.
288   group->update_status_ = AppCacheGroup::CHECKING;
289   group->StartUpdate();
290   EXPECT_TRUE(group->update_job_);
291 
292   // Pretend group's update job is terminating so that next update is queued.
293   group->update_job_->internal_state_ =
294       AppCacheUpdateJobState::REFETCH_MANIFEST;
295   EXPECT_TRUE(group->update_job_->IsTerminating());
296 
297   TestAppCacheFrontend frontend;
298   TestAppCacheHost host(base::UnguessableToken::Create(), &frontend, &service);
299   host.new_master_entry_url_ = GURL("http://foo.com/bar.txt");
300   group->StartUpdateWithNewMasterEntry(&host, host.new_master_entry_url_);
301   EXPECT_FALSE(group->queued_updates_.empty());
302 
303   group->AddUpdateObserver(&host);
304   EXPECT_FALSE(group->FindObserver(&host, group->observers_));
305   EXPECT_TRUE(group->FindObserver(&host, group->queued_observers_));
306 
307   // Delete update to cause it to complete. Verify no update complete notice
308   // sent to host.
309   delete group->update_job_;
310   EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
311   EXPECT_FALSE(group->restart_update_task_.IsCancelled());
312   EXPECT_FALSE(host.update_completed_);
313 
314   // Start another update. Cancels task and will run queued updates.
315   group->update_status_ = AppCacheGroup::CHECKING;  // prevent actual fetches
316   group->StartUpdate();
317   EXPECT_TRUE(group->update_job_);
318   EXPECT_TRUE(group->restart_update_task_.IsCancelled());
319   EXPECT_TRUE(group->queued_updates_.empty());
320   EXPECT_FALSE(group->update_job_->pending_master_entries_.empty());
321   EXPECT_FALSE(group->FindObserver(&host, group->queued_observers_));
322   EXPECT_TRUE(group->FindObserver(&host, group->observers_));
323 
324   // Delete update to cause it to complete. Verify host is notified.
325   delete group->update_job_;
326   EXPECT_EQ(AppCacheGroup::IDLE, group->update_status_);
327   EXPECT_TRUE(group->restart_update_task_.IsCancelled());
328   EXPECT_TRUE(host.update_completed_);
329 }
330 
331 }  // namespace content
332