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

时间:2019-01-14 12:38:38

标签: 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";
    }
});

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

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

  

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

并且返回的响应未定义。

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

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

6 个答案:

答案 0 :(得分:5)

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

使用电话是

  1. 从后台页面:

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

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

"abcdefghijklmnopqrstuvwxyzabcdef"是您的扩展名。

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

  1. 从内容脚本中:

    chrome.runtime.sendMessage / chrome.runtime.connect,第一个参数为extensionId

在此处了解更多 https://developer.chrome.com/extensions/manifest/externally_connectable

答案 1 :(得分:4)

好。我发现了问题所在。我猜这是自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);
        }
    });

如果您在内容脚本端设置了一个用于断开连接事件的侦听器,则当您尝试chrome.runtime.connect时,实际上会被调用,并且另一端没有任何侦听。根据{{​​3}}

,这是正确的行为
   port = chrome.runtime.connect(null, {name: 'hi'});      
   port.onDisconnect.addListener(obj => {
            console.log('disconnected port');
    })

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

更新:我为此问题制作了Port LifeTime

答案 2 :(得分:1)

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

contentScript.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();

backgroundScript.js

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

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => sendResponse('pong'));

答案 3 :(得分:1)

在研究了我的扩展的解决方案后,我考虑将Long-lived connection更改为Simple one-time requests,严重的是,我花了两天时间寻找解决方案,但没有找到解决此问题的解决方案在我的扩展中。弹出窗口是用 ReactJs 制作的。把runtime.connectruntime.onConnect都去掉后,改api改用onMessage,只用addListner的回调来处理消息,问题消失了。我这样做了,因为我不需要更多的实时更改并仅通过请求更改它,请考虑一下,如果您不需要 Long-lived connection 将其更改为 Simple one-time requests, for more information。我知道这不是解决方案,但对于需要加速或完成开发的每个人,这就是我所做的。

我的解决方案:

// content_script.js
if (!chrome.runtime.onMessage.hasListeners()) {
  window.chrome.runtime.onMessage.addListener(
    (request, sender, sendResponse) => {
      if (request?.type === 'load_titles') {
        sendResponse({
          type: 'loaded_titles',
          titles,
        });
      }
      if (request?.type === 'anything_else') {
        // do anything else
      }
    },
  );
}

如果你需要在另一个函数中使用异步发送响应,你需要在 return true 的回调中使用 onMessage.addListner

负责发送和接收消息的函数:

 // used in app.js on popup
 export function messager(data, callback) {
  window.chrome?.tabs?.query({ url: 'https://example.com/' }, tabs => {
    const tabId = tabs[0].id;
    window.chrome.tabs.sendMessage(tabId, data, callback);
  });
}

 // app.js
 const loadTitles = useCallback(() => {
    messager({ type: 'load_titles' }, response => {
      if (response?.type === 'loaded_titles') {
         console.log(response.titles)
        // do anything you want
      }
    });
  }, []);

答案 4 :(得分:0)

如果在开发Chrome扩展程序时收到此错误消息,请确保禁用已在浏览器中安装的其他Chrome扩展程序。特别是“ AdBlock”扩展名似乎干扰了消息传递,并导致我的扩展程序抛出此错误。禁用AdBlock为我解决了此问题。

答案 5 :(得分:0)

我在这里添加这个是因为我遇到了这个错误并且不是一个 StackOverflow 帖子解决了我的问题。我的问题是因为我的 background.js 向我的内容脚本发送了一堆消息,只访问了 1 次网页。一旦我添加了 sender.status 检查,它只会在选项卡更改“完成”时发送消息,并且我的错误消失了。

chrome.tabs.onUpdated.addListener(function(activeInfo, sender, sendResponse) {
    // conditional check prevents sending multiple messages per refresh
    if(sender.status ===  "complete") {
      // do stuff here
    }
});