Can I create a Google chrome extension to prevent the page from doing an alert() ?
As #MrGlass said, currently, Chrome Extensions run in a separate environment, limiting access to the actual window object and providing a duplicate that is only valid for the extension.
To solve this, we can inject a script element directly into the document. This way, you access the document's environment and the real window object.
First, lets create the function (I added the "confirm" as well, because some confirms were annoying me so much):
var disablerFunction = function () {
window.alert = function alert(msg) { console.log('Hidden Alert ' + msg); };
window.confirm = function confirm(msg) {
console.log("Hidden Confirm " + msg);
return true; /*simulates user clicking yes*/
};
};
Now, what we're going to do is to transform that function in a text script and enclose it in parentheses (to avoid possible conflicts with actual vars in the page environment):
var disablerCode = "(" + disablerFunction.toString() + ")();";
And finally, we inject a script element, and immediately remove it:
var disablerScriptElement = document.createElement('script');
disablerScriptElement.textContent = disablerCode;
document.documentElement.appendChild(disablerScriptElement);
disablerScriptElement.parentNode.removeChild(disablerScriptElement);
Yes you can, alert() is just a JavaScript method, you can override its functionality by doing.
window.alert = function alert(msg) {
console.log('Hidden Alert ' + msg);
};
Just remember to run that content script at document_start within the manifest via run_at manifest content script modifier.
I believe there is an extension that just does that. The developer names it Nice Alert.
https://chrome.google.com/extensions/detail/ehnbelnegmgdnjaghgomaakjcmpcakhk
Thank you. That helped. However, I realized I needed to do this to get it to work
location.href="javascript: window.alert = function(x) {console.log(x)};"
if I wanted to remove alerts and confirms, I can do
location.href="javascript: window.alert = function(x) {console.log(x)}; window.confirm = function(){return true;};";
Related
I'd like to run some javascript in the context of an iframe's window. Right now the only way I can think to do that is to inject a script tag:
myIframe = document.createElement('iframe');
myIframe.setAttribute('name', 'xyz123');
document.body.appendChild(myIframe);
myIframe.contentWindow.document.write(`
<script>
console.log('The current window name is:', window.name);
</script>
`);
Note: this is a same-domain iframe, without a src, so I have full access to the contentWindow.
It's important for my use case that the code runs with the correct globals; window, document etc should all be scoped to the iframe itself.
Is there any other way I can do this? The above works, but the script needs to run on different domains all with different CSP rules, which means adding support for nonces/hashes etc.
Is it possible to do something like:
myIframe.contentWindow.run(function() {
console.log('The current window name is:' window.name);
});
I've tried myIframe.contentWindow.setTimeout but that still seems to run the code in the context of the parent window.
You can actually create that run function, and then apply a callback function to this which of course will be the iframe context. Then you can access iframe elements by using this:
myIframe.contentWindow.run = function(fn) {
fn.apply(this);
};
myIframe.contentWindow.run(function() {
console.log('(run) The current window name is:', this.window.name);
});
Console output
(run) The current window name is: xyz123
You can check my example here: http://zikro.gr/dbg/html/con-frame/
EDIT
If you want to just use window rather than this.window, then you can create a parameter to the inline function with he name window, and then just pass this.window to that function like this:
myIframe.contentWindow.run = function(fn) {
fn.call(this, this.window);
};
myIframe.contentWindow.run(function(window) {
console.log('(run) The current window name is:', window.name);
});
And it still works as expected.
Maybe split the javascript to part run from main window (let's call it main.js) and from iframe (let's call it iframe.js). Then in iframe's src place link to iframe.js or iframe.html which loads js file (I'm not sure if you can include javascript straight from src attribute).
If you load js into the iframe, use solution at Calling a function inside an iframe from outside the iframe.
window.name='main window'; // just for debugging
var myIframe = document.createElement('iframe'), frameScript = document.createElement('script');
document.body.appendChild(myIframe);
frameScript.textContent = "window.top.name='topWindow';window.name = 'xyz123';function WhereAmI(){return(window.name);} window.parent.postMessage('frame_initialized', '*'); "
myIframe.contentWindow.document.documentElement.appendChild(frameScript);
function OnMessage(event) {
if(typeof event.data == 'string') switch(event.data) {
case 'frame_initialized':
myIframe.contentWindow.document.body.appendChild( document.createTextNode(myIframe.contentWindow.WhereAmI()) );
console.log('Now running in', myIframe.contentWindow.WhereAmI());
break;
}
}
window.addEventListener('message', OnMessage, false);
Tested with Firefox and Chromium.
Instead of .textContent you can apply the .src to frameScript, so the script can load asynchronously. Then you can call postMessage as shown above or call a callback function to notify the parent window.
Note that in your original code window.frameElement.name is initialized. Your script asks then for window.name.
FireFox copies the value automatically to the window, causing some confusion.
You need to load the script asynchronously (i.e. $.getScript()) and then invoke it on .contentWindow. I haven't tested but this should work.
(function() {
var myIframe = document.createElement('iframe#iframe'):
var jsSnippet;
var iframeScript = function(url) {
script = document.createElement('script'),
scripts = document.getElementsByTagName('script')[0];
script.src = url;
return jsSnippet = scripts.parentNode.insertBefore(script, scripts);
};
myIframe.setAttribute('name', 'xyz123'):
myIframe.appendChild(jsSnippet);
document.body.appendChild(myIframe);
return document.getElementById('#iframe').contentWindow.iframeScript();
})
These two resources will be helpful if this solution doesn't work:
https://plainjs.com/javascript/ajax/load-a-script-file-asynchronously-49/
Invoking JavaScript code in an iframe from the parent page
This question asks for a way to open a new window using window.open and then inject it with a script. It was not possible because of cross-domain security issues.
However, my problem is that I want to do the exact same thing, except from the same domain to the same domain. Is this possible?
Note that .write does not solve this problem because it wipes all the html from the page first.
You can do something like this:
var theWindow = window.open('http://stackoverflow.com'),
theDoc = theWindow.document,
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
theScript.innerHTML = 'window.onload = ' + injectThis.toString() + ';';
theDoc.body.appendChild(theScript);
This also seems to work:
var theWindow = window.open('http://stackoverflow.com'),
theScript = document.createElement('script');
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript.innerHTML = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
// Append the script to the new window's body.
// Only seems to work with `this`
this.document.body.appendChild(theScript);
};
And if for some reason you want to use eval:
var theWindow = window.open('http://stackoverflow.com'),
theScript;
function injectThis() {
// The code you want to inject goes here
alert(document.body.innerHTML);
}
// Self executing function
theScript = '(' + injectThis.toString() + '());';
theWindow.onload = function () {
this.eval(theScript);
};
What this does (Explanation for the first bit of code. All examples are quite similar):
Opens the new window
Gets a reference to the new window's document
Creates a script element
Places all the code you want to 'inject' into a function
Changes the script's innerHTML to load said function when the window
loads, with the window.onload event (you can also use addEventListener). I used toString() for convenience, so you don't have to concatenate a bunch of strings. toString basically returns the whole injectThis function as a string.
Appends the script to the new window's document.body, it won't actually append it to the document that is loaded, it appends it before it loads (to an empty body), and that's why you have to use window.onload, so that your script can manipulate the new document.
It's probably a good idea to use window.addEventListener('load', injectThis.toString()); instead of window.onload, in case you already have a script within your new page that uses the window.onload event (it'd overwrite the injection script).
Note that you can do anything inside of the injectThis function: append DIVs, do DOM queries, add even more scripts, etc...
Also note that you can manipulate the new window's DOM inside of the theWindow.onload event, using this.
Yes...
var w = window.open(<your local url>);
w.document.write('<html><head>...</head><body>...</body></html>');
Here's a trick I use, it uses query strings, and is client side. Not perfect but it works:
On the sending page, do:
var javascriptToSend = encodeURIComponent("alert('Hi!');");
window.open('mypage.html?javascript=' + javascriptToSend);
Replace mypage.html with your page. Now on the receiving page, add:
(location.href.match(/(?:javascript)=([^&]+)/)[1])&&eval(decodeURIComponent(location.href.match(/(?:javascript)=([^&]+)/)[1]));
You'll have to do some back-and forth to make sure this works.
If you HAVE PHP you can use this more reliable solution on the receiving page:
eval(decodeURIComponent(<?=$_GET['javascript'] ?>));
I am trying to develop a plugin to internet explorer browser using csharp and I try to inject a javascript to the loaded page.
To inject the javascript i used the following code. The code is injected and the alert is working fine.
but code given below should return the value of "msg" to output.
when i run this code i get null value for output. kindly help.
var output= HTMLDocument.parentWindow.execScript("msg()","JScript");
function msg(){
var msg = "This is sample";
alert(msg);
return msg;
}
According to this page:
https://msdn.microsoft.com/en-us/library/ie/ms536420%28v=vs.85%29.aspx
The execCode method returns some sort of null value. Use eval if you want the value of msg().
IE cannot eval functions (Presumably for security reasons).
The best workaround is to put the function in an array, like this:
var func = eval('[' + funcStr + ']')
How can I pass a parameter to the JavaScript in a content script file which is injected using:
chrome.tabs.executeScript(tab.id, {file: "content.js"});
There's not such a thing as "pass a parameter to a file".
What you can do is to either insert a content script before executing the file, or sending a message after inserting the file. I will show an example for these distinct methods below.
Set parameters before execution of the JS file
If you want to define some variables before inserting the file, just nest chrome.tabs.executeScript calls:
chrome.tabs.executeScript(tab.id, {
code: 'var config = 1;'
}, function() {
chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});
If your variable is not as simple, then I recommend to use JSON.stringify to turn an object in a string:
var config = {somebigobject: 'complicated value'};
chrome.tabs.executeScript(tab.id, {
code: 'var config = ' + JSON.stringify(config)
}, function() {
chrome.tabs.executeScript(tab.id, {file: 'content.js'});
});
With the previous method, the variables can be used in content.js in the following way:
// content.js
alert('Example:' + config);
Set parameters after execution of the JS file
The previous method can be used to set parameters after the JS file. Instead of defining variables directly in the global scope, you can use the message passing API to pass parameters:
chrome.tabs.executeScript(tab.id, {file: 'content.js'}, function() {
chrome.tabs.sendMessage(tab.id, 'whatever value; String, object, whatever');
});
In the content script (content.js), you can listen for these messages using the chrome.runtime.onMessage event, and handle the message:
chrome.runtime.onMessage.addListener(function(message, sender, sendResponse) {
// Handle message.
// In this example, message === 'whatever value; String, object, whatever'
});
There are five general ways to pass data to a content script injected with tabs.executeScript()(MDN):
Set the data prior to injecting the script
Use chrome.storage.local(MDN) to pass the data (set prior to injecting your script).
Inject code prior to your script which sets a variable with the data (see detailed discussion for possible security issue).
Set a cookie for the domain in which the content script is being injected. This method can also be used to pass data to manifest.json content scripts which are injected at document_start, without the need for the content script to perform an asynchronous request.
Send/set the data after injecting the script
Use message passing(MDN) to pass the data after your script is injected.
Use chrome.storage.onChanged(MDN) in your content script to listen for the background script to set a value using chrome.storage.local.set()(MDN).
Use chrome.storage.local (set prior to executing your script)
Using this method maintains the execution paradigm you are using of injecting a script that performs a function and then exits. It also does not have the potential security issue of using a dynamic value to build executing code, which is done in the second option below.
From your popup script:
Store the data using chrome.storage.local.set()(MDN).
In the callback for chrome.storage.local.set(), call tabs.executeScript()(MDN).
var updateTextTo = document.getElementById('comments').value;
chrome.storage.local.set({
updateTextTo: updateTextTo
}, function () {
chrome.tabs.executeScript({
file: "content_script3.js"
});
});
From your content script:
Read the data from chrome.storage.local.get()(MDN).
Make the changes to the DOM.
Invalidate the data in storage.local (e.g. remove the key with: chrome.storage.local.remove() (MDN)).
chrome.storage.local.get('updateTextTo', function (items) {
assignTextToTextareas(items.updateTextTo);
chrome.storage.local.remove('updateTextTo');
});
function assignTextToTextareas(newText){
if (typeof newText === 'string') {
Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
el.value = newText;
});
}
}
See: Notes 1 & 2.
Inject code prior to your script to set a variable
Prior to executing your script, you can inject some code that sets a variable in the content script context which your primary script can then use:
Security issue:
The following uses "'" + JSON.stringify().replace(/\\/g,'\\\\').replace(/'/g,"\\'") + "'" to encode the data into text which will be proper JSON when interpreted as code, prior to putting it in the code string. The .replace() methods are needed to A) have the text correctly interpreted as a string when used as code, and B) quote any ' which exist in the data. It then uses JSON.parse() to return the data to a string in your content script. While this encoding is not strictly required, it is a good idea as you don't know the content of the value which you are going to send to the content script. This value could easily be something that would corrupt the code you are injecting (i.e. The user may be using ' and/or " in the text they entered). If you do not, in some way, escape the value, there is a security hole which could result in arbitrary code being executed.
From your popup script:
Inject a simple piece of code that sets a variable to contain the data.
In the callback for chrome.tabs.executeScript()(MDN), call tabs.executeScript() to inject your script (Note: tabs.executeScript() will execute scripts in the order in which you call tabs.executeScript(), as long as they have the same value for runAt. Thus, waiting for the callback of the small code is not strictly required).
var updateTextTo = document.getElementById('comments').value;
chrome.tabs.executeScript({
code: "var newText = JSON.parse('" + encodeToPassToContentScript(updateTextTo) + "');"
}, function () {
chrome.tabs.executeScript({
file: "content_script3.js"
});
});
function encodeToPassToContentScript(obj){
//Encodes into JSON and quotes \ characters so they will not break
// when re-interpreted as a string literal. Failing to do so could
// result in the injection of arbitrary code and/or JSON.parse() failing.
return JSON.stringify(obj).replace(/\\/g,'\\\\').replace(/'/g,"\\'")
}
From your content script:
Make the changes to the DOM using the data stored in the variable
if (typeof newText === 'string') {
Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
el.value = newText;
});
}
See: Notes 1, 2, & 3.
Use message passing(MDN)(send data after content script is injected)
This requires your content script code to install a listener for a message sent by the popup, or perhaps the background script (if the interaction with the UI causes the popup to close). It is a bit more complex.
From your popup script:
Determine the active tab using tabs.query()(MDN).
Call tabs.executeScript()(MDN)
In the callback for tabs.executeScript(), use tabs.sendMessage()(MDN)(which requires knowing the tabId), to send the data as a message.
var updateTextTo = document.getElementById('comments').value;
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.executeScript(tabs[0].id, {
file: "content_script3.js"
}, function(){
chrome.tabs.sendMessage(tabs[0].id,{
updateTextTo: updateTextTo
});
});
});
From your content script:
Add a listener using chrome.runtime.onMessage.addListener()(MDN).
Exit your primary code, leaving the listener active. You could return a success indicator, if you choose.
Upon receiving a message with the data:
Make the changes to the DOM.
Remove your runtime.onMessage listener
#3.2 is optional. You could keep your code active waiting for another message, but that would change the paradigm you are using to one where you load your code and it stays resident waiting for messages to initiate actions.
chrome.runtime.onMessage.addListener(assignTextToTextareas);
function assignTextToTextareas(message){
newText = message.updateTextTo;
if (typeof newText === 'string') {
Array.from(document.querySelectorAll('textarea.comments')).forEach(el => {
el.value = newText;
});
}
chrome.runtime.onMessage.removeListener(assignTextToTextareas); //optional
}
See: Notes 1 & 2.
Note 1: Using Array.from() is fine if you are not doing it many times and are using a browser version which has it (Chrome >= version 45, Firefox >= 32). In Chrome and Firefox, Array.from() is slow compared to other methods of getting an array from a NodeList. For a faster, more compatible conversion to an Array, you could use the asArray() code in this answer. The second version of asArray() provided in that answer is also more robust.
Note 2: If you are willing to limit your code to Chrome version >= 51 or Firefox version >= 50, Chrome has a forEach() method for NodeLists as of v51. Thus, you don't need to convert to an array. Obviously, you don't need to convert to an Array if you use a different type of loop.
Note 3: While I have previously used this method (injecting a script with the variable value) in my own code, I was reminded that I should have included it here when reading this answer.
You can use the args property, see this documentation
const color = '#00ff00';
function changeBackgroundColor(backgroundColor) {
document.body.style.backgroundColor = backgroundColor;
}
chrome.scripting.executeScript(
{
target: {tabId},
func: changeBackgroundColor,
args: [color],
},
() => { ... });
Edit: My mistake - This only applies to injected functions, not files as the question specifies.
#RobW's answer is the perfect answer for this. But for you to implement this you need to initiate global variables.
I suggest an alternative for this, which is similar to #RobW's answer. Instead of passing the variable to the file, you load a function from the content.js file and then initiate the function in your current context using the code: and pass variables from current context.
var argString = "abc";
var argInt = 123;
chrome.tabs.executeScript(tabId, { file: "/content.js" }).then(() => {
chrome.tabs.executeScript(tabId, {
allFrames: false,
code: "myFunction('" + argString + "', " + argInt + "); ",
});
});
This is inspired from #wOxxOm's answer here. This method is really going to be helpful to write a common source code for Manifest v2 & v3
I have a WebBrowser control in my C# application. The web browser is under the user's control, that is, he can load any web page his computer can access on the web (of course limited by proxy, hosts file and so on).
I need to know and to be notified when there is a Javascript call inside the page loaded in the web browser component.
First example: given a link like this
test
When the user clicks the link I need to know that the function "jsFunct" has been called.
Second example: given a call like
<script type="text/javascript">
window.setTimeout("jsFunct()", 1000);
</script>
I need to know that, 1 second after the execution of the script, the function jsFunct has been called.
The best thing would be to have an event fired when the function is called. It would also be great if the event could get the Javascript code executed, or at least the function name in the arguments.
EDIT:
Even if the question is related to the webbrowser component, anything that allows the user to detect javascript activation (even via js) would be fine, being able to inject a js that handles the javascript event and passes it to the wb control triggering some event that it can handle.
You can use window.external to call a C# method when a global function is fired in JavaScript. See WebBrowser Control Overview for details on window.external.
You'll need to set ObjectForScripting: Webbrowser control's window.external is ALWAYS null. for this to work.
Take #Krishna's answer to add the JavaScript (but drop jQuery because it won't be needed):
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(WebBrowser.Head, #"InjectMonitor.js");
The JavaScript below (InjectMonitor.js) will find all global functions and attach your specified handler:
function augment(withFn) {
var name, fn;
for (name in window) {
fn = window[name];
if (typeof fn === 'function') {
window[name] = (function(name, fn) {
var args = arguments;
return function() {
withFn.apply(this, args);
fn.apply(this, arguments);
};
})(name, fn);
}
}
}
augment(function(name, fn) {
console.log("calling " + name, fn);
// window.external.yourC#method
});
In this example, taken from Adding Console Log to Every Function, it just logs the call to console; but using window.external you could send some message back to your C# application with details of what function was called from the client.
Finally, here's a JS Bin example (run it and don't forget the console): JS Bin Example
On the webbrowser load event,
Inject Jquery
Inject Monitor scripts
,
private void addScript(HtmlElement head, string scriptSource)
{
HtmlElement lhe_script = head.Document.CreateElement("script");
IHTMLScriptElement script = (IHTMLScriptElement)lhe_script.DomElement;
script.src = scriptSource;
head.AppendChild(lhe_script);
}
addScript(Webbrowser.Head, #"<Change File Path here>jquery.min.js");
addScript(WebBrowser.Head, #"InjectMonitor.js");
your file InjectMonitor.js should be something like this
$(document).ready(function () {
//Add click event for every anchor on the page loaded- note this merely alerts text on click. you can however add your own function
$("a").click(function (e) { alert($(this).text()); return false;})
});
Well what krishna has answered is interms of pure javascript attaching to events, however i see that you might need to attach it to all the tags(a,p,div,input) etc and to all the events attached to each tag.
i believe the another way is to play around with the BHO(browser helper object) available to your in .net, and if not and you are good at VC++ and MFC you can also play around with Windows Hooks.