blob: 432a8856b99929458984da62c5e4b8fefabe68f8 [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 "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/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/font_face_descriptors.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_frame.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/use_counter.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/histogram.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/shared_buffer.h"
namespace blink {
namespace {
const CSSValue* ParseCSSValue(const ExecutionContext* context,
const String& value,
AtRuleDescriptorID descriptor_id) {
CSSParserContext* parser_context =
IsA<Document>(context) ? CSSParserContext::Create(*To<Document>(context))
: CSSParserContext::Create(*context);
return AtRuleDescriptorParser::ParseFontFaceDescriptor(descriptor_id, value,
*parser_context);
}
FontDisplay CSSValueToFontDisplay(const CSSValue* value) {
if (value && value->IsIdentifierValue()) {
switch (ToCSSIdentifierValue(value)->GetValueID()) {
case CSSValueAuto:
return kFontDisplayAuto;
case CSSValueBlock:
return kFontDisplayBlock;
case CSSValueSwap:
return kFontDisplaySwap;
case CSSValueFallback:
return kFontDisplayFallback;
case CSSValueOptional:
return kFontDisplayOptional;
default:
break;
}
}
return kFontDisplayAuto;
}
CSSFontFace* CreateCSSFontFace(FontFace* font_face,
const CSSValue* unicode_range) {
Vector<UnicodeRange> ranges;
if (const CSSValueList* range_list = ToCSSValueList(unicode_range)) {
unsigned num_ranges = range_list->length();
for (unsigned i = 0; i < num_ranges; i++) {
const CSSUnicodeRangeValue& range =
ToCSSUnicodeRangeValue(range_list->Item(i));
ranges.push_back(UnicodeRange(range.From(), range.To()));
}
}
return MakeGarbageCollected<CSSFontFace>(font_face, ranges);
}
} // 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().View(),
descriptors);
}
NOTREACHED();
return nullptr;
}
FontFace* FontFace::Create(ExecutionContext* context,
const AtomicString& family,
const String& source,
const FontFaceDescriptors* descriptors) {
FontFace* font_face = new FontFace(context, family, descriptors);
const CSSValue* src = ParseCSSValue(context, source, AtRuleDescriptorID::Src);
if (!src || !src->IsValueList()) {
font_face->SetError(
DOMException::Create(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 = new 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 = new 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 = new FontFace(document);
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->GetFontSelectionCapabilities().IsValid() &&
!font_face->family().IsEmpty()) {
font_face->InitCSSFontFace(document, *src);
return font_face;
}
return nullptr;
}
FontFace::FontFace(ExecutionContext* context)
: ContextClient(context), status_(kUnloaded) {}
FontFace::FontFace(ExecutionContext* context,
const AtomicString& family,
const FontFaceDescriptors* descriptors)
: ContextClient(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);
}
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";
}
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::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(DOMException::Create(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;
default:
NOTREACHED();
return false;
}
return true;
}
bool FontFace::SetFamilyValue(const CSSValue& family_value) {
AtomicString family;
if (family_value.IsFontFamilyValue()) {
family = AtomicString(ToCSSFontFamilyValue(family_value).Value());
} else if (family_value.IsIdentifierValue()) {
// 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 (ToCSSIdentifierValue(family_value).GetValueID()) {
case CSSValueSerif:
family = font_family_names::kWebkitSerif;
break;
case CSSValueSansSerif:
family = font_family_names::kWebkitSansSerif;
break;
case CSSValueCursive:
family = font_family_names::kWebkitCursive;
break;
case CSSValueFantasy:
family = font_family_names::kWebkitFantasy;
break;
case CSSValueMonospace:
family = font_family_names::kWebkitMonospace;
break;
case CSSValueWebkitPictograph:
family = font_family_names::kWebkitPictograph;
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;
// When promises are resolved with 'thenables', instead of the object being
// returned directly, the 'then' method is executed (the resolver tries to
// resolve the thenable). This can lead to synchronous script execution, so we
// post a task. This does not apply to promise rejection (i.e. a thenable
// would be returned as is).
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
loaded_property_->Reject(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 : DOMException::Create(DOMExceptionCode::kNetworkError);
}
SetLoadStatus(kError);
}
ScriptPromise FontFace::FontStatusPromise(ScriptState* script_state) {
if (!loaded_property_) {
loaded_property_ = new LoadedProperty(ExecutionContext::From(script_state),
this, LoadedProperty::kLoaded);
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();
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 (stretch_->IsIdentifierValue()) {
switch (ToCSSIdentifierValue(stretch_.Get())->GetValueID()) {
case CSSValueUltraCondensed:
capabilities.width = {UltraCondensedWidthValue(),
UltraCondensedWidthValue()};
break;
case CSSValueExtraCondensed:
capabilities.width = {ExtraCondensedWidthValue(),
ExtraCondensedWidthValue()};
break;
case CSSValueCondensed:
capabilities.width = {CondensedWidthValue(), CondensedWidthValue()};
break;
case CSSValueSemiCondensed:
capabilities.width = {SemiCondensedWidthValue(),
SemiCondensedWidthValue()};
break;
case CSSValueSemiExpanded:
capabilities.width = {SemiExpandedWidthValue(),
SemiExpandedWidthValue()};
break;
case CSSValueExpanded:
capabilities.width = {ExpandedWidthValue(), ExpandedWidthValue()};
break;
case CSSValueExtraExpanded:
capabilities.width = {ExtraExpandedWidthValue(),
ExtraExpandedWidthValue()};
break;
case CSSValueUltraExpanded:
capabilities.width = {UltraExpandedWidthValue(),
UltraExpandedWidthValue()};
break;
default:
break;
}
} else if (stretch_->IsValueList()) {
// 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.
const CSSValueList* stretch_list = ToCSSValueList(stretch_);
if (stretch_list->length() != 2)
return normal_capabilities;
if (!stretch_list->Item(0).IsPrimitiveValue() ||
!stretch_list->Item(1).IsPrimitiveValue())
return normal_capabilities;
const CSSPrimitiveValue* stretch_from =
ToCSSPrimitiveValue(&stretch_list->Item(0));
const CSSPrimitiveValue* stretch_to =
ToCSSPrimitiveValue(&stretch_list->Item(1));
if (!stretch_from->IsPercentage() || !stretch_to->IsPercentage())
return normal_capabilities;
capabilities.width = {FontSelectionValue(stretch_from->GetFloatValue()),
FontSelectionValue(stretch_to->GetFloatValue())};
} else if (stretch_->IsPrimitiveValue()) {
float stretch_value =
ToCSSPrimitiveValue(stretch_.Get())->GetFloatValue();
capabilities.width = {FontSelectionValue(stretch_value),
FontSelectionValue(stretch_value)};
} else {
NOTREACHED();
return normal_capabilities;
}
}
if (style_) {
if (style_->IsIdentifierValue()) {
switch (ToCSSIdentifierValue(style_.Get())->GetValueID()) {
case CSSValueNormal:
capabilities.slope = {NormalSlopeValue(), NormalSlopeValue()};
break;
case CSSValueOblique:
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
break;
case CSSValueItalic:
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
break;
default:
break;
}
} else if (style_->IsFontStyleRangeValue()) {
const cssvalue::CSSFontStyleRangeValue* range_value =
cssvalue::ToCSSFontStyleRangeValue(style_);
if (range_value->GetFontStyleValue()->IsIdentifierValue()) {
CSSValueID font_style_id =
range_value->GetFontStyleValue()->GetValueID();
if (!range_value->GetObliqueValues()) {
if (font_style_id == CSSValueNormal)
capabilities.slope = {NormalSlopeValue(), NormalSlopeValue()};
DCHECK(font_style_id == CSSValueItalic ||
font_style_id == CSSValueOblique);
capabilities.slope = {ItalicSlopeValue(), ItalicSlopeValue()};
} else {
DCHECK(font_style_id == CSSValueOblique);
size_t oblique_values_size =
range_value->GetObliqueValues()->length();
if (oblique_values_size == 1) {
const CSSPrimitiveValue& range_start =
ToCSSPrimitiveValue(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 CSSPrimitiveValue& range_start =
ToCSSPrimitiveValue(range_value->GetObliqueValues()->Item(0));
const CSSPrimitiveValue& range_end =
ToCSSPrimitiveValue(range_value->GetObliqueValues()->Item(1));
capabilities.slope = {
FontSelectionValue(range_start.GetFloatValue()),
FontSelectionValue(range_end.GetFloatValue())};
}
}
}
} else {
NOTREACHED();
return normal_capabilities;
}
}
if (weight_) {
if (weight_->IsIdentifierValue()) {
switch (ToCSSIdentifierValue(weight_.Get())->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 CSSValueLighter:
case CSSValueBolder:
break;
case CSSValueNormal:
capabilities.weight = {NormalWeightValue(), NormalWeightValue()};
break;
case CSSValueBold:
capabilities.weight = {BoldWeightValue(), BoldWeightValue()};
break;
default:
NOTREACHED();
break;
}
} else if (weight_->IsValueList()) {
const CSSValueList* weight_list = ToCSSValueList(weight_);
if (weight_list->length() != 2)
return normal_capabilities;
if (!weight_list->Item(0).IsPrimitiveValue() ||
!weight_list->Item(1).IsPrimitiveValue())
return normal_capabilities;
const CSSPrimitiveValue* weight_from =
ToCSSPrimitiveValue(&weight_list->Item(0));
const CSSPrimitiveValue* weight_to =
ToCSSPrimitiveValue(&weight_list->Item(1));
if (!weight_from->IsNumber() || !weight_to->IsNumber() ||
weight_from->GetFloatValue() < 1 || weight_to->GetFloatValue() > 1000)
return normal_capabilities;
capabilities.weight = {FontSelectionValue(weight_from->GetFloatValue()),
FontSelectionValue(weight_to->GetFloatValue())};
} else if (weight_->IsPrimitiveValue()) {
float weight_value = ToCSSPrimitiveValue(weight_.Get())->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 Document* document = DynamicTo<Document>(context)) {
const Settings* settings = document->GetSettings();
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.
DCHECK(src.IsValueList());
const CSSValueList& src_list = ToCSSValueList(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 = ToCSSFontFaceSrcValue(src_list.Item(i));
if (!item.IsLocal()) {
if (ContextAllowsDownload(context) && item.IsSupportedFormat()) {
FontSelector* font_selector = nullptr;
if (auto* document = DynamicTo<Document>(context)) {
font_selector = document->GetStyleEngine().GetFontSelector();
} else if (auto* scope = DynamicTo<WorkerGlobalScope>(context)) {
font_selector = scope->GetFontSelector();
} else {
NOTREACHED();
}
RemoteFontFaceSource* source =
new RemoteFontFaceSource(css_font_face_, font_selector,
CSSValueToFontDisplay(display_.Get()));
item.Fetch(context, source);
css_font_face_->AddSource(source);
}
} else {
css_font_face_->AddSource(new LocalFontFaceSource(item.GetResource()));
}
}
if (display_) {
DEFINE_THREAD_SAFE_STATIC_LOCAL(
EnumerationHistogram, font_display_histogram,
("WebFont.FontDisplayValue", kFontDisplayEnumMax));
font_display_histogram.Count(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 =
new BinaryDataFontFaceSource(buffer.get(), ots_parse_message_);
if (source->IsValid())
SetLoadStatus(kLoaded);
else
SetError(DOMException::Create(DOMExceptionCode::kSyntaxError,
"Invalid font data in ArrayBuffer."));
css_font_face_->AddSource(source);
}
void FontFace::Trace(blink::Visitor* visitor) {
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(error_);
visitor->Trace(loaded_property_);
visitor->Trace(css_font_face_);
visitor->Trace(callbacks_);
ScriptWrappable::Trace(visitor);
ContextClient::Trace(visitor);
}
bool FontFace::HadBlankText() const {
return css_font_face_->HadBlankText();
}
bool FontFace::HasPendingActivity() const {
return status_ == kLoading && GetExecutionContext();
}
} // namespace blink