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