mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	To test:
- Run `cefclient --enable-chrome-runtime --use-alloy-style
                 --use-chrome-style-window [--background-color=green]`
- OS and Chrome theme changes behave as expected.
		
	
		
			
				
	
	
		
			196 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			196 lines
		
	
	
		
			5.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2024 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 "cef/libcef/browser/views/widget_impl.h"
 | |
| 
 | |
| #include "cef/libcef/browser/thread_util.h"
 | |
| #include "cef/libcef/browser/views/window_view.h"
 | |
| #include "cef/libcef/features/runtime.h"
 | |
| #include "chrome/browser/themes/custom_theme_supplier.h"
 | |
| #include "chrome/browser/themes/theme_service.h"
 | |
| #include "chrome/browser/themes/theme_service_factory.h"
 | |
| 
 | |
| #if BUILDFLAG(IS_LINUX)
 | |
| #include "ui/linux/linux_ui.h"
 | |
| #endif
 | |
| 
 | |
| CefWidgetImpl::CefWidgetImpl(CefWindowView* window_view)
 | |
|     : window_view_(window_view) {}
 | |
| 
 | |
| CefWidgetImpl::~CefWidgetImpl() {
 | |
|   DCHECK(associated_profiles_.empty());
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::Initialized() {
 | |
|   initialized_ = true;
 | |
| 
 | |
|   // Based on BrowserFrame::InitBrowserFrame.
 | |
|   // This is the first call that will trigger theme-related client callbacks.
 | |
| #if BUILDFLAG(IS_LINUX)
 | |
|   // Calls ThemeChanged() or OnNativeThemeUpdated().
 | |
|   SelectNativeTheme();
 | |
| #else
 | |
|   // Calls ThemeChanged().
 | |
|   SetNativeTheme(ui::NativeTheme::GetInstanceForNativeUi());
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::AddAssociatedProfile(Profile* profile) {
 | |
|   DCHECK(profile);
 | |
|   ProfileMap::iterator it = associated_profiles_.find(profile);
 | |
|   if (it != associated_profiles_.end()) {
 | |
|     // Another instance of a known Profile.
 | |
|     (it->second)++;
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto* current_profile = GetThemeProfile();
 | |
| 
 | |
|   associated_profiles_.insert(std::make_pair(profile, 1));
 | |
| 
 | |
|   if (auto* theme_service = ThemeServiceFactory::GetForProfile(profile)) {
 | |
|     theme_service->AddObserver(this);
 | |
|   }
 | |
| 
 | |
|   auto* new_profile = GetThemeProfile();
 | |
|   if (new_profile != current_profile) {
 | |
|     // Switching to a different theme.
 | |
|     NotifyThemeColorsChanged(/*chrome_theme=*/!!new_profile,
 | |
|                              /*call_theme_changed=*/true);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::RemoveAssociatedProfile(Profile* profile) {
 | |
|   DCHECK(profile);
 | |
|   ProfileMap::iterator it = associated_profiles_.find(profile);
 | |
|   if (it == associated_profiles_.end()) {
 | |
|     DCHECK(false);  // Not reached.
 | |
|     return;
 | |
|   }
 | |
|   if (--(it->second) > 0) {
 | |
|     // More instances of the Profile exist.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto* current_profile = GetThemeProfile();
 | |
| 
 | |
|   associated_profiles_.erase(it);
 | |
| 
 | |
|   if (auto* theme_service = ThemeServiceFactory::GetForProfile(profile)) {
 | |
|     theme_service->RemoveObserver(this);
 | |
|   }
 | |
| 
 | |
|   auto* new_profile = GetThemeProfile();
 | |
|   if (new_profile != current_profile) {
 | |
|     // Switching to a different theme.
 | |
|     NotifyThemeColorsChanged(/*chrome_theme=*/!!new_profile,
 | |
|                              /*call_theme_changed=*/true);
 | |
|   }
 | |
| }
 | |
| 
 | |
| Profile* CefWidgetImpl::GetThemeProfile() const {
 | |
|   if (!associated_profiles_.empty()) {
 | |
|     return associated_profiles_.begin()->first;
 | |
|   }
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| const ui::ThemeProvider* CefWidgetImpl::GetThemeProvider() const {
 | |
|   auto* profile = GetThemeProfile();
 | |
|   if (!profile) {
 | |
|     return Widget::GetThemeProvider();
 | |
|   }
 | |
| 
 | |
|   // Based on BrowserFrame::GetThemeProvider.
 | |
|   return &ThemeService::GetThemeProviderForProfile(profile);
 | |
| }
 | |
| 
 | |
| ui::ColorProviderKey::ThemeInitializerSupplier* CefWidgetImpl::GetCustomTheme()
 | |
|     const {
 | |
|   auto* profile = GetThemeProfile();
 | |
|   if (!profile) {
 | |
|     return Widget::GetCustomTheme();
 | |
|   }
 | |
| 
 | |
|   // Based on BrowserFrame::GetCustomTheme.
 | |
|   auto* theme_service = ThemeServiceFactory::GetForProfile(profile);
 | |
|   return theme_service->UsingDeviceTheme() ? nullptr
 | |
|                                            : theme_service->GetThemeSupplier();
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::OnNativeWidgetDestroyed() {
 | |
|   window_view_ = nullptr;
 | |
|   views::Widget::OnNativeWidgetDestroyed();
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::OnNativeThemeUpdated(ui::NativeTheme* observed_theme) {
 | |
|   // TODO: Reduce the frequency of this callback on Windows/Linux.
 | |
|   // See https://issues.chromium.org/issues/40280130#comment7
 | |
| 
 | |
|   color_provider_tracker_.OnNativeThemeUpdated();
 | |
| 
 | |
|   // Native/OS theme changed.
 | |
|   NotifyThemeColorsChanged(/*chrome_theme=*/false,
 | |
|                            /*call_theme_changed=*/false);
 | |
| 
 | |
|   // Calls ThemeChanged().
 | |
|   Widget::OnNativeThemeUpdated(observed_theme);
 | |
| }
 | |
| 
 | |
| ui::ColorProviderKey CefWidgetImpl::GetColorProviderKey() const {
 | |
|   const auto& widget_key = Widget::GetColorProviderKey();
 | |
|   if (auto* profile = GetThemeProfile()) {
 | |
|     return CefWidget::GetColorProviderKey(widget_key, profile);
 | |
|   }
 | |
|   return widget_key;
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::OnThemeChanged() {
 | |
|   // When the Chrome theme changes, the NativeTheme may also change.
 | |
|   SelectNativeTheme();
 | |
| 
 | |
|   NotifyThemeColorsChanged(/*chrome_theme=*/true, /*call_theme_changed=*/true);
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::NotifyThemeColorsChanged(bool chrome_theme,
 | |
|                                              bool call_theme_changed) {
 | |
|   if (window_view_) {
 | |
|     window_view_->OnThemeColorsChanged(chrome_theme);
 | |
|     if (call_theme_changed) {
 | |
|       // Call ThemeChanged() asynchronously to avoid possible reentrancy.
 | |
|       CEF_POST_TASK(TID_UI, base::BindOnce(&CefWidgetImpl::ThemeChanged,
 | |
|                                            weak_ptr_factory_.GetWeakPtr()));
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::SelectNativeTheme() {
 | |
|   // Based on BrowserFrame::SelectNativeTheme.
 | |
| #if BUILDFLAG(IS_LINUX)
 | |
|   ui::NativeTheme* native_theme = ui::NativeTheme::GetInstanceForNativeUi();
 | |
| 
 | |
|   // Always use the NativeTheme for forced color modes.
 | |
|   if (ui::NativeTheme::IsForcedDarkMode() ||
 | |
|       ui::NativeTheme::IsForcedLightMode()) {
 | |
|     SetNativeTheme(native_theme);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   const auto* linux_ui_theme =
 | |
|       ui::LinuxUiTheme::GetForWindow(GetNativeWindow());
 | |
|   SetNativeTheme(linux_ui_theme ? linux_ui_theme->GetNativeTheme()
 | |
|                                 : native_theme);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void CefWidgetImpl::OnColorProviderCacheResetMissed() {
 | |
|   // Ignore calls during Widget::Init().
 | |
|   if (!initialized_) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   NotifyThemeColorsChanged(/*chrome_theme=*/false,
 | |
|                            /*call_theme_changed=*/true);
 | |
| }
 |