Inject async function into page - javascript

I am developing a chrome-extension, where I am trying to inject a function into the page. Injecting a regular function works without problems.
But as soon as I try to inject anything with async-functions, I get an error similar to this:
Uncaught ReferenceError: g is not defined
For example, injecting an async-function directly.
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: async () => {
console.log("Does it work?");
}
});
Or even if I inject a regular function, but have an async function inside:
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => {
(async function () {
console.log("Does it work?");
})();
}
});
It can't be right, that I am forced to write and inject synchronous code, making me unable to use the async/await functionality... Can it? What am I doing wrong?
EDIT: ---------------
Upon further investigation, I can see that this function
async () => { console.log("Does it work?"); };
Get translated to this
function(){return e.apply(this,arguments)}
Which explains the ReferenceError but still leaves me clueless as to why this happens.
EDIT2: ---------------
So I tried to resort to "synchronous" code for now. But hoped I at least could use promises without async/await - But also no...
chrome.storage.local.set({ someArray: [] })
.then(() => console.log("It worked?"));
The code above results in the following error:
Cannot read properties of undefined (reading 'then')
So no promises and no async/await for any injected code... Is this really the case?

Injecting async function should not cause a problem. I have included a minimal example below to demonstrate.
manifest.json
{
"name": "TEST",
"version": "0.0.1",
"manifest_version": 3,
"permissions": [
"scripting",
"activeTab"
],
"host_permissions": [
"<all_urls>"
],
"background": {
"service_worker": "background.js"
}
}
background.js
function inject(tab) {
chrome.scripting.executeScript({
target: {tabId: tab.id},
func: async () => {
window.alert("I'm here!");
}
})
}
// first open tab with some web page -> try inject there
chrome.tabs.query({
url: "https://*/*"
}, function (tabs) {
inject(tabs.shift())
});
Regarding OP and each edit:
Uncaught ReferenceError: g is not defined - identifier g does not exist: check what it is and where is it supposed to be defined
EDIT 1: if using a compiler/bundler, renaming may be the source of a reference error
EDIT 2: chrome.storage.local.set docs on usage indicate it needs a callback . There is no return value, i.e. it is undefined, cannot chain a then after it.

Related

Running multiple functions via scripting.executeScript

I want to update extension from manifest v2 to manifest v3. previously I was using tabs api now I have to use scripting api. The problem is I am executing multiple script but in scripting api I have to create multiple files for code or I have to create multiple functions. so is there any better way to do this?
This is what I have in mv2 and I have 7-8 scripts like this in my code
chrome.tabs.executeScript(tab.id, {
code: 'document.querySelector("#recv_address > span").textContent'
}, display_location);
I have tried below code and it is working fine but is there any better way to do this because I want to do same thing for 7-8 scripts
function passScript() {
let passQuery = document.querySelector("#recv_address > span").textContent;
return passQuery;
}
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
func: passScript,
},
display_location
);
If you have many files. You can do it like this in your service worker.
Background.js
const scriptList = ["js/a.js", "js/b.js", "js/c.js"];
scriptList.forEach((script) => {
chrome.scripting.executeScript({
target: {tabId: tabId},
files: [`${script}`],
injectImmediately: true
}, () => void chrome.runtime.lastError);
});
Manifest.json
"permissions": ["scripting"],
The API you're using is correct, but know that you can also pass arguments to func, for example:
function passScript(selector) {
let passQuery = document.querySelector(selector).textContent;
return passQuery;
}
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
func: passScript,
args: ["#recv_address > span"]
},
display_location
);

Cant fire `alert` from `service_worker` (previously background) from Chrome Extension (v3 manifest) [duplicate]

I am attempting to display an alarm that pops up in my browser from background.js in Manifest v3. However, using the code implementation that is described in the Manifest v3 page does not produce an alarm.
Manifest.js:
{
"name": "Focus-Bot",
"description": "A bot meant to help you focus",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage", "activeTab", "scripting"],
"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"
},
"options_page": "options.html"
}
Background.js:
chrome.runtime.onInstalled.addListener(() => {
chrome.scripting.executeScript({
function: showAlert
})
});
function showAlert(){
alert('object input for later');
}
This version of background.js returns the following error
TypeError: Error in invocation of scripting.executeScript(scripting.ScriptInjection injection, optional function callback): Error at parameter 'injection': Missing required property 'target'.
The example code of a working Chrome Extension (the green background button) uses chrome.tabs in a popup.js file to get a target and inject javascript, but when background.js runs the same code like this:
Background.js (tabs):
chrome.runtime.onInstalled.addListener(() => {
let [tab] = await chrome.tabs.query(queryOptions);
console.log(tab)
chrome.scripting.executeScript({
function: showAlert
})
});
function showAlert(){
alert('object input for later');
}
Background.js seems to crash with "Service worker registration failed", with no error logs.
How do I display an alarm for the current active page from background.js?
As the error message says you need to add target to executeScript's parameters. Always look up the exact usage of API methods in the documentation.
Your code uses await but the function isn't declared with async which is a syntax error that causes the service worker to fail the registration. Currently ManifestV3 is riddled with bugs so it doesn't even show the cause of the failure so you'll have to use try/catch manually.
try {
chrome.runtime.onInstalled.addListener(async () => {
const [tab] = await chrome.tabs.query(queryOptions);
chrome.scripting.executeScript({
target: {tabId: tab.id},
function: showAlert,
});
});
} catch (e) {
console.error(e);
}
An arguably better/cleaner approach would be to use two files: the main code in bg.js and the try-catch wrapper in bg-loader.js that imports bg.js, see this example.
Note that the active tab may be un-injectable e.g. a default start page or a chrome:// page (settings, bookmarks, etc.) or a chrome-extension:// page. Instead you can open a small new window:
alert({html: 'Foo <b>bar</b><ul><li>bla<li>bla</ul>'})
.then(() => console.log('alert closed'));
async function alert({
html,
title = chrome.runtime.getManifest().name,
width = 300,
height = 150,
left,
top,
}) {
const w = left == null && top == null && await chrome.windows.getCurrent();
const w2 = await chrome.windows.create({
url: `data:text/html,<title>${title}</title>${html}`.replace(/#/g, '%23'),
type: 'popup',
left: left ?? Math.floor(w.left + (w.width - width) / 2),
top: top ?? Math.floor(w.top + (w.height - height) / 2),
height,
width,
});
return new Promise(resolve => {
chrome.windows.onRemoved.addListener(onRemoved, {windowTypes: ['popup']});
function onRemoved(id) {
if (id === w2.id) {
chrome.windows.onRemoved.removeListener(onRemoved);
resolve();
}
}
});
}

javascript async function, caller not waiting. manifest V3

Do NOT delete my question. I have scoured the web, the documentations, I don't know why this is happening and deleting the question does NOT help. There no other similar questions to mine.
Here goes. Below is how I beleive it should happen. But even with all the async away promis syntax I place there, it doesn't happen. When the async function is called, no one waits for it to finish. Idk what else to do except daisychain everything under the async function which I am trying to avoid. Help please.
I have tried using tabs= await getcurrenttab(), but that causes an error saying it only works on async functions. Which the listener is not.
manifest.json
{
"manifest_version": 3,
"name": "DHL Helper",
"description": "This helper helps DHL's quality of life, improves mental health.",
"version": "1.0",
"action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"content_scripts": [{
"matches":["*://servicenow.*.com/*",
"http://*/*",
"https://*/*"],
"js": ["content_helper.js"]
}],
"permissions": [
"activeTab"
],
"host_permissions": [
"http://www.blogger.com/",
"*://*/*"
]
}
popup.js
document.addEventListener('DOMContentLoaded', function() {
console.log('got tabs id');
tab = getCurrentTab();
console.log('Active tab ' + tab.id);
});
async function getCurrentTab(){
console.log('trying to get tabs id');
let queryOptions = {active: true, currentWindow: true};
return await chrome.tabs.query(queryOptions)
.then((tabs) => {
console.log('Obtained tab ID');
console.log(tabs);
return tabs[0];
})
.catch((Error) => {
console.log('it failed');
console.error;
return;
})
}
document.addEventListener('DOMContentLoaded', async function() {
console.log('got tabs id');
tab = await getCurrentTab();
console.log('Active tab ' + tab.id);
});
Your approach was already correct, but if you want to use await in the function await, you also have to make the executing function asyncronous.
user mentions attempting to use =await getCurrentTab() but encounters error that await is only for async functions...
adding async to event listener function fixed the problem
change this:
document.addEventListener('DOMContentLoaded', function() {
to this:
document.addEventListener('DOMContentLoaded', async function() {

How to display an alert from background.js in Manifest v3

I am attempting to display an alarm that pops up in my browser from background.js in Manifest v3. However, using the code implementation that is described in the Manifest v3 page does not produce an alarm.
Manifest.js:
{
"name": "Focus-Bot",
"description": "A bot meant to help you focus",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"permissions": ["storage", "activeTab", "scripting"],
"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"
},
"options_page": "options.html"
}
Background.js:
chrome.runtime.onInstalled.addListener(() => {
chrome.scripting.executeScript({
function: showAlert
})
});
function showAlert(){
alert('object input for later');
}
This version of background.js returns the following error
TypeError: Error in invocation of scripting.executeScript(scripting.ScriptInjection injection, optional function callback): Error at parameter 'injection': Missing required property 'target'.
The example code of a working Chrome Extension (the green background button) uses chrome.tabs in a popup.js file to get a target and inject javascript, but when background.js runs the same code like this:
Background.js (tabs):
chrome.runtime.onInstalled.addListener(() => {
let [tab] = await chrome.tabs.query(queryOptions);
console.log(tab)
chrome.scripting.executeScript({
function: showAlert
})
});
function showAlert(){
alert('object input for later');
}
Background.js seems to crash with "Service worker registration failed", with no error logs.
How do I display an alarm for the current active page from background.js?
As the error message says you need to add target to executeScript's parameters. Always look up the exact usage of API methods in the documentation.
Your code uses await but the function isn't declared with async which is a syntax error that causes the service worker to fail the registration. Currently ManifestV3 is riddled with bugs so it doesn't even show the cause of the failure so you'll have to use try/catch manually.
try {
chrome.runtime.onInstalled.addListener(async () => {
const [tab] = await chrome.tabs.query(queryOptions);
chrome.scripting.executeScript({
target: {tabId: tab.id},
function: showAlert,
});
});
} catch (e) {
console.error(e);
}
An arguably better/cleaner approach would be to use two files: the main code in bg.js and the try-catch wrapper in bg-loader.js that imports bg.js, see this example.
Note that the active tab may be un-injectable e.g. a default start page or a chrome:// page (settings, bookmarks, etc.) or a chrome-extension:// page. Instead you can open a small new window:
alert({html: 'Foo <b>bar</b><ul><li>bla<li>bla</ul>'})
.then(() => console.log('alert closed'));
async function alert({
html,
title = chrome.runtime.getManifest().name,
width = 300,
height = 150,
left,
top,
}) {
const w = left == null && top == null && await chrome.windows.getCurrent();
const w2 = await chrome.windows.create({
url: `data:text/html,<title>${title}</title>${html}`.replace(/#/g, '%23'),
type: 'popup',
left: left ?? Math.floor(w.left + (w.width - width) / 2),
top: top ?? Math.floor(w.top + (w.height - height) / 2),
height,
width,
});
return new Promise(resolve => {
chrome.windows.onRemoved.addListener(onRemoved, {windowTypes: ['popup']});
function onRemoved(id) {
if (id === w2.id) {
chrome.windows.onRemoved.removeListener(onRemoved);
resolve();
}
}
});
}

Unable to access Google Chrome APIs

I'm working on a Chrome Extension and have tried to access the storage API. However I get a console error:
notes.js:25 Uncaught TypeError: Cannot read property 'sync' of undefined
at HTMLTextAreaElement.save_notes (notes.js:25)
Commenting out the storage.sync call and uncommenting the privacy.services call (strictly to test a 2nd Chrome API) I get this error instead:
notes.js:30 Uncaught TypeError: Cannot read property 'services' of
undefined at HTMLTextAreaElement.save_notes (notes.js:30)
Anyone know what not being able to access any Chrome API explictly listed in the manifest.json permissions could be a sign of?
Here's my javascript file. Under testing, the error occurs on the "save_notes" function call.
console.log("note.js connected")
///// select the text area
var note_area = document.getElementById('notetext')
//// todo Create a function to save the textarea to local storage
// todo First check & load any previously saved notes
;(function load_notes() {
if (note_area === "") {
chrome.storage.sync.get("stored_obj", function(resultObj) {
note_area.value = resultObj.stored_obj
console.log("Attempted to load notes into a blank note area. Did it run correctly?")
})
}
})()
function save_notes () {
chrome.storage.sync.set({
stored_obj: note_area.value
}, function () {
console.log("Saved into Chrome Storage")
})
// chrome.privacy.services.autofillEnabled.get({}, function(details) {
// if (details.value)
// console.log('Autofill is on!');
// else
// console.log('Autofill is off!');
//});
}
note_area.addEventListener('blur', save_notes)
//// setup a blur handler in notes.html to fire function save_notes
// could not be done (inline JS). I need to add a listener and a function in this file
function console_fire() {
console.log ('fired the console log')
}
Here's my manifest.json file:
{
"manifest_version": 2,
"name": "Mindless Blocker",
"version": "0.1",
"description": "Block mindless distractions and have a place to take notes for followup during free time",
"browser_action": {
"default_icon": "cloud-icon.png",
"default_popup": "index.html"
},
"permissions": [
"storage",
"privacy"
]
}

Categories

Resources