123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // Copyright 2013 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 "extensions/browser/event_listener_map.h"
- #include <stddef.h>
- #include <utility>
- #include "base/containers/contains.h"
- #include "base/memory/ptr_util.h"
- #include "base/values.h"
- #include "content/public/browser/render_process_host.h"
- #include "extensions/browser/event_router.h"
- #include "extensions/common/constants.h"
- #include "ipc/ipc_message.h"
- #include "url/gurl.h"
- #include "url/origin.h"
- using base::DictionaryValue;
- namespace extensions {
- typedef EventFilter::MatcherID MatcherID;
- // static
- std::unique_ptr<EventListener> EventListener::ForExtension(
- const std::string& event_name,
- const std::string& extension_id,
- content::RenderProcessHost* process,
- std::unique_ptr<base::DictionaryValue> filter) {
- return base::WrapUnique(
- new EventListener(event_name, extension_id, GURL(), process, false,
- blink::mojom::kInvalidServiceWorkerVersionId,
- kMainThreadId, std::move(filter)));
- }
- // static
- std::unique_ptr<EventListener> EventListener::ForURL(
- const std::string& event_name,
- const GURL& listener_url,
- content::RenderProcessHost* process,
- std::unique_ptr<base::DictionaryValue> filter) {
- // Use only the origin to identify the event listener, e.g. chrome://settings
- // for chrome://settings/accounts, to avoid multiple events being triggered
- // for the same process. See crbug.com/536858 for details. // TODO(devlin): If
- // we dispatched events to processes more intelligently this could be avoided.
- return base::WrapUnique(new EventListener(
- event_name, ExtensionId(), url::Origin::Create(listener_url).GetURL(),
- process, false, blink::mojom::kInvalidServiceWorkerVersionId,
- kMainThreadId, std::move(filter)));
- }
- std::unique_ptr<EventListener> EventListener::ForExtensionServiceWorker(
- const std::string& event_name,
- const std::string& extension_id,
- content::RenderProcessHost* process,
- const GURL& service_worker_scope,
- int64_t service_worker_version_id,
- int worker_thread_id,
- std::unique_ptr<base::DictionaryValue> filter) {
- return base::WrapUnique(new EventListener(
- event_name, extension_id, service_worker_scope, process, true,
- service_worker_version_id, worker_thread_id, std::move(filter)));
- }
- EventListener::~EventListener() {}
- bool EventListener::Equals(const EventListener* other) const {
- // We don't check matcher_id equality because we want a listener with a
- // filter that hasn't been added to EventFilter to match one that is
- // equivalent but has.
- return event_name_ == other->event_name_ &&
- extension_id_ == other->extension_id_ &&
- listener_url_ == other->listener_url_ && process_ == other->process_ &&
- is_for_service_worker_ == other->is_for_service_worker_ &&
- service_worker_version_id_ == other->service_worker_version_id_ &&
- worker_thread_id_ == other->worker_thread_id_ &&
- ((!!filter_.get()) == (!!other->filter_.get())) &&
- (!filter_.get() || *filter_ == *other->filter_);
- }
- std::unique_ptr<EventListener> EventListener::Copy() const {
- std::unique_ptr<DictionaryValue> filter_copy;
- if (filter_)
- filter_copy = filter_->CreateDeepCopy();
- return base::WrapUnique(
- new EventListener(event_name_, extension_id_, listener_url_, process_,
- is_for_service_worker_, service_worker_version_id_,
- worker_thread_id_, std::move(filter_copy)));
- }
- bool EventListener::IsLazy() const {
- return !process_;
- }
- void EventListener::MakeLazy() {
- // A lazy listener neither has a process attached to it nor it has a worker
- // thread (if the listener was for a service worker), so reset these values
- // below to reflect that.
- if (is_for_service_worker_) {
- worker_thread_id_ = kMainThreadId;
- service_worker_version_id_ = blink::mojom::kInvalidServiceWorkerVersionId;
- }
- process_ = nullptr;
- }
- content::BrowserContext* EventListener::GetBrowserContext() const {
- return process_ ? process_->GetBrowserContext() : nullptr;
- }
- EventListener::EventListener(const std::string& event_name,
- const std::string& extension_id,
- const GURL& listener_url,
- content::RenderProcessHost* process,
- bool is_for_service_worker,
- int64_t service_worker_version_id,
- int worker_thread_id,
- std::unique_ptr<DictionaryValue> filter)
- : event_name_(event_name),
- extension_id_(extension_id),
- listener_url_(listener_url),
- process_(process),
- is_for_service_worker_(is_for_service_worker),
- service_worker_version_id_(service_worker_version_id),
- worker_thread_id_(worker_thread_id),
- filter_(std::move(filter)),
- matcher_id_(-1) {
- if (!IsLazy()) {
- DCHECK_EQ(is_for_service_worker, worker_thread_id != kMainThreadId);
- DCHECK_EQ(is_for_service_worker,
- service_worker_version_id !=
- blink::mojom::kInvalidServiceWorkerVersionId);
- }
- }
- EventListenerMap::EventListenerMap(Delegate* delegate)
- : delegate_(delegate) {
- }
- EventListenerMap::~EventListenerMap() {}
- bool EventListenerMap::AddListener(std::unique_ptr<EventListener> listener) {
- if (HasListener(listener.get()))
- return false;
- if (listener->filter()) {
- std::unique_ptr<EventMatcher> matcher(
- ParseEventMatcher(listener->filter()));
- MatcherID id = event_filter_.AddEventMatcher(listener->event_name(),
- std::move(matcher));
- listener->set_matcher_id(id);
- listeners_by_matcher_id_[id] = listener.get();
- filtered_events_.insert(listener->event_name());
- }
- EventListener* listener_ptr = listener.get();
- listeners_[listener->event_name()].push_back(std::move(listener));
- delegate_->OnListenerAdded(listener_ptr);
- return true;
- }
- std::unique_ptr<EventMatcher> EventListenerMap::ParseEventMatcher(
- DictionaryValue* filter_dict) {
- return std::make_unique<EventMatcher>(filter_dict->CreateDeepCopy(),
- MSG_ROUTING_NONE);
- }
- bool EventListenerMap::RemoveListener(const EventListener* listener) {
- auto listener_itr = listeners_.find(listener->event_name());
- if (listener_itr == listeners_.end())
- return false;
- ListenerList& listeners = listener_itr->second;
- for (auto& it : listeners) {
- if (it->Equals(listener)) {
- CleanupListener(it.get());
- // Popping from the back should be cheaper than erase(it).
- std::swap(it, listeners.back());
- listeners.pop_back();
- if (listeners.empty())
- listeners_.erase(listener_itr);
- delegate_->OnListenerRemoved(listener);
- return true;
- }
- }
- return false;
- }
- bool EventListenerMap::HasListenerForEvent(
- const std::string& event_name) const {
- auto it = listeners_.find(event_name);
- return it != listeners_.end() && !it->second.empty();
- }
- bool EventListenerMap::HasListenerForExtension(
- const std::string& extension_id,
- const std::string& event_name) const {
- auto it = listeners_.find(event_name);
- if (it == listeners_.end())
- return false;
- for (const auto& listener_to_search : it->second) {
- if (listener_to_search->extension_id() == extension_id)
- return true;
- }
- return false;
- }
- bool EventListenerMap::HasListener(const EventListener* listener) const {
- auto it = listeners_.find(listener->event_name());
- if (it == listeners_.end())
- return false;
- for (const auto& listener_to_search : it->second) {
- if (listener_to_search->Equals(listener))
- return true;
- }
- return false;
- }
- bool EventListenerMap::HasProcessListener(
- content::RenderProcessHost* process,
- int worker_thread_id,
- const std::string& extension_id) const {
- for (const auto& it : listeners_) {
- for (const auto& listener : it.second) {
- if (listener->process() == process &&
- listener->extension_id() == extension_id &&
- listener->worker_thread_id() == worker_thread_id) {
- return true;
- }
- }
- }
- return false;
- }
- void EventListenerMap::RemoveListenersForExtension(
- const std::string& extension_id) {
- for (auto it = listeners_.begin(); it != listeners_.end();) {
- auto& listener_list = it->second;
- for (auto it2 = listener_list.begin(); it2 != listener_list.end();) {
- if ((*it2)->extension_id() == extension_id) {
- std::unique_ptr<EventListener> listener_removed = std::move(*it2);
- CleanupListener(listener_removed.get());
- it2 = listener_list.erase(it2);
- delegate_->OnListenerRemoved(listener_removed.get());
- } else {
- ++it2;
- }
- }
- // Check if we removed all the listeners from the list. If so,
- // remove the list entry entirely.
- if (listener_list.empty())
- it = listeners_.erase(it);
- else
- ++it;
- }
- }
- void EventListenerMap::LoadUnfilteredLazyListeners(
- const std::string& extension_id,
- const std::set<std::string>& event_names) {
- for (const auto& name : event_names) {
- AddListener(EventListener::ForExtension(
- name, extension_id, nullptr, std::unique_ptr<DictionaryValue>()));
- }
- }
- void EventListenerMap::LoadUnfilteredWorkerListeners(
- const ExtensionId& extension_id,
- const std::set<std::string>& event_names) {
- for (const auto& name : event_names) {
- AddListener(EventListener::ForExtensionServiceWorker(
- name, extension_id, nullptr,
- // TODO(lazyboy): We need to store correct scopes of each worker into
- // ExtensionPrefs for events. This currently assumes all workers are
- // registered in the '/' scope. https://crbug.com/773103.
- Extension::GetBaseURLFromExtensionId(extension_id),
- blink::mojom::kInvalidServiceWorkerVersionId, kMainThreadId, nullptr));
- }
- }
- void EventListenerMap::LoadFilteredLazyListeners(
- const std::string& extension_id,
- bool is_for_service_worker,
- const DictionaryValue& filtered) {
- for (DictionaryValue::Iterator it(filtered); !it.IsAtEnd(); it.Advance()) {
- // We skip entries if they are malformed.
- if (!it.value().is_list())
- continue;
- for (const base::Value& filter_value : it.value().GetListDeprecated()) {
- if (!filter_value.is_dict())
- continue;
- const base::DictionaryValue* filter =
- static_cast<const base::DictionaryValue*>(&filter_value);
- if (is_for_service_worker) {
- AddListener(EventListener::ForExtensionServiceWorker(
- it.key(), extension_id, nullptr,
- // TODO(lazyboy): We need to store correct scopes of each worker
- // into ExtensionPrefs for events. This currently assumes all
- // workers are registered in the '/' scope.
- // https://crbug.com/773103.
- Extension::GetBaseURLFromExtensionId(extension_id),
- blink::mojom::kInvalidServiceWorkerVersionId, kMainThreadId,
- filter->CreateDeepCopy()));
- } else {
- AddListener(EventListener::ForExtension(it.key(), extension_id, nullptr,
- filter->CreateDeepCopy()));
- }
- }
- }
- }
- std::set<const EventListener*> EventListenerMap::GetEventListeners(
- const Event& event) {
- std::set<const EventListener*> interested_listeners;
- if (IsFilteredEvent(event)) {
- // Look up the interested listeners via the EventFilter.
- std::set<MatcherID> ids = event_filter_.MatchEvent(
- event.event_name, *event.filter_info, MSG_ROUTING_NONE);
- for (const MatcherID& id : ids) {
- EventListener* listener = listeners_by_matcher_id_[id];
- CHECK(listener);
- interested_listeners.insert(listener);
- }
- } else {
- for (const auto& listener : listeners_[event.event_name])
- interested_listeners.insert(listener.get());
- }
- return interested_listeners;
- }
- void EventListenerMap::RemoveListenersForProcess(
- const content::RenderProcessHost* process) {
- CHECK(process);
- for (auto it = listeners_.begin(); it != listeners_.end();) {
- auto& listener_list = it->second;
- for (auto it2 = listener_list.begin(); it2 != listener_list.end();) {
- if ((*it2)->process() == process) {
- std::unique_ptr<EventListener> listener_removed = std::move(*it2);
- CleanupListener(listener_removed.get());
- it2 = listener_list.erase(it2);
- delegate_->OnListenerRemoved(listener_removed.get());
- } else {
- ++it2;
- }
- }
- // Check if we removed all the listeners from the list. If so,
- // remove the list entry entirely.
- if (listener_list.empty())
- it = listeners_.erase(it);
- else
- ++it;
- }
- }
- void EventListenerMap::CleanupListener(EventListener* listener) {
- // If the listener doesn't have a filter then we have nothing to clean up.
- if (listener->matcher_id() == -1)
- return;
- // If we're removing the final listener for an event, we can remove the
- // entry from |filtered_events_|, as well.
- auto iter = listeners_.find(listener->event_name());
- if (iter->second.size() == 1)
- filtered_events_.erase(iter->first);
- event_filter_.RemoveEventMatcher(listener->matcher_id());
- CHECK_EQ(1u, listeners_by_matcher_id_.erase(listener->matcher_id()));
- }
- bool EventListenerMap::IsFilteredEvent(const Event& event) const {
- return base::Contains(filtered_events_, event.event_name);
- }
- } // namespace extensions
|