Home | History | Annotate | Download | only in service_worker
      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