9543f7e65d786786f1f8dc38a422cea876043a79
[platform/framework/web/crosswalk.git] / src / content / browser / appcache / appcache_request_handler_unittest.cc
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 <stack>
6 #include <string>
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "content/browser/appcache/appcache.h"
16 #include "content/browser/appcache/appcache_backend_impl.h"
17 #include "content/browser/appcache/appcache_request_handler.h"
18 #include "content/browser/appcache/appcache_url_request_job.h"
19 #include "content/browser/appcache/mock_appcache_policy.h"
20 #include "content/browser/appcache/mock_appcache_service.h"
21 #include "net/base/net_errors.h"
22 #include "net/base/request_priority.h"
23 #include "net/http/http_response_headers.h"
24 #include "net/url_request/url_request.h"
25 #include "net/url_request/url_request_context.h"
26 #include "net/url_request/url_request_error_job.h"
27 #include "net/url_request/url_request_job_factory.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 namespace content {
31
32 static const int kMockProcessId = 1;
33
34 class AppCacheRequestHandlerTest : public testing::Test {
35  public:
36   class MockFrontend : public AppCacheFrontend {
37    public:
38     void OnCacheSelected(int host_id, const AppCacheInfo& info) override {}
39
40     void OnStatusChanged(const std::vector<int>& host_ids,
41                          AppCacheStatus status) override {}
42
43     void OnEventRaised(const std::vector<int>& host_ids,
44                        AppCacheEventID event_id) override {}
45
46     void OnErrorEventRaised(const std::vector<int>& host_ids,
47                             const AppCacheErrorDetails& details) override {}
48
49     void OnProgressEventRaised(const std::vector<int>& host_ids,
50                                const GURL& url,
51                                int num_total,
52                                int num_complete) override {}
53
54     void OnLogMessage(int host_id,
55                       AppCacheLogLevel log_level,
56                       const std::string& message) override {}
57
58     void OnContentBlocked(int host_id, const GURL& manifest_url) override {}
59   };
60
61   // Helper callback to run a test on our io_thread. The io_thread is spun up
62   // once and reused for all tests.
63   template <class Method>
64   void MethodWrapper(Method method) {
65     SetUpTest();
66     (this->*method)();
67   }
68
69   // Subclasses to simulate particular responses so test cases can
70   // exercise fallback code paths.
71
72   class MockURLRequestDelegate : public net::URLRequest::Delegate {
73     void OnResponseStarted(net::URLRequest* request) override {}
74     void OnReadCompleted(net::URLRequest* request, int bytes_read) override {}
75   };
76
77   class MockURLRequestJob : public net::URLRequestJob {
78    public:
79     MockURLRequestJob(net::URLRequest* request,
80                       net::NetworkDelegate* network_delegate,
81                       int response_code)
82         : net::URLRequestJob(request, network_delegate),
83           response_code_(response_code),
84           has_response_info_(false) {}
85     MockURLRequestJob(net::URLRequest* request,
86                       net::NetworkDelegate* network_delegate,
87                       const net::HttpResponseInfo& info)
88         : net::URLRequestJob(request, network_delegate),
89           response_code_(info.headers->response_code()),
90           has_response_info_(true),
91           response_info_(info) {}
92
93    protected:
94     ~MockURLRequestJob() override {}
95     void Start() override { NotifyHeadersComplete(); }
96     int GetResponseCode() const override { return response_code_; }
97     void GetResponseInfo(net::HttpResponseInfo* info) override {
98       if (!has_response_info_)
99         return;
100       *info = response_info_;
101     }
102
103    private:
104     int response_code_;
105     bool has_response_info_;
106     net::HttpResponseInfo response_info_;
107   };
108
109   class MockURLRequestJobFactory : public net::URLRequestJobFactory {
110    public:
111     MockURLRequestJobFactory() : job_(NULL) {
112     }
113
114     ~MockURLRequestJobFactory() override { DCHECK(!job_); }
115
116     void SetJob(net::URLRequestJob* job) {
117       job_ = job;
118     }
119
120     net::URLRequestJob* MaybeCreateJobWithProtocolHandler(
121         const std::string& scheme,
122         net::URLRequest* request,
123         net::NetworkDelegate* network_delegate) const override {
124       if (job_) {
125         net::URLRequestJob* temp = job_;
126         job_ = NULL;
127         return temp;
128       } else {
129         // Some of these tests trigger UpdateJobs which start URLRequests.
130         // We short circuit those be returning error jobs.
131         return new net::URLRequestErrorJob(request,
132                                            network_delegate,
133                                            net::ERR_INTERNET_DISCONNECTED);
134       }
135     }
136
137     net::URLRequestJob* MaybeInterceptRedirect(
138         net::URLRequest* request,
139         net::NetworkDelegate* network_delegate,
140         const GURL& location) const override {
141       return nullptr;
142     }
143
144     net::URLRequestJob* MaybeInterceptResponse(
145         net::URLRequest* request,
146         net::NetworkDelegate* network_delegate) const override {
147       return nullptr;
148     }
149
150     bool IsHandledProtocol(const std::string& scheme) const override {
151       return scheme == "http";
152     };
153
154     bool IsHandledURL(const GURL& url) const override {
155       return url.SchemeIs("http");
156     }
157
158     bool IsSafeRedirectTarget(const GURL& location) const override {
159       return false;
160     }
161
162    private:
163     mutable net::URLRequestJob* job_;
164   };
165
166   static void SetUpTestCase() {
167     io_thread_.reset(new base::Thread("AppCacheRequestHandlerTest Thread"));
168     base::Thread::Options options(base::MessageLoop::TYPE_IO, 0);
169     io_thread_->StartWithOptions(options);
170   }
171
172   static void TearDownTestCase() {
173     io_thread_.reset(NULL);
174   }
175
176   // Test harness --------------------------------------------------
177
178   AppCacheRequestHandlerTest() : host_(NULL) {}
179
180   template <class Method>
181   void RunTestOnIOThread(Method method) {
182     test_finished_event_ .reset(new base::WaitableEvent(false, false));
183     io_thread_->message_loop()->PostTask(
184         FROM_HERE,
185         base::Bind(&AppCacheRequestHandlerTest::MethodWrapper<Method>,
186                    base::Unretained(this), method));
187     test_finished_event_->Wait();
188   }
189
190   void SetUpTest() {
191     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
192     mock_service_.reset(new MockAppCacheService);
193     mock_service_->set_request_context(&empty_context_);
194     mock_policy_.reset(new MockAppCachePolicy);
195     mock_service_->set_appcache_policy(mock_policy_.get());
196     mock_frontend_.reset(new MockFrontend);
197     backend_impl_.reset(new AppCacheBackendImpl);
198     backend_impl_->Initialize(mock_service_.get(), mock_frontend_.get(),
199                               kMockProcessId);
200     const int kHostId = 1;
201     backend_impl_->RegisterHost(kHostId);
202     host_ = backend_impl_->GetHost(kHostId);
203     job_factory_.reset(new MockURLRequestJobFactory());
204     empty_context_.set_job_factory(job_factory_.get());
205   }
206
207   void TearDownTest() {
208     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
209     job_ = NULL;
210     handler_.reset();
211     request_.reset();
212     backend_impl_.reset();
213     mock_frontend_.reset();
214     mock_service_.reset();
215     mock_policy_.reset();
216     job_factory_.reset();
217     host_ = NULL;
218   }
219
220   void TestFinished() {
221     // We unwind the stack prior to finishing up to let stack
222     // based objects get deleted.
223     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
224     base::MessageLoop::current()->PostTask(
225         FROM_HERE,
226         base::Bind(&AppCacheRequestHandlerTest::TestFinishedUnwound,
227                    base::Unretained(this)));
228   }
229
230   void TestFinishedUnwound() {
231     TearDownTest();
232     test_finished_event_->Signal();
233   }
234
235   void PushNextTask(const base::Closure& task) {
236     task_stack_.push(task);
237   }
238
239   void ScheduleNextTask() {
240     DCHECK(base::MessageLoop::current() == io_thread_->message_loop());
241     if (task_stack_.empty()) {
242       TestFinished();
243       return;
244     }
245     base::MessageLoop::current()->PostTask(FROM_HERE, task_stack_.top());
246     task_stack_.pop();
247   }
248
249   // MainResource_Miss --------------------------------------------------
250
251   void MainResource_Miss() {
252     PushNextTask(
253         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Miss,
254                    base::Unretained(this)));
255
256     request_ = empty_context_.CreateRequest(
257         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
258     handler_.reset(host_->CreateRequestHandler(request_.get(),
259                                                RESOURCE_TYPE_MAIN_FRAME));
260     EXPECT_TRUE(handler_.get());
261
262     job_ = handler_->MaybeLoadResource(request_.get(),
263                                        request_->context()->network_delegate());
264     EXPECT_TRUE(job_.get());
265     EXPECT_TRUE(job_->is_waiting());
266
267     // We have to wait for completion of storage->FindResponseForMainRequest.
268     ScheduleNextTask();
269   }
270
271   void Verify_MainResource_Miss() {
272     EXPECT_FALSE(job_->is_waiting());
273     EXPECT_TRUE(job_->is_delivering_network_response());
274
275     int64 cache_id = kAppCacheNoCacheId;
276     GURL manifest_url;
277     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
278     EXPECT_EQ(kAppCacheNoCacheId, cache_id);
279     EXPECT_EQ(GURL(), manifest_url);
280     EXPECT_EQ(0, handler_->found_group_id_);
281
282     AppCacheURLRequestJob* fallback_job;
283     fallback_job = handler_->MaybeLoadFallbackForRedirect(
284         request_.get(),
285         request_->context()->network_delegate(),
286         GURL("http://blah/redirect"));
287     EXPECT_FALSE(fallback_job);
288     fallback_job = handler_->MaybeLoadFallbackForResponse(
289         request_.get(), request_->context()->network_delegate());
290     EXPECT_FALSE(fallback_job);
291
292     EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
293
294     TestFinished();
295   }
296
297   // MainResource_Hit --------------------------------------------------
298
299   void MainResource_Hit() {
300     PushNextTask(
301         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Hit,
302                    base::Unretained(this)));
303
304     request_ = empty_context_.CreateRequest(
305         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
306     handler_.reset(host_->CreateRequestHandler(request_.get(),
307                                                RESOURCE_TYPE_MAIN_FRAME));
308     EXPECT_TRUE(handler_.get());
309
310     mock_storage()->SimulateFindMainResource(
311         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
312         GURL(), AppCacheEntry(),
313         1, 2, GURL("http://blah/manifest/"));
314
315     job_ = handler_->MaybeLoadResource(request_.get(),
316                                        request_->context()->network_delegate());
317     EXPECT_TRUE(job_.get());
318     EXPECT_TRUE(job_->is_waiting());
319
320     // We have to wait for completion of storage->FindResponseForMainRequest.
321     ScheduleNextTask();
322   }
323
324   void Verify_MainResource_Hit() {
325     EXPECT_FALSE(job_->is_waiting());
326     EXPECT_TRUE(job_->is_delivering_appcache_response());
327
328     int64 cache_id = kAppCacheNoCacheId;
329     GURL manifest_url;
330     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
331     EXPECT_EQ(1, cache_id);
332     EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
333     EXPECT_EQ(2, handler_->found_group_id_);
334
335     AppCacheURLRequestJob* fallback_job;
336     fallback_job = handler_->MaybeLoadFallbackForResponse(
337         request_.get(), request_->context()->network_delegate());
338     EXPECT_FALSE(fallback_job);
339
340     EXPECT_EQ(GURL("http://blah/manifest/"),
341               host_->preferred_manifest_url());
342
343     TestFinished();
344   }
345
346   // MainResource_Fallback --------------------------------------------------
347
348   void MainResource_Fallback() {
349     PushNextTask(
350         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Fallback,
351                    base::Unretained(this)));
352
353     request_ = empty_context_.CreateRequest(
354         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
355     handler_.reset(host_->CreateRequestHandler(request_.get(),
356                                                RESOURCE_TYPE_MAIN_FRAME));
357     EXPECT_TRUE(handler_.get());
358
359     mock_storage()->SimulateFindMainResource(
360         AppCacheEntry(),
361         GURL("http://blah/fallbackurl"),
362         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
363         1, 2, GURL("http://blah/manifest/"));
364
365     job_ = handler_->MaybeLoadResource(request_.get(),
366                                        request_->context()->network_delegate());
367     EXPECT_TRUE(job_.get());
368     EXPECT_TRUE(job_->is_waiting());
369
370     // We have to wait for completion of storage->FindResponseForMainRequest.
371     ScheduleNextTask();
372   }
373
374   void SimulateResponseCode(int response_code) {
375     job_factory_->SetJob(
376         new MockURLRequestJob(
377             request_.get(),
378             request_->context()->network_delegate(),
379             response_code));
380     request_->Start();
381     // All our simulation needs  to satisfy are the following two DCHECKs
382     DCHECK(request_->status().is_success());
383     DCHECK_EQ(response_code, request_->GetResponseCode());
384   }
385
386   void SimulateResponseInfo(const net::HttpResponseInfo& info) {
387     job_factory_->SetJob(
388         new MockURLRequestJob(
389             request_.get(),
390             request_->context()->network_delegate(), info));
391     request_->Start();
392   }
393
394   void Verify_MainResource_Fallback() {
395     EXPECT_FALSE(job_->is_waiting());
396     EXPECT_TRUE(job_->is_delivering_network_response());
397
398     // When the request is restarted, the existing job is dropped so a
399     // real network job gets created. We expect NULL here which will cause
400     // the net library to create a real job.
401     job_ = handler_->MaybeLoadResource(request_.get(),
402                                        request_->context()->network_delegate());
403     EXPECT_FALSE(job_.get());
404
405     // Simulate an http error of the real network job.
406     SimulateResponseCode(500);
407
408     job_ = handler_->MaybeLoadFallbackForResponse(
409         request_.get(), request_->context()->network_delegate());
410     EXPECT_TRUE(job_.get());
411     EXPECT_TRUE(job_->is_delivering_appcache_response());
412
413     int64 cache_id = kAppCacheNoCacheId;
414     GURL manifest_url;
415     handler_->GetExtraResponseInfo(&cache_id, &manifest_url);
416     EXPECT_EQ(1, cache_id);
417     EXPECT_EQ(GURL("http://blah/manifest/"), manifest_url);
418     EXPECT_TRUE(host_->main_resource_was_namespace_entry_);
419     EXPECT_EQ(GURL("http://blah/fallbackurl"), host_->namespace_entry_url_);
420
421     EXPECT_EQ(GURL("http://blah/manifest/"),
422               host_->preferred_manifest_url());
423
424     TestFinished();
425   }
426
427   // MainResource_FallbackOverride --------------------------------------------
428
429   void MainResource_FallbackOverride() {
430     PushNextTask(base::Bind(
431         &AppCacheRequestHandlerTest::Verify_MainResource_FallbackOverride,
432         base::Unretained(this)));
433
434     request_ = empty_context_.CreateRequest(
435         GURL("http://blah/fallback-override"), net::DEFAULT_PRIORITY,
436         &delegate_, NULL);
437     handler_.reset(host_->CreateRequestHandler(request_.get(),
438                                                RESOURCE_TYPE_MAIN_FRAME));
439     EXPECT_TRUE(handler_.get());
440
441     mock_storage()->SimulateFindMainResource(
442         AppCacheEntry(),
443         GURL("http://blah/fallbackurl"),
444         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
445         1, 2, GURL("http://blah/manifest/"));
446
447     job_ = handler_->MaybeLoadResource(request_.get(),
448                                        request_->context()->network_delegate());
449     EXPECT_TRUE(job_.get());
450     EXPECT_TRUE(job_->is_waiting());
451
452     // We have to wait for completion of storage->FindResponseForMainRequest.
453     ScheduleNextTask();
454   }
455
456   void Verify_MainResource_FallbackOverride() {
457     EXPECT_FALSE(job_->is_waiting());
458     EXPECT_TRUE(job_->is_delivering_network_response());
459
460     // When the request is restarted, the existing job is dropped so a
461     // real network job gets created. We expect NULL here which will cause
462     // the net library to create a real job.
463     job_ = handler_->MaybeLoadResource(request_.get(),
464                                        request_->context()->network_delegate());
465     EXPECT_FALSE(job_.get());
466
467     // Simulate an http error of the real network job, but with custom
468     // headers that override the fallback behavior.
469     const char kOverrideHeaders[] =
470         "HTTP/1.1 404 BOO HOO\0"
471         "x-chromium-appcache-fallback-override: disallow-fallback\0"
472         "\0";
473     net::HttpResponseInfo info;
474     info.headers = new net::HttpResponseHeaders(
475         std::string(kOverrideHeaders, arraysize(kOverrideHeaders)));
476     SimulateResponseInfo(info);
477
478     job_ = handler_->MaybeLoadFallbackForResponse(
479         request_.get(), request_->context()->network_delegate());
480     EXPECT_FALSE(job_.get());
481
482     TestFinished();
483   }
484
485   // SubResource_Miss_WithNoCacheSelected ----------------------------------
486
487   void SubResource_Miss_WithNoCacheSelected() {
488     request_ = empty_context_.CreateRequest(
489         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
490     handler_.reset(host_->CreateRequestHandler(request_.get(),
491                                                RESOURCE_TYPE_SUB_RESOURCE));
492
493     // We avoid creating handler when possible, sub-resource requests are not
494     // subject to retrieval from an appcache when there's no associated cache.
495     EXPECT_FALSE(handler_.get());
496
497     TestFinished();
498   }
499
500   // SubResource_Miss_WithCacheSelected ----------------------------------
501
502   void SubResource_Miss_WithCacheSelected() {
503     // A sub-resource load where the resource is not in an appcache, or
504     // in a network or fallback namespace, should result in a failed request.
505     host_->AssociateCompleteCache(MakeNewCache());
506
507     request_ = empty_context_.CreateRequest(
508         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
509     handler_.reset(host_->CreateRequestHandler(request_.get(),
510                                                RESOURCE_TYPE_SUB_RESOURCE));
511     EXPECT_TRUE(handler_.get());
512
513     job_ = handler_->MaybeLoadResource(request_.get(),
514                                        request_->context()->network_delegate());
515     EXPECT_TRUE(job_.get());
516     EXPECT_TRUE(job_->is_delivering_error_response());
517
518     AppCacheURLRequestJob* fallback_job;
519     fallback_job = handler_->MaybeLoadFallbackForRedirect(
520         request_.get(),
521         request_->context()->network_delegate(),
522         GURL("http://blah/redirect"));
523     EXPECT_FALSE(fallback_job);
524     fallback_job = handler_->MaybeLoadFallbackForResponse(
525         request_.get(), request_->context()->network_delegate());
526     EXPECT_FALSE(fallback_job);
527
528     TestFinished();
529   }
530
531   // SubResource_Miss_WithWaitForCacheSelection -----------------------------
532
533   void SubResource_Miss_WithWaitForCacheSelection() {
534     // Precondition, the host is waiting on cache selection.
535     scoped_refptr<AppCache> cache(MakeNewCache());
536     host_->pending_selected_cache_id_ = cache->cache_id();
537     host_->set_preferred_manifest_url(cache->owning_group()->manifest_url());
538
539     request_ = empty_context_.CreateRequest(
540         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
541     handler_.reset(host_->CreateRequestHandler(request_.get(),
542                                                RESOURCE_TYPE_SUB_RESOURCE));
543     EXPECT_TRUE(handler_.get());
544     job_ = handler_->MaybeLoadResource(request_.get(),
545                                        request_->context()->network_delegate());
546     EXPECT_TRUE(job_.get());
547     EXPECT_TRUE(job_->is_waiting());
548
549     host_->FinishCacheSelection(cache.get(), NULL);
550     EXPECT_FALSE(job_->is_waiting());
551     EXPECT_TRUE(job_->is_delivering_error_response());
552
553     AppCacheURLRequestJob* fallback_job;
554     fallback_job = handler_->MaybeLoadFallbackForRedirect(
555         request_.get(),
556         request_->context()->network_delegate(),
557         GURL("http://blah/redirect"));
558     EXPECT_FALSE(fallback_job);
559     fallback_job = handler_->MaybeLoadFallbackForResponse(
560         request_.get(), request_->context()->network_delegate());
561     EXPECT_FALSE(fallback_job);
562
563     TestFinished();
564   }
565
566   // SubResource_Hit -----------------------------
567
568   void SubResource_Hit() {
569     host_->AssociateCompleteCache(MakeNewCache());
570
571     mock_storage()->SimulateFindSubResource(
572         AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
573
574     request_ = empty_context_.CreateRequest(
575         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
576     handler_.reset(host_->CreateRequestHandler(request_.get(),
577                                                RESOURCE_TYPE_SUB_RESOURCE));
578     EXPECT_TRUE(handler_.get());
579     job_ = handler_->MaybeLoadResource(request_.get(),
580                                        request_->context()->network_delegate());
581     EXPECT_TRUE(job_.get());
582     EXPECT_TRUE(job_->is_delivering_appcache_response());
583
584     AppCacheURLRequestJob* fallback_job;
585     fallback_job = handler_->MaybeLoadFallbackForRedirect(
586         request_.get(),
587         request_->context()->network_delegate(),
588         GURL("http://blah/redirect"));
589     EXPECT_FALSE(fallback_job);
590     fallback_job = handler_->MaybeLoadFallbackForResponse(
591         request_.get(), request_->context()->network_delegate());
592     EXPECT_FALSE(fallback_job);
593
594     TestFinished();
595   }
596
597   // SubResource_RedirectFallback -----------------------------
598
599   void SubResource_RedirectFallback() {
600     // Redirects to resources in the a different origin are subject to
601     // fallback namespaces.
602     host_->AssociateCompleteCache(MakeNewCache());
603
604     mock_storage()->SimulateFindSubResource(
605         AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
606
607     request_ = empty_context_.CreateRequest(
608         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
609     handler_.reset(host_->CreateRequestHandler(request_.get(),
610                                                RESOURCE_TYPE_SUB_RESOURCE));
611     EXPECT_TRUE(handler_.get());
612     job_ = handler_->MaybeLoadResource(request_.get(),
613                                        request_->context()->network_delegate());
614     EXPECT_FALSE(job_.get());
615
616     job_ = handler_->MaybeLoadFallbackForRedirect(
617         request_.get(),
618         request_->context()->network_delegate(),
619         GURL("http://not_blah/redirect"));
620     EXPECT_TRUE(job_.get());
621     EXPECT_TRUE(job_->is_delivering_appcache_response());
622
623     AppCacheURLRequestJob* fallback_job;
624     fallback_job = handler_->MaybeLoadFallbackForResponse(
625         request_.get(), request_->context()->network_delegate());
626     EXPECT_FALSE(fallback_job);
627
628     TestFinished();
629   }
630
631   // SubResource_NoRedirectFallback -----------------------------
632
633   void SubResource_NoRedirectFallback() {
634     // Redirects to resources in the same-origin are not subject to
635     // fallback namespaces.
636     host_->AssociateCompleteCache(MakeNewCache());
637
638     mock_storage()->SimulateFindSubResource(
639         AppCacheEntry(), AppCacheEntry(AppCacheEntry::EXPLICIT, 1), false);
640
641     request_ = empty_context_.CreateRequest(
642         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
643     handler_.reset(host_->CreateRequestHandler(request_.get(),
644                                                RESOURCE_TYPE_SUB_RESOURCE));
645     EXPECT_TRUE(handler_.get());
646     job_ = handler_->MaybeLoadResource(request_.get(),
647                                        request_->context()->network_delegate());
648     EXPECT_FALSE(job_.get());
649
650     AppCacheURLRequestJob* fallback_job;
651     fallback_job = handler_->MaybeLoadFallbackForRedirect(
652         request_.get(),
653         request_->context()->network_delegate(),
654         GURL("http://blah/redirect"));
655     EXPECT_FALSE(fallback_job);
656
657     SimulateResponseCode(200);
658     fallback_job = handler_->MaybeLoadFallbackForResponse(
659         request_.get(), request_->context()->network_delegate());
660     EXPECT_FALSE(fallback_job);
661
662     TestFinished();
663   }
664
665   // SubResource_Network -----------------------------
666
667   void SubResource_Network() {
668     // A sub-resource load where the resource is in a network namespace,
669     // should result in the system using a 'real' job to do the network
670     // retrieval.
671     host_->AssociateCompleteCache(MakeNewCache());
672
673     mock_storage()->SimulateFindSubResource(
674         AppCacheEntry(), AppCacheEntry(), true);
675
676     request_ = empty_context_.CreateRequest(
677         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
678     handler_.reset(host_->CreateRequestHandler(request_.get(),
679                                                RESOURCE_TYPE_SUB_RESOURCE));
680     EXPECT_TRUE(handler_.get());
681     job_ = handler_->MaybeLoadResource(request_.get(),
682                                        request_->context()->network_delegate());
683     EXPECT_FALSE(job_.get());
684
685     AppCacheURLRequestJob* fallback_job;
686     fallback_job = handler_->MaybeLoadFallbackForRedirect(
687         request_.get(),
688         request_->context()->network_delegate(),
689         GURL("http://blah/redirect"));
690     EXPECT_FALSE(fallback_job);
691     fallback_job = handler_->MaybeLoadFallbackForResponse(
692         request_.get(), request_->context()->network_delegate());
693     EXPECT_FALSE(fallback_job);
694
695     TestFinished();
696   }
697
698   // DestroyedHost -----------------------------
699
700   void DestroyedHost() {
701     host_->AssociateCompleteCache(MakeNewCache());
702
703     mock_storage()->SimulateFindSubResource(
704         AppCacheEntry(AppCacheEntry::EXPLICIT, 1), AppCacheEntry(), false);
705
706     request_ = empty_context_.CreateRequest(
707         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
708     handler_.reset(host_->CreateRequestHandler(request_.get(),
709                                                RESOURCE_TYPE_SUB_RESOURCE));
710     EXPECT_TRUE(handler_.get());
711
712     backend_impl_->UnregisterHost(1);
713     host_ = NULL;
714
715     EXPECT_FALSE(handler_->MaybeLoadResource(
716         request_.get(), request_->context()->network_delegate()));
717     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
718         request_.get(),
719         request_->context()->network_delegate(),
720         GURL("http://blah/redirect")));
721     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
722         request_.get(), request_->context()->network_delegate()));
723
724     TestFinished();
725   }
726
727   // DestroyedHostWithWaitingJob -----------------------------
728
729   void DestroyedHostWithWaitingJob() {
730     // Precondition, the host is waiting on cache selection.
731     host_->pending_selected_cache_id_ = 1;
732
733     request_ = empty_context_.CreateRequest(
734         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
735     handler_.reset(host_->CreateRequestHandler(request_.get(),
736                                                RESOURCE_TYPE_SUB_RESOURCE));
737     EXPECT_TRUE(handler_.get());
738
739     job_ = handler_->MaybeLoadResource(request_.get(),
740                                        request_->context()->network_delegate());
741     EXPECT_TRUE(job_.get());
742     EXPECT_TRUE(job_->is_waiting());
743
744     backend_impl_->UnregisterHost(1);
745     host_ = NULL;
746     EXPECT_TRUE(job_->has_been_killed());
747
748     EXPECT_FALSE(handler_->MaybeLoadResource(
749         request_.get(), request_->context()->network_delegate()));
750     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
751         request_.get(),
752         request_->context()->network_delegate(),
753         GURL("http://blah/redirect")));
754     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
755         request_.get(), request_->context()->network_delegate()));
756
757     TestFinished();
758   }
759
760   // UnsupportedScheme -----------------------------
761
762   void UnsupportedScheme() {
763     // Precondition, the host is waiting on cache selection.
764     host_->pending_selected_cache_id_ = 1;
765
766     request_ = empty_context_.CreateRequest(
767         GURL("ftp://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
768     handler_.reset(host_->CreateRequestHandler(request_.get(),
769                                                RESOURCE_TYPE_SUB_RESOURCE));
770     EXPECT_TRUE(handler_.get());  // we could redirect to http (conceivably)
771
772     EXPECT_FALSE(handler_->MaybeLoadResource(
773         request_.get(), request_->context()->network_delegate()));
774     EXPECT_FALSE(handler_->MaybeLoadFallbackForRedirect(
775         request_.get(),
776         request_->context()->network_delegate(),
777         GURL("ftp://blah/redirect")));
778     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
779         request_.get(), request_->context()->network_delegate()));
780
781     TestFinished();
782   }
783
784   // CanceledRequest -----------------------------
785
786   void CanceledRequest() {
787     request_ = empty_context_.CreateRequest(
788         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
789     handler_.reset(host_->CreateRequestHandler(request_.get(),
790                                                RESOURCE_TYPE_MAIN_FRAME));
791     EXPECT_TRUE(handler_.get());
792
793     job_ = handler_->MaybeLoadResource(request_.get(),
794                                        request_->context()->network_delegate());
795     EXPECT_TRUE(job_.get());
796     EXPECT_TRUE(job_->is_waiting());
797     EXPECT_FALSE(job_->has_been_started());
798
799     job_factory_->SetJob(job_.get());
800     request_->Start();
801     EXPECT_TRUE(job_->has_been_started());
802
803     request_->Cancel();
804     EXPECT_TRUE(job_->has_been_killed());
805
806     EXPECT_FALSE(handler_->MaybeLoadFallbackForResponse(
807         request_.get(), request_->context()->network_delegate()));
808
809     TestFinished();
810   }
811
812   // WorkerRequest -----------------------------
813
814   void WorkerRequest() {
815     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
816         RESOURCE_TYPE_MAIN_FRAME));
817     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
818         RESOURCE_TYPE_SUB_FRAME));
819     EXPECT_TRUE(AppCacheRequestHandler::IsMainResourceType(
820         RESOURCE_TYPE_SHARED_WORKER));
821     EXPECT_FALSE(AppCacheRequestHandler::IsMainResourceType(
822         RESOURCE_TYPE_WORKER));
823
824     request_ = empty_context_.CreateRequest(
825         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
826
827     const int kParentHostId = host_->host_id();
828     const int kWorkerHostId = 2;
829     const int kAbandonedWorkerHostId = 3;
830     const int kNonExsitingHostId = 700;
831
832     backend_impl_->RegisterHost(kWorkerHostId);
833     AppCacheHost* worker_host = backend_impl_->GetHost(kWorkerHostId);
834     worker_host->SelectCacheForWorker(kParentHostId, kMockProcessId);
835     handler_.reset(worker_host->CreateRequestHandler(
836         request_.get(), RESOURCE_TYPE_SHARED_WORKER));
837     EXPECT_TRUE(handler_.get());
838     // Verify that the handler is associated with the parent host.
839     EXPECT_EQ(host_, handler_->host_);
840
841     // Create a new worker host, but associate it with a parent host that
842     // does not exists to simulate the host having been torn down.
843     backend_impl_->UnregisterHost(kWorkerHostId);
844     backend_impl_->RegisterHost(kAbandonedWorkerHostId);
845     worker_host = backend_impl_->GetHost(kAbandonedWorkerHostId);
846     EXPECT_EQ(NULL, backend_impl_->GetHost(kNonExsitingHostId));
847     worker_host->SelectCacheForWorker(kNonExsitingHostId, kMockProcessId);
848     handler_.reset(worker_host->CreateRequestHandler(
849         request_.get(), RESOURCE_TYPE_SHARED_WORKER));
850     EXPECT_FALSE(handler_.get());
851
852     TestFinished();
853   }
854
855   // MainResource_Blocked --------------------------------------------------
856
857   void MainResource_Blocked() {
858     PushNextTask(
859         base::Bind(&AppCacheRequestHandlerTest::Verify_MainResource_Blocked,
860                    base::Unretained(this)));
861
862     request_ = empty_context_.CreateRequest(
863         GURL("http://blah/"), net::DEFAULT_PRIORITY, &delegate_, NULL);
864     handler_.reset(host_->CreateRequestHandler(request_.get(),
865                                                RESOURCE_TYPE_MAIN_FRAME));
866     EXPECT_TRUE(handler_.get());
867
868     mock_policy_->can_load_return_value_ = false;
869     mock_storage()->SimulateFindMainResource(
870         AppCacheEntry(AppCacheEntry::EXPLICIT, 1),
871         GURL(), AppCacheEntry(),
872         1, 2, GURL("http://blah/manifest/"));
873
874     job_ = handler_->MaybeLoadResource(request_.get(),
875                                        request_->context()->network_delegate());
876     EXPECT_TRUE(job_.get());
877     EXPECT_TRUE(job_->is_waiting());
878
879     // We have to wait for completion of storage->FindResponseForMainRequest.
880     ScheduleNextTask();
881   }
882
883   void Verify_MainResource_Blocked() {
884     EXPECT_FALSE(job_->is_waiting());
885     EXPECT_FALSE(job_->is_delivering_appcache_response());
886
887     EXPECT_EQ(0, handler_->found_cache_id_);
888     EXPECT_EQ(0, handler_->found_group_id_);
889     EXPECT_TRUE(handler_->found_manifest_url_.is_empty());
890     EXPECT_TRUE(host_->preferred_manifest_url().is_empty());
891     EXPECT_TRUE(host_->main_resource_blocked_);
892     EXPECT_TRUE(host_->blocked_manifest_url_ == GURL("http://blah/manifest/"));
893
894     TestFinished();
895   }
896
897   // Test case helpers --------------------------------------------------
898
899   AppCache* MakeNewCache() {
900     AppCache* cache = new AppCache(
901         mock_storage(), mock_storage()->NewCacheId());
902     cache->set_complete(true);
903     AppCacheGroup* group = new AppCacheGroup(
904         mock_storage(), GURL("http://blah/manifest"),
905         mock_storage()->NewGroupId());
906     group->AddCache(cache);
907     return cache;
908   }
909
910   MockAppCacheStorage* mock_storage() {
911     return reinterpret_cast<MockAppCacheStorage*>(mock_service_->storage());
912   }
913
914   // Data members --------------------------------------------------
915
916   scoped_ptr<base::WaitableEvent> test_finished_event_;
917   std::stack<base::Closure> task_stack_;
918   scoped_ptr<MockAppCacheService> mock_service_;
919   scoped_ptr<AppCacheBackendImpl> backend_impl_;
920   scoped_ptr<MockFrontend> mock_frontend_;
921   scoped_ptr<MockAppCachePolicy> mock_policy_;
922   AppCacheHost* host_;
923   net::URLRequestContext empty_context_;
924   scoped_ptr<MockURLRequestJobFactory> job_factory_;
925   MockURLRequestDelegate delegate_;
926   scoped_ptr<net::URLRequest> request_;
927   scoped_ptr<AppCacheRequestHandler> handler_;
928   scoped_refptr<AppCacheURLRequestJob> job_;
929
930   static scoped_ptr<base::Thread> io_thread_;
931 };
932
933 // static
934 scoped_ptr<base::Thread> AppCacheRequestHandlerTest::io_thread_;
935
936 TEST_F(AppCacheRequestHandlerTest, MainResource_Miss) {
937   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Miss);
938 }
939
940 TEST_F(AppCacheRequestHandlerTest, MainResource_Hit) {
941   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Hit);
942 }
943
944 TEST_F(AppCacheRequestHandlerTest, MainResource_Fallback) {
945   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Fallback);
946 }
947
948 TEST_F(AppCacheRequestHandlerTest, MainResource_FallbackOverride) {
949   RunTestOnIOThread(
950       &AppCacheRequestHandlerTest::MainResource_FallbackOverride);
951 }
952
953 TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithNoCacheSelected) {
954   RunTestOnIOThread(
955       &AppCacheRequestHandlerTest::SubResource_Miss_WithNoCacheSelected);
956 }
957
958 TEST_F(AppCacheRequestHandlerTest, SubResource_Miss_WithCacheSelected) {
959   RunTestOnIOThread(
960       &AppCacheRequestHandlerTest::SubResource_Miss_WithCacheSelected);
961 }
962
963 TEST_F(AppCacheRequestHandlerTest,
964        SubResource_Miss_WithWaitForCacheSelection) {
965   RunTestOnIOThread(
966       &AppCacheRequestHandlerTest::SubResource_Miss_WithWaitForCacheSelection);
967 }
968
969 TEST_F(AppCacheRequestHandlerTest, SubResource_Hit) {
970   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Hit);
971 }
972
973 TEST_F(AppCacheRequestHandlerTest, SubResource_RedirectFallback) {
974   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_RedirectFallback);
975 }
976
977 TEST_F(AppCacheRequestHandlerTest, SubResource_NoRedirectFallback) {
978   RunTestOnIOThread(
979     &AppCacheRequestHandlerTest::SubResource_NoRedirectFallback);
980 }
981
982 TEST_F(AppCacheRequestHandlerTest, SubResource_Network) {
983   RunTestOnIOThread(&AppCacheRequestHandlerTest::SubResource_Network);
984 }
985
986 TEST_F(AppCacheRequestHandlerTest, DestroyedHost) {
987   RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHost);
988 }
989
990 TEST_F(AppCacheRequestHandlerTest, DestroyedHostWithWaitingJob) {
991   RunTestOnIOThread(&AppCacheRequestHandlerTest::DestroyedHostWithWaitingJob);
992 }
993
994 TEST_F(AppCacheRequestHandlerTest, UnsupportedScheme) {
995   RunTestOnIOThread(&AppCacheRequestHandlerTest::UnsupportedScheme);
996 }
997
998 TEST_F(AppCacheRequestHandlerTest, CanceledRequest) {
999   RunTestOnIOThread(&AppCacheRequestHandlerTest::CanceledRequest);
1000 }
1001
1002 TEST_F(AppCacheRequestHandlerTest, WorkerRequest) {
1003   RunTestOnIOThread(&AppCacheRequestHandlerTest::WorkerRequest);
1004 }
1005
1006 TEST_F(AppCacheRequestHandlerTest, MainResource_Blocked) {
1007   RunTestOnIOThread(&AppCacheRequestHandlerTest::MainResource_Blocked);
1008 }
1009
1010 }  // namespace content