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/embedded_worker_registry.h"
6
7#include "base/bind_helpers.h"
8#include "base/stl_util.h"
9#include "content/browser/renderer_host/render_widget_helper.h"
10#include "content/browser/service_worker/embedded_worker_instance.h"
11#include "content/browser/service_worker/service_worker_context_core.h"
12#include "content/browser/service_worker/service_worker_context_wrapper.h"
13#include "content/common/service_worker/embedded_worker_messages.h"
14#include "content/public/browser/browser_thread.h"
15#include "ipc/ipc_message.h"
16#include "ipc/ipc_sender.h"
17
18namespace content {
19
20// static
21scoped_refptr<EmbeddedWorkerRegistry> EmbeddedWorkerRegistry::Create(
22    const base::WeakPtr<ServiceWorkerContextCore>& context) {
23  return make_scoped_refptr(new EmbeddedWorkerRegistry(context, 0));
24}
25
26// static
27scoped_refptr<EmbeddedWorkerRegistry> EmbeddedWorkerRegistry::Create(
28    const base::WeakPtr<ServiceWorkerContextCore>& context,
29    EmbeddedWorkerRegistry* old_registry) {
30  scoped_refptr<EmbeddedWorkerRegistry> registry =
31      new EmbeddedWorkerRegistry(
32          context,
33          old_registry->next_embedded_worker_id_);
34  registry->process_sender_map_.swap(old_registry->process_sender_map_);
35  return registry;
36}
37
38scoped_ptr<EmbeddedWorkerInstance> EmbeddedWorkerRegistry::CreateWorker() {
39  scoped_ptr<EmbeddedWorkerInstance> worker(
40      new EmbeddedWorkerInstance(context_, next_embedded_worker_id_));
41  worker_map_[next_embedded_worker_id_++] = worker.get();
42  return worker.Pass();
43}
44
45ServiceWorkerStatusCode EmbeddedWorkerRegistry::StopWorker(
46    int process_id, int embedded_worker_id) {
47  return Send(process_id,
48              new EmbeddedWorkerMsg_StopWorker(embedded_worker_id));
49}
50
51bool EmbeddedWorkerRegistry::OnMessageReceived(const IPC::Message& message) {
52  // TODO(kinuko): Move all EmbeddedWorker message handling from
53  // ServiceWorkerDispatcherHost.
54
55  WorkerInstanceMap::iterator found = worker_map_.find(message.routing_id());
56  DCHECK(found != worker_map_.end());
57  if (found == worker_map_.end())
58    return false;
59  return found->second->OnMessageReceived(message);
60}
61
62void EmbeddedWorkerRegistry::Shutdown() {
63  for (WorkerInstanceMap::iterator it = worker_map_.begin();
64       it != worker_map_.end();
65       ++it) {
66    it->second->Stop();
67  }
68}
69
70void EmbeddedWorkerRegistry::OnWorkerReadyForInspection(
71    int process_id,
72    int embedded_worker_id) {
73  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
74  DCHECK(found != worker_map_.end());
75  DCHECK_EQ(found->second->process_id(), process_id);
76  if (found == worker_map_.end() || found->second->process_id() != process_id)
77    return;
78  found->second->OnReadyForInspection();
79}
80
81void EmbeddedWorkerRegistry::OnWorkerScriptLoaded(
82    int process_id,
83    int thread_id,
84    int embedded_worker_id ) {
85  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
86  DCHECK(found != worker_map_.end());
87  DCHECK_EQ(found->second->process_id(), process_id);
88  if (found == worker_map_.end() || found->second->process_id() != process_id)
89    return;
90  found->second->OnScriptLoaded(thread_id);
91}
92
93void EmbeddedWorkerRegistry::OnWorkerScriptLoadFailed(int process_id,
94                                                      int embedded_worker_id) {
95  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
96  DCHECK(found != worker_map_.end());
97  DCHECK_EQ(found->second->process_id(), process_id);
98  if (found == worker_map_.end() || found->second->process_id() != process_id)
99    return;
100  found->second->OnScriptLoadFailed();
101}
102
103void EmbeddedWorkerRegistry::OnWorkerStarted(
104    int process_id, int embedded_worker_id) {
105  DCHECK(!ContainsKey(worker_process_map_, process_id) ||
106         worker_process_map_[process_id].count(embedded_worker_id) == 0);
107  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
108  DCHECK(found != worker_map_.end());
109  DCHECK_EQ(found->second->process_id(), process_id);
110  if (found == worker_map_.end() || found->second->process_id() != process_id)
111    return;
112  worker_process_map_[process_id].insert(embedded_worker_id);
113  found->second->OnStarted();
114}
115
116void EmbeddedWorkerRegistry::OnWorkerStopped(
117    int process_id, int embedded_worker_id) {
118  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
119  DCHECK(found != worker_map_.end());
120  DCHECK_EQ(found->second->process_id(), process_id);
121  if (found == worker_map_.end() || found->second->process_id() != process_id)
122    return;
123  worker_process_map_[process_id].erase(embedded_worker_id);
124  found->second->OnStopped();
125}
126
127void EmbeddedWorkerRegistry::OnPausedAfterDownload(
128    int process_id, int embedded_worker_id) {
129  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
130  DCHECK(found != worker_map_.end());
131  DCHECK_EQ(found->second->process_id(), process_id);
132  if (found == worker_map_.end() || found->second->process_id() != process_id)
133    return;
134  found->second->OnPausedAfterDownload();
135}
136
137void EmbeddedWorkerRegistry::OnReportException(
138    int embedded_worker_id,
139    const base::string16& error_message,
140    int line_number,
141    int column_number,
142    const GURL& source_url) {
143  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
144  DCHECK(found != worker_map_.end());
145  if (found == worker_map_.end())
146    return;
147  found->second->OnReportException(
148      error_message, line_number, column_number, source_url);
149}
150
151void EmbeddedWorkerRegistry::OnReportConsoleMessage(
152    int embedded_worker_id,
153    int source_identifier,
154    int message_level,
155    const base::string16& message,
156    int line_number,
157    const GURL& source_url) {
158  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
159  DCHECK(found != worker_map_.end());
160  if (found == worker_map_.end())
161    return;
162  found->second->OnReportConsoleMessage(
163      source_identifier, message_level, message, line_number, source_url);
164}
165
166void EmbeddedWorkerRegistry::AddChildProcessSender(
167    int process_id, IPC::Sender* sender) {
168  process_sender_map_[process_id] = sender;
169  DCHECK(!ContainsKey(worker_process_map_, process_id));
170}
171
172void EmbeddedWorkerRegistry::RemoveChildProcessSender(int process_id) {
173  process_sender_map_.erase(process_id);
174  std::map<int, std::set<int> >::iterator found =
175      worker_process_map_.find(process_id);
176  if (found != worker_process_map_.end()) {
177    const std::set<int>& worker_set = worker_process_map_[process_id];
178    for (std::set<int>::const_iterator it = worker_set.begin();
179         it != worker_set.end();
180         ++it) {
181      int embedded_worker_id = *it;
182      DCHECK(ContainsKey(worker_map_, embedded_worker_id));
183      worker_map_[embedded_worker_id]->OnStopped();
184    }
185    worker_process_map_.erase(found);
186  }
187}
188
189EmbeddedWorkerInstance* EmbeddedWorkerRegistry::GetWorker(
190    int embedded_worker_id) {
191  WorkerInstanceMap::iterator found = worker_map_.find(embedded_worker_id);
192  if (found == worker_map_.end())
193    return NULL;
194  return found->second;
195}
196
197bool EmbeddedWorkerRegistry::CanHandle(int embedded_worker_id) const {
198  if (embedded_worker_id < initial_embedded_worker_id_ ||
199      next_embedded_worker_id_ <= embedded_worker_id) {
200    return false;
201  }
202  return true;
203}
204
205EmbeddedWorkerRegistry::EmbeddedWorkerRegistry(
206    const base::WeakPtr<ServiceWorkerContextCore>& context,
207    int initial_embedded_worker_id)
208    : context_(context),
209      next_embedded_worker_id_(initial_embedded_worker_id),
210      initial_embedded_worker_id_(initial_embedded_worker_id) {
211}
212
213EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() {
214  Shutdown();
215}
216
217void EmbeddedWorkerRegistry::SendStartWorker(
218    scoped_ptr<EmbeddedWorkerMsg_StartWorker_Params> params,
219    const StatusCallback& callback,
220    int process_id) {
221  // The ServiceWorkerDispatcherHost is supposed to be created when the process
222  // is created, and keep an entry in process_sender_map_ for its whole
223  // lifetime.
224  DCHECK(ContainsKey(process_sender_map_, process_id));
225  callback.Run(Send(process_id, new EmbeddedWorkerMsg_StartWorker(*params)));
226}
227
228ServiceWorkerStatusCode EmbeddedWorkerRegistry::Send(
229    int process_id, IPC::Message* message_ptr) {
230  scoped_ptr<IPC::Message> message(message_ptr);
231  if (!context_)
232    return SERVICE_WORKER_ERROR_ABORT;
233  ProcessToSenderMap::iterator found = process_sender_map_.find(process_id);
234  if (found == process_sender_map_.end())
235    return SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND;
236  if (!found->second->Send(message.release()))
237    return SERVICE_WORKER_ERROR_IPC_FAILED;
238  return SERVICE_WORKER_OK;
239}
240
241void EmbeddedWorkerRegistry::RemoveWorker(int process_id,
242                                          int embedded_worker_id) {
243  DCHECK(ContainsKey(worker_map_, embedded_worker_id));
244  worker_map_.erase(embedded_worker_id);
245  worker_process_map_.erase(process_id);
246}
247
248}  // namespace content
249