EventHandler.cpp 141 KB
Newer Older
darin's avatar
darin committed
1
/*
2
 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2013 Apple Inc. All rights reserved.
ap's avatar
ap committed
3
 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4
 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
darin's avatar
darin committed
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "EventHandler.h"

31
#include "AXObjectCache.h"
32
#include "AutoscrollController.h"
darin's avatar
darin committed
33
#include "CachedImage.h"
34
#include "Chrome.h"
mjs's avatar
mjs committed
35
#include "ChromeClient.h"
darin's avatar
darin committed
36
#include "Cursor.h"
37
#include "CursorList.h"
darin's avatar
darin committed
38
#include "Document.h"
39
#include "DocumentEventQueue.h"
40
#include "DragController.h"
41
#include "DragState.h"
bdakin's avatar
bdakin committed
42
#include "Editor.h"
43
#include "EditorClient.h"
darin's avatar
darin committed
44
#include "EventNames.h"
45
#include "ExceptionCodePlaceholder.h"
46
#include "FileList.h"
ddkilzer's avatar
ddkilzer committed
47
#include "FloatPoint.h"
48
#include "FloatRect.h"
adele's avatar
adele committed
49
#include "FocusController.h"
antti's avatar
antti committed
50
#include "FrameLoader.h"
51
#include "FrameSelection.h"
darin's avatar
darin committed
52 53
#include "FrameTree.h"
#include "FrameView.h"
54
#include "htmlediting.h"
darin's avatar
darin committed
55
#include "HTMLFrameElementBase.h"
56
#include "HTMLFrameSetElement.h"
darin's avatar
darin committed
57 58
#include "HTMLInputElement.h"
#include "HTMLNames.h"
59 60
#include "HitTestRequest.h"
#include "HitTestResult.h"
61
#include "Image.h"
62
#include "InspectorInstrumentation.h"
darin's avatar
darin committed
63
#include "KeyboardEvent.h"
darin@apple.com's avatar
darin@apple.com committed
64
#include "MainFrame.h"
darin's avatar
darin committed
65 66
#include "MouseEvent.h"
#include "MouseEventWithHitTestResults.h"
adele's avatar
adele committed
67
#include "Page.h"
68
#include "PlatformEvent.h"
adele's avatar
adele committed
69
#include "PlatformKeyboardEvent.h"
darin's avatar
darin committed
70
#include "PlatformWheelEvent.h"
71
#include "PluginDocument.h"
72
#include "RenderFrameSet.h"
73
#include "RenderLayer.h"
74
#include "RenderTextControlSingleLine.h"
eric@webkit.org's avatar
eric@webkit.org committed
75
#include "RenderView.h"
76
#include "RenderWidget.h"
77
#include "RuntimeApplicationChecks.h"
78
#include "ScrollAnimator.h"
79
#include "Scrollbar.h"
darin's avatar
darin committed
80
#include "Settings.h"
81
#include "ShadowRoot.h"
82
#include "SpatialNavigation.h"
83
#include "StyleCachedImage.h"
darin's avatar
darin committed
84
#include "TextEvent.h"
85
#include "TextIterator.h"
86
#include "UserGestureIndicator.h"
87
#include "UserTypingGestureIndicator.h"
88
#include "WheelEvent.h"
ap@apple.com's avatar
ap@apple.com committed
89
#include "WindowsKeyboardCodes.h"
90
#include <wtf/Assertions.h>
91
#include <wtf/CurrentTime.h>
92
#include <wtf/StdLibExtras.h>
93
#include <wtf/TemporaryChange.h>
darin's avatar
darin committed
94

mjs's avatar
mjs committed
95
#if ENABLE(SVG)
weinig's avatar
weinig committed
96
#include "SVGDocument.h"
97
#include "SVGElementInstance.h"
darin's avatar
darin committed
98
#include "SVGNames.h"
99
#include "SVGUseElement.h"
darin's avatar
darin committed
100 101
#endif

102
#if ENABLE(TOUCH_EVENTS)
103 104 105
#if ENABLE(IOS_TOUCH_EVENTS)
#include "PlatformTouchEventIOS.h"
#else
106
#include "PlatformTouchEvent.h"
107
#endif
108
#include "TouchEvent.h"
109
#include "TouchList.h"
110 111
#endif

112 113 114 115
#if ENABLE(CSS_IMAGE_SET)
#include "StyleCachedImageSet.h"
#endif

darin's avatar
darin committed
116 117 118 119
namespace WebCore {

using namespace HTMLNames;

bolsinga@apple.com's avatar
bolsinga@apple.com committed
120
#if ENABLE(DRAG_SUPPORT)
aliceli1's avatar
aliceli1 committed
121 122 123
// The link drag hysteresis is much larger than the others because there
// needs to be enough space to cancel the link press without starting a link drag,
// and because dragging links is rare.
oliver's avatar
 
oliver committed
124 125 126 127
const int LinkDragHysteresis = 40;
const int ImageDragHysteresis = 5;
const int TextDragHysteresis = 3;
const int GeneralDragHysteresis = 3;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
128
#endif // ENABLE(DRAG_SUPPORT)
aliceli1's avatar
aliceli1 committed
129

130 131 132 133 134 135 136 137 138
#if ENABLE(IOS_GESTURE_EVENTS)
const float GestureUnknown = 0;
#endif

#if ENABLE(IOS_TOUCH_EVENTS)
// FIXME: Share this constant with EventHandler and SliderThumbElement.
const unsigned InvalidTouchIdentifier = 0;
#endif

oliver's avatar
oliver committed
139 140 141 142
// Match key code of composition keydown event on windows.
// IE sends VK_PROCESSKEY which has value 229;
const int CompositionEventKeyCode = 229;

mjs's avatar
mjs committed
143
#if ENABLE(SVG)
darin's avatar
darin committed
144 145 146
using namespace SVGNames;
#endif

147
// The amount of time to wait before sending a fake mouse event, triggered
148 149 150
// during a scroll. The short interval is used if the content responds to the mouse events
// in fakeMouseMoveDurationThreshold or less, otherwise the long interval is used.
const double fakeMouseMoveDurationThreshold = 0.01;
151
const double fakeMouseMoveShortInterval = 0.1;
152
const double fakeMouseMoveLongInterval = 0.25;
153

154
#if ENABLE(CURSOR_SUPPORT)
155 156 157 158
// The amount of time to wait for a cursor update on style and layout changes
// Set to 50Hz, no need to be faster than common screen refresh rate
const double cursorUpdateInterval = 0.02;

159
const int maximumCursorSize = 128;
160 161
#endif

162 163 164 165 166 167 168
#if ENABLE(MOUSE_CURSOR_SCALE)
// It's pretty unlikely that a scale of less than one would ever be used. But all we really
// need to ensure here is that the scale isn't so small that integer overflow can occur when
// dividing cursor sizes (limited above) by the scale.
const double minimumCursorScale = 0.001;
#endif

169 170 171 172 173 174 175 176
enum NoCursorChangeType { NoCursorChange };

class OptionalCursor {
public:
    OptionalCursor(NoCursorChangeType) : m_isCursorChange(false) { }
    OptionalCursor(const Cursor& cursor) : m_isCursorChange(true), m_cursor(cursor) { }

    bool isCursorChange() const { return m_isCursorChange; }
177
    const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
178 179 180 181 182 183

private:
    bool m_isCursorChange;
    Cursor m_cursor;
};

184
class MaximumDurationTracker {
185
public:
186 187
    explicit MaximumDurationTracker(double *maxDuration)
        : m_maxDuration(maxDuration)
188 189 190 191
        , m_start(monotonicallyIncreasingTime())
    {
    }

192
    ~MaximumDurationTracker()
193
    {
194
        *m_maxDuration = std::max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
195 196 197
    }

private:
198
    double* m_maxDuration;
199 200 201
    double m_start;
};

202
#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
class SyntheticTouchPoint : public PlatformTouchPoint {
public:

    // The default values are based on http://dvcs.w3.org/hg/webevents/raw-file/tip/touchevents.html
    explicit SyntheticTouchPoint(const PlatformMouseEvent& event)
    {
        const static int idDefaultValue = 0;
        const static int radiusYDefaultValue = 1;
        const static int radiusXDefaultValue = 1;
        const static float rotationAngleDefaultValue = 0.0f;
        const static float forceDefaultValue = 1.0f;

        m_id = idDefaultValue; // There is only one active TouchPoint.
        m_screenPos = event.globalPosition();
        m_pos = event.position();
        m_radiusY = radiusYDefaultValue;
        m_radiusX = radiusXDefaultValue;
        m_rotationAngle = rotationAngleDefaultValue;
        m_force = forceDefaultValue;

        PlatformEvent::Type type = event.type();
        ASSERT(type == PlatformEvent::MouseMoved || type == PlatformEvent::MousePressed || type == PlatformEvent::MouseReleased);

        switch (type) {
        case PlatformEvent::MouseMoved:
            m_state = TouchMoved;
            break;
        case PlatformEvent::MousePressed:
            m_state = TouchPressed;
            break;
        case PlatformEvent::MouseReleased:
            m_state = TouchReleased;
            break;
        default:
            ASSERT_NOT_REACHED();
            break;
        }
    }
};

class SyntheticSingleTouchEvent : public PlatformTouchEvent {
public:
    explicit SyntheticSingleTouchEvent(const PlatformMouseEvent& event)
    {
        switch (event.type()) {
        case PlatformEvent::MouseMoved:
            m_type = TouchMove;
            break;
        case PlatformEvent::MousePressed:
            m_type = TouchStart;
            break;
        case PlatformEvent::MouseReleased:
            m_type = TouchEnd;
            break;
        default:
            ASSERT_NOT_REACHED();
            m_type = NoType;
            break;
        }
        m_timestamp = event.timestamp();
        m_modifiers = event.modifiers();
        m_touchPoints.append(SyntheticTouchPoint(event));
    }
};
267
#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
268

269
static inline ScrollGranularity wheelGranularityToScrollGranularity(unsigned deltaMode)
270
{
271
    switch (deltaMode) {
272
    case WheelEvent::DOM_DELTA_PAGE:
273
        return ScrollByPage;
274
    case WheelEvent::DOM_DELTA_LINE:
275
        return ScrollByLine;
276 277 278
    case WheelEvent::DOM_DELTA_PIXEL:
        return ScrollByPixel;
    default:
279 280 281 282
        return ScrollByPixel;
    }
}

283
static inline bool scrollNode(float delta, ScrollGranularity granularity, ScrollDirection positiveDirection, ScrollDirection negativeDirection, Node* node, Element** stopElement)
284
{
285
    if (!delta)
286
        return false;
287 288
    if (!node->renderer())
        return false;
289
    RenderBox* enclosingBox = node->renderer()->enclosingBox();
290
    float absDelta = delta > 0 ? delta : -delta;
291
    return enclosingBox->scroll(delta < 0 ? negativeDirection : positiveDirection, granularity, absDelta, stopElement);
292 293
}

294
#if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS))
295 296 297 298 299 300 301
static inline bool shouldGesturesTriggerActive()
{
    // If the platform we're on supports GestureTapDown and GestureTapCancel then we'll
    // rely on them to set the active state. Unfortunately there's no generic way to
    // know in advance what event types are supported.
    return false;
}
302
#endif
303

304
#if !PLATFORM(MAC)
darin@apple.com's avatar
darin@apple.com committed
305 306 307 308 309 310

inline bool EventHandler::eventLoopHandleMouseUp(const MouseEventWithHitTestResults&)
{
    return false;
}

311
#if ENABLE(DRAG_SUPPORT)
darin@apple.com's avatar
darin@apple.com committed
312 313 314 315
inline bool EventHandler::eventLoopHandleMouseDragged(const MouseEventWithHitTestResults&)
{
    return false;
}
316
#endif
darin@apple.com's avatar
darin@apple.com committed
317 318 319

#endif

320
EventHandler::EventHandler(Frame& frame)
darin's avatar
darin committed
321 322
    : m_frame(frame)
    , m_mousePressed(false)
323
    , m_capturesDragging(false)
darin's avatar
darin committed
324
    , m_mouseDownMayStartSelect(false)
bolsinga@apple.com's avatar
bolsinga@apple.com committed
325
#if ENABLE(DRAG_SUPPORT)
darin's avatar
darin committed
326
    , m_mouseDownMayStartDrag(false)
327
    , m_dragMayStartSelectionInstead(false)
bolsinga@apple.com's avatar
bolsinga@apple.com committed
328
#endif
darin's avatar
darin committed
329
    , m_mouseDownWasSingleClickInSelection(false)
330
    , m_selectionInitiationState(HaveNotStartedSelection)
331
    , m_hoverTimer(this, &EventHandler::hoverTimerFired)
332
#if ENABLE(CURSOR_SUPPORT)
333
    , m_cursorUpdateTimer(this, &EventHandler::cursorUpdateTimerFired)
334
#endif
335
    , m_autoscrollController(adoptPtr(new AutoscrollController))
darin's avatar
darin committed
336
    , m_mouseDownMayStartAutoscroll(false)
darin's avatar
darin committed
337
    , m_mouseDownWasInSubframe(false)
338
    , m_fakeMouseMoveEventTimer(this, &EventHandler::fakeMouseMoveEventTimerFired)
weinig's avatar
weinig committed
339 340 341
#if ENABLE(SVG)
    , m_svgPan(false)
#endif
darin's avatar
darin committed
342
    , m_resizeLayer(0)
343
    , m_eventHandlerWillResetCapturingMouseEventsElement(nullptr)
darin's avatar
darin committed
344
    , m_clickCount(0)
345 346 347 348 349 350 351 352 353
#if ENABLE(IOS_GESTURE_EVENTS)
    , m_gestureInitialDiameter(GestureUnknown)
    , m_gestureLastDiameter(GestureUnknown)
    , m_gestureInitialRotation(GestureUnknown)
    , m_gestureLastRotation(GestureUnknown)
#endif
#if ENABLE(IOS_TOUCH_EVENTS)
    , m_firstTouchID(InvalidTouchIdentifier)
#endif
354
    , m_mousePositionIsUnknown(true)
aliceli1's avatar
aliceli1 committed
355
    , m_mouseDownTimestamp(0)
356
    , m_inTrackingScrollGesturePhase(false)
andersca@apple.com's avatar
andersca@apple.com committed
357
    , m_widgetIsLatched(false)
358
#if PLATFORM(MAC)
darin's avatar
darin committed
359 360
    , m_mouseDownView(nil)
    , m_sendingEventToSubview(false)
361
#if !PLATFORM(IOS)
362
    , m_activationEventNumber(-1)
363
#endif // !PLATFORM(IOS)
darin's avatar
darin committed
364
#endif
365
#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
366
    , m_originatingTouchPointTargetKey(0)
367 368
    , m_touchPressed(false)
#endif
369
    , m_maxMouseMovedDuration(0)
370
    , m_baseEventType(PlatformEvent::NoType)
371 372
    , m_didStartDrag(false)
    , m_didLongPressInvokeContextMenu(false)
373
    , m_isHandlingWheelEvent(false)
374 375 376
#if ENABLE(CURSOR_VISIBILITY)
    , m_autoHideCursorTimer(this, &EventHandler::autoHideCursorTimerFired)
#endif
darin's avatar
darin committed
377 378 379 380 381
{
}

EventHandler::~EventHandler()
{
382
    ASSERT(!m_fakeMouseMoveEventTimer.isActive());
383 384 385
#if ENABLE(CURSOR_VISIBILITY)
    ASSERT(!m_autoHideCursorTimer.isActive());
#endif
darin's avatar
darin committed
386
}
oliver's avatar
 
oliver committed
387
    
bolsinga@apple.com's avatar
bolsinga@apple.com committed
388
#if ENABLE(DRAG_SUPPORT)
389
DragState& EventHandler::dragState()
oliver's avatar
 
oliver committed
390
{
391
    DEFINE_STATIC_LOCAL(DragState, state, ());
oliver's avatar
 
oliver committed
392 393
    return state;
}
bolsinga@apple.com's avatar
bolsinga@apple.com committed
394
#endif // ENABLE(DRAG_SUPPORT)
oliver's avatar
 
oliver committed
395
    
darin's avatar
darin committed
396 397
void EventHandler::clear()
{
398
    m_hoverTimer.stop();
399
#if ENABLE(CURSOR_SUPPORT)
400
    m_cursorUpdateTimer.stop();
401
#endif
402
    m_fakeMouseMoveEventTimer.stop();
403 404 405
#if ENABLE(CURSOR_VISIBILITY)
    cancelAutoHideCursorTimer();
#endif
darin's avatar
darin committed
406
    m_resizeLayer = 0;
407 408
    m_elementUnderMouse = nullptr;
    m_lastElementUnderMouse = nullptr;
409 410 411 412
#if ENABLE(SVG)
    m_instanceUnderMouse = 0;
    m_lastInstanceUnderMouse = 0;
#endif
darin's avatar
darin committed
413 414 415 416
    m_lastMouseMoveEventSubframe = 0;
    m_lastScrollbarUnderMouse = 0;
    m_clickCount = 0;
    m_clickNode = 0;
417 418 419 420 421 422 423 424 425 426 427 428
#if ENABLE(IOS_GESTURE_EVENTS)
    m_gestureInitialDiameter = GestureUnknown;
    m_gestureLastDiameter = GestureUnknown;
    m_gestureInitialRotation = GestureUnknown;
    m_gestureLastRotation = GestureUnknown;
    m_gestureTargets.clear();
#endif
#if ENABLE(IOS_TOUCH_EVENTS)
    m_touches.clear();
    m_firstTouchID = InvalidTouchIdentifier;
    m_touchEventTargetSubframe = 0;
#endif
darin's avatar
darin committed
429
    m_frameSetBeingResized = 0;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
430
#if ENABLE(DRAG_SUPPORT)
darin's avatar
darin committed
431
    m_dragTarget = 0;
432
    m_shouldOnlyFireDragOverEvent = false;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
433
#endif
434 435 436
    m_mousePositionIsUnknown = true;
    m_lastKnownMousePosition = IntPoint();
    m_lastKnownMouseGlobalPosition = IntPoint();
darin's avatar
darin committed
437 438
    m_mousePressNode = 0;
    m_mousePressed = false;
439
    m_capturesDragging = false;
440
    m_capturingMouseEventsElement = nullptr;
441 442
    m_latchedWheelEventElement = nullptr;
    m_previousWheelScrolledElement = nullptr;
443
#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
444
    m_originatingTouchPointTargets.clear();
445 446
    m_originatingTouchPointDocument.clear();
    m_originatingTouchPointTargetKey = 0;
447
#endif
448
    m_maxMouseMovedDuration = 0;
449
    m_baseEventType = PlatformEvent::NoType;
450 451
    m_didStartDrag = false;
    m_didLongPressInvokeContextMenu = false;
darin's avatar
darin committed
452 453
}

454 455 456 457 458 459
void EventHandler::nodeWillBeRemoved(Node* nodeToBeRemoved)
{
    if (nodeToBeRemoved->contains(m_clickNode.get()))
        m_clickNode = 0;
}

460
static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
461
{
462 463
    if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection))
        selection.setSelection(newSelection);
464 465
}

466 467 468 469 470 471 472 473
static inline bool dispatchSelectStart(Node* node)
{
    if (!node || !node->renderer())
        return true;

    return node->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
}

474 475 476 477 478 479 480 481 482 483 484 485 486
static VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const VisibleSelection& selection)
{
#if ENABLE(USERSELECT_ALL)
    Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode);
    if (!rootUserSelectAll)
        return selection;

    VisibleSelection newSelection(selection);
    newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
    newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary));

    return newSelection;
#else
487
    UNUSED_PARAM(targetNode);
488 489 490 491 492
    return selection;
#endif
}

bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
493
{
494 495 496
    if (Position::nodeIsUserSelectNone(targetNode))
        return false;

497 498 499
    if (!dispatchSelectStart(targetNode))
        return false;

500
    if (selection.isRange())
501 502 503 504 505 506
        m_selectionInitiationState = ExtendedSelection;
    else {
        granularity = CharacterGranularity;
        m_selectionInitiationState = PlacedCaret;
    }

507
    m_frame.selection().setNonDirectionalSelectionIfNeeded(selection, granularity);
508 509 510 511

    return true;
}

512
void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
darin's avatar
darin committed
513
{
514
    Node* targetNode = result.targetNode();
515
    VisibleSelection newSelection;
darin's avatar
darin committed
516

517 518
    if (targetNode && targetNode->renderer()) {
        VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint()));
darin's avatar
darin committed
519
        if (pos.isNotNull()) {
520
            newSelection = VisibleSelection(pos);
darin's avatar
darin committed
521 522
            newSelection.expandUsingGranularity(WordGranularity);
        }
523

524
        if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
525 526
            newSelection.appendTrailingWhitespace();

527
        updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity);
darin's avatar
darin committed
528 529 530
    }
}

531 532 533 534
void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestResults& result)
{
    if (m_mouseDownMayStartSelect) {
        selectClosestWordFromHitTestResult(result.hitTestResult(),
535
            (result.event().clickCount() == 2 && m_frame.editor().isSelectTrailingWhitespaceEnabled()) ? ShouldAppendTrailingWhitespace : DontAppendTrailingWhitespace);
536 537 538
    }
}

mitz@apple.com's avatar
mitz@apple.com committed
539 540 541 542 543
void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
{
    if (!result.hitTestResult().isLiveLink())
        return selectClosestWordFromMouseEvent(result);

544
    Node* targetNode = result.targetNode();
mitz@apple.com's avatar
mitz@apple.com committed
545

546
    if (targetNode && targetNode->renderer() && m_mouseDownMayStartSelect) {
547
        VisibleSelection newSelection;
mitz@apple.com's avatar
mitz@apple.com committed
548
        Element* URLElement = result.hitTestResult().URLElement();
549
        VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint()));
550
        if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf(URLElement))
551
            newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement);
mitz@apple.com's avatar
mitz@apple.com committed
552

553
        updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity);
mitz@apple.com's avatar
mitz@apple.com committed
554 555 556
    }
}

aroben's avatar
aroben committed
557
bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
558
{
aroben's avatar
aroben committed
559 560 561
    if (event.event().button() != LeftButton)
        return false;

562
    if (m_frame.selection().isRange())
aroben's avatar
aroben committed
563 564 565 566 567
        // A double-click when range is already selected
        // should not change the selection.  So, do not call
        // selectClosestWordFromMouseEvent, but do set
        // m_beganSelectingText to prevent handleMouseReleaseEvent
        // from setting caret selection.
568
        m_selectionInitiationState = ExtendedSelection;
aroben's avatar
aroben committed
569
    else
570
        selectClosestWordFromMouseEvent(event);
aroben's avatar
aroben committed
571 572

    return true;
darin's avatar
darin committed
573 574
}

aroben's avatar
aroben committed
575
bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
576
{
aroben's avatar
aroben committed
577 578
    if (event.event().button() != LeftButton)
        return false;
darin's avatar
darin committed
579
    
580 581
    Node* targetNode = event.targetNode();
    if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
aroben's avatar
aroben committed
582 583
        return false;

584
    VisibleSelection newSelection;
585
    VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint()));
aroben's avatar
aroben committed
586
    if (pos.isNotNull()) {
587
        newSelection = VisibleSelection(pos);
aroben's avatar
aroben committed
588
        newSelection.expandUsingGranularity(ParagraphGranularity);
darin's avatar
darin committed
589
    }
aroben's avatar
aroben committed
590

591
    return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), ParagraphGranularity);
darin's avatar
darin committed
592 593
}

594 595
static int textDistance(const Position& start, const Position& end)
{
596
    RefPtr<Range> range = Range::create(start.anchorNode()->document(), start, end);
597
    return TextIterator::rangeLength(range.get(), true);
598 599
}

aroben's avatar
aroben committed
600
bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
601
{
602
    m_frame.document()->updateLayoutIgnorePendingStylesheets();
603 604
    Node* targetNode = event.targetNode();
    if (!(targetNode && targetNode->renderer() && m_mouseDownMayStartSelect))
aroben's avatar
aroben committed
605
        return false;
darin's avatar
darin committed
606

aroben's avatar
aroben committed
607 608
    // Extend the selection if the Shift key is down, unless the click is in a link.
    bool extendSelection = event.event().shiftKey() && !event.isOverLink();
darin's avatar
darin committed
609

aroben's avatar
aroben committed
610 611
    // Don't restart the selection when the mouse is pressed on an
    // existing selection so we can allow for text dragging.
612
    if (FrameView* view = m_frame.view()) {
weinig@apple.com's avatar
weinig@apple.com committed
613
        LayoutPoint vPoint = view->windowToContents(event.event().position());
614
        if (!extendSelection && m_frame.selection().contains(vPoint)) {
615 616 617
            m_mouseDownWasSingleClickInSelection = true;
            return false;
        }
darin's avatar
darin committed
618
    }
aroben's avatar
aroben committed
619

620
    VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint()));
aroben's avatar
aroben committed
621
    if (visiblePos.isNull())
622
        visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM);
aroben's avatar
aroben committed
623
    Position pos = visiblePos.deepEquivalent();
624

625
    VisibleSelection newSelection = m_frame.selection().selection();
626 627
    TextGranularity granularity = CharacterGranularity;

aroben's avatar
aroben committed
628
    if (extendSelection && newSelection.isCaretOrRange()) {
629
        VisibleSelection selectionInUserSelectAll = expandSelectionToRespectUserSelectAll(targetNode, VisibleSelection(pos));
630 631 632 633 634 635 636
        if (selectionInUserSelectAll.isRange()) {
            if (comparePositions(selectionInUserSelectAll.start(), newSelection.start()) < 0)
                pos = selectionInUserSelectAll.start();
            else if (comparePositions(newSelection.end(), selectionInUserSelectAll.end()) < 0)
                pos = selectionInUserSelectAll.end();
        }

637
        if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) {
638 639 640 641 642 643 644 645 646 647
            // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection
            // was created right-to-left
            Position start = newSelection.start();
            Position end = newSelection.end();
            int distanceToStart = textDistance(start, pos);
            int distanceToEnd = textDistance(pos, end);
            if (distanceToStart <= distanceToEnd)
                newSelection = VisibleSelection(end, pos);
            else
                newSelection = VisibleSelection(start, pos);
648
        } else
649
            newSelection.setExtent(pos);
aroben's avatar
aroben committed
650

651 652 653
        if (m_frame.selection().granularity() != CharacterGranularity) {
            granularity = m_frame.selection().granularity();
            newSelection.expandUsingGranularity(m_frame.selection().granularity());
654 655
        }
    } else
656
        newSelection = expandSelectionToRespectUserSelectAll(targetNode, visiblePos);
657

658
    bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity);
659 660 661 662 663 664

    if (event.event().button() == MiddleButton) {
        // Ignore handled, since we want to paste to where the caret was placed anyway.
        handled = handlePasteGlobalSelection(event.event()) || handled;
    }
    return handled;
665
}
666

667 668 669 670 671
static inline bool canMouseDownStartSelect(Node* node)
{
    if (!node || !node->renderer())
        return true;

672
    return node->canStartSelection() || Position::nodeIsUserSelectAll(node);
darin's avatar
darin committed
673 674
}

aroben's avatar
aroben committed
675
bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
676
{
bolsinga@apple.com's avatar
bolsinga@apple.com committed
677
#if ENABLE(DRAG_SUPPORT)
678
    // Reset drag state.
679
    dragState().source = 0;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
680
#endif
681

682 683
    cancelFakeMouseMoveEvent();

684
    m_frame.document()->updateLayoutIgnorePendingStylesheets();
685

686
    if (ScrollView* scrollView = m_frame.view()) {
weinig@apple.com's avatar
weinig@apple.com committed
687
        if (scrollView->isPointInScrollbarCorner(event.event().position()))
688 689 690
            return false;
    }

darin's avatar
darin committed
691 692 693
    bool singleClick = event.event().clickCount() <= 1;

    // If we got the event back, that must mean it wasn't prevented,
694 695
    // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
    m_mouseDownMayStartSelect = canMouseDownStartSelect(event.targetNode()) && !event.scrollbar();
darin's avatar
darin committed
696
    
bolsinga@apple.com's avatar
bolsinga@apple.com committed
697
#if ENABLE(DRAG_SUPPORT)
darin's avatar
darin committed
698 699
    // Careful that the drag starting logic stays in sync with eventMayStartDrag()
    m_mouseDownMayStartDrag = singleClick;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
700
#endif
darin's avatar
darin committed
701

darin's avatar
darin committed
702 703
    m_mouseDownWasSingleClickInSelection = false;

704 705
    m_mouseDown = event.event();

706
    if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
aroben's avatar
aroben committed
707
        return true;
darin's avatar
darin committed
708

weinig's avatar
weinig committed
709
#if ENABLE(SVG)
710 711
    if (m_frame.document()->isSVGDocument()
        && toSVGDocument(m_frame.document())->zoomAndPanEnabled()) {
weinig's avatar
weinig committed
712 713
        if (event.event().shiftKey() && singleClick) {
            m_svgPan = true;
714
            toSVGDocument(m_frame.document())->startPan(m_frame.view()->windowToContents(event.event().position()));
weinig's avatar
weinig committed
715 716 717 718 719
            return true;
        }
    }
#endif

darin's avatar
darin committed
720 721 722 723 724
    // We don't do this at the start of mouse down handling,
    // because we don't want to do it until we know we didn't hit a widget.
    if (singleClick)
        focusDocumentView();

725
    m_mousePressNode = event.targetNode();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
726
#if ENABLE(DRAG_SUPPORT)
weinig@apple.com's avatar
weinig@apple.com committed
727
    m_dragStartPos = event.event().position();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
728
#endif
darin's avatar
darin committed
729

aroben's avatar
aroben committed
730
    bool swallowEvent = false;
ap@webkit.org's avatar
ap@webkit.org committed
731
    m_mousePressed = true;
732
    m_selectionInitiationState = HaveNotStartedSelection;
ap@webkit.org's avatar
ap@webkit.org committed
733 734 735 736 737 738 739

    if (event.event().clickCount() == 2)
        swallowEvent = handleMousePressEventDoubleClick(event);
    else if (event.event().clickCount() >= 3)
        swallowEvent = handleMousePressEventTripleClick(event);
    else
        swallowEvent = handleMousePressEventSingleClick(event);
darin's avatar
darin committed
740
    
741
    m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
742
        || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
aroben's avatar
aroben committed
743

744
    return swallowEvent;
darin's avatar
darin committed
745 746
}

bolsinga@apple.com's avatar
bolsinga@apple.com committed
747
#if ENABLE(DRAG_SUPPORT)
748
bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
749
{
ap's avatar
ap committed
750
    if (!m_mousePressed)
aroben's avatar
aroben committed
751
        return false;
darin's avatar
darin committed
752

753 754 755
    if (handleDrag(event, ShouldCheckDragHysteresis))
        return true;

756
    Node* targetNode = event.targetNode();
757
    if (event.event().button() != LeftButton || !targetNode)
aroben's avatar
aroben committed
758
        return false;
darin's avatar
darin committed
759

760 761
    RenderObject* renderer = targetNode->renderer();
    if (!renderer) {
akling@apple.com's avatar
akling@apple.com committed
762
        Element* parent = targetNode->parentOrShadowHostElement();
763 764 765 766
        if (!parent)
            return false;

        renderer = parent->renderer();
767 768 769 770
        if (!renderer || !renderer->isListBox())
            return false;
    }

darin's avatar
darin committed
771 772 773 774 775 776
#if PLATFORM(MAC) // FIXME: Why does this assertion fire on other platforms?
    ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
#endif

    m_mouseDownMayStartDrag = false;

777 778
    if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
        m_autoscrollController->startAutoscrollForSelection(renderer);
779
        m_mouseDownMayStartAutoscroll = false;
darin's avatar
darin committed
780
    }
781

782
    if (m_selectionInitiationState != ExtendedSelection) {
783
        HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent);
784
        HitTestResult result(m_mouseDownPos);
785
        m_frame.document()->renderView()->hitTest(request, result);
786 787

        updateSelectionForMouseDrag(result);
788
    }
789
    updateSelectionForMouseDrag(event.hitTestResult());
aroben's avatar
aroben committed
790
    return true;
darin's avatar
darin committed
791
}
oliver's avatar
 
oliver committed
792 793 794 795 796 797 798
    
bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const
{
    // This is a pre-flight check of whether the event might lead to a drag being started.  Be careful
    // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag
    // in handleMousePressEvent
    
799
    if (!m_frame.contentRenderer() || !m_frame.contentRenderer()->hasLayer())
800 801 802
        return false;

    if (event.button() != LeftButton || event.clickCount() != 1)
oliver's avatar
 
oliver committed
803 804
        return false;
    
805
    FrameView* view = m_frame.view();
806 807 808
    if (!view)
        return false;

809
    Page* page = m_frame.page();
810 811 812
    if (!page)
        return false;

813
    updateDragSourceActionsAllowed();
814
    HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::DisallowShadowContent);
weinig@apple.com's avatar
weinig@apple.com committed
815
    HitTestResult result(view->windowToContents(event.position()));
816
    m_frame.contentRenderer()->hitTest(request, result);
817
    DragState state;
818
    return result.innerElement() && page->dragController().draggableElement(&m_frame, result.innerElement(), result.roundedPointInInnerNodeFrame(), state);
oliver's avatar
 
oliver committed
819
}
darin's avatar
darin committed
820

darin's avatar
darin committed
821 822
void EventHandler::updateSelectionForMouseDrag()
{
823
    FrameView* view = m_frame.view();
darin's avatar
darin committed
824 825
    if (!view)
        return;
826
    RenderView* renderer = m_frame.contentRenderer();
darin's avatar
darin committed
827 828 829
    if (!renderer)
        return;

830
    HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowShadowContent);
831
    HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
832
    renderer->hitTest(request, result);
833
    updateSelectionForMouseDrag(result);
darin's avatar
darin committed
834 835
}

836
static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode)
837
{
838
    LayoutPoint selectionEndPoint = localPoint;
839 840
    Element* editableElement = selection.rootEditableElement();

841 842
    if (!targetNode->renderer())
        return VisiblePosition();
843

844 845
    if (editableElement && !editableElement->contains(targetNode)) {
        if (!editableElement->renderer())
846 847 848
            return VisiblePosition();

        FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
849
        selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint));
850
        targetNode = editableElement;
851 852
    }

853
    return targetNode->renderer()->positionForPoint(selectionEndPoint);
854 855
}

856
void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
darin's avatar
darin committed
857
{
darin's avatar
darin committed
858 859 860
    if (!m_mouseDownMayStartSelect)
        return;

861
    Node* target = hitTestResult.targetNode();
862
    if (!target)
darin's avatar
darin committed
863 864
        return;

865
    VisiblePosition targetPosition = selectionExtentRespectingEditingBoundary(m_frame.selection().selection(), hitTestResult.localPoint(), target);
darin's avatar
darin committed
866

darin's avatar
darin committed
867
    // Don't modify the selection if we're not on a node.
darin's avatar
darin committed
868
    if (targetPosition.isNull())
darin's avatar
darin committed
869 870 871 872
        return;

    // Restart the selection if this is the first mouse move. This work is usually
    // done in handleMousePressEvent, but not if the mouse press was on an existing selection.
873
    VisibleSelection newSelection = m_frame.selection().selection();
darin's avatar
darin committed
874 875 876 877

#if ENABLE(SVG)
    // Special case to limit selection to the containing block for SVG text.
    // FIXME: Isn't there a better non-SVG-specific way to do this?
878
    if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
darin's avatar
darin committed
879 880
        if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
            if (selectionBaseRenderer->isSVGText())
881
                if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
darin's avatar
darin committed
882 883 884
                    return;
#endif

885 886 887 888 889 890
    if (m_selectionInitiationState == HaveNotStartedSelection && !dispatchSelectStart(target))
        return;

    if (m_selectionInitiationState != ExtendedSelection) {
        // Always extend selection here because it's caused by a mouse drag
        m_selectionInitiationState = ExtendedSelection;