Home | History | Annotate | Download | only in cocoa
      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 
     12 namespace {
     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 .
     18 const 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.
    126 void 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.
    155 NSString* 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 
    176 RWHVMEditCommandHelper::RWHVMEditCommandHelper() {
    177   for (size_t i = 0; i < arraysize(kEditCommands); ++i) {
    178     edit_command_set_.insert(kEditCommands[i]);
    179   }
    180 }
    181 
    182 RWHVMEditCommandHelper::~RWHVMEditCommandHelper() {}
    183 
    184 // Dynamically adds Selectors to the aformentioned class.
    185 void 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 
    200 bool 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 
    220 NSArray* 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