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