AX: AXIsolatedTree::updateChildren sometimes fails to update isolated subtrees when...
[WebKit-https.git] / Source / WebCore / page / InteractionRegion.cpp
1 /*
2  * Copyright (C) 2022 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InteractionRegion.h"
28
29 #include "Document.h"
30 #include "Frame.h"
31 #include "FrameSnapshotting.h"
32 #include "FrameView.h"
33 #include "GeometryUtilities.h"
34 #include "HTMLAnchorElement.h"
35 #include "HTMLFormControlElement.h"
36 #include "HitTestResult.h"
37 #include "Page.h"
38 #include "PathUtilities.h"
39 #include "PlatformMouseEvent.h"
40 #include "RenderBox.h"
41 #include "SimpleRange.h"
42 #include <wtf/NeverDestroyed.h>
43
44 namespace WebCore {
45
46 InteractionRegion::~InteractionRegion() = default;
47
48 static FloatRect absoluteBoundingRectForRange(const SimpleRange& range)
49 {
50     return unionRectIgnoringZeroRects(RenderObject::absoluteBorderAndTextRects(range, {
51         RenderObject::BoundingRectBehavior::RespectClipping,
52         RenderObject::BoundingRectBehavior::UseVisibleBounds,
53         RenderObject::BoundingRectBehavior::IgnoreTinyRects,
54     }));
55 }
56
57 static std::optional<InteractionRegion> regionForElement(Element& element)
58 {
59     auto& frameView = *element.document().frame()->view();
60     auto& mainFrameView = *element.document().frame()->mainFrame().view();
61     
62     IntRect frameClipRect;
63 #if PLATFORM(IOS_FAMILY)
64     frameClipRect = enclosingIntRect(frameView.exposedContentRect());
65 #else
66     if (auto viewExposedRect = frameView.viewExposedRect())
67         frameClipRect = enclosingIntRect(*viewExposedRect);
68     else
69         frameClipRect = frameView.visibleContentRect();
70 #endif
71
72     auto* renderer = element.renderer();
73     if (!renderer)
74         return std::nullopt;
75
76     Vector<FloatRect> rectsInContentCoordinates;
77     InteractionRegion region;
78     auto linkRange = makeRangeSelectingNode(element);
79     
80     if (linkRange)
81         region.hasLightBackground = estimatedBackgroundColorForRange(*linkRange, *element.document().frame()).luminance() > 0.5;
82     
83     if (linkRange && renderer->isInline() && !renderer->isReplacedOrInlineBlock()) {
84         static constexpr float inlinePadding = 3;
85         OptionSet<RenderObject::BoundingRectBehavior> behavior { RenderObject::BoundingRectBehavior::RespectClipping };
86         rectsInContentCoordinates = RenderObject::absoluteTextRects(*linkRange, behavior).map([&](auto rect) -> FloatRect {
87             rect.inflate(inlinePadding);
88             return rect;
89         });
90
91         if (rectsInContentCoordinates.isEmpty()) {
92             auto boundingRectForRange = absoluteBoundingRectForRange(*linkRange);
93             if (!boundingRectForRange.isEmpty()) {
94                 boundingRectForRange.inflate(inlinePadding);
95                 rectsInContentCoordinates = { boundingRectForRange };
96             }
97         }
98     }
99
100     if (rectsInContentCoordinates.isEmpty())
101         rectsInContentCoordinates = { renderer->absoluteBoundingBoxRect() };
102     
103     auto layoutArea = mainFrameView.layoutSize().area();
104     rectsInContentCoordinates = compactMap(rectsInContentCoordinates, [&] (auto rect) -> std::optional<FloatRect> {
105         if (rect.area() > layoutArea / 2)
106             return std::nullopt;
107         return rect;
108     });
109     
110     if (is<RenderBox>(*renderer)) {
111         RoundedRect::Radii borderRadii = downcast<RenderBox>(*renderer).borderRadii();
112         region.borderRadius = borderRadii.minimumRadius();
113     }
114     
115     for (auto rect : rectsInContentCoordinates) {
116         auto contentsRect = rect;
117
118         if (&frameView != &mainFrameView)
119             contentsRect.intersect(frameClipRect);
120
121         if (contentsRect.isEmpty())
122             continue;
123
124         region.regionInLayerCoordinates.unite(enclosingIntRect(contentsRect));
125     }
126
127     if (region.regionInLayerCoordinates.isEmpty())
128         return std::nullopt;
129
130     return region;
131 }
132
133 static CursorType cursorTypeForElement(Element& element)
134 {
135     auto* renderer = element.renderer();
136     auto* style = renderer ? &renderer->style() : nullptr;
137     auto cursorType = style ? style->cursor() : CursorType::Auto;
138
139     if (cursorType == CursorType::Auto && element.enclosingLinkEventParentOrSelf() && element.isLink())
140         cursorType = CursorType::Pointer;
141
142     return cursorType;
143 }
144
145 Vector<InteractionRegion> interactionRegions(Page& page, FloatRect rect)
146 {
147     Ref frame(page.mainFrame());
148     RefPtr frameView = frame->view();
149     
150     if (!frameView)
151         return { };
152
153     frameView->updateLayoutAndStyleIfNeededRecursive();
154     
155     RefPtr document = frame->document();
156     if (!document)
157         return { };
158
159     auto result = HitTestResult { LayoutRect(rect) };
160     HitTestRequest request({
161         HitTestRequest::Type::ReadOnly,
162         HitTestRequest::Type::Active,
163         HitTestRequest::Type::AllowVisibleChildFrameContentOnly,
164         HitTestRequest::Type::CollectMultipleElements
165     });
166     document->hitTest(request, result);
167
168     Vector<InteractionRegion> regions;
169
170     for (const auto& node : result.listBasedTestResult()) {
171         if (!is<Element>(node.get()))
172             continue;
173         auto& element = downcast<Element>(node.get());
174
175         if (!element.willRespondToMouseClickEvents() && !element.willRespondToTouchEvents())
176             continue;
177
178         if (cursorTypeForElement(element) != CursorType::Pointer && !is<HTMLFormControlElement>(element))
179             continue;
180
181         auto region = regionForElement(element);
182         if (region)
183             regions.append(*region);
184     }
185
186     return regions;
187 }
188
189 TextStream& operator<<(TextStream& ts, const InteractionRegion& interactionRegion)
190 {
191     ts.dumpProperty("region", interactionRegion.regionInLayerCoordinates);
192     ts.dumpProperty("hasLightBackground", interactionRegion.hasLightBackground);
193     ts.dumpProperty("borderRadius", interactionRegion.borderRadius);
194
195     return ts;
196 }
197
198 }