| /* |
| * Copyright (C) 2016 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 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 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 "config.h" |
| #include "FontFace.h" |
| |
| #include "CSSFontFace.h" |
| #include "CSSFontFeatureValue.h" |
| #include "CSSFontSelector.h" |
| #include "CSSUnicodeRangeValue.h" |
| #include "CSSValue.h" |
| #include "CSSValuePool.h" |
| #include "Dictionary.h" |
| #include "Document.h" |
| #include "ExceptionCodeDescription.h" |
| #include "FontVariantBuilder.h" |
| #include "JSDOMCoreException.h" |
| #include "JSFontFace.h" |
| #include "ScriptExecutionContext.h" |
| #include "StyleProperties.h" |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace WebCore { |
| |
| static FontFace::Promise createPromise(JSC::ExecState& exec) |
| { |
| JSDOMGlobalObject& globalObject = *JSC::jsCast<JSDOMGlobalObject*>(exec.lexicalGlobalObject()); |
| return FontFace::Promise(DeferredWrapper(&exec, &globalObject, JSC::JSPromiseDeferred::create(&exec, &globalObject))); |
| } |
| |
| static inline Optional<String> valueFromDictionary(const Dictionary& dictionary, const char* key) |
| { |
| String result; |
| dictionary.get(key, result); |
| return result.isNull() ? Nullopt : Optional<String>(result); |
| } |
| |
| RefPtr<FontFace> FontFace::create(JSC::ExecState& execState, ScriptExecutionContext& context, const String& family, const Deprecated::ScriptValue& source, const Dictionary& descriptors, ExceptionCode& ec) |
| { |
| if (!context.isDocument()) { |
| ec = TypeError; |
| return nullptr; |
| } |
| |
| Ref<FontFace> result = adoptRef(*new FontFace(execState, downcast<Document>(context).fontSelector())); |
| |
| result->setFamily(family, ec); |
| if (ec) |
| return nullptr; |
| |
| if (source.jsValue().isString()) { |
| String sourceString = source.jsValue().toString(&execState)->value(&execState); |
| auto value = FontFace::parseString(sourceString, CSSPropertySrc); |
| if (is<CSSValueList>(value.get())) { |
| CSSValueList& srcList = downcast<CSSValueList>(*value); |
| CSSFontFace::appendSources(result->backing(), srcList, &downcast<Document>(context), false); |
| } else { |
| ec = SYNTAX_ERR; |
| return nullptr; |
| } |
| } |
| |
| if (auto style = valueFromDictionary(descriptors, "style")) |
| result->setStyle(style.value(), ec); |
| if (ec) |
| return nullptr; |
| if (auto style = valueFromDictionary(descriptors, "weight")) |
| result->setWeight(style.value(), ec); |
| if (ec) |
| return nullptr; |
| if (auto style = valueFromDictionary(descriptors, "stretch")) |
| result->setStretch(style.value(), ec); |
| if (ec) |
| return nullptr; |
| if (auto style = valueFromDictionary(descriptors, "unicodeRange")) |
| result->setUnicodeRange(style.value(), ec); |
| if (ec) |
| return nullptr; |
| if (auto style = valueFromDictionary(descriptors, "variant")) |
| result->setVariant(style.value(), ec); |
| if (ec) |
| return nullptr; |
| if (auto style = valueFromDictionary(descriptors, "featureSettings")) |
| result->setFeatureSettings(style.value(), ec); |
| if (ec) |
| return nullptr; |
| |
| return result.ptr(); |
| } |
| |
| Ref<FontFace> FontFace::create(JSC::ExecState& execState, CSSFontFace& face) |
| { |
| return adoptRef(*new FontFace(execState, face)); |
| } |
| |
| FontFace::FontFace(JSC::ExecState& execState, CSSFontSelector& fontSelector) |
| : m_weakPtrFactory(this) |
| , m_backing(CSSFontFace::create(&fontSelector, nullptr, this)) |
| , m_promise(createPromise(execState)) |
| { |
| m_backing->addClient(*this); |
| } |
| |
| FontFace::FontFace(JSC::ExecState& execState, CSSFontFace& face) |
| : m_weakPtrFactory(this) |
| , m_backing(face) |
| , m_promise(createPromise(execState)) |
| { |
| m_backing->addClient(*this); |
| } |
| |
| FontFace::~FontFace() |
| { |
| m_backing->removeClient(*this); |
| } |
| |
| WeakPtr<FontFace> FontFace::createWeakPtr() const |
| { |
| return m_weakPtrFactory.createWeakPtr(); |
| } |
| |
| RefPtr<CSSValue> FontFace::parseString(const String& string, CSSPropertyID propertyID) |
| { |
| Ref<MutableStyleProperties> style = MutableStyleProperties::create(); |
| auto result = CSSParser::parseValue(style.ptr(), propertyID, string, true, CSSStrictMode, nullptr); |
| if (result == CSSParser::ParseResult::Error) |
| return nullptr; |
| return style->getPropertyCSSValue(propertyID); |
| } |
| |
| void FontFace::setFamily(const String& family, ExceptionCode& ec) |
| { |
| bool success = false; |
| if (auto value = parseString(family, CSSPropertyFontFamily)) |
| success = m_backing->setFamilies(*value); |
| if (!success) |
| ec = SYNTAX_ERR; |
| } |
| |
| void FontFace::setStyle(const String& style, ExceptionCode& ec) |
| { |
| bool success = false; |
| if (auto value = parseString(style, CSSPropertyFontStyle)) |
| success = m_backing->setStyle(*value); |
| if (!success) |
| ec = SYNTAX_ERR; |
| } |
| |
| void FontFace::setWeight(const String& weight, ExceptionCode& ec) |
| { |
| bool success = false; |
| if (auto value = parseString(weight, CSSPropertyFontWeight)) |
| success = m_backing->setWeight(*value); |
| if (!success) |
| ec = SYNTAX_ERR; |
| } |
| |
| void FontFace::setStretch(const String&, ExceptionCode&) |
| { |
| // We don't support font-stretch. Swallow the call. |
| } |
| |
| void FontFace::setUnicodeRange(const String& unicodeRange, ExceptionCode& ec) |
| { |
| bool success = false; |
| if (auto value = parseString(unicodeRange, CSSPropertyUnicodeRange)) |
| success = m_backing->setUnicodeRange(*value); |
| if (!success) |
| ec = SYNTAX_ERR; |
| } |
| |
| void FontFace::setVariant(const String& variant, ExceptionCode& ec) |
| { |
| Ref<MutableStyleProperties> style = MutableStyleProperties::create(); |
| auto result = CSSParser::parseValue(style.ptr(), CSSPropertyFontVariant, variant, true, CSSStrictMode, nullptr); |
| if (result != CSSParser::ParseResult::Error) { |
| FontVariantSettings backup = m_backing->variantSettings(); |
| auto normal = CSSValuePool::singleton().createIdentifierValue(CSSValueNormal); |
| bool success = true; |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantLigatures)) |
| success &= m_backing->setVariantLigatures(*value); |
| else |
| m_backing->setVariantLigatures(normal); |
| |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantPosition)) |
| success &= m_backing->setVariantPosition(*value); |
| else |
| m_backing->setVariantPosition(normal); |
| |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantCaps)) |
| success &= m_backing->setVariantCaps(*value); |
| else |
| m_backing->setVariantCaps(normal); |
| |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantNumeric)) |
| success &= m_backing->setVariantNumeric(*value); |
| else |
| m_backing->setVariantNumeric(normal); |
| |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantAlternates)) |
| success &= m_backing->setVariantAlternates(*value); |
| else |
| m_backing->setVariantAlternates(normal); |
| |
| if (auto value = style->getPropertyCSSValue(CSSPropertyFontVariantEastAsian)) |
| success &= m_backing->setVariantEastAsian(*value); |
| else |
| m_backing->setVariantEastAsian(normal); |
| |
| if (success) |
| return; |
| m_backing->setVariantSettings(backup); |
| } |
| ec = SYNTAX_ERR; |
| } |
| |
| void FontFace::setFeatureSettings(const String& featureSettings, ExceptionCode& ec) |
| { |
| bool success = false; |
| if (auto value = parseString(featureSettings, CSSPropertyFontFeatureSettings)) |
| success = m_backing->setFeatureSettings(*value); |
| if (!success) |
| ec = SYNTAX_ERR; |
| } |
| |
| String FontFace::family() const |
| { |
| return m_backing->families()->cssText(); |
| } |
| |
| String FontFace::style() const |
| { |
| switch (m_backing->traitsMask() & FontStyleMask) { |
| case FontStyleNormalMask: |
| return String("normal", String::ConstructFromLiteral); |
| case FontStyleItalicMask: |
| return String("italic", String::ConstructFromLiteral); |
| } |
| ASSERT_NOT_REACHED(); |
| return String("normal", String::ConstructFromLiteral); |
| } |
| |
| String FontFace::weight() const |
| { |
| switch (m_backing->traitsMask() & FontWeightMask) { |
| case FontWeight100Mask: |
| return String("100", String::ConstructFromLiteral); |
| case FontWeight200Mask: |
| return String("200", String::ConstructFromLiteral); |
| case FontWeight300Mask: |
| return String("300", String::ConstructFromLiteral); |
| case FontWeight400Mask: |
| return String("normal", String::ConstructFromLiteral); |
| case FontWeight500Mask: |
| return String("500", String::ConstructFromLiteral); |
| case FontWeight600Mask: |
| return String("600", String::ConstructFromLiteral); |
| case FontWeight700Mask: |
| return String("bold", String::ConstructFromLiteral); |
| case FontWeight800Mask: |
| return String("800", String::ConstructFromLiteral); |
| case FontWeight900Mask: |
| return String("900", String::ConstructFromLiteral); |
| } |
| ASSERT_NOT_REACHED(); |
| return String("normal", String::ConstructFromLiteral); |
| } |
| |
| String FontFace::stretch() const |
| { |
| return "normal"; |
| } |
| |
| String FontFace::unicodeRange() const |
| { |
| if (!m_backing->ranges().size()) |
| return "U+0-10FFFF"; |
| RefPtr<CSSValueList> values = CSSValueList::createCommaSeparated(); |
| for (auto& range : m_backing->ranges()) |
| values->append(CSSUnicodeRangeValue::create(range.from(), range.to())); |
| return values->cssText(); |
| } |
| |
| String FontFace::variant() const |
| { |
| return computeFontVariant(m_backing->variantSettings())->cssText(); |
| } |
| |
| String FontFace::featureSettings() const |
| { |
| if (!m_backing->featureSettings().size()) |
| return "normal"; |
| RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated(); |
| for (auto& feature : m_backing->featureSettings()) |
| list->append(CSSFontFeatureValue::create(FontFeatureTag(feature.tag()), feature.value())); |
| return list->cssText(); |
| } |
| |
| String FontFace::status() const |
| { |
| switch (m_backing->status()) { |
| case CSSFontFace::Status::Pending: |
| return String("unloaded", String::ConstructFromLiteral); |
| case CSSFontFace::Status::Loading: |
| return String("loading", String::ConstructFromLiteral); |
| case CSSFontFace::Status::TimedOut: |
| return String("error", String::ConstructFromLiteral); |
| case CSSFontFace::Status::Success: |
| return String("loaded", String::ConstructFromLiteral); |
| case CSSFontFace::Status::Failure: |
| return String("error", String::ConstructFromLiteral); |
| } |
| ASSERT_NOT_REACHED(); |
| return String("error", String::ConstructFromLiteral); |
| } |
| |
| void FontFace::fontStateChanged(CSSFontFace& face, CSSFontFace::Status, CSSFontFace::Status newState) |
| { |
| ASSERT_UNUSED(face, &face == m_backing.ptr()); |
| switch (newState) { |
| case CSSFontFace::Status::Loading: |
| // We still need to resolve promises when loading completes, even if all references to use have fallen out of scope. |
| ref(); |
| break; |
| case CSSFontFace::Status::TimedOut: |
| rejectPromise(NETWORK_ERR); |
| deref(); |
| return; |
| case CSSFontFace::Status::Success: |
| fulfillPromise(); |
| deref(); |
| return; |
| case CSSFontFace::Status::Failure: |
| rejectPromise(NETWORK_ERR); |
| deref(); |
| return; |
| default: |
| return; |
| } |
| } |
| |
| void FontFace::load() |
| { |
| m_backing->load(); |
| } |
| |
| void FontFace::fulfillPromise() |
| { |
| // Normally, DeferredWrapper::callFunction resets the reference to the promise. |
| // However, API semantics require our promise to live for the entire lifetime of the FontFace. |
| // Let's make sure it stays alive. |
| |
| Promise guard(m_promise); |
| m_promise.resolve(*this); |
| m_promise = guard; |
| } |
| |
| void FontFace::rejectPromise(ExceptionCode code) |
| { |
| // Normally, DeferredWrapper::callFunction resets the reference to the promise. |
| // However, API semantics require our promise to live for the entire lifetime of the FontFace. |
| // Let's make sure it stays alive. |
| |
| Promise guard(m_promise); |
| m_promise.reject(DOMCoreException::create(ExceptionCodeDescription(code)).get()); |
| m_promise = guard; |
| } |
| |
| } |