1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h" 6 7#import <objc/runtime.h> 8 9#include "content/browser/renderer_host/render_widget_host_impl.h" 10#import "content/browser/renderer_host/render_widget_host_view_mac.h" 11 12namespace content { 13namespace { 14 15// The names of all the objc selectors w/o ':'s added to an object by 16// AddEditingSelectorsToClass(). 17// 18// This needs to be kept in Sync with WEB_COMMAND list in the WebKit tree at: 19// WebKit/mac/WebView/WebHTMLView.mm . 20const char* kEditCommands[] = { 21 "alignCenter", 22 "alignJustified", 23 "alignLeft", 24 "alignRight", 25 "copy", 26 "cut", 27 "delete", 28 "deleteBackward", 29 "deleteBackwardByDecomposingPreviousCharacter", 30 "deleteForward", 31 "deleteToBeginningOfLine", 32 "deleteToBeginningOfParagraph", 33 "deleteToEndOfLine", 34 "deleteToEndOfParagraph", 35 "deleteToMark", 36 "deleteWordBackward", 37 "deleteWordForward", 38 "ignoreSpelling", 39 "indent", 40 "insertBacktab", 41 "insertLineBreak", 42 "insertNewline", 43 "insertNewlineIgnoringFieldEditor", 44 "insertParagraphSeparator", 45 "insertTab", 46 "insertTabIgnoringFieldEditor", 47 "makeTextWritingDirectionLeftToRight", 48 "makeTextWritingDirectionNatural", 49 "makeTextWritingDirectionRightToLeft", 50 "moveBackward", 51 "moveBackwardAndModifySelection", 52 "moveDown", 53 "moveDownAndModifySelection", 54 "moveForward", 55 "moveForwardAndModifySelection", 56 "moveLeft", 57 "moveLeftAndModifySelection", 58 "moveParagraphBackwardAndModifySelection", 59 "moveParagraphForwardAndModifySelection", 60 "moveRight", 61 "moveRightAndModifySelection", 62 "moveToBeginningOfDocument", 63 "moveToBeginningOfDocumentAndModifySelection", 64 "moveToBeginningOfLine", 65 "moveToBeginningOfLineAndModifySelection", 66 "moveToBeginningOfParagraph", 67 "moveToBeginningOfParagraphAndModifySelection", 68 "moveToBeginningOfSentence", 69 "moveToBeginningOfSentenceAndModifySelection", 70 "moveToEndOfDocument", 71 "moveToEndOfDocumentAndModifySelection", 72 "moveToEndOfLine", 73 "moveToEndOfLineAndModifySelection", 74 "moveToEndOfParagraph", 75 "moveToEndOfParagraphAndModifySelection", 76 "moveToEndOfSentence", 77 "moveToEndOfSentenceAndModifySelection", 78 "moveUp", 79 "moveUpAndModifySelection", 80 "moveWordBackward", 81 "moveWordBackwardAndModifySelection", 82 "moveWordForward", 83 "moveWordForwardAndModifySelection", 84 "moveWordLeft", 85 "moveWordLeftAndModifySelection", 86 "moveWordRight", 87 "moveWordRightAndModifySelection", 88 "outdent", 89 "pageDown", 90 "pageDownAndModifySelection", 91 "pageUp", 92 "pageUpAndModifySelection", 93 "selectAll", 94 "selectLine", 95 "selectParagraph", 96 "selectSentence", 97 "selectToMark", 98 "selectWord", 99 "setMark", 100 "showGuessPanel", 101 "subscript", 102 "superscript", 103 "swapWithMark", 104 "transpose", 105 "underline", 106 "unscript", 107 "yank", 108 "yankAndSelect"}; 109 110 111// This function is installed via the objc runtime as the implementation of all 112// the various editing selectors. 113// The objc runtime hookup occurs in 114// RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass(). 115// 116// self - the object we're attached to; it must implement the 117// RenderWidgetHostViewMacOwner protocol. 118// _cmd - the selector that fired. 119// sender - the id of the object that sent the message. 120// 121// The selector is translated into an edit comand and then forwarded down the 122// pipeline to WebCore. 123// The route the message takes is: 124// RenderWidgetHostViewMac -> RenderViewHost -> 125// | IPC | -> 126// RenderView -> currently focused WebFrame. 127// The WebFrame is in the Chrome glue layer and forwards the message to WebCore. 128void EditCommandImp(id self, SEL _cmd, id sender) { 129 // Make sure |self| is the right type. 130 DCHECK([self conformsToProtocol:@protocol(RenderWidgetHostViewMacOwner)]); 131 132 // SEL -> command name string. 133 NSString* command_name_ns = 134 RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector(_cmd); 135 std::string command([command_name_ns UTF8String]); 136 137 // Forward the edit command string down the pipeline. 138 RenderWidgetHostViewMac* rwhv = [(id<RenderWidgetHostViewMacOwner>)self 139 renderWidgetHostViewMac]; 140 DCHECK(rwhv); 141 142 RenderWidgetHostImpl* rwh = 143 RenderWidgetHostImpl::From(rwhv->GetRenderWidgetHost()); 144 // The second parameter is the core command value which isn't used here. 145 rwh->ExecuteEditCommand(command, ""); 146} 147 148} // namespace 149 150// Maps an objc-selector to a core command name. 151// 152// Returns the core command name (which is the selector name with the trailing 153// ':' stripped in most cases). 154// 155// Adapted from a function by the same name in 156// WebKit/mac/WebView/WebHTMLView.mm . 157// Capitalized names are returned from this function, but that's simply 158// matching WebHTMLView.mm. 159NSString* RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector( 160 SEL selector) { 161 if (selector == @selector(insertParagraphSeparator:) || 162 selector == @selector(insertNewlineIgnoringFieldEditor:)) 163 return @"InsertNewline"; 164 if (selector == @selector(insertTabIgnoringFieldEditor:)) 165 return @"InsertTab"; 166 if (selector == @selector(pageDown:)) 167 return @"MovePageDown"; 168 if (selector == @selector(pageDownAndModifySelection:)) 169 return @"MovePageDownAndModifySelection"; 170 if (selector == @selector(pageUp:)) 171 return @"MovePageUp"; 172 if (selector == @selector(pageUpAndModifySelection:)) 173 return @"MovePageUpAndModifySelection"; 174 175 // Remove the trailing colon. 176 NSString* selector_str = NSStringFromSelector(selector); 177 int selector_len = [selector_str length]; 178 return [selector_str substringToIndex:selector_len - 1]; 179} 180 181RenderWidgetHostViewMacEditCommandHelper:: 182 RenderWidgetHostViewMacEditCommandHelper() { 183 for (size_t i = 0; i < arraysize(kEditCommands); ++i) { 184 edit_command_set_.insert(kEditCommands[i]); 185 } 186} 187 188RenderWidgetHostViewMacEditCommandHelper:: 189 ~RenderWidgetHostViewMacEditCommandHelper() {} 190 191// Dynamically adds Selectors to the aformentioned class. 192void RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass( 193 Class klass) { 194 for (size_t i = 0; i < arraysize(kEditCommands); ++i) { 195 // Append trailing ':' to command name to get selector name. 196 NSString* sel_str = [NSString stringWithFormat: @"%s:", kEditCommands[i]]; 197 198 SEL edit_selector = NSSelectorFromString(sel_str); 199 // May want to use @encode() for the last parameter to this method. 200 // If class_addMethod fails we assume that all the editing selectors where 201 // added to the class. 202 // If a certain class already implements a method then class_addMethod 203 // returns NO, which we can safely ignore. 204 class_addMethod(klass, edit_selector, (IMP)EditCommandImp, "v@:@"); 205 } 206} 207 208bool RenderWidgetHostViewMacEditCommandHelper::IsMenuItemEnabled( 209 SEL item_action, 210 id<RenderWidgetHostViewMacOwner> owner) { 211 const char* selector_name = sel_getName(item_action); 212 // TODO(jeremy): The final form of this function will check state 213 // associated with the Browser. 214 215 // For now just mark all edit commands as enabled. 216 NSString* selector_name_ns = [NSString stringWithUTF8String:selector_name]; 217 218 // Remove trailing ':' 219 size_t str_len = [selector_name_ns length]; 220 selector_name_ns = [selector_name_ns substringToIndex:str_len - 1]; 221 std::string edit_command_name([selector_name_ns UTF8String]); 222 223 // search for presence in set and return. 224 bool ret = edit_command_set_.find(edit_command_name) != 225 edit_command_set_.end(); 226 return ret; 227} 228 229NSArray* RenderWidgetHostViewMacEditCommandHelper::GetEditSelectorNames() { 230 size_t num_edit_commands = arraysize(kEditCommands); 231 NSMutableArray* ret = [NSMutableArray arrayWithCapacity:num_edit_commands]; 232 233 for (size_t i = 0; i < num_edit_commands; ++i) { 234 [ret addObject:[NSString stringWithUTF8String:kEditCommands[i]]]; 235 } 236 237 return ret; 238} 239 240} // namespace content 241