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 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:: 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 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 97 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 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 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 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 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