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> | |
11 | 8 |
12 #include "base/basictypes.h" | 9 #include "base/basictypes.h" |
13 #include "base/bind.h" | 10 #include "base/bind.h" |
14 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
15 #include "base/memory/scoped_ptr.h" | 12 #include "base/memory/scoped_ptr.h" |
16 #include "components/crx_file/id_util.h" | 13 #include "components/crx_file/id_util.h" |
17 #include "content/public/child/v8_value_converter.h" | 14 #include "content/public/child/v8_value_converter.h" |
18 #include "content/public/renderer/render_thread.h" | 15 #include "content/public/renderer/render_thread.h" |
19 #include "content/public/renderer/render_view.h" | 16 #include "content/public/renderer/render_view.h" |
20 #include "extensions/common/event_filter.h" | 17 #include "extensions/common/event_filter.h" |
21 #include "extensions/common/extension.h" | 18 #include "extensions/common/extension.h" |
22 #include "extensions/common/extension_messages.h" | 19 #include "extensions/common/extension_messages.h" |
23 #include "extensions/common/manifest_handlers/background_info.h" | 20 #include "extensions/common/manifest_handlers/background_info.h" |
24 #include "extensions/common/value_counter.h" | 21 #include "extensions/common/value_counter.h" |
25 #include "extensions/renderer/dispatcher.h" | 22 #include "extensions/renderer/dispatcher.h" |
26 #include "extensions/renderer/extension_helper.h" | 23 #include "extensions/renderer/extension_helper.h" |
27 #include "extensions/renderer/object_backed_native_handler.h" | |
28 #include "url/gurl.h" | 24 #include "url/gurl.h" |
29 #include "v8/include/v8.h" | |
30 | 25 |
31 namespace extensions { | 26 namespace extensions { |
32 | 27 |
33 namespace { | 28 namespace { |
34 | 29 |
35 // A map of event names to the number of contexts listening to that event. | 30 // 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 | 31 // We notify the browser about event listeners when we transition between 0 |
37 // and 1. | 32 // and 1. |
38 typedef std::map<std::string, int> EventListenerCounts; | 33 typedef std::map<std::string, int> EventListenerCounts; |
39 | 34 |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
136 FilteredEventListenerCounts::iterator it = counts.find(event_name); | 131 FilteredEventListenerCounts::iterator it = counts.find(event_name); |
137 if (it == counts.end()) | 132 if (it == counts.end()) |
138 return false; | 133 return false; |
139 return 0 == it->second->Remove(*filter); | 134 return 0 == it->second->Remove(*filter); |
140 } | 135 } |
141 | 136 |
142 } // namespace | 137 } // namespace |
143 | 138 |
144 EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context) | 139 EventBindings::EventBindings(Dispatcher* dispatcher, ScriptContext* context) |
145 : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { | 140 : ObjectBackedNativeHandler(context), dispatcher_(dispatcher) { |
146 RouteFunction( | 141 RouteFunction("AttachEvent", base::Bind(&EventBindings::AttachEventHandler, |
147 "AttachEvent", | 142 base::Unretained(this))); |
148 base::Bind(&EventBindings::AttachEvent, base::Unretained(this))); | 143 RouteFunction("DetachEvent", base::Bind(&EventBindings::DetachEventHandler, |
149 RouteFunction( | 144 base::Unretained(this))); |
150 "DetachEvent", | |
151 base::Bind(&EventBindings::DetachEvent, base::Unretained(this))); | |
152 RouteFunction( | 145 RouteFunction( |
153 "AttachFilteredEvent", | 146 "AttachFilteredEvent", |
154 base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this))); | 147 base::Bind(&EventBindings::AttachFilteredEvent, base::Unretained(this))); |
155 RouteFunction( | 148 RouteFunction( |
156 "DetachFilteredEvent", | 149 "DetachFilteredEvent", |
157 base::Bind(&EventBindings::DetachFilteredEvent, base::Unretained(this))); | 150 base::Bind(&EventBindings::DetachFilteredEvent, base::Unretained(this))); |
158 RouteFunction("MatchAgainstEventFilter", | 151 RouteFunction("MatchAgainstEventFilter", |
159 base::Bind(&EventBindings::MatchAgainstEventFilter, | 152 base::Bind(&EventBindings::MatchAgainstEventFilter, |
160 base::Unretained(this))); | 153 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))); |
161 } | 159 } |
162 | 160 |
163 EventBindings::~EventBindings() {} | 161 EventBindings::~EventBindings() {} |
164 | 162 |
165 // Attach an event name to an object. | 163 void EventBindings::AttachEventHandler( |
166 void EventBindings::AttachEvent( | |
167 const v8::FunctionCallbackInfo<v8::Value>& args) { | 164 const v8::FunctionCallbackInfo<v8::Value>& args) { |
168 CHECK_EQ(1, args.Length()); | 165 CHECK_EQ(1, args.Length()); |
169 CHECK(args[0]->IsString()); | 166 CHECK(args[0]->IsString()); |
| 167 AttachEvent(*v8::String::Utf8Value(args[0])); |
| 168 } |
170 | 169 |
171 std::string event_name = *v8::String::Utf8Value(args[0]); | 170 void EventBindings::AttachEvent(const std::string& event_name) { |
172 | 171 // This method throws an exception if it returns false. |
173 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) | 172 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) |
174 return; | 173 return; |
175 | 174 |
| 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 |
176 const std::string& extension_id = context()->GetExtensionID(); | 184 const std::string& extension_id = context()->GetExtensionID(); |
177 if (IncrementEventListenerCount(context(), event_name) == 1) { | 185 if (IncrementEventListenerCount(context(), event_name) == 1) { |
178 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener( | 186 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddListener( |
179 extension_id, context()->GetURL(), event_name)); | 187 extension_id, context()->GetURL(), event_name)); |
180 } | 188 } |
181 | 189 |
182 // This is called the first time the page has added a listener. Since | 190 // 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 | 191 // the background page is the only lazy page, we know this is the first |
184 // time this listener has been registered. | 192 // time this listener has been registered. |
185 if (IsLazyBackgroundPage(context()->GetRenderView(), | 193 if (IsLazyBackgroundPage(context()->GetRenderView(), |
186 context()->extension())) { | 194 context()->extension())) { |
187 content::RenderThread::Get()->Send( | 195 content::RenderThread::Get()->Send( |
188 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); | 196 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); |
189 } | 197 } |
190 } | 198 } |
191 | 199 |
192 void EventBindings::DetachEvent( | 200 void EventBindings::DetachEventHandler( |
193 const v8::FunctionCallbackInfo<v8::Value>& args) { | 201 const v8::FunctionCallbackInfo<v8::Value>& args) { |
194 CHECK_EQ(2, args.Length()); | 202 CHECK_EQ(2, args.Length()); |
195 CHECK(args[0]->IsString()); | 203 CHECK(args[0]->IsString()); |
196 CHECK(args[1]->IsBoolean()); | 204 CHECK(args[1]->IsBoolean()); |
| 205 DetachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue()); |
| 206 } |
197 | 207 |
198 std::string event_name = *v8::String::Utf8Value(args[0]); | 208 void EventBindings::DetachEvent(const std::string& event_name, bool is_manual) { |
199 bool is_manual = args[1]->BooleanValue(); | 209 // See comment in AttachEvent(). |
| 210 attached_event_names_.erase(event_name); |
200 | 211 |
201 const std::string& extension_id = context()->GetExtensionID(); | 212 const std::string& extension_id = context()->GetExtensionID(); |
| 213 |
202 if (DecrementEventListenerCount(context(), event_name) == 0) { | 214 if (DecrementEventListenerCount(context(), event_name) == 0) { |
203 content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener( | 215 content::RenderThread::Get()->Send(new ExtensionHostMsg_RemoveListener( |
204 extension_id, context()->GetURL(), event_name)); | 216 extension_id, context()->GetURL(), event_name)); |
205 } | 217 } |
206 | 218 |
207 // DetachEvent is called when the last listener for the context is | 219 // DetachEvent is called when the last listener for the context is |
208 // removed. If the context is the background page, and it removes the | 220 // 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 | 221 // last listener manually, then we assume that it is no longer interested |
210 // in being awakened for this event. | 222 // in being awakened for this event. |
211 if (is_manual && IsLazyBackgroundPage(context()->GetRenderView(), | 223 if (is_manual && IsLazyBackgroundPage(context()->GetRenderView(), |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
317 args.GetReturnValue().Set(array); | 329 args.GetReturnValue().Set(array); |
318 } | 330 } |
319 | 331 |
320 scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher( | 332 scoped_ptr<EventMatcher> EventBindings::ParseEventMatcher( |
321 base::DictionaryValue* filter_dict) { | 333 base::DictionaryValue* filter_dict) { |
322 return scoped_ptr<EventMatcher>(new EventMatcher( | 334 return scoped_ptr<EventMatcher>(new EventMatcher( |
323 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), | 335 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), |
324 context()->GetRenderView()->GetRoutingID())); | 336 context()->GetRenderView()->GetRoutingID())); |
325 } | 337 } |
326 | 338 |
| 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 |
327 } // namespace extensions | 350 } // namespace extensions |
OLD | NEW |