diff options
author | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 21:32:34 +0000 |
---|---|---|
committer | mpcomplete@chromium.org <mpcomplete@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-08 21:32:34 +0000 |
commit | 414785a5dce34bbe90b08c360992fd850612c2a1 (patch) | |
tree | 74a3f7b053dfc409afbc9187281dc0eac87160de | |
parent | 0df4ace4713e5b74a5215638111d514971aaf40d (diff) | |
download | chromium_src-414785a5dce34bbe90b08c360992fd850612c2a1.zip chromium_src-414785a5dce34bbe90b08c360992fd850612c2a1.tar.gz chromium_src-414785a5dce34bbe90b08c360992fd850612c2a1.tar.bz2 |
Ensure global extension events (like bookmarks) are sent to the incognito
extension process for split-mode extensions.
Also cleaned up some naming.
BUG=58189
TEST=no
Review URL: http://codereview.chromium.org/3578017
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62018 0039d316-1c4b-4281-b951-d872f2087c98
28 files changed, 203 insertions, 87 deletions
diff --git a/chrome/browser/extensions/all_urls_apitest.cc b/chrome/browser/extensions/all_urls_apitest.cc index 24166fc..ff82075 100644 --- a/chrome/browser/extensions/all_urls_apitest.cc +++ b/chrome/browser/extensions/all_urls_apitest.cc @@ -46,32 +46,32 @@ IN_PROC_BROWSER_TEST_F(AllUrlsApiTest, WhitelistedExtension) { // Now verify we run content scripts on chrome://newtab/. url = "chrome://newtab/"; - ExtensionTestMessageListener listener1a("content script: " + url); - ExtensionTestMessageListener listener1b("execute: " + url); + ExtensionTestMessageListener listener1a("content script: " + url, false); + ExtensionTestMessageListener listener1b("execute: " + url, false); ui_test_utils::NavigateToURL(browser(), GURL(url)); ASSERT_TRUE(listener1a.WaitUntilSatisfied()); ASSERT_TRUE(listener1b.WaitUntilSatisfied()); // Now verify data: urls. url = "data:text/html;charset=utf-8,<html>asdf</html>"; - ExtensionTestMessageListener listener2a("content script: " + url); - ExtensionTestMessageListener listener2b("execute: " + url); + ExtensionTestMessageListener listener2a("content script: " + url, false); + ExtensionTestMessageListener listener2b("execute: " + url, false); ui_test_utils::NavigateToURL(browser(), GURL(url)); ASSERT_TRUE(listener2a.WaitUntilSatisfied()); ASSERT_TRUE(listener2b.WaitUntilSatisfied()); // Now verify about:version. url = "about:version"; - ExtensionTestMessageListener listener3a("content script: " + url); - ExtensionTestMessageListener listener3b("execute: " + url); + ExtensionTestMessageListener listener3a("content script: " + url, false); + ExtensionTestMessageListener listener3b("execute: " + url, false); ui_test_utils::NavigateToURL(browser(), GURL(url)); ASSERT_TRUE(listener3a.WaitUntilSatisfied()); ASSERT_TRUE(listener3b.WaitUntilSatisfied()); // Now verify about:blank. url = "about:blank"; - ExtensionTestMessageListener listener4a("content script: " + url); - ExtensionTestMessageListener listener4b("execute: " + url); + ExtensionTestMessageListener listener4a("content script: " + url, false); + ExtensionTestMessageListener listener4b("execute: " + url, false); ui_test_utils::NavigateToURL(browser(), GURL(url)); ASSERT_TRUE(listener4a.WaitUntilSatisfied()); ASSERT_TRUE(listener4b.WaitUntilSatisfied()); @@ -79,8 +79,9 @@ IN_PROC_BROWSER_TEST_F(AllUrlsApiTest, WhitelistedExtension) { // Now verify we can script a regular http page. ASSERT_TRUE(test_server()->Start()); GURL page_url = test_server()->GetURL(kAllUrlsTarget); - ExtensionTestMessageListener listener5a("content script: " + page_url.spec()); - ExtensionTestMessageListener listener5b("execute: " + page_url.spec()); + ExtensionTestMessageListener listener5a("content script: " + page_url.spec(), + false); + ExtensionTestMessageListener listener5b("execute: " + page_url.spec(), false); ui_test_utils::NavigateToURL(browser(), page_url); ASSERT_TRUE(listener5a.WaitUntilSatisfied()); ASSERT_TRUE(listener5b.WaitUntilSatisfied()); @@ -104,8 +105,9 @@ IN_PROC_BROWSER_TEST_F(AllUrlsApiTest, RegularExtensions) { // Now verify we can script a regular http page. ASSERT_TRUE(test_server()->Start()); GURL page_url = test_server()->GetURL(kAllUrlsTarget); - ExtensionTestMessageListener listener1a("content script: " + page_url.spec()); - ExtensionTestMessageListener listener1b("execute: " + page_url.spec()); + ExtensionTestMessageListener listener1a("content script: " + page_url.spec(), + false); + ExtensionTestMessageListener listener1b("execute: " + page_url.spec(), false); ui_test_utils::NavigateToURL(browser(), page_url); ASSERT_TRUE(listener1a.WaitUntilSatisfied()); ASSERT_TRUE(listener1b.WaitUntilSatisfied()); diff --git a/chrome/browser/extensions/extension_accessibility_api.cc b/chrome/browser/extensions/extension_accessibility_api.cc index 83a6194..235a521 100644 --- a/chrome/browser/extensions/extension_accessibility_api.cc +++ b/chrome/browser/extensions/extension_accessibility_api.cc @@ -183,7 +183,7 @@ void ExtensionAccessibilityEventRouter::DispatchEvent( const std::string& json_args) { if (enabled_ && profile && profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile, GURL()); + event_name, json_args, NULL, GURL()); } } diff --git a/chrome/browser/extensions/extension_bookmark_manager_api.cc b/chrome/browser/extensions/extension_bookmark_manager_api.cc index 0b0dc0f..f5db93f 100644 --- a/chrome/browser/extensions/extension_bookmark_manager_api.cc +++ b/chrome/browser/extensions/extension_bookmark_manager_api.cc @@ -161,7 +161,7 @@ void ExtensionBookmarkManagerEventRouter::DispatchEvent(const char* event_name, std::string json_args; base::JSONWriter::Write(args, false, &json_args); profile_->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile_, GURL()); + event_name, json_args, NULL, GURL()); } void ExtensionBookmarkManagerEventRouter::DispatchDragEvent( diff --git a/chrome/browser/extensions/extension_bookmarks_module.cc b/chrome/browser/extensions/extension_bookmarks_module.cc index 4044bfc..991ea2d 100644 --- a/chrome/browser/extensions/extension_bookmarks_module.cc +++ b/chrome/browser/extensions/extension_bookmarks_module.cc @@ -97,7 +97,7 @@ void ExtensionBookmarkEventRouter::DispatchEvent(Profile *profile, const std::string json_args) { if (profile->GetExtensionMessageService()) { profile->GetExtensionMessageService()->DispatchEventToRenderers( - event_name, json_args, profile, GURL()); + event_name, json_args, NULL, GURL()); } } diff --git a/chrome/browser/extensions/extension_context_menu_browsertest.cc b/chrome/browser/extensions/extension_context_menu_browsertest.cc index 427a7fc..62221e1 100644 --- a/chrome/browser/extensions/extension_context_menu_browsertest.cc +++ b/chrome/browser/extensions/extension_context_menu_browsertest.cc @@ -180,8 +180,8 @@ class ExtensionContextMenuBrowserTest : public ExtensionBrowserTest { // Tests adding a simple context menu item. IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Simple) { - ExtensionTestMessageListener listener1("created item"); - ExtensionTestMessageListener listener2("onclick fired"); + ExtensionTestMessageListener listener1("created item", false); + ExtensionTestMessageListener listener2("onclick fired", false); ASSERT_TRUE(LoadContextMenuExtension("simple")); // Wait for the extension to tell us it's created an item. @@ -204,7 +204,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Simple) { // Tests that setting "documentUrlPatterns" for an item properly restricts // those items to matching pages. IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Patterns) { - ExtensionTestMessageListener listener("created items"); + ExtensionTestMessageListener listener("created items", false); ASSERT_TRUE(LoadContextMenuExtension("patterns")); @@ -233,7 +233,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Patterns) { // Tests registering an item with a very long title that should get truncated in // the actual menu displayed. IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, LongTitle) { - ExtensionTestMessageListener listener("created"); + ExtensionTestMessageListener listener("created", false); // Load the extension and wait until it's created a menu item. ASSERT_TRUE(LoadContextMenuExtension("long_title")); @@ -307,7 +307,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { // Navigate to test1.html inside the extension, which should create a bunch // of items at the top-level (but they'll get pushed into an auto-generated // parent). - ExtensionTestMessageListener listener1("test1 create finished"); + ExtensionTestMessageListener listener1("test1 create finished", false); ui_test_utils::NavigateToURL(browser(), GURL(extension->GetResourceURL("test1.html"))); listener1.WaitUntilSatisfied(); @@ -332,7 +332,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { // Now run our second test - navigate to test2.html which creates an explicit // parent node and populates that with the same items as in test1. - ExtensionTestMessageListener listener2("test2 create finished"); + ExtensionTestMessageListener listener2("test2 create finished", false); ui_test_utils::NavigateToURL(browser(), GURL(extension->GetResourceURL("test2.html"))); listener2.WaitUntilSatisfied(); @@ -348,7 +348,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, Separators) { // Tests that targetUrlPattern keeps items from appearing when there is no // target url. IN_PROC_BROWSER_TEST_F(ExtensionContextMenuBrowserTest, TargetURLs) { - ExtensionTestMessageListener listener("created items"); + ExtensionTestMessageListener listener("created items", false); ASSERT_TRUE(LoadContextMenuExtension("target_urls")); ASSERT_TRUE(listener.WaitUntilSatisfied()); diff --git a/chrome/browser/extensions/extension_incognito_apitest.cc b/chrome/browser/extensions/extension_incognito_apitest.cc index 51742e8..675e2bb 100644 --- a/chrome/browser/extensions/extension_incognito_apitest.cc +++ b/chrome/browser/extensions/extension_incognito_apitest.cc @@ -7,6 +7,7 @@ #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/browser_action_test_util.h" #include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/browser/extensions/extension_test_message_listener.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/profile.h" @@ -132,6 +133,14 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_IncognitoSplitMode) { ASSERT_TRUE(LoadExtensionIncognito(test_data_dir_ .AppendASCII("incognito").AppendASCII("split"))); + // Wait for both extensions to be ready before telling them to proceed. + ExtensionTestMessageListener listener("waiting", true); + EXPECT_TRUE(listener.WaitUntilSatisfied()); + ExtensionTestMessageListener listener_incognito("waiting", true); + EXPECT_TRUE(listener_incognito.WaitUntilSatisfied()); + listener.Reply("go"); + listener_incognito.Reply("go"); + EXPECT_TRUE(catcher.GetNextResult()) << catcher.message(); EXPECT_TRUE(catcher_incognito.GetNextResult()) << catcher.message(); } diff --git a/chrome/browser/extensions/extension_management_api.cc b/chrome/browser/extensions/extension_management_api.cc index fe6888a..e42764d 100644 --- a/chrome/browser/extensions/extension_management_api.cc +++ b/chrome/browser/extensions/extension_management_api.cc @@ -233,6 +233,6 @@ void ExtensionManagementEventRouter::Observe( profile->GetExtensionMessageService(); message_service->DispatchEventToRenderers(event_name, args_json, - profile, + NULL, GURL()); } diff --git a/chrome/browser/extensions/extension_management_api_browsertest.cc b/chrome/browser/extensions/extension_management_api_browsertest.cc index 7061637..80955ba 100644 --- a/chrome/browser/extensions/extension_management_api_browsertest.cc +++ b/chrome/browser/extensions/extension_management_api_browsertest.cc @@ -10,20 +10,20 @@ class ExtensionManagementApiBrowserTest : public ExtensionBrowserTest {}; // We test this here instead of in an ExtensionApiTest because normal extensions // are not allowed to call the install function. IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, InstallEvent) { - ExtensionTestMessageListener listener1("ready"); + ExtensionTestMessageListener listener1("ready", false); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("management/install_event"))); ASSERT_TRUE(listener1.WaitUntilSatisfied()); - ExtensionTestMessageListener listener2("got_event"); + ExtensionTestMessageListener listener2("got_event", false); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("api_test/management/enabled_extension"))); ASSERT_TRUE(listener2.WaitUntilSatisfied()); } IN_PROC_BROWSER_TEST_F(ExtensionManagementApiBrowserTest, LaunchApp) { - ExtensionTestMessageListener listener1("app_launched"); - ExtensionTestMessageListener listener2("got_expected_error"); + ExtensionTestMessageListener listener1("app_launched", false); + ExtensionTestMessageListener listener2("got_expected_error", false); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("management/simple_extension"))); ASSERT_TRUE(LoadExtension( diff --git a/chrome/browser/extensions/extension_management_browsertest.cc b/chrome/browser/extensions/extension_management_browsertest.cc index 1c6fb68b..2e528b5 100644 --- a/chrome/browser/extensions/extension_management_browsertest.cc +++ b/chrome/browser/extensions/extension_management_browsertest.cc @@ -209,7 +209,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, AutoUpdate) { basedir.AppendASCII("v2.crx")); // Install version 1 of the extension. - ExtensionTestMessageListener listener1("v1 installed"); + ExtensionTestMessageListener listener1("v1 installed", false); ExtensionsService* service = browser()->profile()->GetExtensionsService(); const size_t size_before = service->extensions()->size(); ASSERT_TRUE(service->disabled_extensions()->empty()); @@ -226,7 +226,7 @@ IN_PROC_BROWSER_TEST_F(ExtensionManagementTest, AutoUpdate) { service->updater()->set_blacklist_checks_enabled(false); // Run autoupdate and make sure version 2 of the extension was installed. - ExtensionTestMessageListener listener2("v2 installed"); + ExtensionTestMessageListener listener2("v2 installed", false); service->updater()->CheckNow(); ASSERT_TRUE(WaitForExtensionInstall()); listener2.WaitUntilSatisfied(); diff --git a/chrome/browser/extensions/extension_message_service.cc b/chrome/browser/extensions/extension_message_service.cc index 40bfc32..6a615ff 100644 --- a/chrome/browser/extensions/extension_message_service.cc +++ b/chrome/browser/extensions/extension_message_service.cc @@ -96,13 +96,13 @@ static void DispatchOnMessage(const ExtensionMessageService::MessagePort& port, static void DispatchEvent(const ExtensionMessageService::MessagePort& port, const std::string& event_name, const std::string& event_args, - bool has_incognito_data, + bool cross_incognito, const GURL& event_url) { ListValue args; args.Set(0, Value::CreateStringValue(event_name)); args.Set(1, Value::CreateStringValue(event_args)); port.sender->Send(new ViewMsg_ExtensionMessageInvoke(port.routing_id, - ExtensionMessageService::kDispatchEvent, args, has_incognito_data, + ExtensionMessageService::kDispatchEvent, args, cross_incognito, event_url)); } @@ -392,12 +392,12 @@ void ExtensionMessageService::PostMessageFromRenderer( void ExtensionMessageService::DispatchEventToRenderers( const std::string& event_name, const std::string& event_args, - Profile* source_profile, const GURL& event_url) { + Profile* restrict_to_profile, const GURL& event_url) { if (!profile_) return; // We don't expect to get events from a completely different profile. - DCHECK(!source_profile || profile_->IsSameProfile(source_profile)); + DCHECK(!restrict_to_profile || profile_->IsSameProfile(restrict_to_profile)); ListenerMap::iterator it = listeners_.find(event_name); if (it == listeners_.end()) @@ -418,18 +418,18 @@ void ExtensionMessageService::DispatchEventToRenderers( // 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_profile = - source_profile && renderer->profile() != source_profile; - DispatchEvent(renderer, event_name, event_args, cross_profile, event_url); + bool cross_incognito = + restrict_to_profile && renderer->profile() != restrict_to_profile; + DispatchEvent(renderer, event_name, event_args, cross_incognito, event_url); } } void ExtensionMessageService::DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, - Profile* source_profile, const GURL& event_url) { + Profile* restrict_to_profile, const GURL& event_url) { DispatchEventToRenderers(GetPerExtensionEventName(event_name, extension_id), - event_args, source_profile, event_url); + event_args, restrict_to_profile, event_url); } void ExtensionMessageService::Observe(NotificationType type, diff --git a/chrome/browser/extensions/extension_message_service.h b/chrome/browser/extensions/extension_message_service.h index 9d8c038..79ef344 100644 --- a/chrome/browser/extensions/extension_message_service.h +++ b/chrome/browser/extensions/extension_message_service.h @@ -81,20 +81,22 @@ class ExtensionMessageService // Returns true if there is at least one listener for the given event. bool HasEventListener(const std::string& event_name); - // Send an event to every registered extension renderer. If - // |has_incognito_data| is true, the event is only sent to extension with the - // permission to access incognito data. If |event_url| is not empty, the - // event is only sent to extension with host permissions for this url. + // 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( const std::string& event_name, const std::string& event_args, - Profile* source_profile, const GURL& event_url); + 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. void DispatchEventToExtension( const std::string& extension_id, const std::string& event_name, const std::string& event_args, - Profile* source_profile, const GURL& event_url); + Profile* restrict_to_profile, const GURL& event_url); // Given an extension's ID, opens a channel between the given renderer "port" // and every listening context owned by that extension. |channel_name| is diff --git a/chrome/browser/extensions/extension_test_api.cc b/chrome/browser/extensions/extension_test_api.cc index 858d25b..b5f64ba 100644 --- a/chrome/browser/extensions/extension_test_api.cc +++ b/chrome/browser/extensions/extension_test_api.cc @@ -54,10 +54,16 @@ bool ExtensionTestCreateIncognitoTabFunction::RunImpl() { bool ExtensionTestSendMessageFunction::RunImpl() { std::string message; EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &message)); - std::string id = extension_id(); + AddRef(); // balanced in Reply NotificationService::current()->Notify( NotificationType::EXTENSION_TEST_MESSAGE, - Source<std::string>(&id), + Source<ExtensionTestSendMessageFunction>(this), Details<std::string>(&message)); return true; } + +void ExtensionTestSendMessageFunction::Reply(const std::string& message) { + result_.reset(Value::CreateStringValue(message)); + SendResponse(true); + Release(); // balanced in RunImpl +} diff --git a/chrome/browser/extensions/extension_test_api.h b/chrome/browser/extensions/extension_test_api.h index f59d0b5..e0b8dce 100644 --- a/chrome/browser/extensions/extension_test_api.h +++ b/chrome/browser/extensions/extension_test_api.h @@ -38,7 +38,13 @@ class ExtensionTestCreateIncognitoTabFunction : public SyncExtensionFunction { DECLARE_EXTENSION_FUNCTION_NAME("test.createIncognitoTab") }; -class ExtensionTestSendMessageFunction : public SyncExtensionFunction { +class ExtensionTestSendMessageFunction : public AsyncExtensionFunction { + public: + // Sends a reply back to the calling extension. Many extensions don't need + // a reply and will just ignore it. + void Reply(const std::string& message); + + private: ~ExtensionTestSendMessageFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("test.sendMessage") diff --git a/chrome/browser/extensions/extension_test_message_listener.cc b/chrome/browser/extensions/extension_test_message_listener.cc index bfa9db7..423bf3b 100644 --- a/chrome/browser/extensions/extension_test_message_listener.cc +++ b/chrome/browser/extensions/extension_test_message_listener.cc @@ -4,14 +4,18 @@ #include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/extensions/extension_test_api.h" #include "chrome/common/notification_service.h" #include "chrome/common/notification_type.h" #include "chrome/test/ui_test_utils.h" ExtensionTestMessageListener::ExtensionTestMessageListener( - const std::string& expected_message) - : expected_message_(expected_message), satisfied_(false), - waiting_(false) { + const std::string& expected_message, + bool will_reply) + : expected_message_(expected_message), + satisfied_(false), + waiting_(false), + will_reply_(will_reply) { registrar_.Add(this, NotificationType::EXTENSION_TEST_MESSAGE, NotificationService::AllSources()); } @@ -26,14 +30,27 @@ bool ExtensionTestMessageListener::WaitUntilSatisfied() { return satisfied_; } +void ExtensionTestMessageListener::Reply(const std::string& message) { + DCHECK(satisfied_); + DCHECK(will_reply_); + function_->Reply(message); + function_ = NULL; + will_reply_ = false; +} + void ExtensionTestMessageListener::Observe( NotificationType type, const NotificationSource& source, const NotificationDetails& details) { const std::string& content = *Details<std::string>(details).ptr(); + function_ = Source<ExtensionTestSendMessageFunction>(source).ptr(); if (!satisfied_ && content == expected_message_) { satisfied_ = true; registrar_.RemoveAll(); // Stop listening for more messages. + if (!will_reply_) { + function_->Reply(""); + function_ = NULL; + } if (waiting_) { waiting_ = false; MessageLoopForUI::current()->Quit(); diff --git a/chrome/browser/extensions/extension_test_message_listener.h b/chrome/browser/extensions/extension_test_message_listener.h index 2aeedd8..bead446 100644 --- a/chrome/browser/extensions/extension_test_message_listener.h +++ b/chrome/browser/extensions/extension_test_message_listener.h @@ -11,14 +11,7 @@ #include "chrome/common/notification_observer.h" #include "chrome/common/notification_registrar.h" -// A message from javascript sent via chrome.test.sendMessage(). -struct ExtensionTestMessage { - // The sender's extension id. - std::string extension_id; - - // The message content. - std::string content; -}; +class ExtensionTestSendMessageFunction; // This class helps us wait for incoming messages sent from javascript via // chrome.test.sendMessage(). A sample usage would be: @@ -27,6 +20,18 @@ struct ExtensionTestMessage { // ... do some work // ASSERT_TRUE(listener.WaitUntilSatisfied()); // +// It is also possible to have the extension wait for our reply. This is +// useful for coordinating multiple pages/processes and having them wait on +// each other. Example: +// +// ExtensionTestMessageListener listener1("foo1"); +// ExtensionTestMessageListener listener2("foo2"); +// ASSERT_TRUE(listener1.WaitUntilSatisfied()); +// ASSERT_TRUE(listener2.WaitUntilSatisfied()); +// ... do some work +// listener1.Reply("foo2 is ready"); +// listener2.Reply("foo1 is ready"); +// // TODO(asargent) - In the future we may want to add the ability to listen for // multiple messages, and/or to wait for "any" message and then retrieve the // contents of that message. We may also want to specify an extension id as @@ -40,7 +45,8 @@ struct ExtensionTestMessage { class ExtensionTestMessageListener : public NotificationObserver { public: // We immediately start listening for |expected_message|. - explicit ExtensionTestMessageListener(const std::string& expected_message); + ExtensionTestMessageListener(const std::string& expected_message, + bool will_reply); ~ExtensionTestMessageListener(); // This returns true immediately if we've already gotten the expected @@ -48,6 +54,10 @@ class ExtensionTestMessageListener : public NotificationObserver { // interrupted and we still haven't gotten the message. bool WaitUntilSatisfied(); + // Send the given message as a reply. It is only valid to call this after + // WaitUntilSatisfied has returned true, and if will_reply is true. + void Reply(const std::string& message); + // Implements the NotificationObserver interface. virtual void Observe(NotificationType type, const NotificationSource& source, @@ -65,6 +75,13 @@ class ExtensionTestMessageListener : public NotificationObserver { // If we're waiting, then we want to post a quit task when the expected // message arrives. bool waiting_; + + // If true, we expect the calling code to manually send a reply. Otherwise, + // we send an automatic empty reply to the extension. + bool will_reply_; + + // The function we need to reply to. + ExtensionTestSendMessageFunction* function_; }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_TEST_MESSAGE_LISTENER_H_ diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 142b3a4..860f94d 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -3431,7 +3431,15 @@ "type": "function", "description": "Sends a string message to the browser process, generating a Notification that C++ test code can wait for.", "parameters": [ - {"type": "string", "name": "message"} + {"type": "string", "name": "message"}, + { + "type": "function", + "name": "callback", + "optional": true, + "parameters": [ + {"type": "string", "name": "response"} + ] + } ] } ], diff --git a/chrome/renderer/extensions/event_bindings.cc b/chrome/renderer/extensions/event_bindings.cc index 6f3b7e3..9c3f421 100644 --- a/chrome/renderer/extensions/event_bindings.cc +++ b/chrome/renderer/extensions/event_bindings.cc @@ -140,12 +140,12 @@ 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 requires_incognito_access, + bool cross_incognito, const GURL& event_url) { v8::Context::Scope context_scope(context->context); - bool cross_profile_ok = (!requires_incognito_access || - ExtensionProcessBindings::AllowCrossProfile(context->extension_id)); + bool cross_profile_ok = (!cross_incognito || + ExtensionProcessBindings::AllowCrossIncognito(context->extension_id)); if (!cross_profile_ok) return false; @@ -338,7 +338,7 @@ void EventBindings::HandleContextDestroyed(WebFrame* frame) { void EventBindings::CallFunction(const std::string& function_name, int argc, v8::Handle<v8::Value>* argv, RenderView* render_view, - bool requires_incognito_access, + 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 @@ -353,11 +353,8 @@ void EventBindings::CallFunction(const std::string& function_name, if ((*it)->context.IsEmpty()) continue; - if (!HasSufficientPermissions(it->get(), - requires_incognito_access, - event_url)) { + if (!HasSufficientPermissions(it->get(), cross_incognito, event_url)) continue; - } v8::Handle<v8::Value> retval = CallFunctionInContext((*it)->context, function_name, argc, argv); diff --git a/chrome/renderer/extensions/event_bindings.h b/chrome/renderer/extensions/event_bindings.h index 037a1eb..788a25c 100644 --- a/chrome/renderer/extensions/event_bindings.h +++ b/chrome/renderer/extensions/event_bindings.h @@ -44,7 +44,7 @@ class EventBindings { static void CallFunction(const std::string& function_name, int argc, v8::Handle<v8::Value>* argv, RenderView* render_view, - bool requires_incognito_access, + 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 a5b9fe5..fed2fe3 100644 --- a/chrome/renderer/extensions/extension_process_bindings.cc +++ b/chrome/renderer/extensions/extension_process_bindings.cc @@ -65,7 +65,7 @@ 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> CrossProfileAccessMap; +typedef std::map<std::string, bool> CrossIncognitoAccessMap; const char kExtensionName[] = "chrome/ExtensionProcessBindings"; const char* kExtensionDeps[] = { @@ -80,7 +80,7 @@ struct SingletonData { std::set<std::string> function_names_; PageActionIdMap page_action_ids_; ExtensionPermissionsList permissions_; - CrossProfileAccessMap cross_profile_access_map_; + CrossIncognitoAccessMap cross_incognito_access_map_; }; static std::set<std::string>* GetFunctionNameSet() { @@ -95,8 +95,8 @@ static PermissionsList* GetPermissionsList(const std::string& extension_id) { return &Singleton<SingletonData>()->permissions_[extension_id]; } -static CrossProfileAccessMap* GetCrossProfileAccessMap() { - return &Singleton<SingletonData>()->cross_profile_access_map_; +static CrossIncognitoAccessMap* GetCrossIncognitoAccessMap() { + return &Singleton<SingletonData>()->cross_incognito_access_map_; } static void GetActiveExtensionIDs(std::set<std::string>* extension_ids) { @@ -586,14 +586,15 @@ void ExtensionProcessBindings::SetIncognitoEnabled( // 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. - (*GetCrossProfileAccessMap())[extension_id] = + (*GetCrossIncognitoAccessMap())[extension_id] = enabled && !incognito_split_mode; } // static -bool ExtensionProcessBindings::AllowCrossProfile( +bool ExtensionProcessBindings::AllowCrossIncognito( const std::string& extension_id) { - return (!extension_id.empty() && (*GetCrossProfileAccessMap())[extension_id]); + return (!extension_id.empty() && + (*GetCrossIncognitoAccessMap())[extension_id]); } // static diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h index 2cbbf4c..251cdd9 100644 --- a/chrome/renderer/extensions/extension_process_bindings.h +++ b/chrome/renderer/extensions/extension_process_bindings.h @@ -54,7 +54,7 @@ class ExtensionProcessBindings { // Checks whether the given extension can see events/data from another // profile (normal to incognito or vice versa). - static bool AllowCrossProfile(const std::string& extension_id); + 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 diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc index e3d820a..2e21c56 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.cc +++ b/chrome/renderer/extensions/renderer_extension_bindings.cc @@ -298,7 +298,7 @@ v8::Extension* RendererExtensionBindings::Get() { void RendererExtensionBindings::Invoke(const std::string& function_name, const ListValue& args, RenderView* renderview, - bool requires_incognito_access, + bool cross_incognito, const GURL& event_url) { v8::HandleScope handle_scope; std::vector< v8::Handle<v8::Value> > argv = ListValueToV8(args); @@ -306,6 +306,6 @@ void RendererExtensionBindings::Invoke(const std::string& function_name, argv.size(), &argv[0], renderview, - requires_incognito_access, + cross_incognito, event_url); } diff --git a/chrome/renderer/extensions/renderer_extension_bindings.h b/chrome/renderer/extensions/renderer_extension_bindings.h index 15fe532..7b4280f 100644 --- a/chrome/renderer/extensions/renderer_extension_bindings.h +++ b/chrome/renderer/extensions/renderer_extension_bindings.h @@ -26,7 +26,7 @@ class RendererExtensionBindings { // Call the given javascript function with the specified arguments. static void Invoke(const std::string& function_name, const ListValue& args, - RenderView* renderview, bool requires_incognito_access, + RenderView* renderview, bool cross_incognito, const GURL& event_url); }; diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc index a0cdd5f..5b1b6c2 100644 --- a/chrome/renderer/render_thread.cc +++ b/chrome/renderer/render_thread.cc @@ -976,10 +976,10 @@ void RenderThread::ScheduleIdleHandler(double initial_delay_s) { void RenderThread::OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args, - bool requires_incognito_access, + bool cross_incognito, const GURL& event_url) { RendererExtensionBindings::Invoke( - function_name, args, NULL, requires_incognito_access, event_url); + function_name, args, NULL, cross_incognito, 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 c0d5964..9f2c4c0 100644 --- a/chrome/renderer/render_thread.h +++ b/chrome/renderer/render_thread.h @@ -291,7 +291,7 @@ class RenderThread : public RenderThreadBase, void OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args, - bool requires_incognito_access, + 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 1e8a533..2c84869 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -4869,10 +4869,10 @@ void RenderView::OnExtensionResponse(int request_id, void RenderView::OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args, - bool requires_incognito_access, + bool cross_incognito, const GURL& event_url) { RendererExtensionBindings::Invoke( - function_name, args, this, requires_incognito_access, event_url); + function_name, args, this, cross_incognito, event_url); } // Dump all load time histograms. diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 2fb488c..a09e9f1 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -797,7 +797,7 @@ class RenderView : public RenderWidget, void OnExecuteEditCommand(const std::string& name, const std::string& value); void OnExtensionMessageInvoke(const std::string& function_name, const ListValue& args, - bool requires_incognito_access, + 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/test/data/extensions/api_test/incognito/split/background.html b/chrome/test/data/extensions/api_test/incognito/split/background.html index 32fec55..50ebea6 100644 --- a/chrome/test/data/extensions/api_test/incognito/split/background.html +++ b/chrome/test/data/extensions/api_test/incognito/split/background.html @@ -6,6 +6,27 @@ var assertTrue = chrome.test.assertTrue; var win, tab; var inIncognitoContext = chrome.extension.inIncognitoContext; +// Lifted from the bookmarks api_test. +function compareNode(left, right) { + //chrome.test.log("compareNode()"); + //chrome.test.log(JSON.stringify(left, null, 2)); + //chrome.test.log(JSON.stringify(right, null, 2)); + // TODO(erikkay): do some comparison of dateAdded + if (left.id != right.id) + return "id mismatch: " + left.id + " != " + right.id; + if (left.title != right.title) { + // TODO(erikkay): This resource dependency still isn't working reliably. + // See bug 19866. + // return "title mismatch: " + left.title + " != " + right.title; + chrome.test.log("title mismatch: " + left.title + " != " + right.title); + } + if (left.url != right.url) + return "url mismatch: " + left.url + " != " + right.url; + if (left.index != right.index) + return "index mismatch: " + left.index + " != " + right.index; + return true; +} + // Listen to some events to make sure we don't get events from the other // profile. @@ -84,6 +105,36 @@ chrome.test.runTests([ sendResponse(); done(); }); + }, + + // Tests that we can receive bookmarks events in both extension processes. + function bookmarkCreate() { + // Each process will create 1 bookmark, but expects to see updates from the + // other process. + var nodeNormal = {parentId:"1", title:"normal", url:"http://google.com/"}; + var nodeIncog = {parentId:"1", title:"incog", url:"http://google.com/"}; + var node = inIncognitoContext ? nodeIncog : nodeNormal; + var count = 0; + var done = chrome.test.listenForever(chrome.bookmarks.onCreated, + function(id, created) { + node = (created.title == nodeNormal.title) ? nodeNormal : nodeIncog; + node.id = created.id; + node.index = created.index; + chrome.test.assertEq(id, node.id); + chrome.test.assertTrue(compareNode(node, created)); + if (++count == 2) { + chrome.test.log("Bookmarks created. Incognito=" + inIncognitoContext); + done(); + } + }); + chrome.test.sendMessage("waiting", pass(function() { + chrome.bookmarks.create(node, pass(function(results) { + node.id = results.id; // since we couldn't know this going in + node.index = results.index; + chrome.test.assertTrue(compareNode(node, results), + "created node != source"); + })); + })); } ]); </script> diff --git a/chrome/test/data/extensions/api_test/incognito/split/manifest.json b/chrome/test/data/extensions/api_test/incognito/split/manifest.json index b3a9206..38efa71 100644 --- a/chrome/test/data/extensions/api_test/incognito/split/manifest.json +++ b/chrome/test/data/extensions/api_test/incognito/split/manifest.json @@ -4,5 +4,5 @@ "description": "test that an incognito extension in split mode behaves properly", "background_page": "background.html", "incognito": "split", - "permissions": ["tabs", "http://*/*"] + "permissions": ["tabs", "bookmarks", "http://*/*"] } |