summaryrefslogtreecommitdiffstats
path: root/chromium/webkit/browser/quota/storage_monitor.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/webkit/browser/quota/storage_monitor.cc')
-rw-r--r--chromium/webkit/browser/quota/storage_monitor.cc379
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