diff options
Diffstat (limited to 'WebCore/page/EventHandler.cpp')
-rw-r--r-- | WebCore/page/EventHandler.cpp | 788 |
1 files changed, 629 insertions, 159 deletions
diff --git a/WebCore/page/EventHandler.cpp b/WebCore/page/EventHandler.cpp index 4ecbe13..3f45b92 100644 --- a/WebCore/page/EventHandler.cpp +++ b/WebCore/page/EventHandler.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "EventHandler.h" +#include "AXObjectCache.h" #include "CachedImage.h" #include "ChromeClient.h" #include "Cursor.h" @@ -35,6 +36,7 @@ #include "Editor.h" #include "EventNames.h" #include "FloatPoint.h" +#include "FloatRect.h" #include "FocusController.h" #include "Frame.h" #include "FrameLoader.h" @@ -47,28 +49,35 @@ #include "HTMLInputElement.h" #include "HTMLNames.h" #include "Image.h" +#include "InspectorController.h" #include "KeyboardEvent.h" #include "MouseEvent.h" #include "MouseEventWithHitTestResults.h" #include "Page.h" #include "PlatformKeyboardEvent.h" -#include "PlatformScrollBar.h" #include "PlatformWheelEvent.h" +#include "RenderFrameSet.h" #include "RenderWidget.h" +#include "RenderView.h" +#include "Scrollbar.h" #include "SelectionController.h" #include "Settings.h" #include "TextEvent.h" #if ENABLE(SVG) -#include "SVGCursorElement.h" #include "SVGDocument.h" -#include "SVGLength.h" +#include "SVGElementInstance.h" #include "SVGNames.h" +#include "SVGUseElement.h" +#endif + +#if ENABLE(TOUCH_EVENTS) // Android +#include "TouchEvent.h" +#include "PlatformTouchEvent.h" #endif namespace WebCore { -using namespace EventNames; using namespace HTMLNames; // The link drag hysteresis is much larger than the others because there @@ -78,7 +87,6 @@ const int LinkDragHysteresis = 40; const int ImageDragHysteresis = 5; const int TextDragHysteresis = 3; const int GeneralDragHysteresis = 3; -const double TextDragDelay = 0.15; // Match key code of composition keydown event on windows. // IE sends VK_PROCESSKEY which has value 229; @@ -88,9 +96,27 @@ const int CompositionEventKeyCode = 229; using namespace SVGNames; #endif -const double autoscrollInterval = 0.1; +// When the autoscroll or the panScroll is triggered when do the scroll every 0.05s to make it smooth +const double autoscrollInterval = 0.05; -static Frame* subframeForTargetNode(Node* node); +static Frame* subframeForTargetNode(Node*); +static Frame* subframeForHitTestResult(const MouseEventWithHitTestResults&); + +static inline void scrollAndAcceptEvent(float delta, ScrollDirection positiveDirection, ScrollDirection negativeDirection, PlatformWheelEvent& e, Node* node) +{ + if (!delta) + return; + if (e.granularity() == ScrollByPageWheelEvent) { + if (node->renderer()->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPage, 1)) + e.accept(); + return; + } + float pixelsToScroll = delta > 0 ? delta : -delta; + if (e.granularity() == ScrollByLineWheelEvent) + pixelsToScroll *= cMouseWheelPixelsPerLineStep; + if (node->renderer()->scroll(delta < 0 ? negativeDirection : positiveDirection, ScrollByPixel, pixelsToScroll)) + e.accept(); +} EventHandler::EventHandler(Frame* frame) : m_frame(frame) @@ -99,9 +125,11 @@ EventHandler::EventHandler(Frame* frame) , m_mouseDownMayStartDrag(false) , m_mouseDownWasSingleClickInSelection(false) , m_beganSelectingText(false) + , m_panScrollInProgress(false) , m_hoverTimer(this, &EventHandler::hoverTimerFired) , m_autoscrollTimer(this, &EventHandler::autoscrollTimerFired) , m_autoscrollRenderer(0) + , m_autoscrollInProgress(false) , m_mouseDownMayStartAutoscroll(false) , m_mouseDownWasInSubframe(false) #if ENABLE(SVG) @@ -111,6 +139,8 @@ EventHandler::EventHandler(Frame* frame) , m_capturingMouseEventsNode(0) , m_clickCount(0) , m_mouseDownTimestamp(0) + , m_pendingFrameUnloadEventCount(0) + , m_pendingFrameBeforeUnloadEventCount(0) #if PLATFORM(MAC) , m_mouseDownView(nil) , m_sendingEventToSubview(false) @@ -135,10 +165,17 @@ void EventHandler::clear() m_resizeLayer = 0; m_nodeUnderMouse = 0; m_lastNodeUnderMouse = 0; +#if ENABLE(SVG) + m_instanceUnderMouse = 0; + m_lastInstanceUnderMouse = 0; +#endif m_lastMouseMoveEventSubframe = 0; m_lastScrollbarUnderMouse = 0; m_clickCount = 0; m_clickNode = 0; +#if ENABLE(TOUCH_EVENTS) // Android + m_touch = 0; +#endif m_frameSetBeingResized = 0; m_dragTarget = 0; m_currentMousePosition = IntPoint(); @@ -165,7 +202,7 @@ void EventHandler::selectClosestWordFromMouseEvent(const MouseEventWithHitTestRe } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); } } @@ -189,7 +226,7 @@ void EventHandler::selectClosestWordOrLinkFromMouseEvent(const MouseEventWithHit } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); } } @@ -198,7 +235,7 @@ bool EventHandler::handleMousePressEventDoubleClick(const MouseEventWithHitTestR if (event.event().button() != LeftButton) return false; - if (m_frame->selectionController()->isRange()) + if (m_frame->selection()->isRange()) // A double-click when range is already selected // should not change the selection. So, do not call // selectClosestWordFromMouseEvent, but do set @@ -232,7 +269,7 @@ bool EventHandler::handleMousePressEventTripleClick(const MouseEventWithHitTestR } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); return true; } @@ -252,7 +289,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR // Don't restart the selection when the mouse is pressed on an // existing selection so we can allow for text dragging. IntPoint vPoint = m_frame->view()->windowToContents(event.event().pos()); - if (!extendSelection && m_frame->selectionController()->contains(vPoint)) { + if (!extendSelection && m_frame->selection()->contains(vPoint)) { m_mouseDownWasSingleClickInSelection = true; return false; } @@ -262,9 +299,9 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR visiblePos = VisiblePosition(innerNode, 0, DOWNSTREAM); Position pos = visiblePos.deepEquivalent(); - Selection newSelection = m_frame->selectionController()->selection(); + Selection newSelection = m_frame->selection()->selection(); if (extendSelection && newSelection.isCaretOrRange()) { - m_frame->selectionController()->setLastChangeWasHorizontalExtension(false); + m_frame->selection()->setLastChangeWasHorizontalExtension(false); // See <rdar://problem/3668157> REGRESSION (Mail): shift-click deselects when selection // was created right-to-left @@ -285,7 +322,7 @@ bool EventHandler::handleMousePressEventSingleClick(const MouseEventWithHitTestR } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); return true; } @@ -306,7 +343,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve m_mouseDownWasSingleClickInSelection = false; - if (passWidgetMouseDownEventToWidget(event)) + if (event.isOverWidget() && passWidgetMouseDownEventToWidget(event)) return true; #if ENABLE(SVG) @@ -332,7 +369,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve bool swallowEvent = false; if (event.event().button() == LeftButton || event.event().button() == MiddleButton) { - m_frame->selectionController()->setCaretBlinkingSuspended(true); + m_frame->selection()->setCaretBlinkingSuspended(true); m_mousePressed = true; m_beganSelectingText = false; @@ -345,7 +382,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve } m_mouseDownMayStartAutoscroll = m_mouseDownMayStartSelect || - (m_mousePressNode && m_mousePressNode->renderer() && m_mousePressNode->renderer()->shouldAutoscroll()); + (m_mousePressNode && m_mousePressNode->renderer() && m_mousePressNode->renderer()->canBeProgramaticallyScrolled(true)); return swallowEvent; } @@ -368,14 +405,23 @@ bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& e m_mouseDownMayStartDrag = false; - if (m_mouseDownMayStartAutoscroll) { + if (m_mouseDownMayStartAutoscroll && !m_panScrollInProgress) { // If the selection is contained in a layer that can scroll, that layer should handle the autoscroll // Otherwise, let the bridge handle it so the view can scroll itself. RenderObject* renderer = targetNode->renderer(); - while (renderer && !renderer->shouldAutoscroll()) - renderer = renderer->parent(); - if (renderer) + while (renderer && !renderer->canBeProgramaticallyScrolled(false)) { + if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) + renderer = renderer->document()->ownerElement()->renderer(); + else + renderer = renderer->parent(); + } + + if (renderer) { + m_autoscrollInProgress = true; handleAutoscroll(renderer); + } + + m_mouseDownMayStartAutoscroll = false; } updateSelectionForMouseDrag(targetNode, event.localPoint()); @@ -388,7 +434,7 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const // that its logic needs to stay in sync with handleMouseMoveEvent() and the way we setMouseDownMayStartDrag // in handleMousePressEvent - if (!m_frame->renderer() || !m_frame->renderer()->hasLayer() + if (!m_frame->contentRenderer() || !m_frame->contentRenderer()->hasLayer() || event.button() != LeftButton || event.clickCount() != 1) return false; @@ -400,7 +446,7 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const HitTestRequest request(true, false); HitTestResult result(m_frame->view()->windowToContents(event.pos())); - m_frame->renderer()->layer()->hitTest(request, result); + m_frame->contentRenderer()->layer()->hitTest(request, result); bool srcIsDHTML; return result.innerNode() && result.innerNode()->renderer()->draggableNode(DHTMLFlag, UAFlag, result.point().x(), result.point().y(), srcIsDHTML); } @@ -410,7 +456,7 @@ void EventHandler::updateSelectionForMouseDrag() FrameView* view = m_frame->view(); if (!view) return; - RenderObject* renderer = m_frame->renderer(); + RenderObject* renderer = m_frame->contentRenderer(); if (!renderer) return; RenderLayer* layer = renderer->layer(); @@ -445,7 +491,7 @@ void EventHandler::updateSelectionForMouseDrag(Node* targetNode, const IntPoint& // 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. - Selection newSelection = m_frame->selectionController()->selection(); + Selection newSelection = m_frame->selection()->selection(); #if ENABLE(SVG) // Special case to limit selection to the containing block for SVG text. @@ -467,8 +513,8 @@ void EventHandler::updateSelectionForMouseDrag(Node* targetNode, const IntPoint& newSelection.expandUsingGranularity(m_frame->selectionGranularity()); if (m_frame->shouldChangeSelection(newSelection)) { - m_frame->selectionController()->setLastChangeWasHorizontalExtension(false); - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setLastChangeWasHorizontalExtension(false); + m_frame->selection()->setSelection(newSelection); } } @@ -487,14 +533,15 @@ bool EventHandler::handleMouseUp(const MouseEventWithHitTestResults& event) bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& event) { - stopAutoscrollTimer(); + if (m_autoscrollInProgress) + stopAutoscrollTimer(); if (handleMouseUp(event)) return true; // Used to prevent mouseMoveEvent from initiating a drag before // the mouse is pressed again. - m_frame->selectionController()->setCaretBlinkingSuspended(false); + m_frame->selection()->setCaretBlinkingSuspended(false); m_mousePressed = false; m_mouseDownMayStartDrag = false; m_mouseDownMayStartSelect = false; @@ -508,7 +555,7 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e // However, if we are editing, place the caret. if (m_mouseDownWasSingleClickInSelection && !m_beganSelectingText && m_dragStartPos == event.event().pos() - && m_frame->selectionController()->isRange()) { + && m_frame->selection()->isRange()) { Selection newSelection; Node *node = event.targetNode(); if (node && node->isContentEditable() && node->renderer()) { @@ -516,34 +563,96 @@ bool EventHandler::handleMouseReleaseEvent(const MouseEventWithHitTestResults& e newSelection = Selection(pos); } if (m_frame->shouldChangeSelection(newSelection)) - m_frame->selectionController()->setSelection(newSelection); + m_frame->selection()->setSelection(newSelection); handled = true; } m_frame->notifyRendererOfSelectionChange(true); - m_frame->selectionController()->selectFrameElementInParentIfFullySelected(); + m_frame->selection()->selectFrameElementInParentIfFullySelected(); return handled; } void EventHandler::handleAutoscroll(RenderObject* renderer) { + // We don't want to trigger the autoscroll or the panScroll if it's already active if (m_autoscrollTimer.isActive()) - return; + return; + setAutoscrollRenderer(renderer); + +#if ENABLE(PAN_SCROLLING) + if (m_panScrollInProgress) { + m_panScrollStartPos = currentMousePosition(); + m_frame->view()->addPanScrollIcon(m_panScrollStartPos); + // If we're not in the top frame we notify it that we are using the panScroll + if (m_frame != m_frame->page()->mainFrame()) + m_frame->page()->mainFrame()->eventHandler()->setPanScrollInProgress(true); + } +#endif + startAutoscrollTimer(); } void EventHandler::autoscrollTimerFired(Timer<EventHandler>*) { - if (!m_mousePressed) { + RenderObject* r = autoscrollRenderer(); + if (!r) { stopAutoscrollTimer(); return; } - if (RenderObject* r = autoscrollRenderer()) + + if (m_autoscrollInProgress) { + if (!m_mousePressed) { + stopAutoscrollTimer(); + return; + } r->autoscroll(); + } else { + // we verify that the main frame hasn't received the order to stop the panScroll + if (!m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress()) { + stopAutoscrollTimer(); + return; + } +#if ENABLE(PAN_SCROLLING) + setPanScrollCursor(); + r->panScroll(m_panScrollStartPos); +#endif + } +} + +void EventHandler::setPanScrollCursor() +{ + // At the original click location we draw a 4 arrowed icon. Over this icon there won't be any scroll + // So we don't want to change the cursor over this area + const int noScrollRadius = 9; + bool east = m_panScrollStartPos.x() < (m_currentMousePosition.x() - noScrollRadius); + bool west = m_panScrollStartPos.x() > (m_currentMousePosition.x() + noScrollRadius); + bool north = m_panScrollStartPos.y() > (m_currentMousePosition.y() + noScrollRadius); + bool south = m_panScrollStartPos.y() < (m_currentMousePosition.y() - noScrollRadius); + + if (north) { + if (east) + m_frame->view()->setCursor(northEastPanningCursor()); + else if (west) + m_frame->view()->setCursor(northWestPanningCursor()); + else + m_frame->view()->setCursor(northPanningCursor()); + } else if (south) { + if (east) + m_frame->view()->setCursor(southEastPanningCursor()); + else if (west) + m_frame->view()->setCursor(southWestPanningCursor()); + else + m_frame->view()->setCursor(southPanningCursor()); + } else if (east) + m_frame->view()->setCursor(eastPanningCursor()); + else if (west) + m_frame->view()->setCursor(westPanningCursor()); + else + m_frame->view()->setCursor(middlePanningCursor()); } RenderObject* EventHandler::autoscrollRenderer() const @@ -551,6 +660,20 @@ RenderObject* EventHandler::autoscrollRenderer() const return m_autoscrollRenderer; } +void EventHandler::updateAutoscrollRenderer() +{ + if (!m_autoscrollRenderer) + return; + + HitTestResult hitTest = hitTestResultAtPoint(m_panScrollStartPos, true); + + if (Node* nodeAtPoint = hitTest.innerNode()) + m_autoscrollRenderer = nodeAtPoint->renderer(); + + while (m_autoscrollRenderer && !m_autoscrollRenderer->canBeProgramaticallyScrolled(false)) + m_autoscrollRenderer = m_autoscrollRenderer->parent(); +} + void EventHandler::setAutoscrollRenderer(RenderObject* renderer) { m_autoscrollRenderer = renderer; @@ -561,6 +684,7 @@ void EventHandler::allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const if (!m_frame || !m_frame->document()) { flagDHTML = false; flagUA = false; + return; } unsigned mask = m_frame->page()->dragController()->delegateDragSourceAction(m_frame->view()->contentsToWindow(m_mouseDownPos)); @@ -571,27 +695,37 @@ void EventHandler::allowDHTMLDrag(bool& flagDHTML, bool& flagUA) const HitTestResult EventHandler::hitTestResultAtPoint(const IntPoint& point, bool allowShadowContent) { HitTestResult result(point); - if (!m_frame->renderer()) + if (!m_frame->contentRenderer()) return result; - m_frame->renderer()->layer()->hitTest(HitTestRequest(true, true), result); + m_frame->contentRenderer()->layer()->hitTest(HitTestRequest(true, true), result); while (true) { Node* n = result.innerNode(); - if (!n || !n->renderer() || !n->renderer()->isWidget()) + if (!result.isOverWidget() || !n || !n->renderer() || !n->renderer()->isWidget()) break; Widget* widget = static_cast<RenderWidget*>(n->renderer())->widget(); if (!widget || !widget->isFrameView()) break; Frame* frame = static_cast<HTMLFrameElementBase*>(n)->contentFrame(); - if (!frame || !frame->renderer()) + if (!frame || !frame->contentRenderer()) break; FrameView* view = static_cast<FrameView*>(widget); - IntPoint widgetPoint(result.localPoint().x() + view->contentsX() - n->renderer()->borderLeft() - n->renderer()->paddingLeft(), - result.localPoint().y() + view->contentsY() - n->renderer()->borderTop() - n->renderer()->paddingTop()); + IntPoint widgetPoint(result.localPoint().x() + view->scrollX() - n->renderer()->borderLeft() - n->renderer()->paddingLeft(), + result.localPoint().y() + view->scrollY() - n->renderer()->borderTop() - n->renderer()->paddingTop()); HitTestResult widgetHitTestResult(widgetPoint); - frame->renderer()->layer()->hitTest(HitTestRequest(true, true), widgetHitTestResult); + frame->contentRenderer()->layer()->hitTest(HitTestRequest(true, true), widgetHitTestResult); result = widgetHitTestResult; } + + // If our HitTestResult is not visible, then we started hit testing too far down the frame chain. + // Another hit test at the main frame level should get us the correct visible result. + Frame* resultFrame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document()->frame() : 0; + Frame* mainFrame = m_frame->page()->mainFrame(); + if (m_frame != mainFrame && resultFrame && resultFrame != mainFrame && !resultFrame->editor()->insideVisibleArea(result.point())) { + IntPoint windowPoint = resultFrame->view()->contentsToWindow(result.point()); + IntPoint mainFramePoint = mainFrame->view()->windowToContents(windowPoint); + result = mainFrame->eventHandler()->hitTestResultAtPoint(mainFramePoint, allowShadowContent); + } if (!allowShadowContent) result.setToNonShadowAncestor(); @@ -607,16 +741,34 @@ void EventHandler::startAutoscrollTimer() void EventHandler::stopAutoscrollTimer(bool rendererIsBeingDestroyed) { - if (m_mouseDownWasInSubframe) { - if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get())) - subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed); - return; + if (m_autoscrollInProgress) { + if (m_mouseDownWasInSubframe) { + if (Frame* subframe = subframeForTargetNode(m_mousePressNode.get())) + subframe->eventHandler()->stopAutoscrollTimer(rendererIsBeingDestroyed); + return; + } + } + + if (autoscrollRenderer()) { + if (!rendererIsBeingDestroyed && (m_autoscrollInProgress || m_panScrollInProgress)) + autoscrollRenderer()->stopAutoscroll(); +#if ENABLE(PAN_SCROLLING) + if (m_panScrollInProgress) { + m_frame->view()->removePanScrollIcon(); + m_frame->view()->setCursor(pointerCursor()); + } +#endif + + setAutoscrollRenderer(0); } - if (!rendererIsBeingDestroyed && autoscrollRenderer()) - autoscrollRenderer()->stopAutoscroll(); - setAutoscrollRenderer(0); m_autoscrollTimer.stop(); + + m_panScrollInProgress = false; + // If we're not in the top frame we notify it that we are not using the panScroll anymore + if (m_frame->page() && m_frame != m_frame->page()->mainFrame()) + m_frame->page()->mainFrame()->eventHandler()->setPanScrollInProgress(false); + m_autoscrollInProgress = false; } Node* EventHandler::mousePressNode() const @@ -652,6 +804,13 @@ IntPoint EventHandler::currentMousePosition() const return m_currentMousePosition; } +Frame* subframeForHitTestResult(const MouseEventWithHitTestResults& hitTestResult) +{ + if (!hitTestResult.isOverWidget()) + return 0; + return subframeForTargetNode(hitTestResult.targetNode()); +} + Frame* subframeForTargetNode(Node* node) { if (!node) @@ -677,37 +836,42 @@ static bool isSubmitImage(Node* node) // Returns true if the node's editable block is not current focused for editing static bool nodeIsNotBeingEdited(Node* node, Frame* frame) { - return frame->selectionController()->rootEditableElement() != node->rootEditableElement(); + return frame->selection()->rootEditableElement() != node->rootEditableElement(); } -Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, PlatformScrollbar* scrollbar) +Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, Scrollbar* scrollbar) { // During selection, use an I-beam no matter what we're over. // If you're capturing mouse events for a particular node, don't treat this as a selection. - if (m_mousePressed && m_mouseDownMayStartSelect && m_frame->selectionController()->isCaretOrRange() && !m_capturingMouseEventsNode) + if (m_mousePressed && m_mouseDownMayStartSelect && m_frame->selection()->isCaretOrRange() && !m_capturingMouseEventsNode) return iBeamCursor(); Node* node = event.targetNode(); RenderObject* renderer = node ? node->renderer() : 0; RenderStyle* style = renderer ? renderer->style() : 0; + if (renderer && renderer->isFrameSet()) { + RenderFrameSet* fs = static_cast<RenderFrameSet*>(renderer); + if (fs->canResizeRow(event.localPoint())) + return rowResizeCursor(); + if (fs->canResizeColumn(event.localPoint())) + return columnResizeCursor(); + } + if (style && style->cursors()) { const CursorList* cursors = style->cursors(); for (unsigned i = 0; i < cursors->size(); ++i) { - CachedImage* cimage = (*cursors)[i].cursorImage; + CachedImage* cimage = (*cursors)[i].cursorImage.get(); IntPoint hotSpot = (*cursors)[i].hotSpot; -#if ENABLE(SVG) - if (!cimage) { - Element* e = node->document()->getElementById((*cursors)[i].cursorFragmentId); - if (e && e->hasTagName(cursorTag)) { - hotSpot.setX(int(static_cast<SVGCursorElement*>(e)->x().value())); - hotSpot.setY(int(static_cast<SVGCursorElement*>(e)->y().value())); - cimage = static_cast<SVGCursorElement*>(e)->cachedImage(); - } - } -#endif if (!cimage) continue; + // Limit the size of cursors so that they cannot be used to cover UI elements in chrome. + IntSize size = cimage->image()->size(); + if (size.width() > 128 || size.height() > 128) + continue; + // Do not let the hotspot be outside the bounds of the image. + if (hotSpot.x() < 0 || hotSpot.y() < 0 || hotSpot.x() > size.width() || hotSpot.y() > size.height()) + continue; if (cimage->image()->isNull()) break; if (!cimage->errorOccurred()) @@ -820,6 +984,10 @@ Cursor EventHandler::selectCursor(const MouseEventWithHitTestResults& event, Pla return zoomInCursor(); case CURSOR_WEBKIT_ZOOM_OUT: return zoomOutCursor(); + case CURSOR_WEBKIT_GRAB: + return grabCursor(); + case CURSOR_WEBKIT_GRABBING: + return grabbingCursor(); } return pointerCursor(); } @@ -839,7 +1007,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_mouseDownMayStartAutoscroll = false; m_mouseDownPos = m_frame->view()->windowToContents(mouseEvent.pos()); m_mouseDownWasInSubframe = false; - + MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent); if (!mev.targetNode()) { @@ -849,7 +1017,14 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) m_mousePressNode = mev.targetNode(); - Frame* subframe = subframeForTargetNode(mev.targetNode()); + InspectorController* inspector = m_frame->page()->inspectorController(); + if (inspector && inspector->enabled() && inspector->searchingForNodeInPage()) { + inspector->handleMousePressOnNode(m_mousePressNode.get()); + invalidateClick(); + return true; + } + + Frame* subframe = subframeForHitTestResult(mev); if (subframe && passMousePressEventToSubframe(mev, subframe)) { // Start capturing future events for this frame. We only do this if we didn't clear // the m_mousePressed flag, which may happen if an AppKit widget entered a modal event loop. @@ -859,6 +1034,32 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) return true; } +#if ENABLE(PAN_SCROLLING) + if (m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() || m_autoscrollInProgress) { + stopAutoscrollTimer(); + invalidateClick(); + return true; + } + + if (mouseEvent.button() == MiddleButton && !mev.isOverLink()) { + RenderObject* renderer = mev.targetNode()->renderer(); + + while (renderer && !renderer->canBeProgramaticallyScrolled(false)) { + if (!renderer->parent() && renderer->node() == renderer->document() && renderer->document()->ownerElement()) + renderer = renderer->document()->ownerElement()->renderer(); + else + renderer = renderer->parent(); + } + + if (renderer) { + m_panScrollInProgress = true; + handleAutoscroll(renderer); + invalidateClick(); + return true; + } + } +#endif + m_clickCount = mouseEvent.clickCount(); m_clickNode = mev.targetNode(); @@ -872,7 +1073,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) return true; } - bool swallowEvent = dispatchMouseEvent(mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + bool swallowEvent = dispatchMouseEvent(eventNames().mousedownEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); // If the hit testing originally determined the event was in a scrollbar, refetch the MouseEventWithHitTestResults // in case the scrollbar widget was destroyed when the mouse event was handled. @@ -896,7 +1097,7 @@ bool EventHandler::handleMousePressEvent(const PlatformMouseEvent& mouseEvent) if (mev.targetNode()->isShadowNode() && mev.targetNode()->shadowParentNode()->hasTagName(inputTag)) mev = prepareMouseEvent(HitTestRequest(true, true), mouseEvent); - PlatformScrollbar* scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent); + Scrollbar* scrollbar = m_frame->view()->scrollbarUnderMouse(mouseEvent); if (!scrollbar) scrollbar = mev.scrollbar(); if (scrollbar && passMousePressEventToScrollbar(mev, scrollbar)) @@ -921,20 +1122,23 @@ bool EventHandler::handleMouseDoubleClickEvent(const PlatformMouseEvent& mouseEv m_currentMousePosition = mouseEvent.pos(); MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, true), mouseEvent); - Frame* subframe = subframeForTargetNode(mev.targetNode()); + Frame* subframe = subframeForHitTestResult(mev); if (subframe && passMousePressEventToSubframe(mev, subframe)) { m_capturingMouseEventsNode = 0; return true; } m_clickCount = mouseEvent.clickCount(); - bool swallowMouseUpEvent = dispatchMouseEvent(mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); bool swallowClickEvent = false; // Don't ever dispatch click events for right clicks if (mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode) - swallowClickEvent = dispatchMouseEvent(clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + swallowClickEvent = dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + if (m_lastScrollbarUnderMouse) + swallowMouseUpEvent = m_lastScrollbarUnderMouse->mouseUp(); + bool swallowMouseReleaseEvent = false; if (!swallowMouseUpEvent) swallowMouseReleaseEvent = handleMouseReleaseEvent(mev); @@ -983,11 +1187,11 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi #endif if (m_frameSetBeingResized) - return dispatchMouseEvent(mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); + return dispatchMouseEvent(eventNames().mousemoveEvent, m_frameSetBeingResized.get(), false, 0, mouseEvent, false); // Send events right to a scrollbar if the mouse is pressed. if (m_lastScrollbarUnderMouse && m_mousePressed) - return m_lastScrollbarUnderMouse->handleMouseMoveEvent(mouseEvent); + return m_lastScrollbarUnderMouse->mouseMoved(m_lastScrollbarUnderMouse->transformEvent(mouseEvent)); // Treat mouse move events while the mouse is pressed as "read-only" in prepareMouseEvent // if we are allowed to select. @@ -998,7 +1202,7 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (hoveredNode) *hoveredNode = mev.hitTestResult(); - PlatformScrollbar* scrollbar = 0; + Scrollbar* scrollbar = 0; if (m_resizeLayer && m_resizeLayer->inResizeMode()) m_resizeLayer->resize(mouseEvent, m_offsetFromResizeCorner); @@ -1012,15 +1216,14 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (m_lastScrollbarUnderMouse != scrollbar) { // Send mouse exited to the old scrollbar. if (m_lastScrollbarUnderMouse) - m_lastScrollbarUnderMouse->handleMouseOutEvent(mouseEvent); + m_lastScrollbarUnderMouse->mouseExited(); m_lastScrollbarUnderMouse = m_mousePressed ? 0 : scrollbar; } } bool swallowEvent = false; - Node* targetNode = m_capturingMouseEventsNode ? m_capturingMouseEventsNode.get() : mev.targetNode(); - RefPtr<Frame> newSubframe = subframeForTargetNode(targetNode); - + RefPtr<Frame> newSubframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); + // We want mouseouts to happen first, from the inside out. First send a move event to the last subframe so that it will fire mouseouts. if (m_lastMouseMoveEventSubframe && m_lastMouseMoveEventSubframe->tree()->isDescendantOf(m_frame) && m_lastMouseMoveEventSubframe != newSubframe) passMouseMoveEventToSubframe(mev, m_lastMouseMoveEventSubframe.get()); @@ -1028,11 +1231,15 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (newSubframe) { // Update over/out state before passing the event to the subframe. updateMouseEventTargetNode(mev.targetNode(), mouseEvent, true); - swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); + + // Event dispatch in updateMouseEventTargetNode may have caused the subframe of the target + // node to be detached from its FrameView, in which case the event should not be passed. + if (newSubframe->view()) + swallowEvent |= passMouseMoveEventToSubframe(mev, newSubframe.get(), hoveredNode); } else { if (scrollbar && !m_mousePressed) - scrollbar->handleMouseMoveEvent(mouseEvent); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. - if ((!m_resizeLayer || !m_resizeLayer->inResizeMode()) && m_frame->view()) + scrollbar->mouseMoved(scrollbar->transformEvent(mouseEvent)); // Handle hover effects on platforms that support visual feedback on scrollbar hovering. + if ((!m_resizeLayer || !m_resizeLayer->inResizeMode()) && !m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() && m_frame->view()) m_frame->view()->setCursor(selectCursor(mev, scrollbar)); } @@ -1041,7 +1248,7 @@ bool EventHandler::handleMouseMoveEvent(const PlatformMouseEvent& mouseEvent, Hi if (swallowEvent) return true; - swallowEvent = dispatchMouseEvent(mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); + swallowEvent = dispatchMouseEvent(eventNames().mousemoveEvent, mev.targetNode(), false, 0, mouseEvent, true); if (!swallowEvent) swallowEvent = handleMouseDraggedEvent(mev); @@ -1073,27 +1280,26 @@ bool EventHandler::handleMouseReleaseEvent(const PlatformMouseEvent& mouseEvent) #endif if (m_frameSetBeingResized) - return dispatchMouseEvent(mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); + return dispatchMouseEvent(eventNames().mouseupEvent, m_frameSetBeingResized.get(), true, m_clickCount, mouseEvent, false); if (m_lastScrollbarUnderMouse) { invalidateClick(); - return m_lastScrollbarUnderMouse->handleMouseReleaseEvent(mouseEvent); + return m_lastScrollbarUnderMouse->mouseUp(); } MouseEventWithHitTestResults mev = prepareMouseEvent(HitTestRequest(false, false, false, true), mouseEvent); - Node* targetNode = m_capturingMouseEventsNode.get() ? m_capturingMouseEventsNode.get() : mev.targetNode(); - Frame* subframe = subframeForTargetNode(targetNode); + Frame* subframe = m_capturingMouseEventsNode.get() ? subframeForTargetNode(m_capturingMouseEventsNode.get()) : subframeForHitTestResult(mev); if (subframe && passMouseReleaseEventToSubframe(mev, subframe)) { m_capturingMouseEventsNode = 0; return true; } - bool swallowMouseUpEvent = dispatchMouseEvent(mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); + bool swallowMouseUpEvent = dispatchMouseEvent(eventNames().mouseupEvent, mev.targetNode(), true, m_clickCount, mouseEvent, false); // Don't ever dispatch click events for right clicks bool swallowClickEvent = false; if (m_clickCount > 0 && mouseEvent.button() != RightButton && mev.targetNode() == m_clickNode) - swallowClickEvent = dispatchMouseEvent(clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); + swallowClickEvent = dispatchMouseEvent(eventNames().clickEvent, mev.targetNode(), true, m_clickCount, mouseEvent, true); if (m_resizeLayer) { m_resizeLayer->setInResizeMode(false); @@ -1113,14 +1319,14 @@ bool EventHandler::dispatchDragEvent(const AtomicString& eventType, Node* dragTa { IntPoint contentsPos = m_frame->view()->windowToContents(event.pos()); - RefPtr<MouseEvent> me = new MouseEvent(eventType, + RefPtr<MouseEvent> me = MouseEvent::create(eventType, true, true, m_frame->document()->defaultView(), 0, event.globalX(), event.globalY(), contentsPos.x(), contentsPos.y(), event.ctrlKey(), event.altKey(), event.shiftKey(), event.metaKey(), 0, 0, clipboard); ExceptionCode ec = 0; - EventTargetNodeCast(dragTarget)->dispatchEvent(me.get(), ec, true); + EventTargetNodeCast(dragTarget)->dispatchEvent(me.get(), ec); return me->defaultPrevented(); } @@ -1151,7 +1357,7 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (newTarget->hasTagName(frameTag) || newTarget->hasTagName(iframeTag)) accept = static_cast<HTMLFrameElementBase*>(newTarget)->contentFrame()->eventHandler()->updateDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dragenterEvent, newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragenterEvent, newTarget, event, clipboard); if (m_dragTarget) { Frame* frame = (m_dragTarget->hasTagName(frameTag) || m_dragTarget->hasTagName(iframeTag)) @@ -1159,14 +1365,14 @@ bool EventHandler::updateDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (frame) accept = frame->eventHandler()->updateDragAndDrop(event, clipboard); else - dispatchDragEvent(dragleaveEvent, m_dragTarget.get(), event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), event, clipboard); } } else { if (newTarget) if (newTarget->hasTagName(frameTag) || newTarget->hasTagName(iframeTag)) accept = static_cast<HTMLFrameElementBase*>(newTarget)->contentFrame()->eventHandler()->updateDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dragoverEvent, newTarget, event, clipboard); + accept = dispatchDragEvent(eventNames().dragoverEvent, newTarget, event, clipboard); } m_dragTarget = newTarget; @@ -1181,7 +1387,7 @@ void EventHandler::cancelDragAndDrop(const PlatformMouseEvent& event, Clipboard* if (frame) frame->eventHandler()->cancelDragAndDrop(event, clipboard); else - dispatchDragEvent(dragleaveEvent, m_dragTarget.get(), event, clipboard); + dispatchDragEvent(eventNames().dragleaveEvent, m_dragTarget.get(), event, clipboard); } clearDragState(); } @@ -1195,7 +1401,7 @@ bool EventHandler::performDragAndDrop(const PlatformMouseEvent& event, Clipboard if (frame) accept = frame->eventHandler()->performDragAndDrop(event, clipboard); else - accept = dispatchDragEvent(dropEvent, m_dragTarget.get(), event, clipboard); + accept = dispatchDragEvent(eventNames().dropEvent, m_dragTarget.get(), event, clipboard); } clearDragState(); return accept; @@ -1210,11 +1416,6 @@ void EventHandler::clearDragState() #endif } -Node* EventHandler::nodeUnderMouse() const -{ - return m_nodeUnderMouse.get(); -} - void EventHandler::setCapturingMouseEventsNode(PassRefPtr<Node> n) { m_capturingMouseEventsNode = n; @@ -1229,6 +1430,25 @@ MouseEventWithHitTestResults EventHandler::prepareMouseEvent(const HitTestReques return m_frame->document()->prepareMouseEvent(request, documentPoint, mev); } +#if ENABLE(SVG) +static inline SVGElementInstance* instanceAssociatedWithShadowTreeElement(Node* referenceNode) +{ + if (!referenceNode || !referenceNode->isSVGElement()) + return 0; + + Node* shadowTreeElement = referenceNode->shadowTreeRootNode(); + if (!shadowTreeElement) + return 0; + + Node* shadowTreeParentElement = shadowTreeElement->shadowParentNode(); + if (!shadowTreeParentElement) + return 0; + + ASSERT(shadowTreeParentElement->hasTagName(useTag)); + return static_cast<SVGUseElement*>(shadowTreeParentElement)->instanceForShadowTreeElement(referenceNode); +} +#endif + void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMouseEvent& mouseEvent, bool fireMouseOverOut) { Node* result = targetNode; @@ -1244,23 +1464,63 @@ void EventHandler::updateMouseEventTargetNode(Node* targetNode, const PlatformMo result = result->shadowAncestorNode(); } m_nodeUnderMouse = result; - +#if ENABLE(SVG) + m_instanceUnderMouse = instanceAssociatedWithShadowTreeElement(result); + + // <use> shadow tree elements may have been recloned, update node under mouse in any case + if (m_lastInstanceUnderMouse) { + SVGElement* lastCorrespondingElement = m_lastInstanceUnderMouse->correspondingElement(); + SVGElement* lastCorrespondingUseElement = m_lastInstanceUnderMouse->correspondingUseElement(); + + if (lastCorrespondingElement && lastCorrespondingUseElement) { + HashSet<SVGElementInstance*> instances = lastCorrespondingElement->instancesForElement(); + + // Locate the recloned shadow tree element for our corresponding instance + HashSet<SVGElementInstance*>::iterator end = instances.end(); + for (HashSet<SVGElementInstance*>::iterator it = instances.begin(); it != end; ++it) { + SVGElementInstance* instance = (*it); + ASSERT(instance->correspondingElement() == lastCorrespondingElement); + + if (instance == m_lastInstanceUnderMouse) + continue; + + if (instance->correspondingUseElement() != lastCorrespondingUseElement) + continue; + + SVGElement* shadowTreeElement = instance->shadowTreeElement(); + if (!shadowTreeElement->inDocument() || m_lastNodeUnderMouse == shadowTreeElement) + continue; + + m_lastNodeUnderMouse = shadowTreeElement; + m_lastInstanceUnderMouse = instance; + break; + } + } + } +#endif + // Fire mouseout/mouseover if the mouse has shifted to a different node. if (fireMouseOverOut) { if (m_lastNodeUnderMouse && m_lastNodeUnderMouse->document() != m_frame->document()) { m_lastNodeUnderMouse = 0; m_lastScrollbarUnderMouse = 0; +#if ENABLE(SVG) + m_lastInstanceUnderMouse = 0; +#endif } if (m_lastNodeUnderMouse != m_nodeUnderMouse) { // send mouseout event to the old node if (m_lastNodeUnderMouse) - EventTargetNodeCast(m_lastNodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, mouseoutEvent, 0, m_nodeUnderMouse.get()); + EventTargetNodeCast(m_lastNodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventNames().mouseoutEvent, 0, m_nodeUnderMouse.get()); // send mouseover event to the new node if (m_nodeUnderMouse) - EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, mouseoverEvent, 0, m_lastNodeUnderMouse.get()); + EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventNames().mouseoverEvent, 0, m_lastNodeUnderMouse.get()); } m_lastNodeUnderMouse = m_nodeUnderMouse; +#if ENABLE(SVG) + m_lastInstanceUnderMouse = instanceAssociatedWithShadowTreeElement(m_nodeUnderMouse.get()); +#endif } } @@ -1273,7 +1533,7 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe if (m_nodeUnderMouse) swallowEvent = EventTargetNodeCast(m_nodeUnderMouse.get())->dispatchMouseEvent(mouseEvent, eventType, clickCount); - if (!swallowEvent && eventType == mousedownEvent) { + if (!swallowEvent && eventType == eventNames().mousedownEvent) { // Blur current focus node when a link/button is clicked; this // is expected by some sites that rely on onChange handlers running // from form fields before the button click is processed. @@ -1291,8 +1551,8 @@ bool EventHandler::dispatchMouseEvent(const AtomicString& eventType, Node* targe // will set a selection inside it, which will call setFocuseNodeIfNeeded. ExceptionCode ec = 0; Node* n = node->isShadowNode() ? node->shadowParentNode() : node; - if (m_frame->selectionController()->isRange() && - m_frame->selectionController()->toRange()->compareNode(n, ec) == Range::NODE_INSIDE && + if (m_frame->selection()->isRange() && + m_frame->selection()->toRange()->compareNode(n, ec) == Range::NODE_INSIDE && n->isDescendantOf(m_frame->document()->focusedNode())) return false; @@ -1336,7 +1596,7 @@ bool EventHandler::handleWheelEvent(PlatformWheelEvent& e) // Figure out which view to send the event to. RenderObject* target = node->renderer(); - if (target && target->isWidget()) { + if (result.isOverWidget() && target && target->isWidget()) { Widget* widget = static_cast<RenderWidget*>(target)->widget(); if (widget && passWheelEventToWidget(e, widget)) { @@ -1353,14 +1613,8 @@ bool EventHandler::handleWheelEvent(PlatformWheelEvent& e) if (node->renderer()) { // Just break up into two scrolls if we need to. Diagonal movement on // a MacBook pro is an example of a 2-dimensional mouse wheel event (where both deltaX and deltaY can be set). - float deltaX = e.isContinuous() ? e.continuousDeltaX() : e.deltaX(); - float deltaY = e.isContinuous() ? e.continuousDeltaY() : e.deltaY(); - if (deltaX && node->renderer()->scroll(deltaX < 0 ? ScrollRight : ScrollLeft, e.isContinuous() ? ScrollByPixel : ScrollByLine, - deltaX < 0 ? -deltaX : deltaX)) - e.accept(); - if (deltaY && node->renderer()->scroll(deltaY < 0 ? ScrollDown : ScrollUp, e.isContinuous() ? ScrollByPixel : ScrollByLine, - deltaY < 0 ? -deltaY : deltaY)) - e.accept(); + scrollAndAcceptEvent(e.deltaX(), ScrollLeft, ScrollRight, e, node); + scrollAndAcceptEvent(e.deltaY(), ScrollUp, ScrollDown, e, node); } } @@ -1381,16 +1635,19 @@ bool EventHandler::sendContextMenuEvent(const PlatformMouseEvent& event) IntPoint viewportPos = v->windowToContents(event.pos()); MouseEventWithHitTestResults mev = doc->prepareMouseEvent(HitTestRequest(false, true), viewportPos, event); - if (!m_frame->selectionController()->contains(viewportPos) && + // Context menu events shouldn't select text in GTK+ applications. +#if !PLATFORM(GTK) + if (!m_frame->selection()->contains(viewportPos) && // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse. // If the selection is non-editable, we do word selection to make it easier to use the contextual menu items // available for text selections. But only if we're above text. - (m_frame->selectionController()->isContentEditable() || mev.targetNode() && mev.targetNode()->isTextNode())) { + (m_frame->selection()->isContentEditable() || mev.targetNode() && mev.targetNode()->isTextNode())) { m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection selectClosestWordOrLinkFromMouseEvent(mev); } +#endif - swallowEvent = dispatchMouseEvent(contextmenuEvent, mev.targetNode(), true, 0, event, true); + swallowEvent = dispatchMouseEvent(eventNames().contextmenuEvent, mev.targetNode(), true, 0, event, true); return swallowEvent; } @@ -1413,7 +1670,7 @@ bool EventHandler::canMouseDownStartSelect(Node* node) for (RenderObject* curr = node->renderer(); curr; curr = curr->parent()) if (Node* node = curr->element()) - return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true); + return EventTargetNodeCast(node)->dispatchEventForType(eventNames().selectstartEvent, true, true); return true; } @@ -1425,7 +1682,7 @@ bool EventHandler::canMouseDragExtendSelect(Node* node) for (RenderObject* curr = node->renderer(); curr; curr = curr->parent()) if (Node* node = curr->element()) - return EventTargetNodeCast(node)->dispatchHTMLEvent(selectstartEvent, true, true); + return EventTargetNodeCast(node)->dispatchEventForType(eventNames().selectstartEvent, true, true); return true; } @@ -1448,7 +1705,7 @@ void EventHandler::hoverTimerFired(Timer<EventHandler>*) ASSERT(m_frame); ASSERT(m_frame->document()); - if (RenderObject* renderer = m_frame->renderer()) { + if (RenderObject* renderer = m_frame->contentRenderer()) { HitTestResult result(m_frame->view()->windowToContents(m_currentMousePosition)); renderer->layer()->hitTest(HitTestRequest(false, false, true), result); m_frame->document()->updateRendering(); @@ -1473,21 +1730,14 @@ static EventTargetNode* eventTargetNodeForDocument(Document* doc) bool EventHandler::handleAccessKey(const PlatformKeyboardEvent& evt) { -#if PLATFORM(MAC) || PLATFORM(QT) - if (evt.ctrlKey()) -#else - if (evt.altKey()) -#endif - { - String key = evt.unmodifiedText(); - Element* elem = m_frame->document()->getElementByAccessKey(key.lower()); - if (elem) { - elem->accessKeyAction(false); - return true; - } - } - - return false; + if ((evt.modifiers() & s_accessKeyModifiers) != s_accessKeyModifiers) + return false; + String key = evt.unmodifiedText(); + Element* elem = m_frame->document()->getElementByAccessKey(key.lower()); + if (!elem) + return false; + elem->accessKeyAction(false); + return true; } #if !PLATFORM(MAC) @@ -1499,6 +1749,18 @@ bool EventHandler::needsKeyboardEventDisambiguationQuirks() const bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) { +#if ENABLE(PAN_SCROLLING) + if (m_frame->page()->mainFrame()->eventHandler()->panScrollInProgress() || m_autoscrollInProgress) { + String escKeyId = "U+001B"; + // If a key is pressed while the autoscroll/panScroll is in progress then we want to stop + if (initialKeyEvent.keyIdentifier() == escKeyId && initialKeyEvent.type() == PlatformKeyboardEvent::KeyUp) + stopAutoscrollTimer(); + + // If we were in autoscroll/panscroll mode, we swallow the key event + return true; + } +#endif + // Check for cases where we are too early for events -- possible unmatched key up // from pressing return in the location bar. RefPtr<EventTargetNode> node = eventTargetNodeForDocument(m_frame->document()); @@ -1527,13 +1789,13 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) PlatformKeyboardEvent keyDownEvent = initialKeyEvent; if (keyDownEvent.type() != PlatformKeyboardEvent::RawKeyDown) keyDownEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::RawKeyDown, backwardCompatibilityMode); - RefPtr<KeyboardEvent> keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView()); + RefPtr<KeyboardEvent> keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->defaultView()); if (matchedAnAccessKey) keydown->setDefaultPrevented(true); keydown->setTarget(node); if (initialKeyEvent.type() == PlatformKeyboardEvent::RawKeyDown) { - node->dispatchEvent(keydown, ec, true); + node->dispatchEvent(keydown, ec); return keydown->defaultHandled() || keydown->defaultPrevented(); } @@ -1548,12 +1810,12 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) if (handledByInputMethod) { keyDownEvent.setWindowsVirtualKeyCode(CompositionEventKeyCode); - keydown = new KeyboardEvent(keyDownEvent, m_frame->document()->defaultView()); + keydown = KeyboardEvent::create(keyDownEvent, m_frame->document()->defaultView()); keydown->setTarget(node); keydown->setDefaultHandled(); } - node->dispatchEvent(keydown, ec, true); + node->dispatchEvent(keydown, ec); bool keydownResult = keydown->defaultHandled() || keydown->defaultPrevented(); if (handledByInputMethod || (keydownResult && !backwardCompatibilityMode)) return keydownResult; @@ -1570,28 +1832,59 @@ bool EventHandler::keyEvent(const PlatformKeyboardEvent& initialKeyEvent) keyPressEvent.disambiguateKeyDownEvent(PlatformKeyboardEvent::Char, backwardCompatibilityMode); if (keyPressEvent.text().isEmpty()) return keydownResult; - RefPtr<KeyboardEvent> keypress = new KeyboardEvent(keyPressEvent, m_frame->document()->defaultView()); + RefPtr<KeyboardEvent> keypress = KeyboardEvent::create(keyPressEvent, m_frame->document()->defaultView()); keypress->setTarget(node); if (keydownResult) keypress->setDefaultPrevented(true); #if PLATFORM(MAC) keypress->keypressCommands() = keydown->keypressCommands(); #endif - node->dispatchEvent(keypress, ec, true); + node->dispatchEvent(keypress, ec); return keydownResult || keypress->defaultPrevented() || keypress->defaultHandled(); } +void EventHandler::handleKeyboardSelectionMovement(KeyboardEvent* event) +{ + if (!event) + return; + + String key = event->keyIdentifier(); + bool isShifted = event->getModifierState("Shift"); + bool isOptioned = event->getModifierState("Alt"); + + if (key == "Up") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::BACKWARD, LineGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Down") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::FORWARD, LineGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Left") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::LEFT, (isOptioned) ? WordGranularity : CharacterGranularity, true); + event->setDefaultHandled(); + } + else if (key == "Right") { + m_frame->selection()->modify((isShifted) ? SelectionController::EXTEND : SelectionController::MOVE, SelectionController::RIGHT, (isOptioned) ? WordGranularity : CharacterGranularity, true); + event->setDefaultHandled(); + } +} + void EventHandler::defaultKeyboardEventHandler(KeyboardEvent* event) { - if (event->type() == keydownEvent) { + if (event->type() == eventNames().keydownEvent) { m_frame->editor()->handleKeyboardEvent(event); if (event->defaultHandled()) return; if (event->keyIdentifier() == "U+0009") defaultTabEventHandler(event); + + // provides KB navigation and selection for enhanced accessibility users + if (AXObjectCache::accessibilityEnhancedUserInterfaceEnabled()) + handleKeyboardSelectionMovement(event); } - if (event->type() == keypressEvent) { + if (event->type() == eventNames().keypressEvent) { m_frame->editor()->handleKeyboardEvent(event); if (event->defaultHandled()) return; @@ -1638,7 +1931,7 @@ void EventHandler::dragSourceMovedTo(const PlatformMouseEvent& event) { if (dragState().m_dragSrc && dragState().m_dragSrcMayBeDHTML) // for now we don't care if event handler cancels default behavior, since there is none - dispatchDragSrcEvent(dragEvent, event); + dispatchDragSrcEvent(eventNames().dragEvent, event); } void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperation operation) @@ -1646,7 +1939,7 @@ void EventHandler::dragSourceEndedAt(const PlatformMouseEvent& event, DragOperat if (dragState().m_dragSrc && dragState().m_dragSrcMayBeDHTML) { dragState().m_dragClipboard->setDestinationOperation(operation); // for now we don't care if event handler cancels default behavior, since there is none - dispatchDragSrcEvent(dragendEvent, event); + dispatchDragSrcEvent(eventNames().dragendEvent, event); } freeClipboard(); dragState().m_dragSrc = 0; @@ -1684,7 +1977,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) // try to find an element that wants to be dragged HitTestRequest request(true, false); HitTestResult result(m_mouseDownPos); - m_frame->renderer()->layer()->hitTest(request, result); + m_frame->contentRenderer()->layer()->hitTest(request, result); Node* node = result.innerNode(); if (node && node->renderer()) dragState().m_dragSrc = node->renderer()->draggableNode(dragState().m_dragSrcMayBeDHTML, dragState().m_dragSrcMayBeUA, @@ -1702,7 +1995,7 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) node = result.innerNonSharedNode(); dragState().m_dragSrcIsImage = node && node->renderer() && node->renderer()->isImage(); - dragState().m_dragSrcInSelection = m_frame->selectionController()->contains(m_mouseDownPos); + dragState().m_dragSrcInSelection = m_frame->selection()->contains(m_mouseDownPos); } } @@ -1739,13 +2032,20 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) // image and offset if (dragState().m_dragSrcIsDHTML) { int srcX, srcY; - dragState().m_dragSrc->renderer()->absolutePosition(srcX, srcY); - IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY); - dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint() + delta); + if (RenderObject* renderer = dragState().m_dragSrc->renderer()) { + renderer->absolutePosition(srcX, srcY); + IntSize delta = m_mouseDownPos - IntPoint(srcX, srcY); + dragState().m_dragClipboard->setDragImageElement(dragState().m_dragSrc.get(), IntPoint() + delta); + } else { + // The renderer has disappeared, this can happen if the onStartDrag handler has hidden + // the element in some way. In this case we just kill the drag. + m_mouseDownMayStartDrag = false; + goto cleanupDrag; + } } - m_mouseDownMayStartDrag = dispatchDragSrcEvent(dragstartEvent, m_mouseDown) - && !m_frame->selectionController()->isInPasswordField(); + m_mouseDownMayStartDrag = dispatchDragSrcEvent(eventNames().dragstartEvent, m_mouseDown) + && !m_frame->selection()->isInPasswordField(); // Invalidate clipboard here against anymore pasteboard writing for security. The drag // image can still be changed as we drag, but not the pasteboard data. @@ -1767,11 +2067,12 @@ bool EventHandler::handleDrag(const MouseEventWithHitTestResults& event) bool startedDrag = dragController && dragController->startDrag(m_frame, dragState().m_dragClipboard.get(), srcOp, event.event(), m_mouseDownPos, dragState().m_dragSrcIsDHTML); if (!startedDrag && dragState().m_dragSrcMayBeDHTML) { // Drag was canned at the last minute - we owe m_dragSrc a DRAGEND event - dispatchDragSrcEvent(dragendEvent, event.event()); + dispatchDragSrcEvent(eventNames().dragendEvent, event.event()); m_mouseDownMayStartDrag = false; } } - + +cleanupDrag: if (!m_mouseDownMayStartDrag) { // something failed to start the drag, cleanup freeClipboard(); @@ -1791,7 +2092,7 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve // Platforms should differentiate real commands like selectAll from text input in disguise (like insertNewline), // and avoid dispatching text input events from keydown default handlers. if (underlyingEvent && underlyingEvent->isKeyboardEvent()) - ASSERT(static_cast<KeyboardEvent*>(underlyingEvent)->type() == keypressEvent); + ASSERT(static_cast<KeyboardEvent*>(underlyingEvent)->type() == eventNames().keypressEvent); #endif EventTarget* target; if (underlyingEvent) @@ -1800,12 +2101,12 @@ bool EventHandler::handleTextInputEvent(const String& text, Event* underlyingEve target = eventTargetNodeForDocument(m_frame->document()); if (!target) return false; - RefPtr<TextEvent> event = new TextEvent(m_frame->domWindow(), text); + RefPtr<TextEvent> event = TextEvent::create(m_frame->domWindow(), text); event->setUnderlyingEvent(underlyingEvent); event->setIsLineBreak(isLineBreak); event->setIsBackTab(isBackTab); ExceptionCode ec; - return target->dispatchEvent(event.release(), ec, true); + return target->dispatchEvent(event.release(), ec); } @@ -1873,6 +2174,175 @@ void EventHandler::capsLockStateMayHaveChanged() if (Node* node = d->focusedNode()) if (RenderObject* r = node->renderer()) r->capsLockStateMayHaveChanged(); -} +} + +unsigned EventHandler::pendingFrameUnloadEventCount() +{ + return m_pendingFrameUnloadEventCount; +} + +void EventHandler::addPendingFrameUnloadEventCount() +{ + m_pendingFrameUnloadEventCount += 1; + m_frame->page()->changePendingUnloadEventCount(1); + return; +} + +void EventHandler::removePendingFrameUnloadEventCount() +{ + ASSERT( (-1 + (int)m_pendingFrameUnloadEventCount) >= 0 ); + m_pendingFrameUnloadEventCount -= 1; + m_frame->page()->changePendingUnloadEventCount(-1); + return; +} + +void EventHandler::clearPendingFrameUnloadEventCount() +{ + m_frame->page()->changePendingUnloadEventCount(-((int)m_pendingFrameUnloadEventCount)); + m_pendingFrameUnloadEventCount = 0; + return; +} + +unsigned EventHandler::pendingFrameBeforeUnloadEventCount() +{ + return m_pendingFrameBeforeUnloadEventCount; +} + +void EventHandler::addPendingFrameBeforeUnloadEventCount() +{ + m_pendingFrameBeforeUnloadEventCount += 1; + m_frame->page()->changePendingBeforeUnloadEventCount(1); + return; +} + +void EventHandler::removePendingFrameBeforeUnloadEventCount() +{ + ASSERT( (-1 + (int)m_pendingFrameBeforeUnloadEventCount) >= 0 ); + m_pendingFrameBeforeUnloadEventCount -= 1; + m_frame->page()->changePendingBeforeUnloadEventCount(-1); + return; +} + + void EventHandler::clearPendingFrameBeforeUnloadEventCount() +{ + m_frame->page()->changePendingBeforeUnloadEventCount(-((int)m_pendingFrameBeforeUnloadEventCount)); + m_pendingFrameBeforeUnloadEventCount = 0; + return; +} + +bool EventHandler::passMousePressEventToScrollbar(MouseEventWithHitTestResults& mev, Scrollbar* scrollbar) +{ + if (!scrollbar || !scrollbar->enabled()) + return false; + return scrollbar->mouseDown(mev.event()); +} + +#if ENABLE(TOUCH_EVENTS) // Android +bool EventHandler::handleTouchEvent(const PlatformTouchEvent& e) +{ + // only handle the touch event in the top frame handler + if (m_frame->tree()->parent(true)) + return m_frame->tree()->parent()->eventHandler()->handleTouchEvent(e); + + Document* doc = m_frame->document(); + if (!doc) + return false; + + RenderObject* docRenderer = doc->renderer(); + if (!docRenderer) + return false; + + if (doc->touchEventListeners().size() == 0) + return false; + + TouchEventType type = e.eventType(); + if (type == TouchEventStart) { + Frame* frame = m_frame; + IntPoint vPoint = frame->view()->windowToContents(e.pos()); + HitTestRequest request(true, false); + HitTestResult result(vPoint); + frame->contentRenderer()->layer()->hitTest(request, result); + Node* node = result.innerNode(); + if (node) { + RenderObject* target = node->renderer(); + while (target && target->isWidget()) { + Widget* widget = static_cast<RenderWidget*>(target)->widget(); + if (widget->isFrameView()) { + frame = static_cast<FrameView*>(widget)->frame(); + vPoint = frame->view()->windowToContents(e.pos()); + HitTestResult ret(vPoint); + frame->contentRenderer()->layer()->hitTest(request, ret); + node = ret.innerNode(); + if (!node) + break; + else + target = node->renderer(); + } else + // plugin view?? + break; + } + } + + if (!node || !node->isEventTargetNode()) { + // reset to the top document node + node = doc; + frame = m_frame; + vPoint = frame->view()->windowToContents(e.pos()); + } + + m_touch = Touch::create(frame, EventTargetNodeCast(node), 0, + e.x(), e.y(), vPoint.x(), vPoint.y()); + } else if (m_touch) { + if ((type == TouchEventMove) && (e.x() == m_touch->screenX()) && + (e.y() == m_touch->screenY())) { + // don't trigger the event if it hasn't really moved + return false; + } + + IntPoint vPoint = m_touch->frame()->view()->windowToContents(e.pos()); + m_touch->updateLocation(e.x(), e.y(), vPoint.x(), vPoint.y()); + } else { + return false; + } + + RefPtr<TouchList> touchList = TouchList::create(); + touchList->append(m_touch); + RefPtr<TouchEvent> te; + switch(type) { + case TouchEventStart: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchstartEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventEnd: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchendEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventMove: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchmoveEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + case TouchEventCancel: + te = TouchEvent::create(touchList.get(), touchList.get(), touchList.get(), + eventNames().touchcancelEvent, m_touch->frame()->document()->defaultView(), + m_touch->screenX(), m_touch->screenY(), m_touch->pageX(), m_touch->pageY()); + break; + + default: + return false; + } + ExceptionCode ec = 0; + static_cast<EventTargetNode*>(m_touch->target())->dispatchEvent(te.get(), ec); + if (type == TouchEventEnd || type == TouchEventCancel) { + m_touch = 0; + } + return te->defaultPrevented(); +} +#endif } |