diff options
34 files changed, 319 insertions, 240 deletions
diff --git a/chrome/browser/automation/extension_port_container.cc b/chrome/browser/automation/extension_port_container.cc index cdae059..f6b502e 100644 --- a/chrome/browser/automation/extension_port_container.cc +++ b/chrome/browser/automation/extension_port_container.cc @@ -107,9 +107,9 @@ bool ExtensionPortContainer::Send(IPC::Message *message) { } void ExtensionPortContainer::OnExtensionMessageInvoke( + const std::string& extension_id, const std::string& function_name, const ListValue& args, - bool requires_incognito_access, const GURL& event_url) { if (function_name == ExtensionMessageService::kDispatchOnMessage) { DCHECK_EQ(args.GetSize(), 2u); diff --git a/chrome/browser/automation/extension_port_container.h b/chrome/browser/automation/extension_port_container.h index 1325303..d52e9a8 100644 --- a/chrome/browser/automation/extension_port_container.h +++ b/chrome/browser/automation/extension_port_container.h @@ -63,9 +63,9 @@ class ExtensionPortContainer : public IPC::Message::Sender { // Sends a connect response to the external port. void SendConnectionResponse(int connection_id, int port_id); - void OnExtensionMessageInvoke(const std::string& function_name, + void OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool requires_incognito_access, const GURL& event_url); void OnExtensionHandleMessage(const std::string& message, int source_port_id); void OnExtensionPortDisconnected(int source_port_id); diff --git a/chrome/browser/debugger/extension_ports_remote_service.cc b/chrome/browser/debugger/extension_ports_remote_service.cc index 4fc4c68..3596187 100644 --- a/chrome/browser/debugger/extension_ports_remote_service.cc +++ b/chrome/browser/debugger/extension_ports_remote_service.cc @@ -238,9 +238,9 @@ bool ExtensionPortsRemoteService::Send(IPC::Message *message) { } void ExtensionPortsRemoteService::OnExtensionMessageInvoke( + const std::string& extension_id, const std::string& function_name, const ListValue& args, - bool requires_incognito_access, const GURL& event_url) { if (function_name == ExtensionMessageService::kDispatchOnMessage) { DCHECK_EQ(args.GetSize(), 2u); diff --git a/chrome/browser/debugger/extension_ports_remote_service.h b/chrome/browser/debugger/extension_ports_remote_service.h index 0b4c892..bd9b792 100644 --- a/chrome/browser/debugger/extension_ports_remote_service.h +++ b/chrome/browser/debugger/extension_ports_remote_service.h @@ -75,9 +75,9 @@ class ExtensionPortsRemoteService : public DevToolsRemoteListener, const std::string& destination); // Handles a message from the ExtensionMessageService. - void OnExtensionMessageInvoke(const std::string& function_name, + void OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool requires_incognito_access, const GURL& event_url); // Handles a message sent from an extension through the // ExtensionMessageService, to be passed to the external client. diff --git a/chrome/browser/extensions/events_apitest.cc b/chrome/browser/extensions/events_apitest.cc new file mode 100644 index 0000000..8319ae6 --- /dev/null +++ b/chrome/browser/extensions/events_apitest.cc @@ -0,0 +1,10 @@ +// Copyright (c) 2010 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 "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/chrome_switches.h" + +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Events) { + ASSERT_TRUE(RunExtensionTest("events")) << message_; +} diff --git a/chrome/browser/extensions/extension_browser_event_router.cc b/chrome/browser/extensions/extension_browser_event_router.cc index b9ef775..ce58dc7 100644 --- a/chrome/browser/extensions/extension_browser_event_router.cc +++ b/chrome/browser/extensions/extension_browser_event_router.cc @@ -66,21 +66,36 @@ ExtensionBrowserEventRouter* ExtensionBrowserEventRouter::GetInstance() { static void DispatchEvent(Profile* profile, const char* event_name, - const std::string json_args) { + const std::string& json_args) { if (profile->GetExtensionEventRouter()) { profile->GetExtensionEventRouter()->DispatchEventToRenderers( event_name, json_args, profile, GURL()); } } +static void DispatchEventToExtension(Profile* profile, + const std::string& extension_id, + const char* event_name, + const std::string& json_args) { + if (profile->GetExtensionEventRouter()) { + profile->GetExtensionEventRouter()->DispatchEventToExtension( + extension_id, event_name, json_args, profile, GURL()); + } +} + static void DispatchEventWithTab(Profile* profile, + const std::string& extension_id, const char* event_name, const TabContents* tab_contents) { ListValue args; args.Append(ExtensionTabUtil::CreateTabValue(tab_contents)); std::string json_args; base::JSONWriter::Write(&args, false, &json_args); - DispatchEvent(profile, event_name, json_args); + if (!extension_id.empty()) { + DispatchEventToExtension(profile, extension_id, event_name, json_args); + } else { + DispatchEvent(profile, event_name, json_args); + } } static void DispatchSimpleBrowserEvent(Profile* profile, @@ -244,7 +259,8 @@ void ExtensionBrowserEventRouter::OnBrowserSetLastActive( void ExtensionBrowserEventRouter::TabCreatedAt(TabContents* contents, int index, bool foreground) { - DispatchEventWithTab(contents->profile(), events::kOnTabCreated, contents); + DispatchEventWithTab(contents->profile(), "", events::kOnTabCreated, + contents); RegisterForTabNotifications(contents); } @@ -460,8 +476,7 @@ void ExtensionBrowserEventRouter::DispatchOldPageActionEvent( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); - std::string event_name = std::string("pageActions/") + extension_id; - DispatchEvent(profile, event_name.c_str(), json_args); + DispatchEventToExtension(profile, extension_id, "pageActions", json_args); } void ExtensionBrowserEventRouter::PageActionExecuted( @@ -478,9 +493,8 @@ void ExtensionBrowserEventRouter::PageActionExecuted( NULL, NULL, &tab_contents, NULL)) { return; } - std::string event_name = ExtensionEventRouter::GetPerExtensionEventName( - "pageAction.onClicked", extension_id); - DispatchEventWithTab(profile, event_name.c_str(), tab_contents); + DispatchEventWithTab(profile, extension_id, "pageAction.onClicked", + tab_contents); } void ExtensionBrowserEventRouter::BrowserActionExecuted( @@ -489,7 +503,6 @@ void ExtensionBrowserEventRouter::BrowserActionExecuted( int tab_id = 0; if (!ExtensionTabUtil::GetDefaultTab(browser, &tab_contents, &tab_id)) return; - std::string event_name = ExtensionEventRouter::GetPerExtensionEventName( - "browserAction.onClicked", extension_id); - DispatchEventWithTab(profile, event_name.c_str(), tab_contents); + DispatchEventWithTab(profile, extension_id, "browserAction.onClicked", + tab_contents); } diff --git a/chrome/browser/extensions/extension_event_router.cc b/chrome/browser/extensions/extension_event_router.cc index 5e2707e..9d22da7 100644 --- a/chrome/browser/extensions/extension_event_router.cc +++ b/chrome/browser/extensions/extension_event_router.cc @@ -10,6 +10,7 @@ #include "chrome/browser/extensions/extension_processes_api.h" #include "chrome/browser/extensions/extension_processes_api_constants.h" #include "chrome/browser/extensions/extension_tabs_module.h" +#include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/render_process_host.h" #include "chrome/common/extensions/extension.h" @@ -21,26 +22,46 @@ namespace { const char kDispatchEvent[] = "Event.dispatchJSON"; static void DispatchEvent(RenderProcessHost* renderer, + const std::string& extension_id, const std::string& event_name, const std::string& event_args, - bool cross_incognito, const GURL& event_url) { ListValue args; args.Set(0, Value::CreateStringValue(event_name)); args.Set(1, Value::CreateStringValue(event_args)); renderer->Send(new ViewMsg_ExtensionMessageInvoke(MSG_ROUTING_CONTROL, - kDispatchEvent, args, cross_incognito, event_url)); + extension_id, kDispatchEvent, args, event_url)); +} + +static bool CanCrossIncognito(Profile* profile, + const std::string& extension_id) { + // We allow the extension to see events and data from another profile iff it + // uses "spanning" behavior and it has incognito access. "split" mode + // extensions only see events for a matching profile. + Extension* extension = + profile->GetExtensionsService()->GetExtensionById(extension_id, false); + return (profile->GetExtensionsService()->IsIncognitoEnabled(extension) && + !extension->incognito_split_mode()); } } // namespace -// static -std::string ExtensionEventRouter::GetPerExtensionEventName( - const std::string& event_name, const std::string& extension_id) { - // This should match the method we use in extension_process_binding.js when - // setting up the corresponding chrome.Event object. - return event_name + "/" + extension_id; -} +struct ExtensionEventRouter::EventListener { + RenderProcessHost* process; + std::string extension_id; + + explicit EventListener(RenderProcessHost* process, + const std::string& extension_id) + : process(process), extension_id(extension_id) {} + + bool operator<(const EventListener& that) const { + if (process < that.process) + return true; + if (process == that.process && extension_id < that.extension_id) + return true; + return false; + } +}; ExtensionEventRouter::ExtensionEventRouter(Profile* profile) : profile_(profile), @@ -56,14 +77,14 @@ ExtensionEventRouter::~ExtensionEventRouter() { void ExtensionEventRouter::AddEventListener( const std::string& event_name, - int render_process_id) { - DCHECK_EQ(listeners_[event_name].count(render_process_id), 0u) << event_name; - listeners_[event_name].insert(render_process_id); + RenderProcessHost* process, + const std::string& extension_id) { + EventListener listener(process, extension_id); + DCHECK_EQ(listeners_[event_name].count(listener), 0u) << event_name; + listeners_[event_name].insert(listener); - if (extension_devtools_manager_.get()) { - extension_devtools_manager_->AddEventListener(event_name, - render_process_id); - } + if (extension_devtools_manager_.get()) + extension_devtools_manager_->AddEventListener(event_name, process->id()); // We lazily tell the TaskManager to start updating when listeners to the // processes.onUpdated event arrive. @@ -73,15 +94,16 @@ void ExtensionEventRouter::AddEventListener( void ExtensionEventRouter::RemoveEventListener( const std::string& event_name, - int render_process_id) { - DCHECK_EQ(listeners_[event_name].count(render_process_id), 1u) << - " PID=" << render_process_id << " event=" << event_name; - listeners_[event_name].erase(render_process_id); - - if (extension_devtools_manager_.get()) { - extension_devtools_manager_->RemoveEventListener(event_name, - render_process_id); - } + RenderProcessHost* process, + const std::string& extension_id) { + EventListener listener(process, extension_id); + DCHECK_EQ(listeners_[event_name].count(listener), 1u) << + " PID=" << process->id() << " extension=" << extension_id << + " event=" << event_name; + listeners_[event_name].erase(listener); + + if (extension_devtools_manager_.get()) + extension_devtools_manager_->RemoveEventListener(event_name, process->id()); // If a processes.onUpdated event listener is removed (or a process with one // exits), then we let the TaskManager know that it has one fewer listener. @@ -94,9 +116,40 @@ bool ExtensionEventRouter::HasEventListener(const std::string& event_name) { !listeners_[event_name].empty()); } +bool ExtensionEventRouter::ExtensionHasEventListener( + const std::string& extension_id, const std::string& event_name) { + ListenerMap::iterator it = listeners_.find(event_name); + if (it == listeners_.end()) + return false; + + std::set<EventListener>& listeners = it->second; + for (std::set<EventListener>::iterator listener = listeners.begin(); + listener != listeners.end(); ++listener) { + if (listener->extension_id == extension_id) + return true; + } + return false; +} + void ExtensionEventRouter::DispatchEventToRenderers( const std::string& event_name, const std::string& event_args, Profile* restrict_to_profile, const GURL& event_url) { + DispatchEventImpl("", event_name, event_args, restrict_to_profile, event_url); +} + +void ExtensionEventRouter::DispatchEventToExtension( + const std::string& extension_id, + const std::string& event_name, const std::string& event_args, + Profile* restrict_to_profile, const GURL& event_url) { + DCHECK(!extension_id.empty()); + DispatchEventImpl(extension_id, event_name, event_args, restrict_to_profile, + event_url); +} + +void ExtensionEventRouter::DispatchEventImpl( + const std::string& extension_id, + const std::string& event_name, const std::string& event_args, + Profile* restrict_to_profile, const GURL& event_url) { if (!profile_) return; @@ -107,33 +160,30 @@ void ExtensionEventRouter::DispatchEventToRenderers( if (it == listeners_.end()) return; - std::set<int>& pids = it->second; + std::set<EventListener>& listeners = it->second; // Send the event only to renderers that are listening for it. - for (std::set<int>::iterator pid = pids.begin(); pid != pids.end(); ++pid) { - RenderProcessHost* renderer = RenderProcessHost::FromID(*pid); - if (!renderer) - continue; + for (std::set<EventListener>::iterator listener = listeners.begin(); + listener != listeners.end(); ++listener) { if (!ChildProcessSecurityPolicy::GetInstance()-> - HasExtensionBindings(*pid)) { + HasExtensionBindings(listener->process->id())) { // Don't send browser-level events to unprivileged processes. continue; } + if (!extension_id.empty() && extension_id != listener->extension_id) + continue; + // Is this event from a different profile than the renderer (ie, an // incognito tab event sent to a normal process, or vice versa). - bool cross_incognito = - restrict_to_profile && renderer->profile() != restrict_to_profile; - DispatchEvent(renderer, event_name, event_args, cross_incognito, event_url); - } -} + bool cross_incognito = restrict_to_profile && + listener->process->profile() != restrict_to_profile; + if (cross_incognito && !CanCrossIncognito(profile_, listener->extension_id)) + continue; -void ExtensionEventRouter::DispatchEventToExtension( - const std::string& extension_id, - const std::string& event_name, const std::string& event_args, - Profile* restrict_to_profile, const GURL& event_url) { - DispatchEventToRenderers(GetPerExtensionEventName(event_name, extension_id), - event_args, restrict_to_profile, event_url); + DispatchEvent(listener->process, listener->extension_id, + event_name, event_args, event_url); + } } void ExtensionEventRouter::Observe(NotificationType type, @@ -146,9 +196,16 @@ void ExtensionEventRouter::Observe(NotificationType type, // Remove all event listeners associated with this renderer for (ListenerMap::iterator it = listeners_.begin(); it != listeners_.end(); ) { - ListenerMap::iterator current = it++; - if (current->second.count(renderer->id()) != 0) - RemoveEventListener(current->first, renderer->id()); + ListenerMap::iterator current_it = it++; + for (std::set<EventListener>::iterator jt = current_it->second.begin(); + jt != current_it->second.end(); ) { + std::set<EventListener>::iterator current_jt = jt++; + if (current_jt->process == renderer) { + RemoveEventListener(current_it->first, + current_jt->process, + current_jt->extension_id); + } + } } break; } diff --git a/chrome/browser/extensions/extension_event_router.h b/chrome/browser/extensions/extension_event_router.h index 5fb29e4..5e1ca97 100644 --- a/chrome/browser/extensions/extension_event_router.h +++ b/chrome/browser/extensions/extension_event_router.h @@ -24,37 +24,52 @@ class ExtensionEventRouter : public NotificationObserver { explicit ExtensionEventRouter(Profile* profile); ~ExtensionEventRouter(); - // Returns the event name for an event that is extension-specific. - static std::string GetPerExtensionEventName(const std::string& event_name, - const std::string& extension_id); - - // Add or remove |render_process_id| as a listener for |event_name|. + // Add or remove the process/extension pair as a listener for |event_name|. + // Note that multiple extensions can share a process due to process + // collapsing. Also, a single extension can have 2 processes if it is a split + // mode extension. void AddEventListener(const std::string& event_name, - int render_process_id); + RenderProcessHost* process, + const std::string& extension_id); void RemoveEventListener(const std::string& event_name, - int render_process_id); + RenderProcessHost* process, + const std::string& extension_id); // Returns true if there is at least one listener for the given event. bool HasEventListener(const std::string& event_name); + // Returns true if the extension is listening to the given event. + bool ExtensionHasEventListener(const std::string& extension_id, + const std::string& event_name); + // Send an event to every registered extension renderer. If // |restrict_to_profile| is non-NULL, then the event will not be sent to other // profiles unless the extension has permission (e.g. incognito tab update -> // normal profile only works if extension is allowed incognito access). If // |event_url| is not empty, the event is only sent to extension with host // permissions for this url. - virtual void DispatchEventToRenderers( + void DispatchEventToRenderers( const std::string& event_name, const std::string& event_args, Profile* restrict_to_profile, const GURL& event_url); - // Same as above, except use the extension-specific naming scheme for the - // event. This is used by events that are per-extension. + // Same as above, except only send the event to the given extension. void DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, Profile* restrict_to_profile, const GURL& event_url); + protected: + // Shared by DispatchEvent*. If |extension_id| is empty, the event is + // broadcast. + virtual void DispatchEventImpl( + const std::string& extension_id, + const std::string& event_name, const std::string& event_args, + Profile* restrict_to_profile, const GURL& event_url); + private: + // An extension listening to an event. + struct EventListener; + virtual void Observe(NotificationType type, const NotificationSource& source, const NotificationDetails& details); @@ -65,9 +80,9 @@ class ExtensionEventRouter : public NotificationObserver { scoped_refptr<ExtensionDevToolsManager> extension_devtools_manager_; - // A map between an event name and a set of process id's that are listening + // A map between an event name and a set of extensions that are listening // to that event. - typedef std::map<std::string, std::set<int> > ListenerMap; + typedef std::map<std::string, std::set<EventListener> > ListenerMap; ListenerMap listeners_; DISALLOW_COPY_AND_ASSIGN(ExtensionEventRouter); diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 7a99137..e3ca590 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -373,9 +373,6 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( epm->RegisterExtensionProcess(extension_id(), render_view_host->process()->id()); - bool incognito_enabled = - profile()->GetExtensionsService()->IsIncognitoEnabled(extension); - // If the extension has permission to load chrome://favicon/ resources we need // to make sure that the DOMUIFavIconSource is registered with the // ChromeURLDataManager. @@ -395,8 +392,6 @@ ExtensionFunctionDispatcher::ExtensionFunctionDispatcher( extension->id(), extension->api_permissions())); render_view_host->Send(new ViewMsg_Extension_SetHostPermissions( extension->url(), extension->host_permissions())); - render_view_host->Send(new ViewMsg_Extension_ExtensionSetIncognitoEnabled( - extension->id(), incognito_enabled, extension->incognito_split_mode())); NotificationService::current()->Notify( NotificationType::EXTENSION_FUNCTION_DISPATCHER_CREATED, diff --git a/chrome/browser/extensions/extension_menu_manager.cc b/chrome/browser/extensions/extension_menu_manager.cc index d5f33e6..307c339 100644 --- a/chrome/browser/extensions/extension_menu_manager.cc +++ b/chrome/browser/extensions/extension_menu_manager.cc @@ -440,9 +440,9 @@ void ExtensionMenuManager::ExecuteCommand( std::string json_args; base::JSONWriter::Write(&args, false, &json_args); - std::string event_name = "contextMenus/" + item->extension_id(); - event_router->DispatchEventToRenderers( - event_name, json_args, profile, GURL()); + std::string event_name = "contextMenus"; + event_router->DispatchEventToExtension( + item->extension_id(), event_name, json_args, profile, GURL()); } void ExtensionMenuManager::Observe(NotificationType type, diff --git a/chrome/browser/extensions/extension_menu_manager_unittest.cc b/chrome/browser/extensions/extension_menu_manager_unittest.cc index 3dc6f69..0b653ba 100644 --- a/chrome/browser/extensions/extension_menu_manager_unittest.cc +++ b/chrome/browser/extensions/extension_menu_manager_unittest.cc @@ -336,10 +336,11 @@ class MockExtensionEventRouter : public ExtensionEventRouter { explicit MockExtensionEventRouter(Profile* profile) : ExtensionEventRouter(profile) {} - MOCK_METHOD4(DispatchEventToRenderers, void(const std::string& event_name, - const std::string& event_args, - Profile* source_profile, - const GURL& event_url)); + MOCK_METHOD5(DispatchEventImpl, void(const std::string& extension_id, + const std::string& event_name, + const std::string& event_args, + Profile* source_profile, + const GURL& event_url)); private: DISALLOW_COPY_AND_ASSIGN(MockExtensionEventRouter); @@ -414,15 +415,17 @@ TEST_F(ExtensionMenuManagerTest, ExecuteCommand) { .WillOnce(Return(mock_event_router.get())); // Use the magic of googlemock to save a parameter to our mock's - // DispatchEventToRenderers method into event_args. + // DispatchEventImpl method into event_args. std::string event_args; - std::string expected_event_name = "contextMenus/" + item->extension_id(); + std::string expected_event_name = "contextMenus"; EXPECT_CALL(*mock_event_router.get(), - DispatchEventToRenderers(expected_event_name, _, - &profile, - GURL())) + DispatchEventImpl(item->extension_id(), + expected_event_name, + _, + &profile, + GURL())) .Times(1) - .WillOnce(SaveArg<1>(&event_args)); + .WillOnce(SaveArg<2>(&event_args)); manager_.ExecuteCommand(&profile, NULL /* tab_contents */, params, id); diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index e8562ff..a3a13c1 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -70,7 +70,7 @@ static void DispatchOnConnect(const ExtensionMessageService::MessagePort& port, args.Set(4, Value::CreateStringValue(target_extension_id)); CHECK(port.sender); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, - ExtensionMessageService::kDispatchOnConnect, args, false, GURL())); + "", ExtensionMessageService::kDispatchOnConnect, args, GURL())); } static void DispatchOnDisconnect( @@ -78,7 +78,7 @@ static void DispatchOnDisconnect( ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, - ExtensionMessageService::kDispatchOnDisconnect, args, false, GURL())); + "", ExtensionMessageService::kDispatchOnDisconnect, args, GURL())); } static void DispatchOnMessage(const ExtensionMessageService::MessagePort& port, @@ -87,7 +87,7 @@ static void DispatchOnMessage(const ExtensionMessageService::MessagePort& port, args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, - ExtensionMessageService::kDispatchOnMessage, args, false, GURL())); + "", ExtensionMessageService::kDispatchOnMessage, args, GURL())); } } // namespace diff --git a/chrome/browser/extensions/extension_messages_browsertest.cc b/chrome/browser/extensions/extension_messages_browsertest.cc index 5e78c79..bd36a80 100644 --- a/chrome/browser/extensions/extension_messages_browsertest.cc +++ b/chrome/browser/extensions/extension_messages_browsertest.cc @@ -22,15 +22,14 @@ static void DispatchOnConnect(int source_port_id, const std::string& name, args.Set(3, Value::CreateStringValue(EventBindings::kTestingExtensionId)); args.Set(4, Value::CreateStringValue(EventBindings::kTestingExtensionId)); RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnConnect, args, NULL, false, GURL()); + "", ExtensionMessageService::kDispatchOnConnect, args, NULL, GURL()); } static void DispatchOnDisconnect(int source_port_id) { ListValue args; args.Set(0, Value::CreateIntegerValue(source_port_id)); RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnDisconnect, args, NULL, false, - GURL()); + "", ExtensionMessageService::kDispatchOnDisconnect, args, NULL, GURL()); } static void DispatchOnMessage(const std::string& message, int source_port_id) { @@ -38,7 +37,7 @@ static void DispatchOnMessage(const std::string& message, int source_port_id) { args.Set(0, Value::CreateStringValue(message)); args.Set(1, Value::CreateIntegerValue(source_port_id)); RendererExtensionBindings::Invoke( - ExtensionMessageService::kDispatchOnMessage, args, NULL, false, GURL()); + "", ExtensionMessageService::kDispatchOnMessage, args, NULL, GURL()); } // Tests that the bindings for opening a channel to an extension and sending diff --git a/chrome/browser/extensions/extension_omnibox_api.cc b/chrome/browser/extensions/extension_omnibox_api.cc index 6d9e4e4..3e4887d 100644 --- a/chrome/browser/extensions/extension_omnibox_api.cc +++ b/chrome/browser/extensions/extension_omnibox_api.cc @@ -43,9 +43,8 @@ void ExtensionOmniboxEventRouter::OnInputStarted( bool ExtensionOmniboxEventRouter::OnInputChanged( Profile* profile, const std::string& extension_id, const std::string& input, int suggest_id) { - std::string event_name = ExtensionEventRouter::GetPerExtensionEventName( - events::kOnInputChanged, extension_id); - if (!profile->GetExtensionEventRouter()->HasEventListener(event_name)) + if (!profile->GetExtensionEventRouter()->ExtensionHasEventListener( + extension_id, events::kOnInputChanged)) return false; ListValue args; @@ -63,8 +62,6 @@ bool ExtensionOmniboxEventRouter::OnInputChanged( void ExtensionOmniboxEventRouter::OnInputEntered( Profile* profile, const std::string& extension_id, const std::string& input) { - std::string event_name = events::kOnInputEntered + extension_id; - ListValue args; args.Set(0, Value::CreateStringValue(input)); std::string json_args; diff --git a/chrome/browser/renderer_host/browser_render_process_host.cc b/chrome/browser/renderer_host/browser_render_process_host.cc index 5e254e5..b7af07f 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.cc +++ b/chrome/browser/renderer_host/browser_render_process_host.cc @@ -1066,18 +1066,20 @@ void BrowserRenderProcessHost::OnProcessLaunched() { } void BrowserRenderProcessHost::OnExtensionAddListener( + const std::string& extension_id, const std::string& event_name) { if (profile()->GetExtensionEventRouter()) { profile()->GetExtensionEventRouter()->AddEventListener( - event_name, id()); + event_name, this, extension_id); } } void BrowserRenderProcessHost::OnExtensionRemoveListener( + const std::string& extension_id, const std::string& event_name) { if (profile()->GetExtensionEventRouter()) { profile()->GetExtensionEventRouter()->RemoveEventListener( - event_name, id()); + event_name, this, extension_id); } } diff --git a/chrome/browser/renderer_host/browser_render_process_host.h b/chrome/browser/renderer_host/browser_render_process_host.h index 9983c84..639ba32 100644 --- a/chrome/browser/renderer_host/browser_render_process_host.h +++ b/chrome/browser/renderer_host/browser_render_process_host.h @@ -108,8 +108,10 @@ class BrowserRenderProcessHost : public RenderProcessHost, // Control message handlers. void OnUpdatedCacheStats(const WebKit::WebCache::UsageStats& stats); void SuddenTerminationChanged(bool enabled); - void OnExtensionAddListener(const std::string& event_name); - void OnExtensionRemoveListener(const std::string& event_name); + void OnExtensionAddListener(const std::string& extension_id, + const std::string& event_name); + void OnExtensionRemoveListener(const std::string& extension_id, + const std::string& event_name); void OnExtensionCloseChannel(int port_id); // Initialize support for visited links. Send the renderer process its initial diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index acdd862..e9b2fb3 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1915,6 +1915,7 @@ 'browser/extensions/content_script_all_frames_apitest.cc', 'browser/extensions/content_script_extension_process_apitest.cc', 'browser/extensions/cross_origin_xhr_apitest.cc', + 'browser/extensions/events_apitest.cc', 'browser/extensions/extension_devtools_browsertest.cc', 'browser/extensions/extension_devtools_browsertest.h', 'browser/extensions/extension_devtools_browsertests.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index af52b4f..3fdd1ce 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -1452,7 +1452,6 @@ "events": [ { "name": "onClicked", - "perExtensionEvent": true, "type": "function", "description": "Fired when a page action icon is clicked. This event will not fire if the page action has a popup.", "parameters": [ @@ -1600,7 +1599,6 @@ "events": [ { "name": "onClicked", - "perExtensionEvent": true, "type": "function", "description": "Fired when a browser action icon is clicked. This event will not fire if the browser action has a popup.", "parameters": [ @@ -3849,7 +3847,6 @@ "events": [ { "name": "onStateChanged", - "perExtensionEvent": true, "unprivileged": true, "type": "function", "description": "Notifies about sidebar state changes.", @@ -3950,14 +3947,12 @@ "events": [ { "name": "onInputStarted", - "perExtensionEvent": true, "type": "function", "description": "User has started a keyword input session by typing the extension's keyword. This is guaranteed to be sent exactly once per input session, and before any onInputChanged events.", "parameters": [] }, { "name": "onInputChanged", - "perExtensionEvent": true, "type": "function", "description": "User has changed what is typed into the omnibox.", "parameters": [ @@ -3984,7 +3979,6 @@ }, { "name": "onInputEntered", - "perExtensionEvent": true, "type": "function", "description": "User has accepted what is typed into the omnibox.", "parameters": [ @@ -3996,7 +3990,6 @@ }, { "name": "onInputCancelled", - "perExtensionEvent": true, "type": "function", "description": "User has ended the keyword input session without accepting the input.", "parameters": [] diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 75eacdf..b8e9a70 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -750,11 +750,13 @@ IPC_BEGIN_MESSAGES(View) // will call a javascript function in every registered context in the // target process. If routed, it will be restricted to the contexts that // are part of the target RenderView. - // |args| is a list of primitive Value types that are passed to the function. + // If |extension_id| is non-empty, the function will be invoked only in + // contexts owned by the extension. |args| is a list of primitive Value types + // that are passed to the function. IPC_MESSAGE_ROUTED4(ViewMsg_ExtensionMessageInvoke, + std::string /* extension_id */, std::string /* function_name */, ListValue /* args */, - bool /* requires incognito access */, GURL /* event URL */) // Tell the renderer process all known extension function names. @@ -773,13 +775,6 @@ IPC_BEGIN_MESSAGES(View) GURL /* source extension's origin */, std::vector<URLPattern> /* URLPatterns the extension can access */) - // Tell the renderer process that the given extension is enabled or disabled - // for incognito mode, and what kind of incognito behavior it has. - IPC_MESSAGE_CONTROL3(ViewMsg_Extension_ExtensionSetIncognitoEnabled, - std::string /* extension_id */, - bool /* enabled */, - bool /* incognito_split_mode */) - // Tell the renderer process all known page action ids for a particular // extension. IPC_MESSAGE_CONTROL2(ViewMsg_Extension_UpdatePageActions, @@ -2125,12 +2120,15 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_ROUTED1(ViewHostMsg_ExtensionRequest, ViewHostMsg_DomMessage_Params) - // Notify the browser that this renderer added a listener to an event. - IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionAddListener, + // Notify the browser that the given extension added a listener to an event. + IPC_MESSAGE_CONTROL2(ViewHostMsg_ExtensionAddListener, + std::string /* extension_id */, std::string /* name */) - // Notify the browser that this renderer removed a listener from an event. - IPC_MESSAGE_CONTROL1(ViewHostMsg_ExtensionRemoveListener, + // Notify the browser that the given extension removed a listener from an + // event. + IPC_MESSAGE_CONTROL2(ViewHostMsg_ExtensionRemoveListener, + std::string /* extension_id */, std::string /* name */) #if defined(OS_MACOSX) diff --git a/chrome/renderer/extensions/bindings_utils.h b/chrome/renderer/extensions/bindings_utils.h index 9432f91..b269ad1 100644 --- a/chrome/renderer/extensions/bindings_utils.h +++ b/chrome/renderer/extensions/bindings_utils.h @@ -78,6 +78,10 @@ struct ContextInfo { // a valid pointer, and is used for comparisons only. Do not dereference. RenderView* render_view; + // A map of event names to the number of listeners for that event. We notify + // the browser about event listeners when we transition between 0 and 1. + std::map<std::string, int> listener_counts; + // A count of the number of events that are listening in this context. When // this is zero, |context| will be a weak handle. int num_connected_events; diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 9c3f421..a451953 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -51,18 +51,6 @@ static bool in_unit_tests = false; // are disabled. static bool bindings_registered = false; -struct ExtensionData { - std::map<std::string, int> listener_count; -}; -int EventIncrementListenerCount(const std::string& event_name) { - ExtensionData *data = Singleton<ExtensionData>::get(); - return ++(data->listener_count[event_name]); -} -int EventDecrementListenerCount(const std::string& event_name) { - ExtensionData *data = Singleton<ExtensionData>::get(); - return --(data->listener_count[event_name]); -} - class ExtensionImpl : public ExtensionBase { public: ExtensionImpl() @@ -89,25 +77,25 @@ class ExtensionImpl : public ExtensionBase { DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { + ContextInfo* context_info = GetInfoForCurrentContext(); std::string event_name(*v8::String::AsciiValue(args[0])); bool has_permission = ExtensionProcessBindings::CurrentContextHasPermission(event_name); - // Increment the count even if the caller doesn't have permission, so that - // refcounts stay balanced. - if (EventIncrementListenerCount(event_name) == 1 && has_permission) { - EventBindings::GetRenderThread()->Send( - new ViewHostMsg_ExtensionAddListener(event_name)); - } - - ContextInfo* current_context_info = GetInfoForCurrentContext(); - if (++current_context_info->num_connected_events == 1) - current_context_info->context.ClearWeak(); - if (!has_permission) { return ExtensionProcessBindings::ThrowPermissionDeniedException( event_name); } + + if (++context_info->listener_counts[event_name] == 1) { + EventBindings::GetRenderThread()->Send( + new ViewHostMsg_ExtensionAddListener(context_info->extension_id, + event_name)); + } + + if (++context_info->num_connected_events == 1) + context_info->context.ClearWeak(); + } return v8::Undefined(); @@ -119,17 +107,19 @@ class ExtensionImpl : public ExtensionBase { DCHECK(args[0]->IsString() || args[0]->IsUndefined()); if (args[0]->IsString()) { + ContextInfo* context_info = GetInfoForCurrentContext(); + if (!context_info) + return v8::Undefined(); + std::string event_name(*v8::String::AsciiValue(args[0])); - if (EventDecrementListenerCount(event_name) == 0) { + if (--context_info->listener_counts[event_name] == 0) { EventBindings::GetRenderThread()->Send( - new ViewHostMsg_ExtensionRemoveListener(event_name)); + new ViewHostMsg_ExtensionRemoveListener(context_info->extension_id, + event_name)); } - ContextInfo* current_context_info = GetInfoForCurrentContext(); - if (current_context_info && - --current_context_info->num_connected_events == 0) { - current_context_info->context.MakeWeak(NULL, - &ContextWeakReferenceCallback); + if (--context_info->num_connected_events == 0) { + context_info->context.MakeWeak(NULL, &ContextWeakReferenceCallback); } } @@ -140,15 +130,9 @@ class ExtensionImpl : public ExtensionBase { // Returns true if the extension running in the given |context| has sufficient // permissions to access the data. static bool HasSufficientPermissions(ContextInfo* context, - bool cross_incognito, const GURL& event_url) { v8::Context::Scope context_scope(context->context); - bool cross_profile_ok = (!cross_incognito || - ExtensionProcessBindings::AllowCrossIncognito(context->extension_id)); - if (!cross_profile_ok) - return false; - // During unit tests, we might be invoked without a v8 context. In these // cases, we only allow empty event_urls and short-circuit before retrieving // the render view from the current context. @@ -335,10 +319,10 @@ void EventBindings::HandleContextDestroyed(WebFrame* frame) { } // static -void EventBindings::CallFunction(const std::string& function_name, +void EventBindings::CallFunction(const std::string& extension_id, + const std::string& function_name, int argc, v8::Handle<v8::Value>* argv, RenderView* render_view, - bool cross_incognito, const GURL& event_url) { // We copy the context list, because calling into javascript may modify it // out from under us. We also guard against deleted contexts by checking if @@ -350,10 +334,13 @@ void EventBindings::CallFunction(const std::string& function_name, if (render_view && render_view != (*it)->render_view) continue; + if (!extension_id.empty() && extension_id != (*it)->extension_id) + continue; + if ((*it)->context.IsEmpty()) continue; - if (!HasSufficientPermissions(it->get(), cross_incognito, event_url)) + if (!HasSufficientPermissions(it->get(), event_url)) continue; v8::Handle<v8::Value> retval = CallFunctionInContext((*it)->context, diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index 788a25c..95b049e 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -41,10 +41,10 @@ class EventBindings { // bindings_utils::CallFunctionInContext for more details. // The called javascript function should not return a value other than // v8::Undefined(). A DCHECK is setup to break if it is otherwise. - static void CallFunction(const std::string& function_name, int argc, - v8::Handle<v8::Value>* argv, + static void CallFunction(const std::string& extension_id, + const std::string& function_name, + int argc, v8::Handle<v8::Value>* argv, RenderView* render_view, - bool cross_incognito, const GURL& event_url); }; diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc index fed2fe3..bdbb9aa 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -63,10 +63,6 @@ typedef std::set<std::string> PermissionsList; // A map of extension ID to permissions map. typedef std::map<std::string, PermissionsList> ExtensionPermissionsList; -// A map of extension ID to whether this extension can access data from other -// profiles. -typedef std::map<std::string, bool> CrossIncognitoAccessMap; - const char kExtensionName[] = "chrome/ExtensionProcessBindings"; const char* kExtensionDeps[] = { BaseJsV8Extension::kName, @@ -80,7 +76,6 @@ struct SingletonData { std::set<std::string> function_names_; PageActionIdMap page_action_ids_; ExtensionPermissionsList permissions_; - CrossIncognitoAccessMap cross_incognito_access_map_; }; static std::set<std::string>* GetFunctionNameSet() { @@ -95,10 +90,6 @@ static PermissionsList* GetPermissionsList(const std::string& extension_id) { return &Singleton<SingletonData>()->permissions_[extension_id]; } -static CrossIncognitoAccessMap* GetCrossIncognitoAccessMap() { - return &Singleton<SingletonData>()->cross_incognito_access_map_; -} - static void GetActiveExtensionIDs(std::set<std::string>* extension_ids) { ExtensionPermissionsList& permissions = Singleton<SingletonData>()->permissions_; @@ -581,22 +572,6 @@ void ExtensionProcessBindings::SetFunctionNames( ExtensionImpl::SetFunctionNames(names); } -void ExtensionProcessBindings::SetIncognitoEnabled( - const std::string& extension_id, bool enabled, bool incognito_split_mode) { - // We allow the extension to see events and data from another profile iff it - // uses "spanning" behavior and it has incognito access. "split" mode - // extensions only see events for a matching profile. - (*GetCrossIncognitoAccessMap())[extension_id] = - enabled && !incognito_split_mode; -} - -// static -bool ExtensionProcessBindings::AllowCrossIncognito( - const std::string& extension_id) { - return (!extension_id.empty() && - (*GetCrossIncognitoAccessMap())[extension_id]); -} - // static void ExtensionProcessBindings::HandleResponse(int request_id, bool success, const std::string& response, diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h index 251cdd9..4aa4140 100644 --- a/chrome/renderer/extensions/extension_process_bindings.h +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -47,15 +47,6 @@ class ExtensionProcessBindings { static void SetHostPermissions(const GURL& extension_url, const std::vector<URLPattern>& permissions); - // Sets whether incognito is enabled for a particular extension. - static void SetIncognitoEnabled(const std::string& extension_id, - bool enabled, - bool incognito_split_mode); - - // Checks whether the given extension can see events/data from another - // profile (normal to incognito or vice versa). - static bool AllowCrossIncognito(const std::string& extension_id); - // Check if the extension in the currently running context has permission to // access the given extension function. Must be called with a valid V8 // context in scope. diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index 2e21c56..b109390 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -295,17 +295,17 @@ v8::Extension* RendererExtensionBindings::Get() { return extension; } -void RendererExtensionBindings::Invoke(const std::string& function_name, +void RendererExtensionBindings::Invoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, RenderView* renderview, - bool cross_incognito, const GURL& event_url) { v8::HandleScope handle_scope; std::vector< v8::Handle<v8::Value> > argv = ListValueToV8(args); - EventBindings::CallFunction(function_name, + EventBindings::CallFunction(extension_id, + function_name, argv.size(), &argv[0], renderview, - cross_incognito, event_url); } diff --git a/chrome/renderer/extensions/renderer_extension_bindings.h b/chrome/renderer/extensions/renderer_extension_bindings.h index 7b4280f..194b0c8 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.h +++ b/chrome/renderer/extensions/renderer_extension_bindings.h @@ -25,8 +25,10 @@ class RendererExtensionBindings { static v8::Extension* Get(); // Call the given javascript function with the specified arguments. - static void Invoke(const std::string& function_name, const ListValue& args, - RenderView* renderview, bool cross_incognito, + static void Invoke(const std::string& extension_id, + const std::string& function_name, + const ListValue& args, + RenderView* renderview, const GURL& event_url); }; diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index ebb8af7..4ce5f7f 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -551,12 +551,6 @@ void RenderThread::OnExtensionSetHostPermissions( ExtensionProcessBindings::SetHostPermissions(extension_url, permissions); } -void RenderThread::OnExtensionSetIncognitoEnabled( - const std::string& extension_id, bool enabled, bool incognito_split_mode) { - ExtensionProcessBindings::SetIncognitoEnabled(extension_id, enabled, - incognito_split_mode); -} - void RenderThread::OnDOMStorageEvent( const ViewMsg_DOMStorageEvent_Params& params) { if (!dom_storage_event_dispatcher_.get()) @@ -617,8 +611,6 @@ void RenderThread::OnControlMessageReceived(const IPC::Message& msg) { OnExtensionSetAPIPermissions) IPC_MESSAGE_HANDLER(ViewMsg_Extension_SetHostPermissions, OnExtensionSetHostPermissions) - IPC_MESSAGE_HANDLER(ViewMsg_Extension_ExtensionSetIncognitoEnabled, - OnExtensionSetIncognitoEnabled) IPC_MESSAGE_HANDLER(ViewMsg_DOMStorageEvent, OnDOMStorageEvent) #if defined(IPC_MESSAGE_LOG_ENABLED) @@ -974,12 +966,12 @@ void RenderThread::ScheduleIdleHandler(double initial_delay_s) { this, &RenderThread::IdleHandler); } -void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, +void RenderThread::OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool cross_incognito, const GURL& event_url) { RendererExtensionBindings::Invoke( - function_name, args, NULL, cross_incognito, event_url); + extension_id, function_name, args, NULL, event_url); // Reset the idle handler each time there's any activity like event or message // dispatch, for which Invoke is the chokepoint. diff --git a/chrome/renderer/render_thread.h b/chrome/renderer/render_thread.h index 9f2c4c0..a077c3b 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -267,10 +267,6 @@ class RenderThread : public RenderThreadBase, void OnExtensionSetHostPermissions( const GURL& extension_url, const std::vector<URLPattern>& permissions); - void OnExtensionSetIncognitoEnabled( - const std::string& extension_id, - bool enabled, - bool incognito_split_mode); void OnSetNextPageID(int32 next_page_id); void OnSetIsIncognitoProcess(bool is_incognito_process); void OnSetCSSColors(const std::vector<CSSColors::CSSColorMapping>& colors); @@ -289,9 +285,9 @@ class RenderThread : public RenderThreadBase, void OnGetRendererTcmalloc(); void OnGetV8HeapStats(); - void OnExtensionMessageInvoke(const std::string& function_name, + void OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool cross_incognito, const GURL& event_url); void OnPurgeMemory(); void OnPurgePluginListCache(bool reload_pages); diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 7b2e0a0..8c2a984 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -4947,12 +4947,12 @@ void RenderView::OnExtensionResponse(int request_id, request_id, success, response, error); } -void RenderView::OnExtensionMessageInvoke(const std::string& function_name, +void RenderView::OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool cross_incognito, const GURL& event_url) { RendererExtensionBindings::Invoke( - function_name, args, this, cross_incognito, event_url); + extension_id, function_name, args, this, event_url); } // Dump all load time histograms. diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 621a68c..57c26ff 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -811,9 +811,9 @@ class RenderView : public RenderWidget, void OnEnableViewSourceMode(); void OnExecuteCode(const ViewMsg_ExecuteCode_Params& params); void OnExecuteEditCommand(const std::string& name, const std::string& value); - void OnExtensionMessageInvoke(const std::string& function_name, + void OnExtensionMessageInvoke(const std::string& extension_id, + const std::string& function_name, const ListValue& args, - bool cross_incognito, const GURL& event_url); void OnFileChooserResponse(const std::vector<FilePath>& paths); void OnFind(int request_id, const string16&, const WebKit::WebFindOptions&); diff --git a/chrome/renderer/resources/event_bindings.js b/chrome/renderer/resources/event_bindings.js index f2a82d9..2b8cce8 100644 --- a/chrome/renderer/resources/event_bindings.js +++ b/chrome/renderer/resources/event_bindings.js @@ -110,10 +110,10 @@ var chrome = chrome || {}; // Registers a callback to be called when this event is dispatched. chrome.Event.prototype.addListener = function(cb) { - this.listeners_.push(cb); - if (this.listeners_.length == 1) { + if (this.listeners_.length == 0) { this.attach_(); } + this.listeners_.push(cb); }; // Unregisters a callback. diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index 8106782..de00b14 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -238,7 +238,7 @@ var chrome = chrome || {}; function setupPageActionEvents(extensionId) { var pageActions = GetCurrentPageActions(extensionId); - var oldStyleEventName = "pageActions/" + extensionId; + var oldStyleEventName = "pageActions"; // TODO(EXTENSIONS_DEPRECATED): only one page action for (var i = 0; i < pageActions.length; ++i) { // Setup events for each extension_id/page_action_id string we find. @@ -264,7 +264,7 @@ var chrome = chrome || {}; chromeHidden.contextMenus = {}; chromeHidden.contextMenus.nextId = 1; chromeHidden.contextMenus.handlers = {}; - var eventName = "contextMenus/" + extensionId; + var eventName = "contextMenus"; chromeHidden.contextMenus.event = new chrome.Event(eventName); chromeHidden.contextMenus.ensureListenerSetup = function() { if (chromeHidden.contextMenus.listening) { @@ -382,8 +382,6 @@ var chrome = chrome || {}; return; var eventName = apiDef.namespace + "." + eventDef.name; - if (eventDef.perExtensionEvent) - eventName = eventName + "/" + extensionId; module[eventDef.name] = new chrome.Event(eventName, eventDef.parameters); }); @@ -422,8 +420,7 @@ var chrome = chrome || {}; if (connectInfo) { name = connectInfo.name || name; } - var portId = OpenChannelToTab( - tabId, chromeHidden.extensionId, name); + var portId = OpenChannelToTab(tabId, chromeHidden.extensionId, name); return chromeHidden.Port.createPort(portId, name); }; diff --git a/chrome/test/data/extensions/api_test/events/background.html b/chrome/test/data/extensions/api_test/events/background.html new file mode 100644 index 0000000..edb2104 --- /dev/null +++ b/chrome/test/data/extensions/api_test/events/background.html @@ -0,0 +1,43 @@ +<script>
+chrome.test.runTests([
+ // Tests that attaching and detaching to an event for which we don't have
+ // permission acts as expected (e.g. we don't DCHECK!).
+ function attachAndDetachNoPermisssions() {
+ function dummy() {};
+ try {
+ chrome.tabs.onUpdated.addListener(dummy);
+ chrome.test.fail();
+ } catch (e) {
+ chrome.test.assertTrue(
+ e.message.search("You do not have permission") >= 0,
+ e.message);
+ }
+ chrome.test.assertFalse(chrome.tabs.onUpdated.hasListeners());
+ chrome.tabs.onUpdated.removeListener(dummy); // browser should not DCHECK
+ chrome.test.succeed();
+ },
+
+ // Tests that attaching a named event twice will fail.
+ function doubleAttach() {
+ function dummy() {};
+ var onClicked = new chrome.Event("browserAction.onClicked");
+ var onClicked2 = new chrome.Event("browserAction.onClicked");
+ onClicked.addListener(dummy);
+ chrome.test.assertTrue(onClicked.hasListeners());
+ try {
+ onClicked2.addListener(dummy);
+ chrome.test.fail();
+ } catch (e) {
+ chrome.test.assertTrue(
+ e.message.search("already attached") >= 0,
+ e.message);
+ }
+ chrome.test.assertFalse(onClicked2.hasListeners());
+ onClicked2.removeListener(dummy);
+
+ onClicked.removeListener(dummy);
+ chrome.test.assertFalse(onClicked.hasListeners());
+ chrome.test.succeed();
+ }
+]);
+</script>
diff --git a/chrome/test/data/extensions/api_test/events/manifest.json b/chrome/test/data/extensions/api_test/events/manifest.json new file mode 100644 index 0000000..dff8194 --- /dev/null +++ b/chrome/test/data/extensions/api_test/events/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "permissions failure apitest", + "description": "permissions failure extension", + "version": "0.1", + "background_page": "background.html", + "permissions": [ ] +} |