widget/cocoa/TextInputHandler.mm

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/cocoa/TextInputHandler.mm	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,4588 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim: set ts=2 sw=2 et tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/ArrayUtils.h"
    1.11 +
    1.12 +#include "TextInputHandler.h"
    1.13 +
    1.14 +#ifdef MOZ_LOGGING
    1.15 +#define FORCE_PR_LOG /* Allow logging in the release build */
    1.16 +#endif // MOZ_LOGGING
    1.17 +#include "prlog.h"
    1.18 +
    1.19 +#include "mozilla/MiscEvents.h"
    1.20 +#include "mozilla/MouseEvents.h"
    1.21 +#include "mozilla/TextEvents.h"
    1.22 +
    1.23 +#include "nsChildView.h"
    1.24 +#include "nsObjCExceptions.h"
    1.25 +#include "nsBidiUtils.h"
    1.26 +#include "nsToolkit.h"
    1.27 +#include "nsCocoaUtils.h"
    1.28 +#include "WidgetUtils.h"
    1.29 +#include "nsPrintfCString.h"
    1.30 +
    1.31 +#ifdef __LP64__
    1.32 +#include "ComplexTextInputPanel.h"
    1.33 +#include <objc/runtime.h>
    1.34 +#endif // __LP64__
    1.35 +
    1.36 +#ifdef MOZ_LOGGING
    1.37 +#define FORCE_PR_LOG
    1.38 +#endif
    1.39 +#include "prlog.h"
    1.40 +
    1.41 +#ifndef __LP64__
    1.42 +enum {
    1.43 +  // Currently focused ChildView (while this TSM document is active).
    1.44 +  // Transient (only set while TSMProcessRawKeyEvent() is processing a key
    1.45 +  // event), and the ChildView will be retained and released around the call
    1.46 +  // to TSMProcessRawKeyEvent() -- so it can be weak.
    1.47 +  kFocusedChildViewTSMDocPropertyTag  = 'GKFV', // type ChildView* [WEAK]
    1.48 +};
    1.49 +
    1.50 +// Undocumented HIToolbox function used by WebKit to allow Carbon-based IME
    1.51 +// to work in a Cocoa-based browser (like Safari or Cocoa-widgets Firefox).
    1.52 +// (Recent WebKit versions actually use a thin wrapper around this function
    1.53 +// called WKSendKeyEventToTSM().)
    1.54 +//
    1.55 +// Calling TSMProcessRawKeyEvent() from ChildView's keyDown: and keyUp:
    1.56 +// methods (when the ChildView is a plugin view) bypasses Cocoa's IME
    1.57 +// infrastructure and (instead) causes Carbon TSM events to be sent on each
    1.58 +// NSKeyDown event.  We install a Carbon event handler
    1.59 +// (PluginKeyEventsHandler()) to catch these events and pass them to Gecko
    1.60 +// (which in turn passes them to the plugin).
    1.61 +extern "C" long TSMProcessRawKeyEvent(EventRef carbonEvent);
    1.62 +#endif // __LP64__
    1.63 +
    1.64 +using namespace mozilla;
    1.65 +using namespace mozilla::widget;
    1.66 +
    1.67 +#ifdef PR_LOGGING
    1.68 +
    1.69 +PRLogModuleInfo* gLog = nullptr;
    1.70 +
    1.71 +static const char*
    1.72 +OnOrOff(bool aBool)
    1.73 +{
    1.74 +  return aBool ? "ON" : "off";
    1.75 +}
    1.76 +
    1.77 +static const char*
    1.78 +TrueOrFalse(bool aBool)
    1.79 +{
    1.80 +  return aBool ? "TRUE" : "FALSE";
    1.81 +}
    1.82 +
    1.83 +static const char*
    1.84 +GetKeyNameForNativeKeyCode(unsigned short aNativeKeyCode)
    1.85 +{
    1.86 +  switch (aNativeKeyCode) {
    1.87 +    case kVK_Escape:              return "Escape";
    1.88 +    case kVK_RightCommand:        return "Right-Command";
    1.89 +    case kVK_Command:             return "Command";
    1.90 +    case kVK_Shift:               return "Shift";
    1.91 +    case kVK_CapsLock:            return "CapsLock";
    1.92 +    case kVK_Option:              return "Option";
    1.93 +    case kVK_Control:             return "Control";
    1.94 +    case kVK_RightShift:          return "Right-Shift";
    1.95 +    case kVK_RightOption:         return "Right-Option";
    1.96 +    case kVK_RightControl:        return "Right-Control";
    1.97 +    case kVK_ANSI_KeypadClear:    return "Clear";
    1.98 +
    1.99 +    case kVK_F1:                  return "F1";
   1.100 +    case kVK_F2:                  return "F2";
   1.101 +    case kVK_F3:                  return "F3";
   1.102 +    case kVK_F4:                  return "F4";
   1.103 +    case kVK_F5:                  return "F5";
   1.104 +    case kVK_F6:                  return "F6";
   1.105 +    case kVK_F7:                  return "F7";
   1.106 +    case kVK_F8:                  return "F8";
   1.107 +    case kVK_F9:                  return "F9";
   1.108 +    case kVK_F10:                 return "F10";
   1.109 +    case kVK_F11:                 return "F11";
   1.110 +    case kVK_F12:                 return "F12";
   1.111 +    case kVK_F13:                 return "F13/PrintScreen";
   1.112 +    case kVK_F14:                 return "F14/ScrollLock";
   1.113 +    case kVK_F15:                 return "F15/Pause";
   1.114 +
   1.115 +    case kVK_ANSI_Keypad0:        return "NumPad-0";
   1.116 +    case kVK_ANSI_Keypad1:        return "NumPad-1";
   1.117 +    case kVK_ANSI_Keypad2:        return "NumPad-2";
   1.118 +    case kVK_ANSI_Keypad3:        return "NumPad-3";
   1.119 +    case kVK_ANSI_Keypad4:        return "NumPad-4";
   1.120 +    case kVK_ANSI_Keypad5:        return "NumPad-5";
   1.121 +    case kVK_ANSI_Keypad6:        return "NumPad-6";
   1.122 +    case kVK_ANSI_Keypad7:        return "NumPad-7";
   1.123 +    case kVK_ANSI_Keypad8:        return "NumPad-8";
   1.124 +    case kVK_ANSI_Keypad9:        return "NumPad-9";
   1.125 +
   1.126 +    case kVK_ANSI_KeypadMultiply: return "NumPad-*";
   1.127 +    case kVK_ANSI_KeypadPlus:     return "NumPad-+";
   1.128 +    case kVK_ANSI_KeypadMinus:    return "NumPad--";
   1.129 +    case kVK_ANSI_KeypadDecimal:  return "NumPad-.";
   1.130 +    case kVK_ANSI_KeypadDivide:   return "NumPad-/";
   1.131 +    case kVK_ANSI_KeypadEquals:   return "NumPad-=";
   1.132 +    case kVK_ANSI_KeypadEnter:    return "NumPad-Enter";
   1.133 +    case kVK_Return:              return "Return";
   1.134 +    case kVK_Powerbook_KeypadEnter: return "NumPad-EnterOnPowerBook";
   1.135 +
   1.136 +    case kVK_PC_Insert:           return "Insert/Help";
   1.137 +    case kVK_PC_Delete:           return "Delete";
   1.138 +    case kVK_Tab:                 return "Tab";
   1.139 +    case kVK_PC_Backspace:        return "Backspace";
   1.140 +    case kVK_Home:                return "Home";
   1.141 +    case kVK_End:                 return "End";
   1.142 +    case kVK_PageUp:              return "PageUp";
   1.143 +    case kVK_PageDown:            return "PageDown";
   1.144 +    case kVK_LeftArrow:           return "LeftArrow";
   1.145 +    case kVK_RightArrow:          return "RightArrow";
   1.146 +    case kVK_UpArrow:             return "UpArrow";
   1.147 +    case kVK_DownArrow:           return "DownArrow";
   1.148 +    case kVK_PC_ContextMenu:      return "ContextMenu";
   1.149 +
   1.150 +    case kVK_Function:            return "Function";
   1.151 +    case kVK_VolumeUp:            return "VolumeUp";
   1.152 +    case kVK_VolumeDown:          return "VolumeDown";
   1.153 +    case kVK_Mute:                return "Mute";
   1.154 +
   1.155 +    case kVK_ISO_Section:         return "ISO_Section";
   1.156 +
   1.157 +    case kVK_JIS_Yen:             return "JIS_Yen";
   1.158 +    case kVK_JIS_Underscore:      return "JIS_Underscore";
   1.159 +    case kVK_JIS_KeypadComma:     return "JIS_KeypadComma";
   1.160 +    case kVK_JIS_Eisu:            return "JIS_Eisu";
   1.161 +    case kVK_JIS_Kana:            return "JIS_Kana";
   1.162 +
   1.163 +    case kVK_ANSI_A:              return "A";
   1.164 +    case kVK_ANSI_B:              return "B";
   1.165 +    case kVK_ANSI_C:              return "C";
   1.166 +    case kVK_ANSI_D:              return "D";
   1.167 +    case kVK_ANSI_E:              return "E";
   1.168 +    case kVK_ANSI_F:              return "F";
   1.169 +    case kVK_ANSI_G:              return "G";
   1.170 +    case kVK_ANSI_H:              return "H";
   1.171 +    case kVK_ANSI_I:              return "I";
   1.172 +    case kVK_ANSI_J:              return "J";
   1.173 +    case kVK_ANSI_K:              return "K";
   1.174 +    case kVK_ANSI_L:              return "L";
   1.175 +    case kVK_ANSI_M:              return "M";
   1.176 +    case kVK_ANSI_N:              return "N";
   1.177 +    case kVK_ANSI_O:              return "O";
   1.178 +    case kVK_ANSI_P:              return "P";
   1.179 +    case kVK_ANSI_Q:              return "Q";
   1.180 +    case kVK_ANSI_R:              return "R";
   1.181 +    case kVK_ANSI_S:              return "S";
   1.182 +    case kVK_ANSI_T:              return "T";
   1.183 +    case kVK_ANSI_U:              return "U";
   1.184 +    case kVK_ANSI_V:              return "V";
   1.185 +    case kVK_ANSI_W:              return "W";
   1.186 +    case kVK_ANSI_X:              return "X";
   1.187 +    case kVK_ANSI_Y:              return "Y";
   1.188 +    case kVK_ANSI_Z:              return "Z";
   1.189 +
   1.190 +    case kVK_ANSI_1:              return "1";
   1.191 +    case kVK_ANSI_2:              return "2";
   1.192 +    case kVK_ANSI_3:              return "3";
   1.193 +    case kVK_ANSI_4:              return "4";
   1.194 +    case kVK_ANSI_5:              return "5";
   1.195 +    case kVK_ANSI_6:              return "6";
   1.196 +    case kVK_ANSI_7:              return "7";
   1.197 +    case kVK_ANSI_8:              return "8";
   1.198 +    case kVK_ANSI_9:              return "9";
   1.199 +    case kVK_ANSI_0:              return "0";
   1.200 +    case kVK_ANSI_Equal:          return "Equal";
   1.201 +    case kVK_ANSI_Minus:          return "Minus";
   1.202 +    case kVK_ANSI_RightBracket:   return "RightBracket";
   1.203 +    case kVK_ANSI_LeftBracket:    return "LeftBracket";
   1.204 +    case kVK_ANSI_Quote:          return "Quote";
   1.205 +    case kVK_ANSI_Semicolon:      return "Semicolon";
   1.206 +    case kVK_ANSI_Backslash:      return "Backslash";
   1.207 +    case kVK_ANSI_Comma:          return "Comma";
   1.208 +    case kVK_ANSI_Slash:          return "Slash";
   1.209 +    case kVK_ANSI_Period:         return "Period";
   1.210 +    case kVK_ANSI_Grave:          return "Grave";
   1.211 +
   1.212 +    default:                      return "undefined";
   1.213 +  }
   1.214 +}
   1.215 +
   1.216 +static const char*
   1.217 +GetCharacters(const NSString* aString)
   1.218 +{
   1.219 +  nsAutoString str;
   1.220 +  nsCocoaUtils::GetStringForNSString(aString, str);
   1.221 +  if (str.IsEmpty()) {
   1.222 +    return "";
   1.223 +  }
   1.224 +
   1.225 +  nsAutoString escapedStr;
   1.226 +  for (uint32_t i = 0; i < str.Length(); i++) {
   1.227 +    char16_t ch = str[i];
   1.228 +    if (ch < 0x20) {
   1.229 +      nsPrintfCString utf8str("(U+%04X)", ch);
   1.230 +      escapedStr += NS_ConvertUTF8toUTF16(utf8str);
   1.231 +    } else if (ch <= 0x7E) {
   1.232 +      escapedStr += ch;
   1.233 +    } else {
   1.234 +      nsPrintfCString utf8str("(U+%04X)", ch);
   1.235 +      escapedStr += ch;
   1.236 +      escapedStr += NS_ConvertUTF8toUTF16(utf8str);
   1.237 +    }
   1.238 +  }
   1.239 +
   1.240 +  // the result will be freed automatically by cocoa.
   1.241 +  NSString* result = nsCocoaUtils::ToNSString(escapedStr);
   1.242 +  return [result UTF8String];
   1.243 +}
   1.244 +
   1.245 +static const char*
   1.246 +GetCharacters(const CFStringRef aString)
   1.247 +{
   1.248 +  const NSString* str = reinterpret_cast<const NSString*>(aString);
   1.249 +  return GetCharacters(str);
   1.250 +}
   1.251 +
   1.252 +static const char*
   1.253 +GetNativeKeyEventType(NSEvent* aNativeEvent)
   1.254 +{
   1.255 +  switch ([aNativeEvent type]) {
   1.256 +    case NSKeyDown:      return "NSKeyDown";
   1.257 +    case NSKeyUp:        return "NSKeyUp";
   1.258 +    case NSFlagsChanged: return "NSFlagsChanged";
   1.259 +    default:             return "not key event";
   1.260 +  }
   1.261 +}
   1.262 +
   1.263 +static const char*
   1.264 +GetGeckoKeyEventType(const WidgetEvent& aEvent)
   1.265 +{
   1.266 +  switch (aEvent.message) {
   1.267 +    case NS_KEY_DOWN:    return "NS_KEY_DOWN";
   1.268 +    case NS_KEY_UP:      return "NS_KEY_UP";
   1.269 +    case NS_KEY_PRESS:   return "NS_KEY_PRESS";
   1.270 +    default:             return "not key event";
   1.271 +  }
   1.272 +}
   1.273 +
   1.274 +static const char*
   1.275 +GetRangeTypeName(uint32_t aRangeType)
   1.276 +{
   1.277 +  switch (aRangeType) {
   1.278 +    case NS_TEXTRANGE_RAWINPUT:
   1.279 +      return "NS_TEXTRANGE_RAWINPUT";
   1.280 +    case NS_TEXTRANGE_CONVERTEDTEXT:
   1.281 +      return "NS_TEXTRANGE_CONVERTEDTEXT";
   1.282 +    case NS_TEXTRANGE_SELECTEDRAWTEXT:
   1.283 +      return "NS_TEXTRANGE_SELECTEDRAWTEXT";
   1.284 +    case NS_TEXTRANGE_SELECTEDCONVERTEDTEXT:
   1.285 +      return "NS_TEXTRANGE_SELECTEDCONVERTEDTEXT";
   1.286 +    case NS_TEXTRANGE_CARETPOSITION:
   1.287 +      return "NS_TEXTRANGE_CARETPOSITION";
   1.288 +    default:
   1.289 +      return "invalid range type";
   1.290 +  }
   1.291 +}
   1.292 +
   1.293 +static const char*
   1.294 +GetWindowLevelName(NSInteger aWindowLevel)
   1.295 +{
   1.296 +  switch (aWindowLevel) {
   1.297 +    case kCGBaseWindowLevelKey:
   1.298 +      return "kCGBaseWindowLevelKey (NSNormalWindowLevel)";
   1.299 +    case kCGMinimumWindowLevelKey:
   1.300 +      return "kCGMinimumWindowLevelKey";
   1.301 +    case kCGDesktopWindowLevelKey:
   1.302 +      return "kCGDesktopWindowLevelKey";
   1.303 +    case kCGBackstopMenuLevelKey:
   1.304 +      return "kCGBackstopMenuLevelKey";
   1.305 +    case kCGNormalWindowLevelKey:
   1.306 +      return "kCGNormalWindowLevelKey";
   1.307 +    case kCGFloatingWindowLevelKey:
   1.308 +      return "kCGFloatingWindowLevelKey (NSFloatingWindowLevel)";
   1.309 +    case kCGTornOffMenuWindowLevelKey:
   1.310 +      return "kCGTornOffMenuWindowLevelKey (NSSubmenuWindowLevel, NSTornOffMenuWindowLevel)";
   1.311 +    case kCGDockWindowLevelKey:
   1.312 +      return "kCGDockWindowLevelKey (NSDockWindowLevel)";
   1.313 +    case kCGMainMenuWindowLevelKey:
   1.314 +      return "kCGMainMenuWindowLevelKey (NSMainMenuWindowLevel)";
   1.315 +    case kCGStatusWindowLevelKey:
   1.316 +      return "kCGStatusWindowLevelKey (NSStatusWindowLevel)";
   1.317 +    case kCGModalPanelWindowLevelKey:
   1.318 +      return "kCGModalPanelWindowLevelKey (NSModalPanelWindowLevel)";
   1.319 +    case kCGPopUpMenuWindowLevelKey:
   1.320 +      return "kCGPopUpMenuWindowLevelKey (NSPopUpMenuWindowLevel)";
   1.321 +    case kCGDraggingWindowLevelKey:
   1.322 +      return "kCGDraggingWindowLevelKey";
   1.323 +    case kCGScreenSaverWindowLevelKey:
   1.324 +      return "kCGScreenSaverWindowLevelKey (NSScreenSaverWindowLevel)";
   1.325 +    case kCGMaximumWindowLevelKey:
   1.326 +      return "kCGMaximumWindowLevelKey";
   1.327 +    case kCGOverlayWindowLevelKey:
   1.328 +      return "kCGOverlayWindowLevelKey";
   1.329 +    case kCGHelpWindowLevelKey:
   1.330 +      return "kCGHelpWindowLevelKey";
   1.331 +    case kCGUtilityWindowLevelKey:
   1.332 +      return "kCGUtilityWindowLevelKey";
   1.333 +    case kCGDesktopIconWindowLevelKey:
   1.334 +      return "kCGDesktopIconWindowLevelKey";
   1.335 +    case kCGCursorWindowLevelKey:
   1.336 +      return "kCGCursorWindowLevelKey";
   1.337 +    case kCGNumberOfWindowLevelKeys:
   1.338 +      return "kCGNumberOfWindowLevelKeys";
   1.339 +    default:
   1.340 +      return "unknown window level";
   1.341 +  }
   1.342 +}
   1.343 +
   1.344 +#endif // #ifdef PR_LOGGING
   1.345 +
   1.346 +static bool
   1.347 +IsControlChar(uint32_t aCharCode)
   1.348 +{
   1.349 +  return aCharCode < ' ' || aCharCode == 0x7F;
   1.350 +}
   1.351 +
   1.352 +static uint32_t gHandlerInstanceCount = 0;
   1.353 +static TISInputSourceWrapper gCurrentInputSource;
   1.354 +
   1.355 +static void
   1.356 +InitLogModule()
   1.357 +{
   1.358 +#ifdef PR_LOGGING
   1.359 +  // Clear() is always called when TISInputSourceWrappper is created.
   1.360 +  if (!gLog) {
   1.361 +    gLog = PR_NewLogModule("TextInputHandlerWidgets");
   1.362 +    TextInputHandler::DebugPrintAllKeyboardLayouts();
   1.363 +    IMEInputHandler::DebugPrintAllIMEModes();
   1.364 +  }
   1.365 +#endif
   1.366 +}
   1.367 +
   1.368 +static void
   1.369 +InitCurrentInputSource()
   1.370 +{
   1.371 +  if (gHandlerInstanceCount > 0 &&
   1.372 +      !gCurrentInputSource.IsInitializedByCurrentInputSource()) {
   1.373 +    gCurrentInputSource.InitByCurrentInputSource();
   1.374 +  }
   1.375 +}
   1.376 +
   1.377 +static void
   1.378 +FinalizeCurrentInputSource()
   1.379 +{
   1.380 +  gCurrentInputSource.Clear();
   1.381 +}
   1.382 +
   1.383 +
   1.384 +#pragma mark -
   1.385 +
   1.386 +
   1.387 +/******************************************************************************
   1.388 + *
   1.389 + *  TISInputSourceWrapper implementation
   1.390 + *
   1.391 + ******************************************************************************/
   1.392 +
   1.393 +// static
   1.394 +TISInputSourceWrapper&
   1.395 +TISInputSourceWrapper::CurrentInputSource()
   1.396 +{
   1.397 +  InitCurrentInputSource();
   1.398 +  return gCurrentInputSource;
   1.399 +}
   1.400 +
   1.401 +bool
   1.402 +TISInputSourceWrapper::TranslateToString(UInt32 aKeyCode, UInt32 aModifiers,
   1.403 +                                         UInt32 aKbType, nsAString &aStr)
   1.404 +{
   1.405 +  aStr.Truncate();
   1.406 +
   1.407 +  const UCKeyboardLayout* UCKey = GetUCKeyboardLayout();
   1.408 +
   1.409 +  PR_LOG(gLog, PR_LOG_ALWAYS,
   1.410 +    ("%p TISInputSourceWrapper::TranslateToString, aKeyCode=0x%X, "
   1.411 +     "aModifiers=0x%X, aKbType=0x%X UCKey=%p\n    "
   1.412 +     "Shift: %s, Ctrl: %s, Opt: %s, Cmd: %s, CapsLock: %s, NumLock: %s",
   1.413 +     this, aKeyCode, aModifiers, aKbType, UCKey,
   1.414 +     OnOrOff(aModifiers & shiftKey), OnOrOff(aModifiers & controlKey),
   1.415 +     OnOrOff(aModifiers & optionKey), OnOrOff(aModifiers & cmdKey),
   1.416 +     OnOrOff(aModifiers & alphaLock),
   1.417 +     OnOrOff(aModifiers & kEventKeyModifierNumLockMask)));
   1.418 +
   1.419 +  NS_ENSURE_TRUE(UCKey, false);
   1.420 +
   1.421 +  UInt32 deadKeyState = 0;
   1.422 +  UniCharCount len;
   1.423 +  UniChar chars[5];
   1.424 +  OSStatus err = ::UCKeyTranslate(UCKey, aKeyCode,
   1.425 +                                  kUCKeyActionDown, aModifiers >> 8,
   1.426 +                                  aKbType, kUCKeyTranslateNoDeadKeysMask,
   1.427 +                                  &deadKeyState, 5, &len, chars);
   1.428 +
   1.429 +  PR_LOG(gLog, PR_LOG_ALWAYS,
   1.430 +    ("%p TISInputSourceWrapper::TranslateToString, err=0x%X, len=%llu",
   1.431 +     this, err, len));
   1.432 +
   1.433 +  NS_ENSURE_TRUE(err == noErr, false);
   1.434 +  if (len == 0) {
   1.435 +    return true;
   1.436 +  }
   1.437 +  NS_ENSURE_TRUE(EnsureStringLength(aStr, len), false);
   1.438 +  NS_ASSERTION(sizeof(char16_t) == sizeof(UniChar),
   1.439 +               "size of char16_t and size of UniChar are different");
   1.440 +  memcpy(aStr.BeginWriting(), chars, len * sizeof(char16_t));
   1.441 +
   1.442 +  PR_LOG(gLog, PR_LOG_ALWAYS,
   1.443 +    ("%p TISInputSourceWrapper::TranslateToString, aStr=\"%s\"",
   1.444 +     this, NS_ConvertUTF16toUTF8(aStr).get()));
   1.445 +
   1.446 +  return true;
   1.447 +}
   1.448 +
   1.449 +uint32_t
   1.450 +TISInputSourceWrapper::TranslateToChar(UInt32 aKeyCode, UInt32 aModifiers,
   1.451 +                                       UInt32 aKbType)
   1.452 +{
   1.453 +  nsAutoString str;
   1.454 +  if (!TranslateToString(aKeyCode, aModifiers, aKbType, str) ||
   1.455 +      str.Length() != 1) {
   1.456 +    return 0;
   1.457 +  }
   1.458 +  return static_cast<uint32_t>(str.CharAt(0));
   1.459 +}
   1.460 +
   1.461 +void
   1.462 +TISInputSourceWrapper::InitByInputSourceID(const char* aID)
   1.463 +{
   1.464 +  Clear();
   1.465 +  if (!aID)
   1.466 +    return;
   1.467 +
   1.468 +  CFStringRef idstr = ::CFStringCreateWithCString(kCFAllocatorDefault, aID,
   1.469 +                                                  kCFStringEncodingASCII);
   1.470 +  InitByInputSourceID(idstr);
   1.471 +  ::CFRelease(idstr);
   1.472 +}
   1.473 +
   1.474 +void
   1.475 +TISInputSourceWrapper::InitByInputSourceID(const nsAFlatString &aID)
   1.476 +{
   1.477 +  Clear();
   1.478 +  if (aID.IsEmpty())
   1.479 +    return;
   1.480 +  CFStringRef idstr = ::CFStringCreateWithCharacters(kCFAllocatorDefault,
   1.481 +                                                     reinterpret_cast<const UniChar*>(aID.get()),
   1.482 +                                                     aID.Length());
   1.483 +  InitByInputSourceID(idstr);
   1.484 +  ::CFRelease(idstr);
   1.485 +}
   1.486 +
   1.487 +void
   1.488 +TISInputSourceWrapper::InitByInputSourceID(const CFStringRef aID)
   1.489 +{
   1.490 +  Clear();
   1.491 +  if (!aID)
   1.492 +    return;
   1.493 +  const void* keys[] = { kTISPropertyInputSourceID };
   1.494 +  const void* values[] = { aID };
   1.495 +  CFDictionaryRef filter =
   1.496 +  ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL);
   1.497 +  NS_ASSERTION(filter, "failed to create the filter");
   1.498 +  mInputSourceList = ::TISCreateInputSourceList(filter, true);
   1.499 +  ::CFRelease(filter);
   1.500 +  if (::CFArrayGetCount(mInputSourceList) > 0) {
   1.501 +    mInputSource = static_cast<TISInputSourceRef>(
   1.502 +      const_cast<void *>(::CFArrayGetValueAtIndex(mInputSourceList, 0)));
   1.503 +    if (IsKeyboardLayout()) {
   1.504 +      mKeyboardLayout = mInputSource;
   1.505 +    }
   1.506 +  }
   1.507 +}
   1.508 +
   1.509 +void
   1.510 +TISInputSourceWrapper::InitByLayoutID(SInt32 aLayoutID,
   1.511 +                                      bool aOverrideKeyboard)
   1.512 +{
   1.513 +  // NOTE: Doument new layout IDs in TextInputHandler.h when you add ones.
   1.514 +  switch (aLayoutID) {
   1.515 +    case 0:
   1.516 +      InitByInputSourceID("com.apple.keylayout.US");
   1.517 +      break;
   1.518 +    case 1:
   1.519 +      InitByInputSourceID("com.apple.keylayout.Greek");
   1.520 +      break;
   1.521 +    case 2:
   1.522 +      InitByInputSourceID("com.apple.keylayout.German");
   1.523 +      break;
   1.524 +    case 3:
   1.525 +      InitByInputSourceID("com.apple.keylayout.Swedish-Pro");
   1.526 +      break;
   1.527 +    case 4:
   1.528 +      InitByInputSourceID("com.apple.keylayout.DVORAK-QWERTYCMD");
   1.529 +      break;
   1.530 +    case 5:
   1.531 +      InitByInputSourceID("com.apple.keylayout.Thai");
   1.532 +      break;
   1.533 +    case 6:
   1.534 +      InitByInputSourceID("com.apple.keylayout.Arabic");
   1.535 +      break;
   1.536 +    case 7:
   1.537 +      InitByInputSourceID("com.apple.keylayout.French");
   1.538 +      break;
   1.539 +    case 8:
   1.540 +      InitByInputSourceID("com.apple.keylayout.Hebrew");
   1.541 +      break;
   1.542 +    case 9:
   1.543 +      InitByInputSourceID("com.apple.keylayout.Lithuanian");
   1.544 +      break;
   1.545 +    case 10:
   1.546 +      InitByInputSourceID("com.apple.keylayout.Norwegian");
   1.547 +      break;
   1.548 +    case 11:
   1.549 +      InitByInputSourceID("com.apple.keylayout.Spanish");
   1.550 +      break;
   1.551 +    default:
   1.552 +      Clear();
   1.553 +      break;
   1.554 +  }
   1.555 +  mOverrideKeyboard = aOverrideKeyboard;
   1.556 +}
   1.557 +
   1.558 +void
   1.559 +TISInputSourceWrapper::InitByCurrentInputSource()
   1.560 +{
   1.561 +  Clear();
   1.562 +  mInputSource = ::TISCopyCurrentKeyboardInputSource();
   1.563 +  mKeyboardLayout = ::TISCopyInputMethodKeyboardLayoutOverride();
   1.564 +  if (!mKeyboardLayout) {
   1.565 +    mKeyboardLayout = ::TISCopyCurrentKeyboardLayoutInputSource();
   1.566 +  }
   1.567 +  // If this causes composition, the current keyboard layout may input non-ASCII
   1.568 +  // characters such as Japanese Kana characters or Hangul characters.
   1.569 +  // However, we need to set ASCII characters to DOM key events for consistency
   1.570 +  // with other platforms.
   1.571 +  if (IsOpenedIMEMode()) {
   1.572 +    TISInputSourceWrapper tis(mKeyboardLayout);
   1.573 +    if (!tis.IsASCIICapable()) {
   1.574 +      mKeyboardLayout =
   1.575 +        ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
   1.576 +    }
   1.577 +  }
   1.578 +}
   1.579 +
   1.580 +void
   1.581 +TISInputSourceWrapper::InitByCurrentKeyboardLayout()
   1.582 +{
   1.583 +  Clear();
   1.584 +  mInputSource = ::TISCopyCurrentKeyboardLayoutInputSource();
   1.585 +  mKeyboardLayout = mInputSource;
   1.586 +}
   1.587 +
   1.588 +void
   1.589 +TISInputSourceWrapper::InitByCurrentASCIICapableInputSource()
   1.590 +{
   1.591 +  Clear();
   1.592 +  mInputSource = ::TISCopyCurrentASCIICapableKeyboardInputSource();
   1.593 +  mKeyboardLayout = ::TISCopyInputMethodKeyboardLayoutOverride();
   1.594 +  if (mKeyboardLayout) {
   1.595 +    TISInputSourceWrapper tis(mKeyboardLayout);
   1.596 +    if (!tis.IsASCIICapable()) {
   1.597 +      mKeyboardLayout = nullptr;
   1.598 +    }
   1.599 +  }
   1.600 +  if (!mKeyboardLayout) {
   1.601 +    mKeyboardLayout =
   1.602 +      ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
   1.603 +  }
   1.604 +}
   1.605 +
   1.606 +void
   1.607 +TISInputSourceWrapper::InitByCurrentASCIICapableKeyboardLayout()
   1.608 +{
   1.609 +  Clear();
   1.610 +  mInputSource = ::TISCopyCurrentASCIICapableKeyboardLayoutInputSource();
   1.611 +  mKeyboardLayout = mInputSource;
   1.612 +}
   1.613 +
   1.614 +void
   1.615 +TISInputSourceWrapper::InitByCurrentInputMethodKeyboardLayoutOverride()
   1.616 +{
   1.617 +  Clear();
   1.618 +  mInputSource = ::TISCopyInputMethodKeyboardLayoutOverride();
   1.619 +  mKeyboardLayout = mInputSource;
   1.620 +}
   1.621 +
   1.622 +void
   1.623 +TISInputSourceWrapper::InitByTISInputSourceRef(TISInputSourceRef aInputSource)
   1.624 +{
   1.625 +  Clear();
   1.626 +  mInputSource = aInputSource;
   1.627 +  if (IsKeyboardLayout()) {
   1.628 +    mKeyboardLayout = mInputSource;
   1.629 +  }
   1.630 +}
   1.631 +
   1.632 +void
   1.633 +TISInputSourceWrapper::InitByLanguage(CFStringRef aLanguage)
   1.634 +{
   1.635 +  Clear();
   1.636 +  mInputSource = ::TISCopyInputSourceForLanguage(aLanguage);
   1.637 +  if (IsKeyboardLayout()) {
   1.638 +    mKeyboardLayout = mInputSource;
   1.639 +  }
   1.640 +}
   1.641 +
   1.642 +const UCKeyboardLayout*
   1.643 +TISInputSourceWrapper::GetUCKeyboardLayout()
   1.644 +{
   1.645 +  NS_ENSURE_TRUE(mKeyboardLayout, nullptr);
   1.646 +  if (mUCKeyboardLayout) {
   1.647 +    return mUCKeyboardLayout;
   1.648 +  }
   1.649 +  CFDataRef uchr = static_cast<CFDataRef>(
   1.650 +    ::TISGetInputSourceProperty(mKeyboardLayout,
   1.651 +                                kTISPropertyUnicodeKeyLayoutData));
   1.652 +
   1.653 +  // We should be always able to get the layout here.
   1.654 +  NS_ENSURE_TRUE(uchr, nullptr);
   1.655 +  mUCKeyboardLayout =
   1.656 +    reinterpret_cast<const UCKeyboardLayout*>(CFDataGetBytePtr(uchr));
   1.657 +  return mUCKeyboardLayout;
   1.658 +}
   1.659 +
   1.660 +bool
   1.661 +TISInputSourceWrapper::GetBoolProperty(const CFStringRef aKey)
   1.662 +{
   1.663 +  CFBooleanRef ret = static_cast<CFBooleanRef>(
   1.664 +    ::TISGetInputSourceProperty(mInputSource, aKey));
   1.665 +  return ::CFBooleanGetValue(ret);
   1.666 +}
   1.667 +
   1.668 +bool
   1.669 +TISInputSourceWrapper::GetStringProperty(const CFStringRef aKey,
   1.670 +                                         CFStringRef &aStr)
   1.671 +{
   1.672 +  aStr = static_cast<CFStringRef>(
   1.673 +    ::TISGetInputSourceProperty(mInputSource, aKey));
   1.674 +  return aStr != nullptr;
   1.675 +}
   1.676 +
   1.677 +bool
   1.678 +TISInputSourceWrapper::GetStringProperty(const CFStringRef aKey,
   1.679 +                                         nsAString &aStr)
   1.680 +{
   1.681 +  CFStringRef str;
   1.682 +  GetStringProperty(aKey, str);
   1.683 +  nsCocoaUtils::GetStringForNSString((const NSString*)str, aStr);
   1.684 +  return !aStr.IsEmpty();
   1.685 +}
   1.686 +
   1.687 +bool
   1.688 +TISInputSourceWrapper::IsOpenedIMEMode()
   1.689 +{
   1.690 +  NS_ENSURE_TRUE(mInputSource, false);
   1.691 +  if (!IsIMEMode())
   1.692 +    return false;
   1.693 +  return !IsASCIICapable();
   1.694 +}
   1.695 +
   1.696 +bool
   1.697 +TISInputSourceWrapper::IsIMEMode()
   1.698 +{
   1.699 +  NS_ENSURE_TRUE(mInputSource, false);
   1.700 +  CFStringRef str;
   1.701 +  GetInputSourceType(str);
   1.702 +  NS_ENSURE_TRUE(str, false);
   1.703 +  return ::CFStringCompare(kTISTypeKeyboardInputMode,
   1.704 +                           str, 0) == kCFCompareEqualTo;
   1.705 +}
   1.706 +
   1.707 +bool
   1.708 +TISInputSourceWrapper::IsKeyboardLayout()
   1.709 +{
   1.710 +  NS_ENSURE_TRUE(mInputSource, false);
   1.711 +  CFStringRef str;
   1.712 +  GetInputSourceType(str);
   1.713 +  NS_ENSURE_TRUE(str, false);
   1.714 +  return ::CFStringCompare(kTISTypeKeyboardLayout,
   1.715 +                           str, 0) == kCFCompareEqualTo;
   1.716 +}
   1.717 +
   1.718 +bool
   1.719 +TISInputSourceWrapper::GetLanguageList(CFArrayRef &aLanguageList)
   1.720 +{
   1.721 +  NS_ENSURE_TRUE(mInputSource, false);
   1.722 +  aLanguageList = static_cast<CFArrayRef>(
   1.723 +    ::TISGetInputSourceProperty(mInputSource,
   1.724 +                                kTISPropertyInputSourceLanguages));
   1.725 +  return aLanguageList != nullptr;
   1.726 +}
   1.727 +
   1.728 +bool
   1.729 +TISInputSourceWrapper::GetPrimaryLanguage(CFStringRef &aPrimaryLanguage)
   1.730 +{
   1.731 +  NS_ENSURE_TRUE(mInputSource, false);
   1.732 +  CFArrayRef langList;
   1.733 +  NS_ENSURE_TRUE(GetLanguageList(langList), false);
   1.734 +  if (::CFArrayGetCount(langList) == 0)
   1.735 +    return false;
   1.736 +  aPrimaryLanguage =
   1.737 +    static_cast<CFStringRef>(::CFArrayGetValueAtIndex(langList, 0));
   1.738 +  return aPrimaryLanguage != nullptr;
   1.739 +}
   1.740 +
   1.741 +bool
   1.742 +TISInputSourceWrapper::GetPrimaryLanguage(nsAString &aPrimaryLanguage)
   1.743 +{
   1.744 +  NS_ENSURE_TRUE(mInputSource, false);
   1.745 +  CFStringRef primaryLanguage;
   1.746 +  NS_ENSURE_TRUE(GetPrimaryLanguage(primaryLanguage), false);
   1.747 +  nsCocoaUtils::GetStringForNSString((const NSString*)primaryLanguage,
   1.748 +                                     aPrimaryLanguage);
   1.749 +  return !aPrimaryLanguage.IsEmpty();
   1.750 +}
   1.751 +
   1.752 +bool
   1.753 +TISInputSourceWrapper::IsForRTLLanguage()
   1.754 +{
   1.755 +  if (mIsRTL < 0) {
   1.756 +    // Get the input character of the 'A' key of ANSI keyboard layout.
   1.757 +    nsAutoString str;
   1.758 +    bool ret = TranslateToString(kVK_ANSI_A, 0, eKbdType_ANSI, str);
   1.759 +    NS_ENSURE_TRUE(ret, ret);
   1.760 +    char16_t ch = str.IsEmpty() ? char16_t(0) : str.CharAt(0);
   1.761 +    mIsRTL = UCS2_CHAR_IS_BIDI(ch) || ch == 0xD802 || ch == 0xD803;
   1.762 +  }
   1.763 +  return mIsRTL != 0;
   1.764 +}
   1.765 +
   1.766 +bool
   1.767 +TISInputSourceWrapper::IsInitializedByCurrentInputSource()
   1.768 +{
   1.769 +  return mInputSource == ::TISCopyCurrentKeyboardInputSource();
   1.770 +}
   1.771 +
   1.772 +void
   1.773 +TISInputSourceWrapper::Select()
   1.774 +{
   1.775 +  if (!mInputSource)
   1.776 +    return;
   1.777 +  ::TISSelectInputSource(mInputSource);
   1.778 +}
   1.779 +
   1.780 +void
   1.781 +TISInputSourceWrapper::Clear()
   1.782 +{
   1.783 +  // Clear() is always called when TISInputSourceWrappper is created.
   1.784 +  InitLogModule();
   1.785 +
   1.786 +  if (mInputSourceList) {
   1.787 +    ::CFRelease(mInputSourceList);
   1.788 +  }
   1.789 +  mInputSourceList = nullptr;
   1.790 +  mInputSource = nullptr;
   1.791 +  mKeyboardLayout = nullptr;
   1.792 +  mIsRTL = -1;
   1.793 +  mUCKeyboardLayout = nullptr;
   1.794 +  mOverrideKeyboard = false;
   1.795 +}
   1.796 +
   1.797 +void
   1.798 +TISInputSourceWrapper::InitKeyEvent(NSEvent *aNativeKeyEvent,
   1.799 +                                    WidgetKeyboardEvent& aKeyEvent,
   1.800 +                                    const nsAString *aInsertString)
   1.801 +{
   1.802 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   1.803 +
   1.804 +  PR_LOG(gLog, PR_LOG_ALWAYS,
   1.805 +    ("%p TISInputSourceWrapper::InitKeyEvent, aNativeKeyEvent=%p, "
   1.806 +     "aKeyEvent.message=%s, aInsertString=%p, IsOpenedIMEMode()=%s",
   1.807 +     this, aNativeKeyEvent, GetGeckoKeyEventType(aKeyEvent), aInsertString,
   1.808 +     TrueOrFalse(IsOpenedIMEMode())));
   1.809 +
   1.810 +  NS_ENSURE_TRUE(aNativeKeyEvent, );
   1.811 +
   1.812 +  nsCocoaUtils::InitInputEvent(aKeyEvent, aNativeKeyEvent);
   1.813 +
   1.814 +  // This is used only while dispatching the event (which is a synchronous
   1.815 +  // call), so there is no need to retain and release this data.
   1.816 +  aKeyEvent.mNativeKeyEvent = aNativeKeyEvent;
   1.817 +
   1.818 +  aKeyEvent.refPoint = LayoutDeviceIntPoint(0, 0);
   1.819 +
   1.820 +  // If a keyboard layout override is set, we also need to force the keyboard
   1.821 +  // type to something ANSI to avoid test failures on machines with JIS
   1.822 +  // keyboards (since the pair of keyboard layout and physical keyboard type
   1.823 +  // form the actual key layout).  This assumes that the test setting the
   1.824 +  // override was written assuming an ANSI keyboard.
   1.825 +  UInt32 kbType = mOverrideKeyboard ? eKbdType_ANSI : ::LMGetKbdType();
   1.826 +
   1.827 +  UInt32 nativeKeyCode = [aNativeKeyEvent keyCode];
   1.828 +
   1.829 +  bool isPrintableKey = !TextInputHandler::IsSpecialGeckoKey(nativeKeyCode);
   1.830 +  if (isPrintableKey &&
   1.831 +      [aNativeKeyEvent type] != NSKeyDown &&
   1.832 +      [aNativeKeyEvent type] != NSKeyUp) {
   1.833 +    NS_WARNING("Why the printable key doesn't cause NSKeyDown or NSKeyUp?");
   1.834 +    isPrintableKey = false;
   1.835 +  }
   1.836 +
   1.837 +  // Decide what string will be input.
   1.838 +  nsAutoString insertString;
   1.839 +  if (aInsertString) {
   1.840 +    // If the caller expects that the aInsertString will be input, we shouldn't
   1.841 +    // change it.
   1.842 +    insertString = *aInsertString;
   1.843 +  } else if (isPrintableKey) {
   1.844 +    // If IME is open, [aNativeKeyEvent characters] may be a character
   1.845 +    // which will be appended to the composition string.  However, especially,
   1.846 +    // while IME is disabled, most users and developers expect the key event
   1.847 +    // works as IME closed.  So, we should compute the insertString with
   1.848 +    // the ASCII capable keyboard layout.
   1.849 +    // NOTE: Such keyboard layouts typically change the layout to its ASCII
   1.850 +    //       capable layout when Command key is pressed.  And we don't worry
   1.851 +    //       when Control key is pressed too because it causes inputting
   1.852 +    //       control characters.
   1.853 +    if (!aKeyEvent.IsMeta() && !aKeyEvent.IsControl() && IsOpenedIMEMode()) {
   1.854 +      UInt32 state =
   1.855 +        nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]);
   1.856 +      uint32_t ch = TranslateToChar(nativeKeyCode, state, kbType);
   1.857 +      if (ch) {
   1.858 +        insertString = ch;
   1.859 +      }
   1.860 +    } else {
   1.861 +      // If the caller isn't sure what string will be input, let's use
   1.862 +      // characters of NSEvent.
   1.863 +      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters],
   1.864 +                                         insertString);
   1.865 +    }
   1.866 +
   1.867 +    // If control key is pressed and the eventChars is a non-printable control
   1.868 +    // character, we should convert it to ASCII alphabet.
   1.869 +    if (aKeyEvent.IsControl() &&
   1.870 +        !insertString.IsEmpty() && insertString[0] <= char16_t(26)) {
   1.871 +      insertString = (aKeyEvent.IsShift() ^ aKeyEvent.IsCapsLocked()) ?
   1.872 +        static_cast<char16_t>(insertString[0] + ('A' - 1)) :
   1.873 +        static_cast<char16_t>(insertString[0] + ('a' - 1));
   1.874 +    }
   1.875 +    // If Meta key is pressed, it may cause to switch the keyboard layout like
   1.876 +    // Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
   1.877 +    else if (aKeyEvent.IsMeta() &&
   1.878 +             !(aKeyEvent.IsControl() || aKeyEvent.IsAlt())) {
   1.879 +      UInt32 numLockState =
   1.880 +        aKeyEvent.IsNumLocked() ? kEventKeyModifierNumLockMask : 0;
   1.881 +      UInt32 capsLockState = aKeyEvent.IsCapsLocked() ? alphaLock : 0;
   1.882 +      UInt32 shiftState = aKeyEvent.IsShift() ? shiftKey : 0;
   1.883 +      uint32_t uncmdedChar =
   1.884 +        TranslateToChar(nativeKeyCode, numLockState, kbType);
   1.885 +      uint32_t cmdedChar =
   1.886 +        TranslateToChar(nativeKeyCode, cmdKey | numLockState, kbType);
   1.887 +      // If we can make a good guess at the characters that the user would
   1.888 +      // expect this key combination to produce (with and without Shift) then
   1.889 +      // use those characters.  This also corrects for CapsLock.
   1.890 +      uint32_t ch = 0;
   1.891 +      if (uncmdedChar == cmdedChar) {
   1.892 +        // The characters produced with Command seem similar to those without
   1.893 +        // Command.
   1.894 +        ch = TranslateToChar(nativeKeyCode,
   1.895 +                             shiftState | capsLockState | numLockState, kbType);
   1.896 +      } else {
   1.897 +        TISInputSourceWrapper USLayout("com.apple.keylayout.US");
   1.898 +        uint32_t uncmdedUSChar =
   1.899 +          USLayout.TranslateToChar(nativeKeyCode, numLockState, kbType);
   1.900 +        // If it looks like characters from US keyboard layout when Command key
   1.901 +        // is pressed, we should compute a character in the layout.
   1.902 +        if (uncmdedUSChar == cmdedChar) {
   1.903 +          ch = USLayout.TranslateToChar(nativeKeyCode,
   1.904 +                          shiftState | capsLockState | numLockState, kbType);
   1.905 +        }
   1.906 +      }
   1.907 +
   1.908 +      // If there is a more preferred character for the commanded key event,
   1.909 +      // we should use it.
   1.910 +      if (ch) {
   1.911 +        insertString = ch;
   1.912 +      }
   1.913 +    }
   1.914 +  }
   1.915 +
   1.916 +  // Remove control characters which shouldn't be inputted on editor.
   1.917 +  // XXX Currently, we don't find any cases inserting control characters with
   1.918 +  //     printable character.  So, just checking first character is enough.
   1.919 +  if (!insertString.IsEmpty() && IsControlChar(insertString[0])) {
   1.920 +    insertString.Truncate();
   1.921 +  }
   1.922 +
   1.923 +  aKeyEvent.keyCode =
   1.924 +    ComputeGeckoKeyCode(nativeKeyCode, kbType, aKeyEvent.IsMeta());
   1.925 +
   1.926 +  switch (nativeKeyCode) {
   1.927 +    case kVK_Command:
   1.928 +    case kVK_Shift:
   1.929 +    case kVK_Option:
   1.930 +    case kVK_Control:
   1.931 +      aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_LEFT;
   1.932 +      break;
   1.933 +
   1.934 +    case kVK_RightCommand:
   1.935 +    case kVK_RightShift:
   1.936 +    case kVK_RightOption:
   1.937 +    case kVK_RightControl:
   1.938 +      aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_RIGHT;
   1.939 +      break;
   1.940 +
   1.941 +    case kVK_ANSI_Keypad0:
   1.942 +    case kVK_ANSI_Keypad1:
   1.943 +    case kVK_ANSI_Keypad2:
   1.944 +    case kVK_ANSI_Keypad3:
   1.945 +    case kVK_ANSI_Keypad4:
   1.946 +    case kVK_ANSI_Keypad5:
   1.947 +    case kVK_ANSI_Keypad6:
   1.948 +    case kVK_ANSI_Keypad7:
   1.949 +    case kVK_ANSI_Keypad8:
   1.950 +    case kVK_ANSI_Keypad9:
   1.951 +    case kVK_ANSI_KeypadMultiply:
   1.952 +    case kVK_ANSI_KeypadPlus:
   1.953 +    case kVK_ANSI_KeypadMinus:
   1.954 +    case kVK_ANSI_KeypadDecimal:
   1.955 +    case kVK_ANSI_KeypadDivide:
   1.956 +    case kVK_ANSI_KeypadEquals:
   1.957 +    case kVK_ANSI_KeypadEnter:
   1.958 +    case kVK_JIS_KeypadComma:
   1.959 +    case kVK_Powerbook_KeypadEnter:
   1.960 +      aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_NUMPAD;
   1.961 +      break;
   1.962 +
   1.963 +    default:
   1.964 +      aKeyEvent.location = nsIDOMKeyEvent::DOM_KEY_LOCATION_STANDARD;
   1.965 +      break;
   1.966 +  }
   1.967 +
   1.968 +  aKeyEvent.mIsRepeat =
   1.969 +    ([aNativeKeyEvent type] == NSKeyDown) ? [aNativeKeyEvent isARepeat] : false;
   1.970 +
   1.971 +  PR_LOG(gLog, PR_LOG_ALWAYS,
   1.972 +    ("%p TISInputSourceWrapper::InitKeyEvent, "
   1.973 +     "shift=%s, ctrl=%s, alt=%s, meta=%s",
   1.974 +     this, OnOrOff(aKeyEvent.IsShift()), OnOrOff(aKeyEvent.IsControl()),
   1.975 +     OnOrOff(aKeyEvent.IsAlt()), OnOrOff(aKeyEvent.IsMeta())));
   1.976 +
   1.977 +  if (aKeyEvent.message == NS_KEY_PRESS &&
   1.978 +      (isPrintableKey || !insertString.IsEmpty())) {
   1.979 +    InitKeyPressEvent(aNativeKeyEvent,
   1.980 +                      insertString.IsEmpty() ? 0 : insertString[0],
   1.981 +                      aKeyEvent, kbType);
   1.982 +    MOZ_ASSERT(!aKeyEvent.charCode || !IsControlChar(aKeyEvent.charCode),
   1.983 +               "charCode must not be a control character");
   1.984 +  } else {
   1.985 +    aKeyEvent.charCode = 0;
   1.986 +    aKeyEvent.isChar = false; // XXX not used in XP level
   1.987 +
   1.988 +    PR_LOG(gLog, PR_LOG_ALWAYS,
   1.989 +      ("%p TISInputSourceWrapper::InitKeyEvent, keyCode=0x%X charCode=0x0",
   1.990 +       this, aKeyEvent.keyCode));
   1.991 +  }
   1.992 +
   1.993 +  if (isPrintableKey) {
   1.994 +    aKeyEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
   1.995 +    // If insertText calls this method, let's use the string.
   1.996 +    if (aInsertString && !aInsertString->IsEmpty() &&
   1.997 +        !IsControlChar((*aInsertString)[0])) {
   1.998 +      aKeyEvent.mKeyValue = *aInsertString;
   1.999 +    }
  1.1000 +    // If meta key is pressed, the printable key layout may be switched from
  1.1001 +    // non-ASCII capable layout to ASCII capable, or from Dvorak to QWERTY.
  1.1002 +    // KeyboardEvent.key value should be the switched layout's character.
  1.1003 +    else if (aKeyEvent.IsMeta()) {
  1.1004 +      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters],
  1.1005 +                                         aKeyEvent.mKeyValue);
  1.1006 +    }
  1.1007 +    // If control key is pressed, some keys may produce printable character via
  1.1008 +    // [aNativeKeyEvent characters].  Otherwise, translate input character of
  1.1009 +    // the key without control key.
  1.1010 +    else if (aKeyEvent.IsControl()) {
  1.1011 +      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters],
  1.1012 +                                         aKeyEvent.mKeyValue);
  1.1013 +      if (aKeyEvent.mKeyValue.IsEmpty() ||
  1.1014 +          IsControlChar(aKeyEvent.mKeyValue[0])) {
  1.1015 +        NSUInteger cocoaState =
  1.1016 +          [aNativeKeyEvent modifierFlags] & ~NSControlKeyMask;
  1.1017 +        UInt32 carbonState = nsCocoaUtils::ConvertToCarbonModifier(cocoaState);
  1.1018 +        aKeyEvent.mKeyValue =
  1.1019 +          TranslateToChar(nativeKeyCode, carbonState, kbType);
  1.1020 +      }
  1.1021 +    }
  1.1022 +    // Otherwise, KeyboardEvent.key expose
  1.1023 +    // [aNativeKeyEvent characters] value.  However, if IME is open and the
  1.1024 +    // keyboard layout isn't ASCII capable, exposing the non-ASCII character
  1.1025 +    // doesn't match with other platform's behavior.  For the compatibility
  1.1026 +    // with other platform's Gecko, we need to set a translated character.
  1.1027 +    else if (IsOpenedIMEMode()) {
  1.1028 +      UInt32 state =
  1.1029 +        nsCocoaUtils::ConvertToCarbonModifier([aNativeKeyEvent modifierFlags]);
  1.1030 +      aKeyEvent.mKeyValue = TranslateToChar(nativeKeyCode, state, kbType);
  1.1031 +    } else {
  1.1032 +      nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters],
  1.1033 +                                         aKeyEvent.mKeyValue);
  1.1034 +    }
  1.1035 +
  1.1036 +    // Last resort.  If .key value becomes empty string, we should use
  1.1037 +    // charactersIgnoringModifiers, if it's available.
  1.1038 +    if (aKeyEvent.mKeyValue.IsEmpty() ||
  1.1039 +        IsControlChar(aKeyEvent.mKeyValue[0])) {
  1.1040 +      nsCocoaUtils::GetStringForNSString(
  1.1041 +        [aNativeKeyEvent charactersIgnoringModifiers], aKeyEvent.mKeyValue);
  1.1042 +      // But don't expose it if it's a control character.
  1.1043 +      if (!aKeyEvent.mKeyValue.IsEmpty() &&
  1.1044 +          IsControlChar(aKeyEvent.mKeyValue[0])) {
  1.1045 +        aKeyEvent.mKeyValue.Truncate();
  1.1046 +      }
  1.1047 +    }
  1.1048 +  } else {
  1.1049 +    // Compute the key for non-printable keys and some special printable keys.
  1.1050 +    aKeyEvent.mKeyNameIndex = ComputeGeckoKeyNameIndex(nativeKeyCode);
  1.1051 +  }
  1.1052 +
  1.1053 +  NS_OBJC_END_TRY_ABORT_BLOCK
  1.1054 +}
  1.1055 +
  1.1056 +void
  1.1057 +TISInputSourceWrapper::InitKeyPressEvent(NSEvent *aNativeKeyEvent,
  1.1058 +                                         char16_t aInsertChar,
  1.1059 +                                         WidgetKeyboardEvent& aKeyEvent,
  1.1060 +                                         UInt32 aKbType)
  1.1061 +{
  1.1062 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1063 +
  1.1064 +  NS_ASSERTION(aKeyEvent.message == NS_KEY_PRESS,
  1.1065 +               "aKeyEvent must be NS_KEY_PRESS event");
  1.1066 +
  1.1067 +#ifdef PR_LOGGING
  1.1068 +  if (PR_LOG_TEST(gLog, PR_LOG_ALWAYS)) {
  1.1069 +    nsAutoString chars;
  1.1070 +    nsCocoaUtils::GetStringForNSString([aNativeKeyEvent characters], chars);
  1.1071 +    NS_ConvertUTF16toUTF8 utf8Chars(chars);
  1.1072 +    char16_t expectedChar = static_cast<char16_t>(aInsertChar);
  1.1073 +    NS_ConvertUTF16toUTF8 utf8ExpectedChar(&expectedChar, 1);
  1.1074 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1075 +      ("%p TISInputSourceWrapper::InitKeyPressEvent, aNativeKeyEvent=%p, "
  1.1076 +       "[aNativeKeyEvent characters]=\"%s\", aInsertChar=0x%X(%s), "
  1.1077 +       "aKeyEvent.message=%s, aKbType=0x%X, IsOpenedIMEMode()=%s",
  1.1078 +       this, aNativeKeyEvent, utf8Chars.get(), aInsertChar,
  1.1079 +       utf8ExpectedChar.get(), GetGeckoKeyEventType(aKeyEvent), aKbType,
  1.1080 +       TrueOrFalse(IsOpenedIMEMode())));
  1.1081 +  }
  1.1082 +#endif // #ifdef PR_LOGGING
  1.1083 +
  1.1084 +  aKeyEvent.isChar = true; // this is not a special key  XXX not used in XP
  1.1085 +  aKeyEvent.charCode = aInsertChar;
  1.1086 +  if (aKeyEvent.charCode != 0) {
  1.1087 +    aKeyEvent.keyCode = 0;
  1.1088 +  }
  1.1089 +
  1.1090 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1091 +    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
  1.1092 +     "aKeyEvent.keyCode=0x%X, aKeyEvent.charCode=0x%X",
  1.1093 +     this, aKeyEvent.keyCode, aKeyEvent.charCode));
  1.1094 +
  1.1095 +  if (!aKeyEvent.IsControl() && !aKeyEvent.IsMeta() && !aKeyEvent.IsAlt()) {
  1.1096 +    return;
  1.1097 +  }
  1.1098 +
  1.1099 +  TISInputSourceWrapper USLayout("com.apple.keylayout.US");
  1.1100 +  bool isRomanKeyboardLayout = IsASCIICapable();
  1.1101 +
  1.1102 +  UInt32 key = [aNativeKeyEvent keyCode];
  1.1103 +
  1.1104 +  // Caps lock and num lock modifier state:
  1.1105 +  UInt32 lockState = 0;
  1.1106 +  if ([aNativeKeyEvent modifierFlags] & NSAlphaShiftKeyMask) {
  1.1107 +    lockState |= alphaLock;
  1.1108 +  }
  1.1109 +  if ([aNativeKeyEvent modifierFlags] & NSNumericPadKeyMask) {
  1.1110 +    lockState |= kEventKeyModifierNumLockMask;
  1.1111 +  }
  1.1112 +
  1.1113 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1114 +    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
  1.1115 +     "isRomanKeyboardLayout=%s, key=0x%X",
  1.1116 +     this, TrueOrFalse(isRomanKeyboardLayout), aKbType, key));
  1.1117 +
  1.1118 +  nsString str;
  1.1119 +
  1.1120 +  // normal chars
  1.1121 +  uint32_t unshiftedChar = TranslateToChar(key, lockState, aKbType);
  1.1122 +  UInt32 shiftLockMod = shiftKey | lockState;
  1.1123 +  uint32_t shiftedChar = TranslateToChar(key, shiftLockMod, aKbType);
  1.1124 +
  1.1125 +  // characters generated with Cmd key
  1.1126 +  // XXX we should remove CapsLock state, which changes characters from
  1.1127 +  //     Latin to Cyrillic with Russian layout on 10.4 only when Cmd key
  1.1128 +  //     is pressed.
  1.1129 +  UInt32 numState = (lockState & ~alphaLock); // only num lock state
  1.1130 +  uint32_t uncmdedChar = TranslateToChar(key, numState, aKbType);
  1.1131 +  UInt32 shiftNumMod = numState | shiftKey;
  1.1132 +  uint32_t uncmdedShiftChar = TranslateToChar(key, shiftNumMod, aKbType);
  1.1133 +  uint32_t uncmdedUSChar = USLayout.TranslateToChar(key, numState, aKbType);
  1.1134 +  UInt32 cmdNumMod = cmdKey | numState;
  1.1135 +  uint32_t cmdedChar = TranslateToChar(key, cmdNumMod, aKbType);
  1.1136 +  UInt32 cmdShiftNumMod = shiftKey | cmdNumMod;
  1.1137 +  uint32_t cmdedShiftChar = TranslateToChar(key, cmdShiftNumMod, aKbType);
  1.1138 +
  1.1139 +  // Is the keyboard layout changed by Cmd key?
  1.1140 +  // E.g., Arabic, Russian, Hebrew, Greek and Dvorak-QWERTY.
  1.1141 +  bool isCmdSwitchLayout = uncmdedChar != cmdedChar;
  1.1142 +  // Is the keyboard layout for Latin, but Cmd key switches the layout?
  1.1143 +  // I.e., Dvorak-QWERTY
  1.1144 +  bool isDvorakQWERTY = isCmdSwitchLayout && isRomanKeyboardLayout;
  1.1145 +
  1.1146 +  // If the current keyboard is not Dvorak-QWERTY or Cmd is not pressed,
  1.1147 +  // we should append unshiftedChar and shiftedChar for handling the
  1.1148 +  // normal characters.  These are the characters that the user is most
  1.1149 +  // likely to associate with this key.
  1.1150 +  if ((unshiftedChar || shiftedChar) &&
  1.1151 +      (!aKeyEvent.IsMeta() || !isDvorakQWERTY)) {
  1.1152 +    AlternativeCharCode altCharCodes(unshiftedChar, shiftedChar);
  1.1153 +    aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
  1.1154 +  }
  1.1155 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1156 +    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
  1.1157 +     "aKeyEvent.isMeta=%s, isDvorakQWERTY=%s, "
  1.1158 +     "unshiftedChar=U+%X, shiftedChar=U+%X",
  1.1159 +     this, OnOrOff(aKeyEvent.IsMeta()), TrueOrFalse(isDvorakQWERTY),
  1.1160 +     unshiftedChar, shiftedChar));
  1.1161 +
  1.1162 +  // Most keyboard layouts provide the same characters in the NSEvents
  1.1163 +  // with Command+Shift as with Command.  However, with Command+Shift we
  1.1164 +  // want the character on the second level.  e.g. With a US QWERTY
  1.1165 +  // layout, we want "?" when the "/","?" key is pressed with
  1.1166 +  // Command+Shift.
  1.1167 +
  1.1168 +  // On a German layout, the OS gives us '/' with Cmd+Shift+SS(eszett)
  1.1169 +  // even though Cmd+SS is 'SS' and Shift+'SS' is '?'.  This '/' seems
  1.1170 +  // like a hack to make the Cmd+"?" event look the same as the Cmd+"?"
  1.1171 +  // event on a US keyboard.  The user thinks they are typing Cmd+"?", so
  1.1172 +  // we'll prefer the "?" character, replacing charCode with shiftedChar
  1.1173 +  // when Shift is pressed.  However, in case there is a layout where the
  1.1174 +  // character unique to Cmd+Shift is the character that the user expects,
  1.1175 +  // we'll send it as an alternative char.
  1.1176 +  bool hasCmdShiftOnlyChar =
  1.1177 +    cmdedChar != cmdedShiftChar && uncmdedShiftChar != cmdedShiftChar;
  1.1178 +  uint32_t originalCmdedShiftChar = cmdedShiftChar;
  1.1179 +
  1.1180 +  // If we can make a good guess at the characters that the user would
  1.1181 +  // expect this key combination to produce (with and without Shift) then
  1.1182 +  // use those characters.  This also corrects for CapsLock, which was
  1.1183 +  // ignored above.
  1.1184 +  if (!isCmdSwitchLayout) {
  1.1185 +    // The characters produced with Command seem similar to those without
  1.1186 +    // Command.
  1.1187 +    if (unshiftedChar) {
  1.1188 +      cmdedChar = unshiftedChar;
  1.1189 +    }
  1.1190 +    if (shiftedChar) {
  1.1191 +      cmdedShiftChar = shiftedChar;
  1.1192 +    }
  1.1193 +  } else if (uncmdedUSChar == cmdedChar) {
  1.1194 +    // It looks like characters from a US layout are provided when Command
  1.1195 +    // is down.
  1.1196 +    uint32_t ch = USLayout.TranslateToChar(key, lockState, aKbType);
  1.1197 +    if (ch) {
  1.1198 +      cmdedChar = ch;
  1.1199 +    }
  1.1200 +    ch = USLayout.TranslateToChar(key, shiftLockMod, aKbType);
  1.1201 +    if (ch) {
  1.1202 +      cmdedShiftChar = ch;
  1.1203 +    }
  1.1204 +  }
  1.1205 +
  1.1206 +  // If the current keyboard layout is switched by the Cmd key,
  1.1207 +  // we should append cmdedChar and shiftedCmdChar that are
  1.1208 +  // Latin char for the key.
  1.1209 +  // If the keyboard layout is Dvorak-QWERTY, we should append them only when
  1.1210 +  // command key is pressed because when command key isn't pressed, uncmded
  1.1211 +  // chars have been appended already.
  1.1212 +  if ((cmdedChar || cmdedShiftChar) && isCmdSwitchLayout &&
  1.1213 +      (aKeyEvent.IsMeta() || !isDvorakQWERTY)) {
  1.1214 +    AlternativeCharCode altCharCodes(cmdedChar, cmdedShiftChar);
  1.1215 +    aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
  1.1216 +  }
  1.1217 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1218 +    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
  1.1219 +     "hasCmdShiftOnlyChar=%s, isCmdSwitchLayout=%s, isDvorakQWERTY=%s, "
  1.1220 +     "cmdedChar=U+%X, cmdedShiftChar=U+%X",
  1.1221 +     this, TrueOrFalse(hasCmdShiftOnlyChar), TrueOrFalse(isDvorakQWERTY),
  1.1222 +     TrueOrFalse(isDvorakQWERTY), cmdedChar, cmdedShiftChar));
  1.1223 +  // Special case for 'SS' key of German layout. See the comment of
  1.1224 +  // hasCmdShiftOnlyChar definition for the detail.
  1.1225 +  if (hasCmdShiftOnlyChar && originalCmdedShiftChar) {
  1.1226 +    AlternativeCharCode altCharCodes(0, originalCmdedShiftChar);
  1.1227 +    aKeyEvent.alternativeCharCodes.AppendElement(altCharCodes);
  1.1228 +  }
  1.1229 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1230 +    ("%p TISInputSourceWrapper::InitKeyPressEvent, "
  1.1231 +     "hasCmdShiftOnlyChar=%s, originalCmdedShiftChar=U+%X",
  1.1232 +     this, TrueOrFalse(hasCmdShiftOnlyChar), originalCmdedShiftChar));
  1.1233 +
  1.1234 +  NS_OBJC_END_TRY_ABORT_BLOCK
  1.1235 +}
  1.1236 +
  1.1237 +uint32_t
  1.1238 +TISInputSourceWrapper::ComputeGeckoKeyCode(UInt32 aNativeKeyCode,
  1.1239 +                                           UInt32 aKbType,
  1.1240 +                                           bool aCmdIsPressed)
  1.1241 +{
  1.1242 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1243 +    ("%p TISInputSourceWrapper::ComputeGeckoKeyCode, aNativeKeyCode=0x%X, "
  1.1244 +     "aKbType=0x%X, aCmdIsPressed=%s, IsOpenedIMEMode()=%s, "
  1.1245 +     "IsASCIICapable()=%s",
  1.1246 +     this, aNativeKeyCode, aKbType, TrueOrFalse(aCmdIsPressed),
  1.1247 +     TrueOrFalse(IsOpenedIMEMode()), TrueOrFalse(IsASCIICapable())));
  1.1248 +
  1.1249 +  switch (aNativeKeyCode) {
  1.1250 +    case kVK_Space:             return NS_VK_SPACE;
  1.1251 +    case kVK_Escape:            return NS_VK_ESCAPE;
  1.1252 +
  1.1253 +    // modifiers
  1.1254 +    case kVK_RightCommand:
  1.1255 +    case kVK_Command:           return NS_VK_META;
  1.1256 +    case kVK_RightShift:
  1.1257 +    case kVK_Shift:             return NS_VK_SHIFT;
  1.1258 +    case kVK_CapsLock:          return NS_VK_CAPS_LOCK;
  1.1259 +    case kVK_RightControl:
  1.1260 +    case kVK_Control:           return NS_VK_CONTROL;
  1.1261 +    case kVK_RightOption:
  1.1262 +    case kVK_Option:            return NS_VK_ALT;
  1.1263 +
  1.1264 +    case kVK_ANSI_KeypadClear:  return NS_VK_CLEAR;
  1.1265 +
  1.1266 +    // function keys
  1.1267 +    case kVK_F1:                return NS_VK_F1;
  1.1268 +    case kVK_F2:                return NS_VK_F2;
  1.1269 +    case kVK_F3:                return NS_VK_F3;
  1.1270 +    case kVK_F4:                return NS_VK_F4;
  1.1271 +    case kVK_F5:                return NS_VK_F5;
  1.1272 +    case kVK_F6:                return NS_VK_F6;
  1.1273 +    case kVK_F7:                return NS_VK_F7;
  1.1274 +    case kVK_F8:                return NS_VK_F8;
  1.1275 +    case kVK_F9:                return NS_VK_F9;
  1.1276 +    case kVK_F10:               return NS_VK_F10;
  1.1277 +    case kVK_F11:               return NS_VK_F11;
  1.1278 +    case kVK_F12:               return NS_VK_F12;
  1.1279 +    // case kVK_F13:               return NS_VK_F13;  // clash with the 3 below
  1.1280 +    // case kVK_F14:               return NS_VK_F14;
  1.1281 +    // case kVK_F15:               return NS_VK_F15;
  1.1282 +    case kVK_F16:               return NS_VK_F16;
  1.1283 +    case kVK_F17:               return NS_VK_F17;
  1.1284 +    case kVK_F18:               return NS_VK_F18;
  1.1285 +    case kVK_F19:               return NS_VK_F19;
  1.1286 +
  1.1287 +    case kVK_PC_Pause:          return NS_VK_PAUSE;
  1.1288 +    case kVK_PC_ScrollLock:     return NS_VK_SCROLL_LOCK;
  1.1289 +    case kVK_PC_PrintScreen:    return NS_VK_PRINTSCREEN;
  1.1290 +
  1.1291 +    // keypad
  1.1292 +    case kVK_ANSI_Keypad0:      return NS_VK_NUMPAD0;
  1.1293 +    case kVK_ANSI_Keypad1:      return NS_VK_NUMPAD1;
  1.1294 +    case kVK_ANSI_Keypad2:      return NS_VK_NUMPAD2;
  1.1295 +    case kVK_ANSI_Keypad3:      return NS_VK_NUMPAD3;
  1.1296 +    case kVK_ANSI_Keypad4:      return NS_VK_NUMPAD4;
  1.1297 +    case kVK_ANSI_Keypad5:      return NS_VK_NUMPAD5;
  1.1298 +    case kVK_ANSI_Keypad6:      return NS_VK_NUMPAD6;
  1.1299 +    case kVK_ANSI_Keypad7:      return NS_VK_NUMPAD7;
  1.1300 +    case kVK_ANSI_Keypad8:      return NS_VK_NUMPAD8;
  1.1301 +    case kVK_ANSI_Keypad9:      return NS_VK_NUMPAD9;
  1.1302 +
  1.1303 +    case kVK_ANSI_KeypadMultiply: return NS_VK_MULTIPLY;
  1.1304 +    case kVK_ANSI_KeypadPlus:     return NS_VK_ADD;
  1.1305 +    case kVK_ANSI_KeypadMinus:    return NS_VK_SUBTRACT;
  1.1306 +    case kVK_ANSI_KeypadDecimal:  return NS_VK_DECIMAL;
  1.1307 +    case kVK_ANSI_KeypadDivide:   return NS_VK_DIVIDE;
  1.1308 +
  1.1309 +    case kVK_JIS_KeypadComma:   return NS_VK_SEPARATOR;
  1.1310 +
  1.1311 +    // IME keys
  1.1312 +    case kVK_JIS_Eisu:          return NS_VK_EISU;
  1.1313 +    case kVK_JIS_Kana:          return NS_VK_KANA;
  1.1314 +
  1.1315 +    // these may clash with forward delete and help
  1.1316 +    case kVK_PC_Insert:         return NS_VK_INSERT;
  1.1317 +    case kVK_PC_Delete:         return NS_VK_DELETE;
  1.1318 +
  1.1319 +    case kVK_PC_Backspace:      return NS_VK_BACK;
  1.1320 +    case kVK_Tab:               return NS_VK_TAB;
  1.1321 +
  1.1322 +    case kVK_Home:              return NS_VK_HOME;
  1.1323 +    case kVK_End:               return NS_VK_END;
  1.1324 +
  1.1325 +    case kVK_PageUp:            return NS_VK_PAGE_UP;
  1.1326 +    case kVK_PageDown:          return NS_VK_PAGE_DOWN;
  1.1327 +
  1.1328 +    case kVK_LeftArrow:         return NS_VK_LEFT;
  1.1329 +    case kVK_RightArrow:        return NS_VK_RIGHT;
  1.1330 +    case kVK_UpArrow:           return NS_VK_UP;
  1.1331 +    case kVK_DownArrow:         return NS_VK_DOWN;
  1.1332 +
  1.1333 +    case kVK_PC_ContextMenu:    return NS_VK_CONTEXT_MENU;
  1.1334 +
  1.1335 +    case kVK_ANSI_1:            return NS_VK_1;
  1.1336 +    case kVK_ANSI_2:            return NS_VK_2;
  1.1337 +    case kVK_ANSI_3:            return NS_VK_3;
  1.1338 +    case kVK_ANSI_4:            return NS_VK_4;
  1.1339 +    case kVK_ANSI_5:            return NS_VK_5;
  1.1340 +    case kVK_ANSI_6:            return NS_VK_6;
  1.1341 +    case kVK_ANSI_7:            return NS_VK_7;
  1.1342 +    case kVK_ANSI_8:            return NS_VK_8;
  1.1343 +    case kVK_ANSI_9:            return NS_VK_9;
  1.1344 +    case kVK_ANSI_0:            return NS_VK_0;
  1.1345 +
  1.1346 +    case kVK_ANSI_KeypadEnter:
  1.1347 +    case kVK_Return:
  1.1348 +    case kVK_Powerbook_KeypadEnter: return NS_VK_RETURN;
  1.1349 +  }
  1.1350 +
  1.1351 +  // If Cmd key is pressed, that causes switching keyboard layout temporarily.
  1.1352 +  // E.g., Dvorak-QWERTY.  Therefore, if Cmd key is pressed, we should honor it.
  1.1353 +  UInt32 modifiers = aCmdIsPressed ? cmdKey : 0;
  1.1354 +
  1.1355 +  uint32_t charCode = TranslateToChar(aNativeKeyCode, modifiers, aKbType);
  1.1356 +
  1.1357 +  // Special case for Mac.  Mac inputs Yen sign (U+00A5) directly instead of
  1.1358 +  // Back slash (U+005C).  We should return NS_VK_BACK_SLASH for compatibility
  1.1359 +  // with other platforms.
  1.1360 +  // XXX How about Won sign (U+20A9) which has same problem as Yen sign?
  1.1361 +  if (charCode == 0x00A5) {
  1.1362 +    return NS_VK_BACK_SLASH;
  1.1363 +  }
  1.1364 +
  1.1365 +  uint32_t keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
  1.1366 +  if (keyCode) {
  1.1367 +    return keyCode;
  1.1368 +  }
  1.1369 +
  1.1370 +  // If the unshifed char isn't an ASCII character, use shifted char.
  1.1371 +  charCode = TranslateToChar(aNativeKeyCode, modifiers | shiftKey, aKbType);
  1.1372 +  keyCode = WidgetUtils::ComputeKeyCodeFromChar(charCode);
  1.1373 +  if (keyCode) {
  1.1374 +    return keyCode;
  1.1375 +  }
  1.1376 +
  1.1377 +  // If this is ASCII capable, give up to compute it.
  1.1378 +  if (IsASCIICapable()) {
  1.1379 +    return 0;
  1.1380 +  }
  1.1381 +
  1.1382 +  // Retry with ASCII capable keyboard layout.
  1.1383 +  TISInputSourceWrapper currentKeyboardLayout;
  1.1384 +  currentKeyboardLayout.InitByCurrentASCIICapableKeyboardLayout();
  1.1385 +  NS_ENSURE_TRUE(mInputSource != currentKeyboardLayout.mInputSource, 0);
  1.1386 +  keyCode = currentKeyboardLayout.ComputeGeckoKeyCode(aNativeKeyCode, aKbType,
  1.1387 +                                                      aCmdIsPressed);
  1.1388 +
  1.1389 +  // However, if keyCode isn't for an alphabet keys or a numeric key, we should
  1.1390 +  // ignore it.  For example, comma key of Thai layout is same as close-square-
  1.1391 +  // bracket key of US layout and an unicode character key of Thai layout is
  1.1392 +  // same as comma key of US layout.  If we return NS_VK_COMMA for latter key,
  1.1393 +  // web application developers cannot distinguish with the former key.
  1.1394 +  return ((keyCode >= NS_VK_A && keyCode <= NS_VK_Z) ||
  1.1395 +          (keyCode >= NS_VK_0 && keyCode <= NS_VK_9)) ? keyCode : 0;
  1.1396 +}
  1.1397 +
  1.1398 +// static
  1.1399 +KeyNameIndex
  1.1400 +TISInputSourceWrapper::ComputeGeckoKeyNameIndex(UInt32 aNativeKeyCode)
  1.1401 +{
  1.1402 +  switch (aNativeKeyCode) {
  1.1403 +
  1.1404 +#define NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX(aNativeKey, aKeyNameIndex) \
  1.1405 +    case aNativeKey: return aKeyNameIndex;
  1.1406 +
  1.1407 +#include "NativeKeyToDOMKeyName.h"
  1.1408 +
  1.1409 +#undef NS_NATIVE_KEY_TO_DOM_KEY_NAME_INDEX
  1.1410 +
  1.1411 +    default:
  1.1412 +      return KEY_NAME_INDEX_Unidentified;
  1.1413 +  }
  1.1414 +}
  1.1415 +
  1.1416 +
  1.1417 +#pragma mark -
  1.1418 +
  1.1419 +
  1.1420 +/******************************************************************************
  1.1421 + *
  1.1422 + *  TextInputHandler implementation (static methods)
  1.1423 + *
  1.1424 + ******************************************************************************/
  1.1425 +
  1.1426 +NSUInteger TextInputHandler::sLastModifierState = 0;
  1.1427 +
  1.1428 +// static
  1.1429 +CFArrayRef
  1.1430 +TextInputHandler::CreateAllKeyboardLayoutList()
  1.1431 +{
  1.1432 +  const void* keys[] = { kTISPropertyInputSourceType };
  1.1433 +  const void* values[] = { kTISTypeKeyboardLayout };
  1.1434 +  CFDictionaryRef filter =
  1.1435 +    ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL);
  1.1436 +  NS_ASSERTION(filter, "failed to create the filter");
  1.1437 +  CFArrayRef list = ::TISCreateInputSourceList(filter, true);
  1.1438 +  ::CFRelease(filter);
  1.1439 +  return list;
  1.1440 +}
  1.1441 +
  1.1442 +// static
  1.1443 +void
  1.1444 +TextInputHandler::DebugPrintAllKeyboardLayouts()
  1.1445 +{
  1.1446 +#ifdef PR_LOGGING
  1.1447 +  if (PR_LOG_TEST(gLog, PR_LOG_ALWAYS)) {
  1.1448 +    CFArrayRef list = CreateAllKeyboardLayoutList();
  1.1449 +    PR_LOG(gLog, PR_LOG_ALWAYS, ("Keyboard layout configuration:"));
  1.1450 +    CFIndex idx = ::CFArrayGetCount(list);
  1.1451 +    TISInputSourceWrapper tis;
  1.1452 +    for (CFIndex i = 0; i < idx; ++i) {
  1.1453 +      TISInputSourceRef inputSource = static_cast<TISInputSourceRef>(
  1.1454 +        const_cast<void *>(::CFArrayGetValueAtIndex(list, i)));
  1.1455 +      tis.InitByTISInputSourceRef(inputSource);
  1.1456 +      nsAutoString name, isid;
  1.1457 +      tis.GetLocalizedName(name);
  1.1458 +      tis.GetInputSourceID(isid);
  1.1459 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1460 +             ("  %s\t<%s>%s%s\n",
  1.1461 +              NS_ConvertUTF16toUTF8(name).get(),
  1.1462 +              NS_ConvertUTF16toUTF8(isid).get(),
  1.1463 +              tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)",
  1.1464 +              tis.IsKeyboardLayout() && tis.GetUCKeyboardLayout() ?
  1.1465 +                "" : "\t(uchr is NOT AVAILABLE)"));
  1.1466 +    }
  1.1467 +    ::CFRelease(list);
  1.1468 +  }
  1.1469 +#endif // #ifdef PR_LOGGING
  1.1470 +}
  1.1471 +
  1.1472 +
  1.1473 +#pragma mark -
  1.1474 +
  1.1475 +
  1.1476 +/******************************************************************************
  1.1477 + *
  1.1478 + *  TextInputHandler implementation
  1.1479 + *
  1.1480 + ******************************************************************************/
  1.1481 +
  1.1482 +TextInputHandler::TextInputHandler(nsChildView* aWidget,
  1.1483 +                                   NSView<mozView> *aNativeView) :
  1.1484 +  IMEInputHandler(aWidget, aNativeView)
  1.1485 +{
  1.1486 +  InitLogModule();
  1.1487 +  [mView installTextInputHandler:this];
  1.1488 +}
  1.1489 +
  1.1490 +TextInputHandler::~TextInputHandler()
  1.1491 +{
  1.1492 +  [mView uninstallTextInputHandler];
  1.1493 +}
  1.1494 +
  1.1495 +bool
  1.1496 +TextInputHandler::HandleKeyDownEvent(NSEvent* aNativeEvent)
  1.1497 +{
  1.1498 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.1499 +
  1.1500 +  if (Destroyed()) {
  1.1501 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1502 +      ("%p TextInputHandler::HandleKeyDownEvent, "
  1.1503 +       "widget has been already destroyed", this));
  1.1504 +    return false;
  1.1505 +  }
  1.1506 +
  1.1507 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1508 +    ("%p TextInputHandler::HandleKeyDownEvent, aNativeEvent=%p, "
  1.1509 +     "type=%s, keyCode=%lld (0x%X), modifierFlags=0x%X, characters=\"%s\", "
  1.1510 +     "charactersIgnoringModifiers=\"%s\"",
  1.1511 +     this, aNativeEvent, GetNativeKeyEventType(aNativeEvent),
  1.1512 +     [aNativeEvent keyCode], [aNativeEvent keyCode],
  1.1513 +     [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]),
  1.1514 +     GetCharacters([aNativeEvent charactersIgnoringModifiers])));
  1.1515 +
  1.1516 +  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
  1.1517 +
  1.1518 +  KeyEventState* currentKeyEvent = PushKeyEvent(aNativeEvent);
  1.1519 +  AutoKeyEventStateCleaner remover(this);
  1.1520 +
  1.1521 +  if (!IsIMEComposing()) {
  1.1522 +    NSResponder* firstResponder = [[mView window] firstResponder];
  1.1523 +
  1.1524 +    WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
  1.1525 +    InitKeyEvent(aNativeEvent, keydownEvent);
  1.1526 +
  1.1527 +    currentKeyEvent->mKeyDownHandled = DispatchEvent(keydownEvent);
  1.1528 +    if (Destroyed()) {
  1.1529 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1530 +        ("%p TextInputHandler::HandleKeyDownEvent, "
  1.1531 +         "widget was destroyed by keydown event", this));
  1.1532 +      return currentKeyEvent->IsDefaultPrevented();
  1.1533 +    }
  1.1534 +
  1.1535 +    // The key down event may have shifted the focus, in which
  1.1536 +    // case we should not fire the key press.
  1.1537 +    // XXX This is a special code only on Cocoa widget, why is this needed?
  1.1538 +    if (firstResponder != [[mView window] firstResponder]) {
  1.1539 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1540 +        ("%p TextInputHandler::HandleKeyDownEvent, "
  1.1541 +         "view lost focus by keydown event", this));
  1.1542 +      return currentKeyEvent->IsDefaultPrevented();
  1.1543 +    }
  1.1544 +
  1.1545 +    if (currentKeyEvent->IsDefaultPrevented()) {
  1.1546 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1547 +        ("%p TextInputHandler::HandleKeyDownEvent, "
  1.1548 +         "keydown event's default is prevented", this));
  1.1549 +      return true;
  1.1550 +    }
  1.1551 +  }
  1.1552 +
  1.1553 +  // Let Cocoa interpret the key events, caching IsIMEComposing first.
  1.1554 +  bool wasComposing = IsIMEComposing();
  1.1555 +  bool interpretKeyEventsCalled = false;
  1.1556 +  if (IsIMEEnabled() || IsASCIICapableOnly()) {
  1.1557 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1558 +      ("%p TextInputHandler::HandleKeyDownEvent, calling interpretKeyEvents",
  1.1559 +       this));
  1.1560 +    [mView interpretKeyEvents:[NSArray arrayWithObject:aNativeEvent]];
  1.1561 +    interpretKeyEventsCalled = true;
  1.1562 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1563 +      ("%p TextInputHandler::HandleKeyDownEvent, called interpretKeyEvents",
  1.1564 +       this));
  1.1565 +  }
  1.1566 +
  1.1567 +  if (Destroyed()) {
  1.1568 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1569 +      ("%p TextInputHandler::HandleKeyDownEvent, widget was destroyed",
  1.1570 +       this));
  1.1571 +    return currentKeyEvent->IsDefaultPrevented();
  1.1572 +  }
  1.1573 +
  1.1574 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1575 +    ("%p TextInputHandler::HandleKeyDownEvent, wasComposing=%s, "
  1.1576 +     "IsIMEComposing()=%s",
  1.1577 +     this, TrueOrFalse(wasComposing), TrueOrFalse(IsIMEComposing())));
  1.1578 +
  1.1579 +  if (currentKeyEvent->CanDispatchKeyPressEvent() &&
  1.1580 +      !wasComposing && !IsIMEComposing()) {
  1.1581 +    WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
  1.1582 +    InitKeyEvent(aNativeEvent, keypressEvent);
  1.1583 +
  1.1584 +    // If we called interpretKeyEvents and this isn't normal character input
  1.1585 +    // then IME probably ate the event for some reason. We do not want to
  1.1586 +    // send a key press event in that case.
  1.1587 +    // TODO:
  1.1588 +    // There are some other cases which IME eats the current event.
  1.1589 +    // 1. If key events were nested during calling interpretKeyEvents, it means
  1.1590 +    //    that IME did something.  Then, we should do nothing.
  1.1591 +    // 2. If one or more commands are called like "deleteBackward", we should
  1.1592 +    //    dispatch keypress event at that time.  Note that the command may have
  1.1593 +    //    been a converted or generated action by IME.  Then, we shouldn't do
  1.1594 +    //    our default action for this key.
  1.1595 +    if (!(interpretKeyEventsCalled &&
  1.1596 +          IsNormalCharInputtingEvent(keypressEvent))) {
  1.1597 +      currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent);
  1.1598 +      currentKeyEvent->mKeyPressDispatched = true;
  1.1599 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1600 +        ("%p TextInputHandler::HandleKeyDownEvent, keypress event dispatched",
  1.1601 +         this));
  1.1602 +    }
  1.1603 +  }
  1.1604 +
  1.1605 +  // Note: mWidget might have become null here. Don't count on it from here on.
  1.1606 +
  1.1607 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1608 +    ("%p TextInputHandler::HandleKeyDownEvent, "
  1.1609 +     "keydown handled=%s, keypress handled=%s, causedOtherKeyEvents=%s",
  1.1610 +     this, TrueOrFalse(currentKeyEvent->mKeyDownHandled),
  1.1611 +     TrueOrFalse(currentKeyEvent->mKeyPressHandled),
  1.1612 +     TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents)));
  1.1613 +  return currentKeyEvent->IsDefaultPrevented();
  1.1614 +
  1.1615 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
  1.1616 +}
  1.1617 +
  1.1618 +void
  1.1619 +TextInputHandler::HandleKeyUpEvent(NSEvent* aNativeEvent)
  1.1620 +{
  1.1621 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1622 +
  1.1623 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1624 +    ("%p TextInputHandler::HandleKeyUpEvent, aNativeEvent=%p, "
  1.1625 +     "type=%s, keyCode=%lld (0x%X), modifierFlags=0x%X, characters=\"%s\", "
  1.1626 +     "charactersIgnoringModifiers=\"%s\", "
  1.1627 +     "mIgnoreNextKeyUpEvent=%s, IsIMEComposing()=%s",
  1.1628 +     this, aNativeEvent, GetNativeKeyEventType(aNativeEvent),
  1.1629 +     [aNativeEvent keyCode], [aNativeEvent keyCode],
  1.1630 +     [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]),
  1.1631 +     GetCharacters([aNativeEvent charactersIgnoringModifiers]),
  1.1632 +     TrueOrFalse(mIgnoreNextKeyUpEvent), TrueOrFalse(IsIMEComposing())));
  1.1633 +
  1.1634 +  if (mIgnoreNextKeyUpEvent) {
  1.1635 +    mIgnoreNextKeyUpEvent = false;
  1.1636 +    return;
  1.1637 +  }
  1.1638 +
  1.1639 +  if (Destroyed()) {
  1.1640 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1641 +      ("%p TextInputHandler::HandleKeyUpEvent, "
  1.1642 +       "widget has been already destroyed", this));
  1.1643 +    return;
  1.1644 +  }
  1.1645 +
  1.1646 +  // if we don't have any characters we can't generate a keyUp event
  1.1647 +  if (IsIMEComposing()) {
  1.1648 +    return;
  1.1649 +  }
  1.1650 +
  1.1651 +  WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget);
  1.1652 +  InitKeyEvent(aNativeEvent, keyupEvent);
  1.1653 +
  1.1654 +  DispatchEvent(keyupEvent);
  1.1655 +
  1.1656 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1657 +}
  1.1658 +
  1.1659 +void
  1.1660 +TextInputHandler::HandleFlagsChanged(NSEvent* aNativeEvent)
  1.1661 +{
  1.1662 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1663 +
  1.1664 +  if (Destroyed()) {
  1.1665 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1666 +      ("%p TextInputHandler::HandleFlagsChanged, "
  1.1667 +       "widget has been already destroyed", this));
  1.1668 +    return;
  1.1669 +  }
  1.1670 +
  1.1671 +  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
  1.1672 +
  1.1673 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1674 +    ("%p TextInputHandler::HandleFlagsChanged, aNativeEvent=%p, "
  1.1675 +     "type=%s, keyCode=%s (0x%X), modifierFlags=0x%08X, "
  1.1676 +     "sLastModifierState=0x%08X, IsIMEComposing()=%s",
  1.1677 +     this, aNativeEvent, GetNativeKeyEventType(aNativeEvent),
  1.1678 +     GetKeyNameForNativeKeyCode([aNativeEvent keyCode]), [aNativeEvent keyCode],
  1.1679 +     [aNativeEvent modifierFlags], sLastModifierState,
  1.1680 +     TrueOrFalse(IsIMEComposing())));
  1.1681 +
  1.1682 +  MOZ_ASSERT([aNativeEvent type] == NSFlagsChanged);
  1.1683 +
  1.1684 +  NSUInteger diff = [aNativeEvent modifierFlags] ^ sLastModifierState;
  1.1685 +  // Device dependent flags for left-control key, both shift keys, both command
  1.1686 +  // keys and both option keys have been defined in Next's SDK.  But we
  1.1687 +  // shouldn't use it directly as far as possible since Cocoa SDK doesn't
  1.1688 +  // define them.  Fortunately, we need them only when we dispatch keyup
  1.1689 +  // events.  So, we can usually know the actual relation between keyCode and
  1.1690 +  // device dependent flags.  However, we need to remove following flags first
  1.1691 +  // since the differences don't indicate modifier key state.
  1.1692 +  // NX_STYLUSPROXIMITYMASK: Probably used for pen like device.
  1.1693 +  // kCGEventFlagMaskNonCoalesced (= NX_NONCOALSESCEDMASK): See the document for
  1.1694 +  // Quartz Event Services.
  1.1695 +  diff &= ~(NX_STYLUSPROXIMITYMASK | kCGEventFlagMaskNonCoalesced);
  1.1696 +
  1.1697 +  switch ([aNativeEvent keyCode]) {
  1.1698 +    // CapsLock state and other modifier states are different:
  1.1699 +    // CapsLock state does not revert when the CapsLock key goes up, as the
  1.1700 +    // modifier state does for other modifier keys on key up.
  1.1701 +    case kVK_CapsLock: {
  1.1702 +      // Fire key down event for caps lock.
  1.1703 +      DispatchKeyEventForFlagsChanged(aNativeEvent, true);
  1.1704 +      // XXX should we fire keyup event too? The keyup event for CapsLock key
  1.1705 +      // is never dispatched on Gecko.
  1.1706 +      // XXX WebKit dispatches keydown event when CapsLock is locked, otherwise,
  1.1707 +      // keyup event.  If we do so, we cannot keep the consistency with other
  1.1708 +      // platform's behavior...
  1.1709 +      break;
  1.1710 +    }
  1.1711 +
  1.1712 +    // If the event is caused by pressing or releasing a modifier key, just
  1.1713 +    // dispatch the key's event.
  1.1714 +    case kVK_Shift:
  1.1715 +    case kVK_RightShift:
  1.1716 +    case kVK_Command:
  1.1717 +    case kVK_RightCommand:
  1.1718 +    case kVK_Control:
  1.1719 +    case kVK_RightControl:
  1.1720 +    case kVK_Option:
  1.1721 +    case kVK_RightOption:
  1.1722 +    case kVK_Help: {
  1.1723 +      // We assume that at most one modifier is changed per event if the event
  1.1724 +      // is caused by pressing or releasing a modifier key.
  1.1725 +      bool isKeyDown = ([aNativeEvent modifierFlags] & diff) != 0;
  1.1726 +      DispatchKeyEventForFlagsChanged(aNativeEvent, isKeyDown);
  1.1727 +      // XXX Some applications might send the event with incorrect device-
  1.1728 +      //     dependent flags.
  1.1729 +      if (isKeyDown && ((diff & ~NSDeviceIndependentModifierFlagsMask) != 0)) {
  1.1730 +        unsigned short keyCode = [aNativeEvent keyCode];
  1.1731 +        const ModifierKey* modifierKey =
  1.1732 +          GetModifierKeyForDeviceDependentFlags(diff);
  1.1733 +        if (modifierKey && modifierKey->keyCode != keyCode) {
  1.1734 +          // Although, we're not sure the actual cause of this case, the stored
  1.1735 +          // modifier information and the latest key event information may be
  1.1736 +          // mismatched. Then, let's reset the stored information.
  1.1737 +          // NOTE: If this happens, it may fail to handle NSFlagsChanged event
  1.1738 +          // in the default case (below). However, it's the rare case handler
  1.1739 +          // and this case occurs rarely. So, we can ignore the edge case bug.
  1.1740 +          NS_WARNING("Resetting stored modifier key information");
  1.1741 +          mModifierKeys.Clear();
  1.1742 +          modifierKey = nullptr;
  1.1743 +        }
  1.1744 +        if (!modifierKey) {
  1.1745 +          mModifierKeys.AppendElement(ModifierKey(diff, keyCode));
  1.1746 +        }
  1.1747 +      }
  1.1748 +      break;
  1.1749 +    }
  1.1750 +
  1.1751 +    // Currently we don't support Fn key since other browsers don't dispatch
  1.1752 +    // events for it and we don't have keyCode for this key.
  1.1753 +    // It should be supported when we implement .key and .char.
  1.1754 +    case kVK_Function:
  1.1755 +      break;
  1.1756 +
  1.1757 +    // If the event is caused by something else than pressing or releasing a
  1.1758 +    // single modifier key (for example by the app having been deactivated
  1.1759 +    // using command-tab), use the modifiers themselves to determine which
  1.1760 +    // key's event to dispatch, and whether it's a keyup or keydown event.
  1.1761 +    // In all cases we assume one or more modifiers are being deactivated
  1.1762 +    // (never activated) -- otherwise we'd have received one or more events
  1.1763 +    // corresponding to a single modifier key being pressed.
  1.1764 +    default: {
  1.1765 +      NSUInteger modifiers = sLastModifierState;
  1.1766 +      for (int32_t bit = 0; bit < 32; ++bit) {
  1.1767 +        NSUInteger flag = 1 << bit;
  1.1768 +        if (!(diff & flag)) {
  1.1769 +          continue;
  1.1770 +        }
  1.1771 +
  1.1772 +        // Given correct information from the application, a flag change here
  1.1773 +        // will normally be a deactivation (except for some lockable modifiers
  1.1774 +        // such as CapsLock).  But some applications (like VNC) can send an
  1.1775 +        // activating event with a zero keyCode.  So we need to check for that
  1.1776 +        // here.
  1.1777 +        bool dispatchKeyDown = ((flag & [aNativeEvent modifierFlags]) != 0);
  1.1778 +
  1.1779 +        unsigned short keyCode = 0;
  1.1780 +        if (flag & NSDeviceIndependentModifierFlagsMask) {
  1.1781 +          switch (flag) {
  1.1782 +            case NSAlphaShiftKeyMask:
  1.1783 +              keyCode = kVK_CapsLock;
  1.1784 +              dispatchKeyDown = true;
  1.1785 +              break;
  1.1786 +
  1.1787 +            case NSNumericPadKeyMask:
  1.1788 +              // NSNumericPadKeyMask is fired by VNC a lot. But not all of
  1.1789 +              // these events can really be Clear key events, so we just ignore
  1.1790 +              // them.
  1.1791 +              continue;
  1.1792 +
  1.1793 +            case NSHelpKeyMask:
  1.1794 +              keyCode = kVK_Help;
  1.1795 +              break;
  1.1796 +
  1.1797 +            case NSFunctionKeyMask:
  1.1798 +              // An NSFunctionKeyMask change here will normally be a
  1.1799 +              // deactivation.  But sometimes it will be an activation send (by
  1.1800 +              // VNC for example) with a zero keyCode.
  1.1801 +              continue;
  1.1802 +
  1.1803 +            // These cases (NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask
  1.1804 +            // and NSCommandKeyMask) should be handled by the other branch of
  1.1805 +            // the if statement, below (which handles device dependent flags).
  1.1806 +            // However, some applications (like VNC) can send key events without
  1.1807 +            // any device dependent flags, so we handle them here instead.
  1.1808 +            case NSShiftKeyMask:
  1.1809 +              keyCode = (modifiers & 0x0004) ? kVK_RightShift : kVK_Shift;
  1.1810 +              break;
  1.1811 +            case NSControlKeyMask:
  1.1812 +              keyCode = (modifiers & 0x2000) ? kVK_RightControl : kVK_Control;
  1.1813 +              break;
  1.1814 +            case NSAlternateKeyMask:
  1.1815 +              keyCode = (modifiers & 0x0040) ? kVK_RightOption : kVK_Option;
  1.1816 +              break;
  1.1817 +            case NSCommandKeyMask:
  1.1818 +              keyCode = (modifiers & 0x0010) ? kVK_RightCommand : kVK_Command;
  1.1819 +              break;
  1.1820 +
  1.1821 +            default:
  1.1822 +              continue;
  1.1823 +          }
  1.1824 +        } else {
  1.1825 +          const ModifierKey* modifierKey =
  1.1826 +            GetModifierKeyForDeviceDependentFlags(flag);
  1.1827 +          if (!modifierKey) {
  1.1828 +            // See the note above (in the other branch of the if statement)
  1.1829 +            // about the NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask
  1.1830 +            // and NSCommandKeyMask cases.
  1.1831 +            continue;
  1.1832 +          }
  1.1833 +          keyCode = modifierKey->keyCode;
  1.1834 +        }
  1.1835 +
  1.1836 +        // Remove flags
  1.1837 +        modifiers &= ~flag;
  1.1838 +        switch (keyCode) {
  1.1839 +          case kVK_Shift: {
  1.1840 +            const ModifierKey* modifierKey =
  1.1841 +              GetModifierKeyForNativeKeyCode(kVK_RightShift);
  1.1842 +            if (!modifierKey ||
  1.1843 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1844 +              modifiers &= ~NSShiftKeyMask;
  1.1845 +            }
  1.1846 +            break;
  1.1847 +          }
  1.1848 +          case kVK_RightShift: {
  1.1849 +            const ModifierKey* modifierKey =
  1.1850 +              GetModifierKeyForNativeKeyCode(kVK_Shift);
  1.1851 +            if (!modifierKey ||
  1.1852 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1853 +              modifiers &= ~NSShiftKeyMask;
  1.1854 +            }
  1.1855 +            break;
  1.1856 +          }
  1.1857 +          case kVK_Command: {
  1.1858 +            const ModifierKey* modifierKey =
  1.1859 +              GetModifierKeyForNativeKeyCode(kVK_RightCommand);
  1.1860 +            if (!modifierKey ||
  1.1861 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1862 +              modifiers &= ~NSCommandKeyMask;
  1.1863 +            }
  1.1864 +            break;
  1.1865 +          }
  1.1866 +          case kVK_RightCommand: {
  1.1867 +            const ModifierKey* modifierKey =
  1.1868 +              GetModifierKeyForNativeKeyCode(kVK_Command);
  1.1869 +            if (!modifierKey ||
  1.1870 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1871 +              modifiers &= ~NSCommandKeyMask;
  1.1872 +            }
  1.1873 +            break;
  1.1874 +          }
  1.1875 +          case kVK_Control: {
  1.1876 +            const ModifierKey* modifierKey =
  1.1877 +              GetModifierKeyForNativeKeyCode(kVK_RightControl);
  1.1878 +            if (!modifierKey ||
  1.1879 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1880 +              modifiers &= ~NSControlKeyMask;
  1.1881 +            }
  1.1882 +            break;
  1.1883 +          }
  1.1884 +          case kVK_RightControl: {
  1.1885 +            const ModifierKey* modifierKey =
  1.1886 +              GetModifierKeyForNativeKeyCode(kVK_Control);
  1.1887 +            if (!modifierKey ||
  1.1888 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1889 +              modifiers &= ~NSControlKeyMask;
  1.1890 +            }
  1.1891 +            break;
  1.1892 +          }
  1.1893 +          case kVK_Option: {
  1.1894 +            const ModifierKey* modifierKey =
  1.1895 +              GetModifierKeyForNativeKeyCode(kVK_RightOption);
  1.1896 +            if (!modifierKey ||
  1.1897 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1898 +              modifiers &= ~NSAlternateKeyMask;
  1.1899 +            }
  1.1900 +            break;
  1.1901 +          }
  1.1902 +          case kVK_RightOption: {
  1.1903 +            const ModifierKey* modifierKey =
  1.1904 +              GetModifierKeyForNativeKeyCode(kVK_Option);
  1.1905 +            if (!modifierKey ||
  1.1906 +                !(modifiers & modifierKey->GetDeviceDependentFlags())) {
  1.1907 +              modifiers &= ~NSAlternateKeyMask;
  1.1908 +            }
  1.1909 +            break;
  1.1910 +          }
  1.1911 +          case kVK_Help:
  1.1912 +            modifiers &= ~NSHelpKeyMask;
  1.1913 +            break;
  1.1914 +          default:
  1.1915 +            break;
  1.1916 +        }
  1.1917 +
  1.1918 +        NSEvent* event =
  1.1919 +          [NSEvent keyEventWithType:NSFlagsChanged
  1.1920 +                           location:[aNativeEvent locationInWindow]
  1.1921 +                      modifierFlags:modifiers
  1.1922 +                          timestamp:[aNativeEvent timestamp]
  1.1923 +                       windowNumber:[aNativeEvent windowNumber]
  1.1924 +                            context:[aNativeEvent context]
  1.1925 +                         characters:nil
  1.1926 +        charactersIgnoringModifiers:nil
  1.1927 +                          isARepeat:NO
  1.1928 +                            keyCode:keyCode];
  1.1929 +        DispatchKeyEventForFlagsChanged(event, dispatchKeyDown);
  1.1930 +        if (Destroyed()) {
  1.1931 +          break;
  1.1932 +        }
  1.1933 +
  1.1934 +        // Stop if focus has changed.
  1.1935 +        // Check to see if mView is still the first responder.
  1.1936 +        if (![mView isFirstResponder]) {
  1.1937 +          break;
  1.1938 +        }
  1.1939 +
  1.1940 +      }
  1.1941 +      break;
  1.1942 +    }
  1.1943 +  }
  1.1944 +
  1.1945 +  // Be aware, the widget may have been destroyed.
  1.1946 +  sLastModifierState = [aNativeEvent modifierFlags];
  1.1947 +
  1.1948 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.1949 +}
  1.1950 +
  1.1951 +const TextInputHandler::ModifierKey*
  1.1952 +TextInputHandler::GetModifierKeyForNativeKeyCode(unsigned short aKeyCode) const
  1.1953 +{
  1.1954 +  for (ModifierKeyArray::index_type i = 0; i < mModifierKeys.Length(); ++i) {
  1.1955 +    if (mModifierKeys[i].keyCode == aKeyCode) {
  1.1956 +      return &((ModifierKey&)mModifierKeys[i]);
  1.1957 +    }
  1.1958 +  }
  1.1959 +  return nullptr;
  1.1960 +}
  1.1961 +
  1.1962 +const TextInputHandler::ModifierKey*
  1.1963 +TextInputHandler::GetModifierKeyForDeviceDependentFlags(NSUInteger aFlags) const
  1.1964 +{
  1.1965 +  for (ModifierKeyArray::index_type i = 0; i < mModifierKeys.Length(); ++i) {
  1.1966 +    if (mModifierKeys[i].GetDeviceDependentFlags() ==
  1.1967 +          (aFlags & ~NSDeviceIndependentModifierFlagsMask)) {
  1.1968 +      return &((ModifierKey&)mModifierKeys[i]);
  1.1969 +    }
  1.1970 +  }
  1.1971 +  return nullptr;
  1.1972 +}
  1.1973 +
  1.1974 +void
  1.1975 +TextInputHandler::DispatchKeyEventForFlagsChanged(NSEvent* aNativeEvent,
  1.1976 +                                                  bool aDispatchKeyDown)
  1.1977 +{
  1.1978 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.1979 +
  1.1980 +  if (Destroyed()) {
  1.1981 +    return;
  1.1982 +  }
  1.1983 +
  1.1984 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.1985 +    ("%p TextInputHandler::DispatchKeyEventForFlagsChanged, aNativeEvent=%p, "
  1.1986 +     "type=%s, keyCode=%s (0x%X), aDispatchKeyDown=%s, IsIMEComposing()=%s",
  1.1987 +     this, aNativeEvent, GetNativeKeyEventType(aNativeEvent),
  1.1988 +     GetKeyNameForNativeKeyCode([aNativeEvent keyCode]), [aNativeEvent keyCode],
  1.1989 +     TrueOrFalse(aDispatchKeyDown), TrueOrFalse(IsIMEComposing())));
  1.1990 +
  1.1991 +  if ([aNativeEvent type] != NSFlagsChanged || IsIMEComposing()) {
  1.1992 +    return;
  1.1993 +  }
  1.1994 +
  1.1995 +  uint32_t message = aDispatchKeyDown ? NS_KEY_DOWN : NS_KEY_UP;
  1.1996 +
  1.1997 +  NPCocoaEvent cocoaEvent;
  1.1998 +
  1.1999 +  // Fire a key event.
  1.2000 +  WidgetKeyboardEvent keyEvent(true, message, mWidget);
  1.2001 +  InitKeyEvent(aNativeEvent, keyEvent);
  1.2002 +
  1.2003 +  // create event for use by plugins
  1.2004 +  if ([mView isPluginView]) {
  1.2005 +    if ([mView pluginEventModel] == NPEventModelCocoa) {
  1.2006 +      ConvertCocoaKeyEventToNPCocoaEvent(aNativeEvent, cocoaEvent);
  1.2007 +      keyEvent.pluginEvent = &cocoaEvent;
  1.2008 +    }
  1.2009 +  }
  1.2010 +
  1.2011 +  DispatchEvent(keyEvent);
  1.2012 +
  1.2013 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2014 +}
  1.2015 +
  1.2016 +void
  1.2017 +TextInputHandler::InsertText(NSAttributedString* aAttrString,
  1.2018 +                             NSRange* aReplacementRange)
  1.2019 +{
  1.2020 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2021 +
  1.2022 +  if (Destroyed()) {
  1.2023 +    return;
  1.2024 +  }
  1.2025 +
  1.2026 +  KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
  1.2027 +
  1.2028 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2029 +    ("%p TextInputHandler::InsertText, aAttrString=\"%s\", "
  1.2030 +     "aReplacementRange=%p { location=%llu, length=%llu }, "
  1.2031 +     "IsIMEComposing()=%s, IgnoreIMEComposition()=%s, "
  1.2032 +     "keyevent=%p, keydownHandled=%s, keypressDispatched=%s, "
  1.2033 +     "causedOtherKeyEvents=%s",
  1.2034 +     this, GetCharacters([aAttrString string]), aReplacementRange,
  1.2035 +     aReplacementRange ? aReplacementRange->location : 0,
  1.2036 +     aReplacementRange ? aReplacementRange->length : 0,
  1.2037 +     TrueOrFalse(IsIMEComposing()), TrueOrFalse(IgnoreIMEComposition()),
  1.2038 +     currentKeyEvent ? currentKeyEvent->mKeyEvent : nullptr,
  1.2039 +     currentKeyEvent ?
  1.2040 +       TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A",
  1.2041 +     currentKeyEvent ?
  1.2042 +       TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A",
  1.2043 +     currentKeyEvent ?
  1.2044 +       TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A"));
  1.2045 +
  1.2046 +  if (IgnoreIMEComposition()) {
  1.2047 +    return;
  1.2048 +  }
  1.2049 +
  1.2050 +  InputContext context = mWidget->GetInputContext();
  1.2051 +  bool isEditable = (context.mIMEState.mEnabled == IMEState::ENABLED ||
  1.2052 +                     context.mIMEState.mEnabled == IMEState::PASSWORD);
  1.2053 +  NSRange selectedRange = SelectedRange();
  1.2054 +
  1.2055 +  nsAutoString str;
  1.2056 +  nsCocoaUtils::GetStringForNSString([aAttrString string], str);
  1.2057 +  if (!IsIMEComposing() && str.IsEmpty()) {
  1.2058 +    // nothing to do if there is no content which can be removed.
  1.2059 +    if (!isEditable) {
  1.2060 +      return;
  1.2061 +    }
  1.2062 +    // If replacement range is specified, we need to remove the range.
  1.2063 +    // Otherwise, we need to remove the selected range if it's not collapsed.
  1.2064 +    if (aReplacementRange && aReplacementRange->location != NSNotFound) {
  1.2065 +      // nothing to do since the range is collapsed.
  1.2066 +      if (aReplacementRange->length == 0) {
  1.2067 +        return;
  1.2068 +      }
  1.2069 +      // If the replacement range is different from current selected range,
  1.2070 +      // select the range.
  1.2071 +      if (!NSEqualRanges(selectedRange, *aReplacementRange)) {
  1.2072 +        NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
  1.2073 +      }
  1.2074 +      selectedRange = SelectedRange();
  1.2075 +    }
  1.2076 +    NS_ENSURE_TRUE_VOID(selectedRange.location != NSNotFound);
  1.2077 +    if (selectedRange.length == 0) {
  1.2078 +      return; // nothing to do
  1.2079 +    }
  1.2080 +    // If this is caused by a key input, the keypress event which will be
  1.2081 +    // dispatched later should cause the delete.  Therefore, nothing to do here.
  1.2082 +    // Although, we're not sure if such case is actually possible.
  1.2083 +    if (!currentKeyEvent) {
  1.2084 +      return;
  1.2085 +    }
  1.2086 +    // Delete the selected range.
  1.2087 +    nsRefPtr<TextInputHandler> kungFuDeathGrip(this);
  1.2088 +    WidgetContentCommandEvent deleteCommandEvent(true,
  1.2089 +                                                 NS_CONTENT_COMMAND_DELETE,
  1.2090 +                                                 mWidget);
  1.2091 +    DispatchEvent(deleteCommandEvent);
  1.2092 +    NS_ENSURE_TRUE_VOID(deleteCommandEvent.mSucceeded);
  1.2093 +    // Be aware! The widget might be destroyed here.
  1.2094 +    return;
  1.2095 +  }
  1.2096 +
  1.2097 +  if (str.Length() != 1 || IsIMEComposing()) {
  1.2098 +    InsertTextAsCommittingComposition(aAttrString, aReplacementRange);
  1.2099 +    return;
  1.2100 +  }
  1.2101 +
  1.2102 +  // Don't let the same event be fired twice when hitting
  1.2103 +  // enter/return! (Bug 420502)
  1.2104 +  if (currentKeyEvent && !currentKeyEvent->CanDispatchKeyPressEvent()) {
  1.2105 +    return;
  1.2106 +  }
  1.2107 +
  1.2108 +  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
  1.2109 +
  1.2110 +  // If the replacement range is specified, select the range.  Then, the
  1.2111 +  // selection will be replaced by the later keypress event.
  1.2112 +  if (isEditable &&
  1.2113 +      aReplacementRange && aReplacementRange->location != NSNotFound &&
  1.2114 +      !NSEqualRanges(selectedRange, *aReplacementRange)) {
  1.2115 +    NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
  1.2116 +  }
  1.2117 +
  1.2118 +  // Dispatch keypress event with char instead of textEvent
  1.2119 +  WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
  1.2120 +  keypressEvent.isChar = IsPrintableChar(str.CharAt(0));
  1.2121 +
  1.2122 +  // Don't set other modifiers from the current event, because here in
  1.2123 +  // -insertText: they've already been taken into account in creating
  1.2124 +  // the input string.
  1.2125 +
  1.2126 +  if (currentKeyEvent) {
  1.2127 +    NSEvent* keyEvent = currentKeyEvent->mKeyEvent;
  1.2128 +    InitKeyEvent(keyEvent, keypressEvent, &str);
  1.2129 +  } else {
  1.2130 +    nsCocoaUtils::InitInputEvent(keypressEvent, static_cast<NSEvent*>(nullptr));
  1.2131 +    if (keypressEvent.isChar) {
  1.2132 +      keypressEvent.charCode = str.CharAt(0);
  1.2133 +    }
  1.2134 +    // Note that insertText is not called only at key pressing.
  1.2135 +    if (!keypressEvent.charCode) {
  1.2136 +      keypressEvent.keyCode =
  1.2137 +        WidgetUtils::ComputeKeyCodeFromChar(keypressEvent.charCode);
  1.2138 +    }
  1.2139 +  }
  1.2140 +
  1.2141 +  // Remove basic modifiers from keypress event because if they are included,
  1.2142 +  // nsPlaintextEditor ignores the event.
  1.2143 +  keypressEvent.modifiers &= ~(MODIFIER_CONTROL |
  1.2144 +                               MODIFIER_ALT |
  1.2145 +                               MODIFIER_META);
  1.2146 +
  1.2147 +  // TODO:
  1.2148 +  // If mCurrentKeyEvent.mKeyEvent is null and when we implement textInput
  1.2149 +  // event of DOM3 Events, we should dispatch it instead of keypress event.
  1.2150 +  bool keyPressHandled = DispatchEvent(keypressEvent);
  1.2151 +
  1.2152 +  // Note: mWidget might have become null here. Don't count on it from here on.
  1.2153 +
  1.2154 +  if (currentKeyEvent) {
  1.2155 +    currentKeyEvent->mKeyPressHandled = keyPressHandled;
  1.2156 +    currentKeyEvent->mKeyPressDispatched = true;
  1.2157 +  }
  1.2158 +
  1.2159 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2160 +}
  1.2161 +
  1.2162 +bool
  1.2163 +TextInputHandler::DoCommandBySelector(const char* aSelector)
  1.2164 +{
  1.2165 +  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
  1.2166 +
  1.2167 +  KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
  1.2168 +
  1.2169 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2170 +    ("%p TextInputHandler::DoCommandBySelector, aSelector=\"%s\", "
  1.2171 +     "Destroyed()=%s, keydownHandled=%s, keypressHandled=%s, "
  1.2172 +     "causedOtherKeyEvents=%s",
  1.2173 +     this, aSelector ? aSelector : "", TrueOrFalse(Destroyed()),
  1.2174 +     currentKeyEvent ?
  1.2175 +       TrueOrFalse(currentKeyEvent->mKeyDownHandled) : "N/A",
  1.2176 +     currentKeyEvent ?
  1.2177 +       TrueOrFalse(currentKeyEvent->mKeyPressHandled) : "N/A",
  1.2178 +     currentKeyEvent ?
  1.2179 +       TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A"));
  1.2180 +
  1.2181 +  if (currentKeyEvent && currentKeyEvent->CanDispatchKeyPressEvent()) {
  1.2182 +    WidgetKeyboardEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
  1.2183 +    InitKeyEvent(currentKeyEvent->mKeyEvent, keypressEvent);
  1.2184 +    currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent);
  1.2185 +    currentKeyEvent->mKeyPressDispatched = true;
  1.2186 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2187 +      ("%p TextInputHandler::DoCommandBySelector, keypress event "
  1.2188 +       "dispatched, Destroyed()=%s, keypressHandled=%s",
  1.2189 +       this, TrueOrFalse(Destroyed()),
  1.2190 +       TrueOrFalse(currentKeyEvent->mKeyPressHandled)));
  1.2191 +  }
  1.2192 +
  1.2193 +  return (!Destroyed() && currentKeyEvent &&
  1.2194 +          currentKeyEvent->IsDefaultPrevented());
  1.2195 +}
  1.2196 +
  1.2197 +
  1.2198 +#pragma mark -
  1.2199 +
  1.2200 +
  1.2201 +/******************************************************************************
  1.2202 + *
  1.2203 + *  IMEInputHandler implementation (static methods)
  1.2204 + *
  1.2205 + ******************************************************************************/
  1.2206 +
  1.2207 +bool IMEInputHandler::sStaticMembersInitialized = false;
  1.2208 +CFStringRef IMEInputHandler::sLatestIMEOpenedModeInputSourceID = nullptr;
  1.2209 +IMEInputHandler* IMEInputHandler::sFocusedIMEHandler = nullptr;
  1.2210 +
  1.2211 +// static
  1.2212 +void
  1.2213 +IMEInputHandler::InitStaticMembers()
  1.2214 +{
  1.2215 +  if (sStaticMembersInitialized)
  1.2216 +    return;
  1.2217 +  sStaticMembersInitialized = true;
  1.2218 +  // We need to check the keyboard layout changes on all applications.
  1.2219 +  CFNotificationCenterRef center = ::CFNotificationCenterGetDistributedCenter();
  1.2220 +  // XXX Don't we need to remove the observer at shut down?
  1.2221 +  // Mac Dev Center's document doesn't say how to remove the observer if
  1.2222 +  // the second parameter is NULL.
  1.2223 +  ::CFNotificationCenterAddObserver(center, NULL,
  1.2224 +      OnCurrentTextInputSourceChange,
  1.2225 +      kTISNotifySelectedKeyboardInputSourceChanged, NULL,
  1.2226 +      CFNotificationSuspensionBehaviorDeliverImmediately);
  1.2227 +  // Initiailize with the current keyboard layout
  1.2228 +  OnCurrentTextInputSourceChange(NULL, NULL,
  1.2229 +                                 kTISNotifySelectedKeyboardInputSourceChanged,
  1.2230 +                                 NULL, NULL);
  1.2231 +}
  1.2232 +
  1.2233 +// static
  1.2234 +void
  1.2235 +IMEInputHandler::OnCurrentTextInputSourceChange(CFNotificationCenterRef aCenter,
  1.2236 +                                                void* aObserver,
  1.2237 +                                                CFStringRef aName,
  1.2238 +                                                const void* aObject,
  1.2239 +                                                CFDictionaryRef aUserInfo)
  1.2240 +{
  1.2241 +  // Cache the latest IME opened mode to sLatestIMEOpenedModeInputSourceID.
  1.2242 +  TISInputSourceWrapper tis;
  1.2243 +  tis.InitByCurrentInputSource();
  1.2244 +  if (tis.IsOpenedIMEMode()) {
  1.2245 +    tis.GetInputSourceID(sLatestIMEOpenedModeInputSourceID);
  1.2246 +  }
  1.2247 +
  1.2248 +#ifdef PR_LOGGING
  1.2249 +  if (PR_LOG_TEST(gLog, PR_LOG_ALWAYS)) {
  1.2250 +    static CFStringRef sLastTIS = nullptr;
  1.2251 +    CFStringRef newTIS;
  1.2252 +    tis.GetInputSourceID(newTIS);
  1.2253 +    if (!sLastTIS ||
  1.2254 +        ::CFStringCompare(sLastTIS, newTIS, 0) != kCFCompareEqualTo) {
  1.2255 +      TISInputSourceWrapper tis1, tis2, tis3, tis4, tis5;
  1.2256 +      tis1.InitByCurrentKeyboardLayout();
  1.2257 +      tis2.InitByCurrentASCIICapableInputSource();
  1.2258 +      tis3.InitByCurrentASCIICapableKeyboardLayout();
  1.2259 +      tis4.InitByCurrentInputMethodKeyboardLayoutOverride();
  1.2260 +      tis5.InitByTISInputSourceRef(tis.GetKeyboardLayoutInputSource());
  1.2261 +      CFStringRef is0 = nullptr, is1 = nullptr, is2 = nullptr, is3 = nullptr,
  1.2262 +                  is4 = nullptr, is5 = nullptr, type0 = nullptr,
  1.2263 +                  lang0 = nullptr, bundleID0 = nullptr;
  1.2264 +      tis.GetInputSourceID(is0);
  1.2265 +      tis1.GetInputSourceID(is1);
  1.2266 +      tis2.GetInputSourceID(is2);
  1.2267 +      tis3.GetInputSourceID(is3);
  1.2268 +      tis4.GetInputSourceID(is4);
  1.2269 +      tis5.GetInputSourceID(is5);
  1.2270 +      tis.GetInputSourceType(type0);
  1.2271 +      tis.GetPrimaryLanguage(lang0);
  1.2272 +      tis.GetBundleID(bundleID0);
  1.2273 +
  1.2274 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2275 +        ("IMEInputHandler::OnCurrentTextInputSourceChange,\n"
  1.2276 +         "  Current Input Source is changed to:\n"
  1.2277 +         "    currentInputContext=%p\n"
  1.2278 +         "    %s\n"
  1.2279 +         "      type=%s %s\n"
  1.2280 +         "      overridden keyboard layout=%s\n"
  1.2281 +         "      used keyboard layout for translation=%s\n"
  1.2282 +         "    primary language=%s\n"
  1.2283 +         "    bundle ID=%s\n"
  1.2284 +         "    current ASCII capable Input Source=%s\n"
  1.2285 +         "    current Keyboard Layout=%s\n"
  1.2286 +         "    current ASCII capable Keyboard Layout=%s",
  1.2287 +         [NSTextInputContext currentInputContext], GetCharacters(is0),
  1.2288 +         GetCharacters(type0), tis.IsASCIICapable() ? "- ASCII capable " : "",
  1.2289 +         GetCharacters(is4), GetCharacters(is5),
  1.2290 +         GetCharacters(lang0), GetCharacters(bundleID0),
  1.2291 +         GetCharacters(is2), GetCharacters(is1), GetCharacters(is3)));
  1.2292 +    }
  1.2293 +    sLastTIS = newTIS;
  1.2294 +  }
  1.2295 +#endif // #ifdef PR_LOGGING
  1.2296 +}
  1.2297 +
  1.2298 +// static
  1.2299 +void
  1.2300 +IMEInputHandler::FlushPendingMethods(nsITimer* aTimer, void* aClosure)
  1.2301 +{
  1.2302 +  NS_ASSERTION(aClosure, "aClosure is null");
  1.2303 +  static_cast<IMEInputHandler*>(aClosure)->ExecutePendingMethods();
  1.2304 +}
  1.2305 +
  1.2306 +// static
  1.2307 +CFArrayRef
  1.2308 +IMEInputHandler::CreateAllIMEModeList()
  1.2309 +{
  1.2310 +  const void* keys[] = { kTISPropertyInputSourceType };
  1.2311 +  const void* values[] = { kTISTypeKeyboardInputMode };
  1.2312 +  CFDictionaryRef filter =
  1.2313 +    ::CFDictionaryCreate(kCFAllocatorDefault, keys, values, 1, NULL, NULL);
  1.2314 +  NS_ASSERTION(filter, "failed to create the filter");
  1.2315 +  CFArrayRef list = ::TISCreateInputSourceList(filter, true);
  1.2316 +  ::CFRelease(filter);
  1.2317 +  return list;
  1.2318 +}
  1.2319 +
  1.2320 +// static
  1.2321 +void
  1.2322 +IMEInputHandler::DebugPrintAllIMEModes()
  1.2323 +{
  1.2324 +#ifdef PR_LOGGING
  1.2325 +  if (PR_LOG_TEST(gLog, PR_LOG_ALWAYS)) {
  1.2326 +    CFArrayRef list = CreateAllIMEModeList();
  1.2327 +    PR_LOG(gLog, PR_LOG_ALWAYS, ("IME mode configuration:"));
  1.2328 +    CFIndex idx = ::CFArrayGetCount(list);
  1.2329 +    TISInputSourceWrapper tis;
  1.2330 +    for (CFIndex i = 0; i < idx; ++i) {
  1.2331 +      TISInputSourceRef inputSource = static_cast<TISInputSourceRef>(
  1.2332 +        const_cast<void *>(::CFArrayGetValueAtIndex(list, i)));
  1.2333 +      tis.InitByTISInputSourceRef(inputSource);
  1.2334 +      nsAutoString name, isid;
  1.2335 +      tis.GetLocalizedName(name);
  1.2336 +      tis.GetInputSourceID(isid);
  1.2337 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2338 +             ("  %s\t<%s>%s%s\n",
  1.2339 +              NS_ConvertUTF16toUTF8(name).get(),
  1.2340 +              NS_ConvertUTF16toUTF8(isid).get(),
  1.2341 +              tis.IsASCIICapable() ? "" : "\t(Isn't ASCII capable)",
  1.2342 +              tis.IsEnabled() ? "" : "\t(Isn't Enabled)"));
  1.2343 +    }
  1.2344 +    ::CFRelease(list);
  1.2345 +  }
  1.2346 +#endif // #ifdef PR_LOGGING
  1.2347 +}
  1.2348 +
  1.2349 +//static
  1.2350 +TSMDocumentID
  1.2351 +IMEInputHandler::GetCurrentTSMDocumentID()
  1.2352 +{
  1.2353 +  // At least on Mac OS X 10.6.x and 10.7.x, ::TSMGetActiveDocument() has a bug.
  1.2354 +  // The result of ::TSMGetActiveDocument() isn't modified for new active text
  1.2355 +  // input context until [NSTextInputContext currentInputContext] is called.
  1.2356 +  // Therefore, we need to call it here.
  1.2357 +  [NSTextInputContext currentInputContext];
  1.2358 +  return ::TSMGetActiveDocument();
  1.2359 +}
  1.2360 +
  1.2361 +
  1.2362 +#pragma mark -
  1.2363 +
  1.2364 +
  1.2365 +/******************************************************************************
  1.2366 + *
  1.2367 + *  IMEInputHandler implementation #1
  1.2368 + *    The methods are releated to the pending methods.  Some jobs should be
  1.2369 + *    run after the stack is finished, e.g, some methods cannot run the jobs
  1.2370 + *    during processing the focus event.  And also some other jobs should be
  1.2371 + *    run at the next focus event is processed.
  1.2372 + *    The pending methods are recorded in mPendingMethods.  They are executed
  1.2373 + *    by ExecutePendingMethods via FlushPendingMethods.
  1.2374 + *
  1.2375 + ******************************************************************************/
  1.2376 +
  1.2377 +void
  1.2378 +IMEInputHandler::NotifyIMEOfFocusChangeInGecko()
  1.2379 +{
  1.2380 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2381 +
  1.2382 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2383 +    ("%p IMEInputHandler::NotifyIMEOfFocusChangeInGecko, "
  1.2384 +     "Destroyed()=%s, IsFocused()=%s, inputContext=%p",
  1.2385 +     this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()),
  1.2386 +     mView ? [mView inputContext] : nullptr));
  1.2387 +
  1.2388 +  if (Destroyed()) {
  1.2389 +    return;
  1.2390 +  }
  1.2391 +
  1.2392 +  if (!IsFocused()) {
  1.2393 +    // retry at next focus event
  1.2394 +    mPendingMethods |= kNotifyIMEOfFocusChangeInGecko;
  1.2395 +    return;
  1.2396 +  }
  1.2397 +
  1.2398 +  MOZ_ASSERT(mView);
  1.2399 +  NSTextInputContext* inputContext = [mView inputContext];
  1.2400 +  NS_ENSURE_TRUE_VOID(inputContext);
  1.2401 +
  1.2402 +  // When an <input> element on a XUL <panel> element gets focus from an <input>
  1.2403 +  // element on the opener window of the <panel> element, the owner window
  1.2404 +  // still has native focus.  Therefore, IMEs may store the opener window's
  1.2405 +  // level at this time because they don't know the actual focus is moved to
  1.2406 +  // different window.  If IMEs try to get the newest window level after the
  1.2407 +  // focus change, we return the window level of the XUL <panel>'s widget.
  1.2408 +  // Therefore, let's emulate the native focus change.  Then, IMEs can refresh
  1.2409 +  // the stored window level.
  1.2410 +  [inputContext deactivate];
  1.2411 +  [inputContext activate];
  1.2412 +
  1.2413 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2414 +}
  1.2415 +
  1.2416 +void
  1.2417 +IMEInputHandler::DiscardIMEComposition()
  1.2418 +{
  1.2419 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2420 +
  1.2421 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2422 +    ("%p IMEInputHandler::DiscardIMEComposition, "
  1.2423 +     "Destroyed()=%s, IsFocused()=%s, mView=%p, inputContext=%p",
  1.2424 +     this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()),
  1.2425 +     mView, mView ? [mView inputContext] : nullptr));
  1.2426 +
  1.2427 +  if (Destroyed()) {
  1.2428 +    return;
  1.2429 +  }
  1.2430 +
  1.2431 +  if (!IsFocused()) {
  1.2432 +    // retry at next focus event
  1.2433 +    mPendingMethods |= kDiscardIMEComposition;
  1.2434 +    return;
  1.2435 +  }
  1.2436 +
  1.2437 +  NS_ENSURE_TRUE_VOID(mView);
  1.2438 +  NSTextInputContext* inputContext = [mView inputContext];
  1.2439 +  NS_ENSURE_TRUE_VOID(inputContext);
  1.2440 +  mIgnoreIMECommit = true;
  1.2441 +  [inputContext discardMarkedText];
  1.2442 +  mIgnoreIMECommit = false;
  1.2443 +
  1.2444 +  NS_OBJC_END_TRY_ABORT_BLOCK
  1.2445 +}
  1.2446 +
  1.2447 +void
  1.2448 +IMEInputHandler::SyncASCIICapableOnly()
  1.2449 +{
  1.2450 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2451 +
  1.2452 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2453 +    ("%p IMEInputHandler::SyncASCIICapableOnly, "
  1.2454 +     "Destroyed()=%s, IsFocused()=%s, mIsASCIICapableOnly=%s, "
  1.2455 +     "GetCurrentTSMDocumentID()=%p",
  1.2456 +     this, TrueOrFalse(Destroyed()), TrueOrFalse(IsFocused()),
  1.2457 +     TrueOrFalse(mIsASCIICapableOnly), GetCurrentTSMDocumentID()));
  1.2458 +
  1.2459 +  if (Destroyed()) {
  1.2460 +    return;
  1.2461 +  }
  1.2462 +
  1.2463 +  if (!IsFocused()) {
  1.2464 +    // retry at next focus event
  1.2465 +    mPendingMethods |= kSyncASCIICapableOnly;
  1.2466 +    return;
  1.2467 +  }
  1.2468 +
  1.2469 +  TSMDocumentID doc = GetCurrentTSMDocumentID();
  1.2470 +  if (!doc) {
  1.2471 +    // retry
  1.2472 +    mPendingMethods |= kSyncASCIICapableOnly;
  1.2473 +    NS_WARNING("Application is active but there is no active document");
  1.2474 +    ResetTimer();
  1.2475 +    return;
  1.2476 +  }
  1.2477 +
  1.2478 +  if (mIsASCIICapableOnly) {
  1.2479 +    CFArrayRef ASCIICapableTISList = ::TISCreateASCIICapableInputSourceList();
  1.2480 +    ::TSMSetDocumentProperty(doc,
  1.2481 +                             kTSMDocumentEnabledInputSourcesPropertyTag,
  1.2482 +                             sizeof(CFArrayRef),
  1.2483 +                             &ASCIICapableTISList);
  1.2484 +    ::CFRelease(ASCIICapableTISList);
  1.2485 +  } else {
  1.2486 +    ::TSMRemoveDocumentProperty(doc,
  1.2487 +                                kTSMDocumentEnabledInputSourcesPropertyTag);
  1.2488 +  }
  1.2489 +
  1.2490 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2491 +}
  1.2492 +
  1.2493 +void
  1.2494 +IMEInputHandler::ResetTimer()
  1.2495 +{
  1.2496 +  NS_ASSERTION(mPendingMethods != 0,
  1.2497 +               "There are not pending methods, why this is called?");
  1.2498 +  if (mTimer) {
  1.2499 +    mTimer->Cancel();
  1.2500 +  } else {
  1.2501 +    mTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
  1.2502 +    NS_ENSURE_TRUE(mTimer, );
  1.2503 +  }
  1.2504 +  mTimer->InitWithFuncCallback(FlushPendingMethods, this, 0,
  1.2505 +                               nsITimer::TYPE_ONE_SHOT);
  1.2506 +}
  1.2507 +
  1.2508 +void
  1.2509 +IMEInputHandler::ExecutePendingMethods()
  1.2510 +{
  1.2511 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2512 +
  1.2513 +  if (mTimer) {
  1.2514 +    mTimer->Cancel();
  1.2515 +    mTimer = nullptr;
  1.2516 +  }
  1.2517 +
  1.2518 +  if (![[NSApplication sharedApplication] isActive]) {
  1.2519 +    mIsInFocusProcessing = false;
  1.2520 +    // If we're not active, we should retry at focus event
  1.2521 +    return;
  1.2522 +  }
  1.2523 +
  1.2524 +  uint32_t pendingMethods = mPendingMethods;
  1.2525 +  // First, reset the pending method flags because if each methods cannot
  1.2526 +  // run now, they can reentry to the pending flags by theirselves.
  1.2527 +  mPendingMethods = 0;
  1.2528 +
  1.2529 +  if (pendingMethods & kDiscardIMEComposition)
  1.2530 +    DiscardIMEComposition();
  1.2531 +  if (pendingMethods & kSyncASCIICapableOnly)
  1.2532 +    SyncASCIICapableOnly();
  1.2533 +  if (pendingMethods & kNotifyIMEOfFocusChangeInGecko) {
  1.2534 +    NotifyIMEOfFocusChangeInGecko();
  1.2535 +  }
  1.2536 +
  1.2537 +  mIsInFocusProcessing = false;
  1.2538 +
  1.2539 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2540 +}
  1.2541 +
  1.2542 +#pragma mark -
  1.2543 +
  1.2544 +
  1.2545 +/******************************************************************************
  1.2546 + *
  1.2547 + * IMEInputHandler implementation (native event handlers)
  1.2548 + *
  1.2549 + ******************************************************************************/
  1.2550 +
  1.2551 +uint32_t
  1.2552 +IMEInputHandler::ConvertToTextRangeType(uint32_t aUnderlineStyle,
  1.2553 +                                        NSRange& aSelectedRange)
  1.2554 +{
  1.2555 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2556 +    ("%p IMEInputHandler::ConvertToTextRangeType, "
  1.2557 +     "aUnderlineStyle=%llu, aSelectedRange.length=%llu,",
  1.2558 +     this, aUnderlineStyle, aSelectedRange.length));
  1.2559 +
  1.2560 +  // We assume that aUnderlineStyle is NSUnderlineStyleSingle or
  1.2561 +  // NSUnderlineStyleThick.  NSUnderlineStyleThick should indicate a selected
  1.2562 +  // clause.  Otherwise, should indicate non-selected clause.
  1.2563 +
  1.2564 +  if (aSelectedRange.length == 0) {
  1.2565 +    switch (aUnderlineStyle) {
  1.2566 +      case NSUnderlineStyleSingle:
  1.2567 +        return NS_TEXTRANGE_RAWINPUT;
  1.2568 +      case NSUnderlineStyleThick:
  1.2569 +        return NS_TEXTRANGE_SELECTEDRAWTEXT;
  1.2570 +      default:
  1.2571 +        NS_WARNING("Unexpected line style");
  1.2572 +        return NS_TEXTRANGE_SELECTEDRAWTEXT;
  1.2573 +    }
  1.2574 +  }
  1.2575 +
  1.2576 +  switch (aUnderlineStyle) {
  1.2577 +    case NSUnderlineStyleSingle:
  1.2578 +      return NS_TEXTRANGE_CONVERTEDTEXT;
  1.2579 +    case NSUnderlineStyleThick:
  1.2580 +      return NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  1.2581 +    default:
  1.2582 +      NS_WARNING("Unexpected line style");
  1.2583 +      return NS_TEXTRANGE_SELECTEDCONVERTEDTEXT;
  1.2584 +  }
  1.2585 +}
  1.2586 +
  1.2587 +uint32_t
  1.2588 +IMEInputHandler::GetRangeCount(NSAttributedString *aAttrString)
  1.2589 +{
  1.2590 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.2591 +
  1.2592 +  // Iterate through aAttrString for the NSUnderlineStyleAttributeName and
  1.2593 +  // count the different segments adjusting limitRange as we go.
  1.2594 +  uint32_t count = 0;
  1.2595 +  NSRange effectiveRange;
  1.2596 +  NSRange limitRange = NSMakeRange(0, [aAttrString length]);
  1.2597 +  while (limitRange.length > 0) {
  1.2598 +    [aAttrString  attribute:NSUnderlineStyleAttributeName 
  1.2599 +                    atIndex:limitRange.location 
  1.2600 +      longestEffectiveRange:&effectiveRange
  1.2601 +                    inRange:limitRange];
  1.2602 +    limitRange =
  1.2603 +      NSMakeRange(NSMaxRange(effectiveRange), 
  1.2604 +                  NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
  1.2605 +    count++;
  1.2606 +  }
  1.2607 +
  1.2608 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2609 +    ("%p IMEInputHandler::GetRangeCount, aAttrString=\"%s\", count=%llu",
  1.2610 +     this, GetCharacters([aAttrString string]), count));
  1.2611 +
  1.2612 +  return count;
  1.2613 +
  1.2614 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
  1.2615 +}
  1.2616 +
  1.2617 +already_AddRefed<mozilla::TextRangeArray>
  1.2618 +IMEInputHandler::CreateTextRangeArray(NSAttributedString *aAttrString,
  1.2619 +                                      NSRange& aSelectedRange)
  1.2620 +{
  1.2621 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.2622 +
  1.2623 +  // Convert the Cocoa range into the TextRange Array used in Gecko.
  1.2624 +  // Iterate through the attributed string and map the underline attribute to
  1.2625 +  // Gecko IME textrange attributes.  We may need to change the code here if
  1.2626 +  // we change the implementation of validAttributesForMarkedText.
  1.2627 +  NSRange limitRange = NSMakeRange(0, [aAttrString length]);
  1.2628 +  uint32_t rangeCount = GetRangeCount(aAttrString);
  1.2629 +  nsRefPtr<mozilla::TextRangeArray> textRangeArray =
  1.2630 +                                      new mozilla::TextRangeArray();
  1.2631 +  for (uint32_t i = 0; i < rangeCount && limitRange.length > 0; i++) {
  1.2632 +    NSRange effectiveRange;
  1.2633 +    id attributeValue = [aAttrString attribute:NSUnderlineStyleAttributeName
  1.2634 +                                       atIndex:limitRange.location
  1.2635 +                         longestEffectiveRange:&effectiveRange
  1.2636 +                                       inRange:limitRange];
  1.2637 +
  1.2638 +    TextRange range;
  1.2639 +    range.mStartOffset = effectiveRange.location;
  1.2640 +    range.mEndOffset = NSMaxRange(effectiveRange);
  1.2641 +    range.mRangeType =
  1.2642 +      ConvertToTextRangeType([attributeValue intValue], aSelectedRange);
  1.2643 +    textRangeArray->AppendElement(range);
  1.2644 +
  1.2645 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2646 +      ("%p IMEInputHandler::CreateTextRangeArray, "
  1.2647 +       "range={ mStartOffset=%llu, mEndOffset=%llu, mRangeType=%s }",
  1.2648 +       this, range.mStartOffset, range.mEndOffset,
  1.2649 +       GetRangeTypeName(range.mRangeType)));
  1.2650 +
  1.2651 +    limitRange =
  1.2652 +      NSMakeRange(NSMaxRange(effectiveRange), 
  1.2653 +                  NSMaxRange(limitRange) - NSMaxRange(effectiveRange));
  1.2654 +  }
  1.2655 +
  1.2656 +  // Get current caret position.
  1.2657 +  TextRange range;
  1.2658 +  range.mStartOffset = aSelectedRange.location + aSelectedRange.length;
  1.2659 +  range.mEndOffset = range.mStartOffset;
  1.2660 +  range.mRangeType = NS_TEXTRANGE_CARETPOSITION;
  1.2661 +  textRangeArray->AppendElement(range);
  1.2662 +
  1.2663 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2664 +    ("%p IMEInputHandler::CreateTextRangeArray, "
  1.2665 +     "range={ mStartOffset=%llu, mEndOffset=%llu, mRangeType=%s }",
  1.2666 +     this, range.mStartOffset, range.mEndOffset,
  1.2667 +     GetRangeTypeName(range.mRangeType)));
  1.2668 +
  1.2669 +  return textRangeArray.forget();
  1.2670 +
  1.2671 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.2672 +}
  1.2673 +
  1.2674 +bool
  1.2675 +IMEInputHandler::DispatchTextEvent(const nsString& aText,
  1.2676 +                                   NSAttributedString* aAttrString,
  1.2677 +                                   NSRange& aSelectedRange,
  1.2678 +                                   bool aDoCommit)
  1.2679 +{
  1.2680 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2681 +    ("%p IMEInputHandler::DispatchTextEvent, "
  1.2682 +     "aText=\"%s\", aAttrString=\"%s\", "
  1.2683 +     "aSelectedRange={ location=%llu, length=%llu }, "
  1.2684 +     "aDoCommit=%s, Destroyed()=%s",
  1.2685 +     this, NS_ConvertUTF16toUTF8(aText).get(),
  1.2686 +     GetCharacters([aAttrString string]),
  1.2687 +     aSelectedRange.location, aSelectedRange.length,
  1.2688 +     TrueOrFalse(aDoCommit), TrueOrFalse(Destroyed())));
  1.2689 +
  1.2690 +  NS_ENSURE_TRUE(!Destroyed(), false);
  1.2691 +
  1.2692 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.2693 +
  1.2694 +  WidgetTextEvent textEvent(true, NS_TEXT_TEXT, mWidget);
  1.2695 +  textEvent.time = PR_IntervalNow();
  1.2696 +  textEvent.theText = aText;
  1.2697 +  if (!aDoCommit) {
  1.2698 +    textEvent.mRanges = CreateTextRangeArray(aAttrString, aSelectedRange);
  1.2699 +  }
  1.2700 +
  1.2701 +  if (textEvent.theText != mLastDispatchedCompositionString) {
  1.2702 +    WidgetCompositionEvent compositionUpdate(true, NS_COMPOSITION_UPDATE,
  1.2703 +                                             mWidget);
  1.2704 +    compositionUpdate.time = textEvent.time;
  1.2705 +    compositionUpdate.data = textEvent.theText;
  1.2706 +    mLastDispatchedCompositionString = textEvent.theText;
  1.2707 +    DispatchEvent(compositionUpdate);
  1.2708 +    if (mIsInFocusProcessing || Destroyed()) {
  1.2709 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2710 +        ("%p IMEInputHandler::DispatchTextEvent, compositionupdate causes "
  1.2711 +         "aborting the composition, mIsInFocusProcessing=%s, Destryoed()=%s",
  1.2712 +         this, TrueOrFalse(mIsInFocusProcessing), TrueOrFalse(Destroyed())));
  1.2713 +      if (Destroyed()) {
  1.2714 +        return true;
  1.2715 +      }
  1.2716 +    }
  1.2717 +  }
  1.2718 +
  1.2719 +  return DispatchEvent(textEvent);
  1.2720 +}
  1.2721 +
  1.2722 +void
  1.2723 +IMEInputHandler::InitCompositionEvent(WidgetCompositionEvent& aCompositionEvent)
  1.2724 +{
  1.2725 +  aCompositionEvent.time = PR_IntervalNow();
  1.2726 +}
  1.2727 +
  1.2728 +void
  1.2729 +IMEInputHandler::InsertTextAsCommittingComposition(
  1.2730 +                   NSAttributedString* aAttrString,
  1.2731 +                   NSRange* aReplacementRange)
  1.2732 +{
  1.2733 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2734 +
  1.2735 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2736 +    ("%p IMEInputHandler::InsertTextAsCommittingComposition, "
  1.2737 +     "aAttrString=\"%s\", aReplacementRange=%p { location=%llu, length=%llu }, "
  1.2738 +     "Destroyed()=%s, IsIMEComposing()=%s, "
  1.2739 +     "mMarkedRange={ location=%llu, length=%llu }",
  1.2740 +     this, GetCharacters([aAttrString string]), aReplacementRange,
  1.2741 +     aReplacementRange ? aReplacementRange->location : 0,
  1.2742 +     aReplacementRange ? aReplacementRange->length : 0,
  1.2743 +     TrueOrFalse(Destroyed()), TrueOrFalse(IsIMEComposing()),
  1.2744 +     mMarkedRange.location, mMarkedRange.length));
  1.2745 +
  1.2746 +  if (IgnoreIMECommit()) {
  1.2747 +    MOZ_CRASH("IMEInputHandler::InsertTextAsCommittingComposition() must not"
  1.2748 +              "be called while canceling the composition");
  1.2749 +  }
  1.2750 +
  1.2751 +  if (Destroyed()) {
  1.2752 +    return;
  1.2753 +  }
  1.2754 +
  1.2755 +  // First, commit current composition with the latest composition string if the
  1.2756 +  // replacement range is different from marked range.
  1.2757 +  if (IsIMEComposing() && aReplacementRange &&
  1.2758 +      aReplacementRange->location != NSNotFound &&
  1.2759 +      !NSEqualRanges(MarkedRange(), *aReplacementRange)) {
  1.2760 +    NSString* latestStr =
  1.2761 +      nsCocoaUtils::ToNSString(mLastDispatchedCompositionString);
  1.2762 +    NSAttributedString* attrLatestStr =
  1.2763 +      [[[NSAttributedString alloc] initWithString:latestStr] autorelease];
  1.2764 +    InsertTextAsCommittingComposition(attrLatestStr, nullptr);
  1.2765 +    if (Destroyed()) {
  1.2766 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2767 +        ("%p IMEInputHandler::InsertTextAsCommittingComposition, "
  1.2768 +         "destroyed by commiting composition for setting replacement range",
  1.2769 +         this));
  1.2770 +      return;
  1.2771 +    }
  1.2772 +  }
  1.2773 +
  1.2774 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.2775 +
  1.2776 +  nsString str;
  1.2777 +  nsCocoaUtils::GetStringForNSString([aAttrString string], str);
  1.2778 +
  1.2779 +  if (!IsIMEComposing()) {
  1.2780 +    // If there is no selection and replacement range is specified, set the
  1.2781 +    // range as selection.
  1.2782 +    if (aReplacementRange && aReplacementRange->location != NSNotFound &&
  1.2783 +        !NSEqualRanges(SelectedRange(), *aReplacementRange)) {
  1.2784 +      NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
  1.2785 +    }
  1.2786 +
  1.2787 +    // XXXmnakano Probably, we shouldn't emulate composition in this case.
  1.2788 +    // I think that we should just fire DOM3 textInput event if we implement it.
  1.2789 +    WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
  1.2790 +    InitCompositionEvent(compStart);
  1.2791 +
  1.2792 +    DispatchEvent(compStart);
  1.2793 +    if (Destroyed()) {
  1.2794 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2795 +        ("%p IMEInputHandler::InsertTextAsCommittingComposition, "
  1.2796 +         "destroyed by compositionstart event", this));
  1.2797 +      return;
  1.2798 +    }
  1.2799 +
  1.2800 +    OnStartIMEComposition();
  1.2801 +  }
  1.2802 +
  1.2803 +  NSRange range = NSMakeRange(0, str.Length());
  1.2804 +  DispatchTextEvent(str, aAttrString, range, true);
  1.2805 +  if (Destroyed()) {
  1.2806 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2807 +      ("%p IMEInputHandler::InsertTextAsCommittingComposition, "
  1.2808 +       "destroyed by text event", this));
  1.2809 +    return;
  1.2810 +  }
  1.2811 +
  1.2812 +  OnUpdateIMEComposition([aAttrString string]);
  1.2813 +
  1.2814 +  WidgetCompositionEvent compEnd(true, NS_COMPOSITION_END, mWidget);
  1.2815 +  InitCompositionEvent(compEnd);
  1.2816 +  compEnd.data = mLastDispatchedCompositionString;
  1.2817 +  DispatchEvent(compEnd);
  1.2818 +  if (Destroyed()) {
  1.2819 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2820 +      ("%p IMEInputHandler::InsertTextAsCommittingComposition, "
  1.2821 +       "destroyed by compositionend event", this));
  1.2822 +    return;
  1.2823 +  }
  1.2824 +
  1.2825 +  OnEndIMEComposition();
  1.2826 +
  1.2827 +  mMarkedRange = NSMakeRange(NSNotFound, 0);
  1.2828 +
  1.2829 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2830 +}
  1.2831 +
  1.2832 +void
  1.2833 +IMEInputHandler::SetMarkedText(NSAttributedString* aAttrString,
  1.2834 +                               NSRange& aSelectedRange,
  1.2835 +                               NSRange* aReplacementRange)
  1.2836 +{
  1.2837 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.2838 +
  1.2839 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2840 +    ("%p IMEInputHandler::SetMarkedText, "
  1.2841 +     "aAttrString=\"%s\", aSelectedRange={ location=%llu, length=%llu }, "
  1.2842 +     "aReplacementRange=%p { location=%llu, length=%llu }, "
  1.2843 +     "Destroyed()=%s, IgnoreIMEComposition()=%s, IsIMEComposing()=%s, "
  1.2844 +     "mMarkedRange={ location=%llu, length=%llu }",
  1.2845 +     this, GetCharacters([aAttrString string]),
  1.2846 +     aSelectedRange.location, aSelectedRange.length, aReplacementRange,
  1.2847 +     aReplacementRange ? aReplacementRange->location : 0,
  1.2848 +     aReplacementRange ? aReplacementRange->length : 0,
  1.2849 +     TrueOrFalse(Destroyed()), TrueOrFalse(IgnoreIMEComposition()),
  1.2850 +     TrueOrFalse(IsIMEComposing()),
  1.2851 +     mMarkedRange.location, mMarkedRange.length));
  1.2852 +
  1.2853 +  if (Destroyed() || IgnoreIMEComposition()) {
  1.2854 +    return;
  1.2855 +  }
  1.2856 +
  1.2857 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.2858 +
  1.2859 +  // First, commit current composition with the latest composition string if the
  1.2860 +  // replacement range is different from marked range.
  1.2861 +  if (IsIMEComposing() && aReplacementRange &&
  1.2862 +      aReplacementRange->location != NSNotFound &&
  1.2863 +      !NSEqualRanges(MarkedRange(), *aReplacementRange)) {
  1.2864 +    NSString* latestStr =
  1.2865 +      nsCocoaUtils::ToNSString(mLastDispatchedCompositionString);
  1.2866 +    NSAttributedString* attrLatestStr =
  1.2867 +      [[[NSAttributedString alloc] initWithString:latestStr] autorelease];
  1.2868 +    bool ignoreIMECommit = mIgnoreIMECommit;
  1.2869 +    mIgnoreIMECommit = false;
  1.2870 +    InsertTextAsCommittingComposition(attrLatestStr, nullptr);
  1.2871 +    mIgnoreIMECommit = ignoreIMECommit;
  1.2872 +    if (Destroyed()) {
  1.2873 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2874 +        ("%p IMEInputHandler::SetMarkedText, "
  1.2875 +         "destroyed by commiting composition for setting replacement range",
  1.2876 +         this));
  1.2877 +      return;
  1.2878 +    }
  1.2879 +  }
  1.2880 +
  1.2881 +  nsString str;
  1.2882 +  nsCocoaUtils::GetStringForNSString([aAttrString string], str);
  1.2883 +
  1.2884 +  mMarkedRange.length = str.Length();
  1.2885 +
  1.2886 +  if (!IsIMEComposing() && !str.IsEmpty()) {
  1.2887 +    // If there is no selection and replacement range is specified, set the
  1.2888 +    // range as selection.
  1.2889 +    if (aReplacementRange && aReplacementRange->location != NSNotFound &&
  1.2890 +        !NSEqualRanges(SelectedRange(), *aReplacementRange)) {
  1.2891 +      NS_ENSURE_TRUE_VOID(SetSelection(*aReplacementRange));
  1.2892 +    }
  1.2893 +
  1.2894 +    mMarkedRange.location = SelectedRange().location;
  1.2895 +
  1.2896 +    WidgetCompositionEvent compStart(true, NS_COMPOSITION_START, mWidget);
  1.2897 +    InitCompositionEvent(compStart);
  1.2898 +
  1.2899 +    DispatchEvent(compStart);
  1.2900 +    if (Destroyed()) {
  1.2901 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2902 +        ("%p IMEInputHandler::SetMarkedText, "
  1.2903 +         "destroyed by compositionstart event", this));
  1.2904 +      return;
  1.2905 +    }
  1.2906 +
  1.2907 +    OnStartIMEComposition();
  1.2908 +  }
  1.2909 +
  1.2910 +  if (IsIMEComposing()) {
  1.2911 +    OnUpdateIMEComposition([aAttrString string]);
  1.2912 +
  1.2913 +    bool doCommit = str.IsEmpty();
  1.2914 +    DispatchTextEvent(str, aAttrString, aSelectedRange, doCommit);
  1.2915 +    if (Destroyed()) {
  1.2916 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2917 +        ("%p IMEInputHandler::SetMarkedText, "
  1.2918 +         "destroyed by text event", this));
  1.2919 +      return;
  1.2920 +    }
  1.2921 +
  1.2922 +    if (doCommit) {
  1.2923 +      WidgetCompositionEvent compEnd(true, NS_COMPOSITION_END, mWidget);
  1.2924 +      InitCompositionEvent(compEnd);
  1.2925 +      compEnd.data = mLastDispatchedCompositionString;
  1.2926 +      DispatchEvent(compEnd);
  1.2927 +      if (Destroyed()) {
  1.2928 +        PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2929 +          ("%p IMEInputHandler::SetMarkedText, "
  1.2930 +           "destroyed by compositionend event", this));
  1.2931 +        return;
  1.2932 +      }
  1.2933 +      OnEndIMEComposition();
  1.2934 +    }
  1.2935 +  }
  1.2936 +
  1.2937 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.2938 +}
  1.2939 +
  1.2940 +NSInteger
  1.2941 +IMEInputHandler::ConversationIdentifier()
  1.2942 +{
  1.2943 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2944 +    ("%p IMEInputHandler::ConversationIdentifier, Destroyed()=%s",
  1.2945 +     this, TrueOrFalse(Destroyed())));
  1.2946 +
  1.2947 +  if (Destroyed()) {
  1.2948 +    return reinterpret_cast<NSInteger>(mView);
  1.2949 +  }
  1.2950 +
  1.2951 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.2952 +
  1.2953 +  // NOTE: The size of NSInteger is same as pointer size.
  1.2954 +  WidgetQueryContentEvent textContent(true, NS_QUERY_TEXT_CONTENT, mWidget);
  1.2955 +  textContent.InitForQueryTextContent(0, 0);
  1.2956 +  DispatchEvent(textContent);
  1.2957 +  if (!textContent.mSucceeded) {
  1.2958 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2959 +      ("%p IMEInputHandler::ConversationIdentifier, Failed", this));
  1.2960 +    return reinterpret_cast<NSInteger>(mView);
  1.2961 +  }
  1.2962 +  // XXX This might return same ID as a previously existing editor if the
  1.2963 +  //     deleted editor was created at the same address.  Is there a better way?
  1.2964 +  return reinterpret_cast<NSInteger>(textContent.mReply.mContentsRoot);
  1.2965 +}
  1.2966 +
  1.2967 +NSAttributedString*
  1.2968 +IMEInputHandler::GetAttributedSubstringFromRange(NSRange& aRange,
  1.2969 +                                                 NSRange* aActualRange)
  1.2970 +{
  1.2971 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.2972 +
  1.2973 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2974 +    ("%p IMEInputHandler::GetAttributedSubstringFromRange, "
  1.2975 +     "aRange={ location=%llu, length=%llu }, aActualRange=%p, Destroyed()=%s",
  1.2976 +     this, aRange.location, aRange.length, aActualRange,
  1.2977 +     TrueOrFalse(Destroyed())));
  1.2978 +
  1.2979 +  if (aActualRange) {
  1.2980 +    *aActualRange = NSMakeRange(NSNotFound, 0);
  1.2981 +  }
  1.2982 +
  1.2983 +  if (Destroyed() || aRange.location == NSNotFound || aRange.length == 0) {
  1.2984 +    return nil;
  1.2985 +  }
  1.2986 +
  1.2987 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.2988 +
  1.2989 +  nsAutoString str;
  1.2990 +  WidgetQueryContentEvent textContent(true, NS_QUERY_TEXT_CONTENT, mWidget);
  1.2991 +  textContent.InitForQueryTextContent(aRange.location, aRange.length);
  1.2992 +  DispatchEvent(textContent);
  1.2993 +
  1.2994 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.2995 +    ("%p IMEInputHandler::GetAttributedSubstringFromRange, "
  1.2996 +     "textContent={ mSucceeded=%s, mReply={ mString=\"%s\", mOffset=%llu } }",
  1.2997 +     this, TrueOrFalse(textContent.mSucceeded),
  1.2998 +     NS_ConvertUTF16toUTF8(textContent.mReply.mString).get(),
  1.2999 +     textContent.mReply.mOffset));
  1.3000 +
  1.3001 +  if (!textContent.mSucceeded) {
  1.3002 +    return nil;
  1.3003 +  }
  1.3004 +
  1.3005 +  NSString* nsstr = nsCocoaUtils::ToNSString(textContent.mReply.mString);
  1.3006 +  NSAttributedString* result =
  1.3007 +    [[[NSAttributedString alloc] initWithString:nsstr
  1.3008 +                                     attributes:nil] autorelease];
  1.3009 +  if (aActualRange) {
  1.3010 +    aActualRange->location = textContent.mReply.mOffset;
  1.3011 +    aActualRange->length = textContent.mReply.mString.Length();
  1.3012 +  }
  1.3013 +  return result;
  1.3014 +
  1.3015 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.3016 +}
  1.3017 +
  1.3018 +bool
  1.3019 +IMEInputHandler::HasMarkedText()
  1.3020 +{
  1.3021 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3022 +    ("%p IMEInputHandler::HasMarkedText, "
  1.3023 +     "mMarkedRange={ location=%llu, length=%llu }",
  1.3024 +     this, mMarkedRange.location, mMarkedRange.length));
  1.3025 +
  1.3026 +  return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
  1.3027 +}
  1.3028 +
  1.3029 +NSRange
  1.3030 +IMEInputHandler::MarkedRange()
  1.3031 +{
  1.3032 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3033 +    ("%p IMEInputHandler::MarkedRange, "
  1.3034 +     "mMarkedRange={ location=%llu, length=%llu }",
  1.3035 +     this, mMarkedRange.location, mMarkedRange.length));
  1.3036 +
  1.3037 +  if (!HasMarkedText()) {
  1.3038 +    return NSMakeRange(NSNotFound, 0);
  1.3039 +  }
  1.3040 +  return mMarkedRange;
  1.3041 +}
  1.3042 +
  1.3043 +NSRange
  1.3044 +IMEInputHandler::SelectedRange()
  1.3045 +{
  1.3046 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.3047 +
  1.3048 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3049 +    ("%p IMEInputHandler::SelectedRange, Destroyed()=%s, mSelectedRange={ "
  1.3050 +     "location=%llu, length=%llu }",
  1.3051 +     this, TrueOrFalse(Destroyed()), mSelectedRange.location,
  1.3052 +     mSelectedRange.length));
  1.3053 +
  1.3054 +  if (Destroyed()) {
  1.3055 +    return mSelectedRange;
  1.3056 +  }
  1.3057 +
  1.3058 +  if (mSelectedRange.location != NSNotFound) {
  1.3059 +    MOZ_ASSERT(mIMEHasFocus);
  1.3060 +    return mSelectedRange;
  1.3061 +  }
  1.3062 +
  1.3063 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.3064 +
  1.3065 +  WidgetQueryContentEvent selection(true, NS_QUERY_SELECTED_TEXT, mWidget);
  1.3066 +  DispatchEvent(selection);
  1.3067 +
  1.3068 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3069 +    ("%p IMEInputHandler::SelectedRange, selection={ mSucceeded=%s, "
  1.3070 +     "mReply={ mOffset=%llu, mString.Length()=%llu } }",
  1.3071 +     this, TrueOrFalse(selection.mSucceeded), selection.mReply.mOffset,
  1.3072 +     selection.mReply.mString.Length()));
  1.3073 +
  1.3074 +  if (!selection.mSucceeded) {
  1.3075 +    return mSelectedRange;
  1.3076 +  }
  1.3077 +
  1.3078 +  if (mIMEHasFocus) {
  1.3079 +    mSelectedRange.location = selection.mReply.mOffset;
  1.3080 +    mSelectedRange.length = selection.mReply.mString.Length();
  1.3081 +    return mSelectedRange;
  1.3082 +  }
  1.3083 +
  1.3084 +  return NSMakeRange(selection.mReply.mOffset,
  1.3085 +                     selection.mReply.mString.Length());
  1.3086 +
  1.3087 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(mSelectedRange);
  1.3088 +}
  1.3089 +
  1.3090 +NSRect
  1.3091 +IMEInputHandler::FirstRectForCharacterRange(NSRange& aRange,
  1.3092 +                                            NSRange* aActualRange)
  1.3093 +{
  1.3094 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.3095 +
  1.3096 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3097 +    ("%p IMEInputHandler::FirstRectForCharacterRange, Destroyed()=%s, "
  1.3098 +     "aRange={ location=%llu, length=%llu }, aActualRange=%p }",
  1.3099 +     this, TrueOrFalse(Destroyed()), aRange.location, aRange.length,
  1.3100 +     aActualRange));
  1.3101 +
  1.3102 +  // XXX this returns first character rect or caret rect, it is limitation of
  1.3103 +  // now. We need more work for returns first line rect. But current
  1.3104 +  // implementation is enough for IMEs.
  1.3105 +
  1.3106 +  NSRect rect = NSMakeRect(0.0, 0.0, 0.0, 0.0);
  1.3107 +  NSRange actualRange = NSMakeRange(NSNotFound, 0);
  1.3108 +  if (aActualRange) {
  1.3109 +    *aActualRange = actualRange;
  1.3110 +  }
  1.3111 +  if (Destroyed() || aRange.location == NSNotFound) {
  1.3112 +    return rect;
  1.3113 +  }
  1.3114 +
  1.3115 +  nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.3116 +
  1.3117 +  nsIntRect r;
  1.3118 +  bool useCaretRect = (aRange.length == 0);
  1.3119 +  if (!useCaretRect) {
  1.3120 +    WidgetQueryContentEvent charRect(true, NS_QUERY_TEXT_RECT, mWidget);
  1.3121 +    charRect.InitForQueryTextRect(aRange.location, 1);
  1.3122 +    DispatchEvent(charRect);
  1.3123 +    if (charRect.mSucceeded) {
  1.3124 +      r = charRect.mReply.mRect;
  1.3125 +      actualRange.location = charRect.mReply.mOffset;
  1.3126 +      actualRange.length = charRect.mReply.mString.Length();
  1.3127 +    } else {
  1.3128 +      useCaretRect = true;
  1.3129 +    }
  1.3130 +  }
  1.3131 +
  1.3132 +  if (useCaretRect) {
  1.3133 +    WidgetQueryContentEvent caretRect(true, NS_QUERY_CARET_RECT, mWidget);
  1.3134 +    caretRect.InitForQueryCaretRect(aRange.location);
  1.3135 +    DispatchEvent(caretRect);
  1.3136 +    if (!caretRect.mSucceeded) {
  1.3137 +      return rect;
  1.3138 +    }
  1.3139 +    r = caretRect.mReply.mRect;
  1.3140 +    r.width = 0;
  1.3141 +    actualRange.location = caretRect.mReply.mOffset;
  1.3142 +    actualRange.length = 0;
  1.3143 +  }
  1.3144 +
  1.3145 +  nsIWidget* rootWidget = mWidget->GetTopLevelWidget();
  1.3146 +  NSWindow* rootWindow =
  1.3147 +    static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
  1.3148 +  NSView* rootView =
  1.3149 +    static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
  1.3150 +  if (!rootWindow || !rootView) {
  1.3151 +    return rect;
  1.3152 +  }
  1.3153 +  rect = nsCocoaUtils::DevPixelsToCocoaPoints(r, mWidget->BackingScaleFactor());
  1.3154 +  rect = [rootView convertRect:rect toView:nil];
  1.3155 +  rect.origin = [rootWindow convertBaseToScreen:rect.origin];
  1.3156 +
  1.3157 +  if (aActualRange) {
  1.3158 +    *aActualRange = actualRange;
  1.3159 +  }
  1.3160 +
  1.3161 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3162 +    ("%p IMEInputHandler::FirstRectForCharacterRange, "
  1.3163 +     "useCaretRect=%s rect={ x=%f, y=%f, width=%f, height=%f }, "
  1.3164 +     "actualRange={ location=%llu, length=%llu }",
  1.3165 +     this, TrueOrFalse(useCaretRect), rect.origin.x, rect.origin.y,
  1.3166 +     rect.size.width, rect.size.height, actualRange.location,
  1.3167 +     actualRange.length));
  1.3168 +
  1.3169 +  return rect;
  1.3170 +
  1.3171 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakeRect(0.0, 0.0, 0.0, 0.0));
  1.3172 +}
  1.3173 +
  1.3174 +NSUInteger
  1.3175 +IMEInputHandler::CharacterIndexForPoint(NSPoint& aPoint)
  1.3176 +{
  1.3177 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3178 +    ("%p IMEInputHandler::CharacterIndexForPoint, aPoint={ x=%f, y=%f }",
  1.3179 +     this, aPoint.x, aPoint.y));
  1.3180 +
  1.3181 +  //nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.3182 +
  1.3183 +  // To implement this, we'd have to grovel in text frames looking at text
  1.3184 +  // offsets.
  1.3185 +  return 0;
  1.3186 +}
  1.3187 +
  1.3188 +NSArray*
  1.3189 +IMEInputHandler::GetValidAttributesForMarkedText()
  1.3190 +{
  1.3191 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
  1.3192 +
  1.3193 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3194 +    ("%p IMEInputHandler::GetValidAttributesForMarkedText", this));
  1.3195 +
  1.3196 +  //nsRefPtr<IMEInputHandler> kungFuDeathGrip(this);
  1.3197 +
  1.3198 +  //return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName,
  1.3199 +  //                                 NSMarkedClauseSegmentAttributeName,
  1.3200 +  //                                 NSTextInputReplacementRangeAttributeName,
  1.3201 +  //                                 nil];
  1.3202 +  // empty array; we don't support any attributes right now
  1.3203 +  return [NSArray array];
  1.3204 +
  1.3205 +  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
  1.3206 +}
  1.3207 +
  1.3208 +
  1.3209 +#pragma mark -
  1.3210 +
  1.3211 +
  1.3212 +/******************************************************************************
  1.3213 + *
  1.3214 + *  IMEInputHandler implementation #2
  1.3215 + *
  1.3216 + ******************************************************************************/
  1.3217 +
  1.3218 +IMEInputHandler::IMEInputHandler(nsChildView* aWidget,
  1.3219 +                                 NSView<mozView> *aNativeView) :
  1.3220 +  PluginTextInputHandler(aWidget, aNativeView),
  1.3221 +  mPendingMethods(0), mIMECompositionString(nullptr),
  1.3222 +  mIsIMEComposing(false), mIsIMEEnabled(true),
  1.3223 +  mIsASCIICapableOnly(false), mIgnoreIMECommit(false),
  1.3224 +  mIsInFocusProcessing(false), mIMEHasFocus(false)
  1.3225 +{
  1.3226 +  InitStaticMembers();
  1.3227 +
  1.3228 +  mMarkedRange.location = NSNotFound;
  1.3229 +  mMarkedRange.length = 0;
  1.3230 +  mSelectedRange.location = NSNotFound;
  1.3231 +  mSelectedRange.length = 0;
  1.3232 +}
  1.3233 +
  1.3234 +IMEInputHandler::~IMEInputHandler()
  1.3235 +{
  1.3236 +  if (mTimer) {
  1.3237 +    mTimer->Cancel();
  1.3238 +    mTimer = nullptr;
  1.3239 +  }
  1.3240 +  if (sFocusedIMEHandler == this) {
  1.3241 +    sFocusedIMEHandler = nullptr;
  1.3242 +  }
  1.3243 +}
  1.3244 +
  1.3245 +void
  1.3246 +IMEInputHandler::OnFocusChangeInGecko(bool aFocus)
  1.3247 +{
  1.3248 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3249 +    ("%p IMEInputHandler::OnFocusChangeInGecko, aFocus=%s, Destroyed()=%s, "
  1.3250 +     "sFocusedIMEHandler=%p",
  1.3251 +     this, TrueOrFalse(aFocus), TrueOrFalse(Destroyed()), sFocusedIMEHandler));
  1.3252 +
  1.3253 +  mSelectedRange.location = NSNotFound; // Marking dirty
  1.3254 +  mIMEHasFocus = aFocus;
  1.3255 +
  1.3256 +  // This is called when the native focus is changed and when the native focus
  1.3257 +  // isn't changed but the focus is changed in Gecko.
  1.3258 +  if (!aFocus) {
  1.3259 +    if (sFocusedIMEHandler == this)
  1.3260 +      sFocusedIMEHandler = nullptr;
  1.3261 +    return;
  1.3262 +  }
  1.3263 +
  1.3264 +  sFocusedIMEHandler = this;
  1.3265 +  mIsInFocusProcessing = true;
  1.3266 +
  1.3267 +  // We need to notify IME of focus change in Gecko as native focus change
  1.3268 +  // because the window level of the focused element in Gecko may be changed.
  1.3269 +  mPendingMethods |= kNotifyIMEOfFocusChangeInGecko;
  1.3270 +  ResetTimer();
  1.3271 +}
  1.3272 +
  1.3273 +bool
  1.3274 +IMEInputHandler::OnDestroyWidget(nsChildView* aDestroyingWidget)
  1.3275 +{
  1.3276 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3277 +    ("%p IMEInputHandler::OnDestroyWidget, aDestroyingWidget=%p, "
  1.3278 +     "sFocusedIMEHandler=%p, IsIMEComposing()=%s",
  1.3279 +     this, aDestroyingWidget, sFocusedIMEHandler,
  1.3280 +     TrueOrFalse(IsIMEComposing())));
  1.3281 +
  1.3282 +  // If we're not focused, the focused IMEInputHandler may have been
  1.3283 +  // created by another widget/nsChildView.
  1.3284 +  if (sFocusedIMEHandler && sFocusedIMEHandler != this) {
  1.3285 +    sFocusedIMEHandler->OnDestroyWidget(aDestroyingWidget);
  1.3286 +  }
  1.3287 +
  1.3288 +  if (!PluginTextInputHandler::OnDestroyWidget(aDestroyingWidget)) {
  1.3289 +    return false;
  1.3290 +  }
  1.3291 +
  1.3292 +  if (IsIMEComposing()) {
  1.3293 +    // If our view is in the composition, we should clean up it.
  1.3294 +    CancelIMEComposition();
  1.3295 +    OnEndIMEComposition();
  1.3296 +  }
  1.3297 +
  1.3298 +  mSelectedRange.location = NSNotFound; // Marking dirty
  1.3299 +  mIMEHasFocus = false;
  1.3300 +
  1.3301 +  return true;
  1.3302 +}
  1.3303 +
  1.3304 +void
  1.3305 +IMEInputHandler::OnStartIMEComposition()
  1.3306 +{
  1.3307 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3308 +
  1.3309 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3310 +    ("%p IMEInputHandler::OnStartIMEComposition, mView=%p, mWidget=%p"
  1.3311 +     "inputContext=%p, mIsIMEComposing=%s",
  1.3312 +     this, mView, mWidget, mView ? [mView inputContext] : nullptr,
  1.3313 +     TrueOrFalse(mIsIMEComposing)));
  1.3314 +
  1.3315 +  NS_ASSERTION(!mIsIMEComposing, "There is a composition already");
  1.3316 +  mIsIMEComposing = true;
  1.3317 +
  1.3318 +  mLastDispatchedCompositionString.Truncate();
  1.3319 +
  1.3320 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3321 +}
  1.3322 +
  1.3323 +void
  1.3324 +IMEInputHandler::OnUpdateIMEComposition(NSString* aIMECompositionString)
  1.3325 +{
  1.3326 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3327 +
  1.3328 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3329 +    ("%p IMEInputHandler::OnUpdateIMEComposition, mView=%p, mWidget=%p, "
  1.3330 +     "inputContext=%p, mIsIMEComposing=%s, aIMECompositionString=\"%s\"",
  1.3331 +     this, mView, mWidget, mView ? [mView inputContext] : nullptr,
  1.3332 +     TrueOrFalse(mIsIMEComposing), GetCharacters(aIMECompositionString)));
  1.3333 +
  1.3334 +  NS_ASSERTION(mIsIMEComposing, "We're not in composition");
  1.3335 +
  1.3336 +  if (mIMECompositionString)
  1.3337 +    [mIMECompositionString release];
  1.3338 +  mIMECompositionString = [aIMECompositionString retain];
  1.3339 +
  1.3340 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3341 +}
  1.3342 +
  1.3343 +void
  1.3344 +IMEInputHandler::OnEndIMEComposition()
  1.3345 +{
  1.3346 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3347 +
  1.3348 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3349 +    ("%p IMEInputHandler::OnEndIMEComposition, mView=%p, mWidget=%p, "
  1.3350 +     "inputContext=%p, mIsIMEComposing=%s",
  1.3351 +     this, mView, mWidget, mView ? [mView inputContext] : nullptr,
  1.3352 +     TrueOrFalse(mIsIMEComposing)));
  1.3353 +
  1.3354 +  NS_ASSERTION(mIsIMEComposing, "We're not in composition");
  1.3355 +
  1.3356 +  mIsIMEComposing = false;
  1.3357 +
  1.3358 +  if (mIMECompositionString) {
  1.3359 +    [mIMECompositionString release];
  1.3360 +    mIMECompositionString = nullptr;
  1.3361 +  }
  1.3362 +
  1.3363 +  mLastDispatchedCompositionString.Truncate();
  1.3364 +
  1.3365 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3366 +}
  1.3367 +
  1.3368 +void
  1.3369 +IMEInputHandler::SendCommittedText(NSString *aString)
  1.3370 +{
  1.3371 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3372 +
  1.3373 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3374 +    ("%p IMEInputHandler::SendCommittedText, mView=%p, mWidget=%p, "
  1.3375 +     "inputContext=%p, mIsIMEComposing=%s",
  1.3376 +     this, mView, mWidget, mView ? [mView inputContext] : nullptr,
  1.3377 +     TrueOrFalse(mIsIMEComposing), mWidget));
  1.3378 +
  1.3379 +  NS_ENSURE_TRUE(mWidget, );
  1.3380 +  // XXX We should send the string without mView.
  1.3381 +  if (!mView) {
  1.3382 +    return;
  1.3383 +  }
  1.3384 +
  1.3385 +  NSAttributedString* attrStr =
  1.3386 +    [[NSAttributedString alloc] initWithString:aString];
  1.3387 +  [mView insertText:attrStr];
  1.3388 +  [attrStr release];
  1.3389 +
  1.3390 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3391 +}
  1.3392 +
  1.3393 +void
  1.3394 +IMEInputHandler::KillIMEComposition()
  1.3395 +{
  1.3396 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3397 +
  1.3398 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3399 +    ("%p IMEInputHandler::KillIMEComposition, mView=%p, mWidget=%p, "
  1.3400 +     "inputContext=%p, mIsIMEComposing=%s, "
  1.3401 +     "Destroyed()=%s, IsFocused()=%s",
  1.3402 +     this, mView, mWidget, mView ? [mView inputContext] : nullptr,
  1.3403 +     TrueOrFalse(mIsIMEComposing), TrueOrFalse(Destroyed()),
  1.3404 +     TrueOrFalse(IsFocused())));
  1.3405 +
  1.3406 +  if (Destroyed()) {
  1.3407 +    return;
  1.3408 +  }
  1.3409 +
  1.3410 +  if (IsFocused()) {
  1.3411 +    NS_ENSURE_TRUE_VOID(mView);
  1.3412 +    NSTextInputContext* inputContext = [mView inputContext];
  1.3413 +    NS_ENSURE_TRUE_VOID(inputContext);
  1.3414 +    [inputContext discardMarkedText];
  1.3415 +    return;
  1.3416 +  }
  1.3417 +
  1.3418 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3419 +    ("%p IMEInputHandler::KillIMEComposition, Pending...", this));
  1.3420 +
  1.3421 +  // Commit the composition internally.
  1.3422 +  SendCommittedText(mIMECompositionString);
  1.3423 +  NS_ASSERTION(!mIsIMEComposing, "We're still in a composition");
  1.3424 +  // The pending method will be fired by the next focus event.
  1.3425 +  mPendingMethods |= kDiscardIMEComposition;
  1.3426 +
  1.3427 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3428 +}
  1.3429 +
  1.3430 +void
  1.3431 +IMEInputHandler::CommitIMEComposition()
  1.3432 +{
  1.3433 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3434 +
  1.3435 +  if (!IsIMEComposing())
  1.3436 +    return;
  1.3437 +
  1.3438 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3439 +    ("%p IMEInputHandler::CommitIMEComposition, mIMECompositionString=%s",
  1.3440 +     this, GetCharacters(mIMECompositionString)));
  1.3441 +
  1.3442 +  KillIMEComposition();
  1.3443 +
  1.3444 +  if (!IsIMEComposing())
  1.3445 +    return;
  1.3446 +
  1.3447 +  // If the composition is still there, KillIMEComposition only kills the
  1.3448 +  // composition in TSM.  We also need to finish the our composition too.
  1.3449 +  SendCommittedText(mIMECompositionString);
  1.3450 +
  1.3451 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3452 +}
  1.3453 +
  1.3454 +void
  1.3455 +IMEInputHandler::CancelIMEComposition()
  1.3456 +{
  1.3457 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3458 +
  1.3459 +  if (!IsIMEComposing())
  1.3460 +    return;
  1.3461 +
  1.3462 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3463 +    ("%p IMEInputHandler::CancelIMEComposition, mIMECompositionString=%s",
  1.3464 +     this, GetCharacters(mIMECompositionString)));
  1.3465 +
  1.3466 +  // For canceling the current composing, we need to ignore the param of
  1.3467 +  // insertText.  But this code is ugly...
  1.3468 +  mIgnoreIMECommit = true;
  1.3469 +  KillIMEComposition();
  1.3470 +  mIgnoreIMECommit = false;
  1.3471 +
  1.3472 +  if (!IsIMEComposing())
  1.3473 +    return;
  1.3474 +
  1.3475 +  // If the composition is still there, KillIMEComposition only kills the
  1.3476 +  // composition in TSM.  We also need to kill the our composition too.
  1.3477 +  SendCommittedText(@"");
  1.3478 +
  1.3479 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3480 +}
  1.3481 +
  1.3482 +bool
  1.3483 +IMEInputHandler::IsFocused()
  1.3484 +{
  1.3485 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3486 +
  1.3487 +  NS_ENSURE_TRUE(!Destroyed(), false);
  1.3488 +  NSWindow* window = [mView window];
  1.3489 +  NS_ENSURE_TRUE(window, false);
  1.3490 +  return [window firstResponder] == mView &&
  1.3491 +         [window isKeyWindow] &&
  1.3492 +         [[NSApplication sharedApplication] isActive];
  1.3493 +
  1.3494 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
  1.3495 +}
  1.3496 +
  1.3497 +bool
  1.3498 +IMEInputHandler::IsIMEOpened()
  1.3499 +{
  1.3500 +  TISInputSourceWrapper tis;
  1.3501 +  tis.InitByCurrentInputSource();
  1.3502 +  return tis.IsOpenedIMEMode();
  1.3503 +}
  1.3504 +
  1.3505 +void
  1.3506 +IMEInputHandler::SetASCIICapableOnly(bool aASCIICapableOnly)
  1.3507 +{
  1.3508 +  if (aASCIICapableOnly == mIsASCIICapableOnly)
  1.3509 +    return;
  1.3510 +
  1.3511 +  CommitIMEComposition();
  1.3512 +  mIsASCIICapableOnly = aASCIICapableOnly;
  1.3513 +  SyncASCIICapableOnly();
  1.3514 +}
  1.3515 +
  1.3516 +void
  1.3517 +IMEInputHandler::EnableIME(bool aEnableIME)
  1.3518 +{
  1.3519 +  if (aEnableIME == mIsIMEEnabled)
  1.3520 +    return;
  1.3521 +
  1.3522 +  CommitIMEComposition();
  1.3523 +  mIsIMEEnabled = aEnableIME;
  1.3524 +}
  1.3525 +
  1.3526 +void
  1.3527 +IMEInputHandler::SetIMEOpenState(bool aOpenIME)
  1.3528 +{
  1.3529 +  if (!IsFocused() || IsIMEOpened() == aOpenIME)
  1.3530 +    return;
  1.3531 +
  1.3532 +  if (!aOpenIME) {
  1.3533 +    TISInputSourceWrapper tis;
  1.3534 +    tis.InitByCurrentASCIICapableInputSource();
  1.3535 +    tis.Select();
  1.3536 +    return;
  1.3537 +  }
  1.3538 +
  1.3539 +  // If we know the latest IME opened mode, we should select it.
  1.3540 +  if (sLatestIMEOpenedModeInputSourceID) {
  1.3541 +    TISInputSourceWrapper tis;
  1.3542 +    tis.InitByInputSourceID(sLatestIMEOpenedModeInputSourceID);
  1.3543 +    tis.Select();
  1.3544 +    return;
  1.3545 +  }
  1.3546 +
  1.3547 +  // XXX If the current input source is a mode of IME, we should turn on it,
  1.3548 +  // but we haven't found such way...
  1.3549 +
  1.3550 +  // Finally, we should refer the system locale but this is a little expensive,
  1.3551 +  // we shouldn't retry this (if it was succeeded, we already set
  1.3552 +  // sLatestIMEOpenedModeInputSourceID at that time).
  1.3553 +  static bool sIsPrefferredIMESearched = false;
  1.3554 +  if (sIsPrefferredIMESearched)
  1.3555 +    return;
  1.3556 +  sIsPrefferredIMESearched = true;
  1.3557 +  OpenSystemPreferredLanguageIME();
  1.3558 +}
  1.3559 +
  1.3560 +void
  1.3561 +IMEInputHandler::OpenSystemPreferredLanguageIME()
  1.3562 +{
  1.3563 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3564 +    ("%p IMEInputHandler::OpenSystemPreferredLanguageIME", this));
  1.3565 +
  1.3566 +  CFArrayRef langList = ::CFLocaleCopyPreferredLanguages();
  1.3567 +  if (!langList) {
  1.3568 +    PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3569 +      ("%p IMEInputHandler::OpenSystemPreferredLanguageIME, langList is NULL",
  1.3570 +       this));
  1.3571 +    return;
  1.3572 +  }
  1.3573 +  CFIndex count = ::CFArrayGetCount(langList);
  1.3574 +  for (CFIndex i = 0; i < count; i++) {
  1.3575 +    CFLocaleRef locale =
  1.3576 +      ::CFLocaleCreate(kCFAllocatorDefault,
  1.3577 +          static_cast<CFStringRef>(::CFArrayGetValueAtIndex(langList, i)));
  1.3578 +    if (!locale) {
  1.3579 +      continue;
  1.3580 +    }
  1.3581 +
  1.3582 +    bool changed = false;
  1.3583 +    CFStringRef lang = static_cast<CFStringRef>(
  1.3584 +      ::CFLocaleGetValue(locale, kCFLocaleLanguageCode));
  1.3585 +    NS_ASSERTION(lang, "lang is null");
  1.3586 +    if (lang) {
  1.3587 +      TISInputSourceWrapper tis;
  1.3588 +      tis.InitByLanguage(lang);
  1.3589 +      if (tis.IsOpenedIMEMode()) {
  1.3590 +#ifdef PR_LOGGING
  1.3591 +        if (PR_LOG_TEST(gLog, PR_LOG_ALWAYS)) {
  1.3592 +          CFStringRef foundTIS;
  1.3593 +          tis.GetInputSourceID(foundTIS);
  1.3594 +          PR_LOG(gLog, PR_LOG_ALWAYS,
  1.3595 +            ("%p IMEInputHandler::OpenSystemPreferredLanguageIME, "
  1.3596 +             "foundTIS=%s, lang=%s",
  1.3597 +             this, GetCharacters(foundTIS), GetCharacters(lang)));
  1.3598 +        }
  1.3599 +#endif // #ifdef PR_LOGGING
  1.3600 +        tis.Select();
  1.3601 +        changed = true;
  1.3602 +      }
  1.3603 +    }
  1.3604 +    ::CFRelease(locale);
  1.3605 +    if (changed) {
  1.3606 +      break;
  1.3607 +    }
  1.3608 +  }
  1.3609 +  ::CFRelease(langList);
  1.3610 +}
  1.3611 +
  1.3612 +
  1.3613 +#pragma mark -
  1.3614 +
  1.3615 +
  1.3616 +/******************************************************************************
  1.3617 + *
  1.3618 + *  PluginTextInputHandler implementation
  1.3619 + *
  1.3620 + ******************************************************************************/
  1.3621 +
  1.3622 +PluginTextInputHandler::PluginTextInputHandler(nsChildView* aWidget,
  1.3623 +                                               NSView<mozView> *aNativeView) :
  1.3624 +  TextInputHandlerBase(aWidget, aNativeView),
  1.3625 +  mIgnoreNextKeyUpEvent(false),
  1.3626 +#ifndef __LP64__
  1.3627 +  mPluginTSMDoc(0), mPluginTSMInComposition(false),
  1.3628 +#endif // #ifndef __LP64__
  1.3629 +  mPluginComplexTextInputRequested(false)
  1.3630 +{
  1.3631 +}
  1.3632 +
  1.3633 +PluginTextInputHandler::~PluginTextInputHandler()
  1.3634 +{
  1.3635 +#ifndef __LP64__
  1.3636 +  if (mPluginTSMDoc) {
  1.3637 +    ::DeleteTSMDocument(mPluginTSMDoc);
  1.3638 +  }
  1.3639 +#endif // #ifndef __LP64__
  1.3640 +}
  1.3641 +
  1.3642 +/* static */ void
  1.3643 +PluginTextInputHandler::ConvertCocoaKeyEventToNPCocoaEvent(
  1.3644 +                          NSEvent* aCocoaEvent,
  1.3645 +                          NPCocoaEvent& aPluginEvent)
  1.3646 +{
  1.3647 +  nsCocoaUtils::InitNPCocoaEvent(&aPluginEvent);
  1.3648 +  NSEventType nativeType = [aCocoaEvent type];
  1.3649 +  switch (nativeType) {
  1.3650 +    case NSKeyDown:
  1.3651 +      aPluginEvent.type = NPCocoaEventKeyDown;
  1.3652 +      break;
  1.3653 +    case NSKeyUp:
  1.3654 +      aPluginEvent.type = NPCocoaEventKeyUp;
  1.3655 +      break;
  1.3656 +    case NSFlagsChanged:
  1.3657 +      aPluginEvent.type = NPCocoaEventFlagsChanged;
  1.3658 +      break;
  1.3659 +    default:
  1.3660 +      NS_WARNING("Asked to convert key event of unknown type to Cocoa plugin event!");
  1.3661 +  }
  1.3662 +  aPluginEvent.data.key.modifierFlags = [aCocoaEvent modifierFlags];
  1.3663 +  aPluginEvent.data.key.keyCode = [aCocoaEvent keyCode];
  1.3664 +  // don't try to access character data for flags changed events,
  1.3665 +  // it will raise an exception
  1.3666 +  if (nativeType != NSFlagsChanged) {
  1.3667 +    aPluginEvent.data.key.characters = (NPNSString*)[aCocoaEvent characters];
  1.3668 +    aPluginEvent.data.key.charactersIgnoringModifiers =
  1.3669 +      (NPNSString*)[aCocoaEvent charactersIgnoringModifiers];
  1.3670 +    aPluginEvent.data.key.isARepeat = [aCocoaEvent isARepeat];
  1.3671 +  }
  1.3672 +}
  1.3673 +
  1.3674 +#ifndef __LP64__
  1.3675 +
  1.3676 +EventHandlerRef PluginTextInputHandler::sPluginKeyEventsHandler = NULL;
  1.3677 +
  1.3678 +/* static */ void
  1.3679 +PluginTextInputHandler::InstallPluginKeyEventsHandler()
  1.3680 +{
  1.3681 +  if (sPluginKeyEventsHandler) {
  1.3682 +    return;
  1.3683 +  }
  1.3684 +  static const EventTypeSpec sTSMEvents[] =
  1.3685 +    { { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } };
  1.3686 +  ::InstallEventHandler(::GetEventDispatcherTarget(),
  1.3687 +                        ::NewEventHandlerUPP(PluginKeyEventsHandler),
  1.3688 +                        GetEventTypeCount(sTSMEvents),
  1.3689 +                        sTSMEvents,
  1.3690 +                        NULL,
  1.3691 +                        &sPluginKeyEventsHandler);
  1.3692 +}
  1.3693 +
  1.3694 +/* static */ void
  1.3695 +PluginTextInputHandler::RemovePluginKeyEventsHandler()
  1.3696 +{
  1.3697 +  if (!sPluginKeyEventsHandler) {
  1.3698 +    return;
  1.3699 +  }
  1.3700 +  ::RemoveEventHandler(sPluginKeyEventsHandler);
  1.3701 +  sPluginKeyEventsHandler = NULL;
  1.3702 +}
  1.3703 +
  1.3704 +/* static */ void
  1.3705 +PluginTextInputHandler::SwizzleMethods()
  1.3706 +{
  1.3707 +  Class IMKInputSessionClass = ::NSClassFromString(@"IMKInputSession");
  1.3708 +  nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(handleEvent:),
  1.3709 +    @selector(PluginTextInputHandler_IMKInputSession_handleEvent:));
  1.3710 +  nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(commitComposition),
  1.3711 +    @selector(PluginTextInputHandler_IMKInputSession_commitComposition));
  1.3712 +  nsToolkit::SwizzleMethods(IMKInputSessionClass, @selector(finishSession),
  1.3713 +    @selector(PluginTextInputHandler_IMKInputSession_finishSession));
  1.3714 +}
  1.3715 +
  1.3716 +/* static */ OSStatus
  1.3717 +PluginTextInputHandler::PluginKeyEventsHandler(EventHandlerCallRef aHandlerRef,
  1.3718 +                                               EventRef aEvent,
  1.3719 +                                               void *aUserData)
  1.3720 +{
  1.3721 +  nsAutoreleasePool localPool;
  1.3722 +
  1.3723 +  TSMDocumentID activeDoc = ::TSMGetActiveDocument();
  1.3724 +  NS_ENSURE_TRUE(activeDoc, eventNotHandledErr);
  1.3725 +
  1.3726 +  ChildView *target = nil;
  1.3727 +  OSStatus status = ::TSMGetDocumentProperty(activeDoc,
  1.3728 +                                             kFocusedChildViewTSMDocPropertyTag,
  1.3729 +                                             sizeof(ChildView *), nil, &target);
  1.3730 +  NS_ENSURE_TRUE(status == noErr, eventNotHandledErr);
  1.3731 +  NS_ENSURE_TRUE(target, eventNotHandledErr);
  1.3732 +
  1.3733 +  EventRef keyEvent = NULL;
  1.3734 +  status = ::GetEventParameter(aEvent, kEventParamTextInputSendKeyboardEvent,
  1.3735 +                               typeEventRef, NULL, sizeof(EventRef), NULL,
  1.3736 +                               &keyEvent);
  1.3737 +  NS_ENSURE_TRUE(status == noErr, eventNotHandledErr);
  1.3738 +  NS_ENSURE_TRUE(keyEvent, eventNotHandledErr);
  1.3739 +
  1.3740 +  nsIWidget* widget = [target widget];
  1.3741 +  NS_ENSURE_TRUE(widget, eventNotHandledErr);
  1.3742 +  TextInputHandler*  handler =
  1.3743 +    static_cast<nsChildView*>(widget)->GetTextInputHandler();
  1.3744 +  NS_ENSURE_TRUE(handler, eventNotHandledErr);
  1.3745 +  handler->HandleCarbonPluginKeyEvent(keyEvent);
  1.3746 +
  1.3747 +  return noErr;
  1.3748 +}
  1.3749 +
  1.3750 +// Called from PluginKeyEventsHandler() (a handler for Carbon TSM events) to
  1.3751 +// process a Carbon key event for the currently focused plugin.  Both Unicode
  1.3752 +// characters and "Mac encoding characters" (in the MBCS or "multibyte
  1.3753 +// character system") are (or should be) available from aKeyEvent, but here we
  1.3754 +// use the MCBS characters.  This is how the WebKit does things, and seems to
  1.3755 +// be what plugins expect.
  1.3756 +void
  1.3757 +PluginTextInputHandler::HandleCarbonPluginKeyEvent(EventRef aKeyEvent)
  1.3758 +{
  1.3759 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1.3760 +
  1.3761 +  if (Destroyed()) {
  1.3762 +    return;
  1.3763 +  }
  1.3764 +
  1.3765 +  NS_ASSERTION(mView, "mView must not be NULL");
  1.3766 +
  1.3767 +  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
  1.3768 +
  1.3769 +  if ([mView pluginEventModel] == NPEventModelCocoa) {
  1.3770 +    UInt32 size;
  1.3771 +    OSStatus status =
  1.3772 +      ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes, typeUnicodeText,
  1.3773 +                          NULL, 0, &size, NULL);
  1.3774 +    NS_ENSURE_TRUE(status == noErr, );
  1.3775 +
  1.3776 +    UniChar* chars = (UniChar*)malloc(size);
  1.3777 +    NS_ENSURE_TRUE(chars, );
  1.3778 +
  1.3779 +    status = ::GetEventParameter(aKeyEvent, kEventParamKeyUnicodes,
  1.3780 +                                 typeUnicodeText, NULL, size, NULL, chars);
  1.3781 +    if (status != noErr) {
  1.3782 +      free(chars);
  1.3783 +      return;
  1.3784 +    }
  1.3785 +
  1.3786 +    CFStringRef text =
  1.3787 +      ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, chars,
  1.3788 +                                           (size / sizeof(UniChar)),
  1.3789 +                                           kCFAllocatorNull);
  1.3790 +    if (!text) {
  1.3791 +      free(chars);
  1.3792 +      return;
  1.3793 +    }
  1.3794 +
  1.3795 +    NPCocoaEvent cocoaTextEvent;
  1.3796 +    nsCocoaUtils::InitNPCocoaEvent(&cocoaTextEvent);
  1.3797 +    cocoaTextEvent.type = NPCocoaEventTextInput;
  1.3798 +    cocoaTextEvent.data.text.text = (NPNSString*)text;
  1.3799 +
  1.3800 +    WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, mWidget);
  1.3801 +    nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaTextEvent);
  1.3802 +    DispatchEvent(pluginEvent);
  1.3803 +
  1.3804 +    ::CFRelease(text);
  1.3805 +    free(chars);
  1.3806 +
  1.3807 +    return;
  1.3808 +  }
  1.3809 +
  1.3810 +  UInt32 numCharCodes;
  1.3811 +  OSStatus status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
  1.3812 +                                        typeChar, NULL, 0, &numCharCodes, NULL);
  1.3813 +  NS_ENSURE_TRUE(status == noErr, );
  1.3814 +
  1.3815 +  nsAutoTArray<unsigned char, 3> charCodes;
  1.3816 +  charCodes.SetLength(numCharCodes);
  1.3817 +  status = ::GetEventParameter(aKeyEvent, kEventParamKeyMacCharCodes,
  1.3818 +                               typeChar, NULL, numCharCodes, NULL,
  1.3819 +                               charCodes.Elements());
  1.3820 +  NS_ENSURE_TRUE(status == noErr, );
  1.3821 +
  1.3822 +  UInt32 modifiers;
  1.3823 +  status = ::GetEventParameter(aKeyEvent, kEventParamKeyModifiers,
  1.3824 +                               typeUInt32, NULL, sizeof(modifiers), NULL,
  1.3825 +                               &modifiers);
  1.3826 +  NS_ENSURE_TRUE(status == noErr, );
  1.3827 +
  1.3828 +  NSUInteger cocoaModifiers = 0;
  1.3829 +  if (modifiers & shiftKey) {
  1.3830 +    cocoaModifiers |= NSShiftKeyMask;
  1.3831 +  }
  1.3832 +  if (modifiers & controlKey) {
  1.3833 +    cocoaModifiers |= NSControlKeyMask;
  1.3834 +  }
  1.3835 +  if (modifiers & optionKey) {
  1.3836 +    cocoaModifiers |= NSAlternateKeyMask;
  1.3837 +  }
  1.3838 +  if (modifiers & cmdKey) { // Should never happen
  1.3839 +    cocoaModifiers |= NSCommandKeyMask;
  1.3840 +  }
  1.3841 +
  1.3842 +  UInt32 macKeyCode;
  1.3843 +  status = ::GetEventParameter(aKeyEvent, kEventParamKeyCode,
  1.3844 +                               typeUInt32, NULL, sizeof(macKeyCode), NULL,
  1.3845 +                               &macKeyCode);
  1.3846 +  NS_ENSURE_TRUE(status == noErr, );
  1.3847 +
  1.3848 +  TISInputSourceWrapper& currentInputSource =
  1.3849 +    TISInputSourceWrapper::CurrentInputSource();
  1.3850 +
  1.3851 +  EventRef cloneEvent = ::CopyEvent(aKeyEvent);
  1.3852 +  for (uint32_t i = 0; i < numCharCodes; ++i) {
  1.3853 +    status = ::SetEventParameter(cloneEvent, kEventParamKeyMacCharCodes,
  1.3854 +                                 typeChar, 1, charCodes.Elements() + i);
  1.3855 +    NS_ENSURE_TRUE(status == noErr, );
  1.3856 +
  1.3857 +    EventRecord eventRec;
  1.3858 +    if (::ConvertEventRefToEventRecord(cloneEvent, &eventRec)) {
  1.3859 +      WidgetKeyboardEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
  1.3860 +      nsCocoaUtils::InitInputEvent(keydownEvent, cocoaModifiers);
  1.3861 +
  1.3862 +      uint32_t keyCode =
  1.3863 +        currentInputSource.ComputeGeckoKeyCode(macKeyCode, ::LMGetKbdType(),
  1.3864 +                                               keydownEvent.IsMeta());
  1.3865 +      uint32_t charCode(charCodes.ElementAt(i));
  1.3866 +
  1.3867 +      keydownEvent.time = PR_IntervalNow();
  1.3868 +      keydownEvent.pluginEvent = &eventRec;
  1.3869 +      if (IsSpecialGeckoKey(macKeyCode)) {
  1.3870 +        keydownEvent.keyCode = keyCode;
  1.3871 +      } else {
  1.3872 +        // XXX This is wrong. charCode must be 0 for keydown event.
  1.3873 +        keydownEvent.charCode = charCode;
  1.3874 +        keydownEvent.isChar   = true;
  1.3875 +      }
  1.3876 +      DispatchEvent(keydownEvent);
  1.3877 +      if (Destroyed()) {
  1.3878 +        break;
  1.3879 +      }
  1.3880 +    }
  1.3881 +  }
  1.3882 +
  1.3883 +  ::ReleaseEvent(cloneEvent);
  1.3884 +
  1.3885 +  NS_OBJC_END_TRY_ABORT_BLOCK;
  1.3886 +}
  1.3887 +
  1.3888 +void
  1.3889 +PluginTextInputHandler::ActivatePluginTSMDocument()
  1.3890 +{
  1.3891 +  if (!mPluginTSMDoc) {
  1.3892 +    // Create a TSM document that supports both non-Unicode and Unicode input.
  1.3893 +    // Though ProcessPluginKeyEvent() only sends Mac char codes to
  1.3894 +    // the plugin, this makes the input window behave better when it contains
  1.3895 +    // more than one kind of input (say Hiragana and Romaji).  This is what
  1.3896 +    // the OS does when it creates a TSM document for use by an
  1.3897 +    // NSTSMInputContext class.
  1.3898 +    InterfaceTypeList supportedServices;
  1.3899 +    supportedServices[0] = kTextServiceDocumentInterfaceType;
  1.3900 +    supportedServices[1] = kUnicodeDocumentInterfaceType;
  1.3901 +    ::NewTSMDocument(2, supportedServices, &mPluginTSMDoc, 0);
  1.3902 +    // We'll need to use the "input window".
  1.3903 +    ::UseInputWindow(mPluginTSMDoc, YES);
  1.3904 +    ::ActivateTSMDocument(mPluginTSMDoc);
  1.3905 +  } else if (::TSMGetActiveDocument() != mPluginTSMDoc) {
  1.3906 +    ::ActivateTSMDocument(mPluginTSMDoc);
  1.3907 +  }
  1.3908 +}
  1.3909 +
  1.3910 +#endif // #ifndef __LP64__
  1.3911 +
  1.3912 +void
  1.3913 +PluginTextInputHandler::HandleKeyDownEventForPlugin(NSEvent* aNativeKeyEvent)
  1.3914 +{
  1.3915 +  if (Destroyed()) {
  1.3916 +    return;
  1.3917 +  }
  1.3918 +
  1.3919 +  NS_ASSERTION(mView, "mView must not be NULL");
  1.3920 +
  1.3921 +#ifdef __LP64__
  1.3922 +
  1.3923 +  if ([mView pluginEventModel] != NPEventModelCocoa) {
  1.3924 +    return;
  1.3925 +  }
  1.3926 +
  1.3927 +  ComplexTextInputPanel* ctiPanel =
  1.3928 +    [ComplexTextInputPanel sharedComplexTextInputPanel];
  1.3929 +  [ctiPanel adjustTo:mView];
  1.3930 +
  1.3931 +  // If a composition is in progress then simply let the input panel continue
  1.3932 +  // it.
  1.3933 +  if (IsInPluginComposition()) {
  1.3934 +    // Don't send key up events for key downs associated with compositions.
  1.3935 +    mIgnoreNextKeyUpEvent = true;
  1.3936 +
  1.3937 +    NSString* textString = nil;
  1.3938 +    [ctiPanel interpretKeyEvent:aNativeKeyEvent string:&textString];
  1.3939 +    if (textString) {
  1.3940 +      DispatchCocoaNPAPITextEvent(textString);
  1.3941 +    }
  1.3942 +
  1.3943 +    return;
  1.3944 +  }
  1.3945 +
  1.3946 +  // Reset complex text input request flag.
  1.3947 +  mPluginComplexTextInputRequested = false;
  1.3948 +
  1.3949 +  // Send key down event to the plugin.
  1.3950 +  WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, mWidget);
  1.3951 +  NPCocoaEvent cocoaEvent;
  1.3952 +  ConvertCocoaKeyEventToNPCocoaEvent(aNativeKeyEvent, cocoaEvent);
  1.3953 +  nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaEvent);
  1.3954 +  DispatchEvent(pluginEvent);
  1.3955 +  if (Destroyed()) {
  1.3956 +    return;
  1.3957 +  }
  1.3958 +
  1.3959 +  // Start complex text composition if requested.
  1.3960 +  if (mPluginComplexTextInputRequested) {
  1.3961 +    // Don't send key up events for key downs associated with compositions.
  1.3962 +    mIgnoreNextKeyUpEvent = true;
  1.3963 +
  1.3964 +    NSString* textString = nil;
  1.3965 +    [ctiPanel interpretKeyEvent:aNativeKeyEvent string:&textString];
  1.3966 +    if (textString) {
  1.3967 +      DispatchCocoaNPAPITextEvent(textString);
  1.3968 +    }
  1.3969 +  }
  1.3970 +
  1.3971 +#else // #ifdef __LP64__
  1.3972 +
  1.3973 +  bool wasInComposition = false;
  1.3974 +  if ([mView pluginEventModel] == NPEventModelCocoa) {
  1.3975 +    if (IsInPluginComposition()) {
  1.3976 +      wasInComposition = true;
  1.3977 +
  1.3978 +      // Don't send key up events for key downs associated with compositions.
  1.3979 +      mIgnoreNextKeyUpEvent = true;
  1.3980 +    } else {
  1.3981 +      // Reset complex text input request flag.
  1.3982 +      mPluginComplexTextInputRequested = false;
  1.3983 +
  1.3984 +      // Send key down event to the plugin.
  1.3985 +      WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, mWidget);
  1.3986 +      NPCocoaEvent cocoaEvent;
  1.3987 +      ConvertCocoaKeyEventToNPCocoaEvent(aNativeKeyEvent, cocoaEvent);
  1.3988 +      nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaEvent);
  1.3989 +      DispatchEvent(pluginEvent);
  1.3990 +      if (Destroyed()) {
  1.3991 +        return;
  1.3992 +      }
  1.3993 +
  1.3994 +      // Only continue if plugin wants complex text input.
  1.3995 +      if (!mPluginComplexTextInputRequested) {
  1.3996 +        return;
  1.3997 +      }
  1.3998 +
  1.3999 +      // Don't send key up events for key downs associated with compositions.
  1.4000 +      mIgnoreNextKeyUpEvent = true;
  1.4001 +    }
  1.4002 +
  1.4003 +    // Don't send complex text input to a plugin in Cocoa event mode if
  1.4004 +    // either the Control key or the Command key is pressed -- even if the
  1.4005 +    // plugin has requested it, or we are already in IME composition.  This
  1.4006 +    // conforms to our behavior in 64-bit mode and fixes bug 619217.
  1.4007 +    NSUInteger modifierFlags = [aNativeKeyEvent modifierFlags];
  1.4008 +    if ((modifierFlags & NSControlKeyMask) ||
  1.4009 +        (modifierFlags & NSCommandKeyMask)) {
  1.4010 +      return;
  1.4011 +    }
  1.4012 +  }
  1.4013 +
  1.4014 +  // This will take care of all Carbon plugin events and also send Cocoa plugin
  1.4015 +  // text events when NSInputContext is not available (ifndef NP_NO_CARBON).
  1.4016 +  ActivatePluginTSMDocument();
  1.4017 +
  1.4018 +  // We use the active TSM document to pass a pointer to ourselves (the
  1.4019 +  // currently focused ChildView) to PluginKeyEventsHandler().  Because this
  1.4020 +  // pointer is weak, we should retain and release ourselves around the call
  1.4021 +  // to TSMProcessRawKeyEvent().
  1.4022 +  nsAutoRetainCocoaObject kungFuDeathGrip(mView);
  1.4023 +  ::TSMSetDocumentProperty(mPluginTSMDoc,
  1.4024 +                           kFocusedChildViewTSMDocPropertyTag,
  1.4025 +                           sizeof(ChildView *), &mView);
  1.4026 +  ::TSMProcessRawKeyEvent([aNativeKeyEvent _eventRef]);
  1.4027 +  ::TSMRemoveDocumentProperty(mPluginTSMDoc,
  1.4028 +                              kFocusedChildViewTSMDocPropertyTag);
  1.4029 +
  1.4030 +#endif // #ifdef __LP64__ else
  1.4031 +}
  1.4032 +
  1.4033 +void
  1.4034 +PluginTextInputHandler::HandleKeyUpEventForPlugin(NSEvent* aNativeKeyEvent)
  1.4035 +{
  1.4036 +  if (mIgnoreNextKeyUpEvent) {
  1.4037 +    mIgnoreNextKeyUpEvent = false;
  1.4038 +    return;
  1.4039 +  }
  1.4040 +
  1.4041 +  if (Destroyed()) {
  1.4042 +    return;
  1.4043 +  }
  1.4044 +
  1.4045 +  NS_ASSERTION(mView, "mView must not be NULL");
  1.4046 +
  1.4047 +  NPEventModel eventModel = [mView pluginEventModel];
  1.4048 +  if (eventModel == NPEventModelCocoa) {
  1.4049 +    // Don't send key up events to Cocoa plugins during composition.
  1.4050 +    if (IsInPluginComposition()) {
  1.4051 +      return;
  1.4052 +    }
  1.4053 +
  1.4054 +    WidgetKeyboardEvent keyupEvent(true, NS_KEY_UP, mWidget);
  1.4055 +    InitKeyEvent(aNativeKeyEvent, keyupEvent);
  1.4056 +    NPCocoaEvent pluginEvent;
  1.4057 +    ConvertCocoaKeyEventToNPCocoaEvent(aNativeKeyEvent, pluginEvent);
  1.4058 +    keyupEvent.pluginEvent = &pluginEvent;
  1.4059 +    DispatchEvent(keyupEvent);
  1.4060 +    return;
  1.4061 +  }
  1.4062 +}
  1.4063 +
  1.4064 +bool
  1.4065 +PluginTextInputHandler::IsInPluginComposition()
  1.4066 +{
  1.4067 +  return
  1.4068 +#ifdef __LP64__
  1.4069 +    [[ComplexTextInputPanel sharedComplexTextInputPanel] inComposition] != NO;
  1.4070 +#else // #ifdef __LP64__
  1.4071 +    mPluginTSMInComposition;
  1.4072 +#endif // #ifdef __LP64__ else
  1.4073 +}
  1.4074 +
  1.4075 +bool
  1.4076 +PluginTextInputHandler::DispatchCocoaNPAPITextEvent(NSString* aString)
  1.4077 +{
  1.4078 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.4079 +
  1.4080 +  NPCocoaEvent cocoaTextEvent;
  1.4081 +  nsCocoaUtils::InitNPCocoaEvent(&cocoaTextEvent);
  1.4082 +  cocoaTextEvent.type = NPCocoaEventTextInput;
  1.4083 +  cocoaTextEvent.data.text.text = (NPNSString*)aString;
  1.4084 +
  1.4085 +  WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, mWidget);
  1.4086 +  nsCocoaUtils::InitPluginEvent(pluginEvent, cocoaTextEvent);
  1.4087 +  return DispatchEvent(pluginEvent);
  1.4088 +
  1.4089 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
  1.4090 +}
  1.4091 +
  1.4092 +
  1.4093 +#pragma mark -
  1.4094 +
  1.4095 +
  1.4096 +#ifndef __LP64__
  1.4097 +
  1.4098 +/******************************************************************************
  1.4099 + *
  1.4100 + *  PluginTextInputHandler_IMKInputSession_* implementation
  1.4101 + *
  1.4102 + ******************************************************************************/
  1.4103 +
  1.4104 +// IMKInputSession is an undocumented class in the HIToolbox framework.  It's
  1.4105 +// present on both Leopard and SnowLeopard, and is used at a low level to
  1.4106 +// process IME input regardless of which high-level API is used (Text Services
  1.4107 +// Manager or Cocoa).  It works the same way in both 32-bit and 64-bit code.
  1.4108 +@interface NSObject (IMKInputSessionMethodSwizzling)
  1.4109 +- (BOOL)PluginTextInputHandler_IMKInputSession_handleEvent:(EventRef)theEvent;
  1.4110 +- (void)PluginTextInputHandler_IMKInputSession_commitComposition;
  1.4111 +- (void)PluginTextInputHandler_IMKInputSession_finishSession;
  1.4112 +@end
  1.4113 +
  1.4114 +@implementation NSObject (IMKInputSessionMethodSwizzling)
  1.4115 +
  1.4116 +- (BOOL)PluginTextInputHandler_IMKInputSession_handleEvent:(EventRef)theEvent
  1.4117 +{
  1.4118 +  [self retain];
  1.4119 +  BOOL retval =
  1.4120 +    [self PluginTextInputHandler_IMKInputSession_handleEvent:theEvent];
  1.4121 +  NSUInteger retainCount = [self retainCount];
  1.4122 +  [self release];
  1.4123 +  // Return without doing anything if we've been deleted.
  1.4124 +  if (retainCount == 1) {
  1.4125 +    return retval;
  1.4126 +  }
  1.4127 +
  1.4128 +  NSWindow *mainWindow = [NSApp mainWindow];
  1.4129 +  NSResponder *firstResponder = [mainWindow firstResponder];
  1.4130 +  if (![firstResponder isKindOfClass:[ChildView class]]) {
  1.4131 +    return retval;
  1.4132 +  }
  1.4133 +
  1.4134 +  // 'charactersEntered' is the length (in bytes) of currently "marked text"
  1.4135 +  // -- text that's been entered in IME but not yet committed.  If it's
  1.4136 +  // non-zero we're composing text in an IME session; if it's zero we're
  1.4137 +  // not in an IME session.
  1.4138 +  NSInteger entered = 0;
  1.4139 +  object_getInstanceVariable(self, "charactersEntered",
  1.4140 +                             (void **) &entered);
  1.4141 +  nsIWidget* widget = [(ChildView*)firstResponder widget];
  1.4142 +  NS_ENSURE_TRUE(widget, retval);
  1.4143 +  TextInputHandler* handler =
  1.4144 +    static_cast<nsChildView*>(widget)->GetTextInputHandler();
  1.4145 +  NS_ENSURE_TRUE(handler, retval);
  1.4146 +  handler->SetPluginTSMInComposition(entered != 0);
  1.4147 +
  1.4148 +  return retval;
  1.4149 +}
  1.4150 +
  1.4151 +// This method is called whenever IME input is committed as a result of an
  1.4152 +// "abnormal" termination -- for example when changing the keyboard focus from
  1.4153 +// one input field to another.
  1.4154 +- (void)PluginTextInputHandler_IMKInputSession_commitComposition
  1.4155 +{
  1.4156 +  NSWindow *mainWindow = [NSApp mainWindow];
  1.4157 +  NSResponder *firstResponder = [mainWindow firstResponder];
  1.4158 +  if ([firstResponder isKindOfClass:[ChildView class]]) {
  1.4159 +    nsIWidget* widget = [(ChildView*)firstResponder widget];
  1.4160 +    if (widget) {
  1.4161 +      TextInputHandler* handler =
  1.4162 +        static_cast<nsChildView*>(widget)->GetTextInputHandler();
  1.4163 +      if (handler) {
  1.4164 +        handler->SetPluginTSMInComposition(false);
  1.4165 +      }
  1.4166 +    }
  1.4167 +  }
  1.4168 +  [self PluginTextInputHandler_IMKInputSession_commitComposition];
  1.4169 +}
  1.4170 +
  1.4171 +// This method is called just before we're deallocated.
  1.4172 +- (void)PluginTextInputHandler_IMKInputSession_finishSession
  1.4173 +{
  1.4174 +  NSWindow *mainWindow = [NSApp mainWindow];
  1.4175 +  NSResponder *firstResponder = [mainWindow firstResponder];
  1.4176 +  if ([firstResponder isKindOfClass:[ChildView class]]) {
  1.4177 +    nsIWidget* widget = [(ChildView*)firstResponder widget];
  1.4178 +    if (widget) {
  1.4179 +      TextInputHandler* handler =
  1.4180 +        static_cast<nsChildView*>(widget)->GetTextInputHandler();
  1.4181 +      if (handler) {
  1.4182 +        handler->SetPluginTSMInComposition(false);
  1.4183 +      }
  1.4184 +    }
  1.4185 +  }
  1.4186 +  [self PluginTextInputHandler_IMKInputSession_finishSession];
  1.4187 +}
  1.4188 +
  1.4189 +@end
  1.4190 +
  1.4191 +#endif // #ifndef __LP64__
  1.4192 +
  1.4193 +
  1.4194 +#pragma mark -
  1.4195 +
  1.4196 +
  1.4197 +/******************************************************************************
  1.4198 + *
  1.4199 + *  TextInputHandlerBase implementation
  1.4200 + *
  1.4201 + ******************************************************************************/
  1.4202 +
  1.4203 +int32_t TextInputHandlerBase::sSecureEventInputCount = 0;
  1.4204 +
  1.4205 +TextInputHandlerBase::TextInputHandlerBase(nsChildView* aWidget,
  1.4206 +                                           NSView<mozView> *aNativeView) :
  1.4207 +  mWidget(aWidget)
  1.4208 +{
  1.4209 +  gHandlerInstanceCount++;
  1.4210 +  mView = [aNativeView retain];
  1.4211 +}
  1.4212 +
  1.4213 +TextInputHandlerBase::~TextInputHandlerBase()
  1.4214 +{
  1.4215 +  [mView release];
  1.4216 +  if (--gHandlerInstanceCount == 0) {
  1.4217 +    FinalizeCurrentInputSource();
  1.4218 +  }
  1.4219 +}
  1.4220 +
  1.4221 +bool
  1.4222 +TextInputHandlerBase::OnDestroyWidget(nsChildView* aDestroyingWidget)
  1.4223 +{
  1.4224 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.4225 +    ("%p TextInputHandlerBase::OnDestroyWidget, "
  1.4226 +     "aDestroyingWidget=%p, mWidget=%p",
  1.4227 +     this, aDestroyingWidget, mWidget));
  1.4228 +
  1.4229 +  if (aDestroyingWidget != mWidget) {
  1.4230 +    return false;
  1.4231 +  }
  1.4232 +
  1.4233 +  mWidget = nullptr;
  1.4234 +  return true;
  1.4235 +}
  1.4236 +
  1.4237 +bool
  1.4238 +TextInputHandlerBase::DispatchEvent(WidgetGUIEvent& aEvent)
  1.4239 +{
  1.4240 +  if (aEvent.message == NS_KEY_PRESS) {
  1.4241 +    WidgetInputEvent& inputEvent = *aEvent.AsInputEvent();
  1.4242 +    if (!inputEvent.IsMeta()) {
  1.4243 +      PR_LOG(gLog, PR_LOG_ALWAYS,
  1.4244 +        ("%p TextInputHandlerBase::DispatchEvent, hiding mouse cursor", this));
  1.4245 +      [NSCursor setHiddenUntilMouseMoves:YES];
  1.4246 +    }
  1.4247 +  }
  1.4248 +  return mWidget->DispatchWindowEvent(aEvent);
  1.4249 +}
  1.4250 +
  1.4251 +void
  1.4252 +TextInputHandlerBase::InitKeyEvent(NSEvent *aNativeKeyEvent,
  1.4253 +                                   WidgetKeyboardEvent& aKeyEvent,
  1.4254 +                                   const nsAString* aInsertString)
  1.4255 +{
  1.4256 +  NS_ASSERTION(aNativeKeyEvent, "aNativeKeyEvent must not be NULL");
  1.4257 +
  1.4258 +  if (mKeyboardOverride.mOverrideEnabled) {
  1.4259 +    TISInputSourceWrapper tis;
  1.4260 +    tis.InitByLayoutID(mKeyboardOverride.mKeyboardLayout, true);
  1.4261 +    tis.InitKeyEvent(aNativeKeyEvent, aKeyEvent, aInsertString);
  1.4262 +    return;
  1.4263 +  }
  1.4264 +  TISInputSourceWrapper::CurrentInputSource().
  1.4265 +    InitKeyEvent(aNativeKeyEvent, aKeyEvent, aInsertString);
  1.4266 +}
  1.4267 +
  1.4268 +nsresult
  1.4269 +TextInputHandlerBase::SynthesizeNativeKeyEvent(
  1.4270 +                        int32_t aNativeKeyboardLayout,
  1.4271 +                        int32_t aNativeKeyCode,
  1.4272 +                        uint32_t aModifierFlags,
  1.4273 +                        const nsAString& aCharacters,
  1.4274 +                        const nsAString& aUnmodifiedCharacters)
  1.4275 +{
  1.4276 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.4277 +
  1.4278 +  static const uint32_t sModifierFlagMap[][2] = {
  1.4279 +    { nsIWidget::CAPS_LOCK,       NSAlphaShiftKeyMask },
  1.4280 +    { nsIWidget::SHIFT_L,         NSShiftKeyMask      | 0x0002 },
  1.4281 +    { nsIWidget::SHIFT_R,         NSShiftKeyMask      | 0x0004 },
  1.4282 +    { nsIWidget::CTRL_L,          NSControlKeyMask    | 0x0001 },
  1.4283 +    { nsIWidget::CTRL_R,          NSControlKeyMask    | 0x2000 },
  1.4284 +    { nsIWidget::ALT_L,           NSAlternateKeyMask  | 0x0020 },
  1.4285 +    { nsIWidget::ALT_R,           NSAlternateKeyMask  | 0x0040 },
  1.4286 +    { nsIWidget::COMMAND_L,       NSCommandKeyMask    | 0x0008 },
  1.4287 +    { nsIWidget::COMMAND_R,       NSCommandKeyMask    | 0x0010 },
  1.4288 +    { nsIWidget::NUMERIC_KEY_PAD, NSNumericPadKeyMask },
  1.4289 +    { nsIWidget::HELP,            NSHelpKeyMask },
  1.4290 +    { nsIWidget::FUNCTION,        NSFunctionKeyMask }
  1.4291 +  };
  1.4292 +
  1.4293 +  uint32_t modifierFlags = 0;
  1.4294 +  for (uint32_t i = 0; i < ArrayLength(sModifierFlagMap); ++i) {
  1.4295 +    if (aModifierFlags & sModifierFlagMap[i][0]) {
  1.4296 +      modifierFlags |= sModifierFlagMap[i][1];
  1.4297 +    }
  1.4298 +  }
  1.4299 +
  1.4300 +  NSInteger windowNumber = [[mView window] windowNumber];
  1.4301 +  bool sendFlagsChangedEvent = IsModifierKey(aNativeKeyCode);
  1.4302 +  NSEventType eventType = sendFlagsChangedEvent ? NSFlagsChanged : NSKeyDown;
  1.4303 +  NSEvent* downEvent =
  1.4304 +    [NSEvent     keyEventWithType:eventType
  1.4305 +                         location:NSMakePoint(0,0)
  1.4306 +                    modifierFlags:modifierFlags
  1.4307 +                        timestamp:0
  1.4308 +                     windowNumber:windowNumber
  1.4309 +                          context:[NSGraphicsContext currentContext]
  1.4310 +                       characters:nsCocoaUtils::ToNSString(aCharacters)
  1.4311 +      charactersIgnoringModifiers:nsCocoaUtils::ToNSString(aUnmodifiedCharacters)
  1.4312 +                        isARepeat:NO
  1.4313 +                          keyCode:aNativeKeyCode];
  1.4314 +
  1.4315 +  NSEvent* upEvent = sendFlagsChangedEvent ?
  1.4316 +    nil : nsCocoaUtils::MakeNewCocoaEventWithType(NSKeyUp, downEvent);
  1.4317 +
  1.4318 +  if (downEvent && (sendFlagsChangedEvent || upEvent)) {
  1.4319 +    KeyboardLayoutOverride currentLayout = mKeyboardOverride;
  1.4320 +    mKeyboardOverride.mKeyboardLayout = aNativeKeyboardLayout;
  1.4321 +    mKeyboardOverride.mOverrideEnabled = true;
  1.4322 +    [NSApp sendEvent:downEvent];
  1.4323 +    if (upEvent) {
  1.4324 +      [NSApp sendEvent:upEvent];
  1.4325 +    }
  1.4326 +    // processKeyDownEvent and keyUp block exceptions so we're sure to
  1.4327 +    // reach here to restore mKeyboardOverride
  1.4328 +    mKeyboardOverride = currentLayout;
  1.4329 +  }
  1.4330 +
  1.4331 +  return NS_OK;
  1.4332 +
  1.4333 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.4334 +}
  1.4335 +
  1.4336 +NSInteger
  1.4337 +TextInputHandlerBase::GetWindowLevel()
  1.4338 +{
  1.4339 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
  1.4340 +
  1.4341 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.4342 +    ("%p TextInputHandlerBase::GetWindowLevel, Destryoed()=%s",
  1.4343 +     this, TrueOrFalse(Destroyed())));
  1.4344 +
  1.4345 +  if (Destroyed()) {
  1.4346 +    return NSNormalWindowLevel;
  1.4347 +  }
  1.4348 +
  1.4349 +  // When an <input> element on a XUL <panel> is focused, the actual focused view
  1.4350 +  // is the panel's parent view (mView). But the editor is displayed on the
  1.4351 +  // popped-up widget's view (editorView).  We want the latter's window level.
  1.4352 +  NSView<mozView>* editorView = mWidget->GetEditorView();
  1.4353 +  NS_ENSURE_TRUE(editorView, NSNormalWindowLevel);
  1.4354 +  NSInteger windowLevel = [[editorView window] level];
  1.4355 +
  1.4356 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.4357 +    ("%p TextInputHandlerBase::GetWindowLevel, windowLevel=%s (%X)",
  1.4358 +     this, GetWindowLevelName(windowLevel), windowLevel));
  1.4359 +
  1.4360 +  return windowLevel;
  1.4361 +
  1.4362 +  NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSNormalWindowLevel);
  1.4363 +}
  1.4364 +
  1.4365 +NS_IMETHODIMP
  1.4366 +TextInputHandlerBase::AttachNativeKeyEvent(WidgetKeyboardEvent& aKeyEvent)
  1.4367 +{
  1.4368 +  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  1.4369 +
  1.4370 +  // Don't try to replace a native event if one already exists.
  1.4371 +  // OS X doesn't have an OS modifier, can't make a native event.
  1.4372 +  if (aKeyEvent.mNativeKeyEvent || aKeyEvent.modifiers & MODIFIER_OS) {
  1.4373 +    return NS_OK;
  1.4374 +  }
  1.4375 +
  1.4376 +  PR_LOG(gLog, PR_LOG_ALWAYS,
  1.4377 +    ("%p TextInputHandlerBase::AttachNativeKeyEvent, key=0x%X, char=0x%X, "
  1.4378 +     "mod=0x%X", this, aKeyEvent.keyCode, aKeyEvent.charCode,
  1.4379 +     aKeyEvent.modifiers));
  1.4380 +
  1.4381 +  NSEventType eventType;
  1.4382 +  if (aKeyEvent.message == NS_KEY_UP) {
  1.4383 +    eventType = NSKeyUp;
  1.4384 +  } else {
  1.4385 +    eventType = NSKeyDown;
  1.4386 +  }
  1.4387 +
  1.4388 +  static const uint32_t sModifierFlagMap[][2] = {
  1.4389 +    { MODIFIER_SHIFT,    NSShiftKeyMask },
  1.4390 +    { MODIFIER_CONTROL,  NSControlKeyMask },
  1.4391 +    { MODIFIER_ALT,      NSAlternateKeyMask },
  1.4392 +    { MODIFIER_ALTGRAPH, NSAlternateKeyMask },
  1.4393 +    { MODIFIER_META,     NSCommandKeyMask },
  1.4394 +    { MODIFIER_CAPSLOCK, NSAlphaShiftKeyMask },
  1.4395 +    { MODIFIER_NUMLOCK,  NSNumericPadKeyMask }
  1.4396 +  };
  1.4397 +
  1.4398 +  NSUInteger modifierFlags = 0;
  1.4399 +  for (uint32_t i = 0; i < ArrayLength(sModifierFlagMap); ++i) {
  1.4400 +    if (aKeyEvent.modifiers & sModifierFlagMap[i][0]) {
  1.4401 +      modifierFlags |= sModifierFlagMap[i][1];
  1.4402 +    }
  1.4403 +  }
  1.4404 +
  1.4405 +  NSInteger windowNumber = [[mView window] windowNumber];
  1.4406 +
  1.4407 +  NSString* characters;
  1.4408 +  if (aKeyEvent.charCode) {
  1.4409 +    characters = [NSString stringWithCharacters:
  1.4410 +      reinterpret_cast<const unichar*>(&(aKeyEvent.charCode)) length:1];
  1.4411 +  } else {
  1.4412 +    uint32_t cocoaCharCode =
  1.4413 +      nsCocoaUtils::ConvertGeckoKeyCodeToMacCharCode(aKeyEvent.keyCode);
  1.4414 +    characters = [NSString stringWithCharacters:
  1.4415 +      reinterpret_cast<const unichar*>(&cocoaCharCode) length:1];
  1.4416 +  }
  1.4417 +
  1.4418 +  aKeyEvent.mNativeKeyEvent =
  1.4419 +    [NSEvent     keyEventWithType:eventType
  1.4420 +                         location:NSMakePoint(0,0)
  1.4421 +                    modifierFlags:modifierFlags
  1.4422 +                        timestamp:0
  1.4423 +                     windowNumber:windowNumber
  1.4424 +                          context:[NSGraphicsContext currentContext]
  1.4425 +                       characters:characters
  1.4426 +      charactersIgnoringModifiers:characters
  1.4427 +                        isARepeat:NO
  1.4428 +                          keyCode:0]; // Native key code not currently needed
  1.4429 +
  1.4430 +  return NS_OK;
  1.4431 +
  1.4432 +  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  1.4433 +}
  1.4434 +
  1.4435 +bool
  1.4436 +TextInputHandlerBase::SetSelection(NSRange& aRange)
  1.4437 +{
  1.4438 +  MOZ_ASSERT(!Destroyed());
  1.4439 +
  1.4440 +  nsRefPtr<TextInputHandlerBase> kungFuDeathGrip(this);
  1.4441 +  WidgetSelectionEvent selectionEvent(true, NS_SELECTION_SET, mWidget);
  1.4442 +  selectionEvent.mOffset = aRange.location;
  1.4443 +  selectionEvent.mLength = aRange.length;
  1.4444 +  selectionEvent.mReversed = false;
  1.4445 +  selectionEvent.mExpandToClusterBoundary = false;
  1.4446 +  DispatchEvent(selectionEvent);
  1.4447 +  NS_ENSURE_TRUE(selectionEvent.mSucceeded, false);
  1.4448 +  return !Destroyed();
  1.4449 +}
  1.4450 +
  1.4451 +/* static */ bool
  1.4452 +TextInputHandlerBase::IsPrintableChar(char16_t aChar)
  1.4453 +{
  1.4454 +  return (aChar >= 0x20 && aChar <= 0x7E) || aChar >= 0xA0;
  1.4455 +}
  1.4456 +
  1.4457 +
  1.4458 +/* static */ bool
  1.4459 +TextInputHandlerBase::IsSpecialGeckoKey(UInt32 aNativeKeyCode)
  1.4460 +{
  1.4461 +  // this table is used to determine which keys are special and should not
  1.4462 +  // generate a charCode
  1.4463 +  switch (aNativeKeyCode) {
  1.4464 +    // modifiers - we don't get separate events for these yet
  1.4465 +    case kVK_Escape:
  1.4466 +    case kVK_Shift:
  1.4467 +    case kVK_RightShift:
  1.4468 +    case kVK_Command:
  1.4469 +    case kVK_RightCommand:
  1.4470 +    case kVK_CapsLock:
  1.4471 +    case kVK_Control:
  1.4472 +    case kVK_RightControl:
  1.4473 +    case kVK_Option:
  1.4474 +    case kVK_RightOption:
  1.4475 +    case kVK_ANSI_KeypadClear:
  1.4476 +    case kVK_Function:
  1.4477 +
  1.4478 +    // function keys
  1.4479 +    case kVK_F1:
  1.4480 +    case kVK_F2:
  1.4481 +    case kVK_F3:
  1.4482 +    case kVK_F4:
  1.4483 +    case kVK_F5:
  1.4484 +    case kVK_F6:
  1.4485 +    case kVK_F7:
  1.4486 +    case kVK_F8:
  1.4487 +    case kVK_F9:
  1.4488 +    case kVK_F10:
  1.4489 +    case kVK_F11:
  1.4490 +    case kVK_F12:
  1.4491 +    case kVK_PC_Pause:
  1.4492 +    case kVK_PC_ScrollLock:
  1.4493 +    case kVK_PC_PrintScreen:
  1.4494 +    case kVK_F16:
  1.4495 +    case kVK_F17:
  1.4496 +    case kVK_F18:
  1.4497 +    case kVK_F19:
  1.4498 +
  1.4499 +    case kVK_PC_Insert:
  1.4500 +    case kVK_PC_Delete:
  1.4501 +    case kVK_Tab:
  1.4502 +    case kVK_PC_Backspace:
  1.4503 +    case kVK_PC_ContextMenu:
  1.4504 +
  1.4505 +    case kVK_JIS_Eisu:
  1.4506 +    case kVK_JIS_Kana:
  1.4507 +
  1.4508 +    case kVK_Home:
  1.4509 +    case kVK_End:
  1.4510 +    case kVK_PageUp:
  1.4511 +    case kVK_PageDown:
  1.4512 +    case kVK_LeftArrow:
  1.4513 +    case kVK_RightArrow:
  1.4514 +    case kVK_UpArrow:
  1.4515 +    case kVK_DownArrow:
  1.4516 +    case kVK_Return:
  1.4517 +    case kVK_ANSI_KeypadEnter:
  1.4518 +    case kVK_Powerbook_KeypadEnter:
  1.4519 +      return true;
  1.4520 +  }
  1.4521 +  return false;
  1.4522 +}
  1.4523 +
  1.4524 +/* static */ bool
  1.4525 +TextInputHandlerBase::IsNormalCharInputtingEvent(
  1.4526 +                        const WidgetKeyboardEvent& aKeyEvent)
  1.4527 +{
  1.4528 +  // this is not character inputting event, simply.
  1.4529 +  if (!aKeyEvent.isChar || !aKeyEvent.charCode || aKeyEvent.IsMeta()) {
  1.4530 +    return false;
  1.4531 +  }
  1.4532 +  // if this is unicode char inputting event, we don't need to check
  1.4533 +  // ctrl/alt/command keys
  1.4534 +  if (aKeyEvent.charCode > 0x7F) {
  1.4535 +    return true;
  1.4536 +  }
  1.4537 +  // ASCII chars should be inputted without ctrl/alt/command keys
  1.4538 +  return !aKeyEvent.IsControl() && !aKeyEvent.IsAlt();
  1.4539 +}
  1.4540 +
  1.4541 +/* static */ bool
  1.4542 +TextInputHandlerBase::IsModifierKey(UInt32 aNativeKeyCode)
  1.4543 +{
  1.4544 +  switch (aNativeKeyCode) {
  1.4545 +    case kVK_CapsLock:
  1.4546 +    case kVK_RightCommand:
  1.4547 +    case kVK_Command:
  1.4548 +    case kVK_Shift:
  1.4549 +    case kVK_Option:
  1.4550 +    case kVK_Control:
  1.4551 +    case kVK_RightShift:
  1.4552 +    case kVK_RightOption:
  1.4553 +    case kVK_RightControl:
  1.4554 +    case kVK_Function:
  1.4555 +      return true;
  1.4556 +  }
  1.4557 +  return false;
  1.4558 +}
  1.4559 +
  1.4560 +/* static */ void
  1.4561 +TextInputHandlerBase::EnableSecureEventInput()
  1.4562 +{
  1.4563 +  sSecureEventInputCount++;
  1.4564 +  ::EnableSecureEventInput();
  1.4565 +}
  1.4566 +
  1.4567 +/* static */ void
  1.4568 +TextInputHandlerBase::DisableSecureEventInput()
  1.4569 +{
  1.4570 +  if (!sSecureEventInputCount) {
  1.4571 +    return;
  1.4572 +  }
  1.4573 +  sSecureEventInputCount--;
  1.4574 +  ::DisableSecureEventInput();
  1.4575 +}
  1.4576 +
  1.4577 +/* static */ bool
  1.4578 +TextInputHandlerBase::IsSecureEventInputEnabled()
  1.4579 +{
  1.4580 +  NS_ASSERTION(!!sSecureEventInputCount == !!::IsSecureEventInputEnabled(),
  1.4581 +               "Some other process has enabled secure event input");
  1.4582 +  return !!sSecureEventInputCount;
  1.4583 +}
  1.4584 +
  1.4585 +/* static */ void
  1.4586 +TextInputHandlerBase::EnsureSecureEventInputDisabled()
  1.4587 +{
  1.4588 +  while (sSecureEventInputCount) {
  1.4589 +    TextInputHandlerBase::DisableSecureEventInput();
  1.4590 +  }
  1.4591 +}

mercurial