How to get current tab URL using Manifest v3? - javascript

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

Related

How to pass Data between files Chrome Extension?

Currently, I mainly work with two files, background.js and popup.js.
In background.js
I have a bunch of functions that let me store data in an IndexedDB. And in popup.js I call the functions like this:
chrome.runtime.sendMessage({
message: "insert",
payload: [
{
url: form_data.get("message"),
text: form_data.get("message"),
},
],
});
Depending on the message, a certain function is called. When the function has successfully executed I do this from the background.js file:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "insert") {
let insert_request = insert_records(request.payload);
insert_request.then((res) => {
chrome.runtime.sendMessage({
message: "insert_success",
payload: res,
});
});
});
This is my problem:
How do I send data from background.js to popup.js. What I want is to get the URL of the current page, and then send it to popup.js and store it in the Database.
I have already looked at already existing posts, but none of them really helped.
Can someone please help me out.
Update
Currently I use this is in background.js to get the current URL. It works just fine. But how can I pass the tab.url to my popup.js file?:
let activeTabId, lastUrl, lastTitle;
function getTabInfo(tabId) {
chrome.tabs.get(tabId, function (tab) {
if (lastUrl != tab.url || lastTitle != tab.title)
console.log((lastUrl = tab.url), (lastTitle = tab.title));
});
}
chrome.tabs.onActivated.addListener(function (activeInfo) {
getTabInfo((activeTabId = activeInfo.tabId));
});
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
if (activeTabId == tabId) {
getTabInfo(tabId);
}
});

Send message from background.js to popup

I want to implement FCM in my chrome extension.
At the mement after a lot of research I've found that the quick and best way to implement fcm is using the old API chrome.gcm. At the moment this solution seems working fine and when the extension is loaded I'm able to get an fcm token.
Now what I want to do is to pass the token to the popup that is powered by vue.js I'm trying with this code but without success.
background.js
const openPopup = () => {
chrome.windows.create({
type: 'popup',
height: 520,
width: 440,
url: chrome.runtime.getURL('popup.html')
})
}
const registeredToken = (registrationId) => {
console.log('FCM Token')
console.log(registrationId)
openPopup()
chrome.runtime.sendMessage({fcmToken: registrationId})
if( chrome.runtime.lastError ) {
console.log('error')
}
}
const notificationId = (id) => {
if(chrome.runtime.lastError) {
console.log(chrome.runtime.lastError)
}
console.log(id)
}
chrome.runtime.onInstalled.addListener( () => {
console.log('FCM extension installed')
})
chrome.action.onClicked.addListener( (tab) => {
console.log(tab)
openPopup()
})
chrome.gcm.register(['my_sender_id'], registeredToken)
chrome.gcm.onMessage.addListener( (message) => {
console.log(message, message.data["gcm.notification.title"])
chrome.notifications.create('', {
type: 'basic',
iconUrl: 'letter.png',
title: message.data["gcm.notification.title"],
message: message.data["gcm.notification.body"],
buttons: [
{ title: 'Dismiss' },
{ title: 'Reply' }
]
}, notificationId)
})
chrome.notifications.onButtonClicked.addListener( (notificationId, buttonIndex) => {
console.log('button clicked')
console.log(notificationId, buttonIndex)
})
popup.vue file
<template>
<div class="main_app">
<h1>Hello {{msg}}</h1>
</div>
</template>
<script>
export default {
name: 'popupView',
data () {
return {
msg: ''
}
},
mounted() {
chrome.runtime.onMessage( (message, sender, sendResponse) => {
console.log(message, sender, sendResponse)
this.msg = message
})
},
methods: {
}
}
</script>
What I've noticed is that the chrome.runtime.sendMessage({fcmToken: registrationId}) will not work and on the popup side I'm unable to send or get messages from background
How I can pass messages between the vue.js powered popup and the background.js file of the extension?
Is better to use firebase client library to get push messages or the gcm is fine for this scope?
You can use the chrome.tabs.query and chrome.tabs.sendMessage APIs to send a message from the background to the Popup.
chrome.tabs.query({}, function (tabs) {
tabs.forEach((tab) => {
chrome.tabs.sendMessage(
tab.id,
youtPayload,
function (response) {
// do something here if you want
}
);
});
});
That's it!
I spend lots of hours to finding solution to the same proble and still not find any.
My current understanding is, that we are trying to do and use method for the purpose, they wasnt ment to be used for.
Key information leading to this:
popup.js can share the same. Js file and objects with background.js
documentation primarely is talking about passing data between web page (content.js) and others (popup.js or background.js)

How to pass parameters to the function called by chrome.scripting.executeScripts? - Chrome extension [duplicate]

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

chrome.scripting.executeScript - Unexpected property: 'arguments'

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

chrome extension - issue with propertie "active" of chrome.tabs.create

I am creating a new tab and and injecting some code in it straight after.
But the problem is that the code to be injected is not injected properly when using the property active:true(which I need to use) on tabs.create.
Here is the code in popup.js:
chrome.tabs.create({url: "http://localhost:3000/", index: newTabId, active: false}, (newTab) => {
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
// check status so that it sends only one message, and not one for each status change
if(changeInfo.status === "loading") {
if (tab.id === newTab.id) {
chrome.tabs.sendMessage(newTab.id, {message: "watch_video", videoData: selectedVideoData},
function (resp) {
console.log("Resp",resp);
return true;
}
);
}
}
});
})
Here is the problematic line: chrome.tabs.create({url: "http://localhost:3000/", index: newTabId, active: false}. When active is false, the code is injected, but when it is true, nothing seems to happen.
inject.js:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.message === "watch_video") {
console.log("inject soon")
injectScript(request.videoData);
sendResponse("watch_video script is to be injected in " + window.location.href)
}
});
function injectScript(videoData) {
console.log("injected")
$(document).ready(function() {
document.test = "ABCDE"
const checkState = setInterval(() => {
$(".bkg").css({ "background-color": "#ffffff"})
}, 100)
})
}
Here I tried something with setInterval(), it does not work when active is true.
However it does work with a timeout. But does not not work without any timeout or interval when active is set to true.
I could use just use a timeout, but it is not really clean, I would prefer to understand why it behaves like it does. I am using react btw.
Here is what it is said about the active property:
Whether the tab should become the active tab in the window. Does not affect whether the window is focused (see windows.update). Defaults to true.
Source: https://developer.chrome.com/extensions/tabs#method-create

Categories

Resources