Bug 582459 - Reset the focused element when a frame loses document focus; r=mikedeboer,NeilDeakin,hsivonen,jaws

Resetting focus would also clear selection on editable element, so get
current selected text before moving focus to findbar to make
prefill-with-selection work if the content is loaded in chrome process.

Differential Revision: https://phabricator.services.mozilla.com/D89557
This commit is contained in:
Edgar Chen 2020-12-21 21:35:49 +00:00
Родитель 4b4f627747
Коммит 0fd119dae0
13 изменённых файлов: 192 добавлений и 26 удалений

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

@ -109,15 +109,15 @@ add_task(async function test_dialog_focus_trap() {
ok(displayElChildSpan.tabIndex === -1, "The tabIndex value for elements with a hardcoded tabIndex attribute should be reset to '-1'.")
ok(displayElChildSpan.dataset.oldTabIndex === "0", "Existing tabIndex values should be stored in `dataset.oldTabIndex`.")
const isActiveElemDialogOrHTML = (elemTagName) => {
return (["HTML", "CONFIRMATION-DIALOG"].includes(elemTagName));
const isActiveElemDialogOrHTMLorBODY = (elemTagName) => {
return (["HTML", "BODY", "CONFIRMATION-DIALOG"].includes(elemTagName));
}
let iterator = 0;
while(iterator < 20) {
sendKey("TAB");
isnot(document.activeElement.id, "display-child", "The display-child element should not gain focus when the dialog is showing");
is(isActiveElemDialogOrHTML(document.activeElement.tagName), true, "The confirmation-dialog should always have focus when the dialog is showing");
ok(isActiveElemDialogOrHTMLorBODY(document.activeElement.tagName), "The confirmation-dialog should always have focus when the dialog is showing");
iterator++;
}
});

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

@ -1626,13 +1626,6 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
}
// return if blurring fails or the focus changes during the blur
if (focusedBrowsingContext) {
// if the focus is being moved to another element in the same document,
// or to a descendant, pass the existing window to Blur so that the
// current node in the existing window is cleared. If moving to a
// window elsewhere, we want to maintain the current node in the
// window but still blur it.
bool currentIsSameOrAncestor =
IsSameOrAncestor(focusedBrowsingContext, newWindow);
// find the common ancestor of the currently focused window and the new
// window. The ancestor will need to have its currently focused node
// cleared once the document has been blurred. Otherwise, we'll be in a
@ -1650,11 +1643,28 @@ void nsFocusManager::SetFocusInner(Element* aNewContent, int32_t aFlags,
commonAncestor = GetCommonAncestor(newWindow, focusedBrowsingContext);
}
if (!Blur(
currentIsSameOrAncestor ? focusedBrowsingContext.get() : nullptr,
commonAncestor ? commonAncestor.get() : nullptr,
focusMovesToDifferentBC, aAdjustWidget, aActionId,
elementToFocus)) {
bool needToClearFocusedElement = false;
if (focusedBrowsingContext->IsChrome()) {
// Always reset focused element if focus is currently in chrome window.
needToClearFocusedElement = true;
} else {
// Only reset focused element if focus moves within the same top-level
// content window.
if (focusedBrowsingContext->Top() == browsingContextToFocus->Top()) {
// XXX for the case that we try to focus an
// already-focused-remote-frame, we would still send blur and focus
// IPC to it, but they will not generate blur or focus event, we don't
// want to reset activeElement on the remote frame.
needToClearFocusedElement = (focusMovesToDifferentBC ||
focusedBrowsingContext->IsInProcess());
}
}
if (!Blur(needToClearFocusedElement ? focusedBrowsingContext.get()
: nullptr,
commonAncestor ? commonAncestor.get() : nullptr,
focusMovesToDifferentBC, aAdjustWidget, aActionId,
elementToFocus)) {
return;
}
}
@ -3545,11 +3555,12 @@ nsresult nsFocusManager::DetermineElementToMoveFocus(
docShell->TabToTreeOwner(forward, forDocumentNavigation, &tookFocus);
// If the tree owner took the focus, blur the current element.
if (tookFocus) {
nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
if (window->GetFocusedElement() == mFocusedElement) {
if (GetFocusedBrowsingContext() &&
GetFocusedBrowsingContext()->IsInProcess()) {
Blur(GetFocusedBrowsingContext(), nullptr, true, true,
GenerateFocusActionId());
} else {
nsCOMPtr<nsPIDOMWindowOuter> window = docShell->GetWindow();
window->SetFocusedElement(nullptr);
}
return NS_OK;

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

@ -59,7 +59,6 @@ skip-if = verify
[test_bug448602.html]
[test_bug450876.html]
skip-if = verify
fail-if = (xorigin && fission)
[test_bug456273.html]
[test_bug457672.html]
skip-if = toolkit == 'android' #CRASH_DUMP, RANDOM

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

@ -31,7 +31,7 @@ function doTest() {
is(document.activeElement, document.getElementById('a'), "link should have focus");
is(document.hasFocus(), true, "document should be focused");
synthesizeKey("KEY_Tab");
is(document.activeElement, document.getElementById('a'), "body element should be focused");
is(document.activeElement, document.body, "body element should be focused");
is(document.hasFocus(), false, "document should not be focused");
SimpleTest.finish();

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

@ -1448,8 +1448,7 @@ function doFrameSwitchingTests()
[framed.contentDocument, "blur", null, null, window, null],
[framed.contentWindow, "blur", null, null, window, null],
[frameb.contentDocument, "focus", null, frameb.contentWindow, window, frameb],
[frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb],
[inputb, "focus", inputb, frameb.contentWindow, window, frameb]];
[frameb.contentWindow, "focus", null, frameb.contentWindow, window, frameb]];
fm.focusedWindow = frameb.contentWindow;
ok(gEventMatched && gExpectedEvents.length == 0, "frame switch using focusedWindow");
@ -1461,8 +1460,7 @@ function doFrameSwitchingTests()
// focus a sibling frame by setting focusedWindow when no element is focused in that frame
gEventMatched = true;
gExpectedEvents = [[inputb, "blur", null, frameb.contentWindow, window, frameb],
[frameb.contentDocument, "blur", null, null, window, null],
gExpectedEvents = [[frameb.contentDocument, "blur", null, null, window, null],
[frameb.contentWindow, "blur", null, null, window, null],
[framec.contentDocument, "focus", null, framec.contentWindow, window, framea],
[framec.contentWindow, "focus", null, framec.contentWindow, window, framea]];

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

@ -53,5 +53,5 @@ needs-focus == semitransparent-decoration-line.html semitransparent-decoration-l
fuzzy-if(OSX,0-1,0-6) fuzzy-if(Android,0-188,0-39) needs-focus == writing-mode.html writing-mode-ref.html
needs-focus == 1478604.html 1478604-ref.html
needs-focus != disabled-1.html disabled-1-notref.html
needs-focus == disabled-1.html disabled-1-notref.html
needs-focus != disabled-2.html disabled-2-notref.html

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

@ -0,0 +1,50 @@
<!doctype html>
<meta charset=utf-8>
<title>iframe activeElement after focusing out iframe</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
function waitForEvent(target, event, checkFn) {
return new Promise(resolve => {
target.addEventListener(event, e => {
if (checkFn && !checkFn(e)) {
return;
}
resolve();
}, { once: true });
});
}
function focusTopLevel(w) {
w.postMessage("focus", "*");
}
async function getLog(w) {
let log = "";
step_timeout(function() {
w.postMessage("getlog", "*");
}, 0);
await waitForEvent(window, "message", (e) => {
log = e.data;
return true;
});
return log;
}
async function runTest(t, url) {
let w = window.open(url);
t.add_cleanup(() => { w.close(); });
await waitForEvent(window, "message", e => e.data === "ready");
focusTopLevel(w);
assert_equals(await getLog(w), 'outerlog:willfocusinput,windowfocus,didfocusinput,innerlog:willfocusinput,windowfocus,didfocusinput,activeElement:INPUT,windowblur,activeElement:BODY,');
}
promise_test(async t => {
await runTest(t, "support/iframe-activeelement-after-focusing-out-different-site-iframes-outer.sub.html");
}, "Check iframe activeElement after focusing out different site iframe");
promise_test(async t => {
await runTest(t, "support/iframe-activeelement-after-focusing-out-same-site-iframes-outer.html");
}, "Check iframe activeElement after focusing out same site iframe");
</script>

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

@ -0,0 +1,34 @@
<!doctype html>
<meta charset="utf-8">
<title>iframe active element after focusing out different site iframes outer</title>
<input></input></br>
<iframe src="http://{{hosts[alt][www]}}:{{ports[http][0]}}/focus/support/iframe-activeelement-after-focusing-out-iframes-inner.html"></iframe>
<script>
let outerlog = "outerlog:";
let input = document.querySelector("input");
let iframe = document.querySelector("iframe");
window.onmessage = function(e) {
if (e.data == "ready") {
opener.postMessage("ready", "*");
} else if (e.data == "focus") {
outerlog += "willfocusinput,";
input.focus();
outerlog += "didfocusinput,";
} else if (e.data == "getlog") {
iframe.contentWindow.postMessage("getlog", "*");
} else {
opener.postMessage(outerlog + e.data, "*");
}
};
window.onload = function() {
window.onfocus = function() {
outerlog += "windowfocus,";
};
window.onblur = function() {
outerlog += "windowblur,";
};
};
</script>

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

@ -0,0 +1,38 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>iframe active element inner document</title>
</head>
<body>
<h1>Inner</h1><br>
<input></input>
<script>
let innerlog = "innerlog:";
window.onmessage = function(e) {
if (e.data == "getlog") {
parent.postMessage(innerlog, "*");
}
};
window.onfocus = function() {
innerlog += "windowfocus,";
};
window.onblur = function() {
innerlog += "windowblur,";
innerlog += "activeElement:" + document.activeElement.tagName + ",";
};
let input = document.querySelector("input");
window.onload = function() {
innerlog += "willfocusinput,";
input.focus();
innerlog += "didfocusinput,";
innerlog += "activeElement:" + document.activeElement.tagName + ",";
parent.postMessage("ready", "*");
};
</script>
</body>
</html>

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

@ -0,0 +1,34 @@
<!doctype html>
<meta charset="utf-8">
<title>iframe active element after focusing out same site iframes outer</title>
<input></input></br>
<iframe src="iframe-activeelement-after-focusing-out-iframes-inner.html"></iframe>
<script>
let outerlog = "outerlog:";
let input = document.querySelector("input");
let iframe = document.querySelector("iframe");
window.onmessage = function(e) {
if (e.data == "ready") {
opener.postMessage("ready", "*");
} else if (e.data == "focus") {
outerlog += "willfocusinput,";
input.focus();
outerlog += "didfocusinput,";
} else if (e.data == "getlog") {
iframe.contentWindow.postMessage("getlog", "*");
} else {
opener.postMessage(outerlog + e.data, "*");
}
};
window.onload = function() {
window.onfocus = function() {
outerlog += "windowfocus,";
};
window.onblur = function() {
outerlog += "windowblur,";
};
};
</script>

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

@ -25,6 +25,7 @@
assert_true(e.isTrusted, "blur event is trusted");
assert_false(e.bubbles, "blur event doesn't bubble");
assert_false(e.cancelable, "blur event is not cancelable");
assert_equals(document.activeElement, document.body);
});
i1.focus();

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

@ -1147,6 +1147,8 @@
}
if (this.prefillWithSelection && userWantsPrefill) {
this.browser.finder.getInitialSelection();
// NB: We have to focus this._findField here so tests that send
// key events can open and close the find bar synchronously.
this._findField.focus();
@ -1157,7 +1159,6 @@
// jumbled up queries.
this._findField.select();
this.browser.finder.getInitialSelection();
return startFindPromise;
}

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

@ -374,8 +374,8 @@ Finder.prototype = {
},
getInitialSelection() {
let initialSelection = this.getActiveSelectionText().selectedText;
this._getWindow().setTimeout(() => {
let initialSelection = this.getActiveSelectionText().selectedText;
for (let l of this._listeners) {
try {
l.onCurrentSelection(initialSelection, true);