chromium/content/child/dwrite_font_proxy/dwrite_font_proxy_win.cc

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

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/342213636): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "content/child/dwrite_font_proxy/dwrite_font_proxy_win.h"

#include <stddef.h>
#include <stdint.h>

#include <utility>

#include "base/check_op.h"
#include "base/debug/crash_logging.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_restrictions.h"
#include "base/trace_event/trace_event.h"
#include "content/child/dwrite_font_proxy/dwrite_localized_strings_win.h"
#include "content/public/child/child_thread.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"

namespace mswr = Microsoft::WRL;

namespace content {

namespace {

// Limits to 20 the number of family names that can be accessed by a renderer.
// This feature will be enabled for a subset of users to assess the impact on
// input delay. It will not ship as-is, because it breaks some pages. Local
// experiments show that accessing >20 fonts is typically done by fingerprinting
// scripts.
// TODO(crbug.com/40133493): Remove this feature when the experiment is
// complete. If the experiment shows a significant input delay improvement,
// replace with a more refined mitigation for pages that access many fonts.
BASE_FEATURE(kLimitFontFamilyNamesPerRenderer,
             "LimitFontFamilyNamesPerRenderer",
             base::FEATURE_DISABLED_BY_DEFAULT);
constexpr size_t kFamilyNamesLimit = 20;

// Family names that opted-out from the limit enforced by
// |kLimitFontFamilyNamesPerRenderer|. This is required because Blink uses these
// fonts as last resort and crashes if they can't be loaded.
const char16_t* kLastResortFontNames[] = {
    u"Sans",     u"Arial",   u"MS UI Gothic",    u"Microsoft Sans Serif",
    u"Segoe UI", u"Calibri", u"Times New Roman", u"Courier New"};

bool IsLastResortFontName(const std::u16string& font_name) {
  for (const char16_t* last_resort_font_name : kLastResortFontNames) {
    if (font_name == last_resort_font_name)
      return true;
  }
  return false;
}

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

}  // namespace

HRESULT DWriteFontCollectionProxy::Create(
    DWriteFontCollectionProxy** proxy_out,
    IDWriteFactory* dwrite_factory,
    mojo::PendingRemote<blink::mojom::DWriteFontProxy> proxy) {
  return Microsoft::WRL::MakeAndInitialize<DWriteFontCollectionProxy>(
      proxy_out, dwrite_factory, std::move(proxy));
}

DWriteFontCollectionProxy::DWriteFontCollectionProxy() = default;

DWriteFontCollectionProxy::~DWriteFontCollectionProxy() = default;

// TODO(crbug.com/40200438): Confirm this is useful and remove it otherwise.
void DWriteFontCollectionProxy::InitializePrewarmer() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // |PrewarmFamilyOnWorker| invokes |GetFontProxy| while holding a
  // |family_lock_|. If |GetFontProxy| is invoked from a sequence for which no
  // |mojo::Remote<blink::mojom::DWriteFontProxy>| exists in sequence-local
  // storage, it posts to the main thread to bind a new one. To avoid a
  // potential deadlock on a |family_lock_| during that operation,
  // pre-initialize the |mojo::Remote<blink::mojom::DWriteFontProxy>| for
  // |prewarm_task_runner_| here.
  //
  // Also, to avoid creating too many
  // |mojo::Remote<blink::mojom::DWriteFontProxy>|, a single SequencedTaskRunner
  // is used for all font prewarming operations.
  DCHECK(!prewarm_task_runner_);
  prewarm_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
      {base::TaskPriority::USER_VISIBLE, base::MayBlock()});

  // Let |prewarm_task_runner_| bind its |font_proxy_|. This needs to be done in
  // the sequence, because |font_proxy_| is stored in
  // |SequenceLocalStorageSlot|.
  mojo::PendingRemote<blink::mojom::DWriteFontProxy> font_proxy;
  BindHostReceiverOnMainThread(font_proxy.InitWithNewPipeAndPassReceiver());
  prewarm_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &DWriteFontCollectionProxy::BindFontProxy,
          // |DWriteFontCollectionProxy| is kept in global, never destructed.
          base::Unretained(this), std::move(font_proxy)));
}

void DWriteFontCollectionProxy::InitializePrewarmerForTesting(
    mojo::PendingRemote<blink::mojom::DWriteFontProxy> remote) {
  // See |InitializePrewarmer|.
  prewarm_task_runner_ = base::ThreadPool::CreateSequencedTaskRunner(
      {base::TaskPriority::USER_VISIBLE, base::MayBlock()});
  prewarm_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(
          &DWriteFontCollectionProxy::BindFontProxy,
          // |DWriteFontCollectionProxy| is kept in global, never destructed.
          base::Unretained(this), std::move(remote)));
}

inline DWriteFontFamilyProxy* DWriteFontCollectionProxy::GetFamilyLockRequired(
    UINT32 family_index) {
  if (family_index < families_.size())
    return families_[family_index].Get();
  return nullptr;
}

DWriteFontFamilyProxy* DWriteFontCollectionProxy::GetFamily(
    UINT32 family_index) {
  base::AutoLock families_lock(families_lock_);
  return GetFamilyLockRequired(family_index);
}

HRESULT DWriteFontCollectionProxy::FindFamilyName(const WCHAR* family_name,
                                                  UINT32* index,
                                                  BOOL* exists) {
  DCHECK(family_name);
  static_assert(sizeof(WCHAR) == sizeof(char16_t), "WCHAR should be UTF-16.");
  const std::u16string name(reinterpret_cast<const char16_t*>(family_name));
  return FindFamilyName(name, index, exists);
}

HRESULT DWriteFontCollectionProxy::FindFamilyName(
    const std::u16string& family_name,
    UINT32* index,
    BOOL* exists) {
  DCHECK(index);
  DCHECK(exists);
  TRACE_EVENT0("dwrite,fonts", "FontProxy::FindFamilyName");

  HRESULT hr = S_OK;
  if (std::optional<UINT32> family_index = FindFamilyIndex(family_name, &hr)) {
    DCHECK_EQ(hr, S_OK);
    DCHECK(IsValidFamilyIndex(*family_index));
    *index = *family_index;
    *exists = TRUE;
  } else {
    // |hr| can be failures, or |S_OK| if the |family_name| is not found.
    *exists = FALSE;
    *index = kFamilyNotFound;
  }
  return hr;
}

std::optional<UINT32> DWriteFontCollectionProxy::FindFamilyIndex(
    const std::u16string& family_name,
    HRESULT* hresult_out) {
  DCHECK(!hresult_out || *hresult_out == S_OK);
  {
    base::AutoLock families_lock(families_lock_);
    auto iter = family_names_.find(family_name);
    if (iter != family_names_.end()) {
      if (iter->second != kFamilyNotFound)
        return iter->second;
      return std::nullopt;
    }

    if (base::FeatureList::IsEnabled(kLimitFontFamilyNamesPerRenderer) &&
        family_names_.size() > kFamilyNamesLimit &&
        !IsLastResortFontName(family_name)) {
      return std::nullopt;
    }
  }

  // Release the lock while making the |FindFamily| sync mojo call. Crash logs
  // indicate that this may hang, or take long, for offscreen canvas. Releasing
  // the lock protects the main thread in such case. crbug.com/1289576
  uint32_t family_index = 0;
  if (!GetFontProxy().FindFamily(family_name, &family_index)) {
    if (hresult_out)
      *hresult_out = E_FAIL;
    return std::nullopt;
  }

  {
    base::AutoLock families_lock(families_lock_);
    DCHECK(family_names_.find(family_name) == family_names_.end() ||
           family_names_[family_name] == family_index);
    family_names_[family_name] = family_index;
    if (family_index == kFamilyNotFound) [[unlikely]] {
      return std::nullopt;
    }
    DCHECK(IsValidFamilyIndex(family_index));

    if (DWriteFontFamilyProxy* family =
            GetOrCreateFamilyLockRequired(family_index)) {
      family->SetName(family_name);
      return family_index;
    }

    if (hresult_out)
      *hresult_out = E_FAIL;
    return std::nullopt;
  }
}

DWriteFontFamilyProxy* DWriteFontCollectionProxy::FindFamily(
    const std::u16string& family_name) {
  if (const std::optional<UINT32> family_index = FindFamilyIndex(family_name)) {
    if (DWriteFontFamilyProxy* family = GetFamily(*family_index))
      return family;
  }
  return nullptr;
}

void DWriteFontCollectionProxy::PrewarmFamily(
    const blink::WebString& family_name) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!prewarm_task_runner_) {
    // |BindHostReceiverOnMainThread| requires |ChildThread::Get()|, but it may
    // not be available in some tests. Disable the prewarmer.
    if (!ChildThread::Get()) [[unlikely]] {
      return;
    }
    InitializePrewarmer();
  }

  DCHECK(prewarm_task_runner_);
  prewarm_task_runner_->PostTask(
      FROM_HERE,
      base::BindOnce(&DWriteFontCollectionProxy::PrewarmFamilyOnWorker,
                     // |this| is kept in global, never destructed.
                     base::Unretained(this), family_name.Utf16()));
}

void DWriteFontCollectionProxy::PrewarmFamilyOnWorker(
    const std::u16string family_name) {
  base::ScopedAllowBaseSyncPrimitives allow_sync;
  if (DWriteFontFamilyProxy* family = FindFamily(family_name))
    family->PrewarmFamilyOnWorker();
}

HRESULT DWriteFontCollectionProxy::GetFontFamily(
    UINT32 index,
    IDWriteFontFamily** font_family) {
  DCHECK(font_family);
  base::AutoLock families_lock(families_lock_);

  if (index < families_.size() && families_[index]) {
    families_[index].CopyTo(font_family);
    return S_OK;
  }

  if (DWriteFontFamilyProxy* family = GetOrCreateFamilyLockRequired(index)) {
    const mswr::ComPtr<DWriteFontFamilyProxy> family_ptr = family;
    family_ptr.CopyTo(font_family);
    return S_OK;
  }
  return E_FAIL;
}

UINT32 DWriteFontCollectionProxy::GetFontFamilyCount() {
  base::AutoLock families_lock(families_lock_);
  return GetFontFamilyCountLockRequired();
}

UINT32 DWriteFontCollectionProxy::GetFontFamilyCountLockRequired() {
  families_lock_.AssertAcquired();

  if (family_count_ != UINT_MAX)
    return family_count_;

  TRACE_EVENT0("dwrite,fonts", "FontProxy::GetFontFamilyCount");

  uint32_t family_count = 0;
  if (!GetFontProxy().GetFamilyCount(&family_count)) {
    return 0;
  }

  family_count_ = family_count;
  return family_count;
}

HRESULT DWriteFontCollectionProxy::GetFontFromFontFace(
    IDWriteFontFace* font_face,
    IDWriteFont** font) {
  DCHECK(font_face);
  DCHECK(font);
  base::AutoLock families_lock(families_lock_);

  for (const auto& family : families_) {
    if (family && family->GetFontFromFontFace(font_face, font)) {
      return S_OK;
    }
  }
  // If the font came from our collection, at least one family should match
  DCHECK(false);

  return E_FAIL;
}

// Code downstream from DWrite's |CreateCustomFontCollection| calls this
// function back.
//
// |families_[family_index]| should be locked, but |families_| may or may not be
// locked. This function is so expensive that reading, writing, or locking
// |families_| should be avoided.
HRESULT DWriteFontCollectionProxy::CreateEnumeratorFromKey(
    IDWriteFactory* factory,
    const void* collection_key,
    UINT32 collection_key_size,
    IDWriteFontFileEnumerator** font_file_enumerator) {
  if (!collection_key || collection_key_size != sizeof(uint32_t)) {
    return E_INVALIDARG;
  }

  TRACE_EVENT0("dwrite,fonts", "FontProxy::LoadingFontFiles");

  const uint32_t* family_index =
      reinterpret_cast<const uint32_t*>(collection_key);

  std::vector<base::File> file_handles;
  {
    // The Mojo call cannot be asynchronous because CreateEnumeratorFromKey is
    // invoked synchronously by DWrite. |ScopedAllowBaseSyncPrimitives| is
    // needed to allow the synchronous Mojo call from the ThreadPool
    // (CreateEnumeratorFromKey can be invoked from the main thread or the
    // ThreadPool).
    base::ScopedAllowBaseSyncPrimitives allow_sync;
    if (!GetFontProxy().GetFontFileHandles(*family_index, &file_handles)) {
      return E_FAIL;
    }
  }

  std::vector<HANDLE> handles;
  handles.reserve(file_handles.size());
  for (auto& file_handle : file_handles) {
    handles.push_back(file_handle.TakePlatformFile());
  }

  // This leaks the handles, since they are used as the reference key to
  // CreateStreamFromKey, and DirectWrite requires the reference keys to
  // remain valid for the lifetime of the loader. The loader is the font
  // collection proxy, which remains alive for the lifetime of the renderer.
  HRESULT hr = mswr::MakeAndInitialize<FontFileEnumerator>(
      font_file_enumerator, factory, this, &handles);

  if (!SUCCEEDED(hr)) {
    DCHECK(false);
    return E_FAIL;
  }

  return S_OK;
}

HRESULT DWriteFontCollectionProxy::CreateStreamFromKey(
    const void* font_file_reference_key,
    UINT32 font_file_reference_key_size,
    IDWriteFontFileStream** font_file_stream) {
  if (font_file_reference_key_size != sizeof(HANDLE)) {
    return E_FAIL;
  }

  TRACE_EVENT0("dwrite,fonts", "FontFileEnumerator::CreateStreamFromKey");

  HANDLE file_handle =
      *reinterpret_cast<const HANDLE*>(font_file_reference_key);

  if (file_handle == NULL || file_handle == INVALID_HANDLE_VALUE) {
    DCHECK(false);
    return E_FAIL;
  }

  mswr::ComPtr<FontFileStream> stream;
  if (!SUCCEEDED(
          mswr::MakeAndInitialize<FontFileStream>(&stream, file_handle))) {
    return E_FAIL;
  }
  *font_file_stream = stream.Detach();
  return S_OK;
}

HRESULT DWriteFontCollectionProxy::RuntimeClassInitialize(
    IDWriteFactory* factory,
    mojo::PendingRemote<blink::mojom::DWriteFontProxy> proxy) {
  DCHECK(factory);
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  factory_ = factory;
  if (proxy)
    font_proxy_.GetOrCreateValue().Bind(std::move(proxy));
  main_task_runner_ = base::SingleThreadTaskRunner::GetCurrentDefault();
  // |prewarm_task_runner_| needs to be initialized later because ThreadPool is
  // not setup yet when |this| is instantiated. See |InitializePrewarmer|.

  HRESULT hr = factory->RegisterFontCollectionLoader(this);
  DCHECK(SUCCEEDED(hr));
  hr = factory_->RegisterFontFileLoader(this);
  DCHECK(SUCCEEDED(hr));
  return S_OK;
}

void DWriteFontCollectionProxy::Unregister() {
  factory_->UnregisterFontCollectionLoader(this);
  factory_->UnregisterFontFileLoader(this);
}

// |families_[family_index]| should be locked, but |families_| may or may not be
// locked. This function is so expensive that reading, writing, or locking
// |families_| should be avoided.
bool DWriteFontCollectionProxy::LoadFamily(
    UINT32 family_index,
    IDWriteFontCollection** containing_collection) {
  TRACE_EVENT0("dwrite,fonts", "FontProxy::LoadFamily");

  uint32_t index = family_index;
  // CreateCustomFontCollection ends up calling
  // DWriteFontCollectionProxy::CreateEnumeratorFromKey.
  HRESULT hr = factory_->CreateCustomFontCollection(
      this /*collectionLoader*/, reinterpret_cast<const void*>(&index),
      sizeof(index), containing_collection);

  return SUCCEEDED(hr);
}

bool DWriteFontCollectionProxy::GetFontFamily(UINT32 family_index,
                                              const std::u16string& family_name,
                                              IDWriteFontFamily** font_family) {
  DCHECK(font_family);
  DCHECK(!family_name.empty());
  base::AutoLock families_lock(families_lock_);

  DWriteFontFamilyProxy* family = GetOrCreateFamilyLockRequired(family_index);
  if (!family)
    return false;

  family->SetNameIfNotLoaded(family_name);

  const mswr::ComPtr<DWriteFontFamilyProxy> family_ptr = family;
  family_ptr.CopyTo(font_family);
  return true;
}

bool DWriteFontCollectionProxy::LoadFamilyNames(
    UINT32 family_index,
    IDWriteLocalizedStrings** localized_strings) {
  TRACE_EVENT0("dwrite,fonts", "FontProxy::LoadFamilyNames");

  std::vector<blink::mojom::DWriteStringPairPtr> pairs;
  if (!GetFontProxy().GetFamilyNames(family_index, &pairs)) {
    return false;
  }
  std::vector<std::pair<std::u16string, std::u16string>> strings;
  for (auto& pair : pairs) {
    strings.emplace_back(pair->first, pair->second);
  }

  HRESULT hr = mswr::MakeAndInitialize<DWriteLocalizedStrings>(
      localized_strings, &strings);

  return SUCCEEDED(hr);
}

DWriteFontFamilyProxy* DWriteFontCollectionProxy::GetOrCreateFamilyLockRequired(
    UINT32 family_index) {
  DCHECK(IsValidFamilyIndex(family_index));

  if (family_index < families_.size()) {
    if (DWriteFontFamilyProxy* family = families_[family_index].Get())
      return family;
  }

  const UINT32 family_count = GetFontFamilyCountLockRequired();
  if (family_index >= family_count)
    return nullptr;

  if (families_.size() < family_count)
    families_.resize(family_count);

  mswr::ComPtr<DWriteFontFamilyProxy> family;
  HRESULT hr = mswr::MakeAndInitialize<DWriteFontFamilyProxy>(&family, this,
                                                              family_index);
  DCHECK(SUCCEEDED(hr));
  DCHECK_LT(family_index, families_.size());

  families_[family_index] = family;
  return family.Get();
}

blink::mojom::DWriteFontProxy& DWriteFontCollectionProxy::GetFontProxy() {
  mojo::Remote<blink::mojom::DWriteFontProxy>& font_proxy =
      font_proxy_.GetOrCreateValue();
  if (!font_proxy) {
    if (main_task_runner_->RunsTasksInCurrentSequence()) {
      BindHostReceiverOnMainThread(font_proxy.BindNewPipeAndPassReceiver());
    } else {
      main_task_runner_->PostTask(
          FROM_HERE, base::BindOnce(&BindHostReceiverOnMainThread,
                                    font_proxy.BindNewPipeAndPassReceiver()));
    }
  }
  return *font_proxy;
}

void DWriteFontCollectionProxy::BindFontProxyUsingBroker(
    blink::ThreadSafeBrowserInterfaceBrokerProxy* interface_broker) {
  mojo::Remote<blink::mojom::DWriteFontProxy>& font_proxy =
      font_proxy_.GetOrCreateValue();
  DCHECK(!font_proxy);
  interface_broker->GetInterface(font_proxy.BindNewPipeAndPassReceiver());
}

void DWriteFontCollectionProxy::BindFontProxy(
    mojo::PendingRemote<blink::mojom::DWriteFontProxy> remote) {
  mojo::Remote<blink::mojom::DWriteFontProxy>& font_proxy =
      font_proxy_.GetOrCreateValue();
  DCHECK(!font_proxy);
  font_proxy.Bind(std::move(remote));
}

DWriteFontFamilyProxy::DWriteFontFamilyProxy() = default;

DWriteFontFamilyProxy::~DWriteFontFamilyProxy() = default;

HRESULT DWriteFontFamilyProxy::GetFontCollection(
    IDWriteFontCollection** font_collection) {
  DCHECK(font_collection);

  proxy_collection_.CopyTo(font_collection);
  return S_OK;
}

UINT32 DWriteFontFamilyProxy::GetFontCount() {
  // We could conceivably proxy just the font count. However, calling
  // GetFontCount is almost certain to be followed by a series of GetFont
  // calls which will need to load all the fonts anyway, so we might as
  // well save an IPC here.
  if (IDWriteFontFamily* family = LoadFamily())
    return family->GetFontCount();
  return 0;
}

HRESULT DWriteFontFamilyProxy::GetFont(UINT32 index, IDWriteFont** font) {
  DCHECK(font);

  if (index >= GetFontCount())
    return E_INVALIDARG;
  if (IDWriteFontFamily* family = LoadFamily())
    return family->GetFont(index, font);
  return E_FAIL;
}

HRESULT DWriteFontFamilyProxy::GetFamilyNames(IDWriteLocalizedStrings** names) {
  DCHECK(names);
  base::AutoLock family_lock(family_lock_);

  // Prefer the real thing, if available.
  if (family_) {
    family_names_.Reset();  // Release cached data.
    return family_->GetFamilyNames(names);
  }

  // If already cached, use the cache.
  if (family_names_) {
    family_names_.CopyTo(names);
    return S_OK;
  }

  TRACE_EVENT0("dwrite,fonts", "FontProxy::GetFamilyNames");

  // Otherwise, do the IPC.
  if (!proxy_collection_->LoadFamilyNames(family_index_, &family_names_))
    return E_FAIL;

  family_names_.CopyTo(names);
  return S_OK;
}

HRESULT DWriteFontFamilyProxy::GetFirstMatchingFont(
    DWRITE_FONT_WEIGHT weight,
    DWRITE_FONT_STRETCH stretch,
    DWRITE_FONT_STYLE style,
    IDWriteFont** matching_font) {
  DCHECK(matching_font);

  if (IDWriteFontFamily* family = LoadFamily())
    return family->GetFirstMatchingFont(weight, stretch, style, matching_font);
  return E_FAIL;
}

HRESULT DWriteFontFamilyProxy::GetMatchingFonts(
    DWRITE_FONT_WEIGHT weight,
    DWRITE_FONT_STRETCH stretch,
    DWRITE_FONT_STYLE style,
    IDWriteFontList** matching_fonts) {
  DCHECK(matching_fonts);

  if (IDWriteFontFamily* family = LoadFamily())
    return family->GetMatchingFonts(weight, stretch, style, matching_fonts);
  return E_FAIL;
}

HRESULT DWriteFontFamilyProxy::RuntimeClassInitialize(
    DWriteFontCollectionProxy* collection,
    UINT32 index) {
  DCHECK(collection);
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  proxy_collection_ = collection;
  family_index_ = index;
  return S_OK;
}

bool DWriteFontFamilyProxy::GetFontFromFontFace(IDWriteFontFace* font_face,
                                                IDWriteFont** font) {
  DCHECK(font_face);
  DCHECK(font);
  base::AutoLock family_lock(family_lock_);

  if (!family_)
    return false;

  mswr::ComPtr<IDWriteFontCollection> collection;
  HRESULT hr = family_->GetFontCollection(&collection);
  DCHECK(SUCCEEDED(hr));
  hr = collection->GetFontFromFontFace(font_face, font);

  return SUCCEEDED(hr);
}

void DWriteFontFamilyProxy::SetName(const std::u16string& family_name) {
  base::AutoLock family_lock(family_lock_);
  family_name_.assign(family_name);
}

void DWriteFontFamilyProxy::SetNameIfNotLoaded(
    const std::u16string& family_name) {
  base::AutoLock family_lock(family_lock_);
  if (!family_ || family_name_.empty())
    family_name_.assign(family_name);
}

const std::u16string& DWriteFontFamilyProxy::GetName() {
  base::AutoLock family_lock(family_lock_);
  return family_name_;
}

IDWriteFontFamily* DWriteFontFamilyProxy::LoadFamily() {
  TRACE_EVENT0("dwrite,fonts", "DWriteFontFamilyProxy::LoadFamily");

  base::AutoLock family_lock(family_lock_);

  if (family_)
    return family_.Get();
  return LoadFamilyCoreLockRequired();
}

void DWriteFontFamilyProxy::PrewarmFamilyOnWorker() {
  // Load the family only if other threads haven't loaded this family.
  base::AutoLock family_lock(family_lock_);
  if (!family_)
    LoadFamilyCoreLockRequired();
}

// Note this function may run in the main thread, or in a worker thread.
IDWriteFontFamily* DWriteFontFamilyProxy::LoadFamilyCoreLockRequired() {
  DCHECK(!family_);
  family_lock_.AssertAcquired();

  // TODO(dcheng): Is this crash key still used? There does not appear to be
  // anything obvious below that would trigger a crash report.
  SCOPED_CRASH_KEY_STRING32("LoadFamily", "font_key_name",
                            base::UTF16ToUTF8(family_name_));

  mswr::ComPtr<IDWriteFontCollection> collection;
  if (!proxy_collection_->LoadFamily(family_index_, &collection)) {
    return nullptr;
  }

  UINT32 family_count = collection->GetFontFamilyCount();

  HRESULT hr;
  if (family_count > 1) {
    // Some fonts are packaged in a single file containing multiple families. In
    // such a case we can find the right family by family name.
    DCHECK(!family_name_.empty());
    UINT32 family_index = 0;
    BOOL found = FALSE;
    static_assert(sizeof(WCHAR) == sizeof(char16_t), "WCHAR should be UTF-16.");
    hr = collection->FindFamilyName(
        reinterpret_cast<const WCHAR*>(family_name_.c_str()), &family_index,
        &found);
    if (SUCCEEDED(hr) && found) {
      hr = collection->GetFontFamily(family_index, &family_);
      return SUCCEEDED(hr) ? family_.Get() : nullptr;
    }
  }

  DCHECK_LE(family_count, 1u);

  if (family_count == 0) {
    // This is really strange, we successfully loaded no fonts?!
    return nullptr;
  }

  hr = collection->GetFontFamily(0, &family_);
  return SUCCEEDED(hr) ? family_.Get() : nullptr;
}

FontFileEnumerator::FontFileEnumerator() = default;

FontFileEnumerator::~FontFileEnumerator() = default;

HRESULT FontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** file) {
  DCHECK(file);
  if (current_file_ >= files_.size()) {
    return E_FAIL;
  }

  TRACE_EVENT0("dwrite,fonts", "FontFileEnumerator::GetCurrentFontFile");

  // CreateCustomFontFileReference ends up calling
  // DWriteFontCollectionProxy::CreateStreamFromKey.
  HRESULT hr = factory_->CreateCustomFontFileReference(
      reinterpret_cast<const void*>(&files_[current_file_]),
      sizeof(files_[current_file_]), loader_.Get() /*IDWriteFontFileLoader*/,
      file);
  DCHECK(SUCCEEDED(hr));
  return hr;
}

HRESULT FontFileEnumerator::MoveNext(BOOL* has_current_file) {
  DCHECK(has_current_file);

  TRACE_EVENT0("dwrite,fonts", "FontFileEnumerator::MoveNext");
  if (next_file_ >= files_.size()) {
    *has_current_file = FALSE;
    current_file_ = UINT_MAX;
    return S_OK;
  }

  current_file_ = next_file_;
  ++next_file_;
  *has_current_file = TRUE;
  return S_OK;
}

HRESULT FontFileEnumerator::RuntimeClassInitialize(
    IDWriteFactory* factory,
    IDWriteFontFileLoader* loader,
    std::vector<HANDLE>* files) {
  factory_ = factory;
  loader_ = loader;
  files_.swap(*files);
  return S_OK;
}

FontFileStream::FontFileStream() = default;

FontFileStream::~FontFileStream() = default;

HRESULT FontFileStream::GetFileSize(UINT64* file_size) {
  *file_size = data_.length();
  return S_OK;
}

HRESULT FontFileStream::GetLastWriteTime(UINT64* last_write_time) {
  *last_write_time = 0;
  return S_OK;
}

HRESULT FontFileStream::ReadFileFragment(const void** fragment_start,
                                         UINT64 fragment_offset,
                                         UINT64 fragment_size,
                                         void** fragment_context) {
  if (fragment_offset + fragment_size < fragment_offset)
    return E_FAIL;
  if (fragment_offset + fragment_size > data_.length())
    return E_FAIL;
  *fragment_start = data_.data() + fragment_offset;
  *fragment_context = nullptr;
  return S_OK;
}

HRESULT FontFileStream::RuntimeClassInitialize(HANDLE handle) {
  // Duplicate the original handle so we can reopen the file after the memory
  // mapped section closes it.
  HANDLE duplicate_handle;
  if (!DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(),
                       &duplicate_handle, 0 /* dwDesiredAccess */,
                       false /* bInheritHandle */, DUPLICATE_SAME_ACCESS)) {
    return E_FAIL;
  }

  if (!data_.Initialize(base::File(duplicate_handle))) {
    return E_FAIL;
  }
  return S_OK;
}

}  // namespace content