多个chrome.runtime.onMessage-Listeners - 已断开连接的端口错误

时间:2016-03-22 21:17:53

标签: javascript jquery json google-chrome-extension indexeddb

我正在使用chrome-extension,它使用indexedDB-Wrapper Dexie,几个jQuery-Libraries和syncfusions eGrid来显示和修改数据库。我知道之前曾多次询问过这个问题,但现在我发现了一些奇怪的行为。

我的设置:

  • background-script
  • 内容脚本(注入网站)
  • index.html(用于显示网格)
  • 的manifest.json

当我重新加载我的扩展时,一切都很好。我在内容脚本中注入了一个可滚动的名称列表。这在我重新加载选项卡时也有效。但是当我在(官方文档中的popup.html)之前打开index.html时,后台脚本中的sendResponse会在一段时间后停止工作。重新加载选项卡将无济于事。我正在寻找几个小时,发现了很多类似的案例和一些关于googles bugtracker的问题,他们声称这个问题已经解决了。我也知道如果我有与indexedDB一样的异步函数,我必须使用return true。

为了给你提供更多信息,我将列出我缩短的脚本。

内容脚本

chrome.runtime.sendMessage({greeting: 'getNames'},function(response){
    $('.names-container').append(response.farewell);
});
chrome.runtime.sendMessage({greeting: 'getData'+name},function(name){
    chrome.runtime.sendMessage({greeting: 'delete'},function(response){
        console.log(response.farewell);
    });
    chrome.runtime.sendMessage({greeting: 'set'}, function(response) {
        console.log(response.farewell);
    });
});

背景脚本

chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
  if(message.greeting == 'getNames'){
    // Get array of names from idb and send it back
    idb.opendb().then(function(a){
      sendResponse(a);
    });
    return true;
  } else if(message.greeting.indexOf('getData') >= 0){
    // get more information for selected name
    idb.opendb().then(function(a){
      sendResponse(a);
    });
    return true;
  } else if(message.greeting == 'delete'){
    // further processing to delete some stuff
    idb.opendb().then(function(a){
      sendResponse(a);
    });
    return true;
  } else if(message.greeting == 'set'){
    // synchronous function
    // do something ...
    return false;
  } else {
    // Do something
  }
  //return true;
});

funtion indexhtml(){
  chrome.runtime.sendMessage(
    "notifications",
    function (response) {
      console.log(response);
    }
  );
}

的index.html

chrome.runtime.onMessage.addListener(function(request, sender, sendResponse){
  if(request == 'notifications'){
    notifications();
  }

  sendResponse("Message received from background-page for "+request);
});

清单

{
    "manifest_version": 2,
    "name": "MyExtension",
    "description": "Some desc...",
    "version": "0.2",
    "background": {
        "scripts": ["js/jquery-2.1.4.min.js", "js/background.js", "...",
        "persistent": true
    },
    "default_locale": "en",
    "content_scripts": [
        {
            "matches": ["<all_urls>"],
            "js": ["js/jquery-2.1.4.min.js"]
        },
        {
          "matches": ["https://domain.tld/*"],
          "js": ["js/jquery-2.1.4.min.js", "js/contentscript.js"],
          "css" : ["css/contentscript.css",
          "run_at": "document_start"
        }
    ],

    "icons": {
        "16": "images/icon-16x16.png",
        "48": "images/icon-48x48.png",
        "128": "images/icon-128x128.png"
    },
    "browser_action": {
        "default_icon": "images/icon-128x128.png"
    },
    "permissions": [
        "cookies", "tabs", "alarms", "webRequest", "webRequestBlocking", 
        "contextMenus", "<all_urls>", "storage"
    ],

    "content_security_policy": "script-src 'self' 'unsafe-eval'; 
    object-src 'self'"
}

我很难调试此行为,因为我无法确切知道何时出现错误Attempting to use a disconnected port object。看起来端口在执行sendResponse后没有正确关闭,并且只有在index.html被打开时才会被关闭。

最后缩短的错误消息:

Error: Attempting to use a disconnected port object
    at Error (native)
    at PortImpl.postMessage (extensions::messaging:65:22)
    at Port.publicClass.(anonymous function) [as postMessage] (extensions::utils:94:26)
    at responseCallback (extensions::messaging:163:16)

idb-functions是自定义的,不会像我在这里写的那样工作,但我只想展示我在那里做的事情。我在结果可用时使用.then()sendReponse,如果异步则使用return true;等待结果。我还搜索了onMessagesendMessage的每个脚本。列出的与onMessage / sendMessage相关的函数都是我使用的,它们不在别处使用。我使用indexhtml()的计时器来保存包含指定时间间隔内通知的数组,然后在index.html执行一个函数来重新加载通知。

index.html标签通过browserAction打开:

chrome.browserAction.onClicked.addListener(function(tab){
    chrome.tabs.create({url: chrome.extension.getURL('index.html')});
});

1 个答案:

答案 0 :(得分:3)

好的,我在添加越来越多的调试后自己找到了答案。这里的问题是,我正在使用多个(2)onMessage-Listeners。没有特定的监听器只监听发送到我的内容页面的消息(index.html)。每次我打开index.html时,如果在我的内容脚本中使用sendMessage,则会首先调用额外的onMessage-Listener。我不知道嵌入到index.html中的onMessage-Listener也是每次调用的。然后我在我的内容脚本中更改了sendMessage的目标 - 具有相同的结果。关于这个主题的谷歌文档也有一个注释:

  

注意:如果多个页面正在侦听onMessage事件,则只有第一个调用特定事件的sendResponse()才能成功发送响应。对该事件的所有其他回复都将被忽略。

Link here

第二个问题是,我在if子句之外使用了sendResponse,这样每次都调用它。所以最后我只需要将sendResponse放在if子句中,这样只有在来自background-page的指定消息通过sendMessage调用时才会调用它。

<强>背景脚本

  • 通过搜索chrome-extension://extid/index.html
  • 获取tab.id.
  • 仅将信息发送至index.html
chrome.windows.getAll({populate:true},function(windows){
  windows.forEach(function(window){
    window.tabs.forEach(function(tab){
      if(tab.url.indexOf(chrome.extension.getURL('index.html')) >= 0){
         chrome.tabs.sendMessage(tab.id,{ greeting: "notifications"}, function(response){
           console.log(response);
         });
      }
    });
  });
});

<强>的index.html

chrome.runtime.onMessage.addListener(function(message, sender, sendResponse){
        if(message.greeting == "notifications-trades"){
            Notifications();
            sendResponse("Message received from bg "+message.greeting);
            //if async add return true;
        } else {
            //Do something
        }
});