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