forked from mirrors/gecko-dev
		
	Bug 515003 - Rewrite native mouse event handling. Also add tests for native mouse events (bug 470845). r=josh, sr=roc
This commit is contained in:
		
							parent
							
								
									d234cc450e
								
							
						
					
					
						commit
						c2055691da
					
				
					 14 changed files with 842 additions and 352 deletions
				
			
		|  | @ -391,6 +391,27 @@ nsDOMWindowUtils::SendNativeKeyEvent(PRInt32 aNativeKeyboardLayout, | |||
|                                           aModifiers, aCharacters, aUnmodifiedCharacters); | ||||
| } | ||||
| 
 | ||||
| NS_IMETHODIMP | ||||
| nsDOMWindowUtils::SendNativeMouseEvent(PRInt32 aScreenX, | ||||
|                                        PRInt32 aScreenY, | ||||
|                                        PRInt32 aNativeMessage, | ||||
|                                        PRInt32 aModifierFlags, | ||||
|                                        nsIDOMElement* aElement) | ||||
| { | ||||
|   PRBool hasCap = PR_FALSE; | ||||
|   if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap)) | ||||
|       || !hasCap) | ||||
|     return NS_ERROR_DOM_SECURITY_ERR; | ||||
| 
 | ||||
|   // get the widget to send the event to
 | ||||
|   nsCOMPtr<nsIWidget> widget = GetWidgetForElement(aElement); | ||||
|   if (!widget) | ||||
|     return NS_ERROR_FAILURE; | ||||
| 
 | ||||
|   return widget->SynthesizeNativeMouseEvent(nsIntPoint(aScreenX, aScreenY), | ||||
|                                             aNativeMessage, aModifierFlags); | ||||
| } | ||||
| 
 | ||||
| NS_IMETHODIMP | ||||
| nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString) | ||||
| { | ||||
|  | @ -442,6 +463,28 @@ nsDOMWindowUtils::GetWidget(nsPoint* aOffset) | |||
|   return nsnull; | ||||
| } | ||||
| 
 | ||||
| nsIWidget* | ||||
| nsDOMWindowUtils::GetWidgetForElement(nsIDOMElement* aElement) | ||||
| { | ||||
|   if (!aElement) | ||||
|     return GetWidget(); | ||||
| 
 | ||||
|   nsCOMPtr<nsIContent> content = do_QueryInterface(aElement); | ||||
|   nsIDocument* doc = content->GetCurrentDoc(); | ||||
|   nsIPresShell* presShell = doc ? doc->GetPrimaryShell() : nsnull; | ||||
| 
 | ||||
|   if (presShell) { | ||||
|     nsIFrame* frame = presShell->GetPrimaryFrameFor(content); | ||||
|     if (!frame) { | ||||
|       frame = presShell->GetRootFrame(); | ||||
|     } | ||||
|     if (frame) | ||||
|       return frame->GetWindow(); | ||||
|   } | ||||
| 
 | ||||
|   return nsnull; | ||||
| } | ||||
| 
 | ||||
| NS_IMETHODIMP | ||||
| nsDOMWindowUtils::Focus(nsIDOMElement* aElement) | ||||
| { | ||||
|  |  | |||
|  | @ -57,6 +57,7 @@ protected: | |||
|   // If aOffset is non-null, it gets filled in with an offset, in app
 | ||||
|   // units, that should be added to any event offset we're given.
 | ||||
|   nsIWidget* GetWidget(nsPoint* aOffset = nsnull); | ||||
|   nsIWidget* GetWidgetForElement(nsIDOMElement* aElement); | ||||
| 
 | ||||
|   nsPresContext* GetPresContext(); | ||||
| }; | ||||
|  |  | |||
|  | @ -48,7 +48,7 @@ | |||
| interface nsIDOMElement; | ||||
| interface nsIDOMHTMLCanvasElement; | ||||
| 
 | ||||
| [scriptable, uuid(99d904c0-3b9e-44b7-b1e0-372766d18308)] | ||||
| [scriptable, uuid(c1b779af-7297-4e2e-9fc4-a6f22038770f)] | ||||
| interface nsIDOMWindowUtils : nsISupports { | ||||
| 
 | ||||
|   /** | ||||
|  | @ -198,6 +198,20 @@ interface nsIDOMWindowUtils : nsISupports { | |||
|                           in AString aCharacters, | ||||
|                           in AString aUnmodifiedCharacters); | ||||
| 
 | ||||
|   /** | ||||
|    * See nsIWidget::SynthesizeNativeMouseEvent | ||||
|    * | ||||
|    * Will be called on the widget that contains aElement. | ||||
|    * Cannot be accessed from unprivileged context (not content-accessible) | ||||
|    * Will throw a DOM security error if called without UniversalXPConnect | ||||
|    * privileges. | ||||
|    */ | ||||
|   void sendNativeMouseEvent(in long aScreenX, | ||||
|                             in long aScreenY, | ||||
|                             in long aNativeMessage, | ||||
|                             in long aModifierFlags, | ||||
|                             in nsIDOMElement aElement); | ||||
| 
 | ||||
|   /** | ||||
|    * See nsIWidget::ActivateNativeMenuItemAt | ||||
|    * | ||||
|  |  | |||
|  | @ -102,8 +102,8 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event); | |||
| #endif | ||||
| 
 | ||||
| #define NS_IWIDGET_IID \ | ||||
|   { 0xb681539f, 0x5dac, 0x45af, \ | ||||
|     { 0x8a, 0x25, 0xdf, 0xd7, 0x14, 0xe0, 0x9f, 0x43 } } | ||||
|   { 0x5c55f106, 0xb7ab, 0x4f54, \ | ||||
|     { 0x89, 0xf3, 0xd3, 0xcf, 0x91, 0xf9, 0x63, 0x95 } } | ||||
| 
 | ||||
| /*
 | ||||
|  * Window shadow styles | ||||
|  | @ -930,6 +930,18 @@ class nsIWidget : public nsISupports { | |||
|                                               const nsAString& aCharacters, | ||||
|                                               const nsAString& aUnmodifiedCharacters) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Utility method intended for testing. Dispatches native mouse events | ||||
|      * to this widget and may even move the mouse cursor. | ||||
|      * @param aPoint screen location of the mouse, in pixels, with origin at | ||||
|      * the top left | ||||
|      * @param aNativeMessage *platform-specific* event type (e.g. NSMouseMoved) | ||||
|      * @param aModifierFlags *platform-specific* modifier flags | ||||
|      */ | ||||
|     virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint, | ||||
|                                                 PRUint32 aNativeMessage, | ||||
|                                                 PRUint32 aModifierFlags) = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Activates a native menu item at the position specified by the index | ||||
|      * string. The index string is a string of positive integers separated | ||||
|  |  | |||
|  | @ -209,6 +209,12 @@ enum { | |||
| 
 | ||||
| - (void)sendFocusEvent:(PRUint32)eventType; | ||||
| 
 | ||||
| - (void)handleMouseMoved:(NSEvent*)aEvent; | ||||
| 
 | ||||
| - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent | ||||
|                             enter:(BOOL)aEnter | ||||
|                              type:(nsMouseEvent::exitType)aType; | ||||
| 
 | ||||
| - (void) processPluginKeyEvent:(EventRef)aKeyEvent; | ||||
| 
 | ||||
| // Simple gestures support
 | ||||
|  | @ -271,6 +277,22 @@ private: | |||
|   static void KillComposing(); | ||||
| }; | ||||
| 
 | ||||
| class ChildViewMouseTracker { | ||||
| 
 | ||||
| public: | ||||
| 
 | ||||
|   static void MouseMoved(NSEvent* aEvent); | ||||
|   static void OnDestroyView(ChildView* aView); | ||||
|   static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent); | ||||
| 
 | ||||
|   static ChildView* sLastMouseEventView; | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|   static NSWindow* WindowForEvent(NSEvent* aEvent); | ||||
|   static ChildView* ViewForEvent(NSEvent* aEvent); | ||||
| }; | ||||
| 
 | ||||
| //-------------------------------------------------------------------------
 | ||||
| //
 | ||||
| // nsChildView
 | ||||
|  | @ -386,6 +408,16 @@ public: | |||
|   virtual nsTransparencyMode GetTransparencyMode(); | ||||
|   virtual void                SetTransparencyMode(nsTransparencyMode aMode); | ||||
| 
 | ||||
|   virtual nsresult SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, | ||||
|                                             PRInt32 aNativeKeyCode, | ||||
|                                             PRUint32 aModifierFlags, | ||||
|                                             const nsAString& aCharacters, | ||||
|                                             const nsAString& aUnmodifiedCharacters); | ||||
| 
 | ||||
|   virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint, | ||||
|                                               PRUint32 aNativeMessage, | ||||
|                                               PRUint32 aModifierFlags); | ||||
|    | ||||
|   // Mac specific methods
 | ||||
|    | ||||
|   virtual PRBool    DispatchWindowEvent(nsGUIEvent& event); | ||||
|  | @ -424,12 +456,6 @@ protected: | |||
|   void              TearDownView(); | ||||
|   nsCocoaWindow*    GetXULWindowWidget(); | ||||
| 
 | ||||
|   virtual nsresult SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, | ||||
|                                             PRInt32 aNativeKeyCode, | ||||
|                                             PRUint32 aModifierFlags, | ||||
|                                             const nsAString& aCharacters, | ||||
|                                             const nsAString& aUnmodifiedCharacters); | ||||
| 
 | ||||
| protected: | ||||
| 
 | ||||
|   NSView<mozView>*      mView;      // my parallel cocoa view (ChildView or NativeScrollbarView), [STRONG]
 | ||||
|  |  | |||
|  | @ -300,9 +300,9 @@ 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); | ||||
|  | @ -354,8 +354,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0; | |||
| 
 | ||||
| - (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv; | ||||
| 
 | ||||
| - (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent; | ||||
| 
 | ||||
| - (void)maybeInitContextMenuTracking; | ||||
| 
 | ||||
| + (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent; | ||||
|  | @ -1684,6 +1682,40 @@ nsresult nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout, | |||
|   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) | ||||
|  | @ -2394,9 +2426,6 @@ NSEvent* gLastDragEvent = nil; | |||
|   if (mPluginTSMDoc) | ||||
|     ::DeleteTSMDocument(mPluginTSMDoc); | ||||
| 
 | ||||
|   if (sLastViewEntered == self) | ||||
|     sLastViewEntered = nil; | ||||
| 
 | ||||
|   [[NSNotificationCenter defaultCenter] removeObserver:self]; | ||||
|   [[NSDistributedNotificationCenter defaultCenter] removeObserver:self]; | ||||
| 
 | ||||
|  | @ -2414,6 +2443,7 @@ NSEvent* gLastDragEvent = nil; | |||
| - (void)widgetDestroyed | ||||
| { | ||||
|   nsTSMManager::OnDestroyView(self); | ||||
|   ChildViewMouseTracker::OnDestroyView(self); | ||||
|   mGeckoChild = nsnull; | ||||
| 
 | ||||
|   // Just in case we're destroyed abruptly and missed the draggingExited | ||||
|  | @ -2798,85 +2828,6 @@ static const PRInt32 sShadowInvalidationInterval = 100; | |||
| } | ||||
| #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 | ||||
|  | @ -3190,12 +3141,10 @@ static const PRInt32 sShadowInvalidationInterval = 100; | |||
|     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 | ||||
|  | @ -3258,8 +3207,6 @@ static const PRInt32 sShadowInvalidationInterval = 100; | |||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK; | ||||
| 
 | ||||
|   if (![self ensureCorrectMouseEventTarget:theEvent]) | ||||
|     return; | ||||
| 
 | ||||
|   if (!mGeckoChild) | ||||
|     return; | ||||
|  | @ -3306,39 +3253,27 @@ static const PRInt32 sShadowInvalidationInterval = 100; | |||
|   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) | ||||
| - (void)sendMouseEnterOrExitEvent:(NSEvent*)aEvent | ||||
|                             enter:(BOOL)aEnter | ||||
|                              type:(nsMouseEvent::exitType)aType | ||||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; | ||||
|   if (!mGeckoChild) | ||||
|     return; | ||||
| 
 | ||||
|   if (!widget || !localEventLocation) | ||||
|     return nsEventStatus_eIgnore; | ||||
|   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, [self window]); | ||||
|   NSPoint localEventLocation = [self convertPoint:windowEventLocation fromView:nil]; | ||||
| 
 | ||||
|   nsMouseEvent event(isTrusted, msg, widget, aReason); | ||||
|   event.refPoint.x = nscoord((PRInt32)localEventLocation->x); | ||||
|   event.refPoint.y = nscoord((PRInt32)localEventLocation->y); | ||||
|   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(); | ||||
|  | @ -3348,142 +3283,37 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
|   } | ||||
| #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; | ||||
|   event.exit = aType; | ||||
| 
 | ||||
|   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]; | ||||
|   nsEventStatus status; // ignored | ||||
|   mGeckoChild->DispatchEvent(&event, status); | ||||
| } | ||||
| 
 | ||||
|   return status; | ||||
| 
 | ||||
|   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore); | ||||
| - (void)mouseMoved:(NSEvent*)aEvent | ||||
| { | ||||
|   ChildViewMouseTracker::MouseMoved(aEvent); | ||||
| } | ||||
| 
 | ||||
| - (void)mouseMoved:(NSEvent*)theEvent | ||||
| - (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]; | ||||
| 
 | ||||
|  | @ -3526,9 +3356,6 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK; | ||||
| 
 | ||||
|   if (![self ensureCorrectMouseEventTarget:theEvent]) | ||||
|     return; | ||||
| 
 | ||||
|   if (!mGeckoChild) | ||||
|     return; | ||||
| 
 | ||||
|  | @ -3582,9 +3409,6 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK; | ||||
| 
 | ||||
|   if (![self ensureCorrectMouseEventTarget:theEvent]) | ||||
|     return; | ||||
| 
 | ||||
|   nsAutoRetainCocoaObject kungFuDeathGrip(self); | ||||
| 
 | ||||
|   [self maybeRollup:theEvent]; | ||||
|  | @ -3639,9 +3463,6 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK; | ||||
| 
 | ||||
|   if (![self ensureCorrectMouseEventTarget:theEvent]) | ||||
|     return; | ||||
| 
 | ||||
|   if (!mGeckoChild) | ||||
|     return; | ||||
| 
 | ||||
|  | @ -3686,9 +3507,6 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
| 
 | ||||
| - (void)rightMouseDragged:(NSEvent*)theEvent | ||||
| { | ||||
|   if (![self ensureCorrectMouseEventTarget:theEvent]) | ||||
|     return; | ||||
| 
 | ||||
|   if (!mGeckoChild) | ||||
|     return; | ||||
| 
 | ||||
|  | @ -3705,12 +3523,10 @@ static nsEventStatus SendGeckoMouseEnterOrExitEvent(PRBool isTrusted, | |||
| { | ||||
|   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) | ||||
|  | @ -6774,6 +6590,128 @@ nsTSMManager::CancelIME() | |||
|   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 | ||||
|  |  | |||
|  | @ -140,18 +140,12 @@ class nsCocoaUtils | |||
|   // is for the window. Does not take window z-order into account.
 | ||||
|   static BOOL IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow); | ||||
| 
 | ||||
|   // Determines if the window should accept mouse events.
 | ||||
|   static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* anEvent); | ||||
| 
 | ||||
|   // Events are set up so that their coordinates refer to the window to which they
 | ||||
|   // were originally sent. If we reroute the event somewhere else, we'll have
 | ||||
|   // to get the window coordinates this way. Do not call this unless the window
 | ||||
|   // the event was originally targeted at is still alive!
 | ||||
|   static NSPoint EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow); | ||||
| 
 | ||||
|   // Finds the foremost window that is under the mouse for the current application.
 | ||||
|   static NSWindow* FindWindowForEvent(NSEvent* anEvent, BOOL* isUnderMouse); | ||||
| 
 | ||||
|   // Hides the Menu bar and the Dock. Multiple hide/show requests can be nested.
 | ||||
|   static void HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen); | ||||
| 
 | ||||
|  |  | |||
|  | @ -126,90 +126,6 @@ NSPoint nsCocoaUtils::EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow | |||
|   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0)); | ||||
| } | ||||
| 
 | ||||
| BOOL nsCocoaUtils::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; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Find the active window under the mouse. If the mouse isn't over any active | ||||
| // window, just return the topmost active window and set *isUnderMouse to NO. | ||||
| NSWindow* nsCocoaUtils::FindWindowForEvent(NSEvent* anEvent, BOOL* isUnderMouse) | ||||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; | ||||
| 
 | ||||
|   *isUnderMouse = NO; | ||||
|   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); | ||||
| 
 | ||||
|   NSWindow* activeWindow = nil; | ||||
|   NSPoint screenPoint = ScreenLocationForEvent(anEvent); | ||||
| 
 | ||||
|   for (NSInteger i = 0; i < windowCount; i++) { | ||||
|     NSWindow* currentWindow = [NSApp windowWithWindowNumber:windowList[i]]; | ||||
|     if (currentWindow && WindowAcceptsEvent(currentWindow, anEvent)) { | ||||
|       if (NSPointInRect(screenPoint, [currentWindow frame])) { | ||||
|         free(windowList); | ||||
|         *isUnderMouse = YES; | ||||
|         return currentWindow; | ||||
|       } | ||||
|       if (!activeWindow) | ||||
|         activeWindow = currentWindow; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   free(windowList); | ||||
|   return activeWindow; | ||||
| 
 | ||||
|   NS_OBJC_END_TRY_ABORT_BLOCK_NIL; | ||||
| } | ||||
| 
 | ||||
| void nsCocoaUtils::HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen) | ||||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK; | ||||
|  |  | |||
|  | @ -237,6 +237,9 @@ public: | |||
|     NS_IMETHOD SetWindowShadowStyle(PRInt32 aStyle); | ||||
|     virtual void SetShowsToolbarButton(PRBool aShow); | ||||
|     NS_IMETHOD SetWindowTitlebarColor(nscolor aColor, PRBool aActive); | ||||
|     virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint, | ||||
|                                                 PRUint32 aNativeMessage, | ||||
|                                                 PRUint32 aModifierFlags); | ||||
| 
 | ||||
|     void DispatchSizeModeEvent(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -1427,6 +1427,21 @@ NS_IMETHODIMP nsCocoaWindow::SetWindowTitlebarColor(nscolor aColor, PRBool aActi | |||
|   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; | ||||
| } | ||||
| 
 | ||||
| NS_IMETHODIMP nsCocoaWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint, | ||||
|                                                         PRUint32 aNativeMessage, | ||||
|                                                         PRUint32 aModifierFlags) | ||||
| { | ||||
|   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; | ||||
| 
 | ||||
|   if (mPopupContentView) | ||||
|     return mPopupContentView->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, | ||||
|                                                          aModifierFlags); | ||||
| 
 | ||||
|   return NS_OK; | ||||
| 
 | ||||
|   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; | ||||
| } | ||||
| 
 | ||||
| gfxASurface* nsCocoaWindow::GetThebesSurface() | ||||
| { | ||||
|   if (mPopupContentView) | ||||
|  |  | |||
|  | @ -169,6 +169,11 @@ protected: | |||
|                                             const nsAString& aUnmodifiedCharacters) | ||||
|   { return NS_ERROR_UNEXPECTED; } | ||||
| 
 | ||||
|   virtual nsresult SynthesizeNativeMouseEvent(nsIntPoint aPoint, | ||||
|                                               PRUint32 aNativeMessage, | ||||
|                                               PRUint32 aModifierFlags) | ||||
|   { return NS_ERROR_UNEXPECTED; } | ||||
| 
 | ||||
|   // Stores the clip rectangles in aRects into mClipRects. Returns true
 | ||||
|   // if the new rectangles are different from the old rectangles.
 | ||||
|   PRBool StoreWindowClipRegion(const nsTArray<nsIntRect>& aRects); | ||||
|  |  | |||
|  | @ -66,6 +66,8 @@ _TEST_FILES =	test_bug343416.xul \ | |||
| ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) | ||||
| _TEST_FILES += native_menus_window.xul \
 | ||||
|                test_native_menus.xul \
 | ||||
|                native_mouse_mac_window.xul \
 | ||||
|                test_native_mouse_mac.xul \
 | ||||
|                test_bug428405.xul \
 | ||||
|                test_bug466599.xul \
 | ||||
|                $(NULL) | ||||
|  |  | |||
							
								
								
									
										487
									
								
								widget/tests/native_mouse_mac_window.xul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										487
									
								
								widget/tests/native_mouse_mac_window.xul
									
									
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
							
								
								
									
										34
									
								
								widget/tests/test_native_mouse_mac.xul
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								widget/tests/test_native_mouse_mac.xul
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,34 @@ | |||
| <?xml version="1.0"?> | ||||
| <?xml-stylesheet href="chrome://global/skin" type="text/css"?> | ||||
| <?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" | ||||
|                  type="text/css"?> | ||||
| <window title="Native mouse event tests" | ||||
|   xmlns:html="http://www.w3.org/1999/xhtml" | ||||
|   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> | ||||
| 
 | ||||
|   <title>Native mouse event tests</title> | ||||
|   <script type="application/javascript" | ||||
|           src="chrome://mochikit/content/MochiKit/packed.js"></script> | ||||
|   <script type="application/javascript" | ||||
|           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" /> | ||||
| 
 | ||||
| <body  xmlns="http://www.w3.org/1999/xhtml"> | ||||
| <p id="display"></p> | ||||
| <div id="content" style="display: none"> | ||||
| 
 | ||||
| </div> | ||||
| <pre id="test"> | ||||
| </pre> | ||||
| </body> | ||||
| 
 | ||||
| <script class="testbody" type="application/javascript"> | ||||
| <![CDATA[ | ||||
| 
 | ||||
| SimpleTest.waitForExplicitFinish(); | ||||
| window.open("native_mouse_mac_window.xul", "NativeMouseWindow", | ||||
|             "chrome,width=600,height=600"); | ||||
| 
 | ||||
| ]]> | ||||
| </script> | ||||
| 
 | ||||
| </window> | ||||
		Loading…
	
		Reference in a new issue
	
	 Markus Stange
						Markus Stange