4e49854ba175a60f5523788801854529a825781c
[WebKit-https.git] / Source / WebCore / page / AutoscrollController.cpp
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3  * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4  * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "AutoscrollController.h"
30
31 #include "Chrome.h"
32 #include "ChromeClient.h"
33 #include "EventHandler.h"
34 #include "Frame.h"
35 #include "FrameView.h"
36 #include "HitTestResult.h"
37 #include "Page.h"
38 #include "RenderBox.h"
39 #include "RenderView.h"
40 #include "ScrollView.h"
41 #include <wtf/CurrentTime.h>
42
43 namespace WebCore {
44
45 // Delay time in second for start autoscroll if pointer is in border edge of scrollable element.
46 static double autoscrollDelay = 0.2;
47
48 // When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth
49 static const double autoscrollInterval = 0.05;
50
51 #if ENABLE(PAN_SCROLLING)
52 static Frame* getMainFrame(Frame* frame)
53 {
54     Page* page = frame->page();
55     return page ? page->mainFrame() : 0;
56 }
57 #endif
58
59 AutoscrollController::AutoscrollController()
60     : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired)
61     , m_autoscrollRenderer(0)
62     , m_autoscrollType(NoAutoscroll)
63     , m_dragAndDropAutoscrollStartTime(0)
64 {
65 }
66
67 RenderBox* AutoscrollController::autoscrollRenderer() const
68 {
69     return m_autoscrollRenderer;
70 }
71
72 bool AutoscrollController::autoscrollInProgress() const
73 {
74     return m_autoscrollType == AutoscrollForSelection;
75 }
76
77 void AutoscrollController::startAutoscrollForSelection(RenderObject* renderer)
78 {
79     // We don't want to trigger the autoscroll or the panScroll if it's already active
80     if (m_autoscrollTimer.isActive())
81         return;
82     RenderBox* scrollable = RenderBox::findAutoscrollable(renderer);
83     if (!scrollable)
84         return;
85     m_autoscrollType = AutoscrollForSelection;
86     m_autoscrollRenderer = scrollable;
87     startAutoscrollTimer();
88 }
89
90 void AutoscrollController::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
91 {
92     RenderBox* scrollable = m_autoscrollRenderer;
93     m_autoscrollTimer.stop();
94     m_autoscrollRenderer = 0;
95
96     if (!scrollable)
97         return;
98
99     Frame& frame = scrollable->frame();
100     if (autoscrollInProgress() && frame.eventHandler().mouseDownWasInSubframe()) {
101         if (Frame* subframe = frame.eventHandler().subframeForTargetNode(frame.eventHandler().mousePressNode()))
102             subframe->eventHandler().stopAutoscrollTimer(rendererIsBeingDestroyed);
103         return;
104     }
105
106     if (!rendererIsBeingDestroyed)
107         scrollable->stopAutoscroll();
108 #if ENABLE(PAN_SCROLLING)
109     if (panScrollInProgress()) {
110         FrameView& frameView = scrollable->view().frameView();
111         frameView.removePanScrollIcon();
112         frameView.setCursor(pointerCursor());
113     }
114 #endif
115
116     m_autoscrollType = NoAutoscroll;
117
118 #if ENABLE(PAN_SCROLLING)
119     // If we're not in the top frame we notify it that we are not doing a panScroll any more.
120     if (Frame* mainFrame = getMainFrame(&frame)) {
121         if (&frame != mainFrame)
122             mainFrame->eventHandler().didPanScrollStop();
123     }
124 #endif
125 }
126
127 void AutoscrollController::updateAutoscrollRenderer()
128 {
129     if (!m_autoscrollRenderer)
130         return;
131
132     RenderObject* renderer = m_autoscrollRenderer;
133
134 #if ENABLE(PAN_SCROLLING)
135     HitTestResult hitTest = m_autoscrollRenderer->frame().eventHandler().hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active);
136
137     if (Node* nodeAtPoint = hitTest.innerNode())
138         renderer = nodeAtPoint->renderer();
139 #endif
140
141     while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll()))
142         renderer = renderer->parent();
143     m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
144 }
145
146 void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime)
147 {
148     if (!dropTargetNode) {
149         stopAutoscrollTimer();
150         return;
151     }
152
153     RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer());
154     if (!scrollable) {
155         stopAutoscrollTimer();
156         return;
157     }
158
159     Frame& frame = scrollable->frame();
160
161     Page* page = frame.page();
162     if (!page || !page->chrome().client().shouldAutoscrollForDragAndDrop(scrollable)) {
163         stopAutoscrollTimer();
164         return;
165     }
166
167     IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition);
168     if (offset.isZero()) {
169         stopAutoscrollTimer();
170         return;
171     }
172
173     m_dragAndDropAutoscrollReferencePosition = eventPosition + offset;
174
175     if (m_autoscrollType == NoAutoscroll) {
176         m_autoscrollType = AutoscrollForDragAndDrop;
177         m_autoscrollRenderer = scrollable;
178         m_dragAndDropAutoscrollStartTime = eventTime;
179         startAutoscrollTimer();
180     } else if (m_autoscrollRenderer != scrollable) {
181         m_dragAndDropAutoscrollStartTime = eventTime;
182         m_autoscrollRenderer = scrollable;
183     }
184 }
185
186 #if ENABLE(PAN_SCROLLING)
187 void AutoscrollController::didPanScrollStart()
188 {
189     m_autoscrollType = AutoscrollForPan;
190 }
191
192 void AutoscrollController::didPanScrollStop()
193 {
194     m_autoscrollType = NoAutoscroll;
195 }
196
197 void AutoscrollController::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
198 {
199     switch (m_autoscrollType) {
200     case AutoscrollForPan:
201         if (mouseEvent.button() == MiddleButton)
202             m_autoscrollType = AutoscrollForPanCanStop;
203         break;
204     case AutoscrollForPanCanStop:
205         stopAutoscrollTimer();
206         break;
207     }
208 }
209
210 bool AutoscrollController::panScrollInProgress() const
211 {
212     return m_autoscrollType == AutoscrollForPan || m_autoscrollType == AutoscrollForPanCanStop;
213 }
214
215 void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition)
216 {
217     // We don't want to trigger the autoscroll or the panScroll if it's already active
218     if (m_autoscrollTimer.isActive())
219         return;
220
221     m_autoscrollType = AutoscrollForPan;
222     m_autoscrollRenderer = scrollable;
223     m_panScrollStartPos = lastKnownMousePosition;
224
225     if (FrameView* view = scrollable->frame().view())
226         view->addPanScrollIcon(lastKnownMousePosition);
227     scrollable->frame().eventHandler().didPanScrollStart();
228     startAutoscrollTimer();
229 }
230 #else
231 bool AutoscrollController::panScrollInProgress() const
232 {
233     return false;
234 }
235 #endif
236
237 void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>*)
238 {
239     if (!m_autoscrollRenderer) {
240         stopAutoscrollTimer();
241         return;
242     }
243
244     Frame& frame = m_autoscrollRenderer->frame();
245     switch (m_autoscrollType) {
246     case AutoscrollForDragAndDrop:
247         if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay)
248             m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition);
249         break;
250     case AutoscrollForSelection: {
251         if (!frame.eventHandler().mousePressed()) {
252             stopAutoscrollTimer();
253             return;
254         }
255 #if ENABLE(DRAG_SUPPORT)
256         frame.eventHandler().updateSelectionForMouseDrag();
257 #endif
258         m_autoscrollRenderer->autoscroll(frame.eventHandler().lastKnownMousePosition());
259         break;
260     }
261     case NoAutoscroll:
262         break;
263 #if ENABLE(PAN_SCROLLING)
264     case AutoscrollForPanCanStop:
265     case AutoscrollForPan:
266         // we verify that the main frame hasn't received the order to stop the panScroll
267         if (Frame* mainFrame = getMainFrame(&frame)) {
268             if (!mainFrame->eventHandler().panScrollInProgress()) {
269                 stopAutoscrollTimer();
270                 return;
271             }
272         }
273         if (FrameView* view = frame.view())
274             updatePanScrollState(view, frame.eventHandler().lastKnownMousePosition());
275         m_autoscrollRenderer->panScroll(m_panScrollStartPos);
276         break;
277 #endif
278     }
279 }
280
281 void AutoscrollController::startAutoscrollTimer()
282 {
283     m_autoscrollTimer.startRepeating(autoscrollInterval);
284 }
285
286 #if ENABLE(PAN_SCROLLING)
287 void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition)
288 {
289     // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll
290     // So we don't want to change the cursor over this area
291     bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius);
292     bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius);
293     bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius);
294     bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius);
295
296     if (m_autoscrollType == AutoscrollForPan && (east || west || north || south))
297         m_autoscrollType = AutoscrollForPanCanStop;
298
299     if (north) {
300         if (east)
301             view->setCursor(northEastPanningCursor());
302         else if (west)
303             view->setCursor(northWestPanningCursor());
304         else
305             view->setCursor(northPanningCursor());
306     } else if (south) {
307         if (east)
308             view->setCursor(southEastPanningCursor());
309         else if (west)
310             view->setCursor(southWestPanningCursor());
311         else
312             view->setCursor(southPanningCursor());
313     } else if (east)
314         view->setCursor(eastPanningCursor());
315     else if (west)
316         view->setCursor(westPanningCursor());
317     else
318         view->setCursor(middlePanningCursor());
319 }
320 #endif
321
322 } // namespace WebCore