Skip to content

Commit

Permalink
Make fullscreen elements match the :modal pseudo-class
Browse files Browse the repository at this point in the history
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 56d7f89
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -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>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@


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

Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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

4 changes: 4 additions & 0 deletions Source/WebCore/css/SelectorCheckerTestFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Source/WebCore/dom/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 56d7f89

Please sign in to comment.