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 * 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 Loading... | |
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() { |
(...skipping 16 matching lines...) Expand all Loading... | |
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').innerText = |
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 if (this.className == 'extension-error-simple-wrapper') |
89 this.appendChild(metadata); | |
90 else | |
91 this.querySelector('summary').appendChild(metadata); | |
not at google - send to devlin
2013/09/06 17:05:19
You could do this in a semi-template-agnostic way
Devlin
2013/09/06 18:18:02
Oooh, that's much cleaner. Thanks :)
| |
92 | |
93 if (this.error_.contextUrl) { | |
94 this.querySelector('.extension-error-details'). | |
95 appendChild(this.getContextNode_()); | |
96 } | |
97 if (this.error_.stackTrace) { | |
98 this.querySelector('.extension-error-details'). | |
99 appendChild(this.getStackNode_()); | |
100 } | |
not at google - send to devlin
2013/09/06 17:05:19
instead:
var errorDetails = this.querySelector('.
Devlin
2013/09/06 18:18:02
Done (s/errorDetails/detailsNode to keep consisten
| |
88 }, | 101 }, |
89 | 102 |
90 /** | 103 /** |
91 * Return a div with text |description|. If it's possible to view the source | 104 * Return a div with text |description|. If it's possible to view the source |
92 * for |url|, linkify the div to do so. | 105 * for |url|, linkify the div to do so. |
93 * @param {string} description a human-friendly description the location | 106 * @param {string} description a human-friendly description the location |
94 * (e.g., filename, line). | 107 * (e.g., filename, line). |
95 * @param {string} url The url of the resource to view. | 108 * @param {string} url The url of the resource to view. |
109 * @param {?number} line An optional line number of the resource. | |
96 * @return {HTMLElement} The created node, either a link or plaintext. | 110 * @return {HTMLElement} The created node, either a link or plaintext. |
97 * @private | 111 * @private |
98 */ | 112 */ |
99 getViewSourceOrPlain_: function(description, url) { | 113 getViewSourceOrPlain_: function(description, url, line) { |
100 if (this.canViewSource_(url)) | 114 if (this.canViewSource_(url)) |
101 var node = this.getViewSourceLink_(url); | 115 var node = this.getViewSourceLink_(url, line); |
102 else | 116 else |
103 var node = document.createElement('div'); | 117 var node = document.createElement('div'); |
104 node.className = 'extension-error-view-source'; | 118 node.className = 'extension-error-view-source'; |
105 node.innerText = description; | 119 node.innerText = description; |
106 return node; | 120 return node; |
107 }, | 121 }, |
108 | 122 |
109 /** | 123 /** |
110 * Determine whether we can view the source of a given url. | 124 * Determine whether we can view the source of a given url. |
111 * @param {string} url The url of the resource to view. | 125 * @param {string} url The url of the resource to view. |
112 * @return {boolean} Whether or not we can view the source for the url. | 126 * @return {boolean} Whether or not we can view the source for the url. |
113 * @private | 127 * @private |
114 */ | 128 */ |
115 canViewSource_: function(url) { | 129 canViewSource_: function(url) { |
116 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; | 130 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; |
117 }, | 131 }, |
118 | 132 |
119 /** | 133 /** |
120 * Create a clickable node to view the source for the given url. | 134 * Create a clickable node to view the source for the given url. |
121 * @param {string} url The url to the resource to view. | 135 * @param {string} url The url to the resource to view. |
122 * @return {HTMLElement} The clickable node to view the source. | 136 * @return {HTMLElement} The clickable node to view the source. |
123 * @private | 137 * @private |
124 */ | 138 */ |
125 getViewSourceLink_: function(url) { | 139 getViewSourceLink_: function(url, line) { |
126 var node = document.createElement('a'); | 140 var node = document.createElement('a'); |
127 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); | 141 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); |
142 var dictionary = { 'extensionId': this.error_.extensionId, | |
not at google - send to devlin
2013/09/06 17:05:19
s/dictionary/requestFileSourceArgs?
Devlin
2013/09/06 18:18:02
Done.
| |
143 'message': this.error_.message, | |
144 'pathSuffix': relativeUrl }; | |
145 if (relativeUrl === 'manifest.json') { | |
not at google - send to devlin
2013/09/06 17:05:19
just ==. No point ===ing for a comparison to a non
Devlin
2013/09/06 18:18:02
s/===/==. We have to do the check on the JS side
| |
146 dictionary.fileType = 'manifest'; | |
147 dictionary.manifestKey = this.error_.manifestKey; | |
148 dictionary.manifestSpecific = this.error_.manifestSpecific; | |
149 } else { | |
150 dictionary.fileType = 'source'; | |
151 // If we specify a line in the function call, use that. Otherwise, use | |
152 // the line of the last stack frame, or 0 if we don't have a stack (0 | |
153 // results in no highlighting). | |
154 dictionary.lineNumber = | |
155 line ? line : this.error_.stackTrace ? | |
156 this.error_.stackTrace[0].lineNumber : 0; | |
not at google - send to devlin
2013/09/06 17:05:19
mmmm nested ternary-if confusing.
Devlin
2013/09/06 18:18:02
Yeah, I was afraid of that (hence the comment)...
| |
157 } | |
128 | 158 |
129 node.addEventListener('click', function(e) { | 159 node.addEventListener('click', function(e) { |
130 chrome.send('extensionErrorRequestFileSource', | 160 chrome.send('extensionErrorRequestFileSource', [dictionary]); |
131 [{'extensionId': this.error_.extensionId, | 161 }); |
132 'message': this.error_.message, | 162 node.title = loadTimeData.getString('extensionErrorViewSource'); |
133 'fileType': 'manifest', | 163 return node; |
134 'pathSuffix': relativeUrl, | 164 }, |
135 'manifestKey': this.error_.manifestKey, | 165 |
136 'manifestSpecific': this.error_.manifestSpecific}]); | 166 /** |
137 }.bind(this)); | 167 * Get the context node for this error. This will attempt to link to the |
168 * context in which the error occurred, and can be either an extension page | |
169 * or an external page. | |
170 * @return {HTMLDivElement} The context node for the error, including the | |
171 * label and a link to the context. | |
172 * @private | |
173 */ | |
174 getContextNode_: function() { | |
175 var node = cloneTemplate('extension-error-context-wrapper'); | |
176 var linkNode = node.querySelector('a'); | |
177 if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) { | |
178 linkNode.innerText = getRelativeUrl(this.error_.contextUrl, | |
179 this.extensionUrl_); | |
not at google - send to devlin
2013/09/06 17:05:19
I believe that innerText is deprecated in favour o
Devlin
2013/09/06 18:18:02
Hadn't heard that before. Good to know. Done.
| |
180 } else { | |
181 linkNode.innerText = this.error_.contextUrl; | |
182 linkNode.href = this.error_.contextUrl; | |
183 linkNode.target = '_blank'; | |
not at google - send to devlin
2013/09/06 17:05:19
I don't understand why the <a>s get hrefs here, wh
Devlin
2013/09/06 18:18:02
I didn't actually realize we could navigate direct
| |
184 } | |
185 return node; | |
186 }, | |
187 | |
188 /** | |
189 * Get a node for the stack trace for this error. Each stack frame will | |
190 * include a resource url, line number, and function name (possibly | |
191 * anonymous). If possible, these frames will also be linked for viewing the | |
192 * source. | |
193 * @return {HTMLDetailsElement} The stack trace node for this error, with | |
194 * all stack frames nested in a details-summary object. | |
195 * @private | |
196 */ | |
197 getStackNode_: function() { | |
198 var node = cloneTemplate('extension-error-stack-trace'); | |
199 var listNode = node.querySelector('.extension-error-stack-trace-list'); | |
200 this.error_.stackTrace.forEach(function(frame) { | |
201 var frameNode = document.createElement('div'); | |
202 var description = getRelativeUrl(frame.url, this.extensionUrl_) + | |
203 ':' + frame.lineNumber; | |
204 if (frame.functionName) { | |
205 var functionName = frame.functionName == '(anonymous function)' ? | |
206 loadTimeData.getString('extensionErrorAnonymousFunction') : | |
207 frame.functionName; | |
208 description += ' (' + functionName + ')'; | |
not at google - send to devlin
2013/09/06 17:05:19
small point, but + concatenation is inefficient; b
Devlin
2013/09/06 18:18:02
Is it, though?
http://stackoverflow.com/questions/
not at google - send to devlin
2013/09/06 19:00:15
Heh ok. So in Python it makes a huge, huge differe
| |
209 } | |
210 frameNode.appendChild(this.getViewSourceOrPlain_( | |
211 description, frame.url, frame.lineNumber)); | |
212 listNode.appendChild( | |
213 document.createElement('li')).appendChild(frameNode); | |
214 }, this); | |
215 | |
138 return node; | 216 return node; |
139 }, | 217 }, |
140 }; | 218 }; |
141 | 219 |
142 /** | 220 /** |
143 * A variable length list of runtime or manifest errors for a given extension. | 221 * A variable length list of runtime or manifest errors for a given extension. |
222 * @param {Array.<Object>} errors The list of extension errors with which | |
223 * to populate the list. | |
224 * @param {string} type The type of errors, either 'manifest' or 'runtime'. | |
144 * @constructor | 225 * @constructor |
145 * @extends {HTMLDivElement} | 226 * @extends {HTMLDivElement} |
146 */ | 227 */ |
147 function ExtensionErrorList(errors) { | 228 function ExtensionErrorList(errors, type) { |
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.type_ = type; | |
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 var title = this.type_ == 'manifest' ? 'extensionErrorsManifestErrors' : | |
250 'extensionErrorsRuntimeErrors'; | |
251 this.querySelector('.extension-error-list-title').innerText = | |
252 loadTimeData.getString(title); | |
253 | |
167 this.contents_ = this.querySelector('.extension-error-list-contents'); | 254 this.contents_ = this.querySelector('.extension-error-list-contents'); |
168 this.errors_.forEach(function(error) { | 255 this.errors_.forEach(function(error) { |
169 this.contents_.appendChild(document.createElement('li')).appendChild( | 256 this.contents_.appendChild(document.createElement('li')).appendChild( |
170 new ExtensionError(error)); | 257 new ExtensionError(error, |
258 error.contextUrl || error.stackTrace ? | |
259 'extension-error-detailed-wrapper' : | |
260 'extension-error-simple-wrapper')); | |
171 }, this); | 261 }, this); |
172 | 262 |
173 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { | 263 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { |
174 for (var i = this.MAX_ERRORS_TO_SHOW_; | 264 for (var i = this.MAX_ERRORS_TO_SHOW_; |
175 i < this.contents_.children.length; ++i) { | 265 i < this.contents_.children.length; ++i) { |
176 this.contents_.children[i].hidden = true; | 266 this.contents_.children[i].hidden = true; |
177 } | 267 } |
178 this.initShowMoreButton_(); | 268 this.initShowMoreButton_(); |
179 } | 269 } |
180 }, | 270 }, |
(...skipping 17 matching lines...) Expand all Loading... | |
198 button.innerText = loadTimeData.getString(message); | 288 button.innerText = loadTimeData.getString(message); |
199 button.isShowingAll = !button.isShowingAll; | 289 button.isShowingAll = !button.isShowingAll; |
200 }.bind(this)); | 290 }.bind(this)); |
201 } | 291 } |
202 }; | 292 }; |
203 | 293 |
204 return { | 294 return { |
205 ExtensionErrorList: ExtensionErrorList | 295 ExtensionErrorList: ExtensionErrorList |
206 }; | 296 }; |
207 }); | 297 }); |
OLD | NEW |