chrome.runtime.onMessage undefined in background script (chrome extension) - javascript

I am trying to create a chrome extension, but for some reasons, sometimes, the chrome.runtime object seems incomplete, and a lot of methods are missing (including onMessage, which is the one I want).
It seems sometimes it works, sometimes not. I assumed it may be a time related issue, but I don't understand why I can't simply create a message listener on background?
My background script:
setTimeout(function () {
console.log("gogo!");
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.type == "tab") {
console.log("tab!");
sendResponse({status: "ok"});
}
}),
2
});
Where "chrome.runtime.onMessage" is undefined.
Thanks!
Edit2: I have built a much simpler prototype, and it's failing again. Now I am really confused. Here is what I have:
$tree
.
├── manifest.json
├── src
│   ├── background.html
│   ├── background.js
│   └── test.js
└── vendor
└── jquery.js
2 directories, 5 files
manifest.json file:
{
"manifest_version": 2,
"name": "test",
"description": "test",
"version": "1.0",
"author": "test",
"homepage_url": "http://www.test.com",
"content_scripts": [
{
"run_at" : "document_idle",
"matches": ["https://www.google*"],
"js": ["vendor/jquery.js", "src/test.js"]
}
],
"background": {
"page": "src/background.html",
"persistent": false
},
"permissions": [
"tabs",
"https://www.google*"
]
}
background.html file:
<script src="../vendor/jquery.js"></script>
<script src="background.js"></script>
background.js file:
function run () {
console.log("gogo!");
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.type == "tab") {
console.log("tab!");
sendResponse({status: "ok"});
}
});
}
run();
test.js file:
'use strict';
run();
function run() {
var url = window.location.href;
// Error if no URI
if (!url) {
return 1;
}
var uriRe = /https:\/\/www\.google.*/;
var reParse = uriRe.exec(url);
if (!reParse) {
return 2;
}
chrome.runtime.sendMessage({type: "tab"}, function(response) {
console.log(response);
});
}
I am using Chrome 49.0.2623.112 (64-bit) on OSX.
Edit: Here is a screenshot of what happens the times it fails:
I want to precise again that it doesn't fail all the time (maybe 50% of the time?), which makes it even more weird and makes me believe there is a kind of "race" condition somewhere I am not aware of.

Problem solved: I had to disable, then re-enable all my other extensions. Seems like a bug from Chrome.

click the top and set the console in extension environmint
enter image description here
the background just run once.you should run more to check the resutl
this is my code it wroks well:
chrome.tabs.onUpdated.addListener(
function(tabId,info,tab){
if(info.status=="complete")
console.log("complete")
chrome.tabs.sendMessage(tabId, {greeting: "inupdate"}, function(response) {
console.log(response);
});
}
)

I just encountered the same problem. Typing "chrome.runtime" on the console and expanding the resulting object by clicking on the little triangle showed that it had only 1 function instead of many. In particular, chrome.runtime.onMessage was undefined.
On the page chrome://extensions I clicked the "Remove" button on the extension I was working on. I then clicked "Load unpacked" near the top of the page and selected the directory with my extension. After that, chrome.runtime was fully populated, and my extension ran correctly. I have no idea why that worked.

I think it has to do with the timeout function you are using, Google already has something called alarms which helps to schedule code to run periodically or at a specified time.
https://developer.chrome.com/apps/alarms
Try that instead of timeout

Related

Listen to chrome extension installation in app

How can I listen/track when chrome extension is installed from the web store?
I previously had an inline installation of the extension but by inline installations coming to an end soon, I want the user action to open the web store to install the extension and listen for when they add the extension for UI changes and act based on that.
I tried the messaging approach found in here but it seems not working.
manifest.json looks like:
"background": {
"scripts":["index.js"],
"persistent": false
},
"permissions": ["desktopCapture"],
"externally_connectable": {
"matches": [
"*://localhost:*/*",
"https://*.stageten.tv/*"
]
}
and index.js :
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (request === screenShareExtensionId) {
if (request.message) {
if (request.message == "version") {
sendResponse({version: 1.0})
alert('Hiiii')
}
}
}
return true;
})
and inside my app:
chrome.runtime.sendMessage(screenShareExtensionId, { message: "version" },
function (reply) {
if (reply) {
if (reply.version) {
return true;
}
}
else {
return false;
}
})
and based on the value in my redux logic, the UI either changes or not/waits for the extension to get installed.
You can do it at the start of your background page.
You need to save a flag (for example, it can be a version of the extension) to the localStorage.
After that, on each start of the background page, you need to check if this flag is in your storage. If there is no flag - then you need to track install, otherwise, it's just usual reload of the background page.
The same way can be used to track updates of the extension from the store, just need to compare versions.
Solved this this in this self-answered question, which I can't mark as a duplicate of this because of no accepted/up votes.
Here's how I solved it from the background script (w/o using a content script):
background.js
Listen for onInstalled event.
Query all opened tabs that match the URL's you want to notify.
Execute a small script in each tab that will postMessage notifying
that installation was succesful.
chrome.runtime.onInstalled.addListener(function listener(details) {
if (details.reason === chrome.runtime.OnInstalledReason.INSTALL) {
chrome.tabs.query({
url: [
'https://localhost:3000/*',
'https://staging.foo.com/*',
'https://production.foo.com/*'
]
}, tabs => {
Array.from(tabs).forEach(tab => {
chrome.tabs.executeScript(tab.id, {
code: `window.postMessage('screenshare-ext-installed', window.origin);`
});
});
});
chrome.runtime.onInstalled.removeListener(listener);
}
});
manifest.json
Just make sure both externally_connectable and permissions declare
the URL patterns of the sites you want to notify.
"externally_connectable": {
"matches": [
"https://localhost:3000/*",
"https://staging.foo.com/*",
"https://production.foo.com/*"
]
},
"permissions": [
"desktopCapture",
"https://localhost:3000/*",
"https://staging.foo.com/*",
"https://production.foo.com/*"
],
Web page
Just listen somewhere for the postMessage message fired by
the extension on succesful installation.
window.onmessage = e => {
if (e.data === 'screenshare-ext-installed') {
// extension successfully installed
startScreenShare()
}
}
Credits
#wOxxOm's comment

How to fix issues sending data from web page to chrome extension?

I am trying to pass data from my webpage to the chrome extension.
manifest.json (I am trying to do this in my local environment first)
"externally_connectable": {
"matches": ["*://localhost/*"]
}
In listen.js (a background script):
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor)
alert('test2');
alert(request.openUrlInEditor);
});
None of the alerts display above.
I got the extension ID of the unpacked chrome extension by viewing the ID when I navigate to chrome://extensions/. In my webpage in localhost environment
// DEVELOPMENT extension ID
var editorExtensionId = "fppgjikaoolnlcmdjalbfkmlcadcckmb";
var url = 'test';
// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
function(response) {
if (!response.success)
handleError(url);
});
When I run this. in the browser, I get an error saying:
Error in event handler for (unknown): TypeError: Cannot read property 'success' of undefined
I'm not sure where to begin debugging this.
After making a few changes in your code I am able achieve your goal.
See below the complete code.
manifest.json
{
"manifest_version": 2,
"name": "CS to Bg Communication",
"version": "0.1",
"background": {
"scripts": ["listen.js"]
},
"content_scripts": [
{
"all_frames" : true,
"matches": ["<all_urls>"],
"js": ["contentscript.js"]
}
],
"browser_action": {
"default_popup": "popup.html"
},
"externally_connectable": {
"matches": ["*://localhost/*"]
}
}
listen.js - the background script
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
blacklistedWebsite = 'http : / / yourdomain . com /';
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
if (request.openUrlInEditor) {
alert('test2 - ' + request.openUrlInEditor);
sendResponse({"success": true, "AckFromBG": "I have received your messgae. Thanks!"}); // sending back the acknowlege to the webpage
}
});
contentscript.js - the content script - actually does nothing
console.log("this is content script");
web-page.html - The local web page
<html>
<body>
This page will will send some message to BG script
<script type="text/javascript">
// DEVELOPMENT extension ID
var editorExtensionId = "fjaedjckfjgifecmgonfmpaoemochghb"; // replace with your extension ID
var url = 'test';
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url}, function(response) {
console.log(response);
if (!response.success)
handleError(url);
});
function handleError(url) {
console.log(url);
}
</script>
</body>
</html>
Summary of changes:
Defined blacklistedWebsite - this was undefined.
Added sendResponse({"success": true, "AckFromBG": "I have received
your messgae. Thanks!"}); to send back the acknowledgment to the
webpage.
Define function handleError(url) {
console.log(url);
} this was not defined.
That's it. Hope this will solve your issue.
The error you are getting indicates that chrome.runtime.sendMessage is executed. This means that external messaging is set up correctly: otherwise, chrome.runtime.sendMessage wouldn't be exposed to your page's code.
I think the problem is here:
if (sender.url == blacklistedWebsite)
return; // don't allow this web page access
I suspect the problem is blacklistedWebsite being undefined; the code fails with a ReferenceError and no meaningful response is being sent. Since the onMessage listener terminates (even with an error), the calling code gets an undefined response.
Check your background console to confirm if that's the case; if you copied this partial example code but aren't using this blacklisting functionality, delete this branch.
In future, please make sure you understand what every line of code you copy does!

chrome.webRequest.onBeforeRequest.addListener Cannot read property 'onBeforeRequest' of undefined

I'm trying to build my own chrome extension and I'm trying to add an event handler using onBeforeRequest.
My manifest.json:
{
"manifest_version": 2,
"name": "My extension",
"description": "some descrpition",
"version": "1.0",
"permissions": [
"activeTab",
"tabs",
"webRequest",
"webNavigation",
"management",
"http://*/*",
"https://*/*"
],
"background": {
"scripts": [
"js/jquery-2.1.4.min.js",
"js/background.js"
],
"persistent": true
},
"browser_action": {
"default_icon": "imgs/img.png",
"default_title": "extension"
},
"icons" : {
"64" : "imgs/vergrootglas.png"
}
}
My background.js:
function callback(param1,param2,param3){
alert(param1);
alert(param2);
alert(param3);
}
//alert("test");
chrome.webRequest.onBeforeRequest.addListener(callback);
I got this loaded into my chrome. But everytime I get this message in my console:
Uncaught TypeError: Cannot read property 'onBeforeRequest' of
undefined
I can't figure out what I'm oding wrong, I've found this:
https://developer.chrome.com/extensions/webRequest
But the example of the code seems to be quite the same as what I do.
What am I missing here?
The comments above don't make sense to me. There is a background script above and it seems to have appropriate permissions... Some extra comments that might help...
You need to add a background page in the manifest file and the appropriate permissions in the manifest so the background page can access the webRequest APIs. See this example: chrome.webRequest not working?
As Mihai mentioned, if you need to have the content script perform the action, check this page out: https://developer.chrome.com/extensions/messaging
Add this to your content script (you can change greeting to action and hello to which action the background script should perform):
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
Add this to your background page (you can do if statements and peform different actions based on the message):
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});
I just ran into a similar issue, except the problem for me was unrelated to permissions and it was in fact a Webpack issue.
My background script was accidentally being bundled with my content script. Background js was exporting certain constants that content script was using, causing methods restricted to background file (such as chrome.webRequest) to be called as part of content script, hence the cannot read property of undefined…

Chrome extension with no interaction with user

I am listening to some form events (mouse dbl clicking some elements), raise an event and catch that event in my content script. I do not need any direct interaction with my extension by user.
Here is my page.js (content script):
chrome.runtime.sendMessage( { method: e.data.methodName, cid: e.data.cbId, jparams: e.data.jsonParams, objectVersion: e.data.objectVersion, objectFile: e.data.objectFile }, function( response ) {
if( !response ){
//errCallback();
}
} );
Popup:
var host_name = "nativeclientapi";
var messageData = null;
var port = null;
// Listen for messages that come from the content script.
chrome.runtime.onMessage.addListener(
function( request, sender, sendResponse ) {
if( request) {
messageData = request;
alert ('hello');
sendResponse( { res: 'done!' } );
}
} );
Manifest:
{
"name": "MyChromeExt.Operarations",
"version": "1.0",
"manifest_version": 2,
"description": "This extension calls a Native API which that API calls some operations.",
"icons": {
"128": "icon.png"
},
"permissions": [
"nativeMessaging", "activeTab"
],
"content_scripts" : [{"matches": ["http://localhost/*","https://localhost/*"], "js": ["page.js"]}]
}
Why alert is not called? If I add browser_action to the manifest and set a popup page, then it works, but I do not need to have any interaction with the extension.
Thanks for your help.
Why alert is not called?
Because with that manifest popup.js is not referenced anywhere, and never gets loaded. Your extension can have lots of unused files and Chrome will not guess what goes where.
Browser / Page actions are, of course, UI elements. If you don't want a UI (or want a persistent script that's "always there") you need a background page (or better yet an event page, but make sure you understand the difference).
All in all, take a look at the Architecture Overview.
I fixed it. Simply added a background page to the manifest:
"background": {
"persistent": false,
"scripts": ["bg.js"]
},

Chrome Extension run without popup or any visual elements

I am trying to write a chrome plugin for the first time and though I have basic javascript knowledge I do not know how to start my script.
My basic problem is that I do not want to use a popup or any other menu to start my script but I cannot figure out how to do this. My intention is that the extension runs commands on startup and then exits.
I am trying to fin out how I can start my Chrome extension can be started without doing it through any visual elements.
Here is what I have:
manifest.json
{
"manifest_version":2,
"name":"Audiotool control",
"version":"1",
"content_scripts":[
{
"matches":[
"http://www.audiotool.com/*"
],
"js":[
"jquery.js",
"content_script.js"
]
}
],
"offline_enabled":true,
"permissions":[
"tabs",
"http://www.audiotool.com/*"
]
}
main.js
chrome.tabs.query({url: "http://www.audiotool.com/*", }, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});
content_script.js (not really important but just in case)
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?);
if (request.greeting == "hello")
alert("message received")
sendResponse({farewell: "function called"});
});
//var e = $.Event("keydown", { keyCode: 32});
//$("body").trigger(e);
To reiterate, I am trying to fin out how I can start my Chrome extension can be started without doing it through any visual elements.
Well, main.js never runs because you have not specified it in the manifest. You probably want something like this in your manifest:
"background": {
"scripts": ["main.js"],
"persistent": false
},
After that, you would only fire main.js when the extension started up. You need to add a listener and perform operations inside it. Such as:
chrome.tabs.onUpdated.addListener(function() {
//do stuff
});

Categories

Resources