Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for css content-visibility: hidden #4936

Closed

Conversation

cathiechen
Copy link
Contributor

@cathiechen cathiechen commented Oct 3, 2022

faf9ff8

Add support for css content-visibility: hidden
https://bugs.webkit.org/show_bug.cgi?id=236710

Reviewed by Antti Koivisto.

This patch implements support for content-visibility: hidden [1].
It introduces the fake inherited property effectiveSkipsContent to keep
track for Elements whether content-visibility is hidden and whether elements
should be skipped for painting and hit testing.

Several points in 4.3. Restrictions and Clarifications [2] are addressed too:
- scrollIntoView is adapted [4.3.5]
- tab-order navigation and focusing take skipped contents into account [3].

[1] https://drafts.csswg.org/css-contain/#using-cv-hidden
[2] https://drafts.csswg.org/css-contain/#cv-notes
[3] https://drafts.csswg.org/css-contain/#valdef-content-visibility-hidden

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-015-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-016-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-017-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-018-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-029-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-035-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-038-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-047-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-072-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-img-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-input-image-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-svg-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-006-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-009-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-010-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr-expected.txt:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::isFocusable const): skipped contents are not focusable
* Source/WebCore/page/FrameView.cpp:
(WebCore::FrameView::scrollRectToVisible): do not scroll if the RenderObject is skipped content
* Source/WebCore/rendering/RenderBlock.cpp:
(WebCore::RenderBlock::paintContents): do not paint contents for c-v: hidden
* Source/WebCore/rendering/RenderElement.h:
(WebCore::RenderElement::visibleToHitTesting const): mark skipped content as not visible to hit testing
(WebCore::RenderElement::shouldApplyLayoutContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyPaintContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyLayoutOrPaintContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplySizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyInlineSizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplySizeOrInlineSizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyStyleContainment const): c-v: hidden forces containment
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintList): do not paint child layers of a c-v: hidden container
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateAfterDescendants):
* Source/WebCore/rendering/RenderObject.cpp:
(WebCore::RenderObject::isSkippedContent const):
(WebCore::RenderObject::shouldSkipContent const):
* Source/WebCore/rendering/RenderObject.h:
* Source/WebCore/rendering/RenderReplaced.cpp:
(WebCore::RenderReplaced::paint): do not paint contents for content-visibility: hidden
* Source/WebCore/rendering/RenderWidget.cpp:
(WebCore::RenderWidget::paint): do not paint contents for content-visibility: hidden
* Source/WebCore/rendering/style/RenderStyle.h:
(WebCore::RenderStyle::effectiveSkipsContent const):
(WebCore::RenderStyle::setEffectiveSkipsContent):
* Source/WebCore/rendering/style/StyleRareInheritedData.cpp:
(WebCore::StyleRareInheritedData::StyleRareInheritedData):
(WebCore::StyleRareInheritedData::operator== const):
* Source/WebCore/rendering/style/StyleRareInheritedData.h:
* Source/WebCore/style/StyleAdjuster.cpp:
(WebCore::Style::Adjuster::adjust const):

faf9ff8

Misc iOS, tvOS & watchOS macOS Linux Windows
βœ… πŸ§ͺ style βœ… πŸ›  ios βœ… πŸ›  mac βœ… πŸ›  wpe βœ… πŸ›  πŸ§ͺ win
βœ… πŸ§ͺ bindings βœ… πŸ›  ios-sim βœ… πŸ›  mac-debug βœ… πŸ›  gtk βœ… πŸ›  wincairo
βœ… πŸ§ͺ webkitperl βœ… πŸ§ͺ ios-wk2 βœ… πŸ›  mac-AS-debug βœ… πŸ§ͺ gtk-wk2
  πŸ§ͺ api-ios   πŸ§ͺ api-mac βœ… πŸ§ͺ api-gtk
βœ… πŸ›  tv   πŸ§ͺ mac-wk1
βœ… πŸ›  tv-sim   πŸ§ͺ mac-wk2
  πŸ›  πŸ§ͺ merge βœ… πŸ›  watch   πŸ§ͺ mac-AS-debug-wk2
βœ… πŸ›  watch-sim βœ… πŸ§ͺ mac-wk2-stress

@cathiechen cathiechen self-assigned this Oct 3, 2022
@cathiechen cathiechen added CSS Cascading Style Sheets implementation WebKit Local Build labels Oct 3, 2022
@rwlbuis rwlbuis requested review from darinadler and smfr and removed request for rniwa and cdumez October 3, 2022 19:52
@rwlbuis
Copy link
Contributor

rwlbuis commented Oct 3, 2022

Note that the patch has been reduced a lot and now has only the basics and should be easier to review. The plan is to file bugs like https://bugs.webkit.org/show_bug.cgi?id=245775 for related functionality on top of this.

@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 4, 2022
@rwlbuis rwlbuis removed the merging-blocked Applied to prevent a change from being merged label Oct 12, 2022
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from 213fc8a to 8ae0db5 Compare October 12, 2022 13:41
@rwlbuis rwlbuis self-assigned this Oct 12, 2022
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 12, 2022
@rwlbuis rwlbuis removed the merging-blocked Applied to prevent a change from being merged label Oct 14, 2022
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from 8ae0db5 to c9adf21 Compare October 14, 2022 14:34
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 14, 2022
Comment on lines 103 to 107
struct ContentVisibilityData {
WTF_MAKE_STRUCT_FAST_ALLOCATED;
bool isSkippedContent { false };
ContentVisibility contentVisibility;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is nothing else to put here, this doesn't really need a heap allocated struct. Those fields could just sit directly in the rare data and take less space.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, we will have more flags here for content-visibility: auto to indicate if the element is relevant to the user, something like:

    bool isIntersectingViewport { false };
    bool subtreeHasFocus { false };
    bool subtreeHasSelection { false };

See the WIP-patch in https://bugs.webkit.org/show_bug.cgi?id=236711, looks like there isn't complex function there.
I'm fine to put isSkippedContent and contentVisibility in ElementRareData.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Cathie, I think the idea was to get rid of the ContentVisibilityData completely, or at least not dynamically allocate it. Anyway I can fix this tomorrow morning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pushed a new version for what I think was the initial suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The failing win test has been flaky for a while so I think we can ignore it:
https://ews-build.webkit.org/#/builders/10

So this is ready for review again.

@@ -3389,7 +3389,7 @@ void RenderLayer::paintList(LayerList layerIterator, GraphicsContext& context, c
if (layerIterator.begin() == layerIterator.end())
return;

if (!hasSelfPaintingLayerDescendant())
if (!hasSelfPaintingLayerDescendant() || renderer().shouldSkipContent())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is it determined where these tests are needed? It seems bit random.

The spec says "...to a large extent do not update their styles at all unless explicitly requested by script" and "user agent should additionally avoid as much layout/rendering work as possible for skipped contents" yet I see only tests about skipping painting and hit testing. This won't really help performance much. Is the plan to add those things later?

The spec also says "This is similar to giving the contents display: none". In display:none case we don't actually build a render tree at all for that subtree. Is that the intention of the spec?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I'll let @rwlbuis explain this, he does most work of this feature:)

Copy link
Contributor

@rwlbuis rwlbuis Oct 25, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests are added in order to comply with the specification/tests or to make things more efficient, see also the per method comments in the commit message.

A lot of the performance comes from enabling all kinds of containment when content-visibility is hidden, we did that by adjusting shouldApply*Containment methods. But note paint containment mostly works through overflow: clip, so this patch optimises more by avoiding painting of contents/children. I agree for hit testing it may not be a big win but for correctness we have to implement that (whenever the contents are skipped).

Regarding display: none, there are a lot of similarities, and I would not be surprised that its problems was the starting point for content-visibility. Basically display: none changes are heavy while content-visibility changes should be instant ideally. I think the hint was a way to help the reader think about content-visibility: hidden. In the content-visibility case I think we do want to build the render tree, to be less costly when updating the property, and to make content-visibility: auto efficient as well.

Work on top, besides the obvious content-visibility: auto work, will be in the area of editing, resize/intersection observer, top layers/dialogs, accessibility. From the top of my head, except maybe resize/intersection observer, I think it is more about correctness/spec compliance than performance.

@cathiechen cathiechen closed this Oct 24, 2022
@cathiechen cathiechen force-pushed the content-visibility-hidden-split-001 branch from c9adf21 to bc9fcf4 Compare October 24, 2022 16:35
@cathiechen cathiechen removed the merging-blocked Applied to prevent a change from being merged label Oct 24, 2022
@cathiechen cathiechen reopened this Oct 24, 2022
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 24, 2022
@cathiechen cathiechen removed the merging-blocked Applied to prevent a change from being merged label Oct 24, 2022
@cathiechen cathiechen force-pushed the content-visibility-hidden-split-001 branch from 7768000 to d640921 Compare October 24, 2022 18:13
@rwlbuis rwlbuis removed the merging-blocked Applied to prevent a change from being merged label Oct 28, 2022
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from 815a829 to ea3d9e7 Compare October 28, 2022 12:21
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 28, 2022
@rwlbuis rwlbuis removed the merging-blocked Applied to prevent a change from being merged label Oct 28, 2022
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from ea3d9e7 to a11cb58 Compare October 28, 2022 15:07
@webkit-ews-buildbot webkit-ews-buildbot added the merging-blocked Applied to prevent a change from being merged label Oct 28, 2022
@rwlbuis rwlbuis removed the merging-blocked Applied to prevent a change from being merged label Oct 31, 2022
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from a11cb58 to 1a136ee Compare October 31, 2022 09:38
Copy link
Contributor

@anttijk anttijk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

much better!

@@ -521,37 +524,37 @@ inline bool RenderElement::shouldApplySizeOrStyleContainment(bool containsAccord

inline bool RenderElement::shouldApplyLayoutContainment() const
{
return shouldApplyLayoutOrPaintContainment(style().containsLayout());
return shouldApplyLayoutOrPaintContainment(style().containsLayout() || style().contentVisibility() != ContentVisibility::Visible);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this test could be in containsLayout() function? Same for containsPaint() etc.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to keep this as-is for now. Future work on this (forced layout/content-visibility: auto) may need to change/extend these code sections and it is not quite clear yet how.

@@ -2553,6 +2553,16 @@ String RenderObject::debugDescription() const
return builder.toString();
}

bool RenderObject::isSkippedContent() const
{
return style().isInSkippedContentSubtree() && parent() && parent()->style().isInSkippedContentSubtree();
Copy link
Contributor

@anttijk anttijk Oct 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is sufficient to test the parent bit only (render is skipped if the parent is skipping the content)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice thanks!

@@ -550,6 +550,8 @@ class RenderStyle {

ContentVisibility contentVisibility() const { return static_cast<ContentVisibility>(m_rareNonInheritedData->contentVisibility); }

bool isInSkippedContentSubtree() const { return m_rareInheritedData->isInSkippedContentSubtree; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something like effectiveSkipsContent might be less clunky and communicate that this is an effective property (similar to others).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

https://bugs.webkit.org/show_bug.cgi?id=236710

Reviewed by Antti Koivisto.

This patch implements support for content-visibility: hidden [1].
It introduces the fake inherited property effectiveSkipsContent to keep
track for Elements whether content-visibility is hidden and whether elements
should be skipped for painting and hit testing.

Several points in 4.3. Restrictions and Clarifications [2] are addressed too:
- scrollIntoView is adapted [4.3.5]
- tab-order navigation and focusing take skipped contents into account [3].

[1] https://drafts.csswg.org/css-contain/#using-cv-hidden
[2] https://drafts.csswg.org/css-contain/#cv-notes
[3] https://drafts.csswg.org/css-contain/#valdef-content-visibility-hidden

* LayoutTests/TestExpectations:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-015-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-016-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-017-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-018-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-029-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-035-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-038-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-047-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-072-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-img-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-input-image-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-contain/content-visibility/content-visibility-svg-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-006-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-009-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/css/css-sizing/contain-intrinsic-size/auto-010-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-focus-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/html/semantics/embedded-content/the-embed-element/embed-document-under-content-visibility-gbcr-expected.txt:
* Source/WebCore/dom/Element.cpp:
(WebCore::Element::isFocusable const): skipped contents are not focusable
* Source/WebCore/page/FrameView.cpp:
(WebCore::FrameView::scrollRectToVisible): do not scroll if the RenderObject is skipped content
* Source/WebCore/rendering/RenderBlock.cpp:
(WebCore::RenderBlock::paintContents): do not paint contents for c-v: hidden
* Source/WebCore/rendering/RenderElement.h:
(WebCore::RenderElement::visibleToHitTesting const): mark skipped content as not visible to hit testing
(WebCore::RenderElement::shouldApplyLayoutContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyPaintContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyLayoutOrPaintContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplySizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyInlineSizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplySizeOrInlineSizeContainment const): c-v: hidden forces containment
(WebCore::RenderElement::shouldApplyStyleContainment const): c-v: hidden forces containment
* Source/WebCore/rendering/RenderLayer.cpp:
(WebCore::RenderLayer::paintList): do not paint child layers of a c-v: hidden container
* Source/WebCore/rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateAfterDescendants):
* Source/WebCore/rendering/RenderObject.cpp:
(WebCore::RenderObject::isSkippedContent const):
(WebCore::RenderObject::shouldSkipContent const):
* Source/WebCore/rendering/RenderObject.h:
* Source/WebCore/rendering/RenderReplaced.cpp:
(WebCore::RenderReplaced::paint): do not paint contents for content-visibility: hidden
* Source/WebCore/rendering/RenderWidget.cpp:
(WebCore::RenderWidget::paint): do not paint contents for content-visibility: hidden
* Source/WebCore/rendering/style/RenderStyle.h:
(WebCore::RenderStyle::effectiveSkipsContent const):
(WebCore::RenderStyle::setEffectiveSkipsContent):
* Source/WebCore/rendering/style/StyleRareInheritedData.cpp:
(WebCore::StyleRareInheritedData::StyleRareInheritedData):
(WebCore::StyleRareInheritedData::operator== const):
* Source/WebCore/rendering/style/StyleRareInheritedData.h:
* Source/WebCore/style/StyleAdjuster.cpp:
(WebCore::Style::Adjuster::adjust const):
@rwlbuis rwlbuis force-pushed the content-visibility-hidden-split-001 branch from 1a136ee to faf9ff8 Compare November 1, 2022 10:05
@rwlbuis rwlbuis added request-merge-queue Request a pull request to be added to merge-queue once ready merge-queue Applied to send a pull request to merge-queue and removed request-merge-queue Request a pull request to be added to merge-queue once ready merge-queue Applied to send a pull request to merge-queue labels Nov 1, 2022
@cathiechen cathiechen removed their assignment Nov 1, 2022
@rwlbuis rwlbuis added merge-queue Applied to send a pull request to merge-queue request-merge-queue Request a pull request to be added to merge-queue once ready and removed merge-queue Applied to send a pull request to merge-queue request-merge-queue Request a pull request to be added to merge-queue once ready labels Nov 1, 2022
@cathiechen cathiechen added the merge-queue Applied to send a pull request to merge-queue label Nov 1, 2022
@rwlbuis
Copy link
Contributor

rwlbuis commented Nov 1, 2022

This system gets confused about multiple people working on a PR, and git-webkit was not able to make me owner of this PR. So I will use good old bugzilla.

@cathiechen cathiechen removed the merge-queue Applied to send a pull request to merge-queue label Nov 1, 2022
@webkit-commit-queue webkit-commit-queue added the merging-blocked Applied to prevent a change from being merged label Nov 1, 2022
@rwlbuis
Copy link
Contributor

rwlbuis commented Nov 1, 2022

This landed as r256186.

@darinadler darinadler closed this Nov 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CSS Cascading Style Sheets implementation merging-blocked Applied to prevent a change from being merged
Projects
None yet
7 participants