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 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 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 "FrameView.h" 33#include "HitTestResult.h" 34#include "MainFrame.h" 35#include "Page.h" 36#include "RenderBox.h" 37#include "RenderView.h" 38#include "ScrollView.h" 39#include "Settings.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 if (autoscrollInProgress() && frame.eventHandler().mouseDownWasInSubframe()) { 100 if (Frame* subframe = frame.eventHandler().subframeForTargetNode(frame.eventHandler().mousePressNode())) 101 subframe->eventHandler().stopAutoscrollTimer(rendererIsBeingDestroyed); 102 return; 103 } 104 105 if (!rendererIsBeingDestroyed) 106 scrollable->stopAutoscroll(); 107#if ENABLE(PAN_SCROLLING) 108 if (panScrollInProgress()) { 109 FrameView& frameView = scrollable->view().frameView(); 110 frameView.removePanScrollIcon(); 111 frameView.setCursor(pointerCursor()); 112 } 113#endif 114 115 m_autoscrollType = NoAutoscroll; 116 117#if ENABLE(PAN_SCROLLING) 118 // If we're not in the top frame we notify it that we are not doing a panScroll any more. 119 if (!frame.isMainFrame()) 120 frame.mainFrame().eventHandler().didPanScrollStop(); 121#endif 122} 123 124void AutoscrollController::updateAutoscrollRenderer() 125{ 126 if (!m_autoscrollRenderer) 127 return; 128 129 RenderObject* renderer = m_autoscrollRenderer; 130 131#if ENABLE(PAN_SCROLLING) 132 HitTestResult hitTest = m_autoscrollRenderer->frame().eventHandler().hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active); 133 134 if (Node* nodeAtPoint = hitTest.innerNode()) 135 renderer = nodeAtPoint->renderer(); 136#endif 137 138 while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll())) 139 renderer = renderer->parent(); 140 m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0; 141} 142 143void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime) 144{ 145 if (!dropTargetNode) { 146 stopAutoscrollTimer(); 147 return; 148 } 149 150 RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer()); 151 if (!scrollable) { 152 stopAutoscrollTimer(); 153 return; 154 } 155 156 Frame& frame = scrollable->frame(); 157 158 Page* page = frame.page(); 159 if (!page || !page->settings().autoscrollForDragAndDropEnabled()) { 160 stopAutoscrollTimer(); 161 return; 162 } 163 164 IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition); 165 if (offset.isZero()) { 166 stopAutoscrollTimer(); 167 return; 168 } 169 170 m_dragAndDropAutoscrollReferencePosition = eventPosition + offset; 171 172 if (m_autoscrollType == NoAutoscroll) { 173 m_autoscrollType = AutoscrollForDragAndDrop; 174 m_autoscrollRenderer = scrollable; 175 m_dragAndDropAutoscrollStartTime = eventTime; 176 startAutoscrollTimer(); 177 } else if (m_autoscrollRenderer != scrollable) { 178 m_dragAndDropAutoscrollStartTime = eventTime; 179 m_autoscrollRenderer = scrollable; 180 } 181} 182 183#if ENABLE(PAN_SCROLLING) 184void AutoscrollController::didPanScrollStart() 185{ 186 m_autoscrollType = AutoscrollForPan; 187} 188 189void AutoscrollController::didPanScrollStop() 190{ 191 m_autoscrollType = NoAutoscroll; 192} 193 194void AutoscrollController::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) 195{ 196 switch (m_autoscrollType) { 197 case AutoscrollForPan: 198 if (mouseEvent.button() == MiddleButton) 199 m_autoscrollType = AutoscrollForPanCanStop; 200 break; 201 case AutoscrollForPanCanStop: 202 stopAutoscrollTimer(); 203 break; 204 } 205} 206 207bool AutoscrollController::panScrollInProgress() const 208{ 209 return m_autoscrollType == AutoscrollForPan || m_autoscrollType == AutoscrollForPanCanStop; 210} 211 212void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition) 213{ 214 // We don't want to trigger the autoscroll or the panScroll if it's already active 215 if (m_autoscrollTimer.isActive()) 216 return; 217 218 m_autoscrollType = AutoscrollForPan; 219 m_autoscrollRenderer = scrollable; 220 m_panScrollStartPos = lastKnownMousePosition; 221 222 if (FrameView* view = scrollable->frame().view()) 223 view->addPanScrollIcon(lastKnownMousePosition); 224 scrollable->frame().eventHandler().didPanScrollStart(); 225 startAutoscrollTimer(); 226} 227#else 228bool AutoscrollController::panScrollInProgress() const 229{ 230 return false; 231} 232#endif 233 234void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>&) 235{ 236 if (!m_autoscrollRenderer) { 237 stopAutoscrollTimer(); 238 return; 239 } 240 241 Frame& frame = m_autoscrollRenderer->frame(); 242 switch (m_autoscrollType) { 243 case AutoscrollForDragAndDrop: 244 if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay) 245 m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition); 246 break; 247 case AutoscrollForSelection: { 248 if (!frame.eventHandler().mousePressed()) { 249 stopAutoscrollTimer(); 250 return; 251 } 252#if ENABLE(DRAG_SUPPORT) 253 frame.eventHandler().updateSelectionForMouseDrag(); 254#endif 255 m_autoscrollRenderer->autoscroll(frame.eventHandler().lastKnownMousePosition()); 256 break; 257 } 258 case NoAutoscroll: 259 break; 260#if ENABLE(PAN_SCROLLING) 261 case AutoscrollForPanCanStop: 262 case AutoscrollForPan: 263 // we verify that the main frame hasn't received the order to stop the panScroll 264 if (Frame* mainFrame = getMainFrame(&frame)) { 265 if (!mainFrame->eventHandler().panScrollInProgress()) { 266 stopAutoscrollTimer(); 267 return; 268 } 269 } 270 if (FrameView* view = frame.view()) 271 updatePanScrollState(view, frame.eventHandler().lastKnownMousePosition()); 272 m_autoscrollRenderer->panScroll(m_panScrollStartPos); 273 break; 274#endif 275 } 276} 277 278void AutoscrollController::startAutoscrollTimer() 279{ 280 m_autoscrollTimer.startRepeating(autoscrollInterval); 281} 282 283#if ENABLE(PAN_SCROLLING) 284void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition) 285{ 286 // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll 287 // So we don't want to change the cursor over this area 288 bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius); 289 bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius); 290 bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius); 291 bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius); 292 293 if (m_autoscrollType == AutoscrollForPan && (east || west || north || south)) 294 m_autoscrollType = AutoscrollForPanCanStop; 295 296 if (north) { 297 if (east) 298 view->setCursor(northEastPanningCursor()); 299 else if (west) 300 view->setCursor(northWestPanningCursor()); 301 else 302 view->setCursor(northPanningCursor()); 303 } else if (south) { 304 if (east) 305 view->setCursor(southEastPanningCursor()); 306 else if (west) 307 view->setCursor(southWestPanningCursor()); 308 else 309 view->setCursor(southPanningCursor()); 310 } else if (east) 311 view->setCursor(eastPanningCursor()); 312 else if (west) 313 view->setCursor(westPanningCursor()); 314 else 315 view->setCursor(middlePanningCursor()); 316} 317#endif 318 319} // namespace WebCore 320