summaryrefslogtreecommitdiffstats
path: root/content/browser/notification_service_impl.cc
blob: 49132883f18bd7e36026556fde2639a5e17beb1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Copyright (c) 2012 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 "content/browser/notification_service_impl.h"

#include "base/lazy_instance.h"
#include "base/threading/thread_local.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_types.h"

namespace  content {

namespace {

base::LazyInstance<base::ThreadLocalPointer<NotificationServiceImpl> >
    lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER;

}  // namespace

// static
NotificationServiceImpl* NotificationServiceImpl::current() {
  return lazy_tls_ptr.Pointer()->Get();
}

// static
NotificationService* NotificationService::current() {
  return NotificationServiceImpl::current();
}

// static
NotificationService* NotificationService::Create() {
  return new NotificationServiceImpl;
}

// static
bool NotificationServiceImpl::HasKey(const NotificationSourceMap& map,
                                     const NotificationSource& source) {
  return map.find(source.map_key()) != map.end();
}

NotificationServiceImpl::NotificationServiceImpl() {
  DCHECK(current() == NULL);
  lazy_tls_ptr.Pointer()->Set(this);
}

void NotificationServiceImpl::AddObserver(NotificationObserver* observer,
                                          int type,
                                          const NotificationSource& source) {
  // We have gotten some crashes where the observer pointer is NULL. The problem
  // is that this happens when we actually execute a notification, so have no
  // way of knowing who the bad observer was. We want to know when this happens
  // in release mode so we know what code to blame the crash on (since this is
  // guaranteed to crash later).
  CHECK(observer);

  NotificationObserverList* observer_list;
  if (HasKey(observers_[type], source)) {
    observer_list = observers_[type][source.map_key()];
  } else {
    observer_list = new NotificationObserverList;
    observers_[type][source.map_key()] = observer_list;
  }

  observer_list->AddObserver(observer);
#ifndef NDEBUG
  ++observer_counts_[type];
#endif
}

void NotificationServiceImpl::RemoveObserver(NotificationObserver* observer,
                                             int type,
                                             const NotificationSource& source) {
  // This is a very serious bug.  An object is most likely being deleted on
  // the wrong thread, and as a result another thread's NotificationServiceImpl
  // has its deleted pointer in its map.  A garbge object will be called in the
  // future.
  // NOTE: when this check shows crashes, use BrowserThread::DeleteOnIOThread or
  // other variants as the trait on the object.
  CHECK(HasKey(observers_[type], source));

  NotificationObserverList* observer_list =
      observers_[type][source.map_key()];
  if (observer_list) {
    observer_list->RemoveObserver(observer);
    if (!observer_list->size()) {
      observers_[type].erase(source.map_key());
      delete observer_list;
    }
#ifndef NDEBUG
    --observer_counts_[type];
#endif
  }
}

void NotificationServiceImpl::Notify(int type,
                                     const NotificationSource& source,
                                     const NotificationDetails& details) {
  DCHECK_GT(type, NOTIFICATION_ALL) <<
      "Allowed for observing, but not posting.";

  // There's no particular reason for the order in which the different
  // classes of observers get notified here.

  // Notify observers of all types and all sources
  if (HasKey(observers_[NOTIFICATION_ALL], AllSources()) &&
      source != AllSources()) {
    FOR_EACH_OBSERVER(NotificationObserver,
                      *observers_[NOTIFICATION_ALL][AllSources().map_key()],
                      Observe(type, source, details));
  }

  // Notify observers of all types and the given source
  if (HasKey(observers_[NOTIFICATION_ALL], source)) {
    FOR_EACH_OBSERVER(NotificationObserver,
                      *observers_[NOTIFICATION_ALL][source.map_key()],
                      Observe(type, source, details));
  }

  // Notify observers of the given type and all sources
  if (HasKey(observers_[type], AllSources()) &&
      source != AllSources()) {
    FOR_EACH_OBSERVER(NotificationObserver,
                      *observers_[type][AllSources().map_key()],
                      Observe(type, source, details));
  }

  // Notify observers of the given type and the given source
  if (HasKey(observers_[type], source)) {
    FOR_EACH_OBSERVER(NotificationObserver,
                      *observers_[type][source.map_key()],
                      Observe(type, source, details));
  }
}


NotificationServiceImpl::~NotificationServiceImpl() {
  lazy_tls_ptr.Pointer()->Set(NULL);

#ifndef NDEBUG
  for (int i = 0; i < static_cast<int>(observer_counts_.size()); i++) {
    if (observer_counts_[i] > 0) {
      // This may not be completely fixable -- see
      // http://code.google.com/p/chromium/issues/detail?id=11010 .
      VLOG(1) << observer_counts_[i] << " notification observer(s) leaked "
                 "of notification type " << i;
    }
  }
#endif

  for (int i = 0; i < static_cast<int>(observers_.size()); i++) {
    NotificationSourceMap omap = observers_[i];
    for (NotificationSourceMap::iterator it = omap.begin();
         it != omap.end(); ++it)
      delete it->second;
  }
}

}  // namespace content