1// Copyright 2014 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 "storage/browser/quota/storage_monitor.h"
6
7#include <algorithm>
8
9#include "base/stl_util.h"
10#include "net/base/net_util.h"
11#include "storage/browser/quota/quota_manager.h"
12#include "storage/common/quota/quota_status_code.h"
13
14namespace storage {
15
16// StorageObserverList:
17
18StorageObserverList::ObserverState::ObserverState()
19    : requires_update(false) {
20}
21
22StorageObserverList::StorageObserverList() {}
23
24StorageObserverList::~StorageObserverList() {}
25
26void StorageObserverList::AddObserver(
27    StorageObserver* observer, const StorageObserver::MonitorParams& params) {
28  ObserverState& observer_state = observers_[observer];
29  observer_state.origin = params.filter.origin;
30  observer_state.rate = params.rate;
31}
32
33void StorageObserverList::RemoveObserver(StorageObserver* observer) {
34  observers_.erase(observer);
35}
36
37int StorageObserverList::ObserverCount() const {
38  return observers_.size();
39}
40
41void StorageObserverList::OnStorageChange(const StorageObserver::Event& event) {
42  for (StorageObserverStateMap::iterator it = observers_.begin();
43       it != observers_.end(); ++it) {
44    it->second.requires_update = true;
45  }
46
47  MaybeDispatchEvent(event);
48}
49
50void StorageObserverList::MaybeDispatchEvent(
51    const StorageObserver::Event& event) {
52  notification_timer_.Stop();
53  base::TimeDelta min_delay = base::TimeDelta::Max();
54  bool all_observers_notified = true;
55
56  for (StorageObserverStateMap::iterator it = observers_.begin();
57       it != observers_.end(); ++it) {
58    if (!it->second.requires_update)
59      continue;
60
61    base::TimeTicks current_time = base::TimeTicks::Now();
62    base::TimeDelta delta = current_time - it->second.last_notification_time;
63    if (it->second.last_notification_time.is_null() ||
64        delta >= it->second.rate) {
65      it->second.requires_update = false;
66      it->second.last_notification_time = current_time;
67
68      if (it->second.origin == event.filter.origin) {
69        it->first->OnStorageEvent(event);
70      } else {
71        // When the quota and usage of an origin is requested, QuotaManager
72        // returns the quota and usage of the host. Multiple origins can map to
73        // to the same host, so ensure the |origin| field in the dispatched
74        // event matches the |origin| specified by the observer when it was
75        // registered.
76        StorageObserver::Event dispatch_event(event);
77        dispatch_event.filter.origin = it->second.origin;
78        it->first->OnStorageEvent(dispatch_event);
79      }
80    } else {
81      all_observers_notified = false;
82      base::TimeDelta delay = it->second.rate - delta;
83      if (delay < min_delay)
84        min_delay = delay;
85    }
86  }
87
88  // We need to respect the notification rate specified by observers. So if it
89  // is too soon to dispatch an event to an observer, save the event and
90  // dispatch it after a delay. If we simply drop the event, another one may
91  // not arrive anytime soon and the observer will miss the most recent event.
92  if (!all_observers_notified) {
93    pending_event_ = event;
94    notification_timer_.Start(
95        FROM_HERE,
96        min_delay,
97        this,
98        &StorageObserverList::DispatchPendingEvent);
99  }
100}
101
102void StorageObserverList::ScheduleUpdateForObserver(StorageObserver* observer) {
103  DCHECK(ContainsKey(observers_, observer));
104  observers_[observer].requires_update = true;
105}
106
107void StorageObserverList::DispatchPendingEvent() {
108  MaybeDispatchEvent(pending_event_);
109}
110
111
112// HostStorageObservers:
113
114HostStorageObservers::HostStorageObservers(QuotaManager* quota_manager)
115    : quota_manager_(quota_manager),
116      initialized_(false),
117      initializing_(false),
118      event_occurred_before_init_(false),
119      usage_deltas_during_init_(0),
120      cached_usage_(0),
121      cached_quota_(0),
122      weak_factory_(this) {
123}
124
125HostStorageObservers::~HostStorageObservers() {}
126
127void HostStorageObservers::AddObserver(
128    StorageObserver* observer,
129    const StorageObserver::MonitorParams& params) {
130  observers_.AddObserver(observer, params);
131
132  if (!params.dispatch_initial_state)
133    return;
134
135  if (initialized_) {
136    StorageObserver::Event event(params.filter,
137                                 std::max<int64>(cached_usage_, 0),
138                                 std::max<int64>(cached_quota_, 0));
139    observer->OnStorageEvent(event);
140    return;
141  }
142
143  // Ensure the observer receives the initial storage state once initialization
144  // is complete.
145  observers_.ScheduleUpdateForObserver(observer);
146  StartInitialization(params.filter);
147}
148
149void HostStorageObservers::RemoveObserver(StorageObserver* observer) {
150  observers_.RemoveObserver(observer);
151}
152
153bool HostStorageObservers::ContainsObservers() const {
154  return observers_.ObserverCount() > 0;
155}
156
157void HostStorageObservers::NotifyUsageChange(
158    const StorageObserver::Filter& filter, int64 delta) {
159  if (initialized_) {
160    cached_usage_ += delta;
161    DispatchEvent(filter, true);
162    return;
163  }
164
165  // If a storage change occurs before initialization, ensure all observers will
166  // receive an event once initialization is complete.
167  event_occurred_before_init_ = true;
168
169  // During QuotaManager::GetUsageAndQuotaForWebApps(), cached data is read
170  // synchronously, but other data may be retrieved asynchronously. A usage
171  // change may occur between the function call and callback. These deltas need
172  // to be added to the usage received by GotHostUsageAndQuota() to ensure
173  // |cached_usage_| is correctly initialized.
174  if (initializing_) {
175    usage_deltas_during_init_ += delta;
176    return;
177  }
178
179  StartInitialization(filter);
180}
181
182void HostStorageObservers::StartInitialization(
183    const StorageObserver::Filter& filter) {
184  if (initialized_ || initializing_)
185    return;
186
187  initializing_ = true;
188  quota_manager_->GetUsageAndQuotaForWebApps(
189      filter.origin,
190      filter.storage_type,
191      base::Bind(&HostStorageObservers::GotHostUsageAndQuota,
192                 weak_factory_.GetWeakPtr(),
193                 filter));
194}
195
196void HostStorageObservers::GotHostUsageAndQuota(
197    const StorageObserver::Filter& filter,
198    QuotaStatusCode status,
199    int64 usage,
200    int64 quota) {
201  initializing_ = false;
202  if (status != kQuotaStatusOk)
203    return;
204
205  initialized_ = true;
206  cached_quota_ = quota;
207  cached_usage_ = usage + usage_deltas_during_init_;
208  DispatchEvent(filter, event_occurred_before_init_);
209}
210
211void HostStorageObservers::DispatchEvent(
212    const StorageObserver::Filter& filter, bool is_update) {
213  StorageObserver::Event event(filter,
214                               std::max<int64>(cached_usage_, 0),
215                               std::max<int64>(cached_quota_, 0));
216  if (is_update)
217    observers_.OnStorageChange(event);
218  else
219    observers_.MaybeDispatchEvent(event);
220}
221
222
223// StorageTypeObservers:
224
225StorageTypeObservers::StorageTypeObservers(QuotaManager* quota_manager)
226    : quota_manager_(quota_manager) {
227}
228
229StorageTypeObservers::~StorageTypeObservers() {
230  STLDeleteValues(&host_observers_map_);
231}
232
233void StorageTypeObservers::AddObserver(
234    StorageObserver* observer, const StorageObserver::MonitorParams& params) {
235  std::string host = net::GetHostOrSpecFromURL(params.filter.origin);
236  if (host.empty())
237    return;
238
239  HostStorageObservers* host_observers = NULL;
240  HostObserversMap::iterator it = host_observers_map_.find(host);
241  if (it == host_observers_map_.end()) {
242    host_observers = new HostStorageObservers(quota_manager_);
243    host_observers_map_[host] = host_observers;
244  } else {
245    host_observers = it->second;
246  }
247
248  host_observers->AddObserver(observer, params);
249}
250
251void StorageTypeObservers::RemoveObserver(StorageObserver* observer) {
252  for (HostObserversMap::iterator it = host_observers_map_.begin();
253       it != host_observers_map_.end(); ) {
254    it->second->RemoveObserver(observer);
255    if (!it->second->ContainsObservers()) {
256      delete it->second;
257      host_observers_map_.erase(it++);
258    } else {
259      ++it;
260    }
261  }
262}
263
264void StorageTypeObservers::RemoveObserverForFilter(
265    StorageObserver* observer, const StorageObserver::Filter& filter) {
266  std::string host = net::GetHostOrSpecFromURL(filter.origin);
267  HostObserversMap::iterator it = host_observers_map_.find(host);
268  if (it == host_observers_map_.end())
269    return;
270
271  it->second->RemoveObserver(observer);
272  if (!it->second->ContainsObservers()) {
273    delete it->second;
274    host_observers_map_.erase(it);
275  }
276}
277
278const HostStorageObservers* StorageTypeObservers::GetHostObservers(
279    const std::string& host) const {
280  HostObserversMap::const_iterator it = host_observers_map_.find(host);
281  if (it != host_observers_map_.end())
282    return it->second;
283
284  return NULL;
285}
286
287void StorageTypeObservers::NotifyUsageChange(
288    const StorageObserver::Filter& filter, int64 delta) {
289  std::string host = net::GetHostOrSpecFromURL(filter.origin);
290  HostObserversMap::iterator it = host_observers_map_.find(host);
291  if (it == host_observers_map_.end())
292    return;
293
294  it->second->NotifyUsageChange(filter, delta);
295}
296
297
298// StorageMonitor:
299
300StorageMonitor::StorageMonitor(QuotaManager* quota_manager)
301    : quota_manager_(quota_manager) {
302}
303
304StorageMonitor::~StorageMonitor() {
305  STLDeleteValues(&storage_type_observers_map_);
306}
307
308void StorageMonitor::AddObserver(
309    StorageObserver* observer, const StorageObserver::MonitorParams& params) {
310  DCHECK(observer);
311
312  // Check preconditions.
313  if (params.filter.storage_type == kStorageTypeUnknown ||
314      params.filter.storage_type == kStorageTypeQuotaNotManaged ||
315      params.filter.origin.is_empty()) {
316    NOTREACHED();
317    return;
318  }
319
320  StorageTypeObservers* type_observers = NULL;
321  StorageTypeObserversMap::iterator it =
322      storage_type_observers_map_.find(params.filter.storage_type);
323  if (it == storage_type_observers_map_.end()) {
324    type_observers = new StorageTypeObservers(quota_manager_);
325    storage_type_observers_map_[params.filter.storage_type] = type_observers;
326  } else {
327    type_observers = it->second;
328  }
329
330  type_observers->AddObserver(observer, params);
331}
332
333void StorageMonitor::RemoveObserver(StorageObserver* observer) {
334  for (StorageTypeObserversMap::iterator it =
335           storage_type_observers_map_.begin();
336       it != storage_type_observers_map_.end(); ++it) {
337    it->second->RemoveObserver(observer);
338  }
339}
340
341void StorageMonitor::RemoveObserverForFilter(
342    StorageObserver* observer, const StorageObserver::Filter& filter) {
343  StorageTypeObserversMap::iterator it =
344      storage_type_observers_map_.find(filter.storage_type);
345  if (it == storage_type_observers_map_.end())
346    return;
347
348  it->second->RemoveObserverForFilter(observer, filter);
349}
350
351const StorageTypeObservers* StorageMonitor::GetStorageTypeObservers(
352    StorageType storage_type) const {
353  StorageTypeObserversMap::const_iterator it =
354      storage_type_observers_map_.find(storage_type);
355  if (it != storage_type_observers_map_.end())
356    return it->second;
357
358  return NULL;
359}
360
361void StorageMonitor::NotifyUsageChange(
362    const StorageObserver::Filter& filter, int64 delta) {
363  // Check preconditions.
364  if (filter.storage_type == kStorageTypeUnknown ||
365      filter.storage_type == kStorageTypeQuotaNotManaged ||
366      filter.origin.is_empty()) {
367    NOTREACHED();
368    return;
369  }
370
371  StorageTypeObserversMap::iterator it =
372      storage_type_observers_map_.find(filter.storage_type);
373  if (it == storage_type_observers_map_.end())
374    return;
375
376  it->second->NotifyUsageChange(filter, delta);
377}
378
379}  // namespace storage
380