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