// 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 #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(cached_usage_, 0), std::max(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(cached_usage_, 0), std::max(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