Enable only one settings page open in chome extension - google-chrome-extension

I chrome extention in my popup.html I have a "settings" button, for now when I click this button it opens the settings page in a new tab, and it's possible to open multiple pages, my question is how to prevent multiple settings instances, i.e I want to enable only one settings page open.
My code:
<button type="button">
<a id="settings-btn"> <i class="fa fa-cog fa-lg fa-fw" style="font-size:27px;"></i></a>
</button>
And in popup.js:
document.addEventListener('DOMContentLoaded', function () {
document.getElementById("settings-btn").addEventListener("click", openIndex);
})
function openIndex() {
chrome.tabs.create({
url: "Options.html"
});
}
Update:
#wOxxOm's answer worked after I added tabs permission in manifest file

There are two methods.
Using tabs permission and chrome.tabs.query to find the existing tab
manifest.json:
"permissions": ["tabs"]
popup.js
openOrActivate('Options.html');
function openOrActivate(url) {
if (!url.includes(':')) url = chrome.runtime.getURL(url);
chrome.tabs.query({url: url + '*'}, tabs => {
const [tab] = tabs;
if (!tab) {
chrome.tabs.create({url});
} else {
chrome.tabs.update(tab.id, {active: true});
chrome.windows.update(tab.windowId, {focused: true});
}
});
}
Send a message that will be received by an options page if it's open
In case of no response we'll open a new tab.
popup.js:
openOrActivate('Options.html');
function openOrActivate(path) {
if (!path.startsWith('/')) path = `/${path}`;
chrome.runtime.sendMessage(path, tab => {
if (chrome.runtime.lastError || !tab) {
chrome.tabs.create({ url: path });
} else {
chrome.tabs.update(tab.id, { active: true });
chrome.windows.update(tab.windowId, { focused: true });
}
});
}
Options.js (the script of Options.html):
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message === location.pathname) {
chrome.tabs.getCurrent(sendResponse);
return true;
}
});

Related

Chrome Extension: How to communicate with Content.js from a newly opened Window?

I have created a new window in chrome.action.onClicked.addListener as given below.
On clicking of "Check" button in newly opened window I need to connect to content.js and print some message in the console of window. I dont know where it is going wrong! I am using Manifest version 3.
content.js
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
if(msg.color === "#00FF00"){
document.body.style.backgroundColor = "green";
sendResponse({ status: "done" });
}
});
background.js
var urlRegex = /^(https?:\/\/)?[a-z0-9-]*\.?[a-z0-9-]+\.[a-z0-9-]+(\/[^<>]*)?$/;
chrome.action.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.windows.create({
tabId: tab.id,
type:"popup",
url:"popup.html",
focused:true
});
}
});
popup.html
<html>
<head>
<script defer src="popup.js"></script>
</head>
<body>
<h3>Test Extension Page</h3>
<input type="button" id="sendMessage" value="Check"/>
</body>
</html>
popup.js
let sendMessageButton = document.getElementById("sendMessage");
console.log(document.URL);
console.log(sendMessageButton.value);
function getTitle()
{
return document.title;
}
sendMessageButton.onclick = function() {
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs){
var tab = tabs[0];
chrome.scripting.executeScript(
{
target: {tabId:tab.id},
func: getTitle,
},
() => {
// This executes only after your content script executes
chrome.tabs.sendMessage(
tab.id,
{ color: "#00FF00" },
function (response) {
console.log(response.status);
}
);
});
});
};
Error in console of newly opened window.
Unchecked runtime.lastError: Cannot access contents of url "chrome-extension://jjaaoafdfmabdajdckiacompibnnmnlh/popup.html". Extension manifest must request permission to access this host.
Error handling response: TypeError: Cannot read properties of undefined (reading 'status') at chrome-extension://jjaaoafdfmabdajdckiacompibnnmnlh/popup.js:25:34
Unchecked runtime.lastError: Could not establish connection. Receiving end does not exist.
The problem is that the window you create becomes active and hence it becomes the result of chrome.tabs.query in your code, meaning that executeScript runs inside your own extension page, which can't work as this method is only for web sites.
The solution is to pass the tab id as URL parameter.
// background.js
chrome.action.onClicked.addListener(tab => {
chrome.windows.create({
type: 'popup',
url: 'popup.html?' + new URLSearchParams({
tabId: tab.id,
title: tab.title,
}),
});
});
// popup.js
const params = new URLSearchParams(location.search);
const tabId = +params.get('tabId');
let title = params.get('title'); // initial title
document.getElementById('sendMessage').onclick = async function () {
title = (await chrome.tabs.get(tabId)).title;
let res = await chrome.tabs.sendMessage(tabId, { color: "#00FF00" });
};

Chrome extension send message from onInstall in backgroundjs to own extension javascript code

I need to check when the extension is installed and change my React state accordingly.
I use chrome.runtime.onInstalled on my background.js where I sendMessage to my react code - which is the content script of my extension.
background.js
async function getCurrentTab() {
let queryOptions = { active: true, currentWindow: true };
let [tab] = await chrome.tabs.query(queryOptions);
return tab;
}
chrome.runtime.onInstalled.addListener((details) => {
if (details?.reason === 'install') {
console.log('installed backgroundjs')
const tab = await getCurrentTab()
chrome.tabs.sendMessage(tab.id, { target: 'onInstall' })
openNewTab()
}
})
In my react Component - Dashboard.js
useEffect(() => {
if (extensionApiObject?.runtime) {
chrome.runtime.sendMessage({ target: 'background', message: 'check_installation', })
console.log('extension')
chrome.runtime.onMessage.addListener(handleMessage)
}
return () => {
if (extensionApiObject?.runtime) {
chrome.runtime.onMessage.removeListener(handleMessage)
}
}
})
function handleMessage(msg) {
console.log('handle messag func', msg)
if (msg.target === 'onInstall') {
console.log('extension on Install')
setShowWelcomeMessage(true)
}
}
What confuse me is that I already have a similar implementation for a different message that works without problem but in there I listen to chrome.runtime.onMessage() not chrome.runtime.onInstalled()
I wonder if I misunderstand how onInstalled method work and I cannot sendMessage from it?
UPDATE:
I change my code as suggested by #wOxxOm, I used chrome.tabs.sendMessage but still no luck.
chrome.runtime.onInstalled doesn't take as argument req, sender, sendResponse like other listener and I wonder if that means it will not be able to send a message from there :/
After #wOxxOm suggestions, I ended up removing the sendMessage solution and simply add an extra parameter to the url whenever I open a new tab after installation:
background.js
function openNewTab(param) {
chrome.tabs.create({
url: param ? `chrome://newtab?${param}` : 'chrome://newtab',
})
}
chrome.runtime.onInstalled.addListener((details) => {
if (details?.reason === 'install') {
chrome.tabs.sendMessage(tab.id, { target: 'onInstall' })
openNewTab('installed')
}
})
on my web app I just need to check the param and I can decide which UI to show

Chrome Extenstion with Vue Invoking nested methods

thanks to all in advanced.
I'm trying to build a simple Chrome extension with vue using this Boilerplate,
but I find it hard to communicate with the content.js file and popup.js file and passing data between them.
currently I'm trying to pass the total number of p tags that are in the current tab.
when I try to invoke a method inside the sendMessage function I'm getting an Error saying this.count is not a function. I'v figured out that probably the function is not firing because it's nested, but I cant figure out how can invoke the method.
Cheers to all
popup.js
<template>
<div>
<p>{{title}}</p>
<button v-on:click="getCount">click Here</button>
<p>{{counts}}</p>
</div>
</template>
<script>
export default {
data() {
return {
title: "count board",
counts: ""
};
},
methods: {
getCount() {
chrome.tabs.query({ currentWindow: true, active: true }, tabs => {
chrome.tabs.sendMessage(tabs[0].id, "null", this.count);
});
}
},
count(res) {
console.log(res.count);
this.counts = res.count
}
};
</script>
Content.js
chrome.runtime.onMessage.addListener(function (req, sender, sendResponse) {
const para = document.querySelectorAll('p')
sendResponse({ count: para.length })
})
I'm trying to invoke this.count on every button click in popup.html and retrieving the total numbers of P tags

How To Send a Message From Popup.js to Content Script in Chrome Extension on Active Tab?

When I click on the toolbar icon from my chrome extension, it brings up the default popup.html along with popup.js. From that popup.js, I want to send a message to the current active tab. I can't figure out how to get the active tab.
My code in the popup.js looks like this:
window.onload = function() {
console.log(`popup.js:window.onload`);
const timeNow = document.getElementById("timeNowId");
timeNow.innerText = new Date().toLocaleTimeString();
var resultsButton = document.getElementById("buttonId");
resultsButton.onclick = () => {
chrome.runtime.sendMessage(
"FromPopupToBackgroundToContent:" + timeNow.innerText,
function(response) {
response === undefined
? alert("popup.js:return from sendMessage:undefined")
: alert("popup.js:return from sendMessage:", response);
}
);
};
};
The full, non-working example is in this github repo at this branch linked to.
https://github.com/pkellner/chrome-extension-message-popup-to-content/tree/feature-popup-to-content-directly-getcurrent
Example:
popup.js
function popup() {
chrome.tabs.query({currentWindow: true, active: true}, function (tabs){
var activeTab = tabs[0];
chrome.tabs.sendMessage(activeTab.id, {"message": "start"});
});
}
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("button1").addEventListener("click", popup);
});
content.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if( request.message === "start" ) {
start();
}
}
);
function start(){
alert("started");
}
popup.html
<!DOCTYPE html>
<html>
<head></head>
<script src="popup.js"></script>
<body>
<input id="button1" type=button value=clickme>
</body>
</html>

Chrome onMessage not working (?)

I've read about onMessage.addListener method in Chrome to pass some data from extensions to script. What I have now:
popup.js
window.onload = function(){
document.getElementById('searchButton').onclick = searchText;
};
function searchText(){
var search = document.getElementById('searchText').value; // f.ex "123"
if(search){
chrome.tabs.query({active:true,currentWindow:true},function(tabs){
chrome.tabs.executeScript(tabs[0].id,{file:search.js});
chrome.tabs.sendMessage(tabs[0].id,{method:'search',searchText:search});
});
}
}
search.js
chrome.runtime.onMessage.addListener(function(message,sender,sendResponse){
alert('text');
});
However, alert ('text') is never fired. What's the problem?
You should quote "search.js" and put the chrome.tabs.sendMessage call in the callback of chrome.tabs.executeScript:
function searchText(){
var search = document.getElementById('searchText').value; // f.ex "123"
if (search) {
chrome.tabs.query({active:true,currentWindow:true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {
file: 'search.js'
}, function() {
chrome.tabs.sendMessage(tabs[0].id, {
method: 'search',
searchText: search
});
});
});
}
}
If this suggestion does not help, inspect the popup and look for error messages.

Resources