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 blink { 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); 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 blink 300