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