From c28e6f9d0166f7864941502f8301538a37cdbf71 Mon Sep 17 00:00:00 2001 From: Josh Aas Date: Sun, 25 Apr 2010 16:58:03 -0400 Subject: [PATCH] Focus event fixes for Cocoa NPAPI. b=559758 r=roc a=#developers --- layout/generic/nsObjectFrame.cpp | 6 +- modules/plugin/test/mochitest/Makefile.in | 2 + .../plugin/test/mochitest/cocoa_focus.html | 75 +++++++++++++++++++ .../test/mochitest/test_cocoa_focus.html | 26 +++++++ modules/plugin/test/testplugin/README | 11 +++ modules/plugin/test/testplugin/nptest.cpp | 51 +++++++++++++ modules/plugin/test/testplugin/nptest.h | 2 + .../plugin/test/testplugin/nptest_macosx.mm | 5 ++ widget/src/cocoa/nsChildView.mm | 43 ++++++++++- 9 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 modules/plugin/test/mochitest/cocoa_focus.html create mode 100644 modules/plugin/test/mochitest/test_cocoa_focus.html diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index de9cf80e45ca..b4ba5b7aaec0 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -4427,12 +4427,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) if (eventModel == NPEventModelCarbon) { synthCarbonEvent.what = (anEvent.message == NS_FOCUS_CONTENT) ? NPEventType_GetFocusEvent : NPEventType_LoseFocusEvent; - } else -#endif - { - synthCocoaEvent.type = NPCocoaEventFocusChanged; - synthCocoaEvent.data.focus.hasFocus = (anEvent.message == NS_FOCUS_CONTENT); } +#endif break; case NS_MOUSE_MOVE: { diff --git a/modules/plugin/test/mochitest/Makefile.in b/modules/plugin/test/mochitest/Makefile.in index 1659b2160efc..50efecece42d 100644 --- a/modules/plugin/test/mochitest/Makefile.in +++ b/modules/plugin/test/mochitest/Makefile.in @@ -116,6 +116,8 @@ _MOCHICHROME_FILES += \ _MOCHITEST_FILES += \ test_cocoa_window_focus.html \ cocoa_window_focus.html \ + test_cocoa_focus.html \ + cocoa_focus.html \ $(NULL) endif diff --git a/modules/plugin/test/mochitest/cocoa_focus.html b/modules/plugin/test/mochitest/cocoa_focus.html new file mode 100644 index 000000000000..5d0fde6581f7 --- /dev/null +++ b/modules/plugin/test/mochitest/cocoa_focus.html @@ -0,0 +1,75 @@ + + + NPCocoaEventFocusChanged Tests + + + + + + + diff --git a/modules/plugin/test/mochitest/test_cocoa_focus.html b/modules/plugin/test/mochitest/test_cocoa_focus.html new file mode 100644 index 000000000000..4da519afc99f --- /dev/null +++ b/modules/plugin/test/mochitest/test_cocoa_focus.html @@ -0,0 +1,26 @@ + + + NPCocoaEventFocusChanged Tests + + + + + + + + diff --git a/modules/plugin/test/testplugin/README b/modules/plugin/test/testplugin/README index dc0cd1ce3529..5c27ea0eb155 100644 --- a/modules/plugin/test/testplugin/README +++ b/modules/plugin/test/testplugin/README @@ -49,6 +49,17 @@ inactive, and throws an exception if the state is unknown (uninitialized). Returns the number of NPCocoaEventWindowFocusChanged events received by the instance. +== Focus Tests == + +* getFocusState() +Returns the plugin's focus state. Returns true for focused, false for unfocused, +and throws an exception if the state is unknown (uninitialized). This does not +necessarily correspond to actual input focus - this corresponds to focus as +defined by the NPAPI event model in use. + +* getFocusEventCount() +Returns the number of focus events received by the instance. + == NPRuntime testing == The test plugin object supports the following scriptable methods: diff --git a/modules/plugin/test/testplugin/nptest.cpp b/modules/plugin/test/testplugin/nptest.cpp index 027cf3b08ed0..1a65c5d189c2 100644 --- a/modules/plugin/test/testplugin/nptest.cpp +++ b/modules/plugin/test/testplugin/nptest.cpp @@ -163,6 +163,8 @@ static bool crashPluginInNestedLoop(NPObject* npobj, const NPVariant* args, uint static bool propertyAndMethod(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool getTopLevelWindowActivationState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool getTopLevelWindowActivationEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getFocusState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getFocusEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool getEventModel(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static const NPUTF8* sPluginMethodIdentifierNames[] = { @@ -210,6 +212,8 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = { "propertyAndMethod", "getTopLevelWindowActivationState", "getTopLevelWindowActivationEventCount", + "getFocusState", + "getFocusEventCount", "getEventModel" }; static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)]; @@ -258,6 +262,8 @@ static const ScriptableFunction sPluginMethodFunctions[] = { propertyAndMethod, getTopLevelWindowActivationState, getTopLevelWindowActivationEventCount, + getFocusState, + getFocusEventCount, getEventModel }; @@ -680,6 +686,8 @@ NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* instanceData->crashOnDestroy = false; instanceData->topLevelWindowActivationState = ACTIVATION_STATE_UNKNOWN; instanceData->topLevelWindowActivationEventCount = 0; + instanceData->focusState = ACTIVATION_STATE_UNKNOWN; + instanceData->focusEventCount = 0; instanceData->eventModel = 0; instance->pdata = instanceData; @@ -2847,6 +2855,49 @@ getTopLevelWindowActivationEventCount(NPObject* npobj, const NPVariant* args, ui return true; } +// Returns top-level window activation state as indicated by Cocoa NPAPI's +// NPCocoaEventWindowFocusChanged events - 'true' if active, 'false' if not. +// Throws an exception if no events have been received and thus this state +// is unknown. +bool +getFocusState(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + // Throw an exception for unknown state. + if (id->focusState == ACTIVATION_STATE_UNKNOWN) { + return false; + } + + if (id->focusState == ACTIVATION_STATE_ACTIVATED) { + BOOLEAN_TO_NPVARIANT(true, *result); + } else if (id->focusState == ACTIVATION_STATE_DEACTIVATED) { + BOOLEAN_TO_NPVARIANT(false, *result); + } + + return true; +} + +bool +getFocusEventCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + INT32_TO_NPVARIANT(id->focusEventCount, *result); + + return true; +} + bool getEventModel(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) diff --git a/modules/plugin/test/testplugin/nptest.h b/modules/plugin/test/testplugin/nptest.h index d4cb0432dfd4..1c81f8ca1c76 100644 --- a/modules/plugin/test/testplugin/nptest.h +++ b/modules/plugin/test/testplugin/nptest.h @@ -131,6 +131,8 @@ typedef struct InstanceData { bool crashOnDestroy; ActivationState topLevelWindowActivationState; int32_t topLevelWindowActivationEventCount; + ActivationState focusState; + int32_t focusEventCount; int32_t eventModel; } InstanceData; diff --git a/modules/plugin/test/testplugin/nptest_macosx.mm b/modules/plugin/test/testplugin/nptest_macosx.mm index c76588ea5d2d..4c9c7894e460 100644 --- a/modules/plugin/test/testplugin/nptest_macosx.mm +++ b/modules/plugin/test/testplugin/nptest_macosx.mm @@ -294,6 +294,11 @@ pluginHandleEvent(InstanceData* instanceData, void* event) ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED; instanceData->topLevelWindowActivationEventCount = instanceData->topLevelWindowActivationEventCount + 1; return 1; + case NPCocoaEventFocusChanged: + instanceData->focusState = cocoaEvent->data.focus.hasFocus ? + ACTIVATION_STATE_ACTIVATED : ACTIVATION_STATE_DEACTIVATED; + instanceData->focusEventCount = instanceData->focusEventCount + 1; + return 1; default: return 1; } diff --git a/widget/src/cocoa/nsChildView.mm b/widget/src/cocoa/nsChildView.mm index 2cecd52b227d..8c5ac7ba1118 100644 --- a/widget/src/cocoa/nsChildView.mm +++ b/widget/src/cocoa/nsChildView.mm @@ -947,7 +947,7 @@ void nsChildView::ResetParent() } nsIWidget* -nsChildView::GetParent(void) +nsChildView::GetParent() { return mParentWidget; } @@ -2265,6 +2265,9 @@ NSEvent* gLastDragMouseDownEvent = nil; - (void)updatePluginTopLevelWindowStatus:(BOOL)hasMain { + if (!mGeckoChild) + return; + nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild); NPCocoaEvent cocoaEvent; InitNPCocoaEvent(&cocoaEvent); @@ -2436,7 +2439,7 @@ NSEvent* gLastDragMouseDownEvent = nil; } // We accept key and mouse events, so don't keep passing them up the chain. Allow -// this to be a 'focussed' widget for event dispatch +// this to be a 'focused' widget for event dispatch. - (BOOL)acceptsFirstResponder { return YES; @@ -5469,16 +5472,48 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf) NS_OBJC_END_TRY_ABORT_BLOCK; } -// This method is called when are are about to lose focus. +- (void)updateCocoaPluginFocusStatus:(BOOL)hasFocus +{ + if (!mGeckoChild) + return; + + nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild); + NPCocoaEvent cocoaEvent; + InitNPCocoaEvent(&cocoaEvent); + cocoaEvent.type = NPCocoaEventFocusChanged; + cocoaEvent.data.focus.hasFocus = hasFocus; + pluginEvent.pluginEvent = &cocoaEvent; + mGeckoChild->DispatchWindowEvent(pluginEvent); +} + +// We must always call through to our superclass, even when mGeckoChild is +// nil -- otherwise the keyboard focus can end up in the wrong NSView. +- (BOOL)becomeFirstResponder +{ + NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + + if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) { + [self updateCocoaPluginFocusStatus:YES]; + } + + return [super becomeFirstResponder]; + + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES); +} + // We must always call through to our superclass, even when mGeckoChild is // nil -- otherwise the keyboard focus can end up in the wrong NSView. - (BOOL)resignFirstResponder { NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; + if (mIsPluginView && mPluginEventModel == NPEventModelCocoa) { + [self updateCocoaPluginFocusStatus:NO]; + } + return [super resignFirstResponder]; - NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO); + NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(YES); } - (void)viewsWindowDidBecomeKey