2 * Copyright (C) 2019 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "EventRegion.h"
30 #include "RenderStyle.h"
31 #include <wtf/text/TextStream.h>
35 EventRegionContext::EventRegionContext(EventRegion& eventRegion)
36 : m_eventRegion(eventRegion)
40 void EventRegionContext::pushTransform(const AffineTransform& transform)
42 if (m_transformStack.isEmpty())
43 m_transformStack.append(transform);
45 m_transformStack.append(m_transformStack.last() * transform);
48 void EventRegionContext::popTransform()
50 if (m_transformStack.isEmpty()) {
54 m_transformStack.removeLast();
57 void EventRegionContext::pushClip(const IntRect& clipRect)
59 auto transformedClip = m_transformStack.isEmpty() ? clipRect : m_transformStack.last().mapRect(clipRect);
61 if (m_clipStack.isEmpty())
62 m_clipStack.append(transformedClip);
64 m_clipStack.append(intersection(m_clipStack.last(), transformedClip));
67 void EventRegionContext::popClip()
69 if (m_clipStack.isEmpty()) {
73 m_clipStack.removeLast();
76 void EventRegionContext::unite(const Region& region, const RenderStyle& style, bool overrideUserModifyIsEditable)
78 if (m_transformStack.isEmpty() && m_clipStack.isEmpty()) {
79 m_eventRegion.unite(region, style, overrideUserModifyIsEditable);
83 auto transformedAndClippedRegion = m_transformStack.isEmpty() ? region : m_transformStack.last().mapRegion(region);
85 if (!m_clipStack.isEmpty())
86 transformedAndClippedRegion.intersect(m_clipStack.last());
88 m_eventRegion.unite(transformedAndClippedRegion, style, overrideUserModifyIsEditable);
91 bool EventRegionContext::contains(const IntRect& rect) const
93 if (m_transformStack.isEmpty())
94 return m_eventRegion.contains(rect);
96 return m_eventRegion.contains(m_transformStack.last().mapRect(rect));
99 EventRegion::EventRegion() = default;
101 bool EventRegion::operator==(const EventRegion& other) const
103 #if ENABLE(TOUCH_ACTION_REGIONS)
104 if (m_touchActionRegions != other.m_touchActionRegions)
108 #if ENABLE(WHEEL_EVENT_REGIONS)
109 if (m_wheelEventListenerRegion != other.m_wheelEventListenerRegion)
111 if (m_nonPassiveWheelEventListenerRegion != other.m_nonPassiveWheelEventListenerRegion)
115 #if ENABLE(EDITABLE_REGION)
116 if (m_editableRegion != other.m_editableRegion)
120 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
121 if (m_interactionRegions != other.m_interactionRegions)
125 return m_region == other.m_region;
128 void EventRegion::unite(const Region& region, const RenderStyle& style, bool overrideUserModifyIsEditable)
130 m_region.unite(region);
132 #if ENABLE(TOUCH_ACTION_REGIONS)
133 uniteTouchActions(region, style.effectiveTouchActions());
136 #if ENABLE(WHEEL_EVENT_REGIONS)
137 uniteEventListeners(region, style.eventListenerRegionTypes());
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");
146 UNUSED_PARAM(overrideUserModifyIsEditable);
149 #if !ENABLE(TOUCH_ACTION_REGIONS) && !ENABLE(WHEEL_EVENT_REGIONS) && !ENABLE(EDITABLE_REGION)
154 void EventRegion::translate(const IntSize& offset)
156 m_region.translate(offset);
158 #if ENABLE(TOUCH_ACTION_REGIONS)
159 for (auto& touchActionRegion : m_touchActionRegions)
160 touchActionRegion.translate(offset);
163 #if ENABLE(WHEEL_EVENT_REGIONS)
164 m_wheelEventListenerRegion.translate(offset);
165 m_nonPassiveWheelEventListenerRegion.translate(offset);
168 #if ENABLE(EDITABLE_REGION)
169 if (m_editableRegion)
170 m_editableRegion->translate(offset);
173 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
174 for (auto& region : m_interactionRegions)
175 region.regionInLayerCoordinates.translate(offset);
179 static inline unsigned toIndex(TouchAction touchAction)
181 switch (touchAction) {
182 case TouchAction::None:
184 case TouchAction::Manipulation:
186 case TouchAction::PanX:
188 case TouchAction::PanY:
190 case TouchAction::PinchZoom:
192 case TouchAction::Auto:
195 ASSERT_NOT_REACHED();
199 static inline TouchAction toTouchAction(unsigned index)
203 return TouchAction::None;
205 return TouchAction::Manipulation;
207 return TouchAction::PanX;
209 return TouchAction::PanY;
211 return TouchAction::PinchZoom;
215 ASSERT_NOT_REACHED();
216 return TouchAction::Auto;
219 #if ENABLE(TOUCH_ACTION_REGIONS)
220 void EventRegion::uniteTouchActions(const Region& touchRegion, OptionSet<TouchAction> touchActions)
222 for (auto touchAction : touchActions) {
223 if (touchAction == TouchAction::Auto)
225 auto index = toIndex(touchAction);
226 if (m_touchActionRegions.size() < index + 1)
227 m_touchActionRegions.grow(index + 1);
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);
236 m_touchActionRegions[i].subtract(touchRegion);
237 LOG_WITH_STREAM(EventRegions, stream << " subtracting for TouchAction " << regionTouchAction);
242 const Region* EventRegion::regionForTouchAction(TouchAction action) const
244 unsigned actionIndex = toIndex(action);
245 if (actionIndex >= m_touchActionRegions.size())
248 return &m_touchActionRegions[actionIndex];
251 OptionSet<TouchAction> EventRegion::touchActionsForPoint(const IntPoint& point) const
253 OptionSet<TouchAction> actions;
255 for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
256 if (m_touchActionRegions[i].contains(point)) {
257 auto action = toTouchAction(i);
259 if (action == TouchAction::None || action == TouchAction::Manipulation)
264 if (actions.isEmpty())
265 return { TouchAction::Auto };
271 #if ENABLE(WHEEL_EVENT_REGIONS)
272 void EventRegion::uniteEventListeners(const Region& region, OptionSet<EventListenerRegionType> eventListenerRegionTypes)
274 if (eventListenerRegionTypes.contains(EventListenerRegionType::Wheel)) {
275 m_wheelEventListenerRegion.unite(region);
276 LOG_WITH_STREAM(EventRegions, stream << " uniting for passive wheel event listener");
278 if (eventListenerRegionTypes.contains(EventListenerRegionType::NonPassiveWheel)) {
279 m_nonPassiveWheelEventListenerRegion.unite(region);
280 LOG_WITH_STREAM(EventRegions, stream << " uniting for active wheel event listener");
284 OptionSet<EventListenerRegionType> EventRegion::eventListenerRegionTypesForPoint(const IntPoint& point) const
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);
295 const Region& EventRegion::eventListenerRegionForType(EventListenerRegionType type) const
298 case EventListenerRegionType::Wheel:
299 return m_wheelEventListenerRegion;
300 case EventListenerRegionType::NonPassiveWheel:
301 return m_nonPassiveWheelEventListenerRegion;
303 ASSERT_NOT_REACHED();
304 return m_wheelEventListenerRegion;
306 #endif // ENABLE(WHEEL_EVENT_REGIONS)
308 #if ENABLE(EDITABLE_REGION)
310 bool EventRegion::containsEditableElementsInRect(const IntRect& rect) const
312 return m_editableRegion && m_editableRegion->intersects(rect);
317 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
319 void EventRegion::uniteInteractionRegions(const Vector<InteractionRegion>& interactionRegions)
321 m_interactionRegions.appendVector(interactionRegions);
324 void EventRegion::computeInteractionRegions(Page& page, IntRect rect)
326 // FIXME: Collect regions from `EventRegion::unite` instead of hit-testing, so that they are per-layer.
327 uniteInteractionRegions(WebCore::interactionRegions(page, rect));
332 void EventRegion::dump(TextStream& ts) const
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())
343 TextStream::IndentScope indentScope(ts);
344 ts << indent << "(" << toTouchAction(i);
345 ts << indent << m_touchActionRegions[i];
346 ts << indent << ")\n";
348 ts << indent << ")\n";
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";
360 ts << indent << ")\n";
364 #if ENABLE(EDITABLE_REGION)
365 if (m_editableRegion && !m_editableRegion->isEmpty()) {
366 ts << indent << "(editable region" << *m_editableRegion;
367 ts << indent << ")\n";
371 #if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
372 if (!m_interactionRegions.isEmpty()) {
373 ts.dumpProperty("interaction regions", m_interactionRegions);
379 TextStream& operator<<(TextStream& ts, TouchAction touchAction)
381 switch (touchAction) {
382 case TouchAction::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:
395 ASSERT_NOT_REACHED();
399 TextStream& operator<<(TextStream& ts, const EventRegion& eventRegion)
401 eventRegion.dump(ts);