I have been looking for the answer to this question, but it only took me to the solution for debugging the extension itself, while I want to debug the webpage.
I want to create an extension that allows me to modify a particular web page (obviously only on my computer).
I have created a very simple script following the "activeTab" permission tutorial on the chrome developer's site, then I have made the following:
// Called when the user clicks on the browser action.
chrome.browserAction.onClicked.addListener(function(tab) {
// No tabs or host permissions needed!
chrome.tabs.executeScript({
code:'
var oSwitchContainer = document.getElementById("norm");
console.log(oSwitchContainer.childNodes);
'
});
});
When I inspect the page I cannot see any console messages, however, I can change the content of that oSwitchContainer easily by modifying its innerHTML.
Is there any way to see the console logs of the page after I enable the extension?
You say that you found solutions to debug "the extension itself".
Take a look at the Architecture Overview.
What you refer to as "extension itself" is its background page, and that's where your browserAction.onClicked listener executes. If you execute console.log() statements from that code, it will go to the background page's console.
However, using chrome.tabs.executeScript, you pass code to be executed in the context1 of an open tab instead of executing it in the background page. All console.log() calls from that context go to the open page's own console - you should look there.
console.log("This will show in the background page console");
chrome.tabs.executeScript(
{ code: 'console.log("This will show in the current tab console");' }
);
1 To be precise, the extension creates an isolated context, but it still belongs to the open page. See Content Scripts documentation for more details.
Related
So, I've got a situation where I want a background and content script to be run everytime the browser extension icon is clicked. The ideal behaviour is that extension icon is clicked, the script runs, and the popup will open, displaying the data that was grabbed by the script (but this should happen quickly, the script runs and gets the data very fast). Since chrome.pageAction.onClicked will not work if there is a default_popup defined in manifest.json, I think this leaves me with two options:
Use default_popup and figure out that the extension icon has been clicked some other way. I found this solution in another stack overflow post, but the workaround is to use default_popup: "popup.html", and then the popup.js that is defined in popup.html will send a message saying that the icon has been clicked, then when background.js receives this message, it executes the script. I implemented this idea and it worked... kinda. The trouble is, the popup will always come up before the script is fully executed, so you can't actually display the data grabbed by the script in the popup until the next click. I'm not sure there's any way to get the behaviour I desire using this method, so on to the next:
The other solution I can possible think of is to use onClicked, and then make the popup come up some other way, besides using default_popup in manifest.json. I'm also not sure if this is possible, I have looked on stackoverflow and haven't found anything similar.
Is the second method possible? Can the first method work somehow?
Your option #1 is correct, I think all that is needed is a loading screen when the user first sees the popup, and add some code that updates the popup as soon as it hears from the backend. Might need to see some code to better help there.
Option #2 will not really work, unless you opened the popup in a new tab (or just made it a whole new HTML page). I say this because there is a note here from the Chrome Dev team they will not support opening a popup unless it is from a user gesture -- https://stackoverflow.com/a/10484764/4875295.
If you wanted to go that route it would probably look something like:
Delete from your manifest.json browser_action.default_popup
In your background script add something like:
chrome.browserAction.onClicked.addListener(() => {
const data = dataMaker();
chrome.tabs.create({
url: `${chrome.runtime.getURL("app.html")}?data=${data}`
});
});
Then in your html file have some JS that reads the query string and updates the page accordingly.
Though, that's a different approach than you asked for and I think your original route may still be the best bet (with some added JS around loading).
There are quite some similar question but they all comes down to chrome.tabs.getSelected or chrome.tabs.query API which is not suitable in my case.
Basically what I need to do is to get an id of the tab where the script is running from - so it's not necessarily an active or selected tab.
As per the doc:
getCurrent chrome.tabs.getCurrent(function callback)
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).
Which implies that it should work from content script but chrome.tabs is undefined in content script. Why is it so? Is there any way to know this tab data (from where the content script is running and not from selected or active tab)?
Even though the doc says the tabs permission is not mandatory for the most APIs I've anyway added it to the manifest with no luck:
{
"manifest_version": 2,
"name": ...
"permissions": [
...
"tabs",
...
}
Any ideas are much appreciated
The use case for get current/this tab is that when the extension does its work it needs to reload a page where it's running from as part of the working flow and user can be on different tab or in different window. The extension's script still needs to get the correct tabId to keep working as expected.
Effectively, you can only use chrome.tabs.getCurrent on a page from the chrome-extension:// scheme (opened as your extension's Options page or via chrome.tabs.create or chrome.windows.create), or if you're using chrome_url_overrides. The background, popup, and embedded options pages don't have a current tab, and the API doesn't exist in a content script.
Well, seems like I've figured out an answer to my question.
The trick is to send a message to background script and extract sender data from there. Sender will include tab object where the script is running from.
I'm using ports so this is what I'll describe as example below:
On content script side:
var port = chrome.extension.connect({
name: "some name"
});
port.postMessage({"key":"some json here"})
On Background side:
chrome.extension.onConnect.addListener(function (port) {
console.log(port.sender.tab)
})
port.sender is a MessageSender object that will contain tabId (and tabUrl if "tabs" permission is added to the manifest)
In my case I'm just sending tabId back from background to the content script:
port.postMessage({"tabId":port.sender.tab.id})
More on this can be found in messaging doc and in this api doc
I am building a simple chrome extension that will only work on a specific domain, and I'm now facing the problem of detecting a tab's close in background page. As I have read around, there is no specific way to achieve it, but window.unonload and window.onbeforeunload can help detecting page variatons (though they also get fired if the page is reloaded). However, since the extension is programmed to work in the same tab but it has to reload the page many times, it would be a bad solution for my case. So this is what I thought:
1-at the very beginning of his execution, the content script sends a message to the backpage, which will now be able to detect the sender tab id and store it to a global variable.
2-then, the backpage should programmaticaly check if a tab with that id is currently open, for example in a while loop or something like it.
3-as the control returns false, the backpage will know that the page has been closed and the extension isn't running.
This one seems to be a simple and short way to check if a page is open, but I can't find a way for the backpage to determine if a tab with a certain id is open. I've also read chrome documentation (https://developer.chrome.com/extensions/tabs) but it sounds like there isn't a specific function to do this. So what I'm wondering about is: what might be a work-around to solve this problem? Might message passing be helpful?
P.S. I'm not using jquery.
Thanks for help.
*EDIT:**
I forgot to say that I have already tried this code but it's not changing the running variable
background.js
var running;
while (0!=1){
chrome.runtime.sendMessage(idtab, {quest: "running"}, function(response) {
if (response.ans!="yep"){
running=false;
}
});
}
content_script
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.quest=="running"){
sendResponse({ans:"yep"});
}
});
-the idtab variable in background is defined before and it already contains the id of the tab in which the script is workin
I would know what is the difference between those three lines of code :
console.log("");
document.write("");
alert(""); (or windows.alert("");)
console.log("") outputs whatever is passed as the parameter.
e.g. console.log("test") will output "test" to your console
document.write("") adds whatever you want to html.
e.g. document.write("<p>paragraph</p>") will add a new paragraph to a document
alert("") is a popup alert.
console.log('foo');
will write 'foo' in your debugging console. You can access it via F12 on most browsers or right click on your page and inspect. You should see a "console" panel on the debugging window.
Be careful of what information you dump, it will be shown to every one browsing the page. Some browsers may not like those logs and you could encounter errors on production websites if you forget to remove them.
document.write('foo');
will append 'foo' to the DOM of your current page. This statement is not to be used for debugging purpose.
alert('foo');
will display a popup window to your browser with a single button to close it. "foo" will be the text displayed on the popup. You can use this method to send very important information to the person browsing the page, but try not to abuse of them as they "block" the visitor by requiring to dismiss the modal before doing anything else.
Developers use console.log() for logging useful info.
document.write modifies what user sees in the browser by adding additional content to DOM.
Alerts are used to alert end users who access the web page.
console.log() is used by developers to just debug their code by printing the value inside console.log() in their console......
document.write() is used to add something to the webpage
How can a chrome extension click on a button on active page?
There is banal page on the Web. There is simple element of button type with specific ID on the page. Also there is Chrome extension with a button. I'd like to click to extension button and the button in turn clicks to page button.
var someElement = document.getElementById('someElement');
someElement.addEventListener('click', function () {
// here I'd like to click on a button with specific ID.
});
The very first thing you want to do is to read the Overview page, especially the Architecture part. Read it thoroughly, and it will answer many questions you have.
Your problem can be split into two parts.
How to trigger something with a click on your extension.
How to click something inside the active tab.
Before I proceed, I'll reiterate what wOxxOm said: there's a great small example in the docs that does nearly what you want. But if you want to be someone taught to fish, not given a fish, read on.
How to trigger something with a click on your extension
It depends on what kind of UI you're using; the simplest is a Browser Action button.
Simplest button:
If you add a browser action to the manifest without specifying a popup:
"browser_action": {
"default_icon": { "38": "icon38.png" }
},
then clicking on it will raise chrome.browserAction.onClicked event to your extension's pages. The only page open at any time you need it is a background page, the role of which is usually the central dispatch for extension events. So, you need a background page that listens to that event:
"background": {
"scripts": ["background.js"]
},
and
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
// Okay, the actual action should go here
// And look, we already have the required Tab object for free!
});
Variations (exercises for the reader):
If you do specify a "default_popup" in your manifest, then chrome.browserAction.onClicked will not trigger. Instead, a small popup page will open with the HTML file you specify; you can add UI/logic there as you wish, the principle will be the same as normal webpages but with Chrome API access, except:
You'll need to query for the current tab yourself.
You'll need to be mindful of Chrome's extension CSP.
In case you run into problems, you need to know how to debug them.
If your extension targets only a few specific pages, consider using Page Actions instead of Browser Actions.
As noted in the documentation, Event pages are preferable to Background pages. In this case, you can use an Event page easily without any side effects, but in general this may require some thinking.
You could inject your own UI into the page itself with Content scripts; this is an advanced topic and will not be covered here.
How to click something inside the active tab
Since you've read the Architecture overview, you already know that the only part of the extension that can interact with the DOM of an open page is a Content script.
Content scripts can either be specified in the manifest (and then they will automatically be injected and ready for you when a matching page is opened), or they can be manually injected into the page.
In your case, you want to do something simple, and only when clicked. This is a perfect job for programmatic injection, so we'll stick with that.
Assuming the solution from the previous section, you are in a context of a background page and already have the current tab as the tab variable:
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
// Do something with tab
});
Programmatic injection is done with chrome.tabs.executeScript method. At a minimum, you need to specify the tab you want to inject to, and the code that will be run:
// background.js
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript(tab.id, {
code: "document.getElementById('#specificId').click()"
});
});
That's not all yet though. The extension must have permissions to execute code in an open tab.
You could use "host permissions" that are defined by a match pattern to give access to specific pages, but remember that we are only triggering it when the user clicks the extension.
For that specific case, there's a special permission "activeTab". It is sufficient to do a lot of things with the currently active tab when the extension is explicitly invoked, and clicking its button is explicit enough.
So, add to manifest:
"permissions": ["activeTab"],
And that should be all you need for this to work.
Extra credit:
While not necessary for this simple purpose, you may want more complicated code than a single line to be executed in the tab. Then it makes sense to use a separate file and invoke executeScript with "file" instead of "code".
Just triggering a click on a button does not require you to directly interact with JavaScript running in the page itself, as DOM events are shared. However, it's important to understand that normally, content scripts can't interact with the page's own scripts, which is called "isolated world". There are ways to bypass it if you really need it, but it's an advanced topic better explained elsewhere.
Sometimes you need the content script to persist, answer some commands and maybe send its own queries to the extension's pages. In that case, it's probably better to auto-inject through the manifest and use Messaging instead of executeScript.