910e62b5创建于 1月15日历史提交
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.


#include "ui/base/webui/web_ui_util.h"

#include <optional>
#include <string>
#include <string_view>
#include <vector>

#include "base/base64.h"
#include "base/check.h"
#include "base/i18n/rtl.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/strings/escape.h"
#include "base/strings/string_number_conversions.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/template_expressions.h"
#include "ui/base/window_open_disposition.h"
#include "ui/base/window_open_disposition_utils.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/font.h"
#include "ui/strings/grit/app_locale_settings.h"
#include "ui/webui/resources/grit/webui_resources.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_WIN)
#include "base/win/windows_version.h"
#endif

namespace webui {
namespace {

// Generous cap to guard against out-of-memory issues.
constexpr float kMaxScaleFactor = 1000.0f;

std::string GetFontFamilyMd() {
#if BUILDFLAG(IS_LINUX)
  return "Roboto, " + GetFontFamily();
#else
  return GetFontFamily();
#endif
}

std::string GetWebUiCssTextDefaults(const std::string& css_template) {
  ui::TemplateReplacements placeholders;
  placeholders["textdirection"] = GetTextDirection();
  placeholders["fontfamily"] = GetFontFamily();
  placeholders["fontfamilyMd"] = GetFontFamilyMd();
  placeholders["fontsize"] = GetFontSize();
  return ui::ReplaceTemplateExpressions(css_template, placeholders);
}

}  // namespace

std::string GetBitmapDataUrl(const SkBitmap& bitmap) {
  TRACE_EVENT2("ui", "GetBitmapDataUrl", "width", bitmap.width(), "height",
               bitmap.height());
  std::optional<std::vector<uint8_t>> output =
      gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, /*discard_transparency=*/false);
  return GetPngDataUrl(output.value_or(std::vector<uint8_t>()));
}

std::string GetPngDataUrl(base::span<const uint8_t> data) {
  std::string output = "data:image/png;base64,";
  base::Base64EncodeAppend(data, &output);
  return output;
}

WindowOpenDisposition GetDispositionFromClick(const base::Value::List& list,
                                              size_t start_index) {
  double button = list[start_index].GetDouble();
  bool alt_key = list[start_index + 1].GetBool();
  bool ctrl_key = list[start_index + 2].GetBool();
  bool meta_key = list[start_index + 3].GetBool();
  bool shift_key = list[start_index + 4].GetBool();

  return ui::DispositionFromClick(
      button == 1.0, alt_key, ctrl_key, meta_key, shift_key);
}

bool ParseScaleFactor(std::string_view identifier, float* scale_factor) {
  *scale_factor = 1.0f;
  if (identifier.empty()) {
    DLOG(WARNING) << "Invalid scale factor format: " << identifier;
    return false;
  }

  if (*identifier.rbegin() != 'x') {
    DLOG(WARNING) << "Invalid scale factor format: " << identifier;
    return false;
  }

  double scale = 0;
  std::string stripped(identifier.substr(0, identifier.length() - 1));
  if (!base::StringToDouble(stripped, &scale)) {
    DLOG(WARNING) << "Invalid scale factor format: " << identifier;
    return false;
  }
  if (scale <= 0) {
    DLOG(WARNING) << "Invalid non-positive scale factor: " << identifier;
    return false;
  }
  if (scale > kMaxScaleFactor) {
    DLOG(WARNING) << "Invalid scale factor, too large: " << identifier;
    return false;
  }
  *scale_factor = static_cast<float>(scale);
  return true;
}

// Parse a formatted frame index string into int and sets to |frame_index|.
bool ParseFrameIndex(std::string_view identifier, int* frame_index) {
  *frame_index = -1;
  if (identifier.empty()) {
    DLOG(WARNING) << "Invalid frame index format: " << identifier;
    return false;
  }

  if (*identifier.rbegin() != ']') {
    DLOG(WARNING) << "Invalid frame index format: " << identifier;
    return false;
  }

  unsigned frame = 0;
  if (!base::StringToUint(identifier.substr(0, identifier.length() - 1),
                          &frame)) {
    DLOG(WARNING) << "Invalid frame index format: " << identifier;
    return false;
  }
  *frame_index = static_cast<int>(frame);
  return true;
}

void ParsePathAndImageSpec(const GURL& url,
                           std::string* path,
                           float* scale_factor,
                           int* frame_index) {
  *path = base::UnescapeBinaryURLComponent(url.path().substr(1));
  if (scale_factor)
    *scale_factor = 1.0f;
  if (frame_index)
    *frame_index = -1;

  // Detect and parse resource string ending in @<scale>x.
  std::size_t pos = path->rfind('@');
  if (pos != std::string::npos) {
    std::string_view stripped_path(*path);
    float factor;

    if (ParseScaleFactor(stripped_path.substr(
            pos + 1, stripped_path.length() - pos - 1), &factor)) {
      // Strip scale factor specification from path.
      stripped_path.remove_suffix(stripped_path.length() - pos);
      *path = std::string(stripped_path);
    }
    if (scale_factor)
      *scale_factor = factor;
  }

  // Detect and parse resource string ending in [<frame>].
  pos = path->rfind('[');
  if (pos != std::string::npos) {
    std::string_view stripped_path(*path);
    int index;

    if (ParseFrameIndex(
            stripped_path.substr(pos + 1, stripped_path.length() - pos - 1),
            &index)) {
      // Strip frame index specification from path.
      stripped_path.remove_suffix(stripped_path.length() - pos);
      *path = std::string(stripped_path);
    }
    if (frame_index)
      *frame_index = index;
  }
}

void SetLoadTimeDataDefaults(const std::string& app_locale,
                             base::Value::Dict* localized_strings) {
  localized_strings->Set("fontfamily", GetFontFamily());
  localized_strings->Set("fontfamilyMd", GetFontFamilyMd());
  localized_strings->Set("fontsize", GetFontSize());
  localized_strings->Set("language", l10n_util::GetLanguage(app_locale));
  localized_strings->Set("textdirection", GetTextDirection());
}

void SetLoadTimeDataDefaults(const std::string& app_locale,
                             ui::TemplateReplacements* replacements) {
  (*replacements)["fontfamily"] = GetFontFamily();
  (*replacements)["fontfamilyMd"] = GetFontFamilyMd();
  (*replacements)["fontsize"] = GetFontSize();
  (*replacements)["language"] = l10n_util::GetLanguage(app_locale);
  (*replacements)["textdirection"] = GetTextDirection();
}

std::string GetWebUiCssTextDefaults() {
  const ui::ResourceBundle& resource_bundle =
      ui::ResourceBundle::GetSharedInstance();
  return GetWebUiCssTextDefaults(
      resource_bundle.LoadDataResourceString(IDR_WEBUI_CSS_TEXT_DEFAULTS_CSS));
}

void AppendWebUiCssTextDefaults(std::string* html) {
  html->append("<style>");
  html->append(GetWebUiCssTextDefaults());
  html->append("</style>");
}

std::string GetFontFamily() {
  std::string font_family = l10n_util::GetStringUTF8(IDS_WEB_FONT_FAMILY);

#if BUILDFLAG(IS_LINUX)
  std::string font_name = ui::ResourceBundle::GetSharedInstance()
                              .GetFont(ui::ResourceBundle::BaseFont)
                              .GetFontName();
  // Wrap |font_name| with quotes to ensure it will always be parsed correctly
  // in CSS.
  font_family = "\"" + font_name + "\", " + font_family;
#endif  // BUILDFLAG(IS_LINUX)

  return font_family;
}

std::string GetFontSize() {
  return l10n_util::GetStringUTF8(IDS_WEB_FONT_SIZE);
}

std::string GetTextDirection() {
  return base::i18n::IsRTL() ? "rtl" : "ltr";
}

std::string GetLocalizedHtml(std::string_view html_template,
                             const base::Value::Dict& strings) {
  // Populate $i18n{...} placeholders.
  ui::TemplateReplacements replacements;
  ui::TemplateReplacementsFromDictionaryValue(strings, &replacements);
  std::string output =
      ui::ReplaceTemplateExpressions(html_template, replacements);

  // Inject data to the UI that will be used to populate loadTimeData upon
  // initialization.
  std::optional<std::string> json = base::WriteJson(strings);
  CHECK(json);
  output.append("<script>");
  output.append("var loadTimeDataRaw = ");
  output.append(*json);
  output.append(";");
  output.append("</script>");

  return output;
}

}  // namespace webui