javascript Chrome扩展消息传递:未检查运行时,lastError:无法建立连接,接收端不存在

li9yvcax  于 2023-02-15  发布在  Java
关注(0)|答案(8)|浏览(417)

我的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,作为Web可访问资源运行:

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

当我在选项卡的控制台中执行任一函数时(自动完成知道它们,所以它们是可用的),我得到错误:
未检查运行时。lastError:无法建立连接。接收端不存在。
并且返回的响应是不确定的。
我已经检查了sendMessage中的id是否与清单和chrome://extensions页面中的id相同,并且我已经打开了扩展的后台页面DevTools,并手动添加了相同的侦听器,以确保侦听器确实已注册。
我的搜索发现此错误意味着监听程序没有正确注册,但我找不到根本原因。您知道导致此错误的原因吗?

gkn4icbw

gkn4icbw1#

好的。我发现了问题所在。我猜这是chrome自72年以来的行为变化。问题是如果你在后台或弹出页面中打开另一端的通道之前试图调用chrome.runtime.connect(),那么你会得到那个错误。
Chrome文档说的是你可以立即发送消息。在过去,这只会起作用,消息要么被发送,要么被丢弃。但现在它失败了。
chrome文档:在调用tabs.connect,runtime.connect或runtime.connectNative时,会创建一个端口,这个端口可以立即用于通过postMessage向另一端发送消息。
因此,我们的解决方案是通过延迟connect()调用,确保在调用connect()之前先设置连接侦听器:

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

  if (port.name === 'hi') {
    port.onMessage.addListener(this.processMessage);
  }
});

如果您在内容脚本端为断开连接事件设置了一个侦听器,那么当您尝试chrome.runtime.connect时,它实际上会被调用,而您在另一端没有任何侦听。

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

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

更新:我为此问题制作了a minimum repro extension

rsaldnfx

rsaldnfx2#

这里有一个我用来解决这个问题的模式。内容脚本试图到达后台脚本。如果后台脚本还不可用,那么我们可以从chrome.runtime.lastError被设置(而不是未定义)中看到。在这种情况下,请在1000 ms后重试。

内容脚本.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();

背景脚本.js

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

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

3ks5zfa03#

chrome中的一些安全性变化似乎让你不得不在内容脚本和后台脚本之间传递消息时采取一种稍微不同的方法。

要使用的调用为

1.从背景页:
chrome.runtime.onMessageExternal.addListener * 注意外部部件。*

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

"abcdefghijklmnopqrstuvwxyzabcdef"是您的分机ID。
"https://example.com/*"是运行内容脚本的域。
1.在内容脚本中:
chrome.runtime.sendMessage/chrome.runtime.connect,其中extensionId作为第一个参数。
在此阅读更多信息https://developer.chrome.com/extensions/manifest/externally_connectable

c90pui9n

c90pui9n4#

我在这里添加这个,因为我有这个错误,而不是一个单一的StackOverflow后解决我的问题.我的问题是因为我的background.js是发送一堆消息到我的内容脚本只是一个网页的访问.一旦我添加了一个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
    }
});
0vvn1miw

0vvn1miw5#

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

to94eoyn

to94eoyn6#

在进一步研究了我的扩展的解决方案后,我考虑将Long-lived connection改为Simple one-time requests,严重的是,我花了两天时间寻找解决方案,但在我的扩展中没有找到这个问题的解决方案。弹出窗口是用ReactJs制作的。删除所有runtime.connectruntime.onConnect后,更改API并使用onMessage,并且只使用addListner的回调来处理消息,问题消失了。我这样做是因为我不需要更多的实时更改,并且只使用请求来更改它,想想看,如果您不需要Long-lived connection,则将其更改为Simple one-time requestsfor 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
      }
    },
  );
}

如果您需要在另一个函数中使用sendResponse asyncronous,则在onMessage.addListner的回调中需要return true
负责发送和接收消息的功能:
x一个一个一个一个x一个一个二个x

pod7payv

pod7payv7#

我在开发Chrome扩展时遇到了这个错误。对我来说,问题是一个不同的扩展ID。如果你需要在代码中使用你的扩展ID(例如,在向后台工作人员发送消息时),请确保你的扩展ID与浏览器中的ID匹配。

2w3kk1z5

2w3kk1z58#

回复:@david-dehghan解决方案
我不知道是否有一种方法可以避免这种情况,除了使用setTimeout和尝试在调用chrome.runtime.onConnect.addListener之后获得chrome.runtime.connect。
我相信我有一个解决方案,除非必要,否则可以推迟chrome.runtime.connect上的setTimeout。
正如@david-dehghan的解决方案所描述的,我们还遇到了调用chrome.runtime.connect触发chrome.runtime.onDisconnect处理程序的问题,这会导致Could not establish connection错误和连接无法创建。
我们可以添加重试逻辑,通过观察lastError从事件处理程序重试chrome.runtime.connect,而不是延迟初始的chrome.runtime.connect调用。
内容脚本示例(相对于上述相同的背景脚本示例):

let extensionPort;

const setupFunction = () => {
  extensionPort = chrome.runtime.connect({ name: 'hi' });
  extensionPort.onDisconnect.addListener(onDisconnectListener);
}

const onDisconnectListener = () => {
  const lastError = chrome.runtime.lastError;

  extensionPort.onDisconnect.removeListener(onDisconnectListener);

  if (lastError) {
    setTimeout(setupFunction, 1000);
  }
}

相关问题