chromium/content/renderer/font_data/font_data_manager.cc

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

#include "content/renderer/font_data/font_data_manager.h"

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

#include "base/functional/bind.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/task/thread_pool.h"
#include "base/trace_event/trace_event.h"
#include "content/public/child/child_thread.h"
#include "content/public/common/content_features.h"
#include "content/public/renderer/render_thread.h"
#include "third_party/skia/include/ports/SkFontMgr_empty.h"
#include "third_party/skia/src/ports/SkTypeface_win_dw.h"  // nogncheck

namespace font_data_service {

namespace {

const int kTypefaceCacheSize = 128;

// Binds a pending receiver. Must be invoked from the main thread.
void BindHostReceiverOnMainThread(
    mojo::PendingReceiver<font_data_service::mojom::FontDataService>
        pending_receiver) {
  content::ChildThread::Get()->BindHostReceiver(std::move(pending_receiver));
}

mojom::TypefaceSlant ConvertToMojomFontStyle(SkFontStyle::Slant slant) {
  switch (slant) {
    case SkFontStyle::Slant::kUpright_Slant:
      return mojom::TypefaceSlant::kRoman;
    case SkFontStyle::Slant::kItalic_Slant:
      return mojom::TypefaceSlant::kItalic;
    case SkFontStyle::Slant::kOblique_Slant:
      return mojom::TypefaceSlant::kOblique;
  }
  NOTREACHED();
}

}  // namespace

FontDataManager::FontDataManager()
    : typeface_cache_(kTypefaceCacheSize),
      custom_fnt_mgr_(SkFontMgr_New_Custom_Empty()),
      main_task_runner_(base::SingleThreadTaskRunner::GetCurrentDefault()) {
  CHECK(content::RenderThread::IsMainThread());
}

FontDataManager::~FontDataManager() = default;

int FontDataManager::onCountFamilies() const {
  NOTREACHED();
}

void FontDataManager::onGetFamilyName(int index,
                                      SkString* requested_family_name) const {
  NOTREACHED();
}

sk_sp<SkFontStyleSet> FontDataManager::onCreateStyleSet(int index) const {
  NOTREACHED();
}

sk_sp<SkFontStyleSet> FontDataManager::onMatchFamily(
    const char requested_family_name[]) const {
  NOTREACHED();
}

sk_sp<SkTypeface> FontDataManager::onMatchFamilyStyle(
    const char requested_family_name[],
    const SkFontStyle& requested_style) const {
  // NOTE: requested_family_name can be null to get default font.
  std::string cpp_requested_family_name;
  if (requested_family_name) {
    cpp_requested_family_name = requested_family_name;
  }

  MatchFamilyRequest match_request{.name = cpp_requested_family_name,
                                   .weight = requested_style.weight(),
                                   .width = requested_style.width(),
                                   .slant = requested_style.slant()};
  {
    base::AutoLock locked(lock_);
    auto iter = typeface_cache_.Get(match_request);
    if (iter != typeface_cache_.end()) {
      return iter->second;
    }
  }
  // Proxy the font request to the font service.
  mojom::TypefaceStylePtr style(mojom::TypefaceStyle::New());
  style->weight = requested_style.weight();
  style->width = requested_style.width();
  style->slant = ConvertToMojomFontStyle(requested_style.slant());

  mojom::MatchFamilyNameResultPtr match_result;
  {
    TRACE_EVENT1("fonts", "FontDataManager::onMakeFromStreamArgs - Remote Call",
                 "family_name", cpp_requested_family_name);
    GetRemoteFontDataService().MatchFamilyName(cpp_requested_family_name,
                                               std::move(style), &match_result);
  }

  // Create the resulting typeface from the bytes received from the font
  // service.
  sk_sp<SkTypeface> typeface;
  if (match_result) {
    base::ReadOnlySharedMemoryRegion font_data_memory_region =
        std::move(match_result->region);
    const void* mapped_memory = nullptr;
    size_t mapped_size = 0;
    // Map the memory (if needed) and keep it alive for the lifetime of this
    // process. A cache is used to avoid mapping the same memory space multiple
    // time.
    {
      base::AutoLock locked(lock_);
      const auto iter =
          mapped_regions_.lower_bound(font_data_memory_region.GetGUID());
      if (iter != mapped_regions_.end() &&
          iter->first == font_data_memory_region.GetGUID()) {
        mapped_memory = iter->second.memory();
        mapped_size = iter->second.size();
      } else {
        base::ReadOnlySharedMemoryMapping mapping =
            font_data_memory_region.Map();
        if (mapping.IsValid()) {
          mapped_memory = mapping.memory();
          mapped_size = mapping.size();
          mapped_regions_.insert_or_assign(
              iter, font_data_memory_region.GetGUID(), std::move(mapping));
        }
      }
    }

    // Create the memory stream from the mapped memory.
    if (mapped_memory && mapped_size > 0) {
      SkFontArguments args;
      args.setCollectionIndex(match_result->ttc_index);
      // Convert the variation position from mojom to an SkFontArguments struct.
      std::vector<SkFontArguments::VariationPosition::Coordinate> typeface_axis;
      if (match_result->variation_position &&
          match_result->variation_position->coordinateCount > 0) {
        typeface_axis.reserve(
            match_result->variation_position->coordinates.size());
        for (const auto& coordinate :
             match_result->variation_position->coordinates) {
          typeface_axis.push_back(
              {.axis = coordinate->axis, .value = coordinate->value});
        }
        args.setVariationDesignPosition(
            {.coordinates = typeface_axis.data(),
             .coordinateCount = base::checked_cast<int>(typeface_axis.size())});
      }
      typeface = onMakeFromStreamArgs(
          SkMemoryStream::MakeDirect(mapped_memory, mapped_size), args);
    }
  }

  // Update the cache with the resulting typeface even in case of a failure to
  // avoid calling the font service again. Failed typeface will go to the font
  // fallback stack.
  {
    base::AutoLock locked(lock_);
    typeface_cache_.Put(std::move(match_request), typeface);
  }

  return typeface;
}

sk_sp<SkTypeface> FontDataManager::onMatchFamilyStyleCharacter(
    const char requested_family_name[],
    const SkFontStyle& requested_style,
    const char* bcp47[],
    int bcp47_count,
    SkUnichar character) const {
  NOTREACHED();
}

sk_sp<SkTypeface> FontDataManager::onMakeFromData(sk_sp<SkData> data,
                                                  int ttc_index) const {
  return makeFromStream(std::make_unique<SkMemoryStream>(std::move(data)),
                        ttc_index);
}

sk_sp<SkTypeface> FontDataManager::onMakeFromStreamIndex(
    std::unique_ptr<SkStreamAsset> stream,
    int ttc_index) const {
  SkFontArguments args;
  args.setCollectionIndex(ttc_index);
  return onMakeFromStreamArgs(std::move(stream), args);
}

sk_sp<SkTypeface> FontDataManager::onMakeFromStreamArgs(
    std::unique_ptr<SkStreamAsset> stream,
    const SkFontArguments& args) const {
  TRACE_EVENT1("fonts", "FontDataManager::onMakeFromStreamArgs", "size",
               stream->getLength());
  // Experiment will test the performance of different SkTypefaces.
  // 'custom_fnt_mgr_' is a wrapper to create an SkFreeType typeface.
  return features::kSkiaFontServiceTypefaceType.Get() ==
                 features::SkiaFontServiceTypefaceType::kFreetype
             ? custom_fnt_mgr_->makeFromStream(std::move(stream), args)
             : DWriteFontTypeface::MakeFromStream(std::move(stream), args);
}

sk_sp<SkTypeface> FontDataManager::onMakeFromFile(const char path[],
                                                  int ttc_index) const {
  std::unique_ptr<SkStreamAsset> stream = SkStream::MakeFromFile(path);
  return stream ? makeFromStream(std::move(stream), ttc_index) : nullptr;
}

sk_sp<SkTypeface> FontDataManager::onLegacyMakeTypeface(
    const char requested_family_name[],
    SkFontStyle requested_style) const {
  return onMatchFamilyStyle(requested_family_name, requested_style);
}

void FontDataManager::SetFontServiceForTesting(
    mojo::PendingRemote<font_data_service::mojom::FontDataService>
        font_data_service) {
  mojo::Remote<font_data_service::mojom::FontDataService>& remote =
      font_data_service_slot_.GetOrCreateValue();
  remote.Bind(std::move(font_data_service));
}

font_data_service::mojom::FontDataService&
FontDataManager::GetRemoteFontDataService() const {
  mojo::Remote<font_data_service::mojom::FontDataService>& remote =
      font_data_service_slot_.GetOrCreateValue();

  if (!remote) {
    if (main_task_runner_->RunsTasksInCurrentSequence()) {
      BindHostReceiverOnMainThread(remote.BindNewPipeAndPassReceiver());
    } else {
      main_task_runner_->PostTask(
          FROM_HERE, base::BindOnce(&BindHostReceiverOnMainThread,
                                    remote.BindNewPipeAndPassReceiver()));
    }
  }
  return *remote;
}

}  // namespace font_data_service