Backed out changeset fee47b64b378 (bug 675208) to see whether it's responsible for the 60% Trace Malloc MaxHeap increase.
authorMarkus Stange <mstange@themasta.com>
Tue, 09 Aug 2011 13:14:10 +0200
changeset 74115 6b943c8d7c5e9b4b82c71a35c6af97ad1977d4d7
parent 74018 fee47b64b3783e70b69987ff828479531fc2fbf1
child 74116 ca13b9114ce6f93aa74da351496d8a4752874195
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs675208
milestone8.0a1
backs outfee47b64b3783e70b69987ff828479531fc2fbf1
Backed out changeset fee47b64b378 (bug 675208) to see whether it's responsible for the 60% Trace Malloc MaxHeap increase.
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsCocoaWindow.h
widget/src/cocoa/nsCocoaWindow.mm
widget/src/cocoa/nsToolkit.h
widget/src/cocoa/nsToolkit.mm
widget/tests/native_mouse_mac_window.xul
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -289,26 +289,26 @@ extern "C" long TSMProcessRawKeyEvent(Ev
 @end
 
 class ChildViewMouseTracker {
 
 public:
 
   static void MouseMoved(NSEvent* aEvent);
   static void OnDestroyView(ChildView* aView);
-  static void OnDestroyWindow(NSWindow* aWindow);
   static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
                                  ChildView* aView, BOOL isClickThrough = NO);
-  static void MouseExitedWindow(NSEvent* aEvent);
-  static void MouseEnteredWindow(NSEvent* aEvent);
   static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil);
   static ChildView* ViewForEvent(NSEvent* aEvent);
 
   static ChildView* sLastMouseEventView;
-  static NSWindow* sWindowUnderMouse;
+
+private:
+
+  static NSWindow* WindowForEvent(NSEvent* aEvent);
 };
 
 //-------------------------------------------------------------------------
 //
 // nsChildView
 //
 //-------------------------------------------------------------------------
 
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -107,30 +107,37 @@ using namespace mozilla;
 #ifdef PR_LOGGING
 PRLogModuleInfo* sCocoaLog = nsnull;
 #endif
 
 extern "C" {
   CG_EXTERN void CGContextResetCTM(CGContextRef);
   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
   CG_EXTERN void CGContextResetClip(CGContextRef);
+
+  // CGSPrivate.h
+  typedef NSInteger CGSConnection;
+  typedef NSInteger CGSWindow;
+  extern CGSConnection _CGSDefaultConnection();
+  extern CGError CGSGetScreenRectForWindow(const CGSConnection cid, CGSWindow wid, CGRect *outRect);
+  extern CGError CGSGetWindowLevel(const CGSConnection cid, CGSWindow wid, CGWindowLevel *level);
+  extern CGError CGSGetWindowAlpha(const CGSConnection cid, const CGSWindow wid, float* alpha);
 }
 
 // defined in nsMenuBarX.mm
 extern NSMenu* sApplicationMenu; // Application menu shared by all menubars
 
 // these are defined in nsCocoaWindow.mm
 extern PRBool gConsumeRollupEvent;
 
 PRBool gChildViewMethodsSwizzled = PR_FALSE;
 
 extern nsISupportsArray *gDraggedTransferables;
 
 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
-NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
 
 #ifdef INVALIDATE_DEBUGGING
 static void blinkRect(Rect* r);
 static void blinkRgn(RgnHandle rgn);
 #endif
 
 nsIRollupListener * gRollupListener = nsnull;
 nsIMenuRollup     * gMenuRollup = nsnull;
@@ -1224,34 +1231,16 @@ nsresult nsChildView::SynthesizeNativeMo
                                        context:nil
                                    eventNumber:0
                                     clickCount:1
                                       pressure:0.0];
 
   if (!event)
     return NS_ERROR_FAILURE;
 
-  if ([[mView window] isKindOfClass:[BaseWindow class]]) {
-    // Tracking area events don't end up in their tracking areas when sent
-    // through [NSApp sendEvent:], so pass them directly to the right methods.
-    BaseWindow* window = (BaseWindow*)[mView window];
-    if (aNativeMessage == NSMouseEntered) {
-      [window mouseEntered:event];
-      return NS_OK;
-    }
-    if (aNativeMessage == NSMouseExited) {
-      [window mouseExited:event];
-      return NS_OK;
-    }
-    if (aNativeMessage == NSMouseMoved) {
-      [window mouseMoved:event];
-      return NS_OK;
-    }
-  }
-
   [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.
@@ -3240,16 +3229,21 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
 
   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 (!mGeckoChild)
     return;
 
   nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
@@ -4757,43 +4751,18 @@ NSEvent* gLastDragMouseDownEvent = nil;
 
 @end
 
 #pragma mark -
 
 void
 ChildViewMouseTracker::OnDestroyView(ChildView* aView)
 {
-  if (sLastMouseEventView == aView) {
+  if (sLastMouseEventView == aView)
     sLastMouseEventView = nil;
-  }
-}
-
-void
-ChildViewMouseTracker::OnDestroyWindow(NSWindow* aWindow)
-{
-  if (sWindowUnderMouse == aWindow) {
-    sWindowUnderMouse = nil;
-  }
-}
-
-void
-ChildViewMouseTracker::MouseEnteredWindow(NSEvent* aEvent)
-{
-  sWindowUnderMouse = [aEvent window];
-  ReEvaluateMouseEnterState(aEvent);
-}
-
-void
-ChildViewMouseTracker::MouseExitedWindow(NSEvent* aEvent)
-{
-  if (sWindowUnderMouse == [aEvent window]) {
-    sWindowUnderMouse = nil;
-    ReEvaluateMouseEnterState(aEvent);
-  }
 }
 
 void
 ChildViewMouseTracker::ReEvaluateMouseEnterState(NSEvent* aEvent)
 {
   ChildView* oldView = sLastMouseEventView;
   sLastMouseEventView = ViewForEvent(aEvent);
   if (sLastMouseEventView != oldView) {
@@ -4807,39 +4776,136 @@ ChildViewMouseTracker::ReEvaluateMouseEn
     }
     [sLastMouseEventView sendMouseEnterOrExitEvent:aEvent enter:YES type:type];
   }
 }
 
 void
 ChildViewMouseTracker::MouseMoved(NSEvent* aEvent)
 {
-  MouseEnteredWindow(aEvent);
+  ReEvaluateMouseEnterState(aEvent);
   [sLastMouseEventView handleMouseMoved:aEvent];
 }
 
 ChildView*
 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
 {
-  NSWindow* window = sWindowUnderMouse;
+  NSWindow* window = WindowForEvent(aEvent);
   if (!window)
     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?");
   if (![view isKindOfClass:[ChildView class]])
     return nil;
 
   ChildView* childView = (ChildView*)view;
   // If childView is being destroyed return nil.
   if (![childView widget])
     return nil;
   return WindowAcceptsEvent(window, aEvent, childView) ? childView : nil;
 }
 
+static CGWindowLevel kDockWindowLevel = 0;
+static CGWindowLevel kPopupWindowLevel = 0;
+
+static BOOL WindowNumberIsUnderPoint(NSInteger aWindowNumber, NSPoint aPoint) {
+  NSWindow* window = [NSApp windowWithWindowNumber:aWindowNumber];
+  if (window) {
+    // This is one of our own windows.
+    return NSMouseInRect(aPoint, [window frame], NO);
+  }
+
+  CGSConnection cid = _CGSDefaultConnection();
+
+  if (!kDockWindowLevel) {
+    // These constants are in fact function calls, so cache them.
+    kDockWindowLevel = kCGDockWindowLevel;
+    kPopupWindowLevel = kCGPopUpMenuWindowLevel;
+  }
+
+  // Some things put transparent windows on top of everything. Ignore them.
+  CGWindowLevel level;
+  if ((kCGErrorSuccess == CGSGetWindowLevel(cid, aWindowNumber, &level)) &&
+      (level == kDockWindowLevel ||     // Transparent layer, spanning the whole screen
+       level > kPopupWindowLevel))      // Snapz Pro X while recording a screencast
+    return false;
+
+  // Ignore transparent windows.
+  float alpha;
+  if ((kCGErrorSuccess == CGSGetWindowAlpha(cid, aWindowNumber, &alpha)) &&
+      alpha < 0.1f)
+    return false;
+
+  CGRect rect;
+  if (kCGErrorSuccess != CGSGetScreenRectForWindow(cid, aWindowNumber, &rect))
+    return false;
+
+  CGPoint point = { aPoint.x, nsCocoaUtils::FlippedScreenY(aPoint.y) };
+  return CGRectContainsPoint(rect, point);
+}
+
+// Find the window number of the window under the given point, regardless of
+// which app the window belongs to. Returns 0 if no window was found.
+static NSInteger WindowNumberAtPoint(NSPoint aPoint) {
+  // We'd like to use the new windowNumberAtPoint API on 10.6 but we can't rely
+  // on it being up-to-date. For example, if we've just opened a window,
+  // windowNumberAtPoint might not know about it yet, so we'd send events to the
+  // wrong window. See bug 557986.
+  // So we'll have to find the right window manually by iterating over all
+  // windows on the screen and testing whether the mouse is inside the window's
+  // rect. We do this using private CGS functions.
+  // Another way of doing it would be to use tracking rects, but those are
+  // view-controlled, so they need to be reset whenever an NSView changes its
+  // size or position, which is expensive. See bug 300904 comment 20.
+  // A problem with using the CGS functions is that we only look at the windows'
+  // rects, not at the transparency of the actual pixel the mouse is over. This
+  // means that we won't treat transparent pixels as transparent to mouse
+  // events, which is a disadvantage over using tracking rects and leads to the
+  // crummy window level workarounds in WindowNumberIsUnderPoint.
+  // But speed is more important.
+
+  // Get the window list.
+  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);
+  for (NSInteger i = 0; i < windowCount; i++) {
+    NSInteger windowNumber = windowList[i];
+    if (WindowNumberIsUnderPoint(windowNumber, aPoint)) {
+      free(windowList);
+      return windowNumber;
+    }
+  }
+
+  free(windowList);
+  return 0;
+}
+
+// Find Gecko window under the mouse. Returns nil if the mouse isn't over
+// any of our windows.
+NSWindow*
+ChildViewMouseTracker::WindowForEvent(NSEvent* anEvent)
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+  NSPoint screenPoint = nsCocoaUtils::ScreenLocationForEvent(anEvent);
+  NSInteger windowNumber = WindowNumberAtPoint(screenPoint);
+
+  // This will return nil if windowNumber belongs to a window that we don't own.
+  return [NSApp windowWithWindowNumber:windowNumber];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
 BOOL
 ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
                                           ChildView* aView, BOOL aIsClickThrough)
 {
   // Right mouse down events may get through to all windows, even to a top level
   // window with an open sheet.
   if (!aWindow || [aEvent type] == NSRightMouseDown)
     return YES;
--- a/widget/src/cocoa/nsCocoaWindow.h
+++ b/widget/src/cocoa/nsCocoaWindow.h
@@ -76,37 +76,29 @@ typedef struct _nsCocoaWindowList {
 
   // Shadow
   BOOL mScheduledShadowInvalidation;
 
   // DPI cache. Getting the physical screen size (CGDisplayScreenSize)
   // is ridiculously slow, so we cache it in the toplevel window for all
   // descendants to use.
   float mDPI;
-
-  NSTrackingArea* mTrackingArea;
 }
 
 - (void)importState:(NSDictionary*)aState;
 - (NSMutableDictionary*)exportState;
 - (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
 - (BOOL)drawsContentsIntoWindowFrame;
 - (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
 - (NSColor*)titlebarColorForActiveWindow:(BOOL)aActive;
 
 - (void)deferredInvalidateShadow;
 - (void)invalidateShadow;
 - (float)getDPI;
 
-- (void)mouseEntered:(NSEvent*)aEvent;
-- (void)mouseExited:(NSEvent*)aEvent;
-- (void)mouseMoved:(NSEvent*)aEvent;
-- (void)updateTrackingArea;
-- (NSView*)trackingAreaView;
-
 @end
 
 @interface NSWindow (Undocumented)
 
 // If a window has been explicitly removed from the "window cache" (to
 // deactivate it), it's sometimes necessary to "reset" it to reactivate it
 // (and put it back in the "window cache").  One way to do this, which Apple
 // often uses, is to set the "window number" to '-1' and then back to its
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -637,30 +637,32 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // this sheet can be displayed. Leave the parent mSheetNeedsShow alone,
       // that is only used to handle sibling sheet contention. The parent will
       // return once there are no more child sheets.
       PRBool parentIsSheet = PR_FALSE;
       if (NS_SUCCEEDED(piParentWidget->GetIsSheet(&parentIsSheet)) &&
           parentIsSheet) {
         piParentWidget->GetSheetWindowParent(&topNonSheetWindow);
         [NSApp endSheet:nativeParentWindow];
+        [nativeParentWindow setAcceptsMouseMovedEvents:NO];
       }
 
       nsCocoaWindow* sheetShown = nsnull;
       if (NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_TRUE, &sheetShown)) &&
           (!sheetShown || sheetShown == this)) {
         // If this sheet is already the sheet actually being shown, don't
         // tell it to show again. Otherwise the number of calls to
         // [NSApp beginSheet...] won't match up with [NSApp endSheet...].
         if (![mWindow isVisible]) {
           mSheetNeedsShow = PR_FALSE;
           mSheetWindowParent = topNonSheetWindow;
           // Only set contextInfo if our parent isn't a sheet.
           NSWindow* contextInfo = parentIsSheet ? nil : mSheetWindowParent;
           [TopLevelWindowData deactivateInWindow:mSheetWindowParent];
+          [mWindow setAcceptsMouseMovedEvents:YES];
           [NSApp beginSheet:mWindow
              modalForWindow:mSheetWindowParent
               modalDelegate:mDelegate
              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
                 contextInfo:contextInfo];
           [TopLevelWindowData activateInWindow:mWindow];
           SendSetZLevelEvent();
         }
@@ -677,16 +679,17 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // for it to receive any mouse events aside from mouse-moved events
       // (because it was removed from the "window cache" when it was hidden
       // -- see below).  Setting the window number to -1 and then back to its
       // original value seems to accomplish this.  The idea was "borrowed"
       // from the Java Embedding Plugin.
       NSInteger windowNumber = [mWindow windowNumber];
       [mWindow _setWindowNumber:-1];
       [mWindow _setWindowNumber:windowNumber];
+      [mWindow setAcceptsMouseMovedEvents:YES];
       // For reasons that aren't yet clear, calls to [NSWindow orderFront:] or
       // [NSWindow makeKeyAndOrderFront:] can sometimes trigger "Error (1000)
       // creating CGSWindow", which in turn triggers an internal inconsistency
       // NSException.  These errors shouldn't be fatal.  So we need to wrap
       // calls to ...orderFront: in LOGONLY blocks.  See bmo bug 470864.
       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
       [mWindow orderFront:nil];
       NS_OBJC_END_TRY_LOGONLY_BLOCK;
@@ -708,16 +711,17 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // appear above the parent and move when the parent does. Setting this
       // needs to happen after the _setWindowNumber calls above, otherwise the
       // window doesn't focus properly.
       if (nativeParentWindow && mPopupLevel == ePopupLevelParent)
         [nativeParentWindow addChildWindow:mWindow
                             ordered:NSWindowAbove];
     }
     else {
+      [mWindow setAcceptsMouseMovedEvents:YES];
       NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
       [mWindow makeKeyAndOrderFront:nil];
       NS_OBJC_END_TRY_LOGONLY_BLOCK;
       SendSetZLevelEvent();
     }
   }
   else {
     // roll up any popups if a top-level window is going away
@@ -734,16 +738,18 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       }
       else {
         // get sheet's parent *before* hiding the sheet (which breaks the linkage)
         NSWindow* sheetParent = mSheetWindowParent;
         
         // hide the sheet
         [NSApp endSheet:mWindow];
         
+        [mWindow setAcceptsMouseMovedEvents:NO];
+
         [TopLevelWindowData deactivateInWindow:mWindow];
 
         nsCocoaWindow* siblingSheetToShow = nsnull;
         PRBool parentIsSheet = PR_FALSE;
 
         if (nativeParentWindow && piParentWidget &&
             NS_SUCCEEDED(piParentWidget->GetChildSheet(PR_FALSE, &siblingSheetToShow)) &&
             siblingSheetToShow) {
@@ -763,28 +769,30 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
             if (piGrandparentWidget && NS_SUCCEEDED(piGrandparentWidget->GetIsSheet(&grandparentIsSheet)) &&
                 grandparentIsSheet) {
                 contextInfo = nil;
             }
           }
           // If there are no sibling sheets, but the parent is a sheet, restore
           // it.  It wasn't sent any deactivate events when it was hidden, so
           // don't call through Show, just let the OS put it back up.
+          [nativeParentWindow setAcceptsMouseMovedEvents:YES];
           [NSApp beginSheet:nativeParentWindow
              modalForWindow:sheetParent
               modalDelegate:[nativeParentWindow delegate]
              didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
                 contextInfo:contextInfo];
         }
         else {
           // Sheet, that was hard.  No more siblings or parents, going back
           // to a real window.
           NS_OBJC_BEGIN_TRY_LOGONLY_BLOCK;
           [sheetParent makeKeyAndOrderFront:nil];
           NS_OBJC_END_TRY_LOGONLY_BLOCK;
+          [sheetParent setAcceptsMouseMovedEvents:YES];
         }
         SendSetZLevelEvent();
       }
     }
     else {
       // If the window is a popup window with a parent window we need to
       // unhook it here before ordering it out. When you order out the child
       // of a window it hides the parent window.
@@ -800,16 +808,20 @@ NS_IMETHODIMP nsCocoaWindow::Show(PRBool
       // surely an Apple bug.  The "window cache" is an undocumented subsystem,
       // all of whose methods are included in the NSWindowCache category of
       // the NSApplication class (in header files generated using class-dump).
       // This workaround was "borrowed" from the Java Embedding Plugin (which
       // uses it for a different purpose).
       if (mWindowType == eWindowType_popup)
         [NSApp _removeWindowFromCache:mWindow];
 
+      // it's very important to turn off mouse moved events when hiding a window, otherwise
+      // the windows' tracking rects will interfere with each other. (bug 356528)
+      [mWindow setAcceptsMouseMovedEvents:NO];
+
       // If our popup window is a non-native context menu, tell the OS (and
       // other programs) that a menu has closed.
       if ([mWindow isKindOfClass:[PopupWindow class]] &&
           [(PopupWindow*) mWindow isContextMenu]) {
         [[NSDistributedNotificationCenter defaultCenter]
           postNotificationName:@"com.apple.HIToolbox.endMenuTrackingNotification"
                         object:@"org.mozilla.gecko.PopupWindow"];
       }
@@ -1400,16 +1412,17 @@ void nsCocoaWindow::SetMenuBar(nsMenuBar
 }
 
 NS_IMETHODIMP nsCocoaWindow::SetFocus(PRBool aState)
 {
   if (mPopupContentView) {
     mPopupContentView->SetFocus(aState);
   }
   else if (aState && ([mWindow isVisible] || [mWindow isMiniaturized])) {
+    [mWindow setAcceptsMouseMovedEvents:YES];
     [mWindow makeKeyAndOrderFront:nil];
     SendSetZLevelEvent();
   }
 
   return NS_OK;
 }
 
 nsIntPoint nsCocoaWindow::WidgetToScreenOffset()
@@ -1748,19 +1761,16 @@ PRBool nsCocoaWindow::ShouldFocusPlugin(
 {
   RollUpPopups();
   
   return proposedFrameSize;
 }
 
 - (void)windowDidResize:(NSNotification *)aNotification
 {
-  BaseWindow* window = [aNotification object];
-  [window updateTrackingArea];
-
   if (!mGeckoWindow)
     return;
 
   // Resizing might have changed our zoom state.
   mGeckoWindow->DispatchSizeModeEvent();
   mGeckoWindow->ReportSizeEvent();
 }
 
@@ -1958,44 +1968,35 @@ GetDPI(NSWindow* aWindow)
   // Currently we don't do our own scaling to take account
   // of userSpaceScaleFactor, so every "pixel" we draw is actually
   // userSpaceScaleFactor screen pixels. So divide the screen height
   // by userSpaceScaleFactor to get the number of "device pixels"
   // available.
   return (heightPx / scaleFactor) / (heightMM / MM_PER_INCH_FLOAT);
 }
 
-@interface BaseWindow(Private)
-- (void)removeTrackingArea;
-- (void)cursorUpdated:(NSEvent*)aEvent;
-@end
-
 @implementation BaseWindow
 
 - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
 {
   [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
   mState = nil;
   mDrawsIntoWindowFrame = NO;
   mActiveTitlebarColor = nil;
   mInactiveTitlebarColor = nil;
   mScheduledShadowInvalidation = NO;
   mDPI = GetDPI(self);
-  mTrackingArea = nil;
-  [self updateTrackingArea];
 
   return self;
 }
 
 - (void)dealloc
 {
   [mActiveTitlebarColor release];
   [mInactiveTitlebarColor release];
-  [self removeTrackingArea];
-  ChildViewMouseTracker::OnDestroyWindow(self);
   [super dealloc];
 }
 
 static const NSString* kStateTitleKey = @"title";
 static const NSString* kStateDrawsContentsIntoWindowFrameKey = @"drawsContentsIntoWindowFrame";
 static const NSString* kStateActiveTitlebarColorKey = @"activeTitlebarColor";
 static const NSString* kStateInactiveTitlebarColorKey = @"inactiveTitlebarColor";
 static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
@@ -2071,65 +2072,16 @@ static const NSString* kStateShowsToolba
   mScheduledShadowInvalidation = NO;
 }
 
 - (float)getDPI
 {
   return mDPI;
 }
 
-- (NSView*)trackingAreaView
-{
-  NSView* contentView = [self contentView];
-  return [contentView superview] ? [contentView superview] : contentView;
-}
-
-- (void)removeTrackingArea
-{
-  if (mTrackingArea) {
-    [[self trackingAreaView] removeTrackingArea:mTrackingArea];
-    [mTrackingArea release];
-    mTrackingArea = nil;
-  }
-}
-
-- (void)updateTrackingArea
-{
-  [self removeTrackingArea];
-
-  NSView* view = [self trackingAreaView];
-  const NSTrackingAreaOptions options =
-    NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingActiveAlways;
-  mTrackingArea = [[NSTrackingArea alloc] initWithRect:[view bounds]
-                                               options:options
-                                                 owner:self
-                                              userInfo:nil];
-  [view addTrackingArea:mTrackingArea];
-}
-
-- (void)mouseEntered:(NSEvent*)aEvent
-{
-  ChildViewMouseTracker::MouseEnteredWindow(aEvent);
-}
-
-- (void)mouseExited:(NSEvent*)aEvent
-{
-  ChildViewMouseTracker::MouseExitedWindow(aEvent);
-}
-
-- (void)mouseMoved:(NSEvent*)aEvent
-{
-  ChildViewMouseTracker::MouseMoved(aEvent);
-}
-
-- (void)cursorUpdated:(NSEvent*)aEvent
-{
-  // Nothing to do here, but NSTrackingArea wants us to implement this method.
-}
-
 - (BOOL)respondsToSelector:(SEL)aSelector
 {
   // Claim the window doesn't respond to this so that the system
   // doesn't steal keyboard equivalents for it. Bug 613710.
   if (aSelector == @selector(cancelOperation:)) {
     return NO;
   }
 
@@ -2502,16 +2454,97 @@ TitlebarDrawCallback(void* aInfo, CGCont
 {
   return NSDeviceRGBColorSpace;
 }
 
 @end
 
 @implementation PopupWindow
 
+// The OS treats our custom popup windows very strangely -- many mouse events
+// sent to them never reach their target NSView objects.  (That these windows
+// are borderless and of level NSPopUpMenuWindowLevel may have something to do
+// with it.)  The best solution is to pre-empt the OS, as follows.  (All
+// events for a given NSWindow object go through its sendEvent: method.)
+- (void)sendEvent:(NSEvent *)anEvent
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  NSView *target = nil;
+  NSView *contentView = nil;
+  NSEventType type = [anEvent type];
+  NSPoint windowLocation = NSZeroPoint;
+  switch (type) {
+    case NSScrollWheel:
+    case NSLeftMouseDown:
+    case NSLeftMouseUp:
+    case NSRightMouseDown:
+    case NSRightMouseUp:
+    case NSOtherMouseDown:
+    case NSOtherMouseUp:
+    case NSMouseMoved:
+    case NSLeftMouseDragged:
+    case NSRightMouseDragged:
+    case NSOtherMouseDragged:
+      if ((contentView = [self contentView])) {
+        // Since [anEvent window] might not be us, we can't use [anEvent locationInWindow].
+        windowLocation = nsCocoaUtils::EventLocationForWindow(anEvent, self);
+        target = [contentView hitTest:[contentView convertPoint:windowLocation fromView:nil]];
+        // If the hit test failed, the event is targeted here but is not over the window.
+        // Send it to our content view.
+        if (!target)
+          target = contentView;
+      }
+      break;
+    default:
+      break;
+  }
+  if (target) {
+    switch (type) {
+      case NSScrollWheel:
+        [target scrollWheel:anEvent];
+        break;
+      case NSLeftMouseUp:
+        [target mouseUp:anEvent];
+        break;
+      case NSRightMouseDown:
+        [target rightMouseDown:anEvent];
+        break;
+      case NSRightMouseUp:
+        [target rightMouseUp:anEvent];
+        break;
+      case NSOtherMouseDown:
+        [target otherMouseDown:anEvent];
+        break;
+      case NSOtherMouseUp:
+        [target otherMouseUp:anEvent];
+        break;
+      case NSMouseMoved:
+        [target mouseMoved:anEvent];
+        break;
+      case NSLeftMouseDragged:
+        [target mouseDragged:anEvent];
+        break;
+      case NSRightMouseDragged:
+        [target rightMouseDragged:anEvent];
+        break;
+      case NSOtherMouseDragged:
+        [target otherMouseDragged:anEvent];
+        break;
+      default:
+        [super sendEvent:anEvent];
+        break;
+    }
+  } else {
+    [super sendEvent:anEvent];
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask
       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   mIsContextMenu = false;
   return [super initWithContentRect:contentRect styleMask:styleMask
           backing:bufferingType defer:deferCreation];
--- a/widget/src/cocoa/nsToolkit.h
+++ b/widget/src/cocoa/nsToolkit.h
@@ -78,15 +78,16 @@ protected:
 
 protected:
 
   bool               mInited;
 
   CFRunLoopSourceRef mSleepWakeNotificationRLS;
   io_object_t        mPowerNotifier;
 
+  EventHandlerRef    mEventMonitorHandler;
   CFMachPortRef      mEventTapPort;
   CFRunLoopSourceRef mEventTapRLS;
 };
 
 extern nsToolkit* NS_CreateToolkitInstance();
 
 #endif // nsToolkit_h_
--- a/widget/src/cocoa/nsToolkit.mm
+++ b/widget/src/cocoa/nsToolkit.mm
@@ -82,16 +82,17 @@ static io_connect_t gRootPort = MACH_POR
 
 // Static thread local storage index of the Toolkit 
 // object associated with a given thread...
 static PRUintn gToolkitTLSIndex = 0;
 
 nsToolkit::nsToolkit()
 : mInited(false)
 , mSleepWakeNotificationRLS(nsnull)
+, mEventMonitorHandler(nsnull)
 , mEventTapPort(nsnull)
 , mEventTapRLS(nsnull)
 {
 }
 
 nsToolkit::~nsToolkit()
 {
   RemoveSleepWakeNotifcations();
@@ -196,16 +197,28 @@ nsToolkit::RemoveSleepWakeNotifcations()
                             kCFRunLoopDefaultMode);
 
     mSleepWakeNotificationRLS = nsnull;
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
+// This is the callback used in RegisterForAllProcessMouseEvents.
+static OSStatus EventMonitorHandler(EventHandlerCallRef aCaller, EventRef aEvent, void* aRefcon)
+{
+  // Up to Mac OS 10.4 (or when building with the 10.4 SDK), installing a Carbon
+  // event handler like this one caused the OS to post the equivalent Cocoa
+  // events to [NSApp sendEvent:]. When using the 10.5 SDK, this doesn't happen
+  // any more, so we need to do it manually.
+  [NSApp sendEvent:[NSEvent eventWithEventRef:aEvent]];
+
+  return eventNotHandledErr;
+}
+
 // Converts aPoint from the CoreGraphics "global display coordinate" system
 // (which includes all displays/screens and has a top-left origin) to its
 // (presumed) Cocoa counterpart (assumed to be the same as the "screen
 // coordinates" system), which has a bottom-left origin.
 static NSPoint ConvertCGGlobalToCocoaScreen(CGPoint aPoint)
 {
   NSPoint cocoaPoint;
   cocoaPoint.x = aPoint.x;
@@ -257,16 +270,22 @@ nsToolkit::RegisterForAllProcessMouseEve
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Don't do this for apps that (like Camino) use native context menus.
 #ifdef MOZ_USE_NATIVE_POPUP_WINDOWS
   return;
 #endif /* MOZ_USE_NATIVE_POPUP_WINDOWS */
 
+  if (!mEventMonitorHandler) {
+    EventTypeSpec kEvents[] = {{kEventClassMouse, kEventMouseMoved}};
+    InstallEventHandler(GetEventMonitorTarget(), EventMonitorHandler,
+                        GetEventTypeCount(kEvents), kEvents, 0,
+                        &mEventMonitorHandler);
+  }
   if (!mEventTapRLS) {
     // Using an event tap for mouseDown events (instead of installing a
     // handler for them on the EventMonitor target) works around an Apple
     // bug that causes OS menus (like the Clock menu) not to work properly
     // on OS X 10.4.X and below (bmo bug 381448).
     // We install our event tap "listen only" to get around yet another Apple
     // bug -- when we install it as an event filter on any kind of mouseDown
     // event, that kind of event stops working in the main menu, and usually
@@ -296,16 +315,20 @@ nsToolkit::RegisterForAllProcessMouseEve
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 void
 nsToolkit::UnregisterAllProcessMouseEventHandlers()
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
+  if (mEventMonitorHandler) {
+    RemoveEventHandler(mEventMonitorHandler);
+    mEventMonitorHandler = nsnull;
+  }
   if (mEventTapRLS) {
     CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mEventTapRLS,
                           kCFRunLoopDefaultMode);
     CFRelease(mEventTapRLS);
     mEventTapRLS = nsnull;
   }
   if (mEventTapPort) {
     // mEventTapPort must be invalidated as well as released.  Otherwise the
--- a/widget/tests/native_mouse_mac_window.xul
+++ b/widget/tests/native_mouse_mac_window.xul
@@ -67,24 +67,22 @@
       window.opener.wrappedJSObject.SimpleTest.todo(condition, message);
     }
 
     function todo_is(a, b, message) {
       window.opener.wrappedJSObject.SimpleTest.todo_is(a, b, message);
     }
 
     function onTestsFinished() {
-      clearTimeout(gAfterLoopExecution);
       observe(window, eventMonitor, false);
       observe(gRightWindow, eventMonitor, false);
       observe(gPopup, eventMonitor, false);
       gRightWindow.close();
-      var openerSimpleTest = window.opener.wrappedJSObject.SimpleTest;
       window.close();
-      openerSimpleTest.finish();
+      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,
@@ -125,37 +123,33 @@
           NSFunctionKeyMask   = 1 << 23;
 
     const gDebug = false;
 
     function printDebug(msg) { if (gDebug) dump(msg); }
 
     var gExpectedEvents = [];
     var gRightWindow = null, gPopup = null;
-    var gCurrentMouseX = 0, gCurrentMouseY = 0;
-    var gAfterLoopExecution = 0;
 
     function testMouse(x, y, msg, elem, win, exp, flags, callback) {
       clearExpectedEvents();
       exp.forEach(function (expEv) {
         expEv.screenX = x;
         expEv.screenY = y;
         gExpectedEvents.push(expEv);
       });
       printDebug("sending event: " + x + ", " + y + " (" + msg + ")\n");
-      gCurrentMouseX = x;
-      gCurrentMouseY = y;
       netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
       var utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                                      getInterface(Components.interfaces.nsIDOMWindowUtils);
       utils.sendNativeMouseEvent(x, y, msg, flags || 0, elem);
-      gAfterLoopExecution = setTimeout(function () {
+      SimpleTest.executeSoon(function () {
         clearExpectedEvents();
         callback();
-      }, 0);
+      });
     }
 
     function eventListenOnce(elem, name, callback) {
       elem.addEventListener(name, function(e) {
         elem.removeEventListener(name, arguments.callee, false);
         callback(e);
       }, false);
     }
@@ -183,25 +177,20 @@
     var gEventNum = 0;
 
     function eventMonitor(e) {
       printDebug("got event: " + eventToString(e) + "\n");
       processEvent(e);
     }
 
     function processEvent(e) {
-      if (e.screenX != gCurrentMouseX || e.screenY != gCurrentMouseY) {
-        todo(false, "Oh no! Received a stray event from a confused tracking area. Aborting test.");
-        onTestsFinished();
-        return;
-      }
       var expectedEvent = gExpectedEvents.shift();
       if (!expectedEvent) {
         ok(false, "received event I didn't expect: " + eventToString(e));
-        return;
+        return true;
       }
       if (e.type != expectedEvent.type) {
         // Didn't get expectedEvent.
         var errFun = expectedEvent.shouldFireButDoesnt ? todo : ok;
         errFun(false, "Didn't receive expected event: " + eventToString(expectedEvent));
         return processEvent(e);
       }
       gEventNum++;
@@ -312,20 +301,16 @@
         [410, 150, NSLeftMouseDragged, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Let go of the mouse.
         [410, 150, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
-        // Move the mouse back over the left window, which is inactive.
-        [150, 170, NSMouseMoved, null, left, [
-          { type: "mouseout", 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, shouldFireButDoesnt: true },
           { type: "mousedown", target: leftElem },
           { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
@@ -337,16 +322,17 @@
           { type: "click", target: leftElem },
           { type: "mouseout", target: leftElem, shouldFireButDoesnt: 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.
         // Still, mouseout and mouseover events should fire.
         function raiseLeftWindow(callback) {
           clearExpectedEvents();
+          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
           gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
           focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
         },
         // It's active, so it should respond to mousemove events now.
         [150, 170, NSMouseMoved, null, left, [
           { type: "mousemove", target: leftElem },
         ]],
         // This was boring... let's introduce a popup. It will overlap both the left
@@ -400,30 +386,35 @@
           { 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");
-          gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+          callback();
+        },
+        // Move the mouse to trigger the appearance of the tooltip.
+        [410, 180, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Wait for the tooltip to appear.
+        function (callback) {
           eventListenOnce(rightElem, "popupshown", callback);
-          gCurrentMouseX = 410;
-          gCurrentMouseY = 180;
-          var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                               getInterface(Components.interfaces.nsIDOMWindowUtils);
-          utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
         },
         // Now the tooltip is visible.
-        // Move the mouse a little to the right.
-        [411, 180, NSMouseMoved, null, right, [
+        // 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.
+        // 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, [
@@ -438,50 +429,51 @@
           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, right, [
+        [390, 171, NSMouseMoved, null, left, [
           { type: "mousemove", target: rightElem },
         ]],
-        [391, 171, NSMouseMoved, null, right, [
+        [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, panel, left, [
+        [260, 170, NSMouseMoved, null, left, [
           { type: "mouseout", target: rightElem },
         ]],
-        [260, 171, NSMouseMoved, panel, left, [
+        [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, [
         ]],
         // 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, panel, left, [
+        [387, 170, NSMouseMoved, null, right, [
           { type: "mouseover", target: panel },
           { type: "mousemove", target: panel },
         ]],
-        [387, 171, NSMouseMoved, panel, left, [
+        [387, 171, NSMouseMoved, null, left, [
           { 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 }
@@ -489,22 +481,22 @@
         [388, 171, NSLeftMouseUp, panel, left, [
           { type: "mouseup", target: panel },
           { type: "click", target: panel },
         ]],
 
         // Last test for this part: 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, null, left, [
+        [173, 200, NSMouseMoved, panel, left, [
           { type: "mouseout", target: panel },
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
-        [173, 201, NSMouseMoved, null, left, [
+        [173, 201, NSMouseMoved, panel, left, [
           { type: "mousemove", target: leftElem },
         ]],
 
         // Part 2: Allow click-through
 
         function hideThatPanel(callback) {
           eventListenOnce(panel, "popuphidden", callback);
           panel.hidePopup();
@@ -570,38 +562,42 @@
         [410, 150, NSLeftMouseDragged, null, right, [
           { type: "mousemove", target: rightElem },
         ]],
         // Let go of the mouse.
         [410, 150, NSLeftMouseUp, null, right, [
           { type: "mouseup", target: rightElem },
           { type: "click", target: rightElem },
         ]],
-        // Move the mouse back over the left window, which is inactive.
-        [150, 170, NSMouseMoved, null, left, [
-          { type: "mouseout", target: rightElem },
-          { type: "mouseover", target: leftElem },
-          { type: "mousemove", target: leftElem },
-        ]],
-        // Right-click it.
+        // 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, shouldFireButDoesnt: true },
           { type: "mousedown", target: leftElem },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: true },
         ]],
         // Let go of the mouse.
         [150, 170, NSRightMouseUp, null, left, [
+          { type: "mouseover", target: leftElem, shouldFireButDoesnt: true },
           { type: "mouseup", target: leftElem },
           { type: "click", target: leftElem },
+          { type: "mouseout", target: leftElem, shouldFireButDoesnt: 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.
+        // Still, mouseout and mouseover events should fire.
         function raiseLeftWindow(callback) {
           clearExpectedEvents();
+          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseout", target: rightElem });
+          gExpectedEvents.push({ screenX: 150, screenY: 170, type: "mouseover", target: leftElem });
           focusAndThen(left, function () { SimpleTest.executeSoon(callback); });
         },
-        // It's active and should still respond to mousemove events.
+        // It's active, so it should respond to mousemove events now.
         [150, 170, NSMouseMoved, null, left, [
           { 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);
@@ -652,32 +648,37 @@
         ]],
         [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 setTooltip2(callback) {
+        function setTooltip(callback) {
           rightElem.setAttribute("tooltip", "tip");
-          gExpectedEvents.push({ screenX: 410, screenY: 180, type: "mousemove", target: rightElem });
+          callback();
+        },
+        // Move the mouse to trigger the appearance of the tooltip.
+        [410, 180, NSMouseMoved, null, right, [
+          { type: "mousemove", target: rightElem },
+        ]],
+        // Wait for the tooltip to appear.
+        function (callback) {
           eventListenOnce(rightElem, "popupshown", callback);
-          gCurrentMouseX = 410;
-          gCurrentMouseY = 180;
-          var utils = right.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
-                               getInterface(Components.interfaces.nsIDOMWindowUtils);
-          utils.sendNativeMouseEvent(410, 180, NSMouseMoved, 0, null);
         },
         // Now the tooltip is visible.
-        // Move the mouse a little to the right.
-        [411, 180, NSMouseMoved, null, right, [
+        // 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.
+        // 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, [
@@ -692,33 +693,34 @@
           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, right, [
+        [390, 171, NSMouseMoved, null, left, [
           { type: "mousemove", target: rightElem },
         ]],
-        [391, 171, NSMouseMoved, null, right, [
+        [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, panel, left, [
+        [260, 170, NSMouseMoved, null, left, [
           { type: "mouseout", target: rightElem },
           { type: "mouseover", target: panel },
           { type: "mousemove", target: panel },
         ]],
-        [260, 171, NSMouseMoved, panel, left, [
+        [260, 171, NSMouseMoved, null, left, [
           { type: "mousemove", target: panel },
         ]],
         [261, 171, NSMouseMoved, panel, left, [
           { type: "mousemove", target: panel },
         ]],
         // Let's be evil and click it.
         [261, 171, NSLeftMouseDown, panel, left, [
           { type: "mousedown", target: panel },
@@ -726,20 +728,20 @@
         [261, 171, NSLeftMouseUp, panel, left, [
           { type: "mouseup", target: panel },
           { type: "click", target: panel },
         ]],
         // This didn't focus the window, unfortunately, so let's do it ourselves.
         function raiseLeftWindowTakeTwo(callback) {
           focusAndThen(left, callback);
         },
-        [387, 170, NSMouseMoved, panel, left, [
+        [387, 170, NSMouseMoved, null, right, [
           { type: "mousemove", target: panel },
         ]],
-        [387, 171, NSMouseMoved, panel, left, [
+        [387, 171, NSMouseMoved, null, left, [
           { 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 }
@@ -747,22 +749,22 @@
         [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, null, left, [
+        [173, 200, NSMouseMoved, panel, left, [
           { type: "mouseout", target: panel },
           { type: "mouseover", target: leftElem },
           { type: "mousemove", target: leftElem },
         ]],
-        [173, 201, NSMouseMoved, null, left, [
+        [173, 201, NSMouseMoved, panel, left, [
           { type: "mousemove", target: leftElem },
         ]],
       ];
       function runNextTest() {
         if (!tests.length)
           return onTestsFinished();
 
         var test = tests.shift();