• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38 using 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 
55 static 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 
120 done:
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