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/Editor.h" 29 30 #include "core/events/KeyboardEvent.h" 31 #include "core/frame/Frame.h" 32 #include "core/page/EditorClient.h" 33 #include "platform/KeyboardCodes.h" 34 #include "platform/PlatformKeyboardEvent.h" 35 36 namespace WebCore { 37 38 // 39 // The below code was adapted from the WebKit file webview.cpp 40 // 41 42 static const unsigned CtrlKey = 1 << 0; 43 static const unsigned AltKey = 1 << 1; 44 static const unsigned ShiftKey = 1 << 2; 45 static const unsigned MetaKey = 1 << 3; 46 #if OS(MACOSX) 47 // Aliases for the generic key defintions to make kbd shortcuts definitions more 48 // readable on OS X. 49 static const unsigned OptionKey = AltKey; 50 51 // Do not use this constant for anything but cursor movement commands. Keys 52 // with cmd set have their |isSystemKey| bit set, so chances are the shortcut 53 // will not be executed. Another, less important, reason is that shortcuts 54 // defined in the renderer do not blink the menu item that they triggered. See 55 // http://crbug.com/25856 and the bugs linked from there for details. 56 static const unsigned CommandKey = MetaKey; 57 #endif 58 59 // Keys with special meaning. These will be delegated to the editor using 60 // the execCommand() method 61 struct KeyDownEntry { 62 unsigned virtualKey; 63 unsigned modifiers; 64 const char* name; 65 }; 66 67 struct KeyPressEntry { 68 unsigned charCode; 69 unsigned modifiers; 70 const char* name; 71 }; 72 73 // Key bindings with command key on Mac and alt key on other platforms are 74 // marked as system key events and will be ignored (with the exception 75 // of Command-B and Command-I) so they shouldn't be added here. 76 static const KeyDownEntry keyDownEntries[] = { 77 { VKEY_LEFT, 0, "MoveLeft" }, 78 { VKEY_LEFT, ShiftKey, "MoveLeftAndModifySelection" }, 79 #if OS(MACOSX) 80 { VKEY_LEFT, OptionKey, "MoveWordLeft" }, 81 { VKEY_LEFT, OptionKey | ShiftKey, 82 "MoveWordLeftAndModifySelection" }, 83 #else 84 { VKEY_LEFT, CtrlKey, "MoveWordLeft" }, 85 { VKEY_LEFT, CtrlKey | ShiftKey, 86 "MoveWordLeftAndModifySelection" }, 87 #endif 88 { VKEY_RIGHT, 0, "MoveRight" }, 89 { VKEY_RIGHT, ShiftKey, "MoveRightAndModifySelection" }, 90 #if OS(MACOSX) 91 { VKEY_RIGHT, OptionKey, "MoveWordRight" }, 92 { VKEY_RIGHT, OptionKey | ShiftKey, "MoveWordRightAndModifySelection" }, 93 #else 94 { VKEY_RIGHT, CtrlKey, "MoveWordRight" }, 95 { VKEY_RIGHT, CtrlKey | ShiftKey, "MoveWordRightAndModifySelection" }, 96 #endif 97 { VKEY_UP, 0, "MoveUp" }, 98 { VKEY_UP, ShiftKey, "MoveUpAndModifySelection" }, 99 { VKEY_PRIOR, ShiftKey, "MovePageUpAndModifySelection" }, 100 { VKEY_DOWN, 0, "MoveDown" }, 101 { VKEY_DOWN, ShiftKey, "MoveDownAndModifySelection" }, 102 { VKEY_NEXT, ShiftKey, "MovePageDownAndModifySelection" }, 103 #if !OS(MACOSX) 104 { VKEY_UP, CtrlKey, "MoveParagraphBackward" }, 105 { VKEY_UP, CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" }, 106 { VKEY_DOWN, CtrlKey, "MoveParagraphForward" }, 107 { VKEY_DOWN, CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection" }, 108 { VKEY_PRIOR, 0, "MovePageUp" }, 109 { VKEY_NEXT, 0, "MovePageDown" }, 110 #endif 111 { VKEY_HOME, 0, "MoveToBeginningOfLine" }, 112 { VKEY_HOME, ShiftKey, 113 "MoveToBeginningOfLineAndModifySelection" }, 114 #if OS(MACOSX) 115 { VKEY_PRIOR, OptionKey, "MovePageUp" }, 116 { VKEY_NEXT, OptionKey, "MovePageDown" }, 117 #endif 118 #if !OS(MACOSX) 119 { VKEY_HOME, CtrlKey, "MoveToBeginningOfDocument" }, 120 { VKEY_HOME, CtrlKey | ShiftKey, 121 "MoveToBeginningOfDocumentAndModifySelection" }, 122 #endif 123 { VKEY_END, 0, "MoveToEndOfLine" }, 124 { VKEY_END, ShiftKey, "MoveToEndOfLineAndModifySelection" }, 125 #if !OS(MACOSX) 126 { VKEY_END, CtrlKey, "MoveToEndOfDocument" }, 127 { VKEY_END, CtrlKey | ShiftKey, 128 "MoveToEndOfDocumentAndModifySelection" }, 129 #endif 130 { VKEY_BACK, 0, "DeleteBackward" }, 131 { VKEY_BACK, ShiftKey, "DeleteBackward" }, 132 { VKEY_DELETE, 0, "DeleteForward" }, 133 #if OS(MACOSX) 134 { VKEY_BACK, OptionKey, "DeleteWordBackward" }, 135 { VKEY_DELETE, OptionKey, "DeleteWordForward" }, 136 #else 137 { VKEY_BACK, CtrlKey, "DeleteWordBackward" }, 138 { VKEY_DELETE, CtrlKey, "DeleteWordForward" }, 139 #endif 140 #if OS(MACOSX) 141 { 'B', CommandKey, "ToggleBold" }, 142 { 'I', CommandKey, "ToggleItalic" }, 143 #else 144 { 'B', CtrlKey, "ToggleBold" }, 145 { 'I', CtrlKey, "ToggleItalic" }, 146 #endif 147 { 'U', CtrlKey, "ToggleUnderline" }, 148 { VKEY_ESCAPE, 0, "Cancel" }, 149 { VKEY_OEM_PERIOD, CtrlKey, "Cancel" }, 150 { VKEY_TAB, 0, "InsertTab" }, 151 { VKEY_TAB, ShiftKey, "InsertBacktab" }, 152 { VKEY_RETURN, 0, "InsertNewline" }, 153 { VKEY_RETURN, CtrlKey, "InsertNewline" }, 154 { VKEY_RETURN, AltKey, "InsertNewline" }, 155 { VKEY_RETURN, AltKey | ShiftKey, "InsertNewline" }, 156 { VKEY_RETURN, ShiftKey, "InsertLineBreak" }, 157 { VKEY_INSERT, CtrlKey, "Copy" }, 158 { VKEY_INSERT, ShiftKey, "Paste" }, 159 { VKEY_DELETE, ShiftKey, "Cut" }, 160 #if !OS(MACOSX) 161 // On OS X, we pipe these back to the browser, so that it can do menu item 162 // blinking. 163 { 'C', CtrlKey, "Copy" }, 164 { 'V', CtrlKey, "Paste" }, 165 { 'V', CtrlKey | ShiftKey, "PasteAndMatchStyle" }, 166 { 'X', CtrlKey, "Cut" }, 167 { 'A', CtrlKey, "SelectAll" }, 168 { 'Z', CtrlKey, "Undo" }, 169 { 'Z', CtrlKey | ShiftKey, "Redo" }, 170 { 'Y', CtrlKey, "Redo" }, 171 #endif 172 { VKEY_INSERT, 0, "OverWrite" }, 173 }; 174 175 static const KeyPressEntry keyPressEntries[] = { 176 { '\t', 0, "InsertTab" }, 177 { '\t', ShiftKey, "InsertBacktab" }, 178 { '\r', 0, "InsertNewline" }, 179 { '\r', CtrlKey, "InsertNewline" }, 180 { '\r', ShiftKey, "InsertLineBreak" }, 181 { '\r', AltKey, "InsertNewline" }, 182 { '\r', AltKey | ShiftKey, "InsertNewline" }, 183 }; 184 185 const char* Editor::interpretKeyEvent(const KeyboardEvent* evt) 186 { 187 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 188 if (!keyEvent) 189 return ""; 190 191 static HashMap<int, const char*>* keyDownCommandsMap = 0; 192 static HashMap<int, const char*>* keyPressCommandsMap = 0; 193 194 if (!keyDownCommandsMap) { 195 keyDownCommandsMap = new HashMap<int, const char*>; 196 keyPressCommandsMap = new HashMap<int, const char*>; 197 198 for (unsigned i = 0; i < arraysize(keyDownEntries); i++) { 199 keyDownCommandsMap->set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name); 200 } 201 202 for (unsigned i = 0; i < arraysize(keyPressEntries); i++) { 203 keyPressCommandsMap->set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name); 204 } 205 } 206 207 unsigned modifiers = 0; 208 if (keyEvent->shiftKey()) 209 modifiers |= ShiftKey; 210 if (keyEvent->altKey()) 211 modifiers |= AltKey; 212 if (keyEvent->ctrlKey()) 213 modifiers |= CtrlKey; 214 if (keyEvent->metaKey()) 215 modifiers |= MetaKey; 216 217 if (keyEvent->type() == PlatformEvent::RawKeyDown) { 218 int mapKey = modifiers << 16 | evt->keyCode(); 219 return mapKey ? keyDownCommandsMap->get(mapKey) : 0; 220 } 221 222 int mapKey = modifiers << 16 | evt->charCode(); 223 return mapKey ? keyPressCommandsMap->get(mapKey) : 0; 224 } 225 226 bool Editor::handleEditingKeyboardEvent(KeyboardEvent* evt) 227 { 228 const PlatformKeyboardEvent* keyEvent = evt->keyEvent(); 229 // do not treat this as text input if it's a system key event 230 if (!keyEvent || keyEvent->isSystemKey()) 231 return false; 232 233 String commandName = interpretKeyEvent(evt); 234 Command command = this->command(commandName); 235 236 if (keyEvent->type() == PlatformEvent::RawKeyDown) { 237 // WebKit doesn't have enough information about mode to decide how 238 // commands that just insert text if executed via Editor should be treated, 239 // so we leave it upon WebCore to either handle them immediately 240 // (e.g. Tab that changes focus) or let a keypress event be generated 241 // (e.g. Tab that inserts a Tab character, or Enter). 242 if (command.isTextInsertion() || commandName.isEmpty()) 243 return false; 244 if (command.execute(evt)) { 245 client().didExecuteCommand(commandName); 246 return true; 247 } 248 return false; 249 } 250 251 if (command.execute(evt)) { 252 client().didExecuteCommand(commandName); 253 return true; 254 } 255 256 // Here we need to filter key events. 257 // On Gtk/Linux, it emits key events with ASCII text and ctrl on for ctrl-<x>. 258 // In Webkit, EditorClient::handleKeyboardEvent in 259 // WebKit/gtk/WebCoreSupport/EditorClientGtk.cpp drop such events. 260 // On Mac, it emits key events with ASCII text and meta on for Command-<x>. 261 // These key events should not emit text insert event. 262 // Alt key would be used to insert alternative character, so we should let 263 // through. Also note that Ctrl-Alt combination equals to AltGr key which is 264 // also used to insert alternative character. 265 // http://code.google.com/p/chromium/issues/detail?id=10846 266 // Windows sets both alt and meta are on when "Alt" key pressed. 267 // http://code.google.com/p/chromium/issues/detail?id=2215 268 // Also, we should not rely on an assumption that keyboards don't 269 // send ASCII characters when pressing a control key on Windows, 270 // which may be configured to do it so by user. 271 // See also http://en.wikipedia.org/wiki/Keyboard_Layout 272 // FIXME(ukai): investigate more detail for various keyboard layout. 273 if (evt->keyEvent()->text().length() == 1) { 274 UChar ch = evt->keyEvent()->text()[0U]; 275 276 // Don't insert null or control characters as they can result in 277 // unexpected behaviour 278 if (ch < ' ') 279 return false; 280 #if !OS(WIN) 281 // Don't insert ASCII character if ctrl w/o alt or meta is on. 282 // On Mac, we should ignore events when meta is on (Command-<x>). 283 if (ch < 0x80) { 284 if (evt->keyEvent()->ctrlKey() && !evt->keyEvent()->altKey()) 285 return false; 286 #if OS(MACOSX) 287 if (evt->keyEvent()->metaKey()) 288 return false; 289 #endif 290 } 291 #endif 292 } 293 294 if (!canEdit()) 295 return false; 296 297 return insertText(evt->keyEvent()->text(), evt); 298 } 299 300 void Editor::handleKeyboardEvent(KeyboardEvent* evt) 301 { 302 // Give the embedder a chance to handle the keyboard event. 303 if (client().handleKeyboardEvent() || handleEditingKeyboardEvent(evt)) 304 evt->setDefaultHandled(); 305 } 306 307 } // namesace WebCore 308