| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "fuchsia_web/webengine/browser/theme_manager.h" |
| |
| #include "base/check.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "third_party/blink/public/common/web_preferences/web_preferences.h" |
| #include "third_party/blink/public/mojom/css/preferred_color_scheme.mojom.h" |
| #include "third_party/blink/public/mojom/webpreferences/web_preferences.mojom.h" |
| |
| namespace { |
| |
| using blink::mojom::PreferredColorScheme; |
| using fuchsia::settings::ThemeType; |
| |
| constexpr PreferredColorScheme kFallbackColorScheme = |
| PreferredColorScheme::kLight; |
| |
| PreferredColorScheme ThemeTypeToBlinkScheme(ThemeType type) { |
| switch (type) { |
| case ThemeType::LIGHT: |
| return PreferredColorScheme::kLight; |
| case ThemeType::DARK: |
| return PreferredColorScheme::kDark; |
| default: |
| NOTREACHED(); |
| return kFallbackColorScheme; |
| } |
| } |
| |
| } // namespace |
| |
| ThemeManager::ThemeManager(content::WebContents* web_contents, |
| base::OnceClosure on_display_error) |
| : web_contents_(web_contents), |
| on_display_error_(std::move(on_display_error)) { |
| DCHECK(web_contents_); |
| |
| // Per the FIDL API, the default theme is LIGHT. |
| SetTheme(ThemeType::LIGHT); |
| } |
| |
| ThemeManager::~ThemeManager() = default; |
| |
| void ThemeManager::SetTheme(ThemeType theme) { |
| requested_theme_ = theme; |
| |
| if (theme == ThemeType::DEFAULT) { |
| if (!EnsureDisplayService()) { |
| OnDisplayServiceMissing(); |
| return; |
| } |
| } |
| } |
| |
| bool ThemeManager::EnsureDisplayService() { |
| if (observed_display_service_error_) |
| return false; |
| |
| if (display_service_) |
| return true; |
| |
| display_service_ = base::ComponentContextForProcess() |
| ->svc() |
| ->Connect<fuchsia::settings::Display>(); |
| |
| display_service_.set_error_handler([this](zx_status_t status) { |
| ZX_LOG_IF(ERROR, status != ZX_ERR_PEER_CLOSED, status) |
| << "fuchsia.settings.Display disconnected."; |
| |
| observed_display_service_error_ = true; |
| |
| // If the channel to the Display service was dropped before we received a |
| // response from WatchForDisplayChanges, then it's likely that the service |
| // isn't available in the namespace at all, which should be reported as |
| // an error on the Frame. |
| // Otherwise, if a failure was detected for a Display that was previously |
| // functioning, it should be treated as a transient issue and the last known |
| // system theme should be used. |
| if (requested_theme_ && (*requested_theme_ == ThemeType::DEFAULT) && |
| !did_receive_first_watch_result_) { |
| OnDisplayServiceMissing(); |
| } |
| }); |
| |
| WatchForDisplayChanges(); |
| return true; |
| } |
| |
| void ThemeManager::OnDisplayServiceMissing() { |
| LOG(ERROR) << "DEFAULT theme requires access to the " |
| "`fuchsia.settings.Display` service to work."; |
| |
| if (on_display_error_) |
| std::move(on_display_error_).Run(); |
| } |
| |
| void ThemeManager::ApplyThemeToWebPreferences( |
| blink::web_pref::WebPreferences* web_prefs) { |
| DCHECK(requested_theme_); |
| |
| if (requested_theme_ == ThemeType::DEFAULT) { |
| if (!system_theme_) { |
| // Defer theme application until we receive a system theme. |
| return; |
| } |
| |
| web_prefs->preferred_color_scheme = ThemeTypeToBlinkScheme(*system_theme_); |
| } else { |
| DCHECK(requested_theme_ == ThemeType::LIGHT || |
| requested_theme_ == ThemeType::DARK); |
| |
| web_prefs->preferred_color_scheme = |
| ThemeTypeToBlinkScheme(*requested_theme_); |
| } |
| } |
| |
| void ThemeManager::WatchForDisplayChanges() { |
| DCHECK(display_service_); |
| |
| // Will reply immediately for the first call of Watch(). Subsequent calls to |
| // Watch() will be replied to as changes occur. |
| display_service_->Watch( |
| fit::bind_member(this, &ThemeManager::OnWatchResultReceived)); |
| } |
| |
| void ThemeManager::OnWatchResultReceived( |
| fuchsia::settings::DisplaySettings settings) { |
| did_receive_first_watch_result_ = true; |
| |
| if (settings.has_theme() && settings.theme().has_theme_type() && |
| (settings.theme().theme_type() == ThemeType::DARK || |
| settings.theme().theme_type() == ThemeType::LIGHT)) { |
| system_theme_ = settings.theme().theme_type(); |
| } else { |
| system_theme_ = absl::nullopt; |
| } |
| |
| web_contents_->OnWebPreferencesChanged(); |
| WatchForDisplayChanges(); |
| } |