Bug 519972 part.8 Move keydown and insertText implementation to TextInputHandler r=smichaud

This commit is contained in:
Masayuki Nakano 2011-02-24 03:06:26 +09:00
parent 940e8ac3cb
commit 8ab42896da
5 changed files with 544 additions and 345 deletions

View File

@ -407,29 +407,6 @@ public:
const nsAString& aCharacters,
const nsAString& aUnmodifiedCharacters);
/**
* IsPrintableChar() checks whether the unicode character is
* a non-printable ASCII character or not. Note that this returns
* TRUE even if aChar is a non-printable UNICODE character.
*
* @param aChar A unicode character.
* @return TRUE if aChar is a printable ASCII character
* or a unicode character. Otherwise, i.e,
* if aChar is a non-printable ASCII character,
* FALSE.
*/
static PRBool IsPrintableChar(PRUnichar aChar);
/**
* ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from
* aChar. If aChar is not an ASCII character, this always returns FALSE.
*
* @param aChar A unicode character.
* @return A Gecko defined keyCode. Or zero if aChar
* is a unicode character.
*/
static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar);
/**
* ComputeGeckoKeyCode() computes Gecko defined keyCode from the native
* keyCode or the characters.
@ -459,24 +436,6 @@ public:
*/
static PRBool IsSpecialGeckoKey(UInt32 aNativeKeyCode);
/**
* IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input.
*
* @param aKeyEvent A key event.
* @return TRUE if the key event causes text input.
* Otherwise, FALSE.
*/
static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent);
/**
* IsModifierKey() checks whether the native keyCode is for a modifier key.
*
* @param aNativeKeyCode A native keyCode.
* @return TRUE if aNativeKeyCode is for a modifier key.
* Otherwise, FALSE.
*/
static PRBool IsModifierKey(UInt32 aNativeKeyCode);
protected:
nsAutoRefCnt mRefCnt;
@ -507,6 +466,121 @@ protected:
PRBool Destroyed() { return !mWidget; }
/**
* mCurrentKeyEvent indicates what key event we are handling. While
* handling a native keydown event, we need to store the event for insertText,
* doCommandBySelector and various action message handlers of NSResponder
* such as [NSResponder insertNewline:sender].
*/
struct KeyEventState
{
// Handling native key event
NSEvent* mKeyEvent;
// Whether keydown event was consumed by web contents or chrome contents.
PRPackedBool mKeyDownHandled;
// Whether keypress event was dispatched for mKeyEvent.
PRPackedBool mKeyPressDispatched;
// Whether keypress event was consumed by web contents or chrome contents.
PRPackedBool mKeyPressHandled;
KeyEventState() : mKeyEvent(nsnull)
{
Clear();
}
~KeyEventState()
{
Clear();
}
void Set(NSEvent* aNativeKeyEvent)
{
NS_PRECONDITION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL");
Clear();
mKeyEvent = [aNativeKeyEvent retain];
}
void Clear()
{
if (mKeyEvent) {
[mKeyEvent release];
mKeyEvent = nsnull;
}
mKeyDownHandled = PR_FALSE;
mKeyPressDispatched = PR_FALSE;
mKeyPressHandled = PR_FALSE;
}
PRBool KeyDownOrPressHandled()
{
return mKeyDownHandled || mKeyPressHandled;
}
};
/**
* Helper class for guaranteeing cleaning mCurrentKeyEvent
*/
class AutoKeyEventStateCleaner
{
public:
AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) :
mHandler(aHandler)
{
}
~AutoKeyEventStateCleaner()
{
mHandler->mCurrentKeyEvent.Clear();
}
private:
TextInputHandlerBase* mHandler;
};
// XXX If keydown event was nested, the key event is overwritten by newer
// event. This is wrong behavior. Some IMEs are making such situation.
KeyEventState mCurrentKeyEvent;
/**
* IsPrintableChar() checks whether the unicode character is
* a non-printable ASCII character or not. Note that this returns
* TRUE even if aChar is a non-printable UNICODE character.
*
* @param aChar A unicode character.
* @return TRUE if aChar is a printable ASCII character
* or a unicode character. Otherwise, i.e,
* if aChar is a non-printable ASCII character,
* FALSE.
*/
static PRBool IsPrintableChar(PRUnichar aChar);
/**
* ComputeGeckoKeyCodeFromChar() computes Gecko defined keyCode value from
* aChar. If aChar is not an ASCII character, this always returns FALSE.
*
* @param aChar A unicode character.
* @return A Gecko defined keyCode. Or zero if aChar
* is a unicode character.
*/
static PRUint32 ComputeGeckoKeyCodeFromChar(PRUnichar aChar);
/**
* IsNormalCharInputtingEvent() checks whether aKeyEvent causes text input.
*
* @param aKeyEvent A key event.
* @return TRUE if the key event causes text input.
* Otherwise, FALSE.
*/
static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aKeyEvent);
/**
* IsModifierKey() checks whether the native keyCode is for a modifier key.
*
* @param aNativeKeyCode A native keyCode.
* @return TRUE if aNativeKeyCode is for a modifier key.
* Otherwise, FALSE.
*/
static PRBool IsModifierKey(UInt32 aNativeKeyCode);
private:
struct KeyboardLayoutOverride {
PRInt32 mKeyboardLayout;
@ -527,9 +601,46 @@ private:
class PluginTextInputHandler : public TextInputHandlerBase
{
public:
#ifndef NP_NO_CARBON
/**
* ConvertCocoaKeyEventToCarbonEvent() converts aCocoaKeyEvent to
* aCarbonKeyEvent.
*
* @param aCocoaKeyEvent A Cocoa key event.
* @param aCarbonKeyEvent Converted Carbon event from aCocoaEvent.
* @param aMakeKeyDownEventIfNSFlagsChanged
* If aCocoaKeyEvent isn't NSFlagsChanged event,
* this is ignored. Otherwise, i.e., if
* aCocoaKeyEvent is NSFlagsChanged event,
* set TRUE if you need a keydown event.
* Otherwise, Set FALSE for a keyup event.
*/
static void ConvertCocoaKeyEventToCarbonEvent(
NSEvent* aCocoaKeyEvent,
EventRecord& aCarbonKeyEvent,
PRBool aMakeKeyDownEventIfNSFlagsChanged = PR_FALSE);
#endif // NP_NO_CARBON
protected:
PluginTextInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView);
~PluginTextInputHandler();
#ifndef NP_NO_CARBON
/**
* ConvertUnicodeToCharCode() converts aUnichar to native encoded string.
*
* @param aUniChar A unicode character.
* @param aOutChar Native encoded string for aUniChar.
* @return TRUE if the converting succeeded.
* Otherwise, FALSE.
*/
static PRBool ConvertUnicodeToCharCode(PRUnichar aUniChar,
unsigned char* aOutChar);
#endif // NP_NO_CARBON
};
/**
@ -581,14 +692,6 @@ public:
void SetMarkedText(NSAttributedString* aAttrString,
NSRange& aSelectedRange);
/**
* InsertTextAsCommittingComposition() commits current composition. If there
* is no composition, this starts a composition and commits it immediately.
*
* @param aAttrString A string which is committed.
*/
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString);
/**
* ConversationIdentifier() returns an ID for the current editor. The ID is
* guaranteed to be unique among currently existing editors. But it might be
@ -713,6 +816,14 @@ protected:
virtual void ExecutePendingMethods();
/**
* InsertTextAsCommittingComposition() commits current composition. If there
* is no composition, this starts a composition and commits it immediately.
*
* @param aAttrString A string which is committed.
*/
void InsertTextAsCommittingComposition(NSAttributedString* aAttrString);
private:
// If mIsIMEComposing is true, the composition string is stored here.
NSString* mIMECompositionString;
@ -831,6 +942,39 @@ public:
TextInputHandler(nsChildView* aWidget, NSView<mozView> *aNativeView);
virtual ~TextInputHandler();
/**
* KeyDown event handler.
*
* @param aNativeEvent A native keydown event which you want to
* handle.
* @return TRUE if the event is consumed by web contents
* or chrome contents. Otherwise, FALSE.
*/
PRBool HandleKeyDownEvent(NSEvent* aNativeEvent);
/**
* Insert the string to content. I.e., this is a text input event handler.
* If this is called during keydown event handling, this may dispatch a
* NS_KEY_PRESS event. If this is called during composition, this commits
* the composition by the aAttrString.
*
* @param aAttrString An inserted string.
*/
void InsertText(NSAttributedString *aAttrString);
/**
* KeyPressWasHandled() checks whether keypress event was handled or not.
*
* @return TRUE if keypress event for latest native key
* event was handled. Otherwise, FALSE.
* If this handler isn't handling any key events,
* always returns FALSE.
*/
PRBool KeyPressWasHandled()
{
return mCurrentKeyEvent.mKeyPressHandled;
}
};
} // namespace widget

View File

@ -804,6 +804,227 @@ TextInputHandler::~TextInputHandler()
[mView uninstallTextInputHandler];
}
PRBool
TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
if (Destroyed()) {
return PR_FALSE;
}
nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
mCurrentKeyEvent.Set(aNativeEvent);
AutoKeyEventStateCleaner remover(this);
BOOL nonDeadKeyPress = [[aNativeEvent characters] length] > 0;
if (nonDeadKeyPress && !IsIMEComposing()) {
NSResponder* firstResponder = [[mView window] firstResponder];
nsKeyEvent keydownEvent(PR_TRUE, NS_KEY_DOWN, mWidget);
InitKeyEvent(aNativeEvent, keydownEvent);
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
if ([mView pluginEventModel] == NPEventModelCarbon) {
ConvertCocoaKeyEventToCarbonEvent(aNativeEvent, carbonEvent, PR_TRUE);
keydownEvent.pluginEvent = &carbonEvent;
}
#endif // #ifndef NP_NO_CARBON
mCurrentKeyEvent.mKeyDownHandled = DispatchEvent(keydownEvent);
if (Destroyed()) {
return mCurrentKeyEvent.KeyDownOrPressHandled();
}
// The key down event may have shifted the focus, in which
// case we should not fire the key press.
// XXX This is a special code only on Cocoa widget, why is this needed?
if (firstResponder != [[mView window] firstResponder]) {
return mCurrentKeyEvent.KeyDownOrPressHandled();
}
// If this is the context menu key command, send a context menu key event.
NSUInteger modifierFlags =
[aNativeEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if (modifierFlags == NSControlKeyMask &&
[[aNativeEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU,
[mView widget], nsMouseEvent::eReal,
nsMouseEvent::eContextMenuKey);
contextMenuEvent.isShift = contextMenuEvent.isControl =
contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
PRBool cmEventHandled = DispatchEvent(contextMenuEvent);
[mView maybeInitContextMenuTracking];
// Bail, there is nothing else to do here.
return (cmEventHandled || mCurrentKeyEvent.KeyDownOrPressHandled());
}
nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget);
InitKeyEvent(aNativeEvent, keypressEvent);
// if this is a non-letter keypress, or the control key is down,
// dispatch the keydown to gecko, so that we trap delete,
// control-letter combinations etc before Cocoa tries to use
// them for keybindings.
// XXX This is wrong. IME may be handle the non-letter keypress event as
// its owning shortcut key. See bug 477291.
if ((!keypressEvent.isChar || keypressEvent.isControl) &&
!IsIMEComposing()) {
if (mCurrentKeyEvent.mKeyDownHandled) {
keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent);
mCurrentKeyEvent.mKeyPressDispatched = PR_TRUE;
if (Destroyed()) {
return mCurrentKeyEvent.KeyDownOrPressHandled();
}
}
}
// Let Cocoa interpret the key events, caching IsIMEComposing first.
PRBool wasComposing = IsIMEComposing();
PRBool interpretKeyEventsCalled = PR_FALSE;
if (IsIMEEnabled() || IsASCIICapableOnly()) {
[mView interpretKeyEvents:[NSArray arrayWithObject:aNativeEvent]];
interpretKeyEventsCalled = PR_TRUE;
}
if (Destroyed()) {
return mCurrentKeyEvent.KeyDownOrPressHandled();
}
if (!mCurrentKeyEvent.mKeyPressDispatched && nonDeadKeyPress &&
!wasComposing && !IsIMEComposing()) {
nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget);
InitKeyEvent(aNativeEvent, keypressEvent);
// If we called interpretKeyEvents and this isn't normal character input
// then IME probably ate the event for some reason. We do not want to
// send a key press event in that case.
// TODO:
// There are some other cases which IME eats the current event.
// 1. If key events were nested during calling interpretKeyEvents, it means
// that IME did something. Then, we should do nothing.
// 2. If one or more commands are called like "deleteBackward", we should
// dispatch keypress event at that time. Note that the command may have
// been a converted or generated action by IME. Then, we shouldn't do
// our default action for this key.
if (!(interpretKeyEventsCalled &&
IsNormalCharInputtingEvent(keypressEvent))) {
if (mCurrentKeyEvent.mKeyDownHandled) {
keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent);
}
}
// Note: mWidget might have become null here. Don't count on it from here on.
return mCurrentKeyEvent.KeyDownOrPressHandled();
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
}
void
TextInputHandler::InsertText(NSAttributedString *aAttrString)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
if (Destroyed()) {
return;
}
if (IgnoreIMEComposition()) {
return;
}
nsString str;
GetStringForNSString([aAttrString string], str);
if (!IsIMEComposing() && str.IsEmpty()) {
return; // nothing to do
}
if (str.Length() != 1 || IsIMEComposing()) {
InsertTextAsCommittingComposition(aAttrString);
return;
}
// Don't let the same event be fired twice when hitting
// enter/return! (Bug 420502)
if (mCurrentKeyEvent.mKeyPressDispatched) {
return;
}
nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
// Dispatch keypress event with char instead of textEvent
nsKeyEvent keypressEvent(PR_TRUE, NS_KEY_PRESS, mWidget);
keypressEvent.time = PR_IntervalNow();
keypressEvent.charCode = str.CharAt(0);
keypressEvent.keyCode = 0;
keypressEvent.isChar = PR_TRUE;
// Don't set other modifiers from the current event, because here in
// -insertText: they've already been taken into account in creating
// the input string.
// create event for use by plugins
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
#endif // #ifndef NP_NO_CARBON
if (mCurrentKeyEvent.mKeyEvent) {
NSEvent* keyEvent = mCurrentKeyEvent.mKeyEvent;
// XXX The ASCII characters inputting mode of egbridge (Japanese IME)
// might send the keyDown event with wrong keyboard layout if other
// keyboard layouts are already loaded. In that case, the native event
// doesn't match to this gecko event...
#ifndef NP_NO_CARBON
if ([mView pluginEventModel] == NPEventModelCarbon) {
ConvertCocoaKeyEventToCarbonEvent(keyEvent, carbonEvent, PR_TRUE);
keypressEvent.pluginEvent = &carbonEvent;
}
#endif // #ifndef NP_NO_CARBON
if (mCurrentKeyEvent.mKeyDownHandled) {
keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
keypressEvent.isShift = ([keyEvent modifierFlags] & NSShiftKeyMask) != 0;
if (!IsPrintableChar(keypressEvent.charCode)) {
keypressEvent.keyCode =
ComputeGeckoKeyCode([keyEvent keyCode],
[keyEvent charactersIgnoringModifiers]);
keypressEvent.charCode = 0;
}
} else {
// Note that insertText is not called only at key pressing.
if (!IsPrintableChar(keypressEvent.charCode)) {
keypressEvent.keyCode =
ComputeGeckoKeyCodeFromChar(keypressEvent.charCode);
keypressEvent.charCode = 0;
}
}
// TODO:
// If mCurrentKeyEvent.mKeyEvent is null and when we implement textInput
// event of DOM3 Events, we should dispatch it instead of keypress event.
PRBool keyPressHandled = DispatchEvent(keypressEvent);
// Note: mWidget might have become null here. Don't count on it from here on.
if (mCurrentKeyEvent.mKeyEvent) {
mCurrentKeyEvent.mKeyPressHandled = keyPressHandled;
mCurrentKeyEvent.mKeyPressDispatched = PR_TRUE;
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#pragma mark -
@ -949,6 +1170,7 @@ IMEInputHandler::GetCurrentTSMDocumentID()
return ::TSMGetActiveDocument();
}
#pragma mark -
@ -2015,6 +2237,103 @@ PluginTextInputHandler::~PluginTextInputHandler()
{
}
#ifndef NP_NO_CARBON
/* static */ PRBool
PluginTextInputHandler::ConvertUnicodeToCharCode(PRUnichar aUniChar,
unsigned char* aOutChar)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
UnicodeToTextInfo converterInfo;
TextEncoding systemEncoding;
Str255 convertedString;
*aOutChar = nsnull;
OSStatus err =
::UpgradeScriptInfoToTextEncoding(smSystemScript,
kTextLanguageDontCare,
kTextRegionDontCare,
NULL,
&systemEncoding);
NS_ENSURE_TRUE(err == noErr, PR_FALSE);
err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
NS_ENSURE_TRUE(err == noErr, PR_FALSE);
err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar),
&aUniChar, convertedString);
NS_ENSURE_TRUE(err == noErr, PR_FALSE);
*aOutChar = convertedString[1];
::DisposeUnicodeToTextInfo(&converterInfo);
return PR_TRUE;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
}
/* static */ void
PluginTextInputHandler::ConvertCocoaKeyEventToCarbonEvent(
NSEvent* aCocoaKeyEvent,
EventRecord& aCarbonKeyEvent,
PRBool aMakeKeyDownEventIfNSFlagsChanged)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
UInt32 charCode = 0;
if ([aCocoaKeyEvent type] == NSFlagsChanged) {
aCarbonKeyEvent.what = aMakeKeyDownEventIfNSFlagsChanged ? keyDown : keyUp;
} else {
if ([[aCocoaKeyEvent characters] length] > 0) {
charCode = [[aCocoaKeyEvent characters] characterAtIndex:0];
}
if ([aCocoaKeyEvent type] == NSKeyDown) {
aCarbonKeyEvent.what = [aCocoaKeyEvent isARepeat] ? autoKey : keyDown;
} else {
aCarbonKeyEvent.what = keyUp;
}
}
if (charCode >= 0x0080) {
switch (charCode) {
case NSUpArrowFunctionKey:
charCode = kUpArrowCharCode;
break;
case NSDownArrowFunctionKey:
charCode = kDownArrowCharCode;
break;
case NSLeftArrowFunctionKey:
charCode = kLeftArrowCharCode;
break;
case NSRightArrowFunctionKey:
charCode = kRightArrowCharCode;
break;
default:
unsigned char convertedCharCode;
if (ConvertUnicodeToCharCode(charCode, &convertedCharCode)) {
charCode = convertedCharCode;
}
//NSLog(@"charcode is %d, converted to %c, char is %@",
// charCode, convertedCharCode, [aCocoaKeyEvent characters]);
break;
}
}
aCarbonKeyEvent.message =
(charCode & 0x00FF) | ([aCocoaKeyEvent keyCode] << 8);
aCarbonKeyEvent.when = ::TickCount();
::GetGlobalMouse(&aCarbonKeyEvent.where);
// XXX Is this correct? If ::GetCurrentKeyModifiers() returns "current"
// state and there is one or more pending modifier key events,
// the result is mismatch with the state at current key event.
aCarbonKeyEvent.modifiers = ::GetCurrentKeyModifiers();
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#endif // NP_NO_CARBON
#pragma mark -

View File

@ -38,6 +38,8 @@
#ifndef mozView_h_
#define mozView_h_
#include "npapi.h"
#undef DARWIN
#import <Cocoa/Cocoa.h>
class nsIWidget;
@ -76,6 +78,15 @@ class TextInputHandler;
- (BOOL)isDragInProgress;
// Gets the plugin event model for the view
- (NPEventModel)pluginEventModel;
// Checks whether the view is first responder or not
- (BOOL)isFirstResponder;
// Call when you dispatch an event which may cause to open context menu.
- (void)maybeInitContextMenuTracking;
@end
// An informal protocol implemented by the NSWindow of the host application.

View File

@ -192,20 +192,6 @@ extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
NPEventModel mPluginEventModel;
NPDrawingModel mPluginDrawingModel;
// The following variables are only valid during key down event processing.
// Their current usage needs to be fixed to avoid problems with nested event
// loops that can confuse them. Once a variable is set during key down event
// processing, if an event spawns a nested event loop the previously set value
// will be wiped out.
NSEvent* mCurKeyEvent;
PRBool mKeyDownHandled;
// While we process key down events we need to keep track of whether or not
// we sent a key press event. This helps us make sure we do send one
// eventually.
BOOL mKeyPressSent;
// Valid when mKeyPressSent is true.
PRBool mKeyPressHandled;
// when mouseDown: is called, we store its event here (strong)
NSEvent* mLastMouseDownEvent;

View File

@ -169,7 +169,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
- (BOOL)isPluginView;
- (void)setPluginEventModel:(NPEventModel)eventModel;
- (void)setPluginDrawingModel:(NPDrawingModel)drawingModel;
- (NPEventModel)pluginEventModel;
- (NPDrawingModel)pluginDrawingModel;
#ifndef NP_NO_CARBON
@ -180,8 +179,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
- (void)processPendingRedraws;
- (void)maybeInitContextMenuTracking;
- (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext;
// Called using performSelector:withObject:afterDelay:0 to release
@ -198,8 +195,6 @@ PRUint32 nsChildView::sLastInputEventCount = 0;
- (id<mozAccessible>)accessible;
#endif
- (BOOL)isFirstResponder;
- (void)fireKeyEventForFlagsChanged:(NSEvent*)theEvent keyDown:(BOOL)isKeyDown;
- (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
@ -2069,10 +2064,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
#else
mPluginDrawingModel = NPDrawingModelCoreGraphics;
#endif
mCurKeyEvent = nil;
mKeyDownHandled = PR_FALSE;
mKeyPressHandled = NO;
mKeyPressSent = NO;
mPendingDisplay = NO;
mBlockedLastMouseDown = NO;
@ -3853,38 +3844,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
}
#ifndef NP_NO_CARBON
static PRBool ConvertUnicodeToCharCode(PRUnichar inUniChar, unsigned char* outChar)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
UnicodeToTextInfo converterInfo;
TextEncoding systemEncoding;
Str255 convertedString;
OSStatus err;
*outChar = 0;
err = ::UpgradeScriptInfoToTextEncoding(smSystemScript, kTextLanguageDontCare, kTextRegionDontCare, NULL, &systemEncoding);
if (err != noErr)
return PR_FALSE;
err = ::CreateUnicodeToTextInfoByEncoding(systemEncoding, &converterInfo);
if (err != noErr)
return PR_FALSE;
err = ::ConvertFromUnicodeToPString(converterInfo, sizeof(PRUnichar), &inUniChar, convertedString);
if (err != noErr)
return PR_FALSE;
*outChar = convertedString[1];
::DisposeUnicodeToTextInfo(&converterInfo);
return PR_TRUE;
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(PR_FALSE);
}
#endif // NP_NO_CARBON
static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* cocoaEvent, NPCocoaEvent& pluginEvent, PRUint32 keyType = 0)
{
InitNPCocoaEvent(&pluginEvent);
@ -3912,54 +3871,6 @@ static void ConvertCocoaKeyEventToNPCocoaEvent(NSEvent* cocoaEvent, NPCocoaEvent
}
}
#ifndef NP_NO_CARBON
static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord& pluginEvent, PRUint32 keyType = 0)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
UInt32 charCode = 0;
if ([cocoaEvent type] == NSFlagsChanged) {
pluginEvent.what = keyType == NS_KEY_DOWN ? keyDown : keyUp;
} else {
if ([[cocoaEvent characters] length] > 0)
charCode = [[cocoaEvent characters] characterAtIndex:0];
if ([cocoaEvent type] == NSKeyDown)
pluginEvent.what = [cocoaEvent isARepeat] ? autoKey : keyDown;
else
pluginEvent.what = keyUp;
}
if (charCode >= 0x0080) {
switch (charCode) {
case NSUpArrowFunctionKey:
charCode = kUpArrowCharCode;
break;
case NSDownArrowFunctionKey:
charCode = kDownArrowCharCode;
break;
case NSLeftArrowFunctionKey:
charCode = kLeftArrowCharCode;
break;
case NSRightArrowFunctionKey:
charCode = kRightArrowCharCode;
break;
default:
unsigned char convertedCharCode;
if (ConvertUnicodeToCharCode(charCode, &convertedCharCode))
charCode = convertedCharCode;
//NSLog(@"charcode is %d, converted to %c, char is %@", charCode, convertedCharCode, [cocoaEvent characters]);
break;
}
}
pluginEvent.message = (charCode & 0x00FF) | ([cocoaEvent keyCode] << 8);
pluginEvent.when = ::TickCount();
::GetGlobalMouse(&pluginEvent.where);
pluginEvent.modifiers = ::GetCurrentKeyModifiers();
NS_OBJC_END_TRY_ABORT_BLOCK;
}
#endif // NP_NO_CARBON
// Basic conversion for cocoa to gecko events, common to all conversions.
// Note that it is OK for inEvent to be nil.
- (void) convertGenericCocoaEvent:(NSEvent*)inEvent toGeckoEvent:(nsInputEvent*)outGeckoEvent
@ -4128,8 +4039,6 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord&
#pragma mark -
// NSTextInput implementation
#define MAX_BUFFER_SIZE 32
- (void)insertText:(id)insertString
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@ -4137,93 +4046,20 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord&
#if DEBUG_IME
NSLog(@"****in insertText: '%@'", insertString);
#endif
if (!mGeckoChild || !mTextInputHandler)
return;
if (mTextInputHandler->IgnoreIMEComposition())
return;
NS_ENSURE_TRUE(mGeckoChild, );
nsAutoRetainCocoaObject kungFuDeathGrip(self);
if (![insertString isKindOfClass:[NSAttributedString class]])
insertString = [[[NSAttributedString alloc] initWithString:insertString] autorelease];
NSString *tmpStr = [insertString string];
unsigned int len = [tmpStr length];
if (!mTextInputHandler->IsIMEComposing() && len == 0)
return; // nothing to do
PRUnichar buffer[MAX_BUFFER_SIZE];
PRUnichar *bufPtr = (len >= MAX_BUFFER_SIZE) ? new PRUnichar[len + 1] : buffer;
[tmpStr getCharacters:bufPtr];
bufPtr[len] = PRUnichar('\0');
if (len == 1 && !mTextInputHandler->IsIMEComposing()) {
// don't let the same event be fired twice when hitting
// enter/return! (Bug 420502)
if (mKeyPressSent)
return;
// dispatch keypress event with char instead of textEvent
nsKeyEvent geckoEvent(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
geckoEvent.time = PR_IntervalNow();
geckoEvent.charCode = bufPtr[0]; // gecko expects OS-translated unicode
geckoEvent.keyCode = 0;
geckoEvent.isChar = PR_TRUE;
if (mKeyDownHandled)
geckoEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
// don't set other modifiers from the current event, because here in
// -insertText: they've already been taken into account in creating
// the input string.
// create event for use by plugins
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
#endif
if (mCurKeyEvent) {
// XXX The ASCII characters inputting mode of egbridge (Japanese IME)
// might send the keyDown event with wrong keyboard layout if other
// keyboard layouts are already loaded. In that case, the native event
// doesn't match to this gecko event...
#ifndef NP_NO_CARBON
if (mPluginEventModel == NPEventModelCarbon) {
ConvertCocoaKeyEventToCarbonEvent(mCurKeyEvent, carbonEvent);
geckoEvent.pluginEvent = &carbonEvent;
}
#endif
geckoEvent.isShift = ([mCurKeyEvent modifierFlags] & NSShiftKeyMask) != 0;
if (!TextInputHandler::IsPrintableChar(geckoEvent.charCode)) {
geckoEvent.keyCode =
TextInputHandler::ComputeGeckoKeyCode([mCurKeyEvent keyCode],
[mCurKeyEvent charactersIgnoringModifiers]);
geckoEvent.charCode = 0;
}
} else {
// Note that insertText is not called only at key pressing.
if (!TextInputHandler::IsPrintableChar(geckoEvent.charCode)) {
geckoEvent.keyCode =
TextInputHandler::ComputeGeckoKeyCodeFromChar(geckoEvent.charCode);
geckoEvent.charCode = 0;
}
}
PRBool keyPressHandled = mTextInputHandler->DispatchEvent(geckoEvent);
// Note: mGeckoChild might have become null here. Don't count on it from here on.
// Only record the results of dispatching geckoEvent if we're currently
// processing a keyDown event.
if (mCurKeyEvent) {
mKeyPressHandled = keyPressHandled;
mKeyPressSent = YES;
}
}
else {
NSAttributedString* attrStr =
static_cast<NSAttributedString*>(insertString);
mTextInputHandler->InsertTextAsCommittingComposition(attrStr);
NSAttributedString* attrStr;
if ([insertString isKindOfClass:[NSAttributedString class]]) {
attrStr = static_cast<NSAttributedString*>(insertString);
} else {
attrStr =
[[[NSAttributedString alloc] initWithString:insertString] autorelease];
}
if (bufPtr != buffer)
delete[] bufPtr;
mTextInputHandler->InsertText(attrStr);
NS_OBJC_END_TRY_ABORT_BLOCK;
}
@ -4237,12 +4073,15 @@ static void ConvertCocoaKeyEventToCarbonEvent(NSEvent* cocoaEvent, EventRecord&
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
PRBool ignore = mGeckoChild && mTextInputHandler->KeyPressWasHandled();
#if DEBUG_IME
NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, mKeyPressHandled);
NSLog(@"**** in doCommandBySelector %s (ignore %d)", aSelector, ignore);
#endif
if (!mKeyPressHandled)
if (!ignore) {
[super doCommandBySelector:aSelector];
}
NS_OBJC_END_TRY_ABORT_BLOCK;
}
@ -4374,110 +4213,7 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
nsAutoRetainCocoaObject kungFuDeathGrip(self);
mCurKeyEvent = theEvent;
BOOL nonDeadKeyPress = [[theEvent characters] length] > 0;
if (nonDeadKeyPress && !mTextInputHandler->IsIMEComposing()) {
NSResponder* firstResponder = [[self window] firstResponder];
nsKeyEvent geckoKeydown(PR_TRUE, NS_KEY_DOWN, mGeckoChild);
mTextInputHandler->InitKeyEvent(theEvent, geckoKeydown);
#ifndef NP_NO_CARBON
EventRecord carbonEvent;
if (mPluginEventModel == NPEventModelCarbon) {
ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent);
geckoKeydown.pluginEvent = &carbonEvent;
}
#endif
mKeyDownHandled = mGeckoChild->DispatchWindowEvent(geckoKeydown);
if (!mGeckoChild) {
return mKeyDownHandled;
}
// The key down event may have shifted the focus, in which
// case we should not fire the key press.
if (firstResponder != [[self window] firstResponder]) {
PRBool handled = mKeyDownHandled;
mCurKeyEvent = nil;
mKeyDownHandled = PR_FALSE;
return handled;
}
// If this is the context menu key command, send a context menu key event.
unsigned int modifierFlags = [theEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
if (modifierFlags == NSControlKeyMask && [[theEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
nsMouseEvent contextMenuEvent(PR_TRUE, NS_CONTEXTMENU, [self widget], nsMouseEvent::eReal, nsMouseEvent::eContextMenuKey);
contextMenuEvent.isShift = contextMenuEvent.isControl = contextMenuEvent.isAlt = contextMenuEvent.isMeta = PR_FALSE;
PRBool cmEventHandled = mGeckoChild->DispatchWindowEvent(contextMenuEvent);
[self maybeInitContextMenuTracking];
// Bail, there is nothing else to do here.
PRBool handled = (cmEventHandled || mKeyDownHandled);
mCurKeyEvent = nil;
mKeyDownHandled = PR_FALSE;
return handled;
}
nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
mTextInputHandler->InitKeyEvent(theEvent, geckoKeypress);
// if this is a non-letter keypress, or the control key is down,
// dispatch the keydown to gecko, so that we trap delete,
// control-letter combinations etc before Cocoa tries to use
// them for keybindings.
if ((!geckoKeypress.isChar || geckoKeypress.isControl) &&
!mTextInputHandler->IsIMEComposing()) {
if (mKeyDownHandled)
geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT;
mKeyPressHandled = mTextInputHandler->DispatchEvent(geckoKeypress);
mKeyPressSent = YES;
if (!mGeckoChild)
return (mKeyDownHandled || mKeyPressHandled);
}
}
// Let Cocoa interpret the key events, caching IsIMEComposing first.
PRBool wasComposing = mTextInputHandler->IsIMEComposing();
PRBool interpretKeyEventsCalled = PR_FALSE;
if (mTextInputHandler->IsIMEEnabled() ||
mTextInputHandler->IsASCIICapableOnly()) {
[super interpretKeyEvents:[NSArray arrayWithObject:theEvent]];
interpretKeyEventsCalled = PR_TRUE;
}
if (!mGeckoChild) {
return (mKeyDownHandled || mKeyPressHandled);
}
if (!mKeyPressSent && nonDeadKeyPress && !wasComposing &&
!mTextInputHandler->IsIMEComposing()) {
nsKeyEvent geckoKeypress(PR_TRUE, NS_KEY_PRESS, mGeckoChild);
mTextInputHandler->InitKeyEvent(theEvent, geckoKeypress);
// If we called interpretKeyEvents and this isn't normal character input
// then IME probably ate the event for some reason. We do not want to
// send a key press event in that case.
if (!(interpretKeyEventsCalled &&
TextInputHandler::IsNormalCharInputtingEvent(geckoKeypress))) {
if (mKeyDownHandled) {
geckoKeypress.flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
mKeyPressHandled = mTextInputHandler->DispatchEvent(geckoKeypress);
}
}
// Note: mGeckoChild might have become null here. Don't count on it from here on.
PRBool handled = (mKeyDownHandled || mKeyPressHandled);
// See note about nested event loops where these variables are declared in header.
mKeyPressHandled = NO;
mKeyPressSent = NO;
mCurKeyEvent = nil;
mKeyDownHandled = PR_FALSE;
return handled;
return mTextInputHandler->HandleKeyDownEvent(theEvent);
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
}
@ -4746,7 +4482,9 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
nsKeyEvent keyUpEvent(PR_TRUE, NS_KEY_UP, mGeckoChild);
mTextInputHandler->InitKeyEvent(theEvent, keyUpEvent);
EventRecord macKeyUpEvent;
ConvertCocoaKeyEventToCarbonEvent(theEvent, macKeyUpEvent);
TextInputHandler::ConvertCocoaKeyEventToCarbonEvent(theEvent,
macKeyUpEvent,
PR_FALSE);
keyUpEvent.pluginEvent = &macKeyUpEvent;
mGeckoChild->DispatchWindowEvent(keyUpEvent);
}
@ -4864,7 +4602,9 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
if (mIsPluginView) {
#ifndef NP_NO_CARBON
if (mPluginEventModel == NPEventModelCarbon) {
ConvertCocoaKeyEventToCarbonEvent(theEvent, carbonEvent, message);
TextInputHandler::ConvertCocoaKeyEventToCarbonEvent(theEvent,
carbonEvent,
isKeyDown);
geckoEvent.pluginEvent = &carbonEvent;
}
#endif
@ -5474,8 +5214,7 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
if (NS_FAILED(rv))
return NO;
if (!mGeckoChild)
return NO;
NS_ENSURE_TRUE(mGeckoChild, PR_FALSE);
nsContentCommandEvent command(PR_TRUE,
NS_CONTENT_COMMAND_PASTE_TRANSFERABLE,