Rename WebKitTools to Tools
[WebKit-https.git] / Tools / DumpRenderTree / mac / TextInputController.m
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