Bug 610015: Implement updated Cocoa NPAPI text input spec. Part of this patch written by Steven Michaud. r=josh r=smichaud a=blocking2.0betaN+

This commit is contained in:
Josh Aas 2011-01-20 20:08:11 -05:00
Родитель 160e885b9b
Коммит 2f5208148e
6 изменённых файлов: 212 добавлений и 35 удалений

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

@ -409,7 +409,12 @@ PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
*((NPBool*)aValue) = true;
return NPERR_NO_ERROR;
}
case NPNVsupportsUpdatedCocoaTextInputBool: {
*static_cast<NPBool*>(aValue) = true;
return NPERR_NO_ERROR;
}
#ifndef NP_NO_QUICKDRAW
case NPNVsupportsQuickDrawBool: {
*((NPBool*)aValue) = false;

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

@ -2234,6 +2234,11 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
return NPERR_NO_ERROR;
}
case NPNVsupportsUpdatedCocoaTextInputBool: {
*(NPBool*)result = true;
return NPERR_NO_ERROR;
}
#endif
// we no longer hand out any XPCOM objects, except on WINCE,

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

@ -39,6 +39,7 @@
- (NSTextInputContext*)inputContext;
- (BOOL)interpretKeyEvent:(NSEvent*)event string:(NSString**)string;
- (void)cancelComposition;
- (BOOL)inComposition;
@end

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

@ -124,4 +124,9 @@
[self orderOut:nil];
}
- (BOOL)inComposition
{
return [mInputTextView hasMarkedText];
}
@end

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

@ -174,9 +174,13 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
// Cocoa TSM documents (those created and managed by the NSTSMInputContext
// class) -- for some reason TSMProcessRawKeyEvent() doesn't work with them.
TSMDocumentID mPluginTSMDoc;
BOOL mPluginTSMInComposition;
#endif
BOOL mPluginComplexTextInputRequested;
// When this is YES the next key up event (keyUp:) will be ignored.
BOOL mIgnoreNextKeyUpEvent;
NSOpenGLContext *mGLContext;
// Simple gestures support

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

@ -182,6 +182,10 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
- (NPEventModel)pluginEventModel;
- (NPDrawingModel)pluginDrawingModel;
#ifndef NP_NO_CARBON
- (void)setPluginTSMInComposition:(BOOL)inComposition;
#endif
- (BOOL)isRectObscuredBySubview:(NSRect)inRect;
- (void)processPendingRedraws;
@ -541,6 +545,15 @@ nsresult nsChildView::Create(nsIWidget *aParent,
if (!gChildViewMethodsSwizzled) {
nsToolkit::SwizzleMethods([NSView class], @selector(mouseDownCanMoveWindow),
@selector(nsChildView_NSView_mouseDownCanMoveWindow));
#ifndef NP_NO_CARBON
Class IMKInputSessionClass = ::NSClassFromString(@"IMKInputSession");
nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(handleEvent:),
@selector(nsChildView_IMKInputSession_handleEvent:));
nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(commitComposition),
@selector(nsChildView_IMKInputSession_commitComposition));
nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(finishSession),
@selector(nsChildView_IMKInputSession_finishSession));
#endif
gChildViewMethodsSwizzled = PR_TRUE;
}
@ -2226,9 +2239,12 @@ NSEvent* gLastDragMouseDownEvent = nil;
#ifndef NP_NO_CARBON
mPluginTSMDoc = nil;
mPluginTSMInComposition = NO;
#endif
mPluginComplexTextInputRequested = NO;
mIgnoreNextKeyUpEvent = NO;
mGestureState = eGestureState_None;
mCumulativeMagnification = 0.0;
mCumulativeRotation = 0.0;
@ -2505,16 +2521,23 @@ NSEvent* gLastDragMouseDownEvent = nil;
mPluginDrawingModel = drawingModel;
}
- (NPEventModel)pluginEventModel;
- (NPEventModel)pluginEventModel
{
return mPluginEventModel;
}
- (NPDrawingModel)pluginDrawingModel;
- (NPDrawingModel)pluginDrawingModel
{
return mPluginDrawingModel;
}
#ifndef NP_NO_CARBON
- (void)setPluginTSMInComposition:(BOOL)inComposition
{
mPluginTSMInComposition = inComposition;
}
#endif
- (void)sendFocusEvent:(PRUint32)eventType
{
if (!mGeckoChild)
@ -5347,60 +5370,117 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
return YES;
}
- (BOOL)inCocoaPluginComposition
{
#ifdef NP_NO_CARBON
return [[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition];
#else
return mPluginTSMInComposition;
#endif
}
- (void)sendCocoaNPAPITextEvent:(NSString*)string
{
NPCocoaEvent cocoaTextEvent;
InitNPCocoaEvent(&cocoaTextEvent);
cocoaTextEvent.type = NPCocoaEventTextInput;
cocoaTextEvent.data.text.text = (NPNSString*)string;
nsGUIEvent pluginTextEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
pluginTextEvent.time = PR_IntervalNow();
pluginTextEvent.pluginEvent = (void*)&cocoaTextEvent;
mGeckoChild->DispatchWindowEvent(pluginTextEvent);
}
- (void)keyDown:(NSEvent*)theEvent
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (mGeckoChild && mIsPluginView) {
// If a plugin has the focus, we need to use an alternate method for
// handling NSKeyDown and NSKeyUp events (otherwise Carbon-based IME won't
// work in plugins like the Flash plugin). The same strategy is used by the
// WebKit. See PluginKeyEventsHandler() and [ChildView processPluginKeyEvent:]
// for more info.
#ifdef NP_NO_CARBON
if (mPluginEventModel == NPEventModelCocoa) {
// Reset complex text input request.
ComplexTextInputPanel* ctiPanel = [ComplexTextInputPanel sharedComplexTextInputPanel];
// If a composition is in progress then simply let the input panel continue it.
if ([self inCocoaPluginComposition]) {
// Don't send key up events for key downs associated with compositions.
mIgnoreNextKeyUpEvent = YES;
NSString* textString = nil;
[ctiPanel interpretKeyEvent:theEvent string:&textString];
if (textString) {
[self sendCocoaNPAPITextEvent:textString];
}
return;
}
// Reset complex text input request flag.
mPluginComplexTextInputRequested = NO;
// Send key down event.
// Send key down event to the plugin.
nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
NPCocoaEvent cocoaEvent;
ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent);
pluginEvent.pluginEvent = &cocoaEvent;
mGeckoChild->DispatchWindowEvent(pluginEvent);
if (!mGeckoChild)
if (!mGeckoChild) {
return;
}
if (!mPluginComplexTextInputRequested) {
#ifdef NP_NO_CARBON
[[ComplexTextInputPanel sharedComplexTextInputPanel] cancelComposition];
#else
if (mPluginTSMDoc) {
::FixTSMDocument(mPluginTSMDoc);
// Start complex text composition if requested.
if (mPluginComplexTextInputRequested) {
// Don't send key up events for key downs associated with compositions.
mIgnoreNextKeyUpEvent = YES;
NSString* textString = nil;
[ctiPanel interpretKeyEvent:theEvent string:&textString];
if (textString) {
[self sendCocoaNPAPITextEvent:textString];
}
#endif
return;
}
#ifdef NP_NO_CARBON
ComplexTextInputPanel* ctInputPanel = [ComplexTextInputPanel sharedComplexTextInputPanel];
NSString* textString = nil;
[ctInputPanel interpretKeyEvent:theEvent string:&textString];
if (textString) {
NPCocoaEvent cocoaTextEvent;
InitNPCocoaEvent(&cocoaTextEvent);
cocoaTextEvent.type = NPCocoaEventTextInput;
cocoaTextEvent.data.text.text = (NPNSString*)textString;
nsGUIEvent pluginTextEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
pluginTextEvent.time = PR_IntervalNow();
pluginTextEvent.pluginEvent = (void*)&cocoaTextEvent;
mGeckoChild->DispatchWindowEvent(pluginTextEvent);
}
// Nothing else to do for Cocoa NPAPI plugins.
return;
#endif
}
#endif
#ifndef NP_NO_CARBON
BOOL wasInComposition = NO;
if (mPluginEventModel == NPEventModelCocoa) {
if ([self inCocoaPluginComposition]) {
wasInComposition = YES;
// Don't send key up events for key downs associated with compositions.
mIgnoreNextKeyUpEvent = YES;
}
else {
// Reset complex text input request flag.
mPluginComplexTextInputRequested = NO;
// Send key down event to the plugin.
nsGUIEvent pluginEvent(PR_TRUE, NS_NON_RETARGETED_PLUGIN_EVENT, mGeckoChild);
NPCocoaEvent cocoaEvent;
ConvertCocoaKeyEventToNPCocoaEvent(theEvent, cocoaEvent);
pluginEvent.pluginEvent = &cocoaEvent;
mGeckoChild->DispatchWindowEvent(pluginEvent);
if (!mGeckoChild) {
return;
}
// Only continue if plugin wants complex text input.
if (mPluginComplexTextInputRequested) {
// Don't send key up events for key downs associated with compositions.
mIgnoreNextKeyUpEvent = YES;
}
else {
return;
}
}
}
// This will take care of all Carbon plugin events and also send Cocoa plugin
// text events when NSInputContext is not available (ifndef NP_NO_CARBON).
[self activatePluginTSMDoc];
@ -5413,6 +5493,7 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
sizeof(ChildView *), &self);
::TSMProcessRawKeyEvent([theEvent _eventRef]);
::TSMRemoveDocumentProperty(mPluginTSMDoc, kFocusedChildViewTSMDocPropertyTag);
return;
#endif
}
@ -5446,10 +5527,20 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
if (!mGeckoChild)
return;
if (mIgnoreNextKeyUpEvent) {
mIgnoreNextKeyUpEvent = NO;
return;
}
nsAutoRetainCocoaObject kungFuDeathGrip(self);
if (mIsPluginView) {
if (mPluginEventModel == NPEventModelCocoa) {
// Don't send key up events to Cocoa plugins during composition.
if ([self inCocoaPluginComposition]) {
return;
}
nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, nsnull);
[self convertCocoaKeyEvent:theEvent toGeckoEvent:&keyUpEvent];
NPCocoaEvent pluginEvent;
@ -6595,6 +6686,72 @@ void NS_RemovePluginKeyEventsHandler()
gPluginKeyEventsHandler = NULL;
}
// IMKInputSession is an undocumented class in the HIToolbox framework. It's
// present on both Leopard and SnowLeopard, and is used at a low level to
// process IME input regardless of which high-level API is used (Text Services
// Manager or Cocoa). It works the same way in both 32-bit and 64-bit code.
@interface NSObject (IMKInputSessionMethodSwizzling)
- (BOOL)nsChildView_IMKInputSession_handleEvent:(EventRef)theEvent;
- (void)nsChildView_IMKInputSession_commitComposition;
- (void)nsChildView_IMKInputSession_finishSession;
@end
@implementation NSObject (IMKInputSessionMethodSwizzling)
- (BOOL)nsChildView_IMKInputSession_handleEvent:(EventRef)theEvent
{
[self retain];
BOOL retval = [self nsChildView_IMKInputSession_handleEvent:theEvent];
NSUInteger retainCount = [self retainCount];
[self release];
// Return without doing anything if we've been deleted.
if (retainCount == 1) {
return retval;
}
NSWindow *mainWindow = [NSApp mainWindow];
NSResponder *firstResponder = [mainWindow firstResponder];
if (![firstResponder isKindOfClass:[ChildView class]]) {
return retval;
}
// 'charactersEntered' is the length (in bytes) of currently "marked text"
// -- text that's been entered in IME but not yet committed. If it's
// non-zero we're composing text in an IME session; if it's zero we're
// not in an IME session.
NSInteger charactersEntered = 0;
object_getInstanceVariable(self, "charactersEntered", (void **) &charactersEntered);
[(ChildView*)firstResponder setPluginTSMInComposition:(charactersEntered != 0)];
return retval;
}
// This method is called whenever IME input is committed as a result of an
// "abnormal" termination -- for example when changing the keyboard focus from
// one input field to another.
- (void)nsChildView_IMKInputSession_commitComposition
{
NSWindow *mainWindow = [NSApp mainWindow];
NSResponder *firstResponder = [mainWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]]) {
[(ChildView*)firstResponder setPluginTSMInComposition:NO];
}
[self nsChildView_IMKInputSession_commitComposition];
}
// This method is called just before we're deallocated.
- (void)nsChildView_IMKInputSession_finishSession
{
NSWindow *mainWindow = [NSApp mainWindow];
NSResponder *firstResponder = [mainWindow firstResponder];
if ([firstResponder isKindOfClass:[ChildView class]]) {
[(ChildView*)firstResponder setPluginTSMInComposition:NO];
}
[self nsChildView_IMKInputSession_finishSession];
}
@end
#endif // NP_NO_CARBON
@interface NSView (MethodSwizzling)