javascript async function, caller not waiting. manifest V3 - javascript

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() {

Related

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();
}
}
});
}

Inject async function into page

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.

Wait for injected script execution to finish in Chrome Extension

I have this function that runs when a certain button in my popup.html is clicked
popup.js
async function execute () {
console.log("Executing...")
const [tab] = await chrome.tabs.query({active: true, currentWindow: true});
let res;
try {
res = await chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ["/src/MainScript.js"],
}, (a) => {
console.log("Done callback?", a)
});
} catch (e) {
return;
}
console.log("Result of script?", res)
}
"Done callback?" and "Result of script?" are printed inmediately after I run execute() so it's not waiting for it's execution to finish but to load the file, I think 🤔
I want to execute the MainScript.js file and wait for it's execution to finish. That file looks something like this:
MainScript.js
(async function () {
function Main () {
this.run = async function () {
// ...
}
}
const main = new Main();
await main.run();
})()
What I want to achieve is, when MainScript.js finishes it's execution I want to change some styling and elements in my popup.html.
I was wondering how to actually wait for the execution to finish or instead send a message from MainScript.js to my popup.js somehow or any alternative way, I'm opened to everything
EDIT
I tried the approach of messaging since I cannot wait for the execution to finish in manifest v3 (ty wOxxOm):
At the end of MainScript.js I placed this
const main = new Main();
await main.run();
chrome.runtime.sendMessage(myExtensionId, {type: 'processDone', value: true});
And then in background.js I listen for that message
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
console.log("Hello background", request);
// Set loading to false in localStorage...
}
);
And the listener is never triggered BUT when I manually type the sendMessage through the console it works as expected, the service worker console prints "Hello background"
How can I send a message from the Content Script (MainScript.js) to the service worker background.js??
Just in case, this is my manifest.json
{
"name": "Extension name",
"description": "Build an Extension!",
"version": "1.0",
"manifest_version": 3,
"background": {
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": "/images/favicon-32x32.png",
"default_title": "Title"
},
"permissions": [
"activeTab",
"tabs",
"webNavigation",
"scripting",
"storage"
],
"externally_connectable": {
"ids": [
"*"
],
"matches": [
"https://*.google.com/*"
]
},
"host_permissions": [
"http://*/*", "https://*/*"
]
}

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();
}
}
});
}

How To Call Chrome Extension Function After Page Redirect?

I am working on building a Javascript (in-browser) Instagram bot. However, I ran into a problem.
If you run this script, the first function will be called and the page will be redirected to "https://www.instagram.com/explore/tags/samplehashtag/" and the second function will be called immediately after (on the previous URL before the page changes to the new URL). Is there a way to make the second function be called after this second URL has been loaded completely?
I have tried setting it to a Window setInterval() Method for an extended time period, window.onload and a couple of other methods. However, I can't seem to get anything to work. Any chance someone has a solution?
This is my first chrome extension and my first real project, so I may be missing something simple..
manifest.json
{
"name": "Inject Me",
"version": "1.0",
"manifest_version": 2,
"description": "Injecting stuff",
"homepage_url": "http://danharper.me",
"background": {
"scripts": [
"background.js"
],
"persistent": true
},
"browser_action": {
"default_title": "Inject!"
},
"permissions": [
"https://*/*",
"http://*/*",
"tabs"
]
}
inject.js
(function() {
let findUrl = () => {
let hashtag = "explore/tags/samplehashtag/";
location.replace("https://www.instagram.com/" + hashtag);
}
findUrl();
})();
background.js
// this is the background code...
// listen for our browerAction to be clicked
chrome.browserAction.onClicked.addListener(function(tab) {
// for the current tab, inject the "inject.js" file & execute it
chrome.tabs.executeScript(tab.ib, {
file: 'inject.js'
});
});
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
chrome.tabs.executeScript(tab.ib, {
file: 'inject2.js'
});
});
inject2.js
(function() {
if (window.location.href.indexOf("https://www.instagram.com/explore/tags/samplehashtag/") != -1){
let likeAndRepeat = () => {
let counter = 0;
let grabPhoto = document.querySelector('._9AhH0');
grabPhoto.click();
let likeAndSkip = function() {
let heart = document.querySelector('.glyphsSpriteHeart__outline__24__grey_9.u-__7');
let arrow = document.querySelector('a.coreSpriteRightPaginationArrow');
if (heart) {
heart.click();
counter++;
console.log(`You have liked ${counter} photographs`)
}
arrow.click();
}
setInterval(likeAndSkip, 3000);
//alert('likeAndRepeat Inserted');
};
likeAndRepeat();
}
})();
It is not clear from the question and the example, when you want to run your function. But in chrome extension there is something called Message Passing
https://developer.chrome.com/extensions/messaging
With message passing you can pass messages from one file to another, and similarly listen for messages.
So as it looks from your use case, you can listen for a particular message and then fire your method.
For example
background.js
chrome.runtime.sendMessage({message: "FIRE_SOME_METHOD"})
popup.js
chrome.runtime.onMessage.addListener(
function(request) {
if (request.message == "FIRE_SOME_METHOD")
someMethod();
});
EDIT
Also if you want to listen for the URL changes, you can simply put a listener provided as in the documentation.
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
console.log('updated tab');
});

Categories

Resources