| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "content/browser/renderer_host/render_widget_host_view_mac_editcommand_helper.h" |
| |
| #import <objc/runtime.h> |
| #include <stddef.h> |
| |
| #include "base/stl_util.h" |
| #include "content/browser/renderer_host/render_widget_host_impl.h" |
| #import "content/browser/renderer_host/render_widget_host_view_mac.h" |
| |
| namespace content { |
| namespace { |
| |
| // The names of all the objc selectors w/o ':'s added to an object by |
| // AddEditingSelectorsToClass(). |
| // |
| // This needs to be kept in Sync with WEB_COMMAND list in the WebKit tree at: |
| // WebKit/mac/WebView/WebHTMLView.mm . |
| const char* const kEditCommands[] = { |
| "alignCenter", |
| "alignJustified", |
| "alignLeft", |
| "alignRight", |
| "copy", |
| "cut", |
| "delete", |
| "deleteBackward", |
| "deleteBackwardByDecomposingPreviousCharacter", |
| "deleteForward", |
| "deleteToBeginningOfLine", |
| "deleteToBeginningOfParagraph", |
| "deleteToEndOfLine", |
| "deleteToEndOfParagraph", |
| "deleteToMark", |
| "deleteWordBackward", |
| "deleteWordForward", |
| "ignoreSpelling", |
| "indent", |
| "insertBacktab", |
| "insertLineBreak", |
| "insertNewline", |
| "insertNewlineIgnoringFieldEditor", |
| "insertParagraphSeparator", |
| "insertTab", |
| "insertTabIgnoringFieldEditor", |
| "makeTextWritingDirectionLeftToRight", |
| "makeTextWritingDirectionNatural", |
| "makeTextWritingDirectionRightToLeft", |
| "moveBackward", |
| "moveBackwardAndModifySelection", |
| "moveDown", |
| "moveDownAndModifySelection", |
| "moveForward", |
| "moveForwardAndModifySelection", |
| "moveLeft", |
| "moveLeftAndModifySelection", |
| "moveParagraphBackwardAndModifySelection", |
| "moveParagraphForwardAndModifySelection", |
| "moveRight", |
| "moveRightAndModifySelection", |
| "moveToBeginningOfDocument", |
| "moveToBeginningOfDocumentAndModifySelection", |
| "moveToBeginningOfLine", |
| "moveToBeginningOfLineAndModifySelection", |
| "moveToBeginningOfParagraph", |
| "moveToBeginningOfParagraphAndModifySelection", |
| "moveToBeginningOfSentence", |
| "moveToBeginningOfSentenceAndModifySelection", |
| "moveToEndOfDocument", |
| "moveToEndOfDocumentAndModifySelection", |
| "moveToEndOfLine", |
| "moveToEndOfLineAndModifySelection", |
| "moveToEndOfParagraph", |
| "moveToEndOfParagraphAndModifySelection", |
| "moveToEndOfSentence", |
| "moveToEndOfSentenceAndModifySelection", |
| "moveUp", |
| "moveUpAndModifySelection", |
| "moveWordBackward", |
| "moveWordBackwardAndModifySelection", |
| "moveWordForward", |
| "moveWordForwardAndModifySelection", |
| "moveWordLeft", |
| "moveWordLeftAndModifySelection", |
| "moveWordRight", |
| "moveWordRightAndModifySelection", |
| "outdent", |
| "pageDown", |
| "pageDownAndModifySelection", |
| "pageUp", |
| "pageUpAndModifySelection", |
| "selectAll", |
| "selectLine", |
| "selectParagraph", |
| "selectSentence", |
| "selectToMark", |
| "selectWord", |
| "setMark", |
| "showGuessPanel", |
| "subscript", |
| "superscript", |
| "swapWithMark", |
| "transpose", |
| "underline", |
| "unscript", |
| "yank", |
| "yankAndSelect" |
| }; |
| |
| // This function is installed via the objc runtime as the implementation of all |
| // the various editing selectors. |
| // The objc runtime hookup occurs in |
| // RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass(). |
| // |
| // self - the object we're attached to; it must implement the |
| // RenderWidgetHostNSViewHostOwner protocol. |
| // _cmd - the selector that fired. |
| // sender - the id of the object that sent the message. |
| // |
| // The selector is translated into an edit comand and then forwarded down the |
| // pipeline to WebCore. |
| // The route the message takes is: |
| // RenderWidgetHostViewMac -> RenderViewHost -> |
| // | IPC | -> |
| // RenderView -> currently focused WebFrame. |
| // The WebFrame is in the Chrome glue layer and forwards the message to WebCore. |
| void EditCommandImp(id self, SEL _cmd, id sender) { |
| // Make sure |self| is the right type. |
| DCHECK([self conformsToProtocol:@protocol(RenderWidgetHostNSViewHostOwner)]); |
| |
| // SEL -> command name string. |
| NSString* command_name_ns = |
| RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector(_cmd); |
| std::string command([command_name_ns UTF8String]); |
| |
| // Forward the edit command string down the pipeline. |
| remote_cocoa::mojom::RenderWidgetHostNSViewHost* host = |
| [(id<RenderWidgetHostNSViewHostOwner>)self renderWidgetHostNSViewHost]; |
| DCHECK(host); |
| host->ExecuteEditCommand(command); |
| } |
| |
| } // namespace |
| |
| // Maps an objc-selector to a core command name. |
| // |
| // Returns the core command name (which is the selector name with the trailing |
| // ':' stripped in most cases). |
| // |
| // Adapted from a function by the same name in |
| // WebKit/mac/WebView/WebHTMLView.mm . |
| // Capitalized names are returned from this function, but that's simply |
| // matching WebHTMLView.mm. |
| NSString* RenderWidgetHostViewMacEditCommandHelper::CommandNameForSelector( |
| SEL selector) { |
| if (selector == @selector(insertParagraphSeparator:) || |
| selector == @selector(insertNewlineIgnoringFieldEditor:)) |
| return @"InsertNewline"; |
| if (selector == @selector(insertTabIgnoringFieldEditor:)) |
| return @"InsertTab"; |
| if (selector == @selector(pageDown:)) |
| return @"MovePageDown"; |
| if (selector == @selector(pageDownAndModifySelection:)) |
| return @"MovePageDownAndModifySelection"; |
| if (selector == @selector(pageUp:)) |
| return @"MovePageUp"; |
| if (selector == @selector(pageUpAndModifySelection:)) |
| return @"MovePageUpAndModifySelection"; |
| if (selector == @selector(showGuessPanel:)) |
| return @"ToggleSpellPanel"; |
| |
| // Remove the trailing colon. |
| NSString* selector_str = NSStringFromSelector(selector); |
| int selector_len = [selector_str length]; |
| return [selector_str substringToIndex:selector_len - 1]; |
| } |
| |
| RenderWidgetHostViewMacEditCommandHelper:: |
| RenderWidgetHostViewMacEditCommandHelper() { |
| for (size_t i = 0; i < base::size(kEditCommands); ++i) { |
| edit_command_set_.insert(kEditCommands[i]); |
| } |
| } |
| |
| RenderWidgetHostViewMacEditCommandHelper:: |
| ~RenderWidgetHostViewMacEditCommandHelper() {} |
| |
| // Dynamically adds Selectors to the aformentioned class. |
| void RenderWidgetHostViewMacEditCommandHelper::AddEditingSelectorsToClass( |
| Class klass) { |
| for (size_t i = 0; i < base::size(kEditCommands); ++i) { |
| // Append trailing ':' to command name to get selector name. |
| NSString* sel_str = [NSString stringWithFormat: @"%s:", kEditCommands[i]]; |
| |
| SEL edit_selector = NSSelectorFromString(sel_str); |
| // May want to use @encode() for the last parameter to this method. |
| // If class_addMethod fails we assume that all the editing selectors where |
| // added to the class. |
| // If a certain class already implements a method then class_addMethod |
| // returns NO, which we can safely ignore. |
| class_addMethod(klass, edit_selector, (IMP)EditCommandImp, "v@:@"); |
| } |
| } |
| |
| bool RenderWidgetHostViewMacEditCommandHelper::IsMenuItemEnabled( |
| SEL item_action, |
| id<RenderWidgetHostNSViewHostOwner> owner) { |
| const char* selector_name = sel_getName(item_action); |
| // TODO(jeremy): The final form of this function will check state |
| // associated with the Browser. |
| |
| // For now just mark all edit commands as enabled. |
| NSString* selector_name_ns = [NSString stringWithUTF8String:selector_name]; |
| |
| // Remove trailing ':' |
| size_t str_len = [selector_name_ns length]; |
| selector_name_ns = [selector_name_ns substringToIndex:str_len - 1]; |
| std::string edit_command_name([selector_name_ns UTF8String]); |
| |
| // search for presence in set and return. |
| bool ret = edit_command_set_.find(edit_command_name) != |
| edit_command_set_.end(); |
| return ret; |
| } |
| |
| NSArray* RenderWidgetHostViewMacEditCommandHelper::GetEditSelectorNames() { |
| size_t num_edit_commands = base::size(kEditCommands); |
| NSMutableArray* ret = [NSMutableArray arrayWithCapacity:num_edit_commands]; |
| |
| for (size_t i = 0; i < num_edit_commands; ++i) { |
| [ret addObject:[NSString stringWithUTF8String:kEditCommands[i]]]; |
| } |
| |
| return ret; |
| } |
| |
| } // namespace content |