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