EventHandler.cpp 143 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

95 96 97 98
#if ENABLE(TOUCH_ADJUSTMENT)
#include "TouchAdjustment.h"
#endif

mjs's avatar
mjs committed
99
#if ENABLE(SVG)
weinig's avatar
weinig committed
100
#include "SVGDocument.h"
101
#include "SVGElementInstance.h"
darin's avatar
darin committed
102
#include "SVGNames.h"
103
#include "SVGUseElement.h"
darin's avatar
darin committed
104 105
#endif

106
#if ENABLE(TOUCH_EVENTS)
107 108 109
#if ENABLE(IOS_TOUCH_EVENTS)
#include "PlatformTouchEventIOS.h"
#else
110
#include "PlatformTouchEvent.h"
111
#endif
112
#include "TouchEvent.h"
113
#include "TouchList.h"
114 115
#endif

116 117 118 119
#if ENABLE(CSS_IMAGE_SET)
#include "StyleCachedImageSet.h"
#endif

darin's avatar
darin committed
120 121 122 123
namespace WebCore {

using namespace HTMLNames;

bolsinga@apple.com's avatar
bolsinga@apple.com committed
124
#if ENABLE(DRAG_SUPPORT)
aliceli1's avatar
aliceli1 committed
125 126 127
// 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
128 129 130 131
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
132
#endif // ENABLE(DRAG_SUPPORT)
aliceli1's avatar
aliceli1 committed
133

134 135 136 137 138 139 140 141 142
#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
143 144 145 146
// 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
147
#if ENABLE(SVG)
darin's avatar
darin committed
148 149 150
using namespace SVGNames;
#endif

151
// The amount of time to wait before sending a fake mouse event, triggered
152 153 154
// 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;
155
const double fakeMouseMoveShortInterval = 0.1;
156
const double fakeMouseMoveLongInterval = 0.25;
157

158
#if ENABLE(CURSOR_SUPPORT)
159 160 161 162
// 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;

163
const int maximumCursorSize = 128;
164 165
#endif

166 167 168 169 170 171 172
#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

173 174 175 176 177 178 179 180
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; }
181
    const Cursor& cursor() const { ASSERT(m_isCursorChange); return m_cursor; }
182 183 184 185 186 187

private:
    bool m_isCursorChange;
    Cursor m_cursor;
};

188
class MaximumDurationTracker {
189
public:
190 191
    explicit MaximumDurationTracker(double *maxDuration)
        : m_maxDuration(maxDuration)
192 193 194 195
        , m_start(monotonicallyIncreasingTime())
    {
    }

196
    ~MaximumDurationTracker()
197
    {
198
        *m_maxDuration = std::max(*m_maxDuration, monotonicallyIncreasingTime() - m_start);
199 200 201
    }

private:
202
    double* m_maxDuration;
203 204 205
    double m_start;
};

206
#if ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
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 267 268 269 270
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));
    }
};
271
#endif // ENABLE(TOUCH_EVENTS) && !ENABLE(IOS_TOUCH_EVENTS)
272

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

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

298
#if (ENABLE(TOUCH_EVENTS) && !PLATFORM(IOS))
299 300 301 302 303 304 305
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;
}
306
#endif
307

308
#if !PLATFORM(MAC)
darin@apple.com's avatar
darin@apple.com committed
309 310 311 312 313 314

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

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

#endif

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

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

458 459 460 461 462 463
void EventHandler::nodeWillBeRemoved(Node* nodeToBeRemoved)
{
    if (nodeToBeRemoved->contains(m_clickNode.get()))
        m_clickNode = 0;
}

464
static void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& newSelection)
465
{
466 467
    if (selection.selection() != newSelection && selection.shouldChangeSelection(newSelection))
        selection.setSelection(newSelection);
468 469
}

470 471 472 473 474 475 476 477
static inline bool dispatchSelectStart(Node* node)
{
    if (!node || !node->renderer())
        return true;

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

478 479 480 481 482 483 484 485 486 487 488 489 490
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
491
    UNUSED_PARAM(targetNode);
492 493 494 495 496
    return selection;
#endif
}

bool EventHandler::updateSelectionForMouseDownDispatchingSelectStart(Node* targetNode, const VisibleSelection& selection, TextGranularity granularity)
497
{
498 499 500
    if (Position::nodeIsUserSelectNone(targetNode))
        return false;

501 502 503
    if (!dispatchSelectStart(targetNode))
        return false;

504
    if (selection.isRange())
505 506 507 508 509 510
        m_selectionInitiationState = ExtendedSelection;
    else {
        granularity = CharacterGranularity;
        m_selectionInitiationState = PlacedCaret;
    }

511
    m_frame.selection().setNonDirectionalSelectionIfNeeded(selection, granularity);
512 513 514 515

    return true;
}

516
void EventHandler::selectClosestWordFromHitTestResult(const HitTestResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
darin's avatar
darin committed
517
{
518
    Node* targetNode = result.targetNode();
519
    VisibleSelection newSelection;
darin's avatar
darin committed
520

521 522
    if (targetNode && targetNode->renderer()) {
        VisiblePosition pos(targetNode->renderer()->positionForPoint(result.localPoint()));
darin's avatar
darin committed
523
        if (pos.isNotNull()) {
524
            newSelection = VisibleSelection(pos);
darin's avatar
darin committed
525 526
            newSelection.expandUsingGranularity(WordGranularity);
        }
527

528
        if (appendTrailingWhitespace == ShouldAppendTrailingWhitespace && newSelection.isRange())
529 530
            newSelection.appendTrailingWhitespace();

531
        updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity);
darin's avatar
darin committed
532 533 534
    }
}

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

mitz@apple.com's avatar
mitz@apple.com committed
543 544 545 546 547
void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHitTestResults& result)
{
    if (!result.hitTestResult().isLiveLink())
        return selectClosestWordFromMouseEvent(result);

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

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

557
        updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), WordGranularity);
mitz@apple.com's avatar
mitz@apple.com committed
558 559 560
    }
}

aroben's avatar
aroben committed
561
bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestResults& event)
darin's avatar
darin committed
562
{
aroben's avatar
aroben committed
563 564 565
    if (event.event().button() != LeftButton)
        return false;

566
    if (m_frame.selection().isRange())
aroben's avatar
aroben committed
567 568 569 570 571
        // 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.
572
        m_selectionInitiationState = ExtendedSelection;
aroben's avatar
aroben committed
573
    else
574
        selectClosestWordFromMouseEvent(event);
aroben's avatar
aroben committed
575 576

    return true;
darin's avatar
darin committed
577 578
}

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

588
    VisibleSelection newSelection;
589
    VisiblePosition pos(targetNode->renderer()->positionForPoint(event.localPoint()));
aroben's avatar
aroben committed
590
    if (pos.isNotNull()) {
591
        newSelection = VisibleSelection(pos);
aroben's avatar
aroben committed
592
        newSelection.expandUsingGranularity(ParagraphGranularity);
darin's avatar
darin committed
593
    }
aroben's avatar
aroben committed
594

595
    return updateSelectionForMouseDownDispatchingSelectStart(targetNode, expandSelectionToRespectUserSelectAll(targetNode, newSelection), ParagraphGranularity);
darin's avatar
darin committed
596 597
}

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

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

aroben's avatar
aroben committed
611 612
    // 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
613

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

624
    VisiblePosition visiblePos(targetNode->renderer()->positionForPoint(event.localPoint()));
aroben's avatar
aroben committed
625
    if (visiblePos.isNull())
626
        visiblePos = VisiblePosition(firstPositionInOrBeforeNode(targetNode), DOWNSTREAM);
aroben's avatar
aroben committed
627
    Position pos = visiblePos.deepEquivalent();
628

629
    VisibleSelection newSelection = m_frame.selection().selection();
630 631
    TextGranularity granularity = CharacterGranularity;

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

641
        if (!m_frame.editor().behavior().shouldConsiderSelectionAsDirectional() && pos.isNotNull()) {
642 643 644 645 646 647 648 649 650 651
            // 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);
652
        } else
653
            newSelection.setExtent(pos);
aroben's avatar
aroben committed
654

655 656 657
        if (m_frame.selection().granularity() != CharacterGranularity) {
            granularity = m_frame.selection().granularity();
            newSelection.expandUsingGranularity(m_frame.selection().granularity());
658 659
        }
    } else
660
        newSelection = expandSelectionToRespectUserSelectAll(targetNode, visiblePos);
661

662
    bool handled = updateSelectionForMouseDownDispatchingSelectStart(targetNode, newSelection, granularity);
663 664 665 666 667 668

    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;
669
}
670

671 672 673 674 675
static inline bool canMouseDownStartSelect(Node* node)
{
    if (!node || !node->renderer())
        return true;

676
    return node->canStartSelection() || Position::nodeIsUserSelectAll(node);
darin's avatar
darin committed
677 678
}

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

686 687
    cancelFakeMouseMoveEvent();

688
    m_frame.document()->updateLayoutIgnorePendingStylesheets();
689

690
    if (ScrollView* scrollView = m_frame.view()) {
weinig@apple.com's avatar
weinig@apple.com committed
691
        if (scrollView->isPointInScrollbarCorner(event.event().position()))
692 693 694
            return false;
    }

darin's avatar
darin committed
695 696 697
    bool singleClick = event.event().clickCount() <= 1;

    // If we got the event back, that must mean it wasn't prevented,
698 699
    // 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
700
    
bolsinga@apple.com's avatar
bolsinga@apple.com committed
701
#if ENABLE(DRAG_SUPPORT)
darin's avatar
darin committed
702 703
    // Careful that the drag starting logic stays in sync with eventMayStartDrag()
    m_mouseDownMayStartDrag = singleClick;
bolsinga@apple.com's avatar
bolsinga@apple.com committed
704
#endif
darin's avatar
darin committed
705

darin's avatar
darin committed
706 707
    m_mouseDownWasSingleClickInSelection = false;

708 709
    m_mouseDown = event.event();

710
    if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event))
aroben's avatar
aroben committed
711
        return true;
darin's avatar
darin committed
712

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

darin's avatar
darin committed
724 725 726 727 728
    // 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();

729
    m_mousePressNode = event.targetNode();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
730
#if ENABLE(DRAG_SUPPORT)
weinig@apple.com's avatar
weinig@apple.com committed
731
    m_dragStartPos = event.event().position();
bolsinga@apple.com's avatar
bolsinga@apple.com committed
732
#endif
darin's avatar
darin committed
733

aroben's avatar
aroben committed
734
    bool swallowEvent = false;
ap@webkit.org's avatar
ap@webkit.org committed
735
    m_mousePressed = true;
736
    m_selectionInitiationState = HaveNotStartedSelection;
ap@webkit.org's avatar
ap@webkit.org committed
737 738 739 740 741 742 743

    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
744
    
745
    m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect
746
        || (m_mousePressNode && m_mousePressNode->renderBox() && m_mousePressNode->renderBox()->canBeProgramaticallyScrolled());
aroben's avatar
aroben committed
747

748
    return swallowEvent;
darin's avatar
darin committed
749 750
}

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

757 758 759
    if (handleDrag(event, ShouldCheckDragHysteresis))
        return true;

760
    Node* targetNode = event.targetNode();
761
    if (event.event().button() != LeftButton || !targetNode)
aroben's avatar
aroben committed
762
        return false;
darin's avatar
darin committed
763

764 765
    RenderObject* renderer = targetNode->renderer();
    if (!renderer) {
akling@apple.com's avatar
akling@apple.com committed
766
        Element* parent = targetNode->parentOrShadowHostElement();
767 768 769 770
        if (!parent)
            return false;

        renderer = parent->renderer();
771 772 773 774
        if (!renderer || !renderer->isListBox())
            return false;
    }

darin's avatar
darin committed
775 776 777 778 779 780
#if PLATFORM(MAC) // FIXME: Why does this assertion fire on other platforms?
    ASSERT(m_mouseDownMayStartSelect || m_mouseDownMayStartAutoscroll);
#endif

    m_mouseDownMayStartDrag = false;

781 782
    if (m_mouseDownMayStartAutoscroll && !panScrollInProgress()) {
        m_autoscrollController->startAutoscrollForSelection(renderer);
783
        m_mouseDownMayStartAutoscroll = false;
darin's avatar
darin committed
784
    }
785

786
    if (m_selectionInitiationState != ExtendedSelection) {
787
        HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent);
788
        HitTestResult result(m_mouseDownPos);
789
        m_frame.document()->renderView()->hitTest(request, result);
790 791

        updateSelectionForMouseDrag(result);
792
    }
793
    updateSelectionForMouseDrag(event.hitTestResult());
aroben's avatar
aroben committed
794
    return true;
darin's avatar
darin committed
795
}
oliver's avatar
 
oliver committed
796 797 798 799 800 801 802
    
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
    
803
    if (!m_frame.contentRenderer() || !m_frame.contentRenderer()->hasLayer())
804 805 806
        return false;

    if (event.button() != LeftButton || event.clickCount() != 1)
oliver's avatar
 
oliver committed
807 808
        return false;
    
809
    FrameView* view = m_frame.view();
810 811 812
    if (!view)
        return false;

813
    Page* page = m_frame.page();
814 815 816
    if (!page)
        return false;

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

darin's avatar
darin committed
825 826
void EventHandler::updateSelectionForMouseDrag()
{
827
    FrameView* view = m_frame.view();
darin's avatar
darin committed
828 829
    if (!view)
        return;
830
    RenderView* renderer = m_frame.contentRenderer();
darin's avatar
darin committed
831 832 833
    if (!renderer)
        return;

834
    HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::Move | HitTestRequest::DisallowShadowContent);
835
    HitTestResult result(view->windowToContents(m_lastKnownMousePosition));
836
    renderer->hitTest(request, result);
837
    updateSelectionForMouseDrag(result);
darin's avatar
darin committed
838 839
}

840
static VisiblePosition selectionExtentRespectingEditingBoundary(const VisibleSelection& selection, const LayoutPoint& localPoint, Node* targetNode)
841
{
842
    LayoutPoint selectionEndPoint = localPoint;
843 844
    Element* editableElement = selection.rootEditableElement();

845 846
    if (!targetNode->renderer())
        return VisiblePosition();
847

848 849
    if (editableElement && !editableElement->contains(targetNode)) {
        if (!editableElement->renderer())
850 851 852
            return VisiblePosition();

        FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
853
        selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint));
854
        targetNode = editableElement;
855 856
    }

857
    return targetNode->renderer()->positionForPoint(selectionEndPoint);
858 859
}

860
void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult)
darin's avatar
darin committed
861
{
darin's avatar
darin committed
862 863 864
    if (!m_mouseDownMayStartSelect)
        return;

865
    Node* target = hitTestResult.targetNode();
866
    if (!target)
darin's avatar
darin committed
867 868
        return;

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

darin's avatar
darin committed
871
    // Don't modify the selection if we're not on a node.
darin's avatar
darin committed
872
    if (targetPosition.isNull())
darin's avatar
darin committed
873 874 875 876
        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.
877
    VisibleSelection newSelection = m_frame.selection().selection();
darin's avatar
darin committed
878 879 880 881

#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?
882
    if (Node* selectionBaseNode = newSelection.base().deprecatedNode())
darin's avatar
darin committed
883 884
        if (RenderObject* selectionBaseRenderer = selectionBaseNode->renderer())
            if (selectionBaseRenderer->isSVGText())
885
                if (target->renderer()->containingBlock() != selectionBaseRenderer->containingBlock())
darin's avatar
darin committed
886 887 888
                    return;
#endif