chromium/chrome/browser/ui/webui/ash/settings/pages/crostini/guest_os_handler.cc

// Copyright 2021 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/crostini/guest_os_handler.h"

#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/guest_os/guest_os_share_path.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_thread.h"

namespace ash::settings {

namespace {

base::Value::List GetSharableUsbDevices(CrosUsbDetector* detector) {
  base::Value::List usb_devices_list;
  for (const auto& device : detector->GetShareableDevices()) {
    base::Value::Dict device_info;
    device_info.Set("guid", device.guid);
    device_info.Set("label", device.label);
    if (device.shared_guest_id.has_value()) {
      base::Value::Dict guest_id;
      guest_id.Set("vm_name", device.shared_guest_id->vm_name);
      guest_id.Set("container_name", device.shared_guest_id->container_name);
      device_info.Set("guestId", std::move(guest_id));
    }
    device_info.Set("vendorId", base::StringPrintf("%04x", device.vendor_id));
    device_info.Set("productId", base::StringPrintf("%04x", device.product_id));
    device_info.Set("serialNumber", device.serial_number);
    device_info.Set("promptBeforeSharing", device.prompt_before_sharing);
    usb_devices_list.Append(std::move(device_info));
  }
  return usb_devices_list;
}

}  // namespace

GuestOsHandler::GuestOsHandler(Profile* profile) : profile_(profile) {}

GuestOsHandler::~GuestOsHandler() = default;

void GuestOsHandler::RegisterMessages() {
  web_ui()->RegisterMessageCallback(
      "getGuestOsSharedPathsDisplayText",
      base::BindRepeating(
          &GuestOsHandler::HandleGetGuestOsSharedPathsDisplayText,
          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "removeGuestOsSharedPath",
      base::BindRepeating(&GuestOsHandler::HandleRemoveGuestOsSharedPath,
                          base::Unretained(this)));
  web_ui()->RegisterMessageCallback(
      "notifyGuestOsSharedUsbDevicesPageReady",
      base::BindRepeating(
          &GuestOsHandler::HandleNotifyGuestOsSharedUsbDevicesPageReady,
          weak_ptr_factory_.GetWeakPtr()));
  web_ui()->RegisterMessageCallback(
      "setGuestOsUsbDeviceShared",
      base::BindRepeating(&GuestOsHandler::HandleSetGuestOsUsbDeviceShared,
                          weak_ptr_factory_.GetWeakPtr()));
}

void GuestOsHandler::OnJavascriptAllowed() {
  if (auto* detector = CrosUsbDetector::Get()) {
    cros_usb_device_observation_.Observe(detector);
  }
}

void GuestOsHandler::OnJavascriptDisallowed() {
  cros_usb_device_observation_.Reset();
}

void GuestOsHandler::HandleGetGuestOsSharedPathsDisplayText(
    const base::Value::List& args) {
  AllowJavascript();
  CHECK_EQ(2U, args.size());
  std::string callback_id = args[0].GetString();

  base::Value::List texts;
  for (const auto& path : args[1].GetList()) {
    texts.Append(file_manager::util::GetPathDisplayTextForSettings(
        profile_, path.GetString()));
  }
  ResolveJavascriptCallback(base::Value(callback_id), texts);
}

void GuestOsHandler::HandleRemoveGuestOsSharedPath(
    const base::Value::List& args) {
  CHECK_EQ(3U, args.size());
  std::string callback_id = args[0].GetString();
  std::string vm_name = args[1].GetString();
  std::string path = args[2].GetString();

  guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath(
      vm_name, base::FilePath(path),
      /*unpersist=*/true,
      base::BindOnce(&GuestOsHandler::OnGuestOsSharedPathRemoved,
                     weak_ptr_factory_.GetWeakPtr(), callback_id, path));
}

void GuestOsHandler::OnGuestOsSharedPathRemoved(
    const std::string& callback_id,
    const std::string& path,
    bool success,
    const std::string& failure_reason) {
  if (!success) {
    LOG(ERROR) << "Error unsharing " << path << ": " << failure_reason;
  }
  ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
}

void GuestOsHandler::HandleNotifyGuestOsSharedUsbDevicesPageReady(
    const base::Value::List& args) {
  AllowJavascript();
  OnUsbDevicesChanged();
}

void GuestOsHandler::HandleSetGuestOsUsbDeviceShared(
    const base::Value::List& args) {
  CHECK_EQ(4U, args.size());
  CrosUsbDetector* detector = CrosUsbDetector::Get();
  if (!detector) {
    return;
  }

  const auto guest_id =
      guest_os::GuestId(args[0].GetString(), args[1].GetString());
  const std::string& guid = args[2].GetString();
  bool shared = args[3].GetBool();

  if (shared) {
    detector->AttachUsbDeviceToGuest(guest_id, guid, base::DoNothing());
    return;
  }
  detector->DetachUsbDeviceFromVm(guest_id.vm_name, guid, base::DoNothing());
}

void GuestOsHandler::OnUsbDevicesChanged() {
  CrosUsbDetector* detector = CrosUsbDetector::Get();
  DCHECK(detector);  // This callback is called by the detector.
  FireWebUIListener("guest-os-shared-usb-devices-changed",
                    GetSharableUsbDevices(detector));
}

}  // namespace ash::settings