• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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