• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
interpretKeyEvent(const KeyboardEvent * evt)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 
handleEditingKeyboardEvent(KeyboardEvent * evt)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 
handleKeyboardEvent(KeyboardEvent * evt)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