Chromium Code Reviews| 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 |