зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
d234cc450e
Коммит
c2055691da
|
@ -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
|
||||
|
@ -385,6 +407,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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -2393,9 +2425,6 @@ NSEvent* gLastDragEvent = nil;
|
|||
[mLastMouseDownEvent release];
|
||||
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];
|
||||
}
|
||||
|
||||
return status;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nsEventStatus_eIgnore);
|
||||
nsEventStatus status; // ignored
|
||||
mGeckoChild->DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
- (void)mouseMoved:(NSEvent*)theEvent
|
||||
- (void)mouseMoved:(NSEvent*)aEvent
|
||||
{
|
||||
ChildViewMouseTracker::MouseMoved(aEvent);
|
||||
}
|
||||
|
||||
- (void)handleMouseMoved:(NSEvent*)theEvent
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
if (![self window])
|
||||
return;
|
||||
|
||||
// Work around an Apple bug that causes the OS to continue sending
|
||||
// mouseMoved events to a window for a while after it's been miniaturized.
|
||||
// This may be related to a similar problem with popup windows (bmo bug
|
||||
// 378645, popup windows continue to receive mouseMoved events after having
|
||||
// been "ordered out"), which is worked around in nsCocoaWindow::Show()
|
||||
// (search on 378645 in nsCocoaWindow.mm). This problem is bmo bug 410219,
|
||||
// and exists in both OS X 10.4 and 10.5.
|
||||
if ([[self window] isMiniaturized])
|
||||
return;
|
||||
|
||||
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(theEvent, [self window]);
|
||||
NSPoint viewEventLocation = [self convertPoint:windowEventLocation fromView:nil];
|
||||
|
||||
// Installing a mouseMoved handler on the EventMonitor target (in
|
||||
// nsToolkit::RegisterForAllProcessMouseEvents()) means that some of the
|
||||
// events received here come from other processes. For this reason we need
|
||||
// to avoid processing them unless they're over a context menu -- otherwise
|
||||
// tooltips and other mouse-hover effects will "work" even when our app
|
||||
// doesn't have the focus.
|
||||
BOOL mouseEventIsOverRollupWidget = NO;
|
||||
if (gRollupWidget) {
|
||||
NSWindow *popupWindow = (NSWindow*)gRollupWidget->GetNativeData(NS_NATIVE_WINDOW);
|
||||
mouseEventIsOverRollupWidget = nsCocoaUtils::IsEventOverWindow(theEvent, popupWindow);
|
||||
}
|
||||
|
||||
if (![NSApp isActive] && !mouseEventIsOverRollupWidget) {
|
||||
if (sLastViewEntered) {
|
||||
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
||||
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
||||
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
||||
&exitEventLocation, nsMouseEvent::eTopLevel,
|
||||
[theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
|
||||
[theEvent deltaY], [theEvent deltaZ]);
|
||||
sLastViewEntered = nil;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (![self ensureCorrectMouseEventTarget:theEvent])
|
||||
return;
|
||||
|
||||
NSView* view = [[[self window] contentView] hitTest:windowEventLocation];
|
||||
if (view) {
|
||||
// we shouldn't handle this if the hit view is not us
|
||||
if (view != (NSView*)self) {
|
||||
[view mouseMoved:theEvent];
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the hit test returned nil then the mouse isn't over the window. If thse mouse
|
||||
// exited the window then send mouse exit to the last view in the window it was over.
|
||||
if (sLastViewEntered) {
|
||||
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
||||
// NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
|
||||
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
||||
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
||||
&exitEventLocation, nsMouseEvent::eTopLevel,
|
||||
[theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
|
||||
[theEvent deltaY], [theEvent deltaZ]);
|
||||
sLastViewEntered = nil;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we are supposed to handle this event. If we were not the last view entered, then
|
||||
// we should send an exit event to the last view entered and an enter event to ourselves.
|
||||
if (!mGeckoChild)
|
||||
return;
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
if (sLastViewEntered != self) {
|
||||
if (sLastViewEntered) {
|
||||
NSPoint exitEventLocation = [sLastViewEntered convertPoint:windowEventLocation fromView:nil];
|
||||
// NSLog(@"sending NS_MOUSE_EXIT event with point %f,%f\n", exitEventLocation.x, exitEventLocation.y);
|
||||
nsIWidget* lastViewEnteredWidget = [(NSView<mozView>*)sLastViewEntered widget];
|
||||
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_EXIT, lastViewEnteredWidget, nsMouseEvent::eReal,
|
||||
&exitEventLocation, nsMouseEvent::eChild,
|
||||
[theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
|
||||
[theEvent deltaY], [theEvent deltaZ]);
|
||||
|
||||
// The mouse exit event we just sent may have destroyed this widget, bail if that happened.
|
||||
if (!mGeckoChild)
|
||||
return;
|
||||
}
|
||||
|
||||
// NSLog(@"sending NS_MOUSE_ENTER event with point %f,%f\n", viewEventLocation.x, viewEventLocation.y);
|
||||
SendGeckoMouseEnterOrExitEvent(PR_TRUE, NS_MOUSE_ENTER, mGeckoChild, nsMouseEvent::eReal,
|
||||
&viewEventLocation, nsMouseEvent::eChild,
|
||||
[theEvent modifierFlags], [theEvent buttonNumber], [theEvent deltaX],
|
||||
[theEvent deltaY], [theEvent deltaZ]);
|
||||
|
||||
// The mouse enter event we just sent may have destroyed this widget, bail if that happened.
|
||||
if (!mGeckoChild)
|
||||
return;
|
||||
|
||||
// mark this view as the last view entered
|
||||
sLastViewEntered = (NSView*)self;
|
||||
}
|
||||
|
||||
nsMouseEvent geckoEvent(PR_TRUE, NS_MOUSE_MOVE, nsnull, nsMouseEvent::eReal);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
|
||||
|
||||
|
@ -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,17 +140,11 @@ 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)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
Загрузка…
Ссылка в новой задаче