diff options
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, |