diff options
Diffstat (limited to 'chromium/webkit/browser/quota/storage_monitor.cc')
-rw-r--r-- | chromium/webkit/browser/quota/storage_monitor.cc | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/chromium/webkit/browser/quota/storage_monitor.cc b/chromium/webkit/browser/quota/storage_monitor.cc new file mode 100644 index 00000000000..bb56af3225a --- /dev/null +++ b/chromium/webkit/browser/quota/storage_monitor.cc @@ -0,0 +1,379 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/browser/quota/storage_monitor.h" + +#include <algorithm> + +#include "base/stl_util.h" +#include "net/base/net_util.h" +#include "webkit/browser/quota/quota_manager.h" +#include "webkit/common/quota/quota_status_code.h" + +namespace quota { + +// StorageObserverList: + +StorageObserverList::ObserverState::ObserverState() + : requires_update(false) { +} + +StorageObserverList::StorageObserverList() {} + +StorageObserverList::~StorageObserverList() {} + +void StorageObserverList::AddObserver( + StorageObserver* observer, const StorageObserver::MonitorParams& params) { + ObserverState& observer_state = observers_[observer]; + observer_state.origin = params.filter.origin; + observer_state.rate = params.rate; +} + +void StorageObserverList::RemoveObserver(StorageObserver* observer) { + observers_.erase(observer); +} + +int StorageObserverList::ObserverCount() const { + return observers_.size(); +} + +void StorageObserverList::OnStorageChange(const StorageObserver::Event& event) { + for (StorageObserverStateMap::iterator it = observers_.begin(); + it != observers_.end(); ++it) { + it->second.requires_update = true; + } + + MaybeDispatchEvent(event); +} + +void StorageObserverList::MaybeDispatchEvent( + const StorageObserver::Event& event) { + notification_timer_.Stop(); + base::TimeDelta min_delay = base::TimeDelta::Max(); + bool all_observers_notified = true; + + for (StorageObserverStateMap::iterator it = observers_.begin(); + it != observers_.end(); ++it) { + if (!it->second.requires_update) + continue; + + base::TimeTicks current_time = base::TimeTicks::Now(); + base::TimeDelta delta = current_time - it->second.last_notification_time; + if (it->second.last_notification_time.is_null() || + delta >= it->second.rate) { + it->second.requires_update = false; + it->second.last_notification_time = current_time; + + if (it->second.origin == event.filter.origin) { + it->first->OnStorageEvent(event); + } else { + // When the quota and usage of an origin is requested, QuotaManager + // returns the quota and usage of the host. Multiple origins can map to + // to the same host, so ensure the |origin| field in the dispatched + // event matches the |origin| specified by the observer when it was + // registered. + StorageObserver::Event dispatch_event(event); + dispatch_event.filter.origin = it->second.origin; + it->first->OnStorageEvent(dispatch_event); + } + } else { + all_observers_notified = false; + base::TimeDelta delay = it->second.rate - delta; + if (delay < min_delay) + min_delay = delay; + } + } + + // We need to respect the notification rate specified by observers. So if it + // is too soon to dispatch an event to an observer, save the event and + // dispatch it after a delay. If we simply drop the event, another one may + // not arrive anytime soon and the observer will miss the most recent event. + if (!all_observers_notified) { + pending_event_ = event; + notification_timer_.Start( + FROM_HERE, + min_delay, + this, + &StorageObserverList::DispatchPendingEvent); + } +} + +void StorageObserverList::ScheduleUpdateForObserver(StorageObserver* observer) { + DCHECK(ContainsKey(observers_, observer)); + observers_[observer].requires_update = true; +} + +void StorageObserverList::DispatchPendingEvent() { + MaybeDispatchEvent(pending_event_); +} + + +// HostStorageObservers: + +HostStorageObservers::HostStorageObservers(QuotaManager* quota_manager) + : quota_manager_(quota_manager), + initialized_(false), + initializing_(false), + event_occurred_before_init_(false), + usage_deltas_during_init_(0), + cached_usage_(0), + cached_quota_(0), + weak_factory_(this) { +} + +HostStorageObservers::~HostStorageObservers() {} + +void HostStorageObservers::AddObserver( + StorageObserver* observer, + const StorageObserver::MonitorParams& params) { + observers_.AddObserver(observer, params); + + if (!params.dispatch_initial_state) + return; + + if (initialized_) { + StorageObserver::Event event(params.filter, + std::max<int64>(cached_usage_, 0), + std::max<int64>(cached_quota_, 0)); + observer->OnStorageEvent(event); + return; + } + + // Ensure the observer receives the initial storage state once initialization + // is complete. + observers_.ScheduleUpdateForObserver(observer); + StartInitialization(params.filter); +} + +void HostStorageObservers::RemoveObserver(StorageObserver* observer) { + observers_.RemoveObserver(observer); +} + +bool HostStorageObservers::ContainsObservers() const { + return observers_.ObserverCount() > 0; +} + +void HostStorageObservers::NotifyUsageChange( + const StorageObserver::Filter& filter, int64 delta) { + if (initialized_) { + cached_usage_ += delta; + DispatchEvent(filter, true); + return; + } + + // If a storage change occurs before initialization, ensure all observers will + // receive an event once initialization is complete. + event_occurred_before_init_ = true; + + // During QuotaManager::GetUsageAndQuotaForWebApps(), cached data is read + // synchronously, but other data may be retrieved asynchronously. A usage + // change may occur between the function call and callback. These deltas need + // to be added to the usage received by GotHostUsageAndQuota() to ensure + // |cached_usage_| is correctly initialized. + if (initializing_) { + usage_deltas_during_init_ += delta; + return; + } + + StartInitialization(filter); +} + +void HostStorageObservers::StartInitialization( + const StorageObserver::Filter& filter) { + if (initialized_ || initializing_) + return; + + initializing_ = true; + quota_manager_->GetUsageAndQuotaForWebApps( + filter.origin, + filter.storage_type, + base::Bind(&HostStorageObservers::GotHostUsageAndQuota, + weak_factory_.GetWeakPtr(), + filter)); +} + +void HostStorageObservers::GotHostUsageAndQuota( + const StorageObserver::Filter& filter, + QuotaStatusCode status, + int64 usage, + int64 quota) { + initializing_ = false; + if (status != kQuotaStatusOk) + return; + + initialized_ = true; + cached_quota_ = quota; + cached_usage_ = usage + usage_deltas_during_init_; + DispatchEvent(filter, event_occurred_before_init_); +} + +void HostStorageObservers::DispatchEvent( + const StorageObserver::Filter& filter, bool is_update) { + StorageObserver::Event event(filter, + std::max<int64>(cached_usage_, 0), + std::max<int64>(cached_quota_, 0)); + if (is_update) + observers_.OnStorageChange(event); + else + observers_.MaybeDispatchEvent(event); +} + + +// StorageTypeObservers: + +StorageTypeObservers::StorageTypeObservers(QuotaManager* quota_manager) + : quota_manager_(quota_manager) { +} + +StorageTypeObservers::~StorageTypeObservers() { + STLDeleteValues(&host_observers_map_); +} + +void StorageTypeObservers::AddObserver( + StorageObserver* observer, const StorageObserver::MonitorParams& params) { + std::string host = net::GetHostOrSpecFromURL(params.filter.origin); + if (host.empty()) + return; + + HostStorageObservers* host_observers = NULL; + HostObserversMap::iterator it = host_observers_map_.find(host); + if (it == host_observers_map_.end()) { + host_observers = new HostStorageObservers(quota_manager_); + host_observers_map_[host] = host_observers; + } else { + host_observers = it->second; + } + + host_observers->AddObserver(observer, params); +} + +void StorageTypeObservers::RemoveObserver(StorageObserver* observer) { + for (HostObserversMap::iterator it = host_observers_map_.begin(); + it != host_observers_map_.end(); ) { + it->second->RemoveObserver(observer); + if (!it->second->ContainsObservers()) { + delete it->second; + host_observers_map_.erase(it++); + } else { + ++it; + } + } +} + +void StorageTypeObservers::RemoveObserverForFilter( + StorageObserver* observer, const StorageObserver::Filter& filter) { + std::string host = net::GetHostOrSpecFromURL(filter.origin); + HostObserversMap::iterator it = host_observers_map_.find(host); + if (it == host_observers_map_.end()) + return; + + it->second->RemoveObserver(observer); + if (!it->second->ContainsObservers()) { + delete it->second; + host_observers_map_.erase(it); + } +} + +const HostStorageObservers* StorageTypeObservers::GetHostObservers( + const std::string& host) const { + HostObserversMap::const_iterator it = host_observers_map_.find(host); + if (it != host_observers_map_.end()) + return it->second; + + return NULL; +} + +void StorageTypeObservers::NotifyUsageChange( + const StorageObserver::Filter& filter, int64 delta) { + std::string host = net::GetHostOrSpecFromURL(filter.origin); + HostObserversMap::iterator it = host_observers_map_.find(host); + if (it == host_observers_map_.end()) + return; + + it->second->NotifyUsageChange(filter, delta); +} + + +// StorageMonitor: + +StorageMonitor::StorageMonitor(QuotaManager* quota_manager) + : quota_manager_(quota_manager) { +} + +StorageMonitor::~StorageMonitor() { + STLDeleteValues(&storage_type_observers_map_); +} + +void StorageMonitor::AddObserver( + StorageObserver* observer, const StorageObserver::MonitorParams& params) { + DCHECK(observer); + + // Check preconditions. + if (params.filter.storage_type == kStorageTypeUnknown || + params.filter.storage_type == kStorageTypeQuotaNotManaged || + params.filter.origin.is_empty()) { + NOTREACHED(); + return; + } + + StorageTypeObservers* type_observers = NULL; + StorageTypeObserversMap::iterator it = + storage_type_observers_map_.find(params.filter.storage_type); + if (it == storage_type_observers_map_.end()) { + type_observers = new StorageTypeObservers(quota_manager_); + storage_type_observers_map_[params.filter.storage_type] = type_observers; + } else { + type_observers = it->second; + } + + type_observers->AddObserver(observer, params); +} + +void StorageMonitor::RemoveObserver(StorageObserver* observer) { + for (StorageTypeObserversMap::iterator it = + storage_type_observers_map_.begin(); + it != storage_type_observers_map_.end(); ++it) { + it->second->RemoveObserver(observer); + } +} + +void StorageMonitor::RemoveObserverForFilter( + StorageObserver* observer, const StorageObserver::Filter& filter) { + StorageTypeObserversMap::iterator it = + storage_type_observers_map_.find(filter.storage_type); + if (it == storage_type_observers_map_.end()) + return; + + it->second->RemoveObserverForFilter(observer, filter); +} + +const StorageTypeObservers* StorageMonitor::GetStorageTypeObservers( + StorageType storage_type) const { + StorageTypeObserversMap::const_iterator it = + storage_type_observers_map_.find(storage_type); + if (it != storage_type_observers_map_.end()) + return it->second; + + return NULL; +} + +void StorageMonitor::NotifyUsageChange( + const StorageObserver::Filter& filter, int64 delta) { + // Check preconditions. + if (filter.storage_type == kStorageTypeUnknown || + filter.storage_type == kStorageTypeQuotaNotManaged || + filter.origin.is_empty()) { + NOTREACHED(); + return; + } + + StorageTypeObserversMap::iterator it = + storage_type_observers_map_.find(filter.storage_type); + if (it == storage_type_observers_map_.end()) + return; + + it->second->NotifyUsageChange(filter, delta); +} + +} // namespace quota |