I am trying to use chrome.scripting.executeScript for a chrome extension I'm building in ManifestV3 and following the Google documentation here (see image below):
I have added an 'arguments' property to pass in the title of the current tab to my function. However, I am getting the following error message:
TypeError: Error in invocation of scripting.executeScript(scripting.ScriptInjection injection, optional function callback): Error at parameter 'injection': Unexpected property: 'arguments'.
Here is my code:
chrome.tabs.query({ active: true }, function (tabs) {
let tab = tabs[0];
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
function: myFunction,
arguments: [tab.title],
},
(injectionResults) => displaySearch(injectionResults[0].result)
);
});
Any help would be appreciated, thanks!
The property name being used is incorrect. The new API uses the property "args".
For example:
function greet(greeting) {
console.log(`${greeting}, World!`);
}
chrome.scripting.executeScript({
target: {tabId: tab.id},
function: greet,
args: ['Hello']
});
Output: Hello, World!
You can find more information here.
There is a workaround for this that you can use until they implement the feature using chrome.storage. What you need to do is first save the argument with chrome.storage.sync.set(), then retrieve it inside the function you're injecting using chrome.storage.sync.get().
chrome.storage.sync.set({ myVariable: valueOfVariable });
chrome.tabs.query({ active: true }, function (tabs) {
let tab = tabs[0];
chrome.scripting.executeScript(
{
target: { tabId: tab.id },
function: myFunction,
}
);
});
function myFunction() {
chrome.storage.sync.get(["myVariable"], ({ myVariable }) => {
// Do stuff
});
}
Related
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
);
This question already has an answer here:
chrome.scripting.executeScript pass parameter
(1 answer)
Closed 4 months ago.
I am trying to pass a parameter to the function called by chrome.scripting.executeScripts but it seems that the function can't get parameters passed from the extension!
My extension contains several inputs, when one input is focused on, the extension should pass a certain parameter from the input dataset to the function executed by chrome scripting to set the value of an input in the page, author
let bookInputs = [...document.getElementsByClassName('book-input')];
bookInputs.forEach(bookInput=> {
bookInput.addEventListener('focus', () => getBook(bookInput.dataset.author))
})
async function getBook(author) {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.scripting.executeScript({
target: { tabId: tab.id },
function: () => enterBookAuthor(author)
})
}
function enterBookAuthor(author) {
const book= document.getElementById('book-author'); //an input in the page
book.value = author;
}
But this code doesn't manage to achieve the mission, so
Why can't functions executed by chrome scripting receive parameters from the extension and what can i do to fix it??
You should use the args property, because scripting does not carry over any of the current execution context of the function.
Here's an example:
let bookInputs = [...document.getElementsByClassName('book-input')];
bookInputs.forEach(bookInput=> {
bookInput.addEventListener('focus', () => getBook(bookInput.dataset.author))
})
async function getBook(author) {
let [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: enterBookAuthor,
args: [author], // this is the args property.
})
}
function enterBookAuthor(author) {
const book= document.getElementById('book-author'); //an input in the page
book.value = author;
}
Here are the links to the docs:
https://developer.chrome.com/docs/extensions/reference/scripting/#runtime-functions
Dynamics CRM has its own XRM JS APIs, Which I'm trying to execute in a chrome extension that I'm working on. I'm using the below code.
chrome.tabs.query({ currentWindow: true, active: true }, function(tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => {
Xrm.Utility.alertDialog("Hello world", () => { });
}
});
});
Xrm.Utility.alertDialog("Hello world", () => { });
This code just shows a message box on the Dynamics CRM screen using this Dynamics API method.
If I run this code in the chrome console, it shows the message properly. If I just put alert("Hello world")", that code also runs which confirms that executeScript is working fine, but somehow Xrm isn't available.
Manifest.json
After overflowing the stack a few times, I learned that the script injection needs to happen by explicitly creating a script tag and injecting it on the page body. similar to the code below.
function runOnXrmPage() {
// create a script tag
var s = document.createElement('script');
s.src = chrome.runtime.getURL('webscript.js');
s.onload = function () {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
}
chrome.tabs.query({ currentWindow: true, active: true }, function (tabs) {
chrome.scripting.executeScript({
target: { tabId: tabs[0].id },
func: () => {
runOnXrmPage();
}
});
});
All the Xrm code was placed in webscript.js file and manifest was updated as pasted below.
"web_accessible_resources": [
{
"resources": [ "webscript.js" ],
"matches": [ "https://*.crm.dynamics.com/*" ]
}
]
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.
How do I get the URL of the current tab in the background service worker in MV3?
Here's what I have:
let currentURL;
chrome.action.onClicked.addListener(handleBrowserActionClicked);
chrome.commands.onCommand.addListener(function(command) {
console.log("Command:", command);
handleBrowserActionClicked();
});
function handleBrowserActionClicked() {
togglePlugin();
}
function togglePlugin() {
console.log("toggle plugin");
chrome.tabs.query({ active: true, currentWindow: true }, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, { greeting: "activateFeedback" });
});
}
// Fires when the active tab in a window changes.
chrome.tabs.onActivated.addListener(function () {
console.log("TAB CHANGED")
//firstTimeRunning = true
//feedbackActivated = false
currentURL = getTab()
.then(console.log("Current URL: " + currentURL))
})
// Fired when a tab is updated.
chrome.tabs.onUpdated.addListener(function () {
console.log("TAB UPDATED")
currentURL = getTab() // line 32
.then(console.log("Current URL: " + currentURL))
})
async function getTab() {
let queryOptions = { active: true, currentWindow: true };
let [tab] = await chrome.tabs.query(chrome.tabs[0].url); // line 38
return tab;
}
Right now the service worker is logging "Current URL: [object Promise]" instead of, for example, "https://www.google.com"
It is also giving an error in the console (see comments above for line numbers)
background.js:38 Uncaught (in promise) TypeError: Cannot read property 'url' of undefined
at getTab (background.js:38)
at background.js:32
I think it may be something to do with my limited knowledge of promises!
Please help.
Thank you in advance.
You function getTab seems not right, you are currently trying to query on the url. Not on the query options. The following function should work.
async function getTab() {
let queryOptions = { active: true, currentWindow: true };
let tabs = await chrome.tabs.query(queryOptions);
return tabs[0].url;
}
Also make sure you have the tabs permission.
In the listener you also don't use the correct async/promise method two examples using Promise.then and await.
Promise.then:
chrome.tabs.onUpdated.addListener(function () {
console.log("TAB UPDATED")
getTab().then(url => {
console.log(url);
})
})
await:
chrome.tabs.onUpdated.addListener(async function () {
console.log("TAB UPDATED")
let url = await getTab()
console.log(url)
})
For the "Error: Tabs cannot be queried right now (user may be dragging a tab)." error you can look at this answer, which suggest a small delay before querying the tab url.
const tab = (await chrome.tabs.query({ active: true }))[0]
Simply use "activeTab" permission in manifest.json
Add activeTab in your manifest.json.
"permissions": [
"activeTab",
],
And In Background.js
chrome.action.onClicked.addListener((tab) => {
console.log(tab.url);
});
I'm sure It will help you to get the current tab URL.
useful links - ActiveTab