Home | History | Annotate | Download | only in browser
      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/browser/notification_service_impl.h"
      6 
      7 #include "base/lazy_instance.h"
      8 #include "base/threading/thread_local.h"
      9 #include "content/public/browser/notification_observer.h"
     10 #include "content/public/browser/notification_types.h"
     11 
     12 namespace  content {
     13 
     14 namespace {
     15 
     16 base::LazyInstance<base::ThreadLocalPointer<NotificationServiceImpl> >
     17     lazy_tls_ptr = LAZY_INSTANCE_INITIALIZER;
     18 
     19 }  // namespace
     20 
     21 // static
     22 NotificationServiceImpl* NotificationServiceImpl::current() {
     23   return lazy_tls_ptr.Pointer()->Get();
     24 }
     25 
     26 // static
     27 NotificationService* NotificationService::current() {
     28   return NotificationServiceImpl::current();
     29 }
     30 
     31 // static
     32 NotificationService* NotificationService::Create() {
     33   return new NotificationServiceImpl;
     34 }
     35 
     36 // static
     37 bool NotificationServiceImpl::HasKey(const NotificationSourceMap& map,
     38                                      const NotificationSource& source) {
     39   return map.find(source.map_key()) != map.end();
     40 }
     41 
     42 NotificationServiceImpl::NotificationServiceImpl() {
     43   DCHECK(current() == NULL);
     44   lazy_tls_ptr.Pointer()->Set(this);
     45 }
     46 
     47 void NotificationServiceImpl::AddObserver(NotificationObserver* observer,
     48                                           int type,
     49                                           const NotificationSource& source) {
     50   // We have gotten some crashes where the observer pointer is NULL. The problem
     51   // is that this happens when we actually execute a notification, so have no
     52   // way of knowing who the bad observer was. We want to know when this happens
     53   // in release mode so we know what code to blame the crash on (since this is
     54   // guaranteed to crash later).
     55   CHECK(observer);
     56 
     57   NotificationObserverList* observer_list;
     58   if (HasKey(observers_[type], source)) {
     59     observer_list = observers_[type][source.map_key()];
     60   } else {
     61     observer_list = new NotificationObserverList;
     62     observers_[type][source.map_key()] = observer_list;
     63   }
     64 
     65   observer_list->AddObserver(observer);
     66 #ifndef NDEBUG
     67   ++observer_counts_[type];
     68 #endif
     69 }
     70 
     71 void NotificationServiceImpl::RemoveObserver(NotificationObserver* observer,
     72                                              int type,
     73                                              const NotificationSource& source) {
     74   // This is a very serious bug.  An object is most likely being deleted on
     75   // the wrong thread, and as a result another thread's NotificationServiceImpl
     76   // has its deleted pointer in its map.  A garbge object will be called in the
     77   // future.
     78   // NOTE: when this check shows crashes, use BrowserThread::DeleteOnIOThread or
     79   // other variants as the trait on the object.
     80   CHECK(HasKey(observers_[type], source));
     81 
     82   NotificationObserverList* observer_list =
     83       observers_[type][source.map_key()];
     84   if (observer_list) {
     85     observer_list->RemoveObserver(observer);
     86     if (!observer_list->size()) {
     87       observers_[type].erase(source.map_key());
     88       delete observer_list;
     89     }
     90 #ifndef NDEBUG
     91     --observer_counts_[type];
     92 #endif
     93   }
     94 }
     95 
     96 void NotificationServiceImpl::Notify(int type,
     97                                      const NotificationSource& source,
     98                                      const NotificationDetails& details) {
     99   DCHECK_GT(type, NOTIFICATION_ALL) <<
    100       "Allowed for observing, but not posting.";
    101 
    102   // There's no particular reason for the order in which the different
    103   // classes of observers get notified here.
    104 
    105   // Notify observers of all types and all sources
    106   if (HasKey(observers_[NOTIFICATION_ALL], AllSources()) &&
    107       source != AllSources()) {
    108     FOR_EACH_OBSERVER(NotificationObserver,
    109                       *observers_[NOTIFICATION_ALL][AllSources().map_key()],
    110                       Observe(type, source, details));
    111   }
    112 
    113   // Notify observers of all types and the given source
    114   if (HasKey(observers_[NOTIFICATION_ALL], source)) {
    115     FOR_EACH_OBSERVER(NotificationObserver,
    116                       *observers_[NOTIFICATION_ALL][source.map_key()],
    117                       Observe(type, source, details));
    118   }
    119 
    120   // Notify observers of the given type and all sources
    121   if (HasKey(observers_[type], AllSources()) &&
    122       source != AllSources()) {
    123     FOR_EACH_OBSERVER(NotificationObserver,
    124                       *observers_[type][AllSources().map_key()],
    125                       Observe(type, source, details));
    126   }
    127 
    128   // Notify observers of the given type and the given source
    129   if (HasKey(observers_[type], source)) {
    130     FOR_EACH_OBSERVER(NotificationObserver,
    131                       *observers_[type][source.map_key()],
    132                       Observe(type, source, details));
    133   }
    134 }
    135 
    136 
    137 NotificationServiceImpl::~NotificationServiceImpl() {
    138   lazy_tls_ptr.Pointer()->Set(NULL);
    139 
    140 #ifndef NDEBUG
    141   for (int i = 0; i < static_cast<int>(observer_counts_.size()); i++) {
    142     if (observer_counts_[i] > 0) {
    143       // This may not be completely fixable -- see
    144       // http://code.google.com/p/chromium/issues/detail?id=11010 .
    145       VLOG(1) << observer_counts_[i] << " notification observer(s) leaked "
    146                  "of notification type " << i;
    147     }
    148   }
    149 #endif
    150 
    151   for (int i = 0; i < static_cast<int>(observers_.size()); i++) {
    152     NotificationSourceMap omap = observers_[i];
    153     for (NotificationSourceMap::iterator it = omap.begin();
    154          it != omap.end(); ++it)
    155       delete it->second;
    156   }
    157 }
    158 
    159 }  // namespace content
    160