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