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.
Related
I want create support extention (only for my site) which will take errors from the console. So, how can I do this?
chrome.debugger gives access to some target, but how can I get all console on click event (or when error appears in the console)
try window.ErrorEvent().it should work
I found a solution, maybe it will be useful to someone:
manifest.json:
"content_scripts": [
{
"matches": ["your_site"],
"run_at": "document_end",
"js": ["content_script.js"]
}
in content_script.js inject another one script (fore example: page_script.js):
const s = document.createElement('script');
s.src = chrome.extension.getURL('page_script.js');
(document.head || document.documentElement).appendChild(s);
s.onload = function() {
s.remove();
};
we can comunicate with main js file of extension by sendMessage() method. here is documentasion
in page_scripte.js we handle errors:
let pageErr = [];
window.console.error = function () {
pageErr.push((arguments.length === 1)?arguments[0]:Array.prototype.slice.call(arguments, 0));
};
dispatch custom event in content_script.js when message from main js is sended, catch it in page_script.js, send another custom event from page_script.js to content_script.js and, at last, send data to main js
I think it's not best solution, but if anyone has any ideas, you are welcome=)
P.S.: I also tried to catch errors in content_script.js, but it doesn't work
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 am writing a chrome extension which will enable transliteration for specific textboxes in facebook.
I have used the script tab to load https://www.google.com/jsapi in background.html
here is the code i have used in a content script
i tried to load using ajax and the generic way.
when i checked it said google undefined.
/*
$.ajax({
url: "https://www.google.com/jsapi",
dataType: "script",
});
*/
var script = document.createElement("script");
script.setAttribute('type','text/javascript');
script.setAttribute('src','https://www.google.com/jsapi?'+(new Date()).getTime());
document.body.appendChild(script);
$(document).ready(function()
{
alert(google)
if(window.location.href.indexOf('facebook.com'))
yes_it_is_facebook();
})
function yes_it_is_facebook()
{
// document.getElementsByName('xhpc_message_text')[0].id = 'facebook_tamil_writer_textarea';
// alert(document.getElementsByName('xhpc_message').length)
google.load("elements", "1", { packages: "transliteration" });
google.setOnLoadCallback(onLoad);
}
function onLoad()
{
var options = {
sourceLanguage:
google.elements.transliteration.LanguageCode.ENGLISH,
destinationLanguage:
[google.elements.transliteration.LanguageCode.HINDI],
shortcutKey: 'ctrl+g',
transliterationEnabled: true
};
var control = new google.elements.transliteration.TransliterationControl(options);
control.makeTransliteratable(['facebook_tamil_writer_textarea']);
}
and i have https://www.google.com/jsapi in manifest.json content script array.
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-1.7.2.min.js", "js/myscript.js", "https://www.google.com/jsapi"]
}
],
it showed an error
Could not load javascript https://www.google.com/jsapi for content
script
here is my manifest.json
{
"name": "Facebook Tamil Writer",
"version": "1.0",
"description": "Facebook Tamil Writer",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background_page": "background.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-1.7.2.min.js", "js/myscript.js", "https://www.google.com/jsapi"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
]
}
in that i have added https://www.google.com/jsapi for your understanding and i have tested removing that also.
so how do i load that javascript into a document context . that is when ever a web page is loaded... here i specifically loading for facebook. still i have to correct the indexof condition because it is not giving the proper result but that is not the problem to this context of my question.
so please suggest me.
I don't seem to find any documentation regarding this but I think you cannot mention an http:// path in content_scripts option. A possible work around could be this:
$('head').append("<script type='text/javascript' src='http://google.com/jsapi'>");
Or loading it via ajax request as you have commented out in your code.
Secondly google.com/jsapi will have to be loaded before you can use it in your script. In your manifest you are loading your script first and then google.com/jsapi.
A friendly advice:
jQuery by default disallows caching by appending timestamp at the end of url. Since the script you are trying to load is not likely to change in short durations you can pass cache: false as an option for saving load time. Check out this page for more info.
Better yet you can bundle the script with your package so that there is no ajax request associated with your extension, that will add to the speed of your extension.
One of the biggest issues with google.load is that it cannot properly load resources after the page has fully loaded, because the API uses document.write to inject scripts/styles. To fix the issue, two methods have to be patched:
(function(g) {
var loader_d = g.loader.d,
setOnLoadCallback = g.setOnLoadCallback;
// Force not to use document.write when the document is loaded
g.loader.d = g.loader.writeLoadTag = function(a, b) {
loader_d(a, b, document.readyState === 'complete');
};
// Executes functions directly when page has loaded
g.setOnLoadCallback = function(a_listener, b) {
if (b || document.readyState !== 'complete') {
setOnLoadCallback(a_listener, b);
} else {
// When the API is not loaded yet, a TypeError with google.
// will be thrown. Not a ReferenceError, because google.* is defined
// Retry max *c* times.
var c = 5;
b = function() {
try {
a_listener();
} catch (e) {
if (e instanceof TypeError && (''+e).indexOf('google.')!=-1) {
if (c--) setTimeout(b, 2000);
}
}
};
b();
}
};
})(google);
Now, problem 2: Content Scripts run in an isolated environment: any properties of the global namespace, window, are not accessible. So, any injected APIs are not visible to your Content Script.
To fix this, see the following Stack Overflow answer: Building a Chrome Extension.
This might help with understanding Chrome's security policies
CSP
In there is says that if you attach a script tag to the page (not the popup or content script) it loads in the context of the page not your extension. And script from the extension can not talk to scripts of the page. If you look at the page script's you'll see it there but not under your extension scripts.
I discovered this while trying to inject the Google API script.
script = document.createElement('script');
script.src = "https://apis.google.com/js/client.js?onload=init";
(document.head||document.documentElement).appendChild(script);
The init function is defined in my content script. But the Goolge API script is loaded as a page script. So if I do this
var script = document.createElement('script');
script.innerText = "function init(){ alert('hi'); }";
(document.head||document.documentElement).appendChild(script);
script = document.createElement('script');
script.src = "https://apis.google.com/js/client.js?onload=init";
(document.head||document.documentElement).appendChild(script);
The injected init function is called and I see the alert 'hi'. Not sure if this helps but I figured I'd make a note of it for anyone else struggling with loading the Google apis. I'll update this answer if I figure out a way to actually get it loaded.
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.
I am writing a chrome extension which will enable transliteration for specific textboxes in facebook.
I have used the script tab to load https://www.google.com/jsapi in background.html
here is the code i have used in a content script
i tried to load using ajax and the generic way.
when i checked it said google undefined.
/*
$.ajax({
url: "https://www.google.com/jsapi",
dataType: "script",
});
*/
var script = document.createElement("script");
script.setAttribute('type','text/javascript');
script.setAttribute('src','https://www.google.com/jsapi?'+(new Date()).getTime());
document.body.appendChild(script);
$(document).ready(function()
{
alert(google)
if(window.location.href.indexOf('facebook.com'))
yes_it_is_facebook();
})
function yes_it_is_facebook()
{
// document.getElementsByName('xhpc_message_text')[0].id = 'facebook_tamil_writer_textarea';
// alert(document.getElementsByName('xhpc_message').length)
google.load("elements", "1", { packages: "transliteration" });
google.setOnLoadCallback(onLoad);
}
function onLoad()
{
var options = {
sourceLanguage:
google.elements.transliteration.LanguageCode.ENGLISH,
destinationLanguage:
[google.elements.transliteration.LanguageCode.HINDI],
shortcutKey: 'ctrl+g',
transliterationEnabled: true
};
var control = new google.elements.transliteration.TransliterationControl(options);
control.makeTransliteratable(['facebook_tamil_writer_textarea']);
}
and i have https://www.google.com/jsapi in manifest.json content script array.
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-1.7.2.min.js", "js/myscript.js", "https://www.google.com/jsapi"]
}
],
it showed an error
Could not load javascript https://www.google.com/jsapi for content
script
here is my manifest.json
{
"name": "Facebook Tamil Writer",
"version": "1.0",
"description": "Facebook Tamil Writer",
"browser_action": {
"default_icon": "images/stick-man1.gif",
"popup":"popup.html"
},
"background_page": "background.html",
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["js/jquery-1.7.2.min.js", "js/myscript.js", "https://www.google.com/jsapi"]
}
],
"permissions": [
"http://*/*",
"https://*/*",
"contextMenus",
"tabs"
]
}
in that i have added https://www.google.com/jsapi for your understanding and i have tested removing that also.
so how do i load that javascript into a document context . that is when ever a web page is loaded... here i specifically loading for facebook. still i have to correct the indexof condition because it is not giving the proper result but that is not the problem to this context of my question.
so please suggest me.
I don't seem to find any documentation regarding this but I think you cannot mention an http:// path in content_scripts option. A possible work around could be this:
$('head').append("<script type='text/javascript' src='http://google.com/jsapi'>");
Or loading it via ajax request as you have commented out in your code.
Secondly google.com/jsapi will have to be loaded before you can use it in your script. In your manifest you are loading your script first and then google.com/jsapi.
A friendly advice:
jQuery by default disallows caching by appending timestamp at the end of url. Since the script you are trying to load is not likely to change in short durations you can pass cache: false as an option for saving load time. Check out this page for more info.
Better yet you can bundle the script with your package so that there is no ajax request associated with your extension, that will add to the speed of your extension.
One of the biggest issues with google.load is that it cannot properly load resources after the page has fully loaded, because the API uses document.write to inject scripts/styles. To fix the issue, two methods have to be patched:
(function(g) {
var loader_d = g.loader.d,
setOnLoadCallback = g.setOnLoadCallback;
// Force not to use document.write when the document is loaded
g.loader.d = g.loader.writeLoadTag = function(a, b) {
loader_d(a, b, document.readyState === 'complete');
};
// Executes functions directly when page has loaded
g.setOnLoadCallback = function(a_listener, b) {
if (b || document.readyState !== 'complete') {
setOnLoadCallback(a_listener, b);
} else {
// When the API is not loaded yet, a TypeError with google.
// will be thrown. Not a ReferenceError, because google.* is defined
// Retry max *c* times.
var c = 5;
b = function() {
try {
a_listener();
} catch (e) {
if (e instanceof TypeError && (''+e).indexOf('google.')!=-1) {
if (c--) setTimeout(b, 2000);
}
}
};
b();
}
};
})(google);
Now, problem 2: Content Scripts run in an isolated environment: any properties of the global namespace, window, are not accessible. So, any injected APIs are not visible to your Content Script.
To fix this, see the following Stack Overflow answer: Building a Chrome Extension.
This might help with understanding Chrome's security policies
CSP
In there is says that if you attach a script tag to the page (not the popup or content script) it loads in the context of the page not your extension. And script from the extension can not talk to scripts of the page. If you look at the page script's you'll see it there but not under your extension scripts.
I discovered this while trying to inject the Google API script.
script = document.createElement('script');
script.src = "https://apis.google.com/js/client.js?onload=init";
(document.head||document.documentElement).appendChild(script);
The init function is defined in my content script. But the Goolge API script is loaded as a page script. So if I do this
var script = document.createElement('script');
script.innerText = "function init(){ alert('hi'); }";
(document.head||document.documentElement).appendChild(script);
script = document.createElement('script');
script.src = "https://apis.google.com/js/client.js?onload=init";
(document.head||document.documentElement).appendChild(script);
The injected init function is called and I see the alert 'hi'. Not sure if this helps but I figured I'd make a note of it for anyone else struggling with loading the Google apis. I'll update this answer if I figure out a way to actually get it loaded.