4801cd35884eb8e8225d4ab39e117d52f6016aef
[WebKit-https.git] / Source / WebKit / WebProcess / WebCoreSupport / wpe / WebEditorClientWPE.cpp
1 /*
2  * Copyright (C) 2014 Igalia S.L.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebEditorClient.h"
28
29 #include "PlatformKeyboardEvent.h"
30 #include <WebCore/Document.h>
31 #include <WebCore/Editor.h>
32 #include <WebCore/EventNames.h>
33 #include <WebCore/Frame.h>
34 #include <WebCore/KeyboardEvent.h>
35 #include <WebCore/Node.h>
36 #include <WebCore/WindowsKeyboardCodes.h>
37 #include <wtf/NeverDestroyed.h>
38
39 using namespace WebCore;
40
41 namespace WebKit {
42
43 // The idea for the array/map below comes from Blink's EditingBehavior.cpp.
44
45 static const unsigned CtrlKey  = 1 << 0;
46 static const unsigned AltKey   = 1 << 1;
47 static const unsigned ShiftKey = 1 << 2;
48 static const unsigned MetaKey  = 1 << 3;
49
50 // Keys with special meaning. These will be delegated to the editor using
51 // the execCommand() method
52 struct KeyDownEntry {
53     unsigned virtualKey;
54     unsigned modifiers;
55     const char* name;
56 };
57
58 struct KeyPressEntry {
59     unsigned charCode;
60     unsigned modifiers;
61     const char* name;
62 };
63
64 static const KeyDownEntry keyDownEntries[] = {
65     { VK_LEFT,   0,                  "MoveLeft"                                },
66     { VK_LEFT,   ShiftKey,           "MoveLeftAndModifySelection"              },
67     { VK_LEFT,   CtrlKey,            "MoveWordLeft"                            },
68     { VK_LEFT,   CtrlKey | ShiftKey,
69         "MoveWordLeftAndModifySelection"                                       },
70     { VK_RIGHT,  0,                  "MoveRight"                               },
71     { VK_RIGHT,  ShiftKey,           "MoveRightAndModifySelection"             },
72     { VK_RIGHT,  CtrlKey,            "MoveWordRight"                           },
73     { VK_RIGHT,  CtrlKey | ShiftKey, "MoveWordRightAndModifySelection"         },
74     { VK_UP,     0,                  "MoveUp"                                  },
75     { VK_UP,     ShiftKey,           "MoveUpAndModifySelection"                },
76     { VK_PRIOR,  ShiftKey,           "MovePageUpAndModifySelection"            },
77     { VK_DOWN,   0,                  "MoveDown"                                },
78     { VK_DOWN,   ShiftKey,           "MoveDownAndModifySelection"              },
79     { VK_NEXT,   ShiftKey,           "MovePageDownAndModifySelection"          },
80     { VK_UP,     CtrlKey | ShiftKey, "MoveParagraphBackwardAndModifySelection" },
81     { VK_DOWN,   CtrlKey | ShiftKey, "MoveParagraphForwardAndModifySelection"  },
82     { VK_PRIOR,  0,                  "MovePageUp"                              },
83     { VK_NEXT,   0,                  "MovePageDown"                            },
84     { VK_HOME,   0,                  "MoveToBeginningOfLine"                   },
85     { VK_HOME,   ShiftKey,
86         "MoveToBeginningOfLineAndModifySelection"                              },
87     { VK_HOME,   CtrlKey,            "MoveToBeginningOfDocument"               },
88     { VK_HOME,   CtrlKey | ShiftKey,
89         "MoveToBeginningOfDocumentAndModifySelection"                          },
90     { VK_END,    0,                  "MoveToEndOfLine"                         },
91     { VK_END,    ShiftKey,           "MoveToEndOfLineAndModifySelection"       },
92     { VK_END,    CtrlKey,            "MoveToEndOfDocument"                     },
93     { VK_END,    CtrlKey | ShiftKey,
94         "MoveToEndOfDocumentAndModifySelection"                                },
95     { VK_BACK,   0,                  "DeleteBackward"                          },
96     { VK_BACK,   ShiftKey,           "DeleteBackward"                          },
97     { VK_DELETE, 0,                  "DeleteForward"                           },
98     { VK_BACK,   CtrlKey,            "DeleteWordBackward"                      },
99     { VK_DELETE, CtrlKey,            "DeleteWordForward"                       },
100     { 'B',         CtrlKey,          "ToggleBold"                              },
101     { 'I',         CtrlKey,          "ToggleItalic"                            },
102     { 'U',         CtrlKey,          "ToggleUnderline"                         },
103     { VK_ESCAPE, 0,                  "Cancel"                                  },
104     { VK_OEM_PERIOD, CtrlKey,        "Cancel"                                  },
105     { VK_TAB,    0,                  "InsertTab"                               },
106     { VK_TAB,    ShiftKey,           "InsertBacktab"                           },
107     { VK_RETURN, 0,                  "InsertNewline"                           },
108     { VK_RETURN, CtrlKey,            "InsertNewline"                           },
109     { VK_RETURN, AltKey,             "InsertNewline"                           },
110     { VK_RETURN, AltKey | ShiftKey,  "InsertNewline"                           },
111     { VK_RETURN, ShiftKey,           "InsertLineBreak"                         },
112     // These probably need handling somewhere else so do not execute them. The
113     // 'Cut' command is removing text so let's avoid losing the user losing data
114     // until we implement clipboard support wherever it should be.
115     { VK_INSERT, CtrlKey,            "Copy"                                    },
116     { VK_INSERT, ShiftKey,           "Paste"                                   },
117     { VK_DELETE, ShiftKey,           "Cut"                                     },
118     { 'C',         CtrlKey,          "Copy"                                    },
119     { 'V',         CtrlKey,          "Paste"                                   },
120     { 'V',         CtrlKey | ShiftKey, "PasteAndMatchStyle"                    },
121     { 'X',         CtrlKey,          "Cut"                                     },
122     { 'A',         CtrlKey,          "SelectAll"                               },
123     { 'Z',         CtrlKey,          "Undo"                                    },
124     { 'Z',         CtrlKey | ShiftKey, "Redo"                                  },
125     { 'Y',         CtrlKey,          "Redo"                                    },
126     { VK_INSERT, 0,                  "OverWrite"                               },
127 };
128
129 static const KeyPressEntry keyPressEntries[] = {
130     { '\t',   0,                  "InsertTab"                                  },
131     { '\t',   ShiftKey,           "InsertBacktab"                              },
132     { '\r',   0,                  "InsertNewline"                              },
133     { '\r',   CtrlKey,            "InsertNewline"                              },
134     { '\r',   ShiftKey,           "InsertLineBreak"                            },
135     { '\r',   AltKey,             "InsertNewline"                              },
136     { '\r',   AltKey | ShiftKey,  "InsertNewline"                              },
137 };
138
139 static const char* interpretKeyEvent(const KeyboardEvent& event)
140 {
141     static NeverDestroyed<HashMap<int, const char*>> keyDownCommandsMap;
142     static NeverDestroyed<HashMap<int, const char*>> keyPressCommandsMap;
143
144     if (keyDownCommandsMap.get().isEmpty()) {
145         for (unsigned i = 0; i < WTF_ARRAY_LENGTH(keyDownEntries); i++)
146             keyDownCommandsMap.get().set(keyDownEntries[i].modifiers << 16 | keyDownEntries[i].virtualKey, keyDownEntries[i].name);
147         for (unsigned i = 0; i < WTF_ARRAY_LENGTH(keyPressEntries); i++)
148             keyPressCommandsMap.get().set(keyPressEntries[i].modifiers << 16 | keyPressEntries[i].charCode, keyPressEntries[i].name);
149     }
150
151     unsigned modifiers = 0;
152     if (event.shiftKey())
153         modifiers |= ShiftKey;
154     if (event.altKey())
155         modifiers |= AltKey;
156     if (event.ctrlKey())
157         modifiers |= CtrlKey;
158     if (event.metaKey())
159         modifiers |= MetaKey;
160
161     if (event.type() == eventNames().keydownEvent) {
162         int mapKey = modifiers << 16 | event.keyCode();
163         return mapKey ? keyDownCommandsMap.get().get(mapKey) : nullptr;
164     }
165
166     int mapKey = modifiers << 16 | event.charCode();
167     return mapKey ? keyPressCommandsMap.get().get(mapKey) : nullptr;
168 }
169
170 static void handleKeyPress(Frame& frame, KeyboardEvent& event, const PlatformKeyboardEvent& platformEvent)
171 {
172     String commandName = interpretKeyEvent(event);
173
174     if (!commandName.isEmpty()) {
175         frame.editor().command(commandName).execute();
176         event.setDefaultHandled();
177         return;
178     }
179
180     // Don't insert null or control characters as they can result in unexpected behaviour
181     if (event.charCode() < ' ')
182         return;
183
184     // Don't insert anything if a modifier is pressed and it has not been handled yet
185     if (platformEvent.ctrlKey() || platformEvent.altKey())
186         return;
187
188     if (frame.editor().insertText(platformEvent.text(), &event))
189         event.setDefaultHandled();
190 }
191
192 static void handleKeyDown(Frame& frame, KeyboardEvent& event, const PlatformKeyboardEvent&)
193 {
194     String commandName = interpretKeyEvent(event);
195     if (commandName.isEmpty())
196         return;
197
198     // We shouldn't insert text through the editor. Let WebCore decide
199     // how to handle that (say, Tab, which could also be used to
200     // change focus).
201     Editor::Command command = frame.editor().command(commandName);
202     if (command.isTextInsertion())
203         return;
204
205     command.execute();
206     event.setDefaultHandled();
207 }
208
209 void WebEditorClient::handleKeyboardEvent(WebCore::KeyboardEvent* event)
210 {
211     ASSERT(event->target());
212     auto* frame = downcast<Node>(event->target())->document().frame();
213     ASSERT(frame);
214
215     // FIXME: Reorder the checks in a more sensible way.
216
217     auto* platformEvent = event->underlyingPlatformEvent();
218     if (!platformEvent)
219         return;
220
221     // If this was an IME event don't do anything.
222     if (platformEvent->windowsVirtualKeyCode() == VK_PROCESSKEY)
223         return;
224
225     // Don't allow text insertion for nodes that cannot edit.
226     if (!frame->editor().canEdit())
227         return;
228
229     // This is just a normal text insertion, so wait to execute the insertion
230     // until a keypress event happens. This will ensure that the insertion will not
231     // be reflected in the contents of the field until the keyup DOM event.
232     if (event->type() == eventNames().keypressEvent)
233         return handleKeyPress(*frame, *event, *platformEvent);
234     if (event->type() == eventNames().keydownEvent)
235         return handleKeyDown(*frame, *event, *platformEvent);
236 }
237
238 void WebEditorClient::handleInputMethodKeydown(WebCore::KeyboardEvent* event)
239 {
240     auto* platformEvent = event->underlyingPlatformEvent();
241     if (platformEvent && platformEvent->windowsVirtualKeyCode() == VK_PROCESSKEY)
242         event->preventDefault();
243 }
244
245 } // namespace WebKit