Improve use of NeverDestroyed
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / ImageDecoderCG.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ImageDecoderCG.h"
28
29 #if USE(CG)
30
31 #include "ImageIOSPI.h"
32 #include "ImageOrientation.h"
33 #include "ImageSourceCG.h"
34 #include "IntPoint.h"
35 #include "IntSize.h"
36 #include "Logging.h"
37 #include "SharedBuffer.h"
38
39 #if !PLATFORM(IOS)
40 #include <ApplicationServices/ApplicationServices.h>
41 #else
42 #include "CoreGraphicsSPI.h"
43 #include <ImageIO/ImageIO.h>
44 #endif
45
46 namespace WebCore {
47
48 const CFStringRef WebCoreCGImagePropertyAPNGUnclampedDelayTime = CFSTR("UnclampedDelayTime");
49 const CFStringRef WebCoreCGImagePropertyAPNGDelayTime = CFSTR("DelayTime");
50 const CFStringRef WebCoreCGImagePropertyAPNGLoopCount = CFSTR("LoopCount");
51
52 #if PLATFORM(WIN)
53 const CFStringRef kCGImageSourceShouldPreferRGB32 = CFSTR("kCGImageSourceShouldPreferRGB32");
54 const CFStringRef kCGImageSourceSkipMetadata = CFSTR("kCGImageSourceSkipMetadata");
55 const CFStringRef kCGImageSourceSubsampleFactor = CFSTR("kCGImageSourceSubsampleFactor");
56 const CFStringRef kCGImageSourceShouldCacheImmediately = CFSTR("kCGImageSourceShouldCacheImmediately");
57 #endif
58
59 static RetainPtr<CFMutableDictionaryRef> createImageSourceOptions()
60 {
61     RetainPtr<CFMutableDictionaryRef> options = adoptCF(CFDictionaryCreateMutable(nullptr, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
62     CFDictionarySetValue(options.get(), kCGImageSourceShouldCache, kCFBooleanTrue);
63     CFDictionarySetValue(options.get(), kCGImageSourceShouldPreferRGB32, kCFBooleanTrue);
64     CFDictionarySetValue(options.get(), kCGImageSourceSkipMetadata, kCFBooleanTrue);
65     return options;
66 }
67     
68 static RetainPtr<CFMutableDictionaryRef> createImageSourceAsyncOptions()
69 {
70     RetainPtr<CFMutableDictionaryRef> options = createImageSourceOptions();
71     CFDictionarySetValue(options.get(), kCGImageSourceShouldCacheImmediately, kCFBooleanTrue);
72     CFDictionarySetValue(options.get(), kCGImageSourceCreateThumbnailFromImageAlways, kCFBooleanTrue);
73     return options;
74 }
75
76 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel)
77 {
78     subsamplingLevel = std::min(SubsamplingLevel::Last, std::max(SubsamplingLevel::First, subsamplingLevel));
79     int subsampleInt = 1 << static_cast<int>(subsamplingLevel); // [0..3] => [1, 2, 4, 8]
80     auto subsampleNumber = adoptCF(CFNumberCreate(nullptr,  kCFNumberIntType,  &subsampleInt));
81     CFDictionarySetValue(options.get(), kCGImageSourceSubsampleFactor, subsampleNumber.get());
82     return WTFMove(options);
83 }
84
85 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOption(RetainPtr<CFMutableDictionaryRef>&& options, const IntSize& sizeForDrawing)
86 {
87     unsigned maxDimension = DecodingOptions::maxDimension(sizeForDrawing);
88     RetainPtr<CFNumberRef> maxDimensionNumber = adoptCF(CFNumberCreate(nullptr, kCFNumberIntType, &maxDimension));
89     CFDictionarySetValue(options.get(), kCGImageSourceThumbnailMaxPixelSize, maxDimensionNumber.get());
90     return WTFMove(options);
91 }
92     
93 static RetainPtr<CFMutableDictionaryRef> appendImageSourceOptions(RetainPtr<CFMutableDictionaryRef>&& options, SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
94 {
95     if (subsamplingLevel != SubsamplingLevel::Default)
96         options = appendImageSourceOption(WTFMove(options), subsamplingLevel);
97     
98     options = appendImageSourceOption(WTFMove(options), sizeForDrawing);
99     return WTFMove(options);
100 }
101     
102 static RetainPtr<CFDictionaryRef> imageSourceOptions(SubsamplingLevel subsamplingLevel = SubsamplingLevel::Default)
103 {
104     static const auto options = createImageSourceOptions().leakRef();
105     if (subsamplingLevel == SubsamplingLevel::Default)
106         return options;
107     return appendImageSourceOption(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options)), subsamplingLevel);
108 }
109
110 static RetainPtr<CFDictionaryRef> imageSourceAsyncOptions(SubsamplingLevel subsamplingLevel, const IntSize& sizeForDrawing)
111 {
112     static const auto options = createImageSourceAsyncOptions().leakRef();
113     return appendImageSourceOptions(adoptCF(CFDictionaryCreateMutableCopy(nullptr, 0, options)), subsamplingLevel, sizeForDrawing);
114 }
115     
116 static ImageOrientation orientationFromProperties(CFDictionaryRef imageProperties)
117 {
118     ASSERT(imageProperties);
119     CFNumberRef orientationProperty = (CFNumberRef)CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
120     if (!orientationProperty)
121         return ImageOrientation();
122     
123     int exifValue;
124     CFNumberGetValue(orientationProperty, kCFNumberIntType, &exifValue);
125     return ImageOrientation::fromEXIFValue(exifValue);
126 }
127
128 #if !PLATFORM(COCOA)
129 size_t sharedBufferGetBytesAtPosition(void* info, void* buffer, off_t position, size_t count)
130 {
131     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
132     size_t sourceSize = sharedBuffer->size();
133     if (position >= sourceSize)
134         return 0;
135     
136     const char* source = sharedBuffer->data() + position;
137     size_t amount = std::min<size_t>(count, sourceSize - position);
138     memcpy(buffer, source, amount);
139     return amount;
140 }
141
142 void sharedBufferRelease(void* info)
143 {
144     SharedBuffer* sharedBuffer = static_cast<SharedBuffer*>(info);
145     sharedBuffer->deref();
146 }
147 #endif
148
149 ImageDecoder::ImageDecoder(SharedBuffer& data, AlphaOption, GammaAndColorProfileOption)
150 {
151     RetainPtr<CFStringRef> utiHint;
152     if (data.size() >= 32)
153         utiHint = adoptCF(CGImageSourceGetTypeWithData(data.createCFData().get(), nullptr, nullptr));
154     
155     if (utiHint) {
156         const void* key = kCGImageSourceTypeIdentifierHint;
157         const void* value = utiHint.get();
158         auto options = adoptCF(CFDictionaryCreate(kCFAllocatorDefault, &key, &value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
159         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(options.get()));
160     } else
161         m_nativeDecoder = adoptCF(CGImageSourceCreateIncremental(nullptr));
162 }
163
164 size_t ImageDecoder::bytesDecodedToDetermineProperties()
165 {
166     // Measured by tracing malloc/calloc calls on Mac OS 10.6.6, x86_64.
167     // A non-zero value ensures cached images with no decoded frames still enter
168     // the live decoded resources list when the CGImageSource decodes image
169     // properties, allowing the cache to prune the partially decoded image.
170     // This value is likely to be inaccurate on other platforms, but the overall
171     // behavior is unchanged.
172     return 13088;
173 }
174     
175 String ImageDecoder::uti() const
176 {
177     return CGImageSourceGetType(m_nativeDecoder.get());
178 }
179
180 String ImageDecoder::filenameExtension() const
181 {
182     return WebCore::preferredExtensionForImageSourceType(uti());
183 }
184
185 EncodedDataStatus ImageDecoder::encodedDataStatus() const
186 {
187     switch (CGImageSourceGetStatus(m_nativeDecoder.get())) {
188     case kCGImageStatusUnknownType:
189         return EncodedDataStatus::Error;
190
191     case kCGImageStatusUnexpectedEOF:
192     case kCGImageStatusInvalidData:
193     case kCGImageStatusReadingHeader:
194         // Ragnaros yells: TOO SOON! You have awakened me TOO SOON, Executus!
195         if (!m_isAllDataReceived)
196             return EncodedDataStatus::Unknown;
197
198         return EncodedDataStatus::Error;
199
200     case kCGImageStatusIncomplete: {
201         RetainPtr<CFDictionaryRef> image0Properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
202         if (!image0Properties)
203             return EncodedDataStatus::TypeAvailable;
204         
205         if (!CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelWidth) || !CFDictionaryContainsKey(image0Properties.get(), kCGImagePropertyPixelHeight))
206             return EncodedDataStatus::TypeAvailable;
207         
208         return EncodedDataStatus::SizeAvailable;
209     }
210
211     case kCGImageStatusComplete:
212         return EncodedDataStatus::Complete;
213     }
214
215     ASSERT_NOT_REACHED();
216     return EncodedDataStatus::Unknown;
217 }
218
219 size_t ImageDecoder::frameCount() const
220 {
221     return CGImageSourceGetCount(m_nativeDecoder.get());
222 }
223
224 RepetitionCount ImageDecoder::repetitionCount() const
225 {
226     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyProperties(m_nativeDecoder.get(), imageSourceOptions().get()));
227     if (!properties)
228         return RepetitionCountOnce;
229     
230     CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
231     if (gifProperties) {
232         CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFLoopCount);
233         
234         // No property means loop once.
235         if (!num)
236             return RepetitionCountOnce;
237         
238         RepetitionCount loopCount;
239         CFNumberGetValue(num, kCFNumberIntType, &loopCount);
240         
241         // A property with value 0 means loop forever.
242         return loopCount ? loopCount : RepetitionCountInfinite;
243     }
244     
245     CFDictionaryRef pngProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPNGDictionary);
246     if (pngProperties) {
247         CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGLoopCount);
248         if (!num)
249             return RepetitionCountOnce;
250         
251         RepetitionCount loopCount;
252         CFNumberGetValue(num, kCFNumberIntType, &loopCount);
253         return loopCount ? loopCount : RepetitionCountInfinite;
254     }
255     
256     // Turns out we're not an animated image after all, so we don't animate.
257     return RepetitionCountNone;
258 }
259
260 std::optional<IntPoint> ImageDecoder::hotSpot() const
261 {
262     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
263     if (!properties)
264         return std::nullopt;
265     
266     int x = -1, y = -1;
267     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotX"));
268     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &x))
269         return std::nullopt;
270     
271     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), CFSTR("hotspotY"));
272     if (!num || !CFNumberGetValue(num, kCFNumberIntType, &y))
273         return std::nullopt;
274     
275     if (x < 0 || y < 0)
276         return std::nullopt;
277     
278     return IntPoint(x, y);
279 }
280
281 IntSize ImageDecoder::frameSizeAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
282 {
283     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions(subsamplingLevel).get()));
284     
285     if (!properties)
286         return { };
287     
288     int width = 0;
289     int height = 0;
290     CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelWidth);
291     if (num)
292         CFNumberGetValue(num, kCFNumberIntType, &width);
293     
294     num = (CFNumberRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPixelHeight);
295     if (num)
296         CFNumberGetValue(num, kCFNumberIntType, &height);
297     
298     return IntSize(width, height);
299 }
300
301 bool ImageDecoder::frameIsCompleteAtIndex(size_t index) const
302 {
303     ASSERT(frameCount());
304     // CGImageSourceGetStatusAtIndex() changes the return status value from kCGImageStatusIncomplete
305     // to kCGImageStatusComplete only if (index > 1 && index < frameCount() - 1). To get an accurate
306     // result for the last frame (or the single frame of the static image) use CGImageSourceGetStatus()
307     // instead for this frame.
308     if (index == frameCount() - 1)
309         return CGImageSourceGetStatus(m_nativeDecoder.get()) == kCGImageStatusComplete;
310     return CGImageSourceGetStatusAtIndex(m_nativeDecoder.get(), index) == kCGImageStatusComplete;
311 }
312
313 ImageOrientation ImageDecoder::frameOrientationAtIndex(size_t index) const
314 {
315     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
316     if (!properties)
317         return ImageOrientation();
318     
319     return orientationFromProperties(properties.get());
320 }
321
322 float ImageDecoder::frameDurationAtIndex(size_t index) const
323 {
324     float duration = 0;
325     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), index, imageSourceOptions().get()));
326     if (properties) {
327         CFDictionaryRef gifProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyGIFDictionary);
328         if (gifProperties) {
329             if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFUnclampedDelayTime)) {
330                 // Use the unclamped frame delay if it exists.
331                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
332             } else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(gifProperties, kCGImagePropertyGIFDelayTime)) {
333                 // Fall back to the clamped frame delay if the unclamped frame delay does not exist.
334                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
335             }
336         }
337         
338         CFDictionaryRef pngProperties = (CFDictionaryRef)CFDictionaryGetValue(properties.get(), kCGImagePropertyPNGDictionary);
339         if (pngProperties) {
340             if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGUnclampedDelayTime))
341                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
342             else if (CFNumberRef num = (CFNumberRef)CFDictionaryGetValue(pngProperties, WebCoreCGImagePropertyAPNGDelayTime))
343                 CFNumberGetValue(num, kCFNumberFloatType, &duration);
344         }
345     }
346     
347     // Many annoying ads specify a 0 duration to make an image flash as quickly as possible.
348     // We follow Firefox's behavior and use a duration of 100 ms for any frames that specify
349     // a duration of <= 10 ms. See <rdar://problem/7689300> and <http://webkit.org/b/36082>
350     // for more information.
351     if (duration < 0.011f)
352         return 0.1f;
353     return duration;
354 }
355
356 bool ImageDecoder::frameAllowSubsamplingAtIndex(size_t) const
357 {
358     RetainPtr<CFDictionaryRef> properties = adoptCF(CGImageSourceCopyPropertiesAtIndex(m_nativeDecoder.get(), 0, imageSourceOptions().get()));
359     if (!properties)
360         return false;
361     
362     CFDictionaryRef jfifProperties = static_cast<CFDictionaryRef>(CFDictionaryGetValue(properties.get(), kCGImagePropertyJFIFDictionary));
363     if (jfifProperties) {
364         CFBooleanRef isProgCFBool = static_cast<CFBooleanRef>(CFDictionaryGetValue(jfifProperties, kCGImagePropertyJFIFIsProgressive));
365         if (isProgCFBool) {
366             bool isProgressive = CFBooleanGetValue(isProgCFBool);
367             // Workaround for <rdar://problem/5184655> - Hang rendering very large progressive JPEG. Decoding progressive
368             // images hangs for a very long time right now. Until this is fixed, don't sub-sample progressive images. This
369             // will cause them to fail our large image check and they won't be decoded.
370             // FIXME: Remove once underlying issue is fixed (<rdar://problem/5191418>)
371             return !isProgressive;
372         }
373     }
374     
375     return true;
376 }
377
378 bool ImageDecoder::frameHasAlphaAtIndex(size_t index) const
379 {
380     if (!frameIsCompleteAtIndex(index))
381         return true;
382     
383     String uti = this->uti();
384     
385     // Return false if there is no image type or the image type is JPEG, because
386     // JPEG does not support alpha transparency.
387     if (uti.isEmpty() || uti == "public.jpeg")
388         return false;
389     
390     // FIXME: Could return false for other non-transparent image formats.
391     // FIXME: Could maybe return false for a GIF Frame if we have enough info in the GIF properties dictionary
392     // to determine whether or not a transparent color was defined.
393     return true;
394 }
395
396 unsigned ImageDecoder::frameBytesAtIndex(size_t index, SubsamplingLevel subsamplingLevel) const
397 {
398     IntSize frameSize = frameSizeAtIndex(index, subsamplingLevel);
399     return (frameSize.area() * 4).unsafeGet();
400 }
401
402 NativeImagePtr ImageDecoder::createFrameImageAtIndex(size_t index, SubsamplingLevel subsamplingLevel, const DecodingOptions& decodingOptions) const
403 {
404     LOG(Images, "ImageDecoder %p createFrameImageAtIndex %lu", this, index);
405     RetainPtr<CFDictionaryRef> options;
406     RetainPtr<CGImageRef> image;
407
408     if (!decodingOptions.isSynchronous()) {
409         // Don't consider the subsamplingLevel when comparing the image native size with sizeForDrawing.
410         IntSize size = frameSizeAtIndex(index, SubsamplingLevel::Default);
411         
412         if (decodingOptions.hasSizeForDrawing()) {
413             // See which size is smaller: the image native size or the sizeForDrawing.
414             std::optional<IntSize> sizeForDrawing = decodingOptions.sizeForDrawing();
415             if (sizeForDrawing.value().unclampedArea() < size.unclampedArea())
416                 size = sizeForDrawing.value();
417         }
418         
419         options = imageSourceAsyncOptions(subsamplingLevel, size);
420         image = adoptCF(CGImageSourceCreateThumbnailAtIndex(m_nativeDecoder.get(), index, options.get()));
421     } else {
422         // Decode an image synchronously for its native size.
423         options = imageSourceOptions(subsamplingLevel);
424         image = adoptCF(CGImageSourceCreateImageAtIndex(m_nativeDecoder.get(), index, options.get()));
425     }
426     
427 #if PLATFORM(IOS)
428     // <rdar://problem/7371198> - CoreGraphics changed the default caching behaviour in iOS 4.0 to kCGImageCachingTransient
429     // which caused a performance regression for us since the images had to be resampled/recreated every time we called
430     // CGContextDrawImage. We now tell CG to cache the drawn images. See also <rdar://problem/14366755> -
431     // CoreGraphics needs to un-deprecate kCGImageCachingTemporary since it's still not the default.
432 #if COMPILER(CLANG)
433 #pragma clang diagnostic push
434 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
435 #endif
436     CGImageSetCachingFlags(image.get(), kCGImageCachingTemporary);
437 #if COMPILER(CLANG)
438 #pragma clang diagnostic pop
439 #endif
440 #endif // PLATFORM(IOS)
441     
442     String uti = this->uti();
443     if (uti.isEmpty() || uti != "public.xbitmap-image")
444         return image;
445     
446     // If it is an xbm image, mask out all the white areas to render them transparent.
447     const CGFloat maskingColors[6] = {255, 255,  255, 255, 255, 255};
448     RetainPtr<CGImageRef> maskedImage = adoptCF(CGImageCreateWithMaskingColors(image.get(), maskingColors));
449     return maskedImage ? maskedImage : image;
450 }
451
452 void ImageDecoder::setData(SharedBuffer& data, bool allDataReceived)
453 {
454     m_isAllDataReceived = allDataReceived;
455
456 #if PLATFORM(COCOA)
457     // On Mac the NSData inside the SharedBuffer can be secretly appended to without the SharedBuffer's knowledge.
458     // We use SharedBuffer's ability to wrap itself inside CFData to get around this, ensuring that ImageIO is
459     // really looking at the SharedBuffer.
460     CGImageSourceUpdateData(m_nativeDecoder.get(), data.createCFData().get(), allDataReceived);
461 #else
462     // Create a CGDataProvider to wrap the SharedBuffer.
463     data.ref();
464     // We use the GetBytesAtPosition callback rather than the GetBytePointer one because SharedBuffer
465     // does not provide a way to lock down the byte pointer and guarantee that it won't move, which
466     // is a requirement for using the GetBytePointer callback.
467     CGDataProviderDirectCallbacks providerCallbacks = { 0, 0, 0, sharedBufferGetBytesAtPosition, sharedBufferRelease };
468     RetainPtr<CGDataProviderRef> dataProvider = adoptCF(CGDataProviderCreateDirect(&data, data.size(), &providerCallbacks));
469     CGImageSourceUpdateDataProvider(m_nativeDecoder.get(), dataProvider.get(), allDataReceived);
470 #endif
471 }
472
473 }
474
475 #endif // USE(CG)