Bug 519631 nsBidiKeybaord.mm is still using KL APIs r=smontagu+josh
authorMasayuki Nakano <masayuki@d-toybox.com>
Fri, 16 Oct 2009 18:12:09 +0900
changeset 33902 cb5685e576543042f9b21668f2b95ff93c862a9f
parent 33901 a847cf5b46690b1957f9fd53132ec19f15424ca8
child 33903 382c73f2650f297a17327605b9afc99b1161ef92
push idunknown
push userunknown
push dateunknown
reviewerssmontagu
bugs519631
milestone1.9.3a1pre
Bug 519631 nsBidiKeybaord.mm is still using KL APIs r=smontagu+josh
widget/src/cocoa/nsBidiKeyboard.mm
widget/src/cocoa/nsCocoaTextInputHandler.h
widget/src/cocoa/nsCocoaTextInputHandler.mm
--- a/widget/src/cocoa/nsBidiKeyboard.mm
+++ b/widget/src/cocoa/nsBidiKeyboard.mm
@@ -35,16 +35,18 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsBidiKeyboard.h"
 #include "nsObjCExceptions.h"
+#include "nsCocoaUtils.h"
+#include "nsCocoaTextInputHandler.h"
 
 #import <Carbon/Carbon.h>
 
 NS_IMPL_ISUPPORTS1(nsBidiKeyboard, nsIBidiKeyboard)
 
 nsBidiKeyboard::nsBidiKeyboard() : nsIBidiKeyboard()
 {
 }
@@ -52,21 +54,19 @@ nsBidiKeyboard::nsBidiKeyboard() : nsIBi
 nsBidiKeyboard::~nsBidiKeyboard()
 {
 }
 
 NS_IMETHODIMP nsBidiKeyboard::IsLangRTL(PRBool *aIsRTL)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-#ifdef __LP64__
-  // There isn't a way to determine this in 64-bit Mac OS X because any keyboard
-  // layout could generate any unicode characters. Apple simply doesn't consider
-  // this to be a valid question.
-  return NS_ERROR_FAILURE;
+#ifdef NS_LEOPARD_AND_LATER
+  *aIsRTL = nsTISInputSource::CurrentKeyboardLayout().IsForRTLLanguage();
+  return NS_OK;
 #else  
   *aIsRTL = PR_FALSE;
   nsresult rv = NS_ERROR_FAILURE;
 
   OSStatus err;
   KeyboardLayoutRef currentKeyboard;
 
   err = ::KLGetCurrentKeyboardLayout(&currentKeyboard);
--- a/widget/src/cocoa/nsCocoaTextInputHandler.h
+++ b/widget/src/cocoa/nsCocoaTextInputHandler.h
@@ -61,16 +61,18 @@ class nsChildView;
  * which is returned by TISCreateInputSourceList.  However, when we release the
  * list, we cannot access the TISInputSourceRef.  So, it's not usable, and it
  * may cause the memory leak bugs.  nsTISInputSource automatically releases the
  * list when the instance is destroyed.
  */
 class nsTISInputSource
 {
 public:
+  static nsTISInputSource& CurrentKeyboardLayout();
+
   nsTISInputSource()
   {
     mInputSourceList = nsnull;
     Clear();
   }
 
   nsTISInputSource(const char* aID)
   {
@@ -167,27 +169,45 @@ public:
   }
 
   PRBool GetInputSourceType(nsAString &aType)
   {
     NS_ENSURE_TRUE(mInputSource, PR_FALSE);
     return GetStringProperty(kTISPropertyInputSourceType, aType);
   }
 
+  PRBool IsForRTLLanguage();
+  PRBool IsInitializedByCurrentKeyboardLayout();
+
+  enum {
+    // 40 is an actual result of the ::LMGetKbdType() when we connect an
+    // unknown keyboard and set the keyboard type to ANSI manually on the
+    // set up dialog.
+    eKbdType_ANSI = 40
+  };
+
+  PRBool TranslateToString(UInt32 aKeyCode, UInt32 aModifiers, UInt32 aKbdType,
+                           nsAString &aStr);
+
   void Select();
+  void Clear();
 
 protected:
-  void Clear();
+  static PRBool UCKeyTranslateToString(const UCKeyboardLayout* aHandle,
+                                       UInt32 aKeyCode, UInt32 aModifiers,
+                                       UInt32 aKbType, nsAString &aStr);
 
   PRBool GetBoolProperty(const CFStringRef aKey);
   PRBool GetStringProperty(const CFStringRef aKey, CFStringRef &aStr);
   PRBool GetStringProperty(const CFStringRef aKey, nsAString &aStr);
 
   TISInputSourceRef mInputSource;
   CFArrayRef mInputSourceList;
+  const UCKeyboardLayout* mUCKeyboardLayout;
+  PRInt8 mIsRTL;
 };
 
 /**
  * nsCocoaIMEHandler manages:
  *   1. The IME/keyboard layout statement of nsChildView.
  *   2. The IME composition statement of nsChildView.
  * And also provides the methods which controls the current IME transaction of
  * the instance.
--- a/widget/src/cocoa/nsCocoaTextInputHandler.mm
+++ b/widget/src/cocoa/nsCocoaTextInputHandler.mm
@@ -38,16 +38,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCocoaTextInputHandler.h"
 
 #ifdef NS_LEOPARD_AND_LATER
 
 #include "nsChildView.h"
 #include "nsObjCExceptions.h"
+#include "nsBidiUtils.h"
 
 #ifdef MOZ_LOGGING
 #define FORCE_PR_LOG
 #endif
 #include "prlog.h"
 
 #undef DEBUG_IME_HANDLER
 #undef DEBUG_TEXT_INPUT_HANDLER
@@ -136,26 +137,99 @@ GetWindowLevelName(NSInteger aWindowLeve
       return "kCGNumberOfWindowLevelKeys";
     default:
       return "unknown window level";
   }
 }
 
 #endif // DEBUG_IME_HANDLER
 
+static PRUint32 gHandlerInstanceCount = 0;
+static nsTISInputSource gCurrentKeyboardLayout;
+
+static void
+InitCurrentKeyboardLayout()
+{
+  if (gHandlerInstanceCount > 0 &&
+      !gCurrentKeyboardLayout.IsInitializedByCurrentKeyboardLayout()) {
+    gCurrentKeyboardLayout.InitByCurrentKeyboardLayout();
+  }
+}
+
+static void
+FinalizeCurrentKeyboardLayout()
+{
+  gCurrentKeyboardLayout.Clear();
+}
+
 
 #pragma mark -
 
 
 /******************************************************************************
  *
  *  nsTISInputSource implementation
  *
  ******************************************************************************/
 
+// static
+nsTISInputSource&
+nsTISInputSource::CurrentKeyboardLayout()
+{
+  InitCurrentKeyboardLayout();
+  return gCurrentKeyboardLayout;
+}
+
+// static
+PRBool
+nsTISInputSource::UCKeyTranslateToString(const UCKeyboardLayout* aHandle,
+                                         UInt32 aKeyCode, UInt32 aModifiers,
+                                         UInt32 aKbType, nsAString &aStr)
+{
+#ifdef DEBUG_TEXT_INPUT_HANDLER
+  NSLog(@"**** nsTISInputSource::UCKeyTranslateToString: aHandle: %p, aKeyCode: %X, aModifiers: %X, aKbType: %X",
+        aHandle, aKeyCode, aModifiers, aKbType);
+  PRBool isShift = aModifiers & shiftKey;
+  PRBool isCtrl = aModifiers & controlKey;
+  PRBool isOpt = aModifiers & optionKey;
+  PRBool isCmd = aModifiers & cmdKey;
+  PRBool isCL = aModifiers & alphaLock;
+  PRBool isNL = aModifiers & kEventKeyModifierNumLockMask;
+  NSLog(@"        Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
+        isShift ? "ON" : "off", isCtrl ? "ON" : "off", isOpt ? "ON" : "off",
+        isCmd ? "ON" : "off", isCL ? "ON" : "off", isNL ? "ON" : "off");
+#endif
+  NS_PRECONDITION(aStr.IsEmpty(), "aStr isn't empty");
+  UInt32 deadKeyState = 0;
+  UniCharCount len;
+  UniChar chars[5];
+  OSStatus err = ::UCKeyTranslate(aHandle, aKeyCode,
+                                  kUCKeyActionDown, aModifiers >> 8,
+                                  aKbType, kUCKeyTranslateNoDeadKeysMask,
+                                  &deadKeyState, 5, &len, chars);
+  if (err != noErr) {
+    return PR_FALSE;
+  }
+  if (len == 0) {
+    return PR_TRUE;
+  }
+  if (!EnsureStringLength(aStr, len)) {
+    return PR_FALSE;
+  }
+  NS_ASSERTION(sizeof(PRUnichar) == sizeof(UniChar),
+               "size of PRUnichar and size of UniChar are different");
+  memcpy(aStr.BeginWriting(), chars, len * sizeof(PRUnichar));
+#ifdef DEBUG_TEXT_INPUT_HANDLER
+  for (PRUint32 i = 0; i < PRUint32(len); i++) {
+    NSLog(@"       result: %X(%C)", chars[i], chars[i] > ' ' ? chars[i] : ' ');
+  }
+#endif
+  return PR_TRUE;
+}
+
 void
 nsTISInputSource::InitByInputSourceID(const char* aID)
 {
   Clear();
   if (!aID)
     return;
 
   CFStringRef idstr = ::CFStringCreateWithCString(kCFAllocatorDefault, aID,
@@ -260,23 +334,28 @@ nsTISInputSource::InitByLanguage(CFStrin
   Clear();
   mInputSource = ::TISCopyInputSourceForLanguage(aLanguage);
 }
 
 const UCKeyboardLayout*
 nsTISInputSource::GetUCKeyboardLayout()
 {
   NS_ENSURE_TRUE(mInputSource, nsnull);
+  if (mUCKeyboardLayout) {
+    return mUCKeyboardLayout;
+  }
   CFDataRef uchr = static_cast<CFDataRef>(
     ::TISGetInputSourceProperty(mInputSource,
                                 kTISPropertyUnicodeKeyLayoutData));
 
   // We should be always able to get the layout here.
   NS_ENSURE_TRUE(uchr, nsnull);
-  return reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(uchr));
+  mUCKeyboardLayout =
+    reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(uchr));
+  return mUCKeyboardLayout;
 }
 
 PRBool
 nsTISInputSource::GetBoolProperty(const CFStringRef aKey)
 {
   CFBooleanRef ret = static_cast<CFBooleanRef>(
     ::TISGetInputSourceProperty(mInputSource, aKey));
   return ::CFBooleanGetValue(ret);
@@ -347,32 +426,64 @@ nsTISInputSource::GetPrimaryLanguage(nsA
 {
   NS_ENSURE_TRUE(mInputSource, PR_FALSE);
   CFStringRef primaryLanguage;
   NS_ENSURE_TRUE(GetPrimaryLanguage(primaryLanguage), PR_FALSE);
   GetStringForNSString((const NSString*)primaryLanguage, aPrimaryLanguage);
   return !aPrimaryLanguage.IsEmpty();
 }
 
+PRBool
+nsTISInputSource::IsForRTLLanguage()
+{
+  if (mIsRTL < 0) {
+    // Get the input character of the 'A' key of ANSI keyboard layout.
+    nsAutoString str;
+    PRBool ret = TranslateToString(kVK_ANSI_A, 0, eKbdType_ANSI, str);
+    NS_ENSURE_TRUE(ret, ret);
+    PRUnichar ch = str.IsEmpty() ? PRUnichar(0) : str.CharAt(0);
+    mIsRTL = UCS2_CHAR_IS_BIDI(ch) || ch == 0xD802 || ch == 0xD803;
+  }
+  return mIsRTL != 0;
+}
+
+PRBool
+nsTISInputSource::IsInitializedByCurrentKeyboardLayout()
+{
+  return mInputSource == ::TISCopyCurrentKeyboardLayoutInputSource();
+}
+
+PRBool
+nsTISInputSource::TranslateToString(UInt32 aKeyCode, UInt32 aModifiers,
+                                    UInt32 aKbdType, nsAString &aStr)
+{
+  aStr.Truncate();
+  const UCKeyboardLayout* UCKey = GetUCKeyboardLayout();
+  NS_ENSURE_TRUE(UCKey, PR_FALSE);
+  return UCKeyTranslateToString(UCKey, aKeyCode, aModifiers, aKbdType, aStr);
+}
+
 void
 nsTISInputSource::Select()
 {
   if (!mInputSource)
     return;
   ::TISSelectInputSource(mInputSource);
 }
 
 void
 nsTISInputSource::Clear()
 {
   if (mInputSourceList) {
     ::CFRelease(mInputSourceList);
   }
   mInputSourceList = nsnull;
   mInputSource = nsnull;
+  mIsRTL = -1;
+  mUCKeyboardLayout = nsnull;
 }
 
 
 #pragma mark -
 
 
 /******************************************************************************
  *
@@ -793,28 +904,32 @@ nsCocoaIMEHandler::ExecutePendingMethods
 
 nsCocoaIMEHandler::nsCocoaIMEHandler() :
   mOwnerWidget(nsnull), mView(nsnull),
   mPendingMethods(0), mIMECompositionString(nsnull),
   mIsIMEComposing(PR_FALSE), mIsIMEEnabled(PR_TRUE),
   mIsASCIICapableOnly(PR_FALSE), mIgnoreIMECommit(PR_FALSE),
   mIsInFocusProcessing(PR_FALSE)
 {
+  gHandlerInstanceCount++;
   InitStaticMembers();
 }
 
 nsCocoaIMEHandler::~nsCocoaIMEHandler()
 {
   if (mTimer) {
     mTimer->Cancel();
     mTimer = nsnull;
   }
   if (sFocusedIMEHandler == this) {
     sFocusedIMEHandler = nsnull;
   }
+  if (--gHandlerInstanceCount == 0) {
+    FinalizeCurrentKeyboardLayout();
+  }
 }
 
 void
 nsCocoaIMEHandler::Init(nsChildView* aOwner)
 {
   mOwnerWidget = aOwner;
   mView =
     static_cast<NSView<mozView>*>(aOwner->GetNativeData(NS_NATIVE_WIDGET));