Home | History | Annotate | Download | only in mac
      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