PDA

View Full Version : [DEFER-717][2.x,3.x] Stack overflow in IE6/7 using prototype 1.6.1_rc3



jared lewis
28 Aug 2009, 7:01 AM
I am really having a difficult time solving this one today....

At this point I am no longer even running any ext code at all I am only trying to get the page to load without creating a stack overflow error.

This occurs when including Prototype 1.6.1_rc3 and Ext 3.0 together on the same page, this is my include structure:

<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/scriptaculous.js"></script>
<script type="text/javascript" src="/js/ext/adapter/prototype/ext-prototype-adapter.js"></script>
<script type="text/javascript" src="/js/ext/ext-all.js"></script>

I have narrowed down the error to the Ext.apply block and the createDelegate function inside the ext-prototype-adapter.js

file, I am not sure if this is where the error is actually occuring or if the prototype library is somehow colliding with this block of code.

This is only an issue in IE6 and randomly in IE7, and only occurs with any of the Prototype 1.6.1(x) versions, when prototype

is rolled back to 1.6.0.3 everything works as it should.

If anyone else has had any luck with this issue or if anyone ahs any suggestions I would be very appreciative. Thanks guys.

jared lewis
28 Aug 2009, 10:10 AM
surely atleast one other person on these forums uses prototype and ext together.....???

Condor
29 Aug 2009, 3:23 AM
Not many users are working with the prototype release candiate.

Could you post a testcase that demonstrates the problem?

jared lewis
29 Aug 2009, 12:53 PM
This is a full page that it throws the error in Internet Explorer 6. Notice all that I am doing is simply including these 2 files on the page, I am not even running any javascript code at all.



<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />

<script type="text/javascript" src="/js/prototype.js"></script>
<script type="text/javascript" src="/js/scriptaculous.js"></script>
<script type="text/javascript" src="/js/ext/adapter/prototype/ext-prototype-adapter.js"></script>
<script type="text/javascript" src="/js/ext/ext-all.js"></script>

</head>

<body>
</body>

</html>

Mike Robinson
30 Aug 2009, 7:09 AM
Could you be attempting "recursive include?"

Condor
30 Aug 2009, 10:29 PM
No, it has to do with the fact that both Ext and Prototype define a defer method on Function.

The Prototype version doesn't allow specifying a delay or a scope and just calls the method with the same scope after 10ms with the specified parameters.

The infinite loop is caused by Prototype calling pollDoScroll.defer() (no parameters) which will cause the Ext defer method to directly call pollDoScroll without any delay.

I'm going to move this thread to bugs sections.

evant
30 Aug 2009, 10:59 PM
I'm not really sure how to go about this one.

We can't modify the default behaviour of the Ext defer function, even if we include a special version for prototype it's still going to cause problems because of the way the methods are called.

Condor
30 Aug 2009, 11:20 PM
I also don't see a way to fix this without breaking compatibility somewhere.

Maybe we should consider deprecating all extensions to build-in javascript types (String, Date, Number, Array, Function) in Ext 4.0.
The methods should be moved within the Ext namespace and the overrides of the build-in types should be moved to a legacy js file (that can be excluded when required).

jared lewis
31 Aug 2009, 5:26 AM
Thank you guys so much for looking at this, I know prototype 1.6.1 is about to be officially released, but I am not sure of any changes to the defer method....

Do any of you have any suggestions or workarounds to possibly get around the issue?

evant
31 Aug 2009, 5:29 AM
Make sure any calls to defer() in prototype pass at least 1 parameter (the delay).

jared lewis
31 Aug 2009, 5:39 AM
thank you very much evant, it works.... I really really appreciate it. There are only about 5 instances of the defer method being called, so it is a fairly easy work around.

Condor
31 Aug 2009, 5:43 AM
The Prototype and Ext delay methods are completely incompatible! The Prototype defer method doesn't have delay and scope parameters at all (it is always delay:10, scope:this). The only parameters are the arguments that get passed to the method.

You could 'hack' this by adding 10 and optionally a scope to every defer call in the prototype library, e.g.

if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(10, this, 1);
...
timer = pollDoScroll.defer(10);
etc.

hendricd
31 Aug 2009, 5:55 AM
Here's a possible solution. Include it somewhere after all the libs are loaded.



(function(){
//should be Ext's implem at this point
var eDefer = Function.prototype.defer;

Function.prototype.defer = function() {
var args = arguments, L = args.length;
if( L==0 || ( L==1 && args[0]==1)) //common for Prototype Ajax requests
{
return this.delay.curry(0.01).apply(this, args);
}
return eDefer.apply(this, args);
};
})();
Tweak it ;)

jjathman
11 Sep 2009, 8:09 AM
Here's an alternative way to work around this problem. I like this better as it doesn't try to hack prototype to use Ext's defer function, it just changes prototype's version to have it's own name. This patch basically changes the name of prototype's defer function to be "p_defer". My group does not use the defer function at all so this worked fine for us since the changes were localized to prototype. Hope it helps people. Note this patch is against the newly released 1.6.1 version, not a release candidate.



Index: prototype.js


===================================================================
--- prototype.js (revision 3901)
+++ prototype.js (working copy)
@@ -345,7 +345,7 @@
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
- defer: defer,
+ p_defer: defer,
wrap: wrap,
methodize: methodize
}
@@ -1412,7 +1412,7 @@
this.transport.open(this.method.toUpperCase(), this.url,
this.options.asynchronous);

- if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1);
+ if (this.options.asynchronous) this.respondToReadyState.bind(this).p_defer(1);

this.transport.onreadystatechange = this.onStateChange.bind(this);
this.setRequestHeaders();
@@ -1894,7 +1894,7 @@
element.innerHTML = content.stripScripts();
}

- content.evalScripts.bind(content).defer();
+ content.evalScripts.bind(content).p_defer();
return element;
}

@@ -1908,7 +1908,7 @@
content = Object.toHTML(content);
var range = element.ownerDocument.createRange();
range.selectNode(element);
- content.evalScripts.bind(content).defer();
+ content.evalScripts.bind(content).p_defer();
content = range.createContextualFragment(content.stripScripts());
}
element.parentNode.replaceChild(content, element);
@@ -1945,7 +1945,7 @@
if (position == 'top' || position == 'after') childNodes.reverse();
childNodes.each(insert.curry(element));

- content.evalScripts.bind(content).defer();
+ content.evalScripts.bind(content).p_defer();
}

return element;
@@ -2803,7 +2803,7 @@
}
else element.outerHTML = content.stripScripts();

- content.evalScripts.bind(content).defer();
+ content.evalScripts.bind(content).p_defer();
return element;
};
}
@@ -4677,7 +4677,7 @@
function pollDoScroll() {
try { document.documentElement.doScroll('left'); }
catch(e) {
- timer = pollDoScroll.defer();
+ timer = pollDoScroll.p_defer();
return;
}
fireContentLoadedEvent();
@@ -4688,7 +4688,7 @@
} else {
document.observe('readystatechange', checkReadyState);
if (window == top)
- timer = pollDoScroll.defer();
+ timer = pollDoScroll.p_defer();
}

Event.observe(window, 'load', fireContentLoadedEvent);

wildspitze
15 Sep 2009, 10:22 PM
Had the same problem in IE6 and IE7 after changing prototype from 1.6.0.3 to 1.6.1.

The solution from @jjathman fixed the problem, but there is one more change (marked red) to do within the prototype source.



===================================================================
--- prototype.js (revision 3901)
+++ prototype.js (working copy)
@@ -345,7 +345,7 @@
bindAsEventListener: bindAsEventListener,
curry: curry,
delay: delay,
- defer: defer,
+ p_defer: p_defer,
wrap: wrap,
methodize: methodize

jjathman
16 Sep 2009, 9:15 AM
I don't think that additional change is necessary (and if you did it you would need to make sure and update the function name higher up in the source). You only need to change the name of the defer function that gets returned as the "public" name of the function. The other defer function is more of a "private" function and hence it doesn't really matter what the name of it is.

Jangla
4 Nov 2009, 8:39 AM
I've had this problem too (I have a google map panel that defers rendering till the tab it's on is selected).

jjathman - fogive the n00b question, but what tool are you using to output that patch and how can I use it to quickly patch my code?

jjathman
4 Nov 2009, 8:55 AM
@Jangla
To help with how to patch your code it would help to know what source code tool you use (most have patching functionality built in) and what OS you use.

Jangla
4 Nov 2009, 8:55 AM
Dreamweaver CS4 on Vista

jjathman
4 Nov 2009, 9:14 AM
On windows you can use the patch program found here: http://gnuwin32.sourceforge.net/packages/patch.htm

Though if you've never used it before it can be kind of cryptic. Personally if you've never done patches before and don't think you will in the future, I would just look at my patch and make the changes to prototype by hand. It will only take you a couple minutes.

vikspl
11 Mar 2010, 12:09 PM
What do you think of the following solution (executed after both libs are included), do you think it will work?
Since Prototype provides the delay() method to do what ExtJS does with it's defer() function, there shouldn't be any calls to prototype's defer() with any arguments.

Thanks,
Doug


Function.prototype.defer_extjs = Function.prototype.defer;

Object.extend(Function.prototype, (function() {

function update(array, args) {
var arrayLength = array.length, length = args.length;
while (length--) array[arrayLength + length] = args[length];
return array;
}

function defer() {
if (arguments.length > 0)
return this.defer_extjs.apply(this, arguments);
var args = update([0.01], arguments);
return this.delay.apply(this, args);
}

return {
defer: defer
}
})());

carl23934
29 Mar 2010, 6:47 AM
Maybe we should consider deprecating all extensions to build-in javascript types (String, Date, Number, Array, Function) in Ext 4.0.


Ding ding ding!!

Trying to put mootools out of business with better compatability? how dare you!

adam.jimenez
31 Mar 2010, 1:23 AM
Is this something that will be fixed in Ext? or is it a problem with prototype?

Condor
31 Mar 2010, 2:37 AM
Is this something that will be fixed in Ext? or is it a problem with prototype?

Neither... and both...

It is a conflict between Ext and Prototype. Both define Function.prototype.defer, but differently.

A 'nice' javascript library should not be defining methods in predefined classes like Function. On the other side, these methods do make programming a lot easier and more readable (which is why both Ext and Prototype use them).

One of the two libraries will need to be modified, but I don't know which one...

adam.jimenez
31 Mar 2010, 2:50 AM
Neither... and both...

It is a conflict between Ext and Prototype. Both define Function.prototype.defer, but differently.

A 'nice' javascript library should not be defining methods in predefined classes like Function. On the other side, these methods do make programming a lot easier and more readable (which is why both Ext and Prototype use them).

One of the two libraries will need to be modified, but I don't know which one...

I've submitted a bug report to prototype:
https://prototype.lighthouseapp.com/projects/8886-prototype/tickets/1018-prototype-161-conflict-with-ext-js-causing-stack-overflow-error

jdalton
31 Mar 2010, 9:12 AM
Seems this has been an issue for a while now. Why is Ext extending natives to begin with :-/
http://dev.rubyonrails.org/ticket/11227

evant
31 Mar 2010, 2:31 PM
Why is Ext extending natives to begin with


For the same reason Prototype does: http://api.prototypejs.org/language/function.html ;)

adam.jimenez
31 Mar 2010, 2:35 PM
For the same reason Prototype does: http://api.prototypejs.org/language/function.html ;)

In fairness prototype is well known for polluting the global namespace. I thought ext on the other hand was different.

jdalton
31 Mar 2010, 2:53 PM
In fairness prototype is well known for polluting the global namespace. I thought ext on the other hand was different.

ditto

vikspl
29 Apr 2010, 5:53 PM
In fairness prototype is well known for polluting the global namespace. I thought ext on the other hand was different.

Polluting is such a harsh word...by your logic PrototypeJS will not be able to serve the primary purpose of the library - to abstract the difference between all major browsers. This is a bad coincidence, not a bad code design.

jdalton
29 Apr 2010, 6:30 PM
Polluting is such a harsh word...by your logic PrototypeJS will not be able to serve the primary purpose of the library - to abstract the difference between all major browsers. This is a bad coincidence, not a bad code design.

There is nothing harsh about the word "polluting". Prototype and others that extend natives/DOM will run into problems.

They have had problems with paving over Array/DOM methods that were later added to browsers with different implementations, problems with name collisions like Element#select and input elements, and problems with third party code such as Ext JS. Keep in mind Prototype started its life as part of Ruby on Rails. It is not intended to play well with 3rd party code and is generally used in controlled environments where everything is built from it.

Additional reading http://perfectionkills.com/whats-wrong-with-extending-the-dom/

Jamie Avins
29 Apr 2010, 6:37 PM
Not the place for a flame war, lets keep it to the bug at hand. You can argue this in the Open Discussion forum if you'd like :)

jdalton
29 Apr 2010, 6:53 PM
Not the place for a flame war, lets keep it to the bug at hand. You can argue this in the Open Discussion forum if you'd like :)

Shame on me for posting legit problems and linking to a Prototype core dev's concerns on the topic.
As for ExtJS extending natives, it really shouldn't if it wants to play well with other frameworks.

Jamie Avins
29 Apr 2010, 6:58 PM
The whole polluting comment started this train off the rails. So lets keep it to the bugs on the bug forum.

nicktak
5 May 2010, 5:28 PM
In the meantime the prototype adapter should either be removed or a big comment placed against it to advise against its use? Getting a stack overflow from something that is meant to be working is a really bad look.

dduffy
3 Jul 2010, 8:48 AM
I get this stackoverflow issue in IE 8, when using prototype version 1.6.1 and EXTJS 3.2.1. I notice the issue only occurs when my GridPanel needs to display the vertical scroll bar, if I make the grid panel height large enough the stackoverflow does not occur. Has anyone a suggested solution apart from taking our prototype which I cant, as it is integarted in out framework.

I tried Vikspl suggested solution and it did not appear to fix the issue.

Darragh

dduffy
5 Jul 2010, 12:31 AM
Vikspl - I tried your suggested solution, but stackoverflow continued to be a problem, in both IE8 and IE7.

EXTJS Guys - Evant & Jamie, do you know if you are maing a change for EXTJS 4.0 to help fix the issue ?

Darragh

Jamie Avins
5 Jul 2010, 9:15 AM
The plan is to try something a bit different with adapters in 4.x which should accommodate this type of difference. Note that we may also remove some of the lesser used adapters from the distribution.

dduffy
22 Aug 2010, 11:55 AM
for the record I used Hendricd suggested solution and it works nicely.



var eDefer = Function.prototype.defer;

Function.prototype.defer = function() {
var args = arguments, L = args.length;
if( L==0 || ( L==1 && args[0]==1)) //common for Prototype Ajax requests
{
return this.delay.curry(0.01).apply(this, args);
}
return eDefer.apply(this, args);
};
}

spikey-erik
15 Sep 2010, 11:36 AM
After having tried just about everything I could think of, I found your thread and tried several things, making sure a 'recursive include' was not happening. I looked everywhere, and its taken quite a chunk of time to find the problem. This is for IE only (FF et al work fine) and ExtJS 3.2.1 with Prototype release 1.6.1 .

I just modified the defer() call in prototype.js for the pollDoScroll() function, and as suggested, added a 0.001 arg to the defer() call, and presto-it works!

Not sure about how to or if we should propagate this mod to the Prototype folks, but I am guessing that its really in ExtJS that should take into account the diff with IE and what happens with Ext.onReady() and what follows (as the timing of things seems quite different from FF et al).


function pollDoScroll() {
try {
document.documentElement.doScroll('left');
}
catch(e) {
// MOD:e - without an arg to defer() this can run too fast and cause a stack overflow in IE (presumably because it just does its init differently than FF et al and takes longer)
timer = pollDoScroll.defer( 0.001 );
return;
}
fireContentLoadedEvent();
}

Starfall
22 Dec 2010, 3:46 AM
I confirm: the solution by spikey-erik works.

kandy
12 Dec 2011, 8:22 AM
Insert before Extjs and prototype


(function() {
if (/MSIE 7/.test(navigator.userAgent)) {
var st = window.setTimeout;
window.setTimeout = function(f, time) {
return st(f, Math.max(time, 50));
};
}
})();