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 "RenderStyle.h"
30
31namespace WebCore {
32
33EventRegionContext::EventRegionContext(EventRegion& eventRegion)
34 : m_eventRegion(eventRegion)
35{
36}
37
38void EventRegionContext::pushTransform(const AffineTransform& transform)
39{
40 if (m_transformStack.isEmpty())
41 m_transformStack.append(transform);
42 else
43 m_transformStack.append(m_transformStack.last() * transform);
44}
45
46void EventRegionContext::popTransform()
47{
48 m_transformStack.removeLast();
49}
50
51void EventRegionContext::unite(const Region& region, const RenderStyle& style)
52{
53 if (m_transformStack.isEmpty())
54 m_eventRegion.unite(region, style);
55 else
56 m_eventRegion.unite(m_transformStack.last().mapRegion(region), style);
57}
58
59bool EventRegionContext::contains(const IntRect& rect) const
60{
61 if (m_transformStack.isEmpty())
62 return m_eventRegion.contains(rect);
63
64 return m_eventRegion.contains(m_transformStack.last().mapRect(rect));
65}
66
67EventRegion::EventRegion() = default;
68
69bool EventRegion::operator==(const EventRegion& other) const
70{
71#if ENABLE(POINTER_EVENTS)
72 if (m_touchActionRegions != other.m_touchActionRegions)
73 return false;
74#endif
75 return m_region == other.m_region;
76}
77
78void EventRegion::unite(const Region& region, const RenderStyle& style)
79{
80 m_region.unite(region);
81
82#if ENABLE(POINTER_EVENTS)
83 uniteTouchActions(region, style.effectiveTouchActions());
84#else
85 UNUSED_PARAM(style);
86#endif
87}
88
89void EventRegion::translate(const IntSize& offset)
90{
91 m_region.translate(offset);
92
93#if ENABLE(POINTER_EVENTS)
94 for (auto& touchActionRegion : m_touchActionRegions)
95 touchActionRegion.translate(offset);
96#endif
97}
98
99#if ENABLE(POINTER_EVENTS)
100
101constexpr unsigned toIndex(TouchAction touchAction)
102{
103 switch (touchAction) {
104 case TouchAction::None:
105 return 0;
106 case TouchAction::Manipulation:
107 return 1;
108 case TouchAction::PanX:
109 return 2;
110 case TouchAction::PanY:
111 return 3;
112 case TouchAction::PinchZoom:
113 return 4;
114 case TouchAction::Auto:
115 break;
116 }
117 ASSERT_NOT_REACHED();
118 return 0;
119}
120
121constexpr TouchAction toTouchAction(unsigned index)
122{
123 switch (index) {
124 case 0:
125 return TouchAction::None;
126 case 1:
127 return TouchAction::Manipulation;
128 case 2:
129 return TouchAction::PanX;
130 case 3:
131 return TouchAction::PanY;
132 case 4:
133 return TouchAction::PinchZoom;
134 default:
135 break;
136 }
137 ASSERT_NOT_REACHED();
138 return TouchAction::Auto;
139}
140
141void EventRegion::uniteTouchActions(const Region& touchRegion, OptionSet<TouchAction> touchActions)
142{
143 for (auto touchAction : touchActions) {
144 if (touchAction == TouchAction::Auto)
145 break;
146 auto index = toIndex(touchAction);
147 if (m_touchActionRegions.size() < index + 1)
148 m_touchActionRegions.grow(index + 1);
149 }
150
151 for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
152 auto regionTouchAction = toTouchAction(i);
153 if (touchActions.contains(regionTouchAction))
154 m_touchActionRegions[i].unite(touchRegion);
155 else
156 m_touchActionRegions[i].subtract(touchRegion);
157 }
158}
159
160OptionSet<TouchAction> EventRegion::touchActionsForPoint(const IntPoint& point) const
161{
162 OptionSet<TouchAction> actions;
163
164 for (unsigned i = 0; i < m_touchActionRegions.size(); ++i) {
165 if (m_touchActionRegions[i].contains(point)) {
166 auto action = toTouchAction(i);
167 actions.add(action);
168 if (action == TouchAction::None || action == TouchAction::Manipulation)
169 break;
170 }
171 }
172
173 if (actions.isEmpty())
174 return { TouchAction::Auto };
175
176 return actions;
177}
178
179TextStream& operator<<(TextStream& ts, TouchAction touchAction)
180{
181 switch (touchAction) {
182 case TouchAction::None:
183 return ts << "none";
184 case TouchAction::Manipulation:
185 return ts << "manipulation";
186 case TouchAction::PanX:
187 return ts << "pan-x";
188 case TouchAction::PanY:
189 return ts << "pan-y";
190 case TouchAction::PinchZoom:
191 return ts << "pinch-zoom";
192 case TouchAction::Auto:
193 return ts << "auto";
194 }
195 ASSERT_NOT_REACHED();
196 return ts;
197}
198
199#endif
200
201TextStream& operator<<(TextStream& ts, const EventRegion& eventRegion)
202{
203 ts << eventRegion.m_region;
204
205#if ENABLE(POINTER_EVENTS)
206 if (!eventRegion.m_touchActionRegions.isEmpty()) {
207 TextStream::IndentScope indentScope(ts);
208 ts << indent << "(touch-action\n";
209 for (unsigned i = 0; i < eventRegion.m_touchActionRegions.size(); ++i) {
210 if (eventRegion.m_touchActionRegions[i].isEmpty())
211 continue;
212 TextStream::IndentScope indentScope(ts);
213 ts << indent << "(" << toTouchAction(i);
214 ts << indent << eventRegion.m_touchActionRegions[i];
215 ts << indent << ")\n";
216 }
217 ts << indent << ")\n";
218 }
219#endif
220
221 return ts;
222}
223
224}
225