chromium/chrome/browser/ui/webui/ash/settings/pages/date_time/date_time_handler.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/settings/pages/date_time/date_time_handler.h"

#include "ash/public/cpp/child_accounts/parent_access_controller.h"
#include "ash/public/cpp/login_screen.h"
#include "base/check_op.h"
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/ash/child_accounts/parent_access_code/parent_access_service.h"
#include "chrome/browser/ash/system/timezone_resolver_manager.h"
#include "chrome/browser/ash/system/timezone_util.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/ui/webui/ash/set_time_dialog/set_time_dialog.h"
#include "chrome/common/pref_names.h"
#include "chromeos/ash/components/dbus/system_clock/system_clock_client.h"
#include "chromeos/ash/components/settings/timezone_settings.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"

namespace ash::settings {

namespace {

// Returns whether the system's automatic time zone detection setting is
// managed, which may override the user's setting.
bool IsSystemTimezoneAutomaticDetectionManaged() {
  return g_browser_process->local_state()->IsManagedPreference(
      prefs::kSystemTimezoneAutomaticDetectionPolicy);
}

// Returns the system's automatic time zone detection policy value, which
// corresponds to the SystemTimezoneProto's AutomaticTimezoneDetectionType
// enum and determines whether the user's setting will be overridden.
int GetSystemTimezoneAutomaticDetectionPolicyValue() {
  DCHECK(IsSystemTimezoneAutomaticDetectionManaged());

  return g_browser_process->local_state()->GetInteger(
      prefs::kSystemTimezoneAutomaticDetectionPolicy);
}

// Returns whether the user can set the automatic detection setting, based on
// flags and policies.
bool IsTimezoneAutomaticDetectionUserEditable() {
  if (system::HasSystemTimezonePolicy()) {
    return false;
  }

  if (IsSystemTimezoneAutomaticDetectionManaged()) {
    return GetSystemTimezoneAutomaticDetectionPolicyValue() ==
           enterprise_management::SystemTimezoneProto::USERS_DECIDE;
  }

  return true;
}

}  // namespace

DateTimeHandler::DateTimeHandler() {}

DateTimeHandler::~DateTimeHandler() = default;

void DateTimeHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "dateTimePageReady",
      base::BindRepeating(&DateTimeHandler::HandleDateTimePageReady,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "getTimeZones", base::BindRepeating(&DateTimeHandler::HandleGetTimeZones,
                                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "showSetDateTimeUI",
      base::BindRepeating(&DateTimeHandler::HandleShowSetDateTimeUI,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "handleShowParentAccessForTimeZone",
      base::BindRepeating(&DateTimeHandler::HandleShowParentAccessForTimeZone,
                          base::Unretained(this)));
}

void DateTimeHandler::OnJavascriptAllowed() {
  SystemClockClient* system_clock_client = SystemClockClient::Get();
  scoped_observation_.Observe(system_clock_client);
  SystemClockCanSetTimeChanged(system_clock_client->CanSetTime());

  // The system time zone policy disables auto-detection entirely. (However,
  // the time zone policy does not override the user's time zone itself.)
  system_timezone_policy_subscription_ =
      CrosSettings::Get()->AddSettingsObserver(
          kSystemTimezonePolicy,
          base::BindRepeating(
              &DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy,
              weak_ptr_factory_.GetWeakPtr()));

  // The auto-detection policy can force auto-detection on or off.
  local_state_pref_change_registrar_.Init(g_browser_process->local_state());
  local_state_pref_change_registrar_.Add(
      prefs::kSystemTimezoneAutomaticDetectionPolicy,
      base::BindRepeating(
          &DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy,
          base::Unretained(this)));
}

void DateTimeHandler::OnJavascriptDisallowed() {
  scoped_observation_.Reset();
  system_timezone_policy_subscription_ = {};
  local_state_pref_change_registrar_.RemoveAll();
}

void DateTimeHandler::HandleDateTimePageReady(const base::Value::List& args) {
  AllowJavascript();

  // Send the time zone automatic detection policy in case it changed after the
  // handler was created.
  NotifyTimezoneAutomaticDetectionPolicy();
}

void DateTimeHandler::HandleGetTimeZones(const base::Value::List& args) {
  AllowJavascript();

  CHECK_EQ(1U, args.size());
  const base::Value& callback_id = args[0];
  ResolveJavascriptCallback(callback_id, system::GetTimezoneList());
}

void DateTimeHandler::HandleShowSetDateTimeUI(const base::Value::List& args) {
  // Make sure the clock status hasn't changed since the button was clicked.
  if (!SystemClockClient::Get()->CanSetTime()) {
    return;
  }
  SetTimeDialog::ShowDialog(
      web_ui()->GetWebContents()->GetTopLevelNativeWindow());
}

void DateTimeHandler::HandleShowParentAccessForTimeZone(
    const base::Value::List& args) {
  DCHECK(user_manager::UserManager::Get()->GetActiveUser()->IsChild());

  if (!parent_access::ParentAccessService::IsApprovalRequired(
          SupervisedAction::kUpdateTimezone)) {
    OnParentAccessValidation(true);
    return;
  }

  ParentAccessController::Get()->ShowWidget(
      user_manager::UserManager::Get()->GetActiveUser()->GetAccountId(),
      base::BindOnce(&DateTimeHandler::OnParentAccessValidation,
                     weak_ptr_factory_.GetWeakPtr()),
      SupervisedAction::kUpdateTimezone, false /* extra_dimmer */,
      base::Time::Now());
}

void DateTimeHandler::OnParentAccessValidation(bool success) {
  if (success) {
    FireWebUIListener("access-code-validation-complete");
  }
}

void DateTimeHandler::NotifyTimezoneAutomaticDetectionPolicy() {
  bool managed = !IsTimezoneAutomaticDetectionUserEditable();
  bool force_enabled = managed && g_browser_process->platform_part()
                                      ->GetTimezoneResolverManager()
                                      ->ShouldApplyResolvedTimezone();

  FireWebUIListener("time-zone-auto-detect-policy", base::Value(managed),
                    base::Value(force_enabled));
}

void DateTimeHandler::SystemClockCanSetTimeChanged(bool can_set_time) {
  FireWebUIListener("can-set-date-time-changed", base::Value(can_set_time));
}

}  // namespace ash::settings