/* * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2006 David Smith (catfish.man@gmail.com) * * 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "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 OR ITS 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. */ #import "WebViewInternal.h" #import "WebFrameInternal.h" #import "WebHTMLView.h" #import "WebTextCompletionController.h" #import "WebViewData.h" #import <WebCore/Frame.h> using namespace WebCore; @class NSTextInputContext; @interface NSResponder (WebNSResponderDetails) - (NSTextInputContext *)inputContext; @end #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) @interface NSObject (NSTextInputContextDetails) - (BOOL)wantsToHandleMouseEvents; - (BOOL)handleMouseEvent:(NSEvent *)event; @end #endif @implementation WebView (WebViewEventHandling) static WebView *lastMouseoverView; - (void)_closingEventHandling { if (lastMouseoverView == self) lastMouseoverView = nil; } - (void)_setMouseDownEvent:(NSEvent *)event { ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); if (event == _private->mouseDownEvent) return; [event retain]; [_private->mouseDownEvent release]; _private->mouseDownEvent = event; } - (void)mouseDown:(NSEvent *)event { // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to // do the same work in the usesDocumentViews case. We don't want to maintain two // duplicate copies of this method. if (_private->usesDocumentViews) { [super mouseDown:event]; return; } // There's a chance that responding to this event will run a nested event loop, and // fetching a new event might release the old one. Retaining and then autoreleasing // the current event prevents that from causing a problem inside WebKit or AppKit code. [[event retain] autorelease]; RetainPtr<WebView> protector = self; if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) return; _private->handlingMouseDownEvent = YES; // Record the mouse down position so we can determine drag hysteresis. [self _setMouseDownEvent:event]; NSInputManager *currentInputManager = [NSInputManager currentInputManager]; if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) goto done; [_private->completionController endRevertingChange:NO moveLeft:NO]; // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. // We don't want to pass them along to KHTML a second time. if (!([event modifierFlags] & NSControlKeyMask)) { _private->ignoringMouseDraggedEvents = NO; // Don't do any mouseover while the mouse is down. [self _cancelUpdateMouseoverTimer]; // Let WebCore get a chance to deal with the event. This will call back to us // to start the autoscroll timer if appropriate. if (Frame* frame = [self _mainCoreFrame]) frame->eventHandler()->mouseDown(event); } done: _private->handlingMouseDownEvent = NO; } - (void)mouseUp:(NSEvent *)event { // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to // do the same work in the usesDocumentViews case. We don't want to maintain two // duplicate copies of this method. if (_private->usesDocumentViews) { [super mouseUp:event]; return; } // There's a chance that responding to this event will run a nested event loop, and // fetching a new event might release the old one. Retaining and then autoreleasing // the current event prevents that from causing a problem inside WebKit or AppKit code. [[event retain] autorelease]; [self _setMouseDownEvent:nil]; NSInputManager *currentInputManager = [NSInputManager currentInputManager]; if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) return; [self retain]; [self _stopAutoscrollTimer]; if (Frame* frame = [self _mainCoreFrame]) frame->eventHandler()->mouseUp(event); [self _updateMouseoverWithFakeEvent]; [self release]; } + (void)_updateMouseoverWithEvent:(NSEvent *)event { WebView *oldView = lastMouseoverView; lastMouseoverView = nil; NSView *contentView = [[event window] contentView]; NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; for (NSView *hitView = [contentView hitTest:locationForHitTest]; hitView; hitView = [hitView superview]) { if ([hitView isKindOfClass:[WebView class]]) { lastMouseoverView = static_cast<WebView *>(hitView); break; } } if (lastMouseoverView && lastMouseoverView->_private->hoverFeedbackSuspended) lastMouseoverView = nil; if (lastMouseoverView != oldView) { if (Frame* oldCoreFrame = [oldView _mainCoreFrame]) { NSEvent *oldViewEvent = [NSEvent mouseEventWithType:NSMouseMoved location:NSMakePoint(-1, -1) modifierFlags:[[NSApp currentEvent] modifierFlags] timestamp:[NSDate timeIntervalSinceReferenceDate] windowNumber:[[oldView window] windowNumber] context:[[NSApp currentEvent] context] eventNumber:0 clickCount:0 pressure:0]; oldCoreFrame->eventHandler()->mouseMoved(oldViewEvent); } } if (!lastMouseoverView) return; if (Frame* coreFrame = core([lastMouseoverView mainFrame])) coreFrame->eventHandler()->mouseMoved(event); } - (void)_updateMouseoverWithFakeEvent { [self _cancelUpdateMouseoverTimer]; NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] modifierFlags:[[NSApp currentEvent] modifierFlags] timestamp:[NSDate timeIntervalSinceReferenceDate] windowNumber:[[self window] windowNumber] context:[[NSApp currentEvent] context] eventNumber:0 clickCount:0 pressure:0]; [[self class] _updateMouseoverWithEvent:fakeEvent]; } - (void)_cancelUpdateMouseoverTimer { if (_private->updateMouseoverTimer) { CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); CFRelease(_private->updateMouseoverTimer); _private->updateMouseoverTimer = NULL; } } - (void)_stopAutoscrollTimer { NSTimer *timer = _private->autoscrollTimer; _private->autoscrollTimer = nil; [_private->autoscrollTriggerEvent release]; _private->autoscrollTriggerEvent = nil; [timer invalidate]; [timer release]; } - (void)_setToolTip:(NSString *)toolTip { if (_private->usesDocumentViews) { id documentView = [[[self _selectedOrMainFrame] frameView] documentView]; if ([documentView isKindOfClass:[WebHTMLView class]]) [documentView _setToolTip:toolTip]; return; } // FIXME (Viewless): Code to handle tooltips needs to move into WebView. } @end