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 38namespace quota { 39 40namespace { 41 42const int64 kMBytes = 1024 * 1024; 43const int kMinutesInMilliSeconds = 60 * 1000; 44 45const int64 kReportHistogramInterval = 60 * 60 * 1000; // 1 hour 46const 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. 53const int64 QuotaManager::kIncognitoDefaultQuotaLimit = 100 * kMBytes; 54 55const int64 QuotaManager::kNoLimit = kint64max; 56 57const 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. 63const int64 QuotaManager::kPerHostPersistentQuotaLimit = 10 * 1024 * kMBytes; 64 65const char QuotaManager::kDatabaseName[] = "QuotaManager"; 66 67const 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) 74int64 QuotaManager::kMinimumPreserveForSystem = 1024 * kMBytes; 75 76const 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. 82int64 QuotaManager::kSyncableStorageDefaultHostQuota = 500 * kMBytes; 83 84namespace { 85 86void 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 106bool 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 117bool 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 125bool 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 135bool 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 146bool 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 156bool DeleteOriginInfoOnDBThread(const GURL& origin, 157 StorageType type, 158 QuotaDatabase* database) { 159 DCHECK(database); 160 return database->DeleteOriginInfo(origin, type); 161} 162 163bool 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 177bool 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 185bool 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 193int64 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 202int64 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 215void 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 229int64 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 250int64 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 260void 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 305UsageAndQuota::UsageAndQuota() 306 : usage(0), 307 global_limited_usage(0), 308 quota(0), 309 available_disk_space(0) { 310} 311 312UsageAndQuota::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 323class 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 466class 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 534class 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 614class 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 713class 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 739class 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 768class 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 800QuotaManager::QuotaManager(bool is_incognito, 801 const base::FilePath& profile_path, 802 base::SingleThreadTaskRunner* io_thread, 803 base::SequencedTaskRunner* db_thread, 804 SpecialStoragePolicy* special_storage_policy) 805 : is_incognito_(is_incognito), 806 profile_path_(profile_path), 807 proxy_(new QuotaManagerProxy( 808 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 822void QuotaManager::GetUsageInfo(const GetUsageInfoCallback& callback) { 823 LazyInitialize(); 824 GetUsageInfoTask* get_usage_info = new GetUsageInfoTask(this, callback); 825 get_usage_info->Start(); 826} 827 828void 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 877void 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 890void 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 897void 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 905void QuotaManager::NotifyOriginInUse(const GURL& origin) { 906 DCHECK(io_thread_->BelongsToCurrentThread()); 907 origins_in_use_[origin]++; 908} 909 910void 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 918void 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 927void 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 943void 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 959void 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 970void 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 993void 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 1020void 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 1046void 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 1084void QuotaManager::GetGlobalUsage(StorageType type, 1085 const GlobalUsageCallback& callback) { 1086 LazyInitialize(); 1087 DCHECK(GetUsageTracker(type)); 1088 GetUsageTracker(type)->GetGlobalUsage(callback); 1089} 1090 1091void 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 1099void 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 1114bool QuotaManager::IsTrackingHostUsage(StorageType type, 1115 QuotaClient::ID client_id) const { 1116 UsageTracker* tracker = GetUsageTracker(type); 1117 return tracker && tracker->GetClientTracker(client_id); 1118} 1119 1120void 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 1133bool 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 1145void 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 1163bool 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 1189void QuotaManager::AddStorageObserver( 1190 StorageObserver* observer, const StorageObserver::MonitorParams& params) { 1191 DCHECK(observer); 1192 storage_monitor_->AddObserver(observer, params); 1193} 1194 1195void QuotaManager::RemoveStorageObserver(StorageObserver* observer) { 1196 DCHECK(observer); 1197 storage_monitor_->RemoveObserver(observer); 1198} 1199 1200void QuotaManager::RemoveStorageObserverForFilter( 1201 StorageObserver* observer, const StorageObserver::Filter& filter) { 1202 DCHECK(observer); 1203 storage_monitor_->RemoveObserverForFilter(observer, filter); 1204} 1205 1206QuotaManager::~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 1214QuotaManager::EvictionContext::EvictionContext() 1215 : evicted_type(kStorageTypeUnknown) { 1216} 1217 1218QuotaManager::EvictionContext::~EvictionContext() { 1219} 1220 1221void 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 1255void QuotaManager::RegisterClient(QuotaClient* client) { 1256 DCHECK(!database_.get()); 1257 clients_.push_back(client); 1258} 1259 1260UsageTracker* 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 1276void 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 1284void 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 1304void 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 1321void 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 1333void 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 1346void 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 1356void 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 1369void 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 1383void 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 1394void 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 1418void 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 1442void 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 1482void 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 1498void 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 1512void 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 1529void 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 1537void 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 1545void 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 1564void 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 1578void 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 1595void QuotaManager::DidInitializeTemporaryOriginsInfo(bool success) { 1596 DidDatabaseWork(success); 1597 if (success) 1598 StartEviction(); 1599} 1600 1601void QuotaManager::DidGetAvailableSpace(int64 space) { 1602 available_space_callbacks_.Run(MakeTuple(kQuotaStatusOk, space)); 1603} 1604 1605void QuotaManager::DidDatabaseWork(bool success) { 1606 db_disabled_ = !success; 1607} 1608 1609void QuotaManager::DeleteOnCorrectThread() const { 1610 if (!io_thread_->BelongsToCurrentThread() && 1611 io_thread_->DeleteSoon(FROM_HERE, this)) { 1612 return; 1613 } 1614 delete this; 1615} 1616 1617void 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 quota 1632