2

I'm developing a google plugin/extension and I came across a problem where I can only speculate about its cause. In my background.js background script I'm attempting to send image data to my content.js content script.

When I load the page in a new tab for the first time I'm only getting "response sent!", but no "response received". Then I refresh with the f5 key and this time I get the awaited "response received" message as well.

Is it possible that my background.js simply isn't in memory yet? And if so, how do I fix that? It also happens when I reload my plugin under chrome://extensions/ and reload an already existing tab.

background.js

chrome.extension.onMessage.addListener(
 function(msg, sender, sendResponse) {
    if((msg.action && (msg.action == "getPixels")) && msg.imgPath)
    {
        var canvas = document.createElement("canvas");
        canvas.style.border = "none";
        var img = new Image();
        img.src = msg.imgPath;
        var thewaitx = function()
        {
            if(img.complete && (img.width + img.height) > 0)
            {
                canvas.width = img.width;
                canvas.height = img.height;
                canvas.getContext("2d").drawImage(img, 0, 0);
                data = canvas.getContext("2d").getImageData(0, 0, img.width, img.height).data

                sendResponse({value: data});
                alert("response sent!");
            }
            else {setTimeout(thewaitx, 2000);}
        };
        thewaitx();

        }
    }
);

content.js

function findDataBySignature()
{
    img = document.getElementsByClassName("someid")[0].getElementsByTagName("img")[0];
    chrome.extension.sendMessage({action: "getPixels", imgPath:img.src}, function(response)
    {
        alert("response received");
        data = response.value;
        //blahblah
    }
}
evolution
  • 593
  • 6
  • 20
  • That's the problem with all the answers I found so far. I'm requesting the data inside of my content script (not background script!). Content script runs and in some point it says hey, you dude, background script, here is url, send me that image data. Background script gets the message and sends the answer, but the answer never arrives back. The fact that I get "response sent!" is a proof that content script already runs because it is the only one who can trigger the call. Hm... and now when I think about that, the backg script must be loaded as well because I get that "response sent!". Weird. – evolution Sep 19 '14 at 00:26
  • I think it has nothing to do with old content scripts hanging around. I know that the new code only executes in new tabs. And it's usually the content script I'm updating. But that's exactly what I am aware about. I reload the extension and THEN I reload or open a new tab. Or do I misunderstand something? – evolution Sep 19 '14 at 00:32
  • Ah ! It's the wrong problem then. But again, answered. A moment.. – Xan Sep 19 '14 at 06:02
  • **Nominated for reopening. The original guess about same problem was wrong.** – Xan Sep 19 '14 at 19:36

1 Answers1

1

Sorry for linking to the wrong problem at first.

In your code, you're requesting an image, and depending on whether it is already loaded you're either doing something to it or use setTimout to wait.

Your code works the second time because the image is already loaded; the first time it is not, and the listener terminates without calling sendResponse (setTimeout is asynchronous).

Google documentation mentions that special care needs to be taken to let Chrome now you're going to call sendResponse asynchronously (as opposed to simply no response):

This function becomes invalid when the event listener returns, unless you return true from the event listener to indicate you wish to send a response asynchronously (this will keep the message channel open to the other end until sendResponse is called).

So, to make it work, you need to return true; from the branch where you're not going to call sendResponse immediately.


That said, I have to comment on your code: you should not use setTimeout to wait for image load. Use an event-based approach:

var img = new Image();
img.onload = function() {
  canvas.width = img.width;
  canvas.height = img.height;
  canvas.getContext("2d").drawImage(img, 0, 0);
  data = canvas.getContext("2d").getImageData(0, 0, img.width, img.height).data

  sendResponse({value: data});
  alert("response sent!");
};
img.src = msg.imgPath;

// Check if image is already loaded (required if from cache)
if(img.complete || img.readyState === 4) { img.onload(); }
else {
  return true; // For asynchronous sendResponse() call
}
evolution
  • 593
  • 6
  • 20
Xan
  • 74,770
  • 16
  • 179
  • 206
  • Thanks! It worked! Since I'm a newbie here, I can't vote this up yet, but as soon as I get 15, I will :) – evolution Sep 19 '14 at 19:29
  • If it worked, you can (and should) accept the answer (the checkmark under the score) – Xan Sep 19 '14 at 19:35
  • And yes, I used onload before, but I had those caching problems you mention. It's good to know about the readystate attribute. I'm having more such pooling in my code. Maybe I'm going to elminate that ugliness now. – evolution Sep 19 '14 at 19:42
  • ahh... yep, I'm such a newbie here ;) answer accepted :) – evolution Sep 19 '14 at 19:45