TextInputController.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. /*
  2. * Copyright (C) 2005, 2007 Apple Inc. All rights reserved.
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. *
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
  14. * its contributors may be used to endorse or promote products derived
  15. * from this software without specific prior written permission.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
  18. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
  21. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  24. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #import "config.h"
  29. #import "TextInputController.h"
  30. #import "DumpRenderTreeMac.h"
  31. #import <AppKit/NSInputManager.h>
  32. #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080
  33. #define SUPPORT_DICTATION_ALTERNATIVES
  34. #import <AppKit/NSTextAlternatives.h>
  35. #endif
  36. #import <WebKit/WebDocument.h>
  37. #import <WebKit/WebFrame.h>
  38. #import <WebKit/WebFramePrivate.h>
  39. #import <WebKit/WebFrameView.h>
  40. #import <WebKit/WebHTMLViewPrivate.h>
  41. #import <WebKit/WebScriptObject.h>
  42. #import <WebKit/WebTypesInternal.h>
  43. #import <WebKit/WebView.h>
  44. @interface TextInputController (DumpRenderTreeInputMethodHandler)
  45. - (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender;
  46. @end
  47. @interface WebHTMLView (DumpRenderTreeInputMethodHandler)
  48. - (void)interpretKeyEvents:(NSArray *)eventArray;
  49. @end
  50. @interface WebHTMLView (WebKitSecretsTextInputControllerIsAwareOf)
  51. - (WebFrame *)_frame;
  52. @end
  53. @implementation WebHTMLView (DumpRenderTreeInputMethodHandler)
  54. - (void)interpretKeyEvents:(NSArray *)eventArray
  55. {
  56. WebScriptObject *obj = [[self _frame] windowObject];
  57. TextInputController *tic = [obj valueForKey:@"textInputController"];
  58. if (![tic interpretKeyEvents:eventArray withSender:self])
  59. [super interpretKeyEvents:eventArray];
  60. }
  61. @end
  62. @implementation NSMutableAttributedString (TextInputController)
  63. + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
  64. {
  65. if (aSelector == @selector(string)
  66. || aSelector == @selector(getLength)
  67. || aSelector == @selector(attributeNamesAtIndex:)
  68. || aSelector == @selector(valueOfAttribute:atIndex:)
  69. || aSelector == @selector(addAttribute:value:)
  70. || aSelector == @selector(addAttribute:value:from:length:)
  71. || aSelector == @selector(addColorAttribute:red:green:blue:alpha:)
  72. || aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:)
  73. || aSelector == @selector(addFontAttribute:fontName:size:)
  74. || aSelector == @selector(addFontAttribute:fontName:size:from:length:))
  75. return NO;
  76. return YES;
  77. }
  78. + (NSString *)webScriptNameForSelector:(SEL)aSelector
  79. {
  80. if (aSelector == @selector(getLength))
  81. return @"length";
  82. if (aSelector == @selector(attributeNamesAtIndex:))
  83. return @"getAttributeNamesAtIndex";
  84. if (aSelector == @selector(valueOfAttribute:atIndex:))
  85. return @"getAttributeValueAtIndex";
  86. if (aSelector == @selector(addAttribute:value:))
  87. return @"addAttribute";
  88. if (aSelector == @selector(addAttribute:value:from:length:))
  89. return @"addAttributeForRange";
  90. if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:))
  91. return @"addColorAttribute";
  92. if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:))
  93. return @"addColorAttributeForRange";
  94. if (aSelector == @selector(addFontAttribute:fontName:size:))
  95. return @"addFontAttribute";
  96. if (aSelector == @selector(addFontAttribute:fontName:size:from:length:))
  97. return @"addFontAttributeForRange";
  98. return nil;
  99. }
  100. - (int)getLength
  101. {
  102. return (int)[self length];
  103. }
  104. - (NSArray *)attributeNamesAtIndex:(int)index
  105. {
  106. NSDictionary *attributes = [self attributesAtIndex:(unsigned)index effectiveRange:nil];
  107. return [attributes allKeys];
  108. }
  109. - (id)valueOfAttribute:(NSString *)attrName atIndex:(int)index
  110. {
  111. return [self attribute:attrName atIndex:(unsigned)index effectiveRange:nil];
  112. }
  113. - (void)addAttribute:(NSString *)attrName value:(id)value
  114. {
  115. [self addAttribute:attrName value:value range:NSMakeRange(0, [self length])];
  116. }
  117. - (void)addAttribute:(NSString *)attrName value:(id)value from:(int)from length:(int)length
  118. {
  119. [self addAttribute:attrName value:value range:NSMakeRange((unsigned)from, (unsigned)length)];
  120. }
  121. - (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
  122. {
  123. [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange(0, [self length])];
  124. }
  125. - (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha from:(int)from length:(int)length
  126. {
  127. [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange((unsigned)from, (unsigned)length)];
  128. }
  129. - (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize
  130. {
  131. [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange(0, [self length])];
  132. }
  133. - (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize from:(int)from length:(int)length
  134. {
  135. [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange((unsigned)from, (unsigned)length)];
  136. }
  137. @end
  138. @implementation TextInputController
  139. + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
  140. {
  141. if (aSelector == @selector(insertText:)
  142. || aSelector == @selector(doCommand:)
  143. || aSelector == @selector(setMarkedText:selectedFrom:length:)
  144. || aSelector == @selector(unmarkText)
  145. || aSelector == @selector(hasMarkedText)
  146. || aSelector == @selector(conversationIdentifier)
  147. || aSelector == @selector(substringFrom:length:)
  148. || aSelector == @selector(attributedSubstringFrom:length:)
  149. || aSelector == @selector(markedRange)
  150. || aSelector == @selector(selectedRange)
  151. || aSelector == @selector(firstRectForCharactersFrom:length:)
  152. || aSelector == @selector(characterIndexForPointX:Y:)
  153. || aSelector == @selector(validAttributesForMarkedText)
  154. || aSelector == @selector(attributedStringWithString:)
  155. || aSelector == @selector(setInputMethodHandler:)
  156. || aSelector == @selector(dictatedStringWithPrimaryString:alternative:alternativeOffset:alternativeLength:))
  157. return NO;
  158. return YES;
  159. }
  160. + (NSString *)webScriptNameForSelector:(SEL)aSelector
  161. {
  162. if (aSelector == @selector(insertText:))
  163. return @"insertText";
  164. else if (aSelector == @selector(doCommand:))
  165. return @"doCommand";
  166. else if (aSelector == @selector(setMarkedText:selectedFrom:length:))
  167. return @"setMarkedText";
  168. else if (aSelector == @selector(substringFrom:length:))
  169. return @"substringFromRange";
  170. else if (aSelector == @selector(attributedSubstringFrom:length:))
  171. return @"attributedSubstringFromRange";
  172. else if (aSelector == @selector(firstRectForCharactersFrom:length:))
  173. return @"firstRectForCharacterRange";
  174. else if (aSelector == @selector(characterIndexForPointX:Y:))
  175. return @"characterIndexForPoint";
  176. else if (aSelector == @selector(attributedStringWithString:))
  177. return @"makeAttributedString"; // just a factory method, doesn't call into NSTextInput
  178. else if (aSelector == @selector(setInputMethodHandler:))
  179. return @"setInputMethodHandler";
  180. else if (aSelector == @selector(dictatedStringWithPrimaryString:alternative:alternativeOffset:alternativeLength:))
  181. return @"makeDictatedString";
  182. return nil;
  183. }
  184. - (id)initWithWebView:(WebView *)wv
  185. {
  186. self = [super init];
  187. webView = wv;
  188. inputMethodView = nil;
  189. inputMethodHandler = nil;
  190. return self;
  191. }
  192. - (void)dealloc
  193. {
  194. [inputMethodHandler release];
  195. inputMethodHandler = nil;
  196. [super dealloc];
  197. }
  198. - (NSObject <NSTextInput> *)textInput
  199. {
  200. NSView <NSTextInput> *view = inputMethodView ? inputMethodView : (id)[[[webView mainFrame] frameView] documentView];
  201. return [view conformsToProtocol:@protocol(NSTextInput)] ? view : nil;
  202. }
  203. - (void)insertText:(id)aString
  204. {
  205. NSObject <NSTextInput> *textInput = [self textInput];
  206. if (textInput)
  207. [textInput insertText:aString];
  208. }
  209. - (void)doCommand:(NSString *)aCommand
  210. {
  211. NSObject <NSTextInput> *textInput = [self textInput];
  212. if (textInput)
  213. [textInput doCommandBySelector:NSSelectorFromString(aCommand)];
  214. }
  215. - (void)setMarkedText:(NSString *)aString selectedFrom:(int)from length:(int)length
  216. {
  217. NSObject <NSTextInput> *textInput = [self textInput];
  218. if (textInput)
  219. [textInput setMarkedText:aString selectedRange:NSMakeRange(from, length)];
  220. }
  221. - (void)unmarkText
  222. {
  223. NSObject <NSTextInput> *textInput = [self textInput];
  224. if (textInput)
  225. [textInput unmarkText];
  226. }
  227. - (BOOL)hasMarkedText
  228. {
  229. NSObject <NSTextInput> *textInput = [self textInput];
  230. if (textInput)
  231. return [textInput hasMarkedText];
  232. return FALSE;
  233. }
  234. - (long)conversationIdentifier
  235. {
  236. NSObject <NSTextInput> *textInput = [self textInput];
  237. if (textInput)
  238. return [textInput conversationIdentifier];
  239. return 0;
  240. }
  241. - (NSString *)substringFrom:(int)from length:(int)length
  242. {
  243. NSObject <NSTextInput> *textInput = [self textInput];
  244. if (textInput)
  245. return [[textInput attributedSubstringFromRange:NSMakeRange(from, length)] string];
  246. return @"";
  247. }
  248. - (NSMutableAttributedString *)attributedSubstringFrom:(int)from length:(int)length
  249. {
  250. NSObject <NSTextInput> *textInput = [self textInput];
  251. NSMutableAttributedString *ret = [[[NSMutableAttributedString alloc] init] autorelease];
  252. if (textInput)
  253. [ret setAttributedString:[textInput attributedSubstringFromRange:NSMakeRange(from, length)]];
  254. return ret;
  255. }
  256. - (NSArray *)markedRange
  257. {
  258. NSObject <NSTextInput> *textInput = [self textInput];
  259. if (textInput) {
  260. NSRange range = [textInput markedRange];
  261. return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil];
  262. }
  263. return nil;
  264. }
  265. - (NSArray *)selectedRange
  266. {
  267. NSObject <NSTextInput> *textInput = [self textInput];
  268. if (textInput) {
  269. NSRange range = [textInput selectedRange];
  270. return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil];
  271. }
  272. return nil;
  273. }
  274. - (NSArray *)firstRectForCharactersFrom:(int)from length:(int)length
  275. {
  276. NSObject <NSTextInput> *textInput = [self textInput];
  277. if (textInput) {
  278. NSRect rect = [textInput firstRectForCharacterRange:NSMakeRange(from, length)];
  279. if (rect.origin.x || rect.origin.y || rect.size.width || rect.size.height) {
  280. rect.origin = [[webView window] convertScreenToBase:rect.origin];
  281. rect = [webView convertRect:rect fromView:nil];
  282. }
  283. return [NSArray arrayWithObjects:
  284. [NSNumber numberWithFloat:rect.origin.x],
  285. [NSNumber numberWithFloat:rect.origin.y],
  286. [NSNumber numberWithFloat:rect.size.width],
  287. [NSNumber numberWithFloat:rect.size.height],
  288. nil];
  289. }
  290. return nil;
  291. }
  292. - (NSInteger)characterIndexForPointX:(float)x Y:(float)y
  293. {
  294. NSObject <NSTextInput> *textInput = [self textInput];
  295. if (textInput) {
  296. NSPoint point = NSMakePoint(x, y);
  297. point = [webView convertPoint:point toView:nil];
  298. point = [[webView window] convertBaseToScreen:point];
  299. NSInteger index = [textInput characterIndexForPoint:point];
  300. if (index == NSNotFound)
  301. return -1;
  302. return index;
  303. }
  304. return 0;
  305. }
  306. - (NSArray *)validAttributesForMarkedText
  307. {
  308. NSObject <NSTextInput> *textInput = [self textInput];
  309. if (textInput)
  310. return [textInput validAttributesForMarkedText];
  311. return nil;
  312. }
  313. - (NSMutableAttributedString *)attributedStringWithString:(NSString *)aString
  314. {
  315. return [[[NSMutableAttributedString alloc] initWithString:aString] autorelease];
  316. }
  317. - (NSMutableAttributedString*)dictatedStringWithPrimaryString:(NSString*)aString alternative:(NSString*)alternative alternativeOffset:(int)offset alternativeLength:(int)length
  318. {
  319. #if defined(SUPPORT_DICTATION_ALTERNATIVES)
  320. NSMutableAttributedString* dictatedString = [self attributedStringWithString:aString];
  321. NSRange rangeWithAlternative = NSMakeRange((NSUInteger)offset, (NSUInteger)length);
  322. NSString* subStringWithAlternative = [aString substringWithRange:rangeWithAlternative];
  323. if (!subStringWithAlternative)
  324. return nil;
  325. NSTextAlternatives* alternativeObject = [[[NSTextAlternatives alloc] initWithPrimaryString:subStringWithAlternative alternativeStrings:[NSArray arrayWithObject:alternative]] autorelease];
  326. if (!alternativeObject)
  327. return nil;
  328. [dictatedString addAttribute:NSTextAlternativesAttributeName value:alternativeObject range:rangeWithAlternative];
  329. return dictatedString;
  330. #else
  331. return nil;
  332. #endif
  333. }
  334. - (void)setInputMethodHandler:(WebScriptObject *)handler
  335. {
  336. if (inputMethodHandler == handler)
  337. return;
  338. [handler retain];
  339. [inputMethodHandler release];
  340. inputMethodHandler = handler;
  341. }
  342. - (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender
  343. {
  344. if (!inputMethodHandler)
  345. return NO;
  346. inputMethodView = sender;
  347. NSEvent *event = [eventArray objectAtIndex:0];
  348. unsigned modifierFlags = [event modifierFlags];
  349. NSMutableArray *modifiers = [[NSMutableArray alloc] init];
  350. if (modifierFlags & NSAlphaShiftKeyMask)
  351. [modifiers addObject:@"NSAlphaShiftKeyMask"];
  352. if (modifierFlags & NSShiftKeyMask)
  353. [modifiers addObject:@"NSShiftKeyMask"];
  354. if (modifierFlags & NSControlKeyMask)
  355. [modifiers addObject:@"NSControlKeyMask"];
  356. if (modifierFlags & NSAlternateKeyMask)
  357. [modifiers addObject:@"NSAlternateKeyMask"];
  358. if (modifierFlags & NSCommandKeyMask)
  359. [modifiers addObject:@"NSCommandKeyMask"];
  360. if (modifierFlags & NSNumericPadKeyMask)
  361. [modifiers addObject:@"NSNumericPadKeyMask"];
  362. if (modifierFlags & NSHelpKeyMask)
  363. [modifiers addObject:@"NSHelpKeyMask"];
  364. if (modifierFlags & NSFunctionKeyMask)
  365. [modifiers addObject:@"NSFunctionKeyMask"];
  366. WebScriptObject* eventParam = [inputMethodHandler evaluateWebScript:@"new Object();"];
  367. [eventParam setValue:[event characters] forKey:@"characters"];
  368. [eventParam setValue:[event charactersIgnoringModifiers] forKey:@"charactersIgnoringModifiers"];
  369. [eventParam setValue:[NSNumber numberWithBool:[event isARepeat]] forKey:@"isARepeat"];
  370. [eventParam setValue:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:@"keyCode"];
  371. [eventParam setValue:modifiers forKey:@"modifierFlags"];
  372. [modifiers release];
  373. id result = [inputMethodHandler callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:inputMethodHandler, eventParam, nil]];
  374. if (![result respondsToSelector:@selector(boolValue)] || ![result boolValue])
  375. [sender doCommandBySelector:@selector(noop:)]; // AppKit sends noop: if the ime does not handle an event
  376. inputMethodView = nil;
  377. return YES;
  378. }
  379. @end