Skip to content

Commit

Permalink
Add :closed pseudo class, for the pop-up API only
Browse files Browse the repository at this point in the history
Per the [1] resolution, the CSSWG will be adding both :open (which
is already implemented for pop-up) and :closed (which this CL adds).
At this point, both `:open` and `:closed` are only supported for
the pop-up API, and only with the HTMLPopupAttribute feature
enabled. More general support for other elements will be added
later.

[1] w3c/csswg-drafts#7319 (comment)

Bug: 1307772
Change-Id: If0c27f1f8c7a30fc00656d5eeafe4b6c21bba890
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3961537
Reviewed-by: Joey Arhar <jarhar@chromium.org>
Commit-Queue: Mason Freed <masonf@chromium.org>
Auto-Submit: Mason Freed <masonf@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1063120}
  • Loading branch information
Mason Freed authored and chromium-wpt-export-bot committed Oct 25, 2022
1 parent ad8a1a6 commit 44ae1c4
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
27 changes: 27 additions & 0 deletions html/semantics/popups/popup-animation-corner-cases.tentative.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,18 @@
const {popUp, descendent} = createPopUp(t,'animation');
assert_false(isElementVisible(popUp));
assert_equals(descendent.parentElement.parentElement,popUp);
assert_true(popUp.matches(':closed'));
assert_false(popUp.matches(':open'));
popUp.showPopUp();
assert_false(popUp.matches(':closed'));
assert_true(popUp.matches(':open'));
assert_true(isElementVisible(popUp));
assert_equals(popUp.getAnimations({subtree: true}).length,0);
popUp.hidePopUp();
const animations = popUp.getAnimations({subtree: true});
assert_equals(animations.length,2,'There should be two animations running');
assert_false(popUp.matches(':open'),'popUp should not match :open as soon as hidden');
assert_false(popUp.matches(':closed'),'popUp should not match :closed until animations complete');
assert_true(isElementVisible(popUp),'but animations should keep the popUp visible');
assert_true(isElementVisible(descendent),'The descendent should also be visible');
await waitForRender();
Expand All @@ -61,6 +65,7 @@
assert_true(isElementVisible(popUp),'PopUp should still be visible due to animation');
animations.forEach(animation => animation.finish()); // Force the animations to finish
await waitForRender(); // Wait one frame
assert_true(popUp.matches(':closed'),'The pop up should now match :closed');
assert_false(popUp.matches(':open'),'The pop up still shouldn\'t match :open');
assert_false(isElementVisible(popUp),'The pop up should now be invisible');
assert_false(isElementVisible(descendent),'The descendent should also be invisible');
Expand All @@ -82,6 +87,7 @@
// Then hide the popUp.
popUp.hidePopUp();
assert_false(popUp.matches(':open'),'pop up should not match :open as soon as hidden');
assert_true(popUp.matches(':closed'),'pop up should match :closed immediately');
assert_equals(popUp.getAnimations({subtree: true}).length,2,'animations should still be running');
await waitForRender();
assert_equals(popUp.getAnimations({subtree: true}).length,2,'animations should still be running');
Expand Down Expand Up @@ -137,6 +143,27 @@
assert_false(isElementVisible(popUp),'Even if hide event is cancelled, the popup still closes');
},'hide event cannot be cancelled');

promise_test(async (t) => {
const {popUp, descendent} = createPopUp(t,'animation');
assert_false(isElementVisible(popUp));
popUp.showPopUp();
assert_false(popUp.matches(':closed'));
assert_true(popUp.matches(':open'));
assert_true(isElementVisible(popUp));
assert_equals(popUp.getAnimations({subtree: true}).length,0);
popUp.popUp = 'manual';
const animations = popUp.getAnimations({subtree: true});
assert_equals(animations.length,2,'There should be two animations running');
assert_false(popUp.matches(':open'),'popUp should not match :open as soon as hidden');
assert_false(popUp.matches(':closed'),'popUp should not match :closed until animations complete');
assert_true(isElementVisible(popUp),'but animations should keep the popUp visible');
animations.forEach(animation => animation.finish()); // Force the animations to finish
await waitForRender(); // Wait one frame
assert_true(popUp.matches(':closed'),'The pop up should now match :closed');
assert_false(popUp.matches(':open'),'The pop up still shouldn\'t match :open');
assert_false(isElementVisible(popUp),'The pop up should now be invisible');
},'Closing animations are triggered by changing the pop-up type');

promise_test(async (t) => {
const {popUp, descendent} = createPopUp(t,'');
popUp.showPopUp();
Expand Down
15 changes: 13 additions & 2 deletions html/semantics/popups/popup-attribute-basic.tentative.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,11 @@
if (isVisible) {
assert_not_equals(window.getComputedStyle(popUp).display,'none');
assert_equals(popUp.matches(':open'),isPopUp,`${message}: Visible pop-ups should match :open`);
assert_false(popUp.matches(':closed'),`${message}: Visible pop-ups and *all* non-pop-ups should *not* match :closed`);
} else {
assert_equals(window.getComputedStyle(popUp).display,'none',`${message}: Non-showing pop-ups should have display:none`);
assert_false(popUp.matches(':open'),`${message}: Non-showing pop-ups should *not* match :open`);
assert_true(popUp.matches(':closed'),`${message}: Non-showing pop-ups should match :closed`);
}
}
function assertIsFunctionalPopUp(popUp) {
Expand Down Expand Up @@ -89,7 +91,7 @@
});

// Then loop through all HTML5 elements that render a box by default:
let elementsThatDontRender = ['audio','base','br','datalist','dialog','embed','head','link','meta','noscript','param','rp','script','style','template','title','wbr'];
let elementsThatDontRender = ['audio','base','br','datalist','dialog','embed','head','link','meta','noscript','param','rp','script','slot','style','template','title','wbr'];
const elements = HTML5_ELEMENTS.filter(el => !elementsThatDontRender.includes(el));
elements.forEach(tag => {
test((t) => {
Expand All @@ -98,7 +100,13 @@
document.body.appendChild(element);
t.add_cleanup(() => element.remove());
assertIsFunctionalPopUp(element);
}, `A <${tag}> element should behave as a pop-up.`);
}, `A <${tag} popup> element should behave as a pop-up.`);
test((t) => {
const element = document.createElement(tag);
document.body.appendChild(element);
t.add_cleanup(() => element.remove());
assertNotAPopUp(element);
}, `A <${tag}> element should *not* behave as a pop-up.`);
});

function createPopUp(t) {
Expand Down Expand Up @@ -318,17 +326,20 @@
assert_false(isElementVisible(popUp));
popUp.showPopUp();
assert_true(popUp.matches(':open'));
assert_false(popUp.matches(':closed'));
assert_true(getComputedStyle(popUp).opacity < 0.1,'Animations should start on show');
assert_throws_dom("InvalidStateError",() => popUp.showPopUp(),'Calling showPopUp on a popup that is in the process of animating show should throw InvalidStateError');
await finishAnimations(popUp);
assert_true(getComputedStyle(popUp).opacity > 0.9);
assert_true(isElementVisible(popUp));
popUp.hidePopUp();
assert_false(popUp.matches(':open'));
assert_false(popUp.matches(':closed'),'Not :closed until animations finish');
assert_true(getComputedStyle(popUp).opacity > 0.9,'Animations should start on hide');
assert_throws_dom("InvalidStateError",() => popUp.hidePopUp(),'Calling hidePopUp on a popup that is in the process of animating hide should throw InvalidStateError');
popUp.showPopUp(); // But showPopUp should still be ok.
popUp.hidePopUp(); // Clean up
await finishAnimations(popUp);
assert_true(popUp.matches(':closed'),':closed should match once animations finish');
},'Exceptions are thrown even when show/hide are animated')
</script>

0 comments on commit 44ae1c4

Please sign in to comment.