Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1005)

Side by Side Diff: chrome/browser/resources/google_now/utility.js

Issue 22518002: Incremental changes in error reporting (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cosmetics Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 /** 7 /**
8 * @fileoverview Utility objects and functions for Google Now extension. 8 * @fileoverview Utility objects and functions for Google Now extension.
9 */ 9 */
10 10
11 // TODO(vadimt): Figure out the server name. Use it in the manifest and for 11 // TODO(vadimt): Figure out the server name. Use it in the manifest and for
12 // NOTIFICATION_CARDS_URL. Meanwhile, to use the feature, you need to manually 12 // NOTIFICATION_CARDS_URL. Meanwhile, to use the feature, you need to manually
13 // set the server name via local storage. 13 // set the server name via local storage.
14 14
15 /** 15 /**
16 * Notification server URL. 16 * Notification server URL.
17 */ 17 */
18 var NOTIFICATION_CARDS_URL = localStorage['server_url']; 18 var NOTIFICATION_CARDS_URL = localStorage['server_url'];
19 19
20 var DEBUG_MODE = localStorage['debug_mode']; 20 var DEBUG_MODE = localStorage['debug_mode'];
21 21
22 /** 22 /**
23 * Shows a message popup in debug mode. 23 * Builds an error object with a message that may be sent to the server.
24 * @param {string} message Diagnostic message. 24 * @param {string} message Error message. This message may be sent to the
25 * server.
26 * @return {Error} Error object.
25 */ 27 */
26 function debugAlert(message) { 28 function buildErrorWithMessageForServer(message) {
27 if (DEBUG_MODE) 29 var error = new Error(message);
28 alert(message); 30 error.canSendMessageToServer = true;
31 return error;
29 } 32 }
30 33
31 /** 34 /**
32 * Checks for internal errors. 35 * Checks for internal errors.
33 * @param {boolean} condition Condition that must be true. 36 * @param {boolean} condition Condition that must be true.
34 * @param {string} message Diagnostic message for the case when the condition is 37 * @param {string} message Diagnostic message for the case when the condition is
35 * false. 38 * false.
36 */ 39 */
37 function verify(condition, message) { 40 function verify(condition, message) {
38 if (!condition) 41 if (!condition)
39 throw new Error('\nASSERT: ' + message); 42 throw buildErrorWithMessageForServer('ASSERT: ' + message);
40 } 43 }
41 44
42 /** 45 /**
43 * Builds a request to the notification server. 46 * Builds a request to the notification server.
44 * @param {string} handlerName Server handler to send the request to. 47 * @param {string} handlerName Server handler to send the request to.
45 * @param {string} contentType Value for the Content-type header. 48 * @param {string} contentType Value for the Content-type header.
46 * @return {XMLHttpRequest} Server request. 49 * @return {XMLHttpRequest} Server request.
47 */ 50 */
48 function buildServerRequest(handlerName, contentType) { 51 function buildServerRequest(handlerName, contentType) {
49 var request = new XMLHttpRequest(); 52 var request = new XMLHttpRequest();
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 * True if currently executed code runs in an instrumented callback. 101 * True if currently executed code runs in an instrumented callback.
99 * @type {boolean} 102 * @type {boolean}
100 */ 103 */
101 var isInInstrumentedCallback = false; 104 var isInInstrumentedCallback = false;
102 105
103 /** 106 /**
104 * Checks that we run in an instrumented callback. 107 * Checks that we run in an instrumented callback.
105 */ 108 */
106 function checkInInstrumentedCallback() { 109 function checkInInstrumentedCallback() {
107 if (!isInInstrumentedCallback) { 110 if (!isInInstrumentedCallback) {
108 // Cannot use verify() since no one will catch the exception. 111 reportError(buildErrorWithMessageForServer(
109 // This check will detect bugs at the development stage, and is very 112 'Not in instrumented callback'));
110 // unlikely to be seen by users.
111 var error = 'Not in instrumented callback: ' + new Error().stack;
112 console.error(error);
113 debugAlert(error);
114 } 113 }
115 } 114 }
116 115
117 /** 116 /**
118 * Starts the first queued task. 117 * Starts the first queued task.
119 */ 118 */
120 function startFirst() { 119 function startFirst() {
121 verify(queue.length >= 1, 'startFirst: queue is empty'); 120 verify(queue.length >= 1, 'startFirst: queue is empty');
122 verify(!isInTask, 'startFirst: already in task'); 121 verify(!isInTask, 'startFirst: already in task');
123 isInTask = true; 122 isInTask = true;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
189 188
190 if (queue.length >= 1) 189 if (queue.length >= 1)
191 startFirst(); 190 startFirst();
192 } 191 }
193 192
194 // Limiting 1 error report per background page load. 193 // Limiting 1 error report per background page load.
195 var errorReported = false; 194 var errorReported = false;
196 195
197 /** 196 /**
198 * Sends an error report to the server. 197 * Sends an error report to the server.
199 * @param {Error} error Error to report. 198 * @param {Error} error Error to send.
200 */ 199 */
201 function sendErrorReport(error) { 200 function sendErrorReport(error) {
202 var filteredStack = error.stack.replace(/.*\n/, '\n'); 201 var filteredStack = error.canSendMessageToServer ?
202 error.stack : error.stack.replace(/.*\n/, '(message removed)\n');
203 var file; 203 var file;
204 var line; 204 var line;
205 var topFrameMatches = filteredStack.match(/\(.*\)/); 205 var topFrameLineMatch = filteredStack.match(/\n at .*\n/);
206 // topFrameMatches's example: 206 var topFrame = topFrameLineMatch && topFrameLineMatch[0];
207 // (chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/utility.js:308:19) 207 if (topFrame) {
208 var crashLocation = topFrameMatches && topFrameMatches[0]; 208 // Examples of a frame:
209 if (crashLocation) { 209 // 1. '\n at someFunction (chrome-extension://
210 var topFrameElements = 210 // pmofbkohncoogjjhahejjfbppikbjigm/background.js:915:15)\n'
211 crashLocation.substring(1, crashLocation.length - 1).split(':'); 211 // 2. '\n at chrome-extension://pmofbkohncoogjjhahejjfbppikbjigm/
212 // topFrameElements for the above example will look like: 212 // utility.js:269:18\n'
213 // [0] chrome-extension 213 // 3. '\n at Function.target.(anonymous function) (extensions::
214 // [1] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js 214 // SafeBuiltins:19:14)\n'
215 // [2] 308 215 // 4. '\n at Event.dispatchToListener (event_bindings:382:22)\n'
216 // [3] 19 216 var errorLocation;
217 // Find the the parentheses at the end of the line, if any.
218 var parenthesesMatch = topFrame.match(/\(.*\)\n/);
219 if (parenthesesMatch && parenthesesMatch[0]) {
220 errorLocation =
221 parenthesesMatch[0].substring(1, parenthesesMatch[0].length - 2);
222 } else {
223 errorLocation = topFrame;
224 }
225
226 var topFrameElements = errorLocation.split(':');
227 // topFrameElements is an array that ends like:
228 // [N-3] //pmofbkohncoogjjhahejjfbppikbjigm/utility.js
229 // [N-2] 308
230 // [N-1] 19
217 if (topFrameElements.length >= 3) { 231 if (topFrameElements.length >= 3) {
218 file = topFrameElements[0] + ':' + topFrameElements[1]; 232 file = topFrameElements[topFrameElements.length - 3];
219 line = topFrameElements[2]; 233 line = topFrameElements[topFrameElements.length - 2];
220 } 234 }
221 } 235 }
236
222 var requestParameters = 237 var requestParameters =
223 'error=' + encodeURIComponent(error.name) + 238 'error=' + encodeURIComponent(error.name) +
224 '&script=' + encodeURIComponent(file) + 239 '&script=' + encodeURIComponent(file) +
225 '&line=' + encodeURIComponent(line) + 240 '&line=' + encodeURIComponent(line) +
226 '&trace=' + encodeURIComponent(filteredStack); 241 '&trace=' + encodeURIComponent(filteredStack);
227 var request = buildServerRequest('jserror', 242 var request = buildServerRequest('jserror',
228 'application/x-www-form-urlencoded'); 243 'application/x-www-form-urlencoded');
229 request.onloadend = function(event) { 244 request.onloadend = function(event) {
230 console.log('sendErrorReport status: ' + request.status); 245 console.log('sendErrorReport status: ' + request.status);
231 }; 246 };
232 request.send(requestParameters); 247 request.send(requestParameters);
233 } 248 }
234 249
235 /** 250 /**
251 * Reports an error to the server and the user, as appropriate.
252 * @param {Error} error Error to report.
253 */
254 function reportError(error) {
255 var message = 'Critical error:\n' + error.stack;
256 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.
257 if (!errorReported) {
258 errorReported = true;
259 chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) {
260 if (isEnabled)
skare_ 2013/08/12 20:47:14 just checking, isEnabled == false and DEBUG_MODE =
vadimt 2013/08/12 20:57:39 Correct.
261 sendErrorReport(error);
262 if (DEBUG_MODE)
263 alert(message);
264 });
265 }
266 }
267
268 /**
236 * Unique ID of the next callback. 269 * Unique ID of the next callback.
237 * @type {number} 270 * @type {number}
238 */ 271 */
239 var nextCallbackId = 0; 272 var nextCallbackId = 0;
240 273
241 /** 274 /**
242 * Adds error processing to an API callback. 275 * Adds error processing to an API callback.
243 * @param {Function} callback Callback to instrument. 276 * @param {Function} callback Callback to instrument.
244 * @param {boolean=} opt_isEventListener True if the callback is an event 277 * @param {boolean=} opt_isEventListener True if the callback is an event
245 * listener. 278 * listener.
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
277 verify(isInTask, 'wrapCallback: not in task at exit'); 310 verify(isInTask, 'wrapCallback: not in task at exit');
278 isInTask = false; 311 isInTask = false;
279 if (--taskPendingCallbackCount == 0) 312 if (--taskPendingCallbackCount == 0)
280 finish(); 313 finish();
281 } 314 }
282 315
283 verify(isInInstrumentedCallback, 316 verify(isInInstrumentedCallback,
284 'Instrumented callback is not instrumented upon exit'); 317 'Instrumented callback is not instrumented upon exit');
285 isInInstrumentedCallback = false; 318 isInInstrumentedCallback = false;
286 } catch (error) { 319 } catch (error) {
287 var message = 'Uncaught exception:\n' + error.stack; 320 reportError(error);
288 console.error(message);
289 if (!errorReported) {
290 errorReported = true;
291 chrome.metricsPrivate.getIsCrashReportingEnabled(function(isEnabled) {
292 if (isEnabled)
293 sendErrorReport(error);
294 });
295 debugAlert(message);
296 }
297 } 321 }
298 }; 322 };
299 } 323 }
300 324
301 /** 325 /**
302 * Returns an instrumented function. 326 * Returns an instrumented function.
303 * @param {array} functionIdentifierParts Path to the chrome.* function. 327 * @param {array} functionIdentifierParts Path to the chrome.* function.
304 * @param {string} functionName Name of the chrome API function. 328 * @param {string} functionName Name of the chrome API function.
305 * @param {number} callbackParameter Index of the callback parameter to this 329 * @param {number} callbackParameter Index of the callback parameter to this
306 * API function. 330 * API function.
307 * @return {function} An instrumented function. 331 * @return {function} An instrumented function.
308 */ 332 */
309 function createInstrumentedFunction( 333 function createInstrumentedFunction(
310 functionIdentifierParts, 334 functionIdentifierParts,
311 functionName, 335 functionName,
312 callbackParameter) { 336 callbackParameter) {
313 return function() { 337 return function() {
314 // This is the wrapper for the API function. Pass the wrapped callback to 338 // This is the wrapper for the API function. Pass the wrapped callback to
315 // the original function. 339 // the original function.
316 var callback = arguments[callbackParameter]; 340 var callback = arguments[callbackParameter];
317 if (typeof callback != 'function') { 341 if (typeof callback != 'function') {
318 debugAlert('Argument ' + callbackParameter + ' of ' + 342 reportError(buildErrorWithMessageForServer(
319 functionIdentifierParts.join('.') + '.' + functionName + 343 'Argument ' + callbackParameter + ' of ' +
320 ' is not a function'); 344 functionIdentifierParts.join('.') + '.' + functionName +
345 ' is not a function'));
321 } 346 }
322 arguments[callbackParameter] = wrapCallback( 347 arguments[callbackParameter] = wrapCallback(
323 callback, functionName == 'addListener'); 348 callback, functionName == 'addListener');
324 349
325 var chromeContainer = chrome; 350 var chromeContainer = chrome;
326 functionIdentifierParts.map(function(fragment) { 351 functionIdentifierParts.map(function(fragment) {
327 chromeContainer = chromeContainer[fragment]; 352 chromeContainer = chromeContainer[fragment];
328 }); 353 });
329 return chromeContainer[functionName]. 354 return chromeContainer[functionName].
330 apply(chromeContainer, arguments); 355 apply(chromeContainer, arguments);
331 }; 356 };
332 } 357 }
333 358
334 /** 359 /**
335 * Instruments an API function to add error processing to its user 360 * Instruments an API function to add error processing to its user
336 * code-provided callback. 361 * code-provided callback.
337 * @param {string} functionIdentifier Full identifier of the function without 362 * @param {string} functionIdentifier Full identifier of the function without
338 * the 'chrome.' portion. 363 * the 'chrome.' portion.
339 * @param {number} callbackParameter Index of the callback parameter to this 364 * @param {number} callbackParameter Index of the callback parameter to this
340 * API function. 365 * API function.
341 */ 366 */
342 function instrumentChromeApiFunction(functionIdentifier, callbackParameter) { 367 function instrumentChromeApiFunction(functionIdentifier, callbackParameter) {
343 var functionIdentifierParts = functionIdentifier.split('.'); 368 var functionIdentifierParts = functionIdentifier.split('.');
344 var functionName = functionIdentifierParts.pop(); 369 var functionName = functionIdentifierParts.pop();
345 var chromeContainer = chrome; 370 var chromeContainer = chrome;
346 var instrumentedContainer = instrumented; 371 var instrumentedContainer = instrumented;
347 functionIdentifierParts.map(function(fragment) { 372 functionIdentifierParts.map(function(fragment) {
348 chromeContainer = chromeContainer[fragment]; 373 chromeContainer = chromeContainer[fragment];
374 if (!chromeContainer) {
375 reportError(buildErrorWithMessageForServer(
376 'Cannot instrument ' + functionIdentifier));
377 }
378
349 if (!(fragment in instrumentedContainer)) 379 if (!(fragment in instrumentedContainer))
350 instrumentedContainer[fragment] = {}; 380 instrumentedContainer[fragment] = {};
351 381
352 instrumentedContainer = instrumentedContainer[fragment]; 382 instrumentedContainer = instrumentedContainer[fragment];
353 }); 383 });
354 384
355 var targetFunction = chromeContainer[functionName]; 385 var targetFunction = chromeContainer[functionName];
356 386 if (!targetFunction) {
357 if (!targetFunction) 387 reportError(buildErrorWithMessageForServer(
358 debugAlert('Cannot instrument ' + functionName); 388 'Cannot instrument ' + functionIdentifier));
389 }
359 390
360 instrumentedContainer[functionName] = createInstrumentedFunction( 391 instrumentedContainer[functionName] = createInstrumentedFunction(
361 functionIdentifierParts, 392 functionIdentifierParts,
362 functionName, 393 functionName,
363 callbackParameter); 394 callbackParameter);
364 } 395 }
365 396
366 instrumentChromeApiFunction('alarms.get', 1); 397 instrumentChromeApiFunction('alarms.get', 1);
367 instrumentChromeApiFunction('alarms.onAlarm.addListener', 0); 398 instrumentChromeApiFunction('alarms.onAlarm.addListener', 0);
368 instrumentChromeApiFunction('identity.getAuthToken', 1); 399 instrumentChromeApiFunction('identity.getAuthToken', 1);
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 // Poll for the sign in state every hour. 600 // Poll for the sign in state every hour.
570 // One hour is just an arbitrary amount of time chosen. 601 // One hour is just an arbitrary amount of time chosen.
571 chrome.alarms.create(alarmName, {periodInMinutes: 60}); 602 chrome.alarms.create(alarmName, {periodInMinutes: 60});
572 603
573 return { 604 return {
574 addListener: addListener, 605 addListener: addListener,
575 isSignedIn: isSignedIn, 606 isSignedIn: isSignedIn,
576 removeToken: removeToken 607 removeToken: removeToken
577 }; 608 };
578 } 609 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698