438e99d311b7aba51694fd4f8eed86ce5d0025be
[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 "EventHandler.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "HitTestResult.h"
35 #include "Page.h"
36 #include "RenderBox.h"
37 #include "ScrollView.h"
38
39 namespace WebCore {
40
41 // When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth
42 static const double autoscrollInterval = 0.05;
43
44 #if ENABLE(PAN_SCROLLING)
45 static Frame* getMainFrame(Frame* frame)
46 {
47     Page* page = frame->page();
48     return page ? page->mainFrame() : 0;
49 }
50 #endif
51
52 AutoscrollController::AutoscrollController()
53     : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired)
54     , m_autoscrollRenderer(0)
55     , m_autoscrollType(NoAutoscroll)
56 #if ENABLE(PAN_SCROLLING)
57     , m_panScrollButtonPressed(false)
58     , m_springLoadedPanScrollInProgress(false)
59 #endif
60 {
61 }
62
63 RenderBox* AutoscrollController::autoscrollRenderer() const
64 {
65     return m_autoscrollRenderer;
66 }
67
68 bool AutoscrollController::autoscrollInProgress() const
69 {
70     return m_autoscrollType == AutoscrollForSelection;
71 }
72
73 void AutoscrollController::startAutoscrollForSelection(RenderObject* renderer)
74 {
75     // We don't want to trigger the autoscroll or the panScroll if it's already active
76     if (m_autoscrollTimer.isActive())
77         return;
78     RenderBox* scrollable = RenderBox::findAutoscrollable(renderer);
79     if (!scrollable)
80         return;
81     m_autoscrollType = AutoscrollForSelection;
82     m_autoscrollRenderer = scrollable;
83     startAutoscrollTimer();
84 }
85
86 void AutoscrollController::stopAutoscrollTimer(bool rendererIsBeingDestroyed)
87 {
88     RenderBox* scrollable = m_autoscrollRenderer;
89     m_autoscrollTimer.stop();
90     m_autoscrollType = NoAutoscroll;
91     m_autoscrollRenderer = 0;
92 #if ENABLE(PAN_SCROLLING)
93     m_panScrollButtonPressed = false;
94     m_springLoadedPanScrollInProgress = false;
95 #endif
96
97     if (!scrollable)
98         return;
99
100     Frame* frame = scrollable->frame();
101     EventHandler* eventHandler = frame->eventHandler();
102     if (autoscrollInProgress() && eventHandler->mouseDownWasInSubframe()) {
103         if (Frame* subframe = eventHandler->subframeForTargetNode(eventHandler->mousePressNode()))
104             subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed);
105         return;
106     }
107
108     if (!rendererIsBeingDestroyed)
109         scrollable->stopAutoscroll();
110 #if ENABLE(PAN_SCROLLING)
111     if (panScrollInProgress()) {
112         if (FrameView* view = frame->view()) {
113             view->removePanScrollIcon();
114             view->setCursor(pointerCursor());
115         }
116     }
117 #endif
118
119 #if ENABLE(PAN_SCROLLING)
120     // If we're not in the top frame we notify it that we are not doing a panScroll any more.
121     if (Frame* mainFrame = getMainFrame(frame)) {
122         if (frame != mainFrame)
123             mainFrame->eventHandler()->didPanScrollStop();
124     }
125 #endif
126 }
127
128 void AutoscrollController::updateAutoscrollRenderer()
129 {
130     if (!m_autoscrollRenderer)
131         return;
132
133     RenderObject* renderer = m_autoscrollRenderer;
134
135 #if ENABLE(PAN_SCROLLING)
136     HitTestResult hitTest = m_autoscrollRenderer->frame()->eventHandler()->hitTestResultAtPoint(m_panScrollStartPos, true);
137
138     if (Node* nodeAtPoint = hitTest.innerNode())
139         renderer = nodeAtPoint->renderer();
140 #endif
141
142     while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll()))
143         renderer = renderer->parent();
144     m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
145 }
146
147 #if ENABLE(PAN_SCROLLING)
148 void AutoscrollController::didPanScrollStart()
149 {
150     m_autoscrollType = AutoscrollForPan;
151 }
152
153 void AutoscrollController::didPanScrollStop()
154 {
155     m_autoscrollType = NoAutoscroll;
156 }
157
158 void AutoscrollController::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent)
159 {
160     if (mouseEvent.button() == MiddleButton)
161         m_panScrollButtonPressed = false;
162     if (m_springLoadedPanScrollInProgress)
163         stopAutoscrollTimer();
164 }
165
166 bool AutoscrollController::panScrollInProgress() const
167 {
168     return m_autoscrollType == AutoscrollForPan;
169 }
170
171 void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition)
172 {
173     // We don't want to trigger the autoscroll or the panScroll if it's already active
174     if (m_autoscrollTimer.isActive())
175         return;
176
177     m_autoscrollType = AutoscrollForPan;
178     m_autoscrollRenderer = scrollable;
179     m_panScrollStartPos = lastKnownMousePosition;
180     m_springLoadedPanScrollInProgress = false;
181
182     if (FrameView* view = scrollable->frame()->view())
183         view->addPanScrollIcon(lastKnownMousePosition);
184     scrollable->frame()->eventHandler()->didPanScrollStart();
185     startAutoscrollTimer();
186 }
187 #else
188 bool AutoscrollController::panScrollInProgress() const
189 {
190     return false;
191 }
192 #endif
193
194 void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>*)
195 {
196     if (!m_autoscrollRenderer) {
197         stopAutoscrollTimer();
198         return;
199     }
200
201     Frame* frame = m_autoscrollRenderer->frame();
202     switch (m_autoscrollType) {
203     case AutoscrollForSelection:
204         if (!frame->eventHandler()->mousePressed()) {
205             stopAutoscrollTimer();
206             return;
207         }
208         m_autoscrollRenderer->autoscroll();
209         break;
210     case NoAutoscroll:
211         break;
212 #if ENABLE(PAN_SCROLLING)
213     case AutoscrollForPan:
214         // we verify that the main frame hasn't received the order to stop the panScroll
215         if (Frame* mainFrame = getMainFrame(frame)) {
216             if (!mainFrame->eventHandler()->panScrollInProgress()) {
217                 stopAutoscrollTimer();
218                 return;
219             }
220         }
221         if (FrameView* view = frame->view())
222             updatePanScrollState(view, frame->eventHandler()->lastKnownMousePosition());
223         m_autoscrollRenderer->panScroll(m_panScrollStartPos);
224         break;
225 #endif
226     }
227 }
228
229 void AutoscrollController::startAutoscrollTimer()
230 {
231     m_autoscrollTimer.startRepeating(autoscrollInterval);
232 }
233
234 #if ENABLE(PAN_SCROLLING)
235 void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition)
236 {
237     // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll
238     // So we don't want to change the cursor over this area
239     bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius);
240     bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius);
241     bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius);
242     bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius);
243
244     if ((east || west || north || south) && m_panScrollButtonPressed)
245         m_springLoadedPanScrollInProgress = true;
246
247     if (north) {
248         if (east)
249             view->setCursor(northEastPanningCursor());
250         else if (west)
251             view->setCursor(northWestPanningCursor());
252         else
253             view->setCursor(northPanningCursor());
254     } else if (south) {
255         if (east)
256             view->setCursor(southEastPanningCursor());
257         else if (west)
258             view->setCursor(southWestPanningCursor());
259         else
260             view->setCursor(southPanningCursor());
261     } else if (east)
262         view->setCursor(eastPanningCursor());
263     else if (west)
264         view->setCursor(westPanningCursor());
265     else
266         view->setCursor(middlePanningCursor());
267 }
268 #endif
269
270 } // namespace WebCore