blob: d8fa9c55ac4cbc83b76b711ddf6c3b56048cf0aa [file] [log] [blame]
// Copyright 2018 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 "third_party/blink/renderer/platform/fonts/win/font_unique_name_lookup_win.h"
#include <memory>
#include <utility>
#include "base/files/file_path.h"
#include "base/metrics/histogram_macros.h"
#include "mojo/public/mojom/base/shared_memory.mojom-blink.h"
#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h"
#include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/skia/include/ports/SkTypeface_win.h"
namespace {
// These enum values correspond to the
// "Blink.Fonts.WindowsUniqueLocalFontInstantiationResult" histogram, new values
// can be added, but old values should never be reused.
enum class InstantiationResult {
kSuccess = 0,
kErrorOutsideWindowsFontsDirectory = 1,
kErrorOther = 2,
kMaxValue = kErrorOther,
};
} // namespace
namespace blink {
FontUniqueNameLookupWin::FontUniqueNameLookupWin() = default;
FontUniqueNameLookupWin::~FontUniqueNameLookupWin() = default;
sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueName(
const String& font_unique_name) {
if (lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups)
return MatchUniqueNameSingleLookup(font_unique_name);
return MatchUniqueNameLookupTable(font_unique_name);
}
sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueNameSingleLookup(
const String& font_unique_name) {
DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups);
base::FilePath font_file_path;
uint32_t ttc_index = 0;
EnsureServiceConnected();
bool matching_mojo_success =
service_->MatchUniqueFont(font_unique_name, &font_file_path, &ttc_index);
DCHECK(matching_mojo_success);
if (!font_file_path.value().size())
return nullptr;
return InstantiateFromPathAndTtcIndex(font_file_path, ttc_index);
}
sk_sp<SkTypeface> FontUniqueNameLookupWin::MatchUniqueNameLookupTable(
const String& font_unique_name) {
DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
if (!IsFontUniqueNameLookupReadyForSyncLookup())
return nullptr;
base::Optional<FontTableMatcher::MatchResult> match_result =
font_table_matcher_->MatchName(font_unique_name.Utf8());
if (!match_result)
return nullptr;
base::FilePath file_path =
base::FilePath::FromUTF8Unsafe(match_result->font_path.c_str());
return InstantiateFromPathAndTtcIndex(file_path, match_result->ttc_index);
}
sk_sp<SkTypeface> FontUniqueNameLookupWin::InstantiateFromPathAndTtcIndex(
base::FilePath font_file_path,
uint32_t ttc_index) {
// Record here when a locally uniquely matched font could not be
// instantiated. One reason could be that the font was outside the
// C:\Windows\Fonts directory and thus not accessible due to sandbox
// restrictions.
sk_sp<SkTypeface> local_typeface = SkTypeface::MakeFromFile(
font_file_path.AsUTF8Unsafe().c_str(), ttc_index);
InstantiationResult result = InstantiationResult::kSuccess;
// There is a chance that some systems have managed to register fonts into the
// Windows system font collection outside the C:\Windows\Fonts directory. For
// sandboxing reasons, we are unable to access them here. This histogram
// serves to quantify how often this case occurs and whether we need and
// additional sandbox helper to open the file handle on the browser process
// side.
if (!local_typeface) {
base::FilePath windows_fonts_path(L"C:\\WINDOWS\\FONTS");
if (!windows_fonts_path.IsParent(font_file_path))
result = InstantiationResult::kErrorOutsideWindowsFontsDirectory;
else
result = InstantiationResult::kErrorOther;
}
UMA_HISTOGRAM_ENUMERATION(
"Blink.Fonts.WindowsUniqueLocalFontInstantiationResult", result);
return local_typeface;
}
bool FontUniqueNameLookupWin::IsFontUniqueNameLookupReadyForSyncLookup() {
if (!RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled())
return true;
EnsureServiceConnected();
if (!lookup_mode_.has_value()) {
blink::mojom::UniqueFontLookupMode lookup_mode_from_mojo;
service_->GetUniqueFontLookupMode(&lookup_mode_from_mojo);
lookup_mode_ = lookup_mode_from_mojo;
}
if (lookup_mode_ == blink::mojom::UniqueFontLookupMode::kSingleLookups) {
return true;
}
DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
// 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;
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.
DCHECK_EQ(pending_callbacks_.size(), 0u);
ReceiveReadOnlySharedMemoryRegion(std::move(shared_memory_region));
}
// If it wasn't available synchronously LocalFontFaceSource has to call
// PrepareFontUniqueNameLookup.
return *sync_available_;
}
void FontUniqueNameLookupWin::EnsureServiceConnected() {
if (service_)
return;
Platform::Current()->GetBrowserInterfaceBroker()->GetInterface(
service_.BindNewPipeAndPassReceiver());
}
void FontUniqueNameLookupWin::PrepareFontUniqueNameLookup(
NotifyFontUniqueNameLookupReady callback) {
DCHECK(!font_table_matcher_.get());
DCHECK(RuntimeEnabledFeatures::FontSrcLocalMatchingEnabled());
DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
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();
service_->GetUniqueNameLookupTable(base::BindOnce(
&FontUniqueNameLookupWin::ReceiveReadOnlySharedMemoryRegion,
base::Unretained(this)));
}
void FontUniqueNameLookupWin::ReceiveReadOnlySharedMemoryRegion(
base::ReadOnlySharedMemoryRegion shared_memory_region) {
DCHECK(lookup_mode_ == blink::mojom::UniqueFontLookupMode::kRetrieveTable);
font_table_matcher_ =
std::make_unique<FontTableMatcher>(shared_memory_region.Map());
while (!pending_callbacks_.IsEmpty()) {
NotifyFontUniqueNameLookupReady callback = pending_callbacks_.TakeFirst();
std::move(callback).Run();
}
}
} // namespace blink