OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/renderer/event_bindings.h" | 5 #include "extensions/renderer/event_bindings.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
| 8 #include <set> |
| 9 #include <string> |
| 10 #include <vector> |
8 | 11 |
9 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
10 #include "base/bind.h" | 13 #include "base/bind.h" |
11 #include "base/lazy_instance.h" | 14 #include "base/lazy_instance.h" |
12 #include "base/memory/scoped_ptr.h" | 15 #include "base/memory/scoped_ptr.h" |
13 #include "components/crx_file/id_util.h" | 16 #include "components/crx_file/id_util.h" |
14 #include "content/public/child/v8_value_converter.h" | 17 #include "content/public/child/v8_value_converter.h" |
15 #include "content/public/renderer/render_thread.h" | 18 #include "content/public/renderer/render_thread.h" |
16 #include "content/public/renderer/render_view.h" | 19 #include "content/public/renderer/render_view.h" |
17 #include "extensions/common/event_filter.h" | 20 #include "extensions/common/event_filter.h" |
18 #include "extensions/common/extension.h" | 21 #include "extensions/common/extension.h" |
19 #include "extensions/common/extension_messages.h" | 22 #include "extensions/common/extension_messages.h" |
20 #include "extensions/common/manifest_handlers/background_info.h" | 23 #include "extensions/common/manifest_handlers/background_info.h" |
21 #include "extensions/common/value_counter.h" | 24 #include "extensions/common/value_counter.h" |
22 #include "extensions/renderer/dispatcher.h" | 25 #include "extensions/renderer/dispatcher.h" |
23 #include "extensions/renderer/extension_helper.h" | 26 #include "extensions/renderer/extension_helper.h" |
| 27 #include "extensions/renderer/object_backed_native_handler.h" |
24 #include "url/gurl.h" | 28 #include "url/gurl.h" |
| 29 #include "v8/include/v8.h" |
25 | 30 |
26 namespace extensions { | 31 namespace extensions { |
27 | 32 |
28 namespace { | 33 namespace { |
29 | 34 |
30 // A map of event names to the number of contexts listening to that event. | 35 // A map of event names to the number of contexts listening to that event. |
31 // We notify the browser about event listeners when we transition between 0 | 36 // We notify the browser about event listeners when we transition between 0 |
32 // and 1. | 37 // and 1. |
33 typedef std::map<std::string, int> EventListenerCounts; | 38 typedef std::map<std::string, int> EventListenerCounts; |
34 | 39 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
131 FilteredEventListenerCounts::iterator it = counts.find(event_name); | 136 FilteredEventListenerCounts::iterator it = counts.find(event_name); |
132 if (it == counts.end()) | 137 if (it == counts.end()) |
133 return false; | 138 return false; |
134 return 0 == it->second->Remove(*filter); | 139 return 0 == it->second->Remove(*filter); |
135 } | 140 } |
136 | 141 |
137 } // namespace | 142 } // namespace |
138 | 143 |
139 EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context) | 144 EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context) |
140 : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { | 145 : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { |
141 RouteFunction("AttachEvent", base::Bind(&EventBindings::AttachEventHandler, | 146 RouteFunction( |
142 base::Unretained(this))); | 147 "AttachEvent", |
143 RouteFunction("DetachEvent", base::Bind(&EventBindings::DetachEventHandler, | 148 base::Bind(&EventBindings::AttachEvent, base::Unretained(this))); |
144 base::Unretained(this))); | 149 RouteFunction( |
| 150 "DetachEvent", |
| 151 base::Bind(&EventBindings::DetachEvent, base::Unretained(this))); |
145 RouteFunction( | 152 RouteFunction( |
146 "AttachFilteredEvent", | 153 "AttachFilteredEvent", |
147 base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this))); | 154 base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this))); |
148 RouteFunction( | 155 RouteFunction( |
149 "DetachFilteredEvent", | 156 "DetachFilteredEvent", |
150 base::Bind(&EventBindings::DetachFilteredEvent, base::Unretained(this))); | 157 base::Bind(&EventBindings::DetachFilteredEvent, base::Unretained(this))); |
151 RouteFunction("MatchAgainstEventFilter", | 158 RouteFunction("MatchAgainstEventFilter", |
152 base::Bind(&EventBindings::MatchAgainstEventFilter, | 159 base::Bind(&EventBindings::MatchAgainstEventFilter, |
153 base::Unretained(this))); | 160 base::Unretained(this))); |
154 | |
155 // It's safe to use base::Unretained here because |context| will always | |
156 // outlive us. | |
157 context->AddInvalidationObserver( | |
158 base::Bind(&EventBindings::OnInvalidated, base::Unretained(this))); | |
159 } | 161 } |
160 | 162 |
161 EventBindings::~EventBindings() {} | 163 EventBindings::~EventBindings() {} |
162 | 164 |
163 void EventBindings::AttachEventHandler( | 165 // Attach an event name to an object. |
| 166 void EventBindings::AttachEvent( |
164 const v8::FunctionCallbackInfo<v8::Value>& args) { | 167 const v8::FunctionCallbackInfo<v8::Value>& args) { |
165 CHECK_EQ(1, args.Length()); | 168 CHECK_EQ(1, args.Length()); |
166 CHECK(args[0]->IsString()); | 169 CHECK(args[0]->IsString()); |
167 AttachEvent(*v8::String::Utf8Value(args[0])); | |
168 } | |
169 | 170 |
170 void EventBindings::AttachEvent(const std::string& event_name) { | 171 std::string event_name = *v8::String::Utf8Value(args[0]); |
171 // This method throws an exception if it returns false. | 172 |
172 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) | 173 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) |
173 return; | 174 return; |
174 | 175 |
175 // Record the attachment for this context so that events can be detached when | |
176 // the context is destroyed. | |
177 // | |
178 // Ideally we'd CHECK that it's not already attached, however that's not | |
179 // possible because extensions can create and attach events themselves. Very | |
180 // silly, but that's the way it is. For an example of this, see | |
181 // chrome/test/data/extensions/api_test/events/background.js. | |
182 attached_event_names_.insert(event_name); | |
183 | |
184 const std::string& extension_id = context()->GetExtensionID(); | 176 const std::string& extension_id = context()->GetExtensionID(); |
185 if (IncrementEventListenerCount(context(), event_name) == 1) { | 177 if (IncrementEventListenerCount(context(), event_name) == 1) { |
186 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener( | 178 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener( |
187 extension_id, context()->GetURL(), event_name)); | 179 extension_id, context()->GetURL(), event_name)); |
188 } | 180 } |
189 | 181 |
190 // This is called the first time the page has added a listener. Since | 182 // This is called the first time the page has added a listener. Since |
191 // the background page is the only lazy page, we know this is the first | 183 // the background page is the only lazy page, we know this is the first |
192 // time this listener has been registered. | 184 // time this listener has been registered. |
193 if (IsLazyBackgroundPage(context()->GetRenderView(), | 185 if (IsLazyBackgroundPage(context()->GetRenderView(), |
194 context()->extension())) { | 186 context()->extension())) { |
195 content::RenderThread::Get()->Send( | 187 content::RenderThread::Get()->Send( |
196 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); | 188 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); |
197 } | 189 } |
198 } | 190 } |
199 | 191 |
200 void EventBindings::DetachEventHandler( | 192 void EventBindings::DetachEvent( |
201 const v8::FunctionCallbackInfo<v8::Value>& args) { | 193 const v8::FunctionCallbackInfo<v8::Value>& args) { |
202 CHECK_EQ(2, args.Length()); | 194 CHECK_EQ(2, args.Length()); |
203 CHECK(args[0]->IsString()); | 195 CHECK(args[0]->IsString()); |
204 CHECK(args[1]->IsBoolean()); | 196 CHECK(args[1]->IsBoolean()); |
205 DetachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue()); | |
206 } | |
207 | 197 |
208 void EventBindings::DetachEvent(const std::string& event_name, bool is_manual) { | 198 std::string event_name = *v8::String::Utf8Value(args[0]); |
209 // See comment in AttachEvent(). | 199 bool is_manual = args[1]->BooleanValue(); |
210 attached_event_names_.erase(event_name); | |
211 | 200 |
212 const std::string& extension_id = context()->GetExtensionID(); | 201 const std::string& extension_id = context()->GetExtensionID(); |
213 | |
214 if (DecrementEventListenerCount(context(), event_name) == 0) { | 202 if (DecrementEventListenerCount(context(), event_name) == 0) { |
215 content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener( | 203 content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener( |
216 extension_id, context()->GetURL(), event_name)); | 204 extension_id, context()->GetURL(), event_name)); |
217 } | 205 } |
218 | 206 |
219 // DetachEvent is called when the last listener for the context is | 207 // DetachEvent is called when the last listener for the context is |
220 // removed. If the context is the background page, and it removes the | 208 // removed. If the context is the background page, and it removes the |
221 // last listener manually, then we assume that it is no longer interested | 209 // last listener manually, then we assume that it is no longer interested |
222 // in being awakened for this event. | 210 // in being awakened for this event. |
223 if (is_manual && IsLazyBackgroundPage(context()->GetRenderView(), | 211 if (is_manual && IsLazyBackgroundPage(context()->GetRenderView(), |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
329 args.GetReturnValue().Set(array); | 317 args.GetReturnValue().Set(array); |
330 } | 318 } |
331 | 319 |
332 scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher( | 320 scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher( |
333 base::DictionaryValue* filter_dict) { | 321 base::DictionaryValue* filter_dict) { |
334 return scoped_ptr<EventMatcher>(new EventMatcher( | 322 return scoped_ptr<EventMatcher>(new EventMatcher( |
335 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), | 323 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), |
336 context()->GetRenderView()->GetRoutingID())); | 324 context()->GetRenderView()->GetRoutingID())); |
337 } | 325 } |
338 | 326 |
339 void EventBindings::OnInvalidated() { | |
340 // Detach all attached events that weren't attached. Iterate over a copy | |
341 // because it will be mutated. | |
342 std::set<std::string> attached_event_names_safe = attached_event_names_; | |
343 for (const std::string& event_name : attached_event_names_safe) { | |
344 DetachEvent(event_name, false /* is_manual */); | |
345 } | |
346 DCHECK(attached_event_names_.empty()) | |
347 << "Events cannot be attached during invalidation"; | |
348 } | |
349 | |
350 } // namespace extensions | 327 } // namespace extensions |
OLD | NEW |