OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 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 cr.define('extensions', function() { | 5 cr.define('extensions', function() { |
6 'use strict'; | 6 'use strict'; |
7 | 7 |
8 /** | 8 /** |
9 * Clear all the content of a given element. | 9 * Clear all the content of a given element. |
10 * @param {HTMLElement} element The element to be cleared. | 10 * @param {HTMLElement} element The element to be cleared. |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
109 assert(this.contextUrl_); | 109 assert(this.contextUrl_); |
110 }, | 110 }, |
111 | 111 |
112 /** | 112 /** |
113 * Sets the error for the content. | 113 * Sets the error for the content. |
114 * @param {(RuntimeError|ManifestError)} error The error whose content | 114 * @param {(RuntimeError|ManifestError)} error The error whose content |
115 * should be displayed. | 115 * should be displayed. |
116 * @param {string} extensionUrl The URL associated with this extension. | 116 * @param {string} extensionUrl The URL associated with this extension. |
117 */ | 117 */ |
118 setError: function(error, extensionUrl) { | 118 setError: function(error, extensionUrl) { |
119 this.clearError(); | |
120 | |
119 this.error_ = error; | 121 this.error_ = error; |
120 this.extensionUrl_ = extensionUrl; | 122 this.extensionUrl_ = extensionUrl; |
121 this.contextUrl_.textContent = error.contextUrl ? | 123 this.contextUrl_.textContent = error.contextUrl ? |
122 getRelativeUrl(error.contextUrl, this.extensionUrl_) : | 124 getRelativeUrl(error.contextUrl, this.extensionUrl_) : |
123 loadTimeData.getString('extensionErrorOverlayContextUnknown'); | 125 loadTimeData.getString('extensionErrorOverlayContextUnknown'); |
124 this.initStackTrace_(); | 126 this.initStackTrace_(); |
125 }, | 127 }, |
126 | 128 |
127 /** | 129 /** |
128 * Wipe content associated with a specific error. | 130 * Wipe content associated with a specific error. |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
239 /** | 241 /** |
240 * The content section for runtime errors; this is re-used for all | 242 * The content section for runtime errors; this is re-used for all |
241 * runtime errors and attached/detached from the overlay as needed. | 243 * runtime errors and attached/detached from the overlay as needed. |
242 * @type {RuntimeErrorContent} | 244 * @type {RuntimeErrorContent} |
243 * @private | 245 * @private |
244 */ | 246 */ |
245 this.runtimeErrorContent_ = new RuntimeErrorContent(); | 247 this.runtimeErrorContent_ = new RuntimeErrorContent(); |
246 } | 248 } |
247 | 249 |
248 /** | 250 /** |
249 * Value of ExtensionError::RUNTIME_ERROR enum. | |
250 * @see extensions/browser/extension_error.h | |
251 * @type {number} | |
252 * @const | |
253 * @private | |
254 */ | |
255 ExtensionErrorOverlay.RUNTIME_ERROR_TYPE_ = 1; | |
256 | |
257 /** | |
258 * The manifest filename. | 251 * The manifest filename. |
259 * @type {string} | 252 * @type {string} |
260 * @const | 253 * @const |
261 * @private | 254 * @private |
262 */ | 255 */ |
263 ExtensionErrorOverlay.MANIFEST_FILENAME_ = 'manifest.json'; | 256 ExtensionErrorOverlay.MANIFEST_FILENAME_ = 'manifest.json'; |
264 | 257 |
265 /** | 258 /** |
266 * Determine whether or not chrome can load the source for a given file; this | 259 * Determine whether or not chrome can load the source for a given file; this |
267 * can only be done if the file belongs to the extension. | 260 * can only be done if the file belongs to the extension. |
268 * @param {string} file The file to load. | 261 * @param {string} file The file to load. |
269 * @param {string} extensionUrl The url for the extension, in the form | 262 * @param {string} extensionUrl The url for the extension, in the form |
270 * chrome-extension://<extension-id>/. | 263 * chrome-extension://<extension-id>/. |
271 * @return {boolean} True if the file can be loaded, false otherwise. | 264 * @return {boolean} True if the file can be loaded, false otherwise. |
272 * @private | 265 * @private |
273 */ | 266 */ |
274 ExtensionErrorOverlay.canLoadFileSource = function(file, extensionUrl) { | 267 ExtensionErrorOverlay.canLoadFileSource = function(file, extensionUrl) { |
275 return file.substr(0, extensionUrl.length) == extensionUrl || | 268 return file.substr(0, extensionUrl.length) == extensionUrl || |
276 file.toLowerCase() == ExtensionErrorOverlay.MANIFEST_FILENAME_; | 269 file.toLowerCase() == ExtensionErrorOverlay.MANIFEST_FILENAME_; |
277 }; | 270 }; |
278 | 271 |
279 /** | |
280 * Determine whether or not we can show an overlay with more details for | |
281 * the given extension error. | |
282 * @param {Object} error The extension error. | |
283 * @param {string} extensionUrl The url for the extension, in the form | |
284 * "chrome-extension://<extension-id>/". | |
285 * @return {boolean} True if we can show an overlay for the error, | |
286 * false otherwise. | |
287 */ | |
288 ExtensionErrorOverlay.canShowOverlayForError = function(error, extensionUrl) { | |
289 if (ExtensionErrorOverlay.canLoadFileSource(error.source, extensionUrl)) | |
290 return true; | |
291 | |
292 if (error.stackTrace) { | |
293 for (var i = 0; i < error.stackTrace.length; ++i) { | |
294 if (RuntimeErrorContent.shouldDisplayForUrl(error.stackTrace[i].url)) | |
295 return true; | |
296 } | |
297 } | |
298 | |
299 return false; | |
300 }; | |
301 | |
302 cr.addSingletonGetter(ExtensionErrorOverlay); | 272 cr.addSingletonGetter(ExtensionErrorOverlay); |
303 | 273 |
304 ExtensionErrorOverlay.prototype = { | 274 ExtensionErrorOverlay.prototype = { |
305 /** | 275 /** |
306 * The underlying error whose details are being displayed. | 276 * The underlying error whose details are being displayed. |
307 * @type {?(RuntimeError|ManifestError)} | 277 * @type {?(RuntimeError|ManifestError)} |
308 * @private | 278 * @private |
309 */ | 279 */ |
310 error_: null, | 280 selectedError_: null, |
311 | 281 |
312 /** | 282 /** |
313 * Initialize the page. | 283 * Initialize the page. |
314 * @param {function(HTMLDivElement)} showOverlay The function to show or | 284 * @param {function(HTMLDivElement)} showOverlay The function to show or |
315 * hide the ExtensionErrorOverlay; this should take a single parameter | 285 * hide the ExtensionErrorOverlay; this should take a single parameter |
316 * which is either the overlay Div if the overlay should be displayed, | 286 * which is either the overlay Div if the overlay should be displayed, |
317 * or null if the overlay should be hidden. | 287 * or null if the overlay should be hidden. |
318 */ | 288 */ |
319 initializePage: function(showOverlay) { | 289 initializePage: function(showOverlay) { |
320 var overlay = $('overlay'); | 290 var overlay = $('overlay'); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
369 * Handles a click on the dismiss ("OK" or close) buttons. | 339 * Handles a click on the dismiss ("OK" or close) buttons. |
370 * @param {Event} e The click event. | 340 * @param {Event} e The click event. |
371 * @private | 341 * @private |
372 */ | 342 */ |
373 handleDismiss_: function(e) { | 343 handleDismiss_: function(e) { |
374 this.setVisible(false); | 344 this.setVisible(false); |
375 | 345 |
376 // There's a chance that the overlay receives multiple dismiss events; in | 346 // There's a chance that the overlay receives multiple dismiss events; in |
377 // this case, handle it gracefully and return (since all necessary work | 347 // this case, handle it gracefully and return (since all necessary work |
378 // will already have been done). | 348 // will already have been done). |
379 if (!this.error_) | 349 if (!this.selectedError_) |
380 return; | 350 return; |
381 | 351 |
382 // Remove all previous content. | 352 // Remove all previous content. |
383 this.codeDiv_.clear(); | 353 this.codeDiv_.clear(); |
384 | 354 |
385 this.openDevtoolsButton_.hidden = true; | 355 this.overlayDiv_.querySelector('.extension-error-list').onRemoved(); |
386 | 356 |
387 if (this.error_.type == ExtensionErrorOverlay.RUNTIME_ERROR_TYPE_) { | 357 this.clearRuntimeContent_(); |
388 this.overlayDiv_.querySelector('.content-area').removeChild( | 358 |
359 this.selectedError_ = null; | |
360 }, | |
361 | |
362 /** | |
363 * Clears the current content. | |
364 * @private | |
365 */ | |
366 clearRuntimeContent_: function() { | |
367 if (this.runtimeErrorContent_.parentNode) { | |
368 this.runtimeErrorContent_.parentNode.removeChild( | |
389 this.runtimeErrorContent_); | 369 this.runtimeErrorContent_); |
390 this.runtimeErrorContent_.clearError(); | 370 this.runtimeErrorContent_.clearError(); |
391 } | 371 } |
392 | 372 this.openDevtoolsButton_.hidden = true; |
393 this.error_ = null; | |
394 }, | 373 }, |
395 | 374 |
396 /** | 375 /** |
397 * Associate an error with the overlay. This will set the error for the | 376 * Sets the active error for the overlay. |
398 * overlay, and, if possible, will populate the code section of the overlay | 377 * @param {?(ManifestError|RuntimeError)} error The error to make active. |
399 * with the relevant file, load the stack trace, and generate links for | 378 * @private |
400 * opening devtools (the latter two only happen for runtime errors). | |
401 * @param {(RuntimeError|ManifestError)} error The error to show in the | |
402 * overlay. | |
403 * @param {string} extensionUrl The URL of the extension, in the form | |
404 * "chrome-extension://<extension_id>". | |
405 * TODO(dbeam): add URL externs and re-enable typechecking in this method. | |
406 * @suppress {missingProperties} | |
Dan Beam
2015/04/28 17:08:07
add this back
Devlin
2015/04/29 16:08:37
Done.
| |
407 */ | 379 */ |
408 setErrorAndShowOverlay: function(error, extensionUrl) { | 380 setActiveError_: function(error) { |
409 this.error_ = error; | 381 this.selectedError_ = error; |
410 | 382 |
411 if (this.error_.type == ExtensionErrorOverlay.RUNTIME_ERROR_TYPE_) { | 383 // If there is no error (this can happen if, e.g., the user deleted all |
412 this.runtimeErrorContent_.setError(this.error_, extensionUrl); | 384 // the errors), then clear the content. |
385 if (!error) { | |
386 this.codeDiv_.populate( | |
387 null, loadTimeData.getString('extensionErrorNoErrorsCodeMessage')); | |
388 this.clearRuntimeContent_(); | |
389 return; | |
390 } | |
391 | |
392 var extensionUrl = 'chrome-extension://' + error.extensionId + '/'; | |
393 // Set or hide runtime content. | |
394 if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) { | |
395 this.runtimeErrorContent_.setError(error, extensionUrl); | |
413 this.overlayDiv_.querySelector('.content-area').insertBefore( | 396 this.overlayDiv_.querySelector('.content-area').insertBefore( |
414 this.runtimeErrorContent_, | 397 this.runtimeErrorContent_, |
415 this.codeDiv_.nextSibling); | 398 this.codeDiv_.nextSibling); |
416 this.openDevtoolsButton_.hidden = false; | 399 this.openDevtoolsButton_.hidden = false; |
417 this.openDevtoolsButton_.disabled = !error.canInspect; | 400 this.openDevtoolsButton_.disabled = !error.canInspect; |
401 } else { | |
402 this.clearRuntimeContent_(); | |
418 } | 403 } |
419 | 404 |
405 // Read the file source to populate the code section, or set it to null if | |
406 // the file is unreadable. | |
420 if (ExtensionErrorOverlay.canLoadFileSource(error.source, extensionUrl)) { | 407 if (ExtensionErrorOverlay.canLoadFileSource(error.source, extensionUrl)) { |
421 // slice(1) because pathname starts with a /. | |
422 var pathname = new URL(error.source).pathname.slice(1); | |
423 | |
424 // Use pathname instead of relativeUrl. | 408 // Use pathname instead of relativeUrl. |
425 var requestFileSourceArgs = {extensionId: error.extensionId, | 409 var requestFileSourceArgs = {extensionId: error.extensionId, |
426 message: error.message, | 410 message: error.message}; |
427 pathSuffix: pathname}; | 411 switch (error.type) { |
428 | 412 case chrome.developerPrivate.ErrorType.MANIFEST: |
429 if (pathname.toLowerCase() == | 413 requestFileSourceArgs.pathSuffix = error.source; |
430 ExtensionErrorOverlay.MANIFEST_FILENAME_) { | 414 requestFileSourceArgs.manifestKey = error.manifestKey; |
431 requestFileSourceArgs.manifestKey = error.manifestKey; | 415 requestFileSourceArgs.manifestSpecific = error.manifestSpecific; |
432 requestFileSourceArgs.manifestSpecific = error.manifestSpecific; | 416 break; |
433 } else { | 417 case chrome.developerPrivate.ErrorType.RUNTIME: |
434 requestFileSourceArgs.lineNumber = | 418 // slice(1) because pathname starts with a /. |
435 error.stackTrace && error.stackTrace[0] ? | 419 var pathname = new URL(error.source).pathname.slice(1); |
436 error.stackTrace[0].lineNumber : 0; | 420 requestFileSourceArgs.pathSuffix = pathname; |
421 requestFileSourceArgs.lineNumber = | |
422 error.stackTrace && error.stackTrace[0] ? | |
423 error.stackTrace[0].lineNumber : 0; | |
424 break; | |
425 default: | |
426 assertNotReached(); | |
437 } | 427 } |
438 this.requestFileSource(requestFileSourceArgs); | 428 this.requestFileSource(requestFileSourceArgs); |
439 } else { | 429 } else { |
440 this.onFileSourceResponse_(null); | 430 this.onFileSourceResponse_(null); |
441 } | 431 } |
442 }, | 432 }, |
443 | 433 |
444 /** | 434 /** |
435 * Associate an error with the overlay. This will set the error for the | |
436 * overlay, and, if possible, will populate the code section of the overlay | |
437 * with the relevant file, load the stack trace, and generate links for | |
438 * opening devtools (the latter two only happen for runtime errors). | |
439 * @param {Array<(RuntimeError|ManifestError)>} errors The error to show in | |
440 * the overlay. | |
441 * @param {string} extensionId The id of the extension. | |
442 * @param {string} extensionName The name of the extension. | |
443 */ | |
444 setErrorsAndShowOverlay: function(errors, extensionId, extensionName) { | |
445 document.querySelector( | |
446 '#extension-error-overlay .extension-error-overlay-title'). | |
447 textContent = extensionName; | |
448 var errorsDiv = this.overlayDiv_.querySelector('.extension-error-list'); | |
449 var extensionErrors = | |
450 new extensions.ExtensionErrorList(errors, extensionId); | |
451 errorsDiv.parentNode.replaceChild(extensionErrors, errorsDiv); | |
452 extensionErrors.addEventListener('activeExtensionErrorChanged', | |
453 function(e) { | |
454 this.setActiveError_(e.detail); | |
455 }.bind(this)); | |
456 | |
457 if (errors.length > 0) | |
458 this.setActiveError_(errors[0]); | |
459 this.setVisible(true); | |
460 }, | |
461 | |
462 /** | |
445 * Requests a file's source. | 463 * Requests a file's source. |
446 * @param {RequestFileSourceProperties} args The arguments for the call. | 464 * @param {RequestFileSourceProperties} args The arguments for the call. |
447 */ | 465 */ |
448 requestFileSource: function(args) { | 466 requestFileSource: function(args) { |
449 chrome.developerPrivate.requestFileSource( | 467 chrome.developerPrivate.requestFileSource( |
450 args, this.onFileSourceResponse_.bind(this)); | 468 args, this.onFileSourceResponse_.bind(this)); |
451 }, | 469 }, |
452 | 470 |
453 /** | 471 /** |
454 * Set the code to be displayed in the code portion of the overlay. | 472 * Set the code to be displayed in the code portion of the overlay. |
455 * @see ExtensionErrorOverlay.requestFileSourceResponse(). | 473 * @see ExtensionErrorOverlay.requestFileSourceResponse(). |
456 * @param {?RequestFileSourceResponse} response The response from the | 474 * @param {?RequestFileSourceResponse} response The response from the |
457 * request file source call, which will be shown as code. If |response| | 475 * request file source call, which will be shown as code. If |response| |
458 * is null, then a "Could not display code" message will be displayed | 476 * is null, then a "Could not display code" message will be displayed |
459 * instead. | 477 * instead. |
460 */ | 478 */ |
461 onFileSourceResponse_: function(response) { | 479 onFileSourceResponse_: function(response) { |
462 if (response) { | |
463 document.querySelector( | |
464 '#extension-error-overlay .extension-error-overlay-title'). | |
465 textContent = response.title; | |
466 } | |
467 this.codeDiv_.populate( | 480 this.codeDiv_.populate( |
468 response, // ExtensionCode can handle a null response. | 481 response, // ExtensionCode can handle a null response. |
469 loadTimeData.getString('extensionErrorOverlayNoCodeToDisplay')); | 482 loadTimeData.getString('extensionErrorOverlayNoCodeToDisplay')); |
470 this.setVisible(true); | 483 this.setVisible(true); |
471 }, | 484 }, |
472 }; | 485 }; |
473 | 486 |
474 // Export | 487 // Export |
475 return { | 488 return { |
476 ExtensionErrorOverlay: ExtensionErrorOverlay | 489 ExtensionErrorOverlay: ExtensionErrorOverlay |
477 }; | 490 }; |
478 }); | 491 }); |
OLD | NEW |