зеркало из https://github.com/mozilla/gecko-dev.git
Focus event fixes for Cocoa NPAPI. b=559758 r=roc a=#developers
This commit is contained in:
Родитель
f1cf0ec98d
Коммит
c28e6f9d01
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче