Home | History | Annotate | Download | only in page
      1 /*
      2  * Copyright (C) 2013 Google Inc. All rights reserved.
      3  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
      4  * Copyright (C) 2006 Alexey Proskuryakov (ap (at) webkit.org)
      5  * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 #include "core/page/AutoscrollController.h"
     31 
     32 #include "core/frame/FrameView.h"
     33 #include "core/frame/LocalFrame.h"
     34 #include "core/page/Chrome.h"
     35 #include "core/page/EventHandler.h"
     36 #include "core/page/Page.h"
     37 #include "core/rendering/HitTestResult.h"
     38 #include "core/rendering/RenderBox.h"
     39 #include "core/rendering/RenderListBox.h"
     40 #include "wtf/CurrentTime.h"
     41 
     42 namespace WebCore {
     43 
     44 // Delay time in second for start autoscroll if pointer is in border edge of scrollable element.
     45 static double autoscrollDelay = 0.2;
     46 
     47 PassOwnPtr<AutoscrollController> AutoscrollController::create(Page& page)
     48 {
     49     return adoptPtr(new AutoscrollController(page));
     50 }
     51 
     52 AutoscrollController::AutoscrollController(Page& page)
     53     : m_page(page)
     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_autoscrollType != NoAutoscroll)
     74         return;
     75     RenderBox* scrollable = RenderBox::findAutoscrollable(renderer);
     76     if (!scrollable)
     77         scrollable = renderer->isListBox() ? toRenderListBox(renderer) : 0;
     78     if (!scrollable)
     79         return;
     80     m_autoscrollType = AutoscrollForSelection;
     81     m_autoscrollRenderer = scrollable;
     82     startAutoscroll();
     83 }
     84 
     85 void AutoscrollController::stopAutoscroll()
     86 {
     87     RenderBox* scrollable = m_autoscrollRenderer;
     88     m_autoscrollRenderer = 0;
     89 
     90     if (!scrollable)
     91         return;
     92 
     93     scrollable->stopAutoscroll();
     94 #if OS(WIN)
     95     if (panScrollInProgress()) {
     96         if (FrameView* view = scrollable->frame()->view()) {
     97             view->removePanScrollIcon();
     98             view->setCursor(pointerCursor());
     99         }
    100     }
    101 #endif
    102 
    103     m_autoscrollType = NoAutoscroll;
    104 }
    105 
    106 void AutoscrollController::stopAutoscrollIfNeeded(RenderObject* renderer)
    107 {
    108     if (m_autoscrollRenderer != renderer)
    109         return;
    110     m_autoscrollRenderer = 0;
    111     m_autoscrollType = NoAutoscroll;
    112 }
    113 
    114 void AutoscrollController::updateAutoscrollRenderer()
    115 {
    116     if (!m_autoscrollRenderer)
    117         return;
    118 
    119     RenderObject* renderer = m_autoscrollRenderer;
    120 
    121 #if OS(WIN)
    122     HitTestResult hitTest = renderer->frame()->eventHandler().hitTestResultAtPoint(m_panScrollStartPos, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
    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 || !dropTargetNode->renderer()) {
    136         stopAutoscroll();
    137         return;
    138     }
    139 
    140     if (m_autoscrollRenderer && m_autoscrollRenderer->frame() != dropTargetNode->renderer()->frame())
    141         return;
    142 
    143     RenderBox* scrollable = RenderBox::findAutoscrollable(dropTargetNode->renderer());
    144     if (!scrollable) {
    145         stopAutoscroll();
    146         return;
    147     }
    148 
    149     Page* page = scrollable->frame() ? scrollable->frame()->page() : 0;
    150     if (!page) {
    151         stopAutoscroll();
    152         return;
    153     }
    154 
    155     IntSize offset = scrollable->calculateAutoscrollDirection(eventPosition);
    156     if (offset.isZero()) {
    157         stopAutoscroll();
    158         return;
    159     }
    160 
    161     m_dragAndDropAutoscrollReferencePosition = eventPosition + offset;
    162 
    163     if (m_autoscrollType == NoAutoscroll) {
    164         m_autoscrollType = AutoscrollForDragAndDrop;
    165         m_autoscrollRenderer = scrollable;
    166         m_dragAndDropAutoscrollStartTime = eventTime;
    167         startAutoscroll();
    168     } else if (m_autoscrollRenderer != scrollable) {
    169         m_dragAndDropAutoscrollStartTime = eventTime;
    170         m_autoscrollRenderer = scrollable;
    171     }
    172 }
    173 
    174 #if OS(WIN)
    175 void AutoscrollController::handleMouseReleaseForPanScrolling(LocalFrame* frame, const PlatformMouseEvent& mouseEvent)
    176 {
    177     if (!frame->isMainFrame())
    178         return;
    179     switch (m_autoscrollType) {
    180     case AutoscrollForPan:
    181         if (mouseEvent.button() == MiddleButton)
    182             m_autoscrollType = AutoscrollForPanCanStop;
    183         break;
    184     case AutoscrollForPanCanStop:
    185         stopAutoscroll();
    186         break;
    187     }
    188 }
    189 
    190 bool AutoscrollController::panScrollInProgress() const
    191 {
    192     return m_autoscrollType == AutoscrollForPanCanStop || m_autoscrollType == AutoscrollForPan;
    193 }
    194 
    195 void AutoscrollController::startPanScrolling(RenderBox* scrollable, const IntPoint& lastKnownMousePosition)
    196 {
    197     // We don't want to trigger the autoscroll or the panScroll if it's already active
    198     if (m_autoscrollType != NoAutoscroll)
    199         return;
    200 
    201     m_autoscrollType = AutoscrollForPan;
    202     m_autoscrollRenderer = scrollable;
    203     m_panScrollStartPos = lastKnownMousePosition;
    204 
    205     if (FrameView* view = scrollable->frame()->view())
    206         view->addPanScrollIcon(lastKnownMousePosition);
    207     startAutoscroll();
    208 }
    209 #else
    210 bool AutoscrollController::panScrollInProgress() const
    211 {
    212     return false;
    213 }
    214 #endif
    215 
    216 // FIXME: This would get get better animation fidelity if it used the monotonicFrameBeginTime instead
    217 // of WTF::currentTime().
    218 void AutoscrollController::animate(double)
    219 {
    220     if (!m_autoscrollRenderer) {
    221         stopAutoscroll();
    222         return;
    223     }
    224 
    225     EventHandler& eventHandler = m_autoscrollRenderer->frame()->eventHandler();
    226     switch (m_autoscrollType) {
    227     case AutoscrollForDragAndDrop:
    228         if (WTF::currentTime() - m_dragAndDropAutoscrollStartTime > autoscrollDelay)
    229             m_autoscrollRenderer->autoscroll(m_dragAndDropAutoscrollReferencePosition);
    230         break;
    231     case AutoscrollForSelection:
    232         if (!eventHandler.mousePressed()) {
    233             stopAutoscroll();
    234             return;
    235         }
    236         eventHandler.updateSelectionForMouseDrag();
    237         m_autoscrollRenderer->autoscroll(eventHandler.lastKnownMousePosition());
    238         break;
    239     case NoAutoscroll:
    240         break;
    241 #if OS(WIN)
    242     case AutoscrollForPanCanStop:
    243     case AutoscrollForPan:
    244         if (!panScrollInProgress()) {
    245             stopAutoscroll();
    246             return;
    247         }
    248         if (FrameView* view = m_autoscrollRenderer->frame()->view())
    249             updatePanScrollState(view, eventHandler.lastKnownMousePosition());
    250         m_autoscrollRenderer->panScroll(m_panScrollStartPos);
    251         break;
    252 #endif
    253     }
    254     if (m_autoscrollType != NoAutoscroll)
    255         m_page.chrome().scheduleAnimation();
    256 }
    257 
    258 void AutoscrollController::startAutoscroll()
    259 {
    260     m_page.chrome().scheduleAnimation();
    261 }
    262 
    263 #if OS(WIN)
    264 void AutoscrollController::updatePanScrollState(FrameView* view, const IntPoint& lastKnownMousePosition)
    265 {
    266     // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll
    267     // So we don't want to change the cursor over this area
    268     bool east = m_panScrollStartPos.x() < (lastKnownMousePosition.x() - ScrollView::noPanScrollRadius);
    269     bool west = m_panScrollStartPos.x() > (lastKnownMousePosition.x() + ScrollView::noPanScrollRadius);
    270     bool north = m_panScrollStartPos.y() > (lastKnownMousePosition.y() + ScrollView::noPanScrollRadius);
    271     bool south = m_panScrollStartPos.y() < (lastKnownMousePosition.y() - ScrollView::noPanScrollRadius);
    272 
    273     if (m_autoscrollType == AutoscrollForPan && (east || west || north || south))
    274         m_autoscrollType = AutoscrollForPanCanStop;
    275 
    276     if (north) {
    277         if (east)
    278             view->setCursor(northEastPanningCursor());
    279         else if (west)
    280             view->setCursor(northWestPanningCursor());
    281         else
    282             view->setCursor(northPanningCursor());
    283     } else if (south) {
    284         if (east)
    285             view->setCursor(southEastPanningCursor());
    286         else if (west)
    287             view->setCursor(southWestPanningCursor());
    288         else
    289             view->setCursor(southPanningCursor());
    290     } else if (east)
    291         view->setCursor(eastPanningCursor());
    292     else if (west)
    293         view->setCursor(westPanningCursor());
    294     else
    295         view->setCursor(middlePanningCursor());
    296 }
    297 #endif
    298 
    299 } // namespace WebCore
    300