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