From a902489dc0a391b0bfa92b8a172aa6369b568436 Mon Sep 17 00:00:00 2001 From: "rafaelw@chromium.org" Date: Tue, 16 Jun 2009 23:13:55 +0000 Subject: submitted on behalf of rogerta (Roger Tawa). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original issue: http://codereview.chromium.org/119325 r=rafaelw,Jói,stoyan,aa Review URL: http://codereview.chromium.org/125206 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@18555 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/automation/automation_provider.cc | 53 +++++- chrome/browser/automation/automation_provider.h | 6 + .../automation/extension_automation_constants.cc | 1 + .../automation/extension_automation_constants.h | 3 + chrome/browser/extensions/extension_uitest.cc | 178 +++++++++++++++++++++ .../extensions/uitest/event_sink/manifest.json | 5 + .../data/extensions/uitest/event_sink/test.html | 79 +++++++++ 7 files changed, 323 insertions(+), 2 deletions(-) create mode 100755 chrome/test/data/extensions/uitest/event_sink/manifest.json create mode 100755 chrome/test/data/extensions/uitest/event_sink/test.html (limited to 'chrome') diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 9445b92..7cde178 100755 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -7,6 +7,7 @@ #include "app/l10n_util.h" #include "app/message_box_flags.h" #include "base/file_version_info.h" +#include "base/json_reader.h" #include "base/message_loop.h" #include "base/path_service.h" #include "base/stl_util-inl.h" @@ -18,6 +19,7 @@ #include "chrome/browser/app_modal_dialog_queue.h" #include "chrome/browser/automation/automation_extension_function.h" #include "chrome/browser/automation/automation_provider_list.h" +#include "chrome/browser/automation/extension_automation_constants.h" #include "chrome/browser/automation/extension_port_container.h" #include "chrome/browser/automation/url_request_failed_dns_job.h" #include "chrome/browser/automation/url_request_mock_http_job.h" @@ -28,6 +30,7 @@ #include "chrome/browser/dom_operation_notification_details.h" #include "chrome/browser/download/download_manager.h" #include "chrome/browser/download/download_shelf.h" +#include "chrome/browser/extensions/extension_message_service.h" #include "chrome/browser/find_bar.h" #include "chrome/browser/find_bar_controller.h" #include "chrome/browser/find_notification_details.h" @@ -2682,13 +2685,18 @@ void AutomationProvider::OnMessageFromExternalHost(int handle, } if (AutomationExtensionFunction::InterceptMessageFromExternalHost( - view_host, message, origin, target)) { + view_host, message, origin, target)) { // Message was diverted. return; } if (ExtensionPortContainer::InterceptMessageFromExternalHost(message, - origin, target, this, view_host, handle)) { + origin, target, this, view_host, handle)) { + // Message was diverted. + return; + } + + if (InterceptBrowserEventMessageFromExternalHost(message, origin, target)) { // Message was diverted. return; } @@ -2696,6 +2704,47 @@ void AutomationProvider::OnMessageFromExternalHost(int handle, view_host->ForwardMessageFromExternalHost(message, origin, target); } } + +bool AutomationProvider::InterceptBrowserEventMessageFromExternalHost( + const std::string& message, const std::string& origin, + const std::string& target) { + if (target != + extension_automation_constants::kAutomationBrowserEventRequestTarget) + return false; + + if (origin != extension_automation_constants::kAutomationOrigin) { + LOG(WARNING) << "Wrong origin on automation browser event " << origin; + return false; + } + + // The message is a JSON-encoded array with two elements, both strings. The + // first is the name of the event to dispatch. The second is a JSON-encoding + // of the arguments specific to that event. + scoped_ptr message_value(JSONReader::Read(message, false)); + if (!message_value.get() || !message_value->IsType(Value::TYPE_LIST)) { + LOG(WARNING) << "Invalid browser event specified through automation"; + return false; + } + + const ListValue* args = static_cast(message_value.get()); + + std::string event_name; + if (!args->GetString(0, &event_name)) { + LOG(WARNING) << "No browser event name specified through automation"; + return false; + } + + std::string json_args; + if (!args->GetString(1, &json_args)) { + LOG(WARNING) << "No browser event args specified through automation"; + return false; + } + + ExtensionMessageService::GetInstance(profile()->GetRequestContext())-> + DispatchEventToRenderers(event_name.c_str(), json_args); + + return true; +} #endif // defined(OS_WIN) || defined(OS_LINUX) TabContents* AutomationProvider::GetTabContentsForHandle( diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index ccf580d..6701655 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -462,6 +462,12 @@ class AutomationProvider : public base::RefCounted, bool success, HistoryService::RedirectList* redirects); + // Determine if the message from the external host represents a browser + // event, and if so dispatch it. + bool InterceptBrowserEventMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); + typedef ObserverList NotificationObserverList; typedef std::map LoginHandlerMap; typedef std::map PortContainerMap; diff --git a/chrome/browser/automation/extension_automation_constants.cc b/chrome/browser/automation/extension_automation_constants.cc index 425c634..6b99a41 100644 --- a/chrome/browser/automation/extension_automation_constants.cc +++ b/chrome/browser/automation/extension_automation_constants.cc @@ -24,5 +24,6 @@ const wchar_t kAutomationPortIdKey[] = L"portid"; const char kAutomationPortRequestTarget[] = "__priv_prtreq"; const char kAutomationPortResponseTarget[] = "__priv_prtres"; +const char kAutomationBrowserEventRequestTarget[] = "__priv_evtreq"; } // namespace extension_automation_constants diff --git a/chrome/browser/automation/extension_automation_constants.h b/chrome/browser/automation/extension_automation_constants.h index 4725bf3..06bd97d 100644 --- a/chrome/browser/automation/extension_automation_constants.h +++ b/chrome/browser/automation/extension_automation_constants.h @@ -35,6 +35,9 @@ extern const char kAutomationPortRequestTarget[]; // All external port message responses have this target. extern const char kAutomationPortResponseTarget[]; +// All external browser events have this target. +extern const char kAutomationBrowserEventRequestTarget[]; + // The command codes for our private port protocol. enum PrivatePortCommand { OPEN_CHANNEL = 0, diff --git a/chrome/browser/extensions/extension_uitest.cc b/chrome/browser/extensions/extension_uitest.cc index 4f5d870..359ba9a 100644 --- a/chrome/browser/extensions/extension_uitest.cc +++ b/chrome/browser/extensions/extension_uitest.cc @@ -9,6 +9,7 @@ #include "base/values.h" #include "chrome/browser/automation/extension_automation_constants.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/extensions/extension.h" #include "chrome/test/automation/automation_proxy_uitest.h" #include "chrome/test/automation/tab_proxy.h" #include "chrome/test/ui/ui_test.h" @@ -20,6 +21,8 @@ static const char kTestDirectorySimpleApiCall[] = "extensions/uitest/simple_api_call"; static const char kTestDirectoryRoundtripApiCall[] = "extensions/uitest/roundtrip_api_call"; +static const char kTestDirectoryBrowserEvent[] = + "extensions/uitest/event_sink"; // Base class to test extensions almost end-to-end by including browser // startup, manifest parsing, and the actual process model in the @@ -280,4 +283,179 @@ TEST_F(RoundtripApiCallExtensionTest, RunTest) { } #endif // defined(OS_WIN) +// This proxy is specific to BrowserEventExtensionTest. +class BrowserEventAutomationProxy : public MultiMessageAutomationProxy { + public: + explicit BrowserEventAutomationProxy(int execution_timeout) + : MultiMessageAutomationProxy(execution_timeout), + tab_(NULL) { + } + + // Must set before initiating test. + TabProxy* tab_; + + // Counts the number of times we got a given event. + std::map event_count_; + + // Array containing the names of the events to fire to the extension. + static const char* event_names_[]; + + protected: + // Process a message received from the test extension. + virtual void HandleMessageFromChrome(); + + // Fire an event of the given name to the test extension. + void FireEvent(const char* event_name); +}; + +const char* BrowserEventAutomationProxy::event_names_[] = { + // Window events. + "window-created", + "window-removed", + "window-focus-changed", + + // Tab events. + "tab-created", + "tab-updated", + "tab-moved", + "tab-selection-changed", + "tab-attached", + "tab-detached", + "tab-removed", + + // Page action events. + "page-action-executed", + + // Bookmark events. + "bookmark-added", + "bookmark-removed", + "bookmark-changed", + "bookmark-moved", + "bookmark-children-reordered", +}; + +void BrowserEventAutomationProxy::HandleMessageFromChrome() { + namespace keys = extension_automation_constants; + ASSERT_TRUE(tab_ != NULL); + + std::string message(message()); + std::string origin(origin()); + std::string target(target()); + + ASSERT_TRUE(message.length() > 0); + ASSERT_STREQ(keys::kAutomationOrigin, origin.c_str()); + + if (target == keys::kAutomationRequestTarget) { + // This should be a request for the current window. We don't need to + // respond, as this is used only as an indication that the extension + // page is now loaded. + scoped_ptr message_value(JSONReader::Read(message, false)); + ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* message_dict = + reinterpret_cast(message_value.get()); + + std::string name; + message_dict->GetString(keys::kAutomationNameKey, &name); + ASSERT_STREQ(name.c_str(), "GetCurrentWindow"); + + // Send an OpenChannelToExtension message to chrome. Note: the JSON + // reader expects quoted property keys. See the comment in + // TEST_F(BrowserEventExtensionTest, RunTest) to understand where the + // extension Id comes from. + tab_->HandleMessageFromExternalHost( + "{\"rqid\":0, \"extid\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"," + " \"connid\": 1}", + keys::kAutomationOrigin, + keys::kAutomationPortRequestTarget); + } else if (target == keys::kAutomationPortResponseTarget) { + // This is a response to the open channel request. This means we know + // that the port is ready to send us messages. Fire all the events now. + for (int i = 0; i < arraysize(event_names_); ++i) { + FireEvent(event_names_[i]); + } + } else if (target == keys::kAutomationPortRequestTarget) { + // This is the test extension calling us back. Make sure its telling + // us that it received an event. We do this by checking to see if the + // message is a simple string of one of the event names that is fired. + // + // There is a special message "ACK" which means that the extension + // received the port connection. This is not an event response and + // should happen before all events. + scoped_ptr message_value(JSONReader::Read(message, false)); + ASSERT_TRUE(message_value->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* message_dict = + reinterpret_cast(message_value.get()); + + std::string event_name; + message_dict->GetString(L"data", &event_name); + if (event_name == "\"ACK\"") { + ASSERT_EQ(0, event_count_.size()); + } else { + ++event_count_[event_name]; + } + } +} + +void BrowserEventAutomationProxy::FireEvent(const char* event_name) { + namespace keys = extension_automation_constants; + + // Build the event message to send to the extension. The only important + // part is the name, as the payload is not used by the test extension. + std::string message; + message += "[\""; + message += event_name; + message += "\", \"[]\"]"; + + tab_->HandleMessageFromExternalHost( + message, + keys::kAutomationOrigin, + keys::kAutomationBrowserEventRequestTarget); +} + +class BrowserEventExtensionTest + : public ExtensionUITest< + CustomAutomationProxyTest> { + public: + BrowserEventExtensionTest() + : ExtensionUITest< + CustomAutomationProxyTest< + BrowserEventAutomationProxy> >(kTestDirectoryBrowserEvent) { + } + + void DoAdditionalPreNavigateSetup(TabProxy* tab) { + BrowserEventAutomationProxy* proxy = + static_cast(automation()); + proxy->tab_ = tab; + } + + private: + + DISALLOW_COPY_AND_ASSIGN(BrowserEventExtensionTest); +}; + +// TODO(port) Should become portable once +// ExternalTabMessageLoop is ported. +#if defined(OS_WIN) +TEST_F(BrowserEventExtensionTest, RunTest) { + // The extension for this test does not specify a "key" property in its + // manifest file. Therefore, the extension system will automatically assign + // it an Id. To make this test consistent and non-flaky, the genetated Id + // counter is reset before the test so that we can hardcode the first Id + // that will be generated. + Extension::ResetGeneratedIdCounter(); + TestWithURL(GURL( + "chrome-extension://aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/test.html")); + BrowserEventAutomationProxy* proxy = + static_cast(automation()); + + EXPECT_EQ(arraysize(BrowserEventAutomationProxy::event_names_), + proxy->event_count_.size()); + for (std::map::iterator i = proxy->event_count_.begin(); + i != proxy->event_count_.end(); ++i) { + const std::pair& value = *i; + ASSERT_EQ(1, value.second); + } +} +#endif // defined(OS_WIN) + } // namespace diff --git a/chrome/test/data/extensions/uitest/event_sink/manifest.json b/chrome/test/data/extensions/uitest/event_sink/manifest.json new file mode 100755 index 0000000..830243d --- /dev/null +++ b/chrome/test/data/extensions/uitest/event_sink/manifest.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.0.0", + "name": "Browser Event Sink Test Extension", + "description": "An extension UITest for testing the sending of browser events." +} diff --git a/chrome/test/data/extensions/uitest/event_sink/test.html b/chrome/test/data/extensions/uitest/event_sink/test.html new file mode 100755 index 0000000..2520d48 --- /dev/null +++ b/chrome/test/data/extensions/uitest/event_sink/test.html @@ -0,0 +1,79 @@ +HOLA!!! + + -- cgit v1.1