Home | History | Annotate | Download | only in service_worker
      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_storage.h"
      6 
      7 #include <string>
      8 
      9 #include "base/strings/string_util.h"
     10 #include "content/browser/service_worker/service_worker_register_job.h"
     11 #include "content/browser/service_worker/service_worker_registration.h"
     12 #include "content/public/browser/browser_thread.h"
     13 #include "webkit/browser/quota/quota_manager.h"
     14 
     15 namespace {
     16 // This is temporary until we figure out how registration ids will be
     17 // calculated.
     18 int64 NextRegistrationId() {
     19   static int64 worker_id = 0;
     20   return worker_id++;
     21 }
     22 }  // namespace
     23 
     24 namespace content {
     25 
     26 const base::FilePath::CharType kServiceWorkerDirectory[] =
     27     FILE_PATH_LITERAL("ServiceWorker");
     28 
     29 ServiceWorkerStorage::ServiceWorkerStorage(
     30     const base::FilePath& path,
     31     quota::QuotaManagerProxy* quota_manager_proxy)
     32     : quota_manager_proxy_(quota_manager_proxy), weak_factory_(this) {
     33   if (!path.empty())
     34     path_ = path.Append(kServiceWorkerDirectory);
     35 }
     36 
     37 ServiceWorkerStorage::~ServiceWorkerStorage() {
     38   for (PatternToRegistrationMap::const_iterator iter =
     39            registration_by_pattern_.begin();
     40        iter != registration_by_pattern_.end();
     41        ++iter) {
     42     iter->second->Shutdown();
     43   }
     44   registration_by_pattern_.clear();
     45 }
     46 
     47 void ServiceWorkerStorage::FindRegistrationForPattern(
     48     const GURL& pattern,
     49     const FindRegistrationCallback& callback) {
     50   PatternToRegistrationMap::const_iterator match =
     51       registration_by_pattern_.find(pattern);
     52   if (match == registration_by_pattern_.end()) {
     53     BrowserThread::PostTask(
     54         BrowserThread::IO,
     55         FROM_HERE,
     56         base::Bind(callback,
     57                    false /* found */,
     58                    REGISTRATION_OK,
     59                    scoped_refptr<ServiceWorkerRegistration>()));
     60     return;
     61   }
     62   BrowserThread::PostTask(
     63       BrowserThread::IO,
     64       FROM_HERE,
     65       base::Bind(callback, true /* found */, REGISTRATION_OK, match->second));
     66 }
     67 
     68 void ServiceWorkerStorage::FindRegistrationForDocument(
     69     const GURL& document_url,
     70     const FindRegistrationCallback& callback) {
     71   // TODO(alecflett): This needs to be synchronous in the fast path,
     72   // but asynchronous in the slow path (when the patterns have to be
     73   // loaded from disk). For now it is always pessimistically async.
     74   for (PatternToRegistrationMap::const_iterator it =
     75            registration_by_pattern_.begin();
     76        it != registration_by_pattern_.end();
     77        ++it) {
     78     if (PatternMatches(it->first, document_url)) {
     79       BrowserThread::PostTask(
     80           BrowserThread::IO,
     81           FROM_HERE,
     82           base::Bind(callback,
     83                      true /* found */,
     84                      REGISTRATION_OK,
     85                      scoped_refptr<ServiceWorkerRegistration>(it->second)));
     86       return;
     87     }
     88   }
     89   BrowserThread::PostTask(
     90       BrowserThread::IO,
     91       FROM_HERE,
     92       base::Bind(callback,
     93                  false /* found */,
     94                  REGISTRATION_OK,
     95                  scoped_refptr<ServiceWorkerRegistration>()));
     96 }
     97 
     98 void ServiceWorkerStorage::Register(const GURL& pattern,
     99                                     const GURL& script_url,
    100                                     const RegistrationCallback& callback) {
    101   scoped_ptr<ServiceWorkerRegisterJob> job(new ServiceWorkerRegisterJob(
    102       weak_factory_.GetWeakPtr(),
    103       base::Bind(&ServiceWorkerStorage::RegisterComplete,
    104                  weak_factory_.GetWeakPtr(),
    105                  callback)));
    106   job->StartRegister(pattern, script_url);
    107   registration_jobs_.push_back(job.release());
    108 }
    109 
    110 void ServiceWorkerStorage::Unregister(const GURL& pattern,
    111                                       const UnregistrationCallback& callback) {
    112   scoped_ptr<ServiceWorkerRegisterJob> job(new ServiceWorkerRegisterJob(
    113       weak_factory_.GetWeakPtr(),
    114       base::Bind(&ServiceWorkerStorage::UnregisterComplete,
    115                  weak_factory_.GetWeakPtr(),
    116                  callback)));
    117   job->StartUnregister(pattern);
    118   registration_jobs_.push_back(job.release());
    119 }
    120 
    121 scoped_refptr<ServiceWorkerRegistration> ServiceWorkerStorage::RegisterInternal(
    122     const GURL& pattern,
    123     const GURL& script_url) {
    124 
    125   PatternToRegistrationMap::const_iterator current(
    126       registration_by_pattern_.find(pattern));
    127   DCHECK(current == registration_by_pattern_.end() ||
    128          current->second->script_url() == script_url);
    129 
    130   if (current == registration_by_pattern_.end()) {
    131     scoped_refptr<ServiceWorkerRegistration> registration(
    132         new ServiceWorkerRegistration(
    133             pattern, script_url, NextRegistrationId()));
    134     // TODO(alecflett): version upgrade path.
    135     registration_by_pattern_[pattern] = registration;
    136     return registration;
    137   }
    138 
    139   return current->second;
    140 }
    141 
    142 void ServiceWorkerStorage::UnregisterInternal(const GURL& pattern) {
    143   PatternToRegistrationMap::iterator match =
    144       registration_by_pattern_.find(pattern);
    145   if (match != registration_by_pattern_.end()) {
    146     match->second->Shutdown();
    147     registration_by_pattern_.erase(match);
    148   }
    149 }
    150 
    151 bool ServiceWorkerStorage::PatternMatches(const GURL& pattern,
    152                                           const GURL& url) {
    153   // This is a really basic, naive
    154   // TODO(alecflett): Formalize what pattern matches mean.
    155   // Temporarily borrowed directly from appcache::Namespace::IsMatch().
    156   // We have to escape '?' characters since MatchPattern also treats those
    157   // as wildcards which we don't want here, we only do '*'s.
    158   std::string pattern_spec(pattern.spec());
    159   if (pattern.has_query())
    160     ReplaceSubstringsAfterOffset(&pattern_spec, 0, "?", "\\?");
    161   return MatchPattern(url.spec(), pattern_spec);
    162 }
    163 
    164 void ServiceWorkerStorage::EraseJob(ServiceWorkerRegisterJob* job) {
    165   ScopedVector<ServiceWorkerRegisterJob>::iterator job_position =
    166       registration_jobs_.begin();
    167   for (; job_position != registration_jobs_.end(); ++job_position) {
    168     if (*job_position == job) {
    169       registration_jobs_.erase(job_position);
    170       return;
    171     }
    172   }
    173   NOTREACHED() << "Deleting non-existent job. ";
    174 }
    175 
    176 void ServiceWorkerStorage::UnregisterComplete(
    177     const UnregistrationCallback& callback,
    178     ServiceWorkerRegisterJob* job,
    179     ServiceWorkerRegistrationStatus status,
    180     ServiceWorkerRegistration* previous_registration) {
    181   callback.Run(status);
    182   EraseJob(job);
    183 }
    184 
    185 void ServiceWorkerStorage::RegisterComplete(
    186     const RegistrationCallback& callback,
    187     ServiceWorkerRegisterJob* job,
    188     ServiceWorkerRegistrationStatus status,
    189     ServiceWorkerRegistration* registration) {
    190   callback.Run(status, registration);
    191   EraseJob(job);
    192 }
    193 
    194 }  // namespace content
    195