| // Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be found |
| // in the LICENSE file. |
| |
| #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" |
| #include "libcef/browser/views/layout_util.h" |
| #include "libcef/browser/views/view_util.h" |
| #include "libcef/browser/views/window_view.h" |
| |
| #include "ui/base/test/ui_controls.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/views/controls/button/menu_button.h" |
| #include "ui/views/controls/menu/menu_runner.h" |
| |
| #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) && defined(USE_X11) |
| #include "ui/views/test/ui_controls_factory_desktop_aurax11.h" |
| #endif |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "ui/display/win/screen_win.h" |
| #endif |
| |
| namespace { |
| |
| // Based on chrome/test/base/interactive_ui_tests_main.cc. |
| void InitializeUITesting() { |
| static bool initialized = false; |
| if (!initialized) { |
| ui_controls::EnableUIControls(); |
| |
| #if defined(USE_AURA) |
| #if defined(OS_LINUX) && defined(USE_X11) |
| ui_controls::InstallUIControlsAura( |
| views::test::CreateUIControlsDesktopAura()); |
| #else |
| ui_controls::InstallUIControlsAura( |
| aura::test::CreateUIControlsAura(nullptr)); |
| #endif |
| #endif |
| |
| initialized = true; |
| } |
| } |
| |
| #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 |
| CefRefPtr<CefWindow> CefWindow::CreateTopLevelWindow( |
| CefRefPtr<CefWindowDelegate> delegate) { |
| return CefWindowImpl::Create(delegate); |
| } |
| |
| // static |
| CefRefPtr<CefWindowImpl> CefWindowImpl::Create( |
| CefRefPtr<CefWindowDelegate> delegate) { |
| CEF_REQUIRE_UIT_RETURN(nullptr); |
| CefRefPtr<CefWindowImpl> window = new CefWindowImpl(delegate); |
| window->Initialize(); |
| window->CreateWidget(); |
| if (delegate) |
| delegate->OnWindowCreated(window.get()); |
| return window; |
| } |
| |
| void CefWindowImpl::Show() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) |
| widget_->Show(); |
| } |
| |
| void CefWindowImpl::Hide() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) |
| widget_->Hide(); |
| } |
| |
| void CefWindowImpl::CenterWindow(const CefSize& size) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) |
| widget_->CenterWindow(gfx::Size(size.width, size.height)); |
| } |
| |
| void CefWindowImpl::Close() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && !widget_->IsClosed()) |
| widget_->Close(); |
| } |
| |
| bool CefWindowImpl::IsClosed() { |
| CEF_REQUIRE_UIT_RETURN(false); |
| return destroyed_ || (widget_ && widget_->IsClosed()); |
| } |
| |
| void CefWindowImpl::Activate() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && widget_->CanActivate() && !widget_->IsActive()) |
| widget_->Activate(); |
| } |
| |
| void CefWindowImpl::Deactivate() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && widget_->CanActivate() && widget_->IsActive()) |
| widget_->Deactivate(); |
| } |
| |
| bool CefWindowImpl::IsActive() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->IsActive(); |
| return false; |
| } |
| |
| void CefWindowImpl::BringToTop() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) |
| widget_->StackAtTop(); |
| } |
| |
| void CefWindowImpl::SetAlwaysOnTop(bool on_top) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && on_top != (widget_->GetZOrderLevel() == |
| ui::ZOrderLevel::kFloatingWindow)) { |
| widget_->SetZOrderLevel(on_top ? ui::ZOrderLevel::kFloatingWindow |
| : ui::ZOrderLevel::kNormal); |
| } |
| } |
| |
| bool CefWindowImpl::IsAlwaysOnTop() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->GetZOrderLevel() == ui::ZOrderLevel::kFloatingWindow; |
| return false; |
| } |
| |
| void CefWindowImpl::Maximize() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && !widget_->IsMaximized()) |
| widget_->Maximize(); |
| } |
| |
| void CefWindowImpl::Minimize() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && !widget_->IsMinimized()) |
| widget_->Minimize(); |
| } |
| |
| void CefWindowImpl::Restore() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && (widget_->IsMaximized() || widget_->IsMinimized())) |
| widget_->Restore(); |
| } |
| |
| void CefWindowImpl::SetFullscreen(bool fullscreen) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_ && fullscreen != widget_->IsFullscreen()) |
| widget_->SetFullscreen(fullscreen); |
| } |
| |
| bool CefWindowImpl::IsMaximized() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->IsMaximized(); |
| return false; |
| } |
| |
| bool CefWindowImpl::IsMinimized() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->IsMinimized(); |
| return false; |
| } |
| |
| bool CefWindowImpl::IsFullscreen() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->IsFullscreen(); |
| return false; |
| } |
| |
| void CefWindowImpl::SetTitle(const CefString& title) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (root_view()) |
| root_view()->SetTitle(title); |
| } |
| |
| CefString CefWindowImpl::GetTitle() { |
| CEF_REQUIRE_VALID_RETURN(CefString()); |
| if (root_view()) |
| return root_view()->title(); |
| return CefString(); |
| } |
| |
| void CefWindowImpl::SetWindowIcon(CefRefPtr<CefImage> image) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (root_view()) |
| root_view()->SetWindowIcon(image); |
| } |
| |
| CefRefPtr<CefImage> CefWindowImpl::GetWindowIcon() { |
| CEF_REQUIRE_VALID_RETURN(nullptr); |
| if (root_view()) |
| return root_view()->window_icon(); |
| return nullptr; |
| } |
| |
| void CefWindowImpl::SetWindowAppIcon(CefRefPtr<CefImage> image) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (root_view()) |
| root_view()->SetWindowAppIcon(image); |
| } |
| |
| CefRefPtr<CefImage> CefWindowImpl::GetWindowAppIcon() { |
| CEF_REQUIRE_VALID_RETURN(nullptr); |
| if (root_view()) |
| return root_view()->window_app_icon(); |
| return nullptr; |
| } |
| |
| void CefWindowImpl::GetDebugInfo(base::DictionaryValue* info, |
| bool include_children) { |
| ParentClass::GetDebugInfo(info, include_children); |
| if (root_view()) |
| info->SetString("title", root_view()->title()); |
| } |
| |
| void CefWindowImpl::ShowMenu(CefRefPtr<CefMenuModel> menu_model, |
| const CefPoint& screen_point, |
| cef_menu_anchor_position_t anchor_position) { |
| ShowMenu(nullptr, menu_model, screen_point, anchor_position); |
| } |
| |
| void CefWindowImpl::Detach() { |
| // OnDeleteDelegate should always be called before Detach(). |
| DCHECK(!widget_); |
| |
| ParentClass::Detach(); |
| } |
| |
| void CefWindowImpl::SetBounds(const CefRect& bounds) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) { |
| widget_->SetBounds( |
| gfx::Rect(bounds.x, bounds.y, bounds.width, bounds.height)); |
| } |
| } |
| |
| CefRect CefWindowImpl::GetBounds() { |
| CEF_REQUIRE_VALID_RETURN(CefRect()); |
| gfx::Rect bounds; |
| if (widget_) |
| bounds = widget_->GetWindowBoundsInScreen(); |
| return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
| } |
| |
| CefRect CefWindowImpl::GetBoundsInScreen() { |
| return GetBounds(); |
| } |
| |
| void CefWindowImpl::SetSize(const CefSize& size) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) |
| widget_->SetSize(gfx::Size(size.width, size.height)); |
| } |
| |
| void CefWindowImpl::SetPosition(const CefPoint& position) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) { |
| gfx::Rect bounds = widget_->GetWindowBoundsInScreen(); |
| bounds.set_origin(gfx::Point(position.x, position.y)); |
| widget_->SetBounds(bounds); |
| } |
| } |
| |
| void CefWindowImpl::SizeToPreferredSize() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (widget_) { |
| if (widget_->non_client_view()) |
| widget_->SetSize(widget_->non_client_view()->GetPreferredSize()); |
| else |
| widget_->SetSize(root_view()->GetPreferredSize()); |
| } |
| } |
| |
| void CefWindowImpl::SetVisible(bool visible) { |
| if (visible) |
| Show(); |
| else |
| Hide(); |
| } |
| |
| bool CefWindowImpl::IsVisible() { |
| CEF_REQUIRE_VALID_RETURN(false); |
| if (widget_) |
| return widget_->IsVisible(); |
| return false; |
| } |
| |
| bool CefWindowImpl::IsDrawn() { |
| return IsVisible(); |
| } |
| |
| void CefWindowImpl::SetBackgroundColor(cef_color_t color) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| ParentClass::SetBackgroundColor(color); |
| if (widget_ && widget_->GetCompositor()) |
| widget_->GetCompositor()->SetBackgroundColor(color); |
| } |
| |
| bool CefWindowImpl::CanWidgetClose() { |
| if (delegate()) |
| return delegate()->CanClose(this); |
| return true; |
| } |
| |
| void CefWindowImpl::OnWindowClosing() { |
| #if defined(USE_AURA) |
| unhandled_key_event_handler_.reset(); |
| #endif |
| } |
| |
| void CefWindowImpl::OnWindowViewDeleted() { |
| CancelMenu(); |
| |
| destroyed_ = true; |
| widget_ = nullptr; |
| |
| if (delegate()) |
| delegate()->OnWindowDestroyed(this); |
| |
| // Call Detach() here instead of waiting for the root View to be deleted so |
| // that any following attempts to call CefWindow methods from the delegate |
| // will fail. |
| Detach(); |
| } |
| |
| // 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<CefMenuModel> menu_model, |
| const CefPoint& screen_point, |
| cef_menu_anchor_position_t anchor_position) { |
| CancelMenu(); |
| |
| if (!widget_) |
| return; |
| |
| CefMenuModelImpl* menu_model_impl = |
| static_cast<CefMenuModelImpl*>(menu_model.get()); |
| if (!menu_model_impl || !menu_model_impl->model()) |
| return; |
| |
| menu_model_ = menu_model_impl; |
| |
| // We'll send the MenuClosed notification manually for better accuracy. |
| menu_model_->set_auto_notify_menu_closed(false); |
| |
| menu_runner_.reset( |
| new views::MenuRunner(menu_model_impl->model(), |
| menu_button ? views::MenuRunner::HAS_MNEMONICS |
| : views::MenuRunner::CONTEXT_MENU, |
| base::Bind(&CefWindowImpl::MenuClosed, this))); |
| |
| menu_runner_->RunMenuAt( |
| widget_, menu_button ? menu_button->button_controller() : nullptr, |
| gfx::Rect(gfx::Point(screen_point.x, screen_point.y), gfx::Size()), |
| static_cast<views::MenuAnchorPosition>(anchor_position), |
| ui::MENU_SOURCE_NONE); |
| } |
| |
| void CefWindowImpl::MenuClosed() { |
| menu_model_->NotifyMenuClosed(); |
| menu_model_ = nullptr; |
| menu_runner_.reset(nullptr); |
| } |
| |
| void CefWindowImpl::CancelMenu() { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (menu_runner_) |
| menu_runner_->Cancel(); |
| DCHECK(!menu_model_); |
| DCHECK(!menu_runner_); |
| } |
| |
| CefRefPtr<CefDisplay> CefWindowImpl::GetDisplay() { |
| CEF_REQUIRE_VALID_RETURN(nullptr); |
| if (widget_ && root_view()) { |
| const display::Display& display = root_view()->GetDisplay(); |
| if (display.is_valid()) |
| return new CefDisplayImpl(display); |
| } |
| return nullptr; |
| } |
| |
| CefRect CefWindowImpl::GetClientAreaBoundsInScreen() { |
| CEF_REQUIRE_VALID_RETURN(CefRect()); |
| if (widget_) { |
| gfx::Rect bounds = widget_->GetClientAreaBoundsInScreen(); |
| |
| views::NonClientFrameView* non_client_frame_view = |
| root_view()->GetNonClientFrameView(); |
| if (non_client_frame_view) { |
| // When using a custom drawn NonClientFrameView the native Window will not |
| // know the actual client bounds. Adjust the native Window bounds for the |
| // reported client bounds. |
| const gfx::Rect& client_bounds = |
| non_client_frame_view->GetBoundsForClientView(); |
| bounds.set_origin(bounds.origin() + client_bounds.OffsetFromOrigin()); |
| bounds.set_size(client_bounds.size()); |
| } |
| |
| return CefRect(bounds.x(), bounds.y(), bounds.width(), bounds.height()); |
| } |
| return CefRect(); |
| } |
| |
| void CefWindowImpl::SetDraggableRegions( |
| const std::vector<CefDraggableRegion>& regions) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (root_view()) |
| root_view()->SetDraggableRegions(regions); |
| } |
| |
| CefWindowHandle CefWindowImpl::GetWindowHandle() { |
| CEF_REQUIRE_VALID_RETURN(kNullWindowHandle); |
| return view_util::GetWindowHandle(widget_); |
| } |
| |
| void CefWindowImpl::SendKeyPress(int key_code, uint32 event_flags) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| InitializeUITesting(); |
| |
| gfx::NativeWindow native_window = view_util::GetNativeWindow(widget_); |
| if (!native_window) |
| return; |
| |
| ui_controls::SendKeyPress(native_window, |
| static_cast<ui::KeyboardCode>(key_code), |
| !!(event_flags & EVENTFLAG_CONTROL_DOWN), |
| !!(event_flags & EVENTFLAG_SHIFT_DOWN), |
| !!(event_flags & EVENTFLAG_ALT_DOWN), |
| false); // Command key is not supported by Aura. |
| } |
| |
| void CefWindowImpl::SendMouseMove(int screen_x, int screen_y) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| InitializeUITesting(); |
| |
| gfx::Point point(screen_x, screen_y); |
| #if defined(OS_WIN) |
| // Windows expects pixel coordinates. |
| point = display::win::ScreenWin::DIPToScreenPoint(point); |
| #endif |
| |
| ui_controls::SendMouseMove(point.x(), point.y()); |
| } |
| |
| void CefWindowImpl::SendMouseEvents(cef_mouse_button_type_t button, |
| bool mouse_down, |
| bool mouse_up) { |
| CEF_REQUIRE_VALID_RETURN_VOID(); |
| if (!mouse_down && !mouse_up) |
| return; |
| |
| InitializeUITesting(); |
| |
| ui_controls::MouseButton type = ui_controls::LEFT; |
| if (button == MBT_MIDDLE) |
| type = ui_controls::MIDDLE; |
| else if (button == MBT_RIGHT) |
| type = ui_controls::RIGHT; |
| |
| int state = 0; |
| if (mouse_down) |
| state |= ui_controls::DOWN; |
| if (mouse_up) |
| state |= ui_controls::UP; |
| |
| 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<ui::KeyboardCode>(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<CefWindowDelegate> delegate) |
| : ParentClass(delegate), widget_(nullptr), destroyed_(false) {} |
| |
| CefWindowView* CefWindowImpl::CreateRootView() { |
| return new CefWindowView(delegate(), this); |
| } |
| |
| void CefWindowImpl::InitializeRootView() { |
| static_cast<CefWindowView*>(root_view())->Initialize(); |
| } |
| |
| void CefWindowImpl::CreateWidget() { |
| DCHECK(!widget_); |
| |
| root_view()->CreateWidget(); |
| widget_ = root_view()->GetWidget(); |
| DCHECK(widget_); |
| |
| #if defined(USE_AURA) |
| unhandled_key_event_handler_ = |
| std::make_unique<CefUnhandledKeyEventHandler>(this, widget_); |
| #endif |
| |
| // The Widget and root View are owned by the native window. Therefore don't |
| // keep an owned reference. |
| std::unique_ptr<views::View> view_ptr = view_util::PassOwnership(this); |
| views::View* view = view_ptr.release(); |
| ALLOW_UNUSED_LOCAL(view); |
| } |