blob: 815e0c4ffa7c0248de821bce16f90ea260a3b691 [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/bind.h"
#include "base/callback.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/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"
// See comment below in CSSSegmentedFontFace::GetFontData - the cache from
// CSSSegmentedFontFace (which represents a group of @font-face declarations
// with identical FontSelectionCapabilities but differing by unicode-range) to
// FontData/SegmentedFontData, (i.e. the actual font blobs that can be used for
// shaping and painting retrieved from a CSSFontFaceSource) is usually small
// (less than a dozen, up to tens) for non-animation-cases, but grows fast to
// thousands when animating variable font parameters. Set a limit until we start
// dropping cache entries in animation scenarios.
static constexpr size_t kFontDataTableMaxSize = 250;
namespace blink {
CSSSegmentedFontFace::CSSSegmentedFontFace(
FontSelectionCapabilities font_selection_capabilities)
: font_selection_capabilities_(font_selection_capabilities),
font_data_table_(kFontDataTableMaxSize),
font_faces_(MakeGarbageCollected<FontFaceList>()),
approximate_character_count_(0) {}
CSSSegmentedFontFace::~CSSSegmentedFontFace() = default;
void CSSSegmentedFontFace::PruneTable() {
// Make sure the glyph page tree prunes out all uses of this custom font.
if (!font_data_table_.size())
return;
font_data_table_.Clear();
}
bool CSSSegmentedFontFace::IsValid() const {
// Valid if at least one font face is valid.
return font_faces_->ForEachUntilTrue(
WTF::BindRepeating([](Member<FontFace> font_face) -> bool {
if (font_face->CssFontFace()->IsValid())
return true;
return false;
}));
}
void CSSSegmentedFontFace::FontFaceInvalidated() {
PruneTable();
}
void CSSSegmentedFontFace::AddFontFace(FontFace* font_face,
bool css_connected) {
PruneTable();
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;
PruneTable();
font_face->CssFontFace()->RemoveSegmentedFontFace(this);
}
scoped_refptr<FontData> CSSSegmentedFontFace::GetFontData(
const FontDescription& font_description) {
if (!IsValid())
return nullptr;
const FontSelectionRequest& font_selection_request =
font_description.GetFontSelectionRequest();
bool is_unique_match = false;
FontCacheKey key = font_description.CacheKey(
FontFaceCreationParams(), is_unique_match, font_selection_request);
// 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.
scoped_refptr<SegmentedFontData>* cached_font_data =
font_data_table_.Get(key);
if (cached_font_data && (*cached_font_data) &&
(*cached_font_data)->NumFaces())
return *cached_font_data;
scoped_refptr<SegmentedFontData> created_font_data;
created_font_data = SegmentedFontData::Create();
FontDescription requested_font_description(font_description);
if (!font_selection_capabilities_.HasRange()) {
requested_font_description.SetSyntheticBold(
font_selection_capabilities_.weight.maximum < BoldThreshold() &&
font_selection_request.weight >= BoldThreshold());
requested_font_description.SetSyntheticItalic(
font_selection_capabilities_.slope.maximum == NormalSlopeValue() &&
font_selection_request.slope == ItalicSlopeValue());
}
font_faces_->ForEachReverse(WTF::BindRepeating(
[](const FontDescription& requested_font_description,
scoped_refptr<SegmentedFontData> created_font_data,
Member<FontFace> font_face) {
if (!font_face->CssFontFace()->IsValid())
return;
if (scoped_refptr<SimpleFontData> face_font_data =
font_face->CssFontFace()->GetFontData(
requested_font_description)) {
DCHECK(!face_font_data->IsSegmented());
if (face_font_data->IsCustomFont()) {
created_font_data->AppendFace(base::AdoptRef(
new FontDataForRangeSet(std::move(face_font_data),
font_face->CssFontFace()->Ranges())));
} else {
created_font_data->AppendFace(
base::AdoptRef(new FontDataForRangeSetFromCache(
std::move(face_font_data),
font_face->CssFontFace()->Ranges())));
}
}
},
requested_font_description, created_font_data));
if (created_font_data->NumFaces()) {
scoped_refptr<SegmentedFontData> put_to_cache(created_font_data);
font_data_table_.Put(std::move(key), std::move(put_to_cache));
// No release, we have a reference to an object in the cache which should
// retain the ref count it has.
return created_font_data;
}
return nullptr;
}
void CSSSegmentedFontFace::WillUseFontData(
const FontDescription& font_description,
const String& text) {
approximate_character_count_ += text.length();
font_faces_->ForEachReverseUntilTrue(WTF::BindRepeating(
[](const FontDescription& font_description, const String& text,
Member<FontFace> font_face) -> bool {
if (font_face->LoadStatus() != FontFace::kUnloaded)
return true;
if (font_face->CssFontFace()->MaybeLoadFont(font_description, text))
return true;
return false;
},
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(WTF::BindRepeating(
[](const blink::FontDescription& font_description,
const blink::FontDataForRangeSet& range_set,
Member<FontFace> font_face) -> bool {
CSSFontFace* css_font_face = font_face->CssFontFace();
if (css_font_face->MaybeLoadFont(font_description, range_set))
return true;
return false;
},
font_description, range_set));
}
bool CSSSegmentedFontFace::CheckFont(const String& text) const {
return font_faces_->ForEachUntilFalse(WTF::BindRepeating(
[](const String& text, Member<FontFace> font_face) -> bool {
if (font_face->LoadStatus() != FontFace::kLoaded &&
font_face->CssFontFace()->Ranges()->IntersectsWith(text))
return false;
return true;
},
text));
}
void CSSSegmentedFontFace::Match(const String& text,
HeapVector<Member<FontFace>>* faces) const {
// WTF::BindRepeating requires WrapPersistent around |faces|, which is fine,
// because the wrap's lifetime is contained to this function.
font_faces_->ForEach(WTF::BindRepeating(
[](const String& text, HeapVector<Member<FontFace>>* faces,
Member<FontFace> font_face) {
if (font_face->CssFontFace()->Ranges()->IntersectsWith(text))
faces->push_back(font_face);
},
text, WrapPersistent(faces)));
}
void CSSSegmentedFontFace::Trace(Visitor* visitor) const {
visitor->Trace(font_faces_);
}
bool FontFaceList::IsEmpty() const {
return css_connected_face_.IsEmpty() && non_css_connected_face_.IsEmpty();
}
void FontFaceList::Insert(FontFace* font_face, bool css_connected) {
if (css_connected) {
css_connected_face_.insert(font_face);
} else {
non_css_connected_face_.insert(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;
}
// A callback that will be fed into |ForEach*UntilTrue|.
// |callback| wants the |ForEach*UntilFalse| operation to stop iterating on
// false, so its return value has to be negated in order for it to work with
// |ForEach*UntilTrue|.
static bool NegatingCallback(
const base::RepeatingCallback<bool(Member<FontFace>)>& callback,
Member<FontFace> font_face) {
return !callback.Run(font_face);
}
// A callback that will be fed into |ForEach*UntilTrue|, when we want it to
// always continue iterating, so false has to be always returned.
static bool FalseReturningCallback(
const base::RepeatingCallback<void(Member<FontFace>)>& callback,
Member<FontFace> font_face) {
callback.Run(font_face);
return false;
}
bool FontFaceList::ForEachUntilTrue(
const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
for (Member<FontFace> font_face : css_connected_face_) {
if (callback.Run(font_face))
return true;
}
for (Member<FontFace> font_face : non_css_connected_face_) {
if (callback.Run(font_face))
return true;
}
return false;
}
bool FontFaceList::ForEachUntilFalse(
const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
base::RepeatingCallback<bool(Member<FontFace>)> negating_callback =
WTF::BindRepeating(NegatingCallback, callback);
// When |callback| returns |false|, |ForEachUntilTrue(negating_callback)| will
// stop iterating and return |true|, so negate it.
return !ForEachUntilTrue(negating_callback);
}
void FontFaceList::ForEach(
const base::RepeatingCallback<void(Member<FontFace>)>& func) const {
base::RepeatingCallback<bool(Member<FontFace>)> false_returning_callback =
WTF::BindRepeating(FalseReturningCallback, func);
ForEachUntilTrue(false_returning_callback);
}
bool FontFaceList::ForEachReverseUntilTrue(
const base::RepeatingCallback<bool(Member<FontFace>)>& func) const {
for (auto it = non_css_connected_face_.rbegin();
it != non_css_connected_face_.rend(); ++it) {
if (func.Run(*it))
return true;
}
for (auto it = css_connected_face_.rbegin(); it != css_connected_face_.rend();
++it) {
if (func.Run(*it))
return true;
}
return false;
}
bool FontFaceList::ForEachReverseUntilFalse(
const base::RepeatingCallback<bool(Member<FontFace>)>& callback) const {
base::RepeatingCallback<bool(Member<FontFace>)> negating_callback =
WTF::BindRepeating(NegatingCallback, callback);
// When |callback| returns |false|,
// |ForEachReverseUntilTrue(negating_callback)| will stop iterating and return
// |true|, so negate it.
return !ForEachReverseUntilTrue(negating_callback);
}
void FontFaceList::ForEachReverse(
const base::RepeatingCallback<void(Member<FontFace>)>& callback) const {
base::RepeatingCallback<bool(Member<FontFace>)> false_returning_callback =
WTF::BindRepeating(FalseReturningCallback, callback);
ForEachReverseUntilTrue(false_returning_callback);
}
void FontFaceList::Trace(Visitor* visitor) const {
visitor->Trace(css_connected_face_);
visitor->Trace(non_css_connected_face_);
}
} // namespace blink