blob: 0eaf08f5e85d71f348fe712b9e48c64a98ab0af4 [file] [log] [blame]
/*
* Copyright (C) 2008 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "third_party/blink/renderer/core/css/css_segmented_font_face.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "third_party/blink/renderer/core/css/cascade_layer_map.h"
#include "third_party/blink/renderer/core/css/css_font_face.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/resolver/scoped_style_resolver.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/platform/fonts/font_cache.h"
#include "third_party/blink/renderer/platform/fonts/font_description.h"
#include "third_party/blink/renderer/platform/fonts/font_face_creation_params.h"
#include "third_party/blink/renderer/platform/fonts/segmented_font_data.h"
#include "third_party/blink/renderer/platform/fonts/simple_font_data.h"
namespace blink {
CSSSegmentedFontFace::CSSSegmentedFontFace(
FontSelectionCapabilities font_selection_capabilities)
: font_selection_capabilities_(font_selection_capabilities),
font_faces_(MakeGarbageCollected<FontFaceList>()),
approximate_character_count_(0) {}
CSSSegmentedFontFace::~CSSSegmentedFontFace() = default;
bool CSSSegmentedFontFace::IsValid() const {
// Valid if at least one font face is valid.
return font_faces_->ForEachUntilTrue(
[](const Member<FontFace>& font_face) -> bool {
return font_face->CssFontFace()->IsValid();
});
}
void CSSSegmentedFontFace::FontFaceInvalidated() {
font_data_table_.clear();
}
void CSSSegmentedFontFace::AddFontFace(FontFace* font_face,
bool css_connected) {
font_data_table_.clear();
font_face->CssFontFace()->AddSegmentedFontFace(this);
font_faces_->Insert(font_face, css_connected);
}
void CSSSegmentedFontFace::RemoveFontFace(FontFace* font_face) {
if (!font_faces_->Erase(font_face)) {
return;
}
font_data_table_.clear();
font_face->CssFontFace()->RemoveSegmentedFontFace(this);
}
const FontData* CSSSegmentedFontFace::GetFontData(
const FontDescription& font_description) {
if (!IsValid()) {
return nullptr;
}
bool is_unique_match = false;
FontCacheKey key =
font_description.CacheKey(FontFaceCreationParams(), is_unique_match);
// font_data_table_ caches FontData and SegmentedFontData instances, which
// provide SimpleFontData objects containing FontPlatformData objects. In the
// case of variable font animations, the variable instance SkTypeface is
// contained in these FontPlatformData objects. In other words, this cache
// stores the recently used variable font instances during a variable font
// animation. The cache reflects in how many different sizes, synthetic styles
// (bold / italic synthetic versions), or for variable fonts, in how many
// variable instances (stretch/style/weightand font-variation-setings
// variations) the font is instantiated. In non animation scenarios, there is
// usually only a small number of FontData/SegmentedFontData instances created
// per CSSSegmentedFontFace. Whereas in variable font animations, this number
// grows rapidly.
auto it = font_data_table_.find(key);
if (it != font_data_table_.end()) {
const SegmentedFontData* cached_font_data = it->value.Get();
if (cached_font_data && cached_font_data->NumFaces()) {
return cached_font_data;
}
}
SegmentedFontData* created_font_data =
MakeGarbageCollected<SegmentedFontData>();
FontDescription requested_font_description(font_description);
const FontSelectionRequest& font_selection_request =
font_description.GetFontSelectionRequest();
requested_font_description.SetSyntheticBold(
font_selection_capabilities_.weight.maximum < kBoldThreshold &&
font_selection_request.weight >= kBoldThreshold &&
font_description.SyntheticBoldAllowed());
requested_font_description.SetSyntheticItalic(
font_selection_capabilities_.slope.maximum < kItalicSlopeValue &&
font_selection_request.slope >= kItalicSlopeValue &&
font_description.SyntheticItalicAllowed());
font_faces_->ForEachReverse([&requested_font_description, &created_font_data](
const Member<FontFace>& font_face) {
if (!font_face->CssFontFace()->IsValid()) {
return;
}
if (const SimpleFontData* face_font_data =
font_face->CssFontFace()->GetFontData(requested_font_description)) {
DCHECK(!face_font_data->IsSegmented());
created_font_data->AppendFace(MakeGarbageCollected<FontDataForRangeSet>(
std::move(face_font_data), font_face->CssFontFace()->Ranges()));
}
});
if (created_font_data->NumFaces()) {
font_data_table_.insert(std::move(key), created_font_data);
return created_font_data;
}
return nullptr;
}
void CSSSegmentedFontFace::WillUseFontData(
const FontDescription& font_description,
const String& text) {
approximate_character_count_ += text.length();
font_faces_->ForEachReverseUntilTrue(
[&font_description, &text](const Member<FontFace>& font_face) -> bool {
return font_face->LoadStatus() != FontFace::kUnloaded ||
font_face->CssFontFace()->MaybeLoadFont(font_description, text);
});
}
void CSSSegmentedFontFace::WillUseRange(
const blink::FontDescription& font_description,
const blink::FontDataForRangeSet& range_set) {
// Iterating backwards since later defined unicode-range faces override
// previously defined ones, according to the CSS3 fonts module.
// https://drafts.csswg.org/css-fonts/#composite-fonts
font_faces_->ForEachReverseUntilTrue(
[&font_description,
&range_set](const Member<FontFace>& font_face) -> bool {
return font_face->CssFontFace()->MaybeLoadFont(font_description,
range_set);
});
}
bool CSSSegmentedFontFace::CheckFont(UChar32 c) const {
return !font_faces_->ForEachUntilTrue(
[&c](const Member<FontFace>& font_face) -> bool {
return font_face->LoadStatus() != FontFace::kLoaded &&
font_face->CssFontFace()->Ranges()->Contains(c);
});
}
void CSSSegmentedFontFace::Match(const String& text,
HeapVector<Member<FontFace>>* faces) const {
font_faces_->ForEach([&text, &faces](const Member<FontFace>& font_face) {
if (font_face->CssFontFace()->Ranges()->IntersectsWith(text)) {
faces->push_back(font_face);
}
});
}
void CSSSegmentedFontFace::Trace(Visitor* visitor) const {
visitor->Trace(font_data_table_);
visitor->Trace(font_faces_);
}
bool FontFaceList::IsEmpty() const {
return css_connected_face_.empty() && non_css_connected_face_.empty();
}
namespace {
bool CascadePriorityHigherThan(const FontFace& new_font_face,
const FontFace& existing_font_face) {
// We should reach here only for CSS-connected font faces, which must have an
// owner document. However, there are cases where we don't have a document
// here, possibly caused by ExecutionContext or Document lifecycle issues.
// TODO(crbug.com/1250831): Find out the root cause and fix it.
// Used to have base::debug::DumpWithoutCrashing(), but caused a lot of
// crashes, particularly on Android (crbug.com/1468721).
if (!new_font_face.GetDocument() || !existing_font_face.GetDocument()) {
// In the buggy case, to ensure a stable ordering, font faces without a
// document are considered higher priority.
return !new_font_face.GetDocument();
}
DCHECK_EQ(new_font_face.GetDocument(), existing_font_face.GetDocument());
DCHECK(new_font_face.GetStyleRule());
DCHECK(existing_font_face.GetStyleRule());
if (new_font_face.IsUserStyle() != existing_font_face.IsUserStyle()) {
return existing_font_face.IsUserStyle();
}
const CascadeLayerMap* map = nullptr;
if (new_font_face.IsUserStyle()) {
map =
new_font_face.GetDocument()->GetStyleEngine().GetUserCascadeLayerMap();
} else if (new_font_face.GetDocument()->GetScopedStyleResolver()) {
map = new_font_face.GetDocument()
->GetScopedStyleResolver()
->GetCascadeLayerMap();
}
if (!map) {
return true;
}
return map->CompareLayerOrder(
existing_font_face.GetStyleRule()->GetCascadeLayer(),
new_font_face.GetStyleRule()->GetCascadeLayer()) <= 0;
}
} // namespace
void FontFaceList::Insert(FontFace* font_face, bool css_connected) {
if (!css_connected) {
non_css_connected_face_.insert(font_face);
return;
}
auto it = css_connected_face_.end();
while (it != css_connected_face_.begin()) {
auto prev = it;
--prev;
if (CascadePriorityHigherThan(*font_face, **prev)) {
break;
}
it = prev;
}
css_connected_face_.InsertBefore(it, font_face);
}
bool FontFaceList::Erase(FontFace* font_face) {
FontFaceListPart::iterator it = css_connected_face_.find(font_face);
if (it != css_connected_face_.end()) {
css_connected_face_.erase(it);
return true;
}
it = non_css_connected_face_.find(font_face);
if (it != non_css_connected_face_.end()) {
non_css_connected_face_.erase(it);
return true;
}
return false;
}
bool FontFaceList::ForEachUntilTrue(
base::FunctionRef<bool(const Member<FontFace>&)> func) const {
for (auto& font_face : css_connected_face_) {
if (func(font_face)) {
return true;
}
}
for (auto& font_face : non_css_connected_face_) {
if (func(font_face)) {
return true;
}
}
return false;
}
void FontFaceList::ForEach(
base::FunctionRef<void(const Member<FontFace>&)> func) const {
for (auto& font_face : css_connected_face_) {
func(font_face);
}
for (auto& font_face : non_css_connected_face_) {
func(font_face);
}
}
void FontFaceList::ForEachReverseUntilTrue(
base::FunctionRef<bool(const Member<FontFace>&)> func) const {
for (auto& font_face : base::Reversed(non_css_connected_face_)) {
if (func(font_face)) {
return;
}
}
for (auto& font_face : base::Reversed(css_connected_face_)) {
if (func(font_face)) {
return;
}
}
}
void FontFaceList::ForEachReverse(
base::FunctionRef<void(const Member<FontFace>&)> func) const {
for (auto& font_face : base::Reversed(non_css_connected_face_)) {
func(font_face);
}
for (auto& font_face : base::Reversed(css_connected_face_)) {
func(font_face);
}
}
void FontFaceList::Trace(Visitor* visitor) const {
visitor->Trace(css_connected_face_);
visitor->Trace(non_css_connected_face_);
}
} // namespace blink