1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141

fuchsia_web / webengine / browser / theme_manager.cc [blame]

// 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;

PreferredColorScheme ThemeTypeToBlinkScheme(ThemeType type) {
  switch (type) {
    case ThemeType::LIGHT:
      return PreferredColorScheme::kLight;
    case ThemeType::DARK:
      return PreferredColorScheme::kDark;
    default:
      NOTREACHED();
  }
}

}  // 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_ = std::nullopt;
  }

  web_contents_->OnWebPreferencesChanged();
  WatchForDisplayChanges();
}