TextInputController.m revision 0bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5
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