Can `content_scripts` detect if a `command` is set? - google-chrome

My extension currently uses the [esc] key to perform an action.
This is done in the content-script, using addEventListener('keydown').
I'd like the user to be able to choose a different shortcut.
I've updated to Manifest V3, and added a new "commands" key, i.e.
{
...
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"content_scripts": [ {
"js": [ "screenshot.js" ],
"matches": [ "http://*/*", "https://*/*" ]
}],
"commands": {
"stop-animations": {
"description": "Stop Animations"
}
}
}
I've updated background.js to use chrome.commands.onCommand, and this works well.
But, how can I get my content-script to determine if the user has set their own shortcut?
Because I should not listen to the 'keydown' event to check if they have pressed the [esc] key.
I've tried using chrome.commands.getAll(), but this method is only available at install time.
A partial option would be to use something like browser.storage to set a simple boolean "custom-command-set" to true whenever onCommand is used, but I cannot think of a way to switch it off again if the user deletes the shortcut command (i.e. they want to go back to using the [esc] key).
At the moment I don't have a toolbar icon (action default_popup), or any other UI, and I'd prefer to not add one just to provide a custom way to set the shortcut.

Content scripts can't call chrome.commands.getAll() directly, so they need to send a message to the service worker. The SW calls chrome.commands.getAll() and sends the result back to the content script.
manifest.json
{
"manifest_version": 3,
"name": "chrome.commands.getAll",
"version": "1.0.0",
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"js": [ "content_script.js" ],
"matches": [ "*://*/*" ]
}
],
"commands": {
"test": {
"description": "Test"
}
},
"permissions": ["commands"]
}
background.js
function commands_on_command(command) {
console.log("on_command", command);
}
function runtime_on_message(message, sender, sendResponse) {
if (message == "get_all_commands") {
chrome.commands.getAll()
.then(commands => {
console.log("background.js", "runtime_on_message", commands);
sendResponse(commands);
});
return true; // https://developer.chrome.com/docs/extensions/mv3/messaging/#simple
}
else {
console.log("background.js", "runtime_on_message", "unknown command");
sendResponse("unknown command");
}
}
chrome.commands.onCommand.addListener(commands_on_command);
chrome.runtime.onMessage.addListener(runtime_on_message);
content_script.js
(async () => {
let response = await chrome.runtime.sendMessage("get_all_commands");
console.log("content_script.js", response);
})();

Related

How to executeScript for webRequest event onBeforeRequest in Google Chrome Extension

Following Chrome Extension Manifest V3 rule I want to create an extension, that listens to particular network request and, for startes, just log them to the console of the currently opened tab (later I want to add custom script and styles to the page in the current tab).
For this I try to utilize chrome.scripting.executeScript.
When I implement the sample from https://github.com/GoogleChrome/chrome-extensions-samples/blob/main/examples/page-redder/manifest.json it works like expected for the chrome.action.onClicked listener.
As soon as I try to execute a script within the chrome.webRequest.onBeforeRequest listener, this error pops up:
Error in event handler: TypeError: Error in invocation of
scripting.executeScript(scripting.ScriptInjection injection, optional
function callback): Error at parameter 'injection': Error at property
'target': Missing required property 'tabId'.
at chrome.webRequest.onBeforeRequest.addListener.urls ()
Missing required property tabId? I assume it has to do with the lifecycle, but I cannot figure out what to do. This is my manifest:
{
"name": "Getting Started Example",
"description": "Build an Extension!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js",
"matches": [ "<all_urls>"]
},
"host_permissions": [
"<all_urls>"
],
"permissions": [
"activeTab",
"tabs",
"webRequest",
"webNavigation",
"management",
"scripting"
]
}
And this is my script, I just slightly modified the "redden"-example:
function reddenPage(url) {
console.log(url);
}
chrome.webRequest.onBeforeRequest.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: reddenPage,
args: [tab.url],
});
},
{urls: ["*://*.google.com/*"]},
[]);
I don't know exactly why, but the script from Github seems not work. This is how it works:
It's not only a couple of changed brackets, look at tab instead of (tab), but also tab.tabId instead of tab.id:
chrome.webRequest.onBeforeRequest.addListener(tab => {
chrome.scripting.executeScript(
{
target: { tabId: tab.tabId },
function: reddenPage,
args: [details.url],
},
() => { console.log('ZZZ') });
}, {
urls: ['<all_urls>']
});

chrome extension: Getting the source HTML of the current page on page load

I found this answer on how to grab the page source of current tab:
Getting the source HTML of the current page from chrome extension
However, this answer requires user to press the extension popup.
I would like to know how I can access the page source upon loading of page (without having to invoke the popup).
In my background.js I've tired this:
chrome.tabs.onUpdated.addListener(function (tabId , info) {
console.log(info)
chrome.tabs.executeScript(null, {
file: "getPagesSource.js"
}, function() {
if (chrome.runtime.lastError) {
message.innerText = 'There was an error injecting script : \n' + chrome.runtime.lastError.message;
}
});
});
but this results in the following error:
There was an error injecting script : Cannot access contents of the
page. Extension manifest must request permission to access the
respective host.
My manifest.js:
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"permissions": ["declarativeContent",
"https://www.example.com/*",
"storage",
"activeTab"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": ["content.js"]
}
],
"options_page": "options.html",
"manifest_version": 2,
"page_action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/get_started16.png",
"32": "images/get_started32.png",
"48": "images/get_started48.png",
"128": "images/get_started128.png"
}
},
"icons": {
"16": "images/get_started16.png",
"32": "images/get_started32.png",
"48": "images/get_started48.png",
"128": "images/get_started128.png"
}
}
I don't think the issue is really with permissions because I am able to get the page source from the popup.html (which is page_action script). But I am not able to get it via "background" or "content_scripts". Why is that and what is the proper way to accomplish this?
It is about the permissions.
Your example a bit insufficient, but as I can see you are using "activeTab" permission.
According to the activeTab docs, the extension will get access (e.g. sources) to current tab after any of these actions will be performed:
Executing a browser action
Executing a page action
Executing a context menu item
Executing a keyboard shortcut from the commands API
Accepting a suggestion from the omnibox API
That's why you can get sources after opening the popup.
In order to get access to tabs without those actions, you need to ask for the following permissions:
tabs
<all_urls>
Be noted, it allows you to run content-script on every tab, not only the active one.
Here is the simplest example:
manifest.json
{
"name": "Getting Started Example",
"version": "1.0",
"description": "Build an Extension!",
"permissions": ["tabs", "<all_urls>"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"manifest_version": 2
}
background.js
chrome.tabs.onUpdated.addListener(function (tabId, info) {
if(info.status === 'complete') {
chrome.tabs.executeScript({
code: "document.documentElement.innerHTML" // or 'file: "getPagesSource.js"'
}, function(result) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError.message);
} else {
console.log(result)
}
});
}
});

Send message from webpage to extension

I want send message from webpage to chrome extension, but it not working. I see background function not receiving message send from content.js
Here:
manifes.json:
{
"name": "My Checker",
"version": "1.0",
"description": "http://www.abc.dev Extension!",
"manifest_version": 2,
"permissions": [
"*://*.google.com/",
],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["*://*.abc.dev/*"],
"js": ["jquery-3.3.1.min.js", "content.js"],
"run_at": "document_end"
}
],
"externally_connectable": {
"matches": ["*://*.abc.dev/*"]
},
"browser_action": {
"default_popup": "popup.html"
}
}
And background.js:
console.log("background page in loading...");
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
console.log("listener for request");
console.log(request);
if (request.openUrlInEditor)
console.log("Open :" + request.openUrlInEditor);
sendResponse({success : "success"});
}
);
And content.js:
chrome.runtime.sendMessage("acldjllcapdfejdafbkjnfmpahdkendo", {openUrlInEditor: "https://www.google.com"}, function(response) {
if (response && !response.success)
handleError(url);
}
);
Cross-extension messaging.
you can use the messaging API to communicate with other extensions. This lets you expose a public API that other extensions can take advantage of.

Chrome Extension Notifications Not Appearing

I've been trying to develop a chrome extension that creates notifications after certain periods of time. I've been using both chrome.alarms and chrome.notifications to accomplish this, but I cannot get notifications to appear, even when creating them from the console. I also don't get any errors from runtime.lastError in the callback. I do know for certain that the alarm listener and callback are being executed.
manifest.json
{
"manifest_version": 2,
"name": "notifications",
"version": "0.0",
"browser_action": {
"default_popup": "popup.html"
},
"background": {
"scripts": ["notifications.js"],
"persistent": false
},
"permissions": [
"alarms",
"storage",
"notifications"
],
"web_accessible_resources": [
"mascot48.png"
]
}
notifications.js
function resetTimer(id, minutes) {
chrome.alarms.create(id,{delayInMinutes: minutes});
}
chrome.alarms.onAlarm.addListener((alarm) => {
console.log('alarm!');
chrome.notifications.create('reminder', {
type: 'basic',
iconUrl: 'mascot48.png',
title: 'Dont forget!',
message: 'You have things to do'
}, function(notificationId) {
console.log(chrome.runtime.lastError);
});
});
Is there anything clear that I'm getting wrong?
EDIT: For clarity, notifications.js is my events page, the popup page is the one that runs the code to start the alarms.

Chrome WebNavigation Listener not working

I'm creating my own extension for Google Chrome (for my own use, not to be published). At the moment I have two files:
manifest.json:
{
"manifest_version": 2,
"name": "abcdef",
"description": "abcdef",
"version": "0.1",
"permissions": [
"tabs",
"webNavigation",
"http://www.ztm.waw.pl/*"
],
"background": {
"scripts": ["bg.js"],
"persistent": false
}
}
bg.js:
chrome.webNavigation.onCompleted.addListener(function(o) {
chrome.tabs.executeScript(o.tabId, {
code: "alert('ok');"
});
}, {
url: {
hostContains: 'ztm.waw.pl'
}
});
I want the alert box to appear when I navigate to http://www.ztm.waw.pl, but it does not work. Could someone tell me why?
The url property of chrome.webNavigation.onCompleted accepts an array of chrome.events.UrlFilter (source), so you'll need to change your bg.js to this (note the square and curly brackets in the url property):
chrome.webNavigation.onCompleted.addListener(function(o) {
chrome.tabs.executeScript(o.tabId, {
code: "alert('ok');"
});
}, {
url: [
{hostContains: 'ztm.waw.pl'}
]
});