Skip to content
Permalink
Browse files
Make fullscreen elements match the :modal pseudo-class
https://bugs.webkit.org/show_bug.cgi?id=246004
rdar://100782746

Reviewed by Antti Koivisto.

https://w3c.github.io/csswg-drafts/selectors/#modal-state
> For example, the dialog element is :modal when opened with the showModal() API.
> Similarly, a :fullscreen element is also :modal when opened with the requestFullscreen() API, since this prevents interaction with the rest of the page.

We implemented the "prevents interaction with the rest of the page" part in https://commits.webkit.org/253803@main

Relevant CSSWG discussion: w3c/csswg-drafts#7311

Also, we don't need to worry about the modal dialog in fullscreen mode case, since the fullscreen API forbids <dialog> elements.

* LayoutTests/platform/mac-wk1/imported/w3c/web-platform-tests/css/selectors/modal-pseudo-class-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has.html:
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/modal-pseudo-class-expected.txt: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/modal-pseudo-class.html: Added.
* LayoutTests/imported/w3c/web-platform-tests/css/selectors/w3c-import.log:
* Source/WebCore/css/SelectorCheckerTestFunctions.h:
(WebCore::matchesModalPseudoClass):
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::setFullscreenFlag):

Canonical link: https://commits.webkit.org/257572@main
  • Loading branch information
nt1m committed Dec 8, 2022
1 parent c76c4c2 commit 56d7f894da59a5d699530cb396dbc9400f1836d1
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 8 deletions.
@@ -1,6 +1,8 @@
This is some text.

PASS :modal pseudo-class is not active with dialog.show()
PASS :modal pseudo-class invalidation with showModal+close
PASS :modal pseudo-class invalidation with showModal+remove
PASS :modal pseudo-class invalidation with showModal + close
PASS :modal pseudo-class invalidation with showModal + remove
PASS :modal pseudo-class invalidation with requestFullscreen + exitFullscreen
PASS :modal pseudo-class invalidation with requestFullscreen + remove

@@ -5,14 +5,19 @@
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
#subject:has(#dialog:modal) { color: green }
#subject:has(#dialog:modal) { color: green; }
#subject:has(#fullscreenTarget:modal) { color: blue; }
</style>
<div id="subject">
This is some text.
<dialog id="dialog">This is a dialog</dialog>
<div id="fullscreenTarget">This is going to be fullscreened</div>
</div>
<script>
// Dialog tests
test(function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since dialog is not modal");
@@ -30,7 +35,7 @@
dialog.close();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since dialog is closed");
}, ":modal pseudo-class invalidation with showModal+close");
}, ":modal pseudo-class invalidation with showModal + close");
test(function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
@@ -39,6 +44,37 @@
"ancestor should be green since dialog is shown modally");
dialog.remove();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since dialog is closed");
}, ":modal pseudo-class invalidation with showModal+remove");
</script>
"ancestor should be black since dialog is removed");
}, ":modal pseudo-class invalidation with showModal + remove");

// Fullscreen tests
let waitForFullscreenChange = () => {
return new Promise((resolve) => {
document.addEventListener("fullscreenchange", resolve, { once: true });
});
};
promise_test(async function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
test_driver.bless("fullscreen", () => fullscreenTarget.requestFullscreen());
await waitForFullscreenChange();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 255)",
"ancestor should be blue since target is fullscreen");
document.exitFullscreen();
await waitForFullscreenChange();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since target is no longer fullscreen");
}, ":modal pseudo-class invalidation with requestFullscreen + exitFullscreen");
promise_test(async function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
test_driver.bless("fullscreen", () => fullscreenTarget.requestFullscreen());
await waitForFullscreenChange();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 255)",
"ancestor should be blue since target is fullscreen");
fullscreenTarget.remove();
await waitForFullscreenChange();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since target is removed");
}, ":modal pseudo-class invalidation with requestFullscreen + remove");
</script>
@@ -0,0 +1,5 @@


PASS Test that :modal matches modal <dialog>
PASS Test that :modal matches the fullscreen element

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<meta charset="utf-8"/>
<title>:modal pseudo-class</title>
<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
<link rel="author" title="Jihwan Marc Kim" href="mailto:bluewhale.marc@gmail.com" />
<link rel="help" href="https://drafts.csswg.org/selectors/#modal-state">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>

<dialog id="dialog">Just another dialog.</dialog>
<div id="container">
<button id="btn"/>
</div>

<script>
test(() => {
const dialog = document.getElementById("dialog");
assert_false(dialog.matches(":modal"), "dialog is initially closed (does not match :modal)");

dialog.showModal();
assert_true(dialog.matches(":modal"), "dialog should match :modal after showModal() call");

dialog.close();
assert_false(dialog.matches(":modal"), "dialog should no longer match :modal after close() call");

dialog.show();
assert_false(dialog.matches(":modal"), "dialog shouldn't match :modal after show() call");

dialog.close();
dialog.showModal();
assert_true(dialog.matches(":modal"), "dialog should match :modal after showModal() call");

dialog.remove();
assert_false(dialog.matches(":modal"), "dialog shouldn't match :modal after being removed from document");
document.body.append(dialog);
assert_false(dialog.matches(":modal"), "dialog shouldn't match :modal after being re-appended to document");

dialog.close();
}, "Test that :modal matches modal <dialog>");

promise_test(async () => {
const container = document.getElementById("container");
const btn = document.getElementById("btn");

assert_false(container.matches(":modal"), "before requestFullscreen (does not match :modal)");

await test_driver.click(btn);

await container.requestFullscreen();

assert_true(container.matches(":modal"), ":fullscreen should match :modal");

await document.exitFullscreen();

assert_false(container.matches(":modal"), "after exitFullscreen (does not match :modal)");
}, "Test that :modal matches the fullscreen element");
</script>
@@ -215,6 +215,7 @@ List of files:
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/last-child.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/last-of-type.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/missing-right-token.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/modal-pseudo-class.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/nesting-expected.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/nesting-parsing.html
/LayoutTests/imported/w3c/web-platform-tests/css/selectors/nesting-ref.html
@@ -0,0 +1,5 @@


PASS Test that :modal matches modal <dialog>
FAIL Test that :modal matches the fullscreen element assert_true: :fullscreen should match :modal expected true got false

@@ -566,7 +566,11 @@ ALWAYS_INLINE bool matchesModalPseudoClass(const Element& element)
{
if (is<HTMLDialogElement>(element))
return downcast<HTMLDialogElement>(element).isModal();
#if ENABLE(FULLSCREEN_API)
return element.hasFullscreenFlag();
#else
return false;
#endif
}

} // namespace WebCore
@@ -4169,7 +4169,7 @@ void Element::requestFullscreen(FullscreenOptions&&, RefPtr<DeferredPromise>&& p

void Element::setFullscreenFlag(bool flag)
{
Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassFullscreen, flag);
Style::PseudoClassChangeInvalidation styleInvalidation(*this, { { CSSSelector::PseudoClassFullscreen, flag }, { CSSSelector::PseudoClassModal, flag } });
if (flag)
setNodeFlag(NodeFlag::IsFullscreen);
else

0 comments on commit 56d7f89

Please sign in to comment.