Bug 1465581 - Export Screenshots 33.0.0 to Firefox (upgrade Raven to 3.25.2); r=ianbicking,_6a68
authorBarry Chen <bchen@mozilla.com>
Mon, 18 Jun 2018 11:01:44 -0500
changeset 423283 3dda0c68e6bd
parent 423282 5d075fe82827
child 423284 3f8b00d17eea
push id34171
push userccoroiu@mozilla.com
push dateFri, 22 Jun 2018 09:54:15 +0000
treeherdermozilla-central@7f7a190b3d0d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersianbicking, _6a68
bugs1465581
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1465581 - Export Screenshots 33.0.0 to Firefox (upgrade Raven to 3.25.2); r=ianbicking,_6a68 MozReview-Commit-ID: 67A865KrF3z
browser/extensions/screenshots/webextension/build/raven.js
--- a/browser/extensions/screenshots/webextension/build/raven.js
+++ b/browser/extensions/screenshots/webextension/build/raven.js
@@ -1,9 +1,9 @@
-/*! Raven.js 3.24.1 (f3b3500) | github.com/getsentry/raven-js */
+/*! Raven.js 3.25.2 (30b6d4e) | github.com/getsentry/raven-js */
 
 /*
  * Includes TraceKit
  * https://github.com/getsentry/TraceKit
  *
  * Copyright 2018 Matt Robenolt and other contributors
  * Released under the BSD license
  * https://github.com/getsentry/raven-js/blob/master/LICENSE
@@ -69,20 +69,22 @@ module.exports = {
 /*global XDomainRequest:false */
 
 var TraceKit = _dereq_(6);
 var stringify = _dereq_(7);
 var md5 = _dereq_(8);
 var RavenConfigError = _dereq_(1);
 
 var utils = _dereq_(5);
+var isErrorEvent = utils.isErrorEvent;
+var isDOMError = utils.isDOMError;
+var isDOMException = utils.isDOMException;
 var isError = utils.isError;
 var isObject = utils.isObject;
 var isPlainObject = utils.isPlainObject;
-var isErrorEvent = utils.isErrorEvent;
 var isUndefined = utils.isUndefined;
 var isFunction = utils.isFunction;
 var isString = utils.isString;
 var isArray = utils.isArray;
 var isEmptyObject = utils.isEmptyObject;
 var each = utils.each;
 var objectMerge = utils.objectMerge;
 var truncate = utils.truncate;
@@ -110,17 +112,21 @@ var dsnKeys = 'source protocol user pass
 function now() {
   return +new Date();
 }
 
 // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785)
 var _window =
   typeof window !== 'undefined'
     ? window
-    : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
+    : typeof global !== 'undefined'
+      ? global
+      : typeof self !== 'undefined'
+        ? self
+        : {};
 var _document = _window.document;
 var _navigator = _window.navigator;
 
 function keepOriginalCallback(original, callback) {
   return isFunction(callback)
     ? function(data) {
         return callback(data, original);
       }
@@ -200,17 +206,17 @@ function Raven() {
  * @this {Raven}
  */
 
 Raven.prototype = {
   // Hardcode version string so that raven source can be loaded directly via
   // webpack (using a build step causes webpack #1617). Grunt verifies that
   // this value matches package.json during build.
   //   See: https://github.com/getsentry/raven-js/issues/465
-  VERSION: '3.24.1',
+  VERSION: '3.25.2',
 
   debug: false,
 
   TraceKit: TraceKit, // alias to TraceKit
 
   /*
      * Configure Raven with a DSN and extra options
      *
@@ -532,27 +538,45 @@ Raven.prototype = {
    * @return {Raven}
    */
   captureException: function(ex, options) {
     options = objectMerge({trimHeadFrames: 0}, options ? options : {});
 
     if (isErrorEvent(ex) && ex.error) {
       // If it is an ErrorEvent with `error` property, extract it to get actual Error
       ex = ex.error;
+    } else if (isDOMError(ex) || isDOMException(ex)) {
+      // If it is a DOMError or DOMException (which are legacy APIs, but still supported in some browsers)
+      // then we just extract the name and message, as they don't provide anything else
+      // https://developer.mozilla.org/en-US/docs/Web/API/DOMError
+      // https://developer.mozilla.org/en-US/docs/Web/API/DOMException
+      var name = ex.name || (isDOMError(ex) ? 'DOMError' : 'DOMException');
+      var message = ex.message ? name + ': ' + ex.message : name;
+
+      return this.captureMessage(
+        message,
+        objectMerge(options, {
+          // neither DOMError or DOMException provide stack trace and we most likely wont get it this way as well
+          // but it's barely any overhead so we may at least try
+          stacktrace: true,
+          trimHeadFrames: options.trimHeadFrames + 1
+        })
+      );
     } else if (isError(ex)) {
       // we have a real Error object
       ex = ex;
     } else if (isPlainObject(ex)) {
       // If it is plain Object, serialize it manually and extract options
       // This will allow us to group events based on top-level keys
       // which is much better than creating new group when any key/value change
       options = this._getCaptureExceptionOptionsFromPlainObject(options, ex);
       ex = new Error(options.message);
     } else {
       // If none of previous checks were valid, then it means that
+      // it's not a DOMError/DOMException
       // it's not a plain Object
       // it's not a valid ErrorEvent (one with an error property)
       // it's not an Error
       // So bail out and capture it as a simple message:
       return this.captureMessage(
         ex,
         objectMerge(options, {
           stacktrace: true, // if we fall back to captureMessage, default to attempting a new trace
@@ -634,16 +658,24 @@ Raven.prototype = {
     }
 
     // null exception name so `Error` isn't prefixed to msg
     ex.name = null;
     var stack = TraceKit.computeStackTrace(ex);
 
     // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1]
     var initialCall = isArray(stack.stack) && stack.stack[1];
+
+    // if stack[1] is `Raven.captureException`, it means that someone passed a string to it and we redirected that call
+    // to be handled by `captureMessage`, thus `initialCall` is the 3rd one, not 2nd
+    // initialCall => captureException(string) => captureMessage(string)
+    if (initialCall && initialCall.func === 'Raven.captureException') {
+      initialCall = stack.stack[2];
+    }
+
     var fileurl = (initialCall && initialCall.url) || '';
 
     if (
       !!this._globalOptions.ignoreUrls.test &&
       this._globalOptions.ignoreUrls.test(fileurl)
     ) {
       return;
     }
@@ -1431,57 +1463,70 @@ Raven.prototype = {
             }
 
             var fetchData = {
               method: method,
               url: url,
               status_code: null
             };
 
-            return origFetch.apply(this, args).then(function(response) {
-              fetchData.status_code = response.status;
-
-              self.captureBreadcrumb({
-                type: 'http',
-                category: 'fetch',
-                data: fetchData
+            return origFetch
+              .apply(this, args)
+              .then(function(response) {
+                fetchData.status_code = response.status;
+
+                self.captureBreadcrumb({
+                  type: 'http',
+                  category: 'fetch',
+                  data: fetchData
+                });
+
+                return response;
+              })
+              ['catch'](function(err) {
+                // if there is an error performing the request
+                self.captureBreadcrumb({
+                  type: 'http',
+                  category: 'fetch',
+                  data: fetchData,
+                  level: 'error'
+                });
+
+                throw err;
               });
-
-              return response;
-            });
           };
         },
         wrappedBuiltIns
       );
     }
 
     // Capture breadcrumbs from any click that is unhandled / bubbled up all the way
     // to the document. Do this before we instrument addEventListener.
     if (autoBreadcrumbs.dom && this._hasDocument) {
       if (_document.addEventListener) {
         _document.addEventListener('click', self._breadcrumbEventHandler('click'), false);
         _document.addEventListener('keypress', self._keypressEventHandler(), false);
-      } else if(_document.attachEvent){
+      } else if (_document.attachEvent) {
         // IE8 Compatibility
         _document.attachEvent('onclick', self._breadcrumbEventHandler('click'));
         _document.attachEvent('onkeypress', self._keypressEventHandler());
       }
     }
 
     // record navigation (URL) changes
     // NOTE: in Chrome App environment, touching history.pushState, *even inside
     //       a try/catch block*, will cause Chrome to output an error to console.error
     // borrowed from: https://github.com/angular/angular.js/pull/13945/files
     var chrome = _window.chrome;
     var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime;
     var hasPushAndReplaceState =
       !isChromePackagedApp &&
       _window.history &&
-      history.pushState &&
-      history.replaceState;
+      _window.history.pushState &&
+      _window.history.replaceState;
     if (autoBreadcrumbs.location && hasPushAndReplaceState) {
       // TODO: remove onpopstate handler on uninstall()
       var oldOnPopState = _window.onpopstate;
       _window.onpopstate = function() {
         var currentHref = self._location.href;
         self._captureUrlChange(self._lastHref, currentHref);
 
         if (oldOnPopState) {
@@ -1500,18 +1545,18 @@ Raven.prototype = {
             // coerce to string (this is what pushState does)
             self._captureUrlChange(self._lastHref, url + '');
           }
 
           return origHistFunction.apply(this, arguments);
         };
       };
 
-      fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns);
-      fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns);
+      fill(_window.history, 'pushState', historyReplacementFunction, wrappedBuiltIns);
+      fill(_window.history, 'replaceState', historyReplacementFunction, wrappedBuiltIns);
     }
 
     if (autoBreadcrumbs.console && 'console' in _window && console.log) {
       // console
       var consoleMethodCallback = function(msg, data) {
         self.captureBreadcrumb({
           message: msg,
           level: data.level,
@@ -1717,17 +1762,17 @@ Raven.prototype = {
           values: [
             {
               type: type,
               value: message,
               stacktrace: stacktrace
             }
           ]
         },
-        culprit: fileurl
+        transaction: fileurl
       },
       options
     );
 
     // Fire away!
     this._send(data);
   },
 
@@ -1791,17 +1836,17 @@ Raven.prototype = {
   },
 
   _getHttpData: function() {
     if (!this._hasNavigator && !this._hasDocument) return;
     var httpData = {};
 
     if (this._hasNavigator && _navigator.userAgent) {
       httpData.headers = {
-        'User-Agent': navigator.userAgent
+        'User-Agent': _navigator.userAgent
       };
     }
 
     // Check in `window` instead of `document`, as we may be in ServiceWorker environment
     if (_window.location && _window.location.href) {
       httpData.url = _window.location.href;
     }
 
@@ -1832,17 +1877,17 @@ Raven.prototype = {
    *       data object with a single frame (derived from the onerror args).
    */
   _isRepeatData: function(current) {
     var last = this._lastData;
 
     if (
       !last ||
       current.message !== last.message || // defined for captureMessage
-      current.culprit !== last.culprit // defined for captureException/onerror
+      current.transaction !== last.transaction // defined for captureException/onerror
     )
       return false;
 
     // Stacktrace interface (i.e. from captureMessage)
     if (current.stacktrace || last.stacktrace) {
       return isSameStacktrace(current.stacktrace, last.stacktrace);
     } else if (current.exception || last.exception) {
       // Exception interface (i.e. from captureException/onerror)
@@ -2177,17 +2222,21 @@ Raven.prototype = {
         evaluated[key] = typeof value === 'function' ? value() : value;
       }
     }
 
     return evaluated;
   },
 
   _logDebug: function(level) {
-    if (this._originalConsoleMethods[level] && this.debug) {
+    // We allow `Raven.debug` and `Raven.config(DSN, { debug: true })` to not make backward incompatible API change
+    if (
+      this._originalConsoleMethods[level] &&
+      (this.debug || this._globalOptions.debug)
+    ) {
       // In IE<10 console methods do not have their own 'apply' method
       Function.prototype.apply.call(
         this._originalConsoleMethods[level],
         this._originalConsole,
         [].slice.call(arguments, 1)
       );
     }
   },
@@ -2290,30 +2339,38 @@ var _window =
 
 function isObject(what) {
   return typeof what === 'object' && what !== null;
 }
 
 // Yanked from https://git.io/vS8DV re-used under CC0
 // with some tiny modifications
 function isError(value) {
-  switch ({}.toString.call(value)) {
+  switch (Object.prototype.toString.call(value)) {
     case '[object Error]':
       return true;
     case '[object Exception]':
       return true;
     case '[object DOMException]':
       return true;
     default:
       return value instanceof Error;
   }
 }
 
 function isErrorEvent(value) {
-  return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]';
+  return Object.prototype.toString.call(value) === '[object ErrorEvent]';
+}
+
+function isDOMError(value) {
+  return Object.prototype.toString.call(value) === '[object DOMError]';
+}
+
+function isDOMException(value) {
+  return Object.prototype.toString.call(value) === '[object DOMException]';
 }
 
 function isUndefined(what) {
   return what === void 0;
 }
 
 function isFunction(what) {
   return typeof what === 'function';
@@ -2346,16 +2403,34 @@ function supportsErrorEvent() {
   try {
     new ErrorEvent(''); // eslint-disable-line no-new
     return true;
   } catch (e) {
     return false;
   }
 }
 
+function supportsDOMError() {
+  try {
+    new DOMError(''); // eslint-disable-line no-new
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
+function supportsDOMException() {
+  try {
+    new DOMException(''); // eslint-disable-line no-new
+    return true;
+  } catch (e) {
+    return false;
+  }
+}
+
 function supportsFetch() {
   if (!('fetch' in _window)) return false;
 
   try {
     new Headers(); // eslint-disable-line no-new
     new Request(''); // eslint-disable-line no-new
     new Response(); // eslint-disable-line no-new
     return true;
@@ -2438,17 +2513,23 @@ function objectMerge(obj1, obj2) {
 function objectFrozen(obj) {
   if (!Object.isFrozen) {
     return false;
   }
   return Object.isFrozen(obj);
 }
 
 function truncate(str, max) {
-  return !max || str.length <= max ? str : str.substr(0, max) + '\u2026';
+  if (typeof max !== 'number') {
+    throw new Error('2nd argument to `truncate` function should be a number');
+  }
+  if (typeof str !== 'string' || max === 0) {
+    return str;
+  }
+  return str.length <= max ? str : str.substr(0, max) + '\u2026';
 }
 
 /**
  * hasKey, a better form of hasOwnProperty
  * Example: hasKey(MainHostObject, property) === true/false
  *
  * @param {Object} host object to check property
  * @param {string} key to check
@@ -2737,20 +2818,19 @@ function utf8Length(value) {
   return ~-encodeURI(value).split(/%..|./).length;
 }
 
 function jsonSize(value) {
   return utf8Length(JSON.stringify(value));
 }
 
 function serializeValue(value) {
-  var maxLength = 40;
-
   if (typeof value === 'string') {
-    return value.length <= maxLength ? value : value.substr(0, maxLength - 1) + '\u2026';
+    var maxLength = 40;
+    return truncate(value, maxLength);
   } else if (
     typeof value === 'number' ||
     typeof value === 'boolean' ||
     typeof value === 'undefined'
   ) {
     return value;
   }
 
@@ -2856,23 +2936,27 @@ function sanitize(input, sanitizeKeys) {
 
   return sanitizeWorker(safeInput);
 }
 
 module.exports = {
   isObject: isObject,
   isError: isError,
   isErrorEvent: isErrorEvent,
+  isDOMError: isDOMError,
+  isDOMException: isDOMException,
   isUndefined: isUndefined,
   isFunction: isFunction,
   isPlainObject: isPlainObject,
   isString: isString,
   isArray: isArray,
   isEmptyObject: isEmptyObject,
   supportsErrorEvent: supportsErrorEvent,
+  supportsDOMError: supportsDOMError,
+  supportsDOMException: supportsDOMException,
   supportsFetch: supportsFetch,
   supportsReferrerPolicy: supportsReferrerPolicy,
   supportsPromiseRejectionEvent: supportsPromiseRejectionEvent,
   wrappedCallback: wrappedCallback,
   each: each,
   objectMerge: objectMerge,
   truncate: truncate,
   objectFrozen: objectFrozen,
@@ -2922,20 +3006,34 @@ var _window =
 var _slice = [].slice;
 var UNKNOWN_FUNCTION = '?';
 
 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
 var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/;
 
 function getLocationHref() {
   if (typeof document === 'undefined' || document.location == null) return '';
-
   return document.location.href;
 }
 
+function getLocationOrigin() {
+  if (typeof document === 'undefined' || document.location == null) return '';
+
+  // Oh dear IE10...
+  if (!document.location.origin) {
+    document.location.origin =
+      document.location.protocol +
+      '//' +
+      document.location.hostname +
+      (document.location.port ? ':' + document.location.port : '');
+  }
+
+  return document.location.origin;
+}
+
 /**
  * TraceKit.report: cross-browser processing of unhandled exceptions
  *
  * Syntax:
  *   TraceKit.report.subscribe(function(stackInfo) { ... })
  *   TraceKit.report.unsubscribe(function(stackInfo) { ... })
  *   TraceKit.report(exception)
  *   try { ...code... } catch(ex) { TraceKit.report(ex); }
@@ -3333,16 +3431,54 @@ TraceKit.computeStackTrace = (function c
       } else {
         continue;
       }
 
       if (!element.func && element.line) {
         element.func = UNKNOWN_FUNCTION;
       }
 
+      if (element.url && element.url.substr(0, 5) === 'blob:') {
+        // Special case for handling JavaScript loaded into a blob.
+        // We use a synchronous AJAX request here as a blob is already in
+        // memory - it's not making a network request.  This will generate a warning
+        // in the browser console, but there has already been an error so that's not
+        // that much of an issue.
+        var xhr = new XMLHttpRequest();
+        xhr.open('GET', element.url, false);
+        xhr.send(null);
+
+        // If we failed to download the source, skip this patch
+        if (xhr.status === 200) {
+          var source = xhr.responseText || '';
+
+          // We trim the source down to the last 300 characters as sourceMappingURL is always at the end of the file.
+          // Why 300? To be in line with: https://github.com/getsentry/sentry/blob/4af29e8f2350e20c28a6933354e4f42437b4ba42/src/sentry/lang/javascript/processor.py#L164-L175
+          source = source.slice(-300);
+
+          // Now we dig out the source map URL
+          var sourceMaps = source.match(/\/\/# sourceMappingURL=(.*)$/);
+
+          // If we don't find a source map comment or we find more than one, continue on to the next element.
+          if (sourceMaps) {
+            var sourceMapAddress = sourceMaps[1];
+
+            // Now we check to see if it's a relative URL.
+            // If it is, convert it to an absolute one.
+            if (sourceMapAddress.charAt(0) === '~') {
+              sourceMapAddress = getLocationOrigin() + sourceMapAddress.slice(1);
+            }
+
+            // Now we strip the '.map' off of the end of the URL and update the
+            // element so that Sentry can match the map to the blob.
+            element.url = sourceMapAddress.slice(0, -4);
+          }
+        }
+      }
+
       stack.push(element);
     }
 
     if (!stack.length) {
       return null;
     }
 
     return {