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 +}