mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-01-31 11:01:40 +00:00
Bug 431503. Infrastructure and tests for native key event translation. r=josh,karlt,sr=jst
This commit is contained in:
parent
4ce4eede88
commit
a41b7ed86d
@ -47,7 +47,7 @@
|
||||
|
||||
interface nsIDOMElement;
|
||||
|
||||
[scriptable, uuid(7a55fc2b-afb3-41c6-9e50-3fee341fa87c)]
|
||||
[scriptable, uuid(1cfc1a0a-e348-4b18-b61b-935c192f85c4)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -144,6 +144,19 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
in long aCharCode,
|
||||
in long aModifiers);
|
||||
|
||||
/**
|
||||
* See nsIWidget::SynthesizeNativeKeyEvent
|
||||
*
|
||||
* Cannot be accessed from unprivileged context (not content-accessible)
|
||||
* Will throw a DOM security error if called without UniversalXPConnect
|
||||
* privileges.
|
||||
*/
|
||||
void sendNativeKeyEvent(in long aNativeKeyboardLayout,
|
||||
in long aNativeKeyCode,
|
||||
in long aModifierFlags,
|
||||
in AString aCharacters,
|
||||
in AString aUnmodifiedCharacters);
|
||||
|
||||
/**
|
||||
* Focus the element aElement. The element should be in the same document
|
||||
* that the window is displaying. Pass null to blur the element, if any,
|
||||
|
@ -276,6 +276,28 @@ nsDOMWindowUtils::SendKeyEvent(const nsAString& aType,
|
||||
return widget->DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::SendNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRInt32 aModifiers,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters)
|
||||
{
|
||||
PRBool hasCap = PR_FALSE;
|
||||
if (NS_FAILED(nsContentUtils::GetSecurityManager()->IsCapabilityEnabled("UniversalXPConnect", &hasCap))
|
||||
|| !hasCap)
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
|
||||
// get the widget to send the event to
|
||||
nsCOMPtr<nsIWidget> widget = GetWidget();
|
||||
if (!widget)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
widget->SynthesizeNativeKeyEvent(aNativeKeyboardLayout, aNativeKeyCode,
|
||||
aModifiers, aCharacters, aUnmodifiedCharacters);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsIWidget*
|
||||
nsDOMWindowUtils::GetWidget()
|
||||
{
|
||||
|
@ -95,10 +95,10 @@ typedef nsEventStatus (*PR_CALLBACK EVENT_CALLBACK)(nsGUIEvent *event);
|
||||
#define NS_NATIVE_PLUGIN_PORT_CG 101
|
||||
#endif
|
||||
|
||||
// 9151e8c9-a1cc-44e9-a70d-afb3956d4e13
|
||||
// e197eeba-a82b-46d9-8aa9-52e1133fc593
|
||||
#define NS_IWIDGET_IID \
|
||||
{ 0x9151e8c9, 0xa1cc, 0x44e9, \
|
||||
{ 0xa7, 0x0d, 0xaf, 0xb3, 0x95, 0x6d, 0x4e, 0x13 } }
|
||||
{ 0xe197eeba, 0xa82b, 0x46d9, \
|
||||
{ 0x8a, 0xa9, 0x52, 0xe1, 0x13, 0x3f, 0xc5, 0x93 } }
|
||||
|
||||
// Hide the native window systems real window type so as to avoid
|
||||
// including native window system types and APIs. This is necessary
|
||||
@ -1062,6 +1062,47 @@ class nsIWidget : public nsISupports {
|
||||
*/
|
||||
NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical) = 0;
|
||||
|
||||
enum Modifiers {
|
||||
CAPS_LOCK = 0x01, // when CapsLock is active
|
||||
NUM_LOCK = 0x02, // when NumLock is active
|
||||
SHIFT_L = 0x0100,
|
||||
SHIFT_R = 0x0200,
|
||||
CTRL_L = 0x0400,
|
||||
CTRL_R = 0x0800,
|
||||
ALT_L = 0x1000, // includes Option
|
||||
ALT_R = 0x2000,
|
||||
COMMAND = 0x4000,
|
||||
HELP = 0x8000,
|
||||
FUNCTION = 0x10000,
|
||||
NUMERIC_KEY_PAD = 0x01000000 // when the key is coming from the keypad
|
||||
};
|
||||
/**
|
||||
* Utility method intended for testing. Dispatches native key events
|
||||
* to this widget to simulate the press and release of a key.
|
||||
* @param aNativeKeyboardLayout a *platform-specific* constant.
|
||||
* On Mac, this is the resource ID for a 'uchr' or 'kchr' resource.
|
||||
* On Windows, it is converted to a hex string and passed to
|
||||
* LoadKeyboardLayout, see
|
||||
* http://msdn.microsoft.com/en-us/library/ms646305(VS.85).aspx
|
||||
* @param aNativeKeyCode a *platform-specific* keycode.
|
||||
* On Windows, this is the virtual key code.
|
||||
* @param aModifiers some combination of the above 'Modifiers' flags;
|
||||
* not all flags will apply to all platforms. Mac ignores the _R
|
||||
* modifiers. Windows ignores COMMAND, NUMERIC_KEY_PAD, HELP and
|
||||
* FUNCTION.
|
||||
* @param aCharacters characters that the OS would decide to generate
|
||||
* from the event. On Windows, this is the charCode passed by
|
||||
* WM_CHAR.
|
||||
* @param aUnmodifiedCharacters characters that the OS would decide
|
||||
* to generate from the event if modifier keys (other than shift)
|
||||
* were assumed inactive. Needed on Mac, ignored on Windows.
|
||||
*/
|
||||
virtual void SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters) = 0;
|
||||
|
||||
protected:
|
||||
// keep the list of children. We also keep track of our siblings.
|
||||
// The ownership model is as follows: parent holds a strong ref to
|
||||
|
@ -385,6 +385,12 @@ protected:
|
||||
virtual NSView* CreateCocoaView(NSRect inFrame);
|
||||
void TearDownView();
|
||||
|
||||
virtual void SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters);
|
||||
|
||||
protected:
|
||||
|
||||
NSView<mozView>* mView; // my parallel cocoa view (ChildView or NativeScrollbarView), [STRONG]
|
||||
|
@ -157,10 +157,14 @@ nsIWidget * gRollupWidget = nsnull;
|
||||
|
||||
- (void)processPendingRedraws;
|
||||
|
||||
- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv;
|
||||
|
||||
- (BOOL)ensureCorrectMouseEventTarget:(NSEvent *)anEvent;
|
||||
|
||||
- (void)maybeInitContextMenuTracking;
|
||||
|
||||
+ (NSEvent*)makeNewCocoaEventWithType:(NSEventType)type fromEvent:(NSEvent*)theEvent;
|
||||
|
||||
#if USE_CLICK_HOLD_CONTEXTMENU
|
||||
// called on a timer two seconds after a mouse down to see if we should display
|
||||
// a context menu (click-hold)
|
||||
@ -1248,6 +1252,68 @@ void nsChildView::LiveResizeEnded()
|
||||
mLiveResizeInProgress = PR_FALSE;
|
||||
}
|
||||
|
||||
static NSString* ToNSString(const nsAString& aString)
|
||||
{
|
||||
return [NSString stringWithCharacters:aString.BeginReading()
|
||||
length:aString.Length()];
|
||||
}
|
||||
|
||||
static PRInt32 gOverrideKeyboardLayout;
|
||||
|
||||
static const PRUint32 sModifierFlagMap[][2] = {
|
||||
{ nsIWidget::CAPS_LOCK, NSAlphaShiftKeyMask },
|
||||
{ nsIWidget::SHIFT_L, NSShiftKeyMask },
|
||||
{ nsIWidget::CTRL_L, NSControlKeyMask },
|
||||
{ nsIWidget::ALT_L, NSAlternateKeyMask },
|
||||
{ nsIWidget::COMMAND, NSCommandKeyMask },
|
||||
{ nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
|
||||
{ nsIWidget::HELP, NSHelpKeyMask },
|
||||
{ nsIWidget::FUNCTION, NSFunctionKeyMask }
|
||||
};
|
||||
void nsChildView::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NS_ASSERTION(aNativeKeyboardLayout, "Layout cannot be 0");
|
||||
|
||||
PRUint32 modifierFlags = 0;
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierFlagMap); ++i) {
|
||||
if (aModifierFlags & sModifierFlagMap[i][0]) {
|
||||
modifierFlags |= sModifierFlagMap[i][1];
|
||||
}
|
||||
}
|
||||
int windowNumber = [[mView window] windowNumber];
|
||||
NSEvent* downEvent = [NSEvent keyEventWithType:NSKeyDown
|
||||
location:NSMakePoint(0,0)
|
||||
modifierFlags:modifierFlags
|
||||
timestamp:0
|
||||
windowNumber:windowNumber
|
||||
context:[NSGraphicsContext currentContext]
|
||||
characters:ToNSString(aCharacters)
|
||||
charactersIgnoringModifiers:ToNSString(aUnmodifiedCharacters)
|
||||
isARepeat:NO
|
||||
keyCode:aNativeKeyCode];
|
||||
|
||||
NSEvent* upEvent = [ChildView makeNewCocoaEventWithType:NSKeyUp
|
||||
fromEvent:downEvent];
|
||||
|
||||
if (downEvent && upEvent) {
|
||||
PRInt32 currentLayout = gOverrideKeyboardLayout;
|
||||
gOverrideKeyboardLayout = aNativeKeyboardLayout;
|
||||
ChildView* view = static_cast<ChildView*>(mView);
|
||||
[view keyDown:downEvent];
|
||||
[view keyUp:upEvent];
|
||||
// processKeyDownEvent and keyUp block exceptions so we're sure to
|
||||
// reach here to restore gOverrideKeyboardLayout
|
||||
gOverrideKeyboardLayout = currentLayout;
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@ -3948,9 +4014,9 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
||||
// unshiftCharCode for accessKeys and accelKeys.
|
||||
if (outGeckoEvent->isControl || outGeckoEvent->isMeta ||
|
||||
outGeckoEvent->isAlt) {
|
||||
SInt16 keyLayoutID =
|
||||
::GetScriptVariable(::GetScriptManagerVariable(smKeyScript),
|
||||
smScriptKeys);
|
||||
SInt16 keyLayoutID = gOverrideKeyboardLayout ? gOverrideKeyboardLayout
|
||||
: ::GetScriptVariable(::GetScriptManagerVariable(smKeyScript),
|
||||
smScriptKeys);
|
||||
Handle handle = ::GetResource('uchr', keyLayoutID);
|
||||
PRUint32 unshiftedChar = 0;
|
||||
PRUint32 shiftedChar = 0;
|
||||
@ -3979,14 +4045,21 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
||||
kbType, 0, &deadKeyState, 1, &len, chars);
|
||||
if (noErr == err && len > 0)
|
||||
shiftedCmdChar = chars[0];
|
||||
} else if ((handle = (char**)::GetScriptManagerVariable(smKCHRCache))) {
|
||||
UInt32 state = 0;
|
||||
UInt32 keyCode = [aKeyEvent keyCode];
|
||||
unshiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
keyCode = [aKeyEvent keyCode] | shiftKey;
|
||||
shiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
keyCode = [aKeyEvent keyCode] | shiftKey | cmdKey;
|
||||
shiftedCmdChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
} else {
|
||||
if (gOverrideKeyboardLayout) {
|
||||
handle = ::GetResource('kchr', gOverrideKeyboardLayout);
|
||||
} else {
|
||||
handle = (char**)::GetScriptManagerVariable(smKCHRCache);
|
||||
}
|
||||
if (handle) {
|
||||
UInt32 state = 0;
|
||||
UInt32 keyCode = [aKeyEvent keyCode];
|
||||
unshiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
keyCode = [aKeyEvent keyCode] | shiftKey;
|
||||
shiftedChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
keyCode = [aKeyEvent keyCode] | shiftKey | cmdKey;
|
||||
shiftedCmdChar = ::KeyTranslate(handle, keyCode, &state) & charCodeMask;
|
||||
}
|
||||
}
|
||||
// If the current keyboad layout is switchable by Cmd key
|
||||
// (e.g., Dvorak-QWERTY layout), we should not append the alternative
|
||||
@ -4385,9 +4458,7 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
||||
if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
|
||||
return nil;
|
||||
|
||||
NSString* nsstr =
|
||||
[NSString stringWithCharacters:textContent.mReply.mString.get()
|
||||
length:textContent.mReply.mString.Length()];
|
||||
NSString* nsstr = ToNSString(textContent.mReply.mString);
|
||||
NSAttributedString* result =
|
||||
[[[NSAttributedString alloc] initWithString:nsstr
|
||||
attributes:nil] autorelease];
|
||||
@ -4551,6 +4622,20 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
|
||||
{
|
||||
for (PRUint32 i = 0; i < [aString length]; ++i) {
|
||||
unichar ch = [aString characterAtIndex:i];
|
||||
if (ch >= 32 && ch < 128) {
|
||||
aBuf.Append(char(ch));
|
||||
} else {
|
||||
aBuf += nsPrintfCString("\\u%04x", ch);
|
||||
}
|
||||
}
|
||||
return aBuf.get();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Returns PR_TRUE if Gecko claims to have handled the event, PR_FALSE otherwise.
|
||||
- (PRBool)processKeyDownEvent:(NSEvent*)theEvent keyEquiv:(BOOL)isKeyEquiv
|
||||
@ -4560,6 +4645,16 @@ static PRBool IsNormalCharInputtingEvent(const nsKeyEvent& aEvent)
|
||||
if (!mGeckoChild)
|
||||
return NO;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsCAutoString str1;
|
||||
nsCAutoString str2;
|
||||
#endif
|
||||
PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
|
||||
("ChildView processKeyDownEvent: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
|
||||
[theEvent keyCode], [theEvent modifierFlags],
|
||||
ToEscapedString([theEvent characters], str1),
|
||||
ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
|
||||
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
mCurKeyEvent = theEvent;
|
||||
|
||||
@ -4744,6 +4839,16 @@ static BOOL keyUpAlreadySentKeyDown = NO;
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
nsCAutoString str1;
|
||||
nsCAutoString str2;
|
||||
#endif
|
||||
PR_LOG(sCocoaLog, PR_LOG_ALWAYS,
|
||||
("ChildView keyUp: keycode=%d,modifiers=%x,chars=%s,charsIgnoringModifiers=%s\n",
|
||||
[theEvent keyCode], [theEvent modifierFlags],
|
||||
ToEscapedString([theEvent characters], str1),
|
||||
ToEscapedString([theEvent charactersIgnoringModifiers], str2)));
|
||||
|
||||
if (mGeckoChild && mIsPluginView) {
|
||||
// I'm not sure the call to TSMProcessRawKeyEvent() is needed here (though
|
||||
// WebKit makes one). But we definitely need to short-circuit NSKeyUp
|
||||
|
@ -2325,7 +2325,7 @@ IsBasicLatinLetterOrNumeral(PRUint32 aChar)
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
|
||||
nsWindow::OnKeyPressEvent(GdkEventKey *aEvent)
|
||||
{
|
||||
LOGFOCUS(("OnKeyPressEvent [%p]\n", (void *)this));
|
||||
|
||||
@ -2500,7 +2500,7 @@ nsWindow::OnKeyPressEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
|
||||
}
|
||||
|
||||
gboolean
|
||||
nsWindow::OnKeyReleaseEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
|
||||
nsWindow::OnKeyReleaseEvent(GdkEventKey *aEvent)
|
||||
{
|
||||
LOGFOCUS(("OnKeyReleaseEvent [%p]\n", (void *)this));
|
||||
|
||||
@ -2528,6 +2528,17 @@ nsWindow::OnKeyReleaseEvent(GtkWidget *aWidget, GdkEventKey *aEvent)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters,
|
||||
PRBool aAllowIME)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::OnScrollEvent(GtkWidget *aWidget, GdkEventScroll *aEvent)
|
||||
{
|
||||
@ -4791,7 +4802,7 @@ key_press_event_cb(GtkWidget *widget, GdkEventKey *event)
|
||||
|
||||
nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
|
||||
|
||||
return focusWindow->OnKeyPressEvent(widget, event);
|
||||
return focusWindow->OnKeyPressEvent(event);
|
||||
}
|
||||
|
||||
gboolean
|
||||
@ -4805,7 +4816,7 @@ key_release_event_cb(GtkWidget *widget, GdkEventKey *event)
|
||||
|
||||
nsRefPtr<nsWindow> focusWindow = gFocusWindow ? gFocusWindow : window;
|
||||
|
||||
return focusWindow->OnKeyReleaseEvent(widget, event);
|
||||
return focusWindow->OnKeyReleaseEvent(event);
|
||||
}
|
||||
|
||||
/* static */
|
||||
|
@ -186,10 +186,8 @@ public:
|
||||
GdkEventFocus *aEvent);
|
||||
void OnContainerFocusOutEvent(GtkWidget *aWidget,
|
||||
GdkEventFocus *aEvent);
|
||||
gboolean OnKeyPressEvent(GtkWidget *aWidget,
|
||||
GdkEventKey *aEvent);
|
||||
gboolean OnKeyReleaseEvent(GtkWidget *aWidget,
|
||||
GdkEventKey *aEvent);
|
||||
gboolean OnKeyPressEvent(GdkEventKey *aEvent);
|
||||
gboolean OnKeyReleaseEvent(GdkEventKey *aEvent);
|
||||
void OnScrollEvent(GtkWidget *aWidget,
|
||||
GdkEventScroll *aEvent);
|
||||
void OnVisibilityNotifyEvent(GtkWidget *aWidget,
|
||||
@ -364,6 +362,12 @@ public:
|
||||
|
||||
gfxASurface *GetThebesSurface();
|
||||
|
||||
virtual void SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters);
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
static PRBool sAccessibilityEnabled;
|
||||
#endif
|
||||
|
@ -204,7 +204,7 @@ KeyboardLayout::KeyboardLayout ()
|
||||
mDeadKeyTableListHead = nsnull;
|
||||
#endif
|
||||
|
||||
LoadLayout ();
|
||||
LoadLayout (::GetKeyboardLayout(0));
|
||||
}
|
||||
|
||||
KeyboardLayout::~KeyboardLayout ()
|
||||
@ -348,7 +348,7 @@ KeyboardLayout::GetUniCharsWithShiftState(PRUint8 aVirtualKey,
|
||||
#endif
|
||||
}
|
||||
|
||||
void KeyboardLayout::LoadLayout ()
|
||||
void KeyboardLayout::LoadLayout (HKL aLayout)
|
||||
{
|
||||
#ifndef WINCE
|
||||
PRUint32 shiftState;
|
||||
@ -361,7 +361,7 @@ void KeyboardLayout::LoadLayout ()
|
||||
|
||||
mActiveDeadKey = -1;
|
||||
mNumOfChars = 0;
|
||||
mKeyboardLayout = ::GetKeyboardLayout (0);
|
||||
mKeyboardLayout = aLayout;
|
||||
|
||||
ReleaseDeadKeyTables ();
|
||||
|
||||
@ -385,7 +385,7 @@ void KeyboardLayout::LoadLayout ()
|
||||
PRUint16 uniChars [5];
|
||||
PRInt32 rv;
|
||||
|
||||
rv = ::ToUnicode (virtualKey, 0, kbdState, (LPWSTR)uniChars, NS_ARRAY_LENGTH (uniChars), 0);
|
||||
rv = ::ToUnicodeEx (virtualKey, 0, kbdState, (LPWSTR)uniChars, NS_ARRAY_LENGTH (uniChars), 0, mKeyboardLayout);
|
||||
|
||||
if (rv < 0) // dead-key
|
||||
{
|
||||
@ -394,7 +394,7 @@ void KeyboardLayout::LoadLayout ()
|
||||
// Repeat dead-key to deactivate it and get its character representation.
|
||||
PRUint16 deadChar [2];
|
||||
|
||||
rv = ::ToUnicode (virtualKey, 0, kbdState, (LPWSTR)deadChar, NS_ARRAY_LENGTH (deadChar), 0);
|
||||
rv = ::ToUnicodeEx (virtualKey, 0, kbdState, (LPWSTR)deadChar, NS_ARRAY_LENGTH (deadChar), 0, mKeyboardLayout);
|
||||
|
||||
NS_ASSERTION (rv == 2, "Expecting twice repeated dead-key character");
|
||||
|
||||
@ -583,7 +583,7 @@ PRBool KeyboardLayout::EnsureDeadKeyActive (PRBool aIsActive, PRUint8 aDeadKey,
|
||||
{
|
||||
PRUint16 dummyChars [5];
|
||||
|
||||
rv = ::ToUnicode (aDeadKey, 0, (PBYTE)aDeadKeyKbdState, (LPWSTR)dummyChars, NS_ARRAY_LENGTH (dummyChars), 0);
|
||||
rv = ::ToUnicodeEx (aDeadKey, 0, (PBYTE)aDeadKeyKbdState, (LPWSTR)dummyChars, NS_ARRAY_LENGTH (dummyChars), 0, mKeyboardLayout);
|
||||
// returned values:
|
||||
// <0 - Dead key state is active. The keyboard driver will wait for next character.
|
||||
// 1 - Previous pressed key was a valid base character that produced exactly one composite character.
|
||||
@ -654,7 +654,7 @@ PRUint32 KeyboardLayout::GetDeadKeyCombinations (PRUint8 aDeadKey, const PBYTE a
|
||||
PRUint16 compositeChars [5];
|
||||
PRInt32 rv;
|
||||
|
||||
rv = ::ToUnicode (virtualKey, 0, kbdState, (LPWSTR)compositeChars, NS_ARRAY_LENGTH (compositeChars), 0);
|
||||
rv = ::ToUnicodeEx (virtualKey, 0, kbdState, (LPWSTR)compositeChars, NS_ARRAY_LENGTH (compositeChars), 0, mKeyboardLayout);
|
||||
|
||||
switch (rv)
|
||||
{
|
||||
@ -668,7 +668,7 @@ PRUint32 KeyboardLayout::GetDeadKeyCombinations (PRUint8 aDeadKey, const PBYTE a
|
||||
// character one more time to determine the base character.
|
||||
PRUint16 baseChars [5];
|
||||
|
||||
rv = ::ToUnicode (virtualKey, 0, kbdState, (LPWSTR)baseChars, NS_ARRAY_LENGTH (baseChars), 0);
|
||||
rv = ::ToUnicodeEx (virtualKey, 0, kbdState, (LPWSTR)baseChars, NS_ARRAY_LENGTH (baseChars), 0, mKeyboardLayout);
|
||||
|
||||
NS_ASSERTION (rv == 1, "One base character expected");
|
||||
|
||||
|
@ -177,7 +177,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoadLayout ();
|
||||
void LoadLayout (HKL aLayout);
|
||||
void OnKeyDown (PRUint8 aVirtualKey);
|
||||
PRUint32 GetUniChars (PRUint16* aUniChars, PRUint8* aShiftStates, PRUint32 aMaxChars) const;
|
||||
PRUint32 GetUniCharsWithShiftState(PRUint8 aVirtualKey, PRUint8 aShiftStates,
|
||||
|
@ -79,6 +79,7 @@
|
||||
#include <windows.h>
|
||||
#include <process.h>
|
||||
#include "nsUnicharUtils.h"
|
||||
#include "prlog.h"
|
||||
|
||||
#ifdef WINCE
|
||||
#include "aygshell.h"
|
||||
@ -151,6 +152,10 @@
|
||||
#include "prprf.h"
|
||||
#include "prmem.h"
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRLogModuleInfo* sWindowsLog = nsnull;
|
||||
#endif
|
||||
|
||||
static const char kMozHeapDumpMessageString[] = "MOZ_HeapDump";
|
||||
|
||||
#define kWindowPositionSlop 20
|
||||
@ -647,6 +652,11 @@ void nsWindow::GlobalMsgWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP
|
||||
//-------------------------------------------------------------------------
|
||||
nsWindow::nsWindow() : nsBaseWidget()
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
if (!sWindowsLog)
|
||||
sWindowsLog = PR_NewLogModule("nsWindowsWidgets");
|
||||
#endif
|
||||
|
||||
mWnd = 0;
|
||||
mPaintDC = 0;
|
||||
mPrevWndProc = NULL;
|
||||
@ -3115,19 +3125,33 @@ StringCaseInsensitiveEquals(const PRUint16* aChars1, const PRUint32 aNumChars1,
|
||||
return comp(aChars1, aChars2, aNumChars1) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* nsWindow::OnKeyDown peeks into the message queue and pulls out
|
||||
* WM_CHAR messages for processing. During testing we don't want to
|
||||
* mess with the real message queue. Instead we pass a
|
||||
* pseudo-WM_CHAR-message using this structure, and OnKeyDown will use
|
||||
* that as if it was in the message queue, and refrain from actually
|
||||
* looking at or touching the message queue.
|
||||
*/
|
||||
struct nsFakeCharMessage {
|
||||
UINT mCharCode;
|
||||
UINT mScanCode;
|
||||
};
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, LPARAM aKeyData,
|
||||
nsFakeCharMessage* aFakeCharMessage)
|
||||
{
|
||||
#ifdef VK_BROWSER_BACK
|
||||
#ifdef VK_BROWSER_BACK
|
||||
// VK_BROWSER_BACK and VK_BROWSER_FORWARD are converted to nsCommandEvents
|
||||
if (aVirtualKeyCode == VK_BROWSER_BACK)
|
||||
{
|
||||
DispatchCommandEvent(APPCOMMAND_BROWSER_BACKWARD);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else if (aVirtualKeyCode == VK_BROWSER_FORWARD)
|
||||
{
|
||||
DispatchCommandEvent(APPCOMMAND_BROWSER_FORWARD);
|
||||
@ -3143,7 +3167,7 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
aVirtualKeyCode : MapFromNativeToDOM(aVirtualKeyCode);
|
||||
|
||||
#ifdef DEBUG
|
||||
//printf("In OnKeyDown virt: %d scan: %d\n", DOMKeyCode, aScanCode);
|
||||
//printf("In OnKeyDown virt: %d\n", DOMKeyCode);
|
||||
#endif
|
||||
|
||||
BOOL noDefault = DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, aKeyData);
|
||||
@ -3161,8 +3185,8 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
|
||||
PRUint32 extraFlags = (noDefault ? NS_EVENT_FLAG_NO_DEFAULT : 0);
|
||||
MSG msg;
|
||||
BOOL gotMsg = ::PeekMessageW(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD);
|
||||
PRBool anyCharMessagesRemoved = PR_FALSE;
|
||||
BOOL gotMsg = aFakeCharMessage ||
|
||||
::PeekMessageW(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD);
|
||||
// Enter and backspace are always handled here to avoid for example the
|
||||
// confusion between ctrl-enter and ctrl-J.
|
||||
if (DOMKeyCode == NS_VK_RETURN || DOMKeyCode == NS_VK_BACK ||
|
||||
@ -3173,14 +3197,21 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
// They can be more than one because of:
|
||||
// * Dead-keys not pairing with base character
|
||||
// * Some keyboard layouts may map up to 4 characters to the single key
|
||||
PRBool anyCharMessagesRemoved = PR_FALSE;
|
||||
|
||||
|
||||
while (gotMsg && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR))
|
||||
{
|
||||
::GetMessageW(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST);
|
||||
if (aFakeCharMessage) {
|
||||
anyCharMessagesRemoved = PR_TRUE;
|
||||
|
||||
gotMsg = ::PeekMessageW (&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD);
|
||||
} else {
|
||||
while (gotMsg && (msg.message == WM_CHAR || msg.message == WM_SYSCHAR))
|
||||
{
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS,
|
||||
("%s charCode=%d scanCode=%d\n", msg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
|
||||
msg.wParam, HIWORD(msg.lParam) & 0xFF));
|
||||
::GetMessageW(&msg, mWnd, WM_KEYFIRST, WM_KEYLAST);
|
||||
anyCharMessagesRemoved = PR_TRUE;
|
||||
|
||||
gotMsg = ::PeekMessageW (&msg, mWnd, WM_KEYFIRST, WM_KEYLAST, PM_NOREMOVE | PM_NOYIELD);
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyCharMessagesRemoved && DOMKeyCode == NS_VK_BACK) {
|
||||
@ -3210,24 +3241,29 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
// http://bugzilla.mozilla.gr.jp/show_bug.cgi?id=2885 (written in Japanese)
|
||||
// http://bugzilla.mozilla.org/show_bug.cgi?id=194559 (written in English)
|
||||
|
||||
NS_ASSERTION(!aFakeCharMessage, "We shouldn't be touching the real msg queue");
|
||||
::GetMessageW(&msg, mWnd, WM_CHAR, WM_CHAR);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gotMsg &&
|
||||
(msg.message == WM_CHAR || msg.message == WM_SYSCHAR || msg.message == WM_DEADCHAR)) {
|
||||
(aFakeCharMessage ||
|
||||
msg.message == WM_CHAR || msg.message == WM_SYSCHAR || msg.message == WM_DEADCHAR)) {
|
||||
if (aFakeCharMessage)
|
||||
return OnChar(aFakeCharMessage->mCharCode, aFakeCharMessage->mScanCode, extraFlags);
|
||||
|
||||
// If prevent default set for keydown, do same for keypress
|
||||
::GetMessageW(&msg, mWnd, msg.message, msg.message);
|
||||
|
||||
if (msg.message == WM_DEADCHAR)
|
||||
return PR_FALSE;
|
||||
|
||||
#ifdef KE_DEBUG
|
||||
printf("%s\tchar=%c\twp=%4x\tlp=%8x\n",
|
||||
(msg.message == WM_SYSCHAR) ? "WM_SYSCHAR" : "WM_CHAR",
|
||||
msg.wParam, msg.wParam, msg.lParam);
|
||||
#endif
|
||||
BOOL result = OnChar(msg.wParam, msg.lParam, extraFlags);
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS,
|
||||
("%s charCode=%d scanCode=%d\n",
|
||||
msg.message == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
|
||||
msg.wParam, HIWORD(msg.lParam) & 0xFF));
|
||||
|
||||
BOOL result = OnChar(msg.wParam, HIWORD(msg.lParam) & 0xFF, extraFlags);
|
||||
// If a syschar keypress wasn't processed, Windows may want to
|
||||
// handle it to activate a native menu.
|
||||
if (!result && msg.message == WM_SYSCHAR)
|
||||
@ -3402,9 +3438,11 @@ BOOL nsWindow::OnKeyDown(UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
BOOL nsWindow::OnKeyUp( UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
BOOL nsWindow::OnKeyUp( UINT aVirtualKeyCode, LPARAM aKeyData)
|
||||
{
|
||||
#ifdef VK_BROWSER_BACK
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS, ("nsWindow::OnKeyUp VK=%d\n", aVirtualKeyCode));
|
||||
|
||||
#ifdef VK_BROWSER_BACK
|
||||
if (aVirtualKeyCode == VK_BROWSER_BACK || aVirtualKeyCode == VK_BROWSER_FORWARD)
|
||||
return TRUE;
|
||||
#endif
|
||||
@ -3419,14 +3457,8 @@ BOOL nsWindow::OnKeyUp( UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyData)
|
||||
//
|
||||
//
|
||||
//-------------------------------------------------------------------------
|
||||
BOOL nsWindow::OnChar(UINT charCode, LPARAM keyData, PRUint32 aFlags)
|
||||
BOOL nsWindow::OnChar(UINT charCode, UINT aScanCode, PRUint32 aFlags)
|
||||
{
|
||||
// These must be checked here too as a lone WM_CHAR could be received
|
||||
// if a child window didn't handle it (for example Alt+Space in a content window)
|
||||
mIsShiftDown = IS_VK_DOWN(NS_VK_SHIFT);
|
||||
mIsControlDown = IS_VK_DOWN(NS_VK_CONTROL);
|
||||
mIsAltDown = IS_VK_DOWN(NS_VK_ALT);
|
||||
|
||||
// ignore [shift+]alt+space so the OS can handle it
|
||||
if (mIsAltDown && !mIsControlDown && IS_VK_DOWN(NS_VK_SPACE)) {
|
||||
return FALSE;
|
||||
@ -3471,10 +3503,10 @@ BOOL nsWindow::OnChar(UINT charCode, LPARAM keyData, PRUint32 aFlags)
|
||||
// Keep the characters unshifted for shortcuts and accesskeys and make sure
|
||||
// that numbers are always passed as such (among others: bugs 50255 and 351310)
|
||||
if (uniChar && (mIsControlDown || mIsAltDown)) {
|
||||
UINT virtualKeyCode = ::MapVirtualKey(HIWORD(keyData) & 0xFF, MAPVK_VSC_TO_VK);
|
||||
UINT virtualKeyCode = ::MapVirtualKeyEx(aScanCode, MAPVK_VSC_TO_VK, gKeyboardLayout);
|
||||
UINT unshiftedCharCode =
|
||||
virtualKeyCode >= '0' && virtualKeyCode <= '9' ? virtualKeyCode :
|
||||
mIsShiftDown ? ::MapVirtualKey(virtualKeyCode, MAPVK_VK_TO_CHAR) : 0;
|
||||
mIsShiftDown ? ::MapVirtualKeyEx(virtualKeyCode, MAPVK_VK_TO_CHAR, gKeyboardLayout) : 0;
|
||||
// ignore diacritics (top bit set) and key mapping errors (char code 0)
|
||||
if ((INT)unshiftedCharCode > 0)
|
||||
uniChar = unshiftedCharCode;
|
||||
@ -3494,6 +3526,96 @@ BOOL nsWindow::OnChar(UINT charCode, LPARAM keyData, PRUint32 aFlags)
|
||||
return result;
|
||||
}
|
||||
|
||||
static const PRUint32 sModifierKeyMap[][3] = {
|
||||
{ nsIWidget::CAPS_LOCK, VK_CAPITAL, 0 },
|
||||
{ nsIWidget::NUM_LOCK, VK_NUMLOCK, 0 },
|
||||
{ nsIWidget::SHIFT_L, VK_SHIFT, VK_LSHIFT },
|
||||
{ nsIWidget::SHIFT_R, VK_SHIFT, VK_RSHIFT },
|
||||
{ nsIWidget::CTRL_L, VK_CONTROL, VK_LCONTROL },
|
||||
{ nsIWidget::CTRL_R, VK_CONTROL, VK_RCONTROL },
|
||||
{ nsIWidget::ALT_L, VK_MENU, VK_LMENU },
|
||||
{ nsIWidget::ALT_R, VK_MENU, VK_RMENU }
|
||||
};
|
||||
|
||||
struct KeyPair {
|
||||
PRUint8 mGeneral;
|
||||
PRUint8 mSpecific;
|
||||
KeyPair(PRUint32 aGeneral, PRUint32 aSpecific)
|
||||
: mGeneral(PRUint8(aGeneral)), mSpecific(PRUint8(aSpecific)) {}
|
||||
};
|
||||
|
||||
static void
|
||||
SetupKeyModifiersSequence(nsTArray<KeyPair>* aArray, PRUint32 aModifiers)
|
||||
{
|
||||
for (PRUint32 i = 0; i < NS_ARRAY_LENGTH(sModifierKeyMap); ++i) {
|
||||
const PRUint32* map = sModifierKeyMap[i];
|
||||
if (aModifiers & map[0]) {
|
||||
aArray->AppendElement(KeyPair(map[1], map[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsWindow::SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters)
|
||||
{
|
||||
// Setup clean key state and load desired layout
|
||||
BYTE originalKbdState[256];
|
||||
::GetKeyboardState(originalKbdState);
|
||||
BYTE kbdState[256];
|
||||
memset(kbdState, 0, sizeof(kbdState));
|
||||
// This changes the state of the keyboard for the current thread only,
|
||||
// and we'll restore it soon, so this should be OK.
|
||||
::SetKeyboardState(kbdState);
|
||||
HKL oldLayout = gKeyboardLayout;
|
||||
gKeyboardLayout = (HKL)aNativeKeyboardLayout;
|
||||
gKbdLayout.LoadLayout(gKeyboardLayout);
|
||||
|
||||
nsAutoTArray<KeyPair,10> keySequence;
|
||||
SetupKeyModifiersSequence(&keySequence, aModifierFlags);
|
||||
NS_ASSERTION(aNativeKeyCode >= 0 && aNativeKeyCode < 256,
|
||||
"Native VK key code out of range");
|
||||
keySequence.AppendElement(KeyPair(aNativeKeyCode, 0));
|
||||
|
||||
// Simulate the pressing of each modifier key and then the real key
|
||||
for (PRUint32 i = 0; i < keySequence.Length(); ++i) {
|
||||
PRUint8 key = keySequence[i].mGeneral;
|
||||
PRUint8 keySpecific = keySequence[i].mSpecific;
|
||||
kbdState[key] = 0x81; // key is down and toggled on if appropriate
|
||||
if (keySpecific) {
|
||||
kbdState[keySpecific] = 0x81;
|
||||
}
|
||||
::SetKeyboardState(kbdState);
|
||||
SetupModKeyState();
|
||||
if (i == keySequence.Length() - 1 && aCharacters.Length() > 0) {
|
||||
UINT scanCode = ::MapVirtualKeyEx(aNativeKeyCode, MAPVK_VK_TO_VSC, gKeyboardLayout);
|
||||
nsFakeCharMessage msg = { aCharacters.CharAt(0), scanCode };
|
||||
OnKeyDown(key, 0, &msg);
|
||||
} else {
|
||||
OnKeyDown(key, 0, nsnull);
|
||||
}
|
||||
}
|
||||
for (PRUint32 i = keySequence.Length(); i > 0; --i) {
|
||||
PRUint8 key = keySequence[i - 1].mGeneral;
|
||||
PRUint8 keySpecific = keySequence[i - 1].mSpecific;
|
||||
kbdState[key] = 0; // key is up and toggled off if appropriate
|
||||
if (keySpecific) {
|
||||
kbdState[keySpecific] = 0;
|
||||
}
|
||||
::SetKeyboardState(kbdState);
|
||||
SetupModKeyState();
|
||||
OnKeyUp(key, 0);
|
||||
}
|
||||
|
||||
// Restore old key state and layout
|
||||
::SetKeyboardState(originalKbdState);
|
||||
gKeyboardLayout = oldLayout;
|
||||
gKbdLayout.LoadLayout(gKeyboardLayout);
|
||||
SetupModKeyState();
|
||||
}
|
||||
|
||||
void nsWindow::ConstrainZLevel(HWND *aAfter)
|
||||
{
|
||||
@ -4003,6 +4125,13 @@ void nsWindow::PostSleepWakeNotification(const char* aNotification)
|
||||
}
|
||||
#endif
|
||||
|
||||
void nsWindow::SetupModKeyState()
|
||||
{
|
||||
mIsShiftDown = IS_VK_DOWN(NS_VK_SHIFT);
|
||||
mIsControlDown = IS_VK_DOWN(NS_VK_CONTROL);
|
||||
mIsAltDown = IS_VK_DOWN(NS_VK_ALT);
|
||||
}
|
||||
|
||||
PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT *aRetValue)
|
||||
{
|
||||
static UINT vkKeyCached = 0; // caches VK code fon WM_KEYDOWN
|
||||
@ -4237,22 +4366,24 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
||||
case WM_SYSCHAR:
|
||||
case WM_CHAR:
|
||||
{
|
||||
#ifdef KE_DEBUG
|
||||
printf("%s\tchar=%c\twp=%4x\tlp=%8x\n", (msg == WM_SYSCHAR) ? "WM_SYSCHAR" : "WM_CHAR", wParam, wParam, lParam);
|
||||
#endif
|
||||
result = OnChar(wParam, lParam);
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS,
|
||||
("%s charCode=%d scanCode=%d\n", msg == WM_SYSCHAR ? "WM_SYSCHAR" : "WM_CHAR",
|
||||
wParam, HIWORD(lParam) & 0xFF));
|
||||
|
||||
// These must be checked here too as a lone WM_CHAR could be received
|
||||
// if a child window didn't handle it (for example Alt+Space in a content window)
|
||||
SetupModKeyState();
|
||||
|
||||
result = OnChar(wParam, HIWORD(lParam) & 0xFF);
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_SYSKEYUP:
|
||||
case WM_KEYUP:
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS,
|
||||
("%s VK=%d\n", msg == WM_SYSKEYDOWN ? "WM_SYSKEYUP" : "WM_KEYUP", wParam));
|
||||
|
||||
#ifdef KE_DEBUG
|
||||
printf("%s\t\twp=%x\tlp=%x\n", (WM_KEYUP==msg) ? "WM_KEYUP" : "WM_SYSKEYUP", wParam, lParam);
|
||||
#endif
|
||||
mIsShiftDown = IS_VK_DOWN(NS_VK_SHIFT);
|
||||
mIsControlDown = IS_VK_DOWN(NS_VK_CONTROL);
|
||||
mIsAltDown = IS_VK_DOWN(NS_VK_ALT);
|
||||
SetupModKeyState();
|
||||
|
||||
// Note: the original code passed (HIWORD(lParam)) to OnKeyUp as
|
||||
// scan code. However, this breaks Alt+Num pad input.
|
||||
@ -4276,7 +4407,7 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
||||
// This helps avoid triggering the menu bar for ALT key accelerators used in
|
||||
// assistive technologies such as Window-Eyes and ZoomText, and when using Alt+Tab
|
||||
// to switch back to Mozilla in Windows 95 and Windows 98
|
||||
result = OnKeyUp(wParam, (HIWORD(lParam)), lParam);
|
||||
result = OnKeyUp(wParam, lParam);
|
||||
}
|
||||
else {
|
||||
result = PR_FALSE;
|
||||
@ -4288,13 +4419,10 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
||||
// Let the fall through if it isn't a key pad
|
||||
case WM_SYSKEYDOWN:
|
||||
case WM_KEYDOWN:
|
||||
#ifdef KE_DEBUG
|
||||
printf("%s\t\twp=%4x\tlp=%8x\n", (WM_KEYDOWN==msg) ? "WM_KEYDOWN" : "WM_SYSKEYDOWN", wParam, lParam);
|
||||
#endif
|
||||
PR_LOG(sWindowsLog, PR_LOG_ALWAYS,
|
||||
("%s VK=%d\n", msg == WM_SYSKEYDOWN ? "WM_SYSKEYDOWN" : "WM_KEYDOWN", wParam));
|
||||
|
||||
mIsShiftDown = IS_VK_DOWN(NS_VK_SHIFT);
|
||||
mIsControlDown = IS_VK_DOWN(NS_VK_CONTROL);
|
||||
mIsAltDown = IS_VK_DOWN(NS_VK_ALT);
|
||||
SetupModKeyState();
|
||||
|
||||
// Note: the original code passed (HIWORD(lParam)) to OnKeyDown as
|
||||
// scan code. However, this breaks Alt+Num pad input.
|
||||
@ -4318,7 +4446,7 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
||||
result = PR_FALSE;
|
||||
}
|
||||
else if (!sIMEIsComposing) {
|
||||
result = OnKeyDown(wParam, (HIWORD(lParam)), lParam);
|
||||
result = OnKeyDown(wParam, lParam, nsnull);
|
||||
}
|
||||
else
|
||||
result = PR_FALSE;
|
||||
@ -6570,8 +6698,7 @@ BOOL nsWindow::OnInputLangChange(HKL aHKL, LRESULT *oRetValue)
|
||||
if (gKeyboardLayout != aHKL)
|
||||
{
|
||||
gKeyboardLayout = aHKL;
|
||||
|
||||
gKbdLayout.LoadLayout();
|
||||
gKbdLayout.LoadLayout(gKeyboardLayout);
|
||||
}
|
||||
|
||||
ResetInputState();
|
||||
|
@ -67,6 +67,7 @@ class nsIFile;
|
||||
class imgIContainer;
|
||||
|
||||
struct nsAlternativeCharCode;
|
||||
struct nsFakeCharMessage;
|
||||
|
||||
#ifdef ACCESSIBILITY
|
||||
#include "OLEACC.H"
|
||||
@ -308,11 +309,12 @@ protected:
|
||||
virtual PRBool OnMove(PRInt32 aX, PRInt32 aY);
|
||||
virtual PRBool OnPaint(HDC aDC = nsnull);
|
||||
virtual PRBool OnResize(nsRect &aWindowRect);
|
||||
|
||||
BOOL OnChar(UINT charCode, LPARAM keyData, PRUint32 aFlags = 0);
|
||||
|
||||
BOOL OnKeyDown( UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyCode);
|
||||
BOOL OnKeyUp( UINT aVirtualKeyCode, UINT aScanCode, LPARAM aKeyCode);
|
||||
|
||||
void SetupModKeyState();
|
||||
BOOL OnChar(UINT charCode, UINT aScanCode, PRUint32 aFlags = 0);
|
||||
BOOL OnKeyDown( UINT aVirtualKeyCode, LPARAM aKeyCode,
|
||||
nsFakeCharMessage* aFakeCharMessage);
|
||||
BOOL OnKeyUp( UINT aVirtualKeyCode, LPARAM aKeyCode);
|
||||
UINT MapFromNativeToDOM(UINT aNativeKeyCode);
|
||||
|
||||
|
||||
@ -367,6 +369,12 @@ protected:
|
||||
|
||||
PRBool CanTakeFocus();
|
||||
|
||||
virtual void SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters);
|
||||
|
||||
private:
|
||||
|
||||
|
||||
|
@ -156,6 +156,12 @@ protected:
|
||||
return mLastRollup;
|
||||
}
|
||||
|
||||
virtual void SynthesizeNativeKeyEvent(PRInt32 aNativeKeyboardLayout,
|
||||
PRInt32 aNativeKeyCode,
|
||||
PRUint32 aModifierFlags,
|
||||
const nsAString& aCharacters,
|
||||
const nsAString& aUnmodifiedCharacters) {}
|
||||
|
||||
protected:
|
||||
void* mClientData;
|
||||
EVENT_CALLBACK mEventCallback;
|
||||
|
@ -45,6 +45,7 @@ include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = test_bug343416.xul \
|
||||
test_keycodes.xul \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
347
widget/tests/test_keycodes.xul
Normal file
347
widget/tests/test_keycodes.xul
Normal file
@ -0,0 +1,347 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<window title="Key event tests"
|
||||
onload="runTest()"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<title>Key event 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" />
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
<!-- for some reason, if we don't have 'accesskey' here, adding it dynamically later
|
||||
doesn't work! -->
|
||||
<button id="button" accesskey="z">Hello</button>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
function synthesizeNativeKey(aLayout, aKeyCode, aModifiers, aSystemChars,
|
||||
aSystemUnmodifiedChars, aWindow)
|
||||
{
|
||||
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
|
||||
|
||||
if (!aWindow)
|
||||
aWindow = window;
|
||||
|
||||
var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
|
||||
getInterface(Components.interfaces.nsIDOMWindowUtils);
|
||||
|
||||
if (utils) {
|
||||
var modifiers = 0;
|
||||
if (aModifiers.capsLock) modifiers |= 0x01;
|
||||
if (aModifiers.numLock) modifiers |= 0x02;
|
||||
if (aModifiers.shift) modifiers |= 0x0100;
|
||||
if (aModifiers.shiftRight) modifiers |= 0x0200;
|
||||
if (aModifiers.ctrl) modifiers |= 0x0400;
|
||||
if (aModifiers.ctrlRight) modifiers |= 0x0800;
|
||||
if (aModifiers.alt) modifiers |= 0x1000;
|
||||
if (aModifiers.altRight) modifiers |= 0x2000;
|
||||
if (aModifiers.command) modifiers |= 0x4000;
|
||||
if (aModifiers.help) modifiers |= 0x8000;
|
||||
if (aModifiers.function) modifiers |= 0x10000;
|
||||
if (aModifiers.numericKeyPad) modifiers |= 0x01000000;
|
||||
|
||||
utils.sendNativeKeyEvent(aLayout, aKeyCode, modifiers,
|
||||
aSystemChars, aSystemUnmodifiedChars);
|
||||
}
|
||||
}
|
||||
|
||||
var keyboardLayouts;
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
// These constants can be found by inspecting files under
|
||||
// /System/Library/Keyboard\ Layouts/Unicode.bundle/Contents/Resources/
|
||||
keyboardLayouts = {
|
||||
"US-Extended":-2,
|
||||
"Greek":-18944
|
||||
};
|
||||
} else if (navigator.platform.indexOf("Win") == 0) {
|
||||
// These constants can be found by inspecting registry keys under
|
||||
// HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Keyboard Layouts
|
||||
keyboardLayouts = {
|
||||
"US":0x409,
|
||||
"Greek":0x408
|
||||
};
|
||||
}
|
||||
|
||||
// Test the charcodes and modifiers being delivered to keypress handlers.
|
||||
function runPressTests()
|
||||
{
|
||||
var pressList;
|
||||
function onKeyPress(e)
|
||||
{
|
||||
pressList.push(e);
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// The first parameter is the complete input event. The second parameter is
|
||||
// what to test against.
|
||||
// XXX should probably check that keydown and keyup events were dispatched too
|
||||
function testKey(aEvent, aExpectGeckoChar)
|
||||
{
|
||||
pressList = [];
|
||||
|
||||
synthesizeNativeKey(keyboardLayouts[aEvent.layout],
|
||||
aEvent.keyCode, aEvent, aEvent.chars, aEvent.unmodifiedChars);
|
||||
|
||||
var name = aEvent.layout + " '" + aEvent.chars + "'";
|
||||
if (aEvent.shift) {
|
||||
name += " [Shift]";
|
||||
}
|
||||
if (aEvent.ctrl) {
|
||||
name += " [Ctrl]";
|
||||
}
|
||||
if (aEvent.alt) {
|
||||
name += " [Alt]";
|
||||
}
|
||||
|
||||
is(pressList.length, aExpectGeckoChar == "" ? 0 : 1, name + ", wrong number of press events");
|
||||
if (pressList.length == 0)
|
||||
return;
|
||||
var e = pressList[0];
|
||||
is(e.ctrlKey, aEvent.ctrl || 0, name + ", Ctrl mismatch");
|
||||
is(e.metaKey, aEvent.command || 0, name + ", Command mismatch");
|
||||
is(e.altKey, aEvent.alt || 0, name + ", Alt mismatch");
|
||||
is(e.shiftKey, aEvent.shift || 0, name + ", Shift mismatch");
|
||||
|
||||
if (aExpectGeckoChar.length > 0) {
|
||||
is(e.charCode, aExpectGeckoChar.charCodeAt(0), name + ", charcode");
|
||||
} else {
|
||||
is(e.charCode, 0, name + ", no charcode");
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("keypress", onKeyPress, false);
|
||||
|
||||
// These tests have to be per-plaform.
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
// On Mac, you can produce event records for any desired keyboard input
|
||||
// by running with NSPR_LOG_MODULES=nsCocoaWidgets:5 and typing into the browser.
|
||||
// We will dump the key event fields to the console. Use the International system
|
||||
// preferences widget to enable other keyboard layouts and select them from the
|
||||
// input menu to see what keyboard events they generate.
|
||||
// Note that it's possible to send bogus key events here, e.g.
|
||||
// {keyCode:0, chars:"z", unmodifiedChars:"P"} --- sendNativeKeyEvent
|
||||
// makes no attempt to verify that the keyCode matches the characters. So only
|
||||
// test key event records that you saw Cocoa send.
|
||||
|
||||
// Plain text input
|
||||
testKey({layout:"US-Extended", keyCode:0, chars:"a", unmodifiedChars:"a"},
|
||||
"a");
|
||||
testKey({layout:"US-Extended", keyCode:11, chars:"b", unmodifiedChars:"b"},
|
||||
"b");
|
||||
testKey({layout:"US-Extended", keyCode:0, shift:1, chars:"A", unmodifiedChars:"A"},
|
||||
"A");
|
||||
|
||||
// Ctrl keys
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"a");
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"A"},
|
||||
"A");
|
||||
|
||||
// Alt keys
|
||||
testKey({layout:"US-Extended", keyCode:0, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
|
||||
"\u00e5");
|
||||
testKey({layout:"US-Extended", keyCode:0, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"A"},
|
||||
"\u00c5");
|
||||
|
||||
// Command keys
|
||||
testKey({layout:"US-Extended", keyCode:0, command:1, chars:"a", unmodifiedChars:"a"},
|
||||
"a");
|
||||
// Shift-cmd gives us the unshifted character
|
||||
testKey({layout:"US-Extended", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"A"},
|
||||
"a");
|
||||
// Ctrl-cmd gives us the unshifted character
|
||||
testKey({layout:"US-Extended", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"a");
|
||||
// Alt-cmd gives us the *shifted* character
|
||||
testKey({layout:"US-Extended", keyCode:0, command:1, alt:1, chars:"\u00e5", unmodifiedChars:"a"},
|
||||
"\u00e5");
|
||||
testKey({layout:"US-Extended", keyCode:0, command:1, alt:1, shift:1, chars:"\u00c5", unmodifiedChars:"a"},
|
||||
"\u00c5");
|
||||
|
||||
// Greek ctrl keys produce Latin charcodes
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"a");
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"\u0391"},
|
||||
"A");
|
||||
|
||||
// Greek command keys
|
||||
testKey({layout:"Greek", keyCode:0, command:1, chars:"a", unmodifiedChars:"\u03b1"},
|
||||
"a");
|
||||
// Shift-cmd gives us the unshifted character
|
||||
testKey({layout:"Greek", keyCode:0, command:1, shift:1, chars:"a", unmodifiedChars:"\u391"},
|
||||
"a");
|
||||
// Ctrl-cmd gives us the unshifted character
|
||||
testKey({layout:"Greek", keyCode:0, command:1, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"a");
|
||||
// Alt-cmd gives us the *shifted* character
|
||||
testKey({layout:"Greek", keyCode:0, command:1, alt:1, chars:"\u00a8", unmodifiedChars:"\u03b1"},
|
||||
"\u00a8");
|
||||
testKey({layout:"Greek", keyCode:0, command:1, alt:1, shift:1, chars:"\u00b9", unmodifiedChars:"\u0391"},
|
||||
"\u00b9");
|
||||
}
|
||||
|
||||
if (navigator.platform.indexOf("Win") == 0) {
|
||||
// On Windows, you can use Spy++ or Winspector (free) to watch window messages.
|
||||
// The keyCode is given by the wParam of the last WM_KEYDOWN message. The
|
||||
// chars string is given by the wParam of the WM_CHAR message. unmodifiedChars
|
||||
// is not needed on Windows.
|
||||
|
||||
// Plain text input
|
||||
testKey({layout:"US", keyCode:65, chars:"a"},
|
||||
"a");
|
||||
testKey({layout:"US", keyCode:66, chars:"b"},
|
||||
"b");
|
||||
testKey({layout:"US", keyCode:65, shift:1, chars:"A"},
|
||||
"A");
|
||||
|
||||
// Ctrl keys
|
||||
testKey({layout:"US", keyCode:65, ctrl:1, chars:"\u0001"},
|
||||
"a");
|
||||
testKey({layout:"US", keyCode:65, ctrl:1, shift:1, chars:"\u0001"},
|
||||
"A");
|
||||
|
||||
// Alt keys
|
||||
testKey({layout:"US", keyCode:65, alt:1, chars:"a"},
|
||||
"a");
|
||||
testKey({layout:"US", keyCode:65, alt:1, shift:1, chars:"A"},
|
||||
"A");
|
||||
|
||||
// Shift-ctrl-alt generates no WM_CHAR, but we still get a keypress
|
||||
testKey({layout:"US", keyCode:65, alt:1, ctrl:1, shift:1, chars:""},
|
||||
"A");
|
||||
|
||||
// Greek plain text
|
||||
testKey({layout:"Greek", keyCode:65, chars:"\u03b1"},
|
||||
"\u03b1");
|
||||
testKey({layout:"Greek", keyCode:65, shift:1, chars:"\u0391"},
|
||||
"\u0391");
|
||||
|
||||
// Greek ctrl keys produce Latin charcodes
|
||||
testKey({layout:"Greek", keyCode:65, ctrl:1, chars:"\u0001"},
|
||||
"a");
|
||||
testKey({layout:"Greek", keyCode:65, ctrl:1, shift:1, chars:"\u0001"},
|
||||
"A");
|
||||
}
|
||||
|
||||
document.removeEventListener("keypress", onKeyPress, false);
|
||||
}
|
||||
|
||||
// Test the activation (or not) of an HTML accesskey
|
||||
function runAccessKeyTests()
|
||||
{
|
||||
var button = document.getElementById("button");
|
||||
var activationCount;
|
||||
|
||||
function onClick(e)
|
||||
{
|
||||
++activationCount;
|
||||
}
|
||||
|
||||
// The first parameter is the complete input event. The second and third parameters are
|
||||
// what to test against.
|
||||
function testKey(aEvent, aAccessKey, aShouldActivate)
|
||||
{
|
||||
activationCount = 0;
|
||||
button.setAttribute("accesskey", aAccessKey);
|
||||
|
||||
synthesizeNativeKey(keyboardLayouts[aEvent.layout],
|
||||
aEvent.keyCode, aEvent, aEvent.chars, aEvent.unmodifiedChars);
|
||||
|
||||
var name = aEvent.layout + " '" + aEvent.chars + "'";
|
||||
|
||||
is(activationCount, aShouldActivate ? 1 : 0, name + ", activating '" + aAccessKey + "'");
|
||||
}
|
||||
|
||||
button.addEventListener("click", onClick, false);
|
||||
|
||||
// These tests have to be per-plaform.
|
||||
if (navigator.platform.indexOf("Mac") == 0) {
|
||||
// Basic sanity checks
|
||||
testKey({layout:"US-Extended", keyCode:0, chars:"a", unmodifiedChars:"a"},
|
||||
"a", false);
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"a", true);
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"A", true);
|
||||
|
||||
// Shift-ctrl does not activate accesskeys
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"A"},
|
||||
"a", false);
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, shift:1, chars:"\u0001", unmodifiedChars:"A"},
|
||||
"A", false);
|
||||
// Alt-ctrl does not activate accesskeys
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, alt:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"a", false);
|
||||
testKey({layout:"US-Extended", keyCode:0, ctrl:1, alt:1, chars:"\u0001", unmodifiedChars:"a"},
|
||||
"A", false);
|
||||
|
||||
// Greek layout can activate a Latin accesskey
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"a", true);
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"A", true);
|
||||
// ... and a Greek accesskey!
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"\u03b1", true);
|
||||
testKey({layout:"Greek", keyCode:0, ctrl:1, chars:"\u0001", unmodifiedChars:"\u03b1"},
|
||||
"\u0391", true);
|
||||
}
|
||||
|
||||
if (navigator.platform.indexOf("Win") == 0) {
|
||||
// Basic sanity checks
|
||||
testKey({layout:"US", keyCode:65, chars:"a"},
|
||||
"a", false);
|
||||
testKey({layout:"US", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"a", true);
|
||||
testKey({layout:"US", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"A", true);
|
||||
|
||||
// shift-alt-ctrl does not activate accesskeys
|
||||
testKey({layout:"US", keyCode:65, ctrl:1, shift:1, alt:1, chars:""},
|
||||
"a", false);
|
||||
testKey({layout:"US", keyCode:65, ctrl:1, shift:1, alt:1, chars:""},
|
||||
"A", false);
|
||||
|
||||
// Greek layout can activate a Latin accesskey
|
||||
testKey({layout:"Greek", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"a", true);
|
||||
testKey({layout:"Greek", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"A", true);
|
||||
// ... and a Greek accesskey!
|
||||
testKey({layout:"Greek", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"\u03b1", true);
|
||||
testKey({layout:"Greek", keyCode:65, shift:1, alt:1, chars:"A"},
|
||||
"\u0391", true);
|
||||
}
|
||||
|
||||
button.removeEventListener("click", onClick, false);
|
||||
}
|
||||
|
||||
function runTest()
|
||||
{
|
||||
runPressTests();
|
||||
runAccessKeyTests();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
]]>
|
||||
</script>
|
||||
|
||||
</window>
|
Loading…
x
Reference in New Issue
Block a user