OLD | NEW |
(Empty) | |
| 1 // Copyright 2017 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/renderer/api_event_listeners.h" |
| 6 |
| 7 #include <algorithm> |
| 8 #include <memory> |
| 9 |
| 10 #include "base/memory/ptr_util.h" |
| 11 #include "content/public/child/v8_value_converter.h" |
| 12 #include "extensions/common/event_filter.h" |
| 13 #include "extensions/common/event_filtering_info.h" |
| 14 #include "extensions/common/event_matcher.h" |
| 15 #include "gin/converter.h" |
| 16 |
| 17 namespace extensions { |
| 18 |
| 19 namespace { |
| 20 |
| 21 // TODO(devlin): The EventFilter supports adding EventMatchers associated with |
| 22 // an id. For now, we ignore it and add/return everything associated with this |
| 23 // constant. We should rethink that. |
| 24 const int kIgnoreRoutingId = 0; |
| 25 |
| 26 // Pseudo-validates the given |filter| and converts it into a |
| 27 // base::DictionaryValue. Returns true on success. |
| 28 // TODO(devlin): This "validation" is pretty terrible. It matches the JS |
| 29 // equivalent, but it's lousy and makes it easy for users to get it wrong. |
| 30 // We should generate an argument spec for it and match it exactly. |
| 31 bool ValidateFilter(v8::Local<v8::Context> context, |
| 32 v8::Local<v8::Object> filter, |
| 33 std::unique_ptr<base::DictionaryValue>* filter_dict, |
| 34 std::string* error) { |
| 35 v8::Isolate* isolate = context->GetIsolate(); |
| 36 v8::HandleScope handle_scope(isolate); |
| 37 |
| 38 if (filter.IsEmpty()) { |
| 39 *filter_dict = base::MakeUnique<base::DictionaryValue>(); |
| 40 return true; |
| 41 } |
| 42 |
| 43 v8::Local<v8::Value> url_filter; |
| 44 if (!filter->Get(context, gin::StringToSymbol(isolate, "url")) |
| 45 .ToLocal(&url_filter)) { |
| 46 return false; |
| 47 } |
| 48 |
| 49 if (!url_filter->IsUndefined() && !url_filter->IsArray()) { |
| 50 *error = "filters.url should be an array."; |
| 51 return false; |
| 52 } |
| 53 |
| 54 v8::Local<v8::Value> service_type; |
| 55 if (!filter->Get(context, gin::StringToSymbol(isolate, "serviceType")) |
| 56 .ToLocal(&service_type)) { |
| 57 return false; |
| 58 } |
| 59 |
| 60 if (!service_type->IsUndefined() && !service_type->IsString()) { |
| 61 *error = "filters.serviceType should be a string."; |
| 62 return false; |
| 63 } |
| 64 |
| 65 std::unique_ptr<content::V8ValueConverter> converter( |
| 66 content::V8ValueConverter::create()); |
| 67 std::unique_ptr<base::Value> value = converter->FromV8Value(filter, context); |
| 68 if (!value || !value->is_dict()) { |
| 69 *error = "could not convert filter."; |
| 70 return false; |
| 71 } |
| 72 |
| 73 *filter_dict = base::DictionaryValue::From(std::move(value)); |
| 74 return true; |
| 75 } |
| 76 |
| 77 } // namespace |
| 78 |
| 79 UnfilteredEventListeners::UnfilteredEventListeners( |
| 80 const ListenersUpdated& listeners_updated) |
| 81 : listeners_updated_(listeners_updated) {} |
| 82 UnfilteredEventListeners::~UnfilteredEventListeners() = default; |
| 83 |
| 84 bool UnfilteredEventListeners::AddListener(v8::Local<v8::Function> listener, |
| 85 v8::Local<v8::Object> filter, |
| 86 v8::Local<v8::Context> context, |
| 87 std::string* error) { |
| 88 // |filter| should be checked before getting here. |
| 89 DCHECK(filter.IsEmpty()) |
| 90 << "Filtered events should use FilteredEventListeners"; |
| 91 |
| 92 if (HasListener(listener)) |
| 93 return false; |
| 94 |
| 95 listeners_.push_back( |
| 96 v8::Global<v8::Function>(context->GetIsolate(), listener)); |
| 97 if (listeners_.size() == 1) { |
| 98 listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS, |
| 99 nullptr, context); |
| 100 } |
| 101 |
| 102 return true; |
| 103 } |
| 104 |
| 105 void UnfilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener, |
| 106 v8::Local<v8::Context> context) { |
| 107 auto iter = std::find(listeners_.begin(), listeners_.end(), listener); |
| 108 if (iter == listeners_.end()) |
| 109 return; |
| 110 |
| 111 listeners_.erase(iter); |
| 112 if (listeners_.empty()) { |
| 113 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS, |
| 114 nullptr, context); |
| 115 } |
| 116 } |
| 117 |
| 118 bool UnfilteredEventListeners::HasListener(v8::Local<v8::Function> listener) { |
| 119 return std::find(listeners_.begin(), listeners_.end(), listener) != |
| 120 listeners_.end(); |
| 121 } |
| 122 |
| 123 size_t UnfilteredEventListeners::GetNumListeners() { |
| 124 return listeners_.size(); |
| 125 } |
| 126 |
| 127 std::vector<v8::Local<v8::Function>> UnfilteredEventListeners::GetListeners( |
| 128 const EventFilteringInfo* filter, |
| 129 v8::Local<v8::Context> context) { |
| 130 std::vector<v8::Local<v8::Function>> listeners; |
| 131 listeners.reserve(listeners_.size()); |
| 132 for (const auto& listener : listeners_) |
| 133 listeners.push_back(listener.Get(context->GetIsolate())); |
| 134 return listeners; |
| 135 } |
| 136 |
| 137 void UnfilteredEventListeners::Invalidate(v8::Local<v8::Context> context) { |
| 138 if (!listeners_.empty()) { |
| 139 listeners_.clear(); |
| 140 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS, |
| 141 nullptr, context); |
| 142 } |
| 143 } |
| 144 |
| 145 struct FilteredEventListeners::ListenerData { |
| 146 bool operator==(v8::Local<v8::Function> other_function) const { |
| 147 // Note that we only consider the listener function here, and not the |
| 148 // filter. This implies that it's invalid to try and add the same |
| 149 // function for multiple filters. |
| 150 // TODO(devlin): It's always been this way, but should it be? |
| 151 return function == other_function; |
| 152 } |
| 153 |
| 154 v8::Global<v8::Function> function; |
| 155 int filter_id; |
| 156 }; |
| 157 |
| 158 FilteredEventListeners::FilteredEventListeners( |
| 159 const ListenersUpdated& listeners_updated, |
| 160 const std::string& event_name, |
| 161 EventFilter* event_filter) |
| 162 : listeners_updated_(listeners_updated), |
| 163 event_name_(event_name), |
| 164 event_filter_(event_filter) {} |
| 165 FilteredEventListeners::~FilteredEventListeners() = default; |
| 166 |
| 167 bool FilteredEventListeners::AddListener(v8::Local<v8::Function> listener, |
| 168 v8::Local<v8::Object> filter, |
| 169 v8::Local<v8::Context> context, |
| 170 std::string* error) { |
| 171 if (HasListener(listener)) |
| 172 return false; |
| 173 |
| 174 std::unique_ptr<base::DictionaryValue> filter_dict; |
| 175 if (!ValidateFilter(context, filter, &filter_dict, error)) |
| 176 return false; |
| 177 |
| 178 int filter_id = event_filter_->AddEventMatcher( |
| 179 event_name_, |
| 180 base::MakeUnique<EventMatcher>(std::move(filter_dict), kIgnoreRoutingId)); |
| 181 |
| 182 if (filter_id == -1) { |
| 183 *error = "Could not add listener"; |
| 184 return false; |
| 185 } |
| 186 |
| 187 const EventMatcher* matcher = event_filter_->GetEventMatcher(filter_id); |
| 188 DCHECK(matcher); |
| 189 listeners_.push_back( |
| 190 {v8::Global<v8::Function>(context->GetIsolate(), listener), filter_id}); |
| 191 if (value_counter_.Add(*matcher->value())) { |
| 192 listeners_updated_.Run(binding::EventListenersChanged::HAS_LISTENERS, |
| 193 matcher->value(), context); |
| 194 } |
| 195 |
| 196 return true; |
| 197 } |
| 198 |
| 199 void FilteredEventListeners::RemoveListener(v8::Local<v8::Function> listener, |
| 200 v8::Local<v8::Context> context) { |
| 201 auto iter = std::find(listeners_.begin(), listeners_.end(), listener); |
| 202 if (iter == listeners_.end()) |
| 203 return; |
| 204 |
| 205 ListenerData data = std::move(*iter); |
| 206 listeners_.erase(iter); |
| 207 |
| 208 InvalidateListener(data, context); |
| 209 } |
| 210 |
| 211 bool FilteredEventListeners::HasListener(v8::Local<v8::Function> listener) { |
| 212 return std::find(listeners_.begin(), listeners_.end(), listener) != |
| 213 listeners_.end(); |
| 214 } |
| 215 |
| 216 size_t FilteredEventListeners::GetNumListeners() { |
| 217 return listeners_.size(); |
| 218 } |
| 219 |
| 220 std::vector<v8::Local<v8::Function>> FilteredEventListeners::GetListeners( |
| 221 const EventFilteringInfo* filter, |
| 222 v8::Local<v8::Context> context) { |
| 223 std::set<int> ids = event_filter_->MatchEvent( |
| 224 event_name_, filter ? *filter : EventFilteringInfo(), kIgnoreRoutingId); |
| 225 |
| 226 std::vector<v8::Local<v8::Function>> listeners; |
| 227 listeners.reserve(ids.size()); |
| 228 for (const auto& listener : listeners_) { |
| 229 if (ids.count(listener.filter_id)) |
| 230 listeners.push_back(listener.function.Get(context->GetIsolate())); |
| 231 } |
| 232 return listeners; |
| 233 } |
| 234 |
| 235 void FilteredEventListeners::Invalidate(v8::Local<v8::Context> context) { |
| 236 for (const auto& listener : listeners_) |
| 237 InvalidateListener(listener, context); |
| 238 listeners_.clear(); |
| 239 } |
| 240 |
| 241 void FilteredEventListeners::InvalidateListener( |
| 242 const ListenerData& listener, |
| 243 v8::Local<v8::Context> context) { |
| 244 EventMatcher* matcher = event_filter_->GetEventMatcher(listener.filter_id); |
| 245 DCHECK(matcher); |
| 246 if (value_counter_.Remove(*matcher->value())) { |
| 247 listeners_updated_.Run(binding::EventListenersChanged::NO_LISTENERS, |
| 248 matcher->value(), context); |
| 249 } |
| 250 |
| 251 event_filter_->RemoveEventMatcher(listener.filter_id); |
| 252 } |
| 253 |
| 254 } // namespace extensions |
OLD | NEW |