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

Unified Diff: chrome/browser/resources/extensions/update_node.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: DO NOT COMMIT - Applied feedback 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
Index: chrome/browser/resources/extensions/update_node.js
diff --git a/chrome/browser/resources/extensions/update_node.js b/chrome/browser/resources/extensions/update_node.js
new file mode 100644
index 0000000000000000000000000000000000000000..55ed20f512aa0353ba6323ce4f57ed6bf575bdc9
--- /dev/null
+++ b/chrome/browser/resources/extensions/update_node.js
@@ -0,0 +1,307 @@
+// TODO(hcarmona): Remove this temporary file.
+
+ /**
+ * 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);
+
+ var toRemove = ['policy-controlled', 'may-not-modify', 'may-not-remove'];
+ node.classList.remove.apply(node.classList, toRemove);
+ var classes = [];
+ if (extension.managedInstall) {
+ classes.push('policy-controlled', 'may-not-modify');
+ } else if (extension.dependentExtensions.length > 0) {
+ classes.push('may-not-remove', 'may-not-modify');
+ } else if (extension.recommendedInstall) {
+ classes.push('may-not-remove');
+ } else if (extension.suspiciousInstall ||
+ extension.corruptInstall ||
+ extension.updateRequiredByPolicy) {
+ classes.push('may-not-modify');
+ }
+ node.classList.add.apply(node.classList, classes);
+
+ 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
+ // timestamp as a query string. The timestamp is recorded when the user
+ // clicks the 'Reload' link. http://crbug.com/159302.
+ if (extensionReloadedTimestamp[extension.id]) {
+ item.style.backgroundImage =
+ 'url(' + extension.icon + '?' +
+ extensionReloadedTimestamp[extension.id] + ')';
+ } else {
+ 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;
+ var descriptionHolder = node.querySelector('.extension-description');
+ descriptionHolder.textContent = '';
+ descriptionHolder.appendChild(description);
not at google - send to devlin 2015/02/05 22:25:03 Any reason why this isn't all just "descriptionHol
hcarmona 2015/02/05 23:27:14 This was done to avoid changing the structure of t
+
+ // The 'Show Browser Action' button.
+ var showButton = node.querySelector('.show-button');
not at google - send to devlin 2015/02/05 22:25:03 General function-level comment, which applies to c
hcarmona 2015/02/05 23:27:14 Awesome, I'll create an update function.
+ showButton.hidden = !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;
+ var butterBar = node.querySelector('.butter-bar');
+ butterBar.hidden = !butterBarVisibility[extension.id];
+
+ // 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.
+ node.querySelector('.error-collection-control').hidden =
+ !extension.wantsErrorCollection;
+ var errorCollection =
+ node.querySelector('.error-collection-control input');
+ errorCollection.checked = extension.wantsErrorCollection &&
+ 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.
+ var allUrls = node.querySelector('.all-urls-control');
+ allUrls.querySelector('input').checked = extension.wantsAllUrls &&
+ extension.allowAllUrls;
+ allUrls.hidden = !extension.wantsAllUrls;
+
+ // The 'allow file:// access' checkbox.
+ var fileAccess = node.querySelector('.file-access-control');
+ fileAccess.querySelector('input').checked = extension.wantsFileAccess &&
+ extension.allowFileAccess;
+ fileAccess.hidden = !extension.wantsFileAccess;
+
+ // The 'Options' button or link, depending on its behaviour.
+ var optionsEnabled = extension.enabled && extension.optionsUrl;
+
+ var optionsLink = node.querySelector('.options-link');
+ var optionsLinkEnabled = optionsEnabled && extension.optionsOpenInTab;
+ optionsLink.hidden = !optionsLinkEnabled;
+
+ var optionsButton = options = node.querySelector('.options-button');
+ var optionsButtonEnabled = optionsEnabled && !extension.optionsOpenInTab;
+ optionsButton.hidden = !optionsButtonEnabled;
+
+ // The 'View in Web Store/View Web Site' link.
+ var siteLinkEnabled = extension.homepageUrl &&
+ !extension.enableExtensionInfoDialog;
+ var siteLink = node.querySelector('.site-link');
+ siteLink.href = extension.homepageUrl;
+ siteLink.textContent = loadTimeData.getString(
+ extension.homepageProvided ? 'extensionSettingsVisitWebsite' :
+ 'extensionSettingsVisitWebStore');
+ siteLink.hidden = !siteLinkEnabled;
+
+ // The 'Reload' link.
+ var reload = node.querySelector('.reload-link');
+ reload.hidden = !extension.allow_reload;
+
+ var canLaunch = extension.allow_reload && extension.is_platform_app;
+ // The 'Launch' link.
+ var launch = node.querySelector('.launch-link');
+ launch.hidden = !canLaunch;
+
+ // The 'Reload' terminated link.
+ var isTerminated = extension.terminated;
+ var terminatedReload = node.querySelector('.terminated-reload-link');
+ terminatedReload.hidden = !isTerminated;
+
+ // The 'Repair' corrupted link.
+ var canRepair = !isTerminated && extension.corruptInstall &&
+ extension.isFromStore;
+ var repair = node.querySelector('.corrupted-repair-button');
+ repair.hidden = !canRepair;
+
+ // The 'Enabled' checkbox.
+ var isOK = !isTerminated && !canRepair;
+ var enable = node.querySelector('.enable-checkbox');
+ enable.hidden = !isOK;
+ var enableCheckboxDisabled = extension.managedInstall ||
+ extension.suspiciousInstall ||
+ extension.corruptInstall ||
+ extension.updateRequiredByPolicy ||
+ extension.dependentExtensions.length > 0;
+ enable.querySelector('input').disabled = enableCheckboxDisabled;
+
+ enable.querySelector('input').checked = isOK && extension.enabled;
+
+ // Button for extensions controlled by policy.
+ var controlNode = node.querySelector('.enable-controls');
+ if (extension.managedInstall &&
+ !controlNode.querySelector('.controlled-extension-indicator')) {
+ var indicator = new cr.ui.ControlledIndicator();
+ indicator.classList.add('controlled-extension-indicator');
+ indicator.setAttribute('controlled-by', 'policy');
+ indicator.setAttribute('textpolicy', extension.policyText || '');
+ controlNode.appendChild(indicator);
+ } else if (!extension.managedInstall) {
+ var indicator = controlNode.querySelector(
+ '.controlled-extension-indicator');
+ if (indicator)
+ controlNode.removeChild(indicator);
+ }
+
+ // Developer mode ////////////////////////////////////////////////////////
+
+ // First we have the id.
+ var idLabel = node.querySelector('.extension-id');
+ idLabel.textContent = ' ' + extension.id;
+
+ // Then the path, if provided by unpacked extension.
+ var loadPath = node.querySelector('.load-path');
+ loadPath.hidden = !extension.isUnpacked;
+ var pathLink = loadPath.querySelector('a:nth-of-type(1)');
+ pathLink.textContent = ' ' + extension.prettifiedPath;
+
+ // Then the 'managed, cannot uninstall/disable' message.
+ var isRequired = extension.managedInstall || extension.recommendedInstall;
+ node.querySelector('.managed-message').hidden = !isRequired;
+
+ // Then the 'This isn't from the webstore, looks suspicious' message.
+ var isSuspicious = !isRequired && extension.suspiciousInstall;
+ node.querySelector('.suspicious-install-message').hidden = !isSuspicious;
+
+ // Then the 'This is a corrupt extension' message.
+ var isCorrupt = !isRequired && extension.corruptInstall;
+ node.querySelector('.corrupt-install-message').hidden = !isCorrupt;
+
+ // Then the 'An update required by enterprise policy' message. Note that
+ // a force-installed extension might be disabled due to being outdated
+ // as well.
+ node.querySelector('.update-required-message').hidden =
+ !extension.updateRequiredByPolicy;
+ // We would like to hide managed installed message since this
+ // extension is disabled.
+ node.querySelector('.managed-message').hidden =
+ extension.updateRequiredByPolicy;
+
+ // The 'following extensions depend on this extension' list.
+ var hasDependents = extension.dependentExtensions.length > 0;
+ node.classList.toggle('developer-extras', hasDependents);
+ var dependentMessage =
+ node.querySelector('.dependent-extensions-message');
+ dependentMessage.hidden = !hasDependents;
+ var dependentList = dependentMessage.querySelector('ul');
+ dependentList.textContent = '';
+ var dependentTemplate = $('template-collection').querySelector(
+ '.dependent-list-item');
+ extension.dependentExtensions.forEach(function(elem) {
+ var depNode = dependentTemplate.cloneNode(true);
+ depNode.querySelector('.dep-extension-title').textContent = elem.name;
+ depNode.querySelector('.dep-extension-id').textContent = elem.id;
+ dependentList.appendChild(depNode);
+ });
+
+ // The active views.
+ var hasActiveViews = extension.views.length > 0;
+ var activeViews = node.querySelector('.active-views');
+ activeViews.hidden = !hasActiveViews;
+
+ var link = activeViews.querySelector('a');
+
+ // Link needs to be an only child before the list is updated.
+ while (link.nextElementSibling)
+ activeViews.removeChild(link.nextElementSibling);
+
+ // Link needs to be cleaned up if it was used before.
+ link.textContent = '';
+ if (link.clickHandler)
+ link.removeEventListener('click', link.clickHandler);
+
+ if (hasActiveViews) {
+ extension.views.forEach(function(view, i) {
+ var displayName = view.generatedBackgroundPage ?
+ loadTimeData.getString('backgroundPage') : view.path;
+ var label = displayName +
+ (view.incognito ?
+ ' ' + loadTimeData.getString('viewIncognito') : '') +
+ (view.renderProcessId == -1 ?
+ ' ' + loadTimeData.getString('viewInactive') : '');
+ link.textContent = label;
+ link.clickHandler = function(e) {
+ // TODO(estade): remove conversion to string?
+ chrome.send('extensionSettingsInspect', [
+ String(extension.id),
+ String(view.renderProcessId),
+ String(view.renderViewId),
+ view.incognito
+ ]);
+ };
+ link.addEventListener('click', link.clickHandler);
+
+ if (i < extension.views.length - 1) {
+ link = link.cloneNode(true);
+ activeViews.appendChild(link);
+ }
+ });
+ }
+
+ // The extension warnings (describing runtime issues).
+ var warningPanel = node.querySelector('.extension-warnings');
+ warningPanel.hidden = !extension.warnings;
+ var warningList = warningPanel.querySelector('ul');
+ warningList.textContent = '';
+ if (extension.warnings) {
+ extension.warnings.forEach(function(warning) {
+ warningList.appendChild(document.createElement('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.
+ // Errors.
+ this.updateErrors_(node.querySelector('.manifest-errors'),
+ extension.manifestErrors);
+ this.updateErrors_(node.querySelector('.runtime-errors'),
+ extension.runtimeErrors);
+
+ // Install warnings.
+ var installWarningPanel = node.querySelector('.install-warnings');
+ installWarningPanel.hidden = !extension.installWarnings;
+ var installWarningList = installWarningPanel.querySelector('ul');
+ installWarningList.textContent = '';
+ if (extension.installWarnings) {
+ extension.installWarnings.forEach(function(warning) {
+ var li = document.createElement('li');
+ li.innerText = warning.message;
+ installWarningList.appendChild(li);
+ });
+ }
+
+ if (location.hash.substr(1) == extension.id) {
+ // Scroll beneath the fixed header so that the extension is not
+ // obscured.
+ var topScroll = node.offsetTop - $('page-header').offsetHeight;
+ var pad = parseInt(window.getComputedStyle(node, null).marginTop, 10);
+ if (!isNaN(pad))
+ topScroll -= pad / 2;
+ setScrollTopForDocument(document, topScroll);
+ }
+ },

Powered by Google App Engine
This is Rietveld 408576698