Do not display popup when clicking Chrome Extension browser action button - javascript

I want my Chrome extension to behave such that when a user clicks on the extension button, it sends a message and then closes the popup. I have nothing I want to display in the popup and ideally don't want it to even appear in the first place.
Half the time, my code works. The other half of the time, that little empty white bubble remains even after the message has been sent. Why is this non-determinism occurring? Is there a way to simply send a message on clicking the extension button and bypassing the pointless popup appearing?
Here is my popup.js:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
// Go immediately to background script.
// This popup's thread of execution will get killed as soon as we move to another tab.
chrome.extension.sendRequest({
tab: tabs[0],
message: "Button was clicked"
});
window.close();
});
which is included in the following popup.html:
<head>
<script src="popup.js"></script>
</head>
<body>
</body>
My manifest.json (with irrelevant fields removed) is:
{
"name": "My Extension",
"description": "This is my extension",
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"]
}
],
"background": { "scripts": ["background.js"] },
"browser_action": {
"default_popup": "popup.html"
},
"permissions": [
"http://*/*",
"https://*/*",
"tabs"
],
}

You just declared default_popup in manifest.json, that means once browser action is clicked, the popup.html will show up. If you don't want that, just remove that field and listen to chrome.browserAction.onClicked in background.js.
manifest.json
{
...
"browser_action": {},
...
}
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.runtime.sendMessage({tab: tab, message: "Button was clicked"});
});

Related

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'query') get current tab url for v3?

I'm referencing the instruction on https://developer.chrome.com/docs/extensions/reference/tabs/#get-the-current-tab.
I know there are many questions on this but I want to get the url of the current tab. The problem is I'm getting an error I'm struggling to understand.
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'query')
I've tried moving the snippet of code to the background.js, pop.js, and the content.js files.
background.js
async function getTabUrl() {
const [tab] = await chrome.tabs.query({ active: true, lastFocusedWindow: true });
console.log(tab);
return tab[0].url;
}
getTabUrl().then(tab => console.log(tab))
manifest.json
{
"manifest_version": 3,
"name": "...",
"description": "...",
"version": "1",
"action": {
"default_popup": "popup.html",
"default_icon": "icons/favicon.ico"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"js": [
"scripts/content.js"
],
"matches": [
...
]
}
],
"permissions": [
"activeTab"
]
}
scripts/content.js is blank.
Cannot read properties of undefined (reading 'query')
This error says that the parent of query is undefined i.e. chrome.tabs is undefined. It happens if you run this code in the wrong place e.g. in a content script or in a web page console.
Note that chrome://extensions will show old errors even after reloading the extension, so you can clear them by clicking the trashcan icon or the Clear all button.
I've tried moving the snippet of code to the background.js, pop.js, and the content.js files.
Each part of the extension has its own method of getting the active URL:
In the content script it's just location.href, no need for any permissions.
In the popup it's your getTabUrl, however since the popup is a separate window it has its own devtools and console: right-click inside the popup and select "inspect" in the menu to see it.
In the background script you would normally use the tabId or tab property of the event in your chrome API listener like chrome.tabs.onUpdated. Note that just calling getTabUrl() in the background script is pretty meaningless: it runs at browser startup, possibly before any tabs are created, or when you reload the extension so the active tab is chrome://extensions.
Note that activeTab is not about granting the permission to any active tab, but only to the active tab in which the user interacted with your extension explicitly, e.g. clicked the extension icon.
This sample gets the URL of the active tab in popup.
popup.js
const elmUrl = document.getElementById("url");
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
const url = tabs[0].url;
console.log("url=", url);
elmUrl.innerText = url;
});
manifest.json
{
"name": "Get URL of Active tab in popup",
"manifest_version": 3,
"version": "1.0",
"permissions": [
"activeTab"
],
"action": {
"default_popup": "popup.html"
}
}
popup.html
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
* {
font-size: x-large;
}
</style>
</head>
<body style="min-width:300px;min-height:100px;">
<span id="url"></span>
<script src="popup.js"></script>
</body>
</html>

The difference between event page and background page

After I read the documentation about Event Page I didn't got the advantage of using Event Page instead of Background Page .
Suppose I have the follow simple case -
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
content.js
chrome.runtime.sendMessage("Hi Background")
background.js
chrome.runtime.onMessage.addListener(messageListener);
function messageListener (request, sender, sendResponse) {
alert(request);
}
In this case , whether persistent is "persistent": false or "persistent": true the listener in the background.js always should be awake in order to get the messages from the content.js and therefore the the background.js couldn't go to suspend mode .
So what is the benefit of the Event Page ("persistent": true) in such cases and in general ? Please provide an example.
The main advantage of the event pages is to release RAM and CPU resources by unloading background script when it is not used.
...background.js couldn't go to suspend mode.
It can. Even if your event page uses message listener it still will be unloaded after some time. Chrome remembers that the page has set the listener, so the browser will awake the page when a message will be sent.
You can try this experiment:
add this extension
manifest.json
{
"manifest_version": 2,
"name": "Test",
"version": "0.0.1",
"description": "",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html"
}
}
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
sendResponse({
msg: "it's alive!"
});
}
);
popup.html
<html>
<body>
<div id="text"></div>
<script src="popup.js"></script>
</body>
</html>
popup.js
chrome.runtime.sendMessage('hello',
function (response) {
document.getElementById('text').textContent = response.msg;
}
);
Open Chrome's Task manager and wait few seconds until Test extension disappears (unloads). If you do not see it (it already unloaded), you can reload extension.
Click to the extension's browser action and you will see message inside the window which came from the event page. Also, you can see two processes in the task manager for the Test extension: one is a popup and the second is the event page.
After popup will be closed, the event page unloads again in a few seconds.

How to have background script and something similar to a default popup?

So, I understand that you cannot have background scripts and a default popup together. If this is so, how can I have something similar to a default popup (where there is some simple HTML that appears when you clicked the extension's icon) and have the background script modify the contents of that popup?
Here's the manifest.json
"browser_action": {
"default_title": "Mark this position!",
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["content.js"]
}
],
You absolutely can have both a popup (i.e. default_popup set) and a background page. The background page will have its own lifecycle (with "persistent": false it's an Event page) and the popup will exist as long as it's open.
I guess your confusion stems from the fact that you cannot have a popup and a chrome.browserAction.onClicked listener at the same time.
This is true, but there are other ways to tell your background page that the popup has opened.
You can message the background page from the popup:
// popup.js, should be included in the popup
chrome.runtime.sendMessage({popupOpen: true}, function(response) {
/* process response */
});
// background.js
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
if(message.popupOpen) {
/* do stuff */
sendResponse(response);
}
});
If you put the above popup code on the top level, then the background will be informed as soon as the popup opens.
While you can directly access the popup's window from the background (see chrome.extension.getViews), it's recommended that you move the UI logic into the popup itself and communicate with the background using Messaging as above and shared chrome.storage.

Chrome Extension get UI element from background script

I am starting to develop a chrome extension.
In my case I have the following manifest.json:
{
"name": "mY Notifier",
"version": "1.0",
"manifest_version": 2,
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"background": {
"scripts": [
"myscript.js"
],
"persistent": false
},
"permissions": [
"tabs","alarms"
]
}
So, as you can see i have a default popup html page and a background script.
I need a Background script because I need to update the icon badge from time to time when the Extension is enabled (not just if the user clicks on the icon). This works fine.
In my popup.html i have a couple of checkboxes. Now in my background script i want to check which of the checkboxes are enabled. How can i do this? I cannot refer to my ui elements in popup.html using elementById. How can this be done?
You can't access to popup.html from myscript.js because popup.html don't loaded before the click on a button.
You can call functon of myscript.js from popup.html:
popup.js code:
var bg = chrome.extension.getBackgroundPage();
bg.someFunction(stateOfCkeckboxes);

Stop chrome from loading all tabs on startup in my extension?

I'm trying to stop all tabs from loading when chrome starts up. I then
want to load only the tab I click on.
I was able to do this by using a content script in
manifest.json.
{
"name": "blah",
"version": "1.0",
"background_page": "background.html",
"content_scripts": [
{
"matches": ["http:// */*"],
"js": ["stop.js"],
"run_at": "document_start"
}
] ,
"permissions": [
"tabs", "http://*/*"
]
}
stop.js just contains one line
window.stop();
Now this isn't ideal because, being a content script it stops loading
everytime, including when I click on a tab.
So, I tried doing in in background.html without a content script, but
I can't get it to work:
background.html
<!doctype html>
<html>
<script>
chrome.tabs.getAllInWindow(null, function stopTabs(tabs) {
for (var i in tabs) {
var tab = tabs[i];
//alert(tab.url);
var stopLoading = {'code': 'window.stop();alert("stopped");'}; //alerts work here, but window.stop doesn't?!!
chrome.tabs.executeScript(tab.id, stopLoading);
}
});
</script>
</html>
How do I do this? Loading the clicked tab seems easy, but I need to do this first.
Looks like this problem is related to this bug report, if you star it maybe it will get fixed sooner.

Categories