Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_controllee_request_handler.cc
blob8f9982214bbc93ee7c10cf2bc4e0e071fb99ac37
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 force_update_started_(false),
50 weak_factory_(this) {}
52 ServiceWorkerControlleeRequestHandler::
53 ~ServiceWorkerControlleeRequestHandler() {
54 // Navigation triggers an update to occur shortly after the page and
55 // its initial subresources load.
56 if (provider_host_ && provider_host_->active_version()) {
57 if (is_main_resource_load_ && !force_update_started_)
58 provider_host_->active_version()->ScheduleUpdate();
59 else
60 provider_host_->active_version()->DeferScheduledUpdate();
63 if (is_main_resource_load_ && provider_host_)
64 provider_host_->SetAllowAssociation(true);
67 net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
68 net::URLRequest* request,
69 net::NetworkDelegate* network_delegate,
70 ResourceContext* resource_context) {
71 if (job_.get() && worker_start_time_.is_null()) {
72 // Save worker timings of the first job.
73 worker_start_time_ = job_->worker_start_time();
74 worker_ready_time_ = job_->worker_ready_time();
77 if (!context_ || !provider_host_) {
78 // We can't do anything other than to fall back to network.
79 job_ = NULL;
80 return NULL;
83 // This may get called multiple times for original and redirect requests:
84 // A. original request case: job_ is null, no previous location info.
85 // B. redirect or restarted request case:
86 // a) job_ is non-null if the previous location was forwarded to SW.
87 // b) job_ is null if the previous location was fallback.
88 // c) job_ is non-null if additional restart was required to fall back.
90 // We've come here by restart, we already have original request and it
91 // tells we should fallback to network. (Case B-c)
92 if ((job_.get() && job_->ShouldFallbackToNetwork()) || skip_service_worker_) {
93 FallbackToNetwork();
94 return NULL;
97 // It's for original request (A) or redirect case (B-a or B-b).
98 DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
100 job_ = new ServiceWorkerURLRequestJob(
101 request, network_delegate, provider_host_, blob_storage_context_,
102 resource_context, request_mode_, credentials_mode_, redirect_mode_,
103 is_main_resource_load_, request_context_type_, frame_type_, body_);
104 resource_context_ = resource_context;
106 if (is_main_resource_load_)
107 PrepareForMainResource(request);
108 else
109 PrepareForSubResource();
111 if (job_->ShouldFallbackToNetwork()) {
112 // If we know we can fallback to network at this point (in case
113 // the storage lookup returned immediately), just return NULL here to
114 // fallback to network.
115 FallbackToNetwork();
116 return NULL;
119 return job_.get();
122 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
123 ResourceResponseInfo* response_info) const {
124 if (!job_.get()) {
125 response_info->was_fetched_via_service_worker = false;
126 response_info->was_fallback_required_by_service_worker = false;
127 response_info->original_url_via_service_worker = GURL();
128 response_info->service_worker_start_time = worker_start_time_;
129 response_info->service_worker_ready_time = worker_ready_time_;
130 return;
132 job_->GetExtraResponseInfo(response_info);
133 if (!worker_start_time_.is_null()) {
134 // If we have worker timings from previous job, use it.
135 response_info->service_worker_start_time = worker_start_time_;
136 response_info->service_worker_ready_time = worker_ready_time_;
140 void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
141 const net::URLRequest* request) {
142 DCHECK(job_.get());
143 DCHECK(context_);
144 DCHECK(provider_host_);
145 TRACE_EVENT_ASYNC_BEGIN1(
146 "ServiceWorker",
147 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
148 job_.get(),
149 "URL", request->url().spec());
150 // The corresponding provider_host may already have associated a registration
151 // in redirect case, unassociate it now.
152 provider_host_->DisassociateRegistration();
154 // Also prevent a registrater job for establishing an association to a new
155 // registration while we're finding an existing registration.
156 provider_host_->SetAllowAssociation(false);
158 stripped_url_ = net::SimplifyUrlForRequest(request->url());
159 provider_host_->SetDocumentUrl(stripped_url_);
160 provider_host_->SetTopmostFrameUrl(request->first_party_for_cookies());
161 context_->storage()->FindRegistrationForDocument(
162 stripped_url_, base::Bind(&self::DidLookupRegistrationForMainResource,
163 weak_factory_.GetWeakPtr()));
166 void
167 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
168 ServiceWorkerStatusCode status,
169 const scoped_refptr<ServiceWorkerRegistration>& registration) {
170 DCHECK(job_.get());
171 const bool need_to_update = !force_update_started_ && registration &&
172 registration->force_update_on_page_load();
174 if (provider_host_ && !need_to_update)
175 provider_host_->SetAllowAssociation(true);
176 if (status != SERVICE_WORKER_OK || !provider_host_ || !context_) {
177 job_->FallbackToNetwork();
178 TRACE_EVENT_ASYNC_END1(
179 "ServiceWorker",
180 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
181 job_.get(),
182 "Status", status);
183 return;
185 DCHECK(registration.get());
187 if (!GetContentClient()->browser()->AllowServiceWorker(
188 registration->pattern(), provider_host_->topmost_frame_url(),
189 resource_context_, provider_host_->process_id(),
190 provider_host_->frame_id())) {
191 job_->FallbackToNetwork();
192 TRACE_EVENT_ASYNC_END2(
193 "ServiceWorker",
194 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
195 job_.get(),
196 "Status", status,
197 "Info", "ServiceWorker is blocked");
198 return;
201 if (need_to_update) {
202 force_update_started_ = true;
203 context_->UpdateServiceWorker(
204 registration.get(), true /* force_bypass_cache */, provider_host_.get(),
205 base::Bind(&self::DidUpdateRegistration, weak_factory_.GetWeakPtr(),
206 registration));
207 return;
210 // Initiate activation of a waiting version.
211 // Usually a register job initiates activation but that
212 // doesn't happen if the browser exits prior to activation
213 // having occurred. This check handles that case.
214 if (registration->waiting_version())
215 registration->ActivateWaitingVersionWhenReady();
217 scoped_refptr<ServiceWorkerVersion> active_version =
218 registration->active_version();
220 // Wait until it's activated before firing fetch events.
221 if (active_version.get() &&
222 active_version->status() == ServiceWorkerVersion::ACTIVATING) {
223 provider_host_->SetAllowAssociation(false);
224 registration->active_version()->RegisterStatusChangeCallback(
225 base::Bind(&self::OnVersionStatusChanged,
226 weak_factory_.GetWeakPtr(),
227 registration,
228 active_version));
229 TRACE_EVENT_ASYNC_END2(
230 "ServiceWorker",
231 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
232 job_.get(),
233 "Status", status,
234 "Info", "Wait until finished SW activation");
235 return;
238 if (!active_version.get() ||
239 active_version->status() != ServiceWorkerVersion::ACTIVATED) {
240 job_->FallbackToNetwork();
241 TRACE_EVENT_ASYNC_END2(
242 "ServiceWorker",
243 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
244 job_.get(),
245 "Status", status,
246 "Info",
247 "ServiceWorkerVersion is not available, so falling back to network");
248 return;
251 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_);
253 provider_host_->AssociateRegistration(registration.get(),
254 false /* notify_controllerchange */);
255 job_->ForwardToServiceWorker();
256 TRACE_EVENT_ASYNC_END2(
257 "ServiceWorker",
258 "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
259 job_.get(),
260 "Status", status,
261 "Info",
262 "Forwarded to the ServiceWorker");
265 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
266 ServiceWorkerRegistration* registration,
267 ServiceWorkerVersion* version) {
268 if (provider_host_)
269 provider_host_->SetAllowAssociation(true);
270 if (version != registration->active_version() ||
271 version->status() != ServiceWorkerVersion::ACTIVATED ||
272 !provider_host_) {
273 job_->FallbackToNetwork();
274 return;
277 ServiceWorkerMetrics::CountControlledPageLoad(stripped_url_);
279 provider_host_->AssociateRegistration(registration,
280 false /* notify_controllerchange */);
281 job_->ForwardToServiceWorker();
284 void ServiceWorkerControlleeRequestHandler::DidUpdateRegistration(
285 const scoped_refptr<ServiceWorkerRegistration>& original_registration,
286 ServiceWorkerStatusCode status,
287 const std::string& status_message,
288 int64 registration_id) {
289 DCHECK(force_update_started_);
290 if (!context_) {
291 job_->FallbackToNetwork();
292 return;
294 if (status != SERVICE_WORKER_OK ||
295 !original_registration->installing_version()) {
296 // Update failed. Look up the registration again since the original
297 // registration was possibly unregistered in the meantime.
298 context_->storage()->FindRegistrationForDocument(
299 stripped_url_, base::Bind(&self::DidLookupRegistrationForMainResource,
300 weak_factory_.GetWeakPtr()));
301 return;
303 DCHECK_EQ(original_registration->id(), registration_id);
304 scoped_refptr<ServiceWorkerVersion> new_version =
305 original_registration->installing_version();
306 new_version->set_skip_waiting(true);
307 new_version->RegisterStatusChangeCallback(base::Bind(
308 &self::OnUpdatedVersionStatusChanged, weak_factory_.GetWeakPtr(),
309 original_registration, new_version));
312 void ServiceWorkerControlleeRequestHandler::OnUpdatedVersionStatusChanged(
313 const scoped_refptr<ServiceWorkerRegistration>& registration,
314 const scoped_refptr<ServiceWorkerVersion>& version) {
315 if (!context_) {
316 job_->FallbackToNetwork();
317 return;
319 if (version->status() == ServiceWorkerVersion::ACTIVATED ||
320 version->status() == ServiceWorkerVersion::REDUNDANT) {
321 // When the status is REDUNDANT, the update failed (eg: script error), we
322 // continue with the incumbent version.
323 // In case unregister job may have run, look up the registration again.
324 context_->storage()->FindRegistrationForDocument(
325 stripped_url_, base::Bind(&self::DidLookupRegistrationForMainResource,
326 weak_factory_.GetWeakPtr()));
327 return;
329 version->RegisterStatusChangeCallback(
330 base::Bind(&self::OnUpdatedVersionStatusChanged,
331 weak_factory_.GetWeakPtr(), registration, version));
334 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
335 DCHECK(job_.get());
336 DCHECK(context_);
337 DCHECK(provider_host_->active_version());
338 job_->ForwardToServiceWorker();
341 void ServiceWorkerControlleeRequestHandler::FallbackToNetwork() {
342 // Once a subresource request was fallbacked to the network, we set
343 // |skip_service_worker_| because the request should not go to the service
344 // worker.
345 if (!is_main_resource_load_)
346 skip_service_worker_ = true;
347 job_ = NULL;
350 } // namespace content