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"
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
,
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
),
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();
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.
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_
) {
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
);
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.
121 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
122 ResourceResponseInfo
* response_info
) const {
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_
;
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
) {
143 DCHECK(provider_host_
);
144 TRACE_EVENT_ASYNC_BEGIN1(
146 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
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()));
166 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
167 ServiceWorkerStatusCode status
,
168 const scoped_refptr
<ServiceWorkerRegistration
>& registration
) {
171 provider_host_
->SetAllowAssociation(true);
172 if (status
!= SERVICE_WORKER_OK
|| !provider_host_
) {
173 job_
->FallbackToNetwork();
174 TRACE_EVENT_ASYNC_END1(
176 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
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(
190 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
193 "Info", "ServiceWorker is blocked");
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(),
216 TRACE_EVENT_ASYNC_END2(
218 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
221 "Info", "Wait until finished SW activation");
225 if (!active_version
.get() ||
226 active_version
->status() != ServiceWorkerVersion::ACTIVATED
) {
227 job_
->FallbackToNetwork();
228 TRACE_EVENT_ASYNC_END2(
230 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
234 "ServiceWorkerVersion is not available, so falling back to network");
238 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_
);
240 provider_host_
->AssociateRegistration(registration
.get(),
241 false /* notify_controllerchange */);
242 job_
->ForwardToServiceWorker();
243 TRACE_EVENT_ASYNC_END2(
245 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
249 "Forwarded to the ServiceWorker");
252 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
253 ServiceWorkerRegistration
* registration
,
254 ServiceWorkerVersion
* version
) {
256 provider_host_
->SetAllowAssociation(true);
257 if (version
!= registration
->active_version() ||
258 version
->status() != ServiceWorkerVersion::ACTIVATED
||
260 job_
->FallbackToNetwork();
264 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_
);
266 provider_host_
->AssociateRegistration(registration
,
267 false /* notify_controllerchange */);
268 job_
->ForwardToServiceWorker();
271 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
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
282 if (!is_main_resource_load_
)
283 skip_service_worker_
= true;
287 } // namespace content