Can't get Promise in response from background script in Firefox Extension - javascript

I need to communicate asynchronously between content and background script in Firefox Extension. Background script receives message from content.
MDN indicates, that browser.runtime.sendMessage returns Promise, so it is possible use .than to attach callbacks to it. However, when trying to call it, I get error message:
TypeError: sending.than is not a function.
What's wrong?
I'm using Linux, Firefox v 60.0.1, loading extension as temporary via about:debugging page.
Manifest.json:
{
"manifest_version": 2,
"name": "messagetest",
"version": "1.0",
"description": "Testing content to background message sending",
"icons": {
"32": "icons/bird.png"
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"permissions": [
"<all_urls>"
]
}
content.js:
function handleResponse(msg)
{
console.log("Response HANDLED: " + msg);
}
function handleError(e)
{
console.log("ERROR HANDLED");
}
var sending = browser.runtime.sendMessage({request: "messagetest"});
sending.than(handleResponse, handleError);
background.js:
function onContentMessage(msg, sender, handleResponse)
{
console.log("MESSAGE RECEIVED");
handleResponse("RESPOND FROM BACKGROUND:");
}
browser.runtime.onMessage.addListener(onContentMessage);

Related

Going bonkers over this small code, chrome.runtime.onMessage gives undefined, always. What can I do?

I know there are similar questions and I've tried almost everything in them. I'm trying to build a chrome extension and it needs to pass a message from content.js to background.js.
The code:
background.js
var xstext;
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
xstext=request.stext;
});
var wtf = "https://www.google.com/search?q="+xstext;
chrome.commands.onCommand.addListener(function(command) {
if(command=="ntab"){
chrome.tabs.create({ url: wtf});
}
});
content.js
var text = window.getSelection();
var stext=text.toString();
chrome.runtime.sendMessage({stext: stext});
manifest.json
"manifest_version": 2,
"name": "vind",
"version": "1.0",
"description": "Search stuff easily!",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [
{
"all_frames": true,
"run_at": "document_start",
"matches": [
"<all_urls>"
],
"js": ["content.js"]
}
],
"browser_action": {
"default_icon": {
"16": "images/icon16.png",
"32": "images/icon32.png"
},
"default_popup": "popup.html"
},
"commands": {
"ntab": {
"suggested_key": {
"default": "Alt+G",
"windows": "Alt+G",
"mac": "Alt+G",
"chromeos": "Alt+G",
"linux": "Alt+G"
},
"description": "New tab for the query"
}
}
}
I want to pass the selected text from content.js to background.js, I have tried adding return: true; in the listener to no avail. I'm getting 'undefined' added to the main string, so nothing seems to get passed. what should I do?
This approach won't work because 1) your content script is saving the text at the moment it runs which happens just one time at page load and 2) since the background script is not persistent it'll unload after receiving a message and xstext will disappear.
Do the reverse: ask the content script when the hotkey is pressed.
background.js, entire contents:
chrome.commands.onCommand.addListener(command => {
if (command === 'ntab') {
chrome.tabs.query({active: true, lastFocusedWindow: true}, ([tab]) => {
if (!tab) return;
chrome.tabs.sendMessage(tab.id, 'getText', text => {
chrome.tabs.create({
url: 'https://www.google.com/search?q=' + encodeURIComponent(text),
});
});
});
}
});
content.js, entire contents:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg === 'getText' && document.hasFocus()
&& (!document.activeElement || !/^I?FRAME$/.test(document.activeElement.tagName))) {
sendResponse(getSelection().toString());
}
});
P.S. An advanced solution would be to run the content script on demand (using chrome.tabs.executeScript in background.js onCommand listener) so you can remove content_scripts section from manifest.json and use activeTab permission instead.

How to show tab urls in the console log from a fiefox extension

I am trying to output all tabs in the current window by creating a firefox extension. It seems to be perfect to me, but still not able to get the output in the log. I am testing it as temporary addon from about:debugging.
I even tried to run the js code under "content_scripts", though it doesn't seem to be changing any content, but should run in the background. none of them works. Just want to know what I am missing
JavaScript
function logTabs(tabs) {
for (let tab of tabs) {
// tab.url requires the `tabs` permission
console.log(tab.url);
}
}
function onError(error) {
console.log(`Error: ${error}`);
}
var querying = browser.tabs.query({{currentWindow: true}});
querying.then(logTabs, onError);
Manifest
{
"manifest_version": 2,
"name": "Tablog",
"version": "1.0",
"description": "Prints the all tabs url in he console",
"icons": {
"48": "icons/icon48.png"
},
"background": {
"scripts": ["tablog.js"]
},
"permissions": [
"<all_urls>",
"tabs",
"activeTab"
]
}

chrome messaging response returning undefined with onclick event

I'm trying to test simple messaging between the content script and background script. But I can't really figure out why my response from the listener is not returning anything. My background script activates the message by the onClicked event listener
my manifest:
{
"background": {
"scripts": ["content.js"]
},
"browser_action": {
"default_icon": "19.png"
},
"manifest_version" : 2,
"name": "test",
"version": "0.1",
"permissions": ["tabs"],
"content_scripts": [ {
"matches": [ "*://twitter.com/*"],
"js": ["scrape.js"]
} ]}
background script(content.js)
chrome.browserAction.onClicked.addListener(function(tabs) {
chrome.tabs.sendMessage(tabs.id, {text: "hey"}, function(response) {
console.log("Response: ", response);
});
});
content script (scrape.js):
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
console.log("Received %o from %o, frame", msg, sender.tab, sender.frameId);
sendResponse({farewell:"bye"});
});
I'm passing the tabID to the sendmessage function, but i'm not sure why my content script is not able to return a valid response. I've browsed several different stack posts and no luck so far

Make Chrome extension script target activeTab DOM [duplicate]

I'm trying to access the activeTab DOM content from my popup. Here is my manifest:
{
"manifest_version": 2,
"name": "Test",
"description": "Test script",
"version": "0.1",
"permissions": [
"activeTab",
"https://api.domain.com/"
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_icon": "icon.png",
"default_title": "Chrome Extension test",
"default_popup": "index.html"
}
}
I'm really confused whether background scripts (event pages with persistence: false) or content_scripts are the way to go. I've read all the documentation and other SO posts and it still makes no sense to me.
Can someone explain why I might use one over the other.
Here is the background.js that I've been trying:
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
// LOG THE CONTENTS HERE
console.log(request.content);
}
);
And I'm just executing this from the popup console:
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, { }, function(response) {
console.log(response);
});
});
I'm getting:
Port: Could not establish connection. Receiving end does not exist.
UPDATE:
{
"manifest_version": 2,
"name": "test",
"description": "test",
"version": "0.1",
"permissions": [
"tabs",
"activeTab",
"https://api.domain.com/"
],
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
"browser_action": {
"default_icon": "icon.png",
"default_title": "Test",
"default_popup": "index.html"
}
}
content.js
chrome.extension.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.text && (request.text == "getDOM")) {
sendResponse({ dom: document.body.innerHTML });
}
}
);
popup.html
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.sendMessage(tab.id, { action: "getDOM" }, function(response) {
console.log(response);
});
});
When I run it, I still get the same error:
undefined
Port: Could not establish connection. Receiving end does not exist. lastError:30
undefined
The terms "background page", "popup", "content script" are still confusing you; I strongly suggest a more in-depth look at the Google Chrome Extensions Documentation.
Regarding your question if content scripts or background pages are the way to go:
Content scripts: Definitely
Content scripts are the only component of an extension that has access to the web-page's DOM.
Background page / Popup: Maybe (probably max. 1 of the two)
You may need to have the content script pass the DOM content to either a background page or the popup for further processing.
Let me repeat that I strongly recommend a more careful study of the available documentation!
That said, here is a sample extension that retrieves the DOM content on StackOverflow pages and sends it to the background page, which in turn prints it in the console:
background.js:
// Regex-pattern to check URLs against.
// It matches URLs like: http[s]://[...]stackoverflow.com[...]
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?stackoverflow\.com/;
// A function to use as callback
function doStuffWithDom(domContent) {
console.log('I received the following DOM content:\n' + domContent);
}
// When the browser-action button is clicked...
chrome.browserAction.onClicked.addListener(function (tab) {
// ...check the URL of the active tab against our pattern and...
if (urlRegex.test(tab.url)) {
// ...if it matches, send a message specifying a callback too
chrome.tabs.sendMessage(tab.id, {text: 'report_back'}, doStuffWithDom);
}
});
content.js:
// Listen for messages
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
// If the received message has the expected format...
if (msg.text === 'report_back') {
// Call the specified callback, passing
// the web-page's DOM content as argument
sendResponse(document.all[0].outerHTML);
}
});
manifest.json:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
...
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.stackoverflow.com/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Test Extension"
},
"permissions": ["activeTab"]
}
Update for manifest v3
chrome.tabs.executeScript doesn't work in manifest v3, as noted in the comments of this answer. Instead, use chrome.scripting. You can specify a separate script to run instead of a function, or specify a function (without having to stringify it!).
Remember that your manifest.json will need to include
...
"manifest_version": 3,
"permissions": ["scripting"],
...
You don't have to use the message passing to obtain or modify DOM. I used chrome.tabs.executeScriptinstead. In my example I am using only activeTab permission, therefore the script is executed only on the active tab.
part of manifest.json
"browser_action": {
"default_title": "Test",
"default_popup": "index.html"
},
"permissions": [
"activeTab",
"<all_urls>"
]
index.html
<!DOCTYPE html>
<html>
<head></head>
<body>
<button id="test">TEST!</button>
<script src="test.js"></script>
</body>
</html>
test.js
document.getElementById("test").addEventListener('click', () => {
console.log("Popup DOM fully loaded and parsed");
function modifyDOM() {
//You can play with your DOM here or check URL against your regex
console.log('Tab script:');
console.log(document.body);
return document.body.innerHTML;
}
//We have permission to access the activeTab, so we can call chrome.tabs.executeScript:
chrome.tabs.executeScript({
code: '(' + modifyDOM + ')();' //argument here is a string but function.toString() returns function's code
}, (results) => {
//Here we have just the innerHTML and not DOM structure
console.log('Popup script:')
console.log(results[0]);
});
});
For those who tried gkalpak answer and it did not work,
be aware that chrome will add the content script to a needed page only when your extension enabled during chrome launch and also a good idea restart browser after making these changes

tabs.getCurrent() result is undefined?

Not sure why I cannot retrieve info about current tab using getCurrent() when I navigate to, say, amazon.com or google.com and hit the browser icon for a browser action. Any hints on what I am missing?
MANIFEST:
{
"name": "testGetCurrentTab",
"version": "1.0",
"description": "",
"manifest_version": 2,
"icons": {
"48": "icons/icon-48.png"
},
"permissions": [
"tabs",
"<all_urls>"
],
"browser_action": {
"default_icon": "icon/icon-32.png"
},
"background": {
"scripts": ["background.js"]
}
}
BACKGROUND:
function displayInfo() {
function onGot(tabInfo) {
console.log('Inside onGot() ...');
console.log(tabInfo);
}
function onError(error) {
console.log(`Error: ${error}`);
}
var gettingCurrent = browser.tabs.getCurrent();
gettingCurrent.then(onGot, onError);
}
browser.browserAction.onClicked.addListener(displayInfo);
Here is the output:
Inside onGot() ... background.js:4:7
undefined background.js:5:7
Firefox Dev Edition 54 (64bit)
https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs/getCurrent
You can get the currently active tab in the background.js file as well when doing
browser.tabs.query({active: true, windowId: browser.windows.WINDOW_ID_CURRENT})
.then(tabs => browser.tabs.get(tabs[0].id))
.then(tab => {
console.info(tab);
});
From MDN tabs.getCurrent():
Get a tabs.Tab containing information about the tab that this script is running in.
You can call this function in contexts where there is a browser tab, such as an options page. If you call it from a background script or a popup, it will return undefined.
The browserAction.onClicked event returns the active tab, you do not need another API call.
browser.browserAction.onClicked.addListener(function(tab) {
console.log(tab)
})
See the tab parameter for browserAction.onClicked listener.

Categories