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