Chromium Code Reviews| Index: chrome/browser/resources/extensions/extension_error.js |
| diff --git a/chrome/browser/resources/extensions/extension_error.js b/chrome/browser/resources/extensions/extension_error.js |
| index fb2699c73b5ada3ce8fd87809d3f315d56e8149b..4bc07ac8aa268c659b8543807196040d1340cbb8 100644 |
| --- a/chrome/browser/resources/extensions/extension_error.js |
| +++ b/chrome/browser/resources/extensions/extension_error.js |
| @@ -45,9 +45,16 @@ cr.define('extensions', function() { |
| * @extends {HTMLDivElement} |
| */ |
| function ExtensionError(error) { |
| - var div = document.createElement('div'); |
| + if (error.stackTrace || error.contextUrl) { |
|
not at google - send to devlin
2013/09/05 00:37:16
A system that I think would make more sense here i
Devlin
2013/09/05 17:53:55
Helps a bit, but we still have to check for where
|
| + var div = cloneTemplate('extension-error-detailed-wrapper'); |
| + div.isDetailed = true; |
| + } else { |
| + var div = document.createElement('div'); |
| + div.className = 'extension-error-simple-wrapper'; |
| + div.isDetailed = false; |
| + } |
| + |
| div.__proto__ = ExtensionError.prototype; |
| - div.className = 'extension-error-simple-wrapper'; |
| div.error_ = error; |
| div.decorate(); |
| return div; |
| @@ -84,7 +91,18 @@ cr.define('extensions', function() { |
| getRelativeUrl(this.error_.source, this.extensionUrl_), |
| this.error_.source)); |
| - this.appendChild(metadata); |
| + if (this.isDetailed) { |
| + var summary = this.querySelector('summary'); |
| + summary.appendChild(metadata); |
| + |
| + var detailsNode = this.querySelector('.extension-error-details'); |
| + if (this.error_.contextUrl) |
| + detailsNode.appendChild(this.getContextNode_()); |
| + if (this.error_.stackTrace) |
| + detailsNode.appendChild(this.getStackNode_()); |
| + } else { |
| + this.appendChild(metadata); |
| + } |
| }, |
| /** |
| @@ -93,12 +111,13 @@ cr.define('extensions', function() { |
| * @param {string} description a human-friendly description the location |
| * (e.g., filename, line). |
| * @param {string} url The url of the resource to view. |
| + * @param {?number} line An optional line number of the resource. |
|
not at google - send to devlin
2013/09/05 00:37:16
s/number/line
Devlin
2013/09/05 17:53:55
I thought it was supposed to follow
@param {type}
not at google - send to devlin
2013/09/06 17:05:19
Oops I totally misread that.
|
| * @return {HTMLElement} The created node, either a link or plaintext. |
| * @private |
| */ |
| - getViewSourceOrPlain_: function(description, url) { |
| + getViewSourceOrPlain_: function(description, url, line) { |
| if (this.canViewSource_(url)) |
| - var node = this.getViewSourceLink_(url); |
| + var node = this.getViewSourceLink_(url, line); |
| else |
| var node = document.createElement('div'); |
| node.className = 'extension-error-view-source'; |
| @@ -122,32 +141,100 @@ cr.define('extensions', function() { |
| * @return {HTMLElement} The clickable node to view the source. |
| * @private |
| */ |
| - getViewSourceLink_: function(url) { |
| + getViewSourceLink_: function(url, line) { |
| var node = document.createElement('a'); |
| var relativeUrl = getRelativeUrl(url, this.extensionUrl_); |
| + var dictionary = { 'extensionId': this.error_.extensionId, |
| + 'message': this.error_.message, |
| + 'pathSuffix': relativeUrl }; |
| + if (relativeUrl === 'manifest.json') { |
| + dictionary.fileType = 'manifest'; |
| + dictionary.manifestKey = this.error_.manifestKey; |
| + dictionary.manifestSpecific = this.error_.manifestSpecific; |
| + } else { |
| + dictionary.fileType = 'source'; |
| + // If we specify a line in the function call, use that. Otherwise, use |
| + // the line of the last stack frame, or 0 if we don't have a stack (0 |
| + // results in no highlighting). |
| + dictionary.lineNumber = |
| + line ? line : this.error_.stackTrace ? |
| + this.error_.stackTrace[0].lineNumber : 0; |
| + } |
| node.addEventListener('click', function(e) { |
| - chrome.send('extensionErrorRequestFileSource', |
| - [{'extensionId': this.error_.extensionId, |
| - 'message': this.error_.message, |
| - 'fileType': 'manifest', |
| - 'pathSuffix': relativeUrl, |
| - 'manifestKey': this.error_.manifestKey, |
| - 'manifestSpecific': this.error_.manifestSpecific}]); |
| - }.bind(this)); |
| + chrome.send('extensionErrorRequestFileSource', [dictionary]); |
| + }); |
| + node.title = loadTimeData.getString('extensionErrorViewSource'); |
| + return node; |
| + }, |
| + |
| + /** |
| + * Get the context node for this error. This will attempt to link to the |
| + * context in which the error occurred, and can be either an extension page |
| + * or an external page. |
| + * @return {HTMLDivElement} The context node for the error, including the |
| + * label and a link to the context. |
| + * @private |
| + */ |
| + getContextNode_: function() { |
| + var node = cloneTemplate('extension-error-context-wrapper'); |
| + var linkNode = node.querySelector('a'); |
| + if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) { |
| + linkNode.innerText = getRelativeUrl(this.error_.contextUrl, |
| + this.extensionUrl_); |
| + } else { |
| + linkNode.innerText = this.error_.contextUrl; |
| + linkNode.href = this.error_.contextUrl; |
| + linkNode.target = '_blank'; |
| + } |
| + return node; |
| + }, |
| + |
| + /** |
| + * Get a node for the stack trace for this error. Each stack frame will |
| + * include a resource url, line number, and function name (possibly |
| + * anonymous). If possible, these frames will also be linked for viewing the |
| + * source. |
| + * @return {HTMLDetailsElement} The stack trace node for this error, with |
| + * all stack frames nested in a details-summary object. |
| + * @private |
| + */ |
| + getStackNode_: function() { |
| + var node = cloneTemplate('extension-error-stack-trace'); |
| + var listNode = node.querySelector('.extension-error-stack-trace-list'); |
| + this.error_.stackTrace.forEach(function(frame) { |
| + var frameNode = document.createElement('div'); |
| + var description = getRelativeUrl(frame.url, this.extensionUrl_) + |
| + ':' + frame.lineNumber; |
| + if (frame.functionName) { |
| + var functionName = frame.functionName == '(anonymous function)' ? |
| + loadTimeData.getString('extensionErrorAnonymousFunction') : |
| + frame.functionName; |
| + description += ' (' + functionName + ')'; |
| + } |
| + frameNode.appendChild(this.getViewSourceOrPlain_( |
| + description, frame.url, frame.lineNumber)); |
| + listNode.appendChild( |
| + document.createElement('li')).appendChild(frameNode); |
| + }, this); |
| + |
| return node; |
| }, |
| }; |
| /** |
| * A variable length list of runtime or manifest errors for a given extension. |
| + * @param {Array.<Object>} errors The list of extension errors with which |
| + * to populate the list. |
| + * @param {string} type The type of errors, either 'manifest' or 'runtime'. |
| * @constructor |
| * @extends {HTMLDivElement} |
| */ |
| - function ExtensionErrorList(errors) { |
| + function ExtensionErrorList(errors, type) { |
| var div = cloneTemplate('extension-error-list'); |
| div.__proto__ = ExtensionErrorList.prototype; |
| div.errors_ = errors; |
| + div.type_ = type; |
| div.decorate(); |
| return div; |
| } |
| @@ -164,6 +251,11 @@ cr.define('extensions', function() { |
| /** @override */ |
| decorate: function() { |
| + var title = this.type_ == 'manifest' ? 'extensionErrorsManifestErrors' : |
| + 'extensionErrorsRuntimeErrors'; |
| + this.querySelector('.extension-error-list-title').innerText = |
| + loadTimeData.getString(title); |
| + |
| this.contents_ = this.querySelector('.extension-error-list-contents'); |
| this.errors_.forEach(function(error) { |
| this.contents_.appendChild(document.createElement('li')).appendChild( |