service_worker_controllee_request_handler.cc revision 03b57e008b61dfcb1fbad3aea950ae0e001748b0
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 "content/browser/service_worker/service_worker_context_core.h"
8#include "content/browser/service_worker/service_worker_metrics.h"
9#include "content/browser/service_worker/service_worker_provider_host.h"
10#include "content/browser/service_worker/service_worker_registration.h"
11#include "content/browser/service_worker/service_worker_url_request_job.h"
12#include "content/browser/service_worker/service_worker_utils.h"
13#include "content/common/resource_request_body.h"
14#include "content/common/service_worker/service_worker_types.h"
15#include "net/base/load_flags.h"
16#include "net/base/net_util.h"
17#include "net/url_request/url_request.h"
18
19namespace content {
20
21ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler(
22    base::WeakPtr<ServiceWorkerContextCore> context,
23    base::WeakPtr<ServiceWorkerProviderHost> provider_host,
24    base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
25    ResourceType resource_type,
26    scoped_refptr<ResourceRequestBody> body)
27    : ServiceWorkerRequestHandler(context,
28                                  provider_host,
29                                  blob_storage_context,
30                                  resource_type),
31      is_main_resource_load_(
32          ServiceWorkerUtils::IsMainResourceType(resource_type)),
33      body_(body),
34      weak_factory_(this) {
35}
36
37ServiceWorkerControlleeRequestHandler::
38    ~ServiceWorkerControlleeRequestHandler() {
39  // Navigation triggers an update to occur shortly after the page and
40  // its initial subresources load.
41  if (provider_host_ && provider_host_->active_version()) {
42    if (is_main_resource_load_)
43      provider_host_->active_version()->ScheduleUpdate();
44    else
45      provider_host_->active_version()->DeferScheduledUpdate();
46  }
47}
48
49net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
50    net::URLRequest* request,
51    net::NetworkDelegate* network_delegate) {
52  if (!context_ || !provider_host_) {
53    // We can't do anything other than to fall back to network.
54    job_ = NULL;
55    return NULL;
56  }
57
58  if (request->load_flags() & net::LOAD_BYPASS_CACHE) {
59    if (is_main_resource_load_) {
60      provider_host_->SetDocumentUrl(
61          net::SimplifyUrlForRequest(request->url()));
62    }
63    job_ = NULL;
64    return NULL;
65  }
66
67  // This may get called multiple times for original and redirect requests:
68  // A. original request case: job_ is null, no previous location info.
69  // B. redirect or restarted request case:
70  //  a) job_ is non-null if the previous location was forwarded to SW.
71  //  b) job_ is null if the previous location was fallback.
72  //  c) job_ is non-null if additional restart was required to fall back.
73
74  // We've come here by restart, we already have original request and it
75  // tells we should fallback to network. (Case B-c)
76  if (job_.get() && job_->ShouldFallbackToNetwork()) {
77    job_ = NULL;
78    return NULL;
79  }
80
81  // It's for original request (A) or redirect case (B-a or B-b).
82  DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
83
84  job_ = new ServiceWorkerURLRequestJob(
85      request, network_delegate, provider_host_, blob_storage_context_, body_);
86  if (is_main_resource_load_)
87    PrepareForMainResource(request->url());
88  else
89    PrepareForSubResource();
90
91  if (job_->ShouldFallbackToNetwork()) {
92    // If we know we can fallback to network at this point (in case
93    // the storage lookup returned immediately), just return NULL here to
94    // fallback to network.
95    job_ = NULL;
96    return NULL;
97  }
98
99  return job_.get();
100}
101
102void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
103    bool* was_fetched_via_service_worker,
104    GURL* original_url_via_service_worker) const {
105  if (!job_) {
106    *was_fetched_via_service_worker = false;
107    *original_url_via_service_worker = GURL();
108    return;
109  }
110  job_->GetExtraResponseInfo(was_fetched_via_service_worker,
111                             original_url_via_service_worker);
112}
113
114void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
115    const GURL& url) {
116  DCHECK(job_.get());
117  DCHECK(context_);
118  // The corresponding provider_host may already have associated a registration
119  // in redirect case, unassociate it now.
120  provider_host_->UnassociateRegistration();
121
122  GURL stripped_url = net::SimplifyUrlForRequest(url);
123  provider_host_->SetDocumentUrl(stripped_url);
124  context_->storage()->FindRegistrationForDocument(
125      stripped_url,
126      base::Bind(&self::DidLookupRegistrationForMainResource,
127                 weak_factory_.GetWeakPtr()));
128}
129
130void
131ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
132    ServiceWorkerStatusCode status,
133    const scoped_refptr<ServiceWorkerRegistration>& registration) {
134  DCHECK(job_.get());
135  if (status != SERVICE_WORKER_OK) {
136    job_->FallbackToNetwork();
137    return;
138  }
139  DCHECK(registration);
140
141  ServiceWorkerMetrics::CountControlledPageLoad();
142
143  // Initiate activation of a waiting version.
144  // Usually a register job initiates activation but that
145  // doesn't happen if the browser exits prior to activation
146  // having occurred. This check handles that case.
147  if (registration->waiting_version())
148    registration->ActivateWaitingVersionWhenReady();
149
150  scoped_refptr<ServiceWorkerVersion> active_version =
151      registration->active_version();
152
153  // Wait until it's activated before firing fetch events.
154  if (active_version &&
155      active_version->status() == ServiceWorkerVersion::ACTIVATING) {
156    registration->active_version()->RegisterStatusChangeCallback(
157        base::Bind(&self::OnVersionStatusChanged,
158                   weak_factory_.GetWeakPtr(),
159                   registration,
160                   active_version));
161    return;
162  }
163
164  if (!active_version ||
165      active_version->status() != ServiceWorkerVersion::ACTIVATED) {
166    job_->FallbackToNetwork();
167    return;
168  }
169
170  provider_host_->AssociateRegistration(registration);
171  job_->ForwardToServiceWorker();
172}
173
174void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
175    ServiceWorkerRegistration* registration,
176    ServiceWorkerVersion* version) {
177  if (version != registration->active_version() ||
178      version->status() != ServiceWorkerVersion::ACTIVATED) {
179    job_->FallbackToNetwork();
180    return;
181  }
182
183  provider_host_->AssociateRegistration(registration);
184  job_->ForwardToServiceWorker();
185}
186
187void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
188  DCHECK(job_.get());
189  DCHECK(context_);
190  DCHECK(provider_host_->active_version());
191  job_->ForwardToServiceWorker();
192}
193
194}  // namespace content
195