Changeset 183777 in webkit


Ignore:
Timestamp:
May 4, 2015 4:31:10 PM (9 years ago)
Author:
Simon Fraser
Message:

display:none iframes cause repeated compositing flushing
https://bugs.webkit.org/show_bug.cgi?id=144529

Reviewed by Darin Adler.

Source/WebCore:

FrameView::updateLayoutAndStyleIfNeededRecursive() only forces layout on rendered
frames, by virtue of using its Widget children which are FrameViews.

However, FrameView::flushCompositingStateIncludingSubframes() iterated over
all frames, and return false if any subframe needed layout. Thus, if it saw
non-rendered frames (which are never laid out), it would return false,
which causes the CFRunLoopObserver that drives flushing to run again.

Fix by having FrameView::flushCompositingStateIncludingSubframes() only check
rendered frames, using FrameTree::traverseNextRendered() (which needs to be public).

Also change FrameView::needsStyleRecalcOrLayout() and FrameView::updateLayoutAndStyleIfNeededRecursive()
to fetch the list of FrameViews using FrameTree's nextRenderedSibling(), rather than using
the Widget tree, since we'd like to eventually remove Widgets, and using the Frame
tree matches flushCompositingStateIncludingSubframes() and other code.

Test: compositing/iframes/display-none-subframe.html

  • page/FrameTree.h:
  • page/FrameView.cpp:

(WebCore::FrameView::flushCompositingStateIncludingSubframes):
(WebCore::FrameView::needsStyleRecalcOrLayout):
(WebCore::FrameView::renderedChildFrameViews): Helper that returns a vector
of Ref<FrameView>s for rendered frames only.
(WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):

  • page/FrameView.h:

LayoutTests:

Test with a display:none iframe that triggers a single compositing flush,
then counts how many occur in 10ms.

  • compositing/iframes/display-none-subframe-expected.txt: Added.
  • compositing/iframes/display-none-subframe.html: Added.
Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r183775 r183777  
     12015-05-04  Simon Fraser  <simon.fraser@apple.com>
     2
     3        display:none iframes cause repeated compositing flushing
     4        https://bugs.webkit.org/show_bug.cgi?id=144529
     5
     6        Reviewed by Darin Adler.
     7       
     8        Test with a display:none iframe that triggers a single compositing flush,
     9        then counts how many occur in 10ms.
     10
     11        * compositing/iframes/display-none-subframe-expected.txt: Added.
     12        * compositing/iframes/display-none-subframe.html: Added.
     13
    1142015-05-04  Simon Fraser  <simon.fraser@apple.com>
    215
  • trunk/Source/WebCore/ChangeLog

    r183776 r183777  
     12015-05-04  Simon Fraser  <simon.fraser@apple.com>
     2
     3        display:none iframes cause repeated compositing flushing
     4        https://bugs.webkit.org/show_bug.cgi?id=144529
     5
     6        Reviewed by Darin Adler.
     7       
     8        FrameView::updateLayoutAndStyleIfNeededRecursive() only forces layout on rendered
     9        frames, by virtue of using its Widget children which are FrameViews.
     10       
     11        However, FrameView::flushCompositingStateIncludingSubframes() iterated over
     12        all frames, and return false if any subframe needed layout. Thus, if it saw
     13        non-rendered frames (which are never laid out), it would return false,
     14        which causes the CFRunLoopObserver that drives flushing to run again.
     15       
     16        Fix by having FrameView::flushCompositingStateIncludingSubframes() only check
     17        rendered frames, using FrameTree::traverseNextRendered() (which needs to be public).
     18       
     19        Also change FrameView::needsStyleRecalcOrLayout() and FrameView::updateLayoutAndStyleIfNeededRecursive()
     20        to fetch the list of FrameViews using FrameTree's nextRenderedSibling(), rather than using
     21        the Widget tree, since we'd like to eventually remove Widgets, and using the Frame
     22        tree matches flushCompositingStateIncludingSubframes() and other code.
     23
     24        Test: compositing/iframes/display-none-subframe.html
     25
     26        * page/FrameTree.h:
     27        * page/FrameView.cpp:
     28        (WebCore::FrameView::flushCompositingStateIncludingSubframes):
     29        (WebCore::FrameView::needsStyleRecalcOrLayout):
     30        (WebCore::FrameView::renderedChildFrameViews): Helper that returns a vector
     31        of Ref<FrameView>s for rendered frames only.
     32        (WebCore::FrameView::updateLayoutAndStyleIfNeededRecursive):
     33        * page/FrameView.h:
     34
    1352015-05-04  Chris Dumez  <cdumez@apple.com>
    236
  • trunk/Source/WebCore/page/FrameTree.h

    r180062 r183777  
    5656        Frame* lastChild() const { return m_lastChild; }
    5757
     58        Frame* firstRenderedChild() const;
     59        Frame* nextRenderedSibling() const;
     60
    5861        WEBCORE_EXPORT bool isDescendantOf(const Frame* ancestor) const;
    5962       
     
    9194        unsigned scopedChildCount(TreeScope*) const;
    9295
    93         Frame* firstRenderedChild() const;
    94         Frame* nextRenderedSibling() const;
    95 
    9696        Frame& m_thisFrame;
    9797
  • trunk/Source/WebCore/page/FrameView.cpp

    r183775 r183777  
    10671067{
    10681068    bool allFramesFlushed = flushCompositingStateForThisFrame(&frame());
    1069    
    1070     for (Frame* child = frame().tree().firstChild(); child; child = child->tree().traverseNext(m_frame.ptr())) {
     1069
     1070    for (Frame* child = frame().tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
     1071        if (!child->view())
     1072            continue;
    10711073        bool flushed = child->view()->flushCompositingStateForThisFrame(&frame());
    10721074        allFramesFlushed &= flushed;
     
    25942596        return false;
    25952597
    2596     // Find child frames via the Widget tree, as updateLayoutAndStyleIfNeededRecursive() does.
    2597     Vector<Ref<FrameView>, 16> childViews;
    2598     childViews.reserveInitialCapacity(children().size());
    2599     for (auto& widget : children()) {
    2600         if (is<FrameView>(*widget))
    2601             childViews.uncheckedAppend(downcast<FrameView>(*widget));
    2602     }
    2603 
    2604     for (unsigned i = 0; i < childViews.size(); ++i) {
    2605         if (childViews[i]->needsStyleRecalcOrLayout())
     2598    for (auto& frameView : renderedChildFrameViews()) {
     2599        if (frameView->needsStyleRecalcOrLayout())
    26062600            return true;
    26072601    }
     
    40374031}
    40384032
     4033FrameView::FrameViewList FrameView::renderedChildFrameViews() const
     4034{
     4035    FrameViewList childViews;
     4036    for (Frame* frame = m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
     4037        if (frame->view())
     4038            childViews.append(*frame->view());
     4039    }
     4040   
     4041    return childViews;
     4042}
     4043
    40394044void FrameView::updateLayoutAndStyleIfNeededRecursive()
    40404045{
     
    40554060        layout();
    40564061
    4057     // Grab a copy of the children() set, as it may be mutated by the following updateLayoutAndStyleIfNeededRecursive
    4058     // calls, as they can potentially re-enter a layout of the parent frame view, which may add/remove scrollbars
    4059     // and thus mutates the children() set.
    4060     // We use the Widget children because walking the Frame tree would include display:none frames.
    4061     // FIXME: use FrameTree::traverseNextRendered().
    4062     Vector<Ref<FrameView>, 16> childViews;
    4063     childViews.reserveInitialCapacity(children().size());
    4064     for (auto& widget : children()) {
    4065         if (is<FrameView>(*widget))
    4066             childViews.uncheckedAppend(downcast<FrameView>(*widget));
    4067     }
    4068 
    4069     for (unsigned i = 0; i < childViews.size(); ++i)
    4070         childViews[i]->updateLayoutAndStyleIfNeededRecursive();
     4062    // Grab a copy of the child views, as the list may be mutated by the following updateLayoutAndStyleIfNeededRecursive
     4063    // calls, as they can potentially re-enter a layout of the parent frame view.
     4064    for (auto& frameView : renderedChildFrameViews())
     4065        frameView->updateLayoutAndStyleIfNeededRecursive();
    40714066
    40724067    // A child frame may have dirtied us during its layout.
  • trunk/Source/WebCore/page/FrameView.h

    r183775 r183777  
    518518    const HashSet<Widget*>& widgetsInRenderTree() const { return m_widgetsInRenderTree; }
    519519
     520    typedef Vector<Ref<FrameView>, 16> FrameViewList;
     521    FrameViewList renderedChildFrameViews() const;
     522
    520523    void addTrackedRepaintRect(const FloatRect&);
    521524
Note: See TracChangeset for help on using the changeset viewer.