Bug 1483828 - [Part 1] Disallow <tab> to move focus to root element r=smaug

Brings us on-par with Chrome and Safari

Differential Revision: https://phabricator.services.mozilla.com/D198436
This commit is contained in:
Sean Feng 2024-03-04 14:16:30 +00:00
Родитель 4b63bf2834
Коммит 8f46bf2a0c
9 изменённых файлов: 133 добавлений и 46 удалений

Просмотреть файл

@ -246,7 +246,10 @@ window.onload = function() {
synthesizeKey("KEY_Tab", { shiftKey: true });
},
state: { current: 0, active: null },
activeElement: listEl.ownerDocument.documentElement,
activeElement:
!SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element.enabled")
? listEl.ownerDocument.documentElement
: defaultFocus,
}];
for (const test of tests) {

Просмотреть файл

@ -4522,7 +4522,7 @@ nsresult nsFocusManager::GetNextTabbableContent(
if (aCurrentTabIndex == (aForward ? 0 : 1)) {
// if going backwards, the canvas should be focused once the beginning
// has been reached, so get the root element.
if (!aForward) {
if (!aForward && !StaticPrefs::dom_disable_tab_focus_to_root_element()) {
nsCOMPtr<nsPIDOMWindowOuter> window = GetCurrentWindow(aRootContent);
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
@ -4576,7 +4576,7 @@ bool nsFocusManager::TryToMoveFocusToSubDocument(
NS_ASSERTION(doc, "content not in document");
Document* subdoc = doc->GetSubDocumentFor(aCurrentContent);
if (subdoc && !subdoc->EventHandlingSuppressed()) {
if (aForward) {
if (aForward && !StaticPrefs::dom_disable_tab_focus_to_root_element()) {
// When tabbing forward into a frame, return the root
// frame so that the canvas becomes focused.
if (nsCOMPtr<nsPIDOMWindowOuter> subframe = subdoc->GetWindow()) {
@ -4597,6 +4597,13 @@ bool nsFocusManager::TryToMoveFocusToSubDocument(
if (*aResultContent) {
return true;
}
if (rootElement->IsEditable() &&
StaticPrefs::dom_disable_tab_focus_to_root_element()) {
// Only move to the root element with a valid reason
*aResultContent = rootElement;
NS_ADDREF(*aResultContent);
return true;
}
}
}
}

Просмотреть файл

@ -79,10 +79,16 @@
opener.is(lastFocusTarget, shadowDate, "Should have focused date element in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (3)");
synthesizeKey("KEY_Tab");
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (3)");
let canTabMoveFocusToRootElement =
!SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
if (canTabMoveFocusToRootElement) {
synthesizeKey("KEY_Tab");
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (3)");
}
synthesizeKey("KEY_Tab");
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.body.firstChild,
@ -99,10 +105,12 @@
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.body.firstChild,
"Should have focused input element in shadow iframe. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (4)");
if (canTabMoveFocusToRootElement) {
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(shadowIframe.contentDocument.activeElement,
shadowIframe.contentDocument.documentElement,
"Should have focused document element in shadow iframe. (4)");
}
synthesizeKey("KEY_Tab", {shiftKey: true});
opener.is(lastFocusTarget, shadowDate, "Should have focused date element with a calendar button in shadow DOM. (4)");
synthesizeKey("KEY_Tab", {shiftKey: true});

Просмотреть файл

@ -20,7 +20,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=226361
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
@ -43,8 +43,11 @@ function tab_to(id) {
}
function tab_iframe() {
doc = document;
tab_to('iframe');
let canTabMoveFocusToRootElement = !SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
if (canTabMoveFocusToRootElement) {
doc = document;
tab_to('iframe');
}
// inside iframe
doc = document.getElementById('iframe').contentDocument

Просмотреть файл

@ -56,10 +56,12 @@ function runTest(aObjectOrEmbed)
const pbutton = document.getElementById("pbutton");
pbutton.focus();
synthesizeKey("KEY_Tab");
is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after tab`);
is(childDoc.activeElement, childDoc.documentElement, `${desc}: focus in child after tab`);
let canTabMoveFocusToRootElement = !SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
if (canTabMoveFocusToRootElement) {
synthesizeKey("KEY_Tab");
is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after tab`);
is(childDoc.activeElement, childDoc.documentElement, `${desc}: focus in child after tab`);
}
synthesizeKey("KEY_Tab");
is(document.activeElement, aObjectOrEmbed, `${desc}: focus in parent after tab 2`);
is(childDoc.activeElement, button, `${desc}: focus in child after tab 2`);

Просмотреть файл

@ -2530,6 +2530,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvNavigateByKey(
aForward
? (aForDocumentNavigation
? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRSTDOC)
: StaticPrefs::dom_disable_tab_focus_to_root_element()
? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRST)
: static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_ROOT))
: (aForDocumentNavigation
? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LASTDOC)

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Tests for for-of loops</title>
<title>Test for using TAB to move focus and scroll into view</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
@ -11,10 +11,18 @@
<div id="content" style="display: none"></div>
<script>
function doTest() {
var canTabMoveFocusToRootElement =
!SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
document.getElementById("button").focus();
const iframe = document.querySelector("iframe");
is(window.scrollY, 0, "Scrolled position initially 0");
synthesizeKey("KEY_Tab");
ok(window.scrollY > 200, "Scrolled child frame into view");
is(document.activeElement, iframe, "Focus moved to the iframe");
if (canTabMoveFocusToRootElement) {
ok(window.scrollY > 200, "Scrolled child frame into view");
} else {
is(window.scrollY, 0, "Scrolled position remained the same");
}
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();

Просмотреть файл

@ -4817,6 +4817,12 @@
value: true
mirror: always
# Whether allowing using <tab> to move focus to root elements
- name: dom.disable_tab_focus_to_root_element
type: bool
value: @IS_NIGHTLY_BUILD@
mirror: always
#---------------------------------------------------------------------------
# Prefs starting with "editor"
#---------------------------------------------------------------------------

Просмотреть файл

@ -49,6 +49,8 @@ function onUnload() {
var gFocusObservingElement = null;
var gBlurObservingElement = null;
var canTabMoveFocusToRootElement =
!SpecialPowers.getBoolPref("dom.disable_tab_focus_to_root_element");
function onFocus(aEvent) {
if (aEvent.target != gFocusObservingElement) {
@ -201,18 +203,30 @@ function runTests() {
root = iframe.contentDocument.documentElement;
resetFocusToInput("initializing for iframe_not_editable");
testTabKey(true, root, false, prev, true,
false, "input#prev[readonly] -> html");
testTabKey(true, editor, true, root, false,
true, "html -> input in the subdoc");
if (canTabMoveFocusToRootElement) {
testTabKey(true, root, false, prev, true,
false, "input#prev[readonly] -> html");
testTabKey(true, editor, true, root, false,
true, "html -> input in the subdoc");
} else {
testTabKey(true, editor, true, prev, true,
true, "input#prev[readonly] -> input in the subdoc");
}
testTabKey(true, next, true, editor, true,
false, "input in the subdoc -> input#next[readonly]");
testTabKey(false, editor, true, next, true,
true, "input#next[readonly] -> input in the subdoc");
testTabKey(false, root, false, editor, true,
false, "input in the subdoc -> html");
testTabKey(false, prev, true, root, false,
false, "html -> input#next[readonly]");
if (canTabMoveFocusToRootElement) {
testTabKey(false, root, false, editor, true,
false, "input in the subdoc -> html");
testTabKey(false, prev, true, root, false,
false, "html -> input#next[readonly]");
} else {
testTabKey(false, prev, true, editor, true,
false, "input in the subdoc -> input#prev[readonly]");
testTabKey(false, next, true, prev, true,
false, "input#prev[readonly] -> input#next[readonly]");
}
iframe.style.display = "none";
@ -236,8 +250,13 @@ function runTests() {
resetFocusToParentHTML("testing iframe_html");
testTabKey(true, editor, true, html, false,
true, "html of parent -> html[contentediable=true]");
testTabKey(false, html, false, editor, true,
false, "html[contenteditable=true] -> html of parent");
if (canTabMoveFocusToRootElement) {
testTabKey(false, html, false, editor, true,
false, "html[contenteditable=true] -> html of parent");
} else {
testTabKey(false, next, true, editor, true,
false, "html[contenteditable=true] -> input#next[readonly]");
}
prev.style.display = "inline";
resetFocusToInput("after parent html <-> html[contenteditable=true]");
@ -270,8 +289,13 @@ function runTests() {
resetFocusToParentHTML("testing iframe_designMode");
testTabKey(true, root, false, html, false,
true, "html of parent -> html in designMode");
testTabKey(false, html, false, root, false,
false, "html in designMode -> html of parent");
if (canTabMoveFocusToRootElement) {
testTabKey(false, html, false, root, false,
false, "html[contenteditable=true] -> html of parent");
} else {
testTabKey(false, next, true, root, false,
false, "html in designMode -> html of parent");
}
prev.style.display = "inline";
resetFocusToInput("after parent html <-> html in designMode");
@ -302,8 +326,13 @@ function runTests() {
resetFocusToParentHTML("testing iframe_body");
testTabKey(true, editor, true, html, false,
true, "html of parent -> body[contentediable=true]");
testTabKey(false, html, false, editor, true,
false, "body[contenteditable=true] -> html of parent");
if (canTabMoveFocusToRootElement) {
testTabKey(false, html, false, editor, true,
false, "body[contenteditable=true] -> html of parent");
} else {
testTabKey(false, next, true, editor, true,
false, "body[contenteditable=true] -> input#next[readonly]");
}
prev.style.display = "inline";
resetFocusToInput("after parent html <-> body[contenteditable=true]");
@ -321,25 +350,44 @@ function runTests() {
root = iframe.contentDocument.documentElement;
resetFocusToInput("initializing for iframe_p");
testTabKey(true, root, false, prev, true,
false, "input#prev[readonly] -> html (has p[contenteditable=true])");
testTabKey(true, editor, true, root, false,
true, "html (has p[contenteditable=true]) -> p[contentediable=true]");
if (canTabMoveFocusToRootElement) {
testTabKey(true, root, false, prev, true,
false, "input#prev[readonly] -> html (has p[contenteditable=true])");
testTabKey(true, editor, true, root, false,
true, "html (has p[contenteditable=true]) -> p[contentediable=true]");
} else {
testTabKey(true, editor, true, prev, true,
true, "input#prev[readonly] -> p[contenteditable=true]");
}
testTabKey(true, next, true, editor, true,
false, "p[contentediable=true] -> input#next[readonly]");
testTabKey(false, editor, true, next, true,
true, "input#next[readonly] -> p[contentediable=true]");
testTabKey(false, root, false, editor, true,
false, "p[contenteditable=true] -> html (has p[contenteditable=true])");
testTabKey(false, prev, true, root, false,
false, "html (has p[contenteditable=true]) -> input#prev[readonly]");
if (canTabMoveFocusToRootElement) {
testTabKey(false, root, false, editor, true,
false, "p[contenteditable=true] -> html (has p[contenteditable=true])");
testTabKey(false, prev, true, root, false,
false, "html (has p[contenteditable=true]) -> input#prev[readonly]");
} else {
testTabKey(false, prev, true, editor, true,
false, "p[contenteditable=true] -> html (has p[contenteditable=true])");
testTabKey(false, next, true, prev, true,
false, "html (has p[contenteditable=true]) -> input#next[readonly]");
}
prev.style.display = "none";
resetFocusToParentHTML("testing iframe_p");
testTabKey(true, root, false, html, false,
false, "html of parent -> html (has p[contentediable=true])");
testTabKey(false, html, false, root, false,
false, "html (has p[contentediable=true]) -> html of parent");
if (canTabMoveFocusToRootElement) {
testTabKey(true, root, false, html, false,
false, "html of parent -> html (has p[contentediable=true])");
testTabKey(false, html, false, root, false,
false, "html (has p[contentediable=true]) -> html of parent");
} else {
testTabKey(true, editor, true, html, false,
true, "html of parent -> p[contentediable=true]");
testTabKey(false, next, true, editor, true,
false, "p[contentediable=true] -> input#next[readonly]");
}
prev.style.display = "inline";
resetFocusToInput("after parent html <-> html (has p[contentediable=true])");