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

Side by Side Diff: chrome/browser/resources/extensions/extension_error.js

Issue 23624002: Add UI for RuntimeErrors in the ErrorConsole (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@dc_ec_merge
Patch Set: Created 7 years, 3 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
OLDNEW
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 * Returns whether or not a given |url| is associated with an extension. 9 * Returns whether or not a given |url| is associated with an extension.
10 * @param {string} url The url to examine. 10 * @param {string} url The url to examine.
(...skipping 23 matching lines...) Expand all
34 */ 34 */
35 function cloneTemplate(templateName) { 35 function cloneTemplate(templateName) {
36 return $('template-collection-extension-error'). 36 return $('template-collection-extension-error').
37 querySelector('.' + templateName).cloneNode(true); 37 querySelector('.' + templateName).cloneNode(true);
38 } 38 }
39 39
40 /** 40 /**
41 * Creates a new ExtensionError HTMLElement; this is used to show a 41 * Creates a new ExtensionError HTMLElement; this is used to show a
42 * notification to the user when an error is caused by an extension. 42 * notification to the user when an error is caused by an extension.
43 * @param {Object} error The error the element should represent. 43 * @param {Object} error The error the element should represent.
44 * @param {string} templateName The name of the template to clone for the
45 * error ('extension-error-[detailed|simple]-wrapper').
44 * @constructor 46 * @constructor
45 * @extends {HTMLDivElement} 47 * @extends {HTMLDivElement}
46 */ 48 */
47 function ExtensionError(error) { 49 function ExtensionError(error, templateName) {
48 var div = document.createElement('div'); 50 var div = cloneTemplate(templateName);
49 div.__proto__ = ExtensionError.prototype; 51 div.__proto__ = ExtensionError.prototype;
50 div.className = 'extension-error-simple-wrapper';
51 div.error_ = error; 52 div.error_ = error;
52 div.decorate(); 53 div.decorate();
53 return div; 54 return div;
54 } 55 }
55 56
56 ExtensionError.prototype = { 57 ExtensionError.prototype = {
57 __proto__: HTMLDivElement.prototype, 58 __proto__: HTMLDivElement.prototype,
58 59
59 /** @override */ 60 /** @override */
60 decorate: function() { 61 decorate: function() {
61 var metadata = cloneTemplate('extension-error-metadata'); 62 var metadata = cloneTemplate('extension-error-metadata');
62 63
63 // Add an additional class for the severity level. 64 // Add an additional class for the severity level.
64 if (this.error_.level == 0) 65 if (this.error_.level == 0)
65 metadata.className += ' extension-error-severity-info'; 66 metadata.className += ' extension-error-severity-info';
66 else if (this.error_.level == 1) 67 else if (this.error_.level == 1)
67 metadata.className += ' extension-error-severity-warning'; 68 metadata.className += ' extension-error-severity-warning';
68 else 69 else
69 metadata.className += ' extension-error-severity-fatal'; 70 metadata.className += ' extension-error-severity-fatal';
70 71
71 var iconNode = document.createElement('img'); 72 var iconNode = document.createElement('img');
72 iconNode.className = 'extension-error-icon'; 73 iconNode.className = 'extension-error-icon';
73 metadata.insertBefore(iconNode, metadata.firstChild); 74 metadata.insertBefore(iconNode, metadata.firstChild);
74 75
75 // Add a property for the extension's base url in order to determine if 76 // Add a property for the extension's base url in order to determine if
76 // a url belongs to the extension. 77 // a url belongs to the extension.
77 this.extensionUrl_ = 78 this.extensionUrl_ =
78 'chrome-extension://' + this.error_.extensionId + '/'; 79 'chrome-extension://' + this.error_.extensionId + '/';
79 80
80 metadata.querySelector('.extension-error-message').innerText = 81 metadata.querySelector('.extension-error-message').textContent =
81 this.error_.message; 82 this.error_.message;
82 83
83 metadata.appendChild(this.getViewSourceOrPlain_( 84 metadata.appendChild(this.getViewSourceOrPlain_(
84 getRelativeUrl(this.error_.source, this.extensionUrl_), 85 getRelativeUrl(this.error_.source, this.extensionUrl_),
85 this.error_.source)); 86 this.error_.source));
86 87
87 this.appendChild(metadata); 88 // The error template may specify a <summary> to put template metadata in.
89 // If not, just append it to the top-level element.
90 var metadataContainer = this.querySelector('summary') || this;
91 metadataContainer.appendChild(metadata);
92
93 var detailsNode = this.querySelector('.extension-error-details');
94 if (detailsNode && this.error_.contextUrl)
95 detailsNode.appendChild(this.getContextNode_());
96 if (detailsNode && this.error_.stackTrace)
97 detailsNode.appendChild(this.getStackNode_());
88 }, 98 },
89 99
90 /** 100 /**
91 * Return a div with text |description|. If it's possible to view the source 101 * Return a div with text |description|. If it's possible to view the source
92 * for |url|, linkify the div to do so. 102 * for |url|, linkify the div to do so.
93 * @param {string} description a human-friendly description the location 103 * @param {string} description a human-friendly description the location
94 * (e.g., filename, line). 104 * (e.g., filename, line).
95 * @param {string} url The url of the resource to view. 105 * @param {string} url The url of the resource to view.
106 * @param {?number} line An optional line number of the resource.
96 * @return {HTMLElement} The created node, either a link or plaintext. 107 * @return {HTMLElement} The created node, either a link or plaintext.
97 * @private 108 * @private
98 */ 109 */
99 getViewSourceOrPlain_: function(description, url) { 110 getViewSourceOrPlain_: function(description, url, line) {
100 if (this.canViewSource_(url)) 111 if (this.canViewSource_(url))
101 var node = this.getViewSourceLink_(url); 112 var node = this.getViewSourceLink_(url, line);
102 else 113 else
103 var node = document.createElement('div'); 114 var node = document.createElement('div');
104 node.className = 'extension-error-view-source'; 115 node.className = 'extension-error-view-source';
105 node.innerText = description; 116 node.textContent = description;
106 return node; 117 return node;
107 }, 118 },
108 119
109 /** 120 /**
110 * Determine whether we can view the source of a given url. 121 * Determine whether we can view the source of a given url.
111 * @param {string} url The url of the resource to view. 122 * @param {string} url The url of the resource to view.
112 * @return {boolean} Whether or not we can view the source for the url. 123 * @return {boolean} Whether or not we can view the source for the url.
113 * @private 124 * @private
114 */ 125 */
115 canViewSource_: function(url) { 126 canViewSource_: function(url) {
116 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; 127 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json';
117 }, 128 },
118 129
119 /** 130 /**
120 * Create a clickable node to view the source for the given url. 131 * Create a clickable node to view the source for the given url.
121 * @param {string} url The url to the resource to view. 132 * @param {string} url The url to the resource to view.
133 * @param {?number} line An optional line number of the resource (for
134 * source files).
122 * @return {HTMLElement} The clickable node to view the source. 135 * @return {HTMLElement} The clickable node to view the source.
123 * @private 136 * @private
124 */ 137 */
125 getViewSourceLink_: function(url) { 138 getViewSourceLink_: function(url, line) {
126 var node = document.createElement('a'); 139 var node = document.createElement('a');
127 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); 140 var relativeUrl = getRelativeUrl(url, this.extensionUrl_);
141 var requestFileSourceArgs = { 'extensionId': this.error_.extensionId,
142 'message': this.error_.message,
143 'pathSuffix': relativeUrl };
144 if (relativeUrl == 'manifest.json') {
145 requestFileSourceArgs.manifestKey = this.error_.manifestKey;
146 requestFileSourceArgs.manifestSpecific = this.error_.manifestSpecific;
147 } else {
148 // Prefer |line| if available, or default to the line of the last stack
149 // frame.
150 if (line) {
151 requestFileSourceArgs.lineNumber = line;
152 } else if (this.error_.stackTrace) {
153 requestFileSourceArgs.lineNumber =
154 this.error_.stackTrace[0].lineNumber;
155 }
156 }
128 157
129 node.addEventListener('click', function(e) { 158 node.addEventListener('click', function(e) {
130 chrome.send('extensionErrorRequestFileSource', 159 chrome.send('extensionErrorRequestFileSource', [requestFileSourceArgs]);
131 [{'extensionId': this.error_.extensionId, 160 });
132 'message': this.error_.message, 161 node.title = loadTimeData.getString('extensionErrorViewSource');
133 'fileType': 'manifest', 162 return node;
134 'pathSuffix': relativeUrl, 163 },
135 'manifestKey': this.error_.manifestKey, 164
136 'manifestSpecific': this.error_.manifestSpecific}]); 165 /**
137 }.bind(this)); 166 * Get the context node for this error. This will attempt to link to the
167 * context in which the error occurred, and can be either an extension page
168 * or an external page.
169 * @return {HTMLDivElement} The context node for the error, including the
170 * label and a link to the context.
171 * @private
172 */
173 getContextNode_: function() {
174 var node = cloneTemplate('extension-error-context-wrapper');
175 var linkNode = node.querySelector('a');
176 if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) {
177 linkNode.textContent = getRelativeUrl(this.error_.contextUrl,
178 this.extensionUrl_);
179 } else {
180 linkNode.textContent = this.error_.contextUrl;
181 }
182 linkNode.href = this.error_.contextUrl;
183 linkNode.target = '_blank';
184 return node;
185 },
186
187 /**
188 * Get a node for the stack trace for this error. Each stack frame will
189 * include a resource url, line number, and function name (possibly
190 * anonymous). If possible, these frames will also be linked for viewing the
191 * source.
192 * @return {HTMLDetailsElement} The stack trace node for this error, with
193 * all stack frames nested in a details-summary object.
194 * @private
195 */
196 getStackNode_: function() {
197 var node = cloneTemplate('extension-error-stack-trace');
198 var listNode = node.querySelector('.extension-error-stack-trace-list');
199 this.error_.stackTrace.forEach(function(frame) {
200 var frameNode = document.createElement('div');
201 var description = getRelativeUrl(frame.url, this.extensionUrl_) +
202 ':' + frame.lineNumber;
203 if (frame.functionName) {
204 var functionName = frame.functionName == '(anonymous function)' ?
205 loadTimeData.getString('extensionErrorAnonymousFunction') :
206 frame.functionName;
207 description += ' (' + functionName + ')';
208 }
209 frameNode.appendChild(this.getViewSourceOrPlain_(
210 description, frame.url, frame.lineNumber));
211 listNode.appendChild(
212 document.createElement('li')).appendChild(frameNode);
213 }, this);
214
138 return node; 215 return node;
139 }, 216 },
140 }; 217 };
141 218
142 /** 219 /**
143 * A variable length list of runtime or manifest errors for a given extension. 220 * A variable length list of runtime or manifest errors for a given extension.
221 * @param {Array.<Object>} errors The list of extension errors with which
222 * to populate the list.
223 * @param {string} title The i18n key for the title of the error list, i.e.
224 * 'extensionErrors[Manifest,Runtime]Errors'.
144 * @constructor 225 * @constructor
145 * @extends {HTMLDivElement} 226 * @extends {HTMLDivElement}
146 */ 227 */
147 function ExtensionErrorList(errors) { 228 function ExtensionErrorList(errors, title) {
148 var div = cloneTemplate('extension-error-list'); 229 var div = cloneTemplate('extension-error-list');
149 div.__proto__ = ExtensionErrorList.prototype; 230 div.__proto__ = ExtensionErrorList.prototype;
150 div.errors_ = errors; 231 div.errors_ = errors;
232 div.title_ = title;
151 div.decorate(); 233 div.decorate();
152 return div; 234 return div;
153 } 235 }
154 236
155 ExtensionErrorList.prototype = { 237 ExtensionErrorList.prototype = {
156 __proto__: HTMLDivElement.prototype, 238 __proto__: HTMLDivElement.prototype,
157 239
158 /** 240 /**
159 * @private 241 * @private
160 * @const 242 * @const
161 * @type {number} 243 * @type {number}
162 */ 244 */
163 MAX_ERRORS_TO_SHOW_: 3, 245 MAX_ERRORS_TO_SHOW_: 3,
164 246
165 /** @override */ 247 /** @override */
166 decorate: function() { 248 decorate: function() {
249 this.querySelector('.extension-error-list-title').textContent =
250 loadTimeData.getString(this.title_);
251
167 this.contents_ = this.querySelector('.extension-error-list-contents'); 252 this.contents_ = this.querySelector('.extension-error-list-contents');
168 this.errors_.forEach(function(error) { 253 this.errors_.forEach(function(error) {
169 this.contents_.appendChild(document.createElement('li')).appendChild( 254 this.contents_.appendChild(document.createElement('li')).appendChild(
170 new ExtensionError(error)); 255 new ExtensionError(error,
256 error.contextUrl || error.stackTrace ?
257 'extension-error-detailed-wrapper' :
258 'extension-error-simple-wrapper'));
171 }, this); 259 }, this);
172 260
173 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { 261 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) {
174 for (var i = this.MAX_ERRORS_TO_SHOW_; 262 for (var i = this.MAX_ERRORS_TO_SHOW_;
175 i < this.contents_.children.length; ++i) { 263 i < this.contents_.children.length; ++i) {
176 this.contents_.children[i].hidden = true; 264 this.contents_.children[i].hidden = true;
177 } 265 }
178 this.initShowMoreButton_(); 266 this.initShowMoreButton_();
179 } 267 }
180 }, 268 },
181 269
182 /** 270 /**
183 * Initialize the "Show More" button for the error list. If there are more 271 * Initialize the "Show More" button for the error list. If there are more
184 * than |MAX_ERRORS_TO_SHOW_| errors in the list. 272 * than |MAX_ERRORS_TO_SHOW_| errors in the list.
185 * @private 273 * @private
186 */ 274 */
187 initShowMoreButton_: function() { 275 initShowMoreButton_: function() {
188 var button = this.querySelector('.extension-error-list-show-more a'); 276 var button = this.querySelector('.extension-error-list-show-more a');
189 button.hidden = false; 277 button.hidden = false;
190 button.isShowingAll = false; 278 button.isShowingAll = false;
191 button.addEventListener('click', function(e) { 279 button.addEventListener('click', function(e) {
192 for (var i = this.MAX_ERRORS_TO_SHOW_; 280 for (var i = this.MAX_ERRORS_TO_SHOW_;
193 i < this.contents_.children.length; ++i) { 281 i < this.contents_.children.length; ++i) {
194 this.contents_.children[i].hidden = button.isShowingAll; 282 this.contents_.children[i].hidden = button.isShowingAll;
195 } 283 }
196 var message = button.isShowingAll ? 'extensionErrorsShowMore' : 284 var message = button.isShowingAll ? 'extensionErrorsShowMore' :
197 'extensionErrorsShowFewer'; 285 'extensionErrorsShowFewer';
198 button.innerText = loadTimeData.getString(message); 286 button.textContent = loadTimeData.getString(message);
199 button.isShowingAll = !button.isShowingAll; 287 button.isShowingAll = !button.isShowingAll;
200 }.bind(this)); 288 }.bind(this));
201 } 289 }
202 }; 290 };
203 291
204 return { 292 return {
205 ExtensionErrorList: ExtensionErrorList 293 ExtensionErrorList: ExtensionErrorList
206 }; 294 };
207 }); 295 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698