Give names to all utility processes.
[chromium-blink-merge.git] / chrome / browser / resources / extensions / extension_error.js
blob5d76630bd73ebd061649d3097e3cfb819547520c
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.
5 cr.define('extensions', function() {
6   'use strict';
8   /**
9    * Clone a template within the extension error template collection.
10    * @param {string} templateName The class name of the template to clone.
11    * @return {HTMLElement} The clone of the template.
12    */
13   function cloneTemplate(templateName) {
14     return /** @type {HTMLElement} */($('template-collection-extension-error').
15         querySelector('.' + templateName).cloneNode(true));
16   }
18   /**
19    * Checks that an Extension ID follows the proper format (i.e., is 32
20    * characters long, is lowercase, and contains letters in the range [a, p]).
21    * @param {string} id The Extension ID to test.
22    * @return {boolean} Whether or not the ID is valid.
23    */
24   function idIsValid(id) {
25     return /^[a-p]{32}$/.test(id);
26   }
28   /**
29    * Creates a new ExtensionError HTMLElement; this is used to show a
30    * notification to the user when an error is caused by an extension.
31    * @param {(RuntimeError|ManifestError)} error The error the element should
32    *     represent.
33    * @param {Element} boundary The boundary for the focus grid.
34    * @constructor
35    * @extends {cr.ui.FocusRow}
36    */
37   function ExtensionError(error, boundary) {
38     var div = cloneTemplate('extension-error-metadata');
39     div.__proto__ = ExtensionError.prototype;
40     div.decorate(error, boundary);
41     return div;
42   }
44   ExtensionError.prototype = {
45     __proto__: cr.ui.FocusRow.prototype,
47     /** @override */
48     getEquivalentElement: function(element) {
49       return assert(this.querySelector('.extension-error-view-details'));
50     },
52     /**
53      * @param {(RuntimeError|ManifestError)} error The error the element should
54      *     represent
55      * @param {Element} boundary The boundary for the FocusGrid.
56      * @override
57      */
58     decorate: function(error, boundary) {
59       cr.ui.FocusRow.prototype.decorate.call(this, boundary);
61       // Add an additional class for the severity level.
62       if (error.type == chrome.developerPrivate.ErrorType.RUNTIME) {
63         switch (error.severity) {
64           case chrome.developerPrivate.ErrorLevel.LOG:
65             this.classList.add('extension-error-severity-info');
66             break;
67           case chrome.developerPrivate.ErrorLevel.WARN:
68             this.classList.add('extension-error-severity-warning');
69             break;
70           case chrome.developerPrivate.ErrorLevel.ERROR:
71             this.classList.add('extension-error-severity-fatal');
72             break;
73           default:
74             assertNotReached();
75         }
76       } else {
77         // We classify manifest errors as "warnings".
78         this.classList.add('extension-error-severity-warning');
79       }
81       var iconNode = document.createElement('img');
82       iconNode.className = 'extension-error-icon';
83       // TODO(hcarmona): Populate alt text with a proper description since this
84       // icon conveys the severity of the error. (info, warning, fatal).
85       iconNode.alt = '';
86       this.insertBefore(iconNode, this.firstChild);
88       var messageSpan = this.querySelector('.extension-error-message');
89       messageSpan.textContent = error.message;
90       messageSpan.title = error.message;
92       var extensionUrl = 'chrome-extension://' + error.extensionId + '/';
93       var viewDetailsLink = this.querySelector('.extension-error-view-details');
95       // If we cannot open the file source and there are no external frames in
96       // the stack, then there are no details to display.
97       if (!extensions.ExtensionErrorOverlay.canShowOverlayForError(
98               error, extensionUrl)) {
99         viewDetailsLink.hidden = true;
100       } else {
101         var stringId = extensionUrl.toLowerCase() == 'manifest.json' ?
102             'extensionErrorViewManifest' : 'extensionErrorViewDetails';
103         viewDetailsLink.textContent = loadTimeData.getString(stringId);
105         viewDetailsLink.addEventListener('click', function(e) {
106           extensions.ExtensionErrorOverlay.getInstance().setErrorAndShowOverlay(
107               error, extensionUrl);
108         });
110         this.addFocusableElement(viewDetailsLink);
111       }
112     },
113   };
115   /**
116    * A variable length list of runtime or manifest errors for a given extension.
117    * @param {Array<(RuntimeError|ManifestError)>} errors The list of extension
118    *     errors with which to populate the list.
119    * @constructor
120    * @extends {HTMLDivElement}
121    */
122   function ExtensionErrorList(errors) {
123     var div = cloneTemplate('extension-error-list');
124     div.__proto__ = ExtensionErrorList.prototype;
125     div.errors_ = errors;
126     div.decorate();
127     return div;
128   }
130   /**
131    * @private
132    * @const
133    * @type {number}
134    */
135   ExtensionErrorList.MAX_ERRORS_TO_SHOW_ = 3;
137   ExtensionErrorList.prototype = {
138     __proto__: HTMLDivElement.prototype,
140     decorate: function() {
141       this.focusGrid_ = new cr.ui.FocusGrid();
142       this.gridBoundary_ = this.querySelector('.extension-error-list-contents');
143       this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this));
144       this.gridBoundary_.addEventListener('focusin',
145                                           this.onFocusin_.bind(this));
146       this.errors_.forEach(function(error) {
147         if (idIsValid(error.extensionId)) {
148           var focusRow = new ExtensionError(error, this.gridBoundary_);
149           this.gridBoundary_.appendChild(
150               document.createElement('li')).appendChild(focusRow);
151           this.focusGrid_.addRow(focusRow);
152         }
153       }, this);
155       var numShowing = this.focusGrid_.rows.length;
156       if (numShowing > ExtensionErrorList.MAX_ERRORS_TO_SHOW_)
157         this.initShowMoreLink_();
158     },
160     /**
161      * @return {?Element} The element that toggles between show more and show
162      *     less, or null if it's hidden. Button will be hidden if there are less
163      *     errors than |MAX_ERRORS_TO_SHOW_|.
164      */
165     getToggleElement: function() {
166       return this.querySelector(
167           '.extension-error-list-show-more [is="action-link"]:not([hidden])');
168     },
170     /** @return {!Element} The element containing the list of errors. */
171     getErrorListElement: function() {
172       return this.gridBoundary_;
173     },
175     /**
176      * The grid should not be focusable once it or an element inside it is
177      * focused. This is necessary to allow tabbing out of the grid in reverse.
178      * @private
179      */
180     onFocusin_: function() {
181       this.gridBoundary_.tabIndex = -1;
182     },
184     /**
185      * Focus the first focusable row when tabbing into the grid for the
186      * first time.
187      * @private
188      */
189     onFocus_: function() {
190       var activeRow = this.gridBoundary_.querySelector('.focus-row-active');
191       var toggleButton = this.getToggleElement();
193       if (toggleButton && !toggleButton.isShowingAll) {
194         var rows = this.focusGrid_.rows;
195         assert(rows.length > ExtensionErrorList.MAX_ERRORS_TO_SHOW_);
197         var firstVisible = rows.length - ExtensionErrorList.MAX_ERRORS_TO_SHOW_;
198         if (rows.indexOf(activeRow) < firstVisible)
199           activeRow = rows[firstVisible];
200       } else if (!activeRow) {
201         activeRow = this.focusGrid_.rows[0];
202       }
204       activeRow.getEquivalentElement(null).focus();
205     },
207     /**
208      * Initialize the "Show More" link for the error list. If there are more
209      * than |MAX_ERRORS_TO_SHOW_| errors in the list.
210      * @private
211      */
212     initShowMoreLink_: function() {
213       var link = this.querySelector(
214           '.extension-error-list-show-more [is="action-link"]');
215       link.hidden = false;
216       link.isShowingAll = false;
218       var listContents = this.querySelector('.extension-error-list-contents');
220       // TODO(dbeam/kalman): trade all this transition voodoo for .animate()?
221       listContents.addEventListener('webkitTransitionEnd', function(e) {
222         if (listContents.classList.contains('deactivating'))
223           listContents.classList.remove('deactivating', 'active');
224         else
225           listContents.classList.add('scrollable');
226       });
228       link.addEventListener('click', function(e) {
229         // Needs to be enabled in case the focused row is now hidden.
230         this.gridBoundary_.tabIndex = 0;
232         link.isShowingAll = !link.isShowingAll;
234         var message = link.isShowingAll ? 'extensionErrorsShowFewer' :
235                                           'extensionErrorsShowMore';
236         link.textContent = loadTimeData.getString(message);
238         // Disable scrolling while transitioning. If the element is active,
239         // scrolling is enabled when the transition ends.
240         listContents.classList.remove('scrollable');
242         if (link.isShowingAll) {
243           listContents.classList.add('active');
244           listContents.classList.remove('deactivating');
245         } else {
246           listContents.classList.add('deactivating');
247         }
248       }.bind(this));
249     }
250   };
252   return {
253     ExtensionErrorList: ExtensionErrorList
254   };