Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2515)

Unified Diff: chrome/browser/resources/extensions/extension_list.js

Issue 893453002: Stop rebuilding all elements in chrome://extensions to preserve focus on refresh (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove unused line Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/extensions/extension_list.js
diff --git a/chrome/browser/resources/extensions/extension_list.js b/chrome/browser/resources/extensions/extension_list.js
index f540d332d60fa7c504d0f155b8e2c5b4fc9eede3..de47102ecf5fdf9c352633ff1552ec0c7aa6c578 100644
--- a/chrome/browser/resources/extensions/extension_list.js
+++ b/chrome/browser/resources/extensions/extension_list.js
@@ -77,13 +77,6 @@ cr.define('options', function() {
var ExtensionsList = cr.ui.define('div');
/**
- * @type {Object.<string, boolean>} A map from extension id to a boolean
- * indicating whether the incognito warning is showing. This persists
- * between calls to decorate.
- */
- var butterBarVisibility = {};
-
- /**
* @type {Object.<string, number>} A map from extension id to last reloaded
* timestamp. The timestamp is recorded when the user click the 'Reload'
* link. It is used to refresh the icon of an unpacked extension.
@@ -103,9 +96,13 @@ cr.define('options', function() {
*/
optionsShown_: false,
- decorate: function() {
- this.textContent = '';
+ /**
+ * Necessary to only show the butterbar once.
+ * @private {boolean}
+ */
+ butterbarShown_: false,
+ decorate: function() {
this.showExtensionNodes_();
},
@@ -122,8 +119,27 @@ cr.define('options', function() {
* @private
*/
showExtensionNodes_: function() {
+ // Any node that is not updated will be removed.
+ var seenIds = [];
+
// Iterate over the extension data and add each item to the list.
- this.data_.extensions.forEach(this.createNode_, this);
+ this.data_.extensions.forEach(function(extension) {
+ var node = $(extension.id);
+ seenIds.push(extension.id);
+
+ if (node)
+ this.updateNode_(extension, node);
+ else
+ this.createNode_(extension);
+ }, this);
+
+ // Remove extensions that are no longer installed.
+ var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]');
+ for (var i = 0; i < nodes.length; ++i) {
+ var node = nodes[i];
+ if (seenIds.indexOf(node.id) < 0)
+ node.parentElement.removeChild(node);
+ }
var idToHighlight = this.getIdQueryParam_();
if (idToHighlight && $(idToHighlight))
@@ -165,9 +181,137 @@ cr.define('options', function() {
var node = template.cloneNode(true);
node.id = extension.id;
- if (!extension.enabled || extension.terminated)
- node.classList.add('inactive-extension');
+ // The 'Show Browser Action' button.
+ this.addListener_('click', node, '.show-button', function(e) {
+ chrome.send('extensionSettingsShowButton', [extension.id]);
+ });
+
+ // The 'allow in incognito' checkbox.
+ this.addListener_('change', node, '.incognito-control input',
+ function(e) {
+ var butterBar = node.querySelector('.butter-bar');
+ var checked = e.target.checked;
+ if (!this.butterbarShown_) {
+ butterBar.hidden = !checked || extension.is_hosted_app;
+ this.butterbarShown_ = !butterBar.hidden;
+ } else
+ butterBar.hidden = true;
+ chrome.send('extensionSettingsEnableIncognito',
+ [extension.id, String(checked)]);
+ }.bind(this));
+
+ // The 'collect errors' checkbox. This should only be visible if the
+ // error console is enabled - we can detect this by the existence of the
+ // |errorCollectionEnabled| property.
+ this.addListener_('change', node, '.error-collection-control input',
+ function(e) {
+ chrome.send('extensionSettingsEnableErrorCollection',
+ [extension.id, String(e.target.checked)]);
+ });
+
+ // The 'allow on all urls' checkbox. This should only be visible if
+ // active script restrictions are enabled. If they are not enabled, no
+ // extensions should want all urls.
+ this.addListener_('click', node, '.all-urls-control', function(e) {
+ chrome.send('extensionSettingsAllowOnAllUrls',
+ [extension.id, String(e.target.checked)]);
+ });
+
+ // The 'allow file:// access' checkbox.
+ this.addListener_('click', node, '.file-access-control', function(e) {
+ chrome.send('extensionSettingsAllowFileAccess',
+ [extension.id, String(e.target.checked)]);
+ });
+
+ // The 'Options' button or link, depending on its behaviour.
+ // Set an href to get the correct mouse-over appearance (link,
+ // footer) - but the actual link opening is done through chrome.send
+ // with a preventDefault().
+ node.querySelector('.options-link').href = extension.optionsPageHref;
+ this.addListener_('click', node, '.options-link', function(e) {
+ chrome.send('extensionSettingsOptions', [extension.id]);
+ e.preventDefault();
+ });
+
+ this.addListener_('click', node, '.options-button', function(e) {
+ this.showEmbeddedExtensionOptions_(extension.id, false);
+ e.preventDefault();
+ }.bind(this));
+
+ // The 'Permissions' link.
+ this.addListener_('click', node, '.permissions-link', function(e) {
+ chrome.send('extensionSettingsPermissions', [extension.id]);
+ e.preventDefault();
+ });
+
+ // The 'Reload' link.
+ this.addListener_('click', node, '.reload-link', function(e) {
+ chrome.send('extensionSettingsReload', [extension.id]);
+ extensionReloadedTimestamp[extension.id] = Date.now();
+ });
+
+ // The 'Launch' link.
+ this.addListener_('click', node, '.launch-link', function(e) {
+ chrome.send('extensionSettingsLaunch', [extension.id]);
+ });
+
+ // The 'Reload' terminated link.
+ this.addListener_('click', node, '.terminated-reload-link', function(e) {
+ chrome.send('extensionSettingsReload', [extension.id]);
+ });
+
+ // The 'Repair' corrupted link.
+ this.addListener_('click', node, '.corrupted-repair-button', function(e) {
+ chrome.send('extensionSettingsRepair', [extension.id]);
+ });
+
+ // The 'Enabled' checkbox.
+ this.addListener_('change', node, '.enable-checkbox input', function(e) {
+ var checked = e.target.checked;
+ chrome.send('extensionSettingsEnable', [extension.id, String(checked)]);
+
+ // This may seem counter-intuitive (to not set/clear the checkmark)
+ // but this page will be updated asynchronously if the extension
+ // becomes enabled/disabled. It also might not become enabled or
+ // disabled, because the user might e.g. get prompted when enabling
+ // and choose not to.
+ e.preventDefault();
+ });
+
+ // 'Remove' button.
+ var trashTemplate = $('template-collection').querySelector('.trash');
+ var trash = trashTemplate.cloneNode(true);
+ trash.title = loadTimeData.getString('extensionUninstall');
+ trash.addEventListener('click', function(e) {
+ chrome.send('extensionSettingsUninstall', [extension.id]);
+ });
+ node.querySelector('.enable-controls').appendChild(trash);
+
+ // Developer mode ////////////////////////////////////////////////////////
+
+ // The path, if provided by unpacked extension.
+ this.addListener_('click', node, '.load-path a:first-of-type',
+ function(e) {
+ chrome.send('extensionSettingsShowPath', [String(extension.id)]);
+ e.preventDefault();
+ });
+
+ this.appendChild(node);
+ this.updateNode_(extension, node);
+ },
+
+ /**
+ * Updates an HTML element for the extension metadata given in |extension|.
+ * @param {ExtensionData} extension A dictionary of extension metadata.
+ * @param {Element} node The node that is being updated.
+ * @private
+ */
+ updateNode_: function(extension, node) {
+ node.classList.toggle('inactive-extension',
+ !extension.enabled || extension.terminated);
+ node.classList.remove('policy-controlled', 'may-not-modify',
+ 'may-not-remove');
var classes = [];
if (extension.managedInstall) {
classes.push('policy-controlled', 'may-not-modify');
@@ -182,9 +326,8 @@ cr.define('options', function() {
}
node.classList.add.apply(node.classList, classes);
- var idToHighlight = this.getIdQueryParam_();
- if (node.id == idToHighlight)
- node.classList.add('extension-highlight');
+ node.classList.toggle('extension-highlight',
+ node.id == this.getIdQueryParam_());
var item = node.querySelector('.extension-list-item');
// Prevent the image cache of extension icon by using the reloaded
@@ -198,214 +341,115 @@ cr.define('options', function() {
item.style.backgroundImage = 'url(' + extension.icon + ')';
}
- var title = node.querySelector('.extension-title');
- title.textContent = extension.name;
-
- var version = node.querySelector('.extension-version');
- version.textContent = extension.version;
-
- var locationText = node.querySelector('.location-text');
- locationText.textContent = extension.locationText;
-
- var blacklistText = node.querySelector('.blacklist-text');
- blacklistText.textContent = extension.blacklistText;
-
- var description = document.createElement('span');
- description.textContent = extension.description;
- node.querySelector('.extension-description').appendChild(description);
+ this.setText_(node, '.extension-title', extension.name);
+ this.setText_(node, '.extension-version', extension.version);
+ this.setText_(node, '.location-text', extension.locationText);
+ this.setText_(node, '.blacklist-text', extension.blacklistText);
+ this.setText_(node, '.extension-description',
+ extension.description);
// The 'Show Browser Action' button.
- if (extension.enable_show_button) {
- var showButton = node.querySelector('.show-button');
- showButton.addEventListener('click', function(e) {
- chrome.send('extensionSettingsShowButton', [extension.id]);
- });
- showButton.hidden = false;
- }
+ this.updateVisibility_(node, '.show-button',
+ extension.enable_show_button);
// The 'allow in incognito' checkbox.
- node.querySelector('.incognito-control').hidden =
- !this.data_.incognitoAvailable;
- var incognito = node.querySelector('.incognito-control input');
- incognito.disabled = !extension.incognitoCanBeEnabled;
- incognito.checked = extension.enabledIncognito;
- if (!incognito.disabled) {
- incognito.addEventListener('change', function(e) {
- var checked = e.target.checked;
- butterBarVisibility[extension.id] = checked;
- butterBar.hidden = !checked || extension.is_hosted_app;
- chrome.send('extensionSettingsEnableIncognito',
- [extension.id, String(checked)]);
- });
- }
+ this.updateVisibility_(node, '.incognito-control',
+ this.data_.incognitoAvailable, function(item) {
+ var incognito = item.querySelector('input');
+ incognito.disabled = !extension.incognitoCanBeEnabled;
+ incognito.checked = extension.enabledIncognito;
+ });
+
+ // Hide butterBar if incognito is not enabled for the extension.
var butterBar = node.querySelector('.butter-bar');
- butterBar.hidden = !butterBarVisibility[extension.id];
+ butterBar.hidden = butterBar.hidden || !extension.enabledIncognito;
// The 'collect errors' checkbox. This should only be visible if the
// error console is enabled - we can detect this by the existence of the
// |errorCollectionEnabled| property.
- if (extension.wantsErrorCollection) {
- node.querySelector('.error-collection-control').hidden = false;
- var errorCollection =
- node.querySelector('.error-collection-control input');
- errorCollection.checked = extension.errorCollectionEnabled;
- errorCollection.addEventListener('change', function(e) {
- chrome.send('extensionSettingsEnableErrorCollection',
- [extension.id, String(e.target.checked)]);
- });
- }
+ this.updateVisibility_(node, '.error-collection-control',
+ extension.wantsErrorCollection, function(item) {
+ item.querySelector('input').checked = extension.errorCollectionEnabled;
+ });
// The 'allow on all urls' checkbox. This should only be visible if
// active script restrictions are enabled. If they are not enabled, no
// extensions should want all urls.
- if (extension.showAllUrls) {
- var allUrls = node.querySelector('.all-urls-control');
- allUrls.addEventListener('click', function(e) {
- chrome.send('extensionSettingsAllowOnAllUrls',
- [extension.id, String(e.target.checked)]);
- });
- allUrls.querySelector('input').checked = extension.allowAllUrls;
- allUrls.hidden = false;
- }
+ this.updateVisibility_(node, '.all-urls-control', extension.showAllUrls,
+ function(item) {
+ item.querySelector('input').checked = extension.allowAllUrls;
+ });
// The 'allow file:// access' checkbox.
- if (extension.wantsFileAccess) {
- var fileAccess = node.querySelector('.file-access-control');
- fileAccess.addEventListener('click', function(e) {
- chrome.send('extensionSettingsAllowFileAccess',
- [extension.id, String(e.target.checked)]);
- });
- fileAccess.querySelector('input').checked = extension.allowFileAccess;
- fileAccess.hidden = false;
- }
+ this.updateVisibility_(node, '.file-access-control',
+ extension.wantsFileAccess, function(item) {
+ item.querySelector('input').checked = extension.allowFileAccess;
+ });
// The 'Options' button or link, depending on its behaviour.
- if (extension.enabled && extension.optionsUrl) {
- var options, optionsClickListener;
- if (extension.optionsOpenInTab) {
- options = node.querySelector('.options-link');
- // Set an href to get the correct mouse-over appearance (link,
- // footer) - but the actual link opening is done through chrome.send
- // with a preventDefault().
- options.setAttribute('href', extension.optionsPageHref);
- optionsClickListener = function() {
- chrome.send('extensionSettingsOptions', [extension.id]);
- };
- } else {
- options = node.querySelector('.options-button');
- optionsClickListener = function() {
- this.showEmbeddedExtensionOptions_(extension.id, false);
- }.bind(this);
- }
- options.addEventListener('click', function(e) {
- optionsClickListener();
- e.preventDefault();
- });
- options.hidden = false;
- }
+ var optionsEnabled = extension.enabled && !!extension.optionsUrl;
+ this.updateVisibility_(node, '.options-link', optionsEnabled &&
+ extension.optionsOpenInTab);
+ this.updateVisibility_(node, '.options-button', optionsEnabled &&
+ !extension.optionsOpenInTab);
- // The 'Permissions' link.
- var permissions = node.querySelector('.permissions-link');
- permissions.addEventListener('click', function(e) {
- chrome.send('extensionSettingsPermissions', [extension.id]);
- e.preventDefault();
+ // The 'View in Web Store/View Web Site' link.
+ var siteLinkEnabled = !!extension.homepageUrl &&
+ !extension.enableExtensionInfoDialog;
+ this.updateVisibility_(node, '.site-link', siteLinkEnabled,
+ function(item) {
+ item.href = extension.homepageUrl;
+ item.textContent = loadTimeData.getString(
+ extension.homepageProvided ? 'extensionSettingsVisitWebsite' :
+ 'extensionSettingsVisitWebStore');
});
- // The 'View in Web Store/View Web Site' link.
- if (extension.homepageUrl && !extension.enableExtensionInfoDialog) {
- var siteLink = node.querySelector('.site-link');
- siteLink.href = extension.homepageUrl;
- siteLink.textContent = loadTimeData.getString(
- extension.homepageProvided ? 'extensionSettingsVisitWebsite' :
- 'extensionSettingsVisitWebStore');
- siteLink.hidden = false;
- }
+ // The 'Reload' link.
+ this.updateVisibility_(node, '.reload-link', extension.allow_reload);
- if (extension.allow_reload) {
- // The 'Reload' link.
- var reload = node.querySelector('.reload-link');
- reload.addEventListener('click', function(e) {
- chrome.send('extensionSettingsReload', [extension.id]);
- extensionReloadedTimestamp[extension.id] = Date.now();
- });
- reload.hidden = false;
+ // The 'Launch' link.
+ this.updateVisibility_(node, '.launch-link', extension.allow_reload &&
+ extension.is_platform_app);
- if (extension.is_platform_app) {
- // The 'Launch' link.
- var launch = node.querySelector('.launch-link');
- launch.addEventListener('click', function(e) {
- chrome.send('extensionSettingsLaunch', [extension.id]);
- });
- launch.hidden = false;
- }
- }
+ // The 'Reload' terminated link.
+ var isTerminated = extension.terminated;
+ this.updateVisibility_(node, '.terminated-reload-link', isTerminated);
- if (extension.terminated) {
- var terminatedReload = node.querySelector('.terminated-reload-link');
- terminatedReload.hidden = false;
- terminatedReload.onclick = function() {
- chrome.send('extensionSettingsReload', [extension.id]);
- };
- } else if (extension.corruptInstall && extension.isFromStore) {
- var repair = node.querySelector('.corrupted-repair-button');
- repair.hidden = false;
- repair.onclick = function() {
- chrome.send('extensionSettingsRepair', [extension.id]);
- };
- } else {
- // The 'Enabled' checkbox.
- var enable = node.querySelector('.enable-checkbox');
- enable.hidden = false;
+ // The 'Repair' corrupted link.
+ var canRepair = !isTerminated && extension.corruptInstall &&
+ extension.isFromStore;
+ this.updateVisibility_(node, '.corrupted-repair-button', canRepair);
+
+ // The 'Enabled' checkbox.
+ var isOK = !isTerminated && !canRepair;
+ this.updateVisibility_(node, '.enable-checkbox', isOK, function(item) {
var enableCheckboxDisabled = extension.managedInstall ||
extension.suspiciousInstall ||
extension.corruptInstall ||
extension.updateRequiredByPolicy ||
extension.dependentExtensions.length > 0;
- enable.querySelector('input').disabled = enableCheckboxDisabled;
-
- if (extension.managedInstall) {
- var indicator = new cr.ui.ControlledIndicator();
- indicator.classList.add('controlled-extension-indicator');
- indicator.setAttribute('controlled-by', 'policy');
- var textPolicy = extension.policyText || '';
- indicator.setAttribute('textpolicy', textPolicy);
- indicator.image.setAttribute('aria-label', textPolicy);
- node.querySelector('.enable-controls').appendChild(indicator);
- }
-
- if (!enableCheckboxDisabled) {
- enable.addEventListener('click', function(e) {
- // When e.target is the label instead of the checkbox, it doesn't
- // have the checked property and the state of the checkbox is
- // left unchanged.
- var checked = e.target.checked;
- if (checked == undefined)
- checked = !e.currentTarget.querySelector('input').checked;
- chrome.send('extensionSettingsEnable',
- [extension.id, checked ? 'true' : 'false']);
-
- // This may seem counter-intuitive (to not set/clear the checkmark)
- // but this page will be updated asynchronously if the extension
- // becomes enabled/disabled. It also might not become enabled or
- // disabled, because the user might e.g. get prompted when enabling
- // and choose not to.
- e.preventDefault();
- });
- }
+ item.querySelector('input').disabled = enableCheckboxDisabled;
+ item.querySelector('input').checked = extension.enabled;
+ });
- enable.querySelector('input').checked = extension.enabled;
+ // Button for extensions controlled by policy.
+ var controlNode = node.querySelector('.enable-controls');
+ var indicator =
+ controlNode.querySelector('.controlled-extension-indicator');
+ var needsIndicator = isOK && extension.managedInstall;
+
+ if (needsIndicator && !indicator) {
+ indicator = new cr.ui.ControlledIndicator();
+ indicator.classList.add('controlled-extension-indicator');
+ indicator.setAttribute('controlled-by', 'policy');
+ var textPolicy = extension.policyText || '';
+ indicator.setAttribute('textpolicy', textPolicy);
+ indicator.image.setAttribute('aria-label', textPolicy);
+ controlNode.appendChild(indicator);
+ } else if (!needsIndicator && indicator) {
+ controlNode.removeChild(indicator);
}
- // 'Remove' button.
- var trashTemplate = $('template-collection').querySelector('.trash');
- var trash = trashTemplate.cloneNode(true);
- trash.title = loadTimeData.getString('extensionUninstall');
- trash.addEventListener('click', function(e) {
- butterBarVisibility[extension.id] = false;
- chrome.send('extensionSettingsUninstall', [extension.id]);
- });
- node.querySelector('.enable-controls').appendChild(trash);
-
// Developer mode ////////////////////////////////////////////////////////
// First we have the id.
@@ -413,47 +457,40 @@ cr.define('options', function() {
idLabel.textContent = ' ' + extension.id;
// Then the path, if provided by unpacked extension.
- if (extension.isUnpacked) {
- var loadPath = node.querySelector('.load-path');
- loadPath.hidden = false;
- var pathLink = loadPath.querySelector('a:nth-of-type(1)');
- pathLink.textContent = ' ' + extension.prettifiedPath;
- pathLink.addEventListener('click', function(e) {
- chrome.send('extensionSettingsShowPath', [String(extension.id)]);
- e.preventDefault();
- });
- }
+ this.updateVisibility_(node, '.load-path', extension.isUnpacked,
+ function(item) {
+ item.querySelector('a:first-of-type').textContent =
+ ' ' + extension.prettifiedPath;
+ });
// Then the 'managed, cannot uninstall/disable' message.
- if (extension.managedInstall || extension.recommendedInstall) {
- node.querySelector('.managed-message').hidden = false;
- } else {
- if (extension.suspiciousInstall) {
- // Then the 'This isn't from the webstore, looks suspicious' message.
- node.querySelector('.suspicious-install-message').hidden = false;
- }
- if (extension.corruptInstall) {
- // Then the 'This is a corrupt extension' message.
- node.querySelector('.corrupt-install-message').hidden = false;
- }
- }
+ // We would like to hide managed installed message since this
+ // extension is disabled.
+ var isRequired = extension.managedInstall || extension.recommendedInstall;
+ this.updateVisibility_(node, '.managed-message', isRequired &&
+ !extension.updateRequiredByPolicy);
+
+ // Then the 'This isn't from the webstore, looks suspicious' message.
+ this.updateVisibility_(node, '.suspicious-install-message', !isRequired &&
+ extension.suspiciousInstall);
+
+ // Then the 'This is a corrupt extension' message.
+ this.updateVisibility_(node, '.corrupt-install-message', !isRequired &&
+ extension.corruptInstall);
// Then the 'An update required by enterprise policy' message. Note that
// a force-installed extension might be disabled due to being outdated
// as well.
- if (extension.updateRequiredByPolicy) {
- node.querySelector('.update-required-message').hidden = false;
- // We would like to hide managed installed message since this
- // extension is disabled.
- node.querySelector('.managed-message').hidden = true;
- }
-
- if (extension.dependentExtensions.length > 0) {
- node.classList.add('developer-extras');
- var dependentMessage =
- node.querySelector('.dependent-extensions-message');
- dependentMessage.hidden = false;
- var dependentList = dependentMessage.querySelector('ul');
+ this.updateVisibility_(node, '.update-required-message',
+ extension.updateRequiredByPolicy);
+
+ // The 'following extensions depend on this extension' list.
+ var hasDependents = extension.dependentExtensions.length > 0;
+ node.classList.toggle('developer-extras', hasDependents);
+ this.updateVisibility_(node, '.dependent-extensions-message',
+ hasDependents, function(item) {
+ var dependentList = item.querySelector('ul');
+ dependentList.textContent = '';
var dependentTemplate = $('template-collection').querySelector(
'.dependent-list-item');
extension.dependentExtensions.forEach(function(elem) {
@@ -462,13 +499,21 @@ cr.define('options', function() {
depNode.querySelector('.dep-extension-id').textContent = elem.id;
dependentList.appendChild(depNode);
});
- }
+ });
+
+ // The active views.
+ this.updateVisibility_(node, '.active-views', extension.views.length > 0,
+ function(item) {
+ var link = item.querySelector('a');
+
+ // Link needs to be an only child before the list is updated.
+ while (link.nextElementSibling)
+ item.removeChild(link.nextElementSibling);
- // Then active views.
- if (extension.views.length > 0) {
- var activeViews = node.querySelector('.active-views');
- activeViews.hidden = false;
- var link = activeViews.querySelector('a');
+ // Link needs to be cleaned up if it was used before.
+ link.textContent = '';
+ if (link.clickHandler)
+ link.removeEventListener('click', link.clickHandler);
extension.views.forEach(function(view, i) {
var displayName = view.generatedBackgroundPage ?
@@ -479,7 +524,7 @@ cr.define('options', function() {
(view.renderProcessId == -1 ?
' ' + loadTimeData.getString('viewInactive') : '');
link.textContent = label;
- link.addEventListener('click', function(e) {
+ link.clickHandler = function(e) {
// TODO(estade): remove conversion to string?
chrome.send('extensionSettingsInspect', [
String(extension.id),
@@ -487,52 +532,50 @@ cr.define('options', function() {
String(view.renderViewId),
view.incognito
]);
- });
+ };
+ link.addEventListener('click', link.clickHandler);
if (i < extension.views.length - 1) {
link = link.cloneNode(true);
- activeViews.appendChild(link);
+ item.appendChild(link);
}
});
- }
+ });
// The extension warnings (describing runtime issues).
- if (extension.warnings) {
- var panel = node.querySelector('.extension-warnings');
- panel.hidden = false;
- var list = panel.querySelector('ul');
+ this.updateVisibility_(node, '.extension-warnings', !!extension.warnings,
+ function(item) {
+ var warningList = item.querySelector('ul');
+ warningList.textContent = '';
extension.warnings.forEach(function(warning) {
- list.appendChild(document.createElement('li')).innerText = warning;
+ var li = document.createElement('li');
+ warningList.appendChild(li).innerText = warning;
});
- }
+ });
// If the ErrorConsole is enabled, we should have manifest and/or runtime
// errors. Otherwise, we may have install warnings. We should not have
// both ErrorConsole errors and install warnings.
- if (extension.manifestErrors) {
- var panel = node.querySelector('.manifest-errors');
- panel.hidden = false;
- panel.appendChild(new extensions.ExtensionErrorList(
- extension.manifestErrors));
- }
- if (extension.runtimeErrors) {
- var panel = node.querySelector('.runtime-errors');
- panel.hidden = false;
- panel.appendChild(new extensions.ExtensionErrorList(
- extension.runtimeErrors));
- }
- if (extension.installWarnings) {
- var panel = node.querySelector('.install-warnings');
- panel.hidden = false;
- var list = panel.querySelector('ul');
- extension.installWarnings.forEach(function(warning) {
- var li = document.createElement('li');
- li.innerText = warning.message;
- list.appendChild(li);
- });
- }
+ // Errors.
+ this.updateErrors_(node.querySelector('.manifest-errors'),
+ extension.manifestErrors);
+ this.updateErrors_(node.querySelector('.runtime-errors'),
+ extension.runtimeErrors);
+
+ // Install warnings.
+ this.updateVisibility_(node, '.install-warnings',
+ !!extension.installWarnings, function(item) {
+ var installWarningList = item.querySelector('ul');
+ installWarningList.textContent = '';
+ if (extension.installWarnings) {
+ extension.installWarnings.forEach(function(warning) {
+ var li = document.createElement('li');
+ li.innerText = warning.message;
+ installWarningList.appendChild(li);
+ });
+ }
+ });
- this.appendChild(node);
if (location.hash.substr(1) == extension.id) {
// Scroll beneath the fixed header so that the extension is not
// obscured.
@@ -545,6 +588,65 @@ cr.define('options', function() {
},
/**
+ * Adds a listener to an element.
+ * @param {string} type The type of listener to add.
+ * @param {Element} node Ancestor of the element specified by |query|.
+ * @param {string} query A query to select an element in |node|.
+ * @param {function(Event)} handler The function that should be called
+ * on click.
+ * @private
+ */
+ addListener_: function(type, node, query, handler) {
+ node.querySelector(query).addEventListener(type, handler);
+ },
+
+ /**
+ * Updates an element's textContent.
+ * @param {Element} node Ancestor of the element specified by |query|.
+ * @param {string} query A query to select an element in |node|.
+ * @param {string} textContent
+ * @private
+ */
+ setText_: function(node, query, textContent) {
+ node.querySelector(query).textContent = textContent;
+ },
+
+ /**
+ * Updates an element's visibility and calls |shownCallback| if it is
+ * visible.
+ * @param {Element} node Ancestor of the element specified by |query|.
+ * @param {string} query A query to select an element in |node|.
+ * @param {boolean} visible Whether the element should be visible or not.
+ * @param {function(Element)=} opt_shownCallback Callback if the element is
+ * visible. The element passed in will be the element specified by
+ * |query|.
+ * @private
+ */
+ updateVisibility_: function(node, query, visible, opt_shownCallback) {
+ var item = assert(node.querySelector(query));
+ item.hidden = !visible;
+ if (visible && opt_shownCallback)
+ opt_shownCallback(item);
+ },
+
+ /**
+ * Updates an element to show a list of errors.
+ * @param {Element} panel An element to hold the errors.
+ * @param {Array<RuntimeError>|undefined} errors The errors to be displayed.
+ * @private
+ */
+ updateErrors_: function(panel, errors) {
+ // TODO(hcarmona): Look into updating the ExtensionErrorList rather than
+ // rebuilding it every time.
+ panel.hidden = !errors || errors.length == 0;
+ panel.textContent = '';
+ if (!panel.hidden) {
+ panel.appendChild(
+ new extensions.ExtensionErrorList(assertInstanceof(errors, Array)));
+ }
+ },
+
+ /**
* Opens the extension options overlay for the extension with the given id.
* @param {string} extensionId The id of extension whose options page should
* be displayed.
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698