3

I'm building a chrome extension which communicates with a nodejs server through websockets. The point of it is to track browsing history with content. It all seems to work, but occasionally (30% of the time) the callback in a function passed to onMessage.addListener doesn't fire correctly. Let me show you the code:

background.js

var socket = io('http://localhost:3000/');

var tabLoad = function (tab) {
    socket.emit('page load', tab);
};

var tabUpdate = function (tabid, changeinfo, tab) {
    var url = tab.url;
    if (url !== undefined && changeinfo.status == "complete") {
        tab.user_agent = navigator.userAgent;
        tab.description = '';
        tab.content = '';

        socket.emit('insert', tab);
    }
};

socket.on('inserted', function(page){
    socket.emit('event', 'Requesting page content\n');
    //page = {tab: page, id: docs._id};
    chrome.tabs.sendMessage(page.tab_id, {requested: "content", page: page}, function(data) {
        socket.emit('content', data);
    });

});

try {
    chrome.tabs.onCreated.addListener(tabLoad);
    chrome.tabs.onUpdated.addListener(tabUpdate);
} catch(e) {
    alert('Error in background.js: ' + e.message);
}

content script - public.js

var messageHandler = function(request, sender, sendContent) {
    if (request.requested == "content") {
        var html = document.getElementsByTagName('html')[0].innerHTML;
        var data = {
            content: html,
            page: request.page
        };
        sendContent(data);
        return true;
    }
};

chrome.extension.onMessage.addListener(messageHandler);

The problem is that sometimes data in sendContent is undefined, while sometimes it is alright. Any ideas how to debug this or what i'm doing wrong?

I've tried replacing document.getElementsByTagName('html')[0].innerHTML with a hardcoded 'test' string, but that didn't help.

Pages like youtube/wikipedia seem to never work, while facebook/google works.

Edit: The sendContent callback does fire 100% of the time it's just that the data passed to it is undefined.

Edit: Here's the manifest file

{
    "manifest_version": 2,

    "name": "Socket test",
    "description": "sockets are cool",
    "version": "1.0",

    "permissions": [
        "http://st-api.localhost/",
        "http://localhost:3000/",
        "tabs",
        "background",
        "history",
        "idle",
        "notifications"
    ],
    "content_scripts": [{
        "matches": ["*://*/"],
        "js": ["public/public.js"]
        //"run_at": "document_start"
    }],
    //"browser_action": {
    //    "default_icon": "logo.png",
    //    "default_popup": "index.html"
    //},
    "background": {
        //"page" : "background.html",
        "scripts": ["socket-io.js", "background.js"],
        "persistent": true
    }
}
Ignas
  • 1,965
  • 2
  • 17
  • 44
  • Try removing `return true;`, you don't need it in this case. – Xan Oct 07 '14 at 18:18
  • Tried that. Didnt make a difference :( – Ignas Oct 07 '14 at 18:59
  • Hm. Check the value of `chrome.runtime.lastError` within `sendMessage` callback. I have a hunch your content script just isn't there when needed. – Xan Oct 07 '14 at 19:00
  • Got: Could not establish connection. Receiving end does not exist. So you are right, though what's causing this and how do i fix it? – Ignas Oct 07 '14 at 19:53
  • going to try [this](http://stackoverflow.com/questions/9106519/port-error-could-not-establish-connection-receiving-end-does-not-exist-in-chr) now – Ignas Oct 07 '14 at 19:55
  • Ah, right. Replace `chrome.extension.onMessage` (deprecated) with `chrome.runtime.onMessage` – Xan Oct 07 '14 at 19:59
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/62635/discussion-between-ignas-and-xan). – Ignas Oct 07 '14 at 22:15
  • Well, obviously the chat idea didn't work, since I was not online at the time. Your problem boils down to "no content script or content script not ready". It's impossible to diagnose without knowing how you specify/inject your content script. – Xan Oct 08 '14 at 14:47
  • I've added the manifest file in my question. – Ignas Oct 08 '14 at 15:08

1 Answers1

6

First off, your understanding that sendContent is executed 100% of the time is wrong.

As established in the comments, the sendMessage callback also gets executed when there was an error; and this error is, in your case, "Receiving end does not exist"


The error lies in your manifest declaration of the content script. A match pattern "*://*/" will only match top-level pages on http and https URIs. I.e. http://example.com/ will match, while http://example.com/test will not.

The easiest fix is "*://*/*", but I would recommend the universal match pattern "<all_urls>".


With that fixed, there are still a couple of improvements to your code.

  • Replace chrome.extension.onMessage (which is deprecated) and use chrome.runtime.onMessage
  • Modify the sendMessage part to be more resilient, by checking for chrome.runtime.lastError. Despite the wide permission, Chrome still won't inject any content scripts into some pages (e.g. chrome:// pages, Chrome Web Store)
  • Make sure you use "run_at" : "document_start" in your content script, to make sure onUpdated with "complete" is not fired before your script is ready.
Xan
  • 74,770
  • 16
  • 179
  • 206
  • Awesome, it works! I'll give you an extra 100 rep as a bounty for taking your time! Cheers! – Ignas Oct 09 '14 at 13:38
  • Glad it helped; I'm not so sure it's worthy of extra bounty, but if you do that, I'll just "pay it forward" to some other answers. – Xan Oct 09 '14 at 14:08
  • You did save me a massive amount of time. The answer is precise to my situation and I'm sure it will help someone else in the future :) – Ignas Oct 09 '14 at 14:13
  • I have to uncheck it as an accepted answer though, before i give you the bounty:) – Ignas Oct 09 '14 at 14:14