1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Handles uncaught exceptions thrown by extensions. By default this is to 6// log an error message, but tests may override this behaviour. 7var handler = function(message, e) { 8 console.error(message); 9}; 10 11/** 12 * Append the error description and stack trace to |message|. 13 * 14 * @param {string} message - The prefix of the error message. 15 * @param {Error|*} e - The thrown error object. This object is potentially 16 * unsafe, because it could be generated by an extension. 17 * @param {string=} priorStackTrace - The stack trace to be appended to the 18 * error message. This stack trace must not include stack frames of |e.stack|, 19 * because both stack traces are concatenated. Overlapping stack traces will 20 * confuse extension developers. 21 * @return {string} The formatted error message. 22 */ 23function formatErrorMessage(message, e, priorStackTrace) { 24 if (e) 25 message += ': ' + safeErrorToString(e, false); 26 27 var stack; 28 try { 29 // If the stack was set, use it. 30 // |e.stack| could be void in the following common example: 31 // throw "Error message"; 32 stack = $String.self(e && e.stack); 33 } catch (e) {} 34 35 // If a stack is not provided, capture a stack trace. 36 if (!priorStackTrace && !stack) 37 stack = getStackTrace(); 38 39 stack = filterExtensionStackTrace(stack); 40 if (stack) 41 message += '\n' + stack; 42 43 // If an asynchronouse stack trace was set, append it. 44 if (priorStackTrace) 45 message += '\n' + priorStackTrace; 46 47 return message; 48} 49 50function filterExtensionStackTrace(stack) { 51 if (!stack) 52 return ''; 53 // Remove stack frames in the stack trace that weren't associated with the 54 // extension, to not confuse extension developers with internal details. 55 stack = $String.split(stack, '\n'); 56 stack = $Array.filter(stack, function(line) { 57 return $String.indexOf(line, 'chrome-extension://') >= 0; 58 }); 59 return $Array.join(stack, '\n'); 60} 61 62function getStackTrace() { 63 var e = {}; 64 $Error.captureStackTrace(e, getStackTrace); 65 return e.stack; 66} 67 68function getExtensionStackTrace() { 69 return filterExtensionStackTrace(getStackTrace()); 70} 71 72/** 73 * Convert an object to a string. 74 * 75 * @param {Error|*} e - A thrown object (possibly user-supplied). 76 * @param {boolean=} omitType - Whether to try to serialize |e.message| instead 77 * of |e.toString()|. 78 * @return {string} The error message. 79 */ 80function safeErrorToString(e, omitType) { 81 try { 82 return $String.self(omitType && e.message || e); 83 } catch (e) { 84 // This error is exceptional and could be triggered by 85 // throw {toString: function() { throw 'Haha' } }; 86 return '(cannot get error message)'; 87 } 88} 89 90/** 91 * Formats the error message and invokes the error handler. 92 * 93 * @param {string} message - Error message prefix. 94 * @param {Error|*} e - Thrown object. 95 * @param {string=} priorStackTrace - Error message suffix. 96 * @see formatErrorMessage 97 */ 98exports.handle = function(message, e, priorStackTrace) { 99 message = formatErrorMessage(message, e, priorStackTrace); 100 handler(message, e); 101}; 102 103// |newHandler| A function which matches |handler|. 104exports.setHandler = function(newHandler) { 105 handler = newHandler; 106}; 107 108exports.getStackTrace = getStackTrace; 109exports.getExtensionStackTrace = getExtensionStackTrace; 110exports.safeErrorToString = safeErrorToString; 111