【问题标题】:How to communicate with WebView in Chrome App?如何在 Chrome App 中与 WebView 通信?
【发布时间】:2015-05-21 07:39:26
【问题描述】:

我开发了一个网站,我打算在 Chrome 应用程序中的 web 视图中显示该网站。这很好用。

现在,我想使用网站上的 postMessage 将消息从 webview 发送到包含的 Chrome 应用程序。这是通过 webview 中的top.postMessage 完成的。

我尝试了以下事件监听器:

webView.contentWindow.addEventListener('message', messageHandler);

webView.addEventListener('message', messageHandler);

window.addEventListener('message', messageHandler);

document.addEventListener('message', messageHandler);

我已经成功实现了以下事件监听器。所有这些都按预期工作:contentloaddialogconsolemessage

除非我可以让它工作,否则我正在考虑使用 consolemessage 将消息从 webview 发送到容器 - 我觉得这很不吸引人,我怀疑在不使用开发者模式时它不会工作。

【问题讨论】:

  • 你有没有考虑过使用"externally_connectable"路由,如果它是针对特定网页的?
  • @Xan 我有,但我很不情愿。该网站有时可以在浏览器窗口中打开,也可以同时通过 Chrome 应用程序打开。 “应用程序”会从 Chrome 中的一个选项卡以及它自己的 web 视图中接收消息,我必须处理这个问题 - 让代码再次变得更丑陋。

标签: javascript google-chrome-extension google-chrome-app cordova-chrome-app


【解决方案1】:
  • 在包含的 webview 内的访客页面中,使用 chrome.runtime.sendMessage() 向包含的应用发送消息。

  • 在应用程序中,使用chrome.runtime.onMessage.addListener() 收听这些消息。

请注意,您可以通过这种方式向任何应用发送消息,不仅是包含您的 web 视图的应用,而且您需要知道应用的 ID,并使用 onMessageExternal 而不是 onMessage。对于包含的应用,ID 是可选的。

这里是a working example of this mechanism。它是一个 Polymer 元素,但这不会改变机制:designerProxy_ 相当于您的访客页面; registerDesignerProxyListener_ 相当于您的应用程序。

【讨论】:

  • 那将要求页面在清单中的"externally_connectable" 中列出,不是吗?
【解决方案2】:

内嵌网页无法向应用发布消息的原因是内嵌网页没有对应用的引用。

top.postMessage 不是对该应用的引用。 top 会在您尝试访问同一个 web 视图中的最顶层框架时起作用。

为了能够向应用发送消息,网页需要对该应用的引用。最简单的方法是让应用向框架发送第一条消息——“hello”消息。

从应用程序:

// 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);
    }
});

【讨论】:

  • webView.contentWindow 未定义,webview 插件中似乎发生了一些变化。任何想法现在如何使用它?感谢您的回复。
  • @andreszs webview 是否可能尚未加载?请参阅 Sarah 关于使用事件等待 webview 完成加载后再与之交互的答案。
【解决方案3】:

webview sample 有一个很好的演示,它使用 postMessage 在应用程序和加载在 web 视图中的外部页面之间发送消息。

这里是关键的代码。

  1. 在应用程序中,监听 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/*");
    }
    
  2. 在外部页面中,监听message事件并保存源和来源。

    window.addEventListener('message', onMessage);
    
    var appWindow, appOrigin;
    
    function onMessage(e) {
     appWindow = e.source;
     appOrigin = e.origin;
    }
    

    然后页面可以使用这些对象将消息发回应用程序。

    function doSendMessage() {
     if (appWindow && appOrigin) {
      appWindow.postMessage("this is a message from the page!", appOrigin);
     } 
    }
    
  3. 应用还应监听message 事件以接收来自外部页面的消息。

    window.addEventListener('message', function(e) {
     log("[???] messagereceived: " + e.data);
    });
    

【讨论】:

  • 此解决方案不使用专门为页面/应用程序通信目的创建的 Chrome API,因此有些复杂,需要更多的同步工作(例如,您不能调用 doSendMessageonMessage 完成其工作之前,等等)。请参阅我的答案进行比较。
  • 我不喜欢使用 Chrome API,如果可以使用标准 API 的话。包含的 Web 应用程序也将托管在其他非 chrome 容器中,例如 MSHTML 或 webkit - 它使用的 chrome 特定代码越少越好。
  • sendInitialMessage 方法失败,因为 e.target 未定义,有什么想法可以避免这个错误吗?感谢您的回复。
  • 您可以通过将嵌入页面引用为window.parent 来绕过等待onMessage 的需要。有关更多信息,请参阅文档:developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-11-02
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 1970-01-01
  • 2019-03-09
  • 2021-12-31
相关资源
最近更新 更多