Wednesday, August 29, 2012

Using JavaScript line breaking in YUI Compressor

For the past 5 months of introducing JavaScript exceptions logging, there have been "Object doesn't support this property or method" or "Object doesn't support property or method 'apply' that has been elusive in trying to diagnose in Internet Explorer browsers.  The error messages never occurred in other browsers but we saw them quite often in different users.  While some of these exceptions were generated from actually calling methods on JavaScript objects didn't exist, many of the stack traces seemed to emanate directly from jQuery.

One of our challenges was to try to understand from where these exceptions within jQuery were occurring.  jQuery usually comes minified with no line breaks, so we ran our JS minifiers with the YUI Compressor with the --line-break option (i.e. --line-break 150).  Before adding this option, Internet Explorer would often report an error on line 2, which pretty much amounted to the entire jQuery code. By breaking the minified code into smaller chunks, the line numbers could then allow further information on pinpointing this exact source of conflicts:

java -jar ../external/yuicompressor-2.4.7.jar jquery-1.7.2.min.js --line-break 150 > jquery-1.7.2_yui.min.js

url: https://www.myhost.com/static/js/jquery-1.7.2_yui.min.js
line: 33
context:
(f._data(m,"events")||{})[c.type]&&f._data(m,"handle"),q&&q.apply(m,d),q=o&&m[o],q&&f.acceptData(m)&&q.apply(m,d)===!1&&c.preve
 ntDefault()
}c.type=h,!g&&!c.isDefaultPrevented()&&(!p._default||p._default.apply(e.ownerDocument,d)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)&&o&&e[h]&&(h!=="focus"&&h!=="blur"||c.target.offsetWidth!==0)&&!f.isWindow(e)&&(n=e[o],n&&(e[o]=null),f.event.triggered=h,e[h](),f.event.triggered=b,n&&(e[o]=n));return c.result}},dispatch:function(c){c=f.event.fix(c||a.event);var d=(f._data(this,"events")||{})[c.type]||[],e=d.delegateCount,g=[].slice.call(arguments,0),h=!c.exclusive&&!c.namespace,i=f.event.special[c.type]||{},j=[],k,l,m,n,o,p,q,r,s,t,u;g[0]=c,c.delegateTarget=this;if(!i.preDispatch||i.preDispatch.call(this,c)!==!1){if(e&&(!c.button||c.type!=="click")){n=f(this),n.context=this.ownerDocument||this;for(m=c.target;m!=this;m=m.parentNode||this){if(m.disabled!==!0){p={},r=[],n[0]=m;for(k=0;ke&&j.push({elem:this,matches:d.slice(e)});fo
 r(k=0;k

url: https://www.myhost.com/static/js/jquery-1.7.2_yui.min.js
column: None
line: 34
func: filter

url: https://www.myhost.com/static/js/jquery-1.7.2_yui.min.js
column: None
line: 31
func: trigger

This stack trace helped us pinpoint the issue to the Comcast Protection Suite, since it indicated the  problem was happening directly inside the jQuery Event module.  The jQuery Event module is used to attach and trigger jQuery events, as well as to implement the event propagation path described in the W3 standard.  By issuing try/except clauses within the dispatch() code, we were able to find exactly where the exception occurred:

        for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {

            cur = eventPath[i][0];
            event.type = eventPath[i][1];

            handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
            if ( handle ) {
  handle.apply( cur, data );
            }
     // Note that this is a bare JS function and not a jQuery handler                                                                                            
            handle = ontype && cur[ ontype ];
            if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
         event.preventDefault();
            }
        }

In other words, what jQuery seems to do is execute all the jQuery-related events before attempting to call the native JavaScript events (i.e. jQuery 'click' events will first be executed before the native 'onclick' events get called).  Somehow the Comcast Protection Suite adds an onclick handler that appears as 'undefined' to jQuery.  The if statement passes except fails when attempt to execute the handle.apply() statement.

More on this finding on the Comcast Protection Suite in this next writeup...

No comments:

Post a Comment