Home | History | Annotate | Download | only in page
      1 /*
      2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006 Alexey Proskuryakov (ap (at) 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 "core/page/AutoscrollController.h"
     30 
     31 #include "core/page/EventHandler.h"
     32 #include "core/page/Frame.h"
     33 #include "core/page/FrameView.h"
     34 #include "core/page/Page.h"
     35 #include "core/rendering/HitTestResult.h"
     36 #include "core/rendering/RenderBox.h"
     37 #include "wtf/CurrentTime.h"
     38 
     39 namespace WebCore {
     40 
     41 // Delay time in second for start autoscroll if pointer is in border edge of scrollable element.
     42 static double autoscrollDelay = 0.2;
     43 
     44 // When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth
     45 static const double autoscrollInterval = 0.05;
     46 
     47 PassOwnPtr<AutoscrollController> AutoscrollController::create()
     48 {
     49     return adoptPtr(new AutoscrollController());
     50 }
     51 
     52 AutoscrollController::AutoscrollController()
     53     : m_autoscrollTimer(this, &AutoscrollController::autoscrollTimerFired)
     54     , m_autoscrollRenderer(0)
     55     , m_autoscrollType(NoAutoscroll)
     56     , m_dragAndDropAutoscrollStartTime(0)
     57 {
     58 }
     59 
     60 bool AutoscrollController::autoscrollInProgress() const
     61 {
     62     return m_autoscrollType == AutoscrollForSelection;
     63 }
     64 
     65 bool AutoscrollController::autoscrollInProgress(const RenderBox* renderer) const
     66 {
     67     return m_autoscrollRenderer == renderer;
     68 }
     69 
     70 void AutoscrollController::startAutoscrollForSelection(RenderObject* renderer)
     71 {
     72     // We don't want to trigger the autoscroll or the panScroll if it's already active
     73     if (m_autoscrollTimer.isActive())
     74         return;
     75     RenderBox* scrollable = RenderBox::findAutoscrollable(renderer);
     76     if (!scrollable)
     77         return;
     78     m_autoscrollType = AutoscrollForSelection;
     79     m_autoscrollRenderer = scrollable;
     80     startAutoscrollTimer();
     81 }
     82 
     83 void AutoscrollController::stopAutoscrollTimer()
     84 {
     85     RenderBox* scrollable = m_autoscrollRenderer;
     86     m_autoscrollTimer.stop();
     87     m_autoscrollRenderer = 0;
     88 
     89     if (!scrollable)
     90         return;
     91 
     92     scrollable->stopAutoscroll();
     93 #if OS(WINDOWS)
     94     if (panScrollInProgress()) {
     95         if (FrameView* view = scrollable->frame()->view()) {
     96             view->removePanScrollIcon();
     97             view->setCursor(pointerCursor());
     98         }
     99     }
    100 #endif
    101 
    102     m_autoscrollType = NoAutoscroll;
    103 }
    104 
    105 void AutoscrollController::stopAutoscrollIfNeeded(RenderObject* renderer)
    106 {
    107     if (m_autoscrollRenderer != renderer)
    108         return;
    109     m_autoscrollRenderer = 0;
    110     m_autoscrollType = NoAutoscroll;
    111     m_autoscrollTimer.stop();
    112 }
    113 
    114 void AutoscrollController::updateAutoscrollRenderer()
    115 {
    116     if (!m_autoscrollRenderer)
    117         return;
    118 
    119     RenderObject* renderer = m_autoscrollRenderer;
    120 
    121 #if OS(WINDOWS)
    122     HitTestResult hitTest = renderer->frame()->eventHandler()->hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent);
    123 
    124     if (Node* nodeAtPoint = hitTest.innerNode())
    125         renderer = nodeAtPoint->renderer();
    126 #endif
    127 
    128     while (renderer && !(renderer->isBox() && toRenderBox(renderer)->canAutoscroll()))
    129         renderer = renderer->parent();
    130     m_autoscrollRenderer = renderer && renderer->isBox() ? toRenderBox(renderer) : 0;
    131 }
    132 
    133 void AutoscrollController::updateDragAndDrop(Node* dropTargetNode, const IntPoint& eventPosition, double eventTime)
    134 {
    135     if (!dropTargetNode) {
    136         stopAutoscrollTimer();
    137         return;
    138     }
    139 
    140     RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer());
    141     if (!scrollable) {
    142         stopAutoscrollTimer();
    143         return;
    144     }
    145 
    146     Page* page = scrollable->frame() ? scrollable->frame()->page() : 0;
    147     if (!page) {
    148         stopAutoscrollTimer();
    149         return;
    150     }
    151 
    152     IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition);
    153     if (offset.isZero()) {
    154         stopAutoscrollTimer();
    155         return;
    156     }
    157 
    158     m_dragAndDropAutoscrollReferencePosition = eventPosition + offset;
    159 
    160     if (m_autoscrollType == NoAutoscroll) {
    161         m_autoscrollType = AutoscrollForDragAndDrop;
    162         m_autoscrollRenderer = scrollable;
    163         m_dragAndDropAutoscrollStartTime = eventTime;
    164         startAutoscrollTimer();
    165     } else if (m_autoscrollRenderer != scrollable) {
    166         m_dragAndDropAutoscrollStartTime = eventTime;
    167         m_autoscrollRenderer = scrollable;
    168     }
    169 }
    170 
    171 #if OS(WINDOWS)
    172 void AutoscrollController::handleMouseReleaseForPanScrolling(Frame* frame, const PlatformMouseEvent& mouseEvent)
    173 {
    174     Page* page = frame->page();
    175     if (!page || page->mainFrame() != frame)
    176         return;
    177     switch (m_autoscrollType) {
    178     case AutoscrollForPan:
    179         if (mouseEvent.button() == MiddleButton)
    180             m_autoscrollType = AutoscrollForPanCanStop;
    181         break;
    182     case AutoscrollForPanCanStop:
    183         stopAutoscrollTimer();
    184         break;
    185     }
    186 }
    187 
    188 bool AutoscrollController::panScrollInProgress() const
    189 {
    190     return m_autoscrollType == AutoscrollForPanCanStop || m_autoscrollType == AutoscrollForPan;
    191 }
    192 
    193 void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition)
    194 {
    195     // We don't want to trigger the autoscroll or the panScroll if it's already active
    196     if (m_autoscrollTimer.isActive())
    197         return;
    198 
    199     m_autoscrollType = AutoscrollForPan;
    200     m_autoscrollRenderer = scrollable;
    201     m_panScrollStartPos = lastKnownMousePosition;
    202 
    203     if (FrameView* view = scrollable->frame()->view())
    204         view->addPanScrollIcon(lastKnownMousePosition);
    205     startAutoscrollTimer();
    206 }
    207 #else
    208 bool AutoscrollController::panScrollInProgress() const
    209 {
    210     return false;
    211 }
    212 #endif
    213 
    214 void AutoscrollController::autoscrollTimerFired(Timer<AutoscrollController>*)
    215 {
    216     if (!m_autoscrollRenderer) {
    217         stopAutoscrollTimer();
    218         return;
    219     }
    220 
    221     EventHandler* eventHandler = m_autoscrollRenderer->frame()->eventHandler();
    222     switch (m_autoscrollType) {
    223     case AutoscrollForDragAndDrop:
    224         if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay)
    225             m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition);
    226         break;
    227     case AutoscrollForSelection:
    228         if (!eventHandler->mousePressed()) {
    229             stopAutoscrollTimer();
    230             return;
    231         }
    232         eventHandler->updateSelectionForMouseDrag();
    233         m_autoscrollRenderer->autoscroll(eventHandler->lastKnownMousePosition());
    234         break;
    235     case NoAutoscroll:
    236         break;
    237 #if OS(WINDOWS)
    238     case AutoscrollForPanCanStop:
    239     case AutoscrollForPan:
    240         if (!panScrollInProgress()) {
    241             stopAutoscrollTimer();
    242             return;
    243         }
    244         if (FrameView* view = m_autoscrollRenderer->frame()->view())
    245             updatePanScrollState(view, eventHandler->lastKnownMousePosition());
    246         m_autoscrollRenderer->panScroll(m_panScrollStartPos);
    247         break;
    248 #endif
    249     }
    250 }
    251 
    252 void AutoscrollController::startAutoscrollTimer()
    253 {
    254     m_autoscrollTimer.startRepeating(autoscrollInterval);
    255 }
    256 
    257 #if OS(WINDOWS)
    258 void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition)
    259 {
    260     // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll
    261     // So we don't want to change the cursor over this area
    262     bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius);
    263     bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius);
    264     bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius);
    265     bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius);
    266 
    267     if (m_autoscrollType == AutoscrollForPan && (east || west || north || south))
    268         m_autoscrollType = AutoscrollForPanCanStop;
    269 
    270     if (north) {
    271         if (east)
    272             view->setCursor(northEastPanningCursor());
    273         else if (west)
    274             view->setCursor(northWestPanningCursor());
    275         else
    276             view->setCursor(northPanningCursor());
    277     } else if (south) {
    278         if (east)
    279             view->setCursor(southEastPanningCursor());
    280         else if (west)
    281             view->setCursor(southWestPanningCursor());
    282         else
    283             view->setCursor(southPanningCursor());
    284     } else if (east)
    285         view->setCursor(eastPanningCursor());
    286     else if (west)
    287         view->setCursor(westPanningCursor());
    288     else
    289         view->setCursor(middlePanningCursor());
    290 }
    291 #endif
    292 
    293 } // namespace WebCore
    294