diff options
author | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-09 19:48:37 +0000 |
---|---|---|
committer | tommi@chromium.org <tommi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-09 19:48:37 +0000 |
commit | 2879092e01ea4ebb93c1b80925c81e059b24607b (patch) | |
tree | 53638b170c3c9c3146d13238d1ed20993cdb4257 | |
parent | 12a6f036f3859f88b439dba009172f5c1dd03dc9 (diff) | |
download | chromium_src-2879092e01ea4ebb93c1b80925c81e059b24607b.zip chromium_src-2879092e01ea4ebb93c1b80925c81e059b24607b.tar.gz chromium_src-2879092e01ea4ebb93c1b80925c81e059b24607b.tar.bz2 |
Changing ForwardMessageToExternalHost to postMessage and passing a proper
MessageEvent object to the onmessage handler.
Also adding support for origin and target parameters. The origin parameter is
implicit but target can be specified when calling postMessage. If no target
is specified we default to "*".
At the moment I'm only allowing target == "*" messages to pass through since
I haven't implemented support for matching more complicated patterns :)
Review URL: http://codereview.chromium.org/40128
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11275 0039d316-1c4b-4281-b951-d872f2087c98
25 files changed, 427 insertions, 69 deletions
diff --git a/chrome/browser/automation/automation_provider.cc b/chrome/browser/automation/automation_provider.cc index 72767c5..d127040 100644 --- a/chrome/browser/automation/automation_provider.cc +++ b/chrome/browser/automation/automation_provider.cc @@ -2435,8 +2435,10 @@ void AutomationProvider::AutocompleteEditIsQueryInProgress( } } -void AutomationProvider::OnMessageFromExternalHost( - int handle, const std::string& message) { +void AutomationProvider::OnMessageFromExternalHost(int handle, + const std::string& message, + const std::string& origin, + const std::string& target) { if (tab_tracker_->ContainsHandle(handle)) { NavigationController* tab = tab_tracker_->GetResource(handle); if (!tab) { @@ -2460,7 +2462,7 @@ void AutomationProvider::OnMessageFromExternalHost( return; } - view_host->ForwardMessageFromExternalHost(message); + view_host->ForwardMessageFromExternalHost(message, origin, target); } } diff --git a/chrome/browser/automation/automation_provider.h b/chrome/browser/automation/automation_provider.h index 26fd979..c9aa0be 100644 --- a/chrome/browser/automation/automation_provider.h +++ b/chrome/browser/automation/automation_provider.h @@ -352,7 +352,9 @@ class AutomationProvider : public base::RefCounted<AutomationProvider>, std::vector<AutocompleteMatchData>* matches); // Handler for a message sent by the automation client. - void OnMessageFromExternalHost(int handle, const std::string& message); + void OnMessageFromExternalHost(int handle, const std::string& message, + const std::string& origin, + const std::string& target); // Retrieves the number of SSL related info-bars currently showing in |count|. void GetSSLInfoBarCount(int handle, int* count); diff --git a/chrome/browser/external_tab_container.cc b/chrome/browser/external_tab_container.cc index be8cbdc..9fc97ce 100644 --- a/chrome/browser/external_tab_container.cc +++ b/chrome/browser/external_tab_container.cc @@ -260,10 +260,12 @@ void ExternalTabContainer::ToolbarSizeChanged(TabContents* source, } void ExternalTabContainer::ForwardMessageToExternalHost( - const std::string& message) { + const std::string& message, const std::string& origin, + const std::string& target) { if(automation_) { automation_->Send( - new AutomationMsg_ForwardMessageToExternalHost(0, message)); + new AutomationMsg_ForwardMessageToExternalHost(0, message, origin, + target)); } } diff --git a/chrome/browser/external_tab_container.h b/chrome/browser/external_tab_container.h index 1bde9f9..d1825d0 100644 --- a/chrome/browser/external_tab_container.h +++ b/chrome/browser/external_tab_container.h @@ -83,7 +83,9 @@ class ExternalTabContainer : public TabContentsDelegate, virtual void UpdateTargetURL(TabContents* source, const GURL& url); virtual void ContentsZoomChange(bool zoom_in); virtual void ToolbarSizeChanged(TabContents* source, bool is_animating); - virtual void ForwardMessageToExternalHost(const std::string& message); + virtual void ForwardMessageToExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); virtual bool IsExternalTabContainer() const { return true; }; diff --git a/chrome/browser/renderer_host/render_view_host.cc b/chrome/browser/renderer_host/render_view_host.cc index 68654f4e..155f6a3 100644 --- a/chrome/browser/renderer_host/render_view_host.cc +++ b/chrome/browser/renderer_host/render_view_host.cc @@ -1048,8 +1048,9 @@ void RenderViewHost::OnMsgDOMUISend( } void RenderViewHost::OnMsgForwardMessageToExternalHost( - const std::string& message) { - delegate_->ProcessExternalHostMessage(message); + const std::string& message, const std::string& origin, + const std::string& target) { + delegate_->ProcessExternalHostMessage(message, origin, target); } #ifdef CHROME_PERSONALIZATION @@ -1337,7 +1338,9 @@ void RenderViewHost::RaisePersonalizationEvent(std::string event_name, } #endif -void RenderViewHost::ForwardMessageFromExternalHost( - const std::string& message) { - Send(new ViewMsg_HandleMessageFromExternalHost(routing_id(), message)); +void RenderViewHost::ForwardMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target) { + Send(new ViewMsg_HandleMessageFromExternalHost(routing_id(), message, origin, + target)); } diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index a10f421..62bdd64 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -398,7 +398,9 @@ class RenderViewHost : public RenderWidgetHost { #endif // Forward a message from external host to chrome renderer. - void ForwardMessageFromExternalHost(const std::string& message); + void ForwardMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); // Message the renderer that we should be counted as a new document and not // as a popup. @@ -485,7 +487,9 @@ class RenderViewHost : public RenderWidgetHost { int automation_id); void OnMsgDOMUISend(const std::string& message, const std::string& content); - void OnMsgForwardMessageToExternalHost(const std::string& message); + void OnMsgForwardMessageToExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); #ifdef CHROME_PERSONALIZATION void OnPersonalizationEvent(const std::string& message, const std::string& content); diff --git a/chrome/browser/renderer_host/render_view_host_delegate.h b/chrome/browser/renderer_host/render_view_host_delegate.h index 302a1d0..db6623d 100644 --- a/chrome/browser/renderer_host/render_view_host_delegate.h +++ b/chrome/browser/renderer_host/render_view_host_delegate.h @@ -258,7 +258,10 @@ class RenderViewHostDelegate { // A message for external host. By default we ignore such messages. // |receiver| can be a receiving script and |message| is any // arbitrary string that makes sense to the receiver. - virtual void ProcessExternalHostMessage(const std::string& message) { } + virtual void ProcessExternalHostMessage(const std::string& message, + const std::string& origin, + const std::string& target) { + } // Navigate to the history entry for the given offset from the current // position within the NavigationController. Makes no change if offset is diff --git a/chrome/browser/tab_contents/tab_contents_delegate.h b/chrome/browser/tab_contents/tab_contents_delegate.h index f97a6dc50..74cee1e 100644 --- a/chrome/browser/tab_contents/tab_contents_delegate.h +++ b/chrome/browser/tab_contents/tab_contents_delegate.h @@ -138,7 +138,10 @@ class TabContentsDelegate : public PageNavigator { } // Send IPC to external host. Default implementation is do nothing. - virtual void ForwardMessageToExternalHost(const std::string& message) {} + virtual void ForwardMessageToExternalHost(const std::string& message, + const std::string& origin, + const std::string& target) { + } // If the delegate is hosting tabs externally. virtual bool IsExternalTabContainer() const { return false; } diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc index affa867..8f1a5ea 100644 --- a/chrome/browser/tab_contents/web_contents.cc +++ b/chrome/browser/tab_contents/web_contents.cc @@ -1007,9 +1007,11 @@ void WebContents::DomOperationResponse(const std::string& json_string, Details<DomOperationNotificationDetails>(&details)); } -void WebContents::ProcessExternalHostMessage(const std::string& message) { +void WebContents::ProcessExternalHostMessage(const std::string& message, + const std::string& origin, + const std::string& target) { if (delegate()) - delegate()->ForwardMessageToExternalHost(message); + delegate()->ForwardMessageToExternalHost(message, origin, target); } void WebContents::GoToEntryAtOffset(int offset) { diff --git a/chrome/browser/tab_contents/web_contents.h b/chrome/browser/tab_contents/web_contents.h index e1d21de..8496841 100644 --- a/chrome/browser/tab_contents/web_contents.h +++ b/chrome/browser/tab_contents/web_contents.h @@ -334,7 +334,9 @@ class WebContents : public TabContents, WindowOpenDisposition disposition); virtual void DomOperationResponse(const std::string& json_string, int automation_id); - virtual void ProcessExternalHostMessage(const std::string& message); + virtual void ProcessExternalHostMessage(const std::string& message, + const std::string& origin, + const std::string& target); virtual void GoToEntryAtOffset(int offset); virtual void GetHistoryListCount(int* back_list_count, int* forward_list_count); diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index e0fd20c..80b78e1 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -457,8 +457,10 @@ IPC_BEGIN_MESSAGES(View) std::string /* event arguments */) #endif // Posts a message to the renderer. - IPC_MESSAGE_ROUTED1(ViewMsg_HandleMessageFromExternalHost, - std::string /* The message */) + IPC_MESSAGE_ROUTED3(ViewMsg_HandleMessageFromExternalHost, + std::string /* The message */, + std::string /* The origin */, + std::string /* The target*/) // Sent to the renderer when a popup window should no longer count against // the current popup count (either because it's not a popup or because it was @@ -807,8 +809,10 @@ IPC_BEGIN_MESSAGES(ViewHost) std::string /* args (as a JSON string) */) // A message for an external host. - IPC_MESSAGE_ROUTED1(ViewHostMsg_ForwardMessageToExternalHost, - std::string /* message */) + IPC_MESSAGE_ROUTED3(ViewHostMsg_ForwardMessageToExternalHost, + std::string /* message */, + std::string /* origin */, + std::string /* target */) #ifdef CHROME_PERSONALIZATION IPC_MESSAGE_ROUTED2(ViewHostMsg_PersonalizationEvent, diff --git a/chrome/common/temp_scaffolding_stubs.cc b/chrome/common/temp_scaffolding_stubs.cc index 969c615..3dd9cef 100644 --- a/chrome/common/temp_scaffolding_stubs.cc +++ b/chrome/common/temp_scaffolding_stubs.cc @@ -161,7 +161,8 @@ void AutomationProvider::AutocompleteEditIsQueryInProgress( } void AutomationProvider::OnMessageFromExternalHost( - int handle, const std::string& message) { + int handle, const std::string& message, const std::string& origin, + const std::string& target) { NOTIMPLEMENTED(); } diff --git a/chrome/renderer/external_host_bindings.cc b/chrome/renderer/external_host_bindings.cc index 27f4a88..09c8e78 100644 --- a/chrome/renderer/external_host_bindings.cc +++ b/chrome/renderer/external_host_bindings.cc @@ -6,43 +6,147 @@ #include "base/values.h" #include "chrome/common/render_messages.h" +#include "webkit/glue/webframe.h" -ExternalHostBindings::ExternalHostBindings() { - BindMethod("ForwardMessageToExternalHost", - &ExternalHostBindings::ForwardMessageToExternalHost); +ExternalHostBindings::ExternalHostBindings() : frame_(NULL) { + BindMethod("postMessage", &ExternalHostBindings::postMessage); BindProperty("onmessage", &on_message_handler_); } -void ExternalHostBindings::ForwardMessageToExternalHost( +void ExternalHostBindings::postMessage( const CppArgumentList& args, CppVariant* result) { - // We only accept a string message identifier. - if (args.size() != 1) - return; + DCHECK(result); - // Args should be strings. - if (!args[0].isString()) + // We need at least one argument (message) and at most 2 arguments. + // Also, the first argument must be a string + if (args.size() < 1 || args.size() > 2 || !args[0].isString()) { + result->Set(false); return; + } const std::string& message = args[0].ToString(); + std::string target; + if (args.size() >= 2 && args[1].isString()) { + target = args[1].ToString(); + } else { + target = "*"; + } + + std::string origin; + GURL origin_url(frame_->GetURL().GetOrigin()); + if (origin_url.is_empty()) { + // If the origin is not a scheme/host/port tuple, then return the literal + // string "null". + origin = "null"; + } else { + origin = origin_url.spec(); + } - sender()->Send(new ViewHostMsg_ForwardMessageToExternalHost( - routing_id(), message)); + result->Set(sender()->Send(new ViewHostMsg_ForwardMessageToExternalHost( + routing_id(), message, origin, target))); } bool ExternalHostBindings::ForwardMessageFromExternalHost( - const std::string& message) { + const std::string& message, const std::string& origin, + const std::string& target) { if (!on_message_handler_.isObject()) return false; - CppVariant result; + bool status = false; + + // TODO(tommi): Do the appropriate target check and drop the event if + // the target doesn't match the url of the current document. + // See: http://dev.w3.org/html5/spec/Overview.html#posting-messages + if (target.compare("*") != 0) { + DLOG(WARNING) << "Dropping posted message since the target wasn't '*' " + "and we haven't implemented parsing of the target param"; + return false; + } + + // Construct an event object, assign the origin to the origin member and + // assign message parameter to the 'data' member of the event. + NPObject* event_obj = NULL; + CreateMessageEvent(&event_obj); + if (!event_obj) { + NOTREACHED() << "CreateMessageEvent failed"; + } else { + NPIdentifier init_message_event = + NPN_GetStringIdentifier("initMessageEvent"); + NPVariant init_args[8]; + STRINGN_TO_NPVARIANT("message", sizeof("message") - 1, + init_args[0]); // type + BOOLEAN_TO_NPVARIANT(false, init_args[1]); // canBubble + BOOLEAN_TO_NPVARIANT(true, init_args[2]); // cancelable + STRINGN_TO_NPVARIANT(message.c_str(), message.length(), \ + init_args[3]); // data + STRINGN_TO_NPVARIANT(origin.c_str(), origin.length(), \ + init_args[4]); // origin + STRINGN_TO_NPVARIANT("", 0, init_args[5]); // lastEventId + NULL_TO_NPVARIANT(init_args[6]); // source + NULL_TO_NPVARIANT(init_args[7]); // messagePort + + NPVariant result; + NULL_TO_NPVARIANT(result); + status = NPN_Invoke(NULL, event_obj, init_message_event, init_args, + arraysize(init_args), &result); + DCHECK(status) << "Failed to initialize MessageEvent"; + NPN_ReleaseVariantValue(&result); - NPVariant arg; - arg.type = NPVariantType_String; - arg.value.stringValue.UTF8Characters = message.c_str(); - arg.value.stringValue.UTF8Length = static_cast<int>(message.length()); + if (status) { + NPVariant event_arg; + OBJECT_TO_NPVARIANT(event_obj, event_arg); + status = NPN_InvokeDefault(NULL, on_message_handler_.value.objectValue, + &event_arg, 1, &result); + // Don't DCHECK here in case the reason for the failure is a script error. + DLOG_IF(ERROR, !status) << "NPN_InvokeDefault failed"; + NPN_ReleaseVariantValue(&result); + } + + NPN_ReleaseObject(event_obj); + } - bool status = NPN_InvokeDefault(NULL, on_message_handler_.value.objectValue, - &arg, 1, &result); - DCHECK(status); return status; } + +bool ExternalHostBindings::CreateMessageEvent(NPObject** message_event) { + DCHECK(message_event != NULL); + DCHECK(frame_ != NULL); + + NPObject* window = frame_->GetWindowNPObject(); + if (!window) { + NOTREACHED() << "frame_->GetWindowNPObject"; + return false; + } + + const char* identifier_names[] = { + "document", + "createEvent", + }; + + NPIdentifier identifiers[arraysize(identifier_names)] = {0}; + NPN_GetStringIdentifiers(identifier_names, arraysize(identifier_names), + identifiers); + + CppVariant document; + bool ok = NPN_GetProperty(NULL, window, identifiers[0], &document); + DCHECK(document.isObject()); + + bool success = false; + if (ok && document.isObject()) { + NPVariant result, event_type; + STRINGN_TO_NPVARIANT("MessageEvent", sizeof("MessageEvent") - 1, \ + event_type); + success = NPN_Invoke(NULL, document.value.objectValue, identifiers[1], + &event_type, 1, &result); + DCHECK(!success || result.type == NPVariantType_Object); + if (result.type != NPVariantType_Object) { + DCHECK(success == false); + } else { + DCHECK(success != false); + // Pass the ownership to the caller (don't call ReleaseVariantValue). + *message_event = result.value.objectValue; + } + } + + return success; +} diff --git a/chrome/renderer/external_host_bindings.h b/chrome/renderer/external_host_bindings.h index b4c46ad..62caac6 100644 --- a/chrome/renderer/external_host_bindings.h +++ b/chrome/renderer/external_host_bindings.h @@ -12,22 +12,37 @@ // accessible from Javascript // // We expose one function, for sending a message to the external host: -// ForwardMessageToExternalHost(String receiver, String message); +// postMessage(String message[, String target]); class ExternalHostBindings : public DOMBoundBrowserObject { public: ExternalHostBindings(); - virtual ~ExternalHostBindings() {}; + virtual ~ExternalHostBindings() { + } - // The ForwardMessageToExternalHost() function provided to Javascript. - void ForwardMessageToExternalHost(const CppArgumentList& args, - CppVariant* result); + // The postMessage() function provided to Javascript. + void postMessage(const CppArgumentList& args, CppVariant* result); // Invokes the registered onmessage handler. // Returns true on successful invocation. - bool ForwardMessageFromExternalHost(const std::string& message); + bool ForwardMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); + + // Overridden to hold onto a pointer back to the web frame. + void BindToJavascript(WebFrame* frame, const std::wstring& classname) { + frame_ = frame; + DOMBoundBrowserObject::BindToJavascript(frame, classname); + } + + protected: + // Creates an uninitialized instance of a MessageEvent object. + // This is equivalent to calling window.document.createEvent("MessageEvent") + // in javascript. + bool CreateMessageEvent(NPObject** message_event); private: CppVariant on_message_handler_; + WebFrame* frame_; DISALLOW_COPY_AND_ASSIGN(ExternalHostBindings); }; diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 6251636..3f11ec2 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -2840,11 +2840,14 @@ void RenderView::DidAddHistoryItem() { history_forward_list_count_ = 0; } -void RenderView::OnMessageFromExternalHost(const std::string& message) { +void RenderView::OnMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target) { if (message.empty()) return; - external_host_bindings_.ForwardMessageFromExternalHost(message); + external_host_bindings_.ForwardMessageFromExternalHost(message, origin, + target); } void RenderView::OnDisassociateFromPopupCount() { diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 700faa9..17f4cd1 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -546,7 +546,9 @@ class RenderView : public RenderWidget, #endif // Handles messages posted from automation. - void OnMessageFromExternalHost(const std::string& message); + void OnMessageFromExternalHost(const std::string& message, + const std::string& origin, + const std::string& target); // Message that we should no longer be part of the current popup window // grouping, and should form our own grouping. diff --git a/chrome/test/automation/automation_messages_internal.h b/chrome/test/automation/automation_messages_internal.h index 8fb532d..98474ac 100644 --- a/chrome/test/automation/automation_messages_internal.h +++ b/chrome/test/automation/automation_messages_internal.h @@ -723,13 +723,17 @@ IPC_BEGIN_MESSAGES(Automation) int /* tab_handle */) // Posts a message from external host to chrome renderer. - IPC_MESSAGE_ROUTED2(AutomationMsg_HandleMessageFromExternalHost, + IPC_MESSAGE_ROUTED4(AutomationMsg_HandleMessageFromExternalHost, int /* automation handle */, - std::string /* message */ ) + std::string /* message */, + std::string /* origin */, + std::string /* target */) // A message for an external host. - IPC_MESSAGE_ROUTED1(AutomationMsg_ForwardMessageToExternalHost, - std::string /* message*/) + IPC_MESSAGE_ROUTED3(AutomationMsg_ForwardMessageToExternalHost, + std::string /* message */, + std::string /* origin */, + std::string /* target */) // This message starts a find within a tab corresponding to the supplied // tab handle. The parameter |request| specifies what to search for. diff --git a/chrome/test/automation/automation_proxy_uitest.cc b/chrome/test/automation/automation_proxy_uitest.cc index ee1d4db..e296f62 100644 --- a/chrome/test/automation/automation_proxy_uitest.cc +++ b/chrome/test/automation/automation_proxy_uitest.cc @@ -37,6 +37,21 @@ class AutomationProxyVisibleTest : public UITest { } }; +template <class AutomationProxyClass> +class CustomAutomationProxyTest : public AutomationProxyVisibleTest { + protected: + CustomAutomationProxyTest() { + } + + // Override UITest's CreateAutomationProxy to provide our the unit test + // with our special implementation of AutomationProxy. + // This function is called from within UITest::LaunchBrowserAndServer. + virtual AutomationProxy* CreateAutomationProxy(int execution_timeout) { + AutomationProxyClass* proxy = new AutomationProxyClass(execution_timeout); + return proxy; + } +}; + TEST_F(AutomationProxyTest, GetBrowserWindowCount) { int window_count = 0; EXPECT_TRUE(automation()->GetBrowserWindowCount(&window_count)); @@ -708,7 +723,13 @@ TEST_F(AutomationProxyTest, CantEscapeByOnloadMoveto) { ASSERT_NE(20, rect.y()); } -bool ExternalTabHandler(HWND external_tab_window) { +// Creates a top-level window, makes the |external_tab_window| a child +// of that window and displays them. After displaying the windows the function +// enters a message loop that processes window messages as well as calling +// MessageLoop::current()->RunAllPending() to process any incoming IPC messages. +// The time_to_wait parameter is the maximum time the loop will run. +// To end the loop earlier, post a quit message to the thread. +bool ExternalTabHandler(HWND external_tab_window, int time_to_wait) { static const wchar_t class_name[] = L"External_Tab_UI_Test_Class"; static const wchar_t window_title[] = L"External Tab Tester"; @@ -718,8 +739,9 @@ bool ExternalTabHandler(HWND external_tab_window) { wnd_class.lpfnWndProc = DefWindowProc; wnd_class.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wnd_class.lpszClassName = class_name; - ATOM result = RegisterClassEx(&wnd_class); - if (0 == result) { + ATOM atom = RegisterClassEx(&wnd_class); + if (0 == atom) { + NOTREACHED() << "RegisterClassEx"; return false; } @@ -741,26 +763,181 @@ bool ExternalTabHandler(HWND external_tab_window) { ShowWindow(external_tab_ui_parent, SW_SHOW); // Allow the renderers to connect. - Sleep(1000); + + const int kTimerIdQuit = 100; + const int kTimerIdProcessPendingMessages = 101; + + ::SetTimer(external_tab_ui_parent, kTimerIdQuit, time_to_wait, NULL); + // Process pending messages every 50 milliseconds + ::SetTimer(external_tab_ui_parent, kTimerIdProcessPendingMessages, 50, NULL); + + MSG msg; + bool quit = false; + do { + BOOL ok = ::GetMessage(&msg, NULL, 0, 0); + if (!ok || ok == -1) + break; + + if (msg.message == WM_TIMER && msg.hwnd == external_tab_ui_parent) { + switch (msg.wParam) { + case kTimerIdProcessPendingMessages: + MessageLoop::current()->RunAllPending(); + break; + + case kTimerIdQuit: + ::KillTimer(external_tab_ui_parent, msg.wParam); + quit = true; + break; + + default: + NOTREACHED() << "invalid timer id"; + break; + } + } else if (msg.message == WM_QUIT) { + quit = true; + } else { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + // In case there's an interactive user that closes the window. + } while (!quit && ::IsWindow(external_tab_ui_parent)); + DestroyWindow(external_tab_ui_parent); + + EXPECT_TRUE(UnregisterClassW(reinterpret_cast<const wchar_t*>(atom), + wnd_class.hInstance)); + return true; } -TEST_F(AutomationProxyVisibleTest, CreateExternalTab) { +// A single-use AutomationProxy implementation that's good +// for a single navigation and a single ForwardMessageToExternalHost +// message. Once the ForwardMessageToExternalHost message is received +// the class posts a quit message to the thread on which the message +// was received. +class AutomationProxyForExternalTab : public AutomationProxy { + public: + AutomationProxyForExternalTab(int execution_timeout) + : AutomationProxy(execution_timeout), + messages_received_(0), + navigate_complete_(false) { + } + + int messages_received() const { + return messages_received_; + } + + const std::string& message() const { + return message_; + } + + const std::string& origin() const { + return origin_; + } + + const std::string& target() const { + return target_; + } + + // Waits for the DidNavigate event to be processed on the current thread. + // Returns true if the event arrived, false if there was a timeout. + bool WaitForNavigationComplete(int max_time_to_wait_ms) { + base::TimeTicks start(base::TimeTicks::Now()); + while (!navigate_complete_) { + Sleep(50); + MessageLoop::current()->RunAllPending(); + base::TimeTicks end(base::TimeTicks::Now()); + base::TimeDelta delta = end - start; + if (static_cast<int>(delta.InMilliseconds()) > max_time_to_wait_ms) + return false; + } + return true; + } + + protected: + virtual void OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(AutomationProxyForExternalTab, msg) + IPC_MESSAGE_HANDLER(AutomationMsg_DidNavigate, OnDidNavigate) + IPC_MESSAGE_HANDLER(AutomationMsg_ForwardMessageToExternalHost, + OnForwardMessageToExternalHost) + IPC_END_MESSAGE_MAP() + } + + void OnDidNavigate(int navigation_type, int relative_offset, + const GURL& url) { + navigate_complete_ = true; + } + + void OnForwardMessageToExternalHost(const std::string& message, + const std::string& origin, + const std::string& target) { + messages_received_++; + message_ = message; + origin_ = origin; + target_ = target; + PostQuitMessage(0); + } + + protected: + bool navigate_complete_; + int messages_received_; + std::string message_, origin_, target_; +}; + +typedef CustomAutomationProxyTest<AutomationProxyForExternalTab> + ExternalTabTestType; + +TEST_F(ExternalTabTestType, CreateExternalTab) { HWND external_tab_container = NULL; - scoped_ptr<TabProxy> tab(automation()->CreateExternalTab( - NULL, gfx::Rect(), WS_POPUP, &external_tab_container)); + scoped_ptr<TabProxy> tab(automation()->CreateExternalTab(NULL, gfx::Rect(), + WS_POPUP, &external_tab_container)); EXPECT_TRUE(tab != NULL); EXPECT_NE(FALSE, ::IsWindow(external_tab_container)); if (tab != NULL) { tab->NavigateInExternalTab(GURL(L"http://www.google.com")); - EXPECT_EQ(true, ExternalTabHandler(external_tab_container)); + EXPECT_EQ(true, ExternalTabHandler(external_tab_container, 1000)); // Since the tab goes away lazily, wait a bit Sleep(1000); EXPECT_FALSE(tab->is_valid()); } } +TEST_F(ExternalTabTestType, ExternalTabPostMessage) { + AutomationProxyForExternalTab* proxy = + static_cast<AutomationProxyForExternalTab*>(automation()); + + HWND external_tab_container = NULL; + scoped_ptr<TabProxy> tab(proxy->CreateExternalTab(NULL, gfx::Rect(), + WS_POPUP, &external_tab_container)); + EXPECT_TRUE(tab != NULL); + EXPECT_NE(FALSE, ::IsWindow(external_tab_container)); + if (tab != NULL) { + std::string content = + "data:text/html,<html><head><script>" + "function onload() {" + " window.externalHost.onmessage = onMessage;" + "}" + "function onMessage(evt) {" + " window.externalHost.postMessage(evt.data, '*');" + "}" + "</script></head>" + "<body onload='onload()'>external tab test<br></div>" + "</body></html>"; + tab->NavigateInExternalTab(GURL(content)); + EXPECT_TRUE(proxy->WaitForNavigationComplete(10000)); + + tab->HandleMessageFromExternalHost(tab->handle(), "Hello from gtest", + "null", "*"); + + EXPECT_TRUE(ExternalTabHandler(external_tab_container, 10000)); + EXPECT_NE(0, proxy->messages_received()); + + if (proxy->messages_received()) { + EXPECT_EQ("Hello from gtest", proxy->message()); + } + } +} + TEST_F(AutomationProxyTest, AutocompleteGetSetText) { scoped_ptr<BrowserProxy> browser(automation()->GetBrowserWindow(0)); ASSERT_TRUE(browser.get()); diff --git a/chrome/test/automation/tab_proxy.cc b/chrome/test/automation/tab_proxy.cc index bca9c03..4033d65 100644 --- a/chrome/test/automation/tab_proxy.cc +++ b/chrome/test/automation/tab_proxy.cc @@ -535,13 +535,15 @@ bool TabProxy::SavePage(const std::wstring& file_name, } void TabProxy::HandleMessageFromExternalHost(AutomationHandle handle, - const std::string& message) { + const std::string& message, + const std::string& origin, + const std::string& target) { if (!is_valid()) return; bool succeeded = sender_->Send(new AutomationMsg_HandleMessageFromExternalHost(0, handle, - message)); + message, origin, target)); DCHECK(succeeded); } diff --git a/chrome/test/automation/tab_proxy.h b/chrome/test/automation/tab_proxy.h index 26cdde5..54e5df4 100644 --- a/chrome/test/automation/tab_proxy.h +++ b/chrome/test/automation/tab_proxy.h @@ -253,7 +253,9 @@ class TabProxy : public AutomationResourceProxy { // Posts a message to the external tab. void HandleMessageFromExternalHost(AutomationHandle handle, - const std::string& message); + const std::string& message, + const std::string& origin, + const std::string& target); // Retrieves the number of SSL related info-bars currently showing in |count|. bool GetSSLInfoBarCount(int* count); diff --git a/chrome/test/ui/ui_test.cc b/chrome/test/ui/ui_test.cc index 1861626..d9920c5 100644 --- a/chrome/test/ui/ui_test.cc +++ b/chrome/test/ui/ui_test.cc @@ -211,9 +211,14 @@ void UITest::InitializeTimeouts() { } } +AutomationProxy* UITest::CreateAutomationProxy(int execution_timeout) { + // By default we create a plain vanilla AutomationProxy. + return new AutomationProxy(execution_timeout); +} + void UITest::LaunchBrowserAndServer() { // Set up IPC testing interface server. - server_.reset(new AutomationProxy(command_execution_timeout_ms_)); + server_.reset(CreateAutomationProxy(command_execution_timeout_ms_)); LaunchBrowser(launch_arguments_, clear_profile_); if (wait_for_initial_loads_) diff --git a/chrome/test/ui/ui_test.h b/chrome/test/ui/ui_test.h index 794391b..2bd3b9e 100644 --- a/chrome/test/ui/ui_test.h +++ b/chrome/test/ui/ui_test.h @@ -71,6 +71,9 @@ class UITest : public testing::Test { // Launches the browser and IPC testing server. void LaunchBrowserAndServer(); + // Overridable so that derived classes can provide their own AutomationProxy. + virtual AutomationProxy* CreateAutomationProxy(int execution_timeout); + // Closes the browser and IPC testing server. void CloseBrowserAndServer(); diff --git a/webkit/glue/webframe.h b/webkit/glue/webframe.h index 4a0cf01..d35376e 100644 --- a/webkit/glue/webframe.h +++ b/webkit/glue/webframe.h @@ -47,6 +47,8 @@ class WebFrame { // TODO(fqian): Remove this method when V8 supports NP runtime. virtual void* GetFrameImplementation() = 0; + virtual NPObject* GetWindowNPObject() = 0; + // Loads the given WebRequest. virtual void LoadRequest(WebRequest* request) = 0; diff --git a/webkit/glue/webframe_impl.cc b/webkit/glue/webframe_impl.cc index 28ba59c..bf4fb03 100644 --- a/webkit/glue/webframe_impl.cc +++ b/webkit/glue/webframe_impl.cc @@ -741,6 +741,13 @@ void WebFrameImpl::GetContentAsPlainText(int max_chars, FrameContentAsPlainText(max_chars, frame_, text); } +NPObject* WebFrameImpl::GetWindowNPObject() { + if (!frame_) + return NULL; + + return frame_->script()->windowScriptNPObject(); +} + void WebFrameImpl::InvalidateArea(AreaToInvalidate area) { ASSERT(frame() && frame()->view()); #if defined(OS_WIN) diff --git a/webkit/glue/webframe_impl.h b/webkit/glue/webframe_impl.h index 63950aa..ed7ee15 100644 --- a/webkit/glue/webframe_impl.h +++ b/webkit/glue/webframe_impl.h @@ -116,6 +116,8 @@ class WebFrameImpl : public WebFrame, public base::RefCounted<WebFrameImpl> { virtual void* GetFrameImplementation() { return frame(); } + virtual NPObject* GetWindowNPObject(); + virtual void GetContentAsPlainText(int max_chars, std::wstring* text) const; virtual bool Find(const FindInPageRequest& request, bool wrap_within_frame, |