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.
Related
I am trying to output all tabs in the current window by creating a firefox extension. It seems to be perfect to me, but still not able to get the output in the log. I am testing it as temporary addon from about:debugging.
I even tried to run the js code under "content_scripts", though it doesn't seem to be changing any content, but should run in the background. none of them works. Just want to know what I am missing
JavaScript
function logTabs(tabs) {
for (let tab of tabs) {
// tab.url requires the `tabs` permission
console.log(tab.url);
}
}
function onError(error) {
console.log(`Error: ${error}`);
}
var querying = browser.tabs.query({{currentWindow: true}});
querying.then(logTabs, onError);
Manifest
{
"manifest_version": 2,
"name": "Tablog",
"version": "1.0",
"description": "Prints the all tabs url in he console",
"icons": {
"48": "icons/icon48.png"
},
"background": {
"scripts": ["tablog.js"]
},
"permissions": [
"<all_urls>",
"tabs",
"activeTab"
]
}
I need to communicate asynchronously between content and background script in Firefox Extension. Background script receives message from content.
MDN indicates, that browser.runtime.sendMessage returns Promise, so it is possible use .than to attach callbacks to it. However, when trying to call it, I get error message:
TypeError: sending.than is not a function.
What's wrong?
I'm using Linux, Firefox v 60.0.1, loading extension as temporary via about:debugging page.
Manifest.json:
{
"manifest_version": 2,
"name": "messagetest",
"version": "1.0",
"description": "Testing content to background message sending",
"icons": {
"32": "icons/bird.png"
},
"background": {
"scripts": ["background.js"]
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"permissions": [
"<all_urls>"
]
}
content.js:
function handleResponse(msg)
{
console.log("Response HANDLED: " + msg);
}
function handleError(e)
{
console.log("ERROR HANDLED");
}
var sending = browser.runtime.sendMessage({request: "messagetest"});
sending.than(handleResponse, handleError);
background.js:
function onContentMessage(msg, sender, handleResponse)
{
console.log("MESSAGE RECEIVED");
handleResponse("RESPOND FROM BACKGROUND:");
}
browser.runtime.onMessage.addListener(onContentMessage);
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
});
Here is my scenario: By clicking the browser icon, I create a sidebar (html and css) next to the whole page, thus creating two columns (one is my sidebar, the other one is the actual page).
What I to achieve is having the sidebar stay when I reload the page or navigate to another page WITHIN the same domain. What I have right now is just the creation of the sidebar, but I have to click the browser action every time I navigate or reload the web page.
Manifest:
{
"name": "apdrop",
"version": "0.1",
"manifest_version": 2,
"description": "first prototype for apdrop extension",
"icons": {
"16": "icons/icon16.png",
"48": "icons/icon48.png",
"128": "icons/icon128.png"
},"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_icon": "icons/icon19.png",
"default_title": "apdrop"
},
"permissions": [
"background",
"tabs",
"http://*/*/",
"https://*/*/"
]
}
Background.js
function injectedScript(tab, method){
chrome.tabs.insertCSS(tab.id, {file:"style.css"});
//chrome.tabs.insertCSS(tab.id, {file:"bootstrap.css"});
chrome.tabs.executeScript(tab.id, { file: 'jquery-2.1.1.min.js'});
//chrome.tabs.executeScript(tab.id, { file: 'bootstrap.min.js'});
chrome.tabs.executeScript(tab.id, { file: 'inject.js'});
}
function click(tab){
console.log("browser action clicked");
injectedScript(tab, 'click');
//alert("action button was clicked");
}
chrome.browserAction.onClicked.addListener(click);
Inject.js
var ev = $("body > *");
if (!document.getElementById('contentxf343487d32'))
{
ev.wrapAll("<div id='insidecontent65675f526567'>");
$("#insidecontent65675f526567").wrapAll("<div id='contentxf343487d32'>");
$("<div id='sidebar343gf87897fh'><div id='insidesidebar87678bbbb'><p>this is my name</p></div></div>").insertBefore("#contentxf343487d32");
}
else
{
$("#sidebar343gf87897fh").remove();
$("#insidecontent65675f526567").unwrap();
$("#insidecontent65675f526567 > div").unwrap();
}
Hope this helps clarify a bit more.
The simplest strategy would be to save state in domain's sessionStorage and have a "detector" script that re-injects your UI.
Add setting the state in your content script:
// inject.js
if (!document.getElementById('contentxf343487d32'))
{
// ...
sessionStorage["contentxf343487d32"] = true;
}
else
{
// ...
sessionStorage["contentxf343487d32"] = false;
}
Add a "detector" script:
// detect.js
if(sessionStorage["contentxf343487d32"])
{
chrome.runtime.sendMessage({injectSidebar: true});
}
Always inject the script on page load, via the manifest (and change to a better permission):
"content_scripts" : [
{
"matches": ["<all_urls>"],
"js": ["detect.js"]
}
],
"permissions": [
"background",
"tabs",
"<all_urls>"
]
In the background, inject the script upon message:
// background.js
chrome.runtime.onMessage.addListener( function (message, sender, sendResponse){
if(message.injectSidebar)
{
click(sender.tab);
}
});
If you need more persistence than sessionStorage provides, use localStorage. If you need a different logic, you can still use this skeleton of a detector signalling the background.
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.