--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -295,19 +295,19 @@ PRBool gChildViewMethodsSwizzled = PR_FA
extern nsISupportsArray *gDraggedTransferables;
PRBool nsTSMManager::sIsIMEEnabled = PR_TRUE;
PRBool nsTSMManager::sIsRomanKeyboardsOnly = PR_FALSE;
PRBool nsTSMManager::sIgnoreCommit = PR_FALSE;
NSView<mozView>* nsTSMManager::sComposingView = nsnull;
TSMDocumentID nsTSMManager::sDocumentID = nsnull;
NSString* nsTSMManager::sComposingString = nsnull;
+ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
static NS_DEFINE_CID(kRegionCID, NS_REGION_CID);
-static NSView* sLastViewEntered = nil;
#ifdef INVALIDATE_DEBUGGING
static void blinkRect(Rect* r);
static void blinkRgn(RgnHandle rgn);
#endif
nsIRollupListener * gRollupListener = nsnull;
nsIWidget * gRollupWidget = nsnull;
@@ -349,18 +349,16 @@ PRUint32 nsChildView::sLastInputEventCou
- (NPEventModel)pluginEventModel;
- (BOOL)isRectObscuredBySubview:(NSRect)inRect;
- (void)processPendingRedraws;
- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
-- (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
-
- (void)maybeInitContextMenuTracking;
+ (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
- (void)maybeInvalidateShadow;
- (void)invalidateShadow;
#if USE_CLICK_HOLD_CONTEXTMENU
@@ -1679,16 +1677,50 @@ nsresult nsChildView::SynthesizeNativeKe
gOverrideKeyboardLayout = currentLayout;
}
return NS_OK;
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
+nsresult nsChildView::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
+ PRUint32 aNativeMessage,
+ PRUint32 aModifierFlags)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Move the mouse cursor to the requested position and reconnect it to the mouse.
+ CGWarpMouseCursorPosition(CGPointMake(aPoint.x, aPoint.y));
+ CGAssociateMouseAndMouseCursorPosition(true);
+
+ // aPoint is given with the origin on the top left, but convertScreenToBase
+ // expects a point in a coordinate system that has its origin on the bottom left.
+ NSPoint screenPoint = NSMakePoint(aPoint.x, [[NSScreen mainScreen] frame].size.height - aPoint.y);
+ NSPoint windowPoint = [[mView window] convertScreenToBase:screenPoint];
+
+ NSEvent* event = [NSEvent mouseEventWithType:aNativeMessage
+ location:windowPoint
+ modifierFlags:aModifierFlags
+ timestamp:[NSDate timeIntervalSinceReferenceDate]
+ windowNumber:[[mView window] windowNumber]
+ context:nil
+ eventNumber:0
+ clickCount:1
+ pressure:0.0];
+
+ if (!event)
+ return NS_ERROR_FAILURE;
+
+ [NSApp sendEvent:event];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
// First argument has to be an NSMenu representing the application's top-level
// menu bar. The returned item is *not* retained.
static NSMenuItem* NativeMenuItemWithLocation(NSMenu* menubar, NSString* locationString)
{
NSArray* indexes = [locationString componentsSeparatedByString:@"|"];
unsigned int indexCount = [indexes count];
if (indexCount == 0)
return nil;
@@ -2388,19 +2420,16 @@ NSEvent* gLastDragEvent = nil;
- (void)dealloc
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[mPendingDirtyRects release];
[mLastMouseDownEvent release];
if (mPluginTSMDoc)
::DeleteTSMDocument(mPluginTSMDoc);
-
- if (sLastViewEntered == self)
- sLastViewEntered = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
#ifndef NP_NO_QUICKDRAW
// This sets the current port to _savePort.
@@ -2409,16 +2438,17 @@ NSEvent* gLastDragEvent = nil;
#endif
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)widgetDestroyed
{
nsTSMManager::OnDestroyView(self);
+ ChildViewMouseTracker::OnDestroyView(self);
mGeckoChild = nsnull;
// Just in case we're destroyed abruptly and missed the draggingExited
// or performDragOperation message.
NS_IF_RELEASE(mDragService);
}
// mozView method, return our gecko child view widget. Note this does not AddRef.
@@ -2793,95 +2823,16 @@ static const PRInt32 sShadowInvalidation
pressure:[theEvent pressure]];
[self rightMouseDown:clickHoldEvent];
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#endif
-// We sometimes need to reroute events when there is a rollup widget and the
-// event isn't targeted at it.
-//
-// Rerouting may be needed when the user tries to navigate a context menu while
-// keeping the mouse-button down (left or right mouse button) -- the OS thinks this
-// is a dragging operation, so it sends events (mouseMoved and mouseUp) to the
-// window where the dragging operation started (the parent of the context
-// menu window). It also works around a bizarre Apple bug - if (while a context
-// menu is open) you move the mouse over another app's window and then back over
-// the context menu, mouseMoved events will be sent to the window underneath the
-// context menu.
-- (BOOL)ensureCorrectMouseEventTarget:(NSEvent*)anEvent
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
- // Don't bother if we've been destroyed: [self window] will now be nil, which
- // makes all our work here pointless, and can even cause us to resend the
- // event to ourselves in an infinte loop (since targetWindow == [self window] no
- // longer tests whether targetWindow is us).
- if (!mGeckoChild || ![self window])
- return YES;
-
- // Find the window that the event is over.
- BOOL isUnderMouse;
- NSWindow* targetWindow = nsCocoaUtils::FindWindowForEvent(anEvent, &isUnderMouse);
-
- // If this is the rollup widget and the event is not a mouse move then trust the OS routing.
- // The reason for this trust is complicated.
- //
- // There are three types of mouse events that can legitimately need to be targeted at a window
- // that they are not over. Mouse moves, mouse drags, and mouse ups. Anything else our app wouldn't
- // handle (if the mouse was not over any window) or it would go to the appropriate window.
- //
- // We need to do manual event rerouting for mouse moves because we know that in some cases, like
- // when there is a submenu opened from a popup window, the OS will route mouse move events to the
- // submenu even if the mouse is over the parent. Mouse move events are never tied to a particular
- // window because of some originating action like the starting point of a drag for drag events or
- // a mouse down event for mouse up events, so it is always safe to do our own routing on them here.
- //
- // As for mouse drags and mouse ups, they have originating actions that tie them to windows they
- // may no longer be over. If there is a rollup window present when one of these events is getting
- // processed but we are not it, we are probably the window where the action originated, and that
- // action must have caused the rollup window to come into existence. In that case, we might need
- // to reroute the event if it is over the rollup window. That is why if we're not the rollup window
- // we don't return YES here.
- if (gRollupWidget) {
- NSWindow* rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
- if ([self window] == rollupWindow && [anEvent type] != NSMouseMoved)
- return YES;
-
- // If the event was not over any window, send it to the rollup window.
- if (!isUnderMouse)
- targetWindow = rollupWindow;
- }
-
- // If there's no window that's more appropriate than our window then just return
- // yes so we handle it. No need to redirect.
- if (!targetWindow || targetWindow == [self window])
- return YES;
-
- // Send the event to its new destination.
- NSPoint newWindowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, targetWindow);
- NSEvent *newEvent = [NSEvent mouseEventWithType:[anEvent type]
- location:newWindowLocation
- modifierFlags:nsCocoaUtils::GetCocoaEventModifierFlags(anEvent)
- timestamp:GetCurrentEventTime()
- windowNumber:[targetWindow windowNumber]
- context:nil
- eventNumber:0
- clickCount:1
- pressure:0.0];
- [targetWindow sendEvent:newEvent];
-
- // Return NO because we just sent the event somewhere else.
- return NO;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
-}
-
// If we've just created a non-native context menu, we need to mark it as
// such and let the OS (and other programs) know when it opens and closes
// (this is how the OS knows to close other programs' context menus when
// ours open). We send the initial notification here, but others are sent
// in nsCocoaWindow::Show().
- (void)maybeInitContextMenuTracking
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@@ -3185,22 +3136,20 @@ static const PRInt32 sShadowInvalidation
mLastMouseDownEvent = nil;
return;
}
else {
[mLastMouseDownEvent release];
mLastMouseDownEvent = [theEvent retain];
}
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
nsAutoRetainCocoaObject kungFuDeathGrip(self);
- if ([self maybeRollup:theEvent])
+ if ([self maybeRollup:theEvent] ||
+ !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent))
return;
#if USE_CLICK_HOLD_CONTEXTMENU
// fire off timer to check for click-hold after two seconds. retains |theEvent|
[self performSelector:@selector(clickHoldCallback:) withObject:theEvent afterDelay:2.0];
#endif
// in order to send gecko events we'll need a gecko widget
@@ -3253,18 +3202,16 @@ static const PRInt32 sShadowInvalidation
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)mouseUp:(NSEvent *)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
if (!mGeckoChild)
return;
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
if (nsCocoaUtils::GetCocoaEventModifierFlags(theEvent) & NSControlKeyMask)
geckoEvent.button = nsMouseEvent::eRightButton;
@@ -3301,194 +3248,77 @@ static const PRInt32 sShadowInvalidation
geckoEvent.nativeMsg = &cocoaEvent;
}
mGeckoChild->DispatchWindowEvent(geckoEvent);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
-// sends a mouse enter or exit event into gecko
-static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted,
- PRUint32 msg,
- nsIWidget *widget,
- nsMouseEvent::reasonType aReason,
- NSPoint* localEventLocation,
- nsMouseEvent::exitType type,
- unsigned int modifierFlags,
- int buttonNumber,
- float deltaX,
- float deltaY,
- float deltaZ)
-{
- NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
-
- if (!widget || !localEventLocation)
- return nsEventStatus_eIgnore;
-
- nsMouseEvent event(isTrusted, msg, widget, aReason);
- event.refPoint.x = nscoord((PRInt32)localEventLocation->x);
- event.refPoint.y = nscoord((PRInt32)localEventLocation->y);
+- (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent
+ enter:(BOOL)aEnter
+ type:(nsMouseEvent::exitType)aType
+{
+ if (!mGeckoChild)
+ return;
+
+ NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, [self window]);
+ NSPoint localEventLocation = [self convertPoint:windowEventLocation fromView:nil];
+
+ PRUint32 msg = aEnter ? NS_MOUSE_ENTER : NS_MOUSE_EXIT;
+ nsMouseEvent event(PR_TRUE, msg, mGeckoChild, nsMouseEvent::eReal);
+ event.refPoint.x = nscoord((PRInt32)localEventLocation.x);
+ event.refPoint.y = nscoord((PRInt32)localEventLocation.y);
// Create event for use by plugins.
- // We need to know the plugin event model for the target widget.
- nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(widget);
- if (!pluginWidget)
- return nsEventStatus_eIgnore;
- int eventModel;
- pluginWidget->GetPluginEventModel(&eventModel);
-
+ // This is going to our child view so we don't need to look up the destination
+ // event type.
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
- if (static_cast<NPEventModel>(eventModel) == NPEventModelCarbon) {
+ if (mPluginEventModel == NPEventModelCarbon) {
carbonEvent.what = NPEventType_AdjustCursorEvent;
carbonEvent.message = 0;
carbonEvent.when = ::TickCount();
::GetGlobalMouse(&carbonEvent.where);
carbonEvent.modifiers = ::GetCurrentEventKeyModifiers();
event.nativeMsg = &carbonEvent;
}
#endif
NPCocoaEvent cocoaEvent;
- if (static_cast<NPEventModel>(eventModel) == NPEventModelCocoa) {
+ if (mPluginEventModel == NPEventModelCocoa) {
InitNPCocoaEvent(&cocoaEvent);
cocoaEvent.type = ((msg == NS_MOUSE_ENTER) ? NPCocoaEventMouseEntered : NPCocoaEventMouseExited);
- cocoaEvent.data.mouse.modifierFlags = modifierFlags;
+ cocoaEvent.data.mouse.modifierFlags = [aEvent modifierFlags];
cocoaEvent.data.mouse.pluginX = 5;
cocoaEvent.data.mouse.pluginY = 5;
- cocoaEvent.data.mouse.buttonNumber = buttonNumber;
- cocoaEvent.data.mouse.deltaX = deltaX;
- cocoaEvent.data.mouse.deltaY = deltaY;
- cocoaEvent.data.mouse.deltaZ = deltaZ;
+ cocoaEvent.data.mouse.buttonNumber = [aEvent buttonNumber];
+ cocoaEvent.data.mouse.deltaX = [aEvent deltaX];
+ cocoaEvent.data.mouse.deltaY = [aEvent deltaY];
+ cocoaEvent.data.mouse.deltaZ = [aEvent deltaZ];
event.nativeMsg = &cocoaEvent;
}
- event.exit = type;
-
- nsEventStatus status;
- widget->DispatchEvent(&event, status);
-
- // After the cursor exits the window set it to a visible regular arrow cursor.
- // This lets us recover from plugins that mess with it.
- if (msg == NS_MOUSE_EXIT && type == nsMouseEvent::eTopLevel) {
- [[nsCursorManager sharedInstance] setCursor:eCursor_standard];
- }
-
- return status;
-
- NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
-}
-
-- (void)mouseMoved:(NSEvent*)theEvent
+ event.exit = aType;
+
+ nsEventStatus status; // ignored
+ mGeckoChild->DispatchEvent(&event, status);
+}
+
+- (void)mouseMoved:(NSEvent*)aEvent
+{
+ ChildViewMouseTracker::MouseMoved(aEvent);
+}
+
+- (void)handleMouseMoved:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self window])
- return;
-
- // Work around an Apple bug that causes the OS to continue sending
- // mouseMoved events to a window for a while after it's been miniaturized.
- // This may be related to a similar problem with popup windows (bmo bug
- // 378645, popup windows continue to receive mouseMoved events after having
- // been "ordered out"), which is worked around in nsCocoaWindow::Show()
- // (search on 378645 in nsCocoaWindow.mm). This problem is bmo bug 410219,
- // and exists in both OS X 10.4 and 10.5.
- if ([[self window] isMiniaturized])
- return;
-
- NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, [self window]);
- NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
-
- // Installing a mouseMoved handler on the EventMonitor target (in
- // nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
- // events received here come from other processes. For this reason we need
- // to avoid processing them unless they're over a context menu -- otherwise
- // tooltips and other mouse-hover effects will "work" even when our app
- // doesn't have the focus.
- BOOL mouseEventIsOverRollupWidget = NO;
- if (gRollupWidget) {
- NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
- mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
- }
-
- if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
- if (sLastViewEntered) {
- nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
- NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
- SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
- &exitEventLocation, nsMouseEvent::eTopLevel,
- [theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
- [theEvent deltaY], [theEvent deltaZ]);
- sLastViewEntered = nil;
- }
- return;
- }
-
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
- NSView* view = [[[self window] contentView] hitTest:windowEventLocation];
- if (view) {
- // we shouldn't handle this if the hit view is not us
- if (view != (NSView*)self) {
- [view mouseMoved:theEvent];
- return;
- }
- }
- else {
- // If the hit test returned nil then the mouse isn't over the window. If thse mouse
- // exited the window then send mouse exit to the last view in the window it was over.
- if (sLastViewEntered) {
- NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
- // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
- nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
- SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
- &exitEventLocation, nsMouseEvent::eTopLevel,
- [theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
- [theEvent deltaY], [theEvent deltaZ]);
- sLastViewEntered = nil;
- }
- return;
- }
-
- // At this point we are supposed to handle this event. If we were not the last view entered, then
- // we should send an exit event to the last view entered and an enter event to ourselves.
if (!mGeckoChild)
return;
- nsAutoRetainCocoaObject kungFuDeathGrip(self);
- if (sLastViewEntered != self) {
- if (sLastViewEntered) {
- NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
- // NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
- nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
- SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
- &exitEventLocation, nsMouseEvent::eChild,
- [theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
- [theEvent deltaY], [theEvent deltaZ]);
-
- // The mouse exit event we just sent may have destroyed this widget, bail if that happened.
- if (!mGeckoChild)
- return;
- }
-
- // NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
- SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
- &viewEventLocation, nsMouseEvent::eChild,
- [theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
- [theEvent deltaY], [theEvent deltaZ]);
-
- // The mouse enter event we just sent may have destroyed this widget, bail if that happened.
- if (!mGeckoChild)
- return;
-
- // mark this view as the last view entered
- sLastViewEntered = (NSView*)self;
- }
-
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
// Create event for use by plugins.
// This is going to our child view so we don't need to look up the destination
// event type.
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
@@ -3521,19 +3351,16 @@ static nsEventStatus SendGeckoMouseEnter
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)mouseDragged:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
if (!mGeckoChild)
return;
gLastDragView = self;
gLastDragEvent = theEvent;
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
@@ -3577,19 +3404,16 @@ static nsEventStatus SendGeckoMouseEnter
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)rightMouseDown:(NSEvent *)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
nsAutoRetainCocoaObject kungFuDeathGrip(self);
[self maybeRollup:theEvent];
if (!mGeckoChild)
return;
// The right mouse went down, fire off a right mouse down event to gecko
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
@@ -3634,19 +3458,16 @@ static nsEventStatus SendGeckoMouseEnter
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)rightMouseUp:(NSEvent *)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
if (!mGeckoChild)
return;
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
geckoEvent.button = nsMouseEvent::eRightButton;
geckoEvent.clickCount = [theEvent clickCount];
@@ -3681,41 +3502,36 @@ static nsEventStatus SendGeckoMouseEnter
nsAutoRetainCocoaObject kungFuDeathGrip(self);
mGeckoChild->DispatchWindowEvent(geckoEvent);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
- (void)rightMouseDragged:(NSEvent*)theEvent
{
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
if (!mGeckoChild)
return;
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
geckoEvent.button = nsMouseEvent::eRightButton;
// send event into Gecko by going directly to the
// the widget.
mGeckoChild->DispatchWindowEvent(geckoEvent);
}
- (void)otherMouseDown:(NSEvent *)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
- if (![self ensureCorrectMouseEventTarget:theEvent])
- return;
-
nsAutoRetainCocoaObject kungFuDeathGrip(self);
- if ([self maybeRollup:theEvent])
+ if ([self maybeRollup:theEvent] ||
+ !ChildViewMouseTracker::WindowAcceptsEvent([self window], theEvent))
return;
if (!mGeckoChild)
return;
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_BUTTON_DOWN, nsnull, nsMouseEvent::eReal);
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
geckoEvent.button = nsMouseEvent::eMiddleButton;
@@ -6769,16 +6585,138 @@ nsTSMManager::CancelIME()
// composing in TSM. We also need to kill the our composing transaction too.
NSAttributedString* str = [[NSAttributedString alloc] initWithString:@""];
[sComposingView insertText:str];
[str release];
NS_OBJC_END_TRY_ABORT_BLOCK;
}
+#pragma mark -
+
+void
+ChildViewMouseTracker::OnDestroyView(ChildView* aView)
+{
+ if (sLastMouseEventView == aView)
+ sLastMouseEventView = nil;
+}
+
+void
+ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
+{
+ ChildView* view = ViewForEvent(aEvent);
+ if (view != sLastMouseEventView) {
+ // Send enter and / or exit events.
+ nsMouseEvent::exitType type = [view window] == [sLastMouseEventView window] ?
+ nsMouseEvent::eChild : nsMouseEvent::eTopLevel;
+ [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:NO type:type];
+ // After the cursor exits the window set it to a visible regular arrow cursor.
+ if (type == nsMouseEvent::eTopLevel) {
+ [[nsCursorManager sharedInstance] setCursor:eCursor_standard];
+ }
+ [view sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
+ }
+ sLastMouseEventView = view;
+ [view handleMouseMoved:aEvent];
+}
+
+ChildView*
+ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
+{
+ NSWindow* window = WindowForEvent(aEvent);
+ if (!window || !WindowAcceptsEvent(window, aEvent))
+ return nil;
+
+ NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
+ NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
+ NS_ASSERTION(view, "How can the mouse be over a window but not over a view in that window?");
+ return [view isKindOfClass:[ChildView class]] ? (ChildView*)view : nil;
+}
+
+// Find the active window under the mouse. Returns nil if the mouse isn't over
+// any active window.
+NSWindow*
+ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSInteger windowCount;
+ NSCountWindows(&windowCount);
+ NSInteger* windowList = (NSInteger*)malloc(sizeof(NSInteger) * windowCount);
+ if (!windowList)
+ return nil;
+ // The list we get back here is in order from front to back.
+ NSWindowList(windowCount, windowList);
+
+ NSPoint screenPoint = nsCocoaUtils::ScreenLocationForEvent(anEvent);
+
+ for (NSInteger i = 0; i < windowCount; i++) {
+ NSWindow* currentWindow = [NSApp windowWithWindowNumber:windowList[i]];
+ if (currentWindow && NSMouseInRect(screenPoint, [currentWindow frame], NO)) {
+ free(windowList);
+ return currentWindow;
+ }
+ }
+
+ free(windowList);
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+BOOL
+ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* anEvent)
+{
+ // Right mouse down events may get through to all windows, even to a top level
+ // window with an open sheet.
+ if (!aWindow || [anEvent type] == NSRightMouseDown)
+ return YES;
+
+ id delegate = [aWindow delegate];
+ if (!delegate || ![delegate isKindOfClass:[WindowDelegate class]])
+ return YES;
+
+ nsIWidget *windowWidget = [(WindowDelegate *)delegate geckoWidget];
+ if (!windowWidget)
+ return YES;
+
+ nsWindowType windowType;
+ windowWidget->GetWindowType(windowType);
+
+ switch (windowType) {
+ case eWindowType_popup:
+ // If this is a context menu, it won't have a parent. So we'll always
+ // accept mouse move events on context menus even when none of our windows
+ // is active, which is the right thing to do.
+ // For panels, the parent window is the XUL window that owns the panel.
+ return WindowAcceptsEvent([aWindow parentWindow], anEvent);
+
+ case eWindowType_toplevel:
+ case eWindowType_dialog:
+ // Block all mouse events other than RightMouseDown on background windows
+ // and on windows behind sheets.
+ return [aWindow isMainWindow] && ![aWindow attachedSheet];
+
+ case eWindowType_sheet: {
+ nsIWidget* parentWidget = windowWidget->GetSheetWindowParent();
+ if (!parentWidget)
+ return YES;
+
+ // Only accept mouse events on a sheet whose containing window is active.
+ NSWindow* parentWindow = (NSWindow*)parentWidget->GetNativeData(NS_NATIVE_WINDOW);
+ return [parentWindow isMainWindow];
+ }
+
+ default:
+ return YES;
+ }
+}
+
+#pragma mark -
+
// Target for text services events sent as the result of calls made to
// TSMProcessRawKeyEvent() in [ChildView keyDown:] (above) when a plugin has
// the focus. The calls to TSMProcessRawKeyEvent() short-circuit Cocoa-based
// IME (which would otherwise interfere with our efforts) and allow Carbon-
// based IME to work in plugins (via the NPAPI). This strategy doesn't cause
// trouble for plugins that (like the Java Embedding Plugin) bypass the NPAPI
// to get their keyboard events and do their own Cocoa-based IME.
OSStatus PluginKeyEventsHandler(EventHandlerCallRef inHandlerRef,
new file mode 100644
--- /dev/null
+++ b/widget/tests/native_mouse_mac_window.xul
@@ -0,0 +1,487 @@
+<?xml version="1.0"?>
+
+<!-- ***** BEGIN LICENSE BLOCK *****
+ - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ -
+ - The contents of this file are subject to the Mozilla Public License Version
+ - 1.1 (the "License"); you may not use this file except in compliance with
+ - the License. You may obtain a copy of the License at
+ - http://www.mozilla.org/MPL/
+ -
+ - Software distributed under the License is distributed on an "AS IS" basis,
+ - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ - for the specific language governing rights and limitations under the
+ - License.
+ -
+ - The Original Code is Native Menus Test code
+ -
+ - The Initial Developer of the Original Code is
+ - Mozilla Corporation.
+ - Portions created by the Initial Developer are Copyright (C) 2009
+ - the Initial Developer. All Rights Reserved.
+ -
+ - Contributor(s):
+ - Markus Stange <mstange@themasta.com>
+ -
+ - Alternatively, the contents of this file may be used under the terms of
+ - either the GNU General Public License Version 2 or later (the "GPL"), or
+ - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ - in which case the provisions of the GPL or the LGPL are applicable instead
+ - of those above. If you wish to allow use of your version of this file only
+ - under the terms of either the GPL or the LGPL, and not to allow others to
+ - use your version of this file under the terms of the MPL, indicate your
+ - decision by deleting the provisions above and replace them with the notice
+ - and other provisions required by the GPL or the LGPL. If you do not delete
+ - the provisions above, a recipient may use your version of this file under
+ - the terms of any one of the MPL, the GPL or the LGPL.
+ -
+ - ***** END LICENSE BLOCK ***** -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window id="NativeMenuWindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ width="600"
+ height="600"
+ title="Native Mouse Event Test"
+ orient="vertical">
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <box height="200" id="box"/>
+ <menupopup id="popup" width="250" height="50"/>
+ <panel id="panel" width="250" height="50" noautohide="true"/>
+
+ <script type="application/javascript"><![CDATA[
+
+ function ok(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.ok(condition, message);
+ }
+
+ function is(a, b, message) {
+ window.opener.wrappedJSObject.SimpleTest.is(a, b, message);
+ }
+
+ function todo(condition, message) {
+ window.opener.wrappedJSObject.SimpleTest.todo(condition, message);
+ }
+
+ function todo_is(a, b, message) {
+ window.opener.wrappedJSObject.SimpleTest.todo_is(a, b, message);
+ }
+
+ function onTestsFinished() {
+ gRightWindow.close();
+ window.close();
+ window.opener.wrappedJSObject.SimpleTest.finish();
+ }
+
+ const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+ const xulWin = 'data:application/vnd.mozilla.xul+xml,<?xml version="1.0"?><?xml-stylesheet href="chrome://global/skin" type="text/css"?><window xmlns="' + XUL_NS + '"/>';
+
+ const NSLeftMouseDown = 1,
+ NSLeftMouseUp = 2,
+ NSRightMouseDown = 3,
+ NSRightMouseUp = 4,
+ NSMouseMoved = 5,
+ NSLeftMouseDragged = 6,
+ NSRightMouseDragged = 7,
+ NSMouseEntered = 8,
+ NSMouseExited = 9,
+ NSKeyDown = 10,
+ NSKeyUp = 11,
+ NSFlagsChanged = 12,
+ NSAppKitDefined = 13,
+ NSSystemDefined = 14,
+ NSApplicationDefined = 15,
+ NSPeriodic = 16,
+ NSCursorUpdate = 17,
+ NSScrollWheel = 22,
+ NSTabletPoint = 23,
+ NSTabletProximity = 24,
+ NSOtherMouseDown = 25,
+ NSOtherMouseUp = 26,
+ NSOtherMouseDragged = 27,
+ NSEventTypeGesture = 29,
+ NSEventTypeMagnify = 30,
+ NSEventTypeSwipe = 31,
+ NSEventTypeRotate = 18,
+ NSEventTypeBeginGesture = 19,
+ NSEventTypeEndGesture = 20;
+
+ var gExpectedEvents = [];
+ var gRightWindow = null, gPopup = null;
+
+ function testMouse(x, y, msg, elem, win, exp, callback) {
+ clearExpectedEvents();
+ exp.forEach(function (expEv) {
+ expEv.screenX = x;
+ expEv.screenY = y;
+ gExpectedEvents.push(expEv);
+ });
+ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
+ var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIDOMWindowUtils);
+ utils.sendNativeMouseEvent(x, y, msg, 0, elem);
+ SimpleTest.executeSoon(function () {
+ clearExpectedEvents();
+ callback();
+ });
+ }
+
+ function eventListenOnce(elem, name, callback) {
+ elem.addEventListener(name, function(e) {
+ elem.removeEventListener(name, arguments.callee, false);
+ callback(e);
+ }, false);
+ }
+
+ function focusAndThen(win, callback) {
+ eventListenOnce(win, "focus", callback);
+ win.focus();
+ }
+
+ function eventToString(e) {
+ return JSON.stringify({
+ type: e.type, target: e.target.nodeName, screenX: e.screenX, screenY: e.screenY
+ });
+ }
+
+ function clearExpectedEvents() {
+ while (gExpectedEvents.length > 0) {
+ var expectedEvent = gExpectedEvents.shift();
+ var errFun = expectedEvent.todoShouldHaveFired ? todo : ok;
+ errFun(false, "didn't receive expected event: " + eventToString(expectedEvent));
+ }
+ }
+
+ var gEventNum = 0;
+
+ function eventMonitor(e) {
+ var expectedEvent = gExpectedEvents.shift();
+ while (expectedEvent && expectedEvent.todoShouldHaveFired) {
+ todo(false, "Should have got event: " + eventToString(expectedEvent));
+ expectedEvent = gExpectedEvents.shift();
+ }
+ if (!expectedEvent) {
+ ok(false, "received event I didn't expect: " + eventToString(e));
+ return true;
+ }
+ gEventNum++;
+ is(e.screenX, expectedEvent.screenX, gEventNum + " | wrong X coord for event " + eventToString(e));
+ is(e.screenY, expectedEvent.screenY, gEventNum + " | wrong Y coord for event " + eventToString(e));
+ is(e.type, expectedEvent.type, gEventNum + " | wrong event type for event " + eventToString(e));
+ is(e.target, expectedEvent.target, gEventNum + " | wrong target for event " + eventToString(e));
+ if (expectedEvent.todoShouldNotHaveFired) {
+ todo(false, gEventNum + " | Got an event that should not have fired: " + eventToString(e));
+ }
+ }
+
+ function observe(elem, fun) {
+ elem.addEventListener("mousemove", fun, false);
+ elem.addEventListener("mouseover", fun, false);
+ elem.addEventListener("mouseout", fun, false);
+ elem.addEventListener("mousedown", fun, false);
+ elem.addEventListener("mouseup", fun, false);
+ elem.addEventListener("click", fun, false);
+ }
+
+ function start() {
+ window.resizeTo(200, 200);
+ window.moveTo(50, 50);
+ gRightWindow = open(xulWin, '', 'chrome,screenX=300,screenY=50,width=200,height=200');
+ eventListenOnce(gRightWindow, "focus", function () {
+ focusAndThen(window, runTests);
+ });
+ gPopup = document.getElementById("popup");
+ }
+
+ function runTests() {
+ observe(window, eventMonitor);
+ observe(gRightWindow, eventMonitor);
+ observe(gPopup, eventMonitor);
+ var left = window, right = gRightWindow;
+ var leftElem = document.getElementById("box");
+ var rightElem = gRightWindow.document.documentElement;
+ var panel = document.getElementById("panel");
+ var tooltip = (function createTooltipInRightWindow() {
+ var _tooltip = right.document.createElementNS(XUL_NS, "tooltip");
+ _tooltip.setAttribute("id", "tip");
+ _tooltip.setAttribute("width", "80");
+ _tooltip.setAttribute("height", "20");
+ right.document.documentElement.appendChild(_tooltip);
+ return _tooltip;
+ })();
+ var tests = [
+ // Enter the left window, which is focused.
+ [150, 150, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem }
+ ]],
+ // Test that moving inside the window fires mousemove events.
+ [170, 150, NSMouseMoved, null, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Leaving the window should fire a mouseout event...
+ [170, 20, NSMouseMoved, null, left, [
+ { type: "mouseout", target: leftElem },
+ ]],
+ // ... and entering a mouseover event.
+ [170, 120, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Move over the right window, which is inactive.
+ // Inactive windows shouldn't respond to mousemove events,
+ // so we should only get a mouseout event, no mouseover event.
+ [400, 150, NSMouseMoved, null, right, [
+ { type: "mouseout", target: leftElem },
+ ]],
+ // Clicking an inactive window shouldn't have any effect, other
+ // than focusing it.
+ [400, 150, NSLeftMouseDown, null, right, [
+ ]],
+ [400, 150, NSLeftMouseUp, null, right, [
+ ]],
+ // Now it's focused, so we should get a mousedown event when clicking.
+ [400, 150, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ { type: "mouseover", target: rightElem, todoShouldHaveFired: true },
+ ]],
+ // Let's drag to the right without letting the button go. It would be better
+ // if the mouseover event had fired as soon as the mouse entered the window,
+ // and not only when dragging, but that's ok.
+ [410, 150, NSLeftMouseDragged, null, right, [
+ { type: "mouseover", target: rightElem, todoShouldNotHaveFired: true },
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Let go of the mouse.
+ [410, 150, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // Now we're being sneaky. The left window is inactive, but *right*-clicks to it
+ // should still get through. Test that.
+ // Ideally we'd be bracketing that event with over and out events, too, but it
+ // probably doesn't matter too much.
+ [150, 170, NSRightMouseDown, null, left, [
+ { type: "mouseover", target: leftElem, todoShouldHaveFired: true },
+ { type: "mousedown", target: leftElem },
+ { type: "mouseout", target: leftElem, todoShouldHaveFired: true },
+ ]],
+ // Let go of the mouse.
+ [150, 170, NSRightMouseUp, null, left, [
+ { type: "mouseover", target: leftElem, todoShouldHaveFired: true },
+ { type: "mouseup", target: leftElem },
+ { type: "click", target: leftElem },
+ { type: "mouseout", target: leftElem, todoShouldHaveFired: true },
+ ]],
+ // Right clicking hasn't focused it, so the window is still inactive.
+ // Let's focus it; this time without the mouse, for variaton's sake.
+ function raiseLeftWindow(callback) {
+ focusAndThen(left, callback);
+ },
+ // It's active, so it should respond to mousemove events now.
+ [150, 170, NSMouseMoved, null, left, [
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+
+ // This was boring... let's introduce a popup. It will overlap both the left
+ // and the right window.
+ function openPopupInLeftWindow(callback) {
+ eventListenOnce(gPopup, "popupshown", callback);
+ gPopup.openPopupAtScreen(150, 50, true);
+ },
+ // Move the mouse over the popup.
+ // We'll get duplicate events on the popup; ignore them.
+ [200, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mouseover", target: gPopup, todoShouldNotHaveFired: true },
+ { type: "mousemove", target: gPopup },
+ { type: "mousemove", target: gPopup, todoShouldNotHaveFired: true },
+ ]],
+ // Move the mouse back over the left window outside the popup.
+ [160, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: gPopup },
+ { type: "mouseout", target: gPopup, todoShouldNotHaveFired: true },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ // Back over the popup... (double events again)
+ [190, 80, NSMouseMoved, gPopup, left, [
+ { type: "mouseout", target: leftElem },
+ { type: "mouseover", target: gPopup },
+ { type: "mouseover", target: gPopup, todoShouldNotHaveFired: true },
+ { type: "mousemove", target: gPopup },
+ { type: "mousemove", target: gPopup, todoShouldNotHaveFired: true },
+ ]],
+ // ...and over into the right window. (... again)
+ // It's inactive, so it shouldn't get mouseover events yet.
+ [400, 170, NSMouseMoved, null, right, [
+ { type: "mouseout", target: gPopup },
+ { type: "mouseout", target: gPopup, todoShouldNotHaveFired: true },
+ ]],
+ // Again, no mouse events please, even though a popup is open. (bug 425556)
+ [400, 180, NSMouseMoved, null, right, [
+ ]],
+ // Activate the right window with a click.
+ // This will close the popup.
+ [400, 180, NSLeftMouseDown, null, right, [
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ ]],
+ function verifyPopupClosed(callback) {
+ is(gPopup.popupBoxObject.popupState, "closed", "popup should have closed when clicking");
+ callback();
+ },
+ // Now the right window is active; click it again, just for fun.
+ // (Would be good to have a mouseover event here.)
+ [400, 180, NSLeftMouseDown, null, right, [
+ { type: "mouseover", target: rightElem, todoShouldHaveFired: true },
+ { type: "mousedown", target: rightElem },
+ ]],
+ [400, 180, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+
+ // Time for our next trick: a tooltip!
+ // Install the tooltip, but don't show it yet.
+ function setTooltip(callback) {
+ rightElem.setAttribute("tooltip", "tip");
+ callback();
+ },
+ // Move the mouse to trigger the appearance of the tooltip.
+ // ... and what's that, a mousemove event without preceding mouseover? Bad.
+ [410, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Wait for the tooltip to appear.
+ function (callback) {
+ var timer = setTimeout(callback, 2000); // just in case the tooltip is shy
+ eventListenOnce(rightElem, "popupshown", function () {
+ clearTimeout(timer);
+ callback();
+ });
+ },
+ // Now the tooltip is visible.
+ // Move the mouse a little to the right, but send the event to the tooltip's
+ // widget, even though the mouse is not over the tooltip, because that's what
+ // Mac OS X does.
+ [411, 180, NSMouseMoved, tooltip, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move another pixel. This time send the event to the right widget.
+ // However, that must not make a difference.
+ [412, 180, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Move up and click to make the tooltip go away.
+ [412, 80, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseDown, null, right, [
+ { type: "mousedown", target: rightElem },
+ ]],
+ [412, 80, NSLeftMouseUp, null, right, [
+ { type: "mouseup", target: rightElem },
+ { type: "click", target: rightElem },
+ ]],
+ // OK, next round. Open a panel in the left window, which is inactive.
+ function openPanel(callback) {
+ eventListenOnce(panel, "popupshown", callback);
+ panel.openPopupAtScreen(150, 150, false);
+ },
+ // The panel is parented, so it will be z-ordered over its parent but
+ // under the active window.
+ // Now we move the mouse over the part where the panel rect intersects the
+ // right window's rect. Since the panel is under the window, all the events
+ // should target the right window.
+ // Try with sending to three different targets.
+ [390, 170, NSMouseMoved, null, right, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [390, 171, NSMouseMoved, null, left, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ [391, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: rightElem },
+ ]],
+ // Now move off the right window, so that the mouse is directly over the
+ // panel.
+ [260, 170, NSMouseMoved, null, left, [
+ { type: "mouseout", target: rightElem },
+ ]],
+ [260, 171, NSMouseMoved, null, left, [
+ ]],
+ [261, 171, NSMouseMoved, panel, left, [
+ ]],
+ // Let's be evil and click it.
+ [261, 171, NSLeftMouseDown, panel, left, [
+ ]],
+ [261, 171, NSLeftMouseUp, panel, left, [
+ { type: "mouseup", target: panel },
+ ]],
+ // This didn't focus the window, unfortunately, so let's do it ourselves.
+ function raiseLeftWindowTakeTwo(callback) {
+ focusAndThen(left, callback);
+ },
+ // Now mouse events should get through to the panel (which is now over the
+ // right window).
+ [387, 170, NSMouseMoved, null, right, [
+ { type: "mouseover", target: panel },
+ { type: "mousemove", target: panel },
+ { type: "mouseout", target: panel, todoShouldNotHaveFired: true },
+ { type: "mouseover", target: left.document.documentElement, todoShouldNotHaveFired: true },
+ ]],
+ // Why does left.document.documentElement get entered? This makes no sense.
+ [387, 171, NSMouseMoved, null, left, [
+ { type: "mouseout", target: left.document.documentElement, todoShouldNotHaveFired: true },
+ { type: "mouseover", target: panel, todoShouldNotHaveFired: true },
+ { type: "mousemove", target: panel },
+ ]],
+ [388, 171, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: panel },
+ ]],
+ // Click the panel.
+ [388, 171, NSLeftMouseDown, panel, left, [
+ { type: "mousedown", target: panel }
+ ]],
+ [388, 171, NSLeftMouseUp, panel, left, [
+ { type: "mouseup", target: panel },
+ { type: "click", target: panel },
+ ]],
+
+ // Last test for today: Hit testing in the Canyon of Nowhere -
+ // the pixel row directly south of the panel, over the left window.
+ // Before bug 515003 we wrongly thought the mouse wasn't over any window.
+ [173, 200, NSMouseMoved, panel, left, [
+ { type: "mouseout", target: panel },
+ { type: "mouseover", target: leftElem },
+ { type: "mousemove", target: leftElem },
+ ]],
+ [173, 201, NSMouseMoved, panel, left, [
+ { type: "mousemove", target: leftElem },
+ ]],
+ ];
+ function runNextTest() {
+ if (!tests.length)
+ return onTestsFinished();
+
+ var test = tests.shift();
+ if (typeof test == "function")
+ return test(runNextTest);
+
+ var [x, y, msg, elem, win, exp] = test;
+ testMouse(x, y, msg, elem, win, exp, runNextTest);
+ }
+ runNextTest();
+ }
+
+ SimpleTest.waitForFocus(start);
+
+ ]]></script>
+</window>