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