1/* 2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 3 * Copyright (C) 2006 David Smith (catfish.man@gmail.com) 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 15 * its contributors may be used to endorse or promote products derived 16 * from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30#import "WebViewInternal.h" 31 32#import "WebFrameInternal.h" 33#import "WebHTMLView.h" 34#import "WebTextCompletionController.h" 35#import "WebViewData.h" 36#import <WebCore/Frame.h> 37 38using namespace WebCore; 39 40@class NSTextInputContext; 41 42@interface NSResponder (WebNSResponderDetails) 43- (NSTextInputContext *)inputContext; 44@end 45 46#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) 47@interface NSObject (NSTextInputContextDetails) 48- (BOOL)wantsToHandleMouseEvents; 49- (BOOL)handleMouseEvent:(NSEvent *)event; 50@end 51#endif 52 53@implementation WebView (WebViewEventHandling) 54 55static WebView *lastMouseoverView; 56 57- (void)_closingEventHandling 58{ 59 if (lastMouseoverView == self) 60 lastMouseoverView = nil; 61} 62 63- (void)_setMouseDownEvent:(NSEvent *)event 64{ 65 ASSERT(!event || [event type] == NSLeftMouseDown || [event type] == NSRightMouseDown || [event type] == NSOtherMouseDown); 66 67 if (event == _private->mouseDownEvent) 68 return; 69 70 [event retain]; 71 [_private->mouseDownEvent release]; 72 _private->mouseDownEvent = event; 73} 74 75- (void)mouseDown:(NSEvent *)event 76{ 77 // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to 78 // do the same work in the usesDocumentViews case. We don't want to maintain two 79 // duplicate copies of this method. 80 81 if (_private->usesDocumentViews) { 82 [super mouseDown:event]; 83 return; 84 } 85 86 // There's a chance that responding to this event will run a nested event loop, and 87 // fetching a new event might release the old one. Retaining and then autoreleasing 88 // the current event prevents that from causing a problem inside WebKit or AppKit code. 89 [[event retain] autorelease]; 90 91 RetainPtr<WebView> protector = self; 92 if ([[self inputContext] wantsToHandleMouseEvents] && [[self inputContext] handleMouseEvent:event]) 93 return; 94 95 _private->handlingMouseDownEvent = YES; 96 97 // Record the mouse down position so we can determine drag hysteresis. 98 [self _setMouseDownEvent:event]; 99 100 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 101 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 102 goto done; 103 104 [_private->completionController endRevertingChange:NO moveLeft:NO]; 105 106 // If the web page handles the context menu event and menuForEvent: returns nil, we'll get control click events here. 107 // We don't want to pass them along to KHTML a second time. 108 if (!([event modifierFlags] & NSControlKeyMask)) { 109 _private->ignoringMouseDraggedEvents = NO; 110 111 // Don't do any mouseover while the mouse is down. 112 [self _cancelUpdateMouseoverTimer]; 113 114 // Let WebCore get a chance to deal with the event. This will call back to us 115 // to start the autoscroll timer if appropriate. 116 if (Frame* frame = [self _mainCoreFrame]) 117 frame->eventHandler()->mouseDown(event); 118 } 119 120done: 121 _private->handlingMouseDownEvent = NO; 122} 123 124- (void)mouseUp:(NSEvent *)event 125{ 126 // FIXME (Viewless): This method should be shared with WebHTMLView, which needs to 127 // do the same work in the usesDocumentViews case. We don't want to maintain two 128 // duplicate copies of this method. 129 130 if (_private->usesDocumentViews) { 131 [super mouseUp:event]; 132 return; 133 } 134 135 // There's a chance that responding to this event will run a nested event loop, and 136 // fetching a new event might release the old one. Retaining and then autoreleasing 137 // the current event prevents that from causing a problem inside WebKit or AppKit code. 138 [[event retain] autorelease]; 139 140 [self _setMouseDownEvent:nil]; 141 142 NSInputManager *currentInputManager = [NSInputManager currentInputManager]; 143 if ([currentInputManager wantsToHandleMouseEvents] && [currentInputManager handleMouseEvent:event]) 144 return; 145 146 [self retain]; 147 148 [self _stopAutoscrollTimer]; 149 if (Frame* frame = [self _mainCoreFrame]) 150 frame->eventHandler()->mouseUp(event); 151 [self _updateMouseoverWithFakeEvent]; 152 153 [self release]; 154} 155 156+ (void)_updateMouseoverWithEvent:(NSEvent *)event 157{ 158 WebView *oldView = lastMouseoverView; 159 160 lastMouseoverView = nil; 161 162 NSView *contentView = [[event window] contentView]; 163 NSPoint locationForHitTest = [[contentView superview] convertPoint:[event locationInWindow] fromView:nil]; 164 for (NSView *hitView = [contentView hitTest:locationForHitTest]; hitView; hitView = [hitView superview]) { 165 if ([hitView isKindOfClass:[WebView class]]) { 166 lastMouseoverView = static_cast<WebView *>(hitView); 167 break; 168 } 169 } 170 171 if (lastMouseoverView && lastMouseoverView->_private->hoverFeedbackSuspended) 172 lastMouseoverView = nil; 173 174 if (lastMouseoverView != oldView) { 175 if (Frame* oldCoreFrame = [oldView _mainCoreFrame]) { 176 NSEvent *oldViewEvent = [NSEvent mouseEventWithType:NSMouseMoved 177 location:NSMakePoint(-1, -1) 178 modifierFlags:[[NSApp currentEvent] modifierFlags] 179 timestamp:[NSDate timeIntervalSinceReferenceDate] 180 windowNumber:[[oldView window] windowNumber] 181 context:[[NSApp currentEvent] context] 182 eventNumber:0 clickCount:0 pressure:0]; 183 oldCoreFrame->eventHandler()->mouseMoved(oldViewEvent); 184 } 185 } 186 187 if (!lastMouseoverView) 188 return; 189 190 if (Frame* coreFrame = core([lastMouseoverView mainFrame])) 191 coreFrame->eventHandler()->mouseMoved(event); 192} 193 194- (void)_updateMouseoverWithFakeEvent 195{ 196 [self _cancelUpdateMouseoverTimer]; 197 198 NSEvent *fakeEvent = [NSEvent mouseEventWithType:NSMouseMoved 199 location:[[self window] convertScreenToBase:[NSEvent mouseLocation]] 200 modifierFlags:[[NSApp currentEvent] modifierFlags] 201 timestamp:[NSDate timeIntervalSinceReferenceDate] 202 windowNumber:[[self window] windowNumber] 203 context:[[NSApp currentEvent] context] 204 eventNumber:0 clickCount:0 pressure:0]; 205 206 [[self class] _updateMouseoverWithEvent:fakeEvent]; 207} 208 209- (void)_cancelUpdateMouseoverTimer 210{ 211 if (_private->updateMouseoverTimer) { 212 CFRunLoopTimerInvalidate(_private->updateMouseoverTimer); 213 CFRelease(_private->updateMouseoverTimer); 214 _private->updateMouseoverTimer = NULL; 215 } 216} 217 218- (void)_stopAutoscrollTimer 219{ 220 NSTimer *timer = _private->autoscrollTimer; 221 _private->autoscrollTimer = nil; 222 [_private->autoscrollTriggerEvent release]; 223 _private->autoscrollTriggerEvent = nil; 224 [timer invalidate]; 225 [timer release]; 226} 227 228- (void)_setToolTip:(NSString *)toolTip 229{ 230 if (_private->usesDocumentViews) { 231 id documentView = [[[self _selectedOrMainFrame] frameView] documentView]; 232 if ([documentView isKindOfClass:[WebHTMLView class]]) 233 [documentView _setToolTip:toolTip]; 234 return; 235 } 236 237 // FIXME (Viewless): Code to handle tooltips needs to move into WebView. 238} 239 240@end 241