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