Chromium Code Reviews| Index: extensions/browser/events/lazy_event_dispatcher.cc |
| diff --git a/extensions/browser/events/lazy_event_dispatcher.cc b/extensions/browser/events/lazy_event_dispatcher.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..4e0d3533548a5deb80e7436b0503706f7c3e4795 |
| --- /dev/null |
| +++ b/extensions/browser/events/lazy_event_dispatcher.cc |
| @@ -0,0 +1,137 @@ |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "extensions/browser/events/lazy_event_dispatcher.h" |
| + |
| +#include "base/bind.h" |
| +#include "base/memory/ptr_util.h" |
| +#include "extensions/browser/event_router.h" |
| +#include "extensions/browser/extension_registry.h" |
| +#include "extensions/browser/extensions_browser_client.h" |
| +#include "extensions/browser/lazy_background_task_queue.h" |
| +#include "extensions/browser/lazy_context_id.h" |
| +#include "extensions/common/manifest_handlers/incognito_info.h" |
| + |
| +using content::BrowserContext; |
| + |
| +namespace extensions { |
| + |
| +LazyEventDispatcher::LazyEventDispatcher( |
| + BrowserContext* browser_context, |
| + const linked_ptr<Event>& event, |
| + const DispatchFunction& dispatch_function) |
| + : browser_context_(browser_context), |
| + event_(event), |
| + dispatch_function_(dispatch_function) {} |
| + |
| +LazyEventDispatcher::~LazyEventDispatcher() {} |
| + |
| +void LazyEventDispatcher::DispatchToEventPage( |
| + const ExtensionId& extension_id, |
| + const base::DictionaryValue* listener_filter) { |
| + LazyContextId dispatch_context(browser_context_, extension_id); |
| + DispatchToLazyContext(&dispatch_context, listener_filter); |
| +} |
| + |
| +bool LazyEventDispatcher::HasAlreadyDispatched( |
| + BrowserContext* context, |
| + const EventListener* listener) const { |
| + auto dispatch_context = |
| + base::MakeUnique<LazyContextId>(context, listener->extension_id()); |
| + return HasAlreadyDispatchedImpl(dispatch_context.get()); |
| +} |
| + |
| +void LazyEventDispatcher::DispatchToLazyContext( |
| + LazyContextId* dispatch_context, |
| + const base::DictionaryValue* listener_filter) { |
| + const Extension* extension = ExtensionRegistry::Get(browser_context_) |
| + ->enabled_extensions() |
| + .GetByID(dispatch_context->extension_id()); |
| + if (!extension) |
| + return; |
| + |
| + // Check both the original and the incognito browser context to see if we |
| + // should load a non-peristent context (a lazy background page or an |
| + // extension service worker) to handle the event. The latter case |
|
Devlin
2017/06/15 15:09:50
nit: with the addition of the "a lazy background p
lazyboy
2017/06/15 17:33:51
Done.
|
| + // occurs in the case of split-mode extensions. |
| + if (QueueEventDispatch(dispatch_context, extension, listener_filter)) |
| + RecordAlreadyDispatched(dispatch_context); |
| + |
| + BrowserContext* additional_context = GetIncognitoContext(extension); |
| + if (!additional_context) |
| + return; |
| + |
| + dispatch_context->set_browser_context(additional_context); |
| + if (QueueEventDispatch(dispatch_context, extension, listener_filter)) |
| + RecordAlreadyDispatched(dispatch_context); |
| +} |
| + |
| +bool LazyEventDispatcher::QueueEventDispatch( |
| + LazyContextId* dispatch_context, |
| + const Extension* extension, |
| + const base::DictionaryValue* listener_filter) { |
| + if (!EventRouter::CanDispatchEventToBrowserContext( |
| + dispatch_context->browser_context(), extension, *event_)) { |
| + return false; |
| + } |
| + |
| + if (HasAlreadyDispatchedImpl(dispatch_context)) |
| + return false; |
| + |
| + LazyBackgroundTaskQueue* queue = dispatch_context->GetTaskQueue(); |
| + if (!queue->ShouldEnqueueTask(dispatch_context->browser_context(), |
| + extension)) { |
| + return false; |
| + } |
| + |
| + linked_ptr<Event> dispatched_event(event_); |
| + |
| + // If there's a dispatch callback, call it now (rather than dispatch time) |
| + // to avoid lifetime issues. Use a separate copy of the event args, so they |
| + // last until the event is dispatched. |
| + if (!event_->will_dispatch_callback.is_null()) { |
| + dispatched_event.reset(event_->DeepCopy()); |
| + if (!dispatched_event->will_dispatch_callback.Run( |
| + dispatch_context->browser_context(), extension, |
| + dispatched_event.get(), listener_filter)) { |
| + // The event has been canceled. |
| + return true; |
| + } |
| + // Ensure we don't call it again at dispatch time. |
| + dispatched_event->will_dispatch_callback.Reset(); |
| + } |
| + |
| + queue->AddPendingTask(dispatch_context->browser_context(), |
| + dispatch_context->extension_id(), |
| + base::Bind(dispatch_function_, dispatched_event)); |
| + |
| + return true; |
| +} |
| + |
| +bool LazyEventDispatcher::HasAlreadyDispatchedImpl( |
| + const LazyContextId* dispatch_context) const { |
| + DCHECK(dispatch_context->is_for_event_page()); |
| + EventPageDispatchIdentifier dispatch_id(dispatch_context->browser_context(), |
| + dispatch_context->extension_id()); |
| + return base::ContainsKey(dispatched_ids_for_event_page_, dispatch_id); |
| +} |
| + |
| +void LazyEventDispatcher::RecordAlreadyDispatched( |
| + LazyContextId* dispatch_context) { |
| + DCHECK(dispatch_context->is_for_event_page()); |
| + dispatched_ids_for_event_page_.insert(std::make_pair( |
| + dispatch_context->browser_context(), dispatch_context->extension_id())); |
| +} |
| + |
| +BrowserContext* LazyEventDispatcher::GetIncognitoContext( |
| + const Extension* extension) { |
| + if (!IncognitoInfo::IsSplitMode(extension)) |
| + return nullptr; |
| + ExtensionsBrowserClient* browser_client = ExtensionsBrowserClient::Get(); |
| + if (!browser_client->HasOffTheRecordContext(browser_context_)) |
| + return nullptr; |
| + return browser_client->GetOffTheRecordContext(browser_context_); |
| +} |
| + |
| +} // namespace extensions |