1// Copyright 2013 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 "extensions/browser/event_listener_map.h"
6
7#include "base/values.h"
8#include "content/public/browser/render_process_host.h"
9#include "extensions/browser/event_router.h"
10#include "ipc/ipc_message.h"
11
12using base::DictionaryValue;
13
14namespace extensions {
15
16typedef EventFilter::MatcherID MatcherID;
17
18EventListener::EventListener(const std::string& event_name,
19                             const std::string& extension_id,
20                             content::RenderProcessHost* process,
21                             scoped_ptr<DictionaryValue> filter)
22    : event_name_(event_name),
23      extension_id_(extension_id),
24      process_(process),
25      filter_(filter.Pass()),
26      matcher_id_(-1) {
27}
28
29EventListener::~EventListener() {}
30
31bool EventListener::Equals(const EventListener* other) const {
32  // We don't check matcher_id equality because we want a listener with a
33  // filter that hasn't been added to EventFilter to match one that is
34  // equivalent but has.
35  return event_name_ == other->event_name_ &&
36         extension_id_ == other->extension_id_ && process_ == other->process_ &&
37         ((!!filter_.get()) == (!!other->filter_.get())) &&
38         (!filter_.get() || filter_->Equals(other->filter_.get()));
39}
40
41scoped_ptr<EventListener> EventListener::Copy() const {
42  scoped_ptr<DictionaryValue> filter_copy;
43  if (filter_)
44    filter_copy.reset(filter_->DeepCopy());
45  return scoped_ptr<EventListener>(new EventListener(
46      event_name_, extension_id_, process_, filter_copy.Pass()));
47}
48
49bool EventListener::IsLazy() const {
50  return !process_;
51}
52
53void EventListener::MakeLazy() {
54  process_ = NULL;
55}
56
57content::BrowserContext* EventListener::GetBrowserContext() const {
58  return process_ ? process_->GetBrowserContext() : NULL;
59}
60
61EventListenerMap::EventListenerMap(Delegate* delegate)
62    : delegate_(delegate) {
63}
64
65EventListenerMap::~EventListenerMap() {}
66
67bool EventListenerMap::AddListener(scoped_ptr<EventListener> listener) {
68  if (HasListener(listener.get()))
69    return false;
70  if (listener->filter()) {
71    scoped_ptr<EventMatcher> matcher(ParseEventMatcher(listener->filter()));
72    MatcherID id =
73        event_filter_.AddEventMatcher(listener->event_name(), matcher.Pass());
74    listener->set_matcher_id(id);
75    listeners_by_matcher_id_[id] = listener.get();
76    filtered_events_.insert(listener->event_name());
77  }
78  linked_ptr<EventListener> listener_ptr(listener.release());
79  listeners_[listener_ptr->event_name()].push_back(listener_ptr);
80
81  delegate_->OnListenerAdded(listener_ptr.get());
82
83  return true;
84}
85
86scoped_ptr<EventMatcher> EventListenerMap::ParseEventMatcher(
87    DictionaryValue* filter_dict) {
88  return scoped_ptr<EventMatcher>(new EventMatcher(
89      scoped_ptr<DictionaryValue>(filter_dict->DeepCopy()), MSG_ROUTING_NONE));
90}
91
92bool EventListenerMap::RemoveListener(const EventListener* listener) {
93  ListenerList& listeners = listeners_[listener->event_name()];
94  for (ListenerList::iterator it = listeners.begin(); it != listeners.end();
95       it++) {
96    if ((*it)->Equals(listener)) {
97      CleanupListener(it->get());
98      // Popping from the back should be cheaper than erase(it).
99      std::swap(*it, listeners.back());
100      listeners.pop_back();
101      delegate_->OnListenerRemoved(listener);
102      return true;
103    }
104  }
105  return false;
106}
107
108bool EventListenerMap::HasListenerForEvent(const std::string& event_name) {
109  ListenerMap::iterator it = listeners_.find(event_name);
110  return it != listeners_.end() && !it->second.empty();
111}
112
113bool EventListenerMap::HasListenerForExtension(
114    const std::string& extension_id,
115    const std::string& event_name) {
116  ListenerMap::iterator it = listeners_.find(event_name);
117  if (it == listeners_.end())
118    return false;
119
120  for (ListenerList::iterator it2 = it->second.begin();
121       it2 != it->second.end(); it2++) {
122    if ((*it2)->extension_id() == extension_id)
123      return true;
124  }
125  return false;
126}
127
128bool EventListenerMap::HasListener(const EventListener* listener) {
129  ListenerMap::iterator it = listeners_.find(listener->event_name());
130  if (it == listeners_.end())
131    return false;
132  for (ListenerList::iterator it2 = it->second.begin();
133       it2 != it->second.end(); it2++) {
134    if ((*it2)->Equals(listener)) {
135      return true;
136    }
137  }
138  return false;
139}
140
141bool EventListenerMap::HasProcessListener(content::RenderProcessHost* process,
142                                          const std::string& extension_id) {
143  for (ListenerMap::iterator it = listeners_.begin(); it != listeners_.end();
144       it++) {
145    for (ListenerList::iterator it2 = it->second.begin();
146         it2 != it->second.end(); it2++) {
147      if ((*it2)->process() == process &&
148          (*it2)->extension_id() == extension_id)
149        return true;
150    }
151  }
152  return false;
153}
154
155void EventListenerMap::RemoveLazyListenersForExtension(
156    const std::string& extension_id) {
157  for (ListenerMap::iterator it = listeners_.begin(); it != listeners_.end();
158       it++) {
159    for (ListenerList::iterator it2 = it->second.begin();
160         it2 != it->second.end();) {
161      if ((*it2)->IsLazy() && (*it2)->extension_id() == extension_id) {
162        CleanupListener(it2->get());
163        it2 = it->second.erase(it2);
164      } else {
165        it2++;
166      }
167    }
168  }
169}
170
171void EventListenerMap::LoadUnfilteredLazyListeners(
172    const std::string& extension_id,
173    const std::set<std::string>& event_names) {
174  for (std::set<std::string>::const_iterator it = event_names.begin();
175       it != event_names.end(); ++it) {
176    AddListener(scoped_ptr<EventListener>(new EventListener(
177        *it, extension_id, NULL, scoped_ptr<DictionaryValue>())));
178  }
179}
180
181void EventListenerMap::LoadFilteredLazyListeners(
182    const std::string& extension_id,
183    const DictionaryValue& filtered) {
184  for (DictionaryValue::Iterator it(filtered); !it.IsAtEnd(); it.Advance()) {
185    // We skip entries if they are malformed.
186    const base::ListValue* filter_list = NULL;
187    if (!it.value().GetAsList(&filter_list))
188      continue;
189    for (size_t i = 0; i < filter_list->GetSize(); i++) {
190      const DictionaryValue* filter = NULL;
191      if (!filter_list->GetDictionary(i, &filter))
192        continue;
193      AddListener(scoped_ptr<EventListener>(new EventListener(
194          it.key(), extension_id, NULL,
195          scoped_ptr<DictionaryValue>(filter->DeepCopy()))));
196    }
197  }
198}
199
200std::set<const EventListener*> EventListenerMap::GetEventListeners(
201    const Event& event) {
202  std::set<const EventListener*> interested_listeners;
203  if (IsFilteredEvent(event)) {
204    // Look up the interested listeners via the EventFilter.
205    std::set<MatcherID> ids =
206        event_filter_.MatchEvent(event.event_name, event.filter_info,
207            MSG_ROUTING_NONE);
208    for (std::set<MatcherID>::iterator id = ids.begin(); id != ids.end();
209         id++) {
210      EventListener* listener = listeners_by_matcher_id_[*id];
211      CHECK(listener);
212      interested_listeners.insert(listener);
213    }
214  } else {
215    ListenerList& listeners = listeners_[event.event_name];
216    for (ListenerList::const_iterator it = listeners.begin();
217         it != listeners.end(); it++) {
218      interested_listeners.insert(it->get());
219    }
220  }
221
222  return interested_listeners;
223}
224
225void EventListenerMap::RemoveListenersForProcess(
226    const content::RenderProcessHost* process) {
227  CHECK(process);
228  for (ListenerMap::iterator it = listeners_.begin(); it != listeners_.end();
229       it++) {
230    for (ListenerList::iterator it2 = it->second.begin();
231         it2 != it->second.end();) {
232      if ((*it2)->process() == process) {
233        linked_ptr<EventListener> listener(*it2);
234        CleanupListener(it2->get());
235        it2 = it->second.erase(it2);
236        delegate_->OnListenerRemoved(listener.get());
237      } else {
238        it2++;
239      }
240    }
241  }
242}
243
244void EventListenerMap::CleanupListener(EventListener* listener) {
245  // If the listener doesn't have a filter then we have nothing to clean up.
246  if (listener->matcher_id() == -1)
247    return;
248  event_filter_.RemoveEventMatcher(listener->matcher_id());
249  CHECK_EQ(1u, listeners_by_matcher_id_.erase(listener->matcher_id()));
250}
251
252bool EventListenerMap::IsFilteredEvent(const Event& event) const {
253  return filtered_events_.count(event.event_name) > 0u;
254}
255
256}  // namespace extensions
257