summaryrefslogtreecommitdiffstats
path: root/content
diff options
context:
space:
mode:
authorfsamuel@chromium.org <fsamuel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 03:19:36 +0000
committerfsamuel@chromium.org <fsamuel@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 03:19:36 +0000
commit0eba810b1382866e3590d3a1a41fca5e7df486bc (patch)
treec42917938267acd51ff34aaf869165f2d72af7dd /content
parentf44fe445c4ec85426b5fe3a1d77f13cc96294131 (diff)
downloadchromium_src-0eba810b1382866e3590d3a1a41fca5e7df486bc.zip
chromium_src-0eba810b1382866e3590d3a1a41fca5e7df486bc.tar.gz
chromium_src-0eba810b1382866e3590d3a1a41fca5e7df486bc.tar.bz2
Browser Plugin: Add HTML5-like postMessage support
This is an implementation of HTML5-like postMessage support for the browser plugin. It works as follows: In BrowserPluginEmbedder:NavigateGuest, the browser process creates two swapped out RenderView: A swapped out RenderView for the guest in the embedder render process and a swapped out RenderView for the embedder in the guest render process. The guest RenderView in the embedder process can be accessed via browserPlugin.contentWindow or through the MessageEvent object received from the guest, event.source. The embedder RenderView in the guest process can be accessed through the MessageEvent object on message events, event.source. BrowserPluginEmbedderHelper, and BrowserPluginGuestHelper intercept ViewHostMsg_RouteMessageEvent messages from the swapped out RenderViews and route them appropriately. Note: BrowserPluginBindings now registers add/removeCustomEventListener instead of add/removeEventListener so that the default WebKit implementations are not shadowed by the custom implementations to allow for WebKit MessageEvents to be registered. BUG=141238 Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=162353 Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=162487 Review URL: https://codereview.chromium.org/10829225 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162631 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content')
-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>