[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / resources / extensions / extension_list.js
blob713ac665f5fa3fe4cd4ce1bfb689ac0c23318374
1 // Copyright (c) 2012 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 <include src="extension_error.js"></include>
7 cr.define('options', function() {
8 'use strict';
10 /**
11 * Creates a new list of extensions.
12 * @param {Object=} opt_propertyBag Optional properties.
13 * @constructor
14 * @extends {cr.ui.div}
16 var ExtensionsList = cr.ui.define('div');
18 /**
19 * @type {Object.<string, boolean>} A map from extension id to a boolean
20 * indicating whether the incognito warning is showing. This persists
21 * between calls to decorate.
23 var butterBarVisibility = {};
25 /**
26 * @type {Object.<string, string>} A map from extension id to last reloaded
27 * timestamp. The timestamp is recorded when the user click the 'Reload'
28 * link. It is used to refresh the icon of an unpacked extension.
29 * This persists between calls to decorate.
31 var extensionReloadedTimestamp = {};
33 ExtensionsList.prototype = {
34 __proto__: HTMLDivElement.prototype,
36 /** @override */
37 decorate: function() {
38 this.textContent = '';
40 this.showExtensionNodes_();
43 getIdQueryParam_: function() {
44 return parseQueryParams(document.location)['id'];
47 /**
48 * Creates all extension items from scratch.
49 * @private
51 showExtensionNodes_: function() {
52 // Iterate over the extension data and add each item to the list.
53 this.data_.extensions.forEach(this.createNode_, this);
55 var idToHighlight = this.getIdQueryParam_();
56 if (idToHighlight && $(idToHighlight)) {
57 // Scroll offset should be calculated slightly higher than the actual
58 // offset of the element being scrolled to, so that it ends up not all
59 // the way at the top. That way it is clear that there are more elements
60 // above the element being scrolled to.
61 var scrollFudge = 1.2;
62 var scrollTop = $(idToHighlight).offsetTop - scrollFudge *
63 $(idToHighlight).clientHeight;
64 setScrollTopForDocument(document, scrollTop);
67 if (this.data_.extensions.length == 0)
68 this.classList.add('empty-extension-list');
69 else
70 this.classList.remove('empty-extension-list');
73 /**
74 * Synthesizes and initializes an HTML element for the extension metadata
75 * given in |extension|.
76 * @param {Object} extension A dictionary of extension metadata.
77 * @private
79 createNode_: function(extension) {
80 var template = $('template-collection').querySelector(
81 '.extension-list-item-wrapper');
82 var node = template.cloneNode(true);
83 node.id = extension.id;
85 if (!extension.enabled || extension.terminated)
86 node.classList.add('inactive-extension');
88 if (extension.managedInstall) {
89 node.classList.add('may-not-modify');
90 node.classList.add('may-not-remove');
91 } else if (extension.suspiciousInstall || extension.corruptInstall) {
92 node.classList.add('may-not-modify');
95 var idToHighlight = this.getIdQueryParam_();
96 if (node.id == idToHighlight)
97 node.classList.add('extension-highlight');
99 var item = node.querySelector('.extension-list-item');
100 // Prevent the image cache of extension icon by using the reloaded
101 // timestamp as a query string. The timestamp is recorded when the user
102 // clicks the 'Reload' link. http://crbug.com/159302.
103 if (extensionReloadedTimestamp[extension.id]) {
104 item.style.backgroundImage =
105 'url(' + extension.icon + '?' +
106 extensionReloadedTimestamp[extension.id] + ')';
107 } else {
108 item.style.backgroundImage = 'url(' + extension.icon + ')';
111 var title = node.querySelector('.extension-title');
112 title.textContent = extension.name;
114 var version = node.querySelector('.extension-version');
115 version.textContent = extension.version;
117 var locationText = node.querySelector('.location-text');
118 locationText.textContent = extension.locationText;
120 var blacklistText = node.querySelector('.blacklist-text');
121 blacklistText.textContent = extension.blacklistText;
123 var description = node.querySelector('.extension-description span');
124 description.textContent = extension.description;
126 // The 'Show Browser Action' button.
127 if (extension.enable_show_button) {
128 var showButton = node.querySelector('.show-button');
129 showButton.addEventListener('click', function(e) {
130 chrome.send('extensionSettingsShowButton', [extension.id]);
132 showButton.hidden = false;
135 // The 'allow in incognito' checkbox.
136 var incognito = node.querySelector('.incognito-control input');
137 incognito.disabled = !extension.incognitoCanBeEnabled;
138 incognito.checked = extension.enabledIncognito;
139 if (!incognito.disabled) {
140 incognito.addEventListener('change', function(e) {
141 var checked = e.target.checked;
142 butterBarVisibility[extension.id] = checked;
143 butterBar.hidden = !checked || extension.is_hosted_app;
144 chrome.send('extensionSettingsEnableIncognito',
145 [extension.id, String(checked)]);
148 var butterBar = node.querySelector('.butter-bar');
149 butterBar.hidden = !butterBarVisibility[extension.id];
151 // The 'collect errors' checkbox. This should only be visible if the
152 // error console is enabled - we can detect this by the existence of the
153 // |errorCollectionEnabled| property.
154 if (extension.wantsErrorCollection) {
155 node.querySelector('.error-collection-control').hidden = false;
156 var errorCollection =
157 node.querySelector('.error-collection-control input');
158 errorCollection.checked = extension.errorCollectionEnabled;
159 errorCollection.addEventListener('change', function(e) {
160 chrome.send('extensionSettingsEnableErrorCollection',
161 [extension.id, String(e.target.checked)]);
165 // The 'allow file:// access' checkbox.
166 if (extension.wantsFileAccess) {
167 var fileAccess = node.querySelector('.file-access-control');
168 fileAccess.addEventListener('click', function(e) {
169 chrome.send('extensionSettingsAllowFileAccess',
170 [extension.id, String(e.target.checked)]);
172 fileAccess.querySelector('input').checked = extension.allowFileAccess;
173 fileAccess.hidden = false;
176 // The 'Options' link.
177 if (extension.enabled && extension.optionsUrl) {
178 var options = node.querySelector('.options-link');
179 options.addEventListener('click', function(e) {
180 chrome.send('extensionSettingsOptions', [extension.id]);
181 e.preventDefault();
183 options.hidden = false;
186 // The 'Permissions' link.
187 var permissions = node.querySelector('.permissions-link');
188 permissions.addEventListener('click', function(e) {
189 chrome.send('extensionSettingsPermissions', [extension.id]);
190 e.preventDefault();
193 // The 'View in Web Store/View Web Site' link.
194 if (extension.homepageUrl) {
195 var siteLink = node.querySelector('.site-link');
196 siteLink.href = extension.homepageUrl;
197 siteLink.textContent = loadTimeData.getString(
198 extension.homepageProvided ? 'extensionSettingsVisitWebsite' :
199 'extensionSettingsVisitWebStore');
200 siteLink.hidden = false;
203 if (extension.allow_reload) {
204 // The 'Reload' link.
205 var reload = node.querySelector('.reload-link');
206 reload.addEventListener('click', function(e) {
207 chrome.send('extensionSettingsReload', [extension.id]);
208 extensionReloadedTimestamp[extension.id] = Date.now();
210 reload.hidden = false;
212 if (extension.is_platform_app) {
213 // The 'Launch' link.
214 var launch = node.querySelector('.launch-link');
215 launch.addEventListener('click', function(e) {
216 chrome.send('extensionSettingsLaunch', [extension.id]);
218 launch.hidden = false;
222 if (!extension.terminated) {
223 // The 'Enabled' checkbox.
224 var enable = node.querySelector('.enable-checkbox');
225 enable.hidden = false;
226 var managedOrHosedExtension = extension.managedInstall ||
227 extension.suspiciousInstall ||
228 extension.corruptInstall;
229 enable.querySelector('input').disabled = managedOrHosedExtension;
231 if (!managedOrHosedExtension) {
232 enable.addEventListener('click', function(e) {
233 // When e.target is the label instead of the checkbox, it doesn't
234 // have the checked property and the state of the checkbox is
235 // left unchanged.
236 var checked = e.target.checked;
237 if (checked == undefined)
238 checked = !e.currentTarget.querySelector('input').checked;
239 chrome.send('extensionSettingsEnable',
240 [extension.id, checked ? 'true' : 'false']);
242 // This may seem counter-intuitive (to not set/clear the checkmark)
243 // but this page will be updated asynchronously if the extension
244 // becomes enabled/disabled. It also might not become enabled or
245 // disabled, because the user might e.g. get prompted when enabling
246 // and choose not to.
247 e.preventDefault();
251 enable.querySelector('input').checked = extension.enabled;
252 } else {
253 var terminatedReload = node.querySelector('.terminated-reload-link');
254 terminatedReload.hidden = false;
255 terminatedReload.addEventListener('click', function(e) {
256 chrome.send('extensionSettingsReload', [extension.id]);
260 // 'Remove' button.
261 var trashTemplate = $('template-collection').querySelector('.trash');
262 var trash = trashTemplate.cloneNode(true);
263 trash.title = loadTimeData.getString('extensionUninstall');
264 trash.addEventListener('click', function(e) {
265 butterBarVisibility[extension.id] = false;
266 chrome.send('extensionSettingsUninstall', [extension.id]);
268 node.querySelector('.enable-controls').appendChild(trash);
270 // Developer mode ////////////////////////////////////////////////////////
272 // First we have the id.
273 var idLabel = node.querySelector('.extension-id');
274 idLabel.textContent = ' ' + extension.id;
276 // Then the path, if provided by unpacked extension.
277 if (extension.isUnpacked) {
278 var loadPath = node.querySelector('.load-path');
279 loadPath.hidden = false;
280 loadPath.querySelector('span:nth-of-type(2)').textContent =
281 ' ' + extension.path;
284 // Then the 'managed, cannot uninstall/disable' message.
285 if (extension.managedInstall) {
286 node.querySelector('.managed-message').hidden = false;
287 } else {
288 if (extension.suspiciousInstall) {
289 // Then the 'This isn't from the webstore, looks suspicious' message.
290 node.querySelector('.suspicious-install-message').hidden = false;
292 if (extension.corruptInstall) {
293 // Then the 'This is a corrupt extension' message.
294 node.querySelector('.corrupt-install-message').hidden = false;
298 // Then active views.
299 if (extension.views.length > 0) {
300 var activeViews = node.querySelector('.active-views');
301 activeViews.hidden = false;
302 var link = activeViews.querySelector('a');
304 extension.views.forEach(function(view, i) {
305 var displayName = view.generatedBackgroundPage ?
306 loadTimeData.getString('backgroundPage') : view.path;
307 var label = displayName +
308 (view.incognito ?
309 ' ' + loadTimeData.getString('viewIncognito') : '') +
310 (view.renderProcessId == -1 ?
311 ' ' + loadTimeData.getString('viewInactive') : '');
312 link.textContent = label;
313 link.addEventListener('click', function(e) {
314 // TODO(estade): remove conversion to string?
315 chrome.send('extensionSettingsInspect', [
316 String(extension.id),
317 String(view.renderProcessId),
318 String(view.renderViewId),
319 view.incognito
323 if (i < extension.views.length - 1) {
324 link = link.cloneNode(true);
325 activeViews.appendChild(link);
330 // The extension warnings (describing runtime issues).
331 if (extension.warnings) {
332 var panel = node.querySelector('.extension-warnings');
333 panel.hidden = false;
334 var list = panel.querySelector('ul');
335 extension.warnings.forEach(function(warning) {
336 list.appendChild(document.createElement('li')).innerText = warning;
340 // If the ErrorConsole is enabled, we should have manifest and/or runtime
341 // errors. Otherwise, we may have install warnings. We should not have
342 // both ErrorConsole errors and install warnings.
343 if (extension.manifestErrors) {
344 var panel = node.querySelector('.manifest-errors');
345 panel.hidden = false;
346 panel.appendChild(new extensions.ExtensionErrorList(
347 extension.manifestErrors));
349 if (extension.runtimeErrors) {
350 var panel = node.querySelector('.runtime-errors');
351 panel.hidden = false;
352 panel.appendChild(new extensions.ExtensionErrorList(
353 extension.runtimeErrors));
355 if (extension.installWarnings) {
356 var panel = node.querySelector('.install-warnings');
357 panel.hidden = false;
358 var list = panel.querySelector('ul');
359 extension.installWarnings.forEach(function(warning) {
360 var li = document.createElement('li');
361 li.innerText = warning.message;
362 list.appendChild(li);
366 this.appendChild(node);
367 if (location.hash.substr(1) == extension.id) {
368 // Scroll beneath the fixed header so that the extension is not
369 // obscured.
370 var topScroll = node.offsetTop - $('page-header').offsetHeight;
371 var pad = parseInt(getComputedStyle(node, null).marginTop, 10);
372 if (!isNaN(pad))
373 topScroll -= pad / 2;
374 setScrollTopForDocument(document, topScroll);
379 return {
380 ExtensionsList: ExtensionsList