Fix display problems with non-native combo box. b=392389 r=joshmoz sr=roc a=roc
authorsmichaud@pobox.com
Mon, 01 Oct 2007 14:39:52 -0700
changeset 6494 7fa0f43f3db5ab4c2beb7d05d18bfaec736c1677
parent 6493 907ecbf88247561217c81eac9f241aad1378bcb8
child 6495 6dbd65c480ec3ce4029b0ca82b5e9fab93209f0c
push idunknown
push userunknown
push dateunknown
reviewersjoshmoz, roc, roc
bugs392389
milestone1.9a9pre
Fix display problems with non-native combo box. b=392389 r=joshmoz sr=roc a=roc
widget/src/cocoa/nsCocoaWindow.mm
--- a/widget/src/cocoa/nsCocoaWindow.mm
+++ b/widget/src/cocoa/nsCocoaWindow.mm
@@ -1031,16 +1031,34 @@ NS_IMETHODIMP nsCocoaWindow::CaptureRoll
   NS_IF_RELEASE(gRollupListener);
   NS_IF_RELEASE(gRollupWidget);
   
   if (aDoCapture) {
     gRollupListener = aListener;
     NS_ADDREF(aListener);
     gRollupWidget = this;
     NS_ADDREF(this);
+    // Sometimes more than one popup window can be visible at the same time
+    // (e.g. nested non-native context menus, or the test case (attachment
+    // 276885) for bmo bug 392389, which displays a non-native combo-box in
+    // a non-native popup window).  In these cases the "active" popup window
+    // (the one that corresponds to the current gRollupWidget) should be the
+    // topmost -- the (nested) context menu the mouse is currently over, or
+    // the combo-box's drop-down list (when it's displayed).  But (among
+    // windows that have the same "level") OS X makes topmost the window that
+    // last received a mouse-down event, which may be incorrect (in the combo-
+    // box case, it makes topmost the window containing the combo-box).  So
+    // here we fiddle with a non-native popup window's level to make sure the
+    // "active" one is always above any other non-native popup windows that
+    // may be visible.
+    if (mWindow && (mWindowType == eWindowType_popup))
+      [mWindow setLevel:NSPopUpMenuWindowLevel];
+  } else {
+    if (mWindow && (mWindowType == eWindowType_popup))
+      [mWindow setLevel:NSModalPanelWindowLevel];
   }
   
   return NS_OK;
 }
 
 
 NS_IMETHODIMP nsCocoaWindow::GetAttention(PRInt32 aCycleCount)
 {
@@ -1292,39 +1310,40 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
 // 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
 {
   NSView *target = nil, *contentView = nil;
-  NSWindow *window = [anEvent window];
   NSEventType type = [anEvent type];
-  if (window) {
-    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 = [window contentView]) != nil) {
-          target = [contentView hitTest:[contentView convertPoint:
-                                        [anEvent locationInWindow] fromView:nil]];
-        }
-        break;
-      default:
-        break;
-    }
+  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]) != nil) {
+        // Since [anEvent window] might not be us, we can't use [anEvent
+        // locationInWindow].
+        windowLocation = [self mouseLocationOutsideOfEventStream];
+        target = [contentView hitTest:[contentView convertPoint:
+                                      windowLocation fromView:nil]];
+      }
+      break;
+    default:
+      break;
   }
   if (target) {
     switch (type) {
       case NSScrollWheel:
         [target scrollWheel:anEvent];
         break;
       case NSLeftMouseDown:
         [target mouseDown:anEvent];
@@ -1345,20 +1364,20 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
         // or not the left-click hit an active menu item.  This is how native
         // context menus behave, but wasn't how our custom context menus
         // behaved previously (on the trunk or e.g. in Firefox 2.0.0.4).
         // If our ChildView's corresponding nsChildView object doesn't
         // dispatch an NS_MOUSE_BUTTON_UP event, none of our active menu items
         // will "work" on a leftMouseDown.
         if (mIsContextMenu) {
           NSEvent *newEvent = [NSEvent mouseEventWithType:NSLeftMouseUp
-                                                 location:[anEvent locationInWindow]
+                                                 location:windowLocation
                                             modifierFlags:[anEvent modifierFlags]
                                                 timestamp:GetCurrentEventTime()
-                                             windowNumber:[[anEvent window] windowNumber]
+                                             windowNumber:[self windowNumber]
                                                   context:nil
                                               eventNumber:0
                                                clickCount:1
                                                  pressure:0.0];
           [target mouseUp:newEvent];
           RollUpPopups();
         }
         break;
@@ -1389,16 +1408,34 @@ NS_IMETHODIMP nsCocoaWindow::EndSecureKe
       case NSOtherMouseDragged:
         [target otherMouseDragged:anEvent];
         break;
       default:
         [super sendEvent:anEvent];
         break;
     }
   } else {
+    // Sometimes more than one popup window can be visible at the same time
+    // (e.g. nested non-native context menus, or the test case (attachment
+    // 276885) for bmo bug 392389, which displays a non-native combo-box in
+    // a non-native popup window).  In these cases the "active" popup window
+    // (the one that corresponds to the current gRollupWidget) should receive
+    // all mouse events that happen over it.  So if anEvent wasn't processed
+    // here, if there's a current gRollupWidget, and if its NSWindow object
+    // isn't us, we send anEvent to the gRollupWidget's NSWindow object, then
+    // return.  Other code (in nsChildView.mm's ChildView class) will redirect
+    // events that happen over us but should be redirected to the current
+    // gRollupWidget.
+    if (gRollupWidget) {
+      NSWindow *rollupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
+      if (rollupWindow && ![rollupWindow isEqual:self]) {
+        [rollupWindow sendEvent:anEvent];
+        return;
+      }
+    }
     [super sendEvent:anEvent];
   }
 }
 
 
 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask
       backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation
 {