OLD | NEW |
| (Empty) |
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/quota/quota_manager.h" | |
6 | |
7 #include <algorithm> | |
8 #include <deque> | |
9 #include <functional> | |
10 #include <set> | |
11 | |
12 #include "base/bind.h" | |
13 #include "base/bind_helpers.h" | |
14 #include "base/callback.h" | |
15 #include "base/command_line.h" | |
16 #include "base/file_util.h" | |
17 #include "base/files/file_path.h" | |
18 #include "base/metrics/histogram.h" | |
19 #include "base/sequenced_task_runner.h" | |
20 #include "base/single_thread_task_runner.h" | |
21 #include "base/strings/string_number_conversions.h" | |
22 #include "base/sys_info.h" | |
23 #include "base/task_runner_util.h" | |
24 #include "base/time.h" | |
25 #include "net/base/net_util.h" | |
26 #include "webkit/quota/quota_database.h" | |
27 #include "webkit/quota/quota_temporary_storage_evictor.h" | |
28 #include "webkit/quota/quota_types.h" | |
29 #include "webkit/quota/usage_tracker.h" | |
30 | |
31 #define UMA_HISTOGRAM_MBYTES(name, sample) \ | |
32 UMA_HISTOGRAM_CUSTOM_COUNTS( \ | |
33 (name), static_cast<int>((sample) / kMBytes), \ | |
34 1, 10 * 1024 * 1024 /* 10TB */, 100) | |
35 | |
36 namespace quota { | |
37 | |
38 namespace { | |
39 | |
40 const int64 kMBytes = 1024 * 1024; | |
41 const int kMinutesInMilliSeconds = 60 * 1000; | |
42 | |
43 const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour | |
44 const double kTemporaryQuotaRatioToAvail = 0.5; // 50% | |
45 | |
46 } // namespace | |
47 | |
48 // Arbitrary for now, but must be reasonably small so that | |
49 // in-memory databases can fit. | |
50 // TODO(kinuko): Refer SysInfo::AmountOfPhysicalMemory() to determine this. | |
51 const int64 QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes; | |
52 | |
53 const int64 QuotaManager::kNoLimit = kint64max; | |
54 | |
55 const int QuotaManager::kPerHostTemporaryPortion = 5; // 20% | |
56 | |
57 const char QuotaManager::kDatabaseName[] = "QuotaManager"; | |
58 | |
59 // Preserve kMinimumPreserveForSystem disk space for system book-keeping | |
60 // when returning the quota to unlimited apps/extensions. | |
61 // TODO(kinuko): This should be like 10% of the actual disk space. | |
62 // For now we simply use a constant as getting the disk size needs | |
63 // platform-dependent code. (http://crbug.com/178976) | |
64 const int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes; | |
65 | |
66 const int QuotaManager::kThresholdOfErrorsToBeBlacklisted = 3; | |
67 | |
68 const int QuotaManager::kEvictionIntervalInMilliSeconds = | |
69 30 * kMinutesInMilliSeconds; | |
70 | |
71 // Heuristics: assuming average cloud server allows a few Gigs storage | |
72 // on the server side and the storage needs to be shared for user data | |
73 // and by multiple apps. | |
74 int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes; | |
75 | |
76 namespace { | |
77 | |
78 void CountOriginType(const std::set<GURL>& origins, | |
79 SpecialStoragePolicy* policy, | |
80 size_t* protected_origins, | |
81 size_t* unlimited_origins) { | |
82 DCHECK(protected_origins); | |
83 DCHECK(unlimited_origins); | |
84 *protected_origins = 0; | |
85 *unlimited_origins = 0; | |
86 if (!policy) | |
87 return; | |
88 for (std::set<GURL>::const_iterator itr = origins.begin(); | |
89 itr != origins.end(); | |
90 ++itr) { | |
91 if (policy->IsStorageProtected(*itr)) | |
92 ++*protected_origins; | |
93 if (policy->IsStorageUnlimited(*itr)) | |
94 ++*unlimited_origins; | |
95 } | |
96 } | |
97 | |
98 bool SetTemporaryGlobalOverrideQuotaOnDBThread(int64* new_quota, | |
99 QuotaDatabase* database) { | |
100 DCHECK(database); | |
101 if (!database->SetQuotaConfigValue( | |
102 QuotaDatabase::kTemporaryQuotaOverrideKey, *new_quota)) { | |
103 *new_quota = -1; | |
104 return false; | |
105 } | |
106 return true; | |
107 } | |
108 | |
109 bool GetPersistentHostQuotaOnDBThread(const std::string& host, | |
110 int64* quota, | |
111 QuotaDatabase* database) { | |
112 DCHECK(database); | |
113 database->GetHostQuota(host, kStorageTypePersistent, quota); | |
114 return true; | |
115 } | |
116 | |
117 bool SetPersistentHostQuotaOnDBThread(const std::string& host, | |
118 int64* new_quota, | |
119 QuotaDatabase* database) { | |
120 DCHECK(database); | |
121 if (database->SetHostQuota(host, kStorageTypePersistent, *new_quota)) | |
122 return true; | |
123 *new_quota = 0; | |
124 return false; | |
125 } | |
126 | |
127 bool InitializeOnDBThread(int64* temporary_quota_override, | |
128 int64* desired_available_space, | |
129 QuotaDatabase* database) { | |
130 DCHECK(database); | |
131 database->GetQuotaConfigValue(QuotaDatabase::kTemporaryQuotaOverrideKey, | |
132 temporary_quota_override); | |
133 database->GetQuotaConfigValue(QuotaDatabase::kDesiredAvailableSpaceKey, | |
134 desired_available_space); | |
135 return true; | |
136 } | |
137 | |
138 bool GetLRUOriginOnDBThread(StorageType type, | |
139 std::set<GURL>* exceptions, | |
140 SpecialStoragePolicy* policy, | |
141 GURL* url, | |
142 QuotaDatabase* database) { | |
143 DCHECK(database); | |
144 database->GetLRUOrigin(type, *exceptions, policy, url); | |
145 return true; | |
146 } | |
147 | |
148 bool DeleteOriginInfoOnDBThread(const GURL& origin, | |
149 StorageType type, | |
150 QuotaDatabase* database) { | |
151 DCHECK(database); | |
152 return database->DeleteOriginInfo(origin, type); | |
153 } | |
154 | |
155 bool InitializeTemporaryOriginsInfoOnDBThread(const std::set<GURL>* origins, | |
156 QuotaDatabase* database) { | |
157 DCHECK(database); | |
158 if (database->IsOriginDatabaseBootstrapped()) | |
159 return true; | |
160 | |
161 // Register existing origins with 0 last time access. | |
162 if (database->RegisterInitialOriginInfo(*origins, kStorageTypeTemporary)) { | |
163 database->SetOriginDatabaseBootstrapped(true); | |
164 return true; | |
165 } | |
166 return false; | |
167 } | |
168 | |
169 bool UpdateAccessTimeOnDBThread(const GURL& origin, | |
170 StorageType type, | |
171 base::Time accessed_time, | |
172 QuotaDatabase* database) { | |
173 DCHECK(database); | |
174 return database->SetOriginLastAccessTime(origin, type, accessed_time); | |
175 } | |
176 | |
177 bool UpdateModifiedTimeOnDBThread(const GURL& origin, | |
178 StorageType type, | |
179 base::Time modified_time, | |
180 QuotaDatabase* database) { | |
181 DCHECK(database); | |
182 return database->SetOriginLastModifiedTime(origin, type, modified_time); | |
183 } | |
184 | |
185 int64 CallSystemGetAmountOfFreeDiskSpace(const base::FilePath& profile_path) { | |
186 // Ensure the profile path exists. | |
187 if(!file_util::CreateDirectory(profile_path)) { | |
188 LOG(WARNING) << "Create directory failed for path" << profile_path.value(); | |
189 return 0; | |
190 } | |
191 return base::SysInfo::AmountOfFreeDiskSpace(profile_path); | |
192 } | |
193 | |
194 int64 CalculateTemporaryGlobalQuota(int64 global_limited_usage, | |
195 int64 available_space) { | |
196 DCHECK_GE(global_limited_usage, 0); | |
197 int64 avail_space = available_space; | |
198 if (avail_space < kint64max - global_limited_usage) { | |
199 // We basically calculate the temporary quota by | |
200 // [available_space + space_used_for_temp] * kTempQuotaRatio, | |
201 // but make sure we'll have no overflow. | |
202 avail_space += global_limited_usage; | |
203 } | |
204 return avail_space * kTemporaryQuotaRatioToAvail; | |
205 } | |
206 | |
207 void DispatchTemporaryGlobalQuotaCallback( | |
208 const QuotaCallback& callback, | |
209 QuotaStatusCode status, | |
210 const UsageAndQuota& usage_and_quota) { | |
211 if (status != kQuotaStatusOk) { | |
212 callback.Run(status, 0); | |
213 return; | |
214 } | |
215 | |
216 callback.Run(status, CalculateTemporaryGlobalQuota( | |
217 usage_and_quota.global_limited_usage, | |
218 usage_and_quota.available_disk_space)); | |
219 } | |
220 | |
221 int64 CalculateQuotaWithDiskSpace( | |
222 int64 available_disk_space, int64 usage, int64 quota) { | |
223 if (available_disk_space < QuotaManager::kMinimumPreserveForSystem || | |
224 quota < usage) { | |
225 // No more space; cap the quota to the current usage. | |
226 return usage; | |
227 } | |
228 | |
229 available_disk_space -= QuotaManager::kMinimumPreserveForSystem; | |
230 if (available_disk_space < quota - usage) | |
231 return available_disk_space + usage; | |
232 | |
233 return quota; | |
234 } | |
235 | |
236 int64 CalculateTemporaryHostQuota(int64 host_usage, | |
237 int64 global_quota, | |
238 int64 global_limited_usage) { | |
239 DCHECK_GE(global_limited_usage, 0); | |
240 int64 host_quota = global_quota / QuotaManager::kPerHostTemporaryPortion; | |
241 if (global_limited_usage > global_quota) | |
242 host_quota = std::min(host_quota, host_usage); | |
243 return host_quota; | |
244 } | |
245 | |
246 void DispatchUsageAndQuotaForWebApps( | |
247 StorageType type, | |
248 bool is_incognito, | |
249 bool is_unlimited, | |
250 bool can_query_disk_size, | |
251 const QuotaManager::GetUsageAndQuotaCallback& callback, | |
252 QuotaStatusCode status, | |
253 const UsageAndQuota& usage_and_quota) { | |
254 if (status != kQuotaStatusOk) { | |
255 callback.Run(status, 0, 0); | |
256 return; | |
257 } | |
258 | |
259 int64 usage = usage_and_quota.usage; | |
260 int64 quota = usage_and_quota.quota; | |
261 | |
262 if (type == kStorageTypeTemporary && !is_unlimited) { | |
263 quota = CalculateTemporaryHostQuota( | |
264 usage, quota, usage_and_quota.global_limited_usage); | |
265 } | |
266 | |
267 if (is_incognito) { | |
268 quota = std::min(quota, QuotaManager::kIncognitoDefaultQuotaLimit); | |
269 callback.Run(status, usage, quota); | |
270 return; | |
271 } | |
272 | |
273 // For apps with unlimited permission or can_query_disk_size is true (and not | |
274 // in incognito mode). | |
275 // We assume we can expose the actual disk size for them and cap the quota by | |
276 // the available disk space. | |
277 if (is_unlimited || can_query_disk_size) { | |
278 callback.Run( | |
279 status, usage, | |
280 CalculateQuotaWithDiskSpace( | |
281 usage_and_quota.available_disk_space, | |
282 usage, quota)); | |
283 return; | |
284 } | |
285 | |
286 callback.Run(status, usage, quota); | |
287 } | |
288 | |
289 } // namespace | |
290 | |
291 UsageAndQuota::UsageAndQuota() | |
292 : usage(0), | |
293 global_limited_usage(0), | |
294 quota(0), | |
295 available_disk_space(0) { | |
296 } | |
297 | |
298 UsageAndQuota::UsageAndQuota( | |
299 int64 usage, | |
300 int64 global_limited_usage, | |
301 int64 quota, | |
302 int64 available_disk_space) | |
303 : usage(usage), | |
304 global_limited_usage(global_limited_usage), | |
305 quota(quota), | |
306 available_disk_space(available_disk_space) { | |
307 } | |
308 | |
309 class UsageAndQuotaCallbackDispatcher | |
310 : public QuotaTask, | |
311 public base::SupportsWeakPtr<UsageAndQuotaCallbackDispatcher> { | |
312 public: | |
313 UsageAndQuotaCallbackDispatcher(QuotaManager* manager) | |
314 : QuotaTask(manager), | |
315 has_usage_(false), | |
316 has_global_limited_usage_(false), | |
317 has_quota_(false), | |
318 has_available_disk_space_(false), | |
319 status_(kQuotaStatusUnknown), | |
320 usage_and_quota_(-1, -1, -1, -1), | |
321 waiting_callbacks_(1) {} | |
322 | |
323 virtual ~UsageAndQuotaCallbackDispatcher() {} | |
324 | |
325 void WaitForResults(const QuotaManager::UsageAndQuotaCallback& callback) { | |
326 callback_ = callback; | |
327 Start(); | |
328 } | |
329 | |
330 void set_usage(int64 usage) { | |
331 usage_and_quota_.usage = usage; | |
332 has_usage_ = true; | |
333 } | |
334 | |
335 void set_global_limited_usage(int64 global_limited_usage) { | |
336 usage_and_quota_.global_limited_usage = global_limited_usage; | |
337 has_global_limited_usage_ = true; | |
338 } | |
339 | |
340 void set_quota(int64 quota) { | |
341 usage_and_quota_.quota = quota; | |
342 has_quota_ = true; | |
343 } | |
344 | |
345 void set_available_disk_space(int64 available_disk_space) { | |
346 usage_and_quota_.available_disk_space = available_disk_space; | |
347 has_available_disk_space_ = true; | |
348 } | |
349 | |
350 UsageCallback GetHostUsageCallback() { | |
351 ++waiting_callbacks_; | |
352 has_usage_ = true; | |
353 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetHostUsage, | |
354 AsWeakPtr()); | |
355 } | |
356 | |
357 UsageCallback GetGlobalLimitedUsageCallback() { | |
358 ++waiting_callbacks_; | |
359 has_global_limited_usage_ = true; | |
360 return base::Bind( | |
361 &UsageAndQuotaCallbackDispatcher::DidGetGlobalLimitedUsage, | |
362 AsWeakPtr()); | |
363 } | |
364 | |
365 QuotaCallback GetQuotaCallback() { | |
366 ++waiting_callbacks_; | |
367 has_quota_ = true; | |
368 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetQuota, | |
369 AsWeakPtr()); | |
370 } | |
371 | |
372 QuotaCallback GetAvailableSpaceCallback() { | |
373 ++waiting_callbacks_; | |
374 has_available_disk_space_ = true; | |
375 return base::Bind(&UsageAndQuotaCallbackDispatcher::DidGetAvailableSpace, | |
376 AsWeakPtr()); | |
377 } | |
378 | |
379 private: | |
380 void DidGetHostUsage(int64 usage) { | |
381 if (status_ == kQuotaStatusUnknown) | |
382 status_ = kQuotaStatusOk; | |
383 usage_and_quota_.usage = usage; | |
384 CheckCompleted(); | |
385 } | |
386 | |
387 void DidGetGlobalLimitedUsage(int64 limited_usage) { | |
388 if (status_ == kQuotaStatusUnknown) | |
389 status_ = kQuotaStatusOk; | |
390 usage_and_quota_.global_limited_usage = limited_usage; | |
391 CheckCompleted(); | |
392 } | |
393 | |
394 void DidGetQuota(QuotaStatusCode status, int64 quota) { | |
395 if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk) | |
396 status_ = status; | |
397 usage_and_quota_.quota = quota; | |
398 CheckCompleted(); | |
399 } | |
400 | |
401 void DidGetAvailableSpace(QuotaStatusCode status, int64 space) { | |
402 DCHECK_GE(space, 0); | |
403 if (status_ == kQuotaStatusUnknown || status_ == kQuotaStatusOk) | |
404 status_ = status; | |
405 usage_and_quota_.available_disk_space = space; | |
406 CheckCompleted(); | |
407 } | |
408 | |
409 virtual void Run() OVERRIDE { | |
410 // We initialize waiting_callbacks to 1 so that we won't run | |
411 // the completion callback until here even some of the callbacks | |
412 // are dispatched synchronously. | |
413 CheckCompleted(); | |
414 } | |
415 | |
416 virtual void Aborted() OVERRIDE { | |
417 callback_.Run(kQuotaErrorAbort, UsageAndQuota()); | |
418 DeleteSoon(); | |
419 } | |
420 | |
421 virtual void Completed() OVERRIDE { | |
422 DCHECK(!has_usage_ || usage_and_quota_.usage >= 0); | |
423 DCHECK(!has_global_limited_usage_ || | |
424 usage_and_quota_.global_limited_usage >= 0); | |
425 DCHECK(!has_quota_ || usage_and_quota_.quota >= 0); | |
426 DCHECK(!has_available_disk_space_ || | |
427 usage_and_quota_.available_disk_space >= 0); | |
428 | |
429 callback_.Run(status_, usage_and_quota_); | |
430 DeleteSoon(); | |
431 } | |
432 | |
433 void CheckCompleted() { | |
434 if (--waiting_callbacks_ <= 0) | |
435 CallCompleted(); | |
436 } | |
437 | |
438 // For sanity checks, they're checked only when DCHECK is on. | |
439 bool has_usage_; | |
440 bool has_global_limited_usage_; | |
441 bool has_quota_; | |
442 bool has_available_disk_space_; | |
443 | |
444 QuotaStatusCode status_; | |
445 UsageAndQuota usage_and_quota_; | |
446 QuotaManager::UsageAndQuotaCallback callback_; | |
447 int waiting_callbacks_; | |
448 | |
449 DISALLOW_COPY_AND_ASSIGN(UsageAndQuotaCallbackDispatcher); | |
450 }; | |
451 | |
452 class QuotaManager::GetUsageInfoTask : public QuotaTask { | |
453 private: | |
454 typedef QuotaManager::GetUsageInfoTask self_type; | |
455 | |
456 public: | |
457 GetUsageInfoTask( | |
458 QuotaManager* manager, | |
459 const GetUsageInfoCallback& callback) | |
460 : QuotaTask(manager), | |
461 callback_(callback), | |
462 weak_factory_(this) { | |
463 } | |
464 | |
465 protected: | |
466 virtual void Run() OVERRIDE { | |
467 remaining_trackers_ = 3; | |
468 // This will populate cached hosts and usage info. | |
469 manager()->GetUsageTracker(kStorageTypeTemporary)->GetGlobalUsage( | |
470 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage, | |
471 weak_factory_.GetWeakPtr(), | |
472 kStorageTypeTemporary)); | |
473 manager()->GetUsageTracker(kStorageTypePersistent)->GetGlobalUsage( | |
474 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage, | |
475 weak_factory_.GetWeakPtr(), | |
476 kStorageTypePersistent)); | |
477 manager()->GetUsageTracker(kStorageTypeSyncable)->GetGlobalUsage( | |
478 base::Bind(&GetUsageInfoTask::DidGetGlobalUsage, | |
479 weak_factory_.GetWeakPtr(), | |
480 kStorageTypeSyncable)); | |
481 } | |
482 | |
483 virtual void Completed() OVERRIDE { | |
484 callback_.Run(entries_); | |
485 DeleteSoon(); | |
486 } | |
487 | |
488 virtual void Aborted() OVERRIDE { | |
489 callback_.Run(UsageInfoEntries()); | |
490 DeleteSoon(); | |
491 } | |
492 | |
493 private: | |
494 void AddEntries(StorageType type, UsageTracker* tracker) { | |
495 std::map<std::string, int64> host_usage; | |
496 tracker->GetCachedHostsUsage(&host_usage); | |
497 for (std::map<std::string, int64>::const_iterator iter = host_usage.begin(); | |
498 iter != host_usage.end(); | |
499 ++iter) { | |
500 entries_.push_back(UsageInfo(iter->first, type, iter->second)); | |
501 } | |
502 if (--remaining_trackers_ == 0) | |
503 CallCompleted(); | |
504 } | |
505 | |
506 void DidGetGlobalUsage(StorageType type, int64, int64) { | |
507 AddEntries(type, manager()->GetUsageTracker(type)); | |
508 } | |
509 | |
510 QuotaManager* manager() const { | |
511 return static_cast<QuotaManager*>(observer()); | |
512 } | |
513 | |
514 GetUsageInfoCallback callback_; | |
515 UsageInfoEntries entries_; | |
516 base::WeakPtrFactory<GetUsageInfoTask> weak_factory_; | |
517 int remaining_trackers_; | |
518 | |
519 DISALLOW_COPY_AND_ASSIGN(GetUsageInfoTask); | |
520 }; | |
521 | |
522 class QuotaManager::OriginDataDeleter : public QuotaTask { | |
523 public: | |
524 OriginDataDeleter(QuotaManager* manager, | |
525 const GURL& origin, | |
526 StorageType type, | |
527 int quota_client_mask, | |
528 const StatusCallback& callback) | |
529 : QuotaTask(manager), | |
530 origin_(origin), | |
531 type_(type), | |
532 quota_client_mask_(quota_client_mask), | |
533 error_count_(0), | |
534 remaining_clients_(-1), | |
535 skipped_clients_(0), | |
536 callback_(callback), | |
537 weak_factory_(this) {} | |
538 | |
539 protected: | |
540 virtual void Run() OVERRIDE { | |
541 error_count_ = 0; | |
542 remaining_clients_ = manager()->clients_.size(); | |
543 for (QuotaClientList::iterator iter = manager()->clients_.begin(); | |
544 iter != manager()->clients_.end(); ++iter) { | |
545 if (quota_client_mask_ & (*iter)->id()) { | |
546 (*iter)->DeleteOriginData( | |
547 origin_, type_, | |
548 base::Bind(&OriginDataDeleter::DidDeleteOriginData, | |
549 weak_factory_.GetWeakPtr())); | |
550 } else { | |
551 ++skipped_clients_; | |
552 if (--remaining_clients_ == 0) | |
553 CallCompleted(); | |
554 } | |
555 } | |
556 } | |
557 | |
558 virtual void Completed() OVERRIDE { | |
559 if (error_count_ == 0) { | |
560 // Only remove the entire origin if we didn't skip any client types. | |
561 if (skipped_clients_ == 0) | |
562 manager()->DeleteOriginFromDatabase(origin_, type_); | |
563 callback_.Run(kQuotaStatusOk); | |
564 } else { | |
565 callback_.Run(kQuotaErrorInvalidModification); | |
566 } | |
567 DeleteSoon(); | |
568 } | |
569 | |
570 virtual void Aborted() OVERRIDE { | |
571 callback_.Run(kQuotaErrorAbort); | |
572 DeleteSoon(); | |
573 } | |
574 | |
575 void DidDeleteOriginData(QuotaStatusCode status) { | |
576 DCHECK_GT(remaining_clients_, 0); | |
577 | |
578 if (status != kQuotaStatusOk) | |
579 ++error_count_; | |
580 | |
581 if (--remaining_clients_ == 0) | |
582 CallCompleted(); | |
583 } | |
584 | |
585 QuotaManager* manager() const { | |
586 return static_cast<QuotaManager*>(observer()); | |
587 } | |
588 | |
589 GURL origin_; | |
590 StorageType type_; | |
591 int quota_client_mask_; | |
592 int error_count_; | |
593 int remaining_clients_; | |
594 int skipped_clients_; | |
595 StatusCallback callback_; | |
596 | |
597 base::WeakPtrFactory<OriginDataDeleter> weak_factory_; | |
598 DISALLOW_COPY_AND_ASSIGN(OriginDataDeleter); | |
599 }; | |
600 | |
601 class QuotaManager::HostDataDeleter : public QuotaTask { | |
602 public: | |
603 HostDataDeleter(QuotaManager* manager, | |
604 const std::string& host, | |
605 StorageType type, | |
606 int quota_client_mask, | |
607 const StatusCallback& callback) | |
608 : QuotaTask(manager), | |
609 host_(host), | |
610 type_(type), | |
611 quota_client_mask_(quota_client_mask), | |
612 error_count_(0), | |
613 remaining_clients_(-1), | |
614 remaining_deleters_(-1), | |
615 callback_(callback), | |
616 weak_factory_(this) {} | |
617 | |
618 protected: | |
619 virtual void Run() OVERRIDE { | |
620 error_count_ = 0; | |
621 remaining_clients_ = manager()->clients_.size(); | |
622 for (QuotaClientList::iterator iter = manager()->clients_.begin(); | |
623 iter != manager()->clients_.end(); ++iter) { | |
624 (*iter)->GetOriginsForHost( | |
625 type_, host_, | |
626 base::Bind(&HostDataDeleter::DidGetOriginsForHost, | |
627 weak_factory_.GetWeakPtr())); | |
628 } | |
629 } | |
630 | |
631 virtual void Completed() OVERRIDE { | |
632 if (error_count_ == 0) { | |
633 callback_.Run(kQuotaStatusOk); | |
634 } else { | |
635 callback_.Run(kQuotaErrorInvalidModification); | |
636 } | |
637 DeleteSoon(); | |
638 } | |
639 | |
640 virtual void Aborted() OVERRIDE { | |
641 callback_.Run(kQuotaErrorAbort); | |
642 DeleteSoon(); | |
643 } | |
644 | |
645 void DidGetOriginsForHost(const std::set<GURL>& origins) { | |
646 DCHECK_GT(remaining_clients_, 0); | |
647 | |
648 origins_.insert(origins.begin(), origins.end()); | |
649 | |
650 if (--remaining_clients_ == 0) { | |
651 if (!origins_.empty()) | |
652 ScheduleOriginsDeletion(); | |
653 else | |
654 CallCompleted(); | |
655 } | |
656 } | |
657 | |
658 void ScheduleOriginsDeletion() { | |
659 remaining_deleters_ = origins_.size(); | |
660 for (std::set<GURL>::const_iterator p = origins_.begin(); | |
661 p != origins_.end(); | |
662 ++p) { | |
663 OriginDataDeleter* deleter = | |
664 new OriginDataDeleter( | |
665 manager(), *p, type_, quota_client_mask_, | |
666 base::Bind(&HostDataDeleter::DidDeleteOriginData, | |
667 weak_factory_.GetWeakPtr())); | |
668 deleter->Start(); | |
669 } | |
670 } | |
671 | |
672 void DidDeleteOriginData(QuotaStatusCode status) { | |
673 DCHECK_GT(remaining_deleters_, 0); | |
674 | |
675 if (status != kQuotaStatusOk) | |
676 ++error_count_; | |
677 | |
678 if (--remaining_deleters_ == 0) | |
679 CallCompleted(); | |
680 } | |
681 | |
682 QuotaManager* manager() const { | |
683 return static_cast<QuotaManager*>(observer()); | |
684 } | |
685 | |
686 std::string host_; | |
687 StorageType type_; | |
688 int quota_client_mask_; | |
689 std::set<GURL> origins_; | |
690 int error_count_; | |
691 int remaining_clients_; | |
692 int remaining_deleters_; | |
693 StatusCallback callback_; | |
694 | |
695 base::WeakPtrFactory<HostDataDeleter> weak_factory_; | |
696 DISALLOW_COPY_AND_ASSIGN(HostDataDeleter); | |
697 }; | |
698 | |
699 class QuotaManager::GetModifiedSinceHelper { | |
700 public: | |
701 bool GetModifiedSinceOnDBThread(StorageType type, | |
702 base::Time modified_since, | |
703 QuotaDatabase* database) { | |
704 DCHECK(database); | |
705 return database->GetOriginsModifiedSince(type, &origins_, modified_since); | |
706 } | |
707 | |
708 void DidGetModifiedSince(QuotaManager* manager, | |
709 const GetOriginsCallback& callback, | |
710 StorageType type, | |
711 bool success) { | |
712 if (!manager) { | |
713 // The operation was aborted. | |
714 callback.Run(std::set<GURL>(), type); | |
715 return; | |
716 } | |
717 manager->DidDatabaseWork(success); | |
718 callback.Run(origins_, type); | |
719 } | |
720 | |
721 private: | |
722 std::set<GURL> origins_; | |
723 }; | |
724 | |
725 class QuotaManager::DumpQuotaTableHelper { | |
726 public: | |
727 bool DumpQuotaTableOnDBThread(QuotaDatabase* database) { | |
728 DCHECK(database); | |
729 return database->DumpQuotaTable( | |
730 new TableCallback(base::Bind(&DumpQuotaTableHelper::AppendEntry, | |
731 base::Unretained(this)))); | |
732 } | |
733 | |
734 void DidDumpQuotaTable(QuotaManager* manager, | |
735 const DumpQuotaTableCallback& callback, | |
736 bool success) { | |
737 if (!manager) { | |
738 // The operation was aborted. | |
739 callback.Run(QuotaTableEntries()); | |
740 return; | |
741 } | |
742 manager->DidDatabaseWork(success); | |
743 callback.Run(entries_); | |
744 } | |
745 | |
746 private: | |
747 typedef QuotaDatabase::QuotaTableCallback TableCallback; | |
748 | |
749 bool AppendEntry(const QuotaTableEntry& entry) { | |
750 entries_.push_back(entry); | |
751 return true; | |
752 } | |
753 | |
754 QuotaTableEntries entries_; | |
755 }; | |
756 | |
757 class QuotaManager::DumpOriginInfoTableHelper { | |
758 public: | |
759 bool DumpOriginInfoTableOnDBThread(QuotaDatabase* database) { | |
760 DCHECK(database); | |
761 return database->DumpOriginInfoTable( | |
762 new TableCallback(base::Bind(&DumpOriginInfoTableHelper::AppendEntry, | |
763 base::Unretained(this)))); | |
764 } | |
765 | |
766 void DidDumpOriginInfoTable(QuotaManager* manager, | |
767 const DumpOriginInfoTableCallback& callback, | |
768 bool success) { | |
769 if (!manager) { | |
770 // The operation was aborted. | |
771 callback.Run(OriginInfoTableEntries()); | |
772 return; | |
773 } | |
774 manager->DidDatabaseWork(success); | |
775 callback.Run(entries_); | |
776 } | |
777 | |
778 private: | |
779 typedef QuotaDatabase::OriginInfoTableCallback TableCallback; | |
780 | |
781 bool AppendEntry(const OriginInfoTableEntry& entry) { | |
782 entries_.push_back(entry); | |
783 return true; | |
784 } | |
785 | |
786 OriginInfoTableEntries entries_; | |
787 }; | |
788 | |
789 // QuotaManager --------------------------------------------------------------- | |
790 | |
791 QuotaManager::QuotaManager(bool is_incognito, | |
792 const base::FilePath& profile_path, | |
793 base::SingleThreadTaskRunner* io_thread, | |
794 base::SequencedTaskRunner* db_thread, | |
795 SpecialStoragePolicy* special_storage_policy) | |
796 : is_incognito_(is_incognito), | |
797 profile_path_(profile_path), | |
798 proxy_(new QuotaManagerProxy( | |
799 this, io_thread)), | |
800 db_disabled_(false), | |
801 eviction_disabled_(false), | |
802 io_thread_(io_thread), | |
803 db_thread_(db_thread), | |
804 temporary_quota_initialized_(false), | |
805 temporary_quota_override_(-1), | |
806 desired_available_space_(-1), | |
807 special_storage_policy_(special_storage_policy), | |
808 weak_factory_(this), | |
809 get_disk_space_fn_(&CallSystemGetAmountOfFreeDiskSpace) { | |
810 } | |
811 | |
812 void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) { | |
813 LazyInitialize(); | |
814 GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback); | |
815 get_usage_info->Start(); | |
816 } | |
817 | |
818 void QuotaManager::GetUsageAndQuotaForWebApps( | |
819 const GURL& origin, | |
820 StorageType type, | |
821 const GetUsageAndQuotaCallback& callback) { | |
822 if (type != kStorageTypeTemporary && | |
823 type != kStorageTypePersistent && | |
824 type != kStorageTypeSyncable) { | |
825 callback.Run(kQuotaErrorNotSupported, 0, 0); | |
826 return; | |
827 } | |
828 | |
829 DCHECK(origin == origin.GetOrigin()); | |
830 LazyInitialize(); | |
831 | |
832 bool unlimited = IsStorageUnlimited(origin, type); | |
833 bool can_query_disk_size = CanQueryDiskSize(origin); | |
834 | |
835 UsageAndQuotaCallbackDispatcher* dispatcher = | |
836 new UsageAndQuotaCallbackDispatcher(this); | |
837 | |
838 UsageAndQuota usage_and_quota; | |
839 if (unlimited) { | |
840 dispatcher->set_quota(kNoLimit); | |
841 } else { | |
842 if (type == kStorageTypeTemporary) { | |
843 GetUsageTracker(type)->GetGlobalLimitedUsage( | |
844 dispatcher->GetGlobalLimitedUsageCallback()); | |
845 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback()); | |
846 } else if (type == kStorageTypePersistent) { | |
847 GetPersistentHostQuota(net::GetHostOrSpecFromURL(origin), | |
848 dispatcher->GetQuotaCallback()); | |
849 } else { | |
850 dispatcher->set_quota(kSyncableStorageDefaultHostQuota); | |
851 } | |
852 } | |
853 | |
854 GetUsageTracker(type)->GetHostUsage(net::GetHostOrSpecFromURL(origin), | |
855 dispatcher->GetHostUsageCallback()); | |
856 | |
857 if (!is_incognito_ && (unlimited || can_query_disk_size)) | |
858 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); | |
859 | |
860 dispatcher->WaitForResults(base::Bind( | |
861 &DispatchUsageAndQuotaForWebApps, | |
862 type, is_incognito_, unlimited, can_query_disk_size, | |
863 callback)); | |
864 } | |
865 | |
866 void QuotaManager::GetUsageAndQuota( | |
867 const GURL& origin, StorageType type, | |
868 const GetUsageAndQuotaCallback& callback) { | |
869 DCHECK(origin == origin.GetOrigin()); | |
870 | |
871 if (IsStorageUnlimited(origin, type)) { | |
872 callback.Run(kQuotaStatusOk, 0, kNoLimit); | |
873 return; | |
874 } | |
875 | |
876 GetUsageAndQuotaForWebApps(origin, type, callback); | |
877 } | |
878 | |
879 void QuotaManager::NotifyStorageAccessed( | |
880 QuotaClient::ID client_id, | |
881 const GURL& origin, StorageType type) { | |
882 DCHECK(origin == origin.GetOrigin()); | |
883 NotifyStorageAccessedInternal(client_id, origin, type, base::Time::Now()); | |
884 } | |
885 | |
886 void QuotaManager::NotifyStorageModified( | |
887 QuotaClient::ID client_id, | |
888 const GURL& origin, StorageType type, int64 delta) { | |
889 DCHECK(origin == origin.GetOrigin()); | |
890 NotifyStorageModifiedInternal(client_id, origin, type, delta, | |
891 base::Time::Now()); | |
892 } | |
893 | |
894 void QuotaManager::NotifyOriginInUse(const GURL& origin) { | |
895 DCHECK(io_thread_->BelongsToCurrentThread()); | |
896 origins_in_use_[origin]++; | |
897 } | |
898 | |
899 void QuotaManager::NotifyOriginNoLongerInUse(const GURL& origin) { | |
900 DCHECK(io_thread_->BelongsToCurrentThread()); | |
901 DCHECK(IsOriginInUse(origin)); | |
902 int& count = origins_in_use_[origin]; | |
903 if (--count == 0) | |
904 origins_in_use_.erase(origin); | |
905 } | |
906 | |
907 void QuotaManager::SetUsageCacheEnabled(QuotaClient::ID client_id, | |
908 const GURL& origin, | |
909 StorageType type, | |
910 bool enabled) { | |
911 LazyInitialize(); | |
912 GetUsageTracker(type)->SetUsageCacheEnabled(client_id, origin, enabled); | |
913 } | |
914 | |
915 void QuotaManager::DeleteOriginData( | |
916 const GURL& origin, StorageType type, int quota_client_mask, | |
917 const StatusCallback& callback) { | |
918 LazyInitialize(); | |
919 | |
920 if (origin.is_empty() || clients_.empty()) { | |
921 callback.Run(kQuotaStatusOk); | |
922 return; | |
923 } | |
924 | |
925 DCHECK(origin == origin.GetOrigin()); | |
926 OriginDataDeleter* deleter = | |
927 new OriginDataDeleter(this, origin, type, quota_client_mask, callback); | |
928 deleter->Start(); | |
929 } | |
930 | |
931 void QuotaManager::DeleteHostData(const std::string& host, | |
932 StorageType type, | |
933 int quota_client_mask, | |
934 const StatusCallback& callback) { | |
935 LazyInitialize(); | |
936 | |
937 if (host.empty() || clients_.empty()) { | |
938 callback.Run(kQuotaStatusOk); | |
939 return; | |
940 } | |
941 | |
942 HostDataDeleter* deleter = | |
943 new HostDataDeleter(this, host, type, quota_client_mask, callback); | |
944 deleter->Start(); | |
945 } | |
946 | |
947 void QuotaManager::GetAvailableSpace(const AvailableSpaceCallback& callback) { | |
948 if (!available_space_callbacks_.Add(callback)) | |
949 return; | |
950 | |
951 PostTaskAndReplyWithResult( | |
952 db_thread_, | |
953 FROM_HERE, | |
954 base::Bind(get_disk_space_fn_, profile_path_), | |
955 base::Bind(&QuotaManager::DidGetAvailableSpace, | |
956 weak_factory_.GetWeakPtr())); | |
957 } | |
958 | |
959 void QuotaManager::GetTemporaryGlobalQuota(const QuotaCallback& callback) { | |
960 LazyInitialize(); | |
961 if (!temporary_quota_initialized_) { | |
962 db_initialization_callbacks_.Add(base::Bind( | |
963 &QuotaManager::GetTemporaryGlobalQuota, | |
964 weak_factory_.GetWeakPtr(), callback)); | |
965 return; | |
966 } | |
967 | |
968 if (temporary_quota_override_ > 0) { | |
969 callback.Run(kQuotaStatusOk, temporary_quota_override_); | |
970 return; | |
971 } | |
972 | |
973 UsageAndQuotaCallbackDispatcher* dispatcher = | |
974 new UsageAndQuotaCallbackDispatcher(this); | |
975 GetUsageTracker(kStorageTypeTemporary)-> | |
976 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback()); | |
977 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); | |
978 dispatcher->WaitForResults( | |
979 base::Bind(&DispatchTemporaryGlobalQuotaCallback, callback)); | |
980 } | |
981 | |
982 void QuotaManager::SetTemporaryGlobalOverrideQuota( | |
983 int64 new_quota, const QuotaCallback& callback) { | |
984 LazyInitialize(); | |
985 | |
986 if (new_quota < 0) { | |
987 if (!callback.is_null()) | |
988 callback.Run(kQuotaErrorInvalidModification, -1); | |
989 return; | |
990 } | |
991 | |
992 if (db_disabled_) { | |
993 if (callback.is_null()) | |
994 callback.Run(kQuotaErrorInvalidAccess, -1); | |
995 return; | |
996 } | |
997 | |
998 int64* new_quota_ptr = new int64(new_quota); | |
999 PostTaskAndReplyWithResultForDBThread( | |
1000 FROM_HERE, | |
1001 base::Bind(&SetTemporaryGlobalOverrideQuotaOnDBThread, | |
1002 base::Unretained(new_quota_ptr)), | |
1003 base::Bind(&QuotaManager::DidSetTemporaryGlobalOverrideQuota, | |
1004 weak_factory_.GetWeakPtr(), | |
1005 callback, | |
1006 base::Owned(new_quota_ptr))); | |
1007 } | |
1008 | |
1009 void QuotaManager::GetPersistentHostQuota(const std::string& host, | |
1010 const QuotaCallback& callback) { | |
1011 LazyInitialize(); | |
1012 if (host.empty()) { | |
1013 // This could happen if we are called on file:///. | |
1014 // TODO(kinuko) We may want to respect --allow-file-access-from-files | |
1015 // command line switch. | |
1016 callback.Run(kQuotaStatusOk, 0); | |
1017 return; | |
1018 } | |
1019 | |
1020 if (!persistent_host_quota_callbacks_.Add(host, callback)) | |
1021 return; | |
1022 | |
1023 int64* quota_ptr = new int64(0); | |
1024 PostTaskAndReplyWithResultForDBThread( | |
1025 FROM_HERE, | |
1026 base::Bind(&GetPersistentHostQuotaOnDBThread, | |
1027 host, | |
1028 base::Unretained(quota_ptr)), | |
1029 base::Bind(&QuotaManager::DidGetPersistentHostQuota, | |
1030 weak_factory_.GetWeakPtr(), | |
1031 host, | |
1032 base::Owned(quota_ptr))); | |
1033 } | |
1034 | |
1035 void QuotaManager::SetPersistentHostQuota(const std::string& host, | |
1036 int64 new_quota, | |
1037 const QuotaCallback& callback) { | |
1038 LazyInitialize(); | |
1039 if (host.empty()) { | |
1040 // This could happen if we are called on file:///. | |
1041 callback.Run(kQuotaErrorNotSupported, 0); | |
1042 return; | |
1043 } | |
1044 if (new_quota < 0) { | |
1045 callback.Run(kQuotaErrorInvalidModification, -1); | |
1046 return; | |
1047 } | |
1048 | |
1049 if (db_disabled_) { | |
1050 callback.Run(kQuotaErrorInvalidAccess, -1); | |
1051 return; | |
1052 } | |
1053 | |
1054 int64* new_quota_ptr = new int64(new_quota); | |
1055 PostTaskAndReplyWithResultForDBThread( | |
1056 FROM_HERE, | |
1057 base::Bind(&SetPersistentHostQuotaOnDBThread, | |
1058 host, | |
1059 base::Unretained(new_quota_ptr)), | |
1060 base::Bind(&QuotaManager::DidSetPersistentHostQuota, | |
1061 weak_factory_.GetWeakPtr(), | |
1062 host, | |
1063 callback, | |
1064 base::Owned(new_quota_ptr))); | |
1065 } | |
1066 | |
1067 void QuotaManager::GetGlobalUsage(StorageType type, | |
1068 const GlobalUsageCallback& callback) { | |
1069 LazyInitialize(); | |
1070 GetUsageTracker(type)->GetGlobalUsage(callback); | |
1071 } | |
1072 | |
1073 void QuotaManager::GetHostUsage(const std::string& host, | |
1074 StorageType type, | |
1075 const UsageCallback& callback) { | |
1076 LazyInitialize(); | |
1077 GetUsageTracker(type)->GetHostUsage(host, callback); | |
1078 } | |
1079 | |
1080 void QuotaManager::GetStatistics( | |
1081 std::map<std::string, std::string>* statistics) { | |
1082 DCHECK(statistics); | |
1083 if (temporary_storage_evictor_) { | |
1084 std::map<std::string, int64> stats; | |
1085 temporary_storage_evictor_->GetStatistics(&stats); | |
1086 for (std::map<std::string, int64>::iterator p = stats.begin(); | |
1087 p != stats.end(); | |
1088 ++p) | |
1089 (*statistics)[p->first] = base::Int64ToString(p->second); | |
1090 } | |
1091 } | |
1092 | |
1093 bool QuotaManager::IsStorageUnlimited(const GURL& origin, | |
1094 StorageType type) const { | |
1095 // For syncable storage we should always enforce quota (since the | |
1096 // quota must be capped by the server limit). | |
1097 if (type == kStorageTypeSyncable) | |
1098 return false; | |
1099 return special_storage_policy_.get() && | |
1100 special_storage_policy_->IsStorageUnlimited(origin); | |
1101 } | |
1102 | |
1103 void QuotaManager::GetOriginsModifiedSince(StorageType type, | |
1104 base::Time modified_since, | |
1105 const GetOriginsCallback& callback) { | |
1106 LazyInitialize(); | |
1107 GetModifiedSinceHelper* helper = new GetModifiedSinceHelper; | |
1108 PostTaskAndReplyWithResultForDBThread( | |
1109 FROM_HERE, | |
1110 base::Bind(&GetModifiedSinceHelper::GetModifiedSinceOnDBThread, | |
1111 base::Unretained(helper), | |
1112 type, | |
1113 modified_since), | |
1114 base::Bind(&GetModifiedSinceHelper::DidGetModifiedSince, | |
1115 base::Owned(helper), | |
1116 weak_factory_.GetWeakPtr(), | |
1117 callback, | |
1118 type)); | |
1119 } | |
1120 | |
1121 bool QuotaManager::ResetUsageTracker(StorageType type) { | |
1122 DCHECK(GetUsageTracker(type)); | |
1123 if (GetUsageTracker(type)->IsWorking()) | |
1124 return false; | |
1125 switch (type) { | |
1126 case kStorageTypeTemporary: | |
1127 temporary_usage_tracker_.reset( | |
1128 new UsageTracker(clients_, kStorageTypeTemporary, | |
1129 special_storage_policy_)); | |
1130 return true; | |
1131 case kStorageTypePersistent: | |
1132 persistent_usage_tracker_.reset( | |
1133 new UsageTracker(clients_, kStorageTypePersistent, | |
1134 special_storage_policy_)); | |
1135 return true; | |
1136 case kStorageTypeSyncable: | |
1137 syncable_usage_tracker_.reset( | |
1138 new UsageTracker(clients_, kStorageTypeSyncable, | |
1139 special_storage_policy_)); | |
1140 return true; | |
1141 default: | |
1142 NOTREACHED(); | |
1143 } | |
1144 return true; | |
1145 } | |
1146 | |
1147 QuotaManager::~QuotaManager() { | |
1148 proxy_->manager_ = NULL; | |
1149 std::for_each(clients_.begin(), clients_.end(), | |
1150 std::mem_fun(&QuotaClient::OnQuotaManagerDestroyed)); | |
1151 if (database_) | |
1152 db_thread_->DeleteSoon(FROM_HERE, database_.release()); | |
1153 } | |
1154 | |
1155 QuotaManager::EvictionContext::EvictionContext() | |
1156 : evicted_type(kStorageTypeUnknown) { | |
1157 } | |
1158 | |
1159 QuotaManager::EvictionContext::~EvictionContext() { | |
1160 } | |
1161 | |
1162 void QuotaManager::LazyInitialize() { | |
1163 DCHECK(io_thread_->BelongsToCurrentThread()); | |
1164 if (database_) { | |
1165 // Initialization seems to be done already. | |
1166 return; | |
1167 } | |
1168 | |
1169 // Use an empty path to open an in-memory only databse for incognito. | |
1170 database_.reset(new QuotaDatabase(is_incognito_ ? base::FilePath() : | |
1171 profile_path_.AppendASCII(kDatabaseName))); | |
1172 | |
1173 temporary_usage_tracker_.reset( | |
1174 new UsageTracker(clients_, kStorageTypeTemporary, | |
1175 special_storage_policy_)); | |
1176 persistent_usage_tracker_.reset( | |
1177 new UsageTracker(clients_, kStorageTypePersistent, | |
1178 special_storage_policy_)); | |
1179 syncable_usage_tracker_.reset( | |
1180 new UsageTracker(clients_, kStorageTypeSyncable, | |
1181 special_storage_policy_)); | |
1182 | |
1183 int64* temporary_quota_override = new int64(-1); | |
1184 int64* desired_available_space = new int64(-1); | |
1185 PostTaskAndReplyWithResultForDBThread( | |
1186 FROM_HERE, | |
1187 base::Bind(&InitializeOnDBThread, | |
1188 base::Unretained(temporary_quota_override), | |
1189 base::Unretained(desired_available_space)), | |
1190 base::Bind(&QuotaManager::DidInitialize, | |
1191 weak_factory_.GetWeakPtr(), | |
1192 base::Owned(temporary_quota_override), | |
1193 base::Owned(desired_available_space))); | |
1194 } | |
1195 | |
1196 void QuotaManager::RegisterClient(QuotaClient* client) { | |
1197 DCHECK(!database_.get()); | |
1198 clients_.push_back(client); | |
1199 } | |
1200 | |
1201 UsageTracker* QuotaManager::GetUsageTracker(StorageType type) const { | |
1202 switch (type) { | |
1203 case kStorageTypeTemporary: | |
1204 return temporary_usage_tracker_.get(); | |
1205 case kStorageTypePersistent: | |
1206 return persistent_usage_tracker_.get(); | |
1207 case kStorageTypeSyncable: | |
1208 return syncable_usage_tracker_.get(); | |
1209 default: | |
1210 NOTREACHED(); | |
1211 } | |
1212 return NULL; | |
1213 } | |
1214 | |
1215 void QuotaManager::GetCachedOrigins( | |
1216 StorageType type, std::set<GURL>* origins) { | |
1217 DCHECK(origins); | |
1218 LazyInitialize(); | |
1219 DCHECK(GetUsageTracker(type)); | |
1220 GetUsageTracker(type)->GetCachedOrigins(origins); | |
1221 } | |
1222 | |
1223 void QuotaManager::NotifyStorageAccessedInternal( | |
1224 QuotaClient::ID client_id, | |
1225 const GURL& origin, StorageType type, | |
1226 base::Time accessed_time) { | |
1227 LazyInitialize(); | |
1228 if (type == kStorageTypeTemporary && !lru_origin_callback_.is_null()) { | |
1229 // Record the accessed origins while GetLRUOrigin task is runing | |
1230 // to filter out them from eviction. | |
1231 access_notified_origins_.insert(origin); | |
1232 } | |
1233 | |
1234 if (db_disabled_) | |
1235 return; | |
1236 PostTaskAndReplyWithResultForDBThread( | |
1237 FROM_HERE, | |
1238 base::Bind(&UpdateAccessTimeOnDBThread, origin, type, accessed_time), | |
1239 base::Bind(&QuotaManager::DidDatabaseWork, | |
1240 weak_factory_.GetWeakPtr())); | |
1241 } | |
1242 | |
1243 void QuotaManager::NotifyStorageModifiedInternal( | |
1244 QuotaClient::ID client_id, | |
1245 const GURL& origin, | |
1246 StorageType type, | |
1247 int64 delta, | |
1248 base::Time modified_time) { | |
1249 LazyInitialize(); | |
1250 GetUsageTracker(type)->UpdateUsageCache(client_id, origin, delta); | |
1251 | |
1252 PostTaskAndReplyWithResultForDBThread( | |
1253 FROM_HERE, | |
1254 base::Bind(&UpdateModifiedTimeOnDBThread, origin, type, modified_time), | |
1255 base::Bind(&QuotaManager::DidDatabaseWork, | |
1256 weak_factory_.GetWeakPtr())); | |
1257 } | |
1258 | |
1259 void QuotaManager::DumpQuotaTable(const DumpQuotaTableCallback& callback) { | |
1260 DumpQuotaTableHelper* helper = new DumpQuotaTableHelper; | |
1261 PostTaskAndReplyWithResultForDBThread( | |
1262 FROM_HERE, | |
1263 base::Bind(&DumpQuotaTableHelper::DumpQuotaTableOnDBThread, | |
1264 base::Unretained(helper)), | |
1265 base::Bind(&DumpQuotaTableHelper::DidDumpQuotaTable, | |
1266 base::Owned(helper), | |
1267 weak_factory_.GetWeakPtr(), | |
1268 callback)); | |
1269 } | |
1270 | |
1271 void QuotaManager::DumpOriginInfoTable( | |
1272 const DumpOriginInfoTableCallback& callback) { | |
1273 DumpOriginInfoTableHelper* helper = new DumpOriginInfoTableHelper; | |
1274 PostTaskAndReplyWithResultForDBThread( | |
1275 FROM_HERE, | |
1276 base::Bind(&DumpOriginInfoTableHelper::DumpOriginInfoTableOnDBThread, | |
1277 base::Unretained(helper)), | |
1278 base::Bind(&DumpOriginInfoTableHelper::DidDumpOriginInfoTable, | |
1279 base::Owned(helper), | |
1280 weak_factory_.GetWeakPtr(), | |
1281 callback)); | |
1282 } | |
1283 | |
1284 void QuotaManager::StartEviction() { | |
1285 DCHECK(!temporary_storage_evictor_.get()); | |
1286 temporary_storage_evictor_.reset(new QuotaTemporaryStorageEvictor( | |
1287 this, kEvictionIntervalInMilliSeconds)); | |
1288 if (desired_available_space_ >= 0) | |
1289 temporary_storage_evictor_->set_min_available_disk_space_to_start_eviction( | |
1290 desired_available_space_); | |
1291 temporary_storage_evictor_->Start(); | |
1292 } | |
1293 | |
1294 void QuotaManager::DeleteOriginFromDatabase( | |
1295 const GURL& origin, StorageType type) { | |
1296 LazyInitialize(); | |
1297 if (db_disabled_) | |
1298 return; | |
1299 | |
1300 PostTaskAndReplyWithResultForDBThread( | |
1301 FROM_HERE, | |
1302 base::Bind(&DeleteOriginInfoOnDBThread, origin, type), | |
1303 base::Bind(&QuotaManager::DidDatabaseWork, | |
1304 weak_factory_.GetWeakPtr())); | |
1305 } | |
1306 | |
1307 void QuotaManager::DidOriginDataEvicted(QuotaStatusCode status) { | |
1308 DCHECK(io_thread_->BelongsToCurrentThread()); | |
1309 | |
1310 // We only try evict origins that are not in use, so basically | |
1311 // deletion attempt for eviction should not fail. Let's record | |
1312 // the origin if we get error and exclude it from future eviction | |
1313 // if the error happens consistently (> kThresholdOfErrorsToBeBlacklisted). | |
1314 if (status != kQuotaStatusOk) | |
1315 origins_in_error_[eviction_context_.evicted_origin]++; | |
1316 | |
1317 eviction_context_.evict_origin_data_callback.Run(status); | |
1318 eviction_context_.evict_origin_data_callback.Reset(); | |
1319 } | |
1320 | |
1321 void QuotaManager::ReportHistogram() { | |
1322 GetGlobalUsage(kStorageTypeTemporary, | |
1323 base::Bind( | |
1324 &QuotaManager::DidGetTemporaryGlobalUsageForHistogram, | |
1325 weak_factory_.GetWeakPtr())); | |
1326 GetGlobalUsage(kStorageTypePersistent, | |
1327 base::Bind( | |
1328 &QuotaManager::DidGetPersistentGlobalUsageForHistogram, | |
1329 weak_factory_.GetWeakPtr())); | |
1330 } | |
1331 | |
1332 void QuotaManager::DidGetTemporaryGlobalUsageForHistogram( | |
1333 int64 usage, | |
1334 int64 unlimited_usage) { | |
1335 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfTemporaryStorage", usage); | |
1336 | |
1337 std::set<GURL> origins; | |
1338 GetCachedOrigins(kStorageTypeTemporary, &origins); | |
1339 | |
1340 size_t num_origins = origins.size(); | |
1341 size_t protected_origins = 0; | |
1342 size_t unlimited_origins = 0; | |
1343 CountOriginType(origins, special_storage_policy_, | |
1344 &protected_origins, &unlimited_origins); | |
1345 | |
1346 UMA_HISTOGRAM_COUNTS("Quota.NumberOfTemporaryStorageOrigins", | |
1347 num_origins); | |
1348 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedTemporaryStorageOrigins", | |
1349 protected_origins); | |
1350 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedTemporaryStorageOrigins", | |
1351 unlimited_origins); | |
1352 } | |
1353 | |
1354 void QuotaManager::DidGetPersistentGlobalUsageForHistogram( | |
1355 int64 usage, | |
1356 int64 unlimited_usage) { | |
1357 UMA_HISTOGRAM_MBYTES("Quota.GlobalUsageOfPersistentStorage", usage); | |
1358 | |
1359 std::set<GURL> origins; | |
1360 GetCachedOrigins(kStorageTypePersistent, &origins); | |
1361 | |
1362 size_t num_origins = origins.size(); | |
1363 size_t protected_origins = 0; | |
1364 size_t unlimited_origins = 0; | |
1365 CountOriginType(origins, special_storage_policy_, | |
1366 &protected_origins, &unlimited_origins); | |
1367 | |
1368 UMA_HISTOGRAM_COUNTS("Quota.NumberOfPersistentStorageOrigins", | |
1369 num_origins); | |
1370 UMA_HISTOGRAM_COUNTS("Quota.NumberOfProtectedPersistentStorageOrigins", | |
1371 protected_origins); | |
1372 UMA_HISTOGRAM_COUNTS("Quota.NumberOfUnlimitedPersistentStorageOrigins", | |
1373 unlimited_origins); | |
1374 } | |
1375 | |
1376 void QuotaManager::GetLRUOrigin( | |
1377 StorageType type, | |
1378 const GetLRUOriginCallback& callback) { | |
1379 LazyInitialize(); | |
1380 // This must not be called while there's an in-flight task. | |
1381 DCHECK(lru_origin_callback_.is_null()); | |
1382 lru_origin_callback_ = callback; | |
1383 if (db_disabled_) { | |
1384 lru_origin_callback_.Run(GURL()); | |
1385 lru_origin_callback_.Reset(); | |
1386 return; | |
1387 } | |
1388 | |
1389 std::set<GURL>* exceptions = new std::set<GURL>; | |
1390 for (std::map<GURL, int>::const_iterator p = origins_in_use_.begin(); | |
1391 p != origins_in_use_.end(); | |
1392 ++p) { | |
1393 if (p->second > 0) | |
1394 exceptions->insert(p->first); | |
1395 } | |
1396 for (std::map<GURL, int>::const_iterator p = origins_in_error_.begin(); | |
1397 p != origins_in_error_.end(); | |
1398 ++p) { | |
1399 if (p->second > QuotaManager::kThresholdOfErrorsToBeBlacklisted) | |
1400 exceptions->insert(p->first); | |
1401 } | |
1402 | |
1403 GURL* url = new GURL; | |
1404 PostTaskAndReplyWithResultForDBThread( | |
1405 FROM_HERE, | |
1406 base::Bind(&GetLRUOriginOnDBThread, | |
1407 type, | |
1408 base::Owned(exceptions), | |
1409 special_storage_policy_, | |
1410 base::Unretained(url)), | |
1411 base::Bind(&QuotaManager::DidGetLRUOrigin, | |
1412 weak_factory_.GetWeakPtr(), | |
1413 base::Owned(url))); | |
1414 } | |
1415 | |
1416 void QuotaManager::EvictOriginData( | |
1417 const GURL& origin, | |
1418 StorageType type, | |
1419 const EvictOriginDataCallback& callback) { | |
1420 DCHECK(io_thread_->BelongsToCurrentThread()); | |
1421 DCHECK_EQ(type, kStorageTypeTemporary); | |
1422 | |
1423 eviction_context_.evicted_origin = origin; | |
1424 eviction_context_.evicted_type = type; | |
1425 eviction_context_.evict_origin_data_callback = callback; | |
1426 | |
1427 DeleteOriginData(origin, type, QuotaClient::kAllClientsMask, | |
1428 base::Bind(&QuotaManager::DidOriginDataEvicted, | |
1429 weak_factory_.GetWeakPtr())); | |
1430 } | |
1431 | |
1432 void QuotaManager::GetUsageAndQuotaForEviction( | |
1433 const UsageAndQuotaCallback& callback) { | |
1434 DCHECK(io_thread_->BelongsToCurrentThread()); | |
1435 LazyInitialize(); | |
1436 | |
1437 UsageAndQuotaCallbackDispatcher* dispatcher = | |
1438 new UsageAndQuotaCallbackDispatcher(this); | |
1439 GetUsageTracker(kStorageTypeTemporary)-> | |
1440 GetGlobalLimitedUsage(dispatcher->GetGlobalLimitedUsageCallback()); | |
1441 GetTemporaryGlobalQuota(dispatcher->GetQuotaCallback()); | |
1442 GetAvailableSpace(dispatcher->GetAvailableSpaceCallback()); | |
1443 dispatcher->WaitForResults(callback); | |
1444 } | |
1445 | |
1446 void QuotaManager::DidSetTemporaryGlobalOverrideQuota( | |
1447 const QuotaCallback& callback, | |
1448 const int64* new_quota, | |
1449 bool success) { | |
1450 QuotaStatusCode status = kQuotaErrorInvalidAccess; | |
1451 DidDatabaseWork(success); | |
1452 if (success) { | |
1453 temporary_quota_override_ = *new_quota; | |
1454 status = kQuotaStatusOk; | |
1455 } | |
1456 | |
1457 if (callback.is_null()) | |
1458 return; | |
1459 | |
1460 callback.Run(status, *new_quota); | |
1461 } | |
1462 | |
1463 void QuotaManager::DidGetPersistentHostQuota(const std::string& host, | |
1464 const int64* quota, | |
1465 bool success) { | |
1466 DidDatabaseWork(success); | |
1467 persistent_host_quota_callbacks_.Run( | |
1468 host, MakeTuple(kQuotaStatusOk, *quota)); | |
1469 } | |
1470 | |
1471 void QuotaManager::DidSetPersistentHostQuota(const std::string& host, | |
1472 const QuotaCallback& callback, | |
1473 const int64* new_quota, | |
1474 bool success) { | |
1475 DidDatabaseWork(success); | |
1476 callback.Run(success ? kQuotaStatusOk : kQuotaErrorInvalidAccess, *new_quota); | |
1477 } | |
1478 | |
1479 void QuotaManager::DidInitialize(int64* temporary_quota_override, | |
1480 int64* desired_available_space, | |
1481 bool success) { | |
1482 temporary_quota_override_ = *temporary_quota_override; | |
1483 desired_available_space_ = *desired_available_space; | |
1484 temporary_quota_initialized_ = true; | |
1485 DidDatabaseWork(success); | |
1486 | |
1487 histogram_timer_.Start(FROM_HERE, | |
1488 base::TimeDelta::FromMilliseconds( | |
1489 kReportHistogramInterval), | |
1490 this, &QuotaManager::ReportHistogram); | |
1491 | |
1492 db_initialization_callbacks_.Run(MakeTuple()); | |
1493 GetTemporaryGlobalQuota( | |
1494 base::Bind(&QuotaManager::DidGetInitialTemporaryGlobalQuota, | |
1495 weak_factory_.GetWeakPtr())); | |
1496 } | |
1497 | |
1498 void QuotaManager::DidGetLRUOrigin(const GURL* origin, | |
1499 bool success) { | |
1500 DidDatabaseWork(success); | |
1501 // Make sure the returned origin is (still) not in the origin_in_use_ set | |
1502 // and has not been accessed since we posted the task. | |
1503 if (origins_in_use_.find(*origin) != origins_in_use_.end() || | |
1504 access_notified_origins_.find(*origin) != access_notified_origins_.end()) | |
1505 lru_origin_callback_.Run(GURL()); | |
1506 else | |
1507 lru_origin_callback_.Run(*origin); | |
1508 access_notified_origins_.clear(); | |
1509 lru_origin_callback_.Reset(); | |
1510 } | |
1511 | |
1512 void QuotaManager::DidGetInitialTemporaryGlobalQuota( | |
1513 QuotaStatusCode status, int64 quota_unused) { | |
1514 if (eviction_disabled_) | |
1515 return; | |
1516 | |
1517 std::set<GURL>* origins = new std::set<GURL>; | |
1518 temporary_usage_tracker_->GetCachedOrigins(origins); | |
1519 // This will call the StartEviction() when initial origin registration | |
1520 // is completed. | |
1521 PostTaskAndReplyWithResultForDBThread( | |
1522 FROM_HERE, | |
1523 base::Bind(&InitializeTemporaryOriginsInfoOnDBThread, | |
1524 base::Owned(origins)), | |
1525 base::Bind(&QuotaManager::DidInitializeTemporaryOriginsInfo, | |
1526 weak_factory_.GetWeakPtr())); | |
1527 } | |
1528 | |
1529 void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) { | |
1530 DidDatabaseWork(success); | |
1531 if (success) | |
1532 StartEviction(); | |
1533 } | |
1534 | |
1535 void QuotaManager::DidGetAvailableSpace(int64 space) { | |
1536 available_space_callbacks_.Run(MakeTuple(kQuotaStatusOk, space)); | |
1537 } | |
1538 | |
1539 void QuotaManager::DidDatabaseWork(bool success) { | |
1540 db_disabled_ = !success; | |
1541 } | |
1542 | |
1543 void QuotaManager::DeleteOnCorrectThread() const { | |
1544 if (!io_thread_->BelongsToCurrentThread() && | |
1545 io_thread_->DeleteSoon(FROM_HERE, this)) { | |
1546 return; | |
1547 } | |
1548 delete this; | |
1549 } | |
1550 | |
1551 void QuotaManager::PostTaskAndReplyWithResultForDBThread( | |
1552 const tracked_objects::Location& from_here, | |
1553 const base::Callback<bool(QuotaDatabase*)>& task, | |
1554 const base::Callback<void(bool)>& reply) { | |
1555 // Deleting manager will post another task to DB thread to delete | |
1556 // |database_|, therefore we can be sure that database_ is alive when this | |
1557 // task runs. | |
1558 base::PostTaskAndReplyWithResult( | |
1559 db_thread_, | |
1560 from_here, | |
1561 base::Bind(task, base::Unretained(database_.get())), | |
1562 reply); | |
1563 } | |
1564 | |
1565 // QuotaManagerProxy ---------------------------------------------------------- | |
1566 | |
1567 void QuotaManagerProxy::RegisterClient(QuotaClient* client) { | |
1568 if (!io_thread_->BelongsToCurrentThread() && | |
1569 io_thread_->PostTask( | |
1570 FROM_HERE, | |
1571 base::Bind(&QuotaManagerProxy::RegisterClient, this, client))) { | |
1572 return; | |
1573 } | |
1574 | |
1575 if (manager_) | |
1576 manager_->RegisterClient(client); | |
1577 else | |
1578 client->OnQuotaManagerDestroyed(); | |
1579 } | |
1580 | |
1581 void QuotaManagerProxy::NotifyStorageAccessed( | |
1582 QuotaClient::ID client_id, | |
1583 const GURL& origin, | |
1584 StorageType type) { | |
1585 if (!io_thread_->BelongsToCurrentThread()) { | |
1586 io_thread_->PostTask( | |
1587 FROM_HERE, | |
1588 base::Bind(&QuotaManagerProxy::NotifyStorageAccessed, this, client_id, | |
1589 origin, type)); | |
1590 return; | |
1591 } | |
1592 | |
1593 if (manager_) | |
1594 manager_->NotifyStorageAccessed(client_id, origin, type); | |
1595 } | |
1596 | |
1597 void QuotaManagerProxy::NotifyStorageModified( | |
1598 QuotaClient::ID client_id, | |
1599 const GURL& origin, | |
1600 StorageType type, | |
1601 int64 delta) { | |
1602 if (!io_thread_->BelongsToCurrentThread()) { | |
1603 io_thread_->PostTask( | |
1604 FROM_HERE, | |
1605 base::Bind(&QuotaManagerProxy::NotifyStorageModified, this, client_id, | |
1606 origin, type, delta)); | |
1607 return; | |
1608 } | |
1609 | |
1610 if (manager_) | |
1611 manager_->NotifyStorageModified(client_id, origin, type, delta); | |
1612 } | |
1613 | |
1614 void QuotaManagerProxy::NotifyOriginInUse( | |
1615 const GURL& origin) { | |
1616 if (!io_thread_->BelongsToCurrentThread()) { | |
1617 io_thread_->PostTask( | |
1618 FROM_HERE, | |
1619 base::Bind(&QuotaManagerProxy::NotifyOriginInUse, this, origin)); | |
1620 return; | |
1621 } | |
1622 | |
1623 if (manager_) | |
1624 manager_->NotifyOriginInUse(origin); | |
1625 } | |
1626 | |
1627 void QuotaManagerProxy::NotifyOriginNoLongerInUse( | |
1628 const GURL& origin) { | |
1629 if (!io_thread_->BelongsToCurrentThread()) { | |
1630 io_thread_->PostTask( | |
1631 FROM_HERE, | |
1632 base::Bind(&QuotaManagerProxy::NotifyOriginNoLongerInUse, this, | |
1633 origin)); | |
1634 return; | |
1635 } | |
1636 if (manager_) | |
1637 manager_->NotifyOriginNoLongerInUse(origin); | |
1638 } | |
1639 | |
1640 void QuotaManagerProxy::SetUsageCacheEnabled(QuotaClient::ID client_id, | |
1641 const GURL& origin, | |
1642 StorageType type, | |
1643 bool enabled) { | |
1644 if (!io_thread_->BelongsToCurrentThread()) { | |
1645 io_thread_->PostTask( | |
1646 FROM_HERE, | |
1647 base::Bind(&QuotaManagerProxy::SetUsageCacheEnabled, this, | |
1648 client_id, origin, type, enabled)); | |
1649 return; | |
1650 } | |
1651 if (manager_) | |
1652 manager_->SetUsageCacheEnabled(client_id, origin, type, enabled); | |
1653 } | |
1654 | |
1655 QuotaManager* QuotaManagerProxy::quota_manager() const { | |
1656 DCHECK(!io_thread_ || io_thread_->BelongsToCurrentThread()); | |
1657 return manager_; | |
1658 } | |
1659 | |
1660 QuotaManagerProxy::QuotaManagerProxy( | |
1661 QuotaManager* manager, base::SingleThreadTaskRunner* io_thread) | |
1662 : manager_(manager), io_thread_(io_thread) { | |
1663 } | |
1664 | |
1665 QuotaManagerProxy::~QuotaManagerProxy() { | |
1666 } | |
1667 | |
1668 } // namespace quota | |
OLD | NEW |