I’m developing a Chrome Extension for a specific website. The script is injected after page load, the script clicks a button, and an iframe popup is loaded.
inject.js
chrome.extension.sendMessage({}, function(response) {
var readyStateCheckInterval = setInterval(function() {
if (document.readyState === "complete") {
clearInterval(readyStateCheckInterval);
document.getElementById(‘aButton’).click();
NOTE: Unlike many similar questions on SO, the resulting iframe is same-origin. It is NOT from an external site, and there are no warnings in the inspector. (Also confirmed in Sources tab)
The script waits 3 seconds for the popup to fully load, accesses the iframe’s document object, then clicks on the target – AND nothing happens.
setTimeout(() => {
let doc = document.getElementById('iframeOne');
let innerDoc = doc.contentDocument || doc.contentWindow.document;
let innerDocElem = innerDoc.getElementById('innerDocElem');
innerDocElem.click();
}, 3000);
What I’ve Tried:
I’ve set my file’s content scripts to include all frames, and since the iframe is of same origin, the wildcard in matches should give the script access to the iframe.
manifest.json
"content_scripts": [{
"matches": "https://awebsite.com/*" ,
"js": "src/inject/inject.js" ,
"allframes": true
}]
I’ve tried using css transforms to move the element to top-left, and then using x,y coordinates to click on the iframe’s buttons
document.elementFromPoint(250, 204).click();
I’ve also tried replacing the click() method with a simulated mouse event as suggested in these answers:
How to simulate a mouse click using JavaScript?
JavaScript click() method only works once in Chrome extension
In a question, I saw people reach to web site's element with Chrome that using Javascript.
It was a big problem for me that get a element from a web site and simulate that. I create 2 question for that but I couldn't reach a answer clearly.
So my firs question was ; click a button on a web site automatically with javascript
My second question was : How can I create a code with Javascript which Automatically click buttons on a web site
Now I reached a lot of method and tried, found a success process like : How to get elements from a webpage DOM into my background.js?
After that point I want to ask "How Can I use a code to get element from IE browser. In this tag there was a code like "chrome.tabs.update". Is there any element that using get code from IE like that which similar to chrome method's.
And If I follow that method Can I access a element (Button,Textbutton or Check Button) to click Automatically?
That code taken from this title : How to get elements from a webpage DOM into my background.js?
"content_scripts": [
{
"matches": ["https://vaf.itslearning.com/elogin/"],
"js": ["content.js"]
}
],
var username = document.getElementById("ctl00_Username");
var password = document.getElementById("ctl00_Password");
var button = document.getElementById("ctl00_ButtonLogin");
if (username && password && button) {
username.value = "####";
password.value = "######";
button.click();
}
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
if (tab.url == "https://www.itslearning.com/Index.aspx?customerid=&username=&redirectlogin=itslearning.com&MustUseSsl=true&")
chrome.tabs.update(tabId, {"url": "https://vaf.itslearning.com/elogin/"});
}
I'm trying to build a basic Chrome extension that, from a browser action popup, opens a website in a new tab, and fills in the login credentials. I can get the Chrome extension to open the new page but can't seem to get it to input text into the input fields.
Manifest.json
{
"manifest_version": 2,
"name": "Vena",
"description": "This extension will allow users to login to vena accounts",
"version": "1.0",
"browser_action": {
"default_icon": "images/icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab"
]
}
popup.html
<!doctype html>
<html>
<head>
<title>Auto-Login</title>
<script src="popup.js"></script>
</head>
<body>
<h1>Login</h1>
<button id="checkPage">Login!</button>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function() {
var newURL = "https://vena.io/";
chrome.tabs.create({ url: newURL });
var loginField = document.getElementsByClassName('js-email form-control input-lg');
var passwordField = document.getElementsByClassName('js-password form-control input-lg');
loginField.value = 'gsand';
passwordField.value = '123';
}, false);
}, false);
How do I fill in the information in the input areas of the new tab?
Another time, you may want to use something other than a popup (e.g. just a plain browser action button, or a panel) to test out your functionality. It is easier to debug things other than a popup due to the fact that the popup will disappear under so many conditions. Once you have the basic functionality debugged, you can move it into a popup and deal with the issues specific to using a popup.
Issues
You need to use a content script to interact with web pages:
The primary issue is that you have to use a content script to interact with a web page, such as manipulating the DOM, as you desire to do. Content scripts have to be injected into the web page. This can be done with a content_scripts entry in your manifest.json, or with chrome.tabs.executeScript() from JavaScript that is in the background context (background scripts, event scripts, popups, panels, tabs containing pages from your add-on, etc.). For what you are doing, chrome.tabs.executeScript() is the way to go.
Additional issues:
chrome.tabs.create() is asynchronous. You need to wait for the callback to execute so the tab exists in order to inject a content script. You can not inject scripts into a tab that does not yet exist. Note: You could use other, more complex, methods of determining when to inject the content script, but the callback for chrome.tabs.create() is a good way to do it in this case.
Once you create the new tab, you want to inject a script. This is not the "active tab", so you need to add "https://vena.io/*" to your permissions in your manifest.json.
The elements you desire to interact with are not immediately available on the page when the content script is run. You need to wait until they are available. I just used a setTimeout loop to poll until the elements are available. I chose to poll on 250ms intervals a maximum of 100 times (25 seconds). The elements were there each time after the first delay.
document.getElementsByClassName() returns an HTMLCollection, not a single element.
Popups close when you activate a different tab. Once the popup is closed/destroyed, you can not do any more processing within the code for the popup. In order to get around that:
In your chrome.tabs.create(), include active:false to prevent the new tab from becoming active immediately.
Call chrome.tabs.update() in the callback for chrome.tabs.executeScript() to active the tab once the content script has been injected (i.e. when you are done with all the processing you are going to do in the popup).
Code
Changes were only needed in your manifest.json and popup.js.
manifest.json
{
"manifest_version": 2,
"name": "Vena",
"description": "This extension will allow users to login to vena accounts",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"permissions": [
"activeTab", //This is not needed as you never interact with the active tab
"https://vena.io/*"
]
}
popup.js
document.addEventListener('DOMContentLoaded', function() {
var checkPageButton = document.getElementById('checkPage');
checkPageButton.addEventListener('click', function() {
var newURL = "https://vena.io/";
//Must not make the tab active, or the popup will be destroyed preventing any
// further processing.
chrome.tabs.create({ url: newURL,active:false }, function(tab){
console.log('Attempting to inject script into tab:',tab);
chrome.tabs.executeScript(tab.id,{code:`
(function(){
var count = 100; //Only try 100 times
function changeLoginWhenExists(){
var loginField = document.getElementsByClassName('js-email form-control input-lg')[0];
var passwordField = document.getElementsByClassName('js-password form-control input-lg')[0];
//loginField and passwordField are each an HTMLCollection
if( loginField && passwordField ){
loginField.value = 'gsand';
passwordField.value = '123';
} else {
if(count-- > 0 ){
//The elements we need don't exist yet, wait a bit to try again.
//This could more appropriately be a MutationObserver
setTimeout(changeLoginWhenExists,250);
}
}
}
changeLoginWhenExists();
})();
`},function(results){
//Now that we are done with processing, make the tab active. This will
// close/destroy the popup.
chrome.tabs.update(tab.id,{active:true});
});
});
}, false);
}, false);
May need use document.execCommand('insertText', false, text);
Depending on the page, you may need/want to use:
document.execCommand('insertText', false, textValue);
If you do so, you will need to first select/focus the desired element. This would be instead of setting the .value property. Which you use will depend on what you are actually doing and the composition of the page you are altering. For the specific example in the question, setting the element's .value property works. For inserting text, using `document.execCommand('insertText') is more generally applicable.
May need a MutationObserver
In the above code I use a setTimeout() loop to delay until the desired elements exist. While that works in the above case, depending on your use case, it may be more appropriate for you to use a MutationObserver. Largely, which to use will depend on how immediately you need to respond to the desired elements being added and what type of load you are putting on the page by looking for the elements. For more information about watching for DOM changes see: Is there a JavaScript/jQuery DOM change listener?
UI comment
Currently you have a popup that has a single button: "Login". From a user interaction point of view, it would probably be better to just use a plain browser action button. If you are intending to add functionality to your popup, then go ahead and keep the popup. If you are not going to add functionality, it does not make a lot of sense to force your user to click twice (click: open popup, then click: login) when they could have just clicked once.
Use an actual Password Manager
If this is functionality that you desire, rather than just something you are putting together just to learn, you should use an actual Password Manager. The functionality of securely storing passwords and inserting them appropriately in websites is non-trivial. You can easily make mistakes that result in compromising your security. I strongly recommend that you investigate the various ones available and choose one which fits your needs. Basically, all the ones that I have seen would easily provide you with the functionality you have at this time: open a popup; select the site; go to the site and fill in the user name and password. A password manager is a very significant project. It is not a project to be taken on lightly, or for someone who is not experienced in security issues.
I have an extension that injects content scripts when the tab is updated.
Main script:
chrome.browserAction.onClicked.addListener(function(tab) {
"use strict";
const sendScriptToPage = function(tabId, changeInfo, tab) {
if (changeInfo.status === "complete" && tab && tab.url && tab.url.indexOf("http") === 0) {
console.log ("executeScript ");
chrome.tabs.executeScript(tabId, {
file: "content.js", allFrames: true
});
}
};
chrome.tabs.onUpdated.addListener(sendScriptToPage);
});
Content script:
const Content = (function() {
"use strict";
console.log("Content");
const makeRandomColor = function(){
let c = '';
while (c.length < 6) {
c += (Math.random()).toString(16).substr(-6).substr(-1)
}
return '#'+c;
};
document.body.style.backgroundColor = makeRandomColor();
}
)();
It works fine when I reload a tab. However, when a tab is reloaded dynamically, the content script gets reloaded although it is already loaded in the tab. This shows in the log since const can't be re-declared.
Should not content scripts be unloaded when an update occurs? How can I know if a content script is already loaded or not?
A URL that shows this behaviour:
http://www.prisjakt.nu/produkt.php?p=391945#rparams=ss=android
Typing something in the search field triggers the onUpdated event handler, but the content script is already in the page.
A test extension:
https://github.com/hawk-lord/chrome-test
A navigation would wipe the content scripts, but onUpdated can be triggered by many things.
The first that comes to mind is an iframe being loaded. The main page doesn't navigate but you do inject indiscriminately.
Many approaches are possible:
Make the content script more robust by checking for Content being defined before you do anything.
Listen to a different event, for example, various webNavigation API events. They will uniquely identify the frame they refer to.
Don't listen to events - always inject, and change your triggering logic (for instance, keep a flag in chrome.storage).
I'm writing a firefox xul extension, and I must have an interaction beetween the web page and extension.
Example: If I press a link from the page I want to call a function in the xul extension.
Anyone know if there is a way?
Thanks a lot
Yes, you can do this. You'll need to access page content with the content object.
In your extension code you can select all links and then add an eventListener:
allLinks = content.document.getElementsByTagName("a"),
for (var i=0, il=allLinks.length; i<il; i++) {
elm = allLinks[i];
elm.addEventListener("click", nowclicked, false);
}
And then your event listener would look something like:
nowclicked : function () {
alert("a linked was clicked!");
}
If you need a working example, I've modified the Link Target Finder extension by Robert Nyman to add an alert when links are clicked. The modified code is in linkTargetFinder.js.
See MDN example for Sending data from unprivileged document to chrome document.
Basically, in your chrome code you have to add a listener:
// The last value is a Mozilla-specific value to indicate untrusted content is allowed to trigger the event.
document.addEventListener("MyExtensionEvent", function(e) {myExtension.myListener(e);}, false, true);
and fire the event from content script. Note that document in the following is the contentDocument not XulDocument
var evt = document.createEvent("Events");
evt.initEvent("MyExtensionEvent", true, false);
element.dispatchEvent(evt);