Chrome extension: Javascript not loading - javascript

I have the following code for a chrome extension, the contentscript.py as per another question on stackoverflow:
manifest.js
{
"name": "test script",
"version": "0.1",
"content_scripts": [{
"js": ["contentscript.js"],
"matches": ["http://*/*"]
}],
"manifest_version": 2,
"web_accessible_resources": ["script.js"]
}
contentscript.js
var s = document.createElement('script');
s.src = chrome.extension.getURL('script.js');
(document.head||document.documentElement).appendChild(s);
s.onload = function() {
s.parentNode.removeChild(s);
};
script.js
document.body.innerHTML = document.body.innerHTML.replace(new RegExp("this", "gi"), "that");
In order to simply replace some text. If I put the same expression that is on script.js on a test html file, it works, but it in the extension it doesn't appear as though the code is actually injected. I cannot for the life of me figure out why. The manifest and the contentscript seem in order, so I don't know what to do here.

try adding "https://*/*" to matches in manifest.json.
I recommend you to put this first line in your contentscript.js:
console.log('Content script loaded and started');
So you can inspect the page (F12, console tab) and see if the content script was injected or not. It'll help a lot with your debugging.
I don't know what is going on in your script.js, but it worth consider that your removechild in the onload event may occur before your task being done.

Related

Chrome Extension: Run Content Script before any Embedded Scripts run on Page

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);

overriding javascript alerts in chrome extension

I am trying to build a chrome extension; minimal experience in this area. I have followed Insert code into the page context using a content script to inject js code into pages using the first method.
I'm trying to build a js framework over someone else's code which relies heavily on alerts which are breaking the functionality of my overlay, so I just want to silence them for a bit--actually, I'd rather pass the messages into console.log but I'll take what I can get at this stage. So I've tried to follow JavaScript: Overriding alert() in setting up my eventual js file (nogo.js) to be injected.
nogo.js is injected but it doesn't seem to have the effect of suppressing the alerts. could it be that because the other html file is itself being initiated by a different js file that the injection is happening too slowly or out of order?
Manifest.json
"content_scripts": [
{
"matches": ["*://URL/*"],
"js": ["myscript.js"],
"run_at": "document_end",
"all_frames": true
},
{
"matches": ["*://URL/*"],
"js": ["noalerts.js"],
"run_at": "document_start",
"all_frames": true
}
],
"web_accessible_resources": ["script.js","nogo.js"]
}
myscript.js
var s = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
s.src = chrome.extension.getURL('script.js');
s.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(s);
noalerts.js
var n = document.createElement('script');
// TODO: add "script.js" to web_accessible_resources in manifest.json
n.src = chrome.extension.getURL('nogo.js');
n.onload = function() {
this.remove();
};
(document.head || document.documentElement).appendChild(n);
nogo.js
window.alert = null;
To make alert do nothing, just paste the lines to override it:
var s = document.createElement('script');
s.innerHTML = "alert = function(){}"
document.body.appendChild(s);
The function will be redeclared with you function body. I've had to do similar things in my extensions.
By analogy you can make confirm function say "yes" everytime:
var s = document.createElement('script');
s.innerHTML = "confirm= function(){return true;}"
document.body.appendChild(s);
That can be used for the simplest cases.. For example, noone else does anything else on the page, etc.
Notice: you can try this approach by pasting the code in the console and trying to invoke alert.
Notice 2: the code can be executed in your content-script because the document it shared between its scripts and your content scripts.

Access DOM elements data document->Iframe ->Iframe->method chrome extension

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.

Chrome extension: load and execute external script

I have trouble loading and executing external js-script into my chrome extension. Looks the same as this question, but I still cant't figure out why it doesn't work in my case.
The idea is that I want to have in my content script some default function which should parse a web-page content. And for some specific web-pages I want to load and use specific parsers, so I try to load proper js-script for a wep-page, and this script shoud extend functionality of default parser.
By now I try only execute code from external script, but have such error: Unchecked runtime.lastError while running tabs.executeScript: No source code or file specified at Object.callback
This is my manifest.json:
{
"name": "Extension name",
"version": "1.2",
"description": "My chrome extension",
"browser_action": {
"default_popup": "popup.html",
},
"content_scripts": [{
"css": [
"style.css"
],
"js": [
"bower_components/jquery/dist/jquery.js",
"bower_components/bootstrap/dist/js/bootstrap.js",
"content.js"
],
"matches": ["*://*/*"]
}],
"web_accessible_resources": [
"frame.html",
"logo-48.png"
],
"icons": {
"16": "logo-16.png",
"48": "logo-48.png",
"128": "logo-128.png"
},
"permissions": [
"tabs",
"storage",
"http://*/",
"https://*/"
],
"manifest_version": 2
}
This is popup.html
<!doctype html>
<html>
<head>
<title>Title</title>
<script src="popup.js"></script>
</head>
<body>
<ul>
<li>Some link</li>
</ul>
</body>
</html>
And in popup.js i execute scrip like this:
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {file: "http://127.0.0.1:8000/static/plugin/somesite.js"});
});
What am I dong wrong, did I miss something? Or should I use another approach to solve the issue?
Running scripts from external sources like you try is forbidden by google chrome and will block or even not publish your extension. All scripts must be in the extension. But there is a solution,
from google chrome doc:
The restriction against resources loaded over HTTP applies only to
those resources which are directly executed. You're still free, for
example, to make XMLHTTPRequest connections to any origin you like;
the default policy doesn't restrict connect-src or any of the other
CSP directives in any way.
If you need badly an external source, you can do a XML HTTP request and use the eval to the content. Here is a part of code from google doc:
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:8000/static/plugin/somesite.js", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
// WARNING! Might be evaluating an evil script!
var resp = eval("(" + xhr.responseText + ")");
// Or this if it's work
chrome.tabs.executeScript(tabs[0].id, {code: xhr.responseText});
}
}
xhr.send();
or you can use some library, $.get() with jquery or $http with angularjs.
if you add eval in your code you must add in manifest.json this:
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"`,
As per the discussion here: https://groups.google.com/a/chromium.org/forum/#!topic/chromium-extensions/LIH7LGXeQHo,
Running scripts from external sources may cause your extension to be unpublished or blocked.
Just providing another approach, you could make an ajax call to the content script then call chrome.tabs.executeScript
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
$.get("http://127.0.0.1:8000/static/plugin/somesite.js", function(result) {
chrome.tabs.executeScript(tabs[0].id, {code: result});
}, "text");
});
The 'file' option for executeScript only relates to files embedded in your extension. I know, the documentation isn't clear on that and while it might work with URLs, it sounds like a hack, not a feature. In order to load scripts from external sources into your active page, you usually have to execute a script that creates a script tag inside the DOM of the loaded document there.
I feel I have answered parts of this question before here: Why is chrome.tabs.executeScript() necessary to change the current website DOM and how can I use jQuery to achieve the same effect?
To summarize:
1) in order to have access to web pages from the extension, you need to add permissions for it:
"permissions" : [
"tabs",
[...]
"http://*/*",
"https://*/*" ],
2) you need to execute some sort of code that creates the DOM script element that loads what you need:
chrome.tabs.executeScript(tab.id, {
code : 'var script=document.createElement(\'script\');' +
'script.onload=function() { /*do something in the page after the script was loaded*/ };' +
'script.src=\'http://127.0.0.1:8000/static/plugin/somesite.js\';' +
'document.body.appendChild(script);'
}, function (returnedValue) {
// do something in the extension context after the code was executed
});
Take a look at the remex function in the link above, which I think solves a lot of the ugliness of javascript written as a string as here.

Communication between scripts in Chrome Extension

I know there are many variations of this question already in existence here, but none of them seem to work for me.
Details:
I'm writing an extension that pulls some email data from emails you send in gmail. In order to achieve this I am using this version of Gmailr https://github.com/joscha/gmailr.
In effect, I have three content scripts: Gmailr.js and main.js (which are pretty much identical to those in the link above) allow me to pull out the information I'm looking for. Then content.js I use to send a message to the background page of the extension.
The problem is that from gmailr.js and main.js I cannot use any of the Chrome APIs, and I'm not really sure why, so I can't send messages from these back to the background page.
That is why I made content.js which can communicate with the background page. However, it does not seem to be able to see anything the other content scripts do. For example, main.js inserts a div at the top of the page. When I try to attach an event listener to a button in this div from content.js, I am told that no such element exists.
How can I get the data pulled out by main.js to be seen by content.js? (I also tried to put the data in local storage, then trigger a custom event listener to tell content.js to read local storage, but no luck because they don't seem to be able to hear each other's event being triggered).
Any insight or alternatives are much appreciated.
(I can post code if necessary, but it's fragmented and long)
My manifest file:
{
"manifest_version": 2,
"name": "Email extractor",
"description": "Extracts data from emails",
"version": "1.0",
"background": {
"script": "background.js"
},
"content_scripts": [
{
"matches": [
"*://mail.google.com/*",
"*://*/*"
],
"js": [
"lib/yepnope.js/yepnope.1.5.4-min.js",
"lib/bootstrap.js",
"main.js",
"gmailr.js",
"content.js"
],
"css": [
"main.css"
],
"run_at": "document_end"
}
],
"permissions": [
"tabs",
"storage",
"background",
"*://mail.google.com/*",
"*://*/*"
],
"browser_action": {
"default_icon": "img/icon.png",
"default_popup": "popup.html"
},
"web_accessible_resources" : [
"writeForm.js",
"disp.js",
"/calendar/jsDatePick.min.1.3.js",
"/calendar/jsDatePick_ltr.min.css",
"lib/gmailr.js",
"lib/jquery-bbq/jquery.ba-bbq.min.js",
"content.js",
"main.js",
"background.js"
]
}
This is main.js:
Gmailr.init(function(G) {
sender = G.emailAddress();
G.insertTop($("<div id='gmailr'><span></span> <span id='status'></span>)");
el = document.getElementById("testid");
el.addEventListener('click', mg, false);
var status = function(msg) {
G.$('#gmailr #status').html(msg); };
G.observe(Gmailr.EVENT_COMPOSE, function(details) {
....
status(" user: " + user);
console.log('user:', user);
//now try to send a message to the background page
//this always returns the error that method sendMessage does not exist for undefined
chrome.runtime.sendMessage({greeting: "test from gmailr"}, function(response) {
console.log("did it send?");
});
});
});
gmailr.js is quite long and is also not my own code but it can be seen here: http://pastebin.com/pK4EG9vh
Hi perhaps 3 likely reason to your problem :
The way you send messages to bgp from main.js and gmailr.js are perhaps wrong because you must arrive to communicate from any content script to your bgp. (in your manifest content script key the gmailr.js is missing). Show us your code it would help.
You seems to have a problem with the moment you search from content.js to access to the element created in main.js. Do you try to access your element with the jQuery $("").on() method ? A simple test must be to declare a function in one cs and to use it in another. If it's not working it's a manifest problem. The order you declare .js file in manifest content script key is important also.
try to in the manifest content script array "run_at":"document_end"
Hope it help !

Categories

Resources