Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 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 "extensions/browser/service_worker_manager.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/callback.h" | |
| 9 #include "components/browser_context_keyed_service/browser_context_dependency_ma nager.h" | |
| 10 #include "content/browser/service_worker/service_worker_context_core.h" | |
| 11 #include "content/browser/service_worker/service_worker_context_wrapper.h" | |
| 12 #include "content/public/browser/browser_context.h" | |
| 13 #include "content/public/browser/browser_thread.h" | |
| 14 #include "content/public/browser/storage_partition.h" | |
| 15 #include "extensions/browser/extensions_browser_client.h" | |
| 16 #include "extensions/common/manifest_handlers/background_info.h" | |
| 17 | |
| 18 namespace extensions { | |
| 19 | |
| 20 using base::Callback; | |
| 21 using base::Closure; | |
| 22 using base::WeakPtr; | |
| 23 using content::BrowserContext; | |
| 24 using content::BrowserThread; | |
| 25 using content::ServiceWorkerStatusCode; | |
| 26 | |
| 27 static const char* CurrentBrowserThreadName() { | |
| 28 BrowserThread::ID id; | |
| 29 if (BrowserThread::GetCurrentThreadIdentifier(&id)) { | |
| 30 switch (id) { | |
| 31 case BrowserThread::UI: | |
| 32 return "UI"; | |
| 33 case BrowserThread::DB: | |
| 34 return "DB"; | |
| 35 case BrowserThread::FILE: | |
| 36 return "FILE"; | |
| 37 case BrowserThread::FILE_USER_BLOCKING: | |
| 38 return "FILE_USER_BLOCKING"; | |
| 39 case BrowserThread::PROCESS_LAUNCHER: | |
| 40 return "PROCESS_LAUNCHER"; | |
| 41 case BrowserThread::CACHE: | |
| 42 return "CACHE"; | |
| 43 case BrowserThread::IO: | |
| 44 return "IO"; | |
| 45 default: | |
| 46 break; | |
| 47 } | |
| 48 } | |
| 49 return "Unknown"; | |
| 50 } | |
| 51 | |
| 52 static void DCheckCurrentlyOn(BrowserThread::ID thread) { | |
| 53 DCHECK(BrowserThread::CurrentlyOn(thread)) << CurrentBrowserThreadName(); | |
|
scheib
2014/03/10 21:31:38
Maybe base::MessageLoop::current()->thread_name();
| |
| 54 } | |
| 55 | |
| 56 ServiceWorkerManager::State::State() {} | |
| 57 ServiceWorkerManager::State::~State() {} | |
| 58 | |
| 59 ServiceWorkerManager::ServiceWorkerManager(BrowserContext* context) | |
| 60 : context_(context), weak_this_factory_(this) {} | |
| 61 ServiceWorkerManager::~ServiceWorkerManager() {} | |
| 62 | |
| 63 ServiceWorkerManager* ServiceWorkerManager::Get( | |
| 64 content::BrowserContext* context) { | |
| 65 return ServiceWorkerManagerFactory::GetForBrowserContext(context); | |
| 66 } | |
| 67 | |
| 68 content::StoragePartition* ServiceWorkerManager::GetStoragePartition( | |
| 69 const Extension* ext) const { | |
| 70 return content::BrowserContext::GetStoragePartitionForSite( | |
| 71 context_, Extension::GetBaseURLFromExtensionId(ext->id())); | |
| 72 } | |
| 73 | |
| 74 scoped_refptr<content::ServiceWorkerContextWrapper> | |
| 75 ServiceWorkerManager::GetSWContext(const Extension* ext) const { | |
| 76 return GetStoragePartition(ext)->GetServiceWorkerContext(); | |
| 77 } | |
| 78 | |
| 79 template <typename Arg1> | |
| 80 static void RunCallback1(const Callback<void(Arg1)>& callback, | |
| 81 const Arg1& arg1) { | |
| 82 callback.Run(arg1); | |
| 83 } | |
| 84 | |
| 85 static void FinishRegister( | |
| 86 const Callback<void(ServiceWorkerStatusCode)>& continuation, | |
| 87 content::ServiceWorkerStatusCode status, | |
| 88 int64 registration_id) { | |
| 89 DCheckCurrentlyOn(BrowserThread::IO); | |
| 90 BrowserThread::PostTask( | |
| 91 BrowserThread::UI, | |
| 92 FROM_HERE, | |
| 93 base::Bind(&RunCallback1<ServiceWorkerStatusCode>, continuation, status)); | |
|
kinuko
2014/03/10 07:03:19
nit: why don't we just do base::Bind(continuation,
| |
| 94 } | |
| 95 | |
| 96 static void DoRegister( | |
| 97 const scoped_refptr<content::ServiceWorkerContextWrapper>& | |
| 98 service_worker_context, | |
| 99 const ExtensionId& extension_id, | |
| 100 const GURL& service_worker_script, | |
| 101 const Callback<void(ServiceWorkerStatusCode)>& continuation) { | |
| 102 DCheckCurrentlyOn(BrowserThread::IO); | |
| 103 service_worker_context->context()->RegisterServiceWorker( | |
| 104 Extension::GetBaseURLFromExtensionId(extension_id).Resolve("/*"), | |
| 105 service_worker_script, | |
| 106 -1, | |
| 107 base::Bind(&FinishRegister, continuation)); | |
| 108 } | |
| 109 | |
| 110 // alecflett says that if we send a series of RegisterServiceWorker and | |
| 111 // UnregisterServiceWorker calls to a ServiceWorkerContextCore, we're guaranteed | |
| 112 // that the callbacks come back in the same order, and that the last one will be | |
|
michaeln
2014/03/10 22:15:03
That order is guaranteed for the same 'scope', but
| |
| 113 // the final state. | |
| 114 void ServiceWorkerManager::RegisterExtension(const Extension* extension) { | |
| 115 DCheckCurrentlyOn(BrowserThread::UI); | |
| 116 CHECK(BackgroundInfo::HasServiceWorker(extension)); | |
| 117 State& ext_state = states_[extension->id()]; | |
| 118 if (ext_state.registration == REGISTERING || | |
| 119 ext_state.registration == REGISTERED) | |
| 120 return; | |
| 121 ext_state.registration = REGISTERING; | |
| 122 ++ext_state.outstanding_state_changes; | |
| 123 BrowserThread::PostTask( | |
| 124 BrowserThread::IO, | |
| 125 FROM_HERE, | |
| 126 base::Bind(&DoRegister, | |
| 127 GetSWContext(extension), | |
| 128 extension->id(), | |
| 129 extension->GetResourceURL( | |
| 130 BackgroundInfo::GetServiceWorkerScript(extension)), | |
| 131 base::Bind(&ServiceWorkerManager::FinishRegistration, | |
| 132 WeakThis(), | |
| 133 extension->id()))); | |
| 134 } | |
| 135 | |
| 136 void ServiceWorkerManager::FinishRegistration(const ExtensionId& extension_id, | |
| 137 ServiceWorkerStatusCode result) { | |
| 138 DCheckCurrentlyOn(BrowserThread::UI); | |
| 139 State& ext_state = states_[extension_id]; | |
| 140 --ext_state.outstanding_state_changes; | |
| 141 DCHECK_GE(ext_state.outstanding_state_changes, 0); | |
| 142 if (ext_state.outstanding_state_changes > 0) | |
| 143 return; | |
| 144 | |
| 145 DCHECK_EQ(ext_state.registration, REGISTERING); | |
| 146 std::vector<Closure> to_run; | |
| 147 switch (result) { | |
| 148 case content::SERVICE_WORKER_OK: | |
| 149 ext_state.registration = REGISTERED; | |
| 150 to_run.swap(ext_state.registration_succeeded); | |
| 151 ext_state.registration_failed.clear(); | |
| 152 break; | |
| 153 default: | |
| 154 LOG(ERROR) << "Service Worker Registration failed for extension " | |
| 155 << extension_id << ": " | |
| 156 << content::ServiceWorkerStatusToString(result); | |
| 157 to_run.swap(ext_state.registration_failed); | |
| 158 states_.erase(extension_id); | |
| 159 break; | |
| 160 } | |
| 161 | |
| 162 for (size_t i = 0; i < to_run.size(); ++i) { | |
| 163 to_run[i].Run(); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 static void FinishUnregister( | |
| 168 const Callback<void(ServiceWorkerStatusCode)>& continuation, | |
| 169 content::ServiceWorkerStatusCode status) { | |
| 170 DCheckCurrentlyOn(BrowserThread::IO); | |
| 171 BrowserThread::PostTask( | |
| 172 BrowserThread::UI, | |
| 173 FROM_HERE, | |
| 174 base::Bind(&RunCallback1<ServiceWorkerStatusCode>, continuation, status)); | |
| 175 } | |
| 176 | |
| 177 static void DoUnregister( | |
| 178 const scoped_refptr<content::ServiceWorkerContextWrapper>& | |
| 179 service_worker_context, | |
| 180 const ExtensionId& extension_id, | |
| 181 const Callback<void(ServiceWorkerStatusCode)>& continuation) { | |
| 182 DCheckCurrentlyOn(BrowserThread::IO); | |
| 183 service_worker_context->context()->UnregisterServiceWorker( | |
| 184 Extension::GetBaseURLFromExtensionId(extension_id).Resolve("/*"), | |
| 185 -1, | |
| 186 base::Bind(&FinishUnregister, continuation)); | |
| 187 } | |
| 188 | |
| 189 void ServiceWorkerManager::UnregisterExtension(const Extension* extension) { | |
| 190 DCheckCurrentlyOn(BrowserThread::UI); | |
| 191 CHECK(BackgroundInfo::HasServiceWorker(extension)); | |
| 192 | |
| 193 base::hash_map<ExtensionId, State>::iterator it = | |
| 194 states_.find(extension->id()); | |
| 195 if (it == states_.end()) { | |
| 196 // Extension isn't registered. | |
| 197 return; | |
| 198 } | |
| 199 State& ext_state = it->second; | |
| 200 if (ext_state.registration == UNREGISTERING) | |
| 201 return; | |
| 202 | |
| 203 ext_state.registration = UNREGISTERING; | |
| 204 ++ext_state.outstanding_state_changes; | |
| 205 BrowserThread::PostTask( | |
| 206 BrowserThread::IO, | |
| 207 FROM_HERE, | |
| 208 base::Bind(&DoUnregister, | |
| 209 GetSWContext(extension), | |
| 210 extension->id(), | |
| 211 base::Bind(&ServiceWorkerManager::FinishUnregistration, | |
| 212 WeakThis(), | |
| 213 extension->id()))); | |
| 214 } | |
| 215 | |
| 216 void ServiceWorkerManager::FinishUnregistration( | |
| 217 const ExtensionId& extension_id, | |
| 218 ServiceWorkerStatusCode result) { | |
| 219 DCheckCurrentlyOn(BrowserThread::UI); | |
| 220 State& ext_state = states_[extension_id]; | |
| 221 --ext_state.outstanding_state_changes; | |
| 222 DCHECK_GE(ext_state.outstanding_state_changes, 0); | |
| 223 if (ext_state.outstanding_state_changes > 0) | |
| 224 return; | |
| 225 | |
| 226 DCHECK_EQ(ext_state.registration, UNREGISTERING); | |
| 227 std::vector<Closure> to_run; | |
| 228 switch (result) { | |
| 229 case content::SERVICE_WORKER_OK: | |
| 230 to_run.swap(ext_state.unregistration_succeeded); | |
| 231 states_.erase(extension_id); | |
| 232 break; | |
| 233 default: | |
| 234 LOG(ERROR) << "Service Worker Unregistration failed for extension " | |
| 235 << extension_id << ": " | |
| 236 << content::ServiceWorkerStatusToString(result); | |
| 237 ext_state.registration = REGISTERED; | |
| 238 to_run.swap(ext_state.unregistration_failed); | |
| 239 ext_state.unregistration_succeeded.clear(); | |
| 240 break; | |
| 241 } | |
| 242 | |
| 243 for (size_t i = 0; i < to_run.size(); ++i) { | |
| 244 to_run[i].Run(); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 WeakPtr<ServiceWorkerManager> ServiceWorkerManager::WeakThis() { | |
| 249 return weak_this_factory_.GetWeakPtr(); | |
| 250 } | |
| 251 | |
| 252 // ServiceWorkerManagerFactory | |
| 253 | |
| 254 ServiceWorkerManager* ServiceWorkerManagerFactory::GetForBrowserContext( | |
| 255 content::BrowserContext* context) { | |
| 256 return static_cast<ServiceWorkerManager*>( | |
| 257 GetInstance()->GetServiceForBrowserContext(context, true)); | |
| 258 } | |
| 259 | |
| 260 ServiceWorkerManagerFactory* ServiceWorkerManagerFactory::GetInstance() { | |
| 261 return Singleton<ServiceWorkerManagerFactory>::get(); | |
| 262 } | |
| 263 | |
| 264 void ServiceWorkerManagerFactory::SetInstanceForTesting( | |
| 265 content::BrowserContext* context, | |
| 266 ServiceWorkerManager* manager) { | |
| 267 Associate(context, manager); | |
| 268 } | |
| 269 | |
| 270 ServiceWorkerManagerFactory::ServiceWorkerManagerFactory() | |
| 271 : BrowserContextKeyedServiceFactory( | |
| 272 "ServiceWorkerManager", | |
| 273 BrowserContextDependencyManager::GetInstance()) {} | |
| 274 | |
| 275 ServiceWorkerManagerFactory::~ServiceWorkerManagerFactory() {} | |
| 276 | |
| 277 BrowserContextKeyedService* | |
| 278 ServiceWorkerManagerFactory::BuildServiceInstanceFor( | |
| 279 content::BrowserContext* context) const { | |
| 280 return new ServiceWorkerManager(context); | |
| 281 } | |
| 282 | |
| 283 // TODO(jyasskin): Deal with incognito mode. | |
| 284 content::BrowserContext* ServiceWorkerManagerFactory::GetBrowserContextToUse( | |
| 285 content::BrowserContext* context) const { | |
| 286 return ExtensionsBrowserClient::Get()->GetOriginalContext(context); | |
| 287 } | |
| 288 | |
| 289 } // namespace extensions | |
| OLD | NEW |