Home | History | Annotate | Download | only in editing
      1 /*
      2  * Copyright (C) 2006, 2007 Apple, Inc.  All rights reserved.
      3  * Copyright (C) 2012 Google, Inc.  All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include "config.h"
     28 #include "core/editing/EditingBehavior.h"
     29 
     30 #include "core/events/KeyboardEvent.h"
     31 #include "platform/KeyboardCodes.h"
     32 #include "platform/PlatformKeyboardEvent.h"
     33 
     34 namespace blink {
     35 
     36 //
     37 // The below code was adapted from the WebKit file webview.cpp
     38 //
     39 
     40 static const unsigned CtrlKey = 1 << 0;
     41 static const unsigned AltKey = 1 << 1;
     42 static const unsigned ShiftKey = 1 << 2;
     43 static const unsigned MetaKey = 1 << 3;
     44 #if OS(MACOSX)
     45 // Aliases for the generic key defintions to make kbd shortcuts definitions more
     46 // readable on OS X.
     47 static const unsigned OptionKey  = AltKey;
     48 
     49 // Do not use this constant for anything but cursor movement commands. Keys
     50 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut
     51 // will not be executed. Another, less important, reason is that shortcuts
     52 // defined in the renderer do not blink the menu item that they triggered. See
     53 // http://crbug.com/25856 and the bugs linked from there for details.
     54 static const unsigned CommandKey = MetaKey;
     55 #endif
     56 
     57 // Keys with special meaning. These will be delegated to the editor using
     58 // the execCommand() method
     59 struct KeyDownEntry {
     60     unsigned virtualKey;
     61     unsigned modifiers;
     62     const char* name;
     63 };
     64 
     65 struct KeyPressEntry {
     66     unsigned charCode;
     67     unsigned modifiers;
     68     const char* name;
     69 };
     70 
     71 // Key bindings with command key on Mac and alt key on other platforms are
     72 // marked as system key events and will be ignored (with the exception
     73 // of Command-B and Command-I) so they shouldn't be added here.
     74 static const KeyDownEntry keyDownEntries[] = {
     75     { VKEY_LEFT,   0,                  "MoveLeft"                             },
     76     { VKEY_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"           },
     77 #if OS(MACOSX)
     78     { VKEY_LEFT,   OptionKey,          "MoveWordLeft"                         },
     79     { VKEY_LEFT,   OptionKey | ShiftKey,
     80         "MoveWordLeftAndModifySelection"                                      },
     81 #else
     82     { VKEY_LEFT,   CtrlKey,            "MoveWordLeft"                         },
     83     { VKEY_LEFT,   CtrlKey | ShiftKey,
     84         "MoveWordLeftAndModifySelection"                                      },
     85 #endif
     86     { VKEY_RIGHT,  0,                  "MoveRight"                            },
     87     { VKEY_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"          },
     88 #if OS(MACOSX)
     89     { VKEY_RIGHT,  OptionKey,          "MoveWordRight"                        },
     90     { VKEY_RIGHT,  OptionKey | ShiftKey, "MoveWordRightAndModifySelection"    },
     91 #else
     92     { VKEY_RIGHT,  CtrlKey,            "MoveWordRight"                        },
     93     { VKEY_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"      },
     94 #endif
     95     { VKEY_UP,     0,                  "MoveUp"                               },
     96     { VKEY_UP,     ShiftKey,           "MoveUpAndModifySelection"             },
     97     { VKEY_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"         },
     98     { VKEY_DOWN,   0,                  "MoveDown"                             },
     99     { VKEY_DOWN,   ShiftKey,           "MoveDownAndModifySelection"           },
    100     { VKEY_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"       },
    101 #if !OS(MACOSX)
    102     { VKEY_UP,     CtrlKey,            "MoveParagraphBackward"                },
    103     { VKEY_UP,     CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" },
    104     { VKEY_DOWN,   CtrlKey,            "MoveParagraphForward"                },
    105     { VKEY_DOWN,   CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection" },
    106     { VKEY_PRIOR,  0,                  "MovePageUp"                           },
    107     { VKEY_NEXT,   0,                  "MovePageDown"                         },
    108 #endif
    109     { VKEY_HOME,   0,                  "MoveToBeginningOfLine"                },
    110     { VKEY_HOME,   ShiftKey,
    111         "MoveToBeginningOfLineAndModifySelection"                             },
    112 #if OS(MACOSX)
    113     { VKEY_PRIOR,  OptionKey,          "MovePageUp"                           },
    114     { VKEY_NEXT,   OptionKey,          "MovePageDown"                         },
    115 #endif
    116 #if !OS(MACOSX)
    117     { VKEY_HOME,   CtrlKey,            "MoveToBeginningOfDocument"            },
    118     { VKEY_HOME,   CtrlKey | ShiftKey,
    119         "MoveToBeginningOfDocumentAndModifySelection"                         },
    120 #endif
    121     { VKEY_END,    0,                  "MoveToEndOfLine"                      },
    122     { VKEY_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"    },
    123 #if !OS(MACOSX)
    124     { VKEY_END,    CtrlKey,            "MoveToEndOfDocument"                  },
    125     { VKEY_END,    CtrlKey | ShiftKey,
    126         "MoveToEndOfDocumentAndModifySelection"                               },
    127 #endif
    128     { VKEY_BACK,   0,                  "DeleteBackward"                       },
    129     { VKEY_BACK,   ShiftKey,           "DeleteBackward"                       },
    130     { VKEY_DELETE, 0,                  "DeleteForward"                        },
    131 #if OS(MACOSX)
    132     { VKEY_BACK,   OptionKey,          "DeleteWordBackward"                   },
    133     { VKEY_DELETE, OptionKey,          "DeleteWordForward"                    },
    134 #else
    135     { VKEY_BACK,   CtrlKey,            "DeleteWordBackward"                   },
    136     { VKEY_DELETE, CtrlKey,            "DeleteWordForward"                    },
    137 #endif
    138 #if OS(MACOSX)
    139     { 'B',         CommandKey,         "ToggleBold"                           },
    140     { 'I',         CommandKey,         "ToggleItalic"                         },
    141 #else
    142     { 'B',         CtrlKey,            "ToggleBold"                           },
    143     { 'I',         CtrlKey,            "ToggleItalic"                         },
    144 #endif
    145     { 'U',         CtrlKey,            "ToggleUnderline"                      },
    146     { VKEY_ESCAPE, 0,                  "Cancel"                               },
    147     { VKEY_OEM_PERIOD, CtrlKey,        "Cancel"                               },
    148     { VKEY_TAB,    0,                  "InsertTab"                            },
    149     { VKEY_TAB,    ShiftKey,           "InsertBacktab"                        },
    150     { VKEY_RETURN, 0,                  "InsertNewline"                        },
    151     { VKEY_RETURN, CtrlKey,            "InsertNewline"                        },
    152     { VKEY_RETURN, AltKey,             "InsertNewline"                        },
    153     { VKEY_RETURN, AltKey | ShiftKey,  "InsertNewline"                        },
    154     { VKEY_RETURN, ShiftKey,           "InsertLineBreak"                      },
    155     { VKEY_INSERT, CtrlKey,            "Copy"                                 },
    156     { VKEY_INSERT, ShiftKey,           "Paste"                                },
    157     { VKEY_DELETE, ShiftKey,           "Cut"                                  },
    158 #if !OS(MACOSX)
    159     // On OS X, we pipe these back to the browser, so that it can do menu item
    160     // blinking.
    161     { 'C',         CtrlKey,            "Copy"                                 },
    162     { 'V',         CtrlKey,            "Paste"                                },
    163     { 'V',         CtrlKey | ShiftKey, "PasteAndMatchStyle"                   },
    164     { 'X',         CtrlKey,            "Cut"                                  },
    165     { 'A',         CtrlKey,            "SelectAll"                            },
    166     { 'Z',         CtrlKey,            "Undo"                                 },
    167     { 'Z',         CtrlKey | ShiftKey, "Redo"                                 },
    168     { 'Y',         CtrlKey,            "Redo"                                 },
    169 #endif
    170     { VKEY_INSERT, 0,                  "OverWrite"                            },
    171 };
    172 
    173 static const KeyPressEntry keyPressEntries[] = {
    174     { '\t',   0,                  "InsertTab"                                 },
    175     { '\t',   ShiftKey,           "InsertBacktab"                             },
    176     { '\r',   0,                  "InsertNewline"                             },
    177     { '\r',   CtrlKey,            "InsertNewline"                             },
    178     { '\r',   ShiftKey,           "InsertLineBreak"                           },
    179     { '\r',   AltKey,             "InsertNewline"                             },
    180     { '\r',   AltKey | ShiftKey,  "InsertNewline"                             },
    181 };
    182 
    183 const char* EditingBehavior::interpretKeyEvent(const KeyboardEvent& event) const
    184 {
    185     const PlatformKeyboardEvent* keyEvent = event.keyEvent();
    186     if (!keyEvent)
    187         return "";
    188 
    189     static HashMap<int, const char*>* keyDownCommandsMap = 0;
    190     static HashMap<int, const char*>* keyPressCommandsMap = 0;
    191 
    192     if (!keyDownCommandsMap) {
    193         keyDownCommandsMap = new HashMap<int, const char*>;
    194         keyPressCommandsMap = new HashMap<int, const char*>;
    195 
    196         for (unsigned i = 0; i < arraysize(keyDownEntries); i++) {
    197             keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
    198         }
    199 
    200         for (unsigned i = 0; i < arraysize(keyPressEntries); i++) {
    201             keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
    202         }
    203     }
    204 
    205     unsigned modifiers = 0;
    206     if (keyEvent->shiftKey())
    207         modifiers |= ShiftKey;
    208     if (keyEvent->altKey())
    209         modifiers |= AltKey;
    210     if (keyEvent->ctrlKey())
    211         modifiers |= CtrlKey;
    212     if (keyEvent->metaKey())
    213         modifiers |= MetaKey;
    214 
    215     if (keyEvent->type() == PlatformEvent::RawKeyDown) {
    216         int mapKey = modifiers << 16 | event.keyCode();
    217         return mapKey ? keyDownCommandsMap->get(mapKey) : 0;
    218     }
    219 
    220     int mapKey = modifiers << 16 | event.charCode();
    221     return mapKey ? keyPressCommandsMap->get(mapKey) : 0;
    222 }
    223 
    224 bool EditingBehavior::shouldInsertCharacter(const KeyboardEvent& event) const
    225 {
    226     if (event.keyEvent()->text().length() != 1)
    227         return true;
    228 
    229     // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>.
    230     // In Webkit, EditorClient::handleKeyboardEvent in
    231     // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events.
    232     // On Mac, it emits key events with ASCII text and meta on for Command-<x>.
    233     // These key events should not emit text insert event.
    234     // Alt key would be used to insert alternative character, so we should let
    235     // through. Also note that Ctrl-Alt combination equals to AltGr key which is
    236     // also used to insert alternative character.
    237     // http://code.google.com/p/chromium/issues/detail?id=10846
    238     // Windows sets both alt and meta are on when "Alt" key pressed.
    239     // http://code.google.com/p/chromium/issues/detail?id=2215
    240     // Also, we should not rely on an assumption that keyboards don't
    241     // send ASCII characters when pressing a control key on Windows,
    242     // which may be configured to do it so by user.
    243     // See also http://en.wikipedia.org/wiki/Keyboard_Layout
    244     // FIXME(ukai): investigate more detail for various keyboard layout.
    245     UChar ch = event.keyEvent()->text()[0U];
    246 
    247     // Don't insert null or control characters as they can result in
    248     // unexpected behaviour
    249     if (ch < ' ')
    250         return false;
    251 #if !OS(WIN)
    252     // Don't insert ASCII character if ctrl w/o alt or meta is on.
    253     // On Mac, we should ignore events when meta is on (Command-<x>).
    254     if (ch < 0x80) {
    255         if (event.keyEvent()->ctrlKey() && !event.keyEvent()->altKey())
    256             return false;
    257 #if OS(MACOSX)
    258         if (event.keyEvent()->metaKey())
    259             return false;
    260 #endif
    261     }
    262 #endif
    263 
    264     return true;
    265 }
    266 } // namespace blink
    267 
    268