Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #include "KeyboardEventManager.h" | |
|
dtapuska
2016/06/06 20:43:30
Chromium copyright?
Navid Zolghadr
2016/06/06 21:37:50
Done.
| |
| 2 | |
| 3 #include "core/dom/Element.h" | |
| 4 #include "core/editing/Editor.h" | |
| 5 #include "core/events/KeyboardEvent.h" | |
| 6 #include "core/html/HTMLDialogElement.h" | |
| 7 #include "core/input/EventHandler.h" | |
| 8 #include "core/layout/LayoutObject.h" | |
| 9 #include "core/layout/LayoutTextControlSingleLine.h" | |
| 10 #include "core/loader/FrameLoaderClient.h" | |
| 11 #include "core/page/ChromeClient.h" | |
| 12 #include "core/page/FocusController.h" | |
| 13 #include "core/page/Page.h" | |
| 14 #include "core/page/SpatialNavigation.h" | |
| 15 #include "platform/PlatformKeyboardEvent.h" | |
| 16 #include "platform/UserGestureIndicator.h" | |
| 17 #include "platform/WindowsKeyboardCodes.h" | |
| 18 | |
| 19 namespace blink { | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 WebFocusType focusDirectionForKey(const AtomicString& keyIdentifier) | |
| 24 { | |
| 25 DEFINE_STATIC_LOCAL(AtomicString, Down, ("Down")); | |
| 26 DEFINE_STATIC_LOCAL(AtomicString, Up, ("Up")); | |
| 27 DEFINE_STATIC_LOCAL(AtomicString, Left, ("Left")); | |
| 28 DEFINE_STATIC_LOCAL(AtomicString, Right, ("Right")); | |
| 29 | |
| 30 WebFocusType retVal = WebFocusTypeNone; | |
| 31 | |
| 32 if (keyIdentifier == Down) | |
| 33 retVal = WebFocusTypeDown; | |
| 34 else if (keyIdentifier == Up) | |
| 35 retVal = WebFocusTypeUp; | |
| 36 else if (keyIdentifier == Left) | |
| 37 retVal = WebFocusTypeLeft; | |
| 38 else if (keyIdentifier == Right) | |
| 39 retVal = WebFocusTypeRight; | |
| 40 | |
| 41 return retVal; | |
| 42 } | |
| 43 | |
| 44 } // namespace | |
| 45 | |
| 46 PlatformEvent::Modifiers KeyboardEventManager::accessKeyModifiers() | |
| 47 { | |
| 48 #if OS(MACOSX) | |
| 49 return static_cast<PlatformEvent::Modifiers>(PlatformEvent::CtrlKey | Platfo rmEvent::AltKey); | |
| 50 #else | |
| 51 return PlatformEvent::AltKey; | |
| 52 #endif | |
| 53 } | |
| 54 | |
| 55 KeyboardEventManager::KeyboardEventManager( | |
| 56 LocalFrame* frame, ScrollManager* scrollManager) | |
| 57 : m_frame(frame) | |
| 58 , m_scrollManager(scrollManager) | |
| 59 { | |
| 60 } | |
| 61 | |
| 62 KeyboardEventManager::~KeyboardEventManager() | |
| 63 { | |
| 64 } | |
| 65 | |
| 66 bool KeyboardEventManager::handleAccessKey(const PlatformKeyboardEvent& evt) | |
| 67 { | |
| 68 // FIXME: Ignoring the state of Shift key is what neither IE nor Firefox do. | |
| 69 // IE matches lower and upper case access keys regardless of Shift key state - but if both upper and | |
| 70 // lower case variants are present in a document, the correct element is mat ched based on Shift key state. | |
| 71 // Firefox only matches an access key if Shift is not pressed, and does that case-insensitively. | |
| 72 DCHECK(!(accessKeyModifiers() & PlatformEvent::ShiftKey)); | |
| 73 if ((evt.getModifiers() & (PlatformEvent::KeyModifiers & ~PlatformEvent::Shi ftKey)) != accessKeyModifiers()) | |
| 74 return false; | |
| 75 String key = evt.unmodifiedText(); | |
| 76 Element* elem = m_frame->document()->getElementByAccessKey(key.lower()); | |
| 77 if (!elem) | |
| 78 return false; | |
| 79 elem->accessKeyAction(false); | |
| 80 return true; | |
| 81 } | |
| 82 | |
| 83 WebInputEventResult KeyboardEventManager::keyEvent( | |
| 84 const PlatformKeyboardEvent& initialKeyEvent) | |
| 85 { | |
| 86 m_frame->chromeClient().clearToolTip(); | |
| 87 | |
| 88 if (initialKeyEvent.windowsVirtualKeyCode() == VK_CAPITAL) | |
| 89 capsLockStateMayHaveChanged(); | |
| 90 | |
| 91 #if OS(WIN) | |
| 92 if (m_scrollManager->panScrollInProgress()) { | |
| 93 // If a key is pressed while the panScroll is in progress then we want t o stop | |
| 94 if (initialKeyEvent.type() == PlatformEvent::KeyDown || initialKeyEvent. type() == PlatformEvent::RawKeyDown) | |
| 95 m_scrollManager->stopAutoscroll(); | |
| 96 | |
| 97 // If we were in panscroll mode, we swallow the key event | |
| 98 return WebInputEventResult::HandledSuppressed; | |
| 99 } | |
| 100 #endif | |
| 101 | |
| 102 // Check for cases where we are too early for events -- possible unmatched k ey up | |
| 103 // from pressing return in the location bar. | |
| 104 Node* node = eventTargetNodeForDocument(m_frame->document()); | |
| 105 if (!node) | |
| 106 return WebInputEventResult::NotHandled; | |
| 107 | |
| 108 UserGestureIndicator gestureIndicator(DefinitelyProcessingUserGesture); | |
| 109 | |
| 110 // In IE, access keys are special, they are handled after default keydown pr ocessing, but cannot be canceled - this is hard to match. | |
| 111 // On Mac OS X, we process them before dispatching keydown, as the default k eydown handler implements Emacs key bindings, which may conflict | |
| 112 // with access keys. Then we dispatch keydown, but suppress its default hand ling. | |
| 113 // On Windows, WebKit explicitly calls handleAccessKey() instead of dispatch ing a keypress event for WM_SYSCHAR messages. | |
| 114 // Other platforms currently match either Mac or Windows behavior, depending on whether they send combined KeyDown events. | |
| 115 bool matchedAnAccessKey = false; | |
| 116 if (initialKeyEvent.type() == PlatformEvent::KeyDown) | |
| 117 matchedAnAccessKey = handleAccessKey(initialKeyEvent); | |
| 118 | |
| 119 // FIXME: it would be fair to let an input method handle KeyUp events before DOM dispatch. | |
| 120 if (initialKeyEvent.type() == PlatformEvent::KeyUp || initialKeyEvent.type() == PlatformEvent::Char) { | |
| 121 KeyboardEvent* domEvent = KeyboardEvent::create(initialKeyEvent, m_frame ->document()->domWindow()); | |
| 122 | |
| 123 return EventHandler::toWebInputEventResult(node->dispatchEvent(domEvent) ); | |
| 124 } | |
| 125 | |
| 126 PlatformKeyboardEvent keyDownEvent = initialKeyEvent; | |
| 127 if (keyDownEvent.type() != PlatformEvent::RawKeyDown) | |
| 128 keyDownEvent.disambiguateKeyDownEvent(PlatformEvent::RawKeyDown); | |
| 129 KeyboardEvent* keydown = KeyboardEvent::create(keyDownEvent, m_frame->docume nt()->domWindow()); | |
| 130 if (matchedAnAccessKey) | |
| 131 keydown->setDefaultPrevented(true); | |
| 132 keydown->setTarget(node); | |
| 133 | |
| 134 DispatchEventResult dispatchResult = node->dispatchEvent(keydown); | |
| 135 if (dispatchResult != DispatchEventResult::NotCanceled) | |
| 136 return EventHandler::toWebInputEventResult(dispatchResult); | |
| 137 // If frame changed as a result of keydown dispatch, then return early to av oid sending a subsequent keypress message to the new frame. | |
| 138 bool changedFocusedFrame = m_frame->page() && m_frame != m_frame->page()->fo cusController().focusedOrMainFrame(); | |
| 139 if (changedFocusedFrame) | |
| 140 return WebInputEventResult::HandledSystem; | |
| 141 | |
| 142 if (initialKeyEvent.type() == PlatformEvent::RawKeyDown) | |
| 143 return WebInputEventResult::NotHandled; | |
| 144 | |
| 145 // Focus may have changed during keydown handling, so refetch node. | |
| 146 // But if we are dispatching a fake backward compatibility keypress, then we pretend that the keypress happened on the original node. | |
| 147 node = eventTargetNodeForDocument(m_frame->document()); | |
| 148 if (!node) | |
| 149 return WebInputEventResult::NotHandled; | |
| 150 | |
| 151 PlatformKeyboardEvent keyPressEvent = initialKeyEvent; | |
| 152 keyPressEvent.disambiguateKeyDownEvent(PlatformEvent::Char); | |
| 153 if (keyPressEvent.text().isEmpty()) | |
| 154 return WebInputEventResult::NotHandled; | |
| 155 KeyboardEvent* keypress = KeyboardEvent::create(keyPressEvent, m_frame->docu ment()->domWindow()); | |
| 156 keypress->setTarget(node); | |
| 157 return EventHandler::toWebInputEventResult(node->dispatchEvent(keypress)); | |
| 158 } | |
| 159 | |
| 160 void KeyboardEventManager::capsLockStateMayHaveChanged() | |
| 161 { | |
| 162 if (Element* element = m_frame->document()->focusedElement()) { | |
| 163 if (LayoutObject* r = element->layoutObject()) { | |
| 164 if (r->isTextField()) | |
| 165 toLayoutTextControlSingleLine(r)->capsLockStateMayHaveChanged(); | |
| 166 } | |
| 167 } | |
| 168 } | |
| 169 | |
| 170 void KeyboardEventManager::defaultKeyboardEventHandler( | |
| 171 KeyboardEvent* event, Node* possibleFocusedNode) | |
| 172 { | |
| 173 if (event->type() == EventTypeNames::keydown) { | |
| 174 // Clear caret blinking suspended state to make sure that caret blinks | |
| 175 // when we type again after long pressing on an empty input field. | |
| 176 if (m_frame && m_frame->selection().isCaretBlinkingSuspended()) | |
| 177 m_frame->selection().setCaretBlinkingSuspended(false); | |
| 178 | |
| 179 m_frame->editor().handleKeyboardEvent(event); | |
| 180 if (event->defaultHandled()) | |
| 181 return; | |
| 182 if (event->keyIdentifier() == "U+0009") { | |
| 183 defaultTabEventHandler(event); | |
| 184 } else if (event->keyIdentifier() == "U+0008") { | |
| 185 defaultBackspaceEventHandler(event); | |
| 186 } else if (event->keyIdentifier() == "U+001B") { | |
| 187 defaultEscapeEventHandler(event); | |
| 188 } else { | |
| 189 WebFocusType type = focusDirectionForKey(AtomicString(event->keyIden tifier())); | |
| 190 if (type != WebFocusTypeNone) | |
| 191 defaultArrowEventHandler(type, event); | |
| 192 } | |
| 193 } | |
| 194 if (event->type() == EventTypeNames::keypress) { | |
| 195 m_frame->editor().handleKeyboardEvent(event); | |
| 196 if (event->defaultHandled()) | |
| 197 return; | |
| 198 if (event->charCode() == ' ') | |
| 199 defaultSpaceEventHandler(event, possibleFocusedNode); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void KeyboardEventManager::defaultSpaceEventHandler( | |
| 204 KeyboardEvent* event, Node* possibleFocusedNode) | |
| 205 { | |
| 206 DCHECK_EQ(event->type(), EventTypeNames::keypress); | |
| 207 | |
| 208 if (event->ctrlKey() || event->metaKey() || event->altKey()) | |
| 209 return; | |
| 210 | |
| 211 ScrollDirection direction = event->shiftKey() ? ScrollBlockDirectionBackward : ScrollBlockDirectionForward; | |
| 212 | |
| 213 // FIXME: enable scroll customization in this case. See crbug.com/410974. | |
| 214 if (m_scrollManager->logicalScroll(direction, ScrollByPage, nullptr, possibl eFocusedNode)) { | |
| 215 event->setDefaultHandled(); | |
| 216 return; | |
| 217 } | |
| 218 } | |
| 219 | |
| 220 void KeyboardEventManager::defaultBackspaceEventHandler(KeyboardEvent* event) | |
| 221 { | |
| 222 DCHECK_EQ(event->type(), EventTypeNames::keydown); | |
| 223 | |
| 224 if (!RuntimeEnabledFeatures::backspaceDefaultHandlerEnabled()) | |
| 225 return; | |
| 226 | |
| 227 if (event->ctrlKey() || event->metaKey() || event->altKey()) | |
| 228 return; | |
| 229 | |
| 230 if (!m_frame->editor().behavior().shouldNavigateBackOnBackspace()) | |
| 231 return; | |
| 232 UseCounter::count(m_frame->document(), UseCounter::BackspaceNavigatedBack); | |
| 233 if (m_frame->page()->chromeClient().hadFormInteraction()) | |
| 234 UseCounter::count(m_frame->document(), UseCounter::BackspaceNavigatedBac kAfterFormInteraction); | |
| 235 bool handledEvent = m_frame->loader().client()->navigateBackForward(event->s hiftKey() ? 1 : -1); | |
| 236 if (handledEvent) | |
| 237 event->setDefaultHandled(); | |
| 238 } | |
| 239 | |
| 240 void KeyboardEventManager::defaultArrowEventHandler(WebFocusType focusType, Keyb oardEvent* event) | |
| 241 { | |
| 242 DCHECK_EQ(event->type(), EventTypeNames::keydown); | |
| 243 | |
| 244 if (event->ctrlKey() || event->metaKey() || event->shiftKey()) | |
| 245 return; | |
| 246 | |
| 247 Page* page = m_frame->page(); | |
| 248 if (!page) | |
| 249 return; | |
| 250 | |
| 251 if (!isSpatialNavigationEnabled(m_frame)) | |
| 252 return; | |
| 253 | |
| 254 // Arrows and other possible directional navigation keys can be used in desi gn | |
| 255 // mode editing. | |
| 256 if (m_frame->document()->inDesignMode()) | |
| 257 return; | |
| 258 | |
| 259 if (page->focusController().advanceFocus(focusType)) | |
| 260 event->setDefaultHandled(); | |
| 261 } | |
| 262 | |
| 263 void KeyboardEventManager::defaultTabEventHandler(KeyboardEvent* event) | |
| 264 { | |
| 265 DCHECK_EQ(event->type(), EventTypeNames::keydown); | |
| 266 | |
| 267 // We should only advance focus on tabs if no special modifier keys are held down. | |
| 268 if (event->ctrlKey() || event->metaKey()) | |
| 269 return; | |
| 270 | |
| 271 #if !OS(MACOSX) | |
| 272 // Option-Tab is a shortcut based on a system-wide preference on Mac but | |
| 273 // should be ignored on all other platforms. | |
| 274 if (event->altKey()) | |
| 275 return; | |
| 276 #endif | |
| 277 | |
| 278 Page* page = m_frame->page(); | |
| 279 if (!page) | |
| 280 return; | |
| 281 if (!page->tabKeyCyclesThroughElements()) | |
| 282 return; | |
| 283 | |
| 284 WebFocusType focusType = event->shiftKey() ? WebFocusTypeBackward : WebFocus TypeForward; | |
| 285 | |
| 286 // Tabs can be used in design mode editing. | |
| 287 if (m_frame->document()->inDesignMode()) | |
| 288 return; | |
| 289 | |
| 290 if (page->focusController().advanceFocus(focusType, InputDeviceCapabilities: :doesntFireTouchEventsSourceCapabilities())) | |
| 291 event->setDefaultHandled(); | |
| 292 } | |
| 293 | |
| 294 void KeyboardEventManager::defaultEscapeEventHandler(KeyboardEvent* event) | |
| 295 { | |
| 296 if (HTMLDialogElement* dialog = m_frame->document()->activeModalDialog()) | |
| 297 dialog->dispatchEvent(Event::createCancelable(EventTypeNames::cancel)); | |
| 298 } | |
| 299 | |
| 300 DEFINE_TRACE(KeyboardEventManager) | |
| 301 { | |
| 302 visitor->trace(m_frame); | |
| 303 } | |
| 304 | |
| 305 } // namespace blink | |
| OLD | NEW |