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
}
}
);
Related
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.
I'm trying to build a somehow dummy Chrome extension. I want it to run only in specific pages, so I'm using a Page Action.
Let's say I want the page action to run on the Instagram website, then (accordingly the docs), I would need something like this on my manifest.json right?
{
"manifest_version": 2,
"name": "Some name",
"version": "0.0.3",
"description": "Some description",
"content_scripts": [
{
"matches": [
"https://www.instagram.com/*"
],
"js": ["content.js"]
}
],
"page_action": {
"default_icon": "icon.png"
},
"background": {
"scripts": ["background.js"]
}
}
while the content script runs only on instagram pages as one would expect, the browser extension is not clickable (gray look, and when I click most options are not clickable).
this makes impossible to act upon extension button click. In my background.js I have:
function click(tab) {
console.log('click from ' + tab);
}
chrome.pageAction.onClicked.addListener(click);
that never gets called.
So, what's wrong that makes impossible to act upon extension click on some pages?
Note: I saw this question/answer, but couldn't find the problem/solution How can I add a click for pageAction?
You have to call pageAction.show in order for your pageAction button to be enabled (clickable).
The pageAction documentation says (emphasis mine):
You make a page action appear and be grayed out using the pageAction.show and pageAction.hide methods, respectively. By default, a page action appears grayed out. When you show it, you specify the tab in which the icon should appear. The icon remains visible until the tab is closed or starts displaying a different URL (because the user clicks a link, for example).
With a manifest.json content_scripts entry
Because you already have a content script that runs on the page you desire to have this function on, probably the easiest way to do this is to have your content script send a message to your background script telling it to show the page-action button for that tab.
Your content script could look something like:
chrome.runtime.sendMessage({type: showPageAction});
Your background script could look something like:
chrome.runtime.onMessage(function(message, sender, sendResponse) {
if(typeof message === 'object' && message.type === 'showPageAction') {
chrome.pageAction.show(sender.tab.id);
}
});
Without a manifest.json content_scripts entry
If you did not have a content script, you would probably want to use a webNavigation.onCompleted listener, or tabs.onUpdated listener, to listen for a change in the tab's URL in order to determine that the page-action button should be shown. Obviously, the trigger for calling pageAction.show() does not have to be the URL which is currently displayed in the tab, but that is the most common.
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.
I am trying to make a Chrome extension with one line of jQuery code but it doesn't work. I'm trying to trigger a click on an element.
The console of chrome doesn't show any error at all,
and when I put ONLY the jQuery code in console it works fine.
My code:
content.js
$(document).ready(function() {
$('.like_post:contains(Like)').click();
});
background.js
chrome.windows.getCurrent( function(currentWindow) {
chrome.tabs.query({active: true, windowId: currentWindow.id}, function(activeTabs){
chrome.tabs.executeScript(
activeTabs[0].id, {file: 'jquery-2.1.3.min.js', allFrames: true}
);
chrome.tabs.executeScript(
activeTabs[0].id, {file: 'content.js', allFrames: true}
);
});
console.log(currentWindow.id);
});
manifest.json
{
"name": "plugin name",
"version": "0",
"description": "What do I do as an extension",
"manifest_version": 2,
"browser_action": {
"name": "project with jquery",
"icons": ["icon.png"],
"default_icon": "icon.png"
},
"content_scripts": [ {
"js": [ "jquery-2.1.3.min.js", "background.js", "content.js" ],
"matches": [ "http://*/*", "https://*/*"]
}]
}
I have also downloaded the jquery-2.1.3.min.js file and have it in the extension folder.
Can anyone explain why it doesn't work???
The root cause of the problem is that extension content scripts execute in an isolated world. One of the reasons for this is so that your code does not conflict with the page's code: for instance, you can use a different version of jQuery.
So, your content script has its own copy of jQuery. The way jQuery's .click() works is by maintaining a list of event handlers that are triggered by the click..
..and you may see the problem already. The content script's copy of jQuery is not aware of the page's copy list of handlers, and cannot trigger them.
That, by the way, explains why it works when you put it in the console - by default, console executes in the page's context and triggers the page's copy of jQuery.
There are ways to overcome this, but the most straightforward for your task is to emit a proper DOM event, that will be caught by the page's code. See this question for an example.
I'm trying to create a chrome extension. When the user clicks my extension's icon (browserAction) the content script appends an extra div to the body of the open page(current tab). It works fine in all the sites except google's search page and youtube. I'm not getting any error message or anything. It simply wont give any response.
This is my code in content.js:
alert('sdsd');
$('body').append("<div id='popup'>My extension name</div>");
I've put the alert for testing purpose. So when extension is toggled it should show an alert message followed by appending the div to body, ideally! But it wont for these 2 sites.
Any idea what could be going wrong here?
manifest
{
"name": "My first extension",
"version": "1.0",
"background": { "scripts": ["background.js"] },
"content_scripts": [{
"all_frames": true,
"css": ["style.css"],
"matches": ["http://*/*","https://*/*"]
}],
"permissions": [ "tabs","http://*/*" ],
"browser_action": { "name": "test" },
"manifest_version": 2
}
background.js
chrome.browserAction.onClicked.addListener(function(tab){
chrome.tabs.executeScript(null,{file:"jquery.min.js"},function(){
chrome.tabs.executeScript(null,{file:"content.js"});
});
});
In Youtube's page, $ is overwritten and isn't jQuery. It's
bound: function ()
{
return document.getElementById.apply(document, arguments)
}
So your code makes an exception as there document.getElementById('body') is undefined.
You should try using noConflict().
EDIT :
Why aren't you simply listing jQuery.min.js and your content.js in the content_scripts instead of injecting them programmatically. This would avoid conflicts.
EDIT 2 :
Now that you use content scripts, you should use communication as described here to send from background.js to the content script the instruction to show the alert.
EDIT 3 :
Another solution would have been to use programmatic injection (as you initially did) and not use jquery, $('body').append("<div id='popup'>My extension name</div>"); being translated in vanilla JS to
var div = document.createElement('div');
div.id = 'popup';
document.body.appendChild(div);
document.getElementById('popup').innerHTML = "My extension name";
But it's generally cleaner (and requires less permissions) to avoid programmatic injection.