diff --git a/include/capi/views/cef_browser_view_capi.h b/include/capi/views/cef_browser_view_capi.h index 71a16313f..b7723b106 100644 --- a/include/capi/views/cef_browser_view_capi.h +++ b/include/capi/views/cef_browser_view_capi.h @@ -63,6 +63,18 @@ typedef struct _cef_browser_view_t { /// struct _cef_browser_t* (CEF_CALLBACK *get_browser)( struct _cef_browser_view_t* self); + + /// + // Sets whether accelerators registered with cef_window_t::SetAccelerator are + // triggered before or after the event is sent to the cef_browser_t. If + // |prefer_accelerators| is true (1) then the matching accelerator will be + // triggered immediately and the event will not be sent to the cef_browser_t. + // If |prefer_accelerators| is false (0) then the matching accelerator will + // only be triggered if the event is not handled by web content or by + // cef_keyboard_handler_t. The default value is false (0). + /// + void (CEF_CALLBACK *set_prefer_accelerators)(struct _cef_browser_view_t* self, + int prefer_accelerators); } cef_browser_view_t; diff --git a/include/capi/views/cef_window_capi.h b/include/capi/views/cef_window_capi.h index f35b5b3c6..80a2bab22 100644 --- a/include/capi/views/cef_window_capi.h +++ b/include/capi/views/cef_window_capi.h @@ -269,6 +269,27 @@ typedef struct _cef_window_t { /// void (CEF_CALLBACK *send_mouse_events)(struct _cef_window_t* self, cef_mouse_button_type_t button, int mouse_down, int mouse_up); + + /// + // Set the keyboard accelerator for the specified |command_id|. |key_code| can + // be any virtual key or character value. cef_window_delegate_t::OnAccelerator + // will be called if the keyboard combination is triggered while this window + // has focus. + /// + void (CEF_CALLBACK *set_accelerator)(struct _cef_window_t* self, + int command_id, int key_code, int shift_pressed, int ctrl_pressed, + int alt_pressed); + + /// + // Remove the keyboard accelerator for the specified |command_id|. + /// + void (CEF_CALLBACK *remove_accelerator)(struct _cef_window_t* self, + int command_id); + + /// + // Remove all keyboard accelerators. + /// + void (CEF_CALLBACK *remove_all_accelerators)(struct _cef_window_t* self); } cef_window_t; diff --git a/include/capi/views/cef_window_delegate_capi.h b/include/capi/views/cef_window_delegate_capi.h index 2144d1b2d..85489b875 100644 --- a/include/capi/views/cef_window_delegate_capi.h +++ b/include/capi/views/cef_window_delegate_capi.h @@ -103,6 +103,22 @@ typedef struct _cef_window_delegate_t { /// int (CEF_CALLBACK *can_close)(struct _cef_window_delegate_t* self, struct _cef_window_t* window); + + /// + // Called when a keyboard accelerator registered with + // cef_window_t::SetAccelerator is triggered. Return true (1) if the + // accelerator was handled or false (0) otherwise. + /// + int (CEF_CALLBACK *on_accelerator)(struct _cef_window_delegate_t* self, + struct _cef_window_t* window, int command_id); + + /// + // Called after all other controls in the window have had a chance to handle + // the event. |event| contains information about the keyboard event. Return + // true (1) if the keyboard event was handled or false (0) otherwise. + /// + int (CEF_CALLBACK *on_key_event)(struct _cef_window_delegate_t* self, + struct _cef_window_t* window, const struct _cef_key_event_t* event); } cef_window_delegate_t; diff --git a/include/views/cef_browser_view.h b/include/views/cef_browser_view.h index 2ae068d96..db622d4d1 100644 --- a/include/views/cef_browser_view.h +++ b/include/views/cef_browser_view.h @@ -74,6 +74,18 @@ class CefBrowserView : public CefView { /// /*--cef()--*/ virtual CefRefPtr GetBrowser() =0; + + /// + // Sets whether accelerators registered with CefWindow::SetAccelerator are + // triggered before or after the event is sent to the CefBrowser. If + // |prefer_accelerators| is true then the matching accelerator will be + // triggered immediately and the event will not be sent to the CefBrowser. If + // |prefer_accelerators| is false then the matching accelerator will only be + // triggered if the event is not handled by web content or by + // CefKeyboardHandler. The default value is false. + /// + /*--cef()--*/ + virtual void SetPreferAccelerators(bool prefer_accelerators) =0; }; #endif // CEF_INCLUDE_VIEWS_CEF_BROWSER_VIEW_H_ diff --git a/include/views/cef_window.h b/include/views/cef_window.h index b6ec638ec..a89ab63a4 100644 --- a/include/views/cef_window.h +++ b/include/views/cef_window.h @@ -289,6 +289,30 @@ class CefWindow : public CefPanel { virtual void SendMouseEvents(cef_mouse_button_type_t button, bool mouse_down, bool mouse_up) =0; + /// + // Set the keyboard accelerator for the specified |command_id|. |key_code| can + // be any virtual key or character value. CefWindowDelegate::OnAccelerator + // will be called if the keyboard combination is triggered while this window + // has focus. + /// + /*--cef()--*/ + virtual void SetAccelerator(int command_id, + int key_code, + bool shift_pressed, + bool ctrl_pressed, + bool alt_pressed) =0; + + /// + // Remove the keyboard accelerator for the specified |command_id|. + /// + /*--cef()--*/ + virtual void RemoveAccelerator(int command_id) =0; + + /// + // Remove all keyboard accelerators. + /// + /*--cef()--*/ + virtual void RemoveAllAccelerators() =0; }; #endif // CEF_INCLUDE_VIEWS_CEF_WINDOW_H_ diff --git a/include/views/cef_window_delegate.h b/include/views/cef_window_delegate.h index 19deae72a..f33137cd9 100644 --- a/include/views/cef_window_delegate.h +++ b/include/views/cef_window_delegate.h @@ -95,6 +95,24 @@ class CefWindowDelegate : public CefPanelDelegate { /// /*--cef()--*/ virtual bool CanClose(CefRefPtr window) { return true; } + + /// + // Called when a keyboard accelerator registered with + // CefWindow::SetAccelerator is triggered. Return true if the accelerator was + // handled or false otherwise. + /// + /*--cef()--*/ + virtual bool OnAccelerator(CefRefPtr window, + int command_id) { return false; } + + /// + // Called after all other controls in the window have had a chance to + // handle the event. |event| contains information about the keyboard event. + // Return true if the keyboard event was handled or false otherwise. + /// + /*--cef()--*/ + virtual bool OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) { return false; } }; #endif // CEF_INCLUDE_VIEWS_CEF_WINDOW_DELEGATE_H_ diff --git a/libcef/browser/views/browser_platform_delegate_views.cc b/libcef/browser/views/browser_platform_delegate_views.cc index 26773c0cf..d4e1a32b1 100644 --- a/libcef/browser/views/browser_platform_delegate_views.cc +++ b/libcef/browser/views/browser_platform_delegate_views.cc @@ -216,7 +216,8 @@ void CefBrowserPlatformDelegateViews::ViewText(const std::string& text) { void CefBrowserPlatformDelegateViews::HandleKeyboardEvent( const content::NativeWebKeyboardEvent& event) { - native_delegate_->HandleKeyboardEvent(event); + // The BrowserView will handle accelerators. + browser_view_->HandleKeyboardEvent(event); } void CefBrowserPlatformDelegateViews::HandleExternalProtocol(const GURL& url) { diff --git a/libcef/browser/views/browser_view_impl.cc b/libcef/browser/views/browser_view_impl.cc index 195707771..3b5f6ea0d 100644 --- a/libcef/browser/views/browser_view_impl.cc +++ b/libcef/browser/views/browser_view_impl.cc @@ -5,9 +5,14 @@ #include "libcef/browser/views/browser_view_impl.h" #include "libcef/browser/browser_host_impl.h" +#include "libcef/browser/browser_util.h" #include "libcef/browser/context.h" +#include "libcef/browser/views/window_impl.h" #include "libcef/browser/thread_util.h" +#include "content/public/browser/native_web_keyboard_event.h" +#include "ui/content_accelerators/accelerator_util.h" + // static CefRefPtr CefBrowserView::CreateBrowserView( CefRefPtr client, @@ -75,11 +80,45 @@ void CefBrowserViewImpl::BrowserDestroyed(CefBrowserHostImpl* browser) { root_view()->SetWebContents(nullptr); } +void CefBrowserViewImpl::HandleKeyboardEvent( + const content::NativeWebKeyboardEvent& event) { + if (!root_view()) + return; + + views::FocusManager* focus_manager = root_view()->GetFocusManager(); + if (!focus_manager) + return; + + if (HandleAccelerator(event, focus_manager)) + return; + + // Give the CefWindowDelegate a chance to handle the event. + CefRefPtr window = + view_util::GetWindowFor(root_view()->GetWidget()); + CefWindowImpl* window_impl = static_cast(window.get()); + if (window_impl) { + CefKeyEvent cef_event; + if (browser_util::GetCefKeyEvent(event, cef_event) && + window_impl->OnKeyEvent(cef_event)) { + return; + } + } + + // Proceed with default native handling. + unhandled_keyboard_event_handler_.HandleKeyboardEvent(event, focus_manager); +} + CefRefPtr CefBrowserViewImpl::GetBrowser() { CEF_REQUIRE_VALID_RETURN(nullptr); return browser_; } +void CefBrowserViewImpl::SetPreferAccelerators(bool prefer_accelerators) { + CEF_REQUIRE_VALID_RETURN_VOID(); + if (root_view()) + root_view()->set_allow_accelerators(prefer_accelerators); +} + void CefBrowserViewImpl::SetBackgroundColor(cef_color_t color) { CEF_REQUIRE_VALID_RETURN_VOID(); ParentClass::SetBackgroundColor(color); @@ -167,3 +206,39 @@ CefBrowserViewView* CefBrowserViewImpl::CreateRootView() { void CefBrowserViewImpl::InitializeRootView() { static_cast(root_view())->Initialize(); } + +bool CefBrowserViewImpl::HandleAccelerator( + const content::NativeWebKeyboardEvent& event, + views::FocusManager* focus_manager) { + // Previous calls to TranslateMessage can generate Char events as well as + // RawKeyDown events, even if the latter triggered an accelerator. In these + // cases, we discard the Char events. + if (event.type() == blink::WebInputEvent::Char && ignore_next_char_event_) { + ignore_next_char_event_ = false; + return true; + } + + // It's necessary to reset this flag, because a RawKeyDown event may not + // always generate a Char event. + ignore_next_char_event_ = false; + + if (event.type() == blink::WebInputEvent::RawKeyDown) { + ui::Accelerator accelerator = + ui::GetAcceleratorFromNativeWebKeyboardEvent(event); + + // This is tricky: we want to set ignore_next_char_event_ if + // ProcessAccelerator returns true. But ProcessAccelerator might delete + // |this| if the accelerator is a "close tab" one. So we speculatively + // set the flag and fix it if no event was handled. + ignore_next_char_event_ = true; + + if (focus_manager->ProcessAccelerator(accelerator)) + return true; + + // ProcessAccelerator didn't handle the accelerator, so we know both + // that |this| is still valid, and that we didn't want to set the flag. + ignore_next_char_event_ = false; + } + + return false; +} diff --git a/libcef/browser/views/browser_view_impl.h b/libcef/browser/views/browser_view_impl.h index 49cb2f06b..332aa7989 100644 --- a/libcef/browser/views/browser_view_impl.h +++ b/libcef/browser/views/browser_view_impl.h @@ -14,6 +14,8 @@ #include "libcef/browser/views/view_impl.h" #include "libcef/browser/views/browser_view_view.h" +#include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" + class CefBrowserViewImpl : public CefViewImpl, @@ -41,8 +43,13 @@ class CefBrowserViewImpl : void BrowserCreated(CefBrowserHostImpl* browser); void BrowserDestroyed(CefBrowserHostImpl* browser); + // Called to handle accelerators when the event is unhandled by the web + // content and the browser client. + void HandleKeyboardEvent(const content::NativeWebKeyboardEvent& event); + // CefBrowserView methods: CefRefPtr GetBrowser() override; + void SetPreferAccelerators(bool prefer_accelerators) override; // CefView methods: CefRefPtr AsBrowserView() override { return this; } @@ -75,10 +82,20 @@ class CefBrowserViewImpl : CefBrowserViewView* CreateRootView() override; void InitializeRootView() override; - std::unique_ptr pending_browser_create_params_; + // Logic extracted from UnhandledKeyboardEventHandler::HandleKeyboardEvent for + // the handling of accelerators. Returns true if the event was handled by the + // accelerator. + bool HandleAccelerator(const content::NativeWebKeyboardEvent& event, + views::FocusManager* focus_manager); + + std::unique_ptr + pending_browser_create_params_; CefRefPtr browser_; + views::UnhandledKeyboardEventHandler unhandled_keyboard_event_handler_; + bool ignore_next_char_event_ = false; + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefBrowserViewImpl); DISALLOW_COPY_AND_ASSIGN(CefBrowserViewImpl); }; diff --git a/libcef/browser/views/button_impl.h b/libcef/browser/views/button_impl.h index 733b01b25..fc9601c7c 100644 --- a/libcef/browser/views/button_impl.h +++ b/libcef/browser/views/button_impl.h @@ -35,6 +35,7 @@ CEF_BUTTON_IMPL_T class CefButtonImpl : public CEF_VIEW_IMPL_D { // CefView methods: CefRefPtr AsButton() override { return this; } + void SetFocusable(bool focusable) override; protected: // Create a new implementation object. @@ -68,4 +69,11 @@ CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetAccessibleName( ParentClass::root_view()->SetAccessibleName(name); } +CEF_BUTTON_IMPL_T void CEF_BUTTON_IMPL_D::SetFocusable( + bool focusable) { + CEF_REQUIRE_VALID_RETURN_VOID(); + ParentClass::root_view()->set_request_focus_on_press(focusable); + ParentClass::SetFocusable(focusable); +} + #endif // CEF_LIBCEF_BROWSER_VIEWS_BUTTON_IMPL_H_ diff --git a/libcef/browser/views/window_impl.cc b/libcef/browser/views/window_impl.cc index fba71433d..5fd52b464 100644 --- a/libcef/browser/views/window_impl.cc +++ b/libcef/browser/views/window_impl.cc @@ -4,6 +4,7 @@ #include "libcef/browser/views/window_impl.h" +#include "libcef/browser/browser_util.h" #include "libcef/browser/thread_util.h" #include "libcef/browser/views/display_impl.h" #include "libcef/browser/views/fill_layout_impl.h" @@ -17,6 +18,7 @@ #if defined(USE_AURA) #include "ui/aura/test/ui_controls_factory_aura.h" +#include "ui/aura/window.h" #include "ui/base/test/ui_controls_aura.h" #if defined(OS_LINUX) #include "ui/views/test/ui_controls_factory_desktop_aurax11.h" @@ -48,6 +50,58 @@ void InitializeUITesting() { } } +#if defined(USE_AURA) + +// This class forwards KeyEvents to the CefWindowImpl associated with a widget. +// This allows KeyEvents to be processed after all other targets. +// Events originating from CefBrowserView will instead be delivered via +// CefBrowserViewImpl::HandleKeyboardEvent. +class CefUnhandledKeyEventHandler : public ui::EventHandler { + public: + CefUnhandledKeyEventHandler(CefWindowImpl* window_impl, + views::Widget* widget) + : window_impl_(window_impl), + widget_(widget), + window_(widget->GetNativeWindow()) { + DCHECK(window_); + window_->AddPostTargetHandler(this); + } + + ~CefUnhandledKeyEventHandler() override { + window_->RemovePostTargetHandler(this); + } + + // Implementation of ui::EventHandler: + void OnKeyEvent(ui::KeyEvent* event) override { + // Give the FocusManager a chance to handle accelerators first. + // Widget::OnKeyEvent would normally call this after all EventHandlers have + // had a shot but we don't want to wait. + if (widget_->GetFocusManager() && + !widget_->GetFocusManager()->OnKeyEvent(*event)) { + event->StopPropagation(); + return; + } + + CefKeyEvent cef_event; + if (browser_util::GetCefKeyEvent(*event, cef_event) && + window_impl_->OnKeyEvent(cef_event)) { + event->StopPropagation(); + } + } + + private: + // Members are guaranteed to outlive this object. + CefWindowImpl* window_impl_; + views::Widget* widget_; + + // |window_| is the event target that is associated with this class. + aura::Window* window_; + + DISALLOW_COPY_AND_ASSIGN(CefUnhandledKeyEventHandler); +}; + +#endif // defined(USE_AURA) + } // namespace // static @@ -315,6 +369,12 @@ bool CefWindowImpl::CanWidgetClose() { return true; } +void CefWindowImpl::OnWindowClosing() { +#if defined(USE_AURA) + unhandled_key_event_handler_.reset(); +#endif +} + void CefWindowImpl::OnWindowViewDeleted() { CancelMenu(); @@ -337,6 +397,27 @@ void CefWindowImpl::MenuClosed(CefRefPtr source) { menu_runner_.reset(nullptr); } +// Will only be called if CanHandleAccelerators() returns true. +bool CefWindowImpl::AcceleratorPressed(const ui::Accelerator& accelerator) { + for (const auto& entry : accelerator_map_) { + if (entry.second == accelerator) + return delegate()->OnAccelerator(this, entry.first); + } + return false; +} + +bool CefWindowImpl::CanHandleAccelerators() const { + if (delegate() && widget_) + return widget_->IsActive(); + return false; +} + +bool CefWindowImpl::OnKeyEvent(const CefKeyEvent& event) { + if (delegate()) + return delegate()->OnKeyEvent(this, event); + return false; +} + void CefWindowImpl::ShowMenu(views::MenuButton* menu_button, CefRefPtr menu_model, const CefPoint& screen_point, @@ -474,6 +555,67 @@ void CefWindowImpl::SendMouseEvents(cef_mouse_button_type_t button, ui_controls::SendMouseEvents(type, state); } +void CefWindowImpl::SetAccelerator(int command_id, + int key_code, + bool shift_pressed, + bool ctrl_pressed, + bool alt_pressed) { + CEF_REQUIRE_VALID_RETURN_VOID(); + if (!widget_) + return; + + AcceleratorMap::const_iterator it = accelerator_map_.find(command_id); + if (it != accelerator_map_.end()) + RemoveAccelerator(command_id); + + int modifiers = 0; + if (shift_pressed) + modifiers |= ui::EF_SHIFT_DOWN; + if (ctrl_pressed) + modifiers |= ui::EF_CONTROL_DOWN; + if (alt_pressed) + modifiers |= ui::EF_ALT_DOWN; + ui::Accelerator accelerator(static_cast(key_code), + modifiers); + + accelerator_map_.insert(std::make_pair(command_id, accelerator)); + + views::FocusManager* focus_manager = widget_->GetFocusManager(); + DCHECK(focus_manager); + focus_manager->RegisterAccelerator( + accelerator, ui::AcceleratorManager::kNormalPriority, this); +} + +void CefWindowImpl::RemoveAccelerator(int command_id) { + CEF_REQUIRE_VALID_RETURN_VOID(); + if (!widget_) + return; + + AcceleratorMap::iterator it = accelerator_map_.find(command_id); + if (it == accelerator_map_.end()) + return; + + ui::Accelerator accelerator = it->second; + + accelerator_map_.erase(it); + + views::FocusManager* focus_manager = widget_->GetFocusManager(); + DCHECK(focus_manager); + focus_manager->UnregisterAccelerator(accelerator, this); +} + +void CefWindowImpl::RemoveAllAccelerators() { + CEF_REQUIRE_VALID_RETURN_VOID(); + if (!widget_) + return; + + accelerator_map_.clear(); + + views::FocusManager* focus_manager = widget_->GetFocusManager(); + DCHECK(focus_manager); + focus_manager->UnregisterAccelerators(this); +} + CefWindowImpl::CefWindowImpl(CefRefPtr delegate) : ParentClass(delegate), widget_(nullptr), @@ -495,6 +637,11 @@ void CefWindowImpl::CreateWidget() { widget_ = root_view()->GetWidget(); DCHECK(widget_); +#if defined(USE_AURA) + unhandled_key_event_handler_ = + base::MakeUnique(this, widget_); +#endif + // The Widget and root View are owned by the native window. Therefore don't // keep an owned reference. std::unique_ptr view_ptr = view_util::PassOwnership(this); diff --git a/libcef/browser/views/window_impl.h b/libcef/browser/views/window_impl.h index 583cd8bb2..4f8598cdf 100644 --- a/libcef/browser/views/window_impl.h +++ b/libcef/browser/views/window_impl.h @@ -6,6 +6,8 @@ #define CEF_LIBCEF_BROWSER_VIEWS_WINDOW_IMPL_H_ #pragma once +#include + #include "include/views/cef_window.h" #include "include/views/cef_window_delegate.h" @@ -13,6 +15,7 @@ #include "libcef/browser/views/panel_impl.h" #include "libcef/browser/views/window_view.h" +#include "ui/base/accelerators/accelerator.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/widget/widget.h" @@ -23,7 +26,8 @@ class MenuButton; class CefWindowImpl : public CefPanelImpl, public CefWindowView::Delegate, - public CefMenuModelImpl::Observer { + public CefMenuModelImpl::Observer, + public ui::AcceleratorTarget { public: typedef CefPanelImpl ParentClass; @@ -70,6 +74,13 @@ class CefWindowImpl : void SendMouseEvents(cef_mouse_button_type_t button, bool mouse_down, bool mouse_up) override; + void SetAccelerator(int command_id, + int key_code, + bool shift_pressed, + bool ctrl_pressed, + bool alt_pressed) override; + void RemoveAccelerator(int command_id) override; + void RemoveAllAccelerators() override; // CefViewAdapter methods: void Detach() override; @@ -91,6 +102,7 @@ class CefWindowImpl : // CefWindowView::Delegate methods: bool CanWidgetClose() override; + void OnWindowClosing() override; void OnWindowViewDeleted() override; // CefMenuModelImpl::Observer methods: @@ -101,6 +113,14 @@ class CefWindowImpl : void GetDebugInfo(base::DictionaryValue* info, bool include_children) override; + // ui::AcceleratorTarget methods: + bool AcceleratorPressed(const ui::Accelerator& accelerator) override; + bool CanHandleAccelerators() const override; + + // Called for key events that have not been handled by other controls in the + // window. Returns true if the event was handled. + bool OnKeyEvent(const CefKeyEvent& event); + void ShowMenu(views::MenuButton* menu_button, CefRefPtr menu_model, const CefPoint& screen_point, @@ -128,6 +148,15 @@ class CefWindowImpl : CefRefPtr menu_model_; std::unique_ptr menu_runner_; + // Map of command_id to accelerator. + typedef std::map AcceleratorMap; + AcceleratorMap accelerator_map_; + +#if defined(USE_AURA) + // Native widget's handler to receive events after the event target. + std::unique_ptr unhandled_key_event_handler_; +#endif + IMPLEMENT_REFCOUNTING_DELETE_ON_UIT(CefWindowImpl); DISALLOW_COPY_AND_ASSIGN(CefWindowImpl); }; diff --git a/libcef/browser/views/window_view.cc b/libcef/browser/views/window_view.cc index e0b6a0654..026cde467 100644 --- a/libcef/browser/views/window_view.cc +++ b/libcef/browser/views/window_view.cc @@ -368,6 +368,10 @@ gfx::ImageSkia CefWindowView::GetWindowAppIcon() { GetForced1xScaleRepresentation(GetDisplay().device_scale_factor()); } +void CefWindowView::WindowClosing() { + window_delegate_->OnWindowClosing(); +} + views::View* CefWindowView::GetContentsView() { // |this| will be the "Contents View" hosted by the Widget via ClientView and // RootView. diff --git a/libcef/browser/views/window_view.h b/libcef/browser/views/window_view.h index bdf93c7af..a65cdae57 100644 --- a/libcef/browser/views/window_view.h +++ b/libcef/browser/views/window_view.h @@ -29,6 +29,9 @@ class CefWindowView : // Returns true to signal that the Widget can be closed. virtual bool CanWidgetClose() = 0; + // Called when the underlying platform window is closing. + virtual void OnWindowClosing() = 0; + // Called when the WindowView is about to be deleted. virtual void OnWindowViewDeleted() = 0; @@ -58,6 +61,7 @@ class CefWindowView : base::string16 GetWindowTitle() const override; gfx::ImageSkia GetWindowIcon() override; gfx::ImageSkia GetWindowAppIcon() override; + void WindowClosing() override; views::View* GetContentsView() override; views::ClientView* CreateClientView(views::Widget* widget) override; views::NonClientFrameView* CreateNonClientFrameView( diff --git a/libcef_dll/cpptoc/views/browser_view_cpptoc.cc b/libcef_dll/cpptoc/views/browser_view_cpptoc.cc index 21cf0e085..23667e731 100644 --- a/libcef_dll/cpptoc/views/browser_view_cpptoc.cc +++ b/libcef_dll/cpptoc/views/browser_view_cpptoc.cc @@ -92,6 +92,19 @@ cef_browser_t* CEF_CALLBACK browser_view_get_browser( return CefBrowserCppToC::Wrap(_retval); } +void CEF_CALLBACK browser_view_set_prefer_accelerators( + struct _cef_browser_view_t* self, int prefer_accelerators) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefBrowserViewCppToC::Get(self)->SetPreferAccelerators( + prefer_accelerators?true:false); +} + cef_browser_view_t* CEF_CALLBACK browser_view_as_browser_view( struct _cef_view_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -905,6 +918,7 @@ int CEF_CALLBACK browser_view_convert_point_from_view(struct _cef_view_t* self, CefBrowserViewCppToC::CefBrowserViewCppToC() { GetStruct()->get_browser = browser_view_get_browser; + GetStruct()->set_prefer_accelerators = browser_view_set_prefer_accelerators; GetStruct()->base.as_browser_view = browser_view_as_browser_view; GetStruct()->base.as_button = browser_view_as_button; GetStruct()->base.as_panel = browser_view_as_panel; diff --git a/libcef_dll/cpptoc/views/window_cpptoc.cc b/libcef_dll/cpptoc/views/window_cpptoc.cc index 83e2fdfe8..d65cf593a 100644 --- a/libcef_dll/cpptoc/views/window_cpptoc.cc +++ b/libcef_dll/cpptoc/views/window_cpptoc.cc @@ -518,6 +518,48 @@ void CEF_CALLBACK window_send_mouse_events(struct _cef_window_t* self, mouse_up?true:false); } +void CEF_CALLBACK window_set_accelerator(struct _cef_window_t* self, + int command_id, int key_code, int shift_pressed, int ctrl_pressed, + int alt_pressed) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefWindowCppToC::Get(self)->SetAccelerator( + command_id, + key_code, + shift_pressed?true:false, + ctrl_pressed?true:false, + alt_pressed?true:false); +} + +void CEF_CALLBACK window_remove_accelerator(struct _cef_window_t* self, + int command_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefWindowCppToC::Get(self)->RemoveAccelerator( + command_id); +} + +void CEF_CALLBACK window_remove_all_accelerators(struct _cef_window_t* self) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return; + + // Execute + CefWindowCppToC::Get(self)->RemoveAllAccelerators(); +} + struct _cef_window_t* CEF_CALLBACK window_as_window(struct _cef_panel_t* self) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -1550,6 +1592,9 @@ CefWindowCppToC::CefWindowCppToC() { GetStruct()->send_key_press = window_send_key_press; GetStruct()->send_mouse_move = window_send_mouse_move; GetStruct()->send_mouse_events = window_send_mouse_events; + GetStruct()->set_accelerator = window_set_accelerator; + GetStruct()->remove_accelerator = window_remove_accelerator; + GetStruct()->remove_all_accelerators = window_remove_all_accelerators; GetStruct()->base.as_window = window_as_window; GetStruct()->base.set_to_fill_layout = window_set_to_fill_layout; GetStruct()->base.set_to_box_layout = window_set_to_box_layout; diff --git a/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc b/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc index 5f1679d16..81114433e 100644 --- a/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc +++ b/libcef_dll/cpptoc/views/window_delegate_cpptoc.cc @@ -153,6 +153,59 @@ int CEF_CALLBACK window_delegate_can_close(struct _cef_window_delegate_t* self, return _retval; } +int CEF_CALLBACK window_delegate_on_accelerator( + struct _cef_window_delegate_t* self, cef_window_t* window, + int command_id) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: window; type: refptr_diff + DCHECK(window); + if (!window) + return 0; + + // Execute + bool _retval = CefWindowDelegateCppToC::Get(self)->OnAccelerator( + CefWindowCToCpp::Wrap(window), + command_id); + + // Return type: bool + return _retval; +} + +int CEF_CALLBACK window_delegate_on_key_event( + struct _cef_window_delegate_t* self, cef_window_t* window, + const struct _cef_key_event_t* event) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + DCHECK(self); + if (!self) + return 0; + // Verify param: window; type: refptr_diff + DCHECK(window); + if (!window) + return 0; + // Verify param: event; type: struct_byref_const + DCHECK(event); + if (!event) + return 0; + + // Translate param: event; type: struct_byref_const + CefKeyEvent eventObj; + if (event) + eventObj.Set(*event, false); + + // Execute + bool _retval = CefWindowDelegateCppToC::Get(self)->OnKeyEvent( + CefWindowCToCpp::Wrap(window), + eventObj); + + // Return type: bool + return _retval; +} + cef_size_t CEF_CALLBACK window_delegate_get_preferred_size( struct _cef_view_delegate_t* self, cef_view_t* view) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING @@ -301,6 +354,8 @@ CefWindowDelegateCppToC::CefWindowDelegateCppToC() { GetStruct()->can_maximize = window_delegate_can_maximize; GetStruct()->can_minimize = window_delegate_can_minimize; GetStruct()->can_close = window_delegate_can_close; + GetStruct()->on_accelerator = window_delegate_on_accelerator; + GetStruct()->on_key_event = window_delegate_on_key_event; GetStruct()->base.base.get_preferred_size = window_delegate_get_preferred_size; GetStruct()->base.base.get_minimum_size = window_delegate_get_minimum_size; diff --git a/libcef_dll/ctocpp/views/browser_view_ctocpp.cc b/libcef_dll/ctocpp/views/browser_view_ctocpp.cc index 5ec9d047f..0aa2470ac 100644 --- a/libcef_dll/ctocpp/views/browser_view_ctocpp.cc +++ b/libcef_dll/ctocpp/views/browser_view_ctocpp.cc @@ -81,6 +81,18 @@ CefRefPtr CefBrowserViewCToCpp::GetBrowser() { return CefBrowserCToCpp::Wrap(_retval); } +void CefBrowserViewCToCpp::SetPreferAccelerators(bool prefer_accelerators) { + cef_browser_view_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, set_prefer_accelerators)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->set_prefer_accelerators(_struct, + prefer_accelerators); +} + CefRefPtr CefBrowserViewCToCpp::AsBrowserView() { cef_view_t* _struct = reinterpret_cast(GetStruct()); if (CEF_MEMBER_MISSING(_struct, as_browser_view)) diff --git a/libcef_dll/ctocpp/views/browser_view_ctocpp.h b/libcef_dll/ctocpp/views/browser_view_ctocpp.h index e72f54ce6..cf8ec5c45 100644 --- a/libcef_dll/ctocpp/views/browser_view_ctocpp.h +++ b/libcef_dll/ctocpp/views/browser_view_ctocpp.h @@ -32,6 +32,7 @@ class CefBrowserViewCToCpp // CefBrowserView methods. CefRefPtr GetBrowser() OVERRIDE; + void SetPreferAccelerators(bool prefer_accelerators) OVERRIDE; // CefView methods. CefRefPtr AsBrowserView() OVERRIDE; diff --git a/libcef_dll/ctocpp/views/window_ctocpp.cc b/libcef_dll/ctocpp/views/window_ctocpp.cc index 40505fcb0..857857cc8 100644 --- a/libcef_dll/ctocpp/views/window_ctocpp.cc +++ b/libcef_dll/ctocpp/views/window_ctocpp.cc @@ -505,6 +505,46 @@ void CefWindowCToCpp::SendMouseEvents(cef_mouse_button_type_t button, mouse_up); } +void CefWindowCToCpp::SetAccelerator(int command_id, int key_code, + bool shift_pressed, bool ctrl_pressed, bool alt_pressed) { + cef_window_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, set_accelerator)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->set_accelerator(_struct, + command_id, + key_code, + shift_pressed, + ctrl_pressed, + alt_pressed); +} + +void CefWindowCToCpp::RemoveAccelerator(int command_id) { + cef_window_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, remove_accelerator)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->remove_accelerator(_struct, + command_id); +} + +void CefWindowCToCpp::RemoveAllAccelerators() { + cef_window_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, remove_all_accelerators)) + return; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Execute + _struct->remove_all_accelerators(_struct); +} + CefRefPtr CefWindowCToCpp::AsWindow() { cef_panel_t* _struct = reinterpret_cast(GetStruct()); if (CEF_MEMBER_MISSING(_struct, as_window)) diff --git a/libcef_dll/ctocpp/views/window_ctocpp.h b/libcef_dll/ctocpp/views/window_ctocpp.h index 6e7593cd5..ba37ad074 100644 --- a/libcef_dll/ctocpp/views/window_ctocpp.h +++ b/libcef_dll/ctocpp/views/window_ctocpp.h @@ -68,6 +68,10 @@ class CefWindowCToCpp void SendMouseMove(int screen_x, int screen_y) OVERRIDE; void SendMouseEvents(cef_mouse_button_type_t button, bool mouse_down, bool mouse_up) OVERRIDE; + void SetAccelerator(int command_id, int key_code, bool shift_pressed, + bool ctrl_pressed, bool alt_pressed) OVERRIDE; + void RemoveAccelerator(int command_id) OVERRIDE; + void RemoveAllAccelerators() OVERRIDE; // CefPanel methods. CefRefPtr AsWindow() OVERRIDE; diff --git a/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc b/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc index 93da33c62..6b66986d8 100644 --- a/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc +++ b/libcef_dll/ctocpp/views/window_delegate_ctocpp.cc @@ -151,6 +151,50 @@ bool CefWindowDelegateCToCpp::CanClose(CefRefPtr window) { return _retval?true:false; } +bool CefWindowDelegateCToCpp::OnAccelerator(CefRefPtr window, + int command_id) { + cef_window_delegate_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_accelerator)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: window; type: refptr_diff + DCHECK(window.get()); + if (!window.get()) + return false; + + // Execute + int _retval = _struct->on_accelerator(_struct, + CefWindowCppToC::Wrap(window), + command_id); + + // Return type: bool + return _retval?true:false; +} + +bool CefWindowDelegateCToCpp::OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) { + cef_window_delegate_t* _struct = GetStruct(); + if (CEF_MEMBER_MISSING(_struct, on_key_event)) + return false; + + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: window; type: refptr_diff + DCHECK(window.get()); + if (!window.get()) + return false; + + // Execute + int _retval = _struct->on_key_event(_struct, + CefWindowCppToC::Wrap(window), + &event); + + // Return type: bool + return _retval?true:false; +} + CefSize CefWindowDelegateCToCpp::GetPreferredSize(CefRefPtr view) { cef_view_delegate_t* _struct = reinterpret_cast( GetStruct()); diff --git a/libcef_dll/ctocpp/views/window_delegate_ctocpp.h b/libcef_dll/ctocpp/views/window_delegate_ctocpp.h index 81a3be8df..9d0ef514c 100644 --- a/libcef_dll/ctocpp/views/window_delegate_ctocpp.h +++ b/libcef_dll/ctocpp/views/window_delegate_ctocpp.h @@ -40,6 +40,9 @@ class CefWindowDelegateCToCpp bool CanMaximize(CefRefPtr window) override; bool CanMinimize(CefRefPtr window) override; bool CanClose(CefRefPtr window) override; + bool OnAccelerator(CefRefPtr window, int command_id) override; + bool OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) override; // CefPanelDelegate methods. diff --git a/tests/cefclient/browser/client_handler.cc b/tests/cefclient/browser/client_handler.cc index 4c32d6b38..c073050a3 100644 --- a/tests/cefclient/browser/client_handler.cc +++ b/tests/cefclient/browser/client_handler.cc @@ -462,6 +462,12 @@ void ClientHandler::OnDraggableRegionsChanged( NotifyDraggableRegions(regions); } +void ClientHandler::OnTakeFocus(CefRefPtr browser, bool next) { + CEF_REQUIRE_UI_THREAD(); + + NotifyTakeFocus(next); +} + bool ClientHandler::OnRequestGeolocationPermission( CefRefPtr browser, const CefString& requesting_url, @@ -990,6 +996,18 @@ void ClientHandler::NotifyDraggableRegions( delegate_->OnSetDraggableRegions(regions); } +void ClientHandler::NotifyTakeFocus(bool next) { + if (!CURRENTLY_ON_MAIN_THREAD()) { + // Execute this method on the main thread. + MAIN_POST_CLOSURE( + base::Bind(&ClientHandler::NotifyTakeFocus, this, next)); + return; + } + + if (delegate_) + delegate_->OnTakeFocus(next); +} + void ClientHandler::BuildTestMenu(CefRefPtr model) { if (model->GetCount() > 0) model->AddSeparator(); diff --git a/tests/cefclient/browser/client_handler.h b/tests/cefclient/browser/client_handler.h index 3745ff556..dee559931 100644 --- a/tests/cefclient/browser/client_handler.h +++ b/tests/cefclient/browser/client_handler.h @@ -30,6 +30,7 @@ class ClientHandler : public CefClient, public CefDisplayHandler, public CefDownloadHandler, public CefDragHandler, + public CefFocusHandler, public CefGeolocationHandler, public CefKeyboardHandler, public CefLifeSpanHandler, @@ -70,6 +71,9 @@ class ClientHandler : public CefClient, virtual void OnSetDraggableRegions( const std::vector& regions) = 0; + // Set focus to the next/previous control. + virtual void OnTakeFocus(bool next) {} + protected: virtual ~Delegate() {} }; @@ -99,6 +103,9 @@ class ClientHandler : public CefClient, CefRefPtr GetDragHandler() OVERRIDE { return this; } + CefRefPtr GetFocusHandler() OVERRIDE { + return this; + } CefRefPtr GetGeolocationHandler() OVERRIDE { return this; } @@ -168,11 +175,13 @@ class ClientHandler : public CefClient, bool OnDragEnter(CefRefPtr browser, CefRefPtr dragData, CefDragHandler::DragOperationsMask mask) OVERRIDE; - void OnDraggableRegionsChanged( CefRefPtr browser, const std::vector& regions) OVERRIDE; + // CefFocusHandler methods + void OnTakeFocus(CefRefPtr browser, bool next) OVERRIDE; + // CefGeolocationHandler methods bool OnRequestGeolocationPermission( CefRefPtr browser, @@ -323,6 +332,7 @@ class ClientHandler : public CefClient, bool canGoForward); void NotifyDraggableRegions( const std::vector& regions); + void NotifyTakeFocus(bool next); // Test context menu creation. void BuildTestMenu(CefRefPtr model); diff --git a/tests/cefclient/browser/root_window_views.cc b/tests/cefclient/browser/root_window_views.cc index 56b6c2a94..540679944 100644 --- a/tests/cefclient/browser/root_window_views.cc +++ b/tests/cefclient/browser/root_window_views.cc @@ -329,6 +329,18 @@ void RootWindowViews::OnSetDraggableRegions( window_->SetDraggableRegions(regions); } +void RootWindowViews::OnTakeFocus(bool next) { + if (!CefCurrentlyOn(TID_UI)) { + // Execute this method on the UI thread. + CefPostTask(TID_UI, + base::Bind(&RootWindowViews::OnTakeFocus, this, next)); + return; + } + + if (window_) + window_->TakeFocus(next); +} + void RootWindowViews::CreateClientHandler(const std::string& url) { DCHECK(!client_handler_); diff --git a/tests/cefclient/browser/root_window_views.h b/tests/cefclient/browser/root_window_views.h index 0f07a428c..a184f6b0c 100644 --- a/tests/cefclient/browser/root_window_views.h +++ b/tests/cefclient/browser/root_window_views.h @@ -73,6 +73,7 @@ class RootWindowViews : public RootWindow, bool canGoForward) OVERRIDE; void OnSetDraggableRegions( const std::vector& regions) OVERRIDE; + void OnTakeFocus(bool next) OVERRIDE; private: void CreateClientHandler(const std::string& url); diff --git a/tests/cefclient/browser/views_window.cc b/tests/cefclient/browser/views_window.cc index 45a6c73ab..87881154e 100644 --- a/tests/cefclient/browser/views_window.cc +++ b/tests/cefclient/browser/views_window.cc @@ -18,6 +18,7 @@ #if !defined(OS_WIN) #define VK_RETURN 0x0D +#define VK_MENU 0x12 // ALT key. #endif namespace client { @@ -204,6 +205,21 @@ void ViewsWindow::SetDraggableRegions( window_->SetDraggableRegions(window_regions); } +void ViewsWindow::TakeFocus(bool next) { + CEF_REQUIRE_UI_THREAD(); + + if (!window_ || !with_controls_) + return; + + if (next) { + // Focus is moving forwards (tab). Give focus to the URL textfield. + window_->GetViewForID(ID_URL_TEXTFIELD)->RequestFocus(); + } else { + // Focus is moving backwards (tab+shift). Give focus to the menu button. + window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus(); + } +} + bool ViewsWindow::OnPopupBrowserViewCreated( CefRefPtr browser_view, CefRefPtr popup_browser_view, @@ -345,6 +361,9 @@ void ViewsWindow::OnWindowCreated(CefRefPtr window) { if (with_controls_) { // Add the BrowserView and other controls to the Window. AddControls(); + + // Add keyboard accelerators to the Window. + AddAccelerators(); } else { // Add the BrowserView as the only child of the Window. window_->AddChildView(browser_view_); @@ -386,6 +405,33 @@ bool ViewsWindow::IsFrameless(CefRefPtr window) { return frameless_; } +bool ViewsWindow::OnAccelerator(CefRefPtr window, int command_id) { + CEF_REQUIRE_UI_THREAD(); + + if (command_id == ID_QUIT) { + delegate_->OnExit(); + return true; + } + + return false; +} + +bool ViewsWindow::OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) { + CEF_REQUIRE_UI_THREAD(); + + if (!window_ || !with_controls_) + return false; + + if (event.type == KEYEVENT_RAWKEYDOWN && event.windows_key_code == VK_MENU) { + // ALT key is pressed. Give focus to the menu button. + window_->GetViewForID(ID_MENU_BUTTON)->RequestFocus(); + return true; + } + + return false; +} + CefSize ViewsWindow::GetMinimumSize(CefRefPtr view) { CEF_REQUIRE_UI_THREAD(); @@ -420,7 +466,7 @@ void ViewsWindow::CreateMenuModel() { menu_model_ = CefMenuModel::CreateMenuModel(this); // Create the test menu. - CefRefPtr test_menu = menu_model_->AddSubMenu(0, "Tests"); + CefRefPtr test_menu = menu_model_->AddSubMenu(0, "&Tests"); test_menu->AddItem(ID_TESTS_GETSOURCE, "Get Source"); test_menu->AddItem(ID_TESTS_GETTEXT, "Get Text"); test_menu->AddItem(ID_TESTS_WINDOW_NEW, "New Window"); @@ -436,7 +482,10 @@ void ViewsWindow::CreateMenuModel() { test_menu->AddItem(ID_TESTS_PRINT_TO_PDF, "Print to PDF"); test_menu->AddItem(ID_TESTS_OTHER_TESTS, "Other Tests"); - menu_model_->AddItem(ID_QUIT, "Exit"); + menu_model_->AddItem(ID_QUIT, "E&xit"); + + // Show the accelerator shortcut text in the menu. + menu_model_->SetAcceleratorAt(1, 'X', false, false, true); } CefRefPtr ViewsWindow::CreateBrowseButton( @@ -473,6 +522,8 @@ void ViewsWindow::AddControls() { menu_button->SetImage(CEF_BUTTON_STATE_NORMAL, LoadImageIcon("menu_icon")); // Override the default minimum size. menu_button->SetMinimumSize(CefSize(0, 0)); + // Menu button must be focusable for keyboard access to work. + menu_button->SetFocusable(true); // Create the top panel. CefRefPtr top_panel = CefPanel::CreatePanel(NULL); @@ -524,6 +575,15 @@ void ViewsWindow::AddControls() { minimum_window_size_ = CefSize(min_width, min_height); } +void ViewsWindow::AddAccelerators() { + // Trigger accelerators without first forwarding to web content. + browser_view_->SetPreferAccelerators(true); + + // Specify the accelerators to handle. OnAccelerator will be called when the + // accelerator is triggered. + window_->SetAccelerator(ID_QUIT, 'X', false, false, true); +} + void ViewsWindow::EnableView(int id, bool enable) { if (!window_) return; diff --git a/tests/cefclient/browser/views_window.h b/tests/cefclient/browser/views_window.h index cb5b9ca1e..39f3d07a0 100644 --- a/tests/cefclient/browser/views_window.h +++ b/tests/cefclient/browser/views_window.h @@ -83,6 +83,7 @@ class ViewsWindow : public CefBrowserViewDelegate, bool canGoBack, bool canGoForward); void SetDraggableRegions(const std::vector& regions); + void TakeFocus(bool next); // CefBrowserViewDelegate methods: bool OnPopupBrowserViewCreated( @@ -111,6 +112,9 @@ class ViewsWindow : public CefBrowserViewDelegate, void OnWindowDestroyed(CefRefPtr window) OVERRIDE; bool IsFrameless(CefRefPtr window) OVERRIDE; bool CanClose(CefRefPtr window) OVERRIDE; + bool OnAccelerator(CefRefPtr window, int command_id) OVERRIDE; + bool OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) OVERRIDE; // CefViewDelegate methods: CefSize GetMinimumSize(CefRefPtr view) override; @@ -131,6 +135,9 @@ class ViewsWindow : public CefBrowserViewDelegate, // Add controls to the Window. void AddControls(); + // Add keyboard accelerators to the Window. + void AddAccelerators(); + // Enable or disable a view by |id|. void EnableView(int id, bool enable); diff --git a/tests/ceftests/views/button_unittest.cc b/tests/ceftests/views/button_unittest.cc index 65b123885..b78025da4 100644 --- a/tests/ceftests/views/button_unittest.cc +++ b/tests/ceftests/views/button_unittest.cc @@ -250,10 +250,13 @@ void LabelButtonClick(CefRefPtr event, bool with_button_text, bool with_button_image, bool with_window_frame) { - TestWindowDelegate::RunTest(event, + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunLabelButtonClick, with_button_frame, with_button_text, - with_button_image), - !with_window_frame, false); + with_button_image); + config.frameless = !with_window_frame; + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void LabelButtonClickFramedWithTextWithImageFramedWindowImpl( @@ -465,10 +468,13 @@ void MenuButtonClick(CefRefPtr event, bool with_button_menu_marker, bool with_button_image, bool with_window_frame) { - TestWindowDelegate::RunTest(event, + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunMenuButtonClick, with_button_frame, with_button_text, - with_button_menu_marker, with_button_image), - !with_window_frame, false); + with_button_menu_marker, with_button_image); + config.frameless = !with_window_frame; + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void MenuButtonClickFramedWithTextWithMarkerWithImageFramedWindowImpl( diff --git a/tests/ceftests/views/scroll_view_unittest.cc b/tests/ceftests/views/scroll_view_unittest.cc index 87d911930..5deacb2db 100644 --- a/tests/ceftests/views/scroll_view_unittest.cc +++ b/tests/ceftests/views/scroll_view_unittest.cc @@ -133,8 +133,9 @@ void RunScrollViewLayout(bool with_delegate, void ScrollViewLayout(CefRefPtr event, bool with_delegate) { - TestWindowDelegate::RunTest(event, - base::Bind(RunScrollViewLayout, with_delegate), false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunScrollViewLayout, with_delegate); + TestWindowDelegate::RunTest(event, config); } void ScrollViewLayoutWithDelegateImpl(CefRefPtr event) { diff --git a/tests/ceftests/views/test_window_delegate.cc b/tests/ceftests/views/test_window_delegate.cc index d8780552f..29b13a42a 100644 --- a/tests/ceftests/views/test_window_delegate.cc +++ b/tests/ceftests/views/test_window_delegate.cc @@ -23,13 +23,9 @@ const int TestWindowDelegate::kWSize = 400; // static void TestWindowDelegate::RunTest(CefRefPtr event, - const WindowTest& window_test, - bool frameless, - bool close_window, - int window_size) { + const Config& config) { CefWindow::CreateTopLevelWindow( - new TestWindowDelegate(event, window_test, frameless, close_window, - window_size)); + new TestWindowDelegate(event, config)); } void TestWindowDelegate::OnWindowCreated(CefRefPtr window) { @@ -60,22 +56,22 @@ void TestWindowDelegate::OnWindowCreated(CefRefPtr window) { // Size will come from GetPreferredSize() on initial Window creation. EXPECT_TRUE(got_get_preferred_size_); CefRect client_bounds = window->GetClientAreaBoundsInScreen(); - if (frameless_) { - EXPECT_EQ(window_size_, client_bounds.width); - EXPECT_EQ(window_size_, client_bounds.height); + if (config_.frameless) { + EXPECT_EQ(config_.window_size, client_bounds.width); + EXPECT_EQ(config_.window_size, client_bounds.height); } else { // Client area bounds calculation might have off-by-one errors on Windows // due to non-client frame size being calculated internally in pixels and // then converted to DIPs. See http://crbug.com/602692. - EXPECT_TRUE(abs(client_bounds.width - window_size_) <= 1); - EXPECT_TRUE(abs(client_bounds.height - window_size_) <= 1); + EXPECT_TRUE(abs(client_bounds.width - config_.window_size) <= 1); + EXPECT_TRUE(abs(client_bounds.height - config_.window_size) <= 1); } - // Run the requested test. - if (!window_test_.is_null()) - window_test_.Run(window); + // Run the callback. + if (!config_.on_window_created.is_null()) + config_.on_window_created.Run(window); - if (close_window_) { + if (config_.close_window) { // Close the window asynchronously. CefPostTask(TID_UI, base::Bind(&TestWindowDelegate::OnCloseWindow, this)); @@ -97,6 +93,10 @@ void TestWindowDelegate::OnWindowDestroyed(CefRefPtr window) { EXPECT_FALSE(window->IsVisible()); EXPECT_FALSE(window->IsDrawn()); + // Run the callback. + if (!config_.on_window_destroyed.is_null()) + config_.on_window_destroyed.Run(window); + window_ = nullptr; // Don't execute the timeout callback. @@ -104,24 +104,32 @@ void TestWindowDelegate::OnWindowDestroyed(CefRefPtr window) { } bool TestWindowDelegate::IsFrameless(CefRefPtr window) { - return frameless_; + return config_.frameless; } CefSize TestWindowDelegate::GetPreferredSize(CefRefPtr view) { got_get_preferred_size_ = true; - return CefSize(window_size_, window_size_); + return CefSize(config_.window_size, config_.window_size); +} + +bool TestWindowDelegate::OnAccelerator(CefRefPtr window, + int command_id) { + if (!config_.on_accelerator.is_null()) + return config_.on_accelerator.Run(window_, command_id); + return false; +} + +bool TestWindowDelegate::OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) { + if (!config_.on_key_event.is_null()) + return config_.on_key_event.Run(window_, event); + return false; } TestWindowDelegate::TestWindowDelegate(CefRefPtr event, - const WindowTest& window_test, - bool frameless, - bool close_window, - int window_size) + const Config& config) : event_(event), - window_test_(window_test), - frameless_(frameless), - close_window_(close_window), - window_size_(window_size), + config_(config), weak_ptr_factory_(this) { } diff --git a/tests/ceftests/views/test_window_delegate.h b/tests/ceftests/views/test_window_delegate.h index 1a8cf297c..bff26d72d 100644 --- a/tests/ceftests/views/test_window_delegate.h +++ b/tests/ceftests/views/test_window_delegate.h @@ -14,7 +14,21 @@ class TestWindowDelegate : public CefWindowDelegate { static const int kWSize; // Test execution callback. - typedef base::Callback)> WindowTest; + typedef base::Callback)> OnWindowCreatedCallback; + typedef base::Callback)> OnWindowDestroyedCallback; + typedef base::Callback,int)> OnAcceleratorCallback; + typedef base::Callback,const CefKeyEvent&)> + OnKeyEventCallback; + + struct Config { + OnWindowCreatedCallback on_window_created; + OnWindowDestroyedCallback on_window_destroyed; + OnAcceleratorCallback on_accelerator; + OnKeyEventCallback on_key_event; + bool frameless = false; + bool close_window = true; + int window_size = kWSize; + }; // Creates a Window with a new TestWindowDelegate instance and executes // |window_test| after the Window is created. |event| will be signaled once @@ -23,33 +37,27 @@ class TestWindowDelegate : public CefWindowDelegate { // immediately after |window_test| returns. Otherwise, the caller is // responsible for closing the Window passed to |window_test|. static void RunTest(CefRefPtr event, - const WindowTest& window_test, - bool frameless = false, - bool close_window = true, - int window_size = kWSize); + const Config& config); // CefWindowDelegate methods: void OnWindowCreated(CefRefPtr window) override; void OnWindowDestroyed(CefRefPtr window) override; bool IsFrameless(CefRefPtr window) override; CefSize GetPreferredSize(CefRefPtr view) override; + bool OnAccelerator(CefRefPtr window, int command_id) override; + bool OnKeyEvent(CefRefPtr window, + const CefKeyEvent& event) override; private: TestWindowDelegate(CefRefPtr event, - const WindowTest& window_test, - bool frameless, - bool close_window, - int window_size); + const Config& config); ~TestWindowDelegate() override; void OnCloseWindow(); void OnTimeoutWindow(); CefRefPtr event_; - WindowTest window_test_; - bool frameless_; - bool close_window_; - int window_size_; + Config config_; CefRefPtr window_; diff --git a/tests/ceftests/views/textfield_unittest.cc b/tests/ceftests/views/textfield_unittest.cc index 1fda6094e..7008af62c 100644 --- a/tests/ceftests/views/textfield_unittest.cc +++ b/tests/ceftests/views/textfield_unittest.cc @@ -278,8 +278,10 @@ void RunTextfieldKeyEvent(CefRefPtr window) { } void TextfieldKeyEventImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, - base::Bind(RunTextfieldKeyEvent), false, false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunTextfieldKeyEvent); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } } // namespace diff --git a/tests/ceftests/views/window_unittest.cc b/tests/ceftests/views/window_unittest.cc index 0bf3a8c1c..d8d70a349 100644 --- a/tests/ceftests/views/window_unittest.cc +++ b/tests/ceftests/views/window_unittest.cc @@ -14,6 +14,10 @@ #define WINDOW_TEST_ASYNC(name) UI_THREAD_TEST_ASYNC(ViewsWindowTest, name) +#if !defined(OS_WIN) +#define VK_MENU 0x12 // ALT key. +#endif + namespace { // Window state change delay in MS. @@ -33,11 +37,14 @@ void ExpectCloseRects(const CefRect& expected, } void WindowCreateImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, TestWindowDelegate::WindowTest(), false); + TestWindowDelegate::Config config; + TestWindowDelegate::RunTest(event, config); } void WindowCreateFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, TestWindowDelegate::WindowTest(), true); + TestWindowDelegate::Config config; + config.frameless = true; + TestWindowDelegate::RunTest(event, config); } void RunWindowShowHide(CefRefPtr window) { @@ -52,11 +59,16 @@ void RunWindowShowHide(CefRefPtr window) { } void WindowShowHideImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowShowHide), false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowShowHide); + TestWindowDelegate::RunTest(event, config); } void WindowShowHideFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowShowHide), true); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowShowHide); + config.frameless = true; + TestWindowDelegate::RunTest(event, config); } const int kWPanel1ID = 1; @@ -180,11 +192,16 @@ void RunWindowLayoutAndCoords(CefRefPtr window) { } void WindowLayoutAndCoordsImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowLayoutAndCoords), false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowLayoutAndCoords); + TestWindowDelegate::RunTest(event, config); } void WindowLayoutAndCoordsFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowLayoutAndCoords), true); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowLayoutAndCoords); + config.frameless = true; + TestWindowDelegate::RunTest(event, config); } void VerifyRestore(CefRefPtr window) { @@ -223,13 +240,18 @@ void RunWindowMaximize(CefRefPtr window) { } void WindowMaximizeImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowMaximize), false, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowMaximize); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void WindowMaximizeFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowMaximize), true, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowMaximize); + config.frameless = true; + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void VerifyMinimize(CefRefPtr window) { @@ -260,13 +282,18 @@ void RunWindowMinimize(CefRefPtr window) { } void WindowMinimizeImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowMinimize), false, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowMinimize); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void WindowMinimizeFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowMinimize), true, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowMinimize); + config.frameless = true; + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void VerifyFullscreenExit(CefRefPtr window) { @@ -307,13 +334,18 @@ void RunWindowFullscreen(CefRefPtr window) { } void WindowFullscreenImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowFullscreen), false, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowFullscreen); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void WindowFullscreenFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowFullscreen), true, - false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowFullscreen); + config.frameless = true; + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } void RunWindowIcon(CefRefPtr window) { @@ -333,11 +365,113 @@ void RunWindowIcon(CefRefPtr window) { } void WindowIconImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowIcon), false); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowIcon); + TestWindowDelegate::RunTest(event, config); } void WindowIconFramelessImpl(CefRefPtr event) { - TestWindowDelegate::RunTest(event, base::Bind(RunWindowIcon), true); + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowIcon); + config.frameless = true; + TestWindowDelegate::RunTest(event, config); +} + +const int kChar = 'A'; +const int kCloseWindowId = 2; +bool got_accelerator; +int got_key_event_alt_count; +bool got_key_event_char; + +void TriggerAccelerator(CefRefPtr window) { + window->SendKeyPress(kChar, EVENTFLAG_ALT_DOWN); +} + +bool OnKeyEvent(CefRefPtr window, const CefKeyEvent& event) { + if (event.type != KEYEVENT_RAWKEYDOWN) + return false; + + if (event.windows_key_code == VK_MENU) { + // First we get the ALT key press in all cases. + EXPECT_FALSE(got_key_event_char); + if (got_key_event_alt_count == 0) + EXPECT_FALSE(got_accelerator); + else + EXPECT_TRUE(got_accelerator); + + EXPECT_EQ(event.modifiers, EVENTFLAG_ALT_DOWN); + got_key_event_alt_count++; + } else if (event.windows_key_code == kChar) { + // Then we get the char key press with the ALT modifier if the accelerator + // isn't registered. + EXPECT_TRUE(got_accelerator); + EXPECT_EQ(got_key_event_alt_count, 2); + EXPECT_FALSE(got_key_event_char); + + EXPECT_EQ(event.modifiers, EVENTFLAG_ALT_DOWN); + got_key_event_char = true; + + // Call this method just to make sure it doesn't crash. + window->RemoveAllAccelerators(); + + // End the test by closing the Window. + window->Close(); + + return true; + } + + return false; +} + +bool OnAccelerator(CefRefPtr window, int command_id) { + EXPECT_FALSE(got_accelerator); + EXPECT_EQ(got_key_event_alt_count, 1); + EXPECT_FALSE(got_key_event_char); + + EXPECT_EQ(kCloseWindowId, command_id); + got_accelerator = true; + + // Remove the accelerator. + window->RemoveAccelerator(kCloseWindowId); + + // Now send the event without the accelerator registered. Should result in a + // call to OnKeyEvent. + TriggerAccelerator(window); + + return true; +} + +void RunWindowAccelerator(CefRefPtr window) { + window->SetAccelerator(kCloseWindowId, kChar, false, false, true); + window->Show(); + + CefPostDelayedTask(TID_UI, base::Bind(TriggerAccelerator, window), + kStateDelayMS); +} + +void VerifyWindowAccelerator(CefRefPtr window) { + EXPECT_TRUE(got_accelerator); + EXPECT_EQ(got_key_event_alt_count, 2); + EXPECT_TRUE(got_key_event_char); +} + +// Expected order of events: +// 1. OnKeyEvent for ALT key press. +// 2. OnAccelerator for ALT+Char key press (with accelerator registered). +// 3. OnKeyEvent for ALT key press. +// 4. OnKeyEvent for ALT+Char key press (without accelerator registered). +void WindowAcceleratorImpl(CefRefPtr event) { + got_accelerator = false; + got_key_event_alt_count = 0; + got_key_event_char = false; + + TestWindowDelegate::Config config; + config.on_window_created = base::Bind(RunWindowAccelerator); + config.on_window_destroyed = base::Bind(VerifyWindowAccelerator); + config.on_accelerator = base::Bind(OnAccelerator); + config.on_key_event = base::Bind(OnKeyEvent); + config.close_window = false; + TestWindowDelegate::RunTest(event, config); } } // namespace @@ -359,3 +493,4 @@ WINDOW_TEST_ASYNC(WindowFullscreen); WINDOW_TEST_ASYNC(WindowFullscreenFrameless); WINDOW_TEST_ASYNC(WindowIcon); WINDOW_TEST_ASYNC(WindowIconFrameless); +WINDOW_TEST_ASYNC(WindowAccelerator);