910e62b5创建于 1月15日历史提交
// Copyright 2016 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/browser/hyphenation/hyphenation_impl.h"

#include <algorithm>
#include <map>
#include <utility>

#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/timer/elapsed_timer.h"
#include "build/build_config.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"

namespace {

struct Dictionaries {
  static Dictionaries* Get() {
    static base::NoDestructor<Dictionaries> dictionaries;
    return dictionaries.get();
  }

#if !BUILDFLAG(IS_ANDROID)
  void SetDirectory(const base::FilePath& new_dir) {
    DVLOG(1) << __func__ << " " << new_dir;
    DCHECK(hyphenation::HyphenationImpl::GetTaskRunner()
               ->RunsTasksInCurrentSequence());
    DCHECK(!new_dir.empty());
    if (new_dir == dir || !base::PathExists(new_dir))
      return;
    dir = new_dir;
    cache.clear();
  }

  base::FilePath dir;
#endif

  // Keep the files open in the cache for subsequent calls.
  std::unordered_map<std::string, base::File> cache;
};

bool IsValidLocale(const std::string& locale) {
  return std::ranges::all_of(locale, [](const char ch) {
    return base::IsAsciiAlpha(ch) || base::IsAsciiDigit(ch) || ch == '-';
  });
}

base::File GetDictionaryFile(const std::string& locale) {
  DCHECK(hyphenation::HyphenationImpl::GetTaskRunner()
             ->RunsTasksInCurrentSequence());
  Dictionaries* dictionaries = Dictionaries::Get();
#if !BUILDFLAG(IS_ANDROID)
  const base::FilePath& dir = dictionaries->dir;
  if (dir.empty())
    return base::File();
#endif

  const auto& inserted =
      dictionaries->cache.insert(std::make_pair(locale, base::File()));
  base::File& file = inserted.first->second;
  // If the |locale| is already in the cache, duplicate the file and return it.
  if (!inserted.second)
    return file.Duplicate();
  DCHECK(!file.IsValid());

#if BUILDFLAG(IS_ANDROID)
  base::FilePath dir("/system/usr/hyphen-data");
#endif
  std::string filename = base::StringPrintf("hyph-%s.hyb", locale.c_str());
  base::FilePath path = dir.AppendASCII(filename);
  base::ElapsedTimer timer;
  file.Initialize(path, base::File::FLAG_OPEN | base::File::FLAG_READ);
  UMA_HISTOGRAM_TIMES("Hyphenation.Open.File", timer.Elapsed());
  return file.Duplicate();
}

}  // namespace

namespace hyphenation {

HyphenationImpl::HyphenationImpl() {}

HyphenationImpl::~HyphenationImpl() {}

// static
void HyphenationImpl::Create(
    mojo::PendingReceiver<blink::mojom::Hyphenation> receiver) {
  mojo::MakeSelfOwnedReceiver(std::make_unique<HyphenationImpl>(),
                              std::move(receiver));
}

// static
scoped_refptr<base::SequencedTaskRunner> HyphenationImpl::GetTaskRunner() {
  static base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>> runner(
      base::ThreadPool::CreateSequencedTaskRunner(
          {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
           base::TaskPriority::USER_BLOCKING}));
  return *runner;
}

#if !BUILDFLAG(IS_ANDROID)
// static
void HyphenationImpl::RegisterGetDictionary() {
  content::ContentBrowserClient* content_browser_client =
      content::GetContentClient()->browser();
  DCHECK(content_browser_client);
  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
  static bool registered = false;
  if (registered)
    return;
  registered = true;
  content_browser_client->GetHyphenationDictionary(
      base::BindOnce(SetDirectory));
}

// static
void HyphenationImpl::SetDirectory(const base::FilePath& dir) {
  GetTaskRunner()->PostTask(FROM_HERE,
                            base::BindOnce(
                                [](const base::FilePath& dir) {
                                  Dictionaries::Get()->SetDirectory(dir);
                                },
                                dir));
}
#endif

void HyphenationImpl::OpenDictionary(const std::string& locale,
                                     OpenDictionaryCallback callback) {
  DCHECK(GetTaskRunner()->RunsTasksInCurrentSequence());
  if (IsValidLocale(locale))
    std::move(callback).Run(GetDictionaryFile(locale));
  else
    std::move(callback).Run(base::File());
}

}  // namespace hyphenation