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