| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "chrome/renderer/extensions/event_bindings.h" | 5 #include "chrome/renderer/extensions/event_bindings.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 | 65 |
| 66 // A map of extension IDs to filtered listener counts for that extension. | 66 // A map of extension IDs to filtered listener counts for that extension. |
| 67 base::LazyInstance<std::map<std::string, FilteredEventListenerCounts> > | 67 base::LazyInstance<std::map<std::string, FilteredEventListenerCounts> > |
| 68 g_filtered_listener_counts = LAZY_INSTANCE_INITIALIZER; | 68 g_filtered_listener_counts = LAZY_INSTANCE_INITIALIZER; |
| 69 | 69 |
| 70 base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; | 70 base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; |
| 71 | 71 |
| 72 // TODO(koz): Merge this into EventBindings. | 72 // TODO(koz): Merge this into EventBindings. |
| 73 class ExtensionImpl : public ChromeV8Extension { | 73 class ExtensionImpl : public ChromeV8Extension { |
| 74 public: | 74 public: |
| 75 explicit ExtensionImpl(Dispatcher* dispatcher) | 75 explicit ExtensionImpl(Dispatcher* dispatcher, |
| 76 : ChromeV8Extension(dispatcher) { | 76 v8::Handle<v8::Context> v8_context) |
| 77 : ChromeV8Extension(dispatcher, v8_context) { |
| 77 RouteStaticFunction("AttachEvent", &AttachEvent); | 78 RouteStaticFunction("AttachEvent", &AttachEvent); |
| 78 RouteStaticFunction("DetachEvent", &DetachEvent); | 79 RouteStaticFunction("DetachEvent", &DetachEvent); |
| 79 RouteStaticFunction("AttachFilteredEvent", &AttachFilteredEvent); | 80 RouteStaticFunction("AttachFilteredEvent", &AttachFilteredEvent); |
| 80 RouteStaticFunction("DetachFilteredEvent", &DetachFilteredEvent); | 81 RouteStaticFunction("DetachFilteredEvent", &DetachFilteredEvent); |
| 81 RouteStaticFunction("MatchAgainstEventFilter", &MatchAgainstEventFilter); | 82 RouteStaticFunction("MatchAgainstEventFilter", &MatchAgainstEventFilter); |
| 82 } | 83 } |
| 83 | 84 |
| 84 virtual ~ExtensionImpl() {} | 85 virtual ~ExtensionImpl() {} |
| 85 | 86 |
| 86 // Attach an event name to an object. | 87 // Attach an event name to an object. |
| 87 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { | 88 static v8::Handle<v8::Value> AttachEvent(const v8::Arguments& args) { |
| 88 DCHECK(args.Length() == 1); | 89 DCHECK(args.Length() == 1); |
| 89 // TODO(erikkay) should enforce that event name is a string in the bindings | 90 // TODO(erikkay) should enforce that event name is a string in the bindings |
| 90 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); | 91 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); |
| 91 | 92 |
| 92 if (args[0]->IsString()) { | 93 if (args[0]->IsString()) { |
| 93 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); | 94 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
| 94 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); | 95 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); |
| 95 Dispatcher* dispatcher = self->dispatcher(); | 96 Dispatcher* dispatcher = self->dispatcher(); |
| 96 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); | 97 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); |
| 97 ChromeV8Context* context = context_set.GetCurrent(); | 98 ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); |
| 98 CHECK(context); | 99 CHECK(context); |
| 99 | 100 |
| 100 if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name)) | 101 if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context)) |
| 101 return v8::Undefined(); | 102 return v8::Undefined(); |
| 102 | 103 |
| 103 std::string extension_id = context->GetExtensionID(); | 104 std::string extension_id = context->GetExtensionID(); |
| 104 EventListenerCounts& listener_counts = | 105 EventListenerCounts& listener_counts = |
| 105 g_listener_counts.Get()[extension_id]; | 106 g_listener_counts.Get()[extension_id]; |
| 106 if (++listener_counts[event_name] == 1) { | 107 if (++listener_counts[event_name] == 1) { |
| 107 content::RenderThread::Get()->Send( | 108 content::RenderThread::Get()->Send( |
| 108 new ExtensionHostMsg_AddListener(extension_id, event_name)); | 109 new ExtensionHostMsg_AddListener(extension_id, event_name)); |
| 109 } | 110 } |
| 110 | 111 |
| 111 // This is called the first time the page has added a listener. Since | 112 // This is called the first time the page has added a listener. Since |
| 112 // the background page is the only lazy page, we know this is the first | 113 // the background page is the only lazy page, we know this is the first |
| 113 // time this listener has been registered. | 114 // time this listener has been registered. |
| 114 if (IsLazyBackgroundPage(context->extension())) { | 115 if (IsLazyBackgroundPage(self->GetRenderView(), context->extension())) { |
| 115 content::RenderThread::Get()->Send( | 116 content::RenderThread::Get()->Send( |
| 116 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); | 117 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); |
| 117 } | 118 } |
| 118 } | 119 } |
| 119 return v8::Undefined(); | 120 return v8::Undefined(); |
| 120 } | 121 } |
| 121 | 122 |
| 122 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { | 123 static v8::Handle<v8::Value> DetachEvent(const v8::Arguments& args) { |
| 123 DCHECK(args.Length() == 2); | 124 DCHECK(args.Length() == 2); |
| 124 // TODO(erikkay) should enforce that event name is a string in the bindings | 125 // TODO(erikkay) should enforce that event name is a string in the bindings |
| 125 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); | 126 DCHECK(args[0]->IsString() || args[0]->IsUndefined()); |
| 126 | 127 |
| 127 if (args[0]->IsString() && args[1]->IsBoolean()) { | 128 if (args[0]->IsString() && args[1]->IsBoolean()) { |
| 128 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); | 129 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); |
| 129 bool is_manual = args[1]->BooleanValue(); | 130 bool is_manual = args[1]->BooleanValue(); |
| 130 | 131 |
| 131 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); | 132 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
| 132 Dispatcher* dispatcher = self->dispatcher(); | 133 Dispatcher* dispatcher = self->dispatcher(); |
| 133 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); | 134 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); |
| 134 ChromeV8Context* context = context_set.GetCurrent(); | 135 ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); |
| 135 if (!context) | 136 if (!context) |
| 136 return v8::Undefined(); | 137 return v8::Undefined(); |
| 137 | 138 |
| 138 std::string extension_id = context->GetExtensionID(); | 139 std::string extension_id = context->GetExtensionID(); |
| 139 EventListenerCounts& listener_counts = | 140 EventListenerCounts& listener_counts = |
| 140 g_listener_counts.Get()[extension_id]; | 141 g_listener_counts.Get()[extension_id]; |
| 141 | 142 |
| 142 if (--listener_counts[event_name] == 0) { | 143 if (--listener_counts[event_name] == 0) { |
| 143 content::RenderThread::Get()->Send( | 144 content::RenderThread::Get()->Send( |
| 144 new ExtensionHostMsg_RemoveListener(extension_id, event_name)); | 145 new ExtensionHostMsg_RemoveListener(extension_id, event_name)); |
| 145 } | 146 } |
| 146 | 147 |
| 147 // DetachEvent is called when the last listener for the context is | 148 // DetachEvent is called when the last listener for the context is |
| 148 // removed. If the context is the background page, and it removes the | 149 // removed. If the context is the background page, and it removes the |
| 149 // last listener manually, then we assume that it is no longer interested | 150 // last listener manually, then we assume that it is no longer interested |
| 150 // in being awakened for this event. | 151 // in being awakened for this event. |
| 151 if (is_manual && IsLazyBackgroundPage(context->extension())) { | 152 if (is_manual && IsLazyBackgroundPage(self->GetRenderView(), |
| 153 context->extension())) { |
| 152 content::RenderThread::Get()->Send( | 154 content::RenderThread::Get()->Send( |
| 153 new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); | 155 new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); |
| 154 } | 156 } |
| 155 } | 157 } |
| 156 return v8::Undefined(); | 158 return v8::Undefined(); |
| 157 } | 159 } |
| 158 | 160 |
| 159 // MatcherID AttachFilteredEvent(string event_name, object filter) | 161 // MatcherID AttachFilteredEvent(string event_name, object filter) |
| 160 // event_name - Name of the event to attach. | 162 // event_name - Name of the event to attach. |
| 161 // filter - Which instances of the named event are we interested in. | 163 // filter - Which instances of the named event are we interested in. |
| 162 // returns the id assigned to the listener, which will be returned from calls | 164 // returns the id assigned to the listener, which will be returned from calls |
| 163 // to MatchAgainstEventFilter where this listener matches. | 165 // to MatchAgainstEventFilter where this listener matches. |
| 164 static v8::Handle<v8::Value> AttachFilteredEvent(const v8::Arguments& args) { | 166 static v8::Handle<v8::Value> AttachFilteredEvent(const v8::Arguments& args) { |
| 165 DCHECK_EQ(2, args.Length()); | 167 DCHECK_EQ(2, args.Length()); |
| 166 DCHECK(args[0]->IsString()); | 168 DCHECK(args[0]->IsString()); |
| 167 DCHECK(args[1]->IsObject()); | 169 DCHECK(args[1]->IsObject()); |
| 168 | 170 |
| 169 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); | 171 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
| 170 Dispatcher* dispatcher = self->dispatcher(); | 172 Dispatcher* dispatcher = self->dispatcher(); |
| 171 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); | 173 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); |
| 172 ChromeV8Context* context = context_set.GetCurrent(); | 174 ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); |
| 173 DCHECK(context); | 175 DCHECK(context); |
| 174 if (!context) | 176 if (!context) |
| 175 return v8::Integer::New(-1); | 177 return v8::Integer::New(-1); |
| 176 | 178 |
| 177 std::string event_name = *v8::String::AsciiValue(args[0]); | 179 std::string event_name = *v8::String::AsciiValue(args[0]); |
| 178 // This method throws an exception if it returns false. | 180 // This method throws an exception if it returns false. |
| 179 if (!dispatcher->CheckCurrentContextAccessToExtensionAPI(event_name)) | 181 if (!dispatcher->CheckContextAccessToExtensionAPI(event_name, context)) |
| 180 return v8::Undefined(); | 182 return v8::Undefined(); |
| 181 | 183 |
| 182 std::string extension_id = context->GetExtensionID(); | 184 std::string extension_id = context->GetExtensionID(); |
| 183 if (extension_id.empty()) | 185 if (extension_id.empty()) |
| 184 return v8::Integer::New(-1); | 186 return v8::Integer::New(-1); |
| 185 | 187 |
| 186 scoped_ptr<base::DictionaryValue> filter; | 188 scoped_ptr<base::DictionaryValue> filter; |
| 187 scoped_ptr<content::V8ValueConverter> converter( | 189 scoped_ptr<content::V8ValueConverter> converter( |
| 188 content::V8ValueConverter::create()); | 190 content::V8ValueConverter::create()); |
| 189 | 191 |
| 190 base::DictionaryValue* filter_dict = NULL; | 192 base::DictionaryValue* filter_dict = NULL; |
| 191 base::Value* filter_value = converter->FromV8Value(args[1]->ToObject(), | 193 base::Value* filter_value = |
| 192 v8::Context::GetCurrent()); | 194 converter->FromV8Value(args[1]->ToObject(), context->v8_context()); |
| 193 if (!filter_value) | 195 if (!filter_value) |
| 194 return v8::Integer::New(-1); | 196 return v8::Integer::New(-1); |
| 195 if (!filter_value->GetAsDictionary(&filter_dict)) { | 197 if (!filter_value->GetAsDictionary(&filter_dict)) { |
| 196 delete filter_value; | 198 delete filter_value; |
| 197 return v8::Integer::New(-1); | 199 return v8::Integer::New(-1); |
| 198 } | 200 } |
| 199 | 201 |
| 200 filter.reset(filter_dict); | 202 filter.reset(filter_dict); |
| 201 EventFilter& event_filter = g_event_filter.Get(); | 203 EventFilter& event_filter = g_event_filter.Get(); |
| 202 int id = event_filter.AddEventMatcher(event_name, ParseEventMatcher( | 204 int id = event_filter.AddEventMatcher(event_name, ParseEventMatcher( |
| 203 filter.get())); | 205 filter.get())); |
| 204 | 206 |
| 205 // Only send IPCs the first time a filter gets added. | 207 // Only send IPCs the first time a filter gets added. |
| 206 if (AddFilter(event_name, extension_id, filter.get())) { | 208 if (AddFilter(event_name, extension_id, filter.get())) { |
| 207 bool lazy = IsLazyBackgroundPage(context->extension()); | 209 bool lazy = IsLazyBackgroundPage(self->GetRenderView(), |
| 210 context->extension()); |
| 208 content::RenderThread::Get()->Send( | 211 content::RenderThread::Get()->Send( |
| 209 new ExtensionHostMsg_AddFilteredListener(extension_id, event_name, | 212 new ExtensionHostMsg_AddFilteredListener(extension_id, event_name, |
| 210 *filter, lazy)); | 213 *filter, lazy)); |
| 211 } | 214 } |
| 212 | 215 |
| 213 return v8::Integer::New(id); | 216 return v8::Integer::New(id); |
| 214 } | 217 } |
| 215 | 218 |
| 216 // Add a filter to |event_name| in |extension_id|, returning true if it | 219 // Add a filter to |event_name| in |extension_id|, returning true if it |
| 217 // was the first filter for that event in that extension. | 220 // was the first filter for that event in that extension. |
| (...skipping 28 matching lines...) Expand all Loading... |
| 246 // manual - false if this is part of the extension unload process where all | 249 // manual - false if this is part of the extension unload process where all |
| 247 // listeners are automatically detached. | 250 // listeners are automatically detached. |
| 248 static v8::Handle<v8::Value> DetachFilteredEvent(const v8::Arguments& args) { | 251 static v8::Handle<v8::Value> DetachFilteredEvent(const v8::Arguments& args) { |
| 249 DCHECK_EQ(2, args.Length()); | 252 DCHECK_EQ(2, args.Length()); |
| 250 DCHECK(args[0]->IsInt32()); | 253 DCHECK(args[0]->IsInt32()); |
| 251 DCHECK(args[1]->IsBoolean()); | 254 DCHECK(args[1]->IsBoolean()); |
| 252 bool is_manual = args[1]->BooleanValue(); | 255 bool is_manual = args[1]->BooleanValue(); |
| 253 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); | 256 ExtensionImpl* self = GetFromArguments<ExtensionImpl>(args); |
| 254 Dispatcher* dispatcher = self->dispatcher(); | 257 Dispatcher* dispatcher = self->dispatcher(); |
| 255 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); | 258 const ChromeV8ContextSet& context_set = dispatcher->v8_context_set(); |
| 256 ChromeV8Context* context = context_set.GetCurrent(); | 259 ChromeV8Context* context = context_set.GetByV8Context(self->v8_context()); |
| 257 if (!context) | 260 if (!context) |
| 258 return v8::Undefined(); | 261 return v8::Undefined(); |
| 259 | 262 |
| 260 std::string extension_id = context->GetExtensionID(); | 263 std::string extension_id = context->GetExtensionID(); |
| 261 if (extension_id.empty()) | 264 if (extension_id.empty()) |
| 262 return v8::Undefined(); | 265 return v8::Undefined(); |
| 263 | 266 |
| 264 int matcher_id = args[0]->Int32Value(); | 267 int matcher_id = args[0]->Int32Value(); |
| 265 EventFilter& event_filter = g_event_filter.Get(); | 268 EventFilter& event_filter = g_event_filter.Get(); |
| 266 EventMatcher* event_matcher = | 269 EventMatcher* event_matcher = |
| 267 event_filter.GetEventMatcher(matcher_id); | 270 event_filter.GetEventMatcher(matcher_id); |
| 268 | 271 |
| 269 const std::string& event_name = event_filter.GetEventName(matcher_id); | 272 const std::string& event_name = event_filter.GetEventName(matcher_id); |
| 270 | 273 |
| 271 // Only send IPCs the last time a filter gets removed. | 274 // Only send IPCs the last time a filter gets removed. |
| 272 if (RemoveFilter(event_name, extension_id, event_matcher->value())) { | 275 if (RemoveFilter(event_name, extension_id, event_matcher->value())) { |
| 273 bool lazy = is_manual && IsLazyBackgroundPage(context->extension()); | 276 bool lazy = is_manual && IsLazyBackgroundPage(self->GetRenderView(), |
| 277 context->extension()); |
| 274 content::RenderThread::Get()->Send( | 278 content::RenderThread::Get()->Send( |
| 275 new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name, | 279 new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name, |
| 276 *event_matcher->value(), | 280 *event_matcher->value(), |
| 277 lazy)); | 281 lazy)); |
| 278 } | 282 } |
| 279 | 283 |
| 280 event_filter.RemoveEventMatcher(matcher_id); | 284 event_filter.RemoveEventMatcher(matcher_id); |
| 281 | 285 |
| 282 return v8::Undefined(); | 286 return v8::Undefined(); |
| 283 } | 287 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 304 EventFilteringInfo info; | 308 EventFilteringInfo info; |
| 305 v8::Handle<v8::String> url(v8::String::New("url")); | 309 v8::Handle<v8::String> url(v8::String::New("url")); |
| 306 if (object->Has(url)) { | 310 if (object->Has(url)) { |
| 307 v8::Handle<v8::Value> url_value(object->Get(url)); | 311 v8::Handle<v8::Value> url_value(object->Get(url)); |
| 308 info.SetURL(GURL(*v8::String::AsciiValue(url_value))); | 312 info.SetURL(GURL(*v8::String::AsciiValue(url_value))); |
| 309 } | 313 } |
| 310 return info; | 314 return info; |
| 311 } | 315 } |
| 312 | 316 |
| 313 private: | 317 private: |
| 314 static bool IsLazyBackgroundPage(const Extension* extension) { | 318 static bool IsLazyBackgroundPage(content::RenderView* render_view, |
| 315 content::RenderView* render_view = GetCurrentRenderView(); | 319 const Extension* extension) { |
| 316 if (!render_view) | 320 if (!render_view) |
| 317 return false; | 321 return false; |
| 318 | |
| 319 ExtensionHelper* helper = ExtensionHelper::Get(render_view); | 322 ExtensionHelper* helper = ExtensionHelper::Get(render_view); |
| 320 return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && | 323 return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && |
| 321 helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); | 324 helper->view_type() == chrome::VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); |
| 322 } | 325 } |
| 323 | 326 |
| 324 static scoped_ptr<EventMatcher> ParseEventMatcher( | 327 static scoped_ptr<EventMatcher> ParseEventMatcher( |
| 325 base::DictionaryValue* filter_dict) { | 328 base::DictionaryValue* filter_dict) { |
| 326 return scoped_ptr<EventMatcher>(new EventMatcher( | 329 return scoped_ptr<EventMatcher>(new EventMatcher( |
| 327 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()))); | 330 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()))); |
| 328 } | 331 } |
| 329 }; | 332 }; |
| 330 | 333 |
| 331 } // namespace | 334 } // namespace |
| 332 | 335 |
| 333 // static | 336 // static |
| 334 ChromeV8Extension* EventBindings::Get(Dispatcher* dispatcher) { | 337 ChromeV8Extension* EventBindings::Create(Dispatcher* dispatcher, |
| 335 return new ExtensionImpl(dispatcher); | 338 v8::Handle<v8::Context> context) { |
| 339 return new ExtensionImpl(dispatcher, context); |
| 336 } | 340 } |
| 337 | 341 |
| 338 } // namespace extensions | 342 } // namespace extensions |
| OLD | NEW |