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_context_core.h"
6
7#include "base/files/file_path.h"
8#include "base/single_thread_task_runner.h"
9#include "base/strings/string_util.h"
10#include "content/browser/service_worker/embedded_worker_registry.h"
11#include "content/browser/service_worker/service_worker_cache_storage_manager.h"
12#include "content/browser/service_worker/service_worker_context_observer.h"
13#include "content/browser/service_worker/service_worker_context_wrapper.h"
14#include "content/browser/service_worker/service_worker_info.h"
15#include "content/browser/service_worker/service_worker_job_coordinator.h"
16#include "content/browser/service_worker/service_worker_process_manager.h"
17#include "content/browser/service_worker/service_worker_provider_host.h"
18#include "content/browser/service_worker/service_worker_register_job.h"
19#include "content/browser/service_worker/service_worker_registration.h"
20#include "content/browser/service_worker/service_worker_storage.h"
21#include "content/public/browser/browser_thread.h"
22#include "url/gurl.h"
23
24namespace content {
25
26const base::FilePath::CharType
27    ServiceWorkerContextCore::kServiceWorkerDirectory[] =
28        FILE_PATH_LITERAL("Service Worker");
29
30ServiceWorkerContextCore::ProviderHostIterator::~ProviderHostIterator() {}
31
32ServiceWorkerProviderHost*
33ServiceWorkerContextCore::ProviderHostIterator::GetProviderHost() {
34  DCHECK(!IsAtEnd());
35  return provider_host_iterator_->GetCurrentValue();
36}
37
38void ServiceWorkerContextCore::ProviderHostIterator::Advance() {
39  DCHECK(!IsAtEnd());
40  DCHECK(!provider_host_iterator_->IsAtEnd());
41  DCHECK(!process_iterator_->IsAtEnd());
42
43  // Advance the inner iterator. If an element is reached, we're done.
44  provider_host_iterator_->Advance();
45  if (!provider_host_iterator_->IsAtEnd())
46    return;
47
48  // Advance the outer iterator until an element is reached, or end is hit.
49  while (true) {
50    process_iterator_->Advance();
51    if (process_iterator_->IsAtEnd())
52      return;
53    ProviderMap* provider_map = process_iterator_->GetCurrentValue();
54    provider_host_iterator_.reset(new ProviderMap::iterator(provider_map));
55    if (!provider_host_iterator_->IsAtEnd())
56      return;
57  }
58}
59
60bool ServiceWorkerContextCore::ProviderHostIterator::IsAtEnd() {
61  return process_iterator_->IsAtEnd() &&
62         (!provider_host_iterator_ || provider_host_iterator_->IsAtEnd());
63}
64
65ServiceWorkerContextCore::ProviderHostIterator::ProviderHostIterator(
66    ProcessToProviderMap* map)
67    : map_(map) {
68  DCHECK(map);
69  Initialize();
70}
71
72void ServiceWorkerContextCore::ProviderHostIterator::Initialize() {
73  process_iterator_.reset(new ProcessToProviderMap::iterator(map_));
74  // Advance to the first element.
75  while (!process_iterator_->IsAtEnd()) {
76    ProviderMap* provider_map = process_iterator_->GetCurrentValue();
77    provider_host_iterator_.reset(new ProviderMap::iterator(provider_map));
78    if (!provider_host_iterator_->IsAtEnd())
79      return;
80    process_iterator_->Advance();
81  }
82}
83
84ServiceWorkerContextCore::ServiceWorkerContextCore(
85    const base::FilePath& path,
86    const scoped_refptr<base::SequencedTaskRunner>& cache_task_runner,
87    const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
88    const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
89    storage::QuotaManagerProxy* quota_manager_proxy,
90    ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list,
91    ServiceWorkerContextWrapper* wrapper)
92    : weak_factory_(this),
93      wrapper_(wrapper),
94      providers_(new ProcessToProviderMap),
95      storage_(ServiceWorkerStorage::Create(path,
96                                            AsWeakPtr(),
97                                            database_task_runner,
98                                            disk_cache_thread,
99                                            quota_manager_proxy)),
100      cache_manager_(
101          ServiceWorkerCacheStorageManager::Create(path,
102                                                   cache_task_runner.get())),
103      embedded_worker_registry_(EmbeddedWorkerRegistry::Create(AsWeakPtr())),
104      job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())),
105      next_handle_id_(0),
106      next_registration_handle_id_(0),
107      observer_list_(observer_list) {
108}
109
110ServiceWorkerContextCore::ServiceWorkerContextCore(
111    ServiceWorkerContextCore* old_context,
112    ServiceWorkerContextWrapper* wrapper)
113    : weak_factory_(this),
114      wrapper_(wrapper),
115      providers_(old_context->providers_.release()),
116      storage_(
117          ServiceWorkerStorage::Create(AsWeakPtr(), old_context->storage())),
118      cache_manager_(ServiceWorkerCacheStorageManager::Create(
119          old_context->cache_manager())),
120      embedded_worker_registry_(EmbeddedWorkerRegistry::Create(
121          AsWeakPtr(),
122          old_context->embedded_worker_registry())),
123      job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())),
124      next_handle_id_(0),
125      next_registration_handle_id_(0),
126      observer_list_(old_context->observer_list_) {
127}
128
129ServiceWorkerContextCore::~ServiceWorkerContextCore() {
130  for (VersionMap::iterator it = live_versions_.begin();
131       it != live_versions_.end();
132       ++it) {
133    it->second->RemoveListener(this);
134  }
135  weak_factory_.InvalidateWeakPtrs();
136}
137
138ServiceWorkerProviderHost* ServiceWorkerContextCore::GetProviderHost(
139    int process_id, int provider_id) {
140  ProviderMap* map = GetProviderMapForProcess(process_id);
141  if (!map)
142    return NULL;
143  return map->Lookup(provider_id);
144}
145
146void ServiceWorkerContextCore::AddProviderHost(
147    scoped_ptr<ServiceWorkerProviderHost> host) {
148  ServiceWorkerProviderHost* host_ptr = host.release();   // we take ownership
149  ProviderMap* map = GetProviderMapForProcess(host_ptr->process_id());
150  if (!map) {
151    map = new ProviderMap;
152    providers_->AddWithID(map, host_ptr->process_id());
153  }
154  map->AddWithID(host_ptr, host_ptr->provider_id());
155}
156
157void ServiceWorkerContextCore::RemoveProviderHost(
158    int process_id, int provider_id) {
159  ProviderMap* map = GetProviderMapForProcess(process_id);
160  DCHECK(map);
161  map->Remove(provider_id);
162}
163
164void ServiceWorkerContextCore::RemoveAllProviderHostsForProcess(
165    int process_id) {
166  if (providers_->Lookup(process_id))
167    providers_->Remove(process_id);
168}
169
170scoped_ptr<ServiceWorkerContextCore::ProviderHostIterator>
171ServiceWorkerContextCore::GetProviderHostIterator() {
172  return make_scoped_ptr(new ProviderHostIterator(providers_.get()));
173}
174
175void ServiceWorkerContextCore::RegisterServiceWorker(
176    const GURL& pattern,
177    const GURL& script_url,
178    ServiceWorkerProviderHost* provider_host,
179    const RegistrationCallback& callback) {
180  DCHECK_CURRENTLY_ON(BrowserThread::IO);
181  if (storage()->IsDisabled()) {
182    callback.Run(SERVICE_WORKER_ERROR_ABORT,
183                 kInvalidServiceWorkerRegistrationId,
184                 kInvalidServiceWorkerVersionId);
185    return;
186  }
187
188  job_coordinator_->Register(
189      pattern,
190      script_url,
191      provider_host,
192      base::Bind(&ServiceWorkerContextCore::RegistrationComplete,
193                 AsWeakPtr(),
194                 pattern,
195                 callback));
196}
197
198void ServiceWorkerContextCore::UnregisterServiceWorker(
199    const GURL& pattern,
200    const UnregistrationCallback& callback) {
201  DCHECK_CURRENTLY_ON(BrowserThread::IO);
202  if (storage()->IsDisabled()) {
203    callback.Run(SERVICE_WORKER_ERROR_ABORT);
204    return;
205  }
206
207  job_coordinator_->Unregister(
208      pattern,
209      base::Bind(&ServiceWorkerContextCore::UnregistrationComplete,
210                 AsWeakPtr(),
211                 pattern,
212                 callback));
213}
214
215void ServiceWorkerContextCore::UpdateServiceWorker(
216    ServiceWorkerRegistration* registration) {
217  DCHECK_CURRENTLY_ON(BrowserThread::IO);
218  if (storage()->IsDisabled())
219    return;
220  job_coordinator_->Update(registration);
221}
222
223void ServiceWorkerContextCore::RegistrationComplete(
224    const GURL& pattern,
225    const ServiceWorkerContextCore::RegistrationCallback& callback,
226    ServiceWorkerStatusCode status,
227    ServiceWorkerRegistration* registration,
228    ServiceWorkerVersion* version) {
229  if (status != SERVICE_WORKER_OK) {
230    DCHECK(!version);
231    callback.Run(status,
232                 kInvalidServiceWorkerRegistrationId,
233                 kInvalidServiceWorkerVersionId);
234    return;
235  }
236
237  DCHECK(version);
238  DCHECK_EQ(version->registration_id(), registration->id());
239  callback.Run(status,
240               registration->id(),
241               version->version_id());
242  if (observer_list_.get()) {
243    observer_list_->Notify(&ServiceWorkerContextObserver::OnRegistrationStored,
244                           pattern);
245  }
246}
247
248void ServiceWorkerContextCore::UnregistrationComplete(
249    const GURL& pattern,
250    const ServiceWorkerContextCore::UnregistrationCallback& callback,
251    ServiceWorkerStatusCode status) {
252  callback.Run(status);
253  if (observer_list_.get()) {
254    observer_list_->Notify(&ServiceWorkerContextObserver::OnRegistrationDeleted,
255                           pattern);
256  }
257}
258
259ServiceWorkerRegistration* ServiceWorkerContextCore::GetLiveRegistration(
260    int64 id) {
261  RegistrationsMap::iterator it = live_registrations_.find(id);
262  return (it != live_registrations_.end()) ? it->second : NULL;
263}
264
265void ServiceWorkerContextCore::AddLiveRegistration(
266    ServiceWorkerRegistration* registration) {
267  DCHECK(!GetLiveRegistration(registration->id()));
268  live_registrations_[registration->id()] = registration;
269}
270
271void ServiceWorkerContextCore::RemoveLiveRegistration(int64 id) {
272  live_registrations_.erase(id);
273}
274
275ServiceWorkerVersion* ServiceWorkerContextCore::GetLiveVersion(
276    int64 id) {
277  VersionMap::iterator it = live_versions_.find(id);
278  return (it != live_versions_.end()) ? it->second : NULL;
279}
280
281void ServiceWorkerContextCore::AddLiveVersion(ServiceWorkerVersion* version) {
282  DCHECK(!GetLiveVersion(version->version_id()));
283  live_versions_[version->version_id()] = version;
284  version->AddListener(this);
285}
286
287void ServiceWorkerContextCore::RemoveLiveVersion(int64 id) {
288  live_versions_.erase(id);
289}
290
291std::vector<ServiceWorkerRegistrationInfo>
292ServiceWorkerContextCore::GetAllLiveRegistrationInfo() {
293  std::vector<ServiceWorkerRegistrationInfo> infos;
294  for (std::map<int64, ServiceWorkerRegistration*>::const_iterator iter =
295           live_registrations_.begin();
296       iter != live_registrations_.end();
297       ++iter) {
298    infos.push_back(iter->second->GetInfo());
299  }
300  return infos;
301}
302
303std::vector<ServiceWorkerVersionInfo>
304ServiceWorkerContextCore::GetAllLiveVersionInfo() {
305  std::vector<ServiceWorkerVersionInfo> infos;
306  for (std::map<int64, ServiceWorkerVersion*>::const_iterator iter =
307           live_versions_.begin();
308       iter != live_versions_.end();
309       ++iter) {
310    infos.push_back(iter->second->GetInfo());
311  }
312  return infos;
313}
314
315int ServiceWorkerContextCore::GetNewServiceWorkerHandleId() {
316  return next_handle_id_++;
317}
318
319int ServiceWorkerContextCore::GetNewRegistrationHandleId() {
320  return next_registration_handle_id_++;
321}
322
323void ServiceWorkerContextCore::ScheduleDeleteAndStartOver() const {
324  storage_->Disable();
325  base::MessageLoop::current()->PostTask(
326      FROM_HERE,
327      base::Bind(&ServiceWorkerContextWrapper::DeleteAndStartOver, wrapper_));
328}
329
330void ServiceWorkerContextCore::DeleteAndStartOver(
331    const StatusCallback& callback) {
332  job_coordinator_->AbortAll();
333  storage_->DeleteAndStartOver(callback);
334}
335
336void ServiceWorkerContextCore::SetBlobParametersForCache(
337    net::URLRequestContext* request_context,
338    base::WeakPtr<storage::BlobStorageContext> blob_storage_context) {
339  DCHECK_CURRENTLY_ON(BrowserThread::IO);
340
341  cache_manager_->SetBlobParametersForCache(request_context,
342                                            blob_storage_context);
343}
344
345void ServiceWorkerContextCore::OnWorkerStarted(ServiceWorkerVersion* version) {
346  if (!observer_list_.get())
347    return;
348  observer_list_->Notify(&ServiceWorkerContextObserver::OnWorkerStarted,
349                         version->version_id(),
350                         version->embedded_worker()->process_id(),
351                         version->embedded_worker()->thread_id());
352}
353
354void ServiceWorkerContextCore::OnWorkerStopped(ServiceWorkerVersion* version) {
355  if (!observer_list_.get())
356    return;
357  observer_list_->Notify(&ServiceWorkerContextObserver::OnWorkerStopped,
358                         version->version_id(),
359                         version->embedded_worker()->process_id(),
360                         version->embedded_worker()->thread_id());
361}
362
363void ServiceWorkerContextCore::OnVersionStateChanged(
364    ServiceWorkerVersion* version) {
365  if (!observer_list_.get())
366    return;
367  observer_list_->Notify(&ServiceWorkerContextObserver::OnVersionStateChanged,
368                         version->version_id());
369}
370
371void ServiceWorkerContextCore::OnErrorReported(
372    ServiceWorkerVersion* version,
373    const base::string16& error_message,
374    int line_number,
375    int column_number,
376    const GURL& source_url) {
377  if (!observer_list_.get())
378    return;
379  observer_list_->Notify(
380      &ServiceWorkerContextObserver::OnErrorReported,
381      version->version_id(),
382      version->embedded_worker()->process_id(),
383      version->embedded_worker()->thread_id(),
384      ServiceWorkerContextObserver::ErrorInfo(
385          error_message, line_number, column_number, source_url));
386}
387
388void ServiceWorkerContextCore::OnReportConsoleMessage(
389    ServiceWorkerVersion* version,
390    int source_identifier,
391    int message_level,
392    const base::string16& message,
393    int line_number,
394    const GURL& source_url) {
395  if (!observer_list_.get())
396    return;
397  observer_list_->Notify(
398      &ServiceWorkerContextObserver::OnReportConsoleMessage,
399      version->version_id(),
400      version->embedded_worker()->process_id(),
401      version->embedded_worker()->thread_id(),
402      ServiceWorkerContextObserver::ConsoleMessage(
403          source_identifier, message_level, message, line_number, source_url));
404}
405
406ServiceWorkerProcessManager* ServiceWorkerContextCore::process_manager() {
407  if (wrapper_)
408    return wrapper_->process_manager();
409  return NULL;
410}
411
412}  // namespace content
413