Chromium Code Reviews| Index: webkit/browser/quota/storage_monitor.cc |
| diff --git a/webkit/browser/quota/storage_monitor.cc b/webkit/browser/quota/storage_monitor.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..f5092d9bc41a403a5c88daf2b663410ba68145d4 |
| --- /dev/null |
| +++ b/webkit/browser/quota/storage_monitor.cc |
| @@ -0,0 +1,385 @@ |
| +// 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 { |
| + |
| +namespace { |
| + |
| +// The minimum rate between event notifications. |
| +const int kMinimumRateMSec = 50; |
| + |
| +} // namespace |
| + |
| +// 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 > 0 ? base::TimeDelta::FromSeconds(params.rate) |
| + : base::TimeDelta::FromMilliseconds(kMinimumRateMSec); |
| +} |
| + |
| +void StorageObserverList::RemoveObserver(StorageObserver* observer) { |
| + StorageObserverStateMap::iterator it = observers_.find(observer); |
|
tzik
2014/03/31 06:54:16
just observers_.erase(observer); works.
|
| + if (it != observers_.end()) |
| + observers_.erase(it); |
| +} |
| + |
| +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); |
| +} |
| + |
| +const StorageObserver::Event* StorageObserverList::GetPendingEvent() const { |
| + return notification_timer_.IsRunning() ? &pending_event_ |
| + : NULL; |
| +} |
| + |
| +int StorageObserverList::GetRequiredUpdatesCount() const { |
| + int count = 0; |
| + for (StorageObserverStateMap::const_iterator it = observers_.begin(); |
| + it != observers_.end(); ++it) { |
| + if (it->second.requires_update) |
| + ++count; |
| + } |
| + |
| + return count; |
| +} |
| + |
| +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::Time current_time = base::Time::Now(); |
| + base::TimeDelta delta = current_time - it->second.last_notification_time; |
| + if (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(observers_.find(observer) != observers_.end()); |
|
tzik
2014/03/31 07:45:48
I think
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), |
| + weak_factory_(this), |
| + initialized_(false), |
| + initializing_(false), |
| + event_occurred_before_init_(false), |
| + cached_usage_(0), |
| + cached_quota_(0) { |
| +} |
| + |
| +HostStorageObservers::~HostStorageObservers() {} |
| + |
| +void HostStorageObservers::AddObserver( |
| + StorageObserver* observer, |
| + const StorageObserver::MonitorParams& params) { |
| + StorageObserverList::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. |
| + ScheduleUpdateForObserver(observer); |
| + StartInitialization(params.filter); |
| +} |
| + |
| +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; |
| + 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; |
| + 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) |
| + OnStorageChange(event); |
| + else |
| + 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->ObserverCount() == 0) { |
| + 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->ObserverCount() == 0) { |
| + 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 |