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