Unable to receive message on tab from background script - javascript

I wrote this script to send a message from a background script to a script in a new tab but for some reason, the script in the tab isn't receiving the message. Is this a problem with my script or my browser (Firefox 62.0.3)
my "manifest":
{
"manifest_version":2,
"name": "test",
"version": "1.0",
"description": "this is a test extension",
"background":{
"scripts": ["OnButtonClick.js"]
},
"permissions": [
"tabs"
],
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"]
}],
"browser_action": {
"default_icon": "button.png",
"default_title": "test button"
}
}
my "OnButtonClick.js":
function action(){
browser.tabs.create({
url: "www.youtube.com"
});
browser.tabs.sendMessage(1,{"message":"hi"})
}
browser.browserAction.onClicked.addListener(action);
and my "input.js":
function handleMessage(msg){
console.log(msg);
}
browser.runtime.onMessage.addListener(handleMessage)

browser.tabs.create() is a asynchronous therefore, browser.tabs.sendMessage() runs even before a tab is created.
You have to wait for it to run first.
Here are some suggestions:
// first create the tab
const newTab = browser.tabs.create({
url: 'https://example.org'
});
newTAb.then(onCreated, onError);
// after tab is created
function onCreated(tab) {
browser.tabs.sendMessage(tab.id,{message: 'hi'});
}
// in case of error
function onError(error) {
console.log(`Error: ${error}`);
}
// above can also be written as this
browser.tabs.create({
url: 'https://example.org'
}).then(
tab => browser.tabs.sendMessage(tab.id,{message: 'hi'}),
error => console.log(error)
);
// another alternative for above
browser.tabs.create({url: 'https://example.org'})
.then(tab => browser.tabs.sendMessage(tab.id,{message: 'hi'}))
.catch(error => console.log(error));
// Using chrome and callback function
chrome.tabs.create({url: 'https://example.org'}, tab =>
browser.tabs.sendMessage(tab.id,{message: 'hi'})
);
// same as above, all with chrome
chrome.tabs.create({url: 'https://example.org'}, tab =>
chrome.tabs.sendMessage(tab.id,{message: 'hi'})
);
You can also use async/await but that may make it more complicated in this case.
Update on comment:
content_scripts by default run at "document_idle" (corresponds to complete. The document and all its resources have finished loading.)
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"]
}],
Therefore, the input.js is injected once everything is loaded. However, the sendMessage() runs as soon as tab is created and thus there is no listener to listen to its message.
In your simple example, that can be fixed by "run_at": "document_start"
"content_scripts": [{
"matches": ["www.youtube.com"],
"js": ["input.js"],
"run_at": "document_start"
}],
However, if input.js needs to access DOM after receiving message, then you need to add a DOMContentLoaded or load listener and run it after the document is loaded.

why do you want to use "sendMessage"?
you can use this code
browser.tabs.executeScript(tabID, { code: "func()" /* your function in content script*/,frameId:0 /* for send to all frame or put id for use a special frame id*/});
or this code as file
browser.tabs.executeScript(tabID, { file: "/filename.js",frameId:0});

Related

I want to get the HTML of the web page, but I am getting the HTML of my chrome extension instead

I want to extract the HTML of a webpage so that I can analyze it and send a notification to my chrome extension. Sort of like how an adblocker does it when analyzing a web page for ads and then tell the extension how many possible ads there are.
I am trying to use the document object in content-scripts to get the HTML, however, I always seem to get the HTML of my popup file instead. Can anybody help?
content-script.js
chrome.tabs.onActivated.addListener(function(activeInfo) {
chrome.tabs.get(activeInfo.tabId, function(tab) {
console.log("[content.js] onActivated");
chrome.tabs.sendMessage(
activeInfo.tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: tab.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
});
});
chrome.tabs.onUpdated.addListener((tabId, change, tab) => {
if (tab.active && change.url) {
console.log("[content.js] onUpdated");
chrome.tabs.sendMessage(
tabId,
{
content: document.all[0].innerText,
type: "from_content_script",
url: change.url
},
{},
function(response) {
console.log("[content.js]" + window.document.all[0].innerText);
}
);
}
});
background.js
let messageObj = {};
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Arbitrary string allowing the background to distinguish
// message types. You might also be able to determine this
// from the `sender`.
if (message.type === "from_content_script") {
messageObj = message;
} else if (message.type === "from_popup") {
sendResponse(messageObj);
}
});
manifest.json
{
"short_name": "Extension",
"version": "1.0.0",
"manifest_version": 3,
"name": "My Extension",
"description": "My Extension Description",
"permissions": ["identity", "activeTab", "tabs"],
"icons": {
"16": "logo-16.png",
"48": "logo-48.png",
"128": "logo-128.png"
},
"action": {
"default_icon": "ogo_alt-16.png",
"default_popup": "popup.html"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["./static/js/content-script.js"],
"run_at": "document_end"
}
],
"background": {
"service_worker": "./static/js/background.js"
}
}
Your current content script is nonfunctional because content scripts cannot access chrome.tabs API. If it kinda worked for you, the only explanation is that you loaded it in the popup, which is wrong because the popup is not a web page, it's a separate page with a chrome-extension:// URL.
For your current goal, there's no need for the background script at all because you can simply send a message from the popup to the content script directly to get the data. Since you're showing the info on demand there's also no need to run the content scripts all the time in all the sites i.e. you can remove content_scripts from manifest.json and inject the code on demand from the popup.
TL;DR. Remove content_scripts and background from manifest.json, remove background.js and content-script.js files.
manifest.json:
"permissions": ["activeTab", "scripting"],
popup.html:
<body>
your UI
<script src=popup.js></script>
</body>
popup.js:
(async () => {
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let result;
try {
[{result}] = await chrome.scripting.executeScript({
target: {tabId: tab.id},
func: () => document.documentElement.innerText,
});
} catch (e) {
document.body.textContent = 'Cannot access page';
return;
}
// process the result
document.body.textContent = result;
})();
If you want to to analyze the page automatically and display some number on the icon then you will need the background script and possibly content_scripts in manifest.json, but that's a different task.

How to set focus to Firefox window using Web Extension

I am trying to create a Firefox addon that will bring the browser window to the forefront whenever a given event is fired.
This is my manifest:
{
"manifest_version": 2,
"name": "Test Extension",
"version": "1.0",
"description": "Test browser extension",
"content_scripts": [{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"permissions": [
"notifications",
"activeTab"
]
}
Content.js:
'use strict';
document.addEventListener('newOrderEvent', function() {
console.log('New Order event received');
browser.runtime.sendMessage('new-order-event');
});
Background.js:
'use strict';
var windowId;
function newOrderListener(message) {
if (message === 'new-order-event') {
console.log('Received event!');
browser.windows.update(windowId, {
drawAttention: true,
focused: true,
state: 'maximized'
});
}
}
browser.windows.getCurrent().then(function(window) {
windowId = window.id;
});
browser.runtime.onMessage.addListener(newOrderListener);
The newOrderEvent is generated by my application's webpage and it is always being called at the right time, however sometimes the browser comes to the foreground and other times it doesn't and I don't understand exactly why. Furthermore the console output from the background.js is not being registered by the browser.
What I am doing wrong?
Note: I am using Firefox 52 and Windows 10.
If what you want is a TAB to make itself focused, then you are mistaken with what windowId is. WindowId will be the ID of the window, not of a tab, and the way you are setting it, it will only ever be a single value, the first windowID when the extension is loaded
if you change background.js to the following
function newOrderListener(message, sender) {
if (message === 'new-order-event') {
console.log('Received event for window %s, tab %s', sender.tab.windowId, sender.tab.id);
browser.windows.update(sender.tab.windowId, {
drawAttention: true,
focused: true,
state: 'maximized'}
).then(() => browser.tabs.update(sender.tab.id, {
active: true
}));
}
}
browser.runtime.onMessage.addListener(newOrderListener);
Then any tab that has the content script loaded (which is basically every tab in your code) will be able to focus itself by
document.dispatchEvent(new Event("newOrderEvent"))

How to Access / Read any Text / Tag Value from the Webpage using Chrome Extension

I am writing my First Chrome Plugin and I just want to get some text present on the current webpage and show it as a alert when i click the Extension. Lets say I am using any any webpage on www.google.com after some Search query, Google shows something like "About 1,21,00,00,000 results (0.39 seconds) " . I want to show this Text as an alert when i execute my plugin. This is what i am doing.
here is the manifest.json that i am using
{
"manifest_version": 2,
"name": "Getting started example",
"description": "This extension shows a Google Image search result for the current page",
"version": "1.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["*://*.google.com/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
]
}
Here is my popup.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("checkPage").addEventListener("click", handler);
});`
function handler() {
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
}
Here is my 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);
}
});
Here is my background.js
var urlRegex = /^https?:\/\/(?:[^./?#]+\.)?google\.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);
}
});
1) run content scrip after document ready ** check "run_at"
"content_scripts": [{
"run_at": "document_idle",
"matches"["*://*.google.com/*"],
"js": ["content.js"]
}],
2) on click of extension make another js to run( popup js). The popup js has access to the ( open page document)
// A function to use as callback
function doStuffWithDom() {
//console.log('I received the following DOM content:\n' + domContent);
//get tabID when browser action clicked # tabId = tab.id
chrome.tabs.executeScript(tabId, {file: "js/popup.js"});
}
3) In popup JS simple you can set alert
var a = document.getElementById("resultStats");
alert(a.innerText); // or alert(a.innerHTML);
Just remove "default_popup" part in manifest.json, as you have listened to chrome.browserAction.onClicked event in background page. They can't live at the same time.

Running a function in chrome extension on double click/select of a word

I am trying to fire a notification whenever I double click on a word/select it. Found several examples online but I still cannot get my example working:
Manifest:
{
"name": "hh",
"description": "hh!",
"manifest_version": 2,
"version": "0.0.0.1",
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*" ],
"js": [ "background.js" ],
"all_frames": true,
"run_at": "document_end"
}
],
"permissions": ["storage", "notifications"],
"icons": { "128": "neoprice.png" }
}
background.js
var listener = function(evt) {
var selection = window.getSelection();
if (selection.rangeCount > 0) {
displayPrice();
var range = selection.getRangeAt(0);
var text = range.cloneContents().textContent;
console.log(text);
}
};
document.addEventListener('dblclick', listener);
function displayPrice(){
chrome.notifications.create(getNotificationId(), {
title: "message.data.name",
iconUrl: 'hh.png',
type: 'basic',
message: "message.data.prompt"
}, function() {});
}
// Returns a new notification ID used in the notification.
function getNotificationId() {
var id = Math.floor(Math.random() * 9007199254740992) + 1;
return id.toString();
}
I was earlier adding the following but I saw people weren't using it, so I removed it
"app": {
"background": {
"scripts": ["background.js", "assets/jquery.min.js"]
}
},
What I am trying to achieve: Whenever they go to ANY page on selecting a word, it fires the function. Later, I wish to use this for a specific page. :)
Tried: How to keep the eventlistener real time for chrome extensions?
Chrome extension double click on a word
https://github.com/max99x/inline-search-chrome-ext
Both don't really work as I want them too. :(
Solution
It seems you are confused with background page and content script. Your background.js is a content script in fact, though its name is "background". While chrome.notifications api can be only called in background page, trying commenting displayPrice function will make your code work.
Next step
Take a look at above tutorials, wdblclick event triggers, use Message Passing to communicate with background page and call chrome.notications api in background page.
What's more
The following code is used in chrome apps rather than chrome extension.
"app": {
"background": {
"scripts": ["background.js", "assets/jquery.min.js"]
}
},

console.log doesn't work from chrome.tabs.executeScript

I need to execute script once user clicked my context menu item.
So for the purpose I created the context menu from my background js:
chrome.contextMenus.create({"title": title, "contexts": contexts,
"onclick": genericOnClick});
It appears as expected. Later on from the genericOnClick I try to execute my script:
chrome.tabs.executeScript(null, {code: "console.log('test 1');"}, function() {
console.log("test 2");
});
I can see that the "test 2" is printed to console but "test 1" never gets printed. What am I doing wrong?
I've tried adding the console.log sentence to a separate js file but it failed to print it as well:
chrome.tabs.executeScript(null, {"file": 'content_script.js'}, function() {
console.log("test 2");
});
Note: my content_script.js is not defined in manifest. My manifest looks like follows:
{
"name": "My First Extension",
"version": "1.0",
"manifest_version": 2,
"description": "Sample extension",
"page_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"http://*/*",
"https://*/*",
"tabs",
"contextMenus"
],
"background": {
"scripts": ["sample.js"]
},
"icons": {
"16": "icon16.png"
}
}
Thank you in advance.
The only piece of code from your extension that has access to the console is the content script that is injected into the original page.
From your code it looks like you are trying to write to the console from a background script. So, to write to the console from a background page you've to inject a content script to do the job for you.
In my extensions I use my own function to write messages to the console from a background script. The code for the same is given below:
function logMessage(msg){
chrome.tabs.executeScript({code:"console.log('"+msg+"')"});
}
Define the above as the first function in your background script and call it from anywhere in the background script to write messages to the console.
In your case you can use this from the genericOnClick function to write messages to the console.
// addListener
chrome.browserAction.onClicked.addListener(function() {
chrome.tabs.executeScript(null, {file: "content_script.js"}, function() {
console.log("test 2");
});
});
// Context Menu
chrome.contextMenus.create({
title: myTitle,
contexts: ['page'],
onclick: function (detail, tab) { fn(tab) }
});
so;
"permissions": [
"tabs", "http://*/*", "https://*/*"
]
chrome.tabs.executeScript(null,{code:"document.body.style.backgroundColor='red'"});
or:
// Functional structure
function hi() { alert("hi"); };
// hi function toString after run function (hi)()
chrome.tabs.executeScript(null, { code: "(" + hi.toString() + ")()" });

Categories

Resources