blob: c64885b9794fe72915ff91baee6b264a3cdb32ae [file] [log] [blame]
// Copyright 2015 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 "content/browser/renderer_host/dwrite_font_proxy_impl_win.h"
#include <dwrite.h>
#include <dwrite_2.h>
#include <memory>
#include "base/file_version_info.h"
#include "base/files/file.h"
#include "base/files/scoped_temp_dir.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/scoped_task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/win/windows_version.h"
#include "content/public/common/content_features.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "services/service_manager/public/cpp/bind_source_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/dwrite_rasterizer_support/dwrite_rasterizer_support.h"
#include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h"
namespace content {
namespace {
struct FontExpectation {
const char font_name[64];
uint16_t ttc_index;
};
constexpr FontExpectation kExpectedTestFonts[] = {{u8"CambriaMath", 1},
{u8"Ming-Lt-HKSCS-ExtB", 2},
{u8"NSimSun", 1},
{u8"calibri-bolditalic", 0}};
// DirectWrite on Windows supports IDWriteFontSet API which allows for querying
// by PostScript name and full font name directly. In the implementation of
// DWriteFontProxy we check whether this API is available by checking for
// whether IDWriteFactory3 is available. In order to validate in a unit test
// whether this check works, compare it against the dwrite.dll major version -
// versions starting from 10 have the required functionality.
constexpr int kDWriteMajorVersionSupportingSingleLookups = 10;
// Base test class that sets up the Mojo connection to DWriteFontProxy so that
// tests can call its Mojo methods.
class DWriteFontProxyImplUnitTest : public testing::Test {
public:
DWriteFontProxyImplUnitTest()
: binding_(&impl_, mojo::MakeRequest(&dwrite_font_proxy_)) {}
blink::mojom::DWriteFontProxy& dwrite_font_proxy() {
return *dwrite_font_proxy_;
}
bool SupportsSingleLookups() {
blink::mojom::UniqueFontLookupMode lookup_mode;
dwrite_font_proxy().GetUniqueFontLookupMode(&lookup_mode);
return lookup_mode == blink::mojom::UniqueFontLookupMode::kSingleLookups;
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
blink::mojom::DWriteFontProxyPtr dwrite_font_proxy_;
DWriteFontProxyImpl impl_;
mojo::Binding<blink::mojom::DWriteFontProxy> binding_;
};
// Derived class for tests that exercise font unique local matching mojo methods
// of DWriteFontProxy. Needs a ScopedFeatureList to activate the feature as it
// is currently behind a flag.
class DWriteFontProxyLocalMatchingTest : public DWriteFontProxyImplUnitTest {
public:
DWriteFontProxyLocalMatchingTest() {
feature_list_.InitAndEnableFeature(features::kFontSrcLocalMatching);
}
private:
base::test::ScopedFeatureList feature_list_;
};
// Derived class for tests that exercise the parts of the DWriteFontProxy Mojo
// interface that deal with accessing the font lookup table created by
// DWriteFontLookupTableBuilder. Initializes the DWriteFontLookupTableBuilder
// and has a ScopedTempDir for testing persisting the lookup table to disk.
class DWriteFontProxyTableMatchingTest
: public DWriteFontProxyLocalMatchingTest {
public:
DWriteFontProxyTableMatchingTest() {
DWriteFontLookupTableBuilder* table_builder_instance =
DWriteFontLookupTableBuilder::GetInstance();
DCHECK(scoped_temp_dir_.CreateUniqueTempDir());
table_builder_instance->OverrideDWriteVersionChecksForTesting();
table_builder_instance->SetCacheDirectoryForTesting(
scoped_temp_dir_.GetPath());
table_builder_instance->ResetLookupTableForTesting();
table_builder_instance->SchedulePrepareFontUniqueNameTableIfNeeded();
}
private:
base::ScopedTempDir scoped_temp_dir_;
};
TEST_F(DWriteFontProxyImplUnitTest, GetFamilyCount) {
UINT32 family_count = 0;
dwrite_font_proxy().GetFamilyCount(&family_count);
EXPECT_NE(0u, family_count); // Assume there's some fonts on the test system.
}
TEST_F(DWriteFontProxyImplUnitTest, FindFamily) {
UINT32 arial_index = 0;
dwrite_font_proxy().FindFamily(L"Arial", &arial_index);
EXPECT_NE(UINT_MAX, arial_index);
UINT32 times_index = 0;
dwrite_font_proxy().FindFamily(L"Times New Roman", &times_index);
EXPECT_NE(UINT_MAX, times_index);
EXPECT_NE(arial_index, times_index);
UINT32 unknown_index = 0;
dwrite_font_proxy().FindFamily(L"Not a font family", &unknown_index);
EXPECT_EQ(UINT_MAX, unknown_index);
}
TEST_F(DWriteFontProxyImplUnitTest, GetFamilyNames) {
UINT32 arial_index = 0;
dwrite_font_proxy().FindFamily(L"Arial", &arial_index);
std::vector<blink::mojom::DWriteStringPairPtr> names;
dwrite_font_proxy().GetFamilyNames(arial_index, &names);
EXPECT_LT(0u, names.size());
for (const auto& pair : names) {
EXPECT_FALSE(pair->first.empty());
EXPECT_FALSE(pair->second.empty());
}
}
TEST_F(DWriteFontProxyImplUnitTest, GetFamilyNamesIndexOutOfBounds) {
std::vector<blink::mojom::DWriteStringPairPtr> names;
UINT32 invalid_index = 1000000;
dwrite_font_proxy().GetFamilyNames(invalid_index, &names);
EXPECT_TRUE(names.empty());
}
TEST_F(DWriteFontProxyImplUnitTest, GetFontFiles) {
UINT32 arial_index = 0;
dwrite_font_proxy().FindFamily(L"Arial", &arial_index);
std::vector<base::FilePath> files;
std::vector<base::File> handles;
dwrite_font_proxy().GetFontFiles(arial_index, &files, &handles);
EXPECT_LT(0u, files.size());
for (const auto& file : files) {
EXPECT_FALSE(file.value().empty());
}
}
TEST_F(DWriteFontProxyImplUnitTest, GetFontFilesIndexOutOfBounds) {
std::vector<base::FilePath> files;
std::vector<base::File> handles;
UINT32 invalid_index = 1000000;
dwrite_font_proxy().GetFontFiles(invalid_index, &files, &handles);
EXPECT_EQ(0u, files.size());
}
TEST_F(DWriteFontProxyImplUnitTest, MapCharacter) {
if (!blink::DWriteRasterizerSupport::IsDWriteFactory2Available())
return;
blink::mojom::MapCharactersResultPtr result;
dwrite_font_proxy().MapCharacters(
L"abc",
blink::mojom::DWriteFontStyle::New(DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL),
L"", DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, L"", &result);
EXPECT_NE(UINT32_MAX, result->family_index);
EXPECT_STRNE(L"", result->family_name.c_str());
EXPECT_EQ(3u, result->mapped_length);
EXPECT_NE(0.0, result->scale);
EXPECT_NE(0, result->font_style->font_weight);
EXPECT_EQ(DWRITE_FONT_STYLE_NORMAL, result->font_style->font_slant);
EXPECT_NE(0, result->font_style->font_stretch);
}
TEST_F(DWriteFontProxyImplUnitTest, MapCharacterInvalidCharacter) {
if (!blink::DWriteRasterizerSupport::IsDWriteFactory2Available())
return;
blink::mojom::MapCharactersResultPtr result;
dwrite_font_proxy().MapCharacters(
L"\ufffe\uffffabc",
blink::mojom::DWriteFontStyle::New(DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL),
L"en-us", DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, L"", &result);
EXPECT_EQ(UINT32_MAX, result->family_index);
EXPECT_STREQ(L"", result->family_name.c_str());
EXPECT_EQ(2u, result->mapped_length);
}
TEST_F(DWriteFontProxyImplUnitTest, MapCharacterInvalidAfterValid) {
if (!blink::DWriteRasterizerSupport::IsDWriteFactory2Available())
return;
blink::mojom::MapCharactersResultPtr result;
dwrite_font_proxy().MapCharacters(
L"abc\ufffe\uffff",
blink::mojom::DWriteFontStyle::New(DWRITE_FONT_WEIGHT_NORMAL,
DWRITE_FONT_STYLE_NORMAL,
DWRITE_FONT_STRETCH_NORMAL),
L"en-us", DWRITE_READING_DIRECTION_LEFT_TO_RIGHT, L"", &result);
EXPECT_NE(UINT32_MAX, result->family_index);
EXPECT_STRNE(L"", result->family_name.c_str());
EXPECT_EQ(3u, result->mapped_length);
EXPECT_NE(0.0, result->scale);
EXPECT_NE(0, result->font_style->font_weight);
EXPECT_EQ(DWRITE_FONT_STYLE_NORMAL, result->font_style->font_slant);
EXPECT_NE(0, result->font_style->font_stretch);
}
TEST_F(DWriteFontProxyImplUnitTest, TestCustomFontFiles) {
// Override windows fonts path to force the custom font file codepath.
impl_.SetWindowsFontsPathForTesting(L"X:\\NotWindowsFonts");
UINT32 arial_index = 0;
dwrite_font_proxy().FindFamily(L"Arial", &arial_index);
std::vector<base::FilePath> files;
std::vector<base::File> handles;
dwrite_font_proxy().GetFontFiles(arial_index, &files, &handles);
EXPECT_TRUE(files.empty());
EXPECT_FALSE(handles.empty());
for (auto& file : handles) {
EXPECT_TRUE(file.IsValid());
EXPECT_LT(0, file.GetLength()); // Check the file exists
}
}
namespace {
void TestWhenLookupTableReady(
bool* did_test_fonts,
base::ReadOnlySharedMemoryRegion font_table_memory) {
blink::FontTableMatcher font_table_matcher(font_table_memory.Map());
for (auto& test_font_name_index : kExpectedTestFonts) {
base::Optional<blink::FontTableMatcher::MatchResult> match_result =
font_table_matcher.MatchName(test_font_name_index.font_name);
ASSERT_TRUE(match_result)
<< "No font matched for font name: " << test_font_name_index.font_name;
base::File unique_font_file(
base::FilePath::FromUTF8Unsafe(match_result->font_path),
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(unique_font_file.IsValid());
ASSERT_GT(unique_font_file.GetLength(), 0);
ASSERT_EQ(test_font_name_index.ttc_index, match_result->ttc_index);
*did_test_fonts = true;
}
}
} // namespace
TEST_F(DWriteFontProxyTableMatchingTest, TestFindUniqueFont) {
bool lookup_table_results_were_tested = false;
dwrite_font_proxy().GetUniqueNameLookupTable(base::BindOnce(
&TestWhenLookupTableReady, &lookup_table_results_were_tested));
scoped_task_environment_.RunUntilIdle();
ASSERT_TRUE(lookup_table_results_were_tested);
}
TEST_F(DWriteFontProxyLocalMatchingTest, TestSingleLookup) {
// Do not run this test on unsupported Windows versions.
if (!SupportsSingleLookups())
return;
for (auto& test_font_name_index : kExpectedTestFonts) {
base::FilePath result_path;
uint32_t ttc_index;
dwrite_font_proxy().MatchUniqueFont(
base::UTF8ToUTF16(test_font_name_index.font_name), &result_path,
&ttc_index);
ASSERT_GT(result_path.value().size(), 0u);
base::File unique_font_file(result_path,
base::File::FLAG_OPEN | base::File::FLAG_READ);
ASSERT_TRUE(unique_font_file.IsValid());
ASSERT_GT(unique_font_file.GetLength(), 0);
ASSERT_EQ(test_font_name_index.ttc_index, ttc_index);
}
}
TEST_F(DWriteFontProxyLocalMatchingTest, TestSingleLookupUnavailable) {
// Do not run this test on unsupported Windows versions.
if (!SupportsSingleLookups())
return;
base::FilePath result_path;
uint32_t ttc_index;
std::string unavailable_font_name =
"Unavailable_Font_Name_56E7EA7E-2C69-4E23-99DC-750BC19B250E";
dwrite_font_proxy().MatchUniqueFont(base::UTF8ToUTF16(unavailable_font_name),
&result_path, &ttc_index);
ASSERT_EQ(result_path.value().size(), 0u);
ASSERT_EQ(ttc_index, 0u);
}
TEST_F(DWriteFontProxyLocalMatchingTest, TestLookupMode) {
std::unique_ptr<FileVersionInfo> dwrite_version_info =
FileVersionInfo::CreateFileVersionInfo(
base::FilePath(FILE_PATH_LITERAL("DWrite.dll")));
std::string dwrite_version =
base::WideToUTF8(dwrite_version_info->product_version());
int dwrite_major_version_number =
std::stoi(dwrite_version.substr(0, dwrite_version.find(".")));
blink::mojom::UniqueFontLookupMode expected_lookup_mode;
if (dwrite_major_version_number >=
kDWriteMajorVersionSupportingSingleLookups) {
expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kSingleLookups;
} else {
expected_lookup_mode = blink::mojom::UniqueFontLookupMode::kRetrieveTable;
}
blink::mojom::UniqueFontLookupMode lookup_mode;
dwrite_font_proxy().GetUniqueFontLookupMode(&lookup_mode);
ASSERT_EQ(lookup_mode, expected_lookup_mode);
}
} // namespace
} // namespace content