Chrome扩展程序消息传递:未经检查的runtime.lastError:无法建立连接。接收端不存在

Ale*_*der 9 google-chrome-extension

我的chrome扩展程序具有以下两个JavaScript:

background.js,作为后台脚本运行:

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
    if (message.data == "takeScreenshot") {
        var resp = sendResponse;
        chrome.tabs.captureVisibleTab(function(screenshotUrl) {
            resp({
                screenshot: screenshotUrl
            });
        });
        return true; // Return true to tell that the response is sent asynchronously
    } else {
        return "TestReply";
    }
});
Run Code Online (Sandbox Code Playgroud)

api.js,作为可通过网络访问的资源运行:

window.takeScreenshot = (function() {
    var isTakingScreenshot = false; // Semaphore
    return function() {
        if(isTakingScreenshot) return Promise.reject();
        isTakingScreenshot = true;
        return new Promise(function(resolve, reject) {
            chrome.runtime.sendMessage("eomfljlchjpefnempfimgminjnegpjod", "takeScreenshot", function(response) {
                console.log(response);
                isTakingScreenshot = false;
                resolve(response.screenshot);
            });
        });
    }
})()
window.test = (function() {
    return function() {
        return new Promise(function(resolve, reject) {
            chrome.runtime.sendMessage("eomfljlchjpefnempfimgminjnegpjod", "test", function(response) {
                console.log(response);
                resolve(response.length);
            });         
        });
    }
})();
Run Code Online (Sandbox Code Playgroud)

当我在选项卡的控制台中执行任一个功能(自动完成功能都知道它们,因此它们可用)时,出现以下错误:

未经检查的runtime.lastError:无法建立连接。接收端不存在。

返回的响应未定义。

我检查了ID sendMessage与清单和chrome:// extensions页面中的ID 是否相同,并且我打开了扩展程序的后台页面DevTools并在其中手动添加了相同的侦听器,以确保确实注册了该侦听器。

我的搜索发现此错误表示该侦听器尚未正确注册,但是我没有找到根本原因。您是否知道导致此错误的原因?

Már*_*rgy 12

这是我用来解决这个问题的模式。内容脚本尝试访问后台脚本。如果后台脚本尚不可用,那么我们可以chrome.runtime.lastError通过设置(而不是未定义)看到它。在这种情况下,请在 1000 毫秒后重试。

内容脚本.js

function ping() {
  chrome.runtime.sendMessage('ping', response => {
    if(chrome.runtime.lastError) {
      setTimeout(ping, 1000);
    } else {
      // Do whatever you want, background script is ready now
    }
  });
}

ping();
Run Code Online (Sandbox Code Playgroud)

后台脚本.js

chrome.runtime.onConnect.addListener(port => {
  port.onMessage.addListener(msg => {
    // Handle message however you want
  }
);

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => sendResponse('pong'));
Run Code Online (Sandbox Code Playgroud)


Dav*_*han 11

好。我发现了问题所在。我猜这是自72以来镀铬行为的变化。问题是,如果您在后台或弹出页面的另一端打开通道之前尝试调用chrome.runtime.connect(),则会收到该错误。

Chrome文档说的是您可以立即发送消息。在过去,这只是行之有效的,消息将被传递或丢弃。但是现在失败了。

chrome docs:调用tabs.connect,runtime.connect或runtime.connectNative时,会创建一个Port。该端口可立即用于通过postMessage将消息发送到另一端。

因此,我们的解决方法是通过仅延迟connect()调用来确保在调用connect()之前,连接侦听器已处于设置状态:

    chrome.runtime.onConnect.addListener(port => {
        console.log('connected ', port);

        if (port.name === 'hi') {
            port.onMessage.addListener(this.processMessage);
        }
    });
Run Code Online (Sandbox Code Playgroud)

如果您在内容脚本方面设置了用于断开连接事件的侦听器,则在尝试chrome.runtime.connect时实际上会被调用,并且另一端没有任何侦听。根据端口寿命,这是正确的行为

   port = chrome.runtime.connect(null, {name: 'hi'});      
   port.onDisconnect.addListener(obj => {
            console.log('disconnected port');
    })
Run Code Online (Sandbox Code Playgroud)

我不知道是否有避免这种情况的方法,而不是使用setTimeout并尝试在调用chrome.runtime.onConnect.addListener之后获取chrome.runtime.connect的方法。这不是一个好的解决方案,因为它会导致计时错误。也许另一个解决方法是反转通道方向。然后从弹出窗口而不是contentscript启动连接。

更新:我对此问题进行了最低限度的扩展

  • 不,这是一个错误。文档说它应该可以立即发送。这意味着在内部它应该阻止调用,直到建立连接。这是一个糟糕的 api 设计,它的实现更糟糕。 (3认同)

Tro*_*eek 9

chrome 中的一些安全更改似乎在内容脚本和后台脚本之间进行消息传递时必须采取略有不同的方法。

要使用的调用是

  1. 从背景页面:

chrome.runtime.onMessageExternal.addListener 注意 外部部分。

  1. manifest.json需要额外的权限
      "externally_connectable": {
        "ids": ["abcdefghijklmnopqrstuvwxyzabcdef"],
        "matches": ["https://example.com/*"],
        "accepts_tls_channel_id": false
      },
Run Code Online (Sandbox Code Playgroud)

"abcdefghijklmnopqrstuvwxyzabcdef" 是你的分机号。

"https://example.com/*" 是内容脚本运行所在的域。

  1. 从内容脚本:

    chrome.runtime.sendMessage/chrome.runtime.connect以 aextensionId作为第一个参数。

在这里阅读更多 https://developer.chrome.com/extensions/manifest/externally_connectable

  • 最佳答案。您可以使用 chrome.runtime.id 在消息发送者中注入 Id,以避免发送消息时出现静态变量 (3认同)