I'm trying to set a periodical alarm to load data on background from time to time. When the extension is installed, the alarm is triggered after the delay correctly, no problem here. But then it goes away and no other repetition is done. I also tried to set persistent=true which prevents it from going to the inactive mode but still no difference in behavior.
manifest.json
{
...
"background": {
"scripts": ["jquery-2.2.3.min.js", "common.js", "background.js"]
"persistent": false/true // tried both
},
"permissions": [
"<all_urls>",
"alarms",
"storage"
]
}
bgscript.js
chrome.runtime.onInstalled.addListener(function () {
chrome.alarms.create({delayInMinutes: 1});
});
chrome.alarms.onAlarm.addListener(function () {
doStuff();
});
As per the description of chrome.alarms.create, you should also set periodInMinutes for repeating.
chrome.runtime.onInstalled.addListener(function () {
chrome.alarms.create({delayInMinutes: 1, periodInMinutes: 1});
});
Related
So I have this problem where I need to open a none active tab and once in a while to set it's URL, everything is working well until an undefined period of time passes, then it seems like the alarm that responsible to set the URL dies along with all of the background script data (variables are wiped), in my manifest I set permission of "background" but it didn't help, I also tried using setInterval but it didn't help much, here's some code for you:
async function setGetJobAlarm() {
// try {
// chrome.alarms.clear("getAndExecuteJobs");
// } catch { }
// chrome.alarms.create("getAndExecuteJobs", { periodInMinutes: 0.3 });
// chrome.alarms.onAlarm.addListener(async (alarm) => {
// if (alarm.name == "getAndExecuteJobs") {
// try {
// await getAndExecuteJobs();
// }
// catch (err) {
// console.log(err);
// }
// }
// });
if (getAndExecuteJobs > 0) {
clearInterval(getAndExecuteJobsInterval);
}
getAndExecuteJobsInterval = setInterval(async () => {
try {
await getAndExecuteJobs();
}
catch (err) {
console.log(err);
}
}, 30000);
}
Manifest:
{
"name": "aaaaa",
"version": "0.0.1",
"manifest_version": 3,
"background": {
"service_worker": "bgjob.js"
},
"permissions": [
"tabs",
"alarms",
"activeTab",
"background"
//"identity",
//"identity.email"
],
"host_permissions": [
"http://*/",
"https://*/"
],
"icons": {
"16": "aaaaa.png",
"48": "aaaa.png",
"128": "aaaa.png"
},
"action": {
"default_popup": "/popout/pop.html",
"default_title": "aaaaa"
},
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"jquery-3.6.0.slim.min.js"
]
}
]
}
I'm not able to figure out what is missing, googled a lot but no use,
Second problem is that I'm trying to load a simple extension's html file named "hello.html", the html get's opened but I get this error:
Cannot access contents of URL"chrome-extension://locblcbeeombbgmpiofcnmhfimfpjipb/hello.html". Extension manifest must request permission to access this host.
I tried to add "chrome-extension://*/" but didn't work, thanks!
The background script automatically terminates after 30 seconds so setTimeout/setInterval with a delay like that or longer will never run.
Remove setTimeout/setInterval and use chrome.alarms API with a periodInMinutes at least 1 because this is the minimum interval allowed for published extensions in the web store.
If your workflow really needs intervals below 1 minute you'll have to prolong the service worker's life artificially, see the second part of this answer.
I have written a chrome extension that successfully records all of a users scrolling data (timestamp with pixel amount). This data is currently logging in the console.
My next step however is causing me a bit of trouble.
I want to then send every log of data that is created to an external webpage to be displayed. ie, the page will auto update and archive every new timestamped entry/pixel counter the extension records.
I've been able to communicate between the extension and webpage, by passing a simple message through a DOM event. But i'm having trouble getting the extension to send the scroll data it's collecting.
I feel like i'm close, and i've read Google's Messaging API's but I feel a bit lost.
Here's my code so far:
Manifest.json
{
"manifest_version": 2,
"name": "Caressing the Silver Rectangle",
"description": "Measures Jesse Bowling's distance scrolled in pixels on Google Chrome",
"version": "1.1",
"content_scripts": [
{
"matches": [
"<all_urls>"
],
"js": [
"scroller.js"
],
"run_at": "document_start"
}
],
"background": {
"scripts": ["background.js"]
},
"externally_connectable": {
"matches": ["http://*/*jessebowling.space/caressingthesilverrectangle/*"]
},
"browser_action": {
"default_icon":"icon.png",
"default_title": "Caressing the Silver Rectangle",
"default_popup": "popup.html"
},
"permissions": [
"activeTab",
"<all_urls>",
"background",
"tabs",
"storage"
]
}
Background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log("background.js got a message")
console.log(request);
console.log(sender);
sendResponse("bar");
}
);
Scroller.js
/*jslint devel: true */
// The ID of the extension we want to talk to.
var caressingthesilverrectangleId =
"http://*/*jessebowling.space/caressingthesilverrectangle/*"
var totalScroll;
var lastKnownScrollPos = 0;
window.addEventListener("scroll", function () {
"use strict";
console.log(lastKnownScrollPos);
totalScroll += Math.abs(window.scrollY - lastKnownScrollPos);
lastKnownScrollPos = window.scrollY;
chrome.storage.sync({ scroll: totalScroll });
function (response) {
console.log(response);
}
});
// send data through a DOM event
document.dispatchEvent(new CustomEvent('csEvent', {detail:
totalScroll}));
// Make a simple request:
chrome.runtime.sendMessage(caressingthesilverrectangleId,
{getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
chrome.runtime.sendMessage(caressingthesilverrectangleId,
{activateLasers: true});
});
// Start a long-running conversation:
var port = chrome.runtime.connect(caressingthesilverrectangleId);
port.postMessage(chrome.storage.sync({ scroll: totalScroll }));
I'm guessing the part where you do
var port = chrome.runtime.connect(caressingthesilverrectangleId);
port.postMessage(chrome.storage.sync({
scroll: totalScroll
}));
is where you are trying to send the message. However the message you are sending is the result of chrome.storage.sync, which is always undefined. You might want to split these two calls up so you actually send some data like:
var port = chrome.runtime.connect(caressingthesilverrectangleId);
chrome.storage.sync({
scroll: totalScroll
});
port.postMessage({
scroll: totalScroll
});
I'm trying to access some DOM elements from a webpage:
<html>
<button id="mybutton">click me</button>
</html>
I want to access the innerHTML ("click me") through a chrome extension:
chrome.browserAction.onClicked.addListener(function(tab) {
var button = document.getElementById("mybutton");
if(button == null){
alert("null!");
}
else{
alert("found!");
}
});
When I click the extension, the popup says: "null".
My manifest.json:
{
"name": "HackExtension",
"description": "Hack all the things",
"version": "2.0",
"permissions": [
"tabs", "http://*/*"
],
"background": {
"scripts": ["contentscript.js"],
"persistent": false
},
"browser_action": {
"scripts": ["contentscript.js"],
"persistent": false
},
"manifest_version": 2
}
The solution:
You need a manifest file, a background script and a content script. This is not really clear in the documentation that you have to use it and also, how to use it. For alerting the full dom, see here. Because I have a hard time finding a complete solution that actually works and not just snippets that are useless for newbies, like me, I included a specific solution:
manifest.json
{
"manifest_version": 2,
"name": "Test Extension",
"version": "0.0",
"background": {
"persistent": false,
"scripts": ["background.js"]
},
"content_scripts": [{
"matches": ["file:///*"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Test Extension"
},
"permissions": ["activeTab"]
}
content.js
/* Listen for messages */
chrome.runtime.onMessage.addListener(function(msg, sender, sendResponse) {
/* If the received message has the expected format... */
if (msg.text && (msg.text == "report_back")) {
/* Call the specified callback, passing
the web-pages DOM content as argument */
sendResponse(document.getElementById("mybutton").innerHTML);
}
});
background.js
/* Regex-pattern to check URLs against.
It matches URLs like: http[s]://[...]stackoverflow.com[...] */
var urlRegex = /^file:\/\/\/:?/;
/* A function creator for callbacks */
function doStuffWithDOM(element) {
alert("I received the following DOM content:\n" + element);
}
/* When the browser-action button is clicked... */
chrome.browserAction.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.tabs.sendMessage(tab.id, { text: "report_back" },
doStuffWithDOM);
}
});
index.html
<html>
<button id="mybutton">click me</button>
</html>
Just save the index.html somewhere and load in the folder as an extension, containing the three other files. Open the index.html and push the extension button. It should show "click me".
Starting with Manifest V3, your content scripts won't be able to access anything generated by other loaded scripts and using a trick like inlining a your code inside <script> tag won't work due to stricter CSP rules. This caused me a lot of head ache since I couldn't figure out how to access library-generated DOM properties similar to React or Redux DevTools.
Instead, you have to now inject your script inside the service_worker with eg:
chrome.scripting.registerContentScripts([
{
id: 'inject',
matches: ['<all_urls>'],
js: ['inject.js'],
runAt: 'document_end',
world: 'MAIN'
}
])
Notice the 'MAIN' property, not the default 'ISOLATED'. Then inside my inject.js I do whatever, eg:
window.addEventListener('load', () => {
findReact()
})
Also you have to add the script to the manifest.json:
"web_accessible_resources": [
{
"resources": ["inject.js"],
"matches": ["<all_urls>"],
"extension_ids": []
}
],
"externally_connectable": {
"ids": ["*"]
},
Not sure is "externally_connectable" needed. And you need to add at least "scripting" permissions. I used the React DevTools migration as my source https://github.com/facebook/react/pull/25145
How to get access to variable app from content script app.js in background script background.js?
Here is how I try it (background.js):
chrome.tabs.executeScript(null, { file: "app.js" }, function() {
app.getSettings('authorizeInProgress'); //...
});
Here is what I get:
Here is manifest.json:
{
"name": "ctrl-vk",
"version": "0.1.3",
"manifest_version": 2,
"description": "Chrome extension for ctrl+v insertion of images to vk.com",
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
"run_at": "document_end"
}],
"web_accessible_resources": [
"jquery-1.9.1.min.js"
],
"permissions" : [
"tabs",
"http://*/*",
"https://*/*"
],
"background": {
"persistent": false,
"scripts": ["background.js"]
}
}
Full code for instance, at github
https://github.com/MaxLord/ctrl-vk/tree/with_bug
To avoid above error use following code
if (tab.url.indexOf("chrome-devtools://") == -1) {
chrome.tabs.executeScript(tabId, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
}
instead of
chrome.tabs.executeScript(null, {
file: "app.js"
}, function () {
if (app.getSettings('authorizeInProgress')) {
alert('my tab');
REDIRECT_URI = app.getSettings('REDIRECT_URI');
if (tab.url.indexOf(REDIRECT_URI + "#access_token") >= 0) {
app.setSettings('authorize_in_progress', false);
chrome.tabs.remove(tabId);
return app.finishAuthorize(tab.url);
}
} else {
alert('not my');
}
});
Explanation
chrome://extensions/ page also fires chrome.tabs.onUpdated event, to avoid it we have to add a filter to skip all dev-tool pages.
(Would've submitted this as comment to the accepted answer but still lack the required reputation)
You should also give the tabId to chrome.tabs.executeScript as first argument when you have it. Otherwise you risk user switching windows/tabs right after requesting a URL and background.js doing executeScript against wrong page.
While fairly obvious on hindsight it threw me for a loop when I got that same error message "Cannot access contents of url "chrome-devtools://.." even though my chrome.tabs.onUpdated eventhandler was checking that the page user requested had some specific domain name just before doing the executeScript call.
So keep in mind, chrome.tabs.executeScript(null,..) runs the script in active window, even if the active window might be developer tools inspector.
We should notice that, in the manifest cofigļ¼
"content_scripts": [{
"matches": [
"http://*/*",
"https://*/*"
],
"js": ["jquery-1.9.1.min.js"
],
in the "matches" part, only http, https are matched, so if you load your extension in page like: 'chrome://extensions/', or 'file:///D:xxx', that error will occur.
You may load your extension in the page with the url 'http://'; or add more rules in your 'matches' array.
I am writing an extension for the Chrome browser where I want to add an event listener for the window resize event. My method is being executed for the window load event, but not being executed for the resize event.
Below is the code for my manifest.json file
{
"name": "A browser action",
"version": "1.0",
"background": { "scripts": ["background.js"] },
"permissions": [
"tabs", "http://*/*"
],
"manifest_version": 2
}
Below is the code for my background.js file.
var myExtension =
{
init: function()
{
// The event can be DOMContentLoaded, pageshow, pagehide, load or unload.
alert("ASHSIH");
window.addEventListener("resize", this.onmyPageResize, false);
},
onmyPageResize: function(aEvent)
{
alert("RESIZED");
}
}
window.addEventListener("load", function load(event){
window.removeEventListener("load", load, false); //remove listener, no longer needed
myExtension.init();
},false);
Chrome-o-Tile is one example of an extension which listens resize in its content script.
In manifest.json:
"content_scripts": [
{
"js": ["contentscript.js"],
"run_at": "document_start",
"matches": [
"<all_urls>"
]
}
],
In contentscript.js:
'use strict';
var timeoutId = 0;
window.addEventListener('resize', function() {
if (timeoutId) {
clearTimeout(timeoutId);
}
timeoutId = setTimeout(function() {
chrome.runtime.sendMessage({method: 'resize'});
timeoutId = 0;
}, 100);
}, false);
In background.js:
chrome.runtime.onMessage.addListener(function requested(request) {
if (request.method === 'resize') {
...
}
)};
There is also an open issue to implement chrome.windows.onResize event for Chrome extensions.
The background.js file cannot capture resize events in the browser. You would need to inject a content script for that.
For future readers of this question, the chrome.windows API provides an onBoundsChanged event:
onBoundsChanged (Since Chrome 86)
Fired when a window has been resized; this event is only dispatched when the new bounds are committed, and not for in-progress changes.
https://developer.chrome.com/extensions/windows#event-onBoundsChanged
This is how the event can be used: chrome.windows.onBoundsChanged.addListener( callback() )
(Make sure the manifest.json file declares the tabs permission)
{
"name": "My extension",
...
"permissions": ["tabs"],
...
}
You can use jQuery's bind() function.. http://api.jquery.com/bind/
$(window).bind('resize', function () {
//do something here
});