Support ArrayBufferViews in the CSS Font Loading API
https://bugs.webkit.org/show_bug.cgi?id=157694
<rdar://problem/25554267>
Source/WebCore:
This patch adds a new mode to CSSFontFaceSource for immediate (ArrayBuffer) data.
Then, FontFace can simply be hooked up to this new mode.
Reviewed by Darin Adler.
Test: fast/text/css-font-loading-arraybuffer.html
* css/CSSFontFaceSource.cpp:
(WebCore::CSSFontFaceSource::CSSFontFaceSource):
(WebCore::CSSFontFaceSource::font):
* css/CSSFontFaceSource.h:
* css/FontFace.cpp:
(WebCore::FontFace::create):
* loader/cache/CachedFont.cpp:
(WebCore::CachedFont::ensureCustomFontData):
(WebCore::CachedFont::createCustomFontData):
(WebCore::CachedFont::createFont):
(WebCore::CachedFont::platformDataFromCustomData):
* loader/cache/CachedFont.h:
LayoutTests:
Reviewed by Darin Adler.
* fast/text/css-font-loading-arraybuffer-expected.txt: Added.
* fast/text/css-font-loading-arraybuffer.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@200921 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 5ca1097..51179e9 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,14 @@
+2016-05-14 Myles C. Maxfield <mmaxfield@apple.com>
+
+ Support ArrayBufferViews in the CSS Font Loading API
+ https://bugs.webkit.org/show_bug.cgi?id=157694
+ <rdar://problem/25554267>
+
+ Reviewed by Darin Adler.
+
+ * fast/text/css-font-loading-arraybuffer-expected.txt: Added.
+ * fast/text/css-font-loading-arraybuffer.html: Added.
+
2016-05-13 Zalan Bujtas <zalan@apple.com>
All scrolling height/width values should be integral rounded.
diff --git a/LayoutTests/fast/text/css-font-loading-arraybuffer-expected.txt b/LayoutTests/fast/text/css-font-loading-arraybuffer-expected.txt
new file mode 100644
index 0000000..728131c
--- /dev/null
+++ b/LayoutTests/fast/text/css-font-loading-arraybuffer-expected.txt
@@ -0,0 +1,13 @@
+This test makes sure that an ArrayBufferView can be successfully passed to the FontFace constructor.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS fontFace1.status is "loaded"
+PASS fontFace2.status is "loaded"
+PASS document.getElementById('probe1').getBoundingClientRect().width is 100
+PASS document.getElementById('probe2').getBoundingClientRect().width is 100
+PASS successfullyParsed is true
+
+TEST COMPLETE
+l l
diff --git a/LayoutTests/fast/text/css-font-loading-arraybuffer.html b/LayoutTests/fast/text/css-font-loading-arraybuffer.html
new file mode 100644
index 0000000..caf241f
--- /dev/null
+++ b/LayoutTests/fast/text/css-font-loading-arraybuffer.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<span id="probe1" style="font-size: 100px;">l</span>
+<span id="probe2" style="font-size: 100px;">l</span>
+<script>
+description("This test makes sure that an ArrayBufferView can be successfully passed to the FontFace constructor.");
+window.jsTestIsAsync = true;
+
+var fontRequest = new XMLHttpRequest();
+fontRequest.open("GET", "../../resources/Ahem.ttf");
+fontRequest.responseType = "arraybuffer";
+var fontFace1;
+var fontFace2;
+fontRequest.addEventListener("load", function() {
+ var arrayBuffer = fontRequest.response;
+ if (arrayBuffer) {
+ var byteArray = new Uint8Array(arrayBuffer);
+ fontFace1 = new FontFace("WebFont1", arrayBuffer, {});
+ fontFace2 = new FontFace("WebFont2", byteArray, {});
+ shouldBeEqualToString("fontFace1.status", "loaded");
+ shouldBeEqualToString("fontFace2.status", "loaded");
+ document.fonts.add(fontFace1);
+ document.fonts.add(fontFace2);
+ document.getElementById("probe1").style.fontFamily = "WebFont1";
+ document.getElementById("probe2").style.fontFamily = "WebFont2";
+ shouldEvaluateTo("document.getElementById('probe1').getBoundingClientRect().width", 100);
+ shouldEvaluateTo("document.getElementById('probe2').getBoundingClientRect().width", 100);
+ fontFace1.loaded.then(function() {
+ return fontFace2.loaded;
+ }, function() {
+ testFailed("fontFace1's promise should be successful");
+ finishJSTest();
+ }).then(function() {
+ finishJSTest();
+ }, function() {
+ testFailed("fontFace2's promise should be successful");
+ finishJSTest();
+ });
+ } else {
+ testFailed("XHR'ing the font should be successful.");
+ finishJSTest();
+ }
+});
+fontRequest.send();
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index 71dc1f9..a5d38ef 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,29 @@
+2016-05-14 Myles C. Maxfield <mmaxfield@apple.com>
+
+ Support ArrayBufferViews in the CSS Font Loading API
+ https://bugs.webkit.org/show_bug.cgi?id=157694
+ <rdar://problem/25554267>
+
+ This patch adds a new mode to CSSFontFaceSource for immediate (ArrayBuffer) data.
+ Then, FontFace can simply be hooked up to this new mode.
+
+ Reviewed by Darin Adler.
+
+ Test: fast/text/css-font-loading-arraybuffer.html
+
+ * css/CSSFontFaceSource.cpp:
+ (WebCore::CSSFontFaceSource::CSSFontFaceSource):
+ (WebCore::CSSFontFaceSource::font):
+ * css/CSSFontFaceSource.h:
+ * css/FontFace.cpp:
+ (WebCore::FontFace::create):
+ * loader/cache/CachedFont.cpp:
+ (WebCore::CachedFont::ensureCustomFontData):
+ (WebCore::CachedFont::createCustomFontData):
+ (WebCore::CachedFont::createFont):
+ (WebCore::CachedFont::platformDataFromCustomData):
+ * loader/cache/CachedFont.h:
+
2016-05-14 Chris Dumez <cdumez@apple.com>
[WebIDL] Add support for dictionary members of integer types
diff --git a/Source/WebCore/css/CSSFontFaceSource.cpp b/Source/WebCore/css/CSSFontFaceSource.cpp
index 5c0ef443..e95656f 100644
--- a/Source/WebCore/css/CSSFontFaceSource.cpp
+++ b/Source/WebCore/css/CSSFontFaceSource.cpp
@@ -34,9 +34,8 @@
#include "ElementIterator.h"
#include "Font.h"
#include "FontCache.h"
-#include "FontDescription.h"
-
#include "FontCustomPlatformData.h"
+#include "FontDescription.h"
#include "SVGToOTFFontConversion.h"
#if ENABLE(SVG_FONTS)
@@ -69,10 +68,11 @@
m_status = newStatus;
}
-CSSFontFaceSource::CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* font, SVGFontFaceElement* fontFace)
+CSSFontFaceSource::CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* font, SVGFontFaceElement* fontFace, RefPtr<JSC::ArrayBufferView>&& arrayBufferView)
: m_familyNameOrURI(familyNameOrURI)
, m_font(font)
, m_face(owner)
+ , m_immediateSource(WTFMove(arrayBufferView))
#if ENABLE(SVG_FONTS)
, m_svgFontFaceElement(fontFace)
#endif
@@ -137,6 +137,17 @@
#endif
if (!m_font && !fontFaceElement) {
+ if (m_immediateSource) {
+ if (!m_immediateFontCustomPlatformData) {
+ bool wrapping;
+ RefPtr<SharedBuffer> buffer = SharedBuffer::create(static_cast<const char*>(m_immediateSource->baseAddress()), m_immediateSource->byteLength());
+ ASSERT(buffer);
+ m_immediateFontCustomPlatformData = CachedFont::createCustomFontData(*buffer, wrapping);
+ } if (!m_immediateFontCustomPlatformData)
+ return nullptr;
+ return Font::create(CachedFont::platformDataFromCustomData(*m_immediateFontCustomPlatformData, fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings), true);
+ }
+
// We're local. Just return a Font from the normal cache.
// We don't want to check alternate font family names here, so pass true as the checkingAlternateName parameter.
return FontCache::singleton().fontForFamily(fontDescription, m_familyNameOrURI, &fontFaceFeatures, &fontFaceVariantSettings, true);
diff --git a/Source/WebCore/css/CSSFontFaceSource.h b/Source/WebCore/css/CSSFontFaceSource.h
index e23b268..08d45a8 100644
--- a/Source/WebCore/css/CSSFontFaceSource.h
+++ b/Source/WebCore/css/CSSFontFaceSource.h
@@ -28,6 +28,7 @@
#include "CachedFontClient.h"
#include "CachedResourceHandle.h"
+#include <runtime/ArrayBufferView.h>
#include <wtf/text/AtomicString.h>
namespace WebCore {
@@ -45,7 +46,7 @@
class CSSFontFaceSource final : public CachedFontClient {
WTF_MAKE_FAST_ALLOCATED;
public:
- CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* = nullptr, SVGFontFaceElement* = nullptr);
+ CSSFontFaceSource(CSSFontFace& owner, const String& familyNameOrURI, CachedFont* = nullptr, SVGFontFaceElement* = nullptr, RefPtr<JSC::ArrayBufferView>&& = nullptr);
virtual ~CSSFontFaceSource();
// => Success
@@ -80,6 +81,8 @@
CSSFontFace& m_face; // Our owning font face.
RefPtr<SharedBuffer> m_generatedOTFBuffer;
+ RefPtr<JSC::ArrayBufferView> m_immediateSource;
+ std::unique_ptr<FontCustomPlatformData> m_immediateFontCustomPlatformData;
#if ENABLE(SVG_FONTS)
RefPtr<SVGFontFaceElement> m_svgFontFaceElement;
diff --git a/Source/WebCore/css/FontFace.cpp b/Source/WebCore/css/FontFace.cpp
index aef5ad3..782e7d3 100644
--- a/Source/WebCore/css/FontFace.cpp
+++ b/Source/WebCore/css/FontFace.cpp
@@ -26,6 +26,7 @@
#include "config.h"
#include "FontFace.h"
+#include "CSSFontFaceSource.h"
#include "CSSFontFeatureValue.h"
#include "CSSUnicodeRangeValue.h"
#include "CSSValuePool.h"
@@ -36,10 +37,19 @@
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;
+}
+
RefPtr<FontFace> FontFace::create(JSC::ExecState& state, Document& document, const String& family, JSC::JSValue source, const Descriptors& descriptors, ExceptionCode& ec)
{
auto result = adoptRef(*new FontFace(document.fontSelector()));
+ bool dataRequiresAsynchronousLoading = true;
+
ec = 0;
result->setFamily(family, ec);
if (ec)
@@ -52,6 +62,11 @@
return nullptr;
}
CSSFontFace::appendSources(result->backing(), downcast<CSSValueList>(*value), &document, false);
+ } else if (auto arrayBufferView = toArrayBufferView(source))
+ dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
+ else if (auto arrayBuffer = toArrayBuffer(source)) {
+ auto arrayBufferView = JSC::Uint8Array::create(arrayBuffer, 0, arrayBuffer->byteLength());
+ dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView.releaseNonNull());
}
result->setStyle(descriptors.style, ec);
@@ -73,6 +88,11 @@
if (ec)
return nullptr;
+ if (!dataRequiresAsynchronousLoading) {
+ result->backing().load();
+ ASSERT(result->backing().status() == CSSFontFace::Status::Success);
+ }
+
return WTFMove(result);
}
diff --git a/Source/WebCore/loader/cache/CachedFont.cpp b/Source/WebCore/loader/cache/CachedFont.cpp
index a307f69..bfa410a 100644
--- a/Source/WebCore/loader/cache/CachedFont.cpp
+++ b/Source/WebCore/loader/cache/CachedFont.cpp
@@ -92,19 +92,9 @@
{
if (!m_fontCustomPlatformData && !errorOccurred() && !isLoading() && data) {
RefPtr<SharedBuffer> buffer(data);
-
-#if !PLATFORM(COCOA)
- if (isWOFF(buffer.get())) {
- Vector<char> convertedFont;
- if (!convertWOFFToSfnt(buffer.get(), convertedFont))
- buffer = nullptr;
- else
- buffer = SharedBuffer::adoptVector(convertedFont);
- }
-#endif
-
- m_fontCustomPlatformData = buffer ? createFontCustomPlatformData(*buffer) : nullptr;
- m_hasCreatedFontDataWrappingResource = m_fontCustomPlatformData && (buffer == m_data);
+ bool wrapping;
+ m_fontCustomPlatformData = createCustomFontData(*buffer, wrapping);
+ m_hasCreatedFontDataWrappingResource = m_fontCustomPlatformData && wrapping;
if (!m_fontCustomPlatformData)
setStatus(DecodeError);
}
@@ -112,20 +102,44 @@
return m_fontCustomPlatformData.get();
}
+std::unique_ptr<FontCustomPlatformData> CachedFont::createCustomFontData(SharedBuffer& bytes, bool& wrapping)
+{
+ RefPtr<SharedBuffer> buffer = &bytes;
+ wrapping = true;
+
+#if !PLATFORM(COCOA)
+ if (isWOFF(*buffer)) {
+ Vector<char> convertedFont;
+ if (!convertWOFFToSfnt(*buffer, convertedFont))
+ buffer = nullptr;
+ else
+ buffer = SharedBuffer::adoptVector(convertedFont);
+ wrapping = false;
+ }
+#endif
+
+ return buffer ? createFontCustomPlatformData(*buffer) : nullptr;
+}
+
RefPtr<Font> CachedFont::createFont(const FontDescription& fontDescription, const AtomicString&, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
{
- return Font::create(platformDataFromCustomData(fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings), true, false);
+ return Font::create(platformDataFromCustomData(fontDescription, syntheticBold, syntheticItalic, fontFaceFeatures, fontFaceVariantSettings), true);
}
FontPlatformData CachedFont::platformDataFromCustomData(const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
{
ASSERT(m_fontCustomPlatformData);
+ return platformDataFromCustomData(*m_fontCustomPlatformData, fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
+}
+
+FontPlatformData CachedFont::platformDataFromCustomData(FontCustomPlatformData& fontCustomPlatformData, const FontDescription& fontDescription, bool bold, bool italic, const FontFeatureSettings& fontFaceFeatures, const FontVariantSettings& fontFaceVariantSettings)
+{
#if PLATFORM(COCOA)
- return m_fontCustomPlatformData->fontPlatformData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
+ return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic, fontFaceFeatures, fontFaceVariantSettings);
#else
UNUSED_PARAM(fontFaceFeatures);
UNUSED_PARAM(fontFaceVariantSettings);
- return m_fontCustomPlatformData->fontPlatformData(fontDescription, bold, italic);
+ return fontCustomPlatformData.fontPlatformData(fontDescription, bold, italic);
#endif
}
diff --git a/Source/WebCore/loader/cache/CachedFont.h b/Source/WebCore/loader/cache/CachedFont.h
index d1188e7..e58c9e0 100644
--- a/Source/WebCore/loader/cache/CachedFont.h
+++ b/Source/WebCore/loader/cache/CachedFont.h
@@ -51,6 +51,9 @@
virtual bool ensureCustomFontData(const AtomicString& remoteURI);
+ static std::unique_ptr<FontCustomPlatformData> createCustomFontData(SharedBuffer&, bool& wrapping);
+ static FontPlatformData platformDataFromCustomData(FontCustomPlatformData&, const FontDescription&, bool bold, bool italic, const FontFeatureSettings&, const FontVariantSettings&);
+
virtual RefPtr<Font> createFont(const FontDescription&, const AtomicString& remoteURI, bool syntheticBold, bool syntheticItalic, const FontFeatureSettings&, const FontVariantSettings&);
protected:
diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
index 61a58b9..420fa48 100644
--- a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
+++ b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp
@@ -37,25 +37,25 @@
namespace WebCore {
-static bool readUInt32(SharedBuffer* buffer, size_t& offset, uint32_t& value)
+static bool readUInt32(SharedBuffer& buffer, size_t& offset, uint32_t& value)
{
- ASSERT_ARG(offset, offset <= buffer->size());
- if (buffer->size() - offset < sizeof(value))
+ ASSERT_ARG(offset, offset <= buffer.size());
+ if (buffer.size() - offset < sizeof(value))
return false;
- value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer->data() + offset));
+ value = ntohl(*reinterpret_cast_ptr<const uint32_t*>(buffer.data() + offset));
offset += sizeof(value);
return true;
}
-static bool readUInt16(SharedBuffer* buffer, size_t& offset, uint16_t& value)
+static bool readUInt16(SharedBuffer& buffer, size_t& offset, uint16_t& value)
{
- ASSERT_ARG(offset, offset <= buffer->size());
- if (buffer->size() - offset < sizeof(value))
+ ASSERT_ARG(offset, offset <= buffer.size());
+ if (buffer.size() - offset < sizeof(value))
return false;
- value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer->data() + offset));
+ value = ntohs(*reinterpret_cast_ptr<const uint16_t*>(buffer.data() + offset));
offset += sizeof(value);
return true;
@@ -75,7 +75,7 @@
static const uint32_t woffSignature = 0x774f4646; /* 'wOFF' */
-bool isWOFF(SharedBuffer* buffer)
+bool isWOFF(SharedBuffer& buffer)
{
size_t offset = 0;
uint32_t signature;
@@ -90,7 +90,7 @@
#endif
}
-bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt)
+bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt)
{
ASSERT_ARG(sfnt, sfnt.isEmpty());
@@ -105,8 +105,8 @@
#if USE(WOFF2)
if (signature == woff2::kWoff2Signature) {
- const uint8_t* woffData = reinterpret_cast_ptr<const uint8_t*>(woff->data());
- const size_t woffSize = woff->size();
+ const uint8_t* woffData = reinterpret_cast_ptr<const uint8_t*>(woff.data());
+ const size_t woffSize = woff.size();
const size_t sfntSize = woff2::ComputeWOFF2FinalSize(woffData, woffSize);
if (!sfnt.tryReserveCapacity(sfntSize))
@@ -127,7 +127,7 @@
return false;
uint32_t length;
- if (!readUInt32(woff, offset, length) || length != woff->size())
+ if (!readUInt32(woff, offset, length) || length != woff.size())
return false;
uint16_t numTables;
@@ -145,7 +145,7 @@
if (!readUInt32(woff, offset, totalSfntSize))
return false;
- if (woff->size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t))
+ if (woff.size() - offset < sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uint32_t))
return false;
offset += sizeof(uint16_t); // majorVersion
@@ -157,7 +157,7 @@
offset += sizeof(uint32_t); // privLength
// Check if the WOFF can supply as many tables as it claims it has.
- if (woff->size() - offset < numTables * 5 * sizeof(uint32_t))
+ if (woff.size() - offset < numTables * 5 * sizeof(uint32_t))
return false;
// Write the sfnt offset subtable.
@@ -201,7 +201,7 @@
if (!readUInt32(woff, offset, tableCompLength))
return false;
- if (tableOffset > woff->size() || tableCompLength > woff->size() - tableOffset)
+ if (tableOffset > woff.size() || tableCompLength > woff.size() - tableOffset)
return false;
uint32_t tableOrigLength;
@@ -225,7 +225,7 @@
if (tableCompLength == tableOrigLength) {
// The table is not compressed.
- if (!sfnt.tryAppend(woff->data() + tableOffset, tableCompLength))
+ if (!sfnt.tryAppend(woff.data() + tableOffset, tableCompLength))
return false;
} else {
uLongf destLen = tableOrigLength;
@@ -233,7 +233,7 @@
return false;
Bytef* dest = reinterpret_cast<Bytef*>(sfnt.end());
sfnt.grow(sfnt.size() + tableOrigLength);
- if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff->data() + tableOffset), tableCompLength) != Z_OK)
+ if (uncompress(dest, &destLen, reinterpret_cast<const Bytef*>(woff.data() + tableOffset), tableCompLength) != Z_OK)
return false;
if (destLen != tableOrigLength)
return false;
diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.h b/Source/WebCore/platform/graphics/WOFFFileFormat.h
index c6892fd..44c3824 100644
--- a/Source/WebCore/platform/graphics/WOFFFileFormat.h
+++ b/Source/WebCore/platform/graphics/WOFFFileFormat.h
@@ -33,11 +33,11 @@
class SharedBuffer;
// Returns whether the buffer is a WOFF file.
-bool isWOFF(SharedBuffer* buffer);
+bool isWOFF(SharedBuffer&);
// Returns false if the WOFF file woff is invalid or could not be converted to sfnt (for example,
// if conversion ran out of memory). Otherwise returns true and writes the sfnt payload into sfnt.
-bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt);
+bool convertWOFFToSfnt(SharedBuffer& woff, Vector<char>& sfnt);
} // namespace WebCore