@@ -31,6 +31,7 @@ function Raven() {
3131 this . _hasDocument = ! isUndefined ( _document ) ;
3232 this . _hasNavigator = ! isUndefined ( _navigator ) ;
3333 this . _lastCapturedException = null ;
34+ this . _lastData = null ;
3435 this . _lastEventId = null ;
3536 this . _globalServer = null ;
3637 this . _globalKey = null ;
@@ -1334,6 +1335,55 @@ Raven.prototype = {
13341335 return this . _backoffDuration && now ( ) - this . _backoffStart < this . _backoffDuration ;
13351336 } ,
13361337
1338+ /**
1339+ * Returns true if the in-process data payload matches the signature
1340+ * of the previously-sent data
1341+ *
1342+ * NOTE: This has to be done at this level because TraceKit can generate
1343+ * data from window.onerror WITHOUT an exception object (IE8, IE9,
1344+ * other old browsers). This can take the form of an "exception"
1345+ * data object with a single frame (derived from the onerror args).
1346+ */
1347+ _isRepeatData : function ( current ) {
1348+ var last = this . _lastData ;
1349+
1350+ if ( ! last ||
1351+ current . message !== last . message ||
1352+ current . culprit !== last . culprit )
1353+ return false ;
1354+
1355+ // If there's no stacktrace, at this point we consider
1356+ // them the same event (e.g. captureMessage)
1357+ if ( ! ( current . exception && last . exception ) )
1358+ return true ;
1359+
1360+ var currentException = current . exception . values [ 0 ] ;
1361+ var lastException = last . exception . values [ 0 ] ;
1362+
1363+ if ( currentException . type !== lastException . type ||
1364+ currentException . value !== lastException . value )
1365+ return false ;
1366+
1367+ var currentFrames = currentException . stacktrace ;
1368+ var lastFrames = lastException . stacktrace ;
1369+
1370+ if ( currentFrames . length !== lastFrames . length )
1371+ return false ;
1372+
1373+ // Iterate through every frame; bail out if anything differs
1374+ var a , b ;
1375+ for ( var i = 0 ; i < currentFrames . length ; i ++ ) {
1376+ a = currentFrames [ i ] ;
1377+ b = lastFrames [ i ] ;
1378+ if ( a . filename !== b . filename ||
1379+ a . lineno !== b . lineno ||
1380+ a . colno !== b . colno ||
1381+ a [ 'function' ] !== b [ 'function' ] )
1382+ return false ;
1383+ }
1384+ return true ;
1385+ } ,
1386+
13371387 _setBackoffState : function ( request ) {
13381388 // If we are already in a backoff state, don't change anything
13391389 if ( this . _shouldBackoff ( ) ) {
@@ -1460,6 +1510,14 @@ Raven.prototype = {
14601510 // Try and clean up the packet before sending by truncating long values
14611511 data = this . _trimPacket ( data ) ;
14621512
1513+ if ( ! this . _globalOptions . allowDuplicates && this . _isRepeatData ( data ) ) {
1514+ this . _logDebug ( 'warn' , 'Raven dropped repeat event: ' , data ) ;
1515+ return ;
1516+ }
1517+
1518+ // Store outbound payload after trim
1519+ this . _lastData = data ;
1520+
14631521 this . _logDebug ( 'debug' , 'Raven about to send:' , data ) ;
14641522
14651523 var auth = {
0 commit comments