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 27 matching lines...) Expand all Loading... | |
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 * @constructor | 44 * @constructor |
45 * @extends {HTMLDivElement} | 45 * @extends {HTMLDivElement} |
46 */ | 46 */ |
47 function ExtensionError(error) { | 47 function ExtensionError(error) { |
48 var div = document.createElement('div'); | 48 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
| |
49 var div = cloneTemplate('extension-error-detailed-wrapper'); | |
50 div.isDetailed = true; | |
51 } else { | |
52 var div = document.createElement('div'); | |
53 div.className = 'extension-error-simple-wrapper'; | |
54 div.isDetailed = false; | |
55 } | |
56 | |
49 div.__proto__ = ExtensionError.prototype; | 57 div.__proto__ = ExtensionError.prototype; |
50 div.className = 'extension-error-simple-wrapper'; | |
51 div.error_ = error; | 58 div.error_ = error; |
52 div.decorate(); | 59 div.decorate(); |
53 return div; | 60 return div; |
54 } | 61 } |
55 | 62 |
56 ExtensionError.prototype = { | 63 ExtensionError.prototype = { |
57 __proto__: HTMLDivElement.prototype, | 64 __proto__: HTMLDivElement.prototype, |
58 | 65 |
59 /** @override */ | 66 /** @override */ |
60 decorate: function() { | 67 decorate: function() { |
(...skipping 16 matching lines...) Expand all Loading... | |
77 this.extensionUrl_ = | 84 this.extensionUrl_ = |
78 'chrome-extension://' + this.error_.extensionId + '/'; | 85 'chrome-extension://' + this.error_.extensionId + '/'; |
79 | 86 |
80 metadata.querySelector('.extension-error-message').innerText = | 87 metadata.querySelector('.extension-error-message').innerText = |
81 this.error_.message; | 88 this.error_.message; |
82 | 89 |
83 metadata.appendChild(this.getViewSourceOrPlain_( | 90 metadata.appendChild(this.getViewSourceOrPlain_( |
84 getRelativeUrl(this.error_.source, this.extensionUrl_), | 91 getRelativeUrl(this.error_.source, this.extensionUrl_), |
85 this.error_.source)); | 92 this.error_.source)); |
86 | 93 |
87 this.appendChild(metadata); | 94 if (this.isDetailed) { |
95 var summary = this.querySelector('summary'); | |
96 summary.appendChild(metadata); | |
97 | |
98 var detailsNode = this.querySelector('.extension-error-details'); | |
99 if (this.error_.contextUrl) | |
100 detailsNode.appendChild(this.getContextNode_()); | |
101 if (this.error_.stackTrace) | |
102 detailsNode.appendChild(this.getStackNode_()); | |
103 } else { | |
104 this.appendChild(metadata); | |
105 } | |
88 }, | 106 }, |
89 | 107 |
90 /** | 108 /** |
91 * Return a div with text |description|. If it's possible to view the source | 109 * Return a div with text |description|. If it's possible to view the source |
92 * for |url|, linkify the div to do so. | 110 * for |url|, linkify the div to do so. |
93 * @param {string} description a human-friendly description the location | 111 * @param {string} description a human-friendly description the location |
94 * (e.g., filename, line). | 112 * (e.g., filename, line). |
95 * @param {string} url The url of the resource to view. | 113 * @param {string} url The url of the resource to view. |
114 * @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.
| |
96 * @return {HTMLElement} The created node, either a link or plaintext. | 115 * @return {HTMLElement} The created node, either a link or plaintext. |
97 * @private | 116 * @private |
98 */ | 117 */ |
99 getViewSourceOrPlain_: function(description, url) { | 118 getViewSourceOrPlain_: function(description, url, line) { |
100 if (this.canViewSource_(url)) | 119 if (this.canViewSource_(url)) |
101 var node = this.getViewSourceLink_(url); | 120 var node = this.getViewSourceLink_(url, line); |
102 else | 121 else |
103 var node = document.createElement('div'); | 122 var node = document.createElement('div'); |
104 node.className = 'extension-error-view-source'; | 123 node.className = 'extension-error-view-source'; |
105 node.innerText = description; | 124 node.innerText = description; |
106 return node; | 125 return node; |
107 }, | 126 }, |
108 | 127 |
109 /** | 128 /** |
110 * Determine whether we can view the source of a given url. | 129 * Determine whether we can view the source of a given url. |
111 * @param {string} url The url of the resource to view. | 130 * @param {string} url The url of the resource to view. |
112 * @return {boolean} Whether or not we can view the source for the url. | 131 * @return {boolean} Whether or not we can view the source for the url. |
113 * @private | 132 * @private |
114 */ | 133 */ |
115 canViewSource_: function(url) { | 134 canViewSource_: function(url) { |
116 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; | 135 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; |
117 }, | 136 }, |
118 | 137 |
119 /** | 138 /** |
120 * Create a clickable node to view the source for the given url. | 139 * Create a clickable node to view the source for the given url. |
121 * @param {string} url The url to the resource to view. | 140 * @param {string} url The url to the resource to view. |
122 * @return {HTMLElement} The clickable node to view the source. | 141 * @return {HTMLElement} The clickable node to view the source. |
123 * @private | 142 * @private |
124 */ | 143 */ |
125 getViewSourceLink_: function(url) { | 144 getViewSourceLink_: function(url, line) { |
126 var node = document.createElement('a'); | 145 var node = document.createElement('a'); |
127 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); | 146 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); |
147 var dictionary = { 'extensionId': this.error_.extensionId, | |
148 'message': this.error_.message, | |
149 'pathSuffix': relativeUrl }; | |
150 if (relativeUrl === 'manifest.json') { | |
151 dictionary.fileType = 'manifest'; | |
152 dictionary.manifestKey = this.error_.manifestKey; | |
153 dictionary.manifestSpecific = this.error_.manifestSpecific; | |
154 } else { | |
155 dictionary.fileType = 'source'; | |
156 // If we specify a line in the function call, use that. Otherwise, use | |
157 // the line of the last stack frame, or 0 if we don't have a stack (0 | |
158 // results in no highlighting). | |
159 dictionary.lineNumber = | |
160 line ? line : this.error_.stackTrace ? | |
161 this.error_.stackTrace[0].lineNumber : 0; | |
162 } | |
128 | 163 |
129 node.addEventListener('click', function(e) { | 164 node.addEventListener('click', function(e) { |
130 chrome.send('extensionErrorRequestFileSource', | 165 chrome.send('extensionErrorRequestFileSource', [dictionary]); |
131 [{'extensionId': this.error_.extensionId, | 166 }); |
132 'message': this.error_.message, | 167 node.title = loadTimeData.getString('extensionErrorViewSource'); |
133 'fileType': 'manifest', | 168 return node; |
134 'pathSuffix': relativeUrl, | 169 }, |
135 'manifestKey': this.error_.manifestKey, | 170 |
136 'manifestSpecific': this.error_.manifestSpecific}]); | 171 /** |
137 }.bind(this)); | 172 * Get the context node for this error. This will attempt to link to the |
173 * context in which the error occurred, and can be either an extension page | |
174 * or an external page. | |
175 * @return {HTMLDivElement} The context node for the error, including the | |
176 * label and a link to the context. | |
177 * @private | |
178 */ | |
179 getContextNode_: function() { | |
180 var node = cloneTemplate('extension-error-context-wrapper'); | |
181 var linkNode = node.querySelector('a'); | |
182 if (isExtensionUrl(this.error_.contextUrl, this.extensionUrl_)) { | |
183 linkNode.innerText = getRelativeUrl(this.error_.contextUrl, | |
184 this.extensionUrl_); | |
185 } else { | |
186 linkNode.innerText = this.error_.contextUrl; | |
187 linkNode.href = this.error_.contextUrl; | |
188 linkNode.target = '_blank'; | |
189 } | |
190 return node; | |
191 }, | |
192 | |
193 /** | |
194 * Get a node for the stack trace for this error. Each stack frame will | |
195 * include a resource url, line number, and function name (possibly | |
196 * anonymous). If possible, these frames will also be linked for viewing the | |
197 * source. | |
198 * @return {HTMLDetailsElement} The stack trace node for this error, with | |
199 * all stack frames nested in a details-summary object. | |
200 * @private | |
201 */ | |
202 getStackNode_: function() { | |
203 var node = cloneTemplate('extension-error-stack-trace'); | |
204 var listNode = node.querySelector('.extension-error-stack-trace-list'); | |
205 this.error_.stackTrace.forEach(function(frame) { | |
206 var frameNode = document.createElement('div'); | |
207 var description = getRelativeUrl(frame.url, this.extensionUrl_) + | |
208 ':' + frame.lineNumber; | |
209 if (frame.functionName) { | |
210 var functionName = frame.functionName == '(anonymous function)' ? | |
211 loadTimeData.getString('extensionErrorAnonymousFunction') : | |
212 frame.functionName; | |
213 description += ' (' + functionName + ')'; | |
214 } | |
215 frameNode.appendChild(this.getViewSourceOrPlain_( | |
216 description, frame.url, frame.lineNumber)); | |
217 listNode.appendChild( | |
218 document.createElement('li')).appendChild(frameNode); | |
219 }, this); | |
220 | |
138 return node; | 221 return node; |
139 }, | 222 }, |
140 }; | 223 }; |
141 | 224 |
142 /** | 225 /** |
143 * A variable length list of runtime or manifest errors for a given extension. | 226 * A variable length list of runtime or manifest errors for a given extension. |
227 * @param {Array.<Object>} errors The list of extension errors with which | |
228 * to populate the list. | |
229 * @param {string} type The type of errors, either 'manifest' or 'runtime'. | |
144 * @constructor | 230 * @constructor |
145 * @extends {HTMLDivElement} | 231 * @extends {HTMLDivElement} |
146 */ | 232 */ |
147 function ExtensionErrorList(errors) { | 233 function ExtensionErrorList(errors, type) { |
148 var div = cloneTemplate('extension-error-list'); | 234 var div = cloneTemplate('extension-error-list'); |
149 div.__proto__ = ExtensionErrorList.prototype; | 235 div.__proto__ = ExtensionErrorList.prototype; |
150 div.errors_ = errors; | 236 div.errors_ = errors; |
237 div.type_ = type; | |
151 div.decorate(); | 238 div.decorate(); |
152 return div; | 239 return div; |
153 } | 240 } |
154 | 241 |
155 ExtensionErrorList.prototype = { | 242 ExtensionErrorList.prototype = { |
156 __proto__: HTMLDivElement.prototype, | 243 __proto__: HTMLDivElement.prototype, |
157 | 244 |
158 /** | 245 /** |
159 * @private | 246 * @private |
160 * @const | 247 * @const |
161 * @type {number} | 248 * @type {number} |
162 */ | 249 */ |
163 MAX_ERRORS_TO_SHOW_: 3, | 250 MAX_ERRORS_TO_SHOW_: 3, |
164 | 251 |
165 /** @override */ | 252 /** @override */ |
166 decorate: function() { | 253 decorate: function() { |
254 var title = this.type_ == 'manifest' ? 'extensionErrorsManifestErrors' : | |
255 'extensionErrorsRuntimeErrors'; | |
256 this.querySelector('.extension-error-list-title').innerText = | |
257 loadTimeData.getString(title); | |
258 | |
167 this.contents_ = this.querySelector('.extension-error-list-contents'); | 259 this.contents_ = this.querySelector('.extension-error-list-contents'); |
168 this.errors_.forEach(function(error) { | 260 this.errors_.forEach(function(error) { |
169 this.contents_.appendChild(document.createElement('li')).appendChild( | 261 this.contents_.appendChild(document.createElement('li')).appendChild( |
170 new ExtensionError(error)); | 262 new ExtensionError(error)); |
171 }, this); | 263 }, this); |
172 | 264 |
173 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { | 265 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { |
174 for (var i = this.MAX_ERRORS_TO_SHOW_; | 266 for (var i = this.MAX_ERRORS_TO_SHOW_; |
175 i < this.contents_.children.length; ++i) { | 267 i < this.contents_.children.length; ++i) { |
176 this.contents_.children[i].hidden = true; | 268 this.contents_.children[i].hidden = true; |
(...skipping 21 matching lines...) Expand all Loading... | |
198 button.innerText = loadTimeData.getString(message); | 290 button.innerText = loadTimeData.getString(message); |
199 button.isShowingAll = !button.isShowingAll; | 291 button.isShowingAll = !button.isShowingAll; |
200 }.bind(this)); | 292 }.bind(this)); |
201 } | 293 } |
202 }; | 294 }; |
203 | 295 |
204 return { | 296 return { |
205 ExtensionErrorList: ExtensionErrorList | 297 ExtensionErrorList: ExtensionErrorList |
206 }; | 298 }; |
207 }); | 299 }); |
OLD | NEW |