Pass variables to current tab via chrome extension - javascript

I am writing my first chrome extension, and I want to pass a variable to the currently opened tab and manipulate the DOM with it.
My extension has a button, and when clicked, is executing this code:
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.executeScript(tab.id, {
file: 'tabscript.js'
});
});
This works fine, but I see no way to pass a variable to tabscript.js so it can be used on the opened tab.

What do you need to pass a variable in to? Do you have a function you are calling in your script?
It must be noted that you don't have access to the pages Javascript, just the DOM.
If you have a particular function that you have to call with specific parameters then you should investigate content scripts and message passing.
Content scripts can get run on every page load (or a selection of pages), and you would use message passing to send a message from your extension button to the function in the content script.
Alternativly, and closer to your original idea you can construct the function you want to call at run time using the following:
chrome.tabs.getSelected(null, function(tab) {
chrome.tabs.executeScript(tab.id, {
code: 'function(){ ...... your code built dynamically ......}'
});
});

Related

How to stop content script from starting itself

My manifest.json matches every URL. So, whenever, a URL is opened in Chrome, the content script is activated.
Current procedure:
(1) My background script updates the current empty URL to something.
(2) My content script is activated, because it's a match in the manifest.json. My content script needs to visit multiple URLs on the same website. It clicks on a link and navigates back (= everything in one tab) -> The content script calls itself again, because another URL is opened.
Problem:
I don't want content script to call itself multiple times. It should for example only activate, when it gets a message from the background script that a new URL was opened.
Thoughts:
chrome.windows.onCreated.addListener(function()) is around all my code in the background script. Is there something similar for the content script that i'm looking for?
Maybe something like:
window.addEventListener
browser.runtime.onMessage.addListener
browser.runtime.sendMessage
browser.runtime.onMessage
browser.tabs.sendMessage()
But i don't understand how to use them properly.
In your background script, do this:
chrome.windows.onCreated.addListener(function(tab) {
let msg = {txt: "execute"};
chrome.tabs.sendMessage(tab.id, msg);
};
And then in your content script, you can intercept this message via:
chrome.runtime.onMessage.addListener(function(msg){
if(msg.txt == "execute") {
doSomething();
}
});
Docs here.
https://developer.chrome.com/extensions/runtime#method-sendMessage
https://developer.chrome.com/extensions/runtime#event-onMessage

Chrome Extension pass node reference from Content Script to DevTools page

I have a simple extension that uses a devtools page to provide debugging functionality. When the browserAction icon is clicked, that page inserts a content script in the current tab. How do I pass a DOM node (or nodeId) from the content script back to the devtools page so that inspect() can be called on it?
Here's what I tried
manifest.json contains:
"devtools_page": "devtools.html"
devtools.html contains:
<script src="devtools-script.js"></script>
devtools-script.js:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {
file: 'js/content.js'
}, function () {
chrome.devtools.inspectedWindow.eval('window.searchedElement', {
useContentScriptContext: true
}, function (result) {
console.log('resulting element:', result);
});
});
});
content.js:
window.searchedElement = document.body;
(I used document.body to simplify the example)
Now, when I navigate to a page, open DevTools and click on the extension, the following happens:
DevTools opens my devtools.html page (when DevTools is opened, before the click)
My devtools page injects my content script (after the click)
Content script code sets the searched element
DevTools page performs eval() to return the element previously set by the content script
The resulting log from the devtools-script is:
resulting element: {}
So apparently you can't pass the node that easily. I can maybe give the element an id attribute in the content script and then query it in the devtools page, but that seems clumsy.
Is there a better way to get access to an element in the inspected window from the devtools page?

Chrome Extension: Close opened tab, regardless of if its the current one

This is the background.js of my chrome extension. I am a newbie on programming chrome extensions.
How can I close the tab that is opened in this example regardless of if the tab is the current one or not?
chrome.tabs.onCreated.addListener(function() {
chrome.tabs.query({'active': true, 'lastFocusedWindow': true},
function (tabs) {
url = tabs[0].url;
if (url=="http://www.example.com"){
setTimeout(function(){
//would be nice to have this executed after EVERYTHING on the page is loaded
chrome.tabs.executeScript(null, {file:"jquery-1.11.1.min.js"});
chrome.tabs.executeScript(null, {file:"contentscript.js"});
},17000);
setTimeout(function(){
tabs[0].remove();
alert('tab removed');
},25000);
}
}
);
});
The comment from gui47 is correct - just use chrome.tabs.remove(... the tab id you want to remove ...)
A lot of this code could use improvement, assuming I understood your intent.
Imagine that you middle-click a link in a tab. It creates a tab in the background, and calls onCreated listener for it.
Yet you query for the active tab, and get the initial tab, not the new one.
You should use the fact that the tab information is passed into onCreated.
chrome.tabs.onCreated.addListener(function(tab) { // Can have a parameter!
/* use tab.url and tab.id */
});
When you call executeScript, it still obeys the run_at parameter, which is by default at document_idle. That means it will not execute until the static DOM is ready at least.
So no need for the timeout. To be extra safe, you can wrap your content script code in $(document).ready()
If you want to wait until the resources like images are loaded, you can use load event of the body.
If you need to wait until a specific event happens / some scripts execute in the page, you can use timeout and/or something like MutationObserver. However, it would make more sense then to add this waiting to contentscript.js and not the injecting code.
chrome.tabs.onCreated.addListener(function(tab) { // Can have a parameter!
// Assuming you want to check the address contains that and not exactly it
if(~tab.url.indexOf("http://www.example.com")) {
chrome.tabs.executeScript(tab.id, {file:"jquery-1.11.1.min.js"});
chrome.tabs.executeScript(tab.id, {file:"contentscript.js"});
}
});
Finally, if you want to close the page, you could call chrome.tabs.remove(tab.id).
But it might be better to simply close it from the content script - it might know better when to do it.
From the content script, you can simply use window.close() if your extension opened the tab.
You should use chrome.tabs.remove(tab.id); to close the tab.
See chrome.tabs.remove( ).
Use chrome.tabs.remove(tabId);
Whereas tabId is the id of your tab. If you don't know where to get the tabId you can use the below code in your background.js.
chrome.tabs.query(
{
active:true,
windowType:"normal",
currentWindow: true
},
function(d)
{
tabId = d[0].id;
});

How do I make it so my Chrome extension will only inject a script once?

I'm using programmatic injection to inject my extension's code into a page only when the browser action is clicked.
This is what I have on my extension's event page (per the example in the documentation):
chrome.browserAction.onClicked.addListener(function callback(tab){
chrome.tabs.executeScript(null, {file: "content-script.js"});
});
However, the way this works, the script is injected every time the button is clicked.
How can I change it so that the script is not injected on subsequent button presses - so that it is inserted only the first time the button is clicked on that page?
Put a global variable in your contentscript to judge if the contentscript has been executed.
if (something) { return; }
One way I can think of right now (easy and simple) is to use html5webstorage. Since you are running this code from your background or popup page it will be ok.
if(!localStorage.getItem("isAlreadyInjected")){
localStorage['isAlreadyInjected'] = "true";
chrome.browserAction.onClicked.addListener(function callback(tab){chrome.tabs.executeScript(null, {file: "content-script.js"});});}
So, the very first time when storage value "isAlreadyInjected" does not exist, the listener will be added. Afterwards, even when the browser closes and opens again this value will remain stored and so the listener will not be added to your extension.
UPDATE
As your background page loads only once at the beginning, it can keep variable that is not re-initialized with the browser action click. So you can use that variable to do your job!
background.js
var isAlreadyInjected =false;
function isInjected(){
if(!isAlreadyInjected ){
isAlreadyInjected=true;
return false;
}
else
return true;
}
popup.js
var bgpage=chrome.extension.getBackgroundPage();
if(!bgpage.isInjected()){
chrome.browserAction.onClicked.addListener(function callback(tab) {chrome.tabs.executeScript(null, {file: "content-script.js"});});
}
or
var bgpage=chrome.extension.getBackgroundPage();
chrome.browserAction.onClicked.addListener(function callback(tab) {
if(!bgpage.isInjected()){
chrome.tabs.executeScript(null, {file: "content-script.js"});
}});
I know this is an older question but I encountered the issue now that Manifest V3 is out and persistent background pages have been replaced with service workers. I figured I'd give what I used as a solution in case anyone else needs it. Code must be executed within the global context of the content script. Anytime it tries to inject it again, the relevant code will only be executed if the global variable is not defined.
if (typeof hasBeenExecuted === 'undefined') {
// Code that needs to execute only once goes here
}
var hasBeenExecuted = true;
Hopefully this is helpful for someone else who comes across the question.

Is it possible to run the injected script without reload?

I did a simple auto form filler, by sending info from a created html to the background and then the content script, so the injected script can change the info on the form.
I know the content script run once the page is load. I want to know if I can run the content script again, without the need of reloading the page.
I got sendRequest function in the content script, that I use to make sure it gets the info, only when the page is ready. It then add the info to the form, and wait for me to send it.
In the content script, I added a onRequest and it works (it get the info). but, I don't see the changes on the form, unless I am realoding the page.
I want to know if it is possible to do and if it does what subjects should I learn to implent this.
I am new to chrome extentions and I am still learning :)
in 1 of the pages, I use jQuery, so an answer with jQuery would be good too.
i found out that if we create a chrome.tabs.sendRequest from background we can use chrome.extestion.onRequest from content script and it will execute every time becuse they both run allmost in the same time.
so i did from background:
chrome.tabs.query({}, function (tabs) {
for (var i = 0; i < tabs.length; i++) {
chrome.tabs.sendRequest(tabs[i].id, {...requests u want to send }, function (response) {
});
}
});
from content script:
chrome.extension.onRequest.addListener(function (request, sender, sendRespons) {
//get requested info here
//call functions.
sendResponse({}); //send info back to background page.
});
form's target could be an iframe which would avoid page reload. not sure how useful it'd be.
The correct way to execute a content script again is by using the chrome.tabs.executeScript method. It receives two arguments. The first argument is the tabId, which can be obtained in many ways, such as one of the chrome.tabs events. Use null to execute the content script in the currently selected tab (caution: this may also be an active dev tools window!).
Examples:
// Reloads the current tab
chrome.tabs.executeScript(null, {code:'location.reload();'});
// Executes contentscript.js in the current tab
chrome.tabs.executeScript(null, {file:'contentscript.js'});
// Executes contentscript.js in all frames in the current tab
chrome.tabs.executeScript(null, {file:'contentscript.js', allFrames: true});
// Receives message from content script, and execute a content script:
chrome.extension.onMessage.addListener(function(details) {
if (details.message === 'load a content script') {
chrome.tabs.executeScript(details.sender.tab.id, {file: 'a_script.js'});
}
});
// The previous one is activated from a content script, as follows:
chrome.extension.sendMessage('load a content script');
(onMessage and sendMessage have to be used instead of onRequest and sendRequest, since Chrome 20)

Categories