summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/FontFace.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/FontFace.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/css/FontFace.cpp')
-rw-r--r--Source/WebCore/css/FontFace.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/Source/WebCore/css/FontFace.cpp b/Source/WebCore/css/FontFace.cpp
new file mode 100644
index 000000000..6fb77c07a
--- /dev/null
+++ b/Source/WebCore/css/FontFace.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 "CSSFontFaceSource.h"
+#include "CSSFontFeatureValue.h"
+#include "CSSParser.h"
+#include "CSSUnicodeRangeValue.h"
+#include "CSSValueList.h"
+#include "CSSValuePool.h"
+#include "Document.h"
+#include "FontVariantBuilder.h"
+#include "JSFontFace.h"
+#include "StyleProperties.h"
+#include <runtime/ArrayBuffer.h>
+#include <runtime/ArrayBufferView.h>
+#include <runtime/JSCInlines.h>
+
+namespace WebCore {
+
+static bool populateFontFaceWithArrayBuffer(CSSFontFace& fontFace, Ref<JSC::ArrayBufferView>&& arrayBufferView)
+{
+ auto source = std::make_unique<CSSFontFaceSource>(fontFace, String(), nullptr, nullptr, WTFMove(arrayBufferView));
+ fontFace.adoptSource(WTFMove(source));
+ return false;
+}
+
+ExceptionOr<Ref<FontFace>> FontFace::create(Document& document, const String& family, Source&& source, const Descriptors& descriptors)
+{
+ auto result = adoptRef(*new FontFace(document.fontSelector()));
+
+ bool dataRequiresAsynchronousLoading = true;
+
+ auto setFamilyResult = result->setFamily(family);
+ if (setFamilyResult.hasException())
+ return setFamilyResult.releaseException();
+
+ auto sourceConversionResult = WTF::switchOn(source,
+ [&] (String& string) -> ExceptionOr<void> {
+ auto value = FontFace::parseString(string, CSSPropertySrc);
+ if (!is<CSSValueList>(value.get()))
+ return Exception { SYNTAX_ERR };
+ CSSFontFace::appendSources(result->backing(), downcast<CSSValueList>(*value), &document, false);
+ return { };
+ },
+ [&] (RefPtr<ArrayBufferView>& arrayBufferView) -> ExceptionOr<void> {
+ dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
+ return { };
+ },
+ [&] (RefPtr<ArrayBuffer>& arrayBuffer) -> ExceptionOr<void> {
+ unsigned byteLength = arrayBuffer->byteLength();
+ auto arrayBufferView = JSC::Uint8Array::create(WTFMove(arrayBuffer), 0, byteLength);
+ dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
+ return { };
+ }
+ );
+
+ if (sourceConversionResult.hasException())
+ return sourceConversionResult.releaseException();
+
+ // These ternaries match the default strings inside the FontFaceDescriptors dictionary inside FontFace.idl.
+ auto setStyleResult = result->setStyle(descriptors.style.isEmpty() ? ASCIILiteral("normal") : descriptors.style);
+ if (setStyleResult.hasException())
+ return setStyleResult.releaseException();
+ auto setWeightResult = result->setWeight(descriptors.weight.isEmpty() ? ASCIILiteral("normal") : descriptors.weight);
+ if (setWeightResult.hasException())
+ return setWeightResult.releaseException();
+ auto setStretchResult = result->setStretch(descriptors.stretch.isEmpty() ? ASCIILiteral("normal") : descriptors.stretch);
+ if (setStretchResult.hasException())
+ return setStretchResult.releaseException();
+ auto setUnicodeRangeResult = result->setUnicodeRange(descriptors.unicodeRange.isEmpty() ? ASCIILiteral("U+0-10FFFF") : descriptors.unicodeRange);
+ if (setUnicodeRangeResult.hasException())
+ return setUnicodeRangeResult.releaseException();
+ auto setVariantResult = result->setVariant(descriptors.variant.isEmpty() ? ASCIILiteral("normal") : descriptors.variant);
+ if (setVariantResult.hasException())
+ return setVariantResult.releaseException();
+ auto setFeatureSettingsResult = result->setFeatureSettings(descriptors.featureSettings.isEmpty() ? ASCIILiteral("normal") : descriptors.featureSettings);
+ if (setFeatureSettingsResult.hasException())
+ return setFeatureSettingsResult.releaseException();
+
+ if (!dataRequiresAsynchronousLoading) {
+ result->backing().load();
+ ASSERT(result->backing().status() == CSSFontFace::Status::Success);
+ }
+
+ return WTFMove(result);
+}
+
+Ref<FontFace> FontFace::create(CSSFontFace& face)
+{
+ return adoptRef(*new FontFace(face));
+}
+
+FontFace::FontFace(CSSFontSelector& fontSelector)
+ : m_weakPtrFactory(this)
+ , m_backing(CSSFontFace::create(&fontSelector, nullptr, this))
+{
+ m_backing->addClient(*this);
+}
+
+FontFace::FontFace(CSSFontFace& face)
+ : m_weakPtrFactory(this)
+ , m_backing(face)
+{
+ 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)
+{
+ // FIXME: Should use the Document to get the right parsing mode.
+ return CSSParser::parseFontFaceDescriptor(propertyID, string, HTMLStandardMode);
+}
+
+ExceptionOr<void> FontFace::setFamily(const String& family)
+{
+ if (family.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ bool success = false;
+ if (auto value = parseString(family, CSSPropertyFontFamily))
+ success = m_backing->setFamilies(*value);
+ if (!success)
+ return Exception { SYNTAX_ERR };
+ return { };
+}
+
+ExceptionOr<void> FontFace::setStyle(const String& style)
+{
+ if (style.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ bool success = false;
+ if (auto value = parseString(style, CSSPropertyFontStyle))
+ success = m_backing->setStyle(*value);
+ if (!success)
+ return Exception { SYNTAX_ERR };
+ return { };
+}
+
+ExceptionOr<void> FontFace::setWeight(const String& weight)
+{
+ if (weight.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ bool success = false;
+ if (auto value = parseString(weight, CSSPropertyFontWeight))
+ success = m_backing->setWeight(*value);
+ if (!success)
+ return Exception { SYNTAX_ERR };
+ return { };
+}
+
+ExceptionOr<void> FontFace::setStretch(const String&)
+{
+ // We don't support font-stretch. Swallow the call.
+ return { };
+}
+
+ExceptionOr<void> FontFace::setUnicodeRange(const String& unicodeRange)
+{
+ if (unicodeRange.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ bool success = false;
+ if (auto value = parseString(unicodeRange, CSSPropertyUnicodeRange))
+ success = m_backing->setUnicodeRange(*value);
+ if (!success)
+ return Exception { SYNTAX_ERR };
+ return { };
+}
+
+ExceptionOr<void> FontFace::setVariant(const String& variant)
+{
+ if (variant.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ auto style = MutableStyleProperties::create();
+ auto result = CSSParser::parseValue(style, CSSPropertyFontVariant, variant, true, HTMLStandardMode);
+ if (result == CSSParser::ParseResult::Error)
+ return Exception { SYNTAX_ERR };
+
+ // FIXME: Would be much better to stage the new settings and set them all at once
+ // instead of this dance where we make a backup and revert to it if something fails.
+ 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) {
+ m_backing->setVariantSettings(backup);
+ return Exception { SYNTAX_ERR };
+ }
+
+ return { };
+}
+
+ExceptionOr<void> FontFace::setFeatureSettings(const String& featureSettings)
+{
+ if (featureSettings.isEmpty())
+ return Exception { SYNTAX_ERR };
+
+ auto value = parseString(featureSettings, CSSPropertyFontFeatureSettings);
+ if (!value)
+ return Exception { SYNTAX_ERR };
+ m_backing->setFeatureSettings(*value);
+ return { };
+}
+
+String FontFace::family() const
+{
+ m_backing->updateStyleIfNeeded();
+ return m_backing->families()->cssText();
+}
+
+String FontFace::style() const
+{
+ m_backing->updateStyleIfNeeded();
+ 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
+{
+ m_backing->updateStyleIfNeeded();
+ 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 ASCIILiteral("normal");
+}
+
+String FontFace::unicodeRange() const
+{
+ m_backing->updateStyleIfNeeded();
+ if (!m_backing->ranges().size())
+ return ASCIILiteral("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
+{
+ m_backing->updateStyleIfNeeded();
+ return computeFontVariant(m_backing->variantSettings())->cssText();
+}
+
+String FontFace::featureSettings() const
+{
+ m_backing->updateStyleIfNeeded();
+ if (!m_backing->featureSettings().size())
+ return ASCIILiteral("normal");
+ RefPtr<CSSValueList> list = CSSValueList::createCommaSeparated();
+ for (auto& feature : m_backing->featureSettings())
+ list->append(CSSFontFeatureValue::create(FontTag(feature.tag()), feature.value()));
+ return list->cssText();
+}
+
+auto FontFace::status() const -> LoadStatus
+{
+ switch (m_backing->status()) {
+ case CSSFontFace::Status::Pending:
+ return LoadStatus::Unloaded;
+ case CSSFontFace::Status::Loading:
+ return LoadStatus::Loading;
+ case CSSFontFace::Status::TimedOut:
+ return LoadStatus::Error;
+ case CSSFontFace::Status::Success:
+ return LoadStatus::Loaded;
+ case CSSFontFace::Status::Failure:
+ return LoadStatus::Error;
+ }
+ ASSERT_NOT_REACHED();
+ return LoadStatus::Error;
+}
+
+void FontFace::adopt(CSSFontFace& newFace)
+{
+ m_backing->removeClient(*this);
+ m_backing = newFace;
+ m_backing->addClient(*this);
+ newFace.setWrapper(*this);
+}
+
+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:
+ break;
+ case CSSFontFace::Status::Success:
+ if (m_promise)
+ std::exchange(m_promise, std::nullopt)->resolve(*this);
+ deref();
+ return;
+ case CSSFontFace::Status::Failure:
+ if (m_promise)
+ std::exchange(m_promise, std::nullopt)->reject(NETWORK_ERR);
+ deref();
+ return;
+ case CSSFontFace::Status::Pending:
+ ASSERT_NOT_REACHED();
+ return;
+ }
+}
+
+void FontFace::registerLoaded(Promise&& promise)
+{
+ ASSERT(!m_promise);
+ switch (m_backing->status()) {
+ case CSSFontFace::Status::Loading:
+ case CSSFontFace::Status::Pending:
+ m_promise = WTFMove(promise);
+ return;
+ case CSSFontFace::Status::Success:
+ promise.resolve(*this);
+ return;
+ case CSSFontFace::Status::TimedOut:
+ case CSSFontFace::Status::Failure:
+ promise.reject(NETWORK_ERR);
+ return;
+ }
+}
+
+void FontFace::load()
+{
+ m_backing->load();
+}
+
+}