Skip to content
Permalink
Browse files
Implement CSS :modal pseudo class
https://bugs.webkit.org/show_bug.cgi?id=240109

Reviewed by Simon Fraser.

LayoutTests/imported/w3c:

Add and extend tests for :modal pseudo-class.

* web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has.html: Added.
* web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-show-modal.html:

Source/WebCore:

Test: imported/w3c/web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has.html

Renames :-internal-modal-dialog to :modal and adds :has() invalidation support.

* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText const):
* css/CSSSelector.h:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):
* css/SelectorCheckerTestFunctions.h:
(WebCore::matchesModalPseudoClass):
(WebCore::matchesModalDialogPseudoClass): Deleted.
* css/SelectorPseudoClassAndCompatibilityElementMap.in:
* css/dialog.css:
(dialog:modal):
(dialog:-internal-modal-dialog): Deleted.
* css/parser/CSSSelectorParser.cpp:
(WebCore::CSSSelectorParser::consumePseudo):
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::JSC_DEFINE_JIT_OPERATION):
(WebCore::SelectorCompiler::addPseudoClassType):
* html/HTMLDialogElement.cpp:
(WebCore::HTMLDialogElement::showModal):
(WebCore::HTMLDialogElement::close):
(WebCore::HTMLDialogElement::removedFromAncestor):
(WebCore::HTMLDialogElement::setIsModal):
* html/HTMLDialogElement.h:

LayoutTests:

Removes :-internal-modal-dialog from internal pseudo classes.

* fast/css/pseudo-class-internal-expected.txt:
* fast/css/pseudo-class-internal.html:

Canonical link: https://commits.webkit.org/250424@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@293987 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
nt1m committed May 9, 2022
1 parent 28654bf commit 55a211a3c58906d213bd87db182fb17000e5d71e
Showing 18 changed files with 148 additions and 28 deletions.
@@ -1,3 +1,15 @@
2022-05-09 Tim Nguyen <ntim@apple.com>

Implement CSS :modal pseudo class
https://bugs.webkit.org/show_bug.cgi?id=240109

Reviewed by Simon Fraser.

Removes :-internal-modal-dialog from internal pseudo classes.

* fast/css/pseudo-class-internal-expected.txt:
* fast/css/pseudo-class-internal.html:

2022-05-09 Arcady Goldmints-Orlov <agoldmints@igalia.com>

[GLIB] Update some test expectations for known failures
@@ -4,7 +4,6 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE


PASS target.matches(":-internal-direct-focus") threw exception SyntaxError: The string did not match the expected pattern..
PASS target.matches(":-internal-modal-dialog") threw exception SyntaxError: The string did not match the expected pattern..
PASS successfullyParsed is true

TEST COMPLETE
@@ -10,7 +10,6 @@
function runTest() {
const internalPseudoClasses = [
':-internal-direct-focus',
':-internal-modal-dialog',
];
for (const pseudo of internalPseudoClasses) {
shouldThrowErrorName('target.matches("' + pseudo + '")', 'SyntaxError');
@@ -1,3 +1,16 @@
2022-05-09 Tim Nguyen <ntim@apple.com>

Implement CSS :modal pseudo class
https://bugs.webkit.org/show_bug.cgi?id=240109

Reviewed by Simon Fraser.

Add and extend tests for :modal pseudo-class.

* web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has-expected.txt: Added.
* web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has.html: Added.
* web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-show-modal.html:

2022-05-06 Rob Buis <rbuis@igalia.com>

Use correct document as root for lazy image observer
@@ -0,0 +1,6 @@
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

@@ -0,0 +1,44 @@
<!DOCTYPE html>
<meta charset="utf-8" />
<title>CSS Selectors Invalidation: :modal pseudo class in :has()</title>
<link rel="author" title="Tim Nguyen" href="https://github.com/nt1m">
<link rel="help" href="https://drafts.csswg.org/selectors/#relational">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#subject:has(#dialog:modal) { color: green }
</style>
<div id="subject">
This is some text.
<dialog id="dialog">This is a dialog</dialog>
</div>
<script>
test(function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since dialog is not modal");
dialog.show();
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black since dialog is not modal");
dialog.close();
}, ":modal pseudo-class is not active with dialog.show()");
test(function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
dialog.showModal();
assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
"ancestor should be green since dialog is shown modally");
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");
test(function() {
assert_equals(getComputedStyle(subject).color, "rgb(0, 0, 0)",
"ancestor should be black");
dialog.showModal();
assert_equals(getComputedStyle(subject).color, "rgb(0, 128, 0)",
"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>
@@ -10,26 +10,29 @@

<script>
test(() => {
const dialog = document.getElementById('mydialog');
const dialog = document.getElementById("mydialog");
const computedStyle = window.getComputedStyle(dialog, null);
assert_equals(computedStyle.display, 'none');
assert_equals(computedStyle.display, "none");
assert_false(dialog.matches(":modal"));

dialog.showModal();
assert_equals(computedStyle.display, 'block');
assert_equals(computedStyle.display, "block");
assert_true(dialog.matches(":modal"));

// The quoted texts output below are from <http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#dom-dialog-showmodal>.
assert_throws_dom('InvalidStateError', () => dialog.showModal());
assert_throws_dom("InvalidStateError", () => dialog.showModal());

dialog.close();
assert_equals(computedStyle.display, 'none');
assert_equals(computedStyle.display, "none");
assert_false(dialog.matches(":modal"));

dialog.parentNode.removeChild(dialog);
assert_throws_dom('InvalidStateError', () => dialog.showModal());
assert_throws_dom("InvalidStateError", () => dialog.showModal());

const doc = document.implementation.createHTMLDocument();
doc.body.appendChild(dialog);
assert_false(dialog.open);
dialog.showModal();
assert_true(dialog.open, 'Although the document is not attached to any pages, showModal() should execute as normal.');
}, 'Tests that showModal() performs the steps specified in the HTML spec.');
assert_true(dialog.open, "Although the document is not attached to any pages, showModal() should execute as normal.");
}, "Tests that showModal() performs the steps specified in the HTML spec.");
</script>
@@ -1,3 +1,38 @@
2022-05-09 Tim Nguyen <ntim@apple.com>

Implement CSS :modal pseudo class
https://bugs.webkit.org/show_bug.cgi?id=240109

Reviewed by Simon Fraser.

Test: imported/w3c/web-platform-tests/css/selectors/invalidation/modal-pseudo-class-in-has.html

Renames :-internal-modal-dialog to :modal and adds :has() invalidation support.

* css/CSSSelector.cpp:
(WebCore::CSSSelector::selectorText const):
* css/CSSSelector.h:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOne const):
* css/SelectorCheckerTestFunctions.h:
(WebCore::matchesModalPseudoClass):
(WebCore::matchesModalDialogPseudoClass): Deleted.
* css/SelectorPseudoClassAndCompatibilityElementMap.in:
* css/dialog.css:
(dialog:modal):
(dialog:-internal-modal-dialog): Deleted.
* css/parser/CSSSelectorParser.cpp:
(WebCore::CSSSelectorParser::consumePseudo):
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::JSC_DEFINE_JIT_OPERATION):
(WebCore::SelectorCompiler::addPseudoClassType):
* html/HTMLDialogElement.cpp:
(WebCore::HTMLDialogElement::showModal):
(WebCore::HTMLDialogElement::close):
(WebCore::HTMLDialogElement::removedFromAncestor):
(WebCore::HTMLDialogElement::setIsModal):
* html/HTMLDialogElement.h:

2022-05-09 Gabriel Nava Marino <gnavamarino@apple.com>

ASSERT in WebCore::RenderTreeUpdater::updateRenderTree
@@ -591,8 +591,8 @@ String CSSSelector::selectorText(const String& rightSide) const
case CSSSelector::PseudoClassLink:
builder.append(":link");
break;
case CSSSelector::PseudoClassModalDialog:
builder.append(":-internal-modal-dialog");
case CSSSelector::PseudoClassModal:
builder.append(":modal");
break;
case CSSSelector::PseudoClassNoButton:
builder.append(":no-button");
@@ -185,7 +185,7 @@ namespace WebCore {
#if ENABLE(ATTACHMENT_ELEMENT)
PseudoClassHasAttachment,
#endif
PseudoClassModalDialog,
PseudoClassModal,
};

enum PseudoElementType {
@@ -1132,8 +1132,8 @@ bool SelectorChecker::checkOne(CheckingContext& checkingContext, const LocalCont
return hasAttachment(element);
#endif

case CSSSelector::PseudoClassModalDialog:
return matchesModalDialogPseudoClass(element);
case CSSSelector::PseudoClassModal:
return matchesModalPseudoClass(element);

case CSSSelector::PseudoClassUnknown:
ASSERT_NOT_REACHED();
@@ -531,7 +531,7 @@ ALWAYS_INLINE bool matchesFocusWithinPseudoClass(const Element& element)
return element.hasFocusWithin() && isFrameFocused(element);
}

ALWAYS_INLINE bool matchesModalDialogPseudoClass(const Element& element)
ALWAYS_INLINE bool matchesModalPseudoClass(const Element& element)
{
if (is<HTMLDialogElement>(element))
return downcast<HTMLDialogElement>(element).isModal();
@@ -1,4 +1,3 @@
-internal-modal-dialog
-khtml-drag
-webkit-any
-webkit-any-link, PseudoClassAnyLinkDeprecated, PseudoElementUnknown
@@ -50,6 +49,7 @@ last-child
last-of-type
link
matches
modal
no-button
not
nth-child
@@ -16,7 +16,7 @@ dialog[open] {
display: block;
}

dialog:-internal-modal-dialog {
dialog:modal {
position: fixed;
overflow: auto;
inset-block-start: 0;
@@ -644,8 +644,6 @@ std::unique_ptr<CSSParserSelector> CSSSelectorParser::consumePseudo(CSSParserTok
if (!selector)
return nullptr;
if (selector->match() == CSSSelector::PseudoClass) {
if (m_context.mode != UASheetMode && selector->pseudoClassType() == CSSSelector::PseudoClassModalDialog)
return nullptr;
if (!m_context.focusVisibleEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassFocusVisible)
return nullptr;
if (!m_context.hasPseudoClassEnabled && selector->pseudoClassType() == CSSSelector::PseudoClassHas)
@@ -125,7 +125,7 @@ static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesVolumeLock
#if ENABLE(ATTACHMENT_ELEMENT)
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationHasAttachment, bool, (const Element&));
#endif
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesModalDialogPseudoClass, bool, (const Element&));
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationMatchesModalPseudoClass, bool, (const Element&));

static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationAttributeValueBeginsWithCaseSensitive, bool, (const Attribute* attribute, AtomStringImpl* expectedString));
static JSC_DECLARE_JIT_OPERATION_WITHOUT_WTF_INTERNAL(operationAttributeValueBeginsWithCaseInsensitive, bool, (const Attribute* attribute, AtomStringImpl* expectedString));
@@ -804,9 +804,9 @@ JSC_DEFINE_JIT_OPERATION(operationMatchesLangPseudoClass, bool, (const Element&
return matchesLangPseudoClass(element, argumentList);
}

JSC_DEFINE_JIT_OPERATION(operationMatchesModalDialogPseudoClass, bool, (const Element& element))
JSC_DEFINE_JIT_OPERATION(operationMatchesModalPseudoClass, bool, (const Element& element))
{
return matchesModalDialogPseudoClass(element);
return matchesModalPseudoClass(element);
}

static inline FunctionType addPseudoClassType(const CSSSelector& selector, SelectorFragment& fragment, SelectorContext selectorContext, FragmentsLevel fragmentLevel, FragmentPositionInRootFragments positionInRootFragments, bool visitedMatchEnabled, VisitedMode& visitedMode, PseudoElementMatchingBehavior pseudoElementMatchingBehavior)
@@ -945,8 +945,8 @@ static inline FunctionType addPseudoClassType(const CSSSelector& selector, Selec
return FunctionType::SimpleSelectorChecker;
#endif

case CSSSelector::PseudoClassModalDialog:
fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesModalDialogPseudoClass));
case CSSSelector::PseudoClassModal:
fragment.unoptimizedPseudoClasses.append(JSC::FunctionPtr<JSC::OperationPtrTag>(operationMatchesModalPseudoClass));
return FunctionType::SimpleSelectorChecker;

// These pseudo-classes only have meaning with scrollbars.
@@ -26,11 +26,13 @@
#include "config.h"
#include "HTMLDialogElement.h"

#include "CSSSelector.h"
#include "DocumentInlines.h"
#include "EventLoop.h"
#include "EventNames.h"
#include "FocusOptions.h"
#include "HTMLNames.h"
#include "PseudoClassChangeInvalidation.h"
#include "RenderElement.h"
#include "ScopedEventQueue.h"
#include "TypedElementDescendantIterator.h"
@@ -75,7 +77,7 @@ ExceptionOr<void> HTMLDialogElement::showModal()
EventQueueScope scope;
setBooleanAttribute(openAttr, true);

m_isModal = true;
setIsModal(true);

if (!isInTopLayer())
addToTopLayer();
@@ -94,7 +96,7 @@ void HTMLDialogElement::close(const String& result)

setBooleanAttribute(openAttr, false);

m_isModal = false;
setIsModal(false);

if (!result.isNull())
m_returnValue = result;
@@ -158,7 +160,15 @@ void HTMLDialogElement::runFocusingSteps()
void HTMLDialogElement::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
{
HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree);
m_isModal = false;
setIsModal(false);
}

void HTMLDialogElement::setIsModal(bool newValue)
{
if (m_isModal == newValue)
return;
Style::PseudoClassChangeInvalidation styleInvalidation(*this, CSSSelector::PseudoClassModal, newValue);
m_isModal = newValue;
}

}
@@ -53,6 +53,7 @@ class HTMLDialogElement final : public HTMLElement {
HTMLDialogElement(const QualifiedName&, Document&);

void removedFromAncestor(RemovalType, ContainerNode& oldParentOfRemovedTree) final;
void setIsModal(bool newValue);

String m_returnValue;
bool m_isModal { false };

0 comments on commit 55a211a

Please sign in to comment.