1010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved. 2010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 3010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// found in the LICENSE file. 4010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 5010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// Handles uncaught exceptions thrown by extensions. By default this is to 6010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)// log an error message, but tests may override this behaviour. 7010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)var handler = function(message, e) { 8010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) console.error(message); 9010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}; 10010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 1103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 1203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Append the error description and stack trace to |message|. 1303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 1403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {string} message - The prefix of the error message. 1503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {Error|*} e - The thrown error object. This object is potentially 1603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * unsafe, because it could be generated by an extension. 1703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {string=} priorStackTrace - The stack trace to be appended to the 1803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * error message. This stack trace must not include stack frames of |e.stack|, 1903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * because both stack traces are concatenated. Overlapping stack traces will 2003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * confuse extension developers. 2103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @return {string} The formatted error message. 2203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 2303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function formatErrorMessage(message, e, priorStackTrace) { 2403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (e) 2503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) message += ': ' + safeErrorToString(e, false); 2603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 2703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) var stack; 2803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) try { 2903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // If the stack was set, use it. 3003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // |e.stack| could be void in the following common example: 3103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // throw "Error message"; 3203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) stack = $String.self(e && e.stack); 3303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } catch (e) {} 3403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 3503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // If a stack is not provided, capture a stack trace. 3603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (!priorStackTrace && !stack) 3703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) stack = getStackTrace(); 3803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 3903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) stack = filterExtensionStackTrace(stack); 4003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (stack) 4103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) message += '\n' + stack; 4203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 4303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // If an asynchronouse stack trace was set, append it. 4403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (priorStackTrace) 4503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) message += '\n' + priorStackTrace; 4603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 4703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return message; 4803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)} 4903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 5003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function filterExtensionStackTrace(stack) { 5103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) if (!stack) 5203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return ''; 5303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // Remove stack frames in the stack trace that weren't associated with the 5403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // extension, to not confuse extension developers with internal details. 5503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) stack = $String.split(stack, '\n'); 5603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) stack = $Array.filter(stack, function(line) { 5703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return $String.indexOf(line, 'chrome-extension://') >= 0; 5803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) }); 5903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return $Array.join(stack, '\n'); 6003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)} 6103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 6203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function getStackTrace() { 6303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) var e = {}; 6403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) $Error.captureStackTrace(e, getStackTrace); 6503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return e.stack; 6603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)} 6703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 6803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function getExtensionStackTrace() { 6903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return filterExtensionStackTrace(getStackTrace()); 7003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)} 7103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 7203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 7303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Convert an object to a string. 7403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 7503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {Error|*} e - A thrown object (possibly user-supplied). 7603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {boolean=} omitType - Whether to try to serialize |e.message| instead 7703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * of |e.toString()|. 7803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @return {string} The error message. 7903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 8003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)function safeErrorToString(e, omitType) { 8103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) try { 8203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return $String.self(omitType && e.message || e); 8303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } catch (e) { 8403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // This error is exceptional and could be triggered by 8503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) // throw {toString: function() { throw 'Haha' } }; 8603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) return '(cannot get error message)'; 8703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) } 8803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)} 8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 9003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)/** 9103b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * Formats the error message and invokes the error handler. 9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * 9303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {string} message - Error message prefix. 9403b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {Error|*} e - Thrown object. 9503b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @param {string=} priorStackTrace - Error message suffix. 9603b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) * @see formatErrorMessage 9703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) */ 9803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)exports.handle = function(message, e, priorStackTrace) { 9903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) message = formatErrorMessage(message, e, priorStackTrace); 100010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) handler(message, e); 101010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}; 102010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) 10303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)// |newHandler| A function which matches |handler|. 104010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)exports.setHandler = function(newHandler) { 105010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles) handler = newHandler; 106010d83a9304c5a91596085d917d248abff47903aTorne (Richard Coles)}; 10703b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles) 10803b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)exports.getStackTrace = getStackTrace; 10903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)exports.getExtensionStackTrace = getExtensionStackTrace; 11003b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)exports.safeErrorToString = safeErrorToString; 111