1// Copyright 2013 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_provider_host.h"
6
7#include "base/stl_util.h"
8#include "content/browser/message_port_message_filter.h"
9#include "content/browser/service_worker/service_worker_context_core.h"
10#include "content/browser/service_worker/service_worker_context_request_handler.h"
11#include "content/browser/service_worker/service_worker_controllee_request_handler.h"
12#include "content/browser/service_worker/service_worker_dispatcher_host.h"
13#include "content/browser/service_worker/service_worker_handle.h"
14#include "content/browser/service_worker/service_worker_registration_handle.h"
15#include "content/browser/service_worker/service_worker_utils.h"
16#include "content/browser/service_worker/service_worker_version.h"
17#include "content/common/resource_request_body.h"
18#include "content/common/service_worker/service_worker_messages.h"
19
20namespace content {
21
22static const int kDocumentMainThreadId = 0;
23
24ServiceWorkerProviderHost::ServiceWorkerProviderHost(
25    int process_id, int provider_id,
26    base::WeakPtr<ServiceWorkerContextCore> context,
27    ServiceWorkerDispatcherHost* dispatcher_host)
28    : process_id_(process_id),
29      provider_id_(provider_id),
30      context_(context),
31      dispatcher_host_(dispatcher_host),
32      allow_association_(true) {
33}
34
35ServiceWorkerProviderHost::~ServiceWorkerProviderHost() {
36  // Clear docurl so the deferred activation of a waiting worker
37  // won't associate the new version with a provider being destroyed.
38  document_url_ = GURL();
39  if (controlling_version_.get())
40    controlling_version_->RemoveControllee(this);
41  if (associated_registration_.get()) {
42    DecreaseProcessReference(associated_registration_->pattern());
43    associated_registration_->RemoveListener(this);
44  }
45  for (std::vector<GURL>::iterator it = associated_patterns_.begin();
46       it != associated_patterns_.end(); ++it) {
47    DecreaseProcessReference(*it);
48  }
49}
50
51void ServiceWorkerProviderHost::OnRegistrationFailed(
52    ServiceWorkerRegistration* registration) {
53  DCHECK_EQ(associated_registration_.get(), registration);
54  DisassociateRegistration();
55}
56
57void ServiceWorkerProviderHost::SetDocumentUrl(const GURL& url) {
58  DCHECK(!url.has_ref());
59  document_url_ = url;
60}
61
62void ServiceWorkerProviderHost::SetControllerVersionAttribute(
63    ServiceWorkerVersion* version) {
64  if (version == controlling_version_.get())
65    return;
66
67  scoped_refptr<ServiceWorkerVersion> previous_version = controlling_version_;
68  controlling_version_ = version;
69  if (version)
70    version->AddControllee(this);
71  if (previous_version.get())
72    previous_version->RemoveControllee(this);
73
74  if (!dispatcher_host_)
75    return;  // Could be NULL in some tests.
76
77  dispatcher_host_->Send(new ServiceWorkerMsg_SetControllerServiceWorker(
78      kDocumentMainThreadId, provider_id(), CreateHandleAndPass(version)));
79}
80
81bool ServiceWorkerProviderHost::SetHostedVersionId(int64 version_id) {
82  if (!context_)
83    return true;  // System is shutting down.
84  if (active_version())
85    return false;  // Unexpected bad message.
86
87  ServiceWorkerVersion* live_version = context_->GetLiveVersion(version_id);
88  if (!live_version)
89    return true;  // Was deleted before it got started.
90
91  ServiceWorkerVersionInfo info = live_version->GetInfo();
92  if (info.running_status != ServiceWorkerVersion::STARTING ||
93      info.process_id != process_id_) {
94    // If we aren't trying to start this version in our process
95    // something is amiss.
96    return false;
97  }
98
99  running_hosted_version_ = live_version;
100  return true;
101}
102
103void ServiceWorkerProviderHost::AssociateRegistration(
104    ServiceWorkerRegistration* registration) {
105  DCHECK(CanAssociateRegistration(registration));
106  if (associated_registration_.get())
107    DecreaseProcessReference(associated_registration_->pattern());
108  IncreaseProcessReference(registration->pattern());
109
110  if (dispatcher_host_) {
111    ServiceWorkerRegistrationHandle* handle =
112        dispatcher_host_->GetOrCreateRegistrationHandle(
113            provider_id(), registration);
114
115    ServiceWorkerVersionAttributes attrs;
116    attrs.installing = handle->CreateServiceWorkerHandleAndPass(
117        registration->installing_version());
118    attrs.waiting = handle->CreateServiceWorkerHandleAndPass(
119        registration->waiting_version());
120    attrs.active = handle->CreateServiceWorkerHandleAndPass(
121        registration->active_version());
122
123    dispatcher_host_->Send(new ServiceWorkerMsg_AssociateRegistration(
124        kDocumentMainThreadId, provider_id(), handle->GetObjectInfo(), attrs));
125  }
126
127  associated_registration_ = registration;
128  associated_registration_->AddListener(this);
129  SetControllerVersionAttribute(registration->active_version());
130}
131
132void ServiceWorkerProviderHost::DisassociateRegistration() {
133  if (!associated_registration_.get())
134    return;
135  DecreaseProcessReference(associated_registration_->pattern());
136  associated_registration_->RemoveListener(this);
137  associated_registration_ = NULL;
138  SetControllerVersionAttribute(NULL);
139
140  if (dispatcher_host_) {
141    dispatcher_host_->Send(new ServiceWorkerMsg_DisassociateRegistration(
142        kDocumentMainThreadId, provider_id()));
143  }
144}
145
146scoped_ptr<ServiceWorkerRequestHandler>
147ServiceWorkerProviderHost::CreateRequestHandler(
148    ResourceType resource_type,
149    base::WeakPtr<storage::BlobStorageContext> blob_storage_context,
150    scoped_refptr<ResourceRequestBody> body) {
151  if (IsHostToRunningServiceWorker()) {
152    return scoped_ptr<ServiceWorkerRequestHandler>(
153        new ServiceWorkerContextRequestHandler(
154            context_, AsWeakPtr(), blob_storage_context, resource_type));
155  }
156  if (ServiceWorkerUtils::IsMainResourceType(resource_type) ||
157      controlling_version()) {
158    return scoped_ptr<ServiceWorkerRequestHandler>(
159        new ServiceWorkerControlleeRequestHandler(
160            context_, AsWeakPtr(), blob_storage_context, resource_type, body));
161  }
162  return scoped_ptr<ServiceWorkerRequestHandler>();
163}
164
165bool ServiceWorkerProviderHost::CanAssociateRegistration(
166    ServiceWorkerRegistration* registration) {
167  if (!context_)
168    return false;
169  if (running_hosted_version_.get())
170    return false;
171  if (!registration || associated_registration_.get() || !allow_association_)
172    return false;
173  return true;
174}
175
176void ServiceWorkerProviderHost::PostMessage(
177    const base::string16& message,
178    const std::vector<int>& sent_message_port_ids) {
179  if (!dispatcher_host_)
180    return;  // Could be NULL in some tests.
181
182  std::vector<int> new_routing_ids;
183  dispatcher_host_->message_port_message_filter()->
184      UpdateMessagePortsWithNewRoutes(sent_message_port_ids,
185                                      &new_routing_ids);
186
187  dispatcher_host_->Send(
188      new ServiceWorkerMsg_MessageToDocument(
189          kDocumentMainThreadId, provider_id(),
190          message,
191          sent_message_port_ids,
192          new_routing_ids));
193}
194
195void ServiceWorkerProviderHost::AddScopedProcessReferenceToPattern(
196    const GURL& pattern) {
197  associated_patterns_.push_back(pattern);
198  IncreaseProcessReference(pattern);
199}
200
201ServiceWorkerObjectInfo ServiceWorkerProviderHost::CreateHandleAndPass(
202    ServiceWorkerVersion* version) {
203  ServiceWorkerObjectInfo info;
204  if (context_ && version) {
205    scoped_ptr<ServiceWorkerHandle> handle =
206        ServiceWorkerHandle::Create(context_,
207                                    dispatcher_host_,
208                                    kDocumentMainThreadId,
209                                    provider_id_,
210                                    version);
211    info = handle->GetObjectInfo();
212    dispatcher_host_->RegisterServiceWorkerHandle(handle.Pass());
213  }
214  return info;
215}
216
217void ServiceWorkerProviderHost::IncreaseProcessReference(
218    const GURL& pattern) {
219  if (context_ && context_->process_manager()) {
220    context_->process_manager()->AddProcessReferenceToPattern(
221        pattern, process_id_);
222  }
223}
224
225void ServiceWorkerProviderHost::DecreaseProcessReference(
226    const GURL& pattern) {
227  if (context_ && context_->process_manager()) {
228    context_->process_manager()->RemoveProcessReferenceFromPattern(
229        pattern, process_id_);
230  }
231}
232
233bool ServiceWorkerProviderHost::IsContextAlive() {
234  return context_ != NULL;
235}
236
237}  // namespace content
238