service_worker_request_handler.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/service_worker/service_worker_request_handler.h"
6
7#include "content/browser/service_worker/service_worker_context_core.h"
8#include "content/browser/service_worker/service_worker_context_wrapper.h"
9#include "content/browser/service_worker/service_worker_provider_host.h"
10#include "content/browser/service_worker/service_worker_registration.h"
11#include "content/browser/service_worker/service_worker_url_request_job.h"
12#include "content/browser/service_worker/service_worker_utils.h"
13#include "content/common/service_worker/service_worker_types.h"
14#include "net/url_request/url_request.h"
15
16namespace content {
17
18namespace {
19
20int kUserDataKey;  // Key value is not important.
21
22class ServiceWorkerRequestInterceptor
23    : public net::URLRequestJobFactory::ProtocolHandler {
24 public:
25  ServiceWorkerRequestInterceptor() {}
26  virtual ~ServiceWorkerRequestInterceptor() {}
27  virtual net::URLRequestJob* MaybeCreateJob(
28      net::URLRequest* request,
29      net::NetworkDelegate* network_delegate) const OVERRIDE {
30    ServiceWorkerRequestHandler* handler =
31        ServiceWorkerRequestHandler::GetHandler(request);
32    if (!handler)
33      return NULL;
34    return handler->MaybeCreateJob(request, network_delegate);
35  }
36
37 private:
38  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerRequestInterceptor);
39};
40
41}  // namespace
42
43void ServiceWorkerRequestHandler::InitializeHandler(
44    net::URLRequest* request,
45    ServiceWorkerContextWrapper* context_wrapper,
46    int process_id,
47    int provider_id,
48    ResourceType::Type resource_type) {
49  if (!ServiceWorkerUtils::IsFeatureEnabled())
50    return;
51
52  if (!context_wrapper || !context_wrapper->context() ||
53      provider_id == kInvalidServiceWorkerProviderId) {
54    return;
55  }
56
57  ServiceWorkerProviderHost* provider_host =
58      context_wrapper->context()->GetProviderHost(process_id, provider_id);
59  if (!provider_host)
60    return;
61
62  if (!provider_host->ShouldHandleRequest(resource_type))
63    return;
64
65  scoped_ptr<ServiceWorkerRequestHandler> handler(
66      new ServiceWorkerRequestHandler(context_wrapper->context()->AsWeakPtr(),
67                                      provider_host->AsWeakPtr(),
68                                      resource_type));
69  request->SetUserData(&kUserDataKey, handler.release());
70}
71
72ServiceWorkerRequestHandler* ServiceWorkerRequestHandler::GetHandler(
73    net::URLRequest* request) {
74  return reinterpret_cast<ServiceWorkerRequestHandler*>(
75      request->GetUserData(&kUserDataKey));
76}
77
78scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>
79ServiceWorkerRequestHandler::CreateInterceptor() {
80  return make_scoped_ptr<net::URLRequestJobFactory::ProtocolHandler>(
81      new ServiceWorkerRequestInterceptor);
82}
83
84ServiceWorkerRequestHandler::~ServiceWorkerRequestHandler() {
85}
86
87net::URLRequestJob* ServiceWorkerRequestHandler::MaybeCreateJob(
88    net::URLRequest* request,
89    net::NetworkDelegate* network_delegate) {
90  if (!context_ || !provider_host_) {
91    // We can't do anything other than to fall back to network.
92    job_ = NULL;
93    return NULL;
94  }
95
96  // This may get called multiple times for original and redirect requests:
97  // A. original request case: job_ is null, no previous location info.
98  // B. redirect or restarted request case:
99  //  a) job_ is non-null if the previous location was forwarded to SW.
100  //  b) job_ is null if the previous location was fallback.
101  //  c) job_ is non-null if additional restart was required to fall back.
102
103  // We've come here by restart, we already have original request and it
104  // tells we should fallback to network. (Case B-c)
105  if (job_.get() && job_->ShouldFallbackToNetwork()) {
106    job_ = NULL;
107    return NULL;
108  }
109
110  // It's for original request (A) or redirect case (B-a or B-b).
111  DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
112
113  job_ = new ServiceWorkerURLRequestJob(request, network_delegate,
114                                        provider_host_);
115  if (ServiceWorkerUtils::IsMainResourceType(resource_type_))
116    PrepareForMainResource(request->url());
117  else
118    PrepareForSubResource();
119
120  if (job_->ShouldFallbackToNetwork()) {
121    // If we know we can fallback to network at this point (in case
122    // the storage lookup returned immediately), just return NULL here to
123    // fallback to network.
124    job_ = NULL;
125    return NULL;
126  }
127
128  return job_.get();
129}
130
131ServiceWorkerRequestHandler::ServiceWorkerRequestHandler(
132    base::WeakPtr<ServiceWorkerContextCore> context,
133    base::WeakPtr<ServiceWorkerProviderHost> provider_host,
134    ResourceType::Type resource_type)
135    : context_(context),
136      provider_host_(provider_host),
137      resource_type_(resource_type),
138      weak_factory_(this) {
139}
140
141void ServiceWorkerRequestHandler::PrepareForMainResource(const GURL& url) {
142  DCHECK(job_.get());
143  DCHECK(context_);
144  // The corresponding provider_host may already have associate version in
145  // redirect case, unassociate it now.
146  provider_host_->SetActiveVersion(NULL);
147  provider_host_->SetPendingVersion(NULL);
148  provider_host_->set_document_url(url);
149  context_->storage()->FindRegistrationForDocument(
150      url,
151      base::Bind(&self::DidLookupRegistrationForMainResource,
152                  weak_factory_.GetWeakPtr()));
153}
154
155void ServiceWorkerRequestHandler::DidLookupRegistrationForMainResource(
156    ServiceWorkerStatusCode status,
157    const scoped_refptr<ServiceWorkerRegistration>& registration) {
158  DCHECK(job_.get());
159  if (status != SERVICE_WORKER_OK || !registration->active_version()) {
160    // No registration, or no active version for the registration is available.
161    job_->FallbackToNetwork();
162    return;
163  }
164  DCHECK(registration);
165  provider_host_->SetActiveVersion(registration->active_version());
166  provider_host_->SetPendingVersion(registration->pending_version());
167  job_->ForwardToServiceWorker();
168}
169
170void ServiceWorkerRequestHandler::PrepareForSubResource() {
171  DCHECK(job_.get());
172  DCHECK(context_);
173  DCHECK(provider_host_->active_version());
174  job_->ForwardToServiceWorker();
175}
176
177}  // namespace content
178