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/browser/service_worker/service_worker_utils.h"
14 #include "content/common/resource_request_body.h"
15 #include "content/common/service_worker/service_worker_types.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"
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 ResourceType resource_type
,
32 RequestContextType request_context_type
,
33 RequestContextFrameType frame_type
,
34 scoped_refptr
<ResourceRequestBody
> body
)
35 : ServiceWorkerRequestHandler(context
,
39 is_main_resource_load_(
40 ServiceWorkerUtils::IsMainResourceType(resource_type
)),
41 request_mode_(request_mode
),
42 credentials_mode_(credentials_mode
),
43 request_context_type_(request_context_type
),
44 frame_type_(frame_type
),
49 ServiceWorkerControlleeRequestHandler::
50 ~ServiceWorkerControlleeRequestHandler() {
51 // Navigation triggers an update to occur shortly after the page and
52 // its initial subresources load.
53 if (provider_host_
&& provider_host_
->active_version()) {
54 if (is_main_resource_load_
)
55 provider_host_
->active_version()->ScheduleUpdate();
57 provider_host_
->active_version()->DeferScheduledUpdate();
60 if (is_main_resource_load_
&& provider_host_
)
61 provider_host_
->SetAllowAssociation(true);
64 net::URLRequestJob
* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
65 net::URLRequest
* request
,
66 net::NetworkDelegate
* network_delegate
,
67 ResourceContext
* resource_context
) {
68 if (job_
.get() && worker_start_time_
.is_null()) {
69 // Save worker timings of the first job.
70 worker_start_time_
= job_
->worker_start_time();
71 worker_ready_time_
= job_
->worker_ready_time();
74 if (!context_
|| !provider_host_
) {
75 // We can't do anything other than to fall back to network.
80 // This may get called multiple times for original and redirect requests:
81 // A. original request case: job_ is null, no previous location info.
82 // B. redirect or restarted request case:
83 // a) job_ is non-null if the previous location was forwarded to SW.
84 // b) job_ is null if the previous location was fallback.
85 // c) job_ is non-null if additional restart was required to fall back.
87 // We've come here by restart, we already have original request and it
88 // tells we should fallback to network. (Case B-c)
89 if (job_
.get() && job_
->ShouldFallbackToNetwork()) {
94 // It's for original request (A) or redirect case (B-a or B-b).
95 DCHECK(!job_
.get() || job_
->ShouldForwardToServiceWorker());
97 job_
= new ServiceWorkerURLRequestJob(
98 request
, network_delegate
, provider_host_
, blob_storage_context_
,
99 resource_context
, request_mode_
, credentials_mode_
,
100 is_main_resource_load_
, request_context_type_
, frame_type_
, body_
);
101 resource_context_
= resource_context
;
103 if (is_main_resource_load_
)
104 PrepareForMainResource(request
);
106 PrepareForSubResource();
108 if (job_
->ShouldFallbackToNetwork()) {
109 // If we know we can fallback to network at this point (in case
110 // the storage lookup returned immediately), just return NULL here to
111 // fallback to network.
119 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
120 ResourceResponseInfo
* response_info
) const {
122 response_info
->was_fetched_via_service_worker
= false;
123 response_info
->was_fallback_required_by_service_worker
= false;
124 response_info
->original_url_via_service_worker
= GURL();
125 response_info
->service_worker_start_time
= worker_start_time_
;
126 response_info
->service_worker_ready_time
= worker_ready_time_
;
129 job_
->GetExtraResponseInfo(response_info
);
130 if (!worker_start_time_
.is_null()) {
131 // If we have worker timings from previous job, use it.
132 response_info
->service_worker_start_time
= worker_start_time_
;
133 response_info
->service_worker_ready_time
= worker_ready_time_
;
137 void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
138 const net::URLRequest
* request
) {
141 DCHECK(provider_host_
);
142 TRACE_EVENT_ASYNC_BEGIN1(
144 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
146 "URL", request
->url().spec());
147 // The corresponding provider_host may already have associated a registration
148 // in redirect case, unassociate it now.
149 provider_host_
->DisassociateRegistration();
151 // Also prevent a registrater job for establishing an association to a new
152 // registration while we're finding an existing registration.
153 provider_host_
->SetAllowAssociation(false);
155 stripped_url_
= net::SimplifyUrlForRequest(request
->url());
156 provider_host_
->SetDocumentUrl(stripped_url_
);
157 provider_host_
->SetTopmostFrameUrl(request
->first_party_for_cookies());
158 context_
->storage()->FindRegistrationForDocument(
159 stripped_url_
, base::Bind(&self::DidLookupRegistrationForMainResource
,
160 weak_factory_
.GetWeakPtr()));
164 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
165 ServiceWorkerStatusCode status
,
166 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
169 provider_host_
->SetAllowAssociation(true);
170 if (status
!= SERVICE_WORKER_OK
|| !provider_host_
) {
171 job_
->FallbackToNetwork();
172 TRACE_EVENT_ASYNC_END1(
174 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
179 DCHECK(registration
.get());
181 if (!GetContentClient()->browser()->AllowServiceWorker(
182 registration
->pattern(), provider_host_
->topmost_frame_url(),
183 resource_context_
, provider_host_
->process_id(),
184 provider_host_
->frame_id())) {
185 job_
->FallbackToNetwork();
186 TRACE_EVENT_ASYNC_END2(
188 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
191 "Info", "ServiceWorker is blocked");
195 // Initiate activation of a waiting version.
196 // Usually a register job initiates activation but that
197 // doesn't happen if the browser exits prior to activation
198 // having occurred. This check handles that case.
199 if (registration
->waiting_version())
200 registration
->ActivateWaitingVersionWhenReady();
202 scoped_refptr
<ServiceWorkerVersion
> active_version
=
203 registration
->active_version();
205 // Wait until it's activated before firing fetch events.
206 if (active_version
.get() &&
207 active_version
->status() == ServiceWorkerVersion::ACTIVATING
) {
208 provider_host_
->SetAllowAssociation(false);
209 registration
->active_version()->RegisterStatusChangeCallback(
210 base::Bind(&self::OnVersionStatusChanged
,
211 weak_factory_
.GetWeakPtr(),
214 TRACE_EVENT_ASYNC_END2(
216 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
219 "Info", "Wait until finished SW activation");
223 if (!active_version
.get() ||
224 active_version
->status() != ServiceWorkerVersion::ACTIVATED
) {
225 job_
->FallbackToNetwork();
226 TRACE_EVENT_ASYNC_END2(
228 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
232 "ServiceWorkerVersion is not available, so falling back to network");
236 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_
);
238 provider_host_
->AssociateRegistration(registration
.get(),
239 false /* notify_controllerchange */);
240 job_
->ForwardToServiceWorker();
241 TRACE_EVENT_ASYNC_END2(
243 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
247 "Forwarded to the ServiceWorker");
250 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
251 ServiceWorkerRegistration
* registration
,
252 ServiceWorkerVersion
* version
) {
254 provider_host_
->SetAllowAssociation(true);
255 if (version
!= registration
->active_version() ||
256 version
->status() != ServiceWorkerVersion::ACTIVATED
||
258 job_
->FallbackToNetwork();
262 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_
);
264 provider_host_
->AssociateRegistration(registration
,
265 false /* notify_controllerchange */);
266 job_
->ForwardToServiceWorker();
269 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
272 DCHECK(provider_host_
->active_version());
273 job_
->ForwardToServiceWorker();
276 } // namespace content