Changeset 249339 in webkit


Ignore:
Timestamp:
Aug 30, 2019 12:28:57 PM (5 years ago)
Author:
Wenson Hsieh
Message:

Caret does not appear in text field inside a transformed, overflow: hidden container
https://bugs.webkit.org/show_bug.cgi?id=201317
<rdar://problem/54859264>

Reviewed by Simon Fraser.

Source/WebCore:

This patch refactors the heuristic for determining whether to suppress selection gestures and UI in a way that
fixes the corner case encountered in this bug. To understand why this test case fails with our existing
heuristic, consider the below test case.

Let's say we have an input field inside an "overflow: hidden;" container, which is positioned in such a way that
it is completely clipped by its enclosing container which is also "overflow: hidden". Our existing logic would
appropriately identify this as a hidden editable element.

However, let's now apply a transform to the input field's closest "overflow: hidden" ancestor, such that the
field is now visible. Since RenderLayer::offsetFromAncestor doesn't take transforms into account when we try to
find the offset of the "overflow: hidden" layer relative to the root view, we end up passing an offsetFromRoot
of (0, 100vw) to RenderLayer::calculateClipRects, which computes a background clip rect of (0, 0, 100vw, 100vh).

This means that at the end of RenderLayer::calculateClipRects, we end up intersecting the background clip rect
(0, 0, 100vw, 100vh) against (100vw, 0, 100vw, 100vh), which results in the empty rect, and subsequently makes
us believe we're editing a hidden editable element.

Instead of tacking on more logic to isTransparentOrFullyClippedRespectingParentFrames, we can fix this by using
RenderObject::computeVisibleRectInContainer instead, performing a similar walk up the render tree to compute the
visible rect of each focused element or subframe relative to its root. This is capable of taking transforms into
account. See comments below for more details.

Test: editing/selection/ios/show-selection-in-transformed-container-2.html

  • rendering/RenderLayer.cpp:

(WebCore::RenderLayer::isTransparentRespectingParentFrames const):

Split out isTransparentOrFullyClippedRespectingParentFrames into two methods: RenderLayer's
isTransparentRespectingParentFrames, and RenderObject's hasNonEmptyVisibleRectRespectingParentFrames. The
transparency check starts at the enclosing layer and walks up the layer tree, while the non-empty visible rect
check looks for renderers that are completely empty relative to their root views.

  • rendering/RenderLayer.h:
  • rendering/RenderObject.cpp:

(WebCore::RenderObject::hasNonEmptyVisibleRectRespectingParentFrames const):

Rewrite logic for detecting completely clipped editable areas (that formerly lived in
isTransparentOrFullyClippedRespectingParentFrames) to use computeVisibleRectInContainer instead.

  • rendering/RenderObject.h:

Source/WebKit:

Adjust isTransparentOrFullyClipped to use the new methods in RenderLayer and RenderObject. See WebCore ChangeLog
for more details.

  • WebProcess/WebPage/ios/WebPageIOS.mm:

(WebKit::WebPage::isTransparentOrFullyClipped const):

LayoutTests:

Add a new layout test that covers this scenario. See WebCore ChangeLog for additional detail.

  • editing/selection/ios/show-selection-in-transformed-container-2-expected.txt: Added.
  • editing/selection/ios/show-selection-in-transformed-container-2.html: Added.
Location:
trunk
Files:
2 added
8 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r249338 r249339  
     12019-08-30  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Caret does not appear in text field inside a transformed, overflow: hidden container
     4        https://bugs.webkit.org/show_bug.cgi?id=201317
     5        <rdar://problem/54859264>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Add a new layout test that covers this scenario. See WebCore ChangeLog for additional detail.
     10
     11        * editing/selection/ios/show-selection-in-transformed-container-2-expected.txt: Added.
     12        * editing/selection/ios/show-selection-in-transformed-container-2.html: Added.
     13
    1142019-08-30  Chris Dumez  <cdumez@apple.com>
    215
  • trunk/Source/WebCore/ChangeLog

    r249338 r249339  
     12019-08-30  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Caret does not appear in text field inside a transformed, overflow: hidden container
     4        https://bugs.webkit.org/show_bug.cgi?id=201317
     5        <rdar://problem/54859264>
     6
     7        Reviewed by Simon Fraser.
     8
     9        This patch refactors the heuristic for determining whether to suppress selection gestures and UI in a way that
     10        fixes the corner case encountered in this bug. To understand why this test case fails with our existing
     11        heuristic, consider the below test case.
     12
     13        Let's say we have an input field inside an "overflow: hidden;" container, which is positioned in such a way that
     14        it is completely clipped by its enclosing container which is also "overflow: hidden". Our existing logic would
     15        appropriately identify this as a hidden editable element.
     16
     17        However, let's now apply a transform to the input field's closest "overflow: hidden" ancestor, such that the
     18        field is now visible. Since RenderLayer::offsetFromAncestor doesn't take transforms into account when we try to
     19        find the offset of the "overflow: hidden" layer relative to the root view, we end up passing an offsetFromRoot
     20        of (0, 100vw) to RenderLayer::calculateClipRects, which computes a background clip rect of (0, 0, 100vw, 100vh).
     21
     22        This means that at the end of RenderLayer::calculateClipRects, we end up intersecting the background clip rect
     23        (0, 0, 100vw, 100vh) against (100vw, 0, 100vw, 100vh), which results in the empty rect, and subsequently makes
     24        us believe we're editing a hidden editable element.
     25
     26        Instead of tacking on more logic to isTransparentOrFullyClippedRespectingParentFrames, we can fix this by using
     27        RenderObject::computeVisibleRectInContainer instead, performing a similar walk up the render tree to compute the
     28        visible rect of each focused element or subframe relative to its root. This is capable of taking transforms into
     29        account. See comments below for more details.
     30
     31        Test: editing/selection/ios/show-selection-in-transformed-container-2.html
     32
     33        * rendering/RenderLayer.cpp:
     34        (WebCore::RenderLayer::isTransparentRespectingParentFrames const):
     35
     36        Split out isTransparentOrFullyClippedRespectingParentFrames into two methods: RenderLayer's
     37        isTransparentRespectingParentFrames, and RenderObject's hasNonEmptyVisibleRectRespectingParentFrames. The
     38        transparency check starts at the enclosing layer and walks up the layer tree, while the non-empty visible rect
     39        check looks for renderers that are completely empty relative to their root views.
     40
     41        * rendering/RenderLayer.h:
     42        * rendering/RenderObject.cpp:
     43        (WebCore::RenderObject::hasNonEmptyVisibleRectRespectingParentFrames const):
     44
     45        Rewrite logic for detecting completely clipped editable areas (that formerly lived in
     46        isTransparentOrFullyClippedRespectingParentFrames) to use computeVisibleRectInContainer instead.
     47
     48        * rendering/RenderObject.h:
     49
    1502019-08-30  Chris Dumez  <cdumez@apple.com>
    251
  • trunk/Source/WebCore/rendering/RenderLayer.cpp

    r249309 r249339  
    68366836}
    68376837
    6838 bool RenderLayer::isTransparentOrFullyClippedRespectingParentFrames() const
     6838bool RenderLayer::isTransparentRespectingParentFrames() const
    68396839{
    68406840    static const double minimumVisibleOpacity = 0.01;
     
    68446844        currentOpacity *= layer->renderer().style().opacity();
    68456845        if (currentOpacity < minimumVisibleOpacity)
    6846             return true;
    6847     }
    6848 
    6849     auto hasEmptyClipRect = [] (const RenderLayer& layer) -> bool {
    6850         auto* frameView = layer.renderer().document().view();
    6851         if (!frameView)
    6852             return false;
    6853 
    6854         auto* renderView = frameView->renderView();
    6855         if (!renderView)
    6856             return false;
    6857 
    6858         auto* renderViewLayer = renderView->layer();
    6859         if (!renderViewLayer)
    6860             return false;
    6861 
    6862         if (is<HTMLFrameOwnerElement>(layer.renderer().element()) && layer.visibleSize().isEmpty())
    6863             return true;
    6864 
    6865         LayoutRect layerBounds;
    6866         ClipRect backgroundRect;
    6867         ClipRect foregroundRect;
    6868         layer.calculateRects({ renderViewLayer, TemporaryClipRects }, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, layer.offsetFromAncestor(renderViewLayer));
    6869         return backgroundRect.isEmpty();
    6870     };
    6871 
    6872     for (auto* layer = this; layer; layer = enclosingFrameRenderLayer(*layer)) {
    6873         if (hasEmptyClipRect(*layer))
    68746846            return true;
    68756847    }
  • trunk/Source/WebCore/rendering/RenderLayer.h

    r249091 r249339  
    898898    bool paintingFrequently() const { return m_paintFrequencyTracker.paintingFrequently(); }
    899899
    900     WEBCORE_EXPORT bool isTransparentOrFullyClippedRespectingParentFrames() const;
     900    WEBCORE_EXPORT bool isTransparentRespectingParentFrames() const;
    901901
    902902    void invalidateEventRegion();
  • trunk/Source/WebCore/rendering/RenderObject.cpp

    r248846 r249339  
    19031903}
    19041904
     1905bool RenderObject::hasNonEmptyVisibleRectRespectingParentFrames() const
     1906{
     1907    auto enclosingFrameRenderer = [] (const RenderObject& renderer) {
     1908        auto* ownerElement = renderer.document().ownerElement();
     1909        return ownerElement ? ownerElement->renderer() : nullptr;
     1910    };
     1911
     1912    auto hasEmptyVisibleRect = [] (const RenderObject& renderer) {
     1913        VisibleRectContext context { false, false, { VisibleRectContextOption::UseEdgeInclusiveIntersection, VisibleRectContextOption::ApplyCompositedClips }};
     1914        auto& box = renderer.enclosingBoxModelObject();
     1915        auto clippedBounds = box.computeVisibleRectInContainer(box.borderBoundingBox(), &box.view(), context);
     1916        return !clippedBounds || clippedBounds->isEmpty();
     1917    };
     1918
     1919    for (auto* renderer = this; renderer; renderer = enclosingFrameRenderer(*renderer)) {
     1920        if (hasEmptyVisibleRect(*renderer))
     1921            return true;
     1922    }
     1923
     1924    return false;
     1925}
     1926
    19051927#if ENABLE(TREE_DEBUGGING)
    19061928
  • trunk/Source/WebCore/rendering/RenderObject.h

    r248807 r249339  
    684684    virtual Optional<LayoutRect> computeVisibleRectInContainer(const LayoutRect&, const RenderLayerModelObject* repaintContainer, VisibleRectContext) const;
    685685    virtual Optional<FloatRect> computeFloatVisibleRectInContainer(const FloatRect&, const RenderLayerModelObject* repaintContainer, VisibleRectContext) const;
     686
     687    WEBCORE_EXPORT bool hasNonEmptyVisibleRectRespectingParentFrames() const;
    686688
    687689    virtual unsigned int length() const { return 1; }
  • trunk/Source/WebKit/ChangeLog

    r249336 r249339  
     12019-08-30  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        Caret does not appear in text field inside a transformed, overflow: hidden container
     4        https://bugs.webkit.org/show_bug.cgi?id=201317
     5        <rdar://problem/54859264>
     6
     7        Reviewed by Simon Fraser.
     8
     9        Adjust isTransparentOrFullyClipped to use the new methods in RenderLayer and RenderObject. See WebCore ChangeLog
     10        for more details.
     11
     12        * WebProcess/WebPage/ios/WebPageIOS.mm:
     13        (WebKit::WebPage::isTransparentOrFullyClipped const):
     14
    1152019-08-30  Simon Fraser  <simon.fraser@apple.com>
    216
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r249296 r249339  
    198198
    199199    auto* enclosingLayer = renderer->enclosingLayer();
    200     return enclosingLayer && enclosingLayer->isTransparentOrFullyClippedRespectingParentFrames();
     200    if (enclosingLayer && enclosingLayer->isTransparentRespectingParentFrames())
     201        return true;
     202
     203    return renderer->hasNonEmptyVisibleRectRespectingParentFrames();
    201204}
    202205
Note: See TracChangeset for help on using the changeset viewer.