[iOS] Draw caps lock indicator in password fields
[WebKit-https.git] / Source / WebCore / platform / ios / ScrollAnimatorIOS.mm
1 /*
2  * Copyright (C) 2011, 2015 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 #import "config.h"
27 #import "ScrollAnimatorIOS.h"
28
29 #if PLATFORM(IOS_FAMILY)
30
31 #import "Frame.h"
32 #import "RenderLayer.h"
33 #import "ScrollableArea.h"
34
35 #if ENABLE(TOUCH_EVENTS)
36 #import "PlatformTouchEventIOS.h"
37 #endif
38
39 namespace WebCore {
40
41 std::unique_ptr<ScrollAnimator> ScrollAnimator::create(ScrollableArea& scrollableArea)
42 {
43     return std::make_unique<ScrollAnimatorIOS>(scrollableArea);
44 }
45
46 ScrollAnimatorIOS::ScrollAnimatorIOS(ScrollableArea& scrollableArea)
47     : ScrollAnimator(scrollableArea)
48 #if ENABLE(TOUCH_EVENTS)
49     , m_touchScrollAxisLatch(AxisLatchNotComputed)
50     , m_inTouchSequence(false)
51     , m_committedToScrollAxis(false)
52     , m_startedScroll(false)
53     , m_scrollableAreaForTouchSequence(0)
54 #endif
55 {
56 }
57
58 ScrollAnimatorIOS::~ScrollAnimatorIOS()
59 {
60 }
61
62 #if ENABLE(TOUCH_EVENTS)
63 bool ScrollAnimatorIOS::handleTouchEvent(const PlatformTouchEvent& touchEvent)
64 {
65     if (touchEvent.type() == PlatformEvent::TouchStart && touchEvent.touchCount() == 1) {
66         m_firstTouchPoint = touchEvent.touchLocationAtIndex(0);
67         m_lastTouchPoint = m_firstTouchPoint;
68         m_inTouchSequence = true;
69         m_committedToScrollAxis = false;
70         m_startedScroll = false;
71         m_touchScrollAxisLatch = AxisLatchNotComputed;
72         // Never claim to have handled the TouchStart, because that will kill default scrolling behavior.
73         return false;
74     }
75
76     if (!m_inTouchSequence)
77         return false;
78
79     if (touchEvent.type() == PlatformEvent::TouchEnd || touchEvent.type() == PlatformEvent::TouchCancel) {
80         m_inTouchSequence = false;
81         m_scrollableAreaForTouchSequence = 0;
82         if (m_startedScroll)
83             scrollableArea().didEndScroll();
84         return false;
85     }
86
87     // If a second touch appears, assume that the user is trying to zoom, and bail on the scrolling sequence.
88     // FIXME: if that second touch is inside the scrollable area, should we keep scrolling?
89     if (touchEvent.touchCount() != 1) {
90         m_inTouchSequence = false;
91         m_scrollableAreaForTouchSequence = 0;
92         if (m_startedScroll)
93             scrollableArea().didEndScroll();
94         return false;
95     }
96     
97     IntPoint currentPoint = touchEvent.touchLocationAtIndex(0);
98
99     IntSize touchDelta = m_lastTouchPoint - currentPoint;
100     m_lastTouchPoint = currentPoint;
101
102     if (!m_scrollableAreaForTouchSequence)
103         determineScrollableAreaForTouchSequence(touchDelta);
104
105     if (!m_committedToScrollAxis) {
106         bool horizontallyScrollable = m_scrollableArea.scrollSize(HorizontalScrollbar);
107         bool verticallyScrollable = m_scrollableArea.scrollSize(VerticalScrollbar);
108
109         if (!horizontallyScrollable && !verticallyScrollable)
110             return false;
111
112         IntSize deltaFromStart = m_firstTouchPoint - currentPoint;
113     
114         const int latchAxisMovementThreshold = 10;
115         if (!horizontallyScrollable && verticallyScrollable) {
116             m_touchScrollAxisLatch = AxisLatchVertical;
117             if (abs(deltaFromStart.height()) >= latchAxisMovementThreshold)
118                 m_committedToScrollAxis = true;
119         } else if (horizontallyScrollable && !verticallyScrollable) {
120             m_touchScrollAxisLatch = AxisLatchHorizontal;
121             if (abs(deltaFromStart.width()) >= latchAxisMovementThreshold)
122                 m_committedToScrollAxis = true;
123         } else {
124             m_committedToScrollAxis = true;
125
126             if (m_touchScrollAxisLatch == AxisLatchNotComputed) {
127                 const float lockAngleDegrees = 20;
128                 if (deltaFromStart.width() && deltaFromStart.height()) {
129                     float dragAngle = atanf(static_cast<float>(abs(deltaFromStart.height())) / abs(deltaFromStart.width()));
130                     if (dragAngle <= deg2rad(lockAngleDegrees))
131                         m_touchScrollAxisLatch = AxisLatchHorizontal;
132                     else if (dragAngle >= deg2rad(90 - lockAngleDegrees))
133                         m_touchScrollAxisLatch = AxisLatchVertical;
134                 }
135             }
136         }
137         
138         if (!m_committedToScrollAxis)
139             return false;
140     }
141
142     bool handled = false;
143     
144     // Horizontal
145     if (m_touchScrollAxisLatch != AxisLatchVertical) {
146         int delta = touchDelta.width();
147         handled |= m_scrollableAreaForTouchSequence->scroll(delta < 0 ? ScrollLeft : ScrollRight, ScrollByPixel, abs(delta));
148     }
149     
150     // Vertical
151     if (m_touchScrollAxisLatch != AxisLatchHorizontal) {
152         int delta = touchDelta.height();
153         handled |= m_scrollableAreaForTouchSequence->scroll(delta < 0 ? ScrollUp : ScrollDown, ScrollByPixel, abs(delta));
154     }
155     
156     // Return false until we manage to scroll at all, and then keep returning true until the gesture ends.
157     if (!m_startedScroll) {
158         if (!handled)
159             return false;
160         m_startedScroll = true;
161         scrollableArea().didStartScroll();
162     } else if (handled)
163         scrollableArea().didUpdateScroll();
164     
165     return true;
166 }
167
168 void ScrollAnimatorIOS::determineScrollableAreaForTouchSequence(const IntSize& scrollDelta)
169 {
170     ASSERT(!m_scrollableAreaForTouchSequence);
171
172     ScrollableArea* scrollableArea = &m_scrollableArea;
173     while (true) {
174         if (!scrollableArea->isPinnedInBothDirections(scrollDelta))
175             break;
176
177         ScrollableArea* enclosingArea = scrollableArea->enclosingScrollableArea();
178         if (!enclosingArea)
179             break;
180
181         scrollableArea = enclosingArea;
182     }
183
184     ASSERT(scrollableArea);
185     m_scrollableAreaForTouchSequence = scrollableArea;
186 }
187 #endif
188
189 } // namespace WebCore
190
191 #endif // PLATFORM(IOS_FAMILY)