I am struggling to get this simple f-ty working... My scenario is:
get current URL
modify it
navigate/redirect to it
execute custom JS code there
The most problems I have is with 4)
manifest.json
{
"name": "Hello, World!",
"description": "Navigate and execute custom js script",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"tabs",
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {}
}
background.js
function myCustomScript() {
alert('myCustomScript test ok!');
console.log('myCustomScript test ok!');
}
chrome.action.onClicked.addListener((tab) => {
chrome.tabs.update({url: "https://example.com"}, myCustomScript);
});
The page got redirected but my js function is not executed! Do you know why and how to fix it?
P.S: this is my first time I am creating my chrome extension, maybe I am doing something wrong...
To execute custom code, use chrome.scripting API. For this scenario you'll need:
"scripting" added to "permissions", which you already have,
"https://example.com/" added to "host_permissions" in manifest.json.
Note that activeTab permission won't apply to the tab after it's navigated to a URL with a different origin because this permission only applies to the currently shown origin.
Due to a bug in Chrome, you need to wait for the URL to be set before executing the script.
The bug is fixed in Chrome 100.
chrome.action.onClicked.addListener(async tab => {
await chrome.tabs.update(tab.id, {url: "https://example.com"});
// Creating a tab needs the same workaround
// tab = await chrome.tabs.create({url: "https://example.com"});
await onTabUrlUpdated(tab.id);
const results = await chrome.scripting.executeScript({
target: {tabId: tab.id},
files: ['content.js'],
});
// do something with results
});
function onTabUrlUpdated(tabId) {
return new Promise((resolve, reject) => {
const onUpdated = (id, info) => id === tabId && info.url && done(true);
const onRemoved = id => id === tabId && done(false);
chrome.tabs.onUpdated.addListener(onUpdated);
chrome.tabs.onRemoved.addListener(onRemoved);
function done(ok) {
chrome.tabs.onUpdated.removeListener(onUpdated);
chrome.tabs.onRemoved.removeListener(onRemoved);
(ok ? resolve : reject)();
}
});
}
P.S. alert can't be used in a service worker. Instead, you should look at devtools console of the background script or use chrome.notifications API.
Related
I'm having fun with Google Chrome extension, and I just want to know how can I store the URL of the current tab in a variable?
Use chrome.tabs.query() like this:
chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => {
let url = tabs[0].url;
// use `url` here inside the callback because it's asynchronous!
});
This requires that you request access to the chrome.tabs API in your extension manifest:
"permissions": [ ...
"tabs"
]
It's important to note that the definition of your "current tab" may differ depending on your extension's needs.
Setting lastFocusedWindow: true in the query is appropriate when you want to access the current tab in the user's focused window (typically the topmost window).
Setting currentWindow: true allows you to get the current tab in the window where your extension's code is currently executing. For example, this might be useful if your extension creates a new window / popup (changing focus), but still wants to access tab information from the window where the extension was run.
I chose to use lastFocusedWindow: true in this example, because Google calls out cases in which currentWindow may not always be present.
You are free to further refine your tab query using any of the properties defined here: chrome.tabs.query
Warning! chrome.tabs.getSelected is deprecated. Please use chrome.tabs.query as shown in the other answers.
First, you've to set the permissions for the API in manifest.json:
"permissions": [
"tabs"
]
And to store the URL :
chrome.tabs.getSelected(null,function(tab) {
var tablink = tab.url;
});
Other answers assume you want to know it from a popup or background script.
In case you want to know the current URL from a content script, the standard JS way applies:
window.location.toString()
You can use properties of window.location to access individual parts of the URL, such as host, protocol or path.
The problem is that chrome.tabs.getSelected is asynchronous. This code below will generally not work as expected. The value of 'tablink' will still be undefined when it is written to the console because getSelected has not yet invoked the callback that resets the value:
var tablink;
chrome.tabs.getSelected(null,function(tab) {
tablink = tab.url;
});
console.log(tablink);
The solution is to wrap the code where you will be using the value in a function and have that invoked by getSelected. In this way you are guaranteed to always have a value set, because your code will have to wait for the value to be provided before it is executed.
Try something like:
chrome.tabs.getSelected(null, function(tab) {
myFunction(tab.url);
});
function myFunction(tablink) {
// do stuff here
console.log(tablink);
}
This is a pretty simple way
window.location.toString();
You probaly have to do this is the content script because it has all the functions that a js file on a wepage can have and more.
Hi here is an Google Chrome Sample which emails the current Site to an friend. The Basic idea behind is what you want...first of all it fetches the content of the page (not interessting for you)...afterwards it gets the URL (<-- good part)
Additionally it is a nice working code example, which i prefer motstly over reading Documents.
Can be found here:
Email this page
This Solution is already TESTED.
set permissions for API in manifest.json
"permissions": [ ...
"tabs",
"activeTab",
"<all_urls>"
]
On first load call function. https://developer.chrome.com/extensions/tabs#event-onActivated
chrome.tabs.onActivated.addListener((activeInfo) => {
sendCurrentUrl()
})
On change call function. https://developer.chrome.com/extensions/tabs#event-onSelectionChanged
chrome.tabs.onSelectionChanged.addListener(() => {
sendCurrentUrl()
})
the function to get the URL
function sendCurrentUrl() {
chrome.tabs.getSelected(null, function(tab) {
var tablink = tab.url
console.log(tablink)
})
async function getCurrentTabUrl () {
const tabs = await chrome.tabs.query({ active: true })
return tabs[0].url
}
You'll need to add "permissions": ["tabs"] in your manifest.
For those using the context menu api, the docs are not immediately clear on how to obtain tab information.
chrome.contextMenus.onClicked.addListener(function(info, tab) {
console.log(info);
return console.log(tab);
});
https://developer.chrome.com/extensions/contextMenus
You have to check on this.
HTML
<button id="saveActionId"> Save </button>
manifest.json
"permissions": [
"activeTab",
"tabs"
]
JavaScript
The below code will save all the urls of active window into JSON object as part of button click.
var saveActionButton = document.getElementById('saveActionId');
saveActionButton.addEventListener('click', function() {
myArray = [];
chrome.tabs.query({"currentWindow": true}, //{"windowId": targetWindow.id, "index": tabPosition});
function (array_of_Tabs) { //Tab tab
arrayLength = array_of_Tabs.length;
//alert(arrayLength);
for (var i = 0; i < arrayLength; i++) {
myArray.push(array_of_Tabs[i].url);
}
obj = JSON.parse(JSON.stringify(myArray));
});
}, false);
If you want the full extension that store the URLs that opened or seen by the use via chrome extension:
use this option in your background:
openOptionsPage = function (hash) {
chrome.tabs.query({ url: options_url }, function (tabs) {
if (tabs.length > 0) {
chrome.tabs.update(
tabs[0].id,
{ active: true, highlighted: true, currentWindow: true },
function (current_tab) {
chrome.windows.update(current_tab.windowId, { focused: true });
}
);
} else {
window.addEventListener(hash, function () {
//url hash # has changed
console.log(" //url hash # has changed 3");
});
chrome.tabs.create({
url: hash !== undefined ? options_url + "#" + hash : options_url,
});
}
});
};
you need index.html file also. which you can find in the this Github
the manifest file should be like this:
{
"manifest_version": 2,
"name": "ind count the Open Tabs in browser ",
"version": "0.3.2",
"description": "Show open tabs",
"homepage_url": "https://github.com/sylouuu/chrome-open-tabs",
"browser_action": {},
"content_security_policy": "script-src 'self' https://ajax.googleapis.com https://www.google-analytics.com; object-src 'self'",
"options_page": "options.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"background": {
"scripts": ["background.js"]
},
"web_accessible_resources": ["img/*.png"],
"permissions": ["tabs", "storage"]
}
The full version of simple app can be found here on this Github:
https://github.com/Farbod29/extract-and-find-the-new-tab-from-the-browser-with-chrome-extention
I'm having fun with Google Chrome extension, and I just want to know how can I store the URL of the current tab in a variable?
Use chrome.tabs.query() like this:
chrome.tabs.query({active: true, lastFocusedWindow: true}, tabs => {
let url = tabs[0].url;
// use `url` here inside the callback because it's asynchronous!
});
This requires that you request access to the chrome.tabs API in your extension manifest:
"permissions": [ ...
"tabs"
]
It's important to note that the definition of your "current tab" may differ depending on your extension's needs.
Setting lastFocusedWindow: true in the query is appropriate when you want to access the current tab in the user's focused window (typically the topmost window).
Setting currentWindow: true allows you to get the current tab in the window where your extension's code is currently executing. For example, this might be useful if your extension creates a new window / popup (changing focus), but still wants to access tab information from the window where the extension was run.
I chose to use lastFocusedWindow: true in this example, because Google calls out cases in which currentWindow may not always be present.
You are free to further refine your tab query using any of the properties defined here: chrome.tabs.query
Warning! chrome.tabs.getSelected is deprecated. Please use chrome.tabs.query as shown in the other answers.
First, you've to set the permissions for the API in manifest.json:
"permissions": [
"tabs"
]
And to store the URL :
chrome.tabs.getSelected(null,function(tab) {
var tablink = tab.url;
});
Other answers assume you want to know it from a popup or background script.
In case you want to know the current URL from a content script, the standard JS way applies:
window.location.toString()
You can use properties of window.location to access individual parts of the URL, such as host, protocol or path.
The problem is that chrome.tabs.getSelected is asynchronous. This code below will generally not work as expected. The value of 'tablink' will still be undefined when it is written to the console because getSelected has not yet invoked the callback that resets the value:
var tablink;
chrome.tabs.getSelected(null,function(tab) {
tablink = tab.url;
});
console.log(tablink);
The solution is to wrap the code where you will be using the value in a function and have that invoked by getSelected. In this way you are guaranteed to always have a value set, because your code will have to wait for the value to be provided before it is executed.
Try something like:
chrome.tabs.getSelected(null, function(tab) {
myFunction(tab.url);
});
function myFunction(tablink) {
// do stuff here
console.log(tablink);
}
This is a pretty simple way
window.location.toString();
You probaly have to do this is the content script because it has all the functions that a js file on a wepage can have and more.
Hi here is an Google Chrome Sample which emails the current Site to an friend. The Basic idea behind is what you want...first of all it fetches the content of the page (not interessting for you)...afterwards it gets the URL (<-- good part)
Additionally it is a nice working code example, which i prefer motstly over reading Documents.
Can be found here:
Email this page
This Solution is already TESTED.
set permissions for API in manifest.json
"permissions": [ ...
"tabs",
"activeTab",
"<all_urls>"
]
On first load call function. https://developer.chrome.com/extensions/tabs#event-onActivated
chrome.tabs.onActivated.addListener((activeInfo) => {
sendCurrentUrl()
})
On change call function. https://developer.chrome.com/extensions/tabs#event-onSelectionChanged
chrome.tabs.onSelectionChanged.addListener(() => {
sendCurrentUrl()
})
the function to get the URL
function sendCurrentUrl() {
chrome.tabs.getSelected(null, function(tab) {
var tablink = tab.url
console.log(tablink)
})
async function getCurrentTabUrl () {
const tabs = await chrome.tabs.query({ active: true })
return tabs[0].url
}
You'll need to add "permissions": ["tabs"] in your manifest.
For those using the context menu api, the docs are not immediately clear on how to obtain tab information.
chrome.contextMenus.onClicked.addListener(function(info, tab) {
console.log(info);
return console.log(tab);
});
https://developer.chrome.com/extensions/contextMenus
You have to check on this.
HTML
<button id="saveActionId"> Save </button>
manifest.json
"permissions": [
"activeTab",
"tabs"
]
JavaScript
The below code will save all the urls of active window into JSON object as part of button click.
var saveActionButton = document.getElementById('saveActionId');
saveActionButton.addEventListener('click', function() {
myArray = [];
chrome.tabs.query({"currentWindow": true}, //{"windowId": targetWindow.id, "index": tabPosition});
function (array_of_Tabs) { //Tab tab
arrayLength = array_of_Tabs.length;
//alert(arrayLength);
for (var i = 0; i < arrayLength; i++) {
myArray.push(array_of_Tabs[i].url);
}
obj = JSON.parse(JSON.stringify(myArray));
});
}, false);
If you want the full extension that store the URLs that opened or seen by the use via chrome extension:
use this option in your background:
openOptionsPage = function (hash) {
chrome.tabs.query({ url: options_url }, function (tabs) {
if (tabs.length > 0) {
chrome.tabs.update(
tabs[0].id,
{ active: true, highlighted: true, currentWindow: true },
function (current_tab) {
chrome.windows.update(current_tab.windowId, { focused: true });
}
);
} else {
window.addEventListener(hash, function () {
//url hash # has changed
console.log(" //url hash # has changed 3");
});
chrome.tabs.create({
url: hash !== undefined ? options_url + "#" + hash : options_url,
});
}
});
};
you need index.html file also. which you can find in the this Github
the manifest file should be like this:
{
"manifest_version": 2,
"name": "ind count the Open Tabs in browser ",
"version": "0.3.2",
"description": "Show open tabs",
"homepage_url": "https://github.com/sylouuu/chrome-open-tabs",
"browser_action": {},
"content_security_policy": "script-src 'self' https://ajax.googleapis.com https://www.google-analytics.com; object-src 'self'",
"options_page": "options.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"background": {
"scripts": ["background.js"]
},
"web_accessible_resources": ["img/*.png"],
"permissions": ["tabs", "storage"]
}
The full version of simple app can be found here on this Github:
https://github.com/Farbod29/extract-and-find-the-new-tab-from-the-browser-with-chrome-extention
I want to write a chrome extension which records the current active tab URL every time a new site is loaded and send it to a server for further use. So far I have managed to write the following code:
manifest.json
{
"manifest_version": 2,
"name": "Currenturl",
"description": "Fetches current tab url.",
"version": "0.1",
"author": "Tarun Khare",
"browser_action": {
"default_icon": "icon.png",
"default_title": "Just observing your current url."
},
"permissions": ["tabs", "activeTab"],
"background": {
"scripts": ["content.js"],
"persistent": false
}
}
content.js
chrome.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
var url = tabs[0].url;
console.log("hello: "+url);
});
I am using background scripts since chrome.tabs doesn't work in content scripts. But this extension is not printing anything in chrome console. What is the issue?
Rename content.js to background.js since this is a background script
Use chrome.tabs.onUpdated listener
Look at the correct console: Where to read console messages from background.js?
chrome.tabs.onUpdated.addListener((tabId, change, tab) => {
if (change.url) {
console.log(change.url);
}
});
It'll report the URL changes in all tabs.
You can also limit the processing to only the active tab by adding a check for tab.active property.
i try to write code based on the information you provide.
const tabUpdatelistenerFun = (tabid, changeInfo, tab) => {
const url = changeInfo.url;
if (!url || ['chrome://', 'about://'].some(p => url.startsWith(p))) return false;
const { index, active, highlighted, windowId } = tab;
if (!active) return false;
chrome.tabs.query({ index, highlighted, windowId, lastFocusedWindow: true }, () => {
console.log(url);
})
}
chrome.tabs.onUpdated.addListener(tabUpdatelistenerFun);
i think this is what you want.
You can count and extract the URL with full detail in background.js
here is the main code from below GihHub repository:
chrome.windows.getAll({ populate: true }, function (windows) {
windows.forEach(function (window) {
window.tabs.forEach(function (tab) {
//i++
collect all of the urls here, I will just log them instead
console.log("tab.ur[![enter image description here][1]][1]l aaaaakahari 2");
console.log(tab.url);
});
});
});
There is a GitHub repository of chrome Extension below here, you can find in the background.js there are methods to count and extract the open URL, and there is and even section that console it imminently when the user open or close any tab.
https://github.com/Farbod29/extract-and-find-the-new-tab-frome-the-browser-with-chrome-extention
I have built a chrome extension and I'm getting hit by a race condition that I need help with.
If you see the answer chrome extension: sharing an object between content scripts and background script it tells us that you cannot share a variable between content and background scripts.
My goal is to generate a unique ID per-browser tab and then share it in between the content.js and the background.js. Then I need to use this value in a content injected javascript as explained in this answer: In Chrome extensions, can you force some javascript to be injected before everything?
The only way I have been able to figure out how to do this is by doing the following async code then I just use the tab ID as the unique ID:
content.js
await pingBackground();
async function pingBackground() {
var info;
await new Promise(function (resolve, reject) {
chrome.runtime.sendMessage({ type: 1 }, function (response) {
if (response !== undefined) {
info = response;
resolve();
}
else {
reject();
}
});
});
console.log("Id is " + info.id);
}
background.js
chrome.runtime.onMessage.addListener(messageHandler);
function messageHandler(message, sender, reply) {
switch (message.type) {
case 1:
reply({ 'id': sender['tab'].id, 'active': true });
break;
}
}
manifest.json
{
"name": "oogi",
"version": "0.1",
"manifest_version": 2,
"background": {
"scripts": [
"common.js",
"background.js"
],
"persistent": true
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["content.js"],
"run_at": "document_start"
}
],
"permissions": [
"contentSettings",
"webRequest",
"webRequestBlocking",
"*://*/*"
]
}
But the problem with this is by the time I get the tab ID from background js, the script's content has already been loaded.
Is there some way to make it so this variable can be asynchronously shared between background.js and content.js? Or is this simply impossible?
Can I switch it around and have background.js load a variable from content.js asynchronously?
UPDATE:
A terrible hack which works is to do this in the foreground of the content.js:
var sleepScript = document.createElement('script');
var sleepCode = `function sleep (ms) {
var start = new Date().getTime();
while (new Date() < start + ms) {}
return 0;
}
sleep(500);`;
sleepScript.textContent = sleepCode;
(document.head || document.documentElement).appendChild(sleepScript);
This will force the page to wait for a bit giving the time to query the background before running the inline dom.
It works but that's awful.
This question was already answered previously, although it is hard to tell that this is the same issue at first glance.
https://stackoverflow.com/a/45105934
The answer is pretty descriptive so give it a read.
Here is the script changes that make it work:
// background.js
function addSeedCookie(details) {
details.responseHeaders.push({
name: "Set-Cookie",
value: `tab_id=${details.tabId}; Max-Age=2`
});
return {
responseHeaders: details.responseHeaders
};
}
chrome.webRequest.onHeadersReceived.addListener(
addSeedCookie, {urls: ["<all_urls>"]}, ["blocking", "responseHeaders"]
);
// inject.js
function getCookie(cookie) { // https://stackoverflow.com/a/19971550/934239
return document.cookie.split(';').reduce(function(prev, c) {
var arr = c.split('=');
return (arr[0].trim() === cookie) ? arr[1] : prev;
}, undefined);
}
var tabId = getCookie("tab_id");
I'm building a chrome extension which communicates with a nodejs server through websockets. The point of it is to track browsing history with content. It all seems to work, but occasionally (30% of the time) the callback in a function passed to onMessage.addListener doesn't fire correctly. Let me show you the code:
background.js
var socket = io('http://localhost:3000/');
var tabLoad = function (tab) {
socket.emit('page load', tab);
};
var tabUpdate = function (tabid, changeinfo, tab) {
var url = tab.url;
if (url !== undefined && changeinfo.status == "complete") {
tab.user_agent = navigator.userAgent;
tab.description = '';
tab.content = '';
socket.emit('insert', tab);
}
};
socket.on('inserted', function(page){
socket.emit('event', 'Requesting page content\n');
//page = {tab: page, id: docs._id};
chrome.tabs.sendMessage(page.tab_id, {requested: "content", page: page}, function(data) {
socket.emit('content', data);
});
});
try {
chrome.tabs.onCreated.addListener(tabLoad);
chrome.tabs.onUpdated.addListener(tabUpdate);
} catch(e) {
alert('Error in background.js: ' + e.message);
}
content script - public.js
var messageHandler = function(request, sender, sendContent) {
if (request.requested == "content") {
var html = document.getElementsByTagName('html')[0].innerHTML;
var data = {
content: html,
page: request.page
};
sendContent(data);
return true;
}
};
chrome.extension.onMessage.addListener(messageHandler);
The problem is that sometimes data in sendContent is undefined, while sometimes it is alright. Any ideas how to debug this or what i'm doing wrong?
I've tried replacing document.getElementsByTagName('html')[0].innerHTML with a hardcoded 'test' string, but that didn't help.
Pages like youtube/wikipedia seem to never work, while facebook/google works.
Edit: The sendContent callback does fire 100% of the time it's just that the data passed to it is undefined.
Edit: Here's the manifest file
{
"manifest_version": 2,
"name": "Socket test",
"description": "sockets are cool",
"version": "1.0",
"permissions": [
"http://st-api.localhost/",
"http://localhost:3000/",
"tabs",
"background",
"history",
"idle",
"notifications"
],
"content_scripts": [{
"matches": ["*://*/"],
"js": ["public/public.js"]
//"run_at": "document_start"
}],
//"browser_action": {
// "default_icon": "logo.png",
// "default_popup": "index.html"
//},
"background": {
//"page" : "background.html",
"scripts": ["socket-io.js", "background.js"],
"persistent": true
}
}
First off, your understanding that sendContent is executed 100% of the time is wrong.
As established in the comments, the sendMessage callback also gets executed when there was an error; and this error is, in your case, "Receiving end does not exist"
The error lies in your manifest declaration of the content script. A match pattern "*://*/" will only match top-level pages on http and https URIs. I.e. http://example.com/ will match, while http://example.com/test will not.
The easiest fix is "*://*/*", but I would recommend the universal match pattern "<all_urls>".
With that fixed, there are still a couple of improvements to your code.
Replace chrome.extension.onMessage (which is deprecated) and use chrome.runtime.onMessage
Modify the sendMessage part to be more resilient, by checking for chrome.runtime.lastError. Despite the wide permission, Chrome still won't inject any content scripts into some pages (e.g. chrome:// pages, Chrome Web Store)
Make sure you use "run_at" : "document_start" in your content script, to make sure onUpdated with "complete" is not fired before your script is ready.