Focus event fixes for Cocoa NPAPI. b=559758 r=roc a=#developers

This commit is contained in:
Josh Aas 2010-04-25 16:58:03 -04:00
Родитель f1cf0ec98d
Коммит c28e6f9d01
9 изменённых файлов: 212 добавлений и 9 удалений

Просмотреть файл

@ -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:
{

Просмотреть файл

@ -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

Просмотреть файл

@ -0,0 +1,75 @@
<html>
<head>
<title>NPCocoaEventFocusChanged Tests</title>
</head>
<body onload="runTests()">
<embed id="plugin1" type="application/x-test" width="400" height="400"></embed>
<embed id="plugin2" type="application/x-test" width="400" height="400"></embed>
<script type="application/javascript">
function is(aLeft, aRight, aMessage) {
window.opener.SimpleTest.is(aLeft, aRight, aMessage);
}
function ok(aValue, aMessage) {
window.opener.SimpleTest.ok(aValue, aMessage);
}
function runTests() {
var plugin1 = document.getElementById("plugin1"); // What we're testing.
var plugin2 = document.getElementById("plugin2"); // Dummy.
// Don't run any tests if we're not testing the Cocoa event model.
if (plugin1.getEventModel() != 1) {
window.opener.testsFinished();
return;
}
// The expected event count which applies to all instances.
// Initialize to 0 since there is no initial state event,
// plugins should assume they do not initially have focus.
var expectedEventCount = 0;
// Make sure initial event count is correct.
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Make sure initial focus state is unkown (assumed false).
var initialStateUnknown = false;
try {
plugin1.getFocusState();
} catch (e) {
initialStateUnknown = true;
}
is(initialStateUnknown, true, "Initial state should be unknown, assumed false.");
// Give the plugin focus.
plugin1.focus();
expectedEventCount++;
is(plugin1.getFocusState(), true, "Plugin should have focus.");
is(plugin1.getFocusEventCount(), expectedEventCount, "Focus event count should be " + expectedEventCount);
// Make sure window activation state changes don't affect plugin focus.
// Past this point we can't really count events because of how Gecko's focus works.
// Only the end result matters anyway.
// Blur the window.
window.focus(); // start in an active state
window.blur();
is(plugin1.getFocusState(), true, "Plugin should have focus.");
// Focus the window.
window.focus();
is(plugin1.getFocusState(), true, "Plugin should have focus.");
// Take focus from the plugin.
plugin2.focus();
is(plugin1.getFocusState(), false, "Plugin should not have focus.");
window.opener.testsFinished();
}
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,26 @@
<html>
<head>
<title>NPCocoaEventFocusChanged 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"></script>
</head>
<body onload="runTests()">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
var gOtherWindow;
function runTests() {
// We have to have two top-level windows in play in order to run these tests.
gOtherWindow = window.open("cocoa_focus.html", "", "width=200,height=200");
}
function testsFinished() {
// Tests have finished running, close the new window and end tests.
gOtherWindow.close();
SimpleTest.finish();
}
</script>
</body>
</html>

Просмотреть файл

@ -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:

Просмотреть файл

@ -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<TestNPObject*>(npobj)->npp;
InstanceData* id = static_cast<InstanceData*>(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<TestNPObject*>(npobj)->npp;
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
INT32_TO_NPVARIANT(id->focusEventCount, *result);
return true;
}
bool
getEventModel(NPObject* npobj, const NPVariant* args, uint32_t argCount,
NPVariant* result)

Просмотреть файл

@ -131,6 +131,8 @@ typedef struct InstanceData {
bool crashOnDestroy;
ActivationState topLevelWindowActivationState;
int32_t topLevelWindowActivationEventCount;
ActivationState focusState;
int32_t focusEventCount;
int32_t eventModel;
} InstanceData;

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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