chromium/ash/assistant/util/resource_util.cc

// 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 "ash/assistant/util/resource_util.h"

#include <map>
#include <sstream>
#include <string>
#include <string_view>

#include "base/logging.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chromeos/ui/vector_icons/vector_icons.h"
#include "net/base/url_util.h"

namespace ash {
namespace assistant {
namespace util {

namespace {

// Constants -------------------------------------------------------------------

// Prefix for all resource links.
constexpr char kResourceLinkPrefix[] = "googleassistant://resource";

// Param keys.
constexpr char kColorParamKey[] = "color";
constexpr char kNameParamKey[] = "name";
constexpr char kTypeParamKey[] = "type";

// Resource types.
constexpr char kIconResourceType[] = "icon";

// Icon names.
constexpr char kAssistantIconName[] = "assistant";
constexpr char kCalculateIconName[] = "calculate";
constexpr char kConversionPathIconName[] = "conversationPath";
constexpr char kPersonPinCircleIconName[] = "personPinCircle";
constexpr char kScreenshotIconName[] = "screenshot";
constexpr char kSentimentVerySatisfiedIconName[] = "sentimentVerySatisfied";
constexpr char kStraightenIconName[] = "straighten";
constexpr char kTimerIconName[] = "timer";
constexpr char kTranslateIconName[] = "translate";

// Helpers ---------------------------------------------------------------------

SkColor ToColor(const std::string& aarrggbb) {
  SkColor color = gfx::kPlaceholderColor;
  std::istringstream hex_stream(aarrggbb);
  hex_stream >> std::hex >> color;
  return color;
}

IconName ToIconName(const std::string& name) {
  const std::map<std::string, IconName> icon_names_by_string_name = {
      {kAssistantIconName, IconName::kAssistant},
      {kCalculateIconName, IconName::kCalculate},
      {kConversionPathIconName, IconName::kConversionPath},
      {kPersonPinCircleIconName, IconName::kPersonPinCircle},
      {kScreenshotIconName, IconName::kScreenshot},
      {kSentimentVerySatisfiedIconName, IconName::kSentimentVerySatisfied},
      {kStraightenIconName, IconName::kStraighten},
      {kTimerIconName, IconName::kTimer},
      {kTranslateIconName, IconName::kTranslate},
  };
  auto it = icon_names_by_string_name.find(name);
  DCHECK(it != icon_names_by_string_name.end());
  return it->second;
}

std::string ToString(SkColor color) {
  std::stringstream hex_stream;
  hex_stream << std::hex << color;
  return hex_stream.str();
}

std::string ToString(IconName name) {
  switch (name) {
    case IconName::kAssistant:
      return kAssistantIconName;
    case IconName::kCalculate:
      return kCalculateIconName;
    case IconName::kConversionPath:
      return kConversionPathIconName;
    case IconName::kPersonPinCircle:
      return kPersonPinCircleIconName;
    case IconName::kScreenshot:
      return kScreenshotIconName;
    case IconName::kSentimentVerySatisfied:
      return kSentimentVerySatisfiedIconName;
    case IconName::kStraighten:
      return kStraightenIconName;
    case IconName::kTimer:
      return kTimerIconName;
    case IconName::kTranslate:
      return kTranslateIconName;
  }
  NOTREACHED();
}

std::string ToString(ResourceLinkParam param) {
  switch (param) {
    case ResourceLinkParam::kColor:
      return kColorParamKey;
    case ResourceLinkParam::kName:
      return kNameParamKey;
    case ResourceLinkParam::kType:
      return kTypeParamKey;
  }
  NOTREACHED();
}

std::string ToString(ResourceLinkType type) {
  switch (type) {
    case ResourceLinkType::kIcon:
      return kIconResourceType;
    case ResourceLinkType::kUnsupported:
      return std::string();
  }
  NOTREACHED();
}

ResourceLinkType ToType(const std::string& type) {
  if (type == kIconResourceType)
    return ResourceLinkType::kIcon;
  return ResourceLinkType::kUnsupported;
}

const gfx::VectorIcon& ToVectorIcon(IconName name) {
  switch (name) {
    case IconName::kAssistant:
      return chromeos::kAssistantIcon;
    case IconName::kCalculate:
      return chromeos::kCalculateIcon;
    case IconName::kConversionPath:
      return chromeos::kConversionPathIcon;
    case IconName::kPersonPinCircle:
      return chromeos::kPersonPinCircleIcon;
    case IconName::kScreenshot:
      return chromeos::kScreenshotIcon;
    case IconName::kSentimentVerySatisfied:
      return chromeos::kSentimentVerySatisfiedIcon;
    case IconName::kStraighten:
      return chromeos::kStraightenIcon;
    case IconName::kTimer:
      return chromeos::kTimerIcon;
    case IconName::kTranslate:
      return chromeos::kTranslateIcon;
  }
  NOTREACHED();
}

const gfx::VectorIcon& ToVectorIcon(const std::string& name) {
  return ToVectorIcon(ToIconName(name));
}

std::optional<std::string> GetParam(const GURL& url, ResourceLinkParam param) {
  if (!url.has_query())
    return std::nullopt;

  const std::string param_key = ToString(param);
  const std::string_view query_piece = url.query_piece();
  url::Component query(0, query_piece.length()), key, value;
  while (url::ExtractQueryKeyValue(query_piece, &query, &key, &value)) {
    if (query_piece.substr(key.begin, base::checked_cast<size_t>(key.len)) ==
        param_key) {
      return std::string(query_piece.substr(
          value.begin, base::checked_cast<size_t>(value.len)));
    }
  }

  return std::nullopt;
}

}  // namespace

// Utilities -------------------------------------------------------------------

GURL AppendOrReplaceColorParam(const GURL& resource_link, SkColor color) {
  DCHECK(IsResourceLinkType(resource_link, ResourceLinkType::kIcon));
  return net::AppendOrReplaceQueryParameter(
      resource_link, ToString(ResourceLinkParam::kColor), ToString(color));
}

GURL CreateIconResourceLink(IconName name, std::optional<SkColor> color) {
  GURL icon_resource_link(kResourceLinkPrefix);
  icon_resource_link = net::AppendOrReplaceQueryParameter(
      icon_resource_link, ToString(ResourceLinkParam::kType),
      ToString(ResourceLinkType::kIcon));
  icon_resource_link = net::AppendOrReplaceQueryParameter(
      icon_resource_link, ToString(ResourceLinkParam::kName), ToString(name));
  if (color.has_value()) {
    icon_resource_link = net::AppendOrReplaceQueryParameter(
        icon_resource_link, ToString(ResourceLinkParam::kColor),
        ToString(color.value()));
  }
  return icon_resource_link;
}

gfx::ImageSkia CreateVectorIcon(const GURL& url, int size) {
  if (!IsResourceLinkType(url, ResourceLinkType::kIcon))
    return gfx::ImageSkia();

  const gfx::VectorIcon& icon =
      ToVectorIcon((GetParam(url, ResourceLinkParam::kName).value_or("")));

  SkColor color = ToColor(GetParam(url, ResourceLinkParam::kColor)
                              .value_or(ToString(gfx::kPlaceholderColor)));

  return gfx::CreateVectorIcon(gfx::IconDescription(icon, size, color));
}

ResourceLinkType GetResourceLinkType(const GURL& url) {
  return IsResourceLinkUrl(url)
             ? ToType(GetParam(url, ResourceLinkParam::kType).value_or(""))
             : ResourceLinkType::kUnsupported;
}

bool IsResourceLinkType(const GURL& url, ResourceLinkType type) {
  return GetResourceLinkType(url) == type;
}

bool IsResourceLinkUrl(const GURL& url) {
  return base::StartsWith(url.spec(), kResourceLinkPrefix,
                          base::CompareCase::SENSITIVE);
}

}  // namespace util
}  // namespace assistant
}  // namespace ash