1 // Copyright (c) 2012 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 "webkit/browser/appcache/appcache_quota_client.h" 6 7 #include <algorithm> 8 #include <map> 9 #include <set> 10 11 #include "base/bind.h" 12 #include "base/bind_helpers.h" 13 #include "webkit/browser/appcache/appcache_service_impl.h" 14 15 using quota::QuotaClient; 16 17 namespace { 18 quota::QuotaStatusCode NetErrorCodeToQuotaStatus(int code) { 19 if (code == net::OK) 20 return quota::kQuotaStatusOk; 21 else if (code == net::ERR_ABORTED) 22 return quota::kQuotaErrorAbort; 23 else 24 return quota::kQuotaStatusUnknown; 25 } 26 27 void RunFront(appcache::AppCacheQuotaClient::RequestQueue* queue) { 28 base::Closure request = queue->front(); 29 queue->pop_front(); 30 request.Run(); 31 } 32 } // namespace 33 34 namespace appcache { 35 36 AppCacheQuotaClient::AppCacheQuotaClient(AppCacheServiceImpl* service) 37 : service_(service), 38 appcache_is_ready_(false), 39 quota_manager_is_destroyed_(false) { 40 } 41 42 AppCacheQuotaClient::~AppCacheQuotaClient() { 43 DCHECK(pending_batch_requests_.empty()); 44 DCHECK(pending_serial_requests_.empty()); 45 DCHECK(current_delete_request_callback_.is_null()); 46 } 47 48 QuotaClient::ID AppCacheQuotaClient::id() const { 49 return kAppcache; 50 } 51 52 void AppCacheQuotaClient::OnQuotaManagerDestroyed() { 53 DeletePendingRequests(); 54 if (!current_delete_request_callback_.is_null()) { 55 current_delete_request_callback_.Reset(); 56 GetServiceDeleteCallback()->Cancel(); 57 } 58 59 quota_manager_is_destroyed_ = true; 60 if (!service_) 61 delete this; 62 } 63 64 void AppCacheQuotaClient::GetOriginUsage( 65 const GURL& origin, 66 quota::StorageType type, 67 const GetUsageCallback& callback) { 68 DCHECK(!callback.is_null()); 69 DCHECK(!quota_manager_is_destroyed_); 70 71 if (!service_) { 72 callback.Run(0); 73 return; 74 } 75 76 if (!appcache_is_ready_) { 77 pending_batch_requests_.push_back( 78 base::Bind(&AppCacheQuotaClient::GetOriginUsage, 79 base::Unretained(this), origin, type, callback)); 80 return; 81 } 82 83 if (type != quota::kStorageTypeTemporary) { 84 callback.Run(0); 85 return; 86 } 87 88 const AppCacheStorage::UsageMap* map = GetUsageMap(); 89 AppCacheStorage::UsageMap::const_iterator found = map->find(origin); 90 if (found == map->end()) { 91 callback.Run(0); 92 return; 93 } 94 callback.Run(found->second); 95 } 96 97 void AppCacheQuotaClient::GetOriginsForType( 98 quota::StorageType type, 99 const GetOriginsCallback& callback) { 100 GetOriginsHelper(type, std::string(), callback); 101 } 102 103 void AppCacheQuotaClient::GetOriginsForHost( 104 quota::StorageType type, 105 const std::string& host, 106 const GetOriginsCallback& callback) { 107 DCHECK(!callback.is_null()); 108 if (host.empty()) { 109 callback.Run(std::set<GURL>()); 110 return; 111 } 112 GetOriginsHelper(type, host, callback); 113 } 114 115 void AppCacheQuotaClient::DeleteOriginData(const GURL& origin, 116 quota::StorageType type, 117 const DeletionCallback& callback) { 118 DCHECK(!quota_manager_is_destroyed_); 119 120 if (!service_) { 121 callback.Run(quota::kQuotaErrorAbort); 122 return; 123 } 124 125 if (!appcache_is_ready_ || !current_delete_request_callback_.is_null()) { 126 pending_serial_requests_.push_back( 127 base::Bind(&AppCacheQuotaClient::DeleteOriginData, 128 base::Unretained(this), origin, type, callback)); 129 return; 130 } 131 132 current_delete_request_callback_ = callback; 133 if (type != quota::kStorageTypeTemporary) { 134 DidDeleteAppCachesForOrigin(net::OK); 135 return; 136 } 137 138 service_->DeleteAppCachesForOrigin( 139 origin, GetServiceDeleteCallback()->callback()); 140 } 141 142 bool AppCacheQuotaClient::DoesSupport(quota::StorageType type) const { 143 return type == quota::kStorageTypeTemporary; 144 } 145 146 void AppCacheQuotaClient::DidDeleteAppCachesForOrigin(int rv) { 147 DCHECK(service_); 148 if (quota_manager_is_destroyed_) 149 return; 150 151 // Finish the request by calling our callers callback. 152 current_delete_request_callback_.Run(NetErrorCodeToQuotaStatus(rv)); 153 current_delete_request_callback_.Reset(); 154 if (pending_serial_requests_.empty()) 155 return; 156 157 // Start the next in the queue. 158 RunFront(&pending_serial_requests_); 159 } 160 161 void AppCacheQuotaClient::GetOriginsHelper( 162 quota::StorageType type, 163 const std::string& opt_host, 164 const GetOriginsCallback& callback) { 165 DCHECK(!callback.is_null()); 166 DCHECK(!quota_manager_is_destroyed_); 167 168 if (!service_) { 169 callback.Run(std::set<GURL>()); 170 return; 171 } 172 173 if (!appcache_is_ready_) { 174 pending_batch_requests_.push_back( 175 base::Bind(&AppCacheQuotaClient::GetOriginsHelper, 176 base::Unretained(this), type, opt_host, callback)); 177 return; 178 } 179 180 if (type != quota::kStorageTypeTemporary) { 181 callback.Run(std::set<GURL>()); 182 return; 183 } 184 185 const AppCacheStorage::UsageMap* map = GetUsageMap(); 186 std::set<GURL> origins; 187 for (AppCacheStorage::UsageMap::const_iterator iter = map->begin(); 188 iter != map->end(); ++iter) { 189 if (opt_host.empty() || iter->first.host() == opt_host) 190 origins.insert(iter->first); 191 } 192 callback.Run(origins); 193 } 194 195 void AppCacheQuotaClient::ProcessPendingRequests() { 196 DCHECK(appcache_is_ready_); 197 while (!pending_batch_requests_.empty()) 198 RunFront(&pending_batch_requests_); 199 200 if (!pending_serial_requests_.empty()) 201 RunFront(&pending_serial_requests_); 202 } 203 204 void AppCacheQuotaClient::DeletePendingRequests() { 205 pending_batch_requests_.clear(); 206 pending_serial_requests_.clear(); 207 } 208 209 const AppCacheStorage::UsageMap* AppCacheQuotaClient::GetUsageMap() { 210 DCHECK(service_); 211 return service_->storage()->usage_map(); 212 } 213 214 net::CancelableCompletionCallback* 215 AppCacheQuotaClient::GetServiceDeleteCallback() { 216 // Lazily created due to CancelableCompletionCallback's threading 217 // restrictions, there is no way to detach from the thread created on. 218 if (!service_delete_callback_) { 219 service_delete_callback_.reset( 220 new net::CancelableCompletionCallback( 221 base::Bind(&AppCacheQuotaClient::DidDeleteAppCachesForOrigin, 222 base::Unretained(this)))); 223 } 224 return service_delete_callback_.get(); 225 } 226 227 void AppCacheQuotaClient::NotifyAppCacheReady() { 228 // Can reoccur during reinitialization. 229 if (!appcache_is_ready_) { 230 appcache_is_ready_ = true; 231 ProcessPendingRequests(); 232 } 233 } 234 235 void AppCacheQuotaClient::NotifyAppCacheDestroyed() { 236 service_ = NULL; 237 while (!pending_batch_requests_.empty()) 238 RunFront(&pending_batch_requests_); 239 240 while (!pending_serial_requests_.empty()) 241 RunFront(&pending_serial_requests_); 242 243 if (!current_delete_request_callback_.is_null()) { 244 current_delete_request_callback_.Run(quota::kQuotaErrorAbort); 245 current_delete_request_callback_.Reset(); 246 GetServiceDeleteCallback()->Cancel(); 247 } 248 249 if (quota_manager_is_destroyed_) 250 delete this; 251 } 252 253 } // namespace appcache 254