I am trying to make a chrome extension that blocks iframes. I have a content.js file that has the javascript code in it but it does not execute.
Here is my manifest.json:
{
"manifest_version":2,
"name":"Ad Killer",
"description":"A Basic program for blocking ads",
"version":"0.1",
"background":{
"scripts":[
"background.js"
]
},
"content_scripts":[
{
"matches":[
"<all_urls>"
],
"js":[
"jquery.js"
]
}
],
"browser_action":{
"default_icon":"ad128.png",
"default_title":"Ad Killer"
}
}
Cotent.js:
var elems = document.getElementsByTagName("iframe");
for (var i = 0, max = elems.length; i < max; i++) {
elems[i].hidden = true;
My question is that is there any way to make the javascript in content.js work? Any help would be appreciated.
Your manifest is currently adding jquery.js to every page you visit. You just need to add content.js to your "content_scripts" array. Assuming content.js is in the same location as your manifest.json:
"content_scripts":[
{
"matches":[
"<all_urls>"
],
"js":[
"jquery.js",
"content.js"
]
}
],
Additionally, you'll want to make sure the code runs when the DOM is ready. Since you're using jQuery:
$(document).ready(function() {
//your code here
})
There are options for the manifest that define when the scripts run, but I've never had much success with them, you can explore them here: https://developer.chrome.com/extensions/content_scripts see the run_at attribute.
And you may even want to set up a timeout or poll to query for iframes since some of them may be created asynchronously, that is, after the ad content is loaded.
Related
I'm trying to change some behavior of the YouTube player, by changing some variables inside of the player_api script that is embedded into the html watch page of videos.
Problem is, whatever i try, the embedded script of the player always runs before my extension adds modifications to it. Thus keeping the behavior of the player the same.
I tried setting the run_at property in my manifest to document-start, but then the script didn't run at all.
What can i do to halt the execution of that script until i make changes to it?
PS: I tried changing the script by intercepting the html call and editing the body with Charles Proxy and the behavior of the player changed as i wanted. So it know it should work, if done at the right time.
.
manifest.json
{
"manifest_version": 2,
"name": "YouFit For YouTube",
"version": "1",
"content_scripts": [{
"js": ["content.js"],
"matches": ["https://*.youtube.com/watch?*",
"https://*.youtube.com/watch?*"],
}],
"browser_action": {
"default_icon": "icon.png"
}
}
content.js
function changeBehavior() {
var scriptElements = document.getElementsByTagName('script');
for (var i = 14; i < scriptElements.length; i++) {
var curScriptBody = scriptElements[i].outerHTML;
// Find the script i'm interested in
if (curScriptBody.indexOf("var ytplayer") != -1) {
scriptElements[i].outerHTML = scriptElements[i].outerHTML.replace("<text>", "<replacement text>");
alert("Replaced");
break;
}
}
}
changeBehavior();
Did you try something like this?
content.js
var script = document.createElement('script');
script.textContent = "/* What you have in content.js right now */";
(document.head||document.documentElement).prepend(script);
Add "run_at": "document_start" to the manifest file for the content script then modify your content script such that changeBehavior is called after the current call stack is exhausted using setTimeout(fn, 0). It will run just after the HTML document is rendered but before any embedded scripts.
This solution also avoids potential issues with running unsafe inline scripts when the content security policy is set.
Content.js
function changeBehavior() {
...
}
setTimeout(() => {
changeBehavior();
}, 0);
I know you might close this now, but please listen. I am making an extension with jquery. Here is my manifest.json:
{
"manifest_version":2,
"name":"Ad Killer",
"description":"A Basic program for blocking ads",
"version":"0.1",
"background":{
"scripts":[
]
},
"content_scripts":[
{
"matches":[
"<all_urls>"
],
"js":[
"content.js",
"jquery.js"
]
}
],
"browser_action":{
"default_icon":"ad128.png",
"default_title":"Ad Killer"
}
}
Here is my jquery.js:
src="http://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"
and finally my content.js:
src="http://code.jquery.com/jquery-3.1.1.min.js"
$(document).onload(function() {
alert('It Works!!')
});
var elems = document.getElementsByTagName("iframe");
for (var i = 0, max = elems.length; i < max; i++) {
elems[i].hidden = true;
};
The problem is that every time I run this, it gives me the error $ is not defined, but I have already initialised JQuery. What am I doing wrong here? Any help would be appreciated.
Yes as Maxim said, download the jquery.js file and make sure you include it in manifest.json. Also, make sure that when you are writing the "js":[ part that you load jquery.js first
I have made a chrome extension with jquery, what worked for me was to download the jquery file and include that in your manifest in the place of "jquery.js".
I'm trying to access some data from an iframe nested within an iframe, from developers console:
Object.keys(document.getElementById("contentBody").
contentDocument.getElementById('rawContent').
contentDocument.defaultView.window.messages)
["29c736c0ed25463c8436f4990ab6c6ec.zip",
"235819a8cf11488e83f0336603b71711.zip",
"66c9260590834d9698568c8a676ef406.zip",
"fae95e31cb424cd6ad21302217ef2cdc.zip",
"f554f712141047aa9aa24f765073e305.zip",
"e5c41819578240e0868f43ab6301aeb3.zip"]
That's what I expect back, but I've tried to get that very same info from a google chrome extension that I'm developing and for some reason I cant access messages array, this is the manifest file and contentscript.js (I've tried everything that came to my mind and searching for a few hours without success :/):
content.js
var iframeContentBody = document.getElementById('contentBody');
var innerDocContentBody = iframeContentBody.contentDocument;
var iframeRawContent = innerDocContentBody.getElementById('rawContent');
var innerDocRawContent = iframeRawContent.contentDocument; // iframeRawContent is undefined here
console.log(iframeRawContent.messages); // this prints undefined
manifest:
{
"manifest_version": 2,
"name": "Read Comments",
"description": "Read all comments from the current forum",
"version": "1.0",
"content_scripts": [{
"matches": ["*://*.forum.net/*"],
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Read Comments"
},
"permissions": ["activeTab", "tabs"]
}
Gists to set everything up:
HTML Example
after downloading and placing these 3 files in the same folder, run this:
python -m SimpleHTTPServer 80 # You may need to run it with sudo
then go to localhost/test.html and you're all set, if you test the line that I posted in the console you should see [1,2,3]
Extension example
this is the extension code
Developers console:
Chrome extension with "all_frames": true
Hacky solution: Partial solution
In this gist there is a way to do it, it's hard to detect when the iframe has been loaded, and it's harded to detect when the iframe inside the another iframe has been loaded, so a setTimeout gives enough time to get it done, then adding a script element to the dom seems to bypass all security measures that chrome extensions may have and it does get the content of the attribute without any other issue, still this seems hacky and it's not what I'm trying to do, I'm looking for a clean solution or a clean way to access the dom of a nested iframe as the example code states...
Thanks, any suggestion is welcome.
This was my solution after all, between what we talk over comments and my research over docs and other threads:
Content script:
(function () {
document.addEventListener("DOMContentLoaded", function () {
contentBody = document.getElementById("contentBody");
contentBody.addEventListener("load", function () {
rawContent = contentBody.contentDocument.getElementById("rawContent");
if (rawContent) {
var s = document.createElement("script");
s.src = chrome.extension.getURL('injected.js');
s.onload = function() {
this.parentNode.removeChild(this);
};
(document.head||document.documentElement).appendChild(s);
}
});
});
})();
Injected file:
keys = Object.keys(document.getElementById("contentBody").contentDocument.getElementById("rawContent").contentDocument.defaultView.window.messages);
console.log(keys);
Manifest:
{
"manifest_version": 2,
"name": "Read Comments",
"description": "Read all comments from the current forum",
"version": "0.0.1",
"content_scripts": [{
"matches": ["*://localhost/*"],
"run_at": "document_start",
"js": ["content.js"]
}],
"browser_action": {
"default_title": "Read Comments"
},
"permissions": [
],
"web_accessible_resources": ["content.js", "injected.js"]
}
As a simple explanation the main issue was the asyc load of iframes and the moment when the extension code ran, so after listening to a lot of events and discarding the ones that doesn't have the required elements on the dom everything went fine...
For completeness, here’s a version with "all_frames":true. There are two problems to work around: (1) getting messages from the inner frame to the top and (2) getting messages from the isolated world of the webpage to the isolated world of the content script (I assume you’re wanting to do more than just write messages to the console). This solves both at once by using postMessage.
if ( window.top !== window.parent ) {
var s = document.createElement("script");
s.textContent = "postMessage(messages,'*');";
s.onload = function() {
this.parentNode.removeChild(this);
};
document.head.appendChild(s);
} else if ( window.top === window ) {
addEventListener('message',function(e) {
console.log(e.data);
});
}
I must confess I’ve not actually tested it out. You may need to try making the injected script send a message from the webpage.
I wanna change src of js javascript before he starts loading using a Chrome Extension.
Manifest.json
{
"name":"Inject DOM",
"version":"1",
"manifest_version":2,
"permissions": [
"webRequest",
"webNavigation",
"http://*/*",
"https://*/*"
],
"content_scripts": [
{
"run_at": "document_end",
"matches": ["http://www.example.com/*"],
"js": ["inject_this.js"]
}
]
}
inject_this.js
I execute this code in various functions scenarios
var element = event.srcElement;
if(/production_path/.test(element.src)){
element.src = element.src.replace(/production_url/gi, "dev_path");
}
Scenario A
document.addEventListener('DOMNodeInserted', nodeInsertedCallback);
Scenario B
Executing this scenario into inject_this.js throw this TypeError:
Uncaught TypeError: Cannot read property 'onBeforeRequest' of undefined
Executing this in another js doesn't work
chrome.webRequest.onBeforeRequest.addListener(
function(details) {
if(/production_url/.test(details.url)){
return {
redirectUrl: details.usl.replace(/production_url/gi, "dev_url")
}
}
},
{urls: ["<all_urls>"]},
["blocking"]);
Where do i put this snippet code ?
Scenario C
document.addEventListener('beforeload', doBeforeLoad , true);
but every time script.src is changed too late, the resource is loaded.
How can I change javascript url before he starts loading?
Your solution B should work.
Two conditions need to be met for this to work:
This code should go to a background script / event page.
You need appropriate permissions: "webRequest", "webRequestBlocking"
You probably also should not use it on <all_urls>, since your extension seems to be specific to a single site (from your manifest). Doing it on all URLs is going to slow your Chrome down and may lead to breaking other sites, consider using a restrictive match pattern like "http://www.example.com/*production_url*"
I’d like to mimic the effect of window.location.reload(), but only for the “isolated world” which my content script is running in. That is, remove all existing JS, particularly callbacks and event bindings. Is there a nice way to do this?
Note: chrome.runtime.reload() doesn’t work for this; it has the effect of reloading the extension and the background script, but it does not reload existing content scripts until the user refreshes.
As far as I can tell, there's no automatic way to re-inject content scripts, for example during an extension update. What you can do is to find all tabs whose url matches the pattern you need, and programmatically re-inject the content scripts using chrome.tabs.executeScript.
Note that this method requires to add a permission for the same URL pattern as the one used by your content script.
Manifest.json:
"content_scripts":
[
{
"matches": [ "http://*.google.com/*" ],
"js": [ "content_script.js" ]
}
],
"permissions":
[
"tabs", "http://*.google.com/*"
]
Background.js:
chrome.runtime.reload();
chrome.tabs.query({ url: "http://*.google.com/*" }, function(tabs)
{
for(var i = 0; i < tabs.length; i++)
{
chrome.tabs.executeScript(tabs[i].id, { file: "content_script.js" }, function() {});
}
});