diff --git a/include/cef.h b/include/cef.h index ba9c08a3a..cb1b4e18b 100644 --- a/include/cef.h +++ b/include/cef.h @@ -1393,21 +1393,26 @@ public: typedef cef_handler_keyevent_type_t KeyEventType; /// - // Called when the browser component receives a keyboard event that has not - // been intercepted via JavaScript. |type| is the type of keyboard event, - // |code| is the windows scan-code for the event, |modifiers| is a set of bit- - // flags describing any pressed modifier keys and |isSystemKey| is true if - // Windows considers this a 'system key' message (see - // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx). Return - // true if the keyboard event was handled or false to allow the browser - // component to handle the event. + // Called when the browser component receives a keyboard event. This method + // is called both before the event is passed to the renderer and after + // JavaScript in the page has had a chance to handle the event. |type| is the + // type of keyboard event, |code| is the windows scan-code for the event, + // |modifiers| is a set of bit- flags describing any pressed modifier keys and + // |isSystemKey| is true if Windows considers this a 'system key' message (see + // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx). If + // |isAfterJavaScript| is true then JavaScript in the page has had a chance + // to handle the event and has chosen not to. Only RAWKEYDOWN, KEYDOWN and + // CHAR events will be sent with |isAfterJavaScript| set to true. Return + // true if the keyboard event was handled or false to allow continued handling + // of the event by the renderer. /// /*--cef()--*/ virtual bool OnKeyEvent(CefRefPtr browser, KeyEventType type, int code, int modifiers, - bool isSystemKey) { return false; } + bool isSystemKey, + bool isAfterJavaScript) { return false; } }; diff --git a/include/cef_capi.h b/include/cef_capi.h index fb5027522..9169f855b 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -1196,18 +1196,22 @@ typedef struct _cef_keyboard_handler_t cef_base_t base; /// - // Called when the browser component receives a keyboard event that has not - // been intercepted via JavaScript. |type| is the type of keyboard event, - // |code| is the windows scan-code for the event, |modifiers| is a set of bit- - // flags describing any pressed modifier keys and |isSystemKey| is true (1) if - // Windows considers this a 'system key' message (see - // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx). Return true - // (1) if the keyboard event was handled or false (0) to allow the browser - // component to handle the event. + // Called when the browser component receives a keyboard event. This function + // is called both before the event is passed to the renderer and after + // JavaScript in the page has had a chance to handle the event. |type| is the + // type of keyboard event, |code| is the windows scan-code for the event, + // |modifiers| is a set of bit- flags describing any pressed modifier keys and + // |isSystemKey| is true (1) if Windows considers this a 'system key' message + // (see http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx). If + // |isAfterJavaScript| is true (1) then JavaScript in the page has had a + // chance to handle the event and has chosen not to. Only RAWKEYDOWN, KEYDOWN + // and CHAR events will be sent with |isAfterJavaScript| set to true (1). + // Return true (1) if the keyboard event was handled or false (0) to allow + // continued handling of the event by the renderer. /// int (CEF_CALLBACK *on_key_event)(struct _cef_keyboard_handler_t* self, struct _cef_browser_t* browser, enum cef_handler_keyevent_type_t type, - int code, int modifiers, int isSystemKey); + int code, int modifiers, int isSystemKey, int isAfterJavaScript); } cef_keyboard_handler_t; diff --git a/libcef/browser_webview_delegate.cc b/libcef/browser_webview_delegate.cc index 69fff5c1f..f183ff19d 100644 --- a/libcef/browser_webview_delegate.cc +++ b/libcef/browser_webview_delegate.cc @@ -268,40 +268,8 @@ bool BrowserWebViewDelegate::isSelectTrailingWhitespaceEnabled() { } bool BrowserWebViewDelegate::handleCurrentKeyboardEvent() { - bool handled = false; - - CefRefPtr client = browser_->GetClient(); - CefRefPtr handler; - if (client.get()) - handler = client->GetKeyboardHandler(); - - if (handler.get()) { - WebWidgetHost* host = GetWidgetHost(); - if (host) { - WebKeyboardEvent event = host->GetLastKeyEvent(); - switch (event.type) - { - case WebKeyboardEvent::RawKeyDown: - handled = handler->OnKeyEvent(browser_, - KEYEVENT_RAWKEYDOWN, event.windowsKeyCode, - event.modifiers, event.isSystemKey?true:false); - break; - case WebKeyboardEvent::KeyUp: - handled = handler->OnKeyEvent(browser_, - KEYEVENT_KEYUP, event.windowsKeyCode, - event.modifiers, event.isSystemKey?true:false); - break; - case WebKeyboardEvent::Char: - handled = handler->OnKeyEvent(browser_, - KEYEVENT_CHAR, event.windowsKeyCode, - event.modifiers, event.isSystemKey?true:false); - break; - default: - break; - } - } - } - if (handled) + WebWidgetHost* host = GetWidgetHost(); + if (host && OnKeyboardEvent(host->GetLastKeyEvent(), true)) return true; if (edit_command_name_.empty()) @@ -1039,6 +1007,38 @@ void BrowserWebViewDelegate::WaitForPolicyDelegate() { // Private methods ----------------------------------------------------------- +bool BrowserWebViewDelegate::OnKeyboardEvent( + const WebKit::WebKeyboardEvent& event, bool isAfterJavaScript) +{ + CefRefPtr client = browser_->GetClient(); + CefRefPtr handler; + if (client.get()) + handler = client->GetKeyboardHandler(); + if (!handler.get()) + return false; + + CefKeyboardHandler::KeyEventType eventType; + switch (event.type) { + case WebKeyboardEvent::RawKeyDown: + eventType = KEYEVENT_RAWKEYDOWN; + break; + case WebKeyboardEvent::KeyDown: + eventType = KEYEVENT_KEYDOWN; + break; + case WebKeyboardEvent::KeyUp: + eventType = KEYEVENT_KEYUP; + break; + case WebKeyboardEvent::Char: + eventType = KEYEVENT_CHAR; + break; + default: + return false; + } + + return handler->OnKeyEvent(browser_, eventType, event.windowsKeyCode, + event.modifiers, event.isSystemKey?true:false, isAfterJavaScript); +} + void BrowserWebViewDelegate::ShowStatus(const WebString& text, cef_handler_statustype_t type) { diff --git a/libcef/browser_webview_delegate.h b/libcef/browser_webview_delegate.h index 2e53819f3..cd4512bd9 100644 --- a/libcef/browser_webview_delegate.h +++ b/libcef/browser_webview_delegate.h @@ -263,6 +263,9 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient, void HandleContextMenu(int selected_id); #endif + bool OnKeyboardEvent(const WebKit::WebKeyboardEvent& event, + bool isAfterJavaScript); + protected: // Default handling of JavaScript messages. void ShowJavaScriptAlert(WebKit::WebFrame* webframe, diff --git a/libcef/webview_host.cc b/libcef/webview_host.cc index e43d81d23..d54f14d30 100644 --- a/libcef/webview_host.cc +++ b/libcef/webview_host.cc @@ -4,7 +4,8 @@ #include "webview_host.h" -WebViewHost::WebViewHost() +WebViewHost::WebViewHost(BrowserWebViewDelegate* delegate) + : delegate_(delegate) { } diff --git a/libcef/webview_host.h b/libcef/webview_host.h index 0f914e8fb..d545c4a7b 100644 --- a/libcef/webview_host.h +++ b/libcef/webview_host.h @@ -48,24 +48,31 @@ class WebViewHost : public WebWidgetHost { webkit::npapi::GtkPluginContainerManager* plugin_container_manager() { return &plugin_container_manager_; } + + virtual void KeyEvent(GdkEventKey* event); #elif defined(OS_MACOSX) void SetIsActive(bool active); virtual void MouseEvent(NSEvent *); + virtual void KeyEvent(NSEvent *); virtual void SetFocus(bool enable); #endif protected: - WebViewHost(); + WebViewHost(BrowserWebViewDelegate* delegate); #if defined(OS_WIN) virtual bool WndProc(UINT message, WPARAM wparam, LPARAM lparam); virtual void MouseEvent(UINT message, WPARAM wparam, LPARAM lparam); + virtual void KeyEvent(UINT message, WPARAM wparam, LPARAM lparam); #endif #if defined(TOOLKIT_USES_GTK) // Helper class that creates and moves plugin containers. webkit::npapi::GtkPluginContainerManager plugin_container_manager_; #endif + + // The delegate pointer will always outlive the WebViewHost object. + BrowserWebViewDelegate* delegate_; }; #endif // _WEBVIEW_HOST_H diff --git a/libcef/webview_host_gtk.cc b/libcef/webview_host_gtk.cc index eeaa2e09e..27405d00f 100644 --- a/libcef/webview_host_gtk.cc +++ b/libcef/webview_host_gtk.cc @@ -8,6 +8,8 @@ #include "browser_webview_delegate.h" #include "base/logging.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/gtk/WebInputEventFactory.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" @@ -15,6 +17,9 @@ #include "webkit/plugins/npapi/gtk_plugin_container.h" using WebKit::WebDevToolsAgentClient; +using WebKit::WebKeyboardEvent; +using WebKit::WebInputEvent; +using WebKit::WebInputEventFactory; using WebKit::WebView; // static @@ -24,7 +29,7 @@ WebViewHost* WebViewHost::Create(GtkWidget* parent_view, PaintDelegate* paint_delegate, WebDevToolsAgentClient* dev_tools_client, const WebPreferences& prefs) { - WebViewHost* host = new WebViewHost(); + WebViewHost* host = new WebViewHost(delegate); host->view_ = WebWidgetHost::CreateWidget(parent_view, host); host->plugin_container_manager_.set_host_widget(host->view_); @@ -53,3 +58,11 @@ void WebViewHost::CreatePluginContainer(gfx::PluginWindowHandle id) { void WebViewHost::DestroyPluginContainer(gfx::PluginWindowHandle id) { plugin_container_manager_.DestroyPluginContainer(id); } + +void WebViewHost::KeyEvent(GdkEventKey* event) { + WebKeyboardEvent keyboard_event(WebInputEventFactory::keyboardEvent(event)); + if (delegate_->OnKeyboardEvent(keyboard_event, false)) + return; + + WebWidgetHost::KeyEvent(event); +} diff --git a/libcef/webview_host_mac.mm b/libcef/webview_host_mac.mm index bb70902a5..725d5eeb9 100644 --- a/libcef/webview_host_mac.mm +++ b/libcef/webview_host_mac.mm @@ -10,6 +10,8 @@ #include "cef_context.h" #include "skia/ext/platform_canvas.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/mac/WebInputEventFactory.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebSize.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" #include "ui/gfx/rect.h" @@ -17,6 +19,9 @@ #include "webkit/glue/webpreferences.h" using WebKit::WebDevToolsAgentClient; +using WebKit::WebKeyboardEvent; +using WebKit::WebInputEvent; +using WebKit::WebInputEventFactory; using WebKit::WebSize; using WebKit::WebView; @@ -27,7 +32,7 @@ WebViewHost* WebViewHost::Create(NSView* parent_view, PaintDelegate* paint_delegate, WebDevToolsAgentClient* dev_tools_client, const WebPreferences& prefs) { - WebViewHost* host = new WebViewHost(); + WebViewHost* host = new WebViewHost(delegate); NSRect content_rect = {{rect.x(), rect.y()}, {rect.width(), rect.height()}}; host->view_ = [[BrowserWebView alloc] initWithFrame:content_rect]; @@ -68,6 +73,14 @@ void WebViewHost::MouseEvent(NSEvent* event) { WebWidgetHost::MouseEvent(event); } +void WebViewHost::KeyEvent(NSEvent *event) { + WebKeyboardEvent keyboard_event(WebInputEventFactory::keyboardEvent(event)); + if (delegate_->OnKeyboardEvent(keyboard_event, false)) + return; + + WebWidgetHost::KeyEvent(event); +} + void WebViewHost::SetFocus(bool enable) { if (enable) { // Set the current WebViewHost in case a drag action is started before mouse diff --git a/libcef/webview_host_win.cc b/libcef/webview_host_win.cc index 8bede67d9..15b7537e5 100644 --- a/libcef/webview_host_win.cc +++ b/libcef/webview_host_win.cc @@ -6,13 +6,19 @@ #include "browser_webview_delegate.h" #include "cef_context.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/WebInputEvent.h" #include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h" +#include "third_party/WebKit/Source/WebKit/chromium/public/win/WebInputEventFactory.h" #include "ui/base/win/hwnd_util.h" #include "ui/gfx/rect.h" #include "ui/gfx/size.h" #include "webkit/glue/webpreferences.h" -using namespace WebKit; +using WebKit::WebDevToolsAgentClient; +using WebKit::WebKeyboardEvent; +using WebKit::WebInputEvent; +using WebKit::WebInputEventFactory; +using WebKit::WebView; static const wchar_t kWindowClassName[] = L"WebViewHost"; @@ -23,7 +29,7 @@ WebViewHost* WebViewHost::Create(HWND parent_view, PaintDelegate* paint_delegate, WebDevToolsAgentClient* dev_tools_client, const WebPreferences& prefs) { - WebViewHost* host = new WebViewHost(); + WebViewHost* host = new WebViewHost(delegate); if (!paint_delegate) { static bool registered_class = false; @@ -79,3 +85,14 @@ void WebViewHost::MouseEvent(UINT message, WPARAM wparam, LPARAM lparam) { _Context->set_current_webviewhost(this); WebWidgetHost::MouseEvent(message, wparam, lparam); } + +void WebViewHost::KeyEvent(UINT message, WPARAM wparam, LPARAM lparam) { + // Give the client a chance to handle keyboard events before they're passed + // to WebKit. + const WebKeyboardEvent& event = WebInputEventFactory::keyboardEvent( + view_, message, wparam, lparam); + if (delegate_->OnKeyboardEvent(event, false)) + return; + + WebWidgetHost::KeyEvent(message, wparam, lparam); +} diff --git a/libcef/webwidget_host.h b/libcef/webwidget_host.h index 4b0691e05..177330705 100644 --- a/libcef/webwidget_host.h +++ b/libcef/webwidget_host.h @@ -24,6 +24,10 @@ #include "ui/base/win/ime_input.h" #endif +#if defined(TOOLKIT_USES_GTK) +#include +#endif + namespace gfx { class Rect; class Size; @@ -130,7 +134,7 @@ class WebWidgetHost { void Resize(LPARAM lparam); virtual void MouseEvent(UINT message, WPARAM wparam, LPARAM lparam); void WheelEvent(WPARAM wparam, LPARAM lparam); - void KeyEvent(UINT message, WPARAM wparam, LPARAM lparam); + virtual void KeyEvent(UINT message, WPARAM wparam, LPARAM lparam); void CaptureLostEvent(); void SetFocus(bool enable); void OnNotify(WPARAM wparam, NMHDR* header); @@ -149,7 +153,7 @@ class WebWidgetHost { void Resize(const gfx::Rect& rect); virtual void MouseEvent(NSEvent *); void WheelEvent(NSEvent *); - void KeyEvent(NSEvent *); + virtual void KeyEvent(NSEvent *); virtual void SetFocus(bool enable); protected: #elif defined(TOOLKIT_USES_GTK) @@ -167,6 +171,7 @@ class WebWidgetHost { WebWidgetHost* host); void WindowDestroyed(); void Resize(const gfx::Size& size); + virtual void KeyEvent(GdkEventKey* event); #endif #if defined(OS_WIN) diff --git a/libcef/webwidget_host_gtk.cc b/libcef/webwidget_host_gtk.cc index fb55e3b49..0211a013d 100644 --- a/libcef/webwidget_host_gtk.cc +++ b/libcef/webwidget_host_gtk.cc @@ -168,21 +168,7 @@ class WebWidgetHostGtkWidget { static gboolean HandleKeyPress(GtkWidget* widget, GdkEventKey* event, WebWidgetHost* host) { - host->webwidget()->handleInputEvent( - WebInputEventFactory::keyboardEvent(event)); - - // In the browser we do a ton of work with IMEs. This is some minimal - // code to make basic text work in test_shell, but doesn't cover IME. - // This is a copy of the logic in ProcessUnfilteredKeyPressEvent in - // render_widget_host_view_gtk.cc . - if (event->type == GDK_KEY_PRESS) { - WebKeyboardEvent wke = WebInputEventFactory::keyboardEvent(event); - if (wke.text[0]) { - wke.type = WebKit::WebInputEvent::Char; - host->webwidget()->handleInputEvent(wke); - } - } - + host->KeyEvent(event); return FALSE; } @@ -523,3 +509,20 @@ void WebWidgetHost::ResetTooltip() { // TODO(port): Implement this method as part of tooltip support. } + +void WebWidgetHost::KeyEvent(GdkEventKey* event) +{ + WebKeyboardEvent keyboard_event(WebInputEventFactory::keyboardEvent(event)); + last_key_event_ = keyboard_event; + webwidget()->handleInputEvent(keyboard_event); + + // In the browser we do a ton of work with IMEs. This is some minimal + // code to make basic text work in test_shell, but doesn't cover IME. + // This is a copy of the logic in ProcessUnfilteredKeyPressEvent in + // render_widget_host_view_gtk.cc . + if (event->type == GDK_KEY_PRESS && keyboard_event.text[0]) { + keyboard_event.type = WebKit::WebInputEvent::Char; + last_key_event_ = keyboard_event; + webwidget()->handleInputEvent(keyboard_event); + } +} diff --git a/libcef/webwidget_host_win.cc b/libcef/webwidget_host_win.cc index bc99bf06b..a7521ae4f 100644 --- a/libcef/webwidget_host_win.cc +++ b/libcef/webwidget_host_win.cc @@ -156,7 +156,14 @@ LRESULT CALLBACK WebWidgetHost::WndProc(HWND hwnd, UINT message, WPARAM wparam, break; case WM_MOUSEWHEEL: - host->WheelEvent(wparam, lparam); + { + // Only send mouse wheel events if the cursor is over the window. + POINT mousePt = { LOWORD(lparam), HIWORD(lparam) }; + RECT wndRect; + GetWindowRect(hwnd, &wndRect); + if (PtInRect(&wndRect, mousePt)) + host->WheelEvent(wparam, lparam); + } break; case WM_MOUSEACTIVATE: diff --git a/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc b/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc index 299c4ef21..1fcdae8ae 100644 --- a/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc +++ b/libcef_dll/cpptoc/keyboard_handler_cpptoc.cc @@ -19,7 +19,7 @@ int CEF_CALLBACK keyboard_handler_on_key_event( struct _cef_keyboard_handler_t* self, cef_browser_t* browser, enum cef_handler_keyevent_type_t type, int code, int modifiers, - int isSystemKey) + int isSystemKey, int isAfterJavaScript) { DCHECK(self); DCHECK(browser); @@ -28,7 +28,7 @@ int CEF_CALLBACK keyboard_handler_on_key_event( return CefKeyboardHandlerCppToC::Get(self)->OnKeyEvent( CefBrowserCToCpp::Wrap(browser), type, code, - modifiers, isSystemKey?true:false); + modifiers, isSystemKey?true:false, isAfterJavaScript?true:false); } diff --git a/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc b/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc index b1a2049f3..545883301 100644 --- a/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc +++ b/libcef_dll/ctocpp/keyboard_handler_ctocpp.cc @@ -17,13 +17,14 @@ // VIRTUAL METHODS - Body may be edited by hand. bool CefKeyboardHandlerCToCpp::OnKeyEvent(CefRefPtr browser, - KeyEventType type, int code, int modifiers, bool isSystemKey) + KeyEventType type, int code, int modifiers, bool isSystemKey, + bool isAfterJavaScript) { if (CEF_MEMBER_MISSING(struct_, on_key_event)) return false; return struct_->on_key_event(struct_, CefBrowserCppToC::Wrap(browser), - type, code, modifiers, isSystemKey) ? true : false; + type, code, modifiers, isSystemKey, isAfterJavaScript) ? true : false; } diff --git a/libcef_dll/ctocpp/keyboard_handler_ctocpp.h b/libcef_dll/ctocpp/keyboard_handler_ctocpp.h index c74f8dd3e..0b77b3e3b 100644 --- a/libcef_dll/ctocpp/keyboard_handler_ctocpp.h +++ b/libcef_dll/ctocpp/keyboard_handler_ctocpp.h @@ -34,7 +34,8 @@ public: // CefKeyboardHandler methods virtual bool OnKeyEvent(CefRefPtr browser, KeyEventType type, - int code, int modifiers, bool isSystemKey) OVERRIDE; + int code, int modifiers, bool isSystemKey, + bool isAfterJavaScript) OVERRIDE; }; #endif // BUILDING_CEF_SHARED diff --git a/tests/cefclient/client_handler.cpp b/tests/cefclient/client_handler.cpp index 30c3b1d88..584b3810e 100644 --- a/tests/cefclient/client_handler.cpp +++ b/tests/cefclient/client_handler.cpp @@ -216,11 +216,12 @@ bool ClientHandler::OnKeyEvent(CefRefPtr browser, KeyEventType type, int code, int modifiers, - bool isSystemKey) + bool isSystemKey, + bool isAfterJavaScript) { REQUIRE_UI_THREAD(); - if (!m_bFormElementHasFocus && code == 0x20) { + if (isAfterJavaScript && !m_bFormElementHasFocus && code == 0x20) { // Special handling for the space character if a form element does not have // focus. if (type == KEYEVENT_RAWKEYDOWN) { diff --git a/tests/cefclient/client_handler.h b/tests/cefclient/client_handler.h index edf434e79..fe7f9e4a0 100644 --- a/tests/cefclient/client_handler.h +++ b/tests/cefclient/client_handler.h @@ -113,7 +113,8 @@ public: KeyEventType type, int code, int modifiers, - bool isSystemKey) OVERRIDE; + bool isSystemKey, + bool isAfterJavaScript) OVERRIDE; // CefPrintHandler methods. virtual bool GetPrintHeaderFooter(CefRefPtr browser,