Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / content / browser / service_worker / service_worker_controllee_request_handler.cc
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 "content/public/browser/content_browser_client.h"
17 #include "content/public/common/content_client.h"
18 #include "net/base/load_flags.h"
19 #include "net/base/net_util.h"
20 #include "net/url_request/url_request.h"
21
22 namespace content {
23
24 ServiceWorkerControlleeRequestHandler::ServiceWorkerControlleeRequestHandler(
25     base::WeakPtr<ServiceWorkerContextCore> context,
26     base::WeakPtr<ServiceWorkerProviderHost> provider_host,
27     base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
28     FetchRequestMode request_mode,
29     FetchCredentialsMode credentials_mode,
30     ResourceType resource_type,
31     RequestContextType request_context_type,
32     RequestContextFrameType frame_type,
33     scoped_refptr<ResourceRequestBody> body)
34     : ServiceWorkerRequestHandler(context,
35                                   provider_host,
36                                   blob_storage_context,
37                                   resource_type),
38       is_main_resource_load_(
39           ServiceWorkerUtils::IsMainResourceType(resource_type)),
40       request_mode_(request_mode),
41       credentials_mode_(credentials_mode),
42       request_context_type_(request_context_type),
43       frame_type_(frame_type),
44       body_(body),
45       weak_factory_(this) {
46 }
47
48 ServiceWorkerControlleeRequestHandler::
49     ~ServiceWorkerControlleeRequestHandler() {
50   // Navigation triggers an update to occur shortly after the page and
51   // its initial subresources load.
52   if (provider_host_ && provider_host_->active_version()) {
53     if (is_main_resource_load_)
54       provider_host_->active_version()->ScheduleUpdate();
55     else
56       provider_host_->active_version()->DeferScheduledUpdate();
57   }
58
59   if (is_main_resource_load_ && provider_host_)
60     provider_host_->SetAllowAssociation(true);
61 }
62
63 net::URLRequestJob* ServiceWorkerControlleeRequestHandler::MaybeCreateJob(
64     net::URLRequest* request,
65     net::NetworkDelegate* network_delegate,
66     ResourceContext* resource_context) {
67   if (!context_ || !provider_host_) {
68     // We can't do anything other than to fall back to network.
69     job_ = NULL;
70     return NULL;
71   }
72
73   // This may get called multiple times for original and redirect requests:
74   // A. original request case: job_ is null, no previous location info.
75   // B. redirect or restarted request case:
76   //  a) job_ is non-null if the previous location was forwarded to SW.
77   //  b) job_ is null if the previous location was fallback.
78   //  c) job_ is non-null if additional restart was required to fall back.
79
80   // We've come here by restart, we already have original request and it
81   // tells we should fallback to network. (Case B-c)
82   if (job_.get() && job_->ShouldFallbackToNetwork()) {
83     job_ = NULL;
84     return NULL;
85   }
86
87   // It's for original request (A) or redirect case (B-a or B-b).
88   DCHECK(!job_.get() || job_->ShouldForwardToServiceWorker());
89
90   job_ = new ServiceWorkerURLRequestJob(request,
91                                         network_delegate,
92                                         provider_host_,
93                                         blob_storage_context_,
94                                         request_mode_,
95                                         credentials_mode_,
96                                         request_context_type_,
97                                         frame_type_,
98                                         body_);
99   resource_context_ = resource_context;
100
101   if (is_main_resource_load_)
102     PrepareForMainResource(request);
103   else
104     PrepareForSubResource();
105
106   if (job_->ShouldFallbackToNetwork()) {
107     // If we know we can fallback to network at this point (in case
108     // the storage lookup returned immediately), just return NULL here to
109     // fallback to network.
110     job_ = NULL;
111     return NULL;
112   }
113
114   return job_.get();
115 }
116
117 void ServiceWorkerControlleeRequestHandler::GetExtraResponseInfo(
118     bool* was_fetched_via_service_worker,
119     bool* was_fallback_required_by_service_worker,
120     GURL* original_url_via_service_worker,
121     blink::WebServiceWorkerResponseType* response_type_via_service_worker,
122     base::TimeTicks* fetch_start_time,
123     base::TimeTicks* fetch_ready_time,
124     base::TimeTicks* fetch_end_time) const {
125   if (!job_.get()) {
126     *was_fetched_via_service_worker = false;
127     *was_fallback_required_by_service_worker = false;
128     *original_url_via_service_worker = GURL();
129     return;
130   }
131   job_->GetExtraResponseInfo(was_fetched_via_service_worker,
132                              was_fallback_required_by_service_worker,
133                              original_url_via_service_worker,
134                              response_type_via_service_worker,
135                              fetch_start_time,
136                              fetch_ready_time,
137                              fetch_end_time);
138 }
139
140 void ServiceWorkerControlleeRequestHandler::PrepareForMainResource(
141     const net::URLRequest* request) {
142   DCHECK(job_.get());
143   DCHECK(context_);
144   DCHECK(provider_host_);
145   TRACE_EVENT_ASYNC_BEGIN1(
146       "ServiceWorker",
147       "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
148       job_.get(),
149       "URL", request->url().spec());
150   // The corresponding provider_host may already have associated a registration
151   // in redirect case, unassociate it now.
152   provider_host_->DisassociateRegistration();
153
154   // Also prevent a registrater job for establishing an association to a new
155   // registration while we're finding an existing registration.
156   provider_host_->SetAllowAssociation(false);
157
158   GURL stripped_url = net::SimplifyUrlForRequest(request->url());
159   provider_host_->SetDocumentUrl(stripped_url);
160   provider_host_->SetTopmostFrameUrl(request->first_party_for_cookies());
161   context_->storage()->FindRegistrationForDocument(
162       stripped_url,
163       base::Bind(&self::DidLookupRegistrationForMainResource,
164                  weak_factory_.GetWeakPtr()));
165 }
166
167 void
168 ServiceWorkerControlleeRequestHandler::DidLookupRegistrationForMainResource(
169     ServiceWorkerStatusCode status,
170     const scoped_refptr<ServiceWorkerRegistration>& registration) {
171   DCHECK(job_.get());
172   if (provider_host_)
173     provider_host_->SetAllowAssociation(true);
174   if (status != SERVICE_WORKER_OK || !provider_host_) {
175     job_->FallbackToNetwork();
176     TRACE_EVENT_ASYNC_END1(
177         "ServiceWorker",
178         "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
179         job_.get(),
180         "Status", status);
181     return;
182   }
183   DCHECK(registration.get());
184
185   if (!GetContentClient()->browser()->AllowServiceWorker(
186           registration->pattern(),
187           provider_host_->topmost_frame_url(),
188           resource_context_)) {
189     job_->FallbackToNetwork();
190     TRACE_EVENT_ASYNC_END2(
191         "ServiceWorker",
192         "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
193         job_.get(),
194         "Status", status,
195         "Info", "ServiceWorker is blocked");
196     return;
197   }
198
199   // Initiate activation of a waiting version.
200   // Usually a register job initiates activation but that
201   // doesn't happen if the browser exits prior to activation
202   // having occurred. This check handles that case.
203   if (registration->waiting_version())
204     registration->ActivateWaitingVersionWhenReady();
205
206   scoped_refptr<ServiceWorkerVersion> active_version =
207       registration->active_version();
208
209   // Wait until it's activated before firing fetch events.
210   if (active_version.get() &&
211       active_version->status() == ServiceWorkerVersion::ACTIVATING) {
212     provider_host_->SetAllowAssociation(false);
213     registration->active_version()->RegisterStatusChangeCallback(
214         base::Bind(&self::OnVersionStatusChanged,
215                    weak_factory_.GetWeakPtr(),
216                    registration,
217                    active_version));
218     TRACE_EVENT_ASYNC_END2(
219         "ServiceWorker",
220         "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
221         job_.get(),
222         "Status", status,
223         "Info", "Wait until finished SW activation");
224     return;
225   }
226
227   if (!active_version.get() ||
228       active_version->status() != ServiceWorkerVersion::ACTIVATED) {
229     job_->FallbackToNetwork();
230     TRACE_EVENT_ASYNC_END2(
231         "ServiceWorker",
232         "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
233         job_.get(),
234         "Status", status,
235         "Info",
236         "ServiceWorkerVersion is not available, so falling back to network");
237     return;
238   }
239
240   ServiceWorkerMetrics::CountControlledPageLoad();
241
242   provider_host_->AssociateRegistration(registration.get());
243   job_->ForwardToServiceWorker();
244   TRACE_EVENT_ASYNC_END2(
245       "ServiceWorker",
246       "ServiceWorkerControlleeRequestHandler::PrepareForMainResource",
247       job_.get(),
248       "Status", status,
249       "Info",
250       "Forwarded to the ServiceWorker");
251 }
252
253 void ServiceWorkerControlleeRequestHandler::OnVersionStatusChanged(
254     ServiceWorkerRegistration* registration,
255     ServiceWorkerVersion* version) {
256   if (provider_host_)
257     provider_host_->SetAllowAssociation(true);
258   if (version != registration->active_version() ||
259       version->status() != ServiceWorkerVersion::ACTIVATED ||
260       !provider_host_) {
261     job_->FallbackToNetwork();
262     return;
263   }
264
265   ServiceWorkerMetrics::CountControlledPageLoad();
266
267   provider_host_->AssociateRegistration(registration);
268   job_->ForwardToServiceWorker();
269 }
270
271 void ServiceWorkerControlleeRequestHandler::PrepareForSubResource() {
272   DCHECK(job_.get());
273   DCHECK(context_);
274   DCHECK(provider_host_->active_version());
275   job_->ForwardToServiceWorker();
276 }
277
278 }  // namespace content