Chromium Code Reviews| Index: chrome/browser/resources/google_now/utility.js | 
| diff --git a/chrome/browser/resources/google_now/utility.js b/chrome/browser/resources/google_now/utility.js | 
| index 12e9f93dd21b49f9540864992e4edf63c5d5fdbd..57db70678714ed73cfce7492e09edc7741411d12 100644 | 
| --- a/chrome/browser/resources/google_now/utility.js | 
| +++ b/chrome/browser/resources/google_now/utility.js | 
| @@ -20,12 +20,15 @@ var NOTIFICATION_CARDS_URL = localStorage['server_url']; | 
| var DEBUG_MODE = localStorage['debug_mode']; | 
| /** | 
| - * Shows a message popup in debug mode. | 
| - * @param {string} message Diagnostic message. | 
| + * Builds an error object with a message that may be sent to the server. | 
| + * @param {string} message Error message. This message may be sent to the | 
| + * server. | 
| + * @return {Error} Error object. | 
| */ | 
| -function debugAlert(message) { | 
| - if (DEBUG_MODE) | 
| - alert(message); | 
| +function buildErrorWithMessageForServer(message) { | 
| + var error = new Error(message); | 
| + error.canSendMessageToServer = true; | 
| + return error; | 
| } | 
| /** | 
| @@ -36,7 +39,7 @@ function debugAlert(message) { | 
| */ | 
| function verify(condition, message) { | 
| if (!condition) | 
| - throw new Error('\nASSERT: ' + message); | 
| + throw buildErrorWithMessageForServer('ASSERT: ' + message); | 
| } | 
| /** | 
| @@ -105,12 +108,8 @@ function buildTaskManager(areConflicting) { | 
| */ | 
| function checkInInstrumentedCallback() { | 
| if (!isInInstrumentedCallback) { | 
| - // Cannot use verify() since no one will catch the exception. | 
| - // This check will detect bugs at the development stage, and is very | 
| - // unlikely to be seen by users. | 
| - var error = 'Not in instrumented callback: ' + new Error().stack; | 
| - console.error(error); | 
| - debugAlert(error); | 
| + reportError(buildErrorWithMessageForServer( | 
| + 'Not in instrumented callback')); | 
| } | 
| } | 
| @@ -196,29 +195,45 @@ function buildTaskManager(areConflicting) { | 
| /** | 
| * Sends an error report to the server. | 
| - * @param {Error} error Error to report. | 
| + * @param {Error} error Error to send. | 
| */ | 
| function sendErrorReport(error) { | 
| - var filteredStack = error.stack.replace(/.*\n/, '\n'); | 
| + var filteredStack = error.canSendMessageToServer ? | 
| + error.stack : error.stack.replace(/.*\n/, '(message removed)\n'); | 
| var file; | 
| var line; | 
| - var topFrameMatches = filteredStack.match(/\(.*\)/); | 
| - // topFrameMatches's example: | 
| - // (chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/utility.js:308:19) | 
| - var crashLocation = topFrameMatches && topFrameMatches[0]; | 
| - if (crashLocation) { | 
| - var topFrameElements = | 
| - crashLocation.substring(1, crashLocation.length - 1).split(':'); | 
| - // topFrameElements for the above example will look like: | 
| - // [0] chrome-extension | 
| - // [1] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js | 
| - // [2] 308 | 
| - // [3] 19 | 
| + var topFrameLineMatch = filteredStack.match(/\n at .*\n/); | 
| + var topFrame = topFrameLineMatch && topFrameLineMatch[0]; | 
| + if (topFrame) { | 
| + // Examples of a frame: | 
| + // 1. '\n at someFunction (chrome-extension:// | 
| + // pmofbkohncoogjjhahejjfbppikbjigm/background.js:915:15)\n' | 
| + // 2. '\n at chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/ | 
| + // utility.js:269:18\n' | 
| + // 3. '\n at Function.target.(anonymous function) (extensions:: | 
| + // SafeBuiltins:19:14)\n' | 
| + // 4. '\n at Event.dispatchToListener (event_bindings:382:22)\n' | 
| + var errorLocation; | 
| + // Find the the parentheses at the end of the line, if any. | 
| + var parenthesesMatch = topFrame.match(/\(.*\)\n/); | 
| + if (parenthesesMatch && parenthesesMatch[0]) { | 
| + errorLocation = | 
| + parenthesesMatch[0].substring(1, parenthesesMatch[0].length - 2); | 
| + } else { | 
| + errorLocation = topFrame; | 
| + } | 
| + | 
| + var topFrameElements = errorLocation.split(':'); | 
| + // topFrameElements is an array that ends like: | 
| + // [N-3] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js | 
| + // [N-2] 308 | 
| + // [N-1] 19 | 
| if (topFrameElements.length >= 3) { | 
| - file = topFrameElements[0] + ':' + topFrameElements[1]; | 
| - line = topFrameElements[2]; | 
| + file = topFrameElements[topFrameElements.length - 3]; | 
| + line = topFrameElements[topFrameElements.length - 2]; | 
| } | 
| } | 
| + | 
| var requestParameters = | 
| 'error=' + encodeURIComponent(error.name) + | 
| '&script=' + encodeURIComponent(file) + | 
| @@ -233,6 +248,24 @@ function buildTaskManager(areConflicting) { | 
| } | 
| /** | 
| + * Reports an error to the server and the user, as appropriate. | 
| + * @param {Error} error Error to report. | 
| + */ | 
| + function reportError(error) { | 
| + var message = 'Critical error:\n' + error.stack; | 
| + console.error(message); | 
| 
 
arv (Not doing code reviews)
2013/08/12 21:31:30
console.error(error)
also prints the stack. If yo
 
vadimt
2013/08/12 23:32:22
Couple of reasons why this would be not ideal:
1.
 
 | 
| + if (!errorReported) { | 
| + errorReported = true; | 
| + chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) { | 
| + if (isEnabled) | 
| 
 
skare_
2013/08/12 20:47:14
just checking, isEnabled == false and DEBUG_MODE =
 
vadimt
2013/08/12 20:57:39
Correct.
 
 | 
| + sendErrorReport(error); | 
| + if (DEBUG_MODE) | 
| + alert(message); | 
| + }); | 
| + } | 
| + } | 
| + | 
| + /** | 
| * Unique ID of the next callback. | 
| * @type {number} | 
| */ | 
| @@ -284,16 +317,7 @@ function buildTaskManager(areConflicting) { | 
| 'Instrumented callback is not instrumented upon exit'); | 
| isInInstrumentedCallback = false; | 
| } catch (error) { | 
| - var message = 'Uncaught exception:\n' + error.stack; | 
| - console.error(message); | 
| - if (!errorReported) { | 
| - errorReported = true; | 
| - chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) { | 
| - if (isEnabled) | 
| - sendErrorReport(error); | 
| - }); | 
| - debugAlert(message); | 
| - } | 
| + reportError(error); | 
| } | 
| }; | 
| } | 
| @@ -315,9 +339,10 @@ function buildTaskManager(areConflicting) { | 
| // the original function. | 
| var callback = arguments[callbackParameter]; | 
| if (typeof callback != 'function') { | 
| - debugAlert('Argument ' + callbackParameter + ' of ' + | 
| - functionIdentifierParts.join('.') + '.' + functionName + | 
| - ' is not a function'); | 
| + reportError(buildErrorWithMessageForServer( | 
| + 'Argument ' + callbackParameter + ' of ' + | 
| + functionIdentifierParts.join('.') + '.' + functionName + | 
| + ' is not a function')); | 
| } | 
| arguments[callbackParameter] = wrapCallback( | 
| callback, functionName == 'addListener'); | 
| @@ -346,6 +371,11 @@ function buildTaskManager(areConflicting) { | 
| var instrumentedContainer = instrumented; | 
| functionIdentifierParts.map(function(fragment) { | 
| chromeContainer = chromeContainer[fragment]; | 
| + if (!chromeContainer) { | 
| + reportError(buildErrorWithMessageForServer( | 
| + 'Cannot instrument ' + functionIdentifier)); | 
| + } | 
| + | 
| if (!(fragment in instrumentedContainer)) | 
| instrumentedContainer[fragment] = {}; | 
| @@ -353,9 +383,10 @@ function buildTaskManager(areConflicting) { | 
| }); | 
| var targetFunction = chromeContainer[functionName]; | 
| - | 
| - if (!targetFunction) | 
| - debugAlert('Cannot instrument ' + functionName); | 
| + if (!targetFunction) { | 
| + reportError(buildErrorWithMessageForServer( | 
| + 'Cannot instrument ' + functionIdentifier)); | 
| + } | 
| instrumentedContainer[functionName] = createInstrumentedFunction( | 
| functionIdentifierParts, |