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);
}
}
});