AX: AXIsolatedTree::updateChildren sometimes fails to update isolated subtrees when...
[WebKit-https.git] / Source / WebCore / rendering / EventRegion.cpp
1 /*
2  * Copyright (C) 2019 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 "EventRegion.h"
28
29 #include "Logging.h"
30 #include "RenderStyle.h"
31 #include <wtf/text/TextStream.h>
32
33 namespace WebCore {
34
35 EventRegionContext::EventRegionContext(EventRegion& eventRegion)
36     : m_eventRegion(eventRegion)
37 {
38 }
39
40 void EventRegionContext::pushTransform(const AffineTransform& transform)
41 {
42     if (m_transformStack.isEmpty())
43         m_transformStack.append(transform);
44     else
45         m_transformStack.append(m_transformStack.last() * transform);
46 }
47
48 void EventRegionContext::popTransform()
49 {
50     if (m_transformStack.isEmpty()) {
51         ASSERT_NOT_REACHED();
52         return;
53     }
54     m_transformStack.removeLast();
55 }
56
57 void EventRegionContext::pushClip(const IntRect& clipRect)
58 {
59     auto transformedClip = m_transformStack.isEmpty() ? clipRect : m_transformStack.last().mapRect(clipRect);
60
61     if (m_clipStack.isEmpty())
62         m_clipStack.append(transformedClip);
63     else
64         m_clipStack.append(intersection(m_clipStack.last(), transformedClip));
65 }
66
67 void EventRegionContext::popClip()
68 {
69     if (m_clipStack.isEmpty()) {
70         ASSERT_NOT_REACHED();
71         return;
72     }
73     m_clipStack.removeLast();
74 }
75
76 void EventRegionContext::unite(const Region& region, const RenderStyle& style, bool overrideUserModifyIsEditable)
77 {
78     if (m_transformStack.isEmpty() && m_clipStack.isEmpty()) {
79         m_eventRegion.unite(region, style, overrideUserModifyIsEditable);
80         return;
81     }
82
83     auto transformedAndClippedRegion = m_transformStack.isEmpty() ? region : m_transformStack.last().mapRegion(region);
84
85     if (!m_clipStack.isEmpty())
86         transformedAndClippedRegion.intersect(m_clipStack.last());
87
88     m_eventRegion.unite(transformedAndClippedRegion, style, overrideUserModifyIsEditable);
89 }
90
91 bool EventRegionContext::contains(const IntRect& rect) const
92 {
93     if (m_transformStack.isEmpty())
94         return m_eventRegion.contains(rect);
95
96     return m_eventRegion.contains(m_transformStack.last().mapRect(rect));
97 }
98
99 EventRegion::EventRegion() = default;
100
101 bool EventRegion::operator==(const EventRegion& other) const
102 {
103 #if ENABLE(TOUCH_ACTION_REGIONS)
104     if (m_touchActionRegions != other.m_touchActionRegions)
105         return false;
106 #endif
107
108 #if ENABLE(WHEEL_EVENT_REGIONS)
109     if (m_wheelEventListenerRegion != other.m_wheelEventListenerRegion)
110         return false;
111     if (m_nonPassiveWheelEventListenerRegion != other.m_nonPassiveWheelEventListenerRegion)
112         return false;
113 #endif
114
115 #if ENABLE(EDITABLE_REGION)
116     if (m_editableRegion != other.m_editableRegion)
117         return false;
118 #endif
119
120 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
121     if (m_interactionRegions != other.m_interactionRegions)
122         return false;
123 #endif
124
125     return m_region == other.m_region;
126 }
127
128 void EventRegion::unite(const Region& region, const RenderStyle& style, bool overrideUserModifyIsEditable)
129 {
130     m_region.unite(region);
131
132 #if ENABLE(TOUCH_ACTION_REGIONS)
133     uniteTouchActions(region, style.effectiveTouchActions());
134 #endif
135
136 #if ENABLE(WHEEL_EVENT_REGIONS)
137     uniteEventListeners(region, style.eventListenerRegionTypes());
138 #endif
139
140 #if ENABLE(EDITABLE_REGION)
141     if (m_editableRegion && (overrideUserModifyIsEditable || style.effectiveUserModify() != UserModify::ReadOnly)) {
142         m_editableRegion->unite(region);
143         LOG_WITH_STREAM(EventRegions, stream << " uniting editable region");
144     }
145 #else
146     UNUSED_PARAM(overrideUserModifyIsEditable);
147 #endif
148
149 #if !ENABLE(TOUCH_ACTION_REGIONS) && !ENABLE(WHEEL_EVENT_REGIONS) && !ENABLE(EDITABLE_REGION)
150     UNUSED_PARAM(style);
151 #endif
152 }
153
154 void EventRegion::translate(const IntSize& offset)
155 {
156     m_region.translate(offset);
157
158 #if ENABLE(TOUCH_ACTION_REGIONS)
159     for (auto& touchActionRegion : m_touchActionRegions)
160         touchActionRegion.translate(offset);
161 #endif
162
163 #if ENABLE(WHEEL_EVENT_REGIONS)
164     m_wheelEventListenerRegion.translate(offset);
165     m_nonPassiveWheelEventListenerRegion.translate(offset);
166 #endif
167
168 #if ENABLE(EDITABLE_REGION)
169     if (m_editableRegion)
170         m_editableRegion->translate(offset);
171 #endif
172
173 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
174     for (auto& region : m_interactionRegions)
175         region.regionInLayerCoordinates.translate(offset);
176 #endif
177 }
178
179 static inline unsigned toIndex(TouchAction touchAction)
180 {
181     switch (touchAction) {
182     case TouchAction::None:
183         return 0;
184     case TouchAction::Manipulation:
185         return 1;
186     case TouchAction::PanX:
187         return 2;
188     case TouchAction::PanY:
189         return 3;
190     case TouchAction::PinchZoom:
191         return 4;
192     case TouchAction::Auto:
193         break;
194     }
195     ASSERT_NOT_REACHED();
196     return 0;
197 }
198
199 static inline TouchAction toTouchAction(unsigned index)
200 {
201     switch (index) {
202     case 0:
203         return TouchAction::None;
204     case 1:
205         return TouchAction::Manipulation;
206     case 2:
207         return TouchAction::PanX;
208     case 3:
209         return TouchAction::PanY;
210     case 4:
211         return TouchAction::PinchZoom;
212     default:
213         break;
214     }
215     ASSERT_NOT_REACHED();
216     return TouchAction::Auto;
217 }
218
219 #if ENABLE(TOUCH_ACTION_REGIONS)
220 void EventRegion::uniteTouchActions(const Region& touchRegion, OptionSet<TouchAction> touchActions)
221 {
222     for (auto touchAction : touchActions) {
223         if (touchAction == TouchAction::Auto)
224             break;
225         auto index = toIndex(touchAction);
226         if (m_touchActionRegions.size() < index + 1)
227             m_touchActionRegions.grow(index + 1);
228     }
229
230     for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
231         auto regionTouchAction = toTouchAction(i);
232         if (touchActions.contains(regionTouchAction)) {
233             m_touchActionRegions[i].unite(touchRegion);
234             LOG_WITH_STREAM(EventRegions, stream << " uniting for TouchAction " << regionTouchAction);
235         } else {
236             m_touchActionRegions[i].subtract(touchRegion);
237             LOG_WITH_STREAM(EventRegions, stream << " subtracting for TouchAction " << regionTouchAction);
238         }
239     }
240 }
241
242 const Region* EventRegion::regionForTouchAction(TouchAction action) const
243 {
244     unsigned actionIndex = toIndex(action);
245     if (actionIndex >= m_touchActionRegions.size())
246         return nullptr;
247
248     return &m_touchActionRegions[actionIndex];
249 }
250
251 OptionSet<TouchAction> EventRegion::touchActionsForPoint(const IntPoint& point) const
252 {
253     OptionSet<TouchAction> actions;
254
255     for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
256         if (m_touchActionRegions[i].contains(point)) {
257             auto action = toTouchAction(i);
258             actions.add(action);
259             if (action == TouchAction::None || action == TouchAction::Manipulation)
260                 break;
261         }
262     }
263
264     if (actions.isEmpty())
265         return { TouchAction::Auto };
266
267     return actions;
268 }
269 #endif
270
271 #if ENABLE(WHEEL_EVENT_REGIONS)
272 void EventRegion::uniteEventListeners(const Region& region, OptionSet<EventListenerRegionType> eventListenerRegionTypes)
273 {
274     if (eventListenerRegionTypes.contains(EventListenerRegionType::Wheel)) {
275         m_wheelEventListenerRegion.unite(region);
276         LOG_WITH_STREAM(EventRegions, stream << " uniting for passive wheel event listener");
277     }
278     if (eventListenerRegionTypes.contains(EventListenerRegionType::NonPassiveWheel)) {
279         m_nonPassiveWheelEventListenerRegion.unite(region);
280         LOG_WITH_STREAM(EventRegions, stream << " uniting for active wheel event listener");
281     }
282 }
283
284 OptionSet<EventListenerRegionType> EventRegion::eventListenerRegionTypesForPoint(const IntPoint& point) const
285 {
286     OptionSet<EventListenerRegionType> regionTypes;
287     if (m_wheelEventListenerRegion.contains(point))
288         regionTypes.add(EventListenerRegionType::Wheel);
289     if (m_nonPassiveWheelEventListenerRegion.contains(point))
290         regionTypes.add(EventListenerRegionType::NonPassiveWheel);
291
292     return regionTypes;
293 }
294
295 const Region& EventRegion::eventListenerRegionForType(EventListenerRegionType type) const
296 {
297     switch (type) {
298     case EventListenerRegionType::Wheel:
299         return m_wheelEventListenerRegion;
300     case EventListenerRegionType::NonPassiveWheel:
301         return m_nonPassiveWheelEventListenerRegion;
302     }
303     ASSERT_NOT_REACHED();
304     return m_wheelEventListenerRegion;
305 }
306 #endif // ENABLE(WHEEL_EVENT_REGIONS)
307
308 #if ENABLE(EDITABLE_REGION)
309
310 bool EventRegion::containsEditableElementsInRect(const IntRect& rect) const
311 {
312     return m_editableRegion && m_editableRegion->intersects(rect);
313 }
314
315 #endif
316
317 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
318
319 void EventRegion::uniteInteractionRegions(const Vector<InteractionRegion>& interactionRegions)
320 {
321     m_interactionRegions.appendVector(interactionRegions);
322 }
323
324 void EventRegion::computeInteractionRegions(Page& page, IntRect rect)
325 {
326     // FIXME: Collect regions from `EventRegion::unite` instead of hit-testing, so that they are per-layer.
327     uniteInteractionRegions(WebCore::interactionRegions(page, rect));
328 }
329
330 #endif
331
332 void EventRegion::dump(TextStream& ts) const
333 {
334     ts << m_region;
335
336 #if ENABLE(TOUCH_ACTION_REGIONS)
337     if (!m_touchActionRegions.isEmpty()) {
338         TextStream::IndentScope indentScope(ts);
339         ts << indent << "(touch-action\n";
340         for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
341             if (m_touchActionRegions[i].isEmpty())
342                 continue;
343             TextStream::IndentScope indentScope(ts);
344             ts << indent << "(" << toTouchAction(i);
345             ts << indent << m_touchActionRegions[i];
346             ts << indent << ")\n";
347         }
348         ts << indent << ")\n";
349     }
350 #endif
351
352 #if ENABLE(WHEEL_EVENT_REGIONS)
353     if (!m_wheelEventListenerRegion.isEmpty()) {
354         ts << indent << "(wheel event listener region" << m_wheelEventListenerRegion;
355         if (!m_nonPassiveWheelEventListenerRegion.isEmpty()) {
356             TextStream::IndentScope indentScope(ts);
357             ts << indent << "(non-passive" << m_nonPassiveWheelEventListenerRegion;
358             ts << indent << ")\n";
359         }
360         ts << indent << ")\n";
361     }
362 #endif
363
364 #if ENABLE(EDITABLE_REGION)
365     if (m_editableRegion && !m_editableRegion->isEmpty()) {
366         ts << indent << "(editable region" << *m_editableRegion;
367         ts << indent << ")\n";
368     }
369 #endif
370     
371 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
372     if (!m_interactionRegions.isEmpty()) {
373         ts.dumpProperty("interaction regions", m_interactionRegions);
374         ts << "\n";
375     }
376 #endif
377 }
378
379 TextStream& operator<<(TextStream& ts, TouchAction touchAction)
380 {
381     switch (touchAction) {
382     case TouchAction::None:
383         return ts << "none";
384     case TouchAction::Manipulation:
385         return ts << "manipulation";
386     case TouchAction::PanX:
387         return ts << "pan-x";
388     case TouchAction::PanY:
389         return ts << "pan-y";
390     case TouchAction::PinchZoom:
391         return ts << "pinch-zoom";
392     case TouchAction::Auto:
393         return ts << "auto";
394     }
395     ASSERT_NOT_REACHED();
396     return ts;
397 }
398
399 TextStream& operator<<(TextStream& ts, const EventRegion& eventRegion)
400 {
401     eventRegion.dump(ts);
402     return ts;
403 }
404
405 }