firefox webextension messaging from popup script to background script - javascript

I have been through the webextension documentation on MDN. I do know the Messaging Api used to communicate from content_script to background script. However, I want to communicate from popup script to background script.
My use case is:
How to communicate to background script from a script associated to popup.html page.
Let's say , manifest.json is
{
"description": "Demonstrating toolbar buttons",
"manifest_version": 2,
"name": "button-demo",
"version": "1.0",
"permissions" : ["activeTab","currentWindow"],//
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"browser_style": true,
"default_popup": "popup.html",
"default_icon": {
"16": "icons/page-16.png",
"32": "icons/page-32.png"
}
}
}
background.js
//background script is listening to
browser.runtime.onMessage.addListener((sentMesssage) =>
{
console.log('got the message: ',sentMessage.actualMessage);
});
popup.html is
<html>
<body>
<script src = 'popup.js'></script>
</body>
<html>
popup.js
My question stands here. Which method to use from the below options:
browser.runtime.sendMessage({"actualMessage":"some message"});
OR
var tabQ = browser.tabs.query({
currentWindow: true,
active: true
});
tabQ.then( (tabs)=>{
browser.tabs.sentMessage(tab[0].id , {'actualMessage' : "some message"});
});

My question stands here. Which method to use from the below options:
You use browser.runtime.sendMessage({"actualMessage":"some message"});

For a popup script (as opposed to content script - the thing you might run in the host tab window) ;
You can just put functions or variables in the background script and call/set/read those from the popup via a ref to the background window object.
background-script.js
let item = null ; //private to background script
function setItem(i){item = i;}
function getItem(){return item;}
var anotherItem ; // publicly available on background window object
var popUpEar ; // if you need to initiate communication from background - set in popup
function talkToPopUp(){
popUpEar("listen to this!") ;
}
then in your popup you can do (using async/Promises)
function earToTheBackground(msg){
console.log(msg);
}
async function f(){
let backgroundWindow = await browser.runtime.getBackgroundPage();
let local_item = backgroundWindow.getItem();
backgroundWindow.setItem("something else");
let local_anotherItem = backgroundWindow.anotherItem ;
backgroundWindow.anotherItem = "something else again";
backgroundWindow.popUpEar = earToTheBackground ;
}

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.

Chrome Extension: How to assign global value from onMessage local scope function

Basically what I am trying to do is send a message from the popup.js by grabbing values from the input fields, and onMessage in the content_script to assign those values from that local scope to the global scope. I can't manage to do it! I can close the whole code in the onMessage function, but then to get to next function of the code I have to keep clicking save on the chrome extension. If someone knows this way better than I do with chrome extensions and what not please review my code and guide me to progress. I have been on this for like a week and my head is about to expload!
I have tried enclosing all the code in the onMessage function in the content_script. I have tried flipping which .js files are sending the message and trying to send the input fields from the extension as a response. I have tried to exclude the sendMessage out of the .click DOM function. Reviewed Messaging API on Google Chrome Extension and im puzzled.
content.js file
chrome.runtime.onMessage.addListener (
function(request) {
item_name = request.name;
item_color = request.color;
item_category = request.category;
item_size = request.size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
});
var url = window.location.href;
var i;
var item_category;
var item_name;
var item_color;
var item_size;
console.log(item_name);
console.log(item_color);
console.log(item_category);
console.log(item_size);
popup.js file
document.getElementById("save_input").onclick = function() {
var item_name_save = document.getElementById("item_name_save").value;
var item_color_save = document.getElementById("item_color_save").value;
var item_category_save = document.getElementById("item_category_save").value;
var item_size_save = document.getElementById("item_size_save").value;
chrome.storage.sync.set({'item_name_save' : item_name_save}, function() {
console.log('Value is set to ' + item_name_save);
});
chrome.storage.sync.set({'item_color_save' : item_color_save}, function() {
console.log('Value is set to ' + item_color_save);
});
chrome.storage.sync.set({'item_category_save' : item_category_save}, function() {
console.log('Value is set to ' + item_category_save);
});
chrome.storage.sync.set({'item_size_save' : item_size_save}, function() {
console.log('Value is set to ' + item_size_save);
});
const msg = { name: item_name_save,
color: item_color_save,
category: item_category_save,
size: item_size_save
};
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, msg, function() {
});
});
location.reload();
// window.close();
}
manifest.json
{
"manifest_version": 2,
"name": "Supreme bot",
"description": "A bot for Supreme",
"version": "1.0",
"background": {
"scripts": ["background.js"]
},
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Supreme bot"
},
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["bot-functionallity.js"]
}
],
"permissions": [
"activeTab",
"tabs",
"storage",
"tabs"
]
}
(When all code is in the onMessage function, works but not as intended)
The Incorrect Functionality
I basically want the content.js to NOT be all inside of the onMessage function because that doesn't work properly, and have it assign values in the global scope from the onMessage function. I just want that info to be sent 1 time from the popup.js to the content.js.
I just set the value with chrome.storage.sync.set and use chrome.storage.sync.get in the other file...

Chrome extension working on two tabs

I am trying to create a Chrome extension that retrieves some content from the DOM of the current tab, then opens a new tab, and performs an operation on this new tab using the previously retrieved content.
I am attempting to use callbacks to perform this sequence, but I can't figure out a proper way to do it. Now I am able to execute the first script, and get the content from the current tab DOM: what I still can't do is passing this content to the second script, utilities2.js, to be executed on the new tab.
Here's my current code:
popup.html
<!doctype html>
<html>
<head><title>Extension</title></head>
<body>
<button id="clickactivity">Click here</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
function injectTheScript() {
var tempStorage;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {file: "content_script.js"}, function(results){
tempStorage = results[0];
});
chrome.tabs.create({"url":"http://www.google.com","selected":true}, function(tab) {
});
});
}
document.getElementById('clickactivity').addEventListener('click', injectTheScript);
contentScript.js
function execContentScript() {
var code = document.getElementById('textarea').value;
return code;
}
execContentScript();
manifest.json
{
"manifest_version": 2,
"name": "My extension",
"description": "My extension",
"version": "1.0",
"permissions": ["tabs", "<all_urls>","activeTab"],
"browser_action": {
"default_popup": "popup.html"
}
}
You can store the content in the local storage of Chrome extension and after navigating to next tab, you can get the content from local storage of Chrome.
The following code may help you in this case:
chrome.storage.local.set({key: value}, function() {
console.log('Value is set to ' + value);
});
chrome.storage.local.get(['key'], function(result) {
console.log('Value currently is ' + result.key);
});
If you want to learn more about the local storage of Chrome, please read from here.
Edit:
I think you don't have idea of background scripts in the Chrome extension. Background scripts which have highest privileges to run the Chrome commands.
The complete workflow will be as follows:
popup.js is included as script
When you click button, injectTheScript function will be called from popup.js
Then it will get the element id from page and then send the request to background.js
Then background.js will save the value to Chrome storage
Then background.js will create new tab
After successful creation of tab, it will execute callback function and get the value from storage of Chrome
The following is the complete code of working extension:
manifest.json
{
"manifest_version": 2,
"name": "My extension",
"description": "My extension",
"version": "1.0",
"permissions": ["tabs", "<all_urls>","activeTab","storage"],
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"scripts": ["background.js"],
"persistent": false
}
}
popup.html
<!doctype html>
<html>
<head><
title>Extension</title>
</head>
<body>
<button id="clickactivity">Click here</button>
<script src="popup.js"></script>
</body>
</html>
popup.js
function injectTheScript() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var code="abc";
//var code = document.getElementById('textarea').value;
chrome.runtime.sendMessage({type: "navigate",value:code}, function(response) {
console.log('code saved to local storage');
});
});
}
document.getElementById('clickactivity').addEventListener('click', injectTheScript);
background.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.type == "navigate"){
chrome.storage.sync.set({key: request.value}, function() {
console.log('Background got request for saving the code '+request.value+' to local storage');
chrome.tabs.create({"url":"http://www.google.com","selected":true}, function( tab) {
console.log('navigated to new tab');
chrome.storage.sync.get(['key'], function(result) {
console.log('new tab key is '+result.key);
});
});
});
}
});
You don't need utilities2.js and content_script.js.
I've tested the code and it's working according to your requirements.
You can not see the console output if anything is consoled in background page. To see the output on console (for anything in background), open the link chrome://extensions
and click the button Background Page as following
A new window will appear as following

How to get input field value from Chrome Extension

I am working to learn how to build a Google Chrome Extension. I have a contact form on a webpage that I'm using for testing. I'm trying to create an extension that will read the input field values from that form. At this time, I have:
manifest.json
{
"manifest_version": 2,
"name": "Contact Form Friend",
"description": "This extension gets contact form info.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>"
]
}
popup.html
<!doctype html>
<html>
<head>
<title>Getting Started Extension's Popup</title>
<style type="text/css">
body {
margin: 10px;
white-space: nowrap;
}
h1 {
font-size: 15px;
}
#container {
align-items: center;
display: flex;
justify-content: space-between;
}
</style>
<script src="popup.js"></script>
</head>
<body>
<h1>Info</h1>
<div id="info">
</div>
</body>
</html>
popup.js
function getHost() {
return document.documentElement;
}
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
const scriptToRun = `(${getHost})()`;
// Run the script in the context of the tab
chrome.tabs.executeScript({
code: scriptToRun
}, (result) => {
var values = [];
var inputFields = result.getElementsByTagName('input');
infoDisplay.innerHTML = 'inputs: ' + inputFields.length;
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
infoDisplay.innerHTML += '<br />fields: ' + values.length;
});
});
When I run this, it acts like it can't access the input fields from the web page the user is on (not the extension's web page). What am I doing wrong? Am I missing a permission? I don't believe so. However, it's not working and I'm not sure why.
Thank you for your help.
As reported by Gabriel Bleu you need a content script to interact with web pages inside Chrome tabs.
Here below is an example of Chrome Extension where a content script exposes two "commands" to popup.js.
See code comments for explanation.
manifest.json (similar to yours)
{
"name": "My ext",
"version": "0.1",
"description": "my desc",
"permissions": [
"activeTab",
"<all_urls>"
],
"icons": {
"128": "icon-128.png"
},
"browser_action": {
"default_title": "My ext",
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"manifest_version": 2,
"content_security_policy": "script-src 'self' https://ajax.googleapis.com object-src 'self'"
}
popup.html (similar to yours except for jQuery usage for easy DOM manipulation)
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script type="application/javascript" charset="utf-8" src="popup.js"></script>
</head>
<body class="body-popup">
<button id="btn_check" class="btn btn-warning">check current tab</button>
<hr>
<div id="pg_url"></div>
<hr>
<div id="log"></div>
</body>
</html>
popup.js
$(function() {
$('#btn_check').click(function() { checkCurrentTab(); });
});
function checkCurrentTab() {
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
var url = tabs[0].url;
console.log("checkCurrentTab: "+url);
$(".pg_url").text(url);
// request content_script to retrieve title element innerHTML from current tab
chrome.tabs.sendMessage(tabs[0].id, "getHeadTitle", null, function(obj) {
console.log("getHeadTitle.from content_script:", obj);
log("from content_script:"+obj);
});
});
}
document.addEventListener('DOMContentLoaded', function () {
chrome.windows.getCurrent(function (currentWindow) {
chrome.tabs.query({active: true, windowId: currentWindow.id}, function(activeTabs) {
// inject content_script to current tab
chrome.tabs.executeScript(activeTabs[0].id, {file: 'content_script.js', allFrames: false});
});
});
});
function log(txt) {
var h = $("#log").html();
$("#log").html(h+"<br>"+txt);
}
content_script.js
// you will see this log in console log of current tab in Chrome when the script is injected
console.log("content_script.js");
chrome.runtime.onMessage.addListener(function(cmd, sender, sendResponse) {
console.log("chrome.runtime.onMessage: "+cmd);
switch(cmd) {
case "getHtml":
// retrieve document HTML and send to popup.js
sendResponse({title: document.title, url: window.location.href, html: document.documentElement.innerHTML});
break;
case "getHeadTitle":
// retrieve title HTML and send to popup.js
sendResponse(document.getElementsByTagName("title")[0].innerHTML);
break;
default:
sendResponse(null);
}
});
P.S.: obviously jQuery is not mandatory
Here is the error you receive:
Error in response to tabs.executeScript: TypeError: result.getElementsByTagName is not a function
at Object.chrome.tabs.executeScript [as callback] (chrome-extension://lmaefdnejmkjjmgalgfofdbobhapfmoh/popup.js:15:32)
at HTMLDocument.document.addEventListener (chrome-extension://lmaefdnejmkjjmgalgfofdbobhapfmoh/popup.js:10:17)
Instead of trying to get the DOM of the current tab in popup.js, I would suggest to use a content script to do the task and send the result as a message to the popup.
manifest.json
{
"manifest_version": 2,
"name": "Contact Form Friend",
"description": "This extension gets contact form info.",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>"
],
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}
]
}
In the manifest.json file you must add the content script and set the URLs of the sites in which you want to inject the script.
popup.js
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
window.addEventListener('DOMContentLoaded', function () {
chrome.tabs.query({active: true, currentWindow: true}, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, {}, function (result) {
infoDisplay.innerHTML = result
});
});
});
});
In popup.js send a request to the content script, to get the number of input fields and insert the response into the div.
content.js
chrome.runtime.onMessage.addListener(function (msg, sender, response) {
var values = [];
var inputFields = document.getElementsByTagName('input');
var result = 'inputs: ' + inputFields.length;
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
result += '<br />fields: ' + values.length;
response(result)
});
Create a new file, named content.js. This file will be injected into the webpage and it listens to messages from the popup.js. When a message arrives it computes the response and sends it back to popup.js.
To learn more about content scripts, check out the documentation.
If your extension needs to interact with web pages, then it needs a content script. A content script is some JavaScript that executes in the context of a page that's been loaded into the browser. Think of a content script as part of that loaded page, not as part of the extension it was packaged with (its parent extension).
https://developer.chrome.com/docs/extensions/mv3/content_scripts/
The main problem of your approach is that you try to send a DOM tree via message/sendResponse from content script to popup/background in order to process it there. You cannot send DOM trees via message/sendResponse.
A better approach would be to handle the DOM in the content script and send back the desired information (in your case, the input values) to the popup/background page.
One possible way to do it would be:
popup.js
document.addEventListener('DOMContentLoaded', () => {
const infoDisplay = document.getElementById('info');
const scriptToRun = `
var values = [];
var inputFields = document.getElementsByTagName('input');
for (var i = 0; i < inputFields.length; i++) {
values.push(inputFields[i].value);
}
values;`; //all this code will be run on the tab page
//and the array "values" will be returned.
chrome.tabs.executeScript({
code: scriptToRun
}, (result) => {
infoDisplay.innerHTML = `There are: ${result[0].length} inputs, with these values: <ol><li>${result[0].join("<li>")}`;
});
});

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.

Categories

Resources