1

I have a Chrome extension where I need the click of the extension button to call my main context so it can then access gmail.js. My background.js is:

chrome.browserAction.onClicked.addListener(function(tab) {
  chrome.browserAction.onClicked.addListener(function(tab) {
      chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
  });
});

My main.js is:

var gmail;

function refresh(f) {
  if ((/in/.test(document.readyState)) || (typeof Gmail === undefined)) {
    setTimeout('refresh(' + f + ')', 10);
  } else {
    f();
  }
}

var main = function() {
  gmail = new Gmail();  

  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    console.log(sender.tab ?  "from a content script:" + sender.tab.url : "from the extension");
  });
}

refresh(main);

My content.js is:

var j = document.createElement('script');
j.src = chrome.extension.getURL('jquery-1.10.2.min.js');
(document.head || document.documentElement).appendChild(j);

var g = document.createElement('script');
g.src = chrome.extension.getURL('gmail.js');
(document.head || document.documentElement).appendChild(g);

var s = document.createElement('script');
s.src = chrome.extension.getURL('main.js');
(document.head || document.documentElement).appendChild(s);

My manifest.json is:

{
  "name": "Test",
  "version": "1.0",
  "description": "Test Gmail chrome extension",
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "browser_action": {
    "default_title": "Export current Gmail message",
    "default_icon": "email_16x16.png"
  },
  "content_scripts": [
    {
      "matches": ["https://mail.google.com/*"],
      "js": ["content.js"]
    }
  ],
  "web_accessible_resources": [
    "jquery-1.10.2.min.js",
    "background.js",
    "gmail.js",
    "main.js"
  ],
  "manifest_version": 2
}

When the extension loads I get this error:

Uncaught TypeError: Cannot read property 'addListener' of undefined
at <anonymous>:9:27
at refresh (chrome-extension://lnndcckhmkllogdhmmlhfdoehbfgipdj/main.js:14)
at <anonymous>:1:1

Thanks for any tips or pointers.

Scott
  • 477
  • 4
  • 20
  • Done, thanks for the tip. – Scott Dec 09 '16 at 15:52
  • The only bad part about your edit is that my answer now makes no sense. Try to avoid doing that. If necessary, you can ask a new question and refer back to this one. Also, what is in `content.js`? – Teepeemm Dec 09 '16 at 16:13
  • Added content.js, which possibly is key to this. @Teepeemm, sorry about that, I realize now that I should have asked a different question. – Scott Dec 09 '16 at 16:59
  • @Scott, Yes, *content.js* was the missing part. It makes your issue clear. – Makyen Dec 09 '16 at 17:22
  • @Teepeemm, I'm of two minds about the changes to the question. Yes, in general, changes should not be made to questions which invalidate answers. However, in this case, my feeling is that you jumped the gun in answering here. Even the first draft of the question significantly implied the possibility that the assumptions you made in your answer were wrong. This was the reason for my note regarding needing a [mcve], which you reiterated in your answer. The other issue is that the question was likely to end up a duplicate (at least broadly) either way. – Makyen Dec 09 '16 at 17:37
  • @Scott, The basics are that your error is because `chrome.runtime.onMessage` does not exist in the page context. To communicate from your *main.js* to your *background.js* 2 ways 1 is: relay through your *content.js* For example: there: *background.js* (`chrome.tabs.sendmessage()`) ➞ (`chrome.runtime.onMessage`) *content.js* (`window.postMessage()`) ➞ (`window.addEventListener('message')`) *main.js* and back again: *main.js* (`window.postMessage()`) ➞ (`window.addEventListener('message')`) *content.js* (`chrome.runtime.sendmessage()`) ➞ (`chrome.runtime.onMessage`) *background.js*. – Makyen Dec 09 '16 at 17:53
  • Possible duplicate of [Executing code at page-level from Background.js and returning the value](http://stackoverflow.com/questions/26140443/executing-code-at-page-level-from-background-js-and-returning-the-value) – Makyen Dec 09 '16 at 17:59
  • @Makyen, I realize that chrome.runtime.OnMessage does not exist, but I don't quite follow your chain. I only want to go one direction: background.js (that is called on click) to main.js (that can access gmail.js). I cannot use window.postMessage(), and chrome.* seems to not work in main.js, so I don't see how to go forward. – Scott Dec 09 '16 at 18:53
  • @Scott, To communicate from your *background.js* to your *main.js* you can relay through your *content.js*: *background.js* sends a message with `chrome.tabs.sendmessage()` ➞ *content.js* receives that message with a `chrome.runtime.onMessage` listener. Then *content.js* sends the message with `window.postMessage()` ➞ *main.js* receives that message with a `window.addEventListener('message')` listener. – Makyen Dec 09 '16 at 19:04
  • @Makyen: It worked! Thank you so much! If you want to post this as an answer I will be happy to mark it as the solution. In all my searching and reading I never guessed I would have to do a combination of the two kinds of message passing. – Scott Dec 09 '16 at 20:14
  • @Scott, I'm glad I was able to help. I am debating what course to take, and how detailed of an answer to provide, if I do so. While I consider this a duplicate, the question I proposed as a duplicate obviously was not sufficient for you to understand and solve your problem. Thus, I am of two minds over close as duplicate vs. provide answer. Then, if I provide an answer, one that is just sufficient, or comprehensive. – Makyen Dec 09 '16 at 21:49

1 Answers1

0

window.postMessage posts to the current window, which is an invisible background html window in the case of a background script. Since this isn't the page you're looking at, it isn't received.

You need to use message passing instead. Since you have a browser (or page?) action, your background.js could look like:

chrome.browserAction.onClicked.addListener(function(tab) {
    chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
});

(And for future questions, Makyen is correct: it really helps to have a mcve. In this case, that would be the 10 line manifest and two javascript files that are at most 5 lines long.)

Community
  • 1
  • 1
Teepeemm
  • 4,331
  • 5
  • 35
  • 58
  • I asked for an [mcve], etc., because the context in which the scripts were running is not clear from the question. While *main.js* is more likely a content script, it is also possible that it is a page script, or even something else. The fact that *main.js* is already getting messages from other sources implies more complexity. Thus, I considered the most common possibility is less likely than "normal". Also having a [mcve] might indicate how this question is *not* a duplicate of any other question asking about how to communicate from a background script to a content script. – Makyen Dec 09 '16 at 00:28
  • As to this answer: It would be good to explain that the OP needs to have a `chrome.runtime.onMessage` listener, to receive messages sent from `chrome.tabs.sendMessage()`. Such messages are not received from `window.addEventListener("message",...)`. – Makyen Dec 09 '16 at 00:29
  • Just FYI: Having a script called *background.js* does not guarantee that it is actually a background script. The name makes it very likely, but not guaranteed. As an example, I recently encountered a question where a [content script was called *background3.js*](http://stackoverflow.com/q/40917541/3773011). – Makyen Dec 09 '16 at 00:40
  • Switched to message passing but am still getting an error. Thanks for the help. – Scott Dec 09 '16 at 15:53