diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-13 13:24:50 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-10-14 10:57:25 +0000 |
commit | af3d4809763ef308f08ced947a73b624729ac7ea (patch) | |
tree | 4402b911e30383f6c6dace1e8cf3b8e85355db3a /chromium/chrome/browser/resources/extensions | |
parent | 0e8ff63a407fe323e215bb1a2c423c09a4747c8a (diff) |
BASELINE: Update Chromium to 47.0.2526.14
Also adding in sources needed for spellchecking.
Change-Id: Idd44170fa1616f26315188970a8d5ba7d472b18a
Reviewed-by: Michael BrĂ¼ning <michael.bruning@theqtcompany.com>
Diffstat (limited to 'chromium/chrome/browser/resources/extensions')
10 files changed, 382 insertions, 460 deletions
diff --git a/chromium/chrome/browser/resources/extensions/chromeos/kiosk_apps.css b/chromium/chrome/browser/resources/extensions/chromeos/kiosk_apps.css index 18b0168ed94..6040b4620bf 100644 --- a/chromium/chrome/browser/resources/extensions/chromeos/kiosk_apps.css +++ b/chromium/chrome/browser/resources/extensions/chromeos/kiosk_apps.css @@ -118,7 +118,8 @@ list .row-delete-button:active { } .kiosk-app-icon.spinner { - background-image: url(chrome://resources/images/throbber.svg) !important; + background-image: url(chrome://resources/images/throbber_small.svg) + !important; } .kiosk-app-name, diff --git a/chromium/chrome/browser/resources/extensions/compiled_resources.gyp b/chromium/chrome/browser/resources/extensions/compiled_resources.gyp index e6127bf181a..a40ae066330 100644 --- a/chromium/chrome/browser/resources/extensions/compiled_resources.gyp +++ b/chromium/chrome/browser/resources/extensions/compiled_resources.gyp @@ -29,6 +29,7 @@ '../../../../ui/webui/resources/js/event_tracker.js', '../../../../ui/webui/resources/js/load_time_data.js', '../../../../ui/webui/resources/js/util.js', + 'focus_row.js', ], 'externs': [ '<(EXTERNS_DIR)/chrome_extensions.js', diff --git a/chromium/chrome/browser/resources/extensions/extension_error.css b/chromium/chrome/browser/resources/extensions/extension_error.css index d6514114bec..79ee0ab3adf 100644 --- a/chromium/chrome/browser/resources/extensions/extension_error.css +++ b/chromium/chrome/browser/resources/extensions/extension_error.css @@ -70,7 +70,9 @@ } .extension-error-metadata > .error-delete-button { + -webkit-appearance: none; background: url(chrome://theme/IDR_CLOSE_DIALOG) center no-repeat; + border: none; height: 14px; opacity: 0.6; width: 14px; diff --git a/chromium/chrome/browser/resources/extensions/extension_error.html b/chromium/chrome/browser/resources/extensions/extension_error.html index 29615777711..01589bf8c44 100644 --- a/chromium/chrome/browser/resources/extensions/extension_error.html +++ b/chromium/chrome/browser/resources/extensions/extension_error.html @@ -14,8 +14,8 @@ in the LICENSE file. <span id="no-errors-span" i18n-content="extensionErrorNoErrors" hidden> </span> </div> - <div class="extension-error-metadata"> - <p class="extension-error-message"></p> - <div class="error-delete-button"></div> - </div> + <li class="extension-error-metadata"> + <p class="extension-error-message" tabindex="0"></p> + <button class="custom-appearance error-delete-button"></button> + </li> </div> diff --git a/chromium/chrome/browser/resources/extensions/extension_error.js b/chromium/chrome/browser/resources/extensions/extension_error.js index c9296554d9a..9ad71e8d753 100644 --- a/chromium/chrome/browser/resources/extensions/extension_error.js +++ b/chromium/chrome/browser/resources/extensions/extension_error.js @@ -43,41 +43,25 @@ cr.define('extensions', function() { * notification to the user when an error is caused by an extension. * @param {(RuntimeError|ManifestError)} error The error the element should * represent. - * @param {Element} boundary The boundary for the focus grid. * @constructor - * @extends {cr.ui.FocusRow} + * @extends {HTMLElement} */ - function ExtensionError(error, boundary) { + function ExtensionError(error) { var div = cloneTemplate('extension-error-metadata'); div.__proto__ = ExtensionError.prototype; - div.decorateWithError_(error, boundary); + div.decorate(error); return div; } ExtensionError.prototype = { - __proto__: cr.ui.FocusRow.prototype, - - /** @override */ - getEquivalentElement: function(element) { - if (element.classList.contains('extension-error-metadata')) - return this; - if (element.classList.contains('error-delete-button')) { - return /** @type {!HTMLElement} */ (this.querySelector( - '.error-delete-button')); - } - assertNotReached(); - return element; - }, + __proto__: HTMLElement.prototype, /** * @param {(RuntimeError|ManifestError)} error The error the element should * represent. - * @param {Element} boundary The boundary for the FocusGrid. * @private */ - decorateWithError_: function(error, boundary) { - this.decorate(boundary); - + decorate: function(error) { /** * The backing error. * @type {(ManifestError|RuntimeError)} @@ -125,13 +109,11 @@ cr.define('extensions', function() { if (e.target != deleteButton) this.requestActive_(); }.bind(this)); + this.addEventListener('keydown', function(e) { if (e.keyIdentifier == 'Enter' && e.target != deleteButton) this.requestActive_(); }); - - this.addFocusableElement(this); - this.addFocusableElement(this.querySelector('.error-delete-button')); }, /** @@ -161,6 +143,23 @@ cr.define('extensions', function() { return div; } + /** + * @param {!Element} root + * @param {?Node} boundary + * @constructor + * @extends {cr.ui.FocusRow} + */ + ExtensionErrorList.FocusRow = function(root, boundary) { + cr.ui.FocusRow.call(this, root, boundary); + + this.addItem('message', '.extension-error-message'); + this.addItem('delete', '.error-delete-button'); + }; + + ExtensionErrorList.FocusRow.prototype = { + __proto__: cr.ui.FocusRow.prototype, + }; + ExtensionErrorList.prototype = { __proto__: HTMLDivElement.prototype, @@ -169,18 +168,19 @@ cr.define('extensions', function() { * @param {Array<(RuntimeError|ManifestError)>} errors The list of errors. */ decorate: function(errors) { - /** - * @private {!Array<(ManifestError|RuntimeError)>} - */ + /** @private {!Array<(ManifestError|RuntimeError)>} */ this.errors_ = []; + /** @private {!cr.ui.FocusGrid} */ this.focusGrid_ = new cr.ui.FocusGrid(); - this.gridBoundary_ = this.querySelector('.extension-error-list-contents'); - this.gridBoundary_.addEventListener('focus', this.onFocus_.bind(this)); - this.gridBoundary_.addEventListener('focusin', - this.onFocusin_.bind(this)); + + /** @private {Element} */ + this.listContents_ = this.querySelector('.extension-error-list-contents'); + errors.forEach(this.addError_, this); + this.focusGrid_.ensureRowActive(); + this.addEventListener('highlightExtensionError', function(e) { this.setActiveErrorNode_(e.target); }); @@ -228,10 +228,12 @@ cr.define('extensions', function() { addError_: function(error) { this.querySelector('#no-errors-span').hidden = true; this.errors_.push(error); - var focusRow = new ExtensionError(error, this.gridBoundary_); - this.gridBoundary_.appendChild(document.createElement('li')). - appendChild(focusRow); - this.focusGrid_.addRow(focusRow); + + var extensionError = new ExtensionError(error); + this.listContents_.appendChild(extensionError); + + this.focusGrid_.addRow( + new ExtensionErrorList.FocusRow(extensionError, this.listContents_)); }, /** @@ -253,6 +255,14 @@ cr.define('extensions', function() { this.errors_.splice(index, 1); var listElement = errorList.children[index]; + + var focusRow = this.focusGrid_.getRowForRoot(listElement); + this.focusGrid_.removeRow(focusRow); + this.focusGrid_.ensureRowActive(); + focusRow.destroy(); + + // TODO(dbeam): in a world where this UI is actually used, we should + // probably move the focus before removing |listElement|. listElement.parentNode.removeChild(listElement); if (wasActive) { @@ -357,25 +367,6 @@ cr.define('extensions', function() { new CustomEvent('activeExtensionErrorChanged', {bubbles: true, detail: node ? node.error : null})); }, - - /** - * The grid should not be focusable once it or an element inside it is - * focused. This is necessary to allow tabbing out of the grid in reverse. - * @private - */ - onFocusin_: function() { - this.gridBoundary_.tabIndex = -1; - }, - - /** - * Focus the first focusable row when tabbing into the grid for the - * first time. - * @private - */ - onFocus_: function() { - var activeRow = this.gridBoundary_.querySelector('.focus-row-active'); - activeRow.getEquivalentElement(null).focus(); - }, }; return { diff --git a/chromium/chrome/browser/resources/extensions/extension_list.js b/chromium/chrome/browser/resources/extensions/extension_list.js index d8cc1e03a9a..2bbed63d122 100644 --- a/chromium/chrome/browser/resources/extensions/extension_list.js +++ b/chromium/chrome/browser/resources/extensions/extension_list.js @@ -4,148 +4,63 @@ <include src="extension_error.js"> -/////////////////////////////////////////////////////////////////////////////// -// ExtensionFocusRow: - -/** - * Provides an implementation for a single column grid. - * @constructor - * @extends {cr.ui.FocusRow} - */ -function ExtensionFocusRow() {} - -/** - * Decorates |focusRow| so that it can be treated as a ExtensionFocusRow. - * @param {Element} focusRow The element that has all the columns. - * @param {Node} boundary Focus events are ignored outside of this node. - */ -ExtensionFocusRow.decorate = function(focusRow, boundary) { - focusRow.__proto__ = ExtensionFocusRow.prototype; - focusRow.decorate(boundary); -}; - -ExtensionFocusRow.prototype = { - __proto__: cr.ui.FocusRow.prototype, - - /** @override */ - getEquivalentElement: function(element) { - if (this.focusableElements.indexOf(element) > -1) - return element; - - // All elements default to another element with the same type. - var columnType = element.getAttribute('column-type'); - var equivalent = this.querySelector('[column-type=' + columnType + ']'); - - if (!equivalent || !this.canAddElement_(equivalent)) { - var actionLinks = ['options', 'website', 'launch', 'localReload']; - var optionalControls = ['showButton', 'incognito', 'dev-collectErrors', - 'allUrls', 'localUrls']; - var removeStyleButtons = ['trash', 'enterprise']; - var enableControls = ['terminatedReload', 'repair', 'enabled']; - - if (actionLinks.indexOf(columnType) > -1) - equivalent = this.getFirstFocusableByType_(actionLinks); - else if (optionalControls.indexOf(columnType) > -1) - equivalent = this.getFirstFocusableByType_(optionalControls); - else if (removeStyleButtons.indexOf(columnType) > -1) - equivalent = this.getFirstFocusableByType_(removeStyleButtons); - else if (enableControls.indexOf(columnType) > -1) - equivalent = this.getFirstFocusableByType_(enableControls); - } - - // Return the first focusable element if no equivalent type is found. - return equivalent || this.focusableElements[0]; - }, - - /** @override */ - makeActive: function(active) { - cr.ui.FocusRow.prototype.makeActive.call(this, active); - - // Only highlight if the row has focus. - this.classList.toggle('extension-highlight', - active && this.contains(document.activeElement)); - }, - - /** Updates the list of focusable elements. */ - updateFocusableElements: function() { - this.focusableElements.length = 0; - - var focusableCandidates = this.querySelectorAll('[column-type]'); - for (var i = 0; i < focusableCandidates.length; ++i) { - var element = focusableCandidates[i]; - if (this.canAddElement_(element)) - this.addFocusableElement(element); - } - }, +cr.define('extensions', function() { + 'use strict'; - /** - * Get the first focusable element that matches a list of types. - * @param {Array<string>} types An array of types to match from. - * @return {?Element} Return the first element that matches a type in |types|. - * @private - */ - getFirstFocusableByType_: function(types) { - for (var i = 0; i < this.focusableElements.length; ++i) { - var element = this.focusableElements[i]; - if (types.indexOf(element.getAttribute('column-type')) > -1) - return element; - } - return null; - }, + var ExtensionType = chrome.developerPrivate.ExtensionType; /** - * Setup a typical column in the ExtensionFocusRow. A column can be any - * element and should have an action when clicked/toggled. This function - * adds a listener and a handler for an event. Also adds the "column-type" - * attribute to make the element focusable in |updateFocusableElements|. - * @param {string} query A query to select the element to set up. - * @param {string} columnType A tag used to identify the column when - * changing focus. - * @param {string} eventType The type of event to listen to. - * @param {function(Event)} handler The function that should be called - * by the event. - * @private + * @param {string} name The name of the template to clone. + * @return {!Element} The freshly cloned template. */ - setupColumn: function(query, columnType, eventType, handler) { - var element = this.querySelector(query); - element.addEventListener(eventType, handler); - element.setAttribute('column-type', columnType); - }, + function cloneTemplate(name) { + var node = $('templates').querySelector('.' + name).cloneNode(true); + return assertInstanceof(node, Element); + } /** - * @param {Element} element - * @return {boolean} - * @private + * @extends {HTMLElement} + * @constructor */ - canAddElement_: function(element) { - if (!element || element.disabled) - return false; - - var developerMode = $('extension-settings').classList.contains('dev-mode'); - if (this.isDeveloperOption_(element) && !developerMode) - return false; + function ExtensionWrapper() { + var wrapper = cloneTemplate('extension-list-item-wrapper'); + wrapper.__proto__ = ExtensionWrapper.prototype; + wrapper.initialize(); + return wrapper; + } - for (var el = element; el; el = el.parentElement) { - if (el.hidden) - return false; - } + ExtensionWrapper.prototype = { + __proto__: HTMLElement.prototype, - return true; - }, + initialize: function() { + var boundary = $('extension-settings-list'); + /** @private {!extensions.FocusRow} */ + this.focusRow_ = new extensions.FocusRow(this, boundary); + }, - /** - * Returns true if the element should only be shown in developer mode. - * @param {Element} element - * @return {boolean} - * @private - */ - isDeveloperOption_: function(element) { - return /^dev-/.test(element.getAttribute('column-type')); - }, -}; + /** @return {!cr.ui.FocusRow} */ + getFocusRow: function() { + return this.focusRow_; + }, -cr.define('extensions', function() { - 'use strict'; + /** + * Add an item to the focus row and listen for |eventType| events. + * @param {string} focusType A tag used to identify equivalent elements when + * changing focus between rows. + * @param {string} query A query to select the element to set up. + * @param {string=} opt_eventType The type of event to listen to. + * @param {function(Event)=} opt_handler The function that should be called + * by the event. + * @private + */ + setupColumn: function(focusType, query, opt_eventType, opt_handler) { + assert(this.focusRow_.addItem(focusType, query)); + if (opt_eventType) { + assert(opt_handler); + this.querySelector(query).addEventListener(opt_eventType, opt_handler); + } + }, + }; var ExtensionCommandsOverlay = extensions.ExtensionCommandsOverlay; @@ -225,10 +140,11 @@ cr.define('extensions', function() { permissionsPromptIsShowing_: false, /** - * Necessary to only show the butterbar once. + * Whether or not any initial navigation (like scrolling to an extension, + * or opening an options page) has occurred. * @private {boolean} */ - butterbarShown_: false, + didInitialNavigation_: false, /** * Whether or not incognito mode is available. @@ -253,15 +169,7 @@ cr.define('extensions', function() { /** @private {!extensions.ExtensionListDelegate} */ this.delegate_ = delegate; - /** - * |loadFinished| should be used for testing purposes and will be - * fulfilled when this list has finished loading the first time. - * @type {Promise} - * */ - this.loadFinished = new Promise(function(resolve, reject) { - /** @private {function(?)} */ - this.resolveLoadFinished_ = resolve; - }.bind(this)); + this.resetLoadFinished(); chrome.developerPrivate.onItemStateChanged.addListener( function(eventData) { @@ -276,14 +184,14 @@ cr.define('extensions', function() { case EventType.ERRORS_REMOVED: case EventType.PREFS_CHANGED: if (eventData.extensionInfo) { - this.updateExtension_(eventData.extensionInfo); + this.updateOrCreateWrapper_(eventData.extensionInfo); this.focusGrid_.ensureRowActive(); } break; case EventType.UNINSTALLED: var index = this.getIndexOfExtension_(eventData.item_id); this.extensions_.splice(index, 1); - this.removeNode_(getRequiredElement(eventData.item_id)); + this.removeWrapper_(getRequiredElement(eventData.item_id)); break; default: assertNotReached(); @@ -312,6 +220,22 @@ cr.define('extensions', function() { }, /** + * Resets the |loadFinished| promise so that it can be used again; this + * is useful if the page updates and tests need to wait for it to finish. + */ + resetLoadFinished: function() { + /** + * |loadFinished| should be used for testing purposes and will be + * fulfilled when this list has finished loading the first time. + * @type {Promise} + * */ + this.loadFinished = new Promise(function(resolve, reject) { + /** @private {function(?)} */ + this.resolveLoadFinished_ = resolve; + }.bind(this)); + }, + + /** * Updates the extensions on the page. * @param {boolean} incognitoAvailable Whether or not incognito is allowed. * @param {boolean} enableAppInfoDialog Whether or not the app info dialog @@ -358,19 +282,27 @@ cr.define('extensions', function() { * @private */ onUpdateFinished_: function() { - // Cannot focus or highlight a extension if there are none. - if (this.extensions_.length == 0) + // Cannot focus or highlight a extension if there are none, and we should + // only scroll to a particular extension or open the options page once. + if (this.extensions_.length == 0 || this.didInitialNavigation_) return; + this.didInitialNavigation_ = true; assert(!this.hidden); assert(!this.parentElement.hidden); - this.updateFocusableElements(); - var idToHighlight = this.getIdQueryParam_(); - if (idToHighlight && $(idToHighlight)) { - this.scrollToNode_(idToHighlight); - this.setInitialFocus_(idToHighlight); + if (idToHighlight) { + var wrapper = $(idToHighlight); + if (wrapper) { + this.scrollToWrapper_(idToHighlight); + + var focusRow = wrapper.getFocusRow(); + (focusRow.getFirstFocusable('enabled') || + focusRow.getFirstFocusable('remove-enterprise') || + focusRow.getFirstFocusable('website') || + focusRow.getFirstFocusable('details')).focus(); + } } var idToOpenOptions = this.getOptionsQueryParam_(); @@ -415,48 +347,46 @@ cr.define('extensions', function() { // Iterate over the extension data and add each item to the list. this.extensions_.forEach(function(extension) { seenIds.push(extension.id); - this.updateExtension_(extension); + this.updateOrCreateWrapper_(extension); }, this); this.focusGrid_.ensureRowActive(); // Remove extensions that are no longer installed. - var nodes = document.querySelectorAll('.extension-list-item-wrapper[id]'); - Array.prototype.forEach.call(nodes, function(node) { - if (seenIds.indexOf(node.id) < 0) - this.removeNode_(node); + var wrappers = document.querySelectorAll( + '.extension-list-item-wrapper[id]'); + Array.prototype.forEach.call(wrappers, function(wrapper) { + if (seenIds.indexOf(wrapper.id) < 0) + this.removeWrapper_(wrapper); }, this); }, - /** Updates each row's focusable elements without rebuilding the grid. */ - updateFocusableElements: function() { - var rows = document.querySelectorAll('.extension-list-item-wrapper[id]'); - for (var i = 0; i < rows.length; ++i) { - assertInstanceof(rows[i], ExtensionFocusRow).updateFocusableElements(); - } - }, - /** - * Removes the node from the DOM, and updates the focused element if needed. - * @param {!HTMLElement} node + * Removes the wrapper from the DOM and updates the focused element if + * needed. + * @param {!Element} wrapper * @private */ - removeNode_: function(node) { - if (node.contains(document.activeElement)) { - var nodes = - document.querySelectorAll('.extension-list-item-wrapper[id]'); - var index = Array.prototype.indexOf.call(nodes, node); + removeWrapper_: function(wrapper) { + // If focus is in the wrapper about to be removed, move it first. This + // happens when clicking the trash can to remove an extension. + if (wrapper.contains(document.activeElement)) { + var wrappers = document.querySelectorAll( + '.extension-list-item-wrapper[id]'); + var index = Array.prototype.indexOf.call(wrappers, wrapper); assert(index != -1); - var focusableNode = nodes[index + 1] || nodes[index - 1]; - if (focusableNode) - focusableNode.getEquivalentElement(document.activeElement).focus(); + var focusableWrapper = wrappers[index + 1] || wrappers[index - 1]; + if (focusableWrapper) { + var newFocusRow = focusableWrapper.getFocusRow(); + newFocusRow.getEquivalentElement(document.activeElement).focus(); + } } - node.parentNode.removeChild(node); - this.focusGrid_.removeRow(assertInstanceof(node, ExtensionFocusRow)); - - // Unregister the removed node from events. - assertInstanceof(node, ExtensionFocusRow).destroy(); + var focusRow = wrapper.getFocusRow(); + this.focusGrid_.removeRow(focusRow); this.focusGrid_.ensureRowActive(); + focusRow.destroy(); + + wrapper.parentNode.removeChild(wrapper); }, /** @@ -464,60 +394,95 @@ cr.define('extensions', function() { * @param {string} extensionId The id of the extension to scroll to. * @private */ - scrollToNode_: function(extensionId) { + scrollToWrapper_: function(extensionId) { // Scroll offset should be calculated slightly higher than the actual // offset of the element being scrolled to, so that it ends up not all // the way at the top. That way it is clear that there are more elements // above the element being scrolled to. + var wrapper = $(extensionId); var scrollFudge = 1.2; - var scrollTop = $(extensionId).offsetTop - scrollFudge * - $(extensionId).clientHeight; + var scrollTop = wrapper.offsetTop - scrollFudge * wrapper.clientHeight; setScrollTopForDocument(document, scrollTop); }, /** - * @param {string} extensionId The id of the extension that should have - * initial focus - * @private - */ - setInitialFocus_: function(extensionId) { - var focusRow = assertInstanceof($(extensionId), ExtensionFocusRow); - var columnTypePriority = ['enabled', 'enterprise', 'website', 'details']; - var elementToFocus = null; - var elementPriority = columnTypePriority.length; - - for (var i = 0; i < focusRow.focusableElements.length; ++i) { - var element = focusRow.focusableElements[i]; - var priority = - columnTypePriority.indexOf(element.getAttribute('column-type')); - if (priority > -1 && priority < elementPriority) { - elementToFocus = element; - elementPriority = priority; - } - } - - focusRow.getEquivalentElement(elementToFocus).focus(); - }, - - /** * Synthesizes and initializes an HTML element for the extension metadata * given in |extension|. * @param {!ExtensionInfo} extension A dictionary of extension metadata. - * @param {?Element} nextNode |node| should be inserted before |nextNode|. - * |node| will be appended to the end if |nextNode| is null. + * @param {?Element} nextWrapper The newly created wrapper will be inserted + * before |nextWrapper| if non-null (else it will be appended to the + * wrapper list). * @private */ - createNode_: function(extension, nextNode) { - var template = $('template-collection').querySelector( - '.extension-list-item-wrapper'); - var node = template.cloneNode(true); - ExtensionFocusRow.decorate(node, $('extension-settings-list')); + createWrapper_: function(extension, nextWrapper) { + var wrapper = new ExtensionWrapper; + wrapper.id = extension.id; + + // The 'Permissions' link. + wrapper.setupColumn('details', '.permissions-link', 'click', function(e) { + if (!this.permissionsPromptIsShowing_) { + chrome.developerPrivate.showPermissionsDialog(extension.id, + function() { + this.permissionsPromptIsShowing_ = false; + }.bind(this)); + this.permissionsPromptIsShowing_ = true; + } + e.preventDefault(); + }); + + wrapper.setupColumn('options', '.options-button', 'click', function(e) { + this.showEmbeddedExtensionOptions_(extension.id, false); + e.preventDefault(); + }.bind(this)); + + // 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 developerPrivate + // API with a preventDefault(). + wrapper.querySelector('.options-link').href = + extension.optionsPage ? extension.optionsPage.url : ''; + wrapper.setupColumn('options', '.options-link', 'click', function(e) { + chrome.developerPrivate.showOptions(extension.id); + e.preventDefault(); + }); + + // The 'View in Web Store/View Web Site' link. + wrapper.setupColumn('website', '.site-link'); + + // The 'Launch' link. + wrapper.setupColumn('launch', '.launch-link', 'click', function(e) { + chrome.management.launchApp(extension.id); + }); - var row = assertInstanceof(node, ExtensionFocusRow); - row.id = extension.id; + // The 'Reload' link. + wrapper.setupColumn('localReload', '.reload-link', 'click', function(e) { + chrome.developerPrivate.reload(extension.id, {failQuietly: true}); + }); + + wrapper.setupColumn('errors', '.errors-link', 'click', function(e) { + var extensionId = extension.id; + assert(this.extensions_.length > 0); + var newEx = this.extensions_.filter(function(e) { + return e.state == chrome.developerPrivate.ExtensionState.ENABLED && + e.id == extensionId; + })[0]; + var errors = newEx.manifestErrors.concat(newEx.runtimeErrors); + extensions.ExtensionErrorOverlay.getInstance().setErrorsAndShowOverlay( + errors, extensionId, newEx.name); + }.bind(this)); + + wrapper.setupColumn('suspiciousLearnMore', + '.suspicious-install-message .learn-more-link'); + + // The path, if provided by unpacked extension. + wrapper.setupColumn('loadPath', '.load-path a:first-of-type', 'click', + function(e) { + chrome.developerPrivate.showPath(extension.id); + e.preventDefault(); + }); // The 'Show Browser Action' button. - row.setupColumn('.show-button', 'showButton', 'click', function(e) { + wrapper.setupColumn('showButton', '.show-button', 'click', function(e) { chrome.developerPrivate.updateExtensionConfiguration({ extensionId: extension.id, showActionButton: true @@ -525,18 +490,12 @@ cr.define('extensions', function() { }); // The 'allow in incognito' checkbox. - row.setupColumn('.incognito-control input', 'incognito', 'change', - function(e) { - var butterBar = row.querySelector('.butter-bar'); + wrapper.setupColumn('incognito', '.incognito-control input', 'change', + function(e) { + var butterBar = wrapper.querySelector('.butter-bar'); var checked = e.target.checked; - if (!this.butterbarShown_) { - butterBar.hidden = !checked || - extension.type == - chrome.developerPrivate.ExtensionType.HOSTED_APP; - this.butterbarShown_ = !butterBar.hidden; - } else { - butterBar.hidden = true; - } + butterBar.hidden = !checked || + extension.type == ExtensionType.HOSTED_APP; chrome.developerPrivate.updateExtensionConfiguration({ extensionId: extension.id, incognitoAccess: e.target.checked @@ -546,8 +505,8 @@ cr.define('extensions', function() { // 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. - row.setupColumn('.error-collection-control input', 'dev-collectErrors', - 'change', function(e) { + wrapper.setupColumn('collectErrors', '.error-collection-control input', + 'change', function(e) { chrome.developerPrivate.updateExtensionConfiguration({ extensionId: extension.id, errorCollection: e.target.checked @@ -557,8 +516,8 @@ cr.define('extensions', function() { // 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. - row.setupColumn('.all-urls-control input', 'allUrls', 'click', - function(e) { + wrapper.setupColumn('allUrls', '.all-urls-control input', 'click', + function(e) { chrome.developerPrivate.updateExtensionConfiguration({ extensionId: extension.id, runOnAllUrls: e.target.checked @@ -566,82 +525,29 @@ cr.define('extensions', function() { }); // The 'allow file:// access' checkbox. - row.setupColumn('.file-access-control input', 'localUrls', 'click', - function(e) { + wrapper.setupColumn('localUrls', '.file-access-control input', 'click', + function(e) { chrome.developerPrivate.updateExtensionConfiguration({ extensionId: extension.id, fileAccess: 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 developerPrivate - // API with a preventDefault(). - row.querySelector('.options-link').href = - extension.optionsPage ? extension.optionsPage.url : ''; - row.setupColumn('.options-link', 'options', 'click', function(e) { - chrome.developerPrivate.showOptions(extension.id); - e.preventDefault(); - }); - - row.setupColumn('.options-button', 'options', 'click', function(e) { - this.showEmbeddedExtensionOptions_(extension.id, false); - e.preventDefault(); - }.bind(this)); - - // The 'View in Web Store/View Web Site' link. - row.querySelector('.site-link').setAttribute('column-type', 'website'); - - // The 'Permissions' link. - row.setupColumn('.permissions-link', 'details', 'click', function(e) { - if (!this.permissionsPromptIsShowing_) { - chrome.developerPrivate.showPermissionsDialog(extension.id, - function() { - this.permissionsPromptIsShowing_ = false; - }.bind(this)); - this.permissionsPromptIsShowing_ = true; - } - e.preventDefault(); - }); - - // The 'Reload' link. - row.setupColumn('.reload-link', 'localReload', 'click', function(e) { - chrome.developerPrivate.reload(extension.id, {failQuietly: true}); - }); - - // The 'Launch' link. - row.setupColumn('.launch-link', 'launch', 'click', function(e) { - chrome.management.launchApp(extension.id); - }); - - row.setupColumn('.errors-link', 'errors', 'click', function(e) { - var extensionId = extension.id; - assert(this.extensions_.length > 0); - var newEx = this.extensions_.filter(function(e) { - return e.state == chrome.developerPrivate.ExtensionState.ENABLED && - e.id == extensionId; - })[0]; - var errors = newEx.manifestErrors.concat(newEx.runtimeErrors); - extensions.ExtensionErrorOverlay.getInstance().setErrorsAndShowOverlay( - errors, extensionId, newEx.name); - }.bind(this)); - // The 'Reload' terminated link. - row.setupColumn('.terminated-reload-link', 'terminatedReload', 'click', - function(e) { + wrapper.setupColumn('terminatedReload', '.terminated-reload-link', + 'click', function(e) { chrome.developerPrivate.reload(extension.id, {failQuietly: true}); }); // The 'Repair' corrupted link. - row.setupColumn('.corrupted-repair-button', 'repair', 'click', - function(e) { + wrapper.setupColumn('repair', '.corrupted-repair-button', 'click', + function(e) { chrome.developerPrivate.repairExtension(extension.id); }); // The 'Enabled' checkbox. - row.setupColumn('.enable-checkbox input', 'enabled', 'change', - function(e) { + wrapper.setupColumn('enabled', '.enable-checkbox input', 'change', + function(e) { var checked = e.target.checked; // TODO(devlin): What should we do if this fails? chrome.management.setEnabled(extension.id, checked); @@ -655,11 +561,12 @@ cr.define('extensions', function() { }); // 'Remove' button. - var trashTemplate = $('template-collection').querySelector('.trash'); - var trash = trashTemplate.cloneNode(true); + var trash = cloneTemplate('trash'); trash.title = loadTimeData.getString('extensionUninstall'); - trash.setAttribute('column-type', 'trash'); - trash.addEventListener('click', function(e) { + + wrapper.querySelector('.enable-controls').appendChild(trash); + + wrapper.setupColumn('remove-enterprise', '.trash', 'click', function(e) { trash.classList.add('open'); trash.classList.toggle('mouse-clicked', e.detail > 0); if (this.uninstallIsShowing_) @@ -683,86 +590,68 @@ cr.define('extensions', function() { } }.bind(this)); }.bind(this)); - row.querySelector('.enable-controls').appendChild(trash); - - // Developer mode //////////////////////////////////////////////////////// - - // The path, if provided by unpacked extension. - row.setupColumn('.load-path a:first-of-type', 'dev-loadPath', 'click', - function(e) { - chrome.developerPrivate.showPath(extension.id); - e.preventDefault(); - }); // Maintain the order that nodes should be in when creating as well as - // when adding only one new row. - this.insertBefore(row, nextNode); - this.updateNode_(extension, row); + // when adding only one new wrapper. + this.insertBefore(wrapper, nextWrapper); + this.updateWrapper_(extension, wrapper); - var nextRow = null; - if (nextNode) - nextRow = assertInstanceof(nextNode, ExtensionFocusRow); - - this.focusGrid_.addRowBefore(row, nextRow); + var nextRow = this.focusGrid_.getRowForRoot(nextWrapper); // May be null. + this.focusGrid_.addRowBefore(wrapper.getFocusRow(), nextRow); }, /** * Updates an HTML element for the extension metadata given in |extension|. * @param {!ExtensionInfo} extension A dictionary of extension metadata. - * @param {!ExtensionFocusRow} row The node that is being updated. + * @param {!Element} wrapper The extension wrapper element to update. * @private */ - updateNode_: function(extension, row) { + updateWrapper_: function(extension, wrapper) { var isActive = extension.state == chrome.developerPrivate.ExtensionState.ENABLED; - row.classList.toggle('inactive-extension', !isActive); + wrapper.classList.toggle('inactive-extension', !isActive); + wrapper.classList.remove('controlled', 'may-not-remove'); - // Hack to keep the closure compiler happy about |remove|. - // TODO(hcarmona): Remove this hack when the closure compiler is updated. - var node = /** @type {Element} */ (row); - node.classList.remove('controlled', 'may-not-remove'); - var classes = []; if (extension.controlledInfo) { - classes.push('controlled'); + wrapper.classList.add('controlled'); } else if (!extension.userMayModify || extension.mustRemainInstalled || extension.dependentExtensions.length > 0) { - classes.push('may-not-remove'); + wrapper.classList.add('may-not-remove'); } - row.classList.add.apply(row.classList, classes); - var item = row.querySelector('.extension-list-item'); + var item = wrapper.querySelector('.extension-list-item'); item.style.backgroundImage = 'url(' + extension.iconUrl + ')'; - this.setText_(row, '.extension-title', extension.name); - this.setText_(row, '.extension-version', extension.version); - this.setText_(row, '.location-text', extension.locationText || ''); - this.setText_(row, '.blacklist-text', extension.blacklistText || ''); - this.setText_(row, '.extension-description', extension.description); + this.setText_(wrapper, '.extension-title', extension.name); + this.setText_(wrapper, '.extension-version', extension.version); + this.setText_(wrapper, '.location-text', extension.locationText || ''); + this.setText_(wrapper, '.blacklist-text', extension.blacklistText || ''); + this.setText_(wrapper, '.extension-description', extension.description); // The 'Show Browser Action' button. - this.updateVisibility_(row, '.show-button', + this.updateVisibility_(wrapper, '.show-button', isActive && extension.actionButtonHidden); // The 'allow in incognito' checkbox. - this.updateVisibility_(row, '.incognito-control', + this.updateVisibility_(wrapper, '.incognito-control', isActive && this.incognitoAvailable_, function(item) { var incognito = item.querySelector('input'); incognito.disabled = !extension.incognitoAccess.isEnabled; incognito.checked = extension.incognitoAccess.isActive; }); - - // Hide butterBar if incognito is not enabled for the extension. - var butterBar = row.querySelector('.butter-bar'); - butterBar.hidden = - butterBar.hidden || !extension.incognitoAccess.isEnabled; + var showButterBar = isActive && + extension.incognitoAccess.isActive && + extension.type != ExtensionType.HOSTED_APP; + // The 'allow in incognito' butter bar. + this.updateVisibility_(wrapper, '.butter-bar', showButterBar); // 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.updateVisibility_( - row, '.error-collection-control', + wrapper, '.error-collection-control', isActive && extension.errorCollection.isEnabled, function(item) { item.querySelector('input').checked = @@ -773,14 +662,14 @@ cr.define('extensions', function() { // active script restrictions are enabled. If they are not enabled, no // extensions should want all urls. this.updateVisibility_( - row, '.all-urls-control', + wrapper, '.all-urls-control', isActive && extension.runOnAllUrls.isEnabled, function(item) { item.querySelector('input').checked = extension.runOnAllUrls.isActive; }); // The 'allow file:// access' checkbox. - this.updateVisibility_(row, '.file-access-control', + this.updateVisibility_(wrapper, '.file-access-control', isActive && extension.fileAccess.isEnabled, function(item) { item.querySelector('input').checked = extension.fileAccess.isActive; @@ -788,15 +677,15 @@ cr.define('extensions', function() { // The 'Options' button or link, depending on its behaviour. var optionsEnabled = isActive && !!extension.optionsPage; - this.updateVisibility_(row, '.options-link', optionsEnabled && + this.updateVisibility_(wrapper, '.options-link', optionsEnabled && extension.optionsPage.openInTab); - this.updateVisibility_(row, '.options-button', optionsEnabled && + this.updateVisibility_(wrapper, '.options-button', optionsEnabled && !extension.optionsPage.openInTab); // The 'View in Web Store/View Web Site' link. var siteLinkEnabled = !!extension.homePage.url && !this.enableAppInfoDialog_; - this.updateVisibility_(row, '.site-link', siteLinkEnabled, + this.updateVisibility_(wrapper, '.site-link', siteLinkEnabled, function(item) { item.href = extension.homePage.url; item.textContent = loadTimeData.getString( @@ -807,18 +696,19 @@ cr.define('extensions', function() { var isUnpacked = extension.location == chrome.developerPrivate.Location.UNPACKED; // The 'Reload' link. - this.updateVisibility_(row, '.reload-link', isUnpacked); + this.updateVisibility_(wrapper, '.reload-link', isUnpacked); // The 'Launch' link. this.updateVisibility_( - row, '.launch-link', - isUnpacked && extension.type == - chrome.developerPrivate.ExtensionType.PLATFORM_APP); + wrapper, '.launch-link', + isUnpacked && extension.type == ExtensionType.PLATFORM_APP && + isActive); // The 'Errors' link. var hasErrors = extension.runtimeErrors.length > 0 || extension.manifestErrors.length > 0; - this.updateVisibility_(row, '.errors-link', hasErrors, function(item) { + this.updateVisibility_(wrapper, '.errors-link', hasErrors, + function(item) { var Level = chrome.developerPrivate.ErrorLevel; var map = {}; @@ -846,18 +736,18 @@ cr.define('extensions', function() { // The 'Reload' terminated link. var isTerminated = extension.state == chrome.developerPrivate.ExtensionState.TERMINATED; - this.updateVisibility_(row, '.terminated-reload-link', isTerminated); + this.updateVisibility_(wrapper, '.terminated-reload-link', isTerminated); // The 'Repair' corrupted link. var canRepair = !isTerminated && extension.disableReasons.corruptInstall && extension.location == chrome.developerPrivate.Location.FROM_STORE; - this.updateVisibility_(row, '.corrupted-repair-button', canRepair); + this.updateVisibility_(wrapper, '.corrupted-repair-button', canRepair); // The 'Enabled' checkbox. var isOK = !isTerminated && !canRepair; - this.updateVisibility_(row, '.enable-checkbox', isOK, function(item) { + this.updateVisibility_(wrapper, '.enable-checkbox', isOK, function(item) { var enableCheckboxDisabled = !extension.userMayModify || extension.disableReasons.suspiciousInstall || @@ -869,7 +759,7 @@ cr.define('extensions', function() { }); // Indicator for extensions controlled by policy. - var controlNode = row.querySelector('.enable-controls'); + var controlNode = wrapper.querySelector('.enable-controls'); var indicator = controlNode.querySelector('.controlled-extension-indicator'); var needsIndicator = isOK && extension.controlledInfo; @@ -895,8 +785,7 @@ cr.define('extensions', function() { indicator.setAttribute('text' + controlledByStr, text); indicator.image.setAttribute('aria-label', text); controlNode.appendChild(indicator); - indicator.querySelector('div').setAttribute('column-type', - 'enterprise'); + wrapper.setupColumn('remove-enterprise', '[controlled-by] div'); } else if (!needsIndicator && indicator) { controlNode.removeChild(indicator); } @@ -904,11 +793,11 @@ cr.define('extensions', function() { // Developer mode //////////////////////////////////////////////////////// // First we have the id. - var idLabel = row.querySelector('.extension-id'); + var idLabel = wrapper.querySelector('.extension-id'); idLabel.textContent = ' ' + extension.id; // Then the path, if provided by unpacked extension. - this.updateVisibility_(row, '.load-path', isUnpacked, + this.updateVisibility_(wrapper, '.load-path', isUnpacked, function(item) { item.querySelector('a:first-of-type').textContent = ' ' + extension.prettifiedPath; @@ -919,32 +808,31 @@ cr.define('extensions', function() { // extension is disabled. var isRequired = !extension.userMayModify || extension.mustRemainInstalled; - this.updateVisibility_(row, '.managed-message', isRequired && + this.updateVisibility_(wrapper, '.managed-message', isRequired && !extension.disableReasons.updateRequired); // Then the 'This isn't from the webstore, looks suspicious' message. - this.updateVisibility_(row, '.suspicious-install-message', !isRequired && - extension.disableReasons.suspiciousInstall); + var isSuspicious = extension.disableReasons.suspiciousInstall; + this.updateVisibility_(wrapper, '.suspicious-install-message', + !isRequired && isSuspicious); // Then the 'This is a corrupt extension' message. - this.updateVisibility_(row, '.corrupt-install-message', !isRequired && + this.updateVisibility_(wrapper, '.corrupt-install-message', !isRequired && extension.disableReasons.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. - this.updateVisibility_(row, '.update-required-message', + this.updateVisibility_(wrapper, '.update-required-message', extension.disableReasons.updateRequired); // The 'following extensions depend on this extension' list. var hasDependents = extension.dependentExtensions.length > 0; - row.classList.toggle('developer-extras', hasDependents); - this.updateVisibility_(row, '.dependent-extensions-message', + wrapper.classList.toggle('developer-extras', hasDependents); + this.updateVisibility_(wrapper, '.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(dependentId) { var dependentExtension = null; for (var i = 0; i < this.extensions_.length; ++i) { @@ -956,7 +844,7 @@ cr.define('extensions', function() { if (!dependentExtension) return; - var depNode = dependentTemplate.cloneNode(true); + var depNode = cloneTemplate('dependent-list-item'); depNode.querySelector('.dep-extension-title').textContent = dependentExtension.name; depNode.querySelector('.dep-extension-id').textContent = @@ -966,8 +854,8 @@ cr.define('extensions', function() { }.bind(this)); // The active views. - this.updateVisibility_(row, '.active-views', extension.views.length > 0, - function(item) { + this.updateVisibility_(wrapper, '.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. @@ -997,7 +885,9 @@ cr.define('extensions', function() { (view.incognito ? ' ' + loadTimeData.getString('viewIncognito') : '') + (view.renderProcessId == -1 ? - ' ' + loadTimeData.getString('viewInactive') : ''); + ' ' + loadTimeData.getString('viewInactive') : '') + + (view.isIframe ? + ' ' + loadTimeData.getString('viewIframe') : ''); link.textContent = label; link.clickHandler = function(e) { chrome.developerPrivate.openDevTools({ @@ -1013,16 +903,13 @@ cr.define('extensions', function() { link = link.cloneNode(true); item.appendChild(link); } - }); - var allLinks = item.querySelectorAll('a'); - for (var i = 0; i < allLinks.length; ++i) { - allLinks[i].setAttribute('column-type', 'dev-activeViews' + i); - } + wrapper.setupColumn('activeView', '.active-views a:last-of-type'); + }); }); // The extension warnings (describing runtime issues). - this.updateVisibility_(row, '.extension-warnings', + this.updateVisibility_(wrapper, '.extension-warnings', extension.runtimeWarnings.length > 0, function(item) { var warningList = item.querySelector('ul'); @@ -1034,7 +921,7 @@ cr.define('extensions', function() { }); // Install warnings. - this.updateVisibility_(row, '.install-warnings', + this.updateVisibility_(wrapper, '.install-warnings', extension.installWarnings.length > 0, function(item) { var installWarningList = item.querySelector('ul'); @@ -1051,19 +938,17 @@ cr.define('extensions', function() { if (location.hash.substr(1) == extension.id) { // Scroll beneath the fixed header so that the extension is not // obscured. - var topScroll = row.offsetTop - $('page-header').offsetHeight; - var pad = parseInt(window.getComputedStyle(row, null).marginTop, 10); + var topScroll = wrapper.offsetTop - $('page-header').offsetHeight; + var pad = parseInt(window.getComputedStyle(wrapper).marginTop, 10); if (!isNaN(pad)) topScroll -= pad / 2; setScrollTopForDocument(document, topScroll); } - - row.updateFocusableElements(); }, /** * Updates an element's textContent. - * @param {Element} node Ancestor of the element specified by |query|. + * @param {Node} node Ancestor of the element specified by |query|. * @param {string} query A query to select an element in |node|. * @param {string} textContent * @private @@ -1075,7 +960,7 @@ cr.define('extensions', function() { /** * Updates an element's visibility and calls |shownCallback| if it is * visible. - * @param {Element} node Ancestor of the element specified by |query|. + * @param {Node} 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 @@ -1084,10 +969,10 @@ cr.define('extensions', function() { * @private */ updateVisibility_: function(node, query, visible, opt_shownCallback) { - var item = assert(node.querySelector(query)); - item.hidden = !visible; + var element = assertInstanceof(node.querySelector(query), Element); + element.hidden = !visible; if (visible && opt_shownCallback) - opt_shownCallback(item); + opt_shownCallback(element); }, /** @@ -1112,7 +997,7 @@ cr.define('extensions', function() { return; if (scroll) - this.scrollToNode_(extensionId); + this.scrollToWrapper_(extensionId); // Add the options query string. Corner case: the 'options' query string // will clobber the 'id' query string if the options link is clicked when @@ -1161,12 +1046,12 @@ cr.define('extensions', function() { }, /** - * Updates the node for the extension. + * Updates or creates a wrapper for |extension|. * @param {!ExtensionInfo} extension The information about the extension to * update. * @private */ - updateExtension_: function(extension) { + updateOrCreateWrapper_: function(extension) { var currIndex = this.getIndexOfExtension_(extension.id); if (currIndex != -1) { // If there is a current version of the extension, update it with the @@ -1180,12 +1065,12 @@ cr.define('extensions', function() { this.extensions_.sort(compareExtensions); } - var node = /** @type {ExtensionFocusRow} */ ($(extension.id)); - if (node) { - this.updateNode_(extension, node); + var wrapper = $(extension.id); + if (wrapper) { + this.updateWrapper_(extension, wrapper); } else { var nextExt = this.extensions_[this.extensions_.indexOf(extension) + 1]; - this.createNode_(extension, nextExt ? $(nextExt.id) : null); + this.createWrapper_(extension, nextExt ? $(nextExt.id) : null); } } }; diff --git a/chromium/chrome/browser/resources/extensions/extensions.css b/chromium/chrome/browser/resources/extensions/extensions.css index c94dfad6d5a..011d0f7d3d8 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.css +++ b/chromium/chrome/browser/resources/extensions/extensions.css @@ -180,6 +180,7 @@ html[dir='rtl'] .extension-list-item { .extension-title { -webkit-padding-end: 20px; + -webkit-user-select: text; color: rgb(48, 57, 66); display: inline; font-size: 14px; @@ -414,7 +415,7 @@ html[dir='rtl'] #extension-settings .trash { } .profile-is-supervised-banner .page-banner-text { - background-image: url(chrome://theme/IDR_WARNING); + background-image: url(../../../../ui/webui/resources/images/warning.svg); } /* Sideload Wipeout */ diff --git a/chromium/chrome/browser/resources/extensions/extensions.html b/chromium/chrome/browser/resources/extensions/extensions.html index 58bbf471d84..155714e11e5 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.html +++ b/chromium/chrome/browser/resources/extensions/extensions.html @@ -127,7 +127,7 @@ <span id="font-measuring-div"></span> -<div id="template-collection" hidden> +<div id="templates" hidden> <div class="extension-list-item-wrapper"> <div class="extension-list-item"> diff --git a/chromium/chrome/browser/resources/extensions/extensions.js b/chromium/chrome/browser/resources/extensions/extensions.js index 863d2fe6b60..e957033c9c8 100644 --- a/chromium/chrome/browser/resources/extensions/extensions.js +++ b/chromium/chrome/browser/resources/extensions/extensions.js @@ -9,6 +9,7 @@ <include src="extension_commands_overlay.js"> <include src="extension_error_overlay.js"> <include src="extension_focus_manager.js"> +<include src="focus_row.js"> <include src="extension_list.js"> <include src="pack_extension_overlay.js"> <include src="extension_loader.js"> @@ -125,6 +126,12 @@ cr.define('extensions', function() { dragEnabled_: false, /** + * True if the page has finished the initial load. + * @private {boolean} + */ + hasLoaded_: false, + + /** * Perform initial setup. */ initialize: function() { @@ -152,7 +159,6 @@ cr.define('extensions', function() { $('toggle-dev-on').addEventListener('change', function(e) { this.updateDevControlsVisibility_(true); - extensionList.updateFocusableElements(); chrome.developerPrivate.updateProfileConfiguration( {inDeveloperMode: e.target.checked}); var suffix = $('toggle-dev-on').checked ? 'Enabled' : 'Disabled'; @@ -241,7 +247,11 @@ cr.define('extensions', function() { * @private */ update_: function(profileInfo) { - this.setLoading_(true); + // We only set the page to be loading if we haven't already finished an + // initial load, because otherwise the updates are all incremental and + // don't need to display the interstitial spinner. + if (!this.hasLoaded_) + this.setLoading_(true); webuiResponded = true; /** @const */ @@ -260,7 +270,10 @@ cr.define('extensions', function() { extensionList.updateExtensionsData( profileInfo.isIncognitoAvailable, profileInfo.appInfoDialogEnabled).then(function() { - this.setLoading_(false); + if (!this.hasLoaded_) { + this.hasLoaded_ = true; + this.setLoading_(false); + } this.onExtensionCountChanged(); }.bind(this)); }, @@ -392,13 +405,11 @@ cr.define('extensions', function() { lastFocused = document.activeElement; $('overlay').addEventListener('cancelOverlay', function f() { - console.log('cancelOverlay'); - console.log('lastFocused', lastFocused); - console.log('focusOutlineManager.visible', focusOutlineManager.visible); if (lastFocused && focusOutlineManager.visible) lastFocused.focus(); $('overlay').removeEventListener('cancelOverlay', f); + uber.replaceState({}, ''); }); node.classList.add('showing'); } diff --git a/chromium/chrome/browser/resources/extensions/focus_row.js b/chromium/chrome/browser/resources/extensions/focus_row.js new file mode 100644 index 00000000000..6e9b078f302 --- /dev/null +++ b/chromium/chrome/browser/resources/extensions/focus_row.js @@ -0,0 +1,30 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +cr.define('extensions', function() { + /** + * @param {!Element} root + * @param {Node} boundary + * @constructor + * @extends {cr.ui.FocusRow} + */ + function FocusRow(root, boundary) { + cr.ui.FocusRow.call(this, root, boundary); + } + + FocusRow.prototype = { + __proto__: cr.ui.FocusRow.prototype, + + /** @override */ + makeActive: function(active) { + cr.ui.FocusRow.prototype.makeActive.call(this, active); + + // Only highlight if the row has focus. + this.root.classList.toggle('extension-highlight', + active && this.root.contains(document.activeElement)); + }, + }; + + return {FocusRow: FocusRow}; +}); |