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