summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--content/browser/browser_plugin/browser_plugin_embedder.cc11
-rw-r--r--content/browser/browser_plugin/browser_plugin_host_browsertest.cc89
-rw-r--r--content/browser/web_contents/web_contents_impl.cc27
-rw-r--r--content/browser/web_contents/web_contents_impl.h6
-rw-r--r--content/common/browser_plugin_messages.h6
-rw-r--r--content/renderer/browser_plugin/browser_plugin.cc17
-rw-r--r--content/renderer/browser_plugin/browser_plugin.h7
-rw-r--r--content/renderer/browser_plugin/browser_plugin_bindings.cc15
-rw-r--r--content/renderer/browser_plugin/browser_plugin_manager_impl.cc9
-rw-r--r--content/renderer/browser_plugin/browser_plugin_manager_impl.h1
-rw-r--r--content/test/data/browser_plugin_embedder.html32
-rw-r--r--content/test/data/browser_plugin_post_message_guest.html23
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>