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.
5 #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h"
7 #import <objc/runtime.h>
9 #include "content/browser/renderer_host/render_widget_host_impl.h"
10 #import "content/browser/renderer_host/render_widget_host_view_mac.h"
15 // The names of all the objc selectors w/o ':'s added to an object by
16 // AddEditingSelectorsToClass().
18 // This needs to be kept in Sync with WEB_COMMAND list in the WebKit tree at:
19 // WebKit/mac/WebView/WebHTMLView.mm .
20 const char* kEditCommands[] = {
29 "deleteBackwardByDecomposingPreviousCharacter",
31 "deleteToBeginningOfLine",
32 "deleteToBeginningOfParagraph",
34 "deleteToEndOfParagraph",
43 "insertNewlineIgnoringFieldEditor",
44 "insertParagraphSeparator",
46 "insertTabIgnoringFieldEditor",
47 "makeTextWritingDirectionLeftToRight",
48 "makeTextWritingDirectionNatural",
49 "makeTextWritingDirectionRightToLeft",
51 "moveBackwardAndModifySelection",
53 "moveDownAndModifySelection",
55 "moveForwardAndModifySelection",
57 "moveLeftAndModifySelection",
58 "moveParagraphBackwardAndModifySelection",
59 "moveParagraphForwardAndModifySelection",
61 "moveRightAndModifySelection",
62 "moveToBeginningOfDocument",
63 "moveToBeginningOfDocumentAndModifySelection",
64 "moveToBeginningOfLine",
65 "moveToBeginningOfLineAndModifySelection",
66 "moveToBeginningOfParagraph",
67 "moveToBeginningOfParagraphAndModifySelection",
68 "moveToBeginningOfSentence",
69 "moveToBeginningOfSentenceAndModifySelection",
70 "moveToEndOfDocument",
71 "moveToEndOfDocumentAndModifySelection",
73 "moveToEndOfLineAndModifySelection",
74 "moveToEndOfParagraph",
75 "moveToEndOfParagraphAndModifySelection",
76 "moveToEndOfSentence",
77 "moveToEndOfSentenceAndModifySelection",
79 "moveUpAndModifySelection",
81 "moveWordBackwardAndModifySelection",
83 "moveWordForwardAndModifySelection",
85 "moveWordLeftAndModifySelection",
87 "moveWordRightAndModifySelection",
90 "pageDownAndModifySelection",
92 "pageUpAndModifySelection",
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().
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.
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 ->
126 // RenderView -> currently focused WebFrame.
127 // The WebFrame is in the Chrome glue layer and forwards the message to WebCore.
128 void EditCommandImp(id self, SEL _cmd, id sender) {
129 // Make sure |self| is the right type.
130 DCHECK([self conformsToProtocol:@protocol(RenderWidgetHostViewMacOwner)]);
132 // SEL -> command name string.
133 NSString* command_name_ns =
134 RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector(_cmd);
135 std::string command([command_name_ns UTF8String]);
137 // Forward the edit command string down the pipeline.
138 RenderWidgetHostViewMac* rwhv = [(id<RenderWidgetHostViewMacOwner>)self
139 renderWidgetHostViewMac];
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, "");
150 // Maps an objc-selector to a core command name.
152 // Returns the core command name (which is the selector name with the trailing
153 // ':' stripped in most cases).
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.
159 NSString* RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector(
161 if (selector == @selector(insertParagraphSeparator:) ||
162 selector == @selector(insertNewlineIgnoringFieldEditor:))
163 return @"InsertNewline";
164 if (selector == @selector(insertTabIgnoringFieldEditor:))
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";
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];
181 RenderWidgetHostViewMacEditCommandHelper::
182 RenderWidgetHostViewMacEditCommandHelper() {
183 for (size_t i = 0; i < arraysize(kEditCommands); ++i) {
184 edit_command_set_.insert(kEditCommands[i]);
188 RenderWidgetHostViewMacEditCommandHelper::
189 ~RenderWidgetHostViewMacEditCommandHelper() {}
191 // Dynamically adds Selectors to the aformentioned class.
192 void RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass(
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]];
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@:@");
208 bool RenderWidgetHostViewMacEditCommandHelper::IsMenuItemEnabled(
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.
215 // For now just mark all edit commands as enabled.
216 NSString* selector_name_ns = [NSString stringWithUTF8String:selector_name];
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]);
223 // search for presence in set and return.
224 bool ret = edit_command_set_.find(edit_command_name) !=
225 edit_command_set_.end();
229 NSArray* RenderWidgetHostViewMacEditCommandHelper::GetEditSelectorNames() {
230 size_t num_edit_commands = arraysize(kEditCommands);
231 NSMutableArray* ret = [NSMutableArray arrayWithCapacity:num_edit_commands];
233 for (size_t i = 0; i < num_edit_commands; ++i) {
234 [ret addObject:[NSString stringWithUTF8String:kEditCommands[i]]];
240 } // namespace content