// Copyright (c) 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 "tests/cefclient/browser/root_window_views.h" #include "include/base/cef_bind.h" #include "include/base/cef_build.h" #include "include/cef_app.h" #include "include/wrapper/cef_helpers.h" #include "tests/cefclient/browser/client_handler_std.h" namespace client { namespace { // Images that are loaded early and cached. static const char* kDefaultImageCache[] = {"menu_icon", "window_icon"}; } // namespace RootWindowViews::RootWindowViews() : with_controls_(false), always_on_top_(false), with_extension_(false), initially_hidden_(false), is_popup_(false), position_on_resize_(false), initialized_(false), window_destroyed_(false), browser_destroyed_(false) {} RootWindowViews::~RootWindowViews() { REQUIRE_MAIN_THREAD(); } void RootWindowViews::Init(RootWindow::Delegate* delegate, const RootWindowConfig& config, const CefBrowserSettings& settings) { DCHECK(delegate); DCHECK(!config.with_osr); // Windowless rendering is not supported. DCHECK(!initialized_); delegate_ = delegate; with_controls_ = config.with_controls; always_on_top_ = config.always_on_top; with_extension_ = config.with_extension; initially_hidden_ = config.initially_hidden; if (initially_hidden_ && !config.source_bounds.IsEmpty()) { // The window will be sized and positioned in OnAutoResize(). initial_bounds_ = config.source_bounds; position_on_resize_ = true; } else { initial_bounds_ = config.bounds; } parent_window_ = config.parent_window; close_callback_ = config.close_callback; CreateClientHandler(config.url); initialized_ = true; // Continue initialization on the UI thread. InitOnUIThread(settings, config.url, delegate_->GetRequestContext(this)); } void RootWindowViews::InitAsPopup(RootWindow::Delegate* delegate, bool with_controls, bool with_osr, const CefPopupFeatures& popupFeatures, CefWindowInfo& windowInfo, CefRefPtr& client, CefBrowserSettings& settings) { CEF_REQUIRE_UI_THREAD(); DCHECK(delegate); DCHECK(!with_osr); // Windowless rendering is not supported. DCHECK(!initialized_); delegate_ = delegate; with_controls_ = with_controls; is_popup_ = true; if (popupFeatures.xSet) initial_bounds_.x = popupFeatures.x; if (popupFeatures.ySet) initial_bounds_.y = popupFeatures.y; if (popupFeatures.widthSet) initial_bounds_.width = popupFeatures.width; if (popupFeatures.heightSet) initial_bounds_.height = popupFeatures.height; CreateClientHandler(std::string()); initialized_ = true; // The Window will be created in ViewsWindow::OnPopupBrowserViewCreated(). client = client_handler_; } void RootWindowViews::Show(ShowMode mode) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::Show, this, mode)); return; } if (!window_) return; window_->Show(); switch (mode) { case ShowMinimized: window_->Minimize(); break; case ShowMaximized: window_->Maximize(); break; default: break; } } void RootWindowViews::Hide() { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::Hide, this)); return; } if (window_) window_->Hide(); } void RootWindowViews::SetBounds(int x, int y, size_t width, size_t height) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::SetBounds, this, x, y, width, height)); return; } if (window_) { window_->SetBounds( CefRect(x, y, static_cast(width), static_cast(height))); } } void RootWindowViews::Close(bool force) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::Close, this, force)); return; } if (window_) window_->Close(force); } void RootWindowViews::SetDeviceScaleFactor(float device_scale_factor) { REQUIRE_MAIN_THREAD(); // Windowless rendering is not supported. NOTREACHED(); } float RootWindowViews::GetDeviceScaleFactor() const { REQUIRE_MAIN_THREAD(); // Windowless rendering is not supported. NOTREACHED(); return 0.0; } CefRefPtr RootWindowViews::GetBrowser() const { REQUIRE_MAIN_THREAD(); return browser_; } ClientWindowHandle RootWindowViews::GetWindowHandle() const { REQUIRE_MAIN_THREAD(); #if defined(OS_LINUX) // ClientWindowHandle is a GtkWidget* on Linux and we don't have one of those. return NULL; #else if (browser_) return browser_->GetHost()->GetWindowHandle(); return kNullWindowHandle; #endif } bool RootWindowViews::WithExtension() const { DCHECK(initialized_); return with_extension_; } bool RootWindowViews::WithControls() { DCHECK(initialized_); return with_controls_; } bool RootWindowViews::WithExtension() { DCHECK(initialized_); return with_extension_; } void RootWindowViews::OnExtensionsChanged(const ExtensionSet& extensions) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnExtensionsChanged, this, extensions)); return; } if (window_) { window_->OnExtensionsChanged(extensions); } else { // Window may not exist yet for popups. pending_extensions_ = extensions; } } bool RootWindowViews::InitiallyHidden() { CEF_REQUIRE_UI_THREAD(); return initially_hidden_; } CefRefPtr RootWindowViews::GetParentWindow() { CEF_REQUIRE_UI_THREAD(); return parent_window_; } CefRect RootWindowViews::GetWindowBounds() { CEF_REQUIRE_UI_THREAD(); return initial_bounds_; } scoped_refptr RootWindowViews::GetImageCache() { CEF_REQUIRE_UI_THREAD(); return image_cache_; } void RootWindowViews::OnViewsWindowCreated(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); DCHECK(!window_); window_ = window; window_->SetAlwaysOnTop(always_on_top_); if (!pending_extensions_.empty()) { window_->OnExtensionsChanged(pending_extensions_); pending_extensions_.clear(); } } void RootWindowViews::OnViewsWindowDestroyed(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); window_ = NULL; // Continue on the main thread. MAIN_POST_CLOSURE( base::Bind(&RootWindowViews::NotifyViewsWindowDestroyed, this)); } void RootWindowViews::OnViewsWindowActivated(CefRefPtr window) { CEF_REQUIRE_UI_THREAD(); // Continue on the main thread. MAIN_POST_CLOSURE( base::Bind(&RootWindowViews::NotifyViewsWindowActivated, this)); } ViewsWindow::Delegate* RootWindowViews::GetDelegateForPopup( CefRefPtr client) { CEF_REQUIRE_UI_THREAD(); // |handler| was created in RootWindowViews::InitAsPopup(). ClientHandlerStd* handler = static_cast(client.get()); RootWindowViews* root_window = static_cast(handler->delegate()); // Transfer some state to the child RootWindowViews. root_window->image_cache_ = image_cache_; return root_window; } void RootWindowViews::CreateExtensionWindow( CefRefPtr extension, const CefRect& source_bounds, CefRefPtr parent_window, const base::Closure& close_callback) { if (!CURRENTLY_ON_MAIN_THREAD()) { // Execute this method on the main thread. MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::CreateExtensionWindow, this, extension, source_bounds, parent_window, close_callback)); return; } delegate_->CreateExtensionWindow(extension, source_bounds, parent_window, close_callback, false); } void RootWindowViews::OnTest(int test_id) { if (!CURRENTLY_ON_MAIN_THREAD()) { // Execute this method on the main thread. MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::OnTest, this, test_id)); return; } delegate_->OnTest(this, test_id); } void RootWindowViews::OnExit() { if (!CURRENTLY_ON_MAIN_THREAD()) { // Execute this method on the main thread. MAIN_POST_CLOSURE(base::Bind(&RootWindowViews::OnExit, this)); return; } delegate_->OnExit(this); } void RootWindowViews::OnBrowserCreated(CefRefPtr browser) { REQUIRE_MAIN_THREAD(); DCHECK(!browser_); browser_ = browser; delegate_->OnBrowserCreated(this, browser); } void RootWindowViews::OnBrowserClosing(CefRefPtr browser) { REQUIRE_MAIN_THREAD(); // Nothing to do here. } void RootWindowViews::OnBrowserClosed(CefRefPtr browser) { REQUIRE_MAIN_THREAD(); if (browser_) { DCHECK_EQ(browser->GetIdentifier(), browser_->GetIdentifier()); browser_ = NULL; } client_handler_->DetachDelegate(); client_handler_ = NULL; browser_destroyed_ = true; NotifyDestroyedIfDone(); } void RootWindowViews::OnSetAddress(const std::string& url) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetAddress, this, url)); return; } if (window_ && with_controls_) window_->SetAddress(url); } void RootWindowViews::OnSetTitle(const std::string& title) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetTitle, this, title)); return; } if (window_) window_->SetTitle(title); } void RootWindowViews::OnSetFavicon(CefRefPtr image) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetFavicon, this, image)); return; } if (window_) window_->SetFavicon(image); } void RootWindowViews::OnSetFullscreen(bool fullscreen) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetFullscreen, this, fullscreen)); return; } if (window_) window_->SetFullscreen(fullscreen); } void RootWindowViews::OnAutoResize(const CefSize& new_size) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnAutoResize, this, new_size)); return; } bool has_position = false; CefPoint position; if (position_on_resize_) { // Position the window centered on and immediately below the source. const int x_offset = (initial_bounds_.width - new_size.width) / 2; position.Set(initial_bounds_.x + x_offset, initial_bounds_.y + initial_bounds_.height); has_position = true; // Don't change the window position on future resizes. position_on_resize_ = false; } if (window_) { window_->SetBrowserSize(new_size, has_position, position); window_->Show(); } } void RootWindowViews::OnSetLoadingState(bool isLoading, bool canGoBack, bool canGoForward) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetLoadingState, this, isLoading, canGoBack, canGoForward)); return; } if (window_) { if (with_controls_) window_->SetLoadingState(isLoading, canGoBack, canGoForward); if (isLoading) { // Reset to the default window icon when loading begins. window_->SetFavicon( delegate_->GetImageCache()->GetCachedImage("window_icon")); } } } void RootWindowViews::OnSetDraggableRegions( const std::vector& regions) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::OnSetDraggableRegions, this, regions)); return; } if (window_) 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::OnBeforeContextMenu(CefRefPtr model) { CEF_REQUIRE_UI_THREAD(); if (window_) window_->OnBeforeContextMenu(model); } void RootWindowViews::CreateClientHandler(const std::string& url) { DCHECK(!client_handler_); client_handler_ = new ClientHandlerStd(this, url); client_handler_->set_download_favicon_images(true); } void RootWindowViews::InitOnUIThread( const CefBrowserSettings& settings, const std::string& startup_url, CefRefPtr request_context) { if (!CefCurrentlyOn(TID_UI)) { // Execute this method on the UI thread. CefPostTask(TID_UI, base::Bind(&RootWindowViews::InitOnUIThread, this, settings, startup_url, request_context)); return; } image_cache_ = delegate_->GetImageCache(); // Populate the default image cache. ImageCache::ImageInfoSet image_set; for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i) image_set.push_back(ImageCache::ImageInfo::Create2x(kDefaultImageCache[i])); image_cache_->LoadImages( image_set, base::Bind(&RootWindowViews::CreateViewsWindow, this, settings, startup_url, request_context)); } void RootWindowViews::CreateViewsWindow( const CefBrowserSettings& settings, const std::string& startup_url, CefRefPtr request_context, const ImageCache::ImageSet& images) { CEF_REQUIRE_UI_THREAD(); DCHECK(!window_); #ifndef NDEBUG // Make sure the default images loaded successfully. DCHECK_EQ(images.size(), arraysize(kDefaultImageCache)); for (size_t i = 0U; i < arraysize(kDefaultImageCache); ++i) { DCHECK(images[i]) << "Default image " << i << " failed to load"; } #endif // Create the ViewsWindow. It will show itself after creation. ViewsWindow::Create(this, client_handler_, startup_url, settings, request_context); } void RootWindowViews::NotifyViewsWindowDestroyed() { REQUIRE_MAIN_THREAD(); window_destroyed_ = true; NotifyDestroyedIfDone(); } void RootWindowViews::NotifyViewsWindowActivated() { REQUIRE_MAIN_THREAD(); delegate_->OnRootWindowActivated(this); } void RootWindowViews::NotifyDestroyedIfDone() { // Notify once both the window and the browser have been destroyed. if (window_destroyed_ && browser_destroyed_) { delegate_->OnRootWindowDestroyed(this); if (!close_callback_.is_null()) close_callback_.Run(); } } } // namespace client