changeset 10465:c458f0ceb559

8180825: Javafx WebView fails to render pdf.js Reviewed-by: kcr, ghb, mbilla
author arajkumar
date Mon, 12 Jun 2017 22:58:51 +0530
parents b298c353d43e
children 7b44520c8003
files modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.cpp modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.h modules/javafx.web/src/main/native/Source/WebCore/css/FontFace.cpp modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.cpp modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.h modules/javafx.web/src/test/java/test/javafx/scene/web/MiscellaneousTest.java
diffstat 6 files changed, 179 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.cpp	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.cpp	Mon Jun 12 22:58:51 2017 +0530
@@ -34,10 +34,10 @@
 #include "ElementIterator.h"
 #include "Font.h"
 #include "FontCache.h"
+#include "FontCustomPlatformData.h"
 #include "FontDescription.h"
 
 #if ENABLE(SVG_OTF_CONVERTER)
-#include "FontCustomPlatformData.h"
 #include "SVGToOTFFontConversion.h"
 #endif
 
@@ -72,10 +72,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) || ENABLE(SVG_OTF_CONVERTER)
     , m_svgFontFaceElement(fontFace)
 #endif
@@ -135,6 +136,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);
--- a/modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.h	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/main/native/Source/WebCore/css/CSSFontFaceSource.h	Mon Jun 12 22:58:51 2017 +0530
@@ -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
@@ -83,6 +84,8 @@
     RefPtr<SharedBuffer> m_generatedOTFBuffer;
 #endif
 
+    RefPtr<JSC::ArrayBufferView> m_immediateSource;
+    std::unique_ptr<FontCustomPlatformData> m_immediateFontCustomPlatformData;
 #if ENABLE(SVG_FONTS) || ENABLE(SVG_OTF_CONVERTER)
     RefPtr<SVGFontFaceElement> m_svgFontFaceElement;
     std::unique_ptr<FontCustomPlatformData> m_inDocumentCustomPlatformData;
--- a/modules/javafx.web/src/main/native/Source/WebCore/css/FontFace.cpp	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/main/native/Source/WebCore/css/FontFace.cpp	Mon Jun 12 22:58:51 2017 +0530
@@ -27,6 +27,7 @@
 #include "FontFace.h"
 
 #include "CSSFontFace.h"
+#include "CSSFontFaceSource.h"
 #include "CSSFontFeatureValue.h"
 #include "CSSFontSelector.h"
 #include "CSSUnicodeRangeValue.h"
@@ -41,6 +42,8 @@
 #include "ScriptExecutionContext.h"
 #include "StyleProperties.h"
 #include <wtf/text/StringBuilder.h>
+#include <runtime/JSArrayBuffer.h>
+#include <runtime/JSArrayBufferView.h>
 
 namespace WebCore {
 
@@ -57,6 +60,13 @@
     return result.isNull() ? Nullopt : Optional<String>(result);
 }
 
+static bool populateFontFaceWithArrayBuffer(CSSFontFace& fontFace, RefPtr<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& execState, ScriptExecutionContext& context, const String& family, const Deprecated::ScriptValue& source, const Dictionary& descriptors, ExceptionCode& ec)
 {
     if (!context.isDocument()) {
@@ -70,6 +80,7 @@
     if (ec)
         return nullptr;
 
+    bool dataRequiresAsynchronousLoading = true;
     if (source.jsValue().isString()) {
         String sourceString = source.jsValue().toString(&execState)->value(&execState);
         auto value = FontFace::parseString(sourceString, CSSPropertySrc);
@@ -80,6 +91,11 @@
             ec = SYNTAX_ERR;
             return nullptr;
         }
+    } else if (auto arrayBufferView = toArrayBufferView(source))
+        dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView);
+    else if (auto arrayBuffer = JSC::toArrayBuffer(source)) {
+        auto arrayBufferView = JSC::Uint8Array::create(arrayBuffer, 0, arrayBuffer->byteLength());
+        dataRequiresAsynchronousLoading = populateFontFaceWithArrayBuffer(result->backing(), arrayBufferView);
     }
 
     if (auto style = valueFromDictionary(descriptors, "style"))
@@ -107,6 +123,10 @@
     if (ec)
         return nullptr;
 
+    if (!dataRequiresAsynchronousLoading) {
+        result->backing().load();
+        ASSERT(result->backing().status() == CSSFontFace::Status::Success);
+    }
     return result.ptr();
 }
 
--- a/modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.cpp	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.cpp	Mon Jun 12 22:58:51 2017 +0530
@@ -101,19 +101,9 @@
 {
     if (!m_fontCustomPlatformData && !errorOccurred() && !isLoading() && data) {
         RefPtr<SharedBuffer> buffer(data);
-
-#if !PLATFORM(COCOA) && !PLATFORM(JAVA)
-        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);
     }
@@ -121,20 +111,44 @@
     return m_fontCustomPlatformData.get();
 }
 
+std::unique_ptr<FontCustomPlatformData> CachedFont::createCustomFontData(SharedBuffer& bytes, bool& wrapping)
+{
+    RefPtr<SharedBuffer> buffer = &bytes;
+    wrapping = true;
+
+#if !PLATFORM(COCOA) && !PLATFORM(JAVA)
+    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
 }
 
--- a/modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.h	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/main/native/Source/WebCore/loader/cache/CachedFont.h	Mon Jun 12 22:58:51 2017 +0530
@@ -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:
--- a/modules/javafx.web/src/test/java/test/javafx/scene/web/MiscellaneousTest.java	Tue Jun 13 21:21:45 2017 +0530
+++ b/modules/javafx.web/src/test/java/test/javafx/scene/web/MiscellaneousTest.java	Mon Jun 12 22:58:51 2017 +0530
@@ -26,6 +26,7 @@
 package test.javafx.scene.web;
 
 import java.io.File;
+import java.io.FileInputStream;
 import static java.lang.String.format;
 import java.net.HttpURLConnection;
 import java.util.ArrayList;
@@ -41,6 +42,7 @@
 import javafx.scene.web.WebView;
 import netscape.javascript.JSObject;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -263,4 +265,110 @@
     private WebEngine createWebEngine() {
         return submit(() -> new WebEngine());
     }
+
+    /**
+     * @test
+     * @bug 8180825
+     * Checks CSS FontFace supports ArrayBuffer and ArrayBufferView arguments.
+     * This test is derived based on a DRT testcase written as part of
+     * WebKit changeset https://trac.webkit.org/changeset/200921/webkit
+    */
+    public class FontFaceTestHelper {
+        private final CountDownLatch latch = new CountDownLatch(1);
+
+        public final byte[] ttfFileContent;
+
+        FontFaceTestHelper(String ttfPath) throws Exception {
+            final File ttfFile = new File(ttfPath);
+            assertNotNull(ttfFile);
+            assertTrue(ttfFile.canRead());
+            assertTrue(ttfFile.length() > 0);
+            final int length = (int) ttfFile.length();
+            ttfFileContent = new byte[length];
+            // Read ttf file contents
+            int offset = 0;
+            final FileInputStream ttfFileStream = new FileInputStream(ttfFile);
+            assertNotNull(ttfFileContent);
+            while (offset < length) {
+                final int available = ttfFileStream.available();
+                ttfFileStream.read(ttfFileContent, (int)offset, available);
+                offset += available;
+            }
+            assertEquals("Offset must equal to file length", length, offset);
+        }
+
+        // Will be called from JS to indicate test complete
+        public void finish() {
+            latch.countDown();
+        }
+
+        private String failureMsg;
+        // Will be called from JS to pass failure message
+        public void failed(String msg) {
+            failureMsg = msg;
+        }
+
+        void waitForCompletion() {
+            try {
+                latch.await();
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+
+            if (failureMsg != null) {
+                fail(failureMsg);
+            }
+        }
+    }
+
+    @Test public void testFontFace() throws Exception {
+        final FontFaceTestHelper fontFaceHelper = new FontFaceTestHelper("src/main/native/Tools/TestWebKitAPI/Tests/mac/Ahem.ttf");
+        loadContent(
+                "<body>\n" +
+                "<span id='probe1' style='font-size: 100px;'>l</span>\n" +
+                "<span id='probe2' style='font-size: 100px;'>l</span>\n" +
+                "</body>\n"
+        );
+        submit(() -> {
+            final JSObject window = (JSObject) getEngine().executeScript("window");
+            assertNotNull(window);
+            assertEquals("undefined", window.getMember("fontFaceHelper"));
+            window.setMember("fontFaceHelper", fontFaceHelper);
+            assertTrue(window.getMember("fontFaceHelper") instanceof FontFaceTestHelper);
+            // Create font-face object from byte[]
+            getEngine().executeScript(
+                "var byteArray = new Uint8Array(fontFaceHelper.ttfFileContent);\n" +
+                "var arrayBuffer = byteArray.buffer;\n" +
+                "window.fontFace1 = new FontFace('WebFont1', arrayBuffer, {});\n" +
+                "window.fontFace2 = new FontFace('WebFont2', byteArray, {});\n"
+            );
+            assertEquals("loaded", getEngine().executeScript("fontFace1.status"));
+            assertEquals("loaded", getEngine().executeScript("fontFace2.status"));
+            // Add font-face to Document, so that it could be usable on styles
+            getEngine().executeScript(
+                "document.fonts.add(fontFace1);\n" +
+                "document.fonts.add(fontFace2);\n" +
+                "document.getElementById('probe1').style.fontFamily = 'WebFont1';\n" +
+                "document.getElementById('probe2').style.fontFamily = 'WebFont2';\n"
+            );
+
+            // Ensure web font is applied by checking width property of bounding rect.
+            assertEquals(100, getEngine().executeScript("document.getElementById('probe1').getBoundingClientRect().width"));
+            assertEquals(100, getEngine().executeScript("document.getElementById('probe2').getBoundingClientRect().width"));
+            getEngine().executeScript(
+                "fontFace1.loaded.then(function() {\n" +
+                "   return fontFace2.loaded;\n" +
+                "}, function() {\n" +
+                "   fontFaceHelper.failed(\"fontFace1's promise should be successful\");\n" +
+                "   fontFaceHelper.finish();\n" +
+                "}).then(function() {\n" +
+                "   fontFaceHelper.finish();\n" +
+                "}, function() {\n" +
+                "   fontFaceHelper.failed(\"fontFace2's promise should be successful\");\n" +
+                "   fontFaceHelper.finish();\n" +
+                "});\n"
+            );
+        });
+        fontFaceHelper.waitForCompletion();
+    }
 }