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