I'm trying to develop extension that gets active tab and generate confirm window at current open tab. But I cannot do this with "window.confirm" because window is not defined. Here's my code:
BACKGROUND.JS
var notifyTimer = setInterval(func,5*1000);
console.log('setinterval ran');
function func(){
let ActiveTab = getCurrentTab();
console.log(ActiveTab)
}
async function getCurrentTab() {
let queryOptions = { active: true, lastFocusedWindow: true };
let [tab] = await chrome.tabs.query(queryOptions);
console.log(tab.url)
//alert(" Hello!")
// SOME CODE TO GENERATE CONFIRM WINDOW or ALERT
return tab;
}
Currently it's fetching current active tab but I don't know how to generate confirm window
Kindly help me to generate alert with this code.
Is this what you want to do?
manifest.json
{
"name": "hoge",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"scripting"
],
"host_permissions":[
"<all_urls>"
],
"background": {
"service_worker": "background.js"
}
}
background.js
var notifyTimer = setInterval(func, 5 * 1000);
console.log('setinterval ran');
function func() {
let ActiveTab = getCurrentTab();
console.log(ActiveTab)
}
const alert = () => {
alert(" Hello!");
}
async function getCurrentTab() {
let queryOptions = { active: true, lastFocusedWindow: true };
let [tab] = await chrome.tabs.query(queryOptions);
console.log(tab.url)
//alert(" Hello!")
// SOME CODE TO GENERATE CONFIRM WINDOW or ALERT
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: alert
});
return tab;
}
Related
I am developing my first chrome extension, I want to have a toggle button on my popup which allows users to enable or disable the extension for that site, though not completely disable the extension but technically control the execution of the content.js for that particular domain.
Something similar to
popup.js
const toggleBtn = document.querySelector(".toggle");
toggleBtn.addEventListener("click", function(){
if(toggleBtn.checked === true){
console.log('inject content.js');
}else{
console.log('remove content.js');
}
});
I can see there is a way to inject it but there is no way to remove it.
chrome.scripting.executeScript({
target: {tabId},
files: ['content.js'],
});
Note, I don't want to reload the page but want to disable the content.js. How can I achieve this functionality inside my chrome extension. I see there are many extensions doing this like Adblocker and Wordtune.
I went through few SO threads like this one but most of them are for Manifest V2 and few packages that are recommended are obsolate now. I also could not find a complete example anywhere.
Todo
I should be able to turn the extension on/off for the current domain from within the Popup.
It should store the state inside a chrome.storage.sync so that next time I installed the extension I should not again turn on/off for the selected websites.
As soon as I toggle the button, the changes should be visible if I have opened the same domain in another tab.
The extension's icon should be changed in real time as well e.g grey icon for disabled status
If I close and open a website, the icon and the toggle button should be restored their on/off status and update the icon accordingly.
My Attempt (Solves #toDo 1,2,3,4,5)
background.js
const icons = {
enabled: {
'16': 'icon-16.png',
'19': 'icon-19.png',
'38': 'icon-38.png',
'48': 'icon-48.png',
'128': 'icon-128.png'
},
disabled: {
'16': 'icon-16-off.png',
'19': 'icon-19-off.png',
'38': 'icon-38-off.png',
'48': 'icon-48-off.png',
'128': 'icon-128-off.png'
}
};
const getExtensionStatus = host => new Promise((resolve, reject) => {
chrome.storage.sync.get(host, result => {
if (result[host] !== undefined) {
resolve(result[host]);
} else {
setExtensionStatus(host, true);
}
});
});
const setExtensionStatus = (host, toggleBtnStatus) => {
const data = { [host]: toggleBtnStatus };
chrome.storage.sync.set(data, () => {});
};
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason === "install") {
chrome.action.setIcon({ path: icons.enabled });
}
});
const init = async tab => {
const url = new URL(tab.url);
if (url.protocol !== "chrome-extension:") {
const host = url.host;
const status = await getExtensionStatus(host);
const icon = status ? icons.enabled : icons.disabled;
chrome.action.setIcon({ path: icon });
}
};
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === "complete") init(tab);
});
chrome.tabs.onActivated.addListener(info => {
chrome.tabs.get(info.tabId, init);
});
chrome.runtime.onMessage.addListener(async (request, sender, sendResponse) => {
if (request.type === "getExtensionStatus") {
const status = await getExtensionStatus(request.host);
sendResponse({ status });
return true;
} else if (request.type === "setExtensionStatus") {
setExtensionStatus(request.host, request.status);
const icon = request.status ? icons.enabled : icons.disabled;
chrome.action.setIcon({ path: icon });
}
});
popup.js
document.addEventListener("DOMContentLoaded", function () {
const toggleBtn = document.querySelector(".toggle");
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const host = new URL(tabs[0].url).host;
chrome.runtime.sendMessage({ type: "getExtensionStatus", host }, (response) => {
toggleBtn.checked = response.status;
});
toggleBtn.addEventListener("click", () => {
chrome.runtime.sendMessage({ type: "setExtensionStatus", host, status: toggleBtn.checked });
});
});
});
What left?
The content.js is not available after some time of inactiveness of the tab. How can I inject the script again and avoid running event multiple times?
content.js
document.addEventListener("keydown", listenEnterkey);
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type === "setExtensionStatus") {
console.log(request.status);
if (request.status) {
// Add event listener here
document.addEventListener("keydown", listenEnterkey);
} else {
// Remove event listener here
document.removeEventListener("keydown", listenEnterkey);
}
}
});
For example, if you add a button with a script, I don't think the added button will disappear even if you remove the script.
Therefore, the ON/OFF function is built into the script, and it is operated by the message.
manifest.json
{
"name": "content_scripts + popup",
"version": "1.0",
"manifest_version": 3,
"content_scripts": [
{
"js": [
"matches.js"
],
"matches": [
"<all_urls>"
]
}
],
"host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html"
}
}
matches.js
console.log("matches.js");
let isEnabled = true;
console.log(isEnabled);
chrome.runtime.onMessage.addListener((message) => {
switch (message) {
case "on":
isEnabled = true;
break;
default:
isEnabled = false;
break;
}
console.log(isEnabled);
});
popup.js
const send = (s) => {
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, s);
});
}
document.getElementById("on").onclick = () => {
send("on");
}
document.getElementById("off").onclick = () => {
send("off");
}
popup.html
<html>
<body>
<button id="on">on</button>
<button id="off">off</button>
<script src="popup.js"></script>
</body>
</html>
I can find plenty of questions for sending a message from Bbackground to content scripts or from popup to background scripts, but I can't find any on how to send a message from options.js (triggered from my options page) to background.js for my Chrome extension
manifest.js
{
...
"manifest_version": 3,
"permissions": [
"alarms",
"contextMenus",
"notifications",
"storage"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html"
},
"options_page": "options.html",
"icons": {
"48": "/assets/icons/icon-48.png",
"128": "/assets/icons/icon-128.png"
}
}
Code in options.js
// Save options
document.querySelector('#save').addEventListener('click', ()=> {
// Check for selected regions
let selectedRegions = [];
regionChecks.forEach(elm => {
if (elm.checked)
selectedRegions.push(elm.id.replace('region-', ''))
});
// Save selections
chrome.storage.sync.set({ 'regions': selectedRegions });
// Check for refresh period
let refreshPeriod = document.querySelector('#refresh');
if (refreshPeriod) {
refreshPeriod = parseInt(refreshPeriod.value);
if (refreshPeriod === NaN) {
refreshPeriod = 5;
} else if(refreshPeriod < 2) {
refreshPeriod = 2;
} else if (refreshPeriod > 120) {
refreshPeriod = 120;
}
// Save period
chrome.storage.sync.set({ 'refresh': refreshPeriod });
}
// Loop through features and remove if region gone
chrome.storage.local.get(['features'], featuresData => {
let featureCount = 0;
if (featuresData.features) {
featureSet = featuresData.features;
Object.keys(featureSet).forEach(key => {
if (!selectedRegions.includes(featureSet[key].Region)) {
delete featureSet[key];
} else if (featureSet[key].Status !== "read") {
featureCount = featureCount + 1;
}
});
// Update features data
chrome.storage.local.set({ 'features': featureSet });
// Update the badge
if (featureCount > 0) {
chrome.action.setBadgeText({ text: featureCount.toString() });
} else {
chrome.action.setBadgeText({ text: '' });
}
}
});
// Trigger an update
chrome.runtime.sendMessage('', {
type: 'update'
});
// Close the options page
close();
});
Code in background.js
// Listen for messages
chrome.runtime.onMessage.addListener(data => {
console.log(data);
if (data.type === 'update') {
scheduleRequest();
}
});
But nothing is happening
I've tried using tabs.sendMessage, but even if I put tabs in permissions it tells me that it isn't defined when I add it to the function chrome.tabs.sendMessage(tabs[0].id, {...
My test results contradict your claim.
manifest.json
{
"manifest_version": 3,
"name": "hoge",
"version": "1.0",
"background": {
"service_worker": "background.js"
},
"options_page": "options.html"
}
background.js
// Listen for messages
chrome.runtime.onMessage.addListener(data => {
console.log(data);
if (data.type === 'update') {
console.log("hoge")
// scheduleRequest();
}
});
options.js
// Trigger an update
chrome.runtime.sendMessage('', {
type: 'update'
});
options.html
<html>
<body>
options page
<script src="options.js"></script>
</body>
</html>
After some input from #woxxom and #Norio, and a little bit more troubleshooting, this is how I managed to get it to work
option.js:
// Trigger an update
chrome.runtime.sendMessage({ type: 'update' }, result => {
// Close the options page
close();
});
I had to move the close(); event into the return function so it didn't prematurely close off the code. I also had to have a paramter in the return function for some reason otherwise it wouldn't run with an empty arrow function.
background.js:
// Listen for messages
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'update') {
startRequest(true).then(sendResponse);
return true;
}
});
My function to be triggered in the background.js file was already an async promise function, so I had to add a .then() for the onMessage response function to trigger once it had run. It also wouldn't work without having return true; in there.
I am making an extension for an online chat that has to count the number of new messages sent since the user entered the tchat.
So I'm looking for a JavaScript function that can analyse permanently the tchat page content (specifically the div that contains all messages) and then execute a function. This function will modify the logo badge of the extension and put the number of new messages instead.
I've already tried with MutationObserver, but it doesn't work (maybe because I made mistakes, I don't know). Here's my manifest.json :
{
"manifest_version":2,
"name":"Tchat",
"description":"test",
"version":"0.0.2",
"permissions": [
"tabs"
],
"background": {
"scripts": ["page-reader.js"],
"persistent": true
},
"commands":{
"_execute_browser_action":{
"suggested_key":{
"default":"Ctrl+Q"
}
}
},
"browser_action": {
"default_popup":"index.html",
"default_icon": {
"16": "images/favicon-16x16.png",
"32": "images/favicon-32x32.png",
"144": "images/android-icon-144x144.png"
}
},
"icons": {
"16": "images/favicon-16x16.png",
"32": "images/favicon-32x32.png",
"144": "images/android-icon-144x144.png"
}
}
Here's my page-reader.js file :
var targetNode = null;
var config = {
attributes: true,
childList: true,
subtree: true
};
var MutationObserver = window.MutationObserver;
var numberOfTchatMessages = 0;
var observer = null;
function tchatAnalyser() {
chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => {
let url = tabs[0].url;
if (url == "here is the tchat url") {
chrome.browserAction.setBadgeBackgroundColor({color: "red"});
chrome.browserAction.setBadgeText({text: "ON"});
targetNode = document.getElementById('zone_tchat');
var callback = function() {
numberOfTchatMessages++;
chrome.browserAction.setBadgeText({text: numberOfTchatMessages.toString()});
};
if (targetNode && MutationObserver) {
observer = new MutationObserver(callback);
observer.observe(targetNode, config);
}
}
else {
chrome.browserAction.setBadgeBackgroundColor({color:"dodgerblue"});
chrome.browserAction.setBadgeText({text:"OFF"});
}
});
}
chrome.tabs.onActivated.addListener(tchatAnalyser);
The logo gets red when I switch to the chat tab, but the text stays at ON… Is there any error in my code ? Is MutationObserver the tool I need ?
Thank you in advance, have a nice day,
I have an extension that declaratively specifies using a content_script:
manifest.json:
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"],
"run_at": "document_end"
}
],
I'm reading that by instead specifying the activeTab permission, it won't alert about permissions during installation:
https://developer.chrome.com/extensions/activeTab
My question is: how can you switch to using
"permissions":["activeTab"]
from using content_scripts?
Here's my popup.js code that calls the content_script:
chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { action: "checkForCode" }, function (response) {
if (!!response) { showResults(response.results); }
});
});
and the content_script's event handler:
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.action == "checkForCode") {
getCode(request, sender, sendResponse);//method sends callback.
return true;
}
});
This code works just fine, but I'm wondering how to use it with the activeTab permissions. Should I just add the content.js via chrome.tags.executeScript(), then reference it the same way?
In your manifest.json you need to set
"permissions": ["activeTab", "tabs"],
and
"background": {
"scripts": ["content.js"],
"persistent": false
},
your content.js as example:
// metaCode will be injected and executed in the current tab
// the returned results you can use in callbackFunction
var metaCode = 'var descr = document.querySelector("meta[name=\'description\']");'
+ 'var keyw = document.querySelector("meta[name=\'keywords\']");'
+ 'if (descr) var descr = descr.getAttribute("content");'
+ 'if (keyw) var keyw = keyw.getAttribute("content");'
+ '({'
+ ' description: descr || "",'
+ ' keywords: keyw || "",'
+ '})';
chrome.tabs.executeScript(
tab.id,
{code: metaCode}, // get meta key words
callbackFunktion
);
function callbackFunktion(results) {
var result = results[0];
var description = result.description;
var keywords = result.keywords;
// and so on ... whatever you want
// to do with results from the
// injected script from 'metaCode'
}
I managed to pass a message between two files/instances, namely my popup and my background scripts, but the callback function still returns undefined, so my popup script does not receive a reply. Can somebody assist me? My code is based on the docs from Google.
Background.js
chrome.runtime.onMessage.addListener(function (sender, sendResponse) {
var resp = {'navURL': "Not set yet"};
if (sender.greeting === "GetURL") {
sendResponse(resp);
}
});
popup.js
function getURL() {
chrome.runtime.sendMessage({
greeting: "GetURL"
},
function (response) {
// RESPONSE IS UNDEFINED //
console.log(response);
alert(response.navURL);
});
}
$("input[type='checkbox']").on('change', function () {
getURL();
});
Manifest.json
{
"name": "FaceBlock",
"description": "This extention gets rid of unwanted content on Facebook like sponsored posts, adds or annoying suggestions. The content you wish to see is enlarged for a better and richer social experience.",
"version": "0.0.1",
"manifest_version": 2,
"content_scripts": [
{
"matches": [
"http://*.facebook.com/*", "https://*.facebook.com/*"],
"css": ["css/popup.css"],
"js": ["js/jquery.min.js", "js/content.js"],
"run_at": "document_end",
"all_frames": true
}],
"options_page": "options.html",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html",
"default_title": "Faceblock"
},
"permissions": [
"activeTab",
"tabs",
"https://www.facebook.com/*",
"https://ajax.googleapis.com/",
"storage"
],
"background": {
"scripts": ["js/background.js"],
"persistent": false
},
"options_ui": {
// Required.
"page": "popup.html",
// Recommended.
"chrome_style": true
// Not recommended; only provided for backwards compatibility,
// and will be unsupported in a future version of Chrome (TBD).
//"open_in_tab": true
},
"web_accessible_resources": [
"images/faceblock.jpg",
"images/seigaiha.png",
"js/popup.js",
"icon.png",
"js/options.js",
"css/popup.css",
"popup.html",
"options.html"
]
}
Thanks in advance,
Niels Vermeiren
EDIT: I now have the following with the same problem
Background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
sendResponse({msg: "Element " + sender.element + " zijn zichtbaarheid wordt nu: " + sender.checked});
return true;
});
Popup.js
function getCurrentTabUrl(callback) {
return new Promise(function (resolve, reject) {
// https://developer.chrome.com/extensions/tabs#method-query
var queryInfo = {
active: true,
currentWindow: true
};
chrome.tabs.query(queryInfo, function (tabs) {
if (callback(tabs[0].url)) {
return resolve();
} else {
return reject();
}
});
});
}
function changeFacebook(data) {
console.log(data);
chrome.runtime.sendMessage(
"changeFacebook",
data,
function (response //undefined) {
console.log(response.msg); // UNDEFINED
});
}
document.addEventListener('DOMContentLoaded', function () {
var callback = function getCurrentTab(tab) {
if (tab == "https://www.facebook.com/") {
return true;
} else {
return false;
}
}
getCurrentTabUrl(callback).then(function () {
alert('on facebook');
$('.section').hide();
}, function () {
alert('not on facebook');
});
$("input[type='checkbox']").on('change', function () {
var id = $(this).attr('id');
var check = this.checked;
var data = {
'element': id,
'checked': check
};
changeFacebook(data);
});
$('.menuItem').hide();
$('.menuItem:first').show();
jQuery('.navitem').on('click', function () {
var value = $(this).html().toLocaleLowerCase();
jQuery('.menuItem').hide();
jQuery('#' + value).show();
});
});
According to doc here:
https://developer.chrome.com/extensions/runtime#event-onMessage
In your background.js, it should be
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
...
});
Since you are missing the last argument, what you really have is this:
chrome.runtime.onMessage.addListener(function (request, sender) {
...
});
So your code pretty much renamed request => sender, sender => sendResponse.
Therefore, you are trying to invoke sender as a function. But the actual sendResponse callback function is undefined. If you inspect your background page (see tips for how), you should see the error.
Tips:
When developing chrome extension, you can Inspect views: background page in the chrome://extensions page if you have any background page running.
When your popup is opened, you can inspect the popup page just you normally do.
And you can throw debuggers wherever you want, and you will be able to play around with it.
Edit:
So I have tested your code, the issue is just missing argument as I stated above. I also noticed the doc says the first two arguments are optional (not sure what the fuzz that is). But if you change your code to as below, it will work.
Background.js
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
var resp = {'navURL': "Not set yet"};
if (request.greeting === "GetURL") {
sendResponse(resp);
}
});
Popup.js
function getURL() {
chrome.runtime.sendMessage({
greeting: "GetURL"
},
function (response) {
console.log(response);
alert(response.navURL);
});
}
To test it
Go to the background page inspect, and paste in the background page snippet.
Go to the popup page inspect (open your popup, and right click within the popup, then choose inspect)
Paste in the popup page snippet
In the popup page inspect, call getURL()