1// Copyright (c) 2012 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 "content/public/browser/notification_registrar.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "content/browser/notification_service_impl.h"
11
12namespace content {
13
14struct NotificationRegistrar::Record {
15  bool operator==(const Record& other) const;
16
17  NotificationObserver* observer;
18  int type;
19  NotificationSource source;
20};
21
22bool NotificationRegistrar::Record::operator==(const Record& other) const {
23  return observer == other.observer &&
24         type == other.type &&
25         source == other.source;
26}
27
28NotificationRegistrar::NotificationRegistrar() {
29  // Force the NotificationService to be constructed (if it isn't already).
30  // This ensures the NotificationService will be registered on the
31  // AtExitManager before any objects which access it via NotificationRegistrar.
32  // This in turn means it will be destroyed after these objects, so they will
33  // never try to access the NotificationService after it's been destroyed.
34  NotificationServiceImpl::current();
35  // It is OK to create a NotificationRegistrar instance on one thread and then
36  // use it (exclusively) on another, so we detach from the initial thread.
37  DetachFromThread();
38}
39
40NotificationRegistrar::~NotificationRegistrar() {
41  RemoveAll();
42}
43
44void NotificationRegistrar::Add(NotificationObserver* observer,
45                                int type,
46                                const NotificationSource& source) {
47  DCHECK(CalledOnValidThread());
48  DCHECK(!IsRegistered(observer, type, source)) << "Duplicate registration.";
49
50  Record record = { observer, type, source };
51  registered_.push_back(record);
52
53  NotificationServiceImpl::current()->AddObserver(observer, type, source);
54}
55
56void NotificationRegistrar::Remove(NotificationObserver* observer,
57                                   int type,
58                                   const NotificationSource& source) {
59  DCHECK(CalledOnValidThread());
60
61  Record record = { observer, type, source };
62  RecordVector::iterator found = std::find(
63      registered_.begin(), registered_.end(), record);
64  DCHECK(found != registered_.end());
65
66  registered_.erase(found);
67
68  // This can be NULL if our owner outlives the NotificationService, e.g. if our
69  // owner is a Singleton.
70  NotificationServiceImpl* service = NotificationServiceImpl::current();
71  if (service)
72    service->RemoveObserver(observer, type, source);
73}
74
75void NotificationRegistrar::RemoveAll() {
76  CHECK(CalledOnValidThread());
77  // Early-exit if no registrations, to avoid calling
78  // NotificationService::current.  If we've constructed an object with a
79  // NotificationRegistrar member, but haven't actually used the notification
80  // service, and we reach prgram exit, then calling current() below could try
81  // to initialize the service's lazy TLS pointer during exit, which throws
82  // wrenches at things.
83  if (registered_.empty())
84    return;
85
86  // This can be NULL if our owner outlives the NotificationService, e.g. if our
87  // owner is a Singleton.
88  NotificationServiceImpl* service = NotificationServiceImpl::current();
89  if (service) {
90    for (size_t i = 0; i < registered_.size(); i++) {
91      service->RemoveObserver(registered_[i].observer,
92                              registered_[i].type,
93                              registered_[i].source);
94    }
95  }
96  registered_.clear();
97}
98
99bool NotificationRegistrar::IsEmpty() const {
100  DCHECK(CalledOnValidThread());
101  return registered_.empty();
102}
103
104bool NotificationRegistrar::IsRegistered(NotificationObserver* observer,
105                                         int type,
106                                         const NotificationSource& source) {
107  DCHECK(CalledOnValidThread());
108  Record record = { observer, type, source };
109  return std::find(registered_.begin(), registered_.end(), record) !=
110      registered_.end();
111}
112
113}  // namespace content
114