OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 cr.define('extensions', function() { |
| 6 'use strict'; |
| 7 |
| 8 /** |
| 9 * Returns whether or not a given |url| is associated with an extension. |
| 10 * @param {string} url The url to examine. |
| 11 * @param {string} extensionUrl The url of the extension. |
| 12 * @return {boolean} Whether or not the url is associated with the extension. |
| 13 */ |
| 14 function isExtensionUrl(url, extensionUrl) { |
| 15 return url.substring(0, extensionUrl.length) == extensionUrl; |
| 16 } |
| 17 |
| 18 /** |
| 19 * Get the url relative to the main extension url. If the url is |
| 20 * unassociated with the extension, this will be the full url. |
| 21 * @param {string} url The url to make relative. |
| 22 * @param {string} extensionUrl The host for which the url is relative. |
| 23 * @return {string} The url relative to the host. |
| 24 */ |
| 25 function getRelativeUrl(url, extensionUrl) { |
| 26 return isExtensionUrl(url, extensionUrl) ? |
| 27 url.substring(extensionUrl.length) : url; |
| 28 } |
| 29 |
| 30 /** |
| 31 * Clone a template within the extension error template collection. |
| 32 * @param {string} templateName The class name of the template to clone. |
| 33 * @return {HTMLElement} The clone of the template. |
| 34 */ |
| 35 function cloneTemplate(templateName) { |
| 36 return $('template-collection-extension-error'). |
| 37 querySelector('.' + templateName).cloneNode(true); |
| 38 } |
| 39 |
| 40 /** |
| 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. |
| 43 * @param {Object} error The error the element should represent. |
| 44 * @constructor |
| 45 * @extends {HTMLDivElement} |
| 46 */ |
| 47 function ExtensionError(error) { |
| 48 var div = document.createElement('div'); |
| 49 div.__proto__ = ExtensionError.prototype; |
| 50 div.className = 'extension-error-simple-wrapper'; |
| 51 div.error_ = error; |
| 52 div.decorate(); |
| 53 return div; |
| 54 } |
| 55 |
| 56 ExtensionError.prototype = { |
| 57 __proto__: HTMLDivElement.prototype, |
| 58 |
| 59 /** @override */ |
| 60 decorate: function() { |
| 61 var metadata = cloneTemplate('extension-error-metadata'); |
| 62 |
| 63 // Add an additional class for the severity level. |
| 64 if (this.error_.level == 0) |
| 65 metadata.className += ' extension-error-severity-info'; |
| 66 else if (this.error_.level == 1) |
| 67 metadata.className += ' extension-error-severity-warning'; |
| 68 else |
| 69 metadata.className += ' extension-error-severity-fatal'; |
| 70 |
| 71 var iconNode = document.createElement('img'); |
| 72 iconNode.className = 'extension-error-icon'; |
| 73 metadata.insertBefore(iconNode, metadata.firstChild); |
| 74 |
| 75 // Add a property for the extension's base url in order to determine if |
| 76 // a url belongs to the extension. |
| 77 this.extensionUrl_ = |
| 78 'chrome-extension://' + this.error_.extensionId + '/'; |
| 79 |
| 80 metadata.querySelector('.extension-error-message').innerText = |
| 81 this.error_.message; |
| 82 |
| 83 metadata.appendChild(this.getViewSourceOrPlain_( |
| 84 getRelativeUrl(this.error_.source, this.extensionUrl_), |
| 85 this.error_.source)); |
| 86 |
| 87 this.appendChild(metadata); |
| 88 }, |
| 89 |
| 90 /** |
| 91 * Return a div with text |description|. If it's possible to view the source |
| 92 * for |url|, linkify the div to do so. |
| 93 * @param {string} description a human-friendly description the location |
| 94 * (e.g., filename, line). |
| 95 * @param {string} url The url of the resource to view. |
| 96 * @return {HTMLElement} The created node, either a link or plaintext. |
| 97 * @private |
| 98 */ |
| 99 getViewSourceOrPlain_: function(description, url) { |
| 100 if (this.canViewSource_(url)) |
| 101 var node = this.getViewSourceLink_(url); |
| 102 else |
| 103 var node = document.createElement('div'); |
| 104 node.className = 'extension-error-view-source'; |
| 105 node.innerText = description; |
| 106 return node; |
| 107 }, |
| 108 |
| 109 /** |
| 110 * Determine whether we can view the source of a given url. |
| 111 * @param {string} url The url of the resource to view. |
| 112 * @return {boolean} Whether or not we can view the source for the url. |
| 113 * @private |
| 114 */ |
| 115 canViewSource_: function(url) { |
| 116 return isExtensionUrl(url, this.extensionUrl_) || url == 'manifest.json'; |
| 117 }, |
| 118 |
| 119 /** |
| 120 * Create a clickable node to view the source for the given url. |
| 121 * @param {string} url The url to the resource to view. |
| 122 * @return {HTMLElement} The clickable node to view the source. |
| 123 * @private |
| 124 */ |
| 125 getViewSourceLink_: function(url) { |
| 126 var node = document.createElement('a'); |
| 127 var relativeUrl = getRelativeUrl(url, this.extensionUrl_); |
| 128 |
| 129 node.addEventListener('click', function(e) { |
| 130 chrome.send('extensionErrorRequestFileSource', |
| 131 [{'extensionId': this.error_.extensionId, |
| 132 'message': this.error_.message, |
| 133 'fileType': 'manifest', |
| 134 'pathSuffix': relativeUrl, |
| 135 'manifestKey': this.error_.manifestKey, |
| 136 'manifestSpecific': this.error_.manifestSpecific}]); |
| 137 }.bind(this)); |
| 138 return node; |
| 139 }, |
| 140 }; |
| 141 |
| 142 /** |
| 143 * A variable length list of runtime or manifest errors for a given extension. |
| 144 * @constructor |
| 145 * @extends {HTMLDivElement} |
| 146 */ |
| 147 function ExtensionErrorList(errors) { |
| 148 var div = cloneTemplate('extension-error-list'); |
| 149 div.__proto__ = ExtensionErrorList.prototype; |
| 150 div.errors_ = errors; |
| 151 div.decorate(); |
| 152 return div; |
| 153 } |
| 154 |
| 155 ExtensionErrorList.prototype = { |
| 156 __proto__: HTMLDivElement.prototype, |
| 157 |
| 158 /** |
| 159 * @private |
| 160 * @const |
| 161 * @type {number} |
| 162 */ |
| 163 MAX_ERRORS_TO_SHOW_: 3, |
| 164 |
| 165 /** @override */ |
| 166 decorate: function() { |
| 167 this.contents_ = this.querySelector('.extension-error-list-contents'); |
| 168 this.errors_.forEach(function(error) { |
| 169 this.contents_.appendChild(document.createElement('li')).appendChild( |
| 170 new ExtensionError(error)); |
| 171 }, this); |
| 172 |
| 173 if (this.contents_.children.length > this.MAX_ERRORS_TO_SHOW_) { |
| 174 for (var i = this.MAX_ERRORS_TO_SHOW_; |
| 175 i < this.contents_.children.length; ++i) { |
| 176 this.contents_.children[i].hidden = true; |
| 177 } |
| 178 this.initShowMoreButton_(); |
| 179 } |
| 180 }, |
| 181 |
| 182 /** |
| 183 * Initialize the "Show More" button for the error list. If there are more |
| 184 * than |MAX_ERRORS_TO_SHOW_| errors in the list. |
| 185 * @private |
| 186 */ |
| 187 initShowMoreButton_: function() { |
| 188 var button = this.querySelector('.extension-error-list-show-more a'); |
| 189 button.hidden = false; |
| 190 button.isShowingAll = false; |
| 191 button.addEventListener('click', function(e) { |
| 192 for (var i = this.MAX_ERRORS_TO_SHOW_; |
| 193 i < this.contents_.children.length; ++i) { |
| 194 this.contents_.children[i].hidden = button.isShowingAll; |
| 195 } |
| 196 var message = button.isShowingAll ? 'extensionErrorsShowMore' : |
| 197 'extensionErrorsShowFewer'; |
| 198 button.innerText = loadTimeData.getString(message); |
| 199 button.isShowingAll = !button.isShowingAll; |
| 200 }.bind(this)); |
| 201 } |
| 202 }; |
| 203 |
| 204 return { |
| 205 ExtensionErrorList: ExtensionErrorList |
| 206 }; |
| 207 }); |
OLD | NEW |