// 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/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/menu/menu_runner.h" #if defined(USE_AURA) #include "ui/aura/test/ui_controls_factory_aura.h" #include "ui/base/test/ui_controls_aura.h" #if defined(OS_LINUX) #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) ui_controls::InstallUIControlsAura( views::test::CreateUIControlsDesktopAura()); #else ui_controls::InstallUIControlsAura(aura::test::CreateUIControlsAura(NULL)); #endif #endif initialized = true; } } } // namespace // static CefRefPtr CefWindow::CreateTopLevelWindow( CefRefPtr delegate) { return CefWindowImpl::Create(delegate); } // static CefRefPtr CefWindowImpl::Create( CefRefPtr delegate) { CEF_REQUIRE_UIT_RETURN(nullptr); CefRefPtr 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_->IsAlwaysOnTop()) widget_->SetAlwaysOnTop(on_top); } bool CefWindowImpl::IsAlwaysOnTop() { CEF_REQUIRE_VALID_RETURN(false); if (widget_) return widget_->IsAlwaysOnTop(); 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 image) { CEF_REQUIRE_VALID_RETURN_VOID(); if (root_view()) root_view()->SetWindowIcon(image); } CefRefPtr CefWindowImpl::GetWindowIcon() { CEF_REQUIRE_VALID_RETURN(nullptr); if (root_view()) return root_view()->window_icon(); return nullptr; } void CefWindowImpl::SetWindowAppIcon(CefRefPtr image) { CEF_REQUIRE_VALID_RETURN_VOID(); if (root_view()) root_view()->SetWindowAppIcon(image); } CefRefPtr 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 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::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(); } void CefWindowImpl::MenuClosed(CefRefPtr source) { DCHECK_EQ(menu_model_, source); menu_model_->RemoveObserver(this); menu_model_ = nullptr; menu_runner_.reset(nullptr); } void CefWindowImpl::ShowMenu(views::MenuButton* menu_button, CefRefPtr menu_model, const CefPoint& screen_point, cef_menu_anchor_position_t anchor_position) { CancelMenu(); if (!widget_) return; CefMenuModelImpl* menu_model_impl = static_cast(menu_model.get()); if (!menu_model_impl || !menu_model_impl->model()) return; menu_model_ = menu_model_impl; menu_model_->AddObserver(this); menu_runner_.reset( new views::MenuRunner(menu_model_impl->model(), menu_button ? views::MenuRunner::HAS_MNEMONICS : views::MenuRunner::CONTEXT_MENU)); views::MenuRunner::RunResult result = menu_runner_->RunMenuAt( widget_, menu_button, gfx::Rect(gfx::Point(screen_point.x, screen_point.y), gfx::Size()), static_cast(anchor_position), ui::MENU_SOURCE_NONE); ALLOW_UNUSED_LOCAL(result); } void CefWindowImpl::CancelMenu() { CEF_REQUIRE_VALID_RETURN_VOID(); if (menu_runner_) menu_runner_->Cancel(); DCHECK(!menu_model_); DCHECK(!menu_runner_); } CefRefPtr 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& 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(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); } CefWindowImpl::CefWindowImpl(CefRefPtr delegate) : ParentClass(delegate), widget_(nullptr), destroyed_(false) { } CefWindowView* CefWindowImpl::CreateRootView() { return new CefWindowView(delegate(), this); } void CefWindowImpl::CreateWidget() { DCHECK(!widget_); root_view()->CreateWidget(); widget_ = root_view()->GetWidget(); DCHECK(widget_); // 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); views::View* view = view_ptr.release(); ALLOW_UNUSED_LOCAL(view); }