summaryrefslogtreecommitdiffstats
path: root/Tools/DumpRenderTree/mac/TextInputController.m
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/DumpRenderTree/mac/TextInputController.m')
-rw-r--r--Tools/DumpRenderTree/mac/TextInputController.m431
1 files changed, 431 insertions, 0 deletions
diff --git a/Tools/DumpRenderTree/mac/TextInputController.m b/Tools/DumpRenderTree/mac/TextInputController.m
new file mode 100644
index 0000000..f780794
--- /dev/null
+++ b/Tools/DumpRenderTree/mac/TextInputController.m
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2005, 2007 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "config.h"
+#import "TextInputController.h"
+
+#import "DumpRenderTreeMac.h"
+#import <AppKit/NSInputManager.h>
+#import <WebKit/WebDocument.h>
+#import <WebKit/WebFrame.h>
+#import <WebKit/WebFramePrivate.h>
+#import <WebKit/WebFrameView.h>
+#import <WebKit/WebHTMLViewPrivate.h>
+#import <WebKit/WebScriptObject.h>
+#import <WebKit/WebTypesInternal.h>
+#import <WebKit/WebView.h>
+
+@interface TextInputController (DumpRenderTreeInputMethodHandler)
+- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender;
+@end
+
+@interface WebHTMLView (DumpRenderTreeInputMethodHandler)
+- (void)interpretKeyEvents:(NSArray *)eventArray;
+@end
+
+@interface WebHTMLView (WebKitSecretsTextInputControllerIsAwareOf)
+- (WebFrame *)_frame;
+@end
+
+@implementation WebHTMLView (DumpRenderTreeInputMethodHandler)
+- (void)interpretKeyEvents:(NSArray *)eventArray
+{
+ WebScriptObject *obj = [[self _frame] windowObject];
+ TextInputController *tic = [obj valueForKey:@"textInputController"];
+ if (![tic interpretKeyEvents:eventArray withSender:self])
+ [super interpretKeyEvents:eventArray];
+}
+@end
+
+@implementation NSMutableAttributedString (TextInputController)
+
++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
+{
+ if (aSelector == @selector(string)
+ || aSelector == @selector(getLength)
+ || aSelector == @selector(attributeNamesAtIndex:)
+ || aSelector == @selector(valueOfAttribute:atIndex:)
+ || aSelector == @selector(addAttribute:value:)
+ || aSelector == @selector(addAttribute:value:from:length:)
+ || aSelector == @selector(addColorAttribute:red:green:blue:alpha:)
+ || aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:)
+ || aSelector == @selector(addFontAttribute:fontName:size:)
+ || aSelector == @selector(addFontAttribute:fontName:size:from:length:))
+ return NO;
+ return YES;
+}
+
++ (NSString *)webScriptNameForSelector:(SEL)aSelector
+{
+ if (aSelector == @selector(getLength))
+ return @"length";
+ if (aSelector == @selector(attributeNamesAtIndex:))
+ return @"getAttributeNamesAtIndex";
+ if (aSelector == @selector(valueOfAttribute:atIndex:))
+ return @"getAttributeValueAtIndex";
+ if (aSelector == @selector(addAttribute:value:))
+ return @"addAttribute";
+ if (aSelector == @selector(addAttribute:value:from:length:))
+ return @"addAttributeForRange";
+ if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:))
+ return @"addColorAttribute";
+ if (aSelector == @selector(addColorAttribute:red:green:blue:alpha:from:length:))
+ return @"addColorAttributeForRange";
+ if (aSelector == @selector(addFontAttribute:fontName:size:))
+ return @"addFontAttribute";
+ if (aSelector == @selector(addFontAttribute:fontName:size:from:length:))
+ return @"addFontAttributeForRange";
+
+ return nil;
+}
+
+- (int)getLength
+{
+ return (int)[self length];
+}
+
+- (NSArray *)attributeNamesAtIndex:(int)index
+{
+ NSDictionary *attributes = [self attributesAtIndex:(unsigned)index effectiveRange:nil];
+ return [attributes allKeys];
+}
+
+- (id)valueOfAttribute:(NSString *)attrName atIndex:(int)index
+{
+ return [self attribute:attrName atIndex:(unsigned)index effectiveRange:nil];
+}
+
+- (void)addAttribute:(NSString *)attrName value:(id)value
+{
+ [self addAttribute:attrName value:value range:NSMakeRange(0, [self length])];
+}
+
+- (void)addAttribute:(NSString *)attrName value:(id)value from:(int)from length:(int)length
+{
+ [self addAttribute:attrName value:value range:NSMakeRange((unsigned)from, (unsigned)length)];
+}
+
+- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha
+{
+ [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange(0, [self length])];
+}
+
+- (void)addColorAttribute:(NSString *)attrName red:(float)red green:(float)green blue:(float)blue alpha:(float)alpha from:(int)from length:(int)length
+{
+ [self addAttribute:attrName value:[NSColor colorWithDeviceRed:red green:green blue:blue alpha:alpha] range:NSMakeRange((unsigned)from, (unsigned)length)];
+}
+
+- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize
+{
+ [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange(0, [self length])];
+}
+
+- (void)addFontAttribute:(NSString *)attrName fontName:(NSString *)fontName size:(float)fontSize from:(int)from length:(int)length
+{
+ [self addAttribute:attrName value:[NSFont fontWithName:fontName size:fontSize] range:NSMakeRange((unsigned)from, (unsigned)length)];
+}
+
+@end
+
+@implementation TextInputController
+
++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector
+{
+ if (aSelector == @selector(insertText:)
+ || aSelector == @selector(doCommand:)
+ || aSelector == @selector(setMarkedText:selectedFrom:length:)
+ || aSelector == @selector(unmarkText)
+ || aSelector == @selector(hasMarkedText)
+ || aSelector == @selector(conversationIdentifier)
+ || aSelector == @selector(substringFrom:length:)
+ || aSelector == @selector(attributedSubstringFrom:length:)
+ || aSelector == @selector(markedRange)
+ || aSelector == @selector(selectedRange)
+ || aSelector == @selector(firstRectForCharactersFrom:length:)
+ || aSelector == @selector(characterIndexForPointX:Y:)
+ || aSelector == @selector(validAttributesForMarkedText)
+ || aSelector == @selector(attributedStringWithString:)
+ || aSelector == @selector(setInputMethodHandler:))
+ return NO;
+ return YES;
+}
+
++ (NSString *)webScriptNameForSelector:(SEL)aSelector
+{
+ if (aSelector == @selector(insertText:))
+ return @"insertText";
+ else if (aSelector == @selector(doCommand:))
+ return @"doCommand";
+ else if (aSelector == @selector(setMarkedText:selectedFrom:length:))
+ return @"setMarkedText";
+ else if (aSelector == @selector(substringFrom:length:))
+ return @"substringFromRange";
+ else if (aSelector == @selector(attributedSubstringFrom:length:))
+ return @"attributedSubstringFromRange";
+ else if (aSelector == @selector(firstRectForCharactersFrom:length:))
+ return @"firstRectForCharacterRange";
+ else if (aSelector == @selector(characterIndexForPointX:Y:))
+ return @"characterIndexForPoint";
+ else if (aSelector == @selector(attributedStringWithString:))
+ return @"makeAttributedString"; // just a factory method, doesn't call into NSTextInput
+ else if (aSelector == @selector(setInputMethodHandler:))
+ return @"setInputMethodHandler";
+
+ return nil;
+}
+
+- (id)initWithWebView:(WebView *)wv
+{
+ self = [super init];
+ webView = wv;
+ inputMethodView = nil;
+ inputMethodHandler = nil;
+ return self;
+}
+
+- (void)dealloc
+{
+ [inputMethodHandler release];
+ inputMethodHandler = nil;
+
+ [super dealloc];
+}
+
+- (NSObject <NSTextInput> *)textInput
+{
+ NSView <NSTextInput> *view = inputMethodView ? inputMethodView : (id)[[[webView mainFrame] frameView] documentView];
+ return [view conformsToProtocol:@protocol(NSTextInput)] ? view : nil;
+}
+
+- (void)insertText:(id)aString
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ [textInput insertText:aString];
+}
+
+- (void)doCommand:(NSString *)aCommand
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ [textInput doCommandBySelector:NSSelectorFromString(aCommand)];
+}
+
+- (void)setMarkedText:(NSString *)aString selectedFrom:(int)from length:(int)length
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ [textInput setMarkedText:aString selectedRange:NSMakeRange(from, length)];
+}
+
+- (void)unmarkText
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ [textInput unmarkText];
+}
+
+- (BOOL)hasMarkedText
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ return [textInput hasMarkedText];
+
+ return FALSE;
+}
+
+- (long)conversationIdentifier
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ return [textInput conversationIdentifier];
+
+ return 0;
+}
+
+- (NSString *)substringFrom:(int)from length:(int)length
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ return [[textInput attributedSubstringFromRange:NSMakeRange(from, length)] string];
+
+ return @"";
+}
+
+- (NSMutableAttributedString *)attributedSubstringFrom:(int)from length:(int)length
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ NSMutableAttributedString *ret = [[[NSMutableAttributedString alloc] init] autorelease];
+
+ if (textInput)
+ [ret setAttributedString:[textInput attributedSubstringFromRange:NSMakeRange(from, length)]];
+
+ return ret;
+}
+
+- (NSArray *)markedRange
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput) {
+ NSRange range = [textInput markedRange];
+ return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil];
+ }
+
+ return nil;
+}
+
+- (NSArray *)selectedRange
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput) {
+ NSRange range = [textInput selectedRange];
+ return [NSArray arrayWithObjects:[NSNumber numberWithUnsignedInt:range.location], [NSNumber numberWithUnsignedInt:range.length], nil];
+ }
+
+ return nil;
+}
+
+
+- (NSArray *)firstRectForCharactersFrom:(int)from length:(int)length
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput) {
+ NSRect rect = [textInput firstRectForCharacterRange:NSMakeRange(from, length)];
+ if (rect.origin.x || rect.origin.y || rect.size.width || rect.size.height) {
+ rect.origin = [[webView window] convertScreenToBase:rect.origin];
+ rect = [webView convertRect:rect fromView:nil];
+ }
+ return [NSArray arrayWithObjects:
+ [NSNumber numberWithFloat:rect.origin.x],
+ [NSNumber numberWithFloat:rect.origin.y],
+ [NSNumber numberWithFloat:rect.size.width],
+ [NSNumber numberWithFloat:rect.size.height],
+ nil];
+ }
+
+ return nil;
+}
+
+- (NSInteger)characterIndexForPointX:(float)x Y:(float)y
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput) {
+ NSPoint point = NSMakePoint(x, y);
+ point = [webView convertPoint:point toView:nil];
+ point = [[webView window] convertBaseToScreen:point];
+ NSInteger index = [textInput characterIndexForPoint:point];
+ if (index == NSNotFound)
+ return -1;
+
+ return index;
+ }
+
+ return 0;
+}
+
+- (NSArray *)validAttributesForMarkedText
+{
+ NSObject <NSTextInput> *textInput = [self textInput];
+
+ if (textInput)
+ return [textInput validAttributesForMarkedText];
+
+ return nil;
+}
+
+- (NSMutableAttributedString *)attributedStringWithString:(NSString *)aString
+{
+ return [[[NSMutableAttributedString alloc] initWithString:aString] autorelease];
+}
+
+- (void)setInputMethodHandler:(WebScriptObject *)handler
+{
+ if (inputMethodHandler == handler)
+ return;
+ [handler retain];
+ [inputMethodHandler release];
+ inputMethodHandler = handler;
+}
+
+- (BOOL)interpretKeyEvents:(NSArray *)eventArray withSender:(WebHTMLView *)sender
+{
+ if (!inputMethodHandler)
+ return NO;
+
+ inputMethodView = sender;
+
+ NSEvent *event = [eventArray objectAtIndex:0];
+ unsigned modifierFlags = [event modifierFlags];
+ NSMutableArray *modifiers = [[NSMutableArray alloc] init];
+ if (modifierFlags & NSAlphaShiftKeyMask)
+ [modifiers addObject:@"NSAlphaShiftKeyMask"];
+ if (modifierFlags & NSShiftKeyMask)
+ [modifiers addObject:@"NSShiftKeyMask"];
+ if (modifierFlags & NSControlKeyMask)
+ [modifiers addObject:@"NSControlKeyMask"];
+ if (modifierFlags & NSAlternateKeyMask)
+ [modifiers addObject:@"NSAlternateKeyMask"];
+ if (modifierFlags & NSCommandKeyMask)
+ [modifiers addObject:@"NSCommandKeyMask"];
+ if (modifierFlags & NSNumericPadKeyMask)
+ [modifiers addObject:@"NSNumericPadKeyMask"];
+ if (modifierFlags & NSHelpKeyMask)
+ [modifiers addObject:@"NSHelpKeyMask"];
+ if (modifierFlags & NSFunctionKeyMask)
+ [modifiers addObject:@"NSFunctionKeyMask"];
+
+ WebScriptObject* eventParam = [inputMethodHandler evaluateWebScript:@"new Object();"];
+ [eventParam setValue:[event characters] forKey:@"characters"];
+ [eventParam setValue:[event charactersIgnoringModifiers] forKey:@"charactersIgnoringModifiers"];
+ [eventParam setValue:[NSNumber numberWithBool:[event isARepeat]] forKey:@"isARepeat"];
+ [eventParam setValue:[NSNumber numberWithUnsignedShort:[event keyCode]] forKey:@"keyCode"];
+ [eventParam setValue:modifiers forKey:@"modifierFlags"];
+
+ [modifiers release];
+
+ id result = [inputMethodHandler callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:inputMethodHandler, eventParam, nil]];
+ if (![result respondsToSelector:@selector(boolValue)] || ![result boolValue])
+ [sender doCommandBySelector:@selector(noop:)]; // AppKit sends noop: if the ime does not handle an event
+
+ inputMethodView = nil;
+ return YES;
+}
+
+@end