I am trying to make a Chrome extension that:
reloads the URL of the tab where the extension is launched (say, for instance, www.example.com) every 7 seconds and performs a text search for "Text" on it;
if "Text" exists, then extensions stops reloading the page, selects a certain radio button and clicks on a certain button on www.example.com.
My popup.html is designed with a simple UI with two buttons "Run" and "Stop", respectively running and stopping the reload-and-search function above.
The script in my popup page is called "app.js": <script src="app.js" charset="UTF-8"></script>
To make my content script perform its reload-and-search function on the webpage where the extension is launched, www.example.com, app.js programmatically injects a code.js file (with the reload-and-search function) in it when the user clicks the "Run" button from the UI. And since I would like to also be able to stop the reload-and-search function from the UI, app.js inject a clearInterval(function) code when the user clicks the "Stop" button from the UI.
This is how app.js looks:
chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-run').addEventListener('click', () => {
chrome.tabs.executeScript(tab.id, { file: 'code.js'}
);
});
});
chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-stop').addEventListener('click', () => {
chrome.tabs.executeScript(tab.id, { code: 'clearInterval(timer);'}
);
});
});
This is code.js:
function reloadAndFind() {
if (document.body.innerHTML.search("Text") != -1) {
clearInterval(timer);
// this is to stop the page from reloading when "Text" is found
beep();
// this is to call a beep() function coded elsewhere in code.js to make a "beep" sound when "Text" is found
var radioButton = document.querySelectorAll('input[type=radio]');
// this is to store the specific radio button I am interested in
var button = document.getElementById("continue-button");
// this is to store the specific button I am interested in
radioButton.checked = true;
button.click();
}
}
timer = setInterval(function(){location.reload(); reloadAndFind();}, 7000);
// this is to concurrently perform a page reload and call the reloadAndFind() function on the page every 7 seconds
I have two problems:
Stopping the function:
When I click the "Run" button on my UI, the reloadAndFind() function starts and the page reloads every 7 seconds. After a number of reloads, when www.example.com shows "Text" I know the function found the tring because the beep() functions warns me, the radio button is selected and the button is clicked. However, clearInterval(timer) does not seem to work and the page keeps reloading again and again.
It seems, however, to work when clearInterval() is injected by app.js when I click the "Stop" button on my UI. But this is useless, since I want the extension to be able to stop the reload by itself when I am not in the front of my computer. Also, problem No. 2 appears.
Using "Stop" to stop the function works only partially: When I click the "Stop" button on my UI and clearInterval(timer) is injected through stopcode.js, it actually stops the page from reloading again. However, if I open another tab on Chrome and go to www.example.com, then the page keeps reloading as if I never injected clearInterval(timer).
How can I fix both problem 1 and 2? Please consider that I am very new to javascript and to programming in general.
If helpful, this is my manifest.json:
{
"manifest_version": 2,
"name": "Find Text",
"version": "0.1",
"permissions": [ "activeTab", "tabs", "storage", "browsingData", "notifications", "http://*/", "https://*/"],
"content_scripts": [{
"matches": ["https://www.example.com/*"],
"all_frames": true,
"js": ["app.js", "code.js"]
}],
"browser_action": {
"default_popup": "popup.html",
"default_title": "Find Text"
}
}
UPDATE:
I have modified my extension while trying to follow wOxxOm's valuable information, but it still does not work.
I now have this background.js script:
chrome.runtime.onMessage.addListener(function(message, callback){
if(message === 'start') {
var timer;
timer = setInterval(function(){chrome.tabs.reload; chrome.tabs.executeScript({ file: 'code.js'});}, 7000);
} else if(message === 'stop') {
clearInterval(timer);
}
});
I have modified app.js as follows:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
document.querySelector('.button-run').addEventListener('click', () => {
var activeTab = tabs[0];
chrome.tabs.sendMessage(activeTab.id, {"message": 'start'});
});
});
chrome.tabs.query({active: true, currentWindow: true}, function(tab) {
document.querySelector('.button-stop').addEventListener('click', () => {
var activeTab = tabs[0];
chrome.tabs.sendMessage(activeTab.id, {"message": 'stop'});
});
});
The modified and simplified content script, code.js to test the new setup:
function findText() {
if (document.body.innerHTML.search("Text") != -1) {
alert("Text found");
// potentially perform other actions
}
}
(I guess that if and when everything will work as intended, I should include chrome.tabs.sendMessage(activeTab.id, {"message": 'stop'}) in code.js when "Text" is found.)
manifest.json:
{
"manifest_version": 2,
"name": "Find Text",
"version": "0.1",
"externally_connectable": {
"matches": ["http://www.example.com"]
},
"permissions": [ "activeTab", "storage", "browsingData", "notifications", "http://*/", "https://*/"],
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html",
"default_title": "Find Text"
}
}
Now when I click the "Run" button on my UI www.example.com reloads every 7 seconds and it stops reloading when I click the "Stop" button on my UI. However, the findText() function in code.js does not seem to work: when "Text" is displayed on the page, nothing happens.
Also, problem No. 2 above still remains: if I click the "Stop" button on my UI the autorefresh stops, but when I open another tab on Chrome and go to www.example.com, then the page keeps reloading as if the autorefresh was never stopped.
I am 100% sure that I made more than one mistake here, but I can't spot them.
Related
I'm building a chrome extension which needs to redirect a script of a certain page. My background.js includes:
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if (/js\example.js/.test(details.url))
// setTimeout(requestScript(details), 0);
return {
redirectUrl: chrome.extension.getURL("novoScript.js")
}
},
{urls: ['*://*.example.com/*.js']},
["blocking"]
);
The manifest.json includes:
"background": {
"scripts": ["background.js"],
"persistent": true
},
It works as expected, but the function only runs when I reload the page after having clicked the popup button. The first time I open the page in a new tab, it doesn't run. If I am in the page, and have clicked the popup button, it always works. If I haven't clicked, it doesn't matter how many times I reload, the redirect doesn't work.
I've tried erasing the whole content of popup.js, and it doesn't make a difference, so it's not because of any code there.
Also, I've checkedthe background.js file is not being ran again, so it seems that the listener is there, but it doesn't listen if I don't click the popup button. I've also added console.log to the listener function, and before I click the button it only logs calls to 'aobut:blank' url.
I'd like to allow the listener to work without clicking the popup, to avoid inconsistencies. Any help is appreciated.
I'm not sure about what was causing the problem. But an alternative approach that seems to be better overall, was pointed by #wOxxOm: declarativeNetRequest. With it, I applied a general rule for whenever the desired resource is requested.
Implementation:
manifest.json:
{
...
"declarative_net_request" : {
"rule_resources" : [{
"id": "ruleset_1",
"enabled": true,
"path": "redirectRules.json"
}]
},
"permissions": [
"declarativeNetRequest",
"*://*.example.com/*"
],
...
}
redirectRules.json:
[{
"id": 1,
"priority": 1,
"action": {"type": "redirect", "redirect": {"extensionPath": "/novoScript.js" }},
"condition" : {
"urlFilter" : "js/app.*.js",
"domains": ["example.com"],
"resourceTypes" : ["script"]
}
}]
If I understand you correctly when you first open the page it is not triggering onBeforeRequest. When you trigger the page load another way then it triggers onBeforeRequest as expected.
You may be able to capture what you want using onBeforeNavigate instead or some combination of onBeforeNavigate and onBeforeRequest.
A certainly full-proof way to capture any tab changes would be to do something like this in your background script:
chrome.tabs.onUpdated.addListener(function
(tabId, changeInfo, tab) {
// read changeInfo data and do something with it (like read the url)
if (changeInfo.url && ..url-comparison-here) {
// do something here
}
}
);
It seeems that my content script document.URL always has the same value even when I click on a random <a href link.
In the below function in the content script, I have a function called performclick() which finds a random link on the current document, then clicks it.
Now shouldn't the next time I call performclick(), I get another document object? i.e. document of the currently focused active tab?
Like when I click a link on the main page and it opens a new tab with another website loaded in it, then I need the content script to give me the document of this new tab that has the clicked website link loaded in it.
But some how I always keep getting the document object of the same main page.
Please help
Manifest
{
"manifest_version": 2,
"name": "OSP Updater",
"version": "1.0.0",
"content_scripts": [{
"run_at": "document_start",
"js": ["content.js"],
"matches": [ "<all_urls>" ]
}],
"permissions": [
"background", "webRequest", "webRequestBlocking", "tabs", "<all_urls>"
],
"background": {
"scripts": ["background.js"],
"persistent": true
},
"web_accessible_resources": ["jsui.js"]
}
Content Script
window.onload = function () {
var elm = document.getElementById("my-container");
if (elm != undefined && elm != null) {
//alert("Main Page LOADED!!!");
}
}
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.clickit == "yes") {
performclick();
}
});
function performclick() {
//document.URL is always the same
var links = document.querySelectorAll("a");
if (links.length) {
var linkToClick = links[1];
var clickEvent = document.createEvent('MouseEvents');
clickEvent.initEvent('click', true, true);
linkToClick.dispatchEvent(clickEvent);
}
}
Update #1:
Silly me, it turns out that I was sending a message from my background script to my content script via the first tab.
I did my clicking decision in the background script than sends a signal to the content script so that the content script can do the actual clicking.
So since I was sending message from the background script to the content script in the 1st tab, it lead the document object to always belong to the first tab (i.e. main page).
Hope this helps someone, always check if you are doing messaging then check its logic.
After I read the documentation about Event Page I didn't got the advantage of using Event Page instead of Background Page .
Suppose I have the follow simple case -
manifest.json
"background": {
"scripts": ["background.js"],
"persistent": false
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
]
content.js
chrome.runtime.sendMessage("Hi Background")
background.js
chrome.runtime.onMessage.addListener(messageListener);
function messageListener (request, sender, sendResponse) {
alert(request);
}
In this case , whether persistent is "persistent": false or "persistent": true the listener in the background.js always should be awake in order to get the messages from the content.js and therefore the the background.js couldn't go to suspend mode .
So what is the benefit of the Event Page ("persistent": true) in such cases and in general ? Please provide an example.
The main advantage of the event pages is to release RAM and CPU resources by unloading background script when it is not used.
...background.js couldn't go to suspend mode.
It can. Even if your event page uses message listener it still will be unloaded after some time. Chrome remembers that the page has set the listener, so the browser will awake the page when a message will be sent.
You can try this experiment:
add this extension
manifest.json
{
"manifest_version": 2,
"name": "Test",
"version": "0.0.1",
"description": "",
"background": {
"scripts": ["background.js"],
"persistent": false
},
"browser_action": {
"default_popup": "popup.html"
}
}
background.js
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
sendResponse({
msg: "it's alive!"
});
}
);
popup.html
<html>
<body>
<div id="text"></div>
<script src="popup.js"></script>
</body>
</html>
popup.js
chrome.runtime.sendMessage('hello',
function (response) {
document.getElementById('text').textContent = response.msg;
}
);
Open Chrome's Task manager and wait few seconds until Test extension disappears (unloads). If you do not see it (it already unloaded), you can reload extension.
Click to the extension's browser action and you will see message inside the window which came from the event page. Also, you can see two processes in the task manager for the Test extension: one is a popup and the second is the event page.
After popup will be closed, the event page unloads again in a few seconds.
I've developed a Chrome extension that injects a button into the toolbar of a rich-text editor of a specific web page, code available here. This basic extension is based on the concept of the "content script" and works well because the toolbar is present as soon as the page has loaded.
Now, however, I'm confronted by another page where I cannot simply inject my button as soon as the page loads because the user needs to interact with the page first (make a selection or press a button) before the toolbar appears.
So I'm looking for a way to track any changes in the active tab (I have a URL pattern for the page). I don't want or need a browser action (i.e. the little button on the right-hand side of the omnibox), so I was hoping to get away with a background.js event page where I can declare an event listener for certain user-originated events but somehow it's not working.
To explain: I've got my manifest.json, great:
{
"name": "basic tab test",
"description": "blah di blah",
"version": "1.0",
"permissions": [
"activeTab"
],
"background": {
"scripts": ["background.js"], // background script shown below
"persistent": false
},
"content_scripts": [
{
"matches": [
"file://*" // for testing only, of course
],
"js": [
"contentscript.js" // <-- content script shown below
]
}
],
"manifest_version": 2
}
The background.js script looks like this at the moment:
console.log("in background.js");
chrome.tabs.getCurrent(function(tab) {
tab.onActivated.addListener(function(){
console.log("GOT HERE onActivated (inside tab)");
});
});
chrome.tabs.getCurrent(function(tab) {
tab.onZoomChange.addListener(function(){
console.log("GOT HERE onZoomChange (inside tab)");
});
});
// this is actually part of the message passing test
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
console.log(sender.tab ?
"from a content script:" + sender.tab.url :
"from the extension");
if (request.greeting == "hello")
sendResponse({farewell: "goodbye"});
});
Of course, these are just tests, but none of the above events actually ever fire. Becoming slightly desperate, I then thought 'well, let's use the message passing method' to send messages from the contentscript.js to the background.js whenever the user presses a button. The contentscript.js looks like this:
document.addEventListener("DOMContentLoaded", function(event) {
console.log("just a canary - got here...");
var btn = document.getElementById("button");
if (btn) {
console.log("there is a button!");
} else {
console.log("there is NO button!");
}
btn.addEventListener("click", function () {
console.log("clicked the button!!!!");
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
})
});
Also, we never arrive inside this event handler (but why oh why?! this is standard jquery-less code for when the DOM is completely loaded). So, this is when I thought I might ask the assembled experts for advice.
TL;DR: I want to track events on the activeTab and if a given DOM element makes its appearance manipulate it (by injecting an element).
By default, "run_at" property for Content Script is "document_idle", which means you scripts will be injected after window.onload event fires, and obviously later than DOMContentLoaded event. So in fact your code in content script is not executed at all.
To make your code work, either:
Remove the outer DOMContentLoaded event listener
Or add "run_at": "document_start" part in your manifest.json
You could take a look at run_at part fore more details.
Whenever my chrome extension icon is clicked, I want to run a script that would make certain changes to the current webpage.
I have tried using content_scripts in my manifest and it worked but the problem is , the script runs even if I did not clicked on the icon.
I have found that, I need to use background script.In my background.js file I have added
chrome.browserAction.onClicked.addListener(function(tab) {
alert();
});
and it is not working.
Here is my manifest file.
{
"manifest_version": 2,
"name": "Reveal Password",
"description": "Reveals password in password input field",
"version": "1.0",
"browser_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
Plus I want to execute the script that I made that manipulates the current web page too.
Use chrome.tabs.executeScript() to execute code in a tab like this:
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.tabs.executeScript({
code: 'alert("Hello")'
});
});
Instead of code, you could also use a file which contains the code :
chrome.tabs.executeScript(null, {file: "content_script.js"});
NOTE : You need activeTab permissions to execute code in an active tab
"permissions": [
"activeTab"
]