diff options
12 files changed, 235 insertions, 8 deletions
diff --git a/content/browser/browser_plugin/browser_plugin_embedder.cc b/content/browser/browser_plugin/browser_plugin_embedder.cc index c70747e..c799222 100644 --- a/content/browser/browser_plugin/browser_plugin_embedder.cc +++ b/content/browser/browser_plugin/browser_plugin_embedder.cc @@ -112,6 +112,14 @@ void BrowserPluginEmbedder::CreateGuest(RenderViewHost* render_view_host, guest_renderer_prefs->browser_handles_all_top_level_requests = false; AddGuest(instance_id, guest_web_contents); guest_web_contents->SetDelegate(guest); + + // Create a swapped out RenderView for the guest in the embedder render + // process, so that the embedder can access the guest's window object. + int guest_routing_id = + static_cast<WebContentsImpl*>(guest->GetWebContents())-> + CreateSwappedOutRenderView(web_contents()->GetSiteInstance()); + render_view_host->Send(new BrowserPluginMsg_GuestContentWindowReady( + instance_id, guest_routing_id)); } void BrowserPluginEmbedder::NavigateGuest( @@ -134,7 +142,7 @@ void BrowserPluginEmbedder::NavigateGuest( // TODO(creis): Check the validity of the URL: http://crbug.com/139397. guest_web_contents->GetController().LoadURL(url, Referrer(), - PAGE_TRANSITION_AUTO_SUBFRAME, + PAGE_TRANSITION_AUTO_TOPLEVEL, std::string()); } @@ -246,7 +254,6 @@ void BrowserPluginEmbedder::DestroyGuestByInstanceID(int instance_id) { void BrowserPluginEmbedder::RenderViewDeleted( RenderViewHost* render_view_host) { - DestroyGuests(); } void BrowserPluginEmbedder::RenderViewGone(base::TerminationStatus status) { diff --git a/content/browser/browser_plugin/browser_plugin_host_browsertest.cc b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc index 4a2ccfb..c40bfde 100644 --- a/content/browser/browser_plugin/browser_plugin_host_browsertest.cc +++ b/content/browser/browser_plugin/browser_plugin_host_browsertest.cc @@ -833,4 +833,93 @@ IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, AcceptDragEvents) { EXPECT_EQ(expected_title, actual_title); } +// This test verifies that round trip postMessage works as expected. +// 1. The embedder posts a message 'testing123' to the guest. +// 2. The guest receives and replies to the message using the event object's +// source object: event.source.postMessage('foobar', '*') +// 3. The embedder receives the message and uses the event's source +// object to do one final reply: 'stop' +// 4. The guest receives the final 'stop' message. +// 5. The guest acks the 'stop' message with a 'stop_ack' message. +// 6. The embedder changes its title to 'main guest' when it sees the 'stop_ack' +// message. +IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, PostMessage) { + const char* kTesting = "testing123"; + const char* kEmbedderURL = "files/browser_plugin_embedder.html"; + const char* kGuestURL = "files/browser_plugin_post_message_guest.html"; + StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, ""); + RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( + test_embedder()->web_contents()->GetRenderViewHost()); + { + const string16 expected_title = ASCIIToUTF16("main guest"); + content::TitleWatcher title_watcher(test_embedder()->web_contents(), + expected_title); + + // By the time we get here 'contentWindow' should be ready because the + // guest has begun sending pixels to the embedder. This happens after + // the browser process sends the guest_routing_id to the embedder via + // BrowserPluginMsg_GuestContentWindowReady and after the browser process + // issues a ViewMsg_New to create the swapped out guest in the embedder's + // render process. + ExecuteSyncJSFunction(rvh, + ASCIIToUTF16(StringPrintf("PostMessage('%s, false');", kTesting))); + + // The title will be updated to "main guest" at the last stage of the + // process described above. + string16 actual_title = title_watcher.WaitAndGetTitle(); + EXPECT_EQ(expected_title, actual_title); + } +} + +// This is the same as BrowserPluginHostTest.PostMessage but also +// posts a message to an iframe. +// TODO(fsamuel): This test should replace the previous test once postMessage +// iframe targeting is fixed (see http://crbug.com/153701). +IN_PROC_BROWSER_TEST_F(BrowserPluginHostTest, DISABLED_PostMessageToIFrame) { + const char* kTesting = "testing123"; + const char* kEmbedderURL = "files/browser_plugin_embedder.html"; + const char* kGuestURL = "files/browser_plugin_post_message_guest.html"; + StartBrowserPluginTest(kEmbedderURL, kGuestURL, false, ""); + RenderViewHostImpl* rvh = static_cast<RenderViewHostImpl*>( + test_embedder()->web_contents()->GetRenderViewHost()); + { + const string16 expected_title = ASCIIToUTF16("main guest"); + content::TitleWatcher title_watcher(test_embedder()->web_contents(), + expected_title); + + ExecuteSyncJSFunction(rvh, + ASCIIToUTF16(StringPrintf("PostMessage('%s, false');", kTesting))); + + // The title will be updated to "main guest" at the last stage of the + // process described above. + string16 actual_title = title_watcher.WaitAndGetTitle(); + EXPECT_EQ(expected_title, actual_title); + } + { + content::TitleWatcher ready_watcher(test_embedder()->web_contents(), + ASCIIToUTF16("ready")); + + RenderViewHostImpl* guest_rvh = static_cast<RenderViewHostImpl*>( + test_guest()->web_contents()->GetRenderViewHost()); + GURL test_url = test_server()->GetURL( + "files/browser_plugin_post_message_guest.html"); + ExecuteSyncJSFunction(guest_rvh, + ASCIIToUTF16(StringPrintf("CreateChildFrame('%s');", + test_url.spec().c_str()))); + + string16 actual_title = ready_watcher.WaitAndGetTitle(); + EXPECT_EQ(ASCIIToUTF16("ready"), actual_title); + + content::TitleWatcher iframe_watcher(test_embedder()->web_contents(), + ASCIIToUTF16("iframe")); + ExecuteSyncJSFunction(rvh, + ASCIIToUTF16(StringPrintf("PostMessage('%s', true);", kTesting))); + + // The title will be updated to "iframe" at the last stage of the + // process described above. + actual_title = iframe_watcher.WaitAndGetTitle(); + EXPECT_EQ(ASCIIToUTF16("iframe"), actual_title); + } +} + } // namespace content diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc index 3bcc18b..b13778c 100644 --- a/content/browser/web_contents/web_contents_impl.cc +++ b/content/browser/web_contents/web_contents_impl.cc @@ -3005,8 +3005,14 @@ void WebContentsImpl::RouteMessageEvent( RenderViewHost* rvh, const ViewMsg_PostMessage_Params& params) { // Only deliver the message to the active RenderViewHost if the request - // came from a RenderViewHost in the same BrowsingInstance. - if (!rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance())) + // came from a RenderViewHost in the same BrowsingInstance or if this + // WebContents is dedicated to a browser plugin guest. + // Note: This check means that an embedder could theoretically receive a + // postMessage from anyone (not just its own guests). However, this is + // probably not a risk for apps since other pages won't have references + // to App windows. + if (!rvh->GetSiteInstance()->IsRelatedSiteInstance(GetSiteInstance()) && + !GetBrowserPluginGuest() && !GetBrowserPluginEmbedder()) return; ViewMsg_PostMessage_Params new_params(params); @@ -3031,8 +3037,16 @@ void WebContentsImpl::RouteMessageEvent( } if (source_contents) { - new_params.source_routing_id = - source_contents->CreateOpenerRenderViews(GetSiteInstance()); + if (GetBrowserPluginGuest()) { + // We create a swapped out RenderView for the embedder in the guest's + // render process but we intentionally do not expose the embedder's + // opener chain to it. + new_params.source_routing_id = + source_contents->CreateSwappedOutRenderView(GetSiteInstance()); + } else { + new_params.source_routing_id = + source_contents->CreateOpenerRenderViews(GetSiteInstance()); + } } else { // We couldn't find it, so don't pass a source frame. new_params.source_routing_id = MSG_ROUTING_NONE; @@ -3135,6 +3149,11 @@ WebPreferences WebContentsImpl::GetWebkitPrefs() { return GetWebkitPrefs(GetRenderViewHost(), url); } +int WebContentsImpl::CreateSwappedOutRenderView( + content::SiteInstance* instance) { + return render_manager_.CreateRenderView(instance, MSG_ROUTING_NONE, true); +} + void WebContentsImpl::OnUserGesture() { // Notify observers. FOR_EACH_OBSERVER(WebContentsObserver, observers_, DidGetUserGesture()); diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h index f87f71d..6524241 100644 --- a/content/browser/web_contents/web_contents_impl.h +++ b/content/browser/web_contents/web_contents_impl.h @@ -103,6 +103,12 @@ class CONTENT_EXPORT WebContentsImpl static webkit_glue::WebPreferences GetWebkitPrefs( content::RenderViewHost* rvh, const GURL& url); + // Creates a swapped out RenderView. This is used by the browser plugin to + // create a swapped out RenderView in the embedder render process for the + // guest, to expose the guest's window object to the embedder. + // This returns the routing ID of the newly created swapped out RenderView. + int CreateSwappedOutRenderView(content::SiteInstance* instance); + // Complex initialization here. Specifically needed to avoid having // members call back into our virtual functions in the constructor. virtual void Init(content::BrowserContext* browser_context, diff --git a/content/common/browser_plugin_messages.h b/content/common/browser_plugin_messages.h index 69de1ee..fe5fd51 100644 --- a/content/common/browser_plugin_messages.h +++ b/content/common/browser_plugin_messages.h @@ -149,6 +149,12 @@ IPC_SYNC_MESSAGE_ROUTED2_0(BrowserPluginHostMsg_ResizeGuest, // ----------------------------------------------------------------------------- // These messages are from the browser process to the embedder. +// Once the swapped out guest RenderView has been created in the embedder render +// process, the browser process informs the embedder of its routing ID. +IPC_MESSAGE_CONTROL2(BrowserPluginMsg_GuestContentWindowReady, + int /* instance_id */, + int /* source_routing_id */) + // When the guest begins to load a page, the browser process informs the // embedder through the BrowserPluginMsg_LoadStart message. IPC_MESSAGE_CONTROL3(BrowserPluginMsg_LoadStart, diff --git a/content/renderer/browser_plugin/browser_plugin.cc b/content/renderer/browser_plugin/browser_plugin.cc index 1aece49..ad90ac0 100644 --- a/content/renderer/browser_plugin/browser_plugin.cc +++ b/content/renderer/browser_plugin/browser_plugin.cc @@ -73,6 +73,7 @@ BrowserPlugin::BrowserPlugin( navigate_src_sent_(false), process_id_(-1), persist_storage_(false), + content_window_routing_id_(MSG_ROUTING_NONE), visible_(true), current_nav_entry_index_(0), nav_entry_count_(0) { @@ -135,6 +136,17 @@ void BrowserPlugin::SetSrcAttribute(const std::string& src) { src_ = src; } +NPObject* BrowserPlugin::GetContentWindow() const { + if (content_window_routing_id_ == MSG_ROUTING_NONE) + return NULL; + RenderViewImpl* guest_render_view = static_cast<RenderViewImpl*>( + ChildThread::current()->ResolveRoute(content_window_routing_id_)); + if (!guest_render_view) + return NULL; + WebKit::WebFrame* guest_frame = guest_render_view->GetWebView()->mainFrame(); + return guest_frame->windowObject(); +} + std::string BrowserPlugin::GetPartitionAttribute() const { std::string value; if (persist_storage_) @@ -487,6 +499,11 @@ void BrowserPlugin::AdvanceFocus(bool reverse) { render_view_->GetWebView()->advanceFocus(reverse); } +void BrowserPlugin::GuestContentWindowReady(int content_window_routing_id) { + DCHECK(content_window_routing_id != MSG_ROUTING_NONE); + content_window_routing_id_ = content_window_routing_id; +} + void BrowserPlugin::SetAcceptTouchEvents(bool accept) { if (container()) container()->setIsAcceptingTouchEvents(accept); diff --git a/content/renderer/browser_plugin/browser_plugin.h b/content/renderer/browser_plugin/browser_plugin.h index 25c1a0f..5bdbf11 100644 --- a/content/renderer/browser_plugin/browser_plugin.h +++ b/content/renderer/browser_plugin/browser_plugin.h @@ -39,6 +39,8 @@ class CONTENT_EXPORT BrowserPlugin : // Set the src attribute value of the BrowserPlugin instance and reset // the guest_crashed_ flag. void SetSrcAttribute(const std::string& src); + // Get the guest's DOMWindow proxy. + NPObject* GetContentWindow() const; // Returns Chrome's process ID for the current guest. int process_id() const { return process_id_; } // The partition identifier string is stored as UTF-8. @@ -73,6 +75,10 @@ class CONTENT_EXPORT BrowserPlugin : // element. void AdvanceFocus(bool reverse); + // Inform the BrowserPlugin that the guest's contentWindow is ready, + // and provide it with a routing ID to grab it. + void GuestContentWindowReady(int content_window_routing_id); + // Informs the BrowserPlugin that the guest has started/stopped accepting // touch events. void SetAcceptTouchEvents(bool accept); @@ -208,6 +214,7 @@ class CONTENT_EXPORT BrowserPlugin : int process_id_; std::string storage_partition_id_; bool persist_storage_; + int content_window_routing_id_; // Tracks the visibility of the browser plugin regardless of the whole // embedder RenderView's visibility. bool visible_; diff --git a/content/renderer/browser_plugin/browser_plugin_bindings.cc b/content/renderer/browser_plugin/browser_plugin_bindings.cc index bf94852..b0dfca7 100644 --- a/content/renderer/browser_plugin/browser_plugin_bindings.cc +++ b/content/renderer/browser_plugin/browser_plugin_bindings.cc @@ -41,6 +41,7 @@ const char kAddEventListener[] = "addEventListener"; const char kBackMethod[] = "back"; const char kCanGoBack[] = "canGoBack"; const char kCanGoForward[] = "canGoForward"; +const char kContentWindow[] = "contentWindow"; const char kForwardMethod[] = "forward"; const char kGetProcessId[] = "getProcessId"; const char kGoMethod[] = "go"; @@ -56,6 +57,10 @@ BrowserPluginBindings* GetBindings(NPObject* object) { message_channel; } +bool IdentifierIsContentWindow(NPIdentifier identifier) { + return WebBindings::getStringIdentifier(kContentWindow) == identifier; +} + bool IdentifierIsPartitionAttribute(NPIdentifier identifier) { return WebBindings::getStringIdentifier(kPartitionAttribute) == identifier; } @@ -141,6 +146,7 @@ bool BrowserPluginBindingsInvokeDefault(NPObject* np_obj, bool BrowserPluginBindingsHasProperty(NPObject* np_obj, NPIdentifier name) { return IdentifierIsSrcAttribute(name) || + IdentifierIsContentWindow(name) || IdentifierIsPartitionAttribute(name); } @@ -163,6 +169,15 @@ bool BrowserPluginBindingsGetProperty(NPObject* np_obj, NPIdentifier name, return StringToNPVariant(src, result); } + if (IdentifierIsContentWindow(name)) { + NPObject* obj = bindings->instance()->GetContentWindow(); + if (obj) { + result->type = NPVariantType_Object; + result->value.objectValue = WebBindings::retainObject(obj); + } + return true; + } + if (IdentifierIsPartitionAttribute(name)) { std::string partition_id = bindings->instance()->GetPartitionAttribute(); return StringToNPVariant(partition_id, result); diff --git a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc index a88b34f..8b4226a 100644 --- a/content/renderer/browser_plugin/browser_plugin_manager_impl.cc +++ b/content/renderer/browser_plugin/browser_plugin_manager_impl.cc @@ -39,6 +39,8 @@ bool BrowserPluginManagerImpl::OnControlMessageReceived( IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestCrashed, OnGuestCrashed) IPC_MESSAGE_HANDLER(BrowserPluginMsg_DidNavigate, OnDidNavigate) IPC_MESSAGE_HANDLER(BrowserPluginMsg_AdvanceFocus, OnAdvanceFocus) + IPC_MESSAGE_HANDLER(BrowserPluginMsg_GuestContentWindowReady, + OnGuestContentWindowReady) IPC_MESSAGE_HANDLER(BrowserPluginMsg_ShouldAcceptTouchEvents, OnShouldAcceptTouchEvents) IPC_MESSAGE_HANDLER(BrowserPluginMsg_LoadStart, OnLoadStart) @@ -78,6 +80,13 @@ void BrowserPluginManagerImpl::OnAdvanceFocus(int instance_id, bool reverse) { plugin->AdvanceFocus(reverse); } +void BrowserPluginManagerImpl::OnGuestContentWindowReady(int instance_id, + int guest_routing_id) { + BrowserPlugin* plugin = GetBrowserPlugin(instance_id); + if (plugin) + plugin->GuestContentWindowReady(guest_routing_id); +} + void BrowserPluginManagerImpl::OnShouldAcceptTouchEvents(int instance_id, bool accept) { BrowserPlugin* plugin = GetBrowserPlugin(instance_id); diff --git a/content/renderer/browser_plugin/browser_plugin_manager_impl.h b/content/renderer/browser_plugin/browser_plugin_manager_impl.h index 9a53f78..28e1486 100644 --- a/content/renderer/browser_plugin/browser_plugin_manager_impl.h +++ b/content/renderer/browser_plugin/browser_plugin_manager_impl.h @@ -37,6 +37,7 @@ class BrowserPluginManagerImpl : public BrowserPluginManager { void OnDidNavigate(int instance_id, const BrowserPluginMsg_DidNavigate_Params& params); void OnAdvanceFocus(int instance_id, bool reverse); + void OnGuestContentWindowReady(int instance_id, int guest_routing_id); void OnShouldAcceptTouchEvents(int instance_id, bool accept); void OnLoadStart(int instance_id, const GURL& url, diff --git a/content/test/data/browser_plugin_embedder.html b/content/test/data/browser_plugin_embedder.html index e192865..5702027 100644 --- a/content/test/data/browser_plugin_embedder.html +++ b/content/test/data/browser_plugin_embedder.html @@ -23,6 +23,16 @@ function SetSize(w, h) { plugin.width = w; plugin.height = h; } +function PostMessage(data, shouldTargetIframe) { + plugin = document.getElementById('plugin'); + // TODO(fsamuel): contentWindow can be accessed directly once + // http://wkbug.com/85679 lands. + if (shouldTargetIframe) { + plugin.contentWindow.frames[0].postMessage('testing123', '*'); + } else { + plugin.contentWindow.frames.postMessage('testing123', '*'); + } +} function Back() { var plugin = document.getElementById('plugin'); plugin.back(); @@ -55,10 +65,28 @@ document.title = 'embedder'; width="640" height="480" border="0px"></object> - -<script> +<script type="text/javascript"> +var msg; +function receiveMessage(event) { + msg = event.data; + if (msg == 'ready') { + document.title = 'ready'; + return; + } + if (msg.indexOf('stop_ack') == -1) { + event.source.postMessage('stop', '*'); + } else { + var name = msg.replace("stop_ack", "").trim(); + if (name !== '') { + window.document.title = name; + } else { + window.document.title = 'main guest'; + } + } +} var plugin = document.getElementById('plugin'); plugin.addEventListener('loadStart', loadStart); plugin.addEventListener('loadAbort', loadAbort); plugin.addEventListener('loadRedirect', loadRedirect); + window.addEventListener('message', receiveMessage, false); </script> diff --git a/content/test/data/browser_plugin_post_message_guest.html b/content/test/data/browser_plugin_post_message_guest.html new file mode 100644 index 0000000..57c1c3c --- /dev/null +++ b/content/test/data/browser_plugin_post_message_guest.html @@ -0,0 +1,23 @@ +<script> + var embedder; + function receiveMessage(event) { + embedder = event.source; + if (event.data !== 'stop') { + event.source.postMessage('foobar', '*'); + } else { + if (event.data !== '') { + event.source.postMessage('stop_ack ' + window.name, '*'); + } + } + } + window.addEventListener('message', receiveMessage, false); + function CreateChildFrame(src) { + var ifrm = document.createElement("IFRAME"); + ifrm.style.width = "640px"; + ifrm.style.height = "480px"; + ifrm.src = src; + ifrm.name = 'iframe'; + document.body.appendChild(ifrm); + embedder.postMessage('ready', '*'); + } +</script> |