chromium/third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.cc

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

#include "third_party/blink/renderer/platform/fonts/android/font_unique_name_lookup_android.h"

#include "base/files/file.h"
#include "base/logging.h"
#include "base/metrics/histogram_macros.h"
#include "base/timer/elapsed_timer.h"
#include "skia/ext/font_utils.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/font_unique_name_lookup/icu_fold_case_util.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/instrumentation/histogram.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/functional.h"
#include "third_party/skia/include/core/SkData.h"
#include "third_party/skia/include/core/SkFontMgr.h"
#include "third_party/skia/include/core/SkRefCnt.h"
#include "third_party/skia/include/core/SkTypeface.h"

namespace blink {
namespace {

void LogFontLatencyFailure(base::TimeDelta delta) {
  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
      "Android.FontLookup.Blink.DLFontsLatencyFailure2", delta,
      base::Microseconds(1), base::Seconds(10), 50);
}

void LogFontLatencySuccess(base::TimeDelta delta) {
  UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
      "Android.FontLookup.Blink.DLFontsLatencySuccess2", delta,
      base::Microseconds(1), base::Seconds(10), 50);
}
}  // namespace

FontUniqueNameLookupAndroid::~FontUniqueNameLookupAndroid() = default;

void FontUniqueNameLookupAndroid::PrepareFontUniqueNameLookup(
    NotifyFontUniqueNameLookupReady callback) {
  DCHECK(!font_table_matcher_.get());
  DCHECK(RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled());

  pending_callbacks_.push_back(std::move(callback));

  // We bind the service on the first call to PrepareFontUniqueNameLookup. After
  // that we do not need to make additional IPC requests to retrieve the table.
  // The observing callback was added to the list, so all clients will be
  // informed when the lookup table has arrived.
  if (pending_callbacks_.size() > 1)
    return;

  EnsureServiceConnected();

  firmware_font_lookup_service_->GetUniqueNameLookupTable(WTF::BindOnce(
      &FontUniqueNameLookupAndroid::ReceiveReadOnlySharedMemoryRegion,
      WTF::Unretained(this)));
}

bool FontUniqueNameLookupAndroid::IsFontUniqueNameLookupReadyForSyncLookup() {
  if (!RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled())
    return true;

  EnsureServiceConnected();

  // If we have the table already, we're ready for sync lookups.
  if (font_table_matcher_.get())
    return true;

  // We have previously determined via IPC whether the table is sync available.
  // Return what we found out before.
  if (sync_available_.has_value())
    return sync_available_.value();

  // If we haven't asked the browser before, probe synchronously - if the table
  // is available on the browser side, we can continue with sync operation.

  bool sync_available_from_mojo = false;
  base::ReadOnlySharedMemoryRegion shared_memory_region;
  firmware_font_lookup_service_->GetUniqueNameLookupTableIfAvailable(
      &sync_available_from_mojo, &shared_memory_region);
  sync_available_ = sync_available_from_mojo;

  if (*sync_available_) {
    // Adopt the shared memory region, do not notify anyone in callbacks as
    // PrepareFontUniqueNameLookup must not have been called yet. Just return
    // true from this function.
    // TODO(crbug.com/1416529): Investigate why pending_callbacks is not 0 in
    // some cases when kPrefetchFontLookupTables is enabled
    if (pending_callbacks_.size() != 0) {
      LOG(WARNING) << "Number of pending callbacks not zero";
    }
    ReceiveReadOnlySharedMemoryRegion(std::move(shared_memory_region));
  }

  // If it wasn't available synchronously LocalFontFaceSource has to call
  // PrepareFontUniqueNameLookup.
  return *sync_available_;
}

sk_sp<SkTypeface> FontUniqueNameLookupAndroid::MatchUniqueName(
    const String& font_unique_name) {
  if (!IsFontUniqueNameLookupReadyForSyncLookup())
    return nullptr;
  sk_sp<SkTypeface> result_font =
      MatchUniqueNameFromFirmwareFonts(font_unique_name);
  if (result_font)
    return result_font;
  if (RuntimeEnabledFeatures::AndroidDownloadableFontsMatchingEnabled()) {
    return MatchUniqueNameFromDownloadableFonts(font_unique_name);
  } else {
    return nullptr;
  }
}

void FontUniqueNameLookupAndroid::Init() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (RuntimeEnabledFeatures::AndroidDownloadableFontsMatchingEnabled()) {
    EnsureServiceConnected();
    if (android_font_lookup_service_) {
      // WTF::Unretained is safe here because |this| owns
      // |android_font_lookup_service_|.
      android_font_lookup_service_->FetchAllFontFiles(
          WTF::BindOnce(&FontUniqueNameLookupAndroid::FontsPrefetched,
                        WTF::Unretained(this)));
    }
  }
  if (base::FeatureList::IsEnabled(features::kPrefetchFontLookupTables) &&
      RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled()) {
    // This call primes IsFontUniqueNameLookupReadyForSyncLookup() by
    // asynchronously fetching the font table so it will be ready when needed.
    // It isn't needed now, so base::DoNothing() is passed as the callback.
    PrepareFontUniqueNameLookup(base::DoNothing());
  }
}

void FontUniqueNameLookupAndroid::EnsureServiceConnected() {
  if (firmware_font_lookup_service_ &&
      (!RuntimeEnabledFeatures::AndroidDownloadableFontsMatchingEnabled() ||
       android_font_lookup_service_))
    return;

  if (!firmware_font_lookup_service_) {
    Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
        firmware_font_lookup_service_.BindNewPipeAndPassReceiver());
  }

  if (RuntimeEnabledFeatures::AndroidDownloadableFontsMatchingEnabled() &&
      !android_font_lookup_service_) {
    Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
        android_font_lookup_service_.BindNewPipeAndPassReceiver());
  }
}

void FontUniqueNameLookupAndroid::ReceiveReadOnlySharedMemoryRegion(
    base::ReadOnlySharedMemoryRegion shared_memory_region) {
  font_table_matcher_ =
      std::make_unique<FontTableMatcher>(shared_memory_region.Map());
  while (!pending_callbacks_.empty()) {
    NotifyFontUniqueNameLookupReady callback = pending_callbacks_.TakeFirst();
    std::move(callback).Run();
  }
}

sk_sp<SkTypeface> FontUniqueNameLookupAndroid::MatchUniqueNameFromFirmwareFonts(
    const String& font_unique_name) {
  std::optional<FontTableMatcher::MatchResult> match_result =
      font_table_matcher_->MatchName(font_unique_name.Utf8().c_str());
  if (!match_result) {
    return nullptr;
  }
  sk_sp<SkFontMgr> mgr = skia::DefaultFontMgr();
  return mgr->makeFromFile(match_result->font_path.c_str(),
                           match_result->ttc_index);
}

bool FontUniqueNameLookupAndroid::RequestedNameInQueryableFonts(
    const String& font_unique_name) {
  if (!queryable_fonts_) {
    SCOPED_UMA_HISTOGRAM_TIMER("Android.FontLookup.Blink.GetTableLatency");
    Vector<String> retrieved_fonts;
    android_font_lookup_service_->GetUniqueNameLookupTable(&retrieved_fonts);
    queryable_fonts_ = std::move(retrieved_fonts);
  }
  return queryable_fonts_ && queryable_fonts_->Contains(String::FromUTF8(
                                 IcuFoldCase(font_unique_name.Utf8()).c_str()));
}

sk_sp<SkTypeface>
FontUniqueNameLookupAndroid::MatchUniqueNameFromDownloadableFonts(
    const String& font_unique_name) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  if (!android_font_lookup_service_.is_bound()) {
    LOG(ERROR) << "Service not connected.";
    return nullptr;
  }

  if (!RequestedNameInQueryableFonts(font_unique_name))
    return nullptr;

  base::File font_file;
  String case_folded_unique_font_name =
      String::FromUTF8(IcuFoldCase(font_unique_name.Utf8()).c_str());

  base::ElapsedTimer elapsed_timer;

  auto it = prefetched_font_map_.find(case_folded_unique_font_name);
  if (it != prefetched_font_map_.end()) {
    font_file = it->value.Duplicate();
  } else if (!android_font_lookup_service_->MatchLocalFontByUniqueName(
                 case_folded_unique_font_name, &font_file)) {
    LOG(ERROR)
        << "Mojo method returned false for case-folded unique font name: "
        << case_folded_unique_font_name;
    LogFontLatencyFailure(elapsed_timer.Elapsed());
    return nullptr;
  }

  if (!font_file.IsValid()) {
    LOG(ERROR) << "Received platform font handle invalid, fd: "
               << font_file.GetPlatformFile();
    LogFontLatencyFailure(elapsed_timer.Elapsed());
    return nullptr;
  }

  sk_sp<SkData> font_data = SkData::MakeFromFD(font_file.GetPlatformFile());

  if (!font_data || font_data->isEmpty()) {
    LOG(ERROR) << "Received file descriptor has 0 size.";
    LogFontLatencyFailure(elapsed_timer.Elapsed());
    return nullptr;
  }

  sk_sp<SkFontMgr> mgr = skia::DefaultFontMgr();
  sk_sp<SkTypeface> return_typeface = mgr->makeFromData(font_data);

  if (!return_typeface) {
    LogFontLatencyFailure(elapsed_timer.Elapsed());
    LOG(ERROR) << "Cannot instantiate SkTypeface from font blob SkData.";
  }

  LogFontLatencySuccess(elapsed_timer.Elapsed());
  return return_typeface;
}

void FontUniqueNameLookupAndroid::FontsPrefetched(
    HashMap<String, base::File> font_files) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  prefetched_font_map_ = std::move(font_files);

  if (base::FeatureList::IsEnabled(features::kPrefetchFontLookupTables)) {
    // The |prefetched_font_map_| contains all the fonts that are available from
    // the AndroidFontLookup service. We can directly set |queryable_fonts_|
    // here from the map keys since |queryable_fonts_| is used to check which
    // fonts can be fetched from the AndroidFontLookup service.
    queryable_fonts_ = Vector<String>();
    CopyKeysToVector(prefetched_font_map_, *queryable_fonts_);
  }
}

}  // namespace blink