service_worker_controllee_request_handler.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
20namespace content {
21
22ServiceWorkerControlleeRequestHandler::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
38ServiceWorkerControlleeRequestHandler::
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
53net::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
97void 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
115void 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
141void
142ServiceWorkerControlleeRequestHandler::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
213void 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
231void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
232  DCHECK(job_.get());
233  DCHECK(context_);
234  DCHECK(provider_host_->active_version());
235  job_->ForwardToServiceWorker();
236}
237
238}  // namespace content
239