Home | History | Annotate | Download | only in WebView
      1 /*
      2  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      3  * Copyright (C) 2006 David Smith (catfish.man (at) 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