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 29#import "config.h" 30#import "TextInputController.h" 31 32#import "DumpRenderTreeMac.h" 33#import <AppKit/NSInputManager.h> 34#import <WebKit/WebDocument.h> 35#import <WebKit/WebFrame.h> 36#import <WebKit/WebFramePrivate.h> 37#import <WebKit/WebFrameView.h> 38#import <WebKit/WebHTMLViewPrivate.h> 39#import <WebKit/WebScriptObject.h> 40#import <WebKit/WebTypesInternal.h> 41#import <WebKit/WebView.h> 42 43@interface TextInputController (DumpRenderTreeInputMethodHandler) 44- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender; 45@end 46 47@interface WebHTMLView (DumpRenderTreeInputMethodHandler) 48- (void)interpretKeyEvents:(NSArray *)eventArray; 49@end 50 51@interface WebHTMLView (WebKitSecretsTextInputControllerIsAwareOf) 52- (WebFrame *)_frame; 53@end 54 55@implementation WebHTMLView (DumpRenderTreeInputMethodHandler) 56- (void)interpretKeyEvents:(NSArray *)eventArray 57{ 58 WebScriptObject *obj = [[self _frame] windowObject]; 59 TextInputController *tic = [obj valueForKey:@"textInputController"]; 60 if (![tic interpretKeyEvents:eventArray withSender:self]) 61 [super interpretKeyEvents:eventArray]; 62} 63@end 64 65@implementation NSMutableAttributedString (TextInputController) 66 67+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector 68{ 69 if (aSelector == @selector(string) 70 || aSelector == @selector(getLength) 71 || aSelector == @selector(attributeNamesAtIndex:) 72 || aSelector == @selector(valueOfAttribute:atIndex:) 73 || aSelector == @selector(addAttribute:value:) 74 || aSelector == @selector(addAttribute:value:from:length:) 75 || aSelector == @selector(addColorAttribute:red:green:blue:alpha:) 76 || aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:) 77 || aSelector == @selector(addFontAttribute:fontName:size:) 78 || aSelector == @selector(addFontAttribute:fontName:size:from:length:)) 79 return NO; 80 return YES; 81} 82 83+ (NSString *)webScriptNameForSelector:(SEL)aSelector 84{ 85 if (aSelector == @selector(getLength)) 86 return @"length"; 87 if (aSelector == @selector(attributeNamesAtIndex:)) 88 return @"getAttributeNamesAtIndex"; 89 if (aSelector == @selector(valueOfAttribute:atIndex:)) 90 return @"getAttributeValueAtIndex"; 91 if (aSelector == @selector(addAttribute:value:)) 92 return @"addAttribute"; 93 if (aSelector == @selector(addAttribute:value:from:length:)) 94 return @"addAttributeForRange"; 95 if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:)) 96 return @"addColorAttribute"; 97 if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:)) 98 return @"addColorAttributeForRange"; 99 if (aSelector == @selector(addFontAttribute:fontName:size:)) 100 return @"addFontAttribute"; 101 if (aSelector == @selector(addFontAttribute:fontName:size:from:length:)) 102 return @"addFontAttributeForRange"; 103 104 return nil; 105} 106 107- (int)getLength 108{ 109 return (int)[self length]; 110} 111 112- (NSArray *)attributeNamesAtIndex:(int)index 113{ 114 NSDictionary *attributes = [self attributesAtIndex:(unsigned)index effectiveRange:nil]; 115 return [attributes allKeys]; 116} 117 118- (id)valueOfAttribute:(NSString *)attrName atIndex:(int)index 119{ 120 return [self attribute:attrName atIndex:(unsigned)index effectiveRange:nil]; 121} 122 123- (void)addAttribute:(NSString *)attrName value:(id)value 124{ 125 [self addAttribute:attrName value:value range:NSMakeRange(0, [self length])]; 126} 127 128- (void)addAttribute:(NSString *)attrName value:(id)value from:(int)from length:(int)length 129{ 130 [self addAttribute:attrName value:value range:NSMakeRange((unsigned)from, (unsigned)length)]; 131} 132 133- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha 134{ 135 [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange(0, [self length])]; 136} 137 138- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha from:(int)from length:(int)length 139{ 140 [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange((unsigned)from, (unsigned)length)]; 141} 142 143- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize 144{ 145 [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange(0, [self length])]; 146} 147 148- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize from:(int)from length:(int)length 149{ 150 [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange((unsigned)from, (unsigned)length)]; 151} 152 153@end 154 155@implementation TextInputController 156 157+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector 158{ 159 if (aSelector == @selector(insertText:) 160 || aSelector == @selector(doCommand:) 161 || aSelector == @selector(setMarkedText:selectedFrom:length:) 162 || aSelector == @selector(unmarkText) 163 || aSelector == @selector(hasMarkedText) 164 || aSelector == @selector(conversationIdentifier) 165 || aSelector == @selector(substringFrom:length:) 166 || aSelector == @selector(attributedSubstringFrom:length:) 167 || aSelector == @selector(markedRange) 168 || aSelector == @selector(selectedRange) 169 || aSelector == @selector(firstRectForCharactersFrom:length:) 170 || aSelector == @selector(characterIndexForPointX:Y:) 171 || aSelector == @selector(validAttributesForMarkedText) 172 || aSelector == @selector(attributedStringWithString:) 173 || aSelector == @selector(setInputMethodHandler:)) 174 return NO; 175 return YES; 176} 177 178+ (NSString *)webScriptNameForSelector:(SEL)aSelector 179{ 180 if (aSelector == @selector(insertText:)) 181 return @"insertText"; 182 else if (aSelector == @selector(doCommand:)) 183 return @"doCommand"; 184 else if (aSelector == @selector(setMarkedText:selectedFrom:length:)) 185 return @"setMarkedText"; 186 else if (aSelector == @selector(substringFrom:length:)) 187 return @"substringFromRange"; 188 else if (aSelector == @selector(attributedSubstringFrom:length:)) 189 return @"attributedSubstringFromRange"; 190 else if (aSelector == @selector(firstRectForCharactersFrom:length:)) 191 return @"firstRectForCharacterRange"; 192 else if (aSelector == @selector(characterIndexForPointX:Y:)) 193 return @"characterIndexForPoint"; 194 else if (aSelector == @selector(attributedStringWithString:)) 195 return @"makeAttributedString"; // just a factory method, doesn't call into NSTextInput 196 else if (aSelector == @selector(setInputMethodHandler:)) 197 return @"setInputMethodHandler"; 198 199 return nil; 200} 201 202- (id)initWithWebView:(WebView *)wv 203{ 204 self = [super init]; 205 webView = wv; 206 inputMethodView = nil; 207 inputMethodHandler = nil; 208 return self; 209} 210 211- (void)dealloc 212{ 213 [inputMethodHandler release]; 214 inputMethodHandler = nil; 215 216 [super dealloc]; 217} 218 219- (NSObject <NSTextInput> *)textInput 220{ 221 NSView <NSTextInput> *view = inputMethodView ? inputMethodView : (id)[[[webView mainFrame] frameView] documentView]; 222 return [view conformsToProtocol:@protocol(NSTextInput)] ? view : nil; 223} 224 225- (void)insertText:(id)aString 226{ 227 NSObject <NSTextInput> *textInput = [self textInput]; 228 229 if (textInput) 230 [textInput insertText:aString]; 231} 232 233- (void)doCommand:(NSString *)aCommand 234{ 235 NSObject <NSTextInput> *textInput = [self textInput]; 236 237 if (textInput) 238 [textInput doCommandBySelector:NSSelectorFromString(aCommand)]; 239} 240 241- (void)setMarkedText:(NSString *)aString selectedFrom:(int)from length:(int)length 242{ 243 NSObject <NSTextInput> *textInput = [self textInput]; 244 245 if (textInput) 246 [textInput setMarkedText:aString selectedRange:NSMakeRange(from, length)]; 247} 248 249- (void)unmarkText 250{ 251 NSObject <NSTextInput> *textInput = [self textInput]; 252 253 if (textInput) 254 [textInput unmarkText]; 255} 256 257- (BOOL)hasMarkedText 258{ 259 NSObject <NSTextInput> *textInput = [self textInput]; 260 261 if (textInput) 262 return [textInput hasMarkedText]; 263 264 return FALSE; 265} 266 267- (long)conversationIdentifier 268{ 269 NSObject <NSTextInput> *textInput = [self textInput]; 270 271 if (textInput) 272 return [textInput conversationIdentifier]; 273 274 return 0; 275} 276 277- (NSString *)substringFrom:(int)from length:(int)length 278{ 279 NSObject <NSTextInput> *textInput = [self textInput]; 280 281 if (textInput) 282 return [[textInput attributedSubstringFromRange:NSMakeRange(from, length)] string]; 283 284 return @""; 285} 286 287- (NSMutableAttributedString *)attributedSubstringFrom:(int)from length:(int)length 288{ 289 NSObject <NSTextInput> *textInput = [self textInput]; 290 291 NSMutableAttributedString *ret = [[[NSMutableAttributedString alloc] init] autorelease]; 292 293 if (textInput) 294 [ret setAttributedString:[textInput attributedSubstringFromRange:NSMakeRange(from, length)]]; 295 296 return ret; 297} 298 299- (NSArray *)markedRange 300{ 301 NSObject <NSTextInput> *textInput = [self textInput]; 302 303 if (textInput) { 304 NSRange range = [textInput markedRange]; 305 return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil]; 306 } 307 308 return nil; 309} 310 311- (NSArray *)selectedRange 312{ 313 NSObject <NSTextInput> *textInput = [self textInput]; 314 315 if (textInput) { 316 NSRange range = [textInput selectedRange]; 317 return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil]; 318 } 319 320 return nil; 321} 322 323 324- (NSArray *)firstRectForCharactersFrom:(int)from length:(int)length 325{ 326 NSObject <NSTextInput> *textInput = [self textInput]; 327 328 if (textInput) { 329 NSRect rect = [textInput firstRectForCharacterRange:NSMakeRange(from, length)]; 330 if (rect.origin.x || rect.origin.y || rect.size.width || rect.size.height) { 331 rect.origin = [[webView window] convertScreenToBase:rect.origin]; 332 rect = [webView convertRect:rect fromView:nil]; 333 } 334 return [NSArray arrayWithObjects: 335 [NSNumber numberWithFloat:rect.origin.x], 336 [NSNumber numberWithFloat:rect.origin.y], 337 [NSNumber numberWithFloat:rect.size.width], 338 [NSNumber numberWithFloat:rect.size.height], 339 nil]; 340 } 341 342 return nil; 343} 344 345- (NSInteger)characterIndexForPointX:(float)x Y:(float)y 346{ 347 NSObject <NSTextInput> *textInput = [self textInput]; 348 349 if (textInput) { 350 NSPoint point = NSMakePoint(x, y); 351 point = [webView convertPoint:point toView:nil]; 352 point = [[webView window] convertBaseToScreen:point]; 353 NSInteger index = [textInput characterIndexForPoint:point]; 354 if (index == NSNotFound) 355 return -1; 356 357 return index; 358 } 359 360 return 0; 361} 362 363- (NSArray *)validAttributesForMarkedText 364{ 365 NSObject <NSTextInput> *textInput = [self textInput]; 366 367 if (textInput) 368 return [textInput validAttributesForMarkedText]; 369 370 return nil; 371} 372 373- (NSMutableAttributedString *)attributedStringWithString:(NSString *)aString 374{ 375 return [[[NSMutableAttributedString alloc] initWithString:aString] autorelease]; 376} 377 378- (void)setInputMethodHandler:(WebScriptObject *)handler 379{ 380 if (inputMethodHandler == handler) 381 return; 382 [handler retain]; 383 [inputMethodHandler release]; 384 inputMethodHandler = handler; 385} 386 387- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender 388{ 389 if (!inputMethodHandler) 390 return NO; 391 392 inputMethodView = sender; 393 394 NSEvent *event = [eventArray objectAtIndex:0]; 395 unsigned modifierFlags = [event modifierFlags]; 396 NSMutableArray *modifiers = [[NSMutableArray alloc] init]; 397 if (modifierFlags & NSAlphaShiftKeyMask) 398 [modifiers addObject:@"NSAlphaShiftKeyMask"]; 399 if (modifierFlags & NSShiftKeyMask) 400 [modifiers addObject:@"NSShiftKeyMask"]; 401 if (modifierFlags & NSControlKeyMask) 402 [modifiers addObject:@"NSControlKeyMask"]; 403 if (modifierFlags & NSAlternateKeyMask) 404 [modifiers addObject:@"NSAlternateKeyMask"]; 405 if (modifierFlags & NSCommandKeyMask) 406 [modifiers addObject:@"NSCommandKeyMask"]; 407 if (modifierFlags & NSNumericPadKeyMask) 408 [modifiers addObject:@"NSNumericPadKeyMask"]; 409 if (modifierFlags & NSHelpKeyMask) 410 [modifiers addObject:@"NSHelpKeyMask"]; 411 if (modifierFlags & NSFunctionKeyMask) 412 [modifiers addObject:@"NSFunctionKeyMask"]; 413 414 WebScriptObject* eventParam = [inputMethodHandler evaluateWebScript:@"new Object();"]; 415 [eventParam setValue:[event characters] forKey:@"characters"]; 416 [eventParam setValue:[event charactersIgnoringModifiers] forKey:@"charactersIgnoringModifiers"]; 417 [eventParam setValue:[NSNumber numberWithBool:[event isARepeat]] forKey:@"isARepeat"]; 418 [eventParam setValue:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:@"keyCode"]; 419 [eventParam setValue:modifiers forKey:@"modifierFlags"]; 420 421 [modifiers release]; 422 423 id result = [inputMethodHandler callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:inputMethodHandler, eventParam, nil]]; 424 if (![result respondsToSelector:@selector(boolValue)] || ![result boolValue]) 425 [sender doCommandBySelector:@selector(noop:)]; // AppKit sends noop: if the ime does not handle an event 426 427 inputMethodView = nil; 428 return YES; 429} 430 431@end 432