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