[CG] Adding support for HEIF-sequence ('public.heics') images
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 15:13:50 +0000 (15:13 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 May 2019 15:13:50 +0000 (15:13 +0000)
https://bugs.webkit.org/show_bug.cgi?id=197384

Patch by Said Abou-Hallawa <sabouhallawa@apple.com> on 2019-05-14
Reviewed by Simon Fraser.

Source/WebCore:

-- Get the image repetitionCount and the frame duration.
-- Add a new function setAdditionalSupportedImageTypesForTesting() which
   takes a delimited String.
-- Add internal APIs to retrive the image frame count and the frame
   duration.

Tests: fast/images/animated-heics-draw.html
       fast/images/animated-heics-verify.html

* platform/graphics/ImageSource.h:
* platform/graphics/cg/ImageDecoderCG.cpp:
(WebCore::animationPropertiesFromProperties):
(WebCore::animationHEICSPropertiesFromProperties):
(WebCore::ImageDecoderCG::repetitionCount const):
(WebCore::ImageDecoderCG::frameDurationAtIndex const):
* platform/graphics/cg/UTIRegistry.cpp:
(WebCore::setAdditionalSupportedImageTypesForTesting):
* platform/graphics/cg/UTIRegistry.h:
* testing/Internals.cpp:
(WebCore::Internals::imageFrameCount):
(WebCore::Internals::imageFrameDurationAtIndex):
* testing/Internals.h:
* testing/Internals.idl:
* testing/js/WebCoreTestSupport.cpp:
(WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting):
* testing/js/WebCoreTestSupport.h:

Tools:

* DumpRenderTree/TestOptions.cpp:
(TestOptions::TestOptions):
* DumpRenderTree/TestOptions.h:
* DumpRenderTree/mac/DumpRenderTree.mm:
(resetWebViewToConsistentStateBeforeTesting):
Parse the new webkit-test-runner paramter: additionalSupportedImageTypes.
Make DRT call setAdditionalSupportedImageTypesForTesting() before starting
the test.

* WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
(WTR::InjectedBundle::stringForKey):
(WTR::InjectedBundle::beginTesting):
* WebKitTestRunner/InjectedBundle/InjectedBundle.h:
* WebKitTestRunner/TestController.cpp:
(WTR::TestController::resetStateToConsistentValues):
(WTR::updateTestOptionsFromTestHeader):
* WebKitTestRunner/TestInvocation.cpp:
(WTR::TestInvocation::createTestSettingsDictionary):
* WebKitTestRunner/TestOptions.h:
(WTR::TestOptions::hasSameInitializationOptions const):
Parse the new webkit-test-runner paramter: additionalSupportedImageTypes.
Make WTR call setAdditionalSupportedImageTypesForTesting() before starting
the test.

LayoutTests:

Disable the tests for all ports and enable it only for [ Mojave+ WK2]
because of <rdar://problem/42625657>.

* TestExpectations:
* fast/images/animated-heics-draw-expected.txt: Added.
* fast/images/animated-heics-draw.html: Added.
* fast/images/animated-heics-verify-expected.txt: Added.
* fast/images/animated-heics-verify.html: Added.
* fast/images/resources/sea_animation.heics: Added.
* fast/images/resources/sticker.heics: Added.
* platform/mac-wk2/TestExpectations:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@245280 268f45cc-cd09-0410-ab3c-d52691b4dbfc

28 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/fast/images/animated-heics-draw-expected.txt [new file with mode: 0644]
LayoutTests/fast/images/animated-heics-draw.html [new file with mode: 0644]
LayoutTests/fast/images/animated-heics-verify-expected.txt [new file with mode: 0644]
LayoutTests/fast/images/animated-heics-verify.html [new file with mode: 0644]
LayoutTests/fast/images/resources/sea_animation.heics [new file with mode: 0644]
LayoutTests/fast/images/resources/sticker.heics [new file with mode: 0644]
LayoutTests/platform/mac-wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/platform/graphics/ImageSource.h
Source/WebCore/platform/graphics/cg/ImageDecoderCG.cpp
Source/WebCore/platform/graphics/cg/UTIRegistry.cpp
Source/WebCore/platform/graphics/cg/UTIRegistry.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebCore/testing/js/WebCoreTestSupport.cpp
Source/WebCore/testing/js/WebCoreTestSupport.h
Tools/ChangeLog
Tools/DumpRenderTree/TestOptions.cpp
Tools/DumpRenderTree/TestOptions.h
Tools/DumpRenderTree/mac/DumpRenderTree.mm
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.cpp
Tools/WebKitTestRunner/InjectedBundle/InjectedBundle.h
Tools/WebKitTestRunner/TestController.cpp
Tools/WebKitTestRunner/TestInvocation.cpp
Tools/WebKitTestRunner/TestOptions.h

index 28c651e..642d5e9 100644 (file)
@@ -1,3 +1,22 @@
+2019-05-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        [CG] Adding support for HEIF-sequence ('public.heics') images
+        https://bugs.webkit.org/show_bug.cgi?id=197384
+
+        Reviewed by Simon Fraser.
+
+        Disable the tests for all ports and enable it only for [ Mojave+ WK2]
+        because of <rdar://problem/42625657>.
+
+        * TestExpectations:
+        * fast/images/animated-heics-draw-expected.txt: Added.
+        * fast/images/animated-heics-draw.html: Added.
+        * fast/images/animated-heics-verify-expected.txt: Added.
+        * fast/images/animated-heics-verify.html: Added.
+        * fast/images/resources/sea_animation.heics: Added.
+        * fast/images/resources/sticker.heics: Added.
+        * platform/mac-wk2/TestExpectations:
+
 2019-05-14  Per Arne Vollan  <pvollan@apple.com>
 
         [Win10] Some tests are failing only on specific machines
index b17e22e..2a348f7 100644 (file)
@@ -1335,6 +1335,10 @@ webkit.org/b/145390 storage/indexeddb/deleteIndex-bug110792.html [ Pass Failure
 fast/images/animated-gif-no-layout.html [ ImageOnlyFailure ]
 fast/images/gif-loop-count.html [ ImageOnlyFailure ]
 
+# HEIF images are only supported on macOS and iOS post Mojave
+fast/images/animated-heics-draw.html [ Skip ]
+fast/images/animated-heics-verify.html [ Skip ]
+
 webkit.org/b/146182 editing/selection/leak-document-with-selection-inside.html [ Pass Failure ]
 
 # Media Sessions is not yet enabled by default: ENABLE(MEDIA_SESSION)
diff --git a/LayoutTests/fast/images/animated-heics-draw-expected.txt b/LayoutTests/fast/images/animated-heics-draw-expected.txt
new file mode 100644 (file)
index 0000000..198ec75
--- /dev/null
@@ -0,0 +1,14 @@
+Verify HEICS image can animate.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Image was loaded successfully.
+Image frame: 0 was displayed.
+Image frame: 1 was displayed.
+Image frame: 2 was displayed.
+The HEICS Image was loaded and three frames of it were displayed successfully.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/images/animated-heics-draw.html b/LayoutTests/fast/images/animated-heics-draw.html
new file mode 100644 (file)
index 0000000..5296f1b
--- /dev/null
@@ -0,0 +1,64 @@
+<!DOCTYPE html><!-- webkit-test-runner [ additionalSupportedImageTypes=public.heic;public.heics ] -->
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <img src="">
+    <script>
+        function drawFrame(image, frame) {
+            return new Promise((resolve) => {
+                // Force layout and display so the image frame starts decoding
+                document.body.offsetHeight;
+                testRunner.display();
+
+                image.addEventListener("webkitImageFrameReady", function listener() {
+                    debug("Image frame: " + frame + " was displayed.");
+                    image.removeEventListener("webkitImageFrameReady", listener, true);
+                    resolve(frame + 1);
+                }, true);
+            });
+        }
+
+        function drawImage(image, frameCount) {
+            let promise = drawFrame(image, 0);
+            for (let i = 1; i < frameCount; ++i) {
+                promise = promise.then((frame) => {
+                    return drawFrame(image, frame);
+                });
+            }
+            return promise;
+        }
+
+        function loadAndDrawImage(image, src, frameCount) {
+            return new Promise((resolve) => {
+                image.onload = (() => {
+                    debug("Image was loaded successfully.");
+                    drawImage(image, frameCount).then(resolve);
+                });
+                image.src = src;
+            });
+        }
+
+        (function() {
+            window.jsTestIsAsync = true;
+
+            if (window.internals) {
+                internals.clearMemoryCache();
+                internals.settings.setWebkitImageReadyEventEnabled(true);
+                internals.settings.setAnimatedImageAsyncDecodingEnabled(true);
+            }
+
+            description("Verify HEICS image can animate.");
+
+            let image = document.querySelector("img");
+
+            loadAndDrawImage(image, "resources/sticker.heics", 3).then(() => {
+                debug("The HEICS Image was loaded and three frames of it were displayed successfully.");
+                finishJSTest();
+            });
+        })();
+    </script>
+    <script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/images/animated-heics-verify-expected.txt b/LayoutTests/fast/images/animated-heics-verify-expected.txt
new file mode 100644 (file)
index 0000000..755027f
--- /dev/null
@@ -0,0 +1,21 @@
+Verify HEICS image properties can be retrieved.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+The image resources/sticker.heics was loaded successfully.
+The image resources/sea_animation.heics was loaded successfully.
+
+PASS internals.imageFrameCount(image1) is 96
+PASS Math.round(internals.imageFrameDurationAtIndex(image1, 0) * 1000) is 100
+PASS Math.round(internals.imageFrameDurationAtIndex(image1, 48) * 1000) is 100
+PASS Math.round(internals.imageFrameDurationAtIndex(image1, 95) * 1000) is 100
+
+PASS internals.imageFrameCount(image2) is 120
+PASS Math.round(internals.imageFrameDurationAtIndex(image2, 0) * 1000) is 40
+PASS Math.round(internals.imageFrameDurationAtIndex(image2, 60) * 1000) is 40
+PASS Math.round(internals.imageFrameDurationAtIndex(image2, 119) * 1000) is 40
+PASS successfullyParsed is true
+
+TEST COMPLETE
+  
diff --git a/LayoutTests/fast/images/animated-heics-verify.html b/LayoutTests/fast/images/animated-heics-verify.html
new file mode 100644 (file)
index 0000000..9a21144
--- /dev/null
@@ -0,0 +1,56 @@
+<!DOCTYPE html><!-- webkit-test-runner [ additionalSupportedImageTypes=public.heic;public.heics ] -->
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+    <img id="image1" src="">
+    <img id="image2" src="">
+    <script>
+        function loadImage(image, src) {
+            return new Promise((resolve) => {
+                image.onload = (() => {
+                    debug("The image " + src + " was loaded successfully.");
+                    resolve();
+                });
+                image.src = src;
+            });
+        }
+
+        function verifyProperties()
+        {
+            if (!window.internals)
+                return;
+
+            debug("");
+            shouldBe("internals.imageFrameCount(image1)", "96");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image1, 0) * 1000)", "100");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image1, 48) * 1000)", "100");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image1, 95) * 1000)", "100");
+
+            debug("");
+            shouldBe("internals.imageFrameCount(image2)", "120");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image2, 0) * 1000)", "40");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image2, 60) * 1000)", "40");
+            shouldBe("Math.round(internals.imageFrameDurationAtIndex(image2, 119) * 1000)", "40");
+        }
+
+        (function() {
+            window.jsTestIsAsync = true;
+
+            description("Verify HEICS image properties can be retrieved.");
+
+            var promises = [];
+
+            promises.push(loadImage(image1, "resources/sticker.heics"));
+            promises.push(loadImage(image2, "resources/sea_animation.heics"));
+            
+            Promise.all(promises).then(() => {
+                verifyProperties();
+                finishJSTest();
+            });
+        })();
+    </script>
+    <script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/fast/images/resources/sea_animation.heics b/LayoutTests/fast/images/resources/sea_animation.heics
new file mode 100644 (file)
index 0000000..0dd3180
Binary files /dev/null and b/LayoutTests/fast/images/resources/sea_animation.heics differ
diff --git a/LayoutTests/fast/images/resources/sticker.heics b/LayoutTests/fast/images/resources/sticker.heics
new file mode 100644 (file)
index 0000000..72ce7d7
Binary files /dev/null and b/LayoutTests/fast/images/resources/sticker.heics differ
index f24cf11..2cd64a1 100644 (file)
@@ -83,6 +83,9 @@ fast/events/inactive-window-no-mouse-event.html [ Pass ]
 
 fast/animation/request-animation-frame-in-two-pages.html [ Pass ]
 
+[ Mojave+ ] fast/images/animated-heics-draw.html [ Pass ]
+[ Mojave+ ] fast/images/animated-heics-verify.html [ Pass ]
+
 #//////////////////////////////////////////////////////////////////////////////////////////
 # End platform-specific directories.
 #//////////////////////////////////////////////////////////////////////////////////////////
index 8b6b239..e76c147 100644 (file)
@@ -1,3 +1,37 @@
+2019-05-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        [CG] Adding support for HEIF-sequence ('public.heics') images
+        https://bugs.webkit.org/show_bug.cgi?id=197384
+
+        Reviewed by Simon Fraser.
+
+        -- Get the image repetitionCount and the frame duration.
+        -- Add a new function setAdditionalSupportedImageTypesForTesting() which
+           takes a delimited String.
+        -- Add internal APIs to retrive the image frame count and the frame
+           duration.
+
+        Tests: fast/images/animated-heics-draw.html
+               fast/images/animated-heics-verify.html
+
+        * platform/graphics/ImageSource.h:
+        * platform/graphics/cg/ImageDecoderCG.cpp:
+        (WebCore::animationPropertiesFromProperties):
+        (WebCore::animationHEICSPropertiesFromProperties):
+        (WebCore::ImageDecoderCG::repetitionCount const):
+        (WebCore::ImageDecoderCG::frameDurationAtIndex const):
+        * platform/graphics/cg/UTIRegistry.cpp:
+        (WebCore::setAdditionalSupportedImageTypesForTesting):
+        * platform/graphics/cg/UTIRegistry.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::imageFrameCount):
+        (WebCore::Internals::imageFrameDurationAtIndex):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+        * testing/js/WebCoreTestSupport.cpp:
+        (WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting):
+        * testing/js/WebCoreTestSupport.h:
+
 2019-05-14  Manuel Rego Casasnovas  <rego@igalia.com>
 
         [css-grid] Use max size to compute auto repeat tracks
index 52f1954..c05c11d 100644 (file)
@@ -88,7 +88,7 @@ public:
     // from the NativeImage if this class was created for a memory image.
     EncodedDataStatus encodedDataStatus();
     bool isSizeAvailable() { return encodedDataStatus() >= EncodedDataStatus::SizeAvailable; }
-    size_t frameCount();
+    WEBCORE_EXPORT size_t frameCount();
     RepetitionCount repetitionCount();
     String uti();
     String filenameExtension();
@@ -112,7 +112,7 @@ public:
     // ImageFrame metadata which forces caching or re-caching the ImageFrame.
     IntSize frameSizeAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
     unsigned frameBytesAtIndex(size_t, SubsamplingLevel = SubsamplingLevel::Default);
-    Seconds frameDurationAtIndex(size_t);
+    WEBCORE_EXPORT Seconds frameDurationAtIndex(size_t);
     ImageOrientation frameOrientationAtIndex(size_t);
 
 #if USE(DIRECT2D)
index f67c527..af74ef2 100644 (file)
 
 namespace WebCore {
 
-const CFStringRef WebCoreCGImagePropertyAPNGUnclampedDelayTime = CFSTR("UnclampedDelayTime");
-const CFStringRef WebCoreCGImagePropertyAPNGDelayTime = CFSTR("DelayTime");
-const CFStringRef WebCoreCGImagePropertyAPNGLoopCount = CFSTR("LoopCount");
+const CFStringRef WebCoreCGImagePropertyHEICSDictionary = CFSTR("{HEICS}");
+const CFStringRef WebCoreCGImagePropertyHEICSFrameInfoArray = CFSTR("FrameInfo");
 
+const CFStringRef WebCoreCGImagePropertyUnclampedDelayTime = CFSTR("UnclampedDelayTime");
+const CFStringRef WebCoreCGImagePropertyDelayTime = CFSTR("DelayTime");
+const CFStringRef WebCoreCGImagePropertyLoopCount = CFSTR("LoopCount");
+    
 #if PLATFORM(WIN)
 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
 const CFStringRef kCGImageSourceSkipMetadata = CFSTR("kCGImageSourceSkipMetadata");
@@ -109,7 +112,45 @@ static RetainPtr<CFDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsa
     static const auto options = createImageSourceAsyncOptions().leakRef();
     return appendImageSourceOptions(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options)), subsamplingLevel, sizeForDrawing);
 }
-    
+
+static CFDictionaryRef animationPropertiesFromProperties(CFDictionaryRef properties)
+{
+    if (!properties)
+        return nullptr;
+
+    if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyGIFDictionary))
+        return animationProperties;
+
+    if (auto animationProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, kCGImagePropertyPNGDictionary))
+        return animationProperties;
+
+    return (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
+}
+
+static CFDictionaryRef animationHEICSPropertiesFromProperties(CFDictionaryRef properties, size_t index)
+{
+    if (!properties)
+        return nullptr;
+
+    // For HEICS images, ImageIO does not create a properties dictionary for each HEICS frame. Instead it maintains
+    // all frames' information in the image properties dictionary. Here is how ImageIO structures the properties
+    // dictionary for HEICS image:
+    //  "{HEICS}" =  {
+    //      FrameInfo = ( { DelayTime = "0.1"; }, { DelayTime = "0.1"; }, ... );
+    //      LoopCount = 0;
+    //      ...
+    //  };
+    CFDictionaryRef heicsProperties = (CFDictionaryRef)CFDictionaryGetValue(properties, WebCoreCGImagePropertyHEICSDictionary);
+    if (!heicsProperties)
+        return nullptr;
+
+    CFArrayRef frameInfoArray = (CFArrayRef)CFDictionaryGetValue(heicsProperties, WebCoreCGImagePropertyHEICSFrameInfoArray);
+    if (!frameInfoArray)
+        return nullptr;
+
+    return (CFDictionaryRef)CFArrayGetValueAtIndex(frameInfoArray, index);
+}
+
 static ImageOrientation orientationFromProperties(CFDictionaryRef imageProperties)
 {
     ASSERT(imageProperties);
@@ -231,39 +272,25 @@ size_t ImageDecoderCG::frameCount() const
 RepetitionCount ImageDecoderCG::repetitionCount() const
 {
     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
-    if (!properties)
-        return RepetitionCountOnce;
-    
-    CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
-    if (gifProperties) {
-        CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFLoopCount);
-        
-        // No property means loop once.
-        if (!num)
-            return RepetitionCountOnce;
-        
-        RepetitionCount loopCount;
-        CFNumberGetValue(num, kCFNumberIntType, &loopCount);
-        
-        // A property with value 0 means loop forever.
-        // For loopCount > 0, the specs is not clear about it. But it looks the meaning
-        // is: play once + loop loopCount which is equivalent to play loopCount + 1.
-        return loopCount ? loopCount + 1 : RepetitionCountInfinite;
-    }
-    
-    CFDictionaryRef pngProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPNGDictionary);
-    if (pngProperties) {
-        CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGLoopCount);
-        if (!num)
-            return RepetitionCountOnce;
-        
-        RepetitionCount loopCount;
-        CFNumberGetValue(num, kCFNumberIntType, &loopCount);
-        return loopCount ? loopCount : RepetitionCountInfinite;
-    }
-    
+    CFDictionaryRef animationProperties = animationPropertiesFromProperties(properties.get());
+
     // Turns out we're not an animated image after all, so we don't animate.
-    return RepetitionCountNone;
+    if (!animationProperties)
+        return RepetitionCountNone;
+
+    CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyLoopCount);
+
+    // No property means loop once.
+    if (!num)
+        return RepetitionCountOnce;
+
+    RepetitionCount loopCount;
+    CFNumberGetValue(num, kCFNumberIntType, &loopCount);
+
+    // A property with value 0 means loop forever.
+    // For loopCount > 0, the specs is not clear about it. But it looks the meaning
+    // is: play once + loop loopCount which is equivalent to play loopCount + 1.
+    return loopCount ? loopCount + 1 : RepetitionCountInfinite;
 }
 
 Optional<IntPoint> ImageDecoderCG::hotSpot() const
@@ -330,27 +357,22 @@ ImageOrientation ImageDecoderCG::frameOrientationAtIndex(size_t index) const
 
 Seconds ImageDecoderCG::frameDurationAtIndex(size_t index) const
 {
+    RetainPtr<CFDictionaryRef> properties = nullptr;
+    RetainPtr<CFDictionaryRef> frameProperties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
+    CFDictionaryRef animationProperties = animationPropertiesFromProperties(frameProperties.get());
+
+    if (frameProperties && !animationProperties) {
+        properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
+        animationProperties = animationHEICSPropertiesFromProperties(properties.get(), index);
+    }
+
+    // Use the unclamped frame delay if it exists. Otherwise use the clamped frame delay.
     float value = 0;
-    RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
-    if (properties) {
-        CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
-        if (gifProperties) {
-            if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime)) {
-                // Use the unclamped frame delay if it exists.
-                CFNumberGetValue(num, kCFNumberFloatType, &value);
-            } else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime)) {
-                // Fall back to the clamped frame delay if the unclamped frame delay does not exist.
-                CFNumberGetValue(num, kCFNumberFloatType, &value);
-            }
-        }
-        
-        CFDictionaryRef pngProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPNGDictionary);
-        if (pngProperties) {
-            if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGUnclampedDelayTime))
-                CFNumberGetValue(num, kCFNumberFloatType, &value);
-            else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGDelayTime))
-                CFNumberGetValue(num, kCFNumberFloatType, &value);
-        }
+    if (animationProperties) {
+        if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyUnclampedDelayTime))
+            CFNumberGetValue(num, kCFNumberFloatType, &value);
+        else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(animationProperties, WebCoreCGImagePropertyDelayTime))
+            CFNumberGetValue(num, kCFNumberFloatType, &value);
     }
 
     Seconds duration(value);
index c78158f..b8c2ab5 100644 (file)
@@ -89,6 +89,11 @@ void setAdditionalSupportedImageTypes(const Vector<String>& imageTypes)
     }
 }
 
+void setAdditionalSupportedImageTypesForTesting(const String& imageTypes)
+{
+    setAdditionalSupportedImageTypes(imageTypes.split(';'));
+}
+
 bool isSupportedImageType(const String& imageType)
 {
     if (imageType.isEmpty())
index 3c84de3..2c3ad32 100644 (file)
@@ -33,6 +33,7 @@ namespace WebCore {
 const HashSet<String>& defaultSupportedImageTypes();
 HashSet<String>& additionalSupportedImageTypes();
 WEBCORE_EXPORT void setAdditionalSupportedImageTypes(const Vector<String>&);
+WEBCORE_EXPORT void setAdditionalSupportedImageTypesForTesting(const String&);
 bool isSupportedImageType(const String&);
 
 }
index 524e3b3..49c0dba 100644 (file)
@@ -835,6 +835,18 @@ unsigned Internals::imageFrameIndex(HTMLImageElement& element)
     return bitmapImage ? bitmapImage->currentFrame() : 0;
 }
 
+unsigned Internals::imageFrameCount(HTMLImageElement& element)
+{
+    auto* bitmapImage = bitmapImageFromImageElement(element);
+    return bitmapImage ? bitmapImage->frameCount() : 0;
+}
+
+float Internals::imageFrameDurationAtIndex(HTMLImageElement& element, unsigned index)
+{
+    auto* bitmapImage = bitmapImageFromImageElement(element);
+    return bitmapImage ? bitmapImage->frameDurationAtIndex(index).value() : 0;
+}
+    
 void Internals::setImageFrameDecodingDuration(HTMLImageElement& element, float duration)
 {
     if (auto* bitmapImage = bitmapImageFromImageElement(element))
index e1f35f0..3342351 100644 (file)
@@ -146,6 +146,8 @@ public:
     unsigned memoryCacheSize() const;
 
     unsigned imageFrameIndex(HTMLImageElement&);
+    unsigned imageFrameCount(HTMLImageElement&);
+    float imageFrameDurationAtIndex(HTMLImageElement&, unsigned index);
     void setImageFrameDecodingDuration(HTMLImageElement&, float duration);
     void resetImageAnimation(HTMLImageElement&);
     bool isImageAnimating(HTMLImageElement&);
index c7a340b..d749e7e 100644 (file)
@@ -346,6 +346,8 @@ enum CompositingPolicy {
     [MayThrowException] boolean isPageBoxVisible(long pageNumber);
 
     unsigned long imageFrameIndex(HTMLImageElement element);
+    unsigned long imageFrameCount(HTMLImageElement element);
+    float imageFrameDurationAtIndex(HTMLImageElement element, unsigned long index);
     void setImageFrameDecodingDuration(HTMLImageElement element, unrestricted float duration);
     void resetImageAnimation(HTMLImageElement element);
     boolean isImageAnimating(HTMLImageElement element);
index 5809f0b..cc363ef 100644 (file)
 #include <JavaScriptCore/JSValueRef.h>
 #include <wtf/URLParser.h>
 
+#if PLATFORM(COCOA)
+#include "UTIRegistry.h"
+#endif
+
 namespace WebCoreTestSupport {
 using namespace JSC;
 using namespace WebCore;
@@ -205,4 +209,11 @@ void setupNewlyCreatedServiceWorker(uint64_t serviceWorkerIdentifier)
 #endif
 }
 
+#if PLATFORM(COCOA)
+void setAdditionalSupportedImageTypesForTesting(const WTF::String& imageTypes)
+{
+    WebCore::setAdditionalSupportedImageTypesForTesting(imageTypes);
+}
+#endif
+
 }
index b3f39cd..4ab9546 100644 (file)
@@ -62,5 +62,7 @@ void setMockGamepadAxisValue(unsigned index, unsigned axisIndex, double value) T
 void setMockGamepadButtonValue(unsigned index, unsigned buttonIndex, double value) TEST_SUPPORT_EXPORT;
 
 void setupNewlyCreatedServiceWorker(uint64_t serviceWorkerIdentifier) TEST_SUPPORT_EXPORT;
+    
+void setAdditionalSupportedImageTypesForTesting(const WTF::String&) TEST_SUPPORT_EXPORT;
 
 } // namespace WebCoreTestSupport
index 17d6ca0..5e71a0d 100644 (file)
@@ -1,3 +1,34 @@
+2019-05-14  Said Abou-Hallawa  <sabouhallawa@apple.com>
+
+        [CG] Adding support for HEIF-sequence ('public.heics') images
+        https://bugs.webkit.org/show_bug.cgi?id=197384
+
+        Reviewed by Simon Fraser.
+
+        * DumpRenderTree/TestOptions.cpp:
+        (TestOptions::TestOptions):
+        * DumpRenderTree/TestOptions.h:
+        * DumpRenderTree/mac/DumpRenderTree.mm:
+        (resetWebViewToConsistentStateBeforeTesting):
+        Parse the new webkit-test-runner paramter: additionalSupportedImageTypes.
+        Make DRT call setAdditionalSupportedImageTypesForTesting() before starting
+        the test.
+
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.cpp:
+        (WTR::InjectedBundle::stringForKey):
+        (WTR::InjectedBundle::beginTesting):
+        * WebKitTestRunner/InjectedBundle/InjectedBundle.h:
+        * WebKitTestRunner/TestController.cpp:
+        (WTR::TestController::resetStateToConsistentValues):
+        (WTR::updateTestOptionsFromTestHeader):
+        * WebKitTestRunner/TestInvocation.cpp:
+        (WTR::TestInvocation::createTestSettingsDictionary):
+        * WebKitTestRunner/TestOptions.h:
+        (WTR::TestOptions::hasSameInitializationOptions const):
+        Parse the new webkit-test-runner paramter: additionalSupportedImageTypes.
+        Make WTR call setAdditionalSupportedImageTypesForTesting() before starting
+        the test.
+
 2019-05-12  Yusuke Suzuki  <ysuzuki@apple.com>
 
         [JSC] Compress Watchpoint size by using enum type and Packed<> data structure
index 3ca91c4..aaa5e72 100644 (file)
@@ -105,6 +105,8 @@ TestOptions::TestOptions(const std::string& pathOrURL, const std::string& absolu
             enableColorFilter = parseBooleanTestHeaderValue(value);
         else if (key == "jscOptions")
             jscOptions = value;
+        else if (key == "additionalSupportedImageTypes")
+            additionalSupportedImageTypes = value;
         else if (key == "experimental:WebGPUEnabled")
             enableWebGPU = parseBooleanTestHeaderValue(value);
         else if (key == "internal:CSSLogicalEnabled")
index f4eb671..d16f758 100644 (file)
@@ -49,6 +49,7 @@ struct TestOptions {
     bool enableResizeObserver { false };
     bool enableCoreMathML { false };
     std::string jscOptions;
+    std::string additionalSupportedImageTypes;
 
     TestOptions(const std::string& pathOrURL, const std::string& absolutePath);
     bool webViewIsCompatibleWithOptions(const TestOptions&) const;
index 9fb0a47..3e0d895 100644 (file)
@@ -1918,6 +1918,8 @@ static void resetWebViewToConsistentStateBeforeTesting(const TestOptions& option
 
     setJSCOptions(options);
 
+    WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(options.additionalSupportedImageTypes.c_str());
+
     [mainFrame _clearOpener];
 
 #if PLATFORM(MAC)
index 31d3b97..ce8ebb6 100644 (file)
@@ -464,6 +464,17 @@ bool InjectedBundle::booleanForKey(WKDictionaryRef dictionary, const char* key)
     return WKBooleanGetValue(static_cast<WKBooleanRef>(value));
 }
 
+String InjectedBundle::stringForKey(WKDictionaryRef dictionary, const char* key)
+{
+    WKRetainPtr<WKStringRef> wkKey = adoptWK(WKStringCreateWithUTF8CString(key));
+    WKStringRef value = static_cast<WKStringRef>(WKDictionaryGetItemForKey(dictionary, wkKey.get()));
+    if (!value) {
+        outputText(makeString("String value for key", key, " not found in dictionary\n"));
+        return emptyString();
+    }
+    return toWTFString(value);
+}
+
 void InjectedBundle::beginTesting(WKDictionaryRef settings, BegingTestingMode testingMode)
 {
     m_state = Testing;
@@ -496,6 +507,10 @@ void InjectedBundle::beginTesting(WKDictionaryRef settings, BegingTestingMode te
     WKBundlePageSetUseTestingViewportConfiguration(page()->page(), !booleanForKey(settings, "UseFlexibleViewport"));
 #endif
 
+#if PLATFORM(COCOA)
+    WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(stringForKey(settings, "additionalSupportedImageTypes"));
+#endif
+
     m_testRunner->setPluginsEnabled(true);
 
     m_testRunner->setUserStyleSheetEnabled(false);
index 26927d2..d576030 100644 (file)
@@ -170,6 +170,7 @@ private:
     void beginTesting(WKDictionaryRef initialSettings, BegingTestingMode);
 
     bool booleanForKey(WKDictionaryRef, const char* key);
+    String stringForKey(WKDictionaryRef, const char* key);
 
     WKBundleRef m_bundle { nullptr };
     WKBundlePageGroupRef m_pageGroup { nullptr };
index b2632c4..8cf8a27 100644 (file)
@@ -923,6 +923,10 @@ bool TestController::resetStateToConsistentValues(const TestOptions& options, Re
         WKDictionarySetItem(resetMessageBody.get(), jscOptionsKey.get(), jscOptionsValue.get());
     }
 
+#if PLATFORM(COCOA)
+    WebCoreTestSupport::setAdditionalSupportedImageTypesForTesting(options.additionalSupportedImageTypes.c_str());
+#endif
+
     WKPagePostMessageToInjectedBundle(TestController::singleton().mainWebView()->page(), messageName.get(), resetMessageBody.get());
 
     WKContextSetShouldUseFontSmoothing(TestController::singleton().context(), false);
@@ -1369,6 +1373,8 @@ static void updateTestOptionsFromTestHeader(TestOptions& testOptions, const std:
             testOptions.punchOutWhiteBackgroundsInDarkMode = parseBooleanTestHeaderValue(value);
         else if (key == "jscOptions")
             testOptions.jscOptions = value;
+        else if (key == "additionalSupportedImageTypes")
+            testOptions.additionalSupportedImageTypes = value;
         else if (key == "runSingly")
             testOptions.runSingly = parseBooleanTestHeaderValue(value);
         else if (key == "shouldIgnoreMetaViewport")
index 6142e19..ccaf103 100644 (file)
@@ -146,7 +146,11 @@ WKRetainPtr<WKMutableDictionaryRef> TestInvocation::createTestSettingsDictionary
     WKRetainPtr<WKStringRef> dumpJSConsoleLogInStdErrKey = adoptWK(WKStringCreateWithUTF8CString("DumpJSConsoleLogInStdErr"));
     WKRetainPtr<WKBooleanRef> dumpJSConsoleLogInStdErrValue = adoptWK(WKBooleanCreate(m_dumpJSConsoleLogInStdErr));
     WKDictionarySetItem(beginTestMessageBody.get(), dumpJSConsoleLogInStdErrKey.get(), dumpJSConsoleLogInStdErrValue.get());
-    
+
+    WKRetainPtr<WKStringRef> additionalSupportedImageTypesKey = adoptWK(WKStringCreateWithUTF8CString("additionalSupportedImageTypes"));
+    WKRetainPtr<WKStringRef> additionalSupportedImageTypesValue = adoptWK(WKStringCreateWithUTF8CString(options().additionalSupportedImageTypes.c_str()));
+    WKDictionarySetItem(beginTestMessageBody.get(), additionalSupportedImageTypesKey.get(), additionalSupportedImageTypesValue.get());
+
     return beginTestMessageBody;
 }
 
index 2b1dbf9..c120484 100644 (file)
@@ -98,6 +98,7 @@ struct TestOptions {
     float deviceScaleFactor { 1 };
     std::string applicationManifest;
     std::string jscOptions;
+    std::string additionalSupportedImageTypes;
     HashMap<String, bool> experimentalFeatures;
     HashMap<String, bool> internalDebugFeatures;
 
@@ -132,6 +133,7 @@ struct TestOptions {
             || enableColorFilter != options.enableColorFilter
             || punchOutWhiteBackgroundsInDarkMode != options.punchOutWhiteBackgroundsInDarkMode
             || jscOptions != options.jscOptions
+            || additionalSupportedImageTypes != options.additionalSupportedImageTypes
             || runSingly != options.runSingly
             || checkForWorldLeaks != options.checkForWorldLeaks
             || shouldShowSpellCheckingDots != options.shouldShowSpellCheckingDots