Service workers: Allow HTTPS pages arrived at via HTTP redirect to use SW
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_controllee_request_handler.cc
blob177373b23b5c1854cfc7d002d38264b9fe678ecd
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.
5 #include "content/browser/service_worker/service_worker_controllee_request_handler.h"
7 #include "base/trace_event/trace_event.h"
8 #include "content/browser/service_worker/service_worker_context_core.h"
9 #include "content/browser/service_worker/service_worker_metrics.h"
10 #include "content/browser/service_worker/service_worker_provider_host.h"
11 #include "content/browser/service_worker/service_worker_registration.h"
12 #include "content/browser/service_worker/service_worker_url_request_job.h"
13 #include "content/common/resource_request_body.h"
14 #include "content/common/service_worker/service_worker_types.h"
15 #include "content/common/service_worker/service_worker_utils.h"
16 #include "content/public/browser/content_browser_client.h"
17 #include "content/public/common/content_client.h"
18 #include "content/public/common/resource_response_info.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/net_util.h"
21 #include "net/url_request/url_request.h"
23 namespace content {
25 ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler(
26 base::WeakPtr<ServiceWorkerContextCore> context,
27 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
28 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
29 FetchRequestMode request_mode,
30 FetchCredentialsMode credentials_mode,
31 FetchRedirectMode redirect_mode,
32 ResourceType resource_type,
33 RequestContextType request_context_type,
34 RequestContextFrameType frame_type,
35 scoped_refptr<ResourceRequestBody> body)
36 : ServiceWorkerRequestHandler(context,
37 provider_host,
38 blob_storage_context,
39 resource_type),
40 is_main_resource_load_(
41 ServiceWorkerUtils::IsMainResourceType(resource_type)),
42 request_mode_(request_mode),
43 credentials_mode_(credentials_mode),
44 redirect_mode_(redirect_mode),
45 request_context_type_(request_context_type),
46 frame_type_(frame_type),
47 body_(body),
48 skip_service_worker_(false),
49 weak_factory_(this) {}
51 ServiceWorkerControlleeRequestHandler::
52 ~ServiceWorkerControlleeRequestHandler() {
53 // Navigation triggers an update to occur shortly after the page and
54 // its initial subresources load.
55 if (provider_host_ && provider_host_->active_version()) {
56 if (is_main_resource_load_)
57 provider_host_->active_version()->ScheduleUpdate();
58 else
59 provider_host_->active_version()->DeferScheduledUpdate();
62 if (is_main_resource_load_ && provider_host_)
63 provider_host_->SetAllowAssociation(true);
66 net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
67 net::URLRequest* request,
68 net::NetworkDelegate* network_delegate,
69 ResourceContext* resource_context) {
70 if (job_.get() && worker_start_time_.is_null()) {
71 // Save worker timings of the first job.
72 worker_start_time_ = job_->worker_start_time();
73 worker_ready_time_ = job_->worker_ready_time();
76 if (!context_ || !provider_host_) {
77 // We can't do anything other than to fall back to network.
78 job_ = NULL;
79 return NULL;
82 // This may get called multiple times for original and redirect requests:
83 // A. original request case: job_ is null, no previous location info.
84 // B. redirect or restarted request case:
85 // a) job_ is non-null if the previous location was forwarded to SW.
86 // b) job_ is null if the previous location was fallback.
87 // c) job_ is non-null if additional restart was required to fall back.
89 // We've come here by restart, we already have original request and it
90 // tells we should fallback to network. (Case B-c)
91 if ((job_.get() && job_->ShouldFallbackToNetwork()) || skip_service_worker_) {
92 FallbackToNetwork();
93 return NULL;
96 // It's for original request (A) or redirect case (B-a or B-b).
97 DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
99 job_ = new ServiceWorkerURLRequestJob(
100 request, network_delegate, provider_host_, blob_storage_context_,
101 resource_context, request_mode_, credentials_mode_, redirect_mode_,
102 is_main_resource_load_, request_context_type_, frame_type_, body_);
103 resource_context_ = resource_context;
105 if (is_main_resource_load_)
106 PrepareForMainResource(request);
107 else
108 PrepareForSubResource();
110 if (job_->ShouldFallbackToNetwork()) {
111 // If we know we can fallback to network at this point (in case
112 // the storage lookup returned immediately), just return NULL here to
113 // fallback to network.
114 FallbackToNetwork();
115 return NULL;
118 return job_.get();
121 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
122 ResourceResponseInfo* response_info) const {
123 if (!job_.get()) {
124 response_info->was_fetched_via_service_worker = false;
125 response_info->was_fallback_required_by_service_worker = false;
126 response_info->original_url_via_service_worker = GURL();
127 response_info->service_worker_start_time = worker_start_time_;
128 response_info->service_worker_ready_time = worker_ready_time_;
129 return;
131 job_->GetExtraResponseInfo(response_info);
132 if (!worker_start_time_.is_null()) {
133 // If we have worker timings from previous job, use it.
134 response_info->service_worker_start_time = worker_start_time_;
135 response_info->service_worker_ready_time = worker_ready_time_;
139 void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
140 const net::URLRequest* request) {
141 DCHECK(job_.get());
142 DCHECK(context_);
143 DCHECK(provider_host_);
144 TRACE_EVENT_ASYNC_BEGIN1(
145 "ServiceWorker",
146 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
147 job_.get(),
148 "URL", request->url().spec());
149 // The corresponding provider_host may already have associated a registration
150 // in redirect case, unassociate it now.
151 provider_host_->DisassociateRegistration();
153 // Also prevent a registrater job for establishing an association to a new
154 // registration while we're finding an existing registration.
155 provider_host_->SetAllowAssociation(false);
157 stripped_url_ = net::SimplifyUrlForRequest(request->url());
158 provider_host_->SetDocumentUrl(stripped_url_);
159 provider_host_->SetTopmostFrameUrl(request->first_party_for_cookies());
160 context_->storage()->FindRegistrationForDocument(
161 stripped_url_, base::Bind(&self::DidLookupRegistrationForMainResource,
162 weak_factory_.GetWeakPtr()));
165 void
166 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
167 ServiceWorkerStatusCode status,
168 const scoped_refptr<ServiceWorkerRegistration>& registration) {
169 DCHECK(job_.get());
170 if (provider_host_)
171 provider_host_->SetAllowAssociation(true);
172 if (status != SERVICE_WORKER_OK || !provider_host_) {
173 job_->FallbackToNetwork();
174 TRACE_EVENT_ASYNC_END1(
175 "ServiceWorker",
176 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
177 job_.get(),
178 "Status", status);
179 return;
181 DCHECK(registration.get());
183 if (!GetContentClient()->browser()->AllowServiceWorker(
184 registration->pattern(), provider_host_->topmost_frame_url(),
185 resource_context_, provider_host_->process_id(),
186 provider_host_->frame_id())) {
187 job_->FallbackToNetwork();
188 TRACE_EVENT_ASYNC_END2(
189 "ServiceWorker",
190 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
191 job_.get(),
192 "Status", status,
193 "Info", "ServiceWorker is blocked");
194 return;
197 // Initiate activation of a waiting version.
198 // Usually a register job initiates activation but that
199 // doesn't happen if the browser exits prior to activation
200 // having occurred. This check handles that case.
201 if (registration->waiting_version())
202 registration->ActivateWaitingVersionWhenReady();
204 scoped_refptr<ServiceWorkerVersion> active_version =
205 registration->active_version();
207 // Wait until it's activated before firing fetch events.
208 if (active_version.get() &&
209 active_version->status() == ServiceWorkerVersion::ACTIVATING) {
210 provider_host_->SetAllowAssociation(false);
211 registration->active_version()->RegisterStatusChangeCallback(
212 base::Bind(&self::OnVersionStatusChanged,
213 weak_factory_.GetWeakPtr(),
214 registration,
215 active_version));
216 TRACE_EVENT_ASYNC_END2(
217 "ServiceWorker",
218 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
219 job_.get(),
220 "Status", status,
221 "Info", "Wait until finished SW activation");
222 return;
225 if (!active_version.get() ||
226 active_version->status() != ServiceWorkerVersion::ACTIVATED) {
227 job_->FallbackToNetwork();
228 TRACE_EVENT_ASYNC_END2(
229 "ServiceWorker",
230 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
231 job_.get(),
232 "Status", status,
233 "Info",
234 "ServiceWorkerVersion is not available, so falling back to network");
235 return;
238 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_);
240 provider_host_->AssociateRegistration(registration.get(),
241 false /* notify_controllerchange */);
242 job_->ForwardToServiceWorker();
243 TRACE_EVENT_ASYNC_END2(
244 "ServiceWorker",
245 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
246 job_.get(),
247 "Status", status,
248 "Info",
249 "Forwarded to the ServiceWorker");
252 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
253 ServiceWorkerRegistration* registration,
254 ServiceWorkerVersion* version) {
255 if (provider_host_)
256 provider_host_->SetAllowAssociation(true);
257 if (version != registration->active_version() ||
258 version->status() != ServiceWorkerVersion::ACTIVATED ||
259 !provider_host_) {
260 job_->FallbackToNetwork();
261 return;
264 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_);
266 provider_host_->AssociateRegistration(registration,
267 false /* notify_controllerchange */);
268 job_->ForwardToServiceWorker();
271 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
272 DCHECK(job_.get());
273 DCHECK(context_);
274 DCHECK(provider_host_->active_version());
275 job_->ForwardToServiceWorker();
278 void ServiceWorkerControlleeRequestHandler::FallbackToNetwork() {
279 // Once a subresource request was fallbacked to the network, we set
280 // |skip_service_worker_| because the request should not go to the service
281 // worker.
282 if (!is_main_resource_load_)
283 skip_service_worker_ = true;
284 job_ = NULL;
287 } // namespace content