How would I iterate through all tabs a user has open and then check if they have a particular HTML item with id = 'item'?
It appears this method has been deprecated in favor of chrome.tabs.query:
http://developer.chrome.com/extensions/tabs.html#method-query
So now you'd want to do:
chrome.tabs.query({}, function(tabs) { /* blah */ } );
Passing an empty queryInfo parameter would return all of the tabs.
You can make it like this :
chrome.tabs.getAllInWindow(null, function(tabs){
for (var i = 0; i < tabs.length; i++) {
chrome.tabs.sendRequest(tabs[i].id, { action: "xxx" });
}
});
After that to look after your item, if you can make it like this :
document.getElementById('item')
Don't forget that you can't manipulate the HTML by using the "background page" So the first code snip is for the background page, and the second have to be on a content script ;)
Edit July 2022:
This answer was originally written for MV3 users. If you're looking for a MV2 answer, see above. Otherwise, this answer will work perfectly fine with MV3.
Original answer
chrome.tabs.query is the function you're looking for. You can pass in parameters to a object to filter the tabs. In your case, you want to iterate over all the open tabs. Here's two versions of the code you're looking for:
Synchronous
chrome.tabs.query({}, function(tabs) {
tabs.forEach(function (tab) {
// do whatever you want with the tab
});
});
Asynchronous
var tabs = await chrome.tabs.query({});
tabs.forEach(function (tab) {
// do whatever you want with the tab
});
In both cases, the parameter tab is a Tab.
This is a not deprecated vanilla way (may 2019):
chrome.tabs.query({}, function(tabs){
tabs.forEach(tb => {
chrome.tabs.sendMessage(tb.id, { action: "xxx" });
});
});
Related
I'm trying to scrape values from a page that have been generated by a JS script. When I inspect the page, i see the values there, but my selectors return null/undefined.
The purpose of the extension is to allow people, on click of a button, to scrape their personalised data from a page that requires login WITHOUT having to provide any login details to the extension.
In chrome-console, the static "title" values return, so i'm pretty sure my selectors are fine and it's just that accessing the document doesn't count for the executed scripts.
From reading, I might need to use something like pupeteer or selenium, but it seems they fire up their own browser instance (bad, as I'd need to take user login details to mock the sign in process) or i'd need to modify how the chrome browser starts with --remote-debugging-port=A_PORT_NUMBER which i want to avoid.
From chrome console and my extension, I can retrieve the values highlighted green, (so it is not an issue with iframes as some posts suggest) and can't retrieve values highlighted red.
HTML structure in image
From popup.html
document.addEventListener("DOMContentLoaded", function () {
...
document.querySelector('button[id="scrape"]').addEventListener("click", function onclick() {
chrome.tabs.query({ currentWindow: true, active: true },
function (activeTab) {
chrome.tabs.sendMessage(activeTab[0].id, { action: "putSource_scrapeSalePage", index: activeTab[0].index })
}
)
})
...
}, false)
From content.js
//Need to import pupeteer/selenium here? How else to use it for active tab?
chrome.runtime.onMessage.addListener(
function (request, sender, sendResponse) {
...
else if (request.action === "putSource_scrapeSalePage") {
let htmlvar = $(document)
console.log(htmlvar);
let test = $('td[desc= "transactionType"]').text().trim() //returns fine
let tableData1raw = $('table.tableDataOne tbody tr').find("tbody").find("tr")
let tableData1raw_almost = $(tableData1raw).each(function (i, element) {
console.log(element)
const $element = $(element).find("td")
console.log($element)
...
The Question:
If there is no better way to do this, how can I do this from content-script with something like pupeteer?
In the end I was able to use the Value i know i COULD get ("transaction type" title) and use it to traverse to it's sibling element (+) and retrieve whatever Div was there, instead of trying to target the Div class directly.
$('td[desc= "transactionType"] + td').find("div").text();
I would like to get elements from chrome tab with a certain URL, it does not have to be active. so far I have:
Test()
function Test() {
chrome.tabs.query({url: "https://www.somewebsite.com/*"}, function(results) {
chrome.tabs.executeScript(results[0].id,{code: 'El = document.getElementsByClassName("someclass")'});
console.log(El);
})
}
Maybe it has to be done through a content script file?
I have this code placed in my background.js file. Given the proper URL and Class this function will not return the Element. Why?
Thanks for any suggestions!
The background script is executed only once when chrome launches. Instead of writing this inside a function which you call in the same file, you have to put it in some kind of event listener. This code should stay in the background file, but has to be triggered by something, not just executed on extension load.
EDIT: Oh, I see, you're trying to pass the element to the background script. You can't.
Background scripts have access to the whole chrome APIs but not to the page content. The script you write in executeScript runs on the tab you specified, so the variables are available to other code within that tab, not within your background script. Extension code that can run on the page and edit its content is either sent using executeScript or put in content scripts.
If you want to share information between various layers of an extension, you need to use messages. They work like events and listeners. Read the docs here. You'll be able to pass data like numbers and strings, but not the actual HTMLElement. So any code that manipulates the DOM has to run on the tab itself.
If your code is small and simple, you could just write it in the executeScript call instead.
You are correct, there does not appear to be a way to manipulate elements in another tab from the background.js;
Test();
function Test() {
chrome.tabs.query({
url: "https://www.google.com/*"
}, function(results) {
if (results.length < 1) { //if there is no tab open with google.com
console.log("Tab not found, creating new tab");
chrome.tabs.create({
"url": "https://www.google.com/",
"selected": true
}, null);
chrome.tabs.query({
url: "https://www.google.com/*"
}, function(results) {
if (results.length >= 1) {
console.log("Found google.com in the new tab");
}
chrome.tabs.executeScript(results[0].id, {
code: "foo = document.getElementsByTagName('input'); console.log(foo,' This is sent from the active tab');
chrome.storage.local.set({
'foo': foo[0]
});
"},function(foo) {chrome.storage.local.get("
foo ", function(foo) {console.log(foo , 'This has retrieved foo right after saving it to storage.');});});
});
}
});
}
search()
function search(result) {
chrome.storage.local.get("foo", function(result) {
console.log(result, 'This has retrieved foo from storage in a separate function within background.js');});
}
the first console log will show the html collection object for the inputs from google.com inside the google.com tab console, the other two will show blank objects in the background.js console. Thanks, I will find another solution.
I need to modify the DOM of a page but can not find how to get the elements of the current window.
window.onload = function() {
var bot = document.getElementById('bot');
bot.style.cursor = "pointer";
bot.addEventListener('click',function(){
chrome.tabs.query({
currentWindow: true,
active: true
}, function(tab) {
// tab[0] <----- I need to get the elements of the current window to modify
// for example document.getElementById('element')
});
});
}
I assume the code you showed runs in a popup.
To access DOM of a page open in an existing tab, you need Content Scripts.
See this Architecture Overview as the first step.
For a concrete example, given a tab ID, you can inject a script and get a single value back (as a simplest solution) like this:
chrome.tabs.executeScript({file: "content.js"}, function(result){
console.log(result);
});
And the content script:
// Simplest possible DOM operation
return document.getElementById('element').value;
For more advanced usage, see the documentation link above and Messaging documentation.
What I want to do is to run go() function in image.js file. I've googled around and I understand that is not possible to run inline scripts.
What is the best method to call the JavaScript I want? Events? Messages? Requests? Any other way?
Here is my code so far:
background.js
chrome.browserAction.onClicked.addListener(function(tab) {
var viewTabUrl = chrome.extension.getURL('image.html');
var newURL = "image.html";
chrome.tabs.create({
url : newURL
});
var tabs = chrome.tabs.query({}, function(tabs) {
for (var i = 0; i < tabs.length; i++) {
var tab = tabs[i];
if (tab.url == viewTabUrl) {
//here i want to call go() function from image.js
}
}
});
});
image.html
<html>
<body>
<script src="js/image.js"></script>
</body>
</html>
image.js
function go(){
alert('working!');
}
There are various ways to achieve this. Based on what exactly you are trying to achieve (which is not clear by your question), one way might be better than the other.
An easy way, would be to inject a content script and communicate with it through Message Passing, but it is not possible to inject content scripts into a page with the chrome-extension:// scheme (despite what the docs say - there is an open issue for correcting the docs).
So, here is one possibility: Use window.postMessage
E.g.:
In background.js:
var viewTabURL = chrome.extension.getURL("image.html");
var win = window.open(viewTabURL); // <-- you need to open the tab like this
// in order to be able to use `postMessage()`
function requestToInvokeGo() {
win.postMessage("Go", viewTabURL);
}
image.js:
window.addEventListener("message", function(evt) {
if (location.href.indexOf(evt.origin) !== -1) {
/* OK, I know this guy */
if (evt.data === "Go") {
/* Master says: "Go" */
alert("Went !");
}
}
});
In general, the easiest method to communicate between the background page and extension views is via direct access to the respective window objects. That way you can invoke functions or access defined properties in the other page.
Obtaining the window object of the background page from another extension page is straightforward: use chrome.extension.getBackgroundPage(), or chrome.runtime.getBackgroundPage(callback) if it's an event page.
To obtain the window object of an extension page from the background page you have at least three options:
Loop through the results of chrome.extension.getViews({type:'tab'}) to find the page you want.
Open the page in the first place using window.open, which directly returns the window object.
Make code in the extension page call a function in the background page to register itself, passing its window object as a parameter. See for instance this answer.
Once you have a reference to the window object of your page, you can call its functions directly: win.go()
As a side note, in your case you are opening an extension view, and then immediately want to invoke a function in it without passing any information from the background page. The easiest way to achieve that would be to simply make the view run the function when it loads. You just need to add the following line to the end of your image.js script:
go();
Note also that the code in your example will probably fail to find your tab, because chrome.tabs.create is asynchronous and will return before your tab is created.
I am writing a chrome extension that when clicked, will close the current tab after a given amount of time.
I am sending a message with the time, from popup.js to background.js. But the tab won't close.
The alert works when I uncomment it, so it seems to be just the remove line. I assume it's something about tab.id.
chrome.extension.onMessage.addListener(
function message(request, sender, callback) {
var ctr = 0;
ctr = parseInt(request.text, 10);
setTimeout(function() {
chrome.tabs.getCurrent(function(tab) {
//window.alert("Working?");
chrome.tabs.remove(tab.id, function(){});
});
}, ctr);
}
);
1.
chrome.extension has no onMessage event. I assume you mean the correct chrome.runtime.onMessage
2.
You have probably misunderstood(*) the purpose of chrome.tabs.getCurrent:
Gets the tab that this script call is being made from. May be undefined if called from a non-tab context (for example: a background page or popup view).
Since, you are calling it from a non-tab context (namely the background page), tab will be undefined.
(*): "misunderstood" as in "not bother to read the manual"...
3.
It is not clear if you want to close the active tab at the moment the timer is set or at the moment it is triggered. (In your code, you are attempting to do the latter, although the former would make more sense to me.)
The correct way to do it:
chrome.runtime.onMessage.addListener(function message(msg) {
var ctr = 0;
ctr = parseInt(msg.text, 10);
setTimeout(function() {
chrome.tabs.query({ active: true }, function(tabs) {
chrome.tabs.remove(tabs[0].id);
});
}, ctr);
});
Also, note that using functions like setTimeout and setInteval will only work reliably in persistent background pages (but not in event pages). If possible, you are advised to migrate to event pages (which are more "resource-friendly"), in which case you will also have to switch to the alarms API.