mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Improve inter-document focus handling (#36649)
*Describe the changes that this pull request makes here. This will be the commit message.* rewritten the PR #28571 Implement [Window#focus](https://html.spec.whatwg.org/multipage/#dom-window-focus), [Window#blur](https://html.spec.whatwg.org/multipage/#dom-window-blur) Testing: WPT Fixes: #8981 #9421 --------- Signed-off-by: kongbai1996 <1782765876@qq.com> Co-authored-by: yvt <i@yvt.jp>
This commit is contained in:
parent
27570987fd
commit
0c0ee04b8e
33 changed files with 1123 additions and 242 deletions
|
@ -1,3 +0,0 @@
|
|||
[activeelement-after-focusing-different-site-iframe-then-immediately-focusing-back.html]
|
||||
[Check focus event and active element after focusing different site iframe then immediately focusing back]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[activeelement-after-focusing-different-site-iframe.html]
|
||||
[Check trailing events]
|
||||
expected: FAIL
|
|
@ -1,2 +1,3 @@
|
|||
[activeelement-after-focusing-same-site-iframe-contentwindow.html]
|
||||
expected: TIMEOUT
|
||||
[Check result]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[activeelement-after-focusing-same-site-iframe.html]
|
||||
[Check trailing events]
|
||||
expected: FAIL
|
|
@ -1,2 +1,3 @@
|
|||
[activeelement-after-immediately-focusing-different-site-iframe-contentwindow.html]
|
||||
expected: TIMEOUT
|
||||
[Check result]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[activeelement-after-immediately-focusing-same-site-iframe-contentwindow.html]
|
||||
expected: TIMEOUT
|
||||
[Check result]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[focus-restoration-in-different-site-iframes-window.html]
|
||||
expected: TIMEOUT
|
||||
[Check result]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
[focus-restoration-in-same-site-iframes-window.html]
|
||||
expected: TIMEOUT
|
||||
[Check result]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[iframe-focuses-parent-same-site.html]
|
||||
expected: TIMEOUT
|
|
@ -1,7 +1,4 @@
|
|||
[cross-origin-objects-function-caching.html]
|
||||
[Cross-origin Window methods are cached]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-origin Location `replace` method is cached]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[focus.window.html]
|
||||
[focus]
|
||||
expected: FAIL
|
|
@ -328,9 +328,3 @@
|
|||
|
||||
[A SecurityError exception must be thrown when window.stop is accessed from a different origin.]
|
||||
expected: FAIL
|
||||
|
||||
[A SecurityError exception should not be thrown when window.blur is accessed from a different origin.]
|
||||
expected: FAIL
|
||||
|
||||
[A SecurityError exception should not be thrown when window.focus is accessed from a different origin.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
[window-properties.https.html]
|
||||
[Window method: focus]
|
||||
expected: FAIL
|
||||
|
||||
[Window method: blur]
|
||||
expected: FAIL
|
||||
|
||||
[Window method: print]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1738,9 +1738,6 @@
|
|||
[Document interface: attribute all]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: operation focus()]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: attribute scrollbars]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1870,9 +1867,6 @@
|
|||
[Document interface: new Document() must inherit property "dir" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "blur()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: operation execCommand(DOMString, optional boolean, optional DOMString)]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1897,9 +1891,6 @@
|
|||
[Document interface: iframe.contentDocument must inherit property "queryCommandEnabled(DOMString)" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: operation blur()]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: iframe.contentDocument must inherit property "onslotchange" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -1924,9 +1915,6 @@
|
|||
[Document interface: documentWithHandlers must inherit property "onauxclick" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Window interface: window must inherit property "focus()" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
[Document interface: documentWithHandlers must inherit property "onwebkitanimationend" with the proper type]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,12 +1,6 @@
|
|||
[event-listeners.window.html]
|
||||
[Standard event listeners are to be removed from Window]
|
||||
expected: FAIL
|
||||
|
||||
[Standard event listeners are to be removed from Window for an active but not fully active document]
|
||||
expected: FAIL
|
||||
|
||||
[Standard event listeners are to be removed from Window for a non-active document that is the associated Document of a Window (frame is removed)]
|
||||
expected: FAIL
|
||||
|
||||
[Custom event listeners are to be removed from Window for an active but not fully active document]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
[global-object-implicit-this-value-cross-realm.html]
|
||||
[Cross-realm global object's operation throws when called on incompatible object]
|
||||
expected: FAIL
|
||||
|
||||
[Cross-realm global object's operation called on null / undefined]
|
||||
expected: FAIL
|
||||
|
|
9
tests/wpt/mozilla/meta/MANIFEST.json
vendored
9
tests/wpt/mozilla/meta/MANIFEST.json
vendored
|
@ -12744,7 +12744,7 @@
|
|||
]
|
||||
},
|
||||
"FocusEvent.html": [
|
||||
"9e002c1088de060b5e7f94c4152bf9fb779c04cc",
|
||||
"7fb7aebf2afbac7f68a16308b9cc5d4588b7022f",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
|
@ -13278,6 +13278,13 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"focus_inter_documents.html": [
|
||||
"5c759772367e844066d1df0081917c9e129d09ec",
|
||||
[
|
||||
null,
|
||||
{}
|
||||
]
|
||||
],
|
||||
"follow-hyperlink.html": [
|
||||
"6ac9eaeb5814a663988ed8c664c113072e329dc5",
|
||||
[
|
||||
|
|
|
@ -48,13 +48,6 @@
|
|||
]
|
||||
},
|
||||
|
||||
{
|
||||
element: document.body,
|
||||
expected_events: [
|
||||
{element: input3, event_name: "blur"},
|
||||
]
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
var idx = 0;
|
||||
|
|
207
tests/wpt/mozilla/tests/mozilla/focus_inter_documents.html
vendored
Normal file
207
tests/wpt/mozilla/tests/mozilla/focus_inter_documents.html
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<iframe id="f1"></iframe>
|
||||
<iframe id="f2"></iframe>
|
||||
<input id="d0">
|
||||
<script>
|
||||
|
||||
/** Wait for an `event` event to be fired on `element`. Resolves to a boolean
|
||||
* value indicating whether the event was fired within a predetermined period. */
|
||||
async function waitForEvent(element, event) {
|
||||
let listener;
|
||||
try {
|
||||
return await new Promise(resolve => {
|
||||
setTimeout(() => resolve(false), 1000);
|
||||
listener = () => resolve(true);
|
||||
element.addEventListener(event, listener);
|
||||
});
|
||||
} finally {
|
||||
if (listener) {
|
||||
element.removeEventListener(event, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
await new Promise(r => window.onload = r);
|
||||
|
||||
const d0 = document.getElementById("d0");
|
||||
|
||||
// This test requires the document to have focus as a starting condition.
|
||||
if (!document.hasFocus() || document.activeElement !== d0) {
|
||||
const p = new Promise(r => d0.onfocus = r);
|
||||
d0.focus();
|
||||
await p;
|
||||
}
|
||||
|
||||
assert_true(document.hasFocus(), "Document has focus as starting condition.");
|
||||
assert_equals(document.activeElement, d0, "`d0` has focus as starting condition.");
|
||||
}, "Starting condition");
|
||||
|
||||
promise_test(async t => {
|
||||
const d0 = document.getElementById("d0");
|
||||
const f1 = document.getElementById("f1");
|
||||
f1.contentDocument.body.innerHTML = '<input id=d1>';
|
||||
const d1 = f1.contentDocument.getElementById("d1");
|
||||
|
||||
const p0 = waitForEvent(d1, 'focus');
|
||||
const p1 = waitForEvent(f1, 'focus');
|
||||
const p2 = waitForEvent(f1.contentWindow, 'focus');
|
||||
const p3 = waitForEvent(d0, 'blur');
|
||||
|
||||
d1.focus();
|
||||
|
||||
assert_true(await p0, "`d1.focus` fires in time");
|
||||
await p1; // FIXME: doesn't fire on Firefox, Blink, Edge, and WebKit
|
||||
assert_true(await p2, "`f1.contentWindow.focus` fires in time");
|
||||
assert_true(await p3, "`d0.blur` fires in time");
|
||||
|
||||
assert_equals(document.activeElement, f1, "The top-level document's activeElement is `f1`");
|
||||
assert_true(f1.contentDocument.hasFocus(), "f1's contentDocument has focus");
|
||||
assert_equals(f1.contentDocument.activeElement, d1, "f1's contentDocument's activeElement is `d1`");
|
||||
}, "Focusing an element in a nested browsing context also focuses the container");
|
||||
|
||||
promise_test(async t => {
|
||||
const f1 = document.getElementById("f1");
|
||||
const d1 = f1.contentDocument.getElementById("d1");
|
||||
|
||||
const f2 = document.getElementById("f2");
|
||||
f2.contentDocument.body.innerHTML = '<input id=d2>';
|
||||
const d2 = f2.contentDocument.getElementById("d2");
|
||||
|
||||
const p0 = waitForEvent(d1, 'blur');
|
||||
const p1 = waitForEvent(f1, 'blur');
|
||||
const p2 = waitForEvent(f1.contentWindow, 'blur');
|
||||
|
||||
d2.focus();
|
||||
|
||||
assert_true(await p0, "`d1.blur` fires in time");
|
||||
await p1; // FIXME: doesn't fire on Firefox, Blink, Edge, and WebKit
|
||||
assert_true(await p2, "`f1.contentWindow.blur` fires in time");
|
||||
|
||||
// Wait for any ongoing execution of the focus update steps to complete
|
||||
await new Promise(r => window.setTimeout(r, 0));
|
||||
|
||||
assert_equals(document.activeElement, f2, "The top-level document's activeElement is `f2`");
|
||||
assert_true(f2.contentDocument.hasFocus(), "f2's contentDocument has focus");
|
||||
assert_equals(f2.contentDocument.activeElement, d2, "f2's contentDocument's activeElement is `d2`");
|
||||
assert_false(f1.contentDocument.hasFocus(), "f1's contentDocument does not have focus");
|
||||
assert_equals(f1.contentDocument.activeElement, f1.contentDocument.body, "f1's contentDocument's activeElement is its body");
|
||||
}, "Focusing an element in a different container also unfocuses the previously focused element and its container");
|
||||
|
||||
promise_test(async t => {
|
||||
const d0 = document.getElementById("d0");
|
||||
|
||||
const f2 = document.getElementById("f2");
|
||||
const d2 = f2.contentDocument.getElementById("d2");
|
||||
|
||||
const p0 = waitForEvent(d2, 'blur');
|
||||
const p1 = waitForEvent(f2, 'blur');
|
||||
const p2 = waitForEvent(f2.contentWindow, 'blur');
|
||||
const p3 = waitForEvent(d0, 'focus');
|
||||
|
||||
d0.focus();
|
||||
|
||||
assert_true(await p0, "`d2.blur` fires in time");
|
||||
await p1; // FIXME: doesn't fire on Firefox, Blink, Edge, and WebKit
|
||||
assert_true(await p2, "`f2.contentWindow.blur` fires in time");
|
||||
assert_true(await p3, "`d0.focus` fires in time");
|
||||
|
||||
// Wait for any ongoing execution of the focus update steps to complete
|
||||
await new Promise(r => window.setTimeout(r, 0));
|
||||
|
||||
assert_equals(document.activeElement, d0, "The top-level document's activeElement is `d0`");
|
||||
assert_false(f2.contentDocument.hasFocus(), "f2's contentDocument does not have focus");
|
||||
assert_equals(f2.contentDocument.activeElement, f2.contentDocument.body, "f2's contentDocument's activeElement is its body");
|
||||
}, "Unfocusing a container also unfocuses any focused elements within");
|
||||
|
||||
promise_test(async t => {
|
||||
const f1 = document.getElementById("f1");
|
||||
|
||||
const p0 = waitForEvent(f1, 'focus');
|
||||
const p1 = waitForEvent(f1.contentWindow, 'focus');
|
||||
|
||||
f1.focus();
|
||||
|
||||
await p0; // FIXME: doesn't fire on Firefox, Blink, Edge, and WebKit
|
||||
assert_true(await p1, "`f1.contentWindow.focus` fires in time");
|
||||
|
||||
assert_equals(document.activeElement, f1, "The top-level document's activeElement is `f1`");
|
||||
assert_true(f1.contentDocument.hasFocus(), "f1's contentDocument has focus");
|
||||
}, "Focusing a container changes the contained document's 'has focus steps' result");
|
||||
|
||||
promise_test(async t => {
|
||||
const f1 = document.getElementById("f1");
|
||||
|
||||
// `f1` should be focused because of the previous step
|
||||
assert_equals(document.activeElement, f1, "The top-level document's activeElement is `f1`");
|
||||
|
||||
// Navigate the focused container
|
||||
const pLoad = new Promise(resolve => window.subframeIsReady = resolve);
|
||||
f1.srcdoc = "<script>window.parent.subframeIsReady();</" + "script>";
|
||||
await pLoad;
|
||||
|
||||
// Allow some delay before the document finally receives focus
|
||||
if (!f1.contentDocument.hasFocus()) {
|
||||
await waitForEvent(f1.contentWindow, 'focus');
|
||||
}
|
||||
|
||||
assert_true(f1.contentDocument.hasFocus(), "f1's contentDocument has focus");
|
||||
}, "When a focused container navigates, the new document should receive focus");
|
||||
|
||||
promise_test(async t => {
|
||||
const f2 = document.getElementById("f2");
|
||||
|
||||
const p0 = waitForEvent(f2, 'focus');
|
||||
const p1 = waitForEvent(f2.contentWindow, 'focus');
|
||||
|
||||
f2.contentWindow.focus();
|
||||
|
||||
await p0; // FIXME: doesn't fire on Firefox, Blink, Edge, and WebKit
|
||||
assert_true(await p1, "`f2.contentWindow.focus` fires in time");
|
||||
|
||||
assert_equals(document.activeElement, f2, "The top-level document's activeElement is `f2`");
|
||||
assert_true(f2.contentDocument.hasFocus(), "f2's contentDocument has focus");
|
||||
}, "Focusing the window of a nested browsing context also focuses the container");
|
||||
|
||||
promise_test(async t => {
|
||||
const f2 = document.getElementById("f2");
|
||||
const d2 = f2.contentDocument.getElementById("d2");
|
||||
|
||||
{
|
||||
const p = waitForEvent(d2, 'focus');
|
||||
f2.focus();
|
||||
d2.focus();
|
||||
await p;
|
||||
}
|
||||
|
||||
const p0 = waitForEvent(d2, 'blur');
|
||||
d2.blur();
|
||||
assert_true(await p0, "`d2.blur` fires in time");
|
||||
|
||||
// FIXME: This passes on Firefox, Blink, and WebKit but is not spec-
|
||||
// compliant. Per spec, the top-level document's viewport should be
|
||||
// focused instead.
|
||||
//
|
||||
// <https://html.spec.whatwg.org/multipage/#get-the-focusable-area>
|
||||
//
|
||||
// > The unfocusing steps for an object `old focus target`` that is either a
|
||||
// > focusable area or an element that is not a focusable area are as
|
||||
// > follows: [...]
|
||||
// >
|
||||
// > 7. If `topDocument`'s browsing context has system focus, then run the
|
||||
// > focusing steps for topDocument's viewport.
|
||||
|
||||
assert_equals(document.activeElement, f2, "The top-level document's activeElement is `f2`");
|
||||
assert_equals(f2.contentDocument.activeElement, f2.contentDocument.body, "f2's contentDocument's activeElement is its body");
|
||||
assert_true(f2.contentDocument.hasFocus(), "f2's contentDocument has focus");
|
||||
}, "Blurring an element in a nested browsing context focuses its node document");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue