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 "ScrollView.h" 40#include <wtf/CurrentTime.h> 41 42namespace WebCore { 43 44// Delay time in second for start autoscroll if pointer is in border edge of scrollable element. 45static double autoscrollDelay = 0.2; 46 47// When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth 48static const double autoscrollInterval = 0.05; 49 50#if ENABLE(PAN_SCROLLING) 51static Frame* getMainFrame(Frame* frame) 52{ 53 Page* page = frame->page(); 54 return page ? page->mainFrame() : 0; 55} 56#endif 57 58AutoscrollController::AutoscrollController() 59 : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired) 60 , m_autoscrollRenderer(0) 61 , m_autoscrollType(NoAutoscroll) 62 , m_dragAndDropAutoscrollStartTime(0) 63{ 64} 65 66RenderBox* AutoscrollController::autoscrollRenderer() const 67{ 68 return m_autoscrollRenderer; 69} 70 71bool AutoscrollController::autoscrollInProgress() const 72{ 73 return m_autoscrollType == AutoscrollForSelection; 74} 75 76void AutoscrollController::startAutoscrollForSelection(RenderObject* renderer) 77{ 78 // We don't want to trigger the autoscroll or the panScroll if it's already active 79 if (m_autoscrollTimer.isActive()) 80 return; 81 RenderBox* scrollable = RenderBox::findAutoscrollable(renderer); 82 if (!scrollable) 83 return; 84 m_autoscrollType = AutoscrollForSelection; 85 m_autoscrollRenderer = scrollable; 86 startAutoscrollTimer(); 87} 88 89void AutoscrollController::stopAutoscrollTimer(bool rendererIsBeingDestroyed) 90{ 91 RenderBox* scrollable = m_autoscrollRenderer; 92 m_autoscrollTimer.stop(); 93 m_autoscrollRenderer = 0; 94 95 if (!scrollable) 96 return; 97 98 Frame* frame = scrollable->frame(); 99 EventHandler* eventHandler = frame->eventHandler(); 100 if (autoscrollInProgress() && eventHandler->mouseDownWasInSubframe()) { 101 if (Frame* subframe = eventHandler->subframeForTargetNode(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 if (FrameView* view = frame->view()) { 111 view->removePanScrollIcon(); 112 view->setCursor(pointerCursor()); 113 } 114 } 115#endif 116 117 m_autoscrollType = NoAutoscroll; 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 128void 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, HitTestRequest::ReadOnly | HitTestRequest::Active); 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 147void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) 148{ 149 if (!dropTargetNode) { 150 stopAutoscrollTimer(); 151 return; 152 } 153 154 RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer()); 155 if (!scrollable) { 156 stopAutoscrollTimer(); 157 return; 158 } 159 160 Frame* frame = scrollable->frame(); 161 if (!frame) { 162 stopAutoscrollTimer(); 163 return; 164 } 165 166 Page* page = frame->page(); 167 if (!page || !page->chrome().client()->shouldAutoscrollForDragAndDrop(scrollable)) { 168 stopAutoscrollTimer(); 169 return; 170 } 171 172 IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition); 173 if (offset.isZero()) { 174 stopAutoscrollTimer(); 175 return; 176 } 177 178 m_dragAndDropAutoscrollReferencePosition = eventPosition + offset; 179 180 if (m_autoscrollType == NoAutoscroll) { 181 m_autoscrollType = AutoscrollForDragAndDrop; 182 m_autoscrollRenderer = scrollable; 183 m_dragAndDropAutoscrollStartTime = eventTime; 184 startAutoscrollTimer(); 185 } else if (m_autoscrollRenderer != scrollable) { 186 m_dragAndDropAutoscrollStartTime = eventTime; 187 m_autoscrollRenderer = scrollable; 188 } 189} 190 191#if ENABLE(PAN_SCROLLING) 192void AutoscrollController::didPanScrollStart() 193{ 194 m_autoscrollType = AutoscrollForPan; 195} 196 197void AutoscrollController::didPanScrollStop() 198{ 199 m_autoscrollType = NoAutoscroll; 200} 201 202void AutoscrollController::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) 203{ 204 switch (m_autoscrollType) { 205 case AutoscrollForPan: 206 if (mouseEvent.button() == MiddleButton) 207 m_autoscrollType = AutoscrollForPanCanStop; 208 break; 209 case AutoscrollForPanCanStop: 210 stopAutoscrollTimer(); 211 break; 212 } 213} 214 215bool AutoscrollController::panScrollInProgress() const 216{ 217 return m_autoscrollType == AutoscrollForPan || m_autoscrollType == AutoscrollForPanCanStop; 218} 219 220void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition) 221{ 222 // We don't want to trigger the autoscroll or the panScroll if it's already active 223 if (m_autoscrollTimer.isActive()) 224 return; 225 226 m_autoscrollType = AutoscrollForPan; 227 m_autoscrollRenderer = scrollable; 228 m_panScrollStartPos = lastKnownMousePosition; 229 230 if (FrameView* view = scrollable->frame()->view()) 231 view->addPanScrollIcon(lastKnownMousePosition); 232 scrollable->frame()->eventHandler()->didPanScrollStart(); 233 startAutoscrollTimer(); 234} 235#else 236bool AutoscrollController::panScrollInProgress() const 237{ 238 return false; 239} 240#endif 241 242void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>*) 243{ 244 if (!m_autoscrollRenderer) { 245 stopAutoscrollTimer(); 246 return; 247 } 248 249 Frame* frame = m_autoscrollRenderer->frame(); 250 switch (m_autoscrollType) { 251 case AutoscrollForDragAndDrop: 252 if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay) 253 m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition); 254 break; 255 case AutoscrollForSelection: { 256 EventHandler* eventHandler = frame->eventHandler(); 257 if (!eventHandler->mousePressed()) { 258 stopAutoscrollTimer(); 259 return; 260 } 261#if ENABLE(DRAG_SUPPORT) 262 eventHandler->updateSelectionForMouseDrag(); 263#endif 264 m_autoscrollRenderer->autoscroll(eventHandler->lastKnownMousePosition()); 265 break; 266 } 267 case NoAutoscroll: 268 break; 269#if ENABLE(PAN_SCROLLING) 270 case AutoscrollForPanCanStop: 271 case AutoscrollForPan: 272 // we verify that the main frame hasn't received the order to stop the panScroll 273 if (Frame* mainFrame = getMainFrame(frame)) { 274 if (!mainFrame->eventHandler()->panScrollInProgress()) { 275 stopAutoscrollTimer(); 276 return; 277 } 278 } 279 if (FrameView* view = frame->view()) 280 updatePanScrollState(view, frame->eventHandler()->lastKnownMousePosition()); 281 m_autoscrollRenderer->panScroll(m_panScrollStartPos); 282 break; 283#endif 284 } 285} 286 287void AutoscrollController::startAutoscrollTimer() 288{ 289 m_autoscrollTimer.startRepeating(autoscrollInterval); 290} 291 292#if ENABLE(PAN_SCROLLING) 293void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition) 294{ 295 // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll 296 // So we don't want to change the cursor over this area 297 bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius); 298 bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius); 299 bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius); 300 bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius); 301 302 if (m_autoscrollType == AutoscrollForPan && (east || west || north || south)) 303 m_autoscrollType = AutoscrollForPanCanStop; 304 305 if (north) { 306 if (east) 307 view->setCursor(northEastPanningCursor()); 308 else if (west) 309 view->setCursor(northWestPanningCursor()); 310 else 311 view->setCursor(northPanningCursor()); 312 } else if (south) { 313 if (east) 314 view->setCursor(southEastPanningCursor()); 315 else if (west) 316 view->setCursor(southWestPanningCursor()); 317 else 318 view->setCursor(southPanningCursor()); 319 } else if (east) 320 view->setCursor(eastPanningCursor()); 321 else if (west) 322 view->setCursor(westPanningCursor()); 323 else 324 view->setCursor(middlePanningCursor()); 325} 326#endif 327 328} // namespace WebCore 329