| 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 |