Chrome Extension pass node reference from Content Script to DevTools page - javascript

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?

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

In background script, listen for newly opened tab

In my background script, or anywhere but a content script, I am trying to listen for the active tab, or a newly opened tab.
I have this, but this is apparently incorrect:
What I am trying to do is to inject a content-script into the active tab. I don't want my content script to run for every tab/window, just for select tabs.
Does anyone know how to inject a content script for certain tabs? I can't figure it out. I assume the best way to inject content scripts is from the background script.
If you want to run a code in already activated tab (chrome.tabs.query):
chrome.tabs.query({active: true, currentWindow: true}, function(foundTabs) {
const activeTabId = foundTabs[0].id;
chrome.tabs.executeScript(activeTabId, {
file: 'inject.js'
});
})
In case you want to inject a file into every newly activated tab (chrome.tabs.onActivated):
chrome.tabs.onActivated.addListener(function(activeInfo) {
chrome.tabs.executeScript(activeInfo.tabId, {
file: 'inject.js'
});
});
Be sure to check if you have injected the file already, in order to prevent multiple injections.
For both cases, permissions should include tabs.
I was missing the:
"webNavigation"
permission in manifest.json. After adding that permission, I now have:
chrome.webNavigation.onDOMContentLoaded.addListener(function (details) {
const tabId = details.tabId;
chrome.tabs.executeScript(tabId, {
file: 'inject.js'
});
});
now it works.
some chrome API we cant access in content script in that case do that code in background.js and do according action from background js. if you want to do some action in content script in that case you need to communicate using send message

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)

Change src of iframe from a popup window

In my web application, the user can open a popup window to select an edit an object. When the user presses OK on the popup, it's supposed to update the src of an iframe in the parent window (and of course reload the iframe) according to which object was selected.
My function (in the parent window) is:
function dismissEditPopup(win, newId) {
newId = html_unescape(newId);
var elem_iframe = document.getElementById("iframe_id");
// (*) this line doesn't work
elem_iframe.src = '/view_object/' + newId;
elem_iframe.contentWindow.location.reload();
win.close();
}
This function is called from a popup window, which contains a script:
<script type="text/javascript">
opener.dismissEditPopup(window, "hash_of_new_object");
</script>
The problem is that the line (*) fails silently. In the inspector in both Firefox 3.6 and Google Chromium, I see that the src attribute of the iframe is being updated, but elem_iframe.contentWindow.location.href is unchanged. (If I add a line elem_iframe.contentWindow.location.href = elem_iframe.src;, the assignment is ignored.). There are no errors in the Javascript error console. Strangely, it does work as expected if I assign to elem_iframe.src from the Javascript console.
I am able to change the value of a hidden <input> field in the same way, using document.getElementById("hidden_id").value = newId;.
Everything is served from the same website.
(Similar to Changing iframe src with Javascript, but the answers to that question don't work, presumably because the code is called from a popup.)
Take this line out:
elem_iframe.contentWindow.location.reload();
It's reloading the iframe and the new src is not loaded.

Pass variables to current tab via chrome extension

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

Categories