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_controllee_request_handler.h"
6
7 #include "base/debug/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 "net/base/load_flags.h"
17 #include "net/base/net_util.h"
18 #include "net/url_request/url_request.h"
19
20 namespace content {
21
ServiceWorkerControlleeRequestHandler(base::WeakPtr<ServiceWorkerContextCore> context,base::WeakPtr<ServiceWorkerProviderHost> provider_host,base::WeakPtr<storage::BlobStorageContext> blob_storage_context,ResourceType resource_type,scoped_refptr<ResourceRequestBody> body)22 ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler(
23 base::WeakPtr<ServiceWorkerContextCore> context,
24 base::WeakPtr<ServiceWorkerProviderHost> provider_host,
25 base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
26 ResourceType resource_type,
27 scoped_refptr<ResourceRequestBody> body)
28 : ServiceWorkerRequestHandler(context,
29 provider_host,
30 blob_storage_context,
31 resource_type),
32 is_main_resource_load_(
33 ServiceWorkerUtils::IsMainResourceType(resource_type)),
34 body_(body),
35 weak_factory_(this) {
36 }
37
38 ServiceWorkerControlleeRequestHandler::
~ServiceWorkerControlleeRequestHandler()39 ~ServiceWorkerControlleeRequestHandler() {
40 // Navigation triggers an update to occur shortly after the page and
41 // its initial subresources load.
42 if (provider_host_ && provider_host_->active_version()) {
43 if (is_main_resource_load_)
44 provider_host_->active_version()->ScheduleUpdate();
45 else
46 provider_host_->active_version()->DeferScheduledUpdate();
47 }
48
49 if (is_main_resource_load_ && provider_host_)
50 provider_host_->SetAllowAssociation(true);
51 }
52
MaybeCreateJob(net::URLRequest * request,net::NetworkDelegate * network_delegate)53 net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
54 net::URLRequest* request,
55 net::NetworkDelegate* network_delegate) {
56 if (!context_ || !provider_host_) {
57 // We can't do anything other than to fall back to network.
58 job_ = NULL;
59 return NULL;
60 }
61
62 // This may get called multiple times for original and redirect requests:
63 // A. original request case: job_ is null, no previous location info.
64 // B. redirect or restarted request case:
65 // a) job_ is non-null if the previous location was forwarded to SW.
66 // b) job_ is null if the previous location was fallback.
67 // c) job_ is non-null if additional restart was required to fall back.
68
69 // We've come here by restart, we already have original request and it
70 // tells we should fallback to network. (Case B-c)
71 if (job_.get() && job_->ShouldFallbackToNetwork()) {
72 job_ = NULL;
73 return NULL;
74 }
75
76 // It's for original request (A) or redirect case (B-a or B-b).
77 DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
78
79 job_ = new ServiceWorkerURLRequestJob(
80 request, network_delegate, provider_host_, blob_storage_context_, body_);
81 if (is_main_resource_load_)
82 PrepareForMainResource(request->url());
83 else
84 PrepareForSubResource();
85
86 if (job_->ShouldFallbackToNetwork()) {
87 // If we know we can fallback to network at this point (in case
88 // the storage lookup returned immediately), just return NULL here to
89 // fallback to network.
90 job_ = NULL;
91 return NULL;
92 }
93
94 return job_.get();
95 }
96
GetExtraResponseInfo(bool * was_fetched_via_service_worker,GURL * original_url_via_service_worker,base::TimeTicks * fetch_start_time,base::TimeTicks * fetch_ready_time,base::TimeTicks * fetch_end_time) const97 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
98 bool* was_fetched_via_service_worker,
99 GURL* original_url_via_service_worker,
100 base::TimeTicks* fetch_start_time,
101 base::TimeTicks* fetch_ready_time,
102 base::TimeTicks* fetch_end_time) const {
103 if (!job_.get()) {
104 *was_fetched_via_service_worker = false;
105 *original_url_via_service_worker = GURL();
106 return;
107 }
108 job_->GetExtraResponseInfo(was_fetched_via_service_worker,
109 original_url_via_service_worker,
110 fetch_start_time,
111 fetch_ready_time,
112 fetch_end_time);
113 }
114
PrepareForMainResource(const GURL & url)115 void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
116 const GURL& url) {
117 DCHECK(job_.get());
118 DCHECK(context_);
119 DCHECK(provider_host_);
120 TRACE_EVENT_ASYNC_BEGIN1(
121 "ServiceWorker",
122 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
123 job_.get(),
124 "URL", url.spec());
125 // The corresponding provider_host may already have associated a registration
126 // in redirect case, unassociate it now.
127 provider_host_->DisassociateRegistration();
128
129 // Also prevent a registrater job for establishing an association to a new
130 // registration while we're finding an existing registration.
131 provider_host_->SetAllowAssociation(false);
132
133 GURL stripped_url = net::SimplifyUrlForRequest(url);
134 provider_host_->SetDocumentUrl(stripped_url);
135 context_->storage()->FindRegistrationForDocument(
136 stripped_url,
137 base::Bind(&self::DidLookupRegistrationForMainResource,
138 weak_factory_.GetWeakPtr()));
139 }
140
141 void
DidLookupRegistrationForMainResource(ServiceWorkerStatusCode status,const scoped_refptr<ServiceWorkerRegistration> & registration)142 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
143 ServiceWorkerStatusCode status,
144 const scoped_refptr<ServiceWorkerRegistration>& registration) {
145 DCHECK(job_.get());
146 if (provider_host_)
147 provider_host_->SetAllowAssociation(true);
148 if (status != SERVICE_WORKER_OK || !provider_host_) {
149 job_->FallbackToNetwork();
150 TRACE_EVENT_ASYNC_END1(
151 "ServiceWorker",
152 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
153 job_.get(),
154 "Status", status);
155 return;
156 }
157 DCHECK(registration.get());
158
159 // Initiate activation of a waiting version.
160 // Usually a register job initiates activation but that
161 // doesn't happen if the browser exits prior to activation
162 // having occurred. This check handles that case.
163 if (registration->waiting_version())
164 registration->ActivateWaitingVersionWhenReady();
165
166 scoped_refptr<ServiceWorkerVersion> active_version =
167 registration->active_version();
168
169 // Wait until it's activated before firing fetch events.
170 if (active_version.get() &&
171 active_version->status() == ServiceWorkerVersion::ACTIVATING) {
172 provider_host_->SetAllowAssociation(false);
173 registration->active_version()->RegisterStatusChangeCallback(
174 base::Bind(&self::OnVersionStatusChanged,
175 weak_factory_.GetWeakPtr(),
176 registration,
177 active_version));
178 TRACE_EVENT_ASYNC_END2(
179 "ServiceWorker",
180 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
181 job_.get(),
182 "Status", status,
183 "Info", "Wait until finished SW activation");
184 return;
185 }
186
187 if (!active_version.get() ||
188 active_version->status() != ServiceWorkerVersion::ACTIVATED) {
189 job_->FallbackToNetwork();
190 TRACE_EVENT_ASYNC_END2(
191 "ServiceWorker",
192 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
193 job_.get(),
194 "Status", status,
195 "Info",
196 "ServiceWorkerVersion is not available, so falling back to network");
197 return;
198 }
199
200 ServiceWorkerMetrics::CountControlledPageLoad();
201
202 provider_host_->AssociateRegistration(registration.get());
203 job_->ForwardToServiceWorker();
204 TRACE_EVENT_ASYNC_END2(
205 "ServiceWorker",
206 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
207 job_.get(),
208 "Status", status,
209 "Info",
210 "Forwarded to the ServiceWorker");
211 }
212
OnVersionStatusChanged(ServiceWorkerRegistration * registration,ServiceWorkerVersion * version)213 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
214 ServiceWorkerRegistration* registration,
215 ServiceWorkerVersion* version) {
216 if (provider_host_)
217 provider_host_->SetAllowAssociation(true);
218 if (version != registration->active_version() ||
219 version->status() != ServiceWorkerVersion::ACTIVATED ||
220 !provider_host_) {
221 job_->FallbackToNetwork();
222 return;
223 }
224
225 ServiceWorkerMetrics::CountControlledPageLoad();
226
227 provider_host_->AssociateRegistration(registration);
228 job_->ForwardToServiceWorker();
229 }
230
PrepareForSubResource()231 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
232 DCHECK(job_.get());
233 DCHECK(context_);
234 DCHECK(provider_host_->active_version());
235 job_->ForwardToServiceWorker();
236 }
237
238 } // namespace content
239