Commit 13dfcd6d authored by rbpotter's avatar rbpotter Committed by Commit Bot

History and Settings: Remove tabIndex = -1

Setting tabIndex=-1 no longer allows child elements to be focusable in
Shadow DOM v1 (see bug for additional context).

Remove logic that does this, and in order to prevent focusing of the
parent element when traversing backwards from the first item in the
focus row, listen for shift+tab on this element.

Bug: 896624
Change-Id: I35e871863418e7bca57f648713b8d172677a949c
Reviewed-on: https://chromium-review.googlesource.com/c/1325352
Commit-Queue: Rebekah Potter <rbpotter@chromium.org>
Reviewed-by: default avatarScott Chen <scottchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608546}
parent b3cc34d8
...@@ -137,8 +137,12 @@ cr.define('md_history', function() { ...@@ -137,8 +137,12 @@ cr.define('md_history', function() {
this.row_ = new HistoryFocusRow( this.row_ = new HistoryFocusRow(
this.$['main-container'], null, new FocusRowDelegate(this)); this.$['main-container'], null, new FocusRowDelegate(this));
this.row_.makeActive(this.ironListTabIndex == 0); this.row_.makeActive(this.ironListTabIndex == 0);
// Adding listeners asynchronously to reduce blocking time, since these
// history items are items in a potentially long list.
this.listen(this, 'focus', 'onFocus_'); this.listen(this, 'focus', 'onFocus_');
this.listen(this, 'dom-change', 'onDomChange_'); this.listen(this, 'dom-change', 'onDomChange_');
this.listen(this.$.checkbox, 'keydown', 'onCheckboxKeydown_');
}); });
}, },
...@@ -146,6 +150,7 @@ cr.define('md_history', function() { ...@@ -146,6 +150,7 @@ cr.define('md_history', function() {
detached: function() { detached: function() {
this.unlisten(this, 'focus', 'onFocus_'); this.unlisten(this, 'focus', 'onFocus_');
this.unlisten(this, 'dom-change', 'onDomChange_'); this.unlisten(this, 'dom-change', 'onDomChange_');
this.unlisten(this.$.checkbox, 'keydown', 'onCheckboxKeydown_');
if (this.row_) if (this.row_)
this.row_.destroy(); this.row_.destroy();
}, },
...@@ -164,8 +169,12 @@ cr.define('md_history', function() { ...@@ -164,8 +169,12 @@ cr.define('md_history', function() {
this.row_.getEquivalentElement(this.lastFocused).focus(); this.row_.getEquivalentElement(this.lastFocused).focus();
else else
this.row_.getFirstFocusable().focus(); this.row_.getFirstFocusable().focus();
},
this.tabIndex = -1; /** @param {!KeyboardEvent} e */
onCheckboxKeydown_: function(e) {
if (e.shiftKey && e.key === 'Tab')
this.focus();
}, },
/** /**
......
...@@ -19,7 +19,6 @@ class FocusRowDelegate { ...@@ -19,7 +19,6 @@ class FocusRowDelegate {
*/ */
onFocus(row, e) { onFocus(row, e) {
this.listItem_.lastFocused = e.path[0]; this.listItem_.lastFocused = e.path[0];
this.listItem_.tabIndex = -1;
} }
/** /**
...@@ -88,6 +87,12 @@ const FocusRowBehavior = { ...@@ -88,6 +87,12 @@ const FocusRowBehavior = {
}, },
}, },
/** @private {?Element} */
firstControl_: null,
/** @private {!Array<!MutationObserver>} */
controlObservers_: [],
/** @override */ /** @override */
attached: function() { attached: function() {
this.classList.add('no-outline'); this.classList.add('no-outline');
...@@ -114,13 +119,42 @@ const FocusRowBehavior = { ...@@ -114,13 +119,42 @@ const FocusRowBehavior = {
this.unlisten(this, 'dom-change', 'addItems_'); this.unlisten(this, 'dom-change', 'addItems_');
this.unlisten(this, 'mousedown', 'onMouseDown_'); this.unlisten(this, 'mousedown', 'onMouseDown_');
this.unlisten(this, 'blur', 'onBlur_'); this.unlisten(this, 'blur', 'onBlur_');
this.removeObservers_();
if (this.row_) if (this.row_)
this.row_.destroy(); this.row_.destroy();
}, },
/** @private */
updateFirstControl_: function() {
const newFirstControl = this.row_.getFirstFocusable();
if (newFirstControl === this.firstControl_)
return;
if (this.firstControl_)
this.unlisten(this.firstControl_, 'keydown', 'onFirstControlKeydown_');
this.firstControl_ = newFirstControl;
if (this.firstControl_) {
this.listen(
/** @type {!Element} */ (this.firstControl_), 'keydown',
'onFirstControlKeydown_');
}
},
/** @private */
removeObservers_: function() {
if (this.firstControl_)
this.unlisten(this.firstControl_, 'keydown', 'onFirstControlKeydown_');
if (this.controlObservers_.length > 0)
this.controlObservers_.forEach(observer => {
observer.disconnect();
});
this.controlObservers_ = [];
},
/** @private */ /** @private */
addItems_: function() { addItems_: function() {
if (this.row_) { if (this.row_) {
this.removeObservers_();
this.row_.destroy(); this.row_.destroy();
const controls = this.root.querySelectorAll('[focus-row-control]'); const controls = this.root.querySelectorAll('[focus-row-control]');
...@@ -130,7 +164,55 @@ const FocusRowBehavior = { ...@@ -130,7 +164,55 @@ const FocusRowBehavior = {
control.getAttribute('focus-type'), control.getAttribute('focus-type'),
/** @type {!HTMLElement} */ /** @type {!HTMLElement} */
(cr.ui.FocusRow.getFocusableElement(control))); (cr.ui.FocusRow.getFocusableElement(control)));
this.addMutationObservers_(assert(control));
}); });
this.updateFirstControl_();
}
},
/**
* @return {!MutationObserver}
* @private
*/
createObserver_: function() {
return new MutationObserver(mutations => {
const mutation = mutations[0];
if (mutation.attributeName === 'style' && mutation.oldValue) {
const newStyle =
window.getComputedStyle(/** @type {!Element} */ (mutation.target));
const oldDisplayValue = mutation.oldValue.match(/^display:(.*)(?=;)/);
const oldVisibilityValue =
mutation.oldValue.match(/^visibility:(.*)(?=;)/);
// Return early if display and visibility have not changed.
if (oldDisplayValue && newStyle.display === oldDisplayValue[1].trim() &&
oldVisibilityValue &&
newStyle.visibility === oldVisibilityValue[1].trim()) {
return;
}
}
this.updateFirstControl_();
});
},
/**
* The first focusable control changes if hidden, disabled, or style.display
* changes for the control or any of its ancestors. Add mutation observers to
* watch for these changes in order to ensure the first control keydown
* listener is always on the correct element.
* @param {!Element} control
* @private
*/
addMutationObservers_: function(control) {
let current = control;
while (current && current != this.root) {
const currentObserver = this.createObserver_();
currentObserver.observe(current, {
attributes: true,
attributeFilter: ['hidden', 'disabled', 'style'],
attributeOldValue: true,
});
this.controlObservers_.push(currentObserver);
current = current.parentNode;
} }
}, },
...@@ -147,11 +229,15 @@ const FocusRowBehavior = { ...@@ -147,11 +229,15 @@ const FocusRowBehavior = {
if (this.lastFocused) { if (this.lastFocused) {
this.row_.getEquivalentElement(this.lastFocused).focus(); this.row_.getEquivalentElement(this.lastFocused).focus();
} else { } else {
const firstFocusable = assert(this.row_.getFirstFocusable()); const firstFocusable = assert(this.firstControl_);
firstFocusable.focus(); firstFocusable.focus();
} }
},
this.tabIndex = -1; /** @param {!KeyboardEvent} e */
onFirstControlKeydown_: function(e) {
if (e.shiftKey && e.key === 'Tab')
this.focus();
}, },
/** @private */ /** @private */
......
...@@ -69,12 +69,27 @@ Polymer({ ...@@ -69,12 +69,27 @@ Polymer({
/** @private {?settings.LocalDataBrowserProxy} */ /** @private {?settings.LocalDataBrowserProxy} */
localDataBrowserProxy_: null, localDataBrowserProxy_: null,
/** @private {?Element} */
button_: null,
/** @override */ /** @override */
created: function() { created: function() {
this.localDataBrowserProxy_ = this.localDataBrowserProxy_ =
settings.LocalDataBrowserProxyImpl.getInstance(); settings.LocalDataBrowserProxyImpl.getInstance();
}, },
/** @override */
detached: function() {
if (this.button_)
this.unlisten(this.button_, 'keydown', 'onButtonKeydown_');
},
/** @param {!KeyboardEvent} e */
onButtonKeydown_: function(e) {
if (e.shiftKey && e.key === 'Tab')
this.focus();
},
/** /**
* Whether the list of origins displayed in this site-entry is a group of * Whether the list of origins displayed in this site-entry is a group of
* eTLD+1 origins or not. * eTLD+1 origins or not.
...@@ -120,7 +135,14 @@ Polymer({ ...@@ -120,7 +135,14 @@ Polymer({
onSiteGroupChanged_: function(siteGroup) { onSiteGroupChanged_: function(siteGroup) {
this.displayName_ = this.siteRepresentation_(siteGroup, -1); this.displayName_ = this.siteRepresentation_(siteGroup, -1);
if (!this.grouped_(SiteGroup)) { // Update the button listener.
if (this.button_)
this.unlisten(this.button_, 'keydown', 'onButtonKeydown_');
this.button_ = /** @type Element */
(this.root.querySelector('#toggleButton *:not([hidden]) button'));
this.listen(assert(this.button_), 'keydown', 'onButtonKeydown_');
if (!this.grouped_(siteGroup)) {
// Ensure ungrouped |siteGroup|s do not get stuck in an opened state. // Ensure ungrouped |siteGroup|s do not get stuck in an opened state.
if (this.$.collapseChild.opened) if (this.$.collapseChild.opened)
this.toggleCollapsible_(); this.toggleCollapsible_();
...@@ -370,9 +392,6 @@ Polymer({ ...@@ -370,9 +392,6 @@ Polymer({
* @private * @private
*/ */
onFocus_: function() { onFocus_: function() {
const button = /** @type Element */ this.button_.focus();
(this.root.querySelector('#toggleButton *:not([hidden]) button'));
button.focus();
this.tabIndex = -1;
}, },
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment