Hi folks
I fixed a very weird bug today.
Initially the bug was raised that some actions causes JavaScript error in Firefox:
TypeError: access to strict mode caller function is censored
This was not happening in IE or Chrome.
I started to debug and found that the problem is within MicrosoftAjax framework. I am working on a legacy ASP.NET WebForms project which uses and relies on it heavily.
For the blogging purposes I looked for a debug/unminified version of the problematic script and found it here: http://ajax.aspnetcdn.com/ajax/4.5.2/1/MicrosoftAjaxWebForms.Debug.js
I would like to refer to some lines so I uploaded the same file to gist.
The problematic line was https://gist.github.com/mnaoumov/55be04ca588ff22f851f#file-microsoftajaxwebforms-debug-js-L738
while (caller.arguments.callee.caller && --recursionLimit) {
where at some stage caller.arguments.callee variable equals to the function that is defined in some script with
"use strict";
directive
and therefore it prohibited to call its caller
And the reason why this issue occurred in Firefox only is the line https://gist.github.com/mnaoumov/55be04ca588ff22f851f#file-microsoftajaxwebforms-debug-js-L733
var event = window.event;
window.event is undefined in Firefox. This event variable is not really needed. The only place it is used https://gist.github.com/mnaoumov/55be04ca588ff22f851f#file-microsoftajaxwebforms-debug-js-L785 would be called under pretty rare condition according to the code flow.
activeElement = event ? (event.target || event.srcElement) : null;
So in order to fix the problem I think it is pretty safe to fake window.event for this function.
So I’ve written a hack
/**
* This is required to fix a bug in MicrosoftAjaxWebForms.js
* in Firefox where if window.event is not initialized, it loops stack
* via arguments.callee.caller chain and breaks because of the
* "use strict" mode
*/
function hackEventWithinDoPostBack() {
var originalDoPostBack = window.__doPostBack;
window.__doPostBack = function hackedDoPostBack() {
if (!window.event)
window.event = {};
return originalDoPostBack.apply(this, arguments);
};
}
And this worked fine in Firefox. I thought it would work fine in IE and Chrome as well because I am checking for window.event before overriding but it was not!
IE11 started to fail in other places with the following error
Assignment to read-only properties is not allowed in strict mode
That was caused by the
window.event = {};
line written above. I found that window.event still can be undefined but you are not allowed to set it explicitly.
That appeared to be a bit tricky to overcome and eventually I came up with a pretty sophisticated solution
/**
* This is required to fix a bug in MicrosoftAjaxWebForms.js
* in Firefox where if window.event is not initialized, it loops stack
* via arguments.callee.caller chain and breaks because of the
* "use strict" mode
*
* Hacking window.event property is required because it is
* not settable in Internet Explorer
*/
function hackEventWithinDoPostBack() {
var originalEventDescriptor = Object.getOwnPropertyDescriptor(Window.prototype, "event");
var hackEventVariable = false;
var eventPropertyHolder;
Object.defineProperty(window, "event", {
configurable: true,
get: function get() {
var result = originalEventDescriptor ? originalEventDescriptor.get.apply(this, arguments) : eventPropertyHolder;
if (result || !hackEventVariable)
return result;
return {};
},
set: function set(value) {
if (originalEventDescriptor)
originalEventDescriptor.set.apply(this, arguments);
else
eventPropertyHolder = value;
}
});
var originalDoPostBack = window.__doPostBack;
window.__doPostBack = function hackedDoPostBack() {
hackEventVariable = true;
originalDoPostBack.apply(this, arguments);
hackEventVariable = false;
};
}
This works in IE, Chrome and Firefox.
Such a workaround, isn’t it?!
Stay tuned