blob: cdf238e471c47f1ca9e49839fb6e44c21f915ec3 [file] [log] [blame]
/*
* Copyright (C) 2013 Google 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:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * 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.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT
* OWNER 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/font_face.h"
#include "base/metrics/histogram_macros.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/string_or_array_buffer_or_array_buffer_view.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_font_face_descriptors.h"
#include "third_party/blink/renderer/core/css/binary_data_font_face_source.h"
#include "third_party/blink/renderer/core/css/css_font_face.h"
#include "third_party/blink/renderer/core/css/css_font_face_src_value.h"
#include "third_party/blink/renderer/core/css/css_font_family_value.h"
#include "third_party/blink/renderer/core/css/css_font_selector.h"
#include "third_party/blink/renderer/core/css/css_font_style_range_value.h"
#include "third_party/blink/renderer/core/css/css_identifier_value.h"
#include "third_party/blink/renderer/core/css/css_property_value_set.h"
#include "third_party/blink/renderer/core/css/css_unicode_range_value.h"
#include "third_party/blink/renderer/core/css/css_value_list.h"
#include "third_party/blink/renderer/core/css/css_value_pair.h"
#include "third_party/blink/renderer/core/css/local_font_face_source.h"
#include "third_party/blink/renderer/core/css/offscreen_font_selector.h"
#include "third_party/blink/renderer/core/css/parser/at_rule_descriptor_parser.h"
#include "third_party/blink/renderer/core/css/parser/css_parser.h"
#include "third_party/blink/renderer/core/css/remote_font_face_source.h"
#include "third_party/blink/renderer/core/css/style_engine.h"
#include "third_party/blink/renderer/core/css/style_rule.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer.h"
#include "third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h"
#include "third_party/blink/renderer/core/workers/worker_global_scope.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/font_family_names.h"
#include "third_party/blink/renderer/platform/fonts/font_metrics_override.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/wtf/shared_buffer.h"
namespace blink {
namespace {
const CSSValue* ParseCSSValue(const ExecutionContext* context,
const String& value,
AtRuleDescriptorID descriptor_id) {
auto* window = DynamicTo<LocalDOMWindow>(context);
CSSParserContext* parser_context =
window ? MakeGarbageCollected<CSSParserContext>(*window->document())
: MakeGarbageCollected<CSSParserContext>(*context);
return AtRuleDescriptorParser::ParseFontFaceDescriptor(descriptor_id, value,
*parser_context);
}
CSSFontFace* CreateCSSFontFace(FontFace* font_face,
const CSSValue* unicode_range) {
Vector<UnicodeRange> ranges;
if (const auto* range_list = To<CSSValueList>(unicode_range)) {
unsigned num_ranges = range_list->length();
for (unsigned i = 0; i < num_ranges; i++) {
const auto& range =
To<cssvalue::CSSUnicodeRangeValue>(range_list->Item(i));
ranges.push_back(UnicodeRange(range.From(), range.To()));
}
}
return MakeGarbageCollected<CSSFontFace>(font_face, ranges);
}
const CSSValue* ConvertFontMetricOverrideValue(const CSSValue* parsed_value) {
if (parsed_value && parsed_value->IsIdentifierValue()) {
// We store the "normal" keyword value as nullptr
DCHECK_EQ(CSSValueID::kNormal,
To<CSSIdentifierValue>(parsed_value)->GetValueID());
return nullptr;
}
return parsed_value;
}
} // namespace
FontFace* FontFace::Create(ExecutionContext* context,
const AtomicString& family,
StringOrArrayBufferOrArrayBufferView& source,
const FontFaceDescriptors* descriptors) {
if (source.IsString())
return Create(context, family, source.GetAsString(), descriptors);
if (source.IsArrayBuffer())
return Create(context, family, source.GetAsArrayBuffer(), descriptors);
if (source.IsArrayBufferView()) {
return Create(context, family, source.GetAsArrayBufferView().Get(),
descriptors);
}
NOTREACHED();
return nullptr;
}
FontFace* FontFace::Create(ExecutionContext* context,
const AtomicString& family,
const String& source,
const FontFaceDescriptors* descriptors) {
FontFace* font_face =
MakeGarbageCollected<FontFace>(context, family, descriptors);
const CSSValue* src = ParseCSSValue(context, source, AtRuleDescriptorID::Src);
if (!src || !src->IsValueList()) {
font_face->SetError(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSyntaxError,
"The source provided ('" + source +
"') could not be parsed as a value list."));
}
font_face->InitCSSFontFace(context, *src);
return font_face;
}
FontFace* FontFace::Create(ExecutionContext* context,
const AtomicString& family,
DOMArrayBuffer* source,
const FontFaceDescriptors* descriptors) {
FontFace* font_face =
MakeGarbageCollected<FontFace>(context, family, descriptors);
font_face->InitCSSFontFace(static_cast<const unsigned char*>(source->Data()),
source->ByteLength());
return font_face;
}
FontFace* FontFace::Create(ExecutionContext* context,
const AtomicString& family,
DOMArrayBufferView* source,
const FontFaceDescriptors* descriptors) {
FontFace* font_face =
MakeGarbageCollected<FontFace>(context, family, descriptors);
font_face->InitCSSFontFace(
static_cast<const unsigned char*>(source->BaseAddress()),
source->byteLength());
return font_face;
}
FontFace* FontFace::Create(Document* document,
const StyleRuleFontFace* font_face_rule) {
const CSSPropertyValueSet& properties = font_face_rule->Properties();
// Obtain the font-family property and the src property. Both must be defined.
const CSSValue* family =
properties.GetPropertyCSSValue(AtRuleDescriptorID::FontFamily);
if (!family || (!family->IsFontFamilyValue() && !family->IsIdentifierValue()))
return nullptr;
const CSSValue* src = properties.GetPropertyCSSValue(AtRuleDescriptorID::Src);
if (!src || !src->IsValueList())
return nullptr;
FontFace* font_face =
MakeGarbageCollected<FontFace>(document->GetExecutionContext());
if (font_face->SetFamilyValue(*family) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::FontStyle) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::FontWeight) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::FontStretch) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::UnicodeRange) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::FontVariant) &&
font_face->SetPropertyFromStyle(
properties, AtRuleDescriptorID::FontFeatureSettings) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::FontDisplay) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::AscentOverride) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::DescentOverride) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::LineGapOverride) &&
font_face->SetPropertyFromStyle(properties,
AtRuleDescriptorID::AdvanceOverride) &&
font_face->GetFontSelectionCapabilities().IsValid() &&
!font_face->family().IsEmpty()) {
font_face->InitCSSFontFace(document->GetExecutionContext(), *src);
return font_face;
}
return nullptr;
}
FontFace::FontFace(ExecutionContext* context)
: ExecutionContextClient(context), status_(kUnloaded) {}
FontFace::FontFace(ExecutionContext* context,
const AtomicString& family,
const FontFaceDescriptors* descriptors)
: ExecutionContextClient(context), family_(family), status_(kUnloaded) {
SetPropertyFromString(context, descriptors->style(),
AtRuleDescriptorID::FontStyle);
SetPropertyFromString(context, descriptors->weight(),
AtRuleDescriptorID::FontWeight);
SetPropertyFromString(context, descriptors->stretch(),
AtRuleDescriptorID::FontStretch);
SetPropertyFromString(context, descriptors->unicodeRange(),
AtRuleDescriptorID::UnicodeRange);
SetPropertyFromString(context, descriptors->variant(),
AtRuleDescriptorID::FontVariant);
SetPropertyFromString(context, descriptors->featureSettings(),
AtRuleDescriptorID::FontFeatureSettings);
SetPropertyFromString(context, descriptors->display(),
AtRuleDescriptorID::FontDisplay);
if (RuntimeEnabledFeatures::CSSFontMetricsOverrideEnabled()) {
SetPropertyFromString(context, descriptors->ascentOverride(),
AtRuleDescriptorID::AscentOverride);
SetPropertyFromString(context, descriptors->descentOverride(),
AtRuleDescriptorID::DescentOverride);
SetPropertyFromString(context, descriptors->lineGapOverride(),
AtRuleDescriptorID::LineGapOverride);
}
}
FontFace::~FontFace() = default;
String FontFace::style() const {
return style_ ? style_->CssText() : "normal";
}
String FontFace::weight() const {
return weight_ ? weight_->CssText() : "normal";
}
String FontFace::stretch() const {
return stretch_ ? stretch_->CssText() : "normal";
}
String FontFace::unicodeRange() const {
return unicode_range_ ? unicode_range_->CssText() : "U+0-10FFFF";
}
String FontFace::variant() const {
return variant_ ? variant_->CssText() : "normal";
}
String FontFace::featureSettings() const {
return feature_settings_ ? feature_settings_->CssText() : "normal";
}
String FontFace::display() const {
return display_ ? display_->CssText() : "auto";
}
String FontFace::ascentOverride() const {
return ascent_override_ ? ascent_override_->CssText() : "normal";
}
String FontFace::descentOverride() const {
return descent_override_ ? descent_override_->CssText() : "normal";
}
String FontFace::lineGapOverride() const {
return line_gap_override_ ? line_gap_override_->CssText() : "normal";
}
void FontFace::setStyle(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontStyle,
&exception_state);
}
void FontFace::setWeight(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontWeight,
&exception_state);
}
void FontFace::setStretch(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontStretch,
&exception_state);
}
void FontFace::setUnicodeRange(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::UnicodeRange,
&exception_state);
}
void FontFace::setVariant(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontVariant,
&exception_state);
}
void FontFace::setFeatureSettings(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontFeatureSettings,
&exception_state);
}
void FontFace::setDisplay(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::FontDisplay,
&exception_state);
}
void FontFace::setAscentOverride(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::AscentOverride,
&exception_state);
}
void FontFace::setDescentOverride(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::DescentOverride,
&exception_state);
}
void FontFace::setLineGapOverride(ExecutionContext* context,
const String& s,
ExceptionState& exception_state) {
SetPropertyFromString(context, s, AtRuleDescriptorID::LineGapOverride,
&exception_state);
}
void FontFace::SetPropertyFromString(const ExecutionContext* context,
const String& s,
AtRuleDescriptorID descriptor_id,
ExceptionState* exception_state) {
const CSSValue* value = ParseCSSValue(context, s, descriptor_id);
if (value && SetPropertyValue(value, descriptor_id))
return;
String message = "Failed to set '" + s + "' as a property value.";
if (exception_state) {
exception_state->ThrowDOMException(DOMExceptionCode::kSyntaxError, message);
} else {
SetError(MakeGarbageCollected<DOMException>(DOMExceptionCode::kSyntaxError,
message));
}
}
bool FontFace::SetPropertyFromStyle(const CSSPropertyValueSet& properties,
AtRuleDescriptorID property_id) {
return SetPropertyValue(properties.GetPropertyCSSValue(property_id),
property_id);
}
bool FontFace::SetPropertyValue(const CSSValue* value,
AtRuleDescriptorID descriptor_id) {
switch (descriptor_id) {
case AtRuleDescriptorID::FontStyle:
style_ = value;
break;
case AtRuleDescriptorID::FontWeight:
weight_ = value;
break;
case AtRuleDescriptorID::FontStretch:
stretch_ = value;
break;
case AtRuleDescriptorID::UnicodeRange:
if (value && !value->IsValueList())
return false;
unicode_range_ = value;
break;
case AtRuleDescriptorID::FontVariant:
variant_ = value;
break;
case AtRuleDescriptorID::FontFeatureSettings:
feature_settings_ = value;
break;
case AtRuleDescriptorID::FontDisplay:
display_ = value;
if (css_font_face_)
css_font_face_->SetDisplay(CSSValueToFontDisplay(display_.Get()));
break;
case AtRuleDescriptorID::AscentOverride:
ascent_override_ = ConvertFontMetricOverrideValue(value);
break;
case AtRuleDescriptorID::DescentOverride:
descent_override_ = ConvertFontMetricOverrideValue(value);
break;
case AtRuleDescriptorID::LineGapOverride:
line_gap_override_ = ConvertFontMetricOverrideValue(value);
break;
case AtRuleDescriptorID::AdvanceOverride:
advance_override_ = ConvertFontMetricOverrideValue(value);
break;
default:
NOTREACHED();
return false;
}
return true;
}
bool FontFace::SetFamilyValue(const CSSValue& value) {
AtomicString family;
if (auto* family_value = DynamicTo<CSSFontFamilyValue>(value)) {
family = AtomicString(family_value->Value());
} else if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(value)) {
// We need to use the raw text for all the generic family types, since
// @font-face is a way of actually defining what font to use for those
// types.
switch (identifier_value->GetValueID()) {
case CSSValueID::kSerif:
family = font_family_names::kWebkitSerif;
break;
case CSSValueID::kSansSerif:
family = font_family_names::kWebkitSansSerif;
break;
case CSSValueID::kCursive:
family = font_family_names::kWebkitCursive;
break;
case CSSValueID::kFantasy:
family = font_family_names::kWebkitFantasy;
break;
case CSSValueID::kMonospace:
family = font_family_names::kWebkitMonospace;
break;
default:
return false;
}
}
family_ = family;
return true;
}
String FontFace::status() const {
switch (status_) {
case kUnloaded:
return "unloaded";
case kLoading:
return "loading";
case kLoaded:
return "loaded";
case kError:
return "error";
default:
NOTREACHED();
}
return g_empty_string;
}
void FontFace::SetLoadStatus(LoadStatusType status) {
status_ = status;
DCHECK(status_ != kError || error_);
if (!GetExecutionContext())
return;
if (status_ == kLoaded || status_ == kError) {
if (loaded_property_) {
if (status_ == kLoaded) {
GetExecutionContext()
->GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&LoadedProperty::Resolve<FontFace*>,
WrapPersistent(loaded_property_.Get()),
WrapPersistent(this)));
} else {
GetExecutionContext()
->GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&LoadedProperty::Reject<DOMException*>,
WrapPersistent(loaded_property_.Get()),
WrapPersistent(error_.Get())));
}
}
GetExecutionContext()
->GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&FontFace::RunCallbacks, WrapPersistent(this)));
}
}
void FontFace::RunCallbacks() {
HeapVector<Member<LoadFontCallback>> callbacks;
callbacks_.swap(callbacks);
for (wtf_size_t i = 0; i < callbacks.size(); ++i) {
if (status_ == kLoaded)
callbacks[i]->NotifyLoaded(this);
else
callbacks[i]->NotifyError(this);
}
}
void FontFace::SetError(DOMException* error) {
if (!error_) {
error_ = error ? error
: MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNetworkError);
}
SetLoadStatus(kError);
}
ScriptPromise FontFace::FontStatusPromise(ScriptState* script_state) {
if (!loaded_property_) {
loaded_property_ = MakeGarbageCollected<LoadedProperty>(
ExecutionContext::From(script_state));
if (status_ == kLoaded)
loaded_property_->Resolve(this);
else if (status_ == kError)
loaded_property_->Reject(error_.Get());
}
return loaded_property_->Promise(script_state->World());
}
ScriptPromise FontFace::load(ScriptState* script_state) {
if (status_ == kUnloaded)
css_font_face_->Load();
DidBeginImperativeLoad();
return FontStatusPromise(script_state);
}
void FontFace::LoadWithCallback(LoadFontCallback* callback) {
if (status_ == kUnloaded)
css_font_face_->Load();
AddCallback(callback);
}
void FontFace::AddCallback(LoadFontCallback* callback) {
if (status_ == kLoaded)
callback->NotifyLoaded(this);
else if (status_ == kError)
callback->NotifyError(this);
else
callbacks_.push_back(callback);
}
FontSelectionCapabilities FontFace::GetFontSelectionCapabilities() const {
// FontSelectionCapabilities represents a range of available width, slope and
// weight values. The first value of each pair is the minimum value, the
// second is the maximum value.
FontSelectionCapabilities normal_capabilities(
{NormalWidthValue(), NormalWidthValue()},
{NormalSlopeValue(), NormalSlopeValue()},
{NormalWeightValue(), NormalWeightValue()});
FontSelectionCapabilities capabilities(normal_capabilities);
if (stretch_) {
if (auto* stretch_identifier_value =
DynamicTo<CSSIdentifierValue>(stretch_.Get())) {
switch (stretch_identifier_value->GetValueID()) {
case CSSValueID::kUltraCondensed:
capabilities.width = {UltraCondensedWidthValue(),
UltraCondensedWidthValue()};
break;
case CSSValueID::kExtraCondensed:
capabilities.width = {ExtraCondensedWidthValue(),
ExtraCondensedWidthValue()};
break;
case CSSValueID::kCondensed:
capabilities.width = {CondensedWidthValue(), CondensedWidthValue()};
break;
case CSSValueID::kSemiCondensed:
capabilities.width = {SemiCondensedWidthValue(),
SemiCondensedWidthValue()};
break;
case CSSValueID::kSemiExpanded:
capabilities.width = {SemiExpandedWidthValue(),
SemiExpandedWidthValue()};
break;
case CSSValueID::kExpanded:
capabilities.width = {ExpandedWidthValue(), ExpandedWidthValue()};
break;
case CSSValueID::kExtraExpanded:
capabilities.width = {ExtraExpandedWidthValue(),
ExtraExpandedWidthValue()};
break;
case CSSValueID::kUltraExpanded:
capabilities.width = {UltraExpandedWidthValue(),
UltraExpandedWidthValue()};
break;
default:
break;
}
} else if (const auto* stretch_list =
DynamicTo<CSSValueList>(stretch_.Get())) {
// Transition FontFace interpretation of parsed values from
// CSSIdentifierValue to CSSValueList or CSSPrimitiveValue.
// TODO(drott) crbug.com/739139: Update the parser to only produce
// CSSPrimitiveValue or CSSValueList.
if (stretch_list->length() != 2)
return normal_capabilities;
const auto* stretch_from =
DynamicTo<CSSPrimitiveValue>(&stretch_list->Item(0));
const auto* stretch_to =
DynamicTo<CSSPrimitiveValue>(&stretch_list->Item(1));
if (!stretch_from || !stretch_to)
return normal_capabilities;
if (!stretch_from->IsPercentage() || !stretch_to->IsPercentage())
return normal_capabilities;
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (stretch_from->GetFloatValue() < stretch_to->GetFloatValue()) {
capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
FontSelectionValue(stretch_to->GetFloatValue())};
} else {
capabilities.width = {
FontSelectionValue(stretch_to->GetFloatValue()),
FontSelectionValue(stretch_from->GetFloatValue())};
}
} else if (auto* stretch_primitive_value =
DynamicTo<CSSPrimitiveValue>(stretch_.Get())) {
float stretch_value = stretch_primitive_value->GetFloatValue();
capabilities.width = {FontSelectionValue(stretch_value),
FontSelectionValue(stretch_value)};
} else {
NOTREACHED();
return normal_capabilities;
}
}
if (style_) {
if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(style_.Get())) {
switch (identifier_value->GetValueID()) {
case CSSValueID::kNormal:
capabilities.slope = {NormalSlopeValue(), NormalSlopeValue()};
break;
case CSSValueID::kOblique:
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
break;
case CSSValueID::kItalic:
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
break;
default:
break;
}
} else if (const auto* range_value =
DynamicTo<cssvalue::CSSFontStyleRangeValue>(style_.Get())) {
if (range_value->GetFontStyleValue()->IsIdentifierValue()) {
CSSValueID font_style_id =
range_value->GetFontStyleValue()->GetValueID();
if (!range_value->GetObliqueValues()) {
if (font_style_id == CSSValueID::kNormal)
capabilities.slope = {NormalSlopeValue(), NormalSlopeValue()};
DCHECK(font_style_id == CSSValueID::kItalic ||
font_style_id == CSSValueID::kOblique);
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
} else {
DCHECK(font_style_id == CSSValueID::kOblique);
size_t oblique_values_size =
range_value->GetObliqueValues()->length();
if (oblique_values_size == 1) {
const auto& range_start =
To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(0));
FontSelectionValue oblique_range(range_start.GetFloatValue());
capabilities.slope = {oblique_range, oblique_range};
} else {
DCHECK_EQ(oblique_values_size, 2u);
const auto& range_start =
To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(0));
const auto& range_end =
To<CSSPrimitiveValue>(range_value->GetObliqueValues()->Item(1));
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (range_start.GetFloatValue() < range_end.GetFloatValue()) {
capabilities.slope = {
FontSelectionValue(range_start.GetFloatValue()),
FontSelectionValue(range_end.GetFloatValue())};
} else {
capabilities.slope = {
FontSelectionValue(range_end.GetFloatValue()),
FontSelectionValue(range_start.GetFloatValue())};
}
}
}
}
} else {
NOTREACHED();
return normal_capabilities;
}
}
if (weight_) {
if (auto* identifier_value = DynamicTo<CSSIdentifierValue>(weight_.Get())) {
switch (identifier_value->GetValueID()) {
// Although 'lighter' and 'bolder' are valid keywords for
// font-weights, they are invalid inside font-face rules so they are
// ignored. Reference:
// http://www.w3.org/TR/css3-fonts/#descdef-font-weight.
case CSSValueID::kLighter:
case CSSValueID::kBolder:
break;
case CSSValueID::kNormal:
capabilities.weight = {NormalWeightValue(), NormalWeightValue()};
break;
case CSSValueID::kBold:
capabilities.weight = {BoldWeightValue(), BoldWeightValue()};
break;
default:
NOTREACHED();
break;
}
} else if (const auto* weight_list =
DynamicTo<CSSValueList>(weight_.Get())) {
if (weight_list->length() != 2)
return normal_capabilities;
const auto* weight_from =
DynamicTo<CSSPrimitiveValue>(&weight_list->Item(0));
const auto* weight_to =
DynamicTo<CSSPrimitiveValue>(&weight_list->Item(1));
if (!weight_from || !weight_to)
return normal_capabilities;
if (!weight_from->IsNumber() || !weight_to->IsNumber() ||
weight_from->GetFloatValue() < 1 || weight_to->GetFloatValue() > 1000)
return normal_capabilities;
// https://drafts.csswg.org/css-fonts/#font-prop-desc
// "User agents must swap the computed value of the startpoint and
// endpoint of the range in order to forbid decreasing ranges."
if (weight_from->GetFloatValue() < weight_to->GetFloatValue()) {
capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
FontSelectionValue(weight_to->GetFloatValue())};
} else {
capabilities.weight = {
FontSelectionValue(weight_to->GetFloatValue()),
FontSelectionValue(weight_from->GetFloatValue())};
}
} else if (auto* weight_primitive_value =
DynamicTo<CSSPrimitiveValue>(weight_.Get())) {
float weight_value = weight_primitive_value->GetFloatValue();
if (weight_value < 1 || weight_value > 1000)
return normal_capabilities;
capabilities.weight = {FontSelectionValue(weight_value),
FontSelectionValue(weight_value)};
} else {
NOTREACHED();
return normal_capabilities;
}
}
return capabilities;
}
size_t FontFace::ApproximateBlankCharacterCount() const {
if (status_ == kLoading)
return css_font_face_->ApproximateBlankCharacterCount();
return 0;
}
bool ContextAllowsDownload(ExecutionContext* context) {
if (!context) {
return false;
}
if (const auto* window = DynamicTo<LocalDOMWindow>(context)) {
const Settings* settings =
window->GetFrame() ? window->GetFrame()->GetSettings() : nullptr;
return settings && settings->GetDownloadableBinaryFontsEnabled();
}
// TODO(fserb): ideally, we would like to have the settings value available
// on workers. Right now, we don't support that.
return true;
}
void FontFace::InitCSSFontFace(ExecutionContext* context, const CSSValue& src) {
css_font_face_ = CreateCSSFontFace(this, unicode_range_.Get());
if (error_)
return;
// Each item in the src property's list is a single CSSFontFaceSource. Put
// them all into a CSSFontFace.
const auto& src_list = To<CSSValueList>(src);
int src_length = src_list.length();
for (int i = 0; i < src_length; i++) {
// An item in the list either specifies a string (local font name) or a URL
// (remote font to download).
const CSSFontFaceSrcValue& item = To<CSSFontFaceSrcValue>(src_list.Item(i));
FontSelector* font_selector = nullptr;
if (auto* window = DynamicTo<LocalDOMWindow>(context)) {
font_selector = window->document()->GetStyleEngine().GetFontSelector();
} else if (auto* scope = DynamicTo<WorkerGlobalScope>(context)) {
font_selector = scope->GetFontSelector();
} else {
NOTREACHED();
}
if (!item.IsLocal()) {
if (ContextAllowsDownload(context) && item.IsSupportedFormat()) {
RemoteFontFaceSource* source =
MakeGarbageCollected<RemoteFontFaceSource>(
css_font_face_, font_selector,
CSSValueToFontDisplay(display_.Get()));
item.Fetch(context, source);
css_font_face_->AddSource(source);
}
} else {
css_font_face_->AddSource(MakeGarbageCollected<LocalFontFaceSource>(
css_font_face_, font_selector, item.GetResource()));
}
}
if (display_) {
UMA_HISTOGRAM_ENUMERATION("WebFont.FontDisplayValue",
CSSValueToFontDisplay(display_.Get()));
}
}
void FontFace::InitCSSFontFace(const unsigned char* data, size_t size) {
css_font_face_ = CreateCSSFontFace(this, unicode_range_.Get());
if (error_)
return;
scoped_refptr<SharedBuffer> buffer = SharedBuffer::Create(data, size);
BinaryDataFontFaceSource* source =
MakeGarbageCollected<BinaryDataFontFaceSource>(
css_font_face_, buffer.get(), ots_parse_message_);
if (source->IsValid()) {
SetLoadStatus(kLoaded);
} else {
SetError(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kSyntaxError, "Invalid font data in ArrayBuffer."));
}
css_font_face_->AddSource(source);
}
void FontFace::Trace(Visitor* visitor) const {
visitor->Trace(style_);
visitor->Trace(weight_);
visitor->Trace(stretch_);
visitor->Trace(unicode_range_);
visitor->Trace(variant_);
visitor->Trace(feature_settings_);
visitor->Trace(display_);
visitor->Trace(ascent_override_);
visitor->Trace(descent_override_);
visitor->Trace(line_gap_override_);
visitor->Trace(advance_override_);
visitor->Trace(error_);
visitor->Trace(loaded_property_);
visitor->Trace(css_font_face_);
visitor->Trace(callbacks_);
ScriptWrappable::Trace(visitor);
ExecutionContextClient::Trace(visitor);
}
bool FontFace::HadBlankText() const {
return css_font_face_->HadBlankText();
}
bool FontFace::HasPendingActivity() const {
return status_ == kLoading && GetExecutionContext();
}
FontDisplay FontFace::GetFontDisplay() const {
return CSSValueToFontDisplay(display_.Get());
}
void FontFace::DidBeginImperativeLoad() {
if (!DomWindow())
return;
DomWindow()->document()->GetFontPreloadManager().ImperativeFontLoadingStarted(
this);
}
FontMetricsOverride FontFace::GetFontMetricsOverride() const {
FontMetricsOverride result;
if (ascent_override_) {
result.ascent_override =
To<CSSPrimitiveValue>(*ascent_override_).GetFloatValue() / 100;
}
if (descent_override_) {
result.descent_override =
To<CSSPrimitiveValue>(*descent_override_).GetFloatValue() / 100;
}
if (line_gap_override_) {
result.line_gap_override =
To<CSSPrimitiveValue>(*line_gap_override_).GetFloatValue() / 100;
}
if (advance_override_) {
const CSSValuePair& pair = To<CSSValuePair>(*advance_override_);
result.advance_override =
To<CSSPrimitiveValue>(pair.First()).GetFloatValue() / 100;
result.advance_override_vertical_upright =
To<CSSPrimitiveValue>(pair.Second()).GetFloatValue() / 100;
}
return result;
}
} // namespace blink