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 <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <map> | 9 #include <map> |
10 #include <memory> | 10 #include <memory> |
(...skipping 211 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
222 context->v8_context()), | 222 context->v8_context()), |
223 listener_ids, | 223 listener_ids, |
224 }; | 224 }; |
225 | 225 |
226 context->module_system()->CallModuleMethodSafe( | 226 context->module_system()->CallModuleMethodSafe( |
227 kEventBindings, "dispatchEvent", arraysize(v8_args), v8_args); | 227 kEventBindings, "dispatchEvent", arraysize(v8_args), v8_args); |
228 } | 228 } |
229 | 229 |
230 void EventBindings::AttachEventHandler( | 230 void EventBindings::AttachEventHandler( |
231 const v8::FunctionCallbackInfo<v8::Value>& args) { | 231 const v8::FunctionCallbackInfo<v8::Value>& args) { |
232 CHECK_EQ(1, args.Length()); | 232 CHECK_EQ(2, args.Length()); |
233 CHECK(args[0]->IsString()); | 233 CHECK(args[0]->IsString()); |
234 AttachEvent(*v8::String::Utf8Value(args[0])); | 234 CHECK(args[1]->IsBoolean()); |
| 235 AttachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue()); |
235 } | 236 } |
236 | 237 |
237 void EventBindings::AttachEvent(const std::string& event_name) { | 238 void EventBindings::AttachEvent(const std::string& event_name, |
| 239 bool supports_lazy_events) { |
238 if (!context()->HasAccessOrThrowError(event_name)) | 240 if (!context()->HasAccessOrThrowError(event_name)) |
239 return; | 241 return; |
240 | 242 |
241 // Record the attachment for this context so that events can be detached when | 243 // Record the attachment for this context so that events can be detached when |
242 // the context is destroyed. | 244 // the context is destroyed. |
243 // | 245 // |
244 // Ideally we'd CHECK that it's not already attached, however that's not | 246 // Ideally we'd CHECK that it's not already attached, however that's not |
245 // possible because extensions can create and attach events themselves. Very | 247 // possible because extensions can create and attach events themselves. Very |
246 // silly, but that's the way it is. For an example of this, see | 248 // silly, but that's the way it is. For an example of this, see |
247 // chrome/test/data/extensions/api_test/events/background.js. | 249 // chrome/test/data/extensions/api_test/events/background.js. |
(...skipping 11 matching lines...) Expand all Loading... |
259 : context()->url(), | 261 : context()->url(), |
260 event_name, worker_thread_id)); | 262 event_name, worker_thread_id)); |
261 } | 263 } |
262 | 264 |
263 // This is called the first time the page has added a listener. Since | 265 // This is called the first time the page has added a listener. Since |
264 // the background page is the only lazy page, we know this is the first | 266 // the background page is the only lazy page, we know this is the first |
265 // time this listener has been registered. | 267 // time this listener has been registered. |
266 bool is_lazy_context = | 268 bool is_lazy_context = |
267 ExtensionFrameHelper::IsContextForEventPage(context()) || | 269 ExtensionFrameHelper::IsContextForEventPage(context()) || |
268 context()->context_type() == Feature::SERVICE_WORKER_CONTEXT; | 270 context()->context_type() == Feature::SERVICE_WORKER_CONTEXT; |
269 if (is_lazy_context) { | 271 if (is_lazy_context && supports_lazy_events) { |
270 if (is_service_worker_context) { | 272 if (is_service_worker_context) { |
271 sender->Send(new ExtensionHostMsg_AddLazyServiceWorkerListener( | 273 sender->Send(new ExtensionHostMsg_AddLazyServiceWorkerListener( |
272 extension_id, event_name, context()->service_worker_scope())); | 274 extension_id, event_name, context()->service_worker_scope())); |
273 } else { | 275 } else { |
274 sender->Send( | 276 sender->Send( |
275 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); | 277 new ExtensionHostMsg_AddLazyListener(extension_id, event_name)); |
276 } | 278 } |
277 } | 279 } |
278 } | 280 } |
279 | 281 |
280 void EventBindings::DetachEventHandler( | 282 void EventBindings::DetachEventHandler( |
281 const v8::FunctionCallbackInfo<v8::Value>& args) { | 283 const v8::FunctionCallbackInfo<v8::Value>& args) { |
282 CHECK_EQ(2, args.Length()); | 284 CHECK_EQ(3, args.Length()); |
283 CHECK(args[0]->IsString()); | 285 CHECK(args[0]->IsString()); |
284 CHECK(args[1]->IsBoolean()); | 286 CHECK(args[1]->IsBoolean()); |
285 DetachEvent(*v8::String::Utf8Value(args[0]), args[1]->BooleanValue()); | 287 CHECK(args[2]->IsBoolean()); |
| 288 bool was_manual = args[1]->BooleanValue(); |
| 289 bool supports_lazy_listeners = args[2]->BooleanValue(); |
| 290 DetachEvent(*v8::String::Utf8Value(args[0]), |
| 291 was_manual && supports_lazy_listeners); |
286 } | 292 } |
287 | 293 |
288 void EventBindings::DetachEvent(const std::string& event_name, bool is_manual) { | 294 void EventBindings::DetachEvent(const std::string& event_name, |
| 295 bool remove_lazy_listener) { |
289 // See comment in AttachEvent(). | 296 // See comment in AttachEvent(). |
290 attached_event_names_.erase(event_name); | 297 attached_event_names_.erase(event_name); |
291 | 298 |
292 int worker_thread_id = content::WorkerThread::GetCurrentId(); | 299 int worker_thread_id = content::WorkerThread::GetCurrentId(); |
293 const bool is_service_worker_context = worker_thread_id != kNonWorkerThreadId; | 300 const bool is_service_worker_context = worker_thread_id != kNonWorkerThreadId; |
294 IPC::Sender* sender = GetIPCSender(); | 301 IPC::Sender* sender = GetIPCSender(); |
295 const std::string& extension_id = context()->GetExtensionID(); | 302 const std::string& extension_id = context()->GetExtensionID(); |
296 | 303 |
297 if (DecrementEventListenerCount(context(), event_name) == 0) { | 304 if (DecrementEventListenerCount(context(), event_name) == 0) { |
298 sender->Send(new ExtensionHostMsg_RemoveListener( | 305 sender->Send(new ExtensionHostMsg_RemoveListener( |
299 extension_id, | 306 extension_id, |
300 is_service_worker_context ? context()->service_worker_scope() | 307 is_service_worker_context ? context()->service_worker_scope() |
301 : context()->url(), | 308 : context()->url(), |
302 event_name, worker_thread_id)); | 309 event_name, worker_thread_id)); |
303 } | 310 } |
304 | 311 |
305 // DetachEvent is called when the last listener for the context is | 312 // DetachEvent is called when the last listener for the context is |
306 // removed. If the context is the background page or service worker, and it | 313 // removed. If the context is the background page or service worker, and it |
307 // removes the last listener manually, then we assume that it is no longer | 314 // removes the last listener manually, then we assume that it is no longer |
308 // interested in being awakened for this event. | 315 // interested in being awakened for this event. |
309 if (is_manual) { | 316 if (remove_lazy_listener) { |
310 bool is_lazy_context = | 317 bool is_lazy_context = |
311 ExtensionFrameHelper::IsContextForEventPage(context()) || | 318 ExtensionFrameHelper::IsContextForEventPage(context()) || |
312 context()->context_type() == Feature::SERVICE_WORKER_CONTEXT; | 319 context()->context_type() == Feature::SERVICE_WORKER_CONTEXT; |
313 if (is_lazy_context) { | 320 if (is_lazy_context) { |
314 if (is_service_worker_context) { | 321 if (is_service_worker_context) { |
315 sender->Send(new ExtensionHostMsg_RemoveLazyServiceWorkerListener( | 322 sender->Send(new ExtensionHostMsg_RemoveLazyServiceWorkerListener( |
316 extension_id, event_name, context()->service_worker_scope())); | 323 extension_id, event_name, context()->service_worker_scope())); |
317 } else { | 324 } else { |
318 sender->Send( | 325 sender->Send( |
319 new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); | 326 new ExtensionHostMsg_RemoveLazyListener(extension_id, event_name)); |
320 } | 327 } |
321 } | 328 } |
322 } | 329 } |
323 } | 330 } |
324 | 331 |
325 // MatcherID AttachFilteredEvent(string event_name, object filter) | 332 // MatcherID AttachFilteredEvent(string event_name, object filter) |
326 // event_name - Name of the event to attach. | 333 // event_name - Name of the event to attach. |
327 // filter - Which instances of the named event are we interested in. | 334 // filter - Which instances of the named event are we interested in. |
328 // returns the id assigned to the listener, which will be provided to calls to | 335 // returns the id assigned to the listener, which will be provided to calls to |
329 // dispatchEvent(). | 336 // dispatchEvent(). |
330 void EventBindings::AttachFilteredEvent( | 337 void EventBindings::AttachFilteredEvent( |
331 const v8::FunctionCallbackInfo<v8::Value>& args) { | 338 const v8::FunctionCallbackInfo<v8::Value>& args) { |
332 CHECK_EQ(2, args.Length()); | 339 CHECK_EQ(3, args.Length()); |
333 CHECK(args[0]->IsString()); | 340 CHECK(args[0]->IsString()); |
334 CHECK(args[1]->IsObject()); | 341 CHECK(args[1]->IsObject()); |
| 342 CHECK(args[2]->IsBoolean()); |
335 | 343 |
336 std::string event_name = *v8::String::Utf8Value(args[0]); | 344 std::string event_name = *v8::String::Utf8Value(args[0]); |
337 if (!context()->HasAccessOrThrowError(event_name)) | 345 if (!context()->HasAccessOrThrowError(event_name)) |
338 return; | 346 return; |
339 | 347 |
340 std::unique_ptr<base::DictionaryValue> filter; | 348 std::unique_ptr<base::DictionaryValue> filter; |
341 { | 349 { |
342 std::unique_ptr<base::Value> filter_value = | 350 std::unique_ptr<base::Value> filter_value = |
343 content::V8ValueConverter::Create()->FromV8Value( | 351 content::V8ValueConverter::Create()->FromV8Value( |
344 v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()); | 352 v8::Local<v8::Object>::Cast(args[1]), context()->v8_context()); |
345 if (!filter_value || !filter_value->IsType(base::Value::Type::DICTIONARY)) { | 353 if (!filter_value || !filter_value->IsType(base::Value::Type::DICTIONARY)) { |
346 args.GetReturnValue().Set(static_cast<int32_t>(-1)); | 354 args.GetReturnValue().Set(static_cast<int32_t>(-1)); |
347 return; | 355 return; |
348 } | 356 } |
349 filter = base::DictionaryValue::From(std::move(filter_value)); | 357 filter = base::DictionaryValue::From(std::move(filter_value)); |
350 } | 358 } |
351 | 359 |
| 360 bool supports_lazy_events = args[2]->BooleanValue(); |
| 361 |
352 int id = g_event_filter.Get().AddEventMatcher( | 362 int id = g_event_filter.Get().AddEventMatcher( |
353 event_name, | 363 event_name, |
354 base::MakeUnique<EventMatcher>( | 364 base::MakeUnique<EventMatcher>( |
355 std::move(filter), context()->GetRenderFrame()->GetRoutingID())); | 365 std::move(filter), context()->GetRenderFrame()->GetRoutingID())); |
356 if (id == -1) { | 366 if (id == -1) { |
357 args.GetReturnValue().Set(static_cast<int32_t>(-1)); | 367 args.GetReturnValue().Set(static_cast<int32_t>(-1)); |
358 return; | 368 return; |
359 } | 369 } |
360 attached_matcher_ids_.insert(id); | 370 attached_matcher_ids_.insert(id); |
361 | 371 |
362 // Only send IPCs the first time a filter gets added. | 372 // Only send IPCs the first time a filter gets added. |
363 const EventMatcher* matcher = g_event_filter.Get().GetEventMatcher(id); | 373 const EventMatcher* matcher = g_event_filter.Get().GetEventMatcher(id); |
364 DCHECK(matcher); | 374 DCHECK(matcher); |
365 base::DictionaryValue* filter_weak = matcher->value(); | 375 base::DictionaryValue* filter_weak = matcher->value(); |
366 std::string extension_id = context()->GetExtensionID(); | 376 std::string extension_id = context()->GetExtensionID(); |
367 if (AddFilter(event_name, extension_id, *filter_weak)) { | 377 if (AddFilter(event_name, extension_id, *filter_weak)) { |
368 bool lazy = ExtensionFrameHelper::IsContextForEventPage(context()); | 378 bool lazy = supports_lazy_events && |
| 379 ExtensionFrameHelper::IsContextForEventPage(context()); |
369 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddFilteredListener( | 380 content::RenderThread::Get()->Send(new ExtensionHostMsg_AddFilteredListener( |
370 extension_id, event_name, *filter_weak, lazy)); | 381 extension_id, event_name, *filter_weak, lazy)); |
371 } | 382 } |
372 | 383 |
373 args.GetReturnValue().Set(static_cast<int32_t>(id)); | 384 args.GetReturnValue().Set(static_cast<int32_t>(id)); |
374 } | 385 } |
375 | 386 |
376 void EventBindings::DetachFilteredEventHandler( | 387 void EventBindings::DetachFilteredEventHandler( |
377 const v8::FunctionCallbackInfo<v8::Value>& args) { | 388 const v8::FunctionCallbackInfo<v8::Value>& args) { |
378 CHECK_EQ(2, args.Length()); | 389 CHECK_EQ(3, args.Length()); |
379 CHECK(args[0]->IsInt32()); | 390 CHECK(args[0]->IsInt32()); |
380 CHECK(args[1]->IsBoolean()); | 391 CHECK(args[1]->IsBoolean()); |
381 DetachFilteredEvent(args[0]->Int32Value(), args[1]->BooleanValue()); | 392 CHECK(args[2]->IsBoolean()); |
| 393 bool was_manual = args[1]->BooleanValue(); |
| 394 bool supports_lazy_events = args[2]->BooleanValue(); |
| 395 DetachFilteredEvent(args[0]->Int32Value(), |
| 396 was_manual && supports_lazy_events); |
382 } | 397 } |
383 | 398 |
384 void EventBindings::DetachFilteredEvent(int matcher_id, bool is_manual) { | 399 void EventBindings::DetachFilteredEvent(int matcher_id, |
| 400 bool remove_lazy_event) { |
385 EventFilter& event_filter = g_event_filter.Get(); | 401 EventFilter& event_filter = g_event_filter.Get(); |
386 EventMatcher* event_matcher = event_filter.GetEventMatcher(matcher_id); | 402 EventMatcher* event_matcher = event_filter.GetEventMatcher(matcher_id); |
387 | 403 |
388 const std::string& event_name = event_filter.GetEventName(matcher_id); | 404 const std::string& event_name = event_filter.GetEventName(matcher_id); |
389 | 405 |
390 // Only send IPCs the last time a filter gets removed. | 406 // Only send IPCs the last time a filter gets removed. |
391 std::string extension_id = context()->GetExtensionID(); | 407 std::string extension_id = context()->GetExtensionID(); |
392 if (RemoveFilter(event_name, extension_id, event_matcher->value())) { | 408 if (RemoveFilter(event_name, extension_id, event_matcher->value())) { |
393 bool remove_lazy = | 409 bool remove_lazy = remove_lazy_event && |
394 is_manual && ExtensionFrameHelper::IsContextForEventPage(context()); | 410 ExtensionFrameHelper::IsContextForEventPage(context()); |
395 content::RenderThread::Get()->Send( | 411 content::RenderThread::Get()->Send( |
396 new ExtensionHostMsg_RemoveFilteredListener( | 412 new ExtensionHostMsg_RemoveFilteredListener( |
397 extension_id, event_name, *event_matcher->value(), remove_lazy)); | 413 extension_id, event_name, *event_matcher->value(), remove_lazy)); |
398 } | 414 } |
399 | 415 |
400 event_filter.RemoveEventMatcher(matcher_id); | 416 event_filter.RemoveEventMatcher(matcher_id); |
401 attached_matcher_ids_.erase(matcher_id); | 417 attached_matcher_ids_.erase(matcher_id); |
402 } | 418 } |
403 | 419 |
404 void EventBindings::AttachUnmanagedEvent( | 420 void EventBindings::AttachUnmanagedEvent( |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
446 for (int matcher_id : attached_matcher_ids_safe) { | 462 for (int matcher_id : attached_matcher_ids_safe) { |
447 DetachFilteredEvent(matcher_id, false /* is_manual */); | 463 DetachFilteredEvent(matcher_id, false /* is_manual */); |
448 } | 464 } |
449 DCHECK(attached_matcher_ids_.empty()) | 465 DCHECK(attached_matcher_ids_.empty()) |
450 << "Filtered events cannot be attached during invalidation"; | 466 << "Filtered events cannot be attached during invalidation"; |
451 | 467 |
452 g_unmanaged_listeners.Get().erase(context()); | 468 g_unmanaged_listeners.Get().erase(context()); |
453 } | 469 } |
454 | 470 |
455 } // namespace extensions | 471 } // namespace extensions |
OLD | NEW |