I would like to pass errors to an alert to warn the user they made mistake in their code even if they don't have console open.
var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
var head = doc.getElementsByTagName('head')[0];
var scriptElement = doc.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.text = scripts;
try{
head.appendChild(scriptElement);
}
catch(e){ alert("error:"+e.message +" linenumber:"+e.lineNumber);}
The appendChild throws an error when the scripts contain an error. It goes straight to the console though, and I want it to display in an alert, because it is for kids and they might not check the console. The try catch block does not catch the error.
I tried it with eval(scripts).
try{
eval(scripts);} catch(e){ alert("error:"+e.message +" linenumber:"+e.lineNumber);}
this does work but it means that the code is executed twice, and that is very inconvenient in some cases.
I tried monkey patching the console.error:
console.log=function(){alert("taking over the log");}
console.error=function(){alert("taking over the log");}
but that only works when I literally use console.error. Not when an actual error is thrown.
What function sends the error to the console in the case of a real error,if it isn't console.error? and can I access it and change it?
Any ideas? Help would be really appreciated.
Thanks Jenita
Whilst try ... catch will work on the code that the script runs initially, as Jenita says it won't catch Syntax Errors, and also it won't catch errors thrown by callback functions which execute later (long after the try-catch has finished). That means no errors from any functions passed to setTimeout or addEventListener.
However, you can try a different approach. Register an error listener on the window.
window.addEventListener("error", handleError, true);
function handleError(evt) {
if (evt.message) { // Chrome sometimes provides this
alert("error: "+evt.message +" at linenumber: "+evt.lineno+" of file: "+evt.filename);
} else {
alert("error: "+evt.type+" from element: "+(evt.srcElement || evt.target));
}
}
This will be called when an exception is thrown from a callback function. But it will also trigger on general DOM errors such as images failing to load, which you may not be interested in.
It should also fire on Syntax Errors but only if it was able to run first so you should put it in a separate script from the one that may contain typos! (A Syntax Error later in a script will prevent valid lines at the top of the same script from running.)
Unfortunately, I never found a way to get a line number from the evt in Firefox. (Edit: Poke around, I think it might be there now.)
I discovered this when trying to write FastJSLogger, an in-page logger I used back when the browser devtools were somewhat slow.
Desperate to catch line numbers, I started to experiment with wrappers for setTimeout and addEventListener that would re-introduce try-catch around those calls. For example:
var realAddEventListener = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function(type,handler,capture,other){
var newHandler = function(evt) {
try {
return handler.apply(this,arguments);
} catch (e) {
alert("error handling "+type+" event:"+e.message +" linenumber:"+e.lineNumber);
}
};
realAddEventListener.call(this,type,newHandler,capture,other);
};
Obviously this should be done before any event listeners are registered, and possibly even before libraries like jQuery are loaded, to prevent them from grabbing a reference to the real addEventListener before we have been able to replace it.
Ok so the less elegant but highly efficient way of doing this is 'refactoring' your innate console functions. Basically any error or warnings you get are being outputted there by a javascript function that is pretty similar to the familiar console.log() function. The functions that I am talking about are console.warn(), console.info() and console.error(). now let's 're-map' what each of those do:
//remap console to some other output
var console = (function(oldCons){
return {
log: function(text){
oldCons.log(text);
//custom code here to be using the 'text' variable
//for example: var content = text;
//document.getElementById(id).innerHTML = content
},
info: function (text) {
oldCons.info(text);
//custom code here to be using the 'text' variable
},
warn: function (text) {
oldCons.warn(text);
//custom code here to be using the 'text' variable
},
error: function (text) {
oldCons.error(text);
//custom code here to be using the 'text' variable
}
};
}(window.console));
//Then redefine the old console
window.console = console;
Now, generally I would highly advise against using something like this into production and limit it to debugging purposes, but since you are trying to develop a functionality that shows the output of the console, the lines are blurry there, so I'll leave it up to you.
You could wrap the script in its own try/catch, something like:
var doc=(frame.contentWindow.document || obj.contentDocument|| obj.contentWindow);
var head = doc.getElementsByTagName('head')[0];
var scriptElement = doc.createElement('script');
scriptElement.setAttribute('type', 'text/javascript');
scriptElement.text = "try{"+scripts+"}catch(e){console.error(e);alert('Found this error: ' + e +'. Check the console.')}"
head.appendChild(scriptElement);
Related
So I'm creating a mod for the singleplayer browser game Cookie Clicker. In my mod I allow the user to insert in their own code to do their own special things to interact with my mod's main function.
However, when the user codes on my custom editor, I want to "test" their code before they save to make sure no errors happen, and if they do, display a error message with what they did and where they did it. Getting the error is easy with a try/catch. But I noticed the error message is:
SynaxError: missing ) after argument list
at new Function (<anonymous>)
at HTMLAnchorElement.save.onclick (chrome-extension://dhdgffkkebhmkfjojejmpbldmpobfkfo/userscript.html?name=Building%2520Sorter.user.js&id=18320655-b018-42e2-8fa5-7fb0cc8d2d70:578:24)
Which isn't helpful for me at all. The most I could salvage from this is the first line. However, that doesn't tell the user at all where the error is located in their code.
the 578:24 that points to the supposed error is:
try{
//code.value is a STRING of the user's code
let func = new Function(code.value);//<-- error points here in my source code.
func.call(null, [[0, 1, 2], Game]);
save.classList.remove('ModBuildingSorter_unsaved');
}
catch(e){
console.dir(e);
}
What I would like to happen is when the user sumbits:
return function(array){
return array.sort(function(building1,building2){
return building1.price - building2.price;
};// missing array.sort closing parenthesis
}
get's ran, I can get a syntax error telling me it's on line 4
Is there a way I can do this? Make the user's code act kinda like it's own file and try running it so I can find out which row & column the error is located?
You could, in theory, run the function from an eval()
i.e.:
try {
let a = "function test(o){console.lo(o)}test('hello');" // Minified function
eval(a)
} catch (e) {
console.log(e)
}
Here is the unminified function for example purposes:
function test(o)
{
console.lo(o) // <-- Error
}
test('hello');
and this returns the error correctly, which is
TypeError: console.lo is not a function
at test (eval at <anonymous> (D:\StackOverflowSandbox\index.js:3:5), <anonymous>:1:26)
Hope I've helped.
To shortcut a long comment section on "don't use new Function" and/or "eval is evil", this question is about how to access, if possible, error information that is related to a new Function() constructor failing. It's mostly a question to discover a limit in what the browser will let me do when trying to exploit JavaScript to the extent that the spec and standard browser implementations allow. So with that disclaimer in place:
When evaluating code through a new Function() call, is there a way to find out where in the function's content a syntax error occurs, if illegal-syntax code is being evaluated? i.e.:
try {
var generator = new Function(input);
try {
generator();
}
catch (runtimeError) {
console.error("legal code; unforeseen result: ", runtimeError);
}
}
catch (syntaxError) {
console.error("illegal code; syntax errors: ", syntaxError);
}
When the building of the generator fails, is there a way to find out (from the browser, not using jslint or another external library) what the error was or where it occurred?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError/prototype mentions that a SyntaxError has a filename and linenumber property, but these are undefined for dynamic code evaluated through a new Function() constructor from what I can tell, so relying on the error object itself seems not to be an option. Are there alternative ways to introduce the code to the browser so that the code, once we know it has syntax errors from a failing new Function call, can be used to find out where the problem is according to the JS engine used?
(Of course, if the goal was to simply find syntax errors, jslint as a preprocess step would be the go-to solution, but I'm more interested in whether or not browsers can in some way be made to report this information, even if in limited form like "there is SOME error on line/char ...")
afaik impossible to find out where it occured. but you may want to see Exception.message to fetch information what the error was.
example: http://jsbin.com/IRoDiJIV/1/watch?js
Found a solution myself using a simiar method to setting breakpoints in evaled code
In chrome dev tools's sources panel, I put the following in a conditional breakpoint on the new Function line (since it's library code and I can't change it.)
(function(eval_js, load_js) {
try {
eval(eval_js);
} catch (e) {
(function addCode(js) {
var e = document.createElement('script');
e.type = 'text/javascript';
e.src = 'data:text/javascript;charset=utf-8,' + escape(js);
document.body.appendChild(e);
console.warn("Inserted Script for ", js);
})(load_js.replace(/;/g,";\n"));
handlerCode = "";
return false;
}
return false;
})("new Function('event', handlerCode)", handlerCode)
This may be a bad question, but I've noticed that as I'm writing coding along using mootools When I've got some code that goes through callbacks, bindings and generally isn't just a straight forward function call, if there's an error it doesn't get picked up by either Firebug or Chrome's console it just silently fails, and I'm forced to track down the error using trys and such that don't give you handy information like the line of code that's failing. It's like writing code for IE6 all you have to go on is some opaque message like 'can not read 'x' of undefined.'
I realize that the question isn't specific enough to ask 'how do I avoid this' but does anyone else run into this problem and if so how do you work around it? I'm also a little confused how an error could be picked up by a try/catch block, but not the javascript console.
EDIT:
OK, I've come up with something that reproduces the error
say you've got a function
function foo(){
var x = value.blah;
}
if I call that function like foo() I rightly get an reference error in my console. If, however, I call it like
(function(){
foo.attempt();
})()
I get no error in the console, but if I change foo to be
function foo(){
try{
var x = value.blah;
} catch(e){console.log(e)}
}
the console will log e but of course without the handle 'line: whatever' information.
I have considerable experience fiddling with errors in JavaScript. I've mostly used Chrome for building my understanding but most of it applies to Firefox and Internet Explorer as well.
I can immediately debunk your assumption about silent JavaScript errors. They don't exist, Errors always show. There might be a bug in Firefox or the Chrome's webdev, but the errors are there.
The most common way for errors not to show up is because you're catching them yourself. Perhaps prematurely.
I've figured out what I think is the best strategy for catching errors:
1. Always throw things that are Errors or inherited from Errors.
Ex: not: throw "Precondition failed" but throw new Error("Precondition failed").
This is because Errors are weird in JavaScript (I have no other word for it). If you want a stacktrace (and heaven's yes you want a stacktrace) you'll need to throw an Error (and not a string).
2. Don't use window.onerror Not much to say here. It's useless. You have no control over what get's flung to this function. It might be your code, it might be a broken plugin that a visitor uses. Also, no stacktrace.
3. Have one (global) error handler / when to catch errors
JavaScript is event driven. This has some unexpected consequences. Observe the following code:
try {
setTimeout(function () {
throw new Error("nope! :D");
}, 1);
} catch (e) {
console.log(e);
}
You will not see this error. (Firebug / console will catch it though)
This is because the inner function runs in it's own event and the try-catch statement no longer applies to it. The correct way is:
try {
setTimeout(function () {
try {
throw new Error("nope! :D");
} catch (e) {
console.log("Hell yea!", e);
}
}, 1);
} catch (e) {
console.log(e);
}
Or just make a function that wraps a function in a try-catch:
function wrap(wrap_dat_func) {
return function () {
try {
wrap_dat_func.apply(wrap_dat_func, arguments);
} catch (e) {
// send to error handler
}
}
}
Use like:
setTimeout(wrap(function () {
// etc
}), 1);
So basically whenever you generate a new event, wrap the callback in your global try catch function. So wrap call to setTimeout, setInterval all DOM related events like onclick onload ondocumentready, also AJAX calls onreadystatechanged.
How to get proper stacktraces (over events!) is another long winded explanation.
I have an air application, in which the user types javascript in a textarea, and it is eval'd in an mx:HTML component, but even with try/catch around the eval, and around the code in the eval, and an HTMLUncaughtScriptExceptionEvent handler, it still throws an error. htmlWindow is html.htmlLoader.window.
try { htmlWindow.eval("try {" + script.text + "} catch (error:Error) { trace(error) }); } catch (error:Error) { trace(error) }
The application errors on that line as soon as I enter text in script, with
ReferenceError: Can't find variable: d
at Main/reloadHTML()[C:\Users\Christian\Adobe Flash Builder 4.5\JavaScript plus Scratch\src\Main.mxml:264]
at Main/__script_change()[C:\Users\Christian\Adobe Flash Builder 4.5\JavaScript plus Scratch\src\Main.mxml:324]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:13128]
at spark.components.supportClasses::SkinnableTextBase/textDisplay_changeHandler()[E:\dev\4.5.1\frameworks\projects\spark\src\spark\components\supportClasses\SkinnableTextBase.as:2265]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.5.1\frameworks\projects\framework\src\mx\core\UIComponent.as:13128]
at spark.components::RichEditableText/textContainerManager_flowOperationCompleteHandler()[E:\dev\4.5.1\frameworks\projects\spark\src\spark\components\RichEditableText.as:4808]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flashx.textLayout.container::TextContainerManager/dispatchEvent()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\container\TextContainerManager.as:1553]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at flashx.textLayout.elements::TextFlow/dispatchEvent()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\elements\TextFlow.as:859]
at flashx.textLayout.edit::EditManager/finalizeDo()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\edit\EditManager.as:669]
at flashx.textLayout.edit::EditManager/doOperation()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\edit\EditManager.as:613]
at flashx.textLayout.edit::EditManager/flushPendingOperations()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\edit\EditManager.as:873]
at flashx.textLayout.edit::SelectionManager/enterFrameHandler()[C:\Vellum\branches\v2\2.0\dev\output\openSource\textLayout\src\flashx\textLayout\edit\SelectionManager.as:1859]`
Any way to stop the error? I tried script.change="html.htmlText = '<script>' + script.text + '</script'>", but I have htmlWindow.log = log; htmlWindow.rotateSprite = rotateSprite;, and if i copy-paste log('test') in before changing it, it works, if I change it before or after, it never works again, so I'm hoping to get this eval working.
I'm not sure you can get this code to be 100% exception free. A user could write completely invalid code like zlçrza ;à"çé°$ which would not throw an exception but simply crash the JS compiler. Could you explain why you let users type javascript code directly? Perhaps you need to approach this problem some other way?
I can call a function directly (I'll use alert as an example) like so
alert("Hello World!"); // pops up an alert window
However, when I put a function in an object, calling it no longer works:
d = {func: alert};
d.func("Hello World!"); // doesn't do anything
d["func"]("Hello World!"); // also doesn't do anything
I figured maybe I needed to explicitly pass in a blank this argument, so I tried
d.func(null, "Hello World!") // still nothing
but to no avail. Interestingly, this does work
d.func.apply(null, ["Hello World!"]); // success!
but that's so gratuitously verbose it makes my teeth hurt (to quote JWZ). Is there a more concise, less ugly way?
Functions in JavaScript are passed by value. The alert() function is natively implemented, meaning it has no JavaScript value. Depending on your browser, the meaninfulness (forgive me for that) of that native wrapper varies. Your code actually works in Google Chrome, but won't work in Firefox, and off the top of my head I'm going to say it won't work in Internet Explorer, either, which is generally not friendly about modifying native objects and functions. You could use the following instead:
d = {
func: function (message) {
alert(message);
}
};
If you try this:
function test(x) {
alert(x);
}
var x = {func: test}
x.func('Hi!');
It works as you expect. When I try doing this to alert directly Firebug gives me the following error message:
[Exception... "Cannot modify properties of a WrappedNative"
nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)"
location: "JS frame :: http://stackoverflow.com/questions/859466/javascript-function-in-an-object-hash :: anonymous :: line 72" data: no]
So I am guessing it's a security thing or something to do with it being a native function.
I've always done it like this:
var d = Object;
d.hello = function(msg) {
alert(msg)
};
d.hello('hello');
Of course, you can also use PrototypeJS to get all object oriented:
var Message = Class.create( {
display: function(msg) {
alert(msg);
}
});
var msg = new Message();
msg.display('hello');