ChromeApp中如何与WebView通信?
How to communicate with WebView in Chrome App?
我开发了一个网站,我打算在 Chrome 应用程序中的 webview 中显示该网站。这很好用。
现在,我想使用网站上的 postMessage,将消息从 webview 发送到包含 Chrome 的应用程序中。这是通过网络视图中的 top.postMessage
完成的。
我尝试了以下事件侦听器:
webView.contentWindow.addEventListener('message', messageHandler);
webView.addEventListener('message', messageHandler);
window.addEventListener('message', messageHandler);
document.addEventListener('message', messageHandler);
我已经成功实现了以下事件侦听器。所有这些都按预期工作:contentload
、dialog
和 consolemessage
.
除非我能让它工作,否则我正在考虑使用 consolemessage
将消息从 webview 发送到容器 - 我觉得这没有吸引力,我怀疑在不使用开发人员时它不会工作模式。
webview sample 有一个很好的演示,使用 postMessage 在应用程序和 webview 中加载的外部页面之间发送消息。
以下是关键代码。
在应用程序中,监听 webview 的 loadstop
事件并向页面发送初始消息。您可以将此消息限制在特定的域或页面。
wv1.addEventListener('loadstop', sendInitialMessage);
function sendInitialMessage(e) {
// only send the message if the page was loaded from googledrive hosting
e.target.contentWindow.postMessage("initial message", "https://googledrive.com/host/*");
}
在外部页面中,侦听message
事件并保存源和来源。
window.addEventListener('message', onMessage);
var appWindow, appOrigin;
function onMessage(e) {
appWindow = e.source;
appOrigin = e.origin;
}
然后页面可以使用这些对象 post 向应用返回消息。
function doSendMessage() {
if (appWindow && appOrigin) {
appWindow.postMessage("this is a message from the page!", appOrigin);
}
}
该应用程序还应侦听 message
事件以接收来自外部页面的消息。
window.addEventListener('message', function(e) {
log("[???] messagereceived: " + e.data);
});
在包含的 webview 内的访客页面中,使用 chrome.runtime.sendMessage()
向包含的应用程序发送消息。
在应用程序中,使用 chrome.runtime.onMessage.addListener()
收听这些消息。
请注意,您可以通过这种方式向任何应用程序发送消息,不仅是包含您的网络视图的应用程序,而且您需要知道该应用程序的 ID,并使用 onMessageExternal
而不是 onMessage
.对于包含应用程序,ID 是可选的。
这是a working example of this mechanism。它是一个 Polymer 元素,但这并没有改变机制:designerProxy_
相当于您的访客页面; registerDesignerProxyListener_
相当于您的应用。
嵌入式网页无法post向应用程序发送消息的原因是嵌入式网页没有对应用程序的引用。
top.postMessage
不是 对应用程序的引用。 top
如果您尝试访问同一个 web 视图中最顶层的框架,则可以使用。
为了能够向应用程序发送消息,网页需要引用该应用程序。最简单的方法是让应用程序向框架发送第一条消息 - "hello"-message.
来自应用:
// Initialize communications
webView.contentWindow.postMessage('hello, webpage!', 'https://your.web.page/*');
addEventListener('message', function(e) {
// I expect this check to work, but I have not tested it.
if (e.source != webView.contentWindow)
return;
// Handle e.data however you want.
});
在网页中:
var messageSource, messageOrigin;
addEventListener('message', function(e) {
if (!messageSource) {
/*
* Once we have a messageSource, we should not allow anybody to change
* messageSource again
*/
if (e.data == "hello, webpage!") {
/*
* If possible, you should validate the `e.origin` value here. It could
* possibly come from somewhere else. However, this is quite safe as it
* stands, since there only is a very narrow time window where the app
* is open willing to accept the "hello, webpage!" message.
*
* Another way of validating, is by having the app wait for the
* "hello, host!" message. If that response is not received within a second
* the app host could simply reload the app.
*/
messageSource = e.source;
messageOrigin = e.origin;
messageSource.postMessage("hello, host!", messageOrigin);
}
} else {
// Handle messages however you like. This will simply respond to every message:
messageSource.postMessage('Your message: ' + e.data, messageOrigin);
}
});
我开发了一个网站,我打算在 Chrome 应用程序中的 webview 中显示该网站。这很好用。
现在,我想使用网站上的 postMessage,将消息从 webview 发送到包含 Chrome 的应用程序中。这是通过网络视图中的 top.postMessage
完成的。
我尝试了以下事件侦听器:
webView.contentWindow.addEventListener('message', messageHandler);
webView.addEventListener('message', messageHandler);
window.addEventListener('message', messageHandler);
document.addEventListener('message', messageHandler);
我已经成功实现了以下事件侦听器。所有这些都按预期工作:contentload
、dialog
和 consolemessage
.
除非我能让它工作,否则我正在考虑使用 consolemessage
将消息从 webview 发送到容器 - 我觉得这没有吸引力,我怀疑在不使用开发人员时它不会工作模式。
webview sample 有一个很好的演示,使用 postMessage 在应用程序和 webview 中加载的外部页面之间发送消息。
以下是关键代码。
在应用程序中,监听 webview 的
loadstop
事件并向页面发送初始消息。您可以将此消息限制在特定的域或页面。wv1.addEventListener('loadstop', sendInitialMessage); function sendInitialMessage(e) { // only send the message if the page was loaded from googledrive hosting e.target.contentWindow.postMessage("initial message", "https://googledrive.com/host/*"); }
在外部页面中,侦听
message
事件并保存源和来源。window.addEventListener('message', onMessage); var appWindow, appOrigin; function onMessage(e) { appWindow = e.source; appOrigin = e.origin; }
然后页面可以使用这些对象 post 向应用返回消息。
function doSendMessage() { if (appWindow && appOrigin) { appWindow.postMessage("this is a message from the page!", appOrigin); } }
该应用程序还应侦听
message
事件以接收来自外部页面的消息。window.addEventListener('message', function(e) { log("[???] messagereceived: " + e.data); });
在包含的 webview 内的访客页面中,使用
chrome.runtime.sendMessage()
向包含的应用程序发送消息。在应用程序中,使用
chrome.runtime.onMessage.addListener()
收听这些消息。
请注意,您可以通过这种方式向任何应用程序发送消息,不仅是包含您的网络视图的应用程序,而且您需要知道该应用程序的 ID,并使用 onMessageExternal
而不是 onMessage
.对于包含应用程序,ID 是可选的。
这是a working example of this mechanism。它是一个 Polymer 元素,但这并没有改变机制:designerProxy_
相当于您的访客页面; registerDesignerProxyListener_
相当于您的应用。
嵌入式网页无法post向应用程序发送消息的原因是嵌入式网页没有对应用程序的引用。
top.postMessage
不是 对应用程序的引用。 top
如果您尝试访问同一个 web 视图中最顶层的框架,则可以使用。
为了能够向应用程序发送消息,网页需要引用该应用程序。最简单的方法是让应用程序向框架发送第一条消息 - "hello"-message.
来自应用:
// Initialize communications
webView.contentWindow.postMessage('hello, webpage!', 'https://your.web.page/*');
addEventListener('message', function(e) {
// I expect this check to work, but I have not tested it.
if (e.source != webView.contentWindow)
return;
// Handle e.data however you want.
});
在网页中:
var messageSource, messageOrigin;
addEventListener('message', function(e) {
if (!messageSource) {
/*
* Once we have a messageSource, we should not allow anybody to change
* messageSource again
*/
if (e.data == "hello, webpage!") {
/*
* If possible, you should validate the `e.origin` value here. It could
* possibly come from somewhere else. However, this is quite safe as it
* stands, since there only is a very narrow time window where the app
* is open willing to accept the "hello, webpage!" message.
*
* Another way of validating, is by having the app wait for the
* "hello, host!" message. If that response is not received within a second
* the app host could simply reload the app.
*/
messageSource = e.source;
messageOrigin = e.origin;
messageSource.postMessage("hello, host!", messageOrigin);
}
} else {
// Handle messages however you like. This will simply respond to every message:
messageSource.postMessage('Your message: ' + e.data, messageOrigin);
}
});