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 3 // found in the LICENSE file. 4 5 #include "chrome/renderer/extensions/event_bindings.h" 6 7 #include <vector> 8 9 #include "base/basictypes.h" 10 #include "base/bind.h" 11 #include "base/lazy_instance.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/message_loop/message_loop.h" 14 #include "chrome/common/extensions/background_info.h" 15 #include "chrome/common/extensions/extension.h" 16 #include "chrome/common/extensions/extension_messages.h" 17 #include "chrome/common/extensions/extension_set.h" 18 #include "chrome/common/extensions/value_counter.h" 19 #include "chrome/common/url_constants.h" 20 #include "chrome/renderer/extensions/chrome_v8_context.h" 21 #include "chrome/renderer/extensions/chrome_v8_context_set.h" 22 #include "chrome/renderer/extensions/chrome_v8_extension.h" 23 #include "chrome/renderer/extensions/dispatcher.h" 24 #include "chrome/renderer/extensions/event_bindings.h" 25 #include "chrome/renderer/extensions/extension_helper.h" 26 #include "chrome/renderer/extensions/user_script_slave.h" 27 #include "content/public/renderer/render_thread.h" 28 #include "content/public/renderer/render_view.h" 29 #include "content/public/renderer/v8_value_converter.h" 30 #include "extensions/common/event_filter.h" 31 #include "extensions/common/view_type.h" 32 #include "grit/renderer_resources.h" 33 #include "third_party/WebKit/public/platform/WebURL.h" 34 #include "third_party/WebKit/public/platform/WebURLRequest.h" 35 #include "third_party/WebKit/public/web/WebDocument.h" 36 #include "third_party/WebKit/public/web/WebFrame.h" 37 #include "third_party/WebKit/public/web/WebView.h" 38 #include "url/gurl.h" 39 #include "v8/include/v8.h" 40 41 using WebKit::WebFrame; 42 using WebKit::WebURL; 43 using content::RenderThread; 44 45 namespace extensions { 46 47 namespace { 48 49 // A map of event names to the number of contexts listening to that event. 50 // We notify the browser about event listeners when we transition between 0 51 // and 1. 52 typedef std::map<std::string, int> EventListenerCounts; 53 54 // A map of extension IDs to listener counts for that extension. 55 base::LazyInstance<std::map<std::string, EventListenerCounts> > 56 g_listener_counts = LAZY_INSTANCE_INITIALIZER; 57 58 // A map of event names to a (filter -> count) map. The map is used to keep 59 // track of which filters are in effect for which events. 60 // We notify the browser about filtered event listeners when we transition 61 // between 0 and 1. 62 typedef std::map<std::string, linked_ptr<ValueCounter> > 63 FilteredEventListenerCounts; 64 65 // A map of extension IDs to filtered listener counts for that extension. 66 base::LazyInstance<std::map<std::string, FilteredEventListenerCounts> > 67 g_filtered_listener_counts = LAZY_INSTANCE_INITIALIZER; 68 69 base::LazyInstance<EventFilter> g_event_filter = LAZY_INSTANCE_INITIALIZER; 70 71 // TODO(koz): Merge this into EventBindings. 72 class ExtensionImpl : public ChromeV8Extension { 73 public: 74 explicit ExtensionImpl(Dispatcher* dispatcher, ChromeV8Context* context) 75 : ChromeV8Extension(dispatcher, context) { 76 RouteFunction("AttachEvent", 77 base::Bind(&ExtensionImpl::AttachEvent, base::Unretained(this))); 78 RouteFunction("DetachEvent", 79 base::Bind(&ExtensionImpl::DetachEvent, base::Unretained(this))); 80 RouteFunction("AttachFilteredEvent", 81 base::Bind(&ExtensionImpl::AttachFilteredEvent, 82 base::Unretained(this))); 83 RouteFunction("DetachFilteredEvent", 84 base::Bind(&ExtensionImpl::DetachFilteredEvent, 85 base::Unretained(this))); 86 RouteFunction("MatchAgainstEventFilter", 87 base::Bind(&ExtensionImpl::MatchAgainstEventFilter, 88 base::Unretained(this))); 89 } 90 91 virtual ~ExtensionImpl() {} 92 93 // Attach an event name to an object. 94 void AttachEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { 95 CHECK_EQ(1, args.Length()); 96 CHECK(args[0]->IsString()); 97 98 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); 99 100 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) 101 return; 102 103 std::string extension_id = context()->GetExtensionID(); 104 EventListenerCounts& listener_counts = 105 g_listener_counts.Get()[extension_id]; 106 if (++listener_counts[event_name] == 1) { 107 content::RenderThread::Get()->Send( 108 new ExtensionHostMsg_AddListener(extension_id, event_name)); 109 } 110 111 // 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 // time this listener has been registered. 114 if (IsLazyBackgroundPage(GetRenderView(), context()->extension())) { 115 content::RenderThread::Get()->Send( 116 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); 117 } 118 } 119 120 void DetachEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { 121 CHECK_EQ(2, args.Length()); 122 CHECK(args[0]->IsString()); 123 CHECK(args[1]->IsBoolean()); 124 125 std::string event_name = *v8::String::AsciiValue(args[0]); 126 bool is_manual = args[1]->BooleanValue(); 127 128 std::string extension_id = context()->GetExtensionID(); 129 EventListenerCounts& listener_counts = 130 g_listener_counts.Get()[extension_id]; 131 132 if (--listener_counts[event_name] == 0) { 133 content::RenderThread::Get()->Send( 134 new ExtensionHostMsg_RemoveListener(extension_id, event_name)); 135 } 136 137 // DetachEvent is called when the last listener for the context is 138 // removed. If the context is the background page, and it removes the 139 // last listener manually, then we assume that it is no longer interested 140 // in being awakened for this event. 141 if (is_manual && IsLazyBackgroundPage(GetRenderView(), 142 context()->extension())) { 143 content::RenderThread::Get()->Send( 144 new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); 145 } 146 } 147 148 // MatcherID AttachFilteredEvent(string event_name, object filter) 149 // event_name - Name of the event to attach. 150 // filter - Which instances of the named event are we interested in. 151 // returns the id assigned to the listener, which will be returned from calls 152 // to MatchAgainstEventFilter where this listener matches. 153 void AttachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { 154 CHECK_EQ(2, args.Length()); 155 CHECK(args[0]->IsString()); 156 CHECK(args[1]->IsObject()); 157 158 std::string event_name = *v8::String::AsciiValue(args[0]); 159 160 // This method throws an exception if it returns false. 161 if (!dispatcher_->CheckContextAccessToExtensionAPI(event_name, context())) 162 return; 163 164 std::string extension_id = context()->GetExtensionID(); 165 if (extension_id.empty()) { 166 args.GetReturnValue().Set(static_cast<int32_t>(-1)); 167 return; 168 } 169 170 scoped_ptr<base::DictionaryValue> filter; 171 scoped_ptr<content::V8ValueConverter> converter( 172 content::V8ValueConverter::create()); 173 174 base::DictionaryValue* filter_dict = NULL; 175 base::Value* filter_value = 176 converter->FromV8Value(args[1]->ToObject(), context()->v8_context()); 177 if (!filter_value) { 178 args.GetReturnValue().Set(static_cast<int32_t>(-1)); 179 return; 180 } 181 if (!filter_value->GetAsDictionary(&filter_dict)) { 182 delete filter_value; 183 args.GetReturnValue().Set(static_cast<int32_t>(-1)); 184 return; 185 } 186 187 filter.reset(filter_dict); 188 EventFilter& event_filter = g_event_filter.Get(); 189 int id = event_filter.AddEventMatcher(event_name, ParseEventMatcher( 190 filter.get())); 191 192 // Only send IPCs the first time a filter gets added. 193 if (AddFilter(event_name, extension_id, filter.get())) { 194 bool lazy = IsLazyBackgroundPage(GetRenderView(), context()->extension()); 195 content::RenderThread::Get()->Send( 196 new ExtensionHostMsg_AddFilteredListener(extension_id, event_name, 197 *filter, lazy)); 198 } 199 200 args.GetReturnValue().Set(static_cast<int32_t>(id)); 201 } 202 203 // Add a filter to |event_name| in |extension_id|, returning true if it 204 // was the first filter for that event in that extension. 205 static bool AddFilter(const std::string& event_name, 206 const std::string& extension_id, 207 base::DictionaryValue* filter) { 208 FilteredEventListenerCounts& counts = 209 g_filtered_listener_counts.Get()[extension_id]; 210 FilteredEventListenerCounts::iterator it = counts.find(event_name); 211 if (it == counts.end()) 212 counts[event_name].reset(new ValueCounter); 213 214 int result = counts[event_name]->Add(*filter); 215 return 1 == result; 216 } 217 218 // Remove a filter from |event_name| in |extension_id|, returning true if it 219 // was the last filter for that event in that extension. 220 static bool RemoveFilter(const std::string& event_name, 221 const std::string& extension_id, 222 base::DictionaryValue* filter) { 223 FilteredEventListenerCounts& counts = 224 g_filtered_listener_counts.Get()[extension_id]; 225 FilteredEventListenerCounts::iterator it = counts.find(event_name); 226 if (it == counts.end()) 227 return false; 228 return 0 == it->second->Remove(*filter); 229 } 230 231 // void DetachFilteredEvent(int id, bool manual) 232 // id - Id of the event to detach. 233 // manual - false if this is part of the extension unload process where all 234 // listeners are automatically detached. 235 void DetachFilteredEvent(const v8::FunctionCallbackInfo<v8::Value>& args) { 236 CHECK_EQ(2, args.Length()); 237 CHECK(args[0]->IsInt32()); 238 CHECK(args[1]->IsBoolean()); 239 bool is_manual = args[1]->BooleanValue(); 240 241 std::string extension_id = context()->GetExtensionID(); 242 if (extension_id.empty()) 243 return; 244 245 int matcher_id = args[0]->Int32Value(); 246 EventFilter& event_filter = g_event_filter.Get(); 247 EventMatcher* event_matcher = 248 event_filter.GetEventMatcher(matcher_id); 249 250 const std::string& event_name = event_filter.GetEventName(matcher_id); 251 252 // Only send IPCs the last time a filter gets removed. 253 if (RemoveFilter(event_name, extension_id, event_matcher->value())) { 254 bool lazy = is_manual && IsLazyBackgroundPage(GetRenderView(), 255 context()->extension()); 256 content::RenderThread::Get()->Send( 257 new ExtensionHostMsg_RemoveFilteredListener(extension_id, event_name, 258 *event_matcher->value(), 259 lazy)); 260 } 261 262 event_filter.RemoveEventMatcher(matcher_id); 263 } 264 265 void MatchAgainstEventFilter( 266 const v8::FunctionCallbackInfo<v8::Value>& args) { 267 typedef std::set<EventFilter::MatcherID> MatcherIDs; 268 EventFilter& event_filter = g_event_filter.Get(); 269 std::string event_name = *v8::String::AsciiValue(args[0]->ToString()); 270 EventFilteringInfo info = ParseFromObject(args[1]->ToObject()); 271 // Only match events routed to this context's RenderView or ones that don't 272 // have a routingId in their filter. 273 MatcherIDs matched_event_filters = event_filter.MatchEvent( 274 event_name, info, context()->GetRenderView()->GetRoutingID()); 275 v8::Handle<v8::Array> array(v8::Array::New(matched_event_filters.size())); 276 int i = 0; 277 for (MatcherIDs::iterator it = matched_event_filters.begin(); 278 it != matched_event_filters.end(); ++it) { 279 array->Set(v8::Integer::New(i++), v8::Integer::New(*it)); 280 } 281 args.GetReturnValue().Set(array); 282 } 283 284 static EventFilteringInfo ParseFromObject(v8::Handle<v8::Object> object) { 285 EventFilteringInfo info; 286 v8::Handle<v8::String> url(v8::String::New("url")); 287 if (object->Has(url)) { 288 v8::Handle<v8::Value> url_value(object->Get(url)); 289 info.SetURL(GURL(*v8::String::AsciiValue(url_value))); 290 } 291 v8::Handle<v8::String> instance_id(v8::String::New("instanceId")); 292 if (object->Has(instance_id)) { 293 v8::Handle<v8::Value> instance_id_value(object->Get(instance_id)); 294 info.SetInstanceID(instance_id_value->IntegerValue()); 295 } 296 return info; 297 } 298 299 private: 300 static bool IsLazyBackgroundPage(content::RenderView* render_view, 301 const Extension* extension) { 302 if (!render_view) 303 return false; 304 ExtensionHelper* helper = ExtensionHelper::Get(render_view); 305 return (extension && BackgroundInfo::HasLazyBackgroundPage(extension) && 306 helper->view_type() == VIEW_TYPE_EXTENSION_BACKGROUND_PAGE); 307 } 308 309 scoped_ptr<EventMatcher> ParseEventMatcher( 310 base::DictionaryValue* filter_dict) { 311 return scoped_ptr<EventMatcher>(new EventMatcher( 312 scoped_ptr<base::DictionaryValue>(filter_dict->DeepCopy()), 313 context()->GetRenderView()->GetRoutingID())); 314 } 315 }; 316 317 } // namespace 318 319 // static 320 ChromeV8Extension* EventBindings::Create(Dispatcher* dispatcher, 321 ChromeV8Context* context) { 322 return new ExtensionImpl(dispatcher, context); 323 } 324 325 } // namespace extensions 326