blob: b40b1cbe464ed40f218d28f553fef1945f41bf09 [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "platform/text/Hyphenation.h"
#include "base/files/file.h"
#include "base/files/memory_mapped_file.h"
#include "base/metrics/histogram.h"
#include "base/timer/elapsed_timer.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "platform/text/hyphenation/HyphenatorAOSP.h"
#include "public/platform/Platform.h"
#include "public/platform/ServiceRegistry.h"
#include "public/platform/modules/hyphenation/hyphenation.mojom-blink.h"
namespace blink {
using Hyphenator = android::Hyphenator;
class HyphenationMinikin : public Hyphenation {
public:
bool openDictionary(const AtomicString& locale);
size_t lastHyphenLocation(const StringView& text, size_t beforeIndex) const override;
Vector<size_t, 8> hyphenLocations(const StringView&) const override;
private:
static base::PlatformFile openDictionaryFile(const AtomicString& locale);
std::vector<uint8_t> hyphenate(const StringView&) const;
base::MemoryMappedFile m_file;
std::unique_ptr<Hyphenator> m_hyphenator;
};
static mojom::blink::HyphenationPtr connectToRemoteService()
{
mojom::blink::HyphenationPtr service;
Platform::current()->serviceRegistry()->connectToRemoteService(
mojo::GetProxy(&service));
return service;
}
static const mojom::blink::HyphenationPtr& getService()
{
DEFINE_STATIC_LOCAL(mojom::blink::HyphenationPtr, service,
(connectToRemoteService()));
return service;
}
base::PlatformFile HyphenationMinikin::openDictionaryFile(const AtomicString& locale)
{
const mojom::blink::HyphenationPtr& service = getService();
mojo::ScopedHandle handle;
base::ElapsedTimer timer;
service->OpenDictionary(locale, &handle);
UMA_HISTOGRAM_TIMES("Hyphenation.Open", timer.Elapsed());
if (!handle.is_valid())
return base::kInvalidPlatformFile;
base::PlatformFile file;
MojoResult result = mojo::UnwrapPlatformFile(std::move(handle), &file);
if (result != MOJO_RESULT_OK) {
DLOG(ERROR) << "UnwrapPlatformFile failed";
return base::kInvalidPlatformFile;
}
return file;
}
bool HyphenationMinikin::openDictionary(const AtomicString& locale)
{
base::PlatformFile file = openDictionaryFile(locale);
if (file == base::kInvalidPlatformFile)
return false;
if (!m_file.Initialize(base::File(file))) {
DLOG(ERROR) << "mmap failed";
return false;
}
m_hyphenator = wrapUnique(Hyphenator::loadBinary(m_file.data()));
return true;
}
std::vector<uint8_t> HyphenationMinikin::hyphenate(const StringView& text) const
{
std::vector<uint8_t> result;
if (text.is8Bit()) {
String text16Bit = text.toString();
text16Bit.ensure16Bit();
m_hyphenator->hyphenate(&result, text16Bit.characters16(), text16Bit.length());
} else {
m_hyphenator->hyphenate(&result, text.characters16(), text.length());
}
return result;
}
size_t HyphenationMinikin::lastHyphenLocation(const StringView& text, size_t beforeIndex) const
{
if (text.length() < minimumPrefixLength + minimumSuffixLength)
return 0;
std::vector<uint8_t> result = hyphenate(text);
static_assert(minimumPrefixLength >= 1, "Change the 'if' above if this fails");
for (size_t i = text.length() - minimumSuffixLength - 1;
i >= minimumPrefixLength; i--) {
if (result[i])
return i;
}
return 0;
}
Vector<size_t, 8> HyphenationMinikin::hyphenLocations(const StringView& text) const
{
Vector<size_t, 8> hyphenLocations;
if (text.length() < minimumPrefixLength + minimumSuffixLength)
return hyphenLocations;
std::vector<uint8_t> result = hyphenate(text);
static_assert(minimumPrefixLength >= 1, "Change the 'if' above if this fails");
for (size_t i = text.length() - minimumSuffixLength - 1;
i >= minimumPrefixLength; i--) {
if (result[i])
hyphenLocations.append(i);
}
return hyphenLocations;
}
using LocaleMap = HashMap<AtomicString, AtomicString, CaseFoldingHash>;
static LocaleMap createLocaleFallbackMap()
{
// This data is from CLDR, compiled by AOSP.
// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/Hyphenator.java
using LocaleFallback = const char*[2];
static LocaleFallback localeFallbackData[] = {
{ "en-AS", "en-US" }, // English (American Samoa)
{ "en-GU", "en-US" }, // English (Guam)
{ "en-MH", "en-US" }, // English (Marshall Islands)
{ "en-MP", "en-US" }, // English (Northern Mariana Islands)
{ "en-PR", "en-US" }, // English (Puerto Rico)
{ "en-UM", "en-US" }, // English (United States Minor Outlying Islands)
{ "en-VI", "en-US" }, // English (Virgin Islands)
{ "no", "nb" },
{ "am", "und-Ethi" }, // Amharic
{ "byn", "und-Ethi" }, // Blin
{ "gez", "und-Ethi" }, // Geʻez
{ "ti", "und-Ethi" }, // Tigrinya
{ "wal", "und-Ethi" }, // Wolaytta
};
LocaleMap map;
for (const auto& it : localeFallbackData)
map.add(it[0], it[1]);
return map;
}
PassRefPtr<Hyphenation> Hyphenation::platformGetHyphenation(const AtomicString& locale)
{
RefPtr<HyphenationMinikin> hyphenation(adoptRef(new HyphenationMinikin));
if (hyphenation->openDictionary(locale.lowerASCII()))
return hyphenation.release();
hyphenation.clear();
DEFINE_STATIC_LOCAL(LocaleMap, localeFallback, (createLocaleFallbackMap()));
const auto& it = localeFallback.find(locale);
if (it != localeFallback.end())
return get(it->value);
return nullptr;
}
} // namespace blink