diff --git a/LayoutTests/TestExpectations b/LayoutTests/TestExpectations index b7d05c612610..5cc0155f9dd7 100644 --- a/LayoutTests/TestExpectations +++ b/LayoutTests/TestExpectations @@ -949,7 +949,6 @@ imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/002. imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/combination_history_002.html [ Failure Pass ] imported/w3c/web-platform-tests/html/browsers/history/the-history-interface/combination_history_003.html [ Failure Pass ] imported/w3c/web-platform-tests/html/cross-origin-opener-policy/tentative/restrict-properties/iframe-popup-to-un.https.html [ Failure Pass ] -imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/show-modal-focusing-steps.html [ Failure Pass ] imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/execution-timing/058.html [ Failure Pass ] imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.sharedworker.html [ Failure Pass ] imported/w3c/web-platform-tests/html/semantics/scripting-1/the-script-element/microtasks/evaluation-order-1.html [ Failure Pass ] diff --git a/LayoutTests/editing/style/apply-style-atomic-expected.txt b/LayoutTests/editing/style/apply-style-atomic-expected.txt index bf2a250ec8f9..bdf54a871559 100644 --- a/LayoutTests/editing/style/apply-style-atomic-expected.txt +++ b/LayoutTests/editing/style/apply-style-atomic-expected.txt @@ -2,19 +2,19 @@ Test that WebKit does not crash when we apply style to atomic elements and that | | href="a" | "<#selection-anchor>1" -| -| -| style="" -| "2" -| -|
-| pseudo="-webkit-progress-inner-element" -| shadow:pseudoId="-webkit-progress-inner-element" +| +| +| style="" +| "2" +| |
-| pseudo="-webkit-progress-bar" -| shadow:pseudoId="-webkit-progress-bar" +| pseudo="-webkit-progress-inner-element" +| shadow:pseudoId="-webkit-progress-inner-element" |
-| pseudo="-webkit-progress-value" -| style="width: -100%;" -| shadow:pseudoId="-webkit-progress-value" -| <#selection-focus> +| pseudo="-webkit-progress-bar" +| shadow:pseudoId="-webkit-progress-bar" +|
+| pseudo="-webkit-progress-value" +| style="width: -100%;" +| shadow:pseudoId="-webkit-progress-value" +| <#selection-focus> diff --git a/LayoutTests/editing/style/apply-style-atomic-live-range-expected.txt b/LayoutTests/editing/style/apply-style-atomic-live-range-expected.txt index bf2a250ec8f9..bdf54a871559 100644 --- a/LayoutTests/editing/style/apply-style-atomic-live-range-expected.txt +++ b/LayoutTests/editing/style/apply-style-atomic-live-range-expected.txt @@ -2,19 +2,19 @@ Test that WebKit does not crash when we apply style to atomic elements and that | | href="a" | "<#selection-anchor>1" -| -| -| style="" -| "2" -| -|
-| pseudo="-webkit-progress-inner-element" -| shadow:pseudoId="-webkit-progress-inner-element" +| +| +| style="" +| "2" +| |
-| pseudo="-webkit-progress-bar" -| shadow:pseudoId="-webkit-progress-bar" +| pseudo="-webkit-progress-inner-element" +| shadow:pseudoId="-webkit-progress-inner-element" |
-| pseudo="-webkit-progress-value" -| style="width: -100%;" -| shadow:pseudoId="-webkit-progress-value" -| <#selection-focus> +| pseudo="-webkit-progress-bar" +| shadow:pseudoId="-webkit-progress-bar" +|
+| pseudo="-webkit-progress-value" +| style="width: -100%;" +| shadow:pseudoId="-webkit-progress-value" +| <#selection-focus> diff --git a/LayoutTests/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document-expected.txt index b1c1d8d47a97..4e42c03a541e 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/remove-dialog-should-unblock-document-expected.txt @@ -1,4 +1,4 @@ -This is a dialog -FAIL Test that removing dialog unblocks the document. assert_equals: expected false but got true + +PASS Test that removing dialog unblocks the document. diff --git a/LayoutTests/imported/w3c/web-platform-tests/inert/inert-node-is-unfocusable-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/inert/inert-node-is-unfocusable-expected.txt index de5ae33eea52..96aea8bb5557 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/inert/inert-node-is-unfocusable-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/inert/inert-node-is-unfocusable-expected.txt @@ -9,5 +9,5 @@ PASS Button with inert atribute is unfocusable. PASS All focusable elements inside inert subtree are unfocusable PASS Can get inert via property PASS Elements inside of inert subtrees return false when getting 'inert' -FAIL Setting inert via property correctly modifies inert state assert_equals: expected true but got false +PASS Setting inert via property correctly modifies inert state diff --git a/LayoutTests/imported/w3c/web-platform-tests/svg/struct/scripted/autofocus-attribute-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/svg/struct/scripted/autofocus-attribute-expected.txt index 8c5b18ad02e5..8bc76c05d8ee 100644 --- a/LayoutTests/imported/w3c/web-platform-tests/svg/struct/scripted/autofocus-attribute-expected.txt +++ b/LayoutTests/imported/w3c/web-platform-tests/svg/struct/scripted/autofocus-attribute-expected.txt @@ -1,5 +1,5 @@ PASS should support autofocus PASS Renderable element with tabindex should support autofocus -FAIL Never-rendered element with tabindex should not support autofocus promise_test: Unhandled rejection with value: object "TypeError: null is not an object (evaluating 'w.document.activeElement.tagName')" +FAIL Never-rendered element with tabindex should not support autofocus assert_equals: expected "svg" but got "metadata" diff --git a/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert-expected.txt b/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert-expected.txt deleted file mode 100644 index 73dc088d9c28..000000000000 --- a/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-focusing-steps-inert-expected.txt +++ /dev/null @@ -1,7 +0,0 @@ - - -FAIL dialog.show(): focusing steps should not change focus when dialog is inert assert_equals: dialog.show(): focusing steps should not change focus when dialog is inert expected Element node but got Element node -FAIL dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert assert_equals: dialog.showModal(): focusing steps should apply focus fixup rule when dialog is inert expected Element node - -... but got Element node - diff --git a/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-expected.txt b/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-expected.txt deleted file mode 100644 index fd172feb829e..000000000000 --- a/LayoutTests/platform/ios-wk2/imported/w3c/web-platform-tests/html/semantics/interactive-elements/the-dialog-element/dialog-showModal-expected.txt +++ /dev/null @@ -1,14 +0,0 @@ - -PASS dialog element: showModal() -PASS showModal() on a that already has an open attribute throws an InvalidStateError exception -PASS showModal() on a after initial showModal() and removing the open attribute -PASS showModal() on a not in a Document throws an InvalidStateError exception -PASS when opening multiple dialogs, only the newest one is non-inert -PASS opening dialog without focusable children -PASS opening dialog with multiple focusable children -FAIL opening dialog with multiple focusable children, one having the autofocus attribute assert_equals: expected Element node but got Element node
- -isFocusable()) { + auto isNewElementFocusable = [&] { + if (!newFocusedElement) + return false; + // Resolving isFocusable() may require matching :focus-within as if the focus was already on the new element. + newFocusedElement->setHasTentativeFocus(true); + bool isFocusable = newFocusedElement->isFocusable(); + newFocusedElement->setHasTentativeFocus(false); + return isFocusable; + }(); + + if (isNewElementFocusable) { if (&newFocusedElement->document() != this) { // Bluring oldFocusedElement may have moved newFocusedElement across documents. return false; diff --git a/Source/WebCore/dom/Element.cpp b/Source/WebCore/dom/Element.cpp index 62ebdc39fde4..73344b8a47f6 100644 --- a/Source/WebCore/dom/Element.cpp +++ b/Source/WebCore/dom/Element.cpp @@ -866,6 +866,15 @@ void Element::setHasFocusWithin(bool value) } } +void Element::setHasTentativeFocus(bool value) +{ + // Tentative focus is used when trying to set the focus on a new element. + for (auto& ancestor : composedTreeAncestors(*this)) { + ASSERT(ancestor.hasFocusWithin() != value); + document().userActionElements().setHasFocusWithin(ancestor, value); + } +} + void Element::setHovered(bool value, Style::InvalidationScope invalidationScope, HitTestRequest) { if (value == hovered()) @@ -3698,25 +3707,58 @@ const RenderStyle* Element::renderOrDisplayContentsStyle(PseudoId pseudoId) cons const RenderStyle* Element::resolveComputedStyle(ResolveComputedStyleMode mode) { ASSERT(isConnected()); - ASSERT(!existingComputedStyle() || hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag)); - Deque, 32> elementsRequiringComputedStyle({ this }); - const RenderStyle* computedStyle = nullptr; + bool isInDisplayNoneTree = false; - // Collect ancestors until we find one that has style. - for (auto& ancestor : composedTreeAncestors(*this)) { - if (auto* existingStyle = ancestor.existingComputedStyle()) { - computedStyle = existingStyle; - break; + // Traverse the ancestor chain to find the rootmost element that has invalid computed style. + auto* rootmostInvalidElement = [&]() -> const Element* { + if (document().hasPendingFullStyleRebuild()) + return document().documentElement(); + + if (!document().documentElement() || document().documentElement()->hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag)) + return document().documentElement(); + + const Element* rootmost = nullptr; + + for (auto* element = this; element; element = element->parentElementInComposedTree()) { + if (element->hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag)) { + rootmost = element; + continue; + } + auto* existing = element->existingComputedStyle(); + if (!existing) { + rootmost = element; + continue; + } + if (mode == ResolveComputedStyleMode::RenderedOnly && existing->display() == DisplayType::None) { + isInDisplayNoneTree = true; + return nullptr; + } } - elementsRequiringComputedStyle.prepend(&ancestor); - } + return rootmost; + }(); + + if (isInDisplayNoneTree) + return nullptr; + + if (!rootmostInvalidElement) + return existingComputedStyle(); + + auto* ancestorWithValidStyle = rootmostInvalidElement->parentElementInComposedTree(); + + Vector, 32> elementsRequiringComputedStyle; + for (auto* toResolve = this; toResolve != ancestorWithValidStyle; toResolve = toResolve->parentElementInComposedTree()) + elementsRequiringComputedStyle.append(toResolve); + + auto* computedStyle = ancestorWithValidStyle ? ancestorWithValidStyle->existingComputedStyle() : nullptr; // On iOS request delegates called during styleForElement may result in re-entering WebKit and killing the style resolver. Style::PostResolutionCallbackDisabler disabler(document(), Style::PostResolutionCallbackDisabler::DrainCallbacks::No); // Resolve and cache styles starting from the most distant ancestor. - for (auto& element : elementsRequiringComputedStyle) { + // FIXME: This is not as efficient as it could be. For example if an ancestor has a non-inherited style change but + // the styles are otherwise clean we would not need to re-resolve descendants. + for (auto& element : makeReversedRange(elementsRequiringComputedStyle)) { bool hadDisplayContents = element->hasDisplayContents(); auto style = document().styleForElementIgnoringPendingStylesheets(*element, computedStyle); computedStyle = style.get(); @@ -3733,21 +3775,6 @@ const RenderStyle* Element::resolveComputedStyle(ResolveComputedStyleMode mode) return computedStyle; } -bool Element::hasValidStyle() const -{ - if (!document().needsStyleRecalc()) - return true; - - if (document().hasPendingFullStyleRebuild()) - return false; - - for (auto& element : lineageOfType(*this)) { - if (element.styleValidity() != Style::Validity::Valid) - return false; - } - return true; -} - bool Element::isFocusableWithoutResolvingFullStyle() const { auto isFocusableStyle = [](const RenderStyle* style) { @@ -3758,26 +3785,12 @@ bool Element::isFocusableWithoutResolvingFullStyle() const && !style->effectiveInert(); }; - if (renderStyle() || hasValidStyle()) + if (renderStyle()) return isFocusableStyle(renderStyle()); - auto computedStyleForElement = [](Element& element) -> const RenderStyle* { - auto* style = element.hasNodeFlag(NodeFlag::IsComputedStyleInvalidFlag) ? nullptr : element.existingComputedStyle(); - return style ? style : element.resolveComputedStyle(ResolveComputedStyleMode::RenderedOnly); - }; - // Compute style in yet unstyled subtree. - auto* style = computedStyleForElement(const_cast(*this)); - if (!isFocusableStyle(style)) - return false; - - for (auto& element : composedTreeAncestors(const_cast(*this))) { - auto* style = computedStyleForElement(element); - if (!style || style->display() == DisplayType::None) - return false; - } - - return true; + auto* style = const_cast(*this).resolveComputedStyle(ResolveComputedStyleMode::RenderedOnly); + return isFocusableStyle(style); } const RenderStyle& Element::resolvePseudoElementStyle(PseudoId pseudoElementSpecifier) diff --git a/Source/WebCore/dom/Element.h b/Source/WebCore/dom/Element.h index 354d0c98493f..f713c8faed18 100644 --- a/Source/WebCore/dom/Element.h +++ b/Source/WebCore/dom/Element.h @@ -373,6 +373,7 @@ class Element : public ContainerNode { void setBeingDragged(bool); void setHasFocusVisible(bool); void setHasFocusWithin(bool); + void setHasTentativeFocus(bool); std::optional tabIndexSetExplicitly() const; bool shouldBeIgnoredInSequentialFocusNavigation() const { return defaultTabIndex() < 0 && !supportsFocus(); } @@ -401,7 +402,6 @@ class Element : public ContainerNode { bool needsStyleInvalidation() const; - bool hasValidStyle() const; bool isFocusableWithoutResolvingFullStyle() const; // Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.)