@@ -20,6 +20,7 @@ var uuid4 = utils.uuid4;
2020var htmlTreeAsString = utils . htmlTreeAsString ;
2121var parseUrl = utils . parseUrl ;
2222var isString = utils . isString ;
23+ var fill = utils . fill ;
2324
2425var wrapConsoleMethod = require ( './console' ) . wrapMethod ;
2526
@@ -30,6 +31,7 @@ function now() {
3031 return + new Date ( ) ;
3132}
3233
34+
3335// First, check for JSON support
3436// If there is no JSON, we no-op the core features of Raven
3537// since JSON is required to encode the payload
@@ -52,7 +54,8 @@ function Raven() {
5254 crossOrigin : 'anonymous' ,
5355 collectWindowErrors : true ,
5456 maxMessageLength : 0 ,
55- stackTraceLimit : 50
57+ stackTraceLimit : 50 ,
58+ autoBreadcrumbs : true
5659 } ;
5760 this . _ignoreOnError = 0 ;
5861 this . _isRavenInstalled = false ;
@@ -138,6 +141,21 @@ Raven.prototype = {
138141 this . _globalOptions . includePaths = joinRegExp ( this . _globalOptions . includePaths ) ;
139142 this . _globalOptions . maxBreadcrumbs = Math . max ( 0 , Math . min ( this . _globalOptions . maxBreadcrumbs || 100 , 100 ) ) ; // default and hard limit is 100
140143
144+ var autoBreadcrumbDefaults = {
145+ xhr : true ,
146+ console : true ,
147+ dom : true ,
148+ location : true
149+ } ;
150+
151+ var autoBreadcrumbs = this . _globalOptions . autoBreadcrumbs ;
152+ if ( { } . toString . call ( autoBreadcrumbs ) === '[object Object]' ) {
153+ autoBreadcrumbs = objectMerge ( autoBreadcrumbDefaults , autoBreadcrumbs ) ;
154+ } else if ( autoBreadcrumbs !== false ) {
155+ autoBreadcrumbs = autoBreadcrumbDefaults ;
156+ }
157+ this . _globalOptions . autoBreadcrumbs = autoBreadcrumbs ;
158+
141159 this . _globalKey = uri . user ;
142160 this . _globalSecret = uri . pass && uri . pass . substr ( 1 ) ;
143161 this . _globalProject = uri . path . substr ( lastSlash + 1 ) ;
@@ -167,7 +185,9 @@ Raven.prototype = {
167185 TraceKit . report . subscribe ( function ( ) {
168186 self . _handleOnErrorStackInfo . apply ( self , arguments ) ;
169187 } ) ;
170- this . _wrapBuiltIns ( ) ;
188+ this . _instrumentTryCatch ( ) ;
189+ if ( self . _globalOptions . autoBreadcrumbs )
190+ this . _instrumentBreadcrumbs ( ) ;
171191
172192 // Install all of the plugins
173193 this . _drainPlugins ( ) ;
@@ -742,16 +762,10 @@ Raven.prototype = {
742762 /**
743763 * Install any queued plugins
744764 */
745- _wrapBuiltIns : function ( ) {
765+ _instrumentTryCatch : function ( ) {
746766 var self = this ;
747767
748- function fill ( obj , name , replacement , noUndo ) {
749- var orig = obj [ name ] ;
750- obj [ name ] = replacement ( orig ) ;
751- if ( ! noUndo ) {
752- self . _wrappedBuiltIns . push ( [ obj , name , orig ] ) ;
753- }
754- }
768+ var wrappedBuiltIns = self . _wrappedBuiltIns ;
755769
756770 function wrapTimeFn ( orig ) {
757771 return function ( fn , t ) { // preserve arity
@@ -777,6 +791,8 @@ Raven.prototype = {
777791 } ;
778792 }
779793
794+ var autoBreadcrumbs = this . _globalOptions . autoBreadcrumbs ;
795+
780796 function wrapEventTarget ( global ) {
781797 var proto = window [ global ] && window [ global ] . prototype ;
782798 if ( proto && proto . hasOwnProperty && proto . hasOwnProperty ( 'addEventListener' ) ) {
@@ -790,10 +806,10 @@ Raven.prototype = {
790806 // can sometimes get 'Permission denied to access property "handle Event'
791807 }
792808
793-
794- // TODO: more than just click
809+ // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs`
810+ // so that we don't have more than one wrapper function
795811 var before ;
796- if ( global === 'EventTarget' || global === 'Node' ) {
812+ if ( autoBreadcrumbs && autoBreadcrumbs . dom && ( global === 'EventTarget' || global === 'Node' ) ) {
797813 if ( evtName === 'click' ) {
798814 before = self . _breadcrumbEventHandler ( evtName ) ;
799815 } else if ( evtName === 'keypress' ) {
@@ -802,46 +818,24 @@ Raven.prototype = {
802818 }
803819 return orig . call ( this , evtName , self . wrap ( fn , undefined , before ) , capture , secure ) ;
804820 } ;
805- } ) ;
821+ } , wrappedBuiltIns ) ;
806822 fill ( proto , 'removeEventListener' , function ( orig ) {
807823 return function ( evt , fn , capture , secure ) {
808824 fn = fn && ( fn . __raven_wrapper__ ? fn . __raven_wrapper__ : fn ) ;
809825 return orig . call ( this , evt , fn , capture , secure ) ;
810826 } ;
811- } ) ;
827+ } , wrappedBuiltIns ) ;
812828 }
813829 }
814830
815- function wrapProp ( prop , xhr ) {
816- if ( prop in xhr && isFunction ( xhr [ prop ] ) ) {
817- fill ( xhr , prop , function ( orig ) {
818- return self . wrap ( orig ) ;
819- } , true /* noUndo */ ) ; // don't track filled methods on XHR instances
820- }
821- }
822-
823- fill ( window , 'setTimeout' , wrapTimeFn ) ;
824- fill ( window , 'setInterval' , wrapTimeFn ) ;
831+ fill ( window , 'setTimeout' , wrapTimeFn , wrappedBuiltIns ) ;
832+ fill ( window , 'setInterval' , wrapTimeFn , wrappedBuiltIns ) ;
825833 if ( window . requestAnimationFrame ) {
826834 fill ( window , 'requestAnimationFrame' , function ( orig ) {
827835 return function ( cb ) {
828836 return orig ( self . wrap ( cb ) ) ;
829837 } ;
830- } ) ;
831- }
832-
833- // Capture breadcrubms from any click that is unhandled / bubbled up all the way
834- // to the document. Do this before we instrument addEventListener.
835- if ( this . _hasDocument ) {
836- if ( document . addEventListener ) {
837- document . addEventListener ( 'click' , self . _breadcrumbEventHandler ( 'click' ) , false ) ;
838- document . addEventListener ( 'keypress' , self . _keypressEventHandler ( ) , false ) ;
839- }
840- else {
841- // IE8 Compatibility
842- document . attachEvent ( 'onclick' , self . _breadcrumbEventHandler ( 'click' ) ) ;
843- document . attachEvent ( 'onkeypress' , self . _keypressEventHandler ( ) ) ;
844- }
838+ } , wrappedBuiltIns ) ;
845839 }
846840
847841 // event targets borrowed from bugsnag-js:
@@ -851,7 +845,41 @@ Raven.prototype = {
851845 wrapEventTarget ( eventTargets [ i ] ) ;
852846 }
853847
854- if ( 'XMLHttpRequest' in window ) {
848+ var $ = window . jQuery || window . $ ;
849+ if ( $ && $ . fn && $ . fn . ready ) {
850+ fill ( $ . fn , 'ready' , function ( orig ) {
851+ return function ( fn ) {
852+ return orig . call ( this , self . wrap ( fn ) ) ;
853+ } ;
854+ } , wrappedBuiltIns ) ;
855+ }
856+ } ,
857+
858+
859+ /**
860+ * Instrument browser built-ins w/ breadcrumb capturing
861+ * - XMLHttpRequests
862+ * - DOM interactions (click/typing)
863+ * - window.location changes
864+ * - console
865+ *
866+ * Can be disabled or individually configured via the `autoBreadcrumbs` config option
867+ */
868+ _instrumentBreadcrumbs : function ( ) {
869+ var self = this ;
870+ var autoBreadcrumbs = this . _globalOptions . autoBreadcrumbs ;
871+
872+ var wrappedBuiltIns = self . _wrappedBuiltIns ;
873+
874+ function wrapProp ( prop , xhr ) {
875+ if ( prop in xhr && isFunction ( xhr [ prop ] ) ) {
876+ fill ( xhr , prop , function ( orig ) {
877+ return self . wrap ( orig ) ;
878+ } ) ; // intentionally don't track filled methods on XHR instances
879+ }
880+ }
881+
882+ if ( autoBreadcrumbs . xhr && 'XMLHttpRequest' in window ) {
855883 var xhrproto = XMLHttpRequest . prototype ;
856884 fill ( xhrproto , 'open' , function ( origOpen ) {
857885 return function ( method , url ) { // preserve arity
@@ -867,7 +895,7 @@ Raven.prototype = {
867895
868896 return origOpen . apply ( this , arguments ) ;
869897 } ;
870- } ) ;
898+ } , wrappedBuiltIns ) ;
871899
872900 fill ( xhrproto , 'send' , function ( origSend ) {
873901 return function ( data ) { // preserve arity
@@ -896,7 +924,7 @@ Raven.prototype = {
896924 if ( 'onreadystatechange' in xhr && isFunction ( xhr . onreadystatechange ) ) {
897925 fill ( xhr , 'onreadystatechange' , function ( orig ) {
898926 return self . wrap ( orig , undefined , onreadystatechangeHandler ) ;
899- } , true /* noUndo */ ) ;
927+ } /* intentionally don't track this instrumentation */ ) ;
900928 } else {
901929 // if onreadystatechange wasn't actually set by the page on this xhr, we
902930 // are free to set our own and capture the breadcrumb
@@ -905,7 +933,21 @@ Raven.prototype = {
905933
906934 return origSend . apply ( this , arguments ) ;
907935 } ;
908- } ) ;
936+ } , wrappedBuiltIns ) ;
937+ }
938+
939+ // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
940+ // to the document. Do this before we instrument addEventListener.
941+ if ( autoBreadcrumbs . dom && this . _hasDocument ) {
942+ if ( document . addEventListener ) {
943+ document . addEventListener ( 'click' , self . _breadcrumbEventHandler ( 'click' ) , false ) ;
944+ document . addEventListener ( 'keypress' , self . _keypressEventHandler ( ) , false ) ;
945+ }
946+ else {
947+ // IE8 Compatibility
948+ document . attachEvent ( 'onclick' , self . _breadcrumbEventHandler ( 'click' ) ) ;
949+ document . attachEvent ( 'onkeypress' , self . _keypressEventHandler ( ) ) ;
950+ }
909951 }
910952
911953 // record navigation (URL) changes
@@ -915,7 +957,7 @@ Raven.prototype = {
915957 var chrome = window . chrome ;
916958 var isChromePackagedApp = chrome && chrome . app && chrome . app . runtime ;
917959 var hasPushState = ! isChromePackagedApp && window . history && history . pushState ;
918- if ( hasPushState ) {
960+ if ( autoBreadcrumbs . location && hasPushState ) {
919961 // TODO: remove onpopstate handler on uninstall()
920962 var oldOnPopState = window . onpopstate ;
921963 window . onpopstate = function ( ) {
@@ -930,7 +972,7 @@ Raven.prototype = {
930972 fill ( history , 'pushState' , function ( origPushState ) {
931973 // note history.pushState.length is 0; intentionally not declaring
932974 // params to preserve 0 arity
933- return function ( /* state, title, url */ ) {
975+ return function ( /* state, title, url */ ) {
934976 var url = arguments . length > 2 ? arguments [ 2 ] : undefined ;
935977
936978 // url argument is optional
@@ -941,32 +983,24 @@ Raven.prototype = {
941983
942984 return origPushState . apply ( this , arguments ) ;
943985 } ;
944- } ) ;
986+ } , wrappedBuiltIns ) ;
945987 }
946988
947- // console
948- var consoleMethodCallback = function ( msg , data ) {
949- self . captureBreadcrumb ( {
950- message : msg ,
951- level : data . level ,
952- category : 'console'
953- } ) ;
954- } ;
989+ if ( autoBreadcrumbs . console && 'console' in window && console . log ) {
990+ // console
991+ var consoleMethodCallback = function ( msg , data ) {
992+ self . captureBreadcrumb ( {
993+ message : msg ,
994+ level : data . level ,
995+ category : 'console'
996+ } ) ;
997+ } ;
955998
956- if ( 'console' in window && console . log ) {
957999 each ( [ 'debug' , 'info' , 'warn' , 'error' , 'log' ] , function ( _ , level ) {
9581000 wrapConsoleMethod ( console , level , consoleMethodCallback ) ;
9591001 } ) ;
9601002 }
9611003
962- var $ = window . jQuery || window . $ ;
963- if ( $ && $ . fn && $ . fn . ready ) {
964- fill ( $ . fn , 'ready' , function ( orig ) {
965- return function ( fn ) {
966- return orig . call ( this , self . wrap ( fn ) ) ;
967- } ;
968- } ) ;
969- }
9701004 } ,
9711005
9721006 _restoreBuiltIns : function ( ) {
0 commit comments