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