Plug leak in jsValueWithDictionaryInContext().
[WebKit-https.git] / Source / WebCore / platform / mac / SerializedPlatformRepresentationMac.mm
1 /*
2  * Copyright (C) 2014 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #if ENABLE(VIDEO_TRACK) && ENABLE(DATACUE_VALUE)
29 #include "SerializedPlatformRepresentationMac.h"
30
31 #import "JSDOMBinding.h"
32 #import "SoftLinking.h"
33 #import <objc/runtime.h>
34 #import <runtime/ArrayBuffer.h>
35 #import <runtime/JSArrayBuffer.h>
36 #import <AVFoundation/AVFoundation.h>
37 #import <CoreMedia/CoreMedia.h>
38 #import <Foundation/NSString.h>
39 #import <JavaScriptCore/APICast.h>
40 #import <JavaScriptCore/JavaScriptCore.h>
41 #import <JavaScriptCore/JSContextRef.h>
42 #import <JavaScriptCore/JSObjectRef.h>
43 #import <wtf/text/Base64.h>
44
45 SOFT_LINK_FRAMEWORK_OPTIONAL(CoreMedia)
46 SOFT_LINK(CoreMedia, CMTimeCopyAsDictionary, CFDictionaryRef, (CMTime time, CFAllocatorRef allocator), (time, allocator))
47
48 typedef AVMetadataItem AVMetadataItemType;
49 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
50 SOFT_LINK_CLASS(AVFoundation, AVMetadataItem)
51 #define AVMetadataItem getAVMetadataItemClass()
52
53
54 namespace WebCore {
55
56 #if JSC_OBJC_API_ENABLED
57 static JSValue *jsValueWithDataInContext(NSData *, JSContext *);
58 static JSValue *jsValueWithArrayInContext(NSArray *, JSContext *);
59 static JSValue *jsValueWithDictionaryInContext(NSDictionary *, JSContext *);
60 static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *, JSContext *);
61 static JSValue *jsValueWithValueInContext(id, JSContext *);
62 #endif
63
64 SerializedPlatformRepresentationMac::SerializedPlatformRepresentationMac(id nativeValue)
65     : SerializedPlatformRepresentation()
66     , m_nativeValue(nativeValue)
67 {
68 }
69
70 SerializedPlatformRepresentationMac::~SerializedPlatformRepresentationMac()
71 {
72 }
73
74 Ref<SerializedPlatformRepresentation> SerializedPlatformRepresentationMac::create(id nativeValue)
75 {
76     return adoptRef(*new SerializedPlatformRepresentationMac(nativeValue));
77 }
78
79 PassRefPtr<ArrayBuffer> SerializedPlatformRepresentationMac::data() const
80 {
81     return nullptr;
82 }
83
84 JSC::JSValue SerializedPlatformRepresentationMac::deserialize(JSC::ExecState* exec) const
85 {
86 #if JSC_OBJC_API_ENABLED
87     if (!m_nativeValue)
88         return JSC::jsNull();
89
90     JSGlobalContextRef jsGlobalContextRef = toGlobalRef(exec->lexicalGlobalObject()->globalExec());
91     JSContext *jsContext = [JSContext contextWithJSGlobalContextRef:jsGlobalContextRef];
92     JSValue *serializedValue = jsValueWithValueInContext(m_nativeValue.get(), jsContext);
93
94     return toJS(exec, [serializedValue JSValueRef]);
95 #else
96     UNUSED_PARAM(exec);
97     return JSC::jsNull();
98 #endif
99 }
100
101 bool SerializedPlatformRepresentationMac::isEqual(const SerializedPlatformRepresentation& other) const
102 {
103     if (other.platformType() != SerializedPlatformRepresentation::ObjC)
104         return false;
105
106     const SerializedPlatformRepresentationMac* otherObjC = toSerializedPlatformRepresentationMac(&other);
107
108     if (!m_nativeValue || !otherObjC->nativeValue())
109         return false;
110
111     return [m_nativeValue.get() isEqual:otherObjC->nativeValue()];
112 }
113
114 SerializedPlatformRepresentationMac* toSerializedPlatformRepresentationMac(SerializedPlatformRepresentation* rep)
115 {
116     return const_cast<SerializedPlatformRepresentationMac*>(toSerializedPlatformRepresentationMac(const_cast<const SerializedPlatformRepresentation*>(rep)));
117 }
118
119 const SerializedPlatformRepresentationMac* toSerializedPlatformRepresentationMac(const SerializedPlatformRepresentation* rep)
120 {
121     ASSERT_WITH_SECURITY_IMPLICATION(rep->platformType() == SerializedPlatformRepresentation::ObjC);
122     return static_cast<const SerializedPlatformRepresentationMac*>(rep);
123 }
124
125 #if JSC_OBJC_API_ENABLED
126 static JSValue *jsValueWithValueInContext(id value, JSContext *context)
127 {
128     if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])
129         return [JSValue valueWithObject:value inContext:context];
130
131     if ([value isKindOfClass:[NSLocale class]])
132         return [JSValue valueWithObject:[value localeIdentifier] inContext:context];
133
134     if ([value isKindOfClass:[NSDictionary class]])
135         return jsValueWithDictionaryInContext(value, context);
136
137     if ([value isKindOfClass:[NSArray class]])
138         return jsValueWithArrayInContext(value, context);
139
140     if ([value isKindOfClass:[NSData class]])
141         return jsValueWithDataInContext(value, context);
142     
143     if ([value isKindOfClass:[AVMetadataItem class]])
144         return jsValueWithAVMetadataItemInContext(value, context);
145     
146     return nil;
147 }
148
149 static JSValue *jsValueWithDataInContext(NSData *data, JSContext *context)
150 {
151     RefPtr<ArrayBuffer> dataArray = ArrayBuffer::create([data bytes], [data length]);
152
153     JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
154     JSC::JSValue array = toJS(exec, JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), dataArray.get());
155
156     return [JSValue valueWithJSValueRef:toRef(exec, array) inContext:context];
157 }
158
159 static JSValue *jsValueWithArrayInContext(NSArray *array, JSContext *context)
160 {
161     JSValueRef exception = 0;
162     JSValue *result = [JSValue valueWithNewArrayInContext:context];
163     JSObjectRef resultObject = JSValueToObject([context JSGlobalContextRef], [result JSValueRef], &exception);
164     if (exception)
165         return [JSValue valueWithUndefinedInContext:context];
166
167     NSUInteger count = [array count];
168     for (NSUInteger i = 0; i < count; ++i) {
169         JSValue *value = jsValueWithValueInContext([array objectAtIndex:i], context);
170         if (!value)
171             continue;
172
173         JSObjectSetPropertyAtIndex([context JSGlobalContextRef], resultObject, (unsigned)i, [value JSValueRef], &exception);
174         if (exception)
175             continue;
176     }
177
178     return result;
179 }
180
181 static JSValue *jsValueWithDictionaryInContext(NSDictionary *dictionary, JSContext *context)
182 {
183     JSValueRef exception = 0;
184     JSValue *result = [JSValue valueWithNewObjectInContext:context];
185     JSObjectRef resultObject = JSValueToObject([context JSGlobalContextRef], [result JSValueRef], &exception);
186     if (exception)
187         return [JSValue valueWithUndefinedInContext:context];
188
189     for (id key in [dictionary keyEnumerator]) {
190         if (![key isKindOfClass:[NSString class]])
191             continue;
192
193         JSValue *value = jsValueWithValueInContext([dictionary objectForKey:key], context);
194         if (!value)
195             continue;
196
197         JSStringRef name = JSStringCreateWithCFString((CFStringRef)key);
198         JSObjectSetProperty([context JSGlobalContextRef], resultObject, name, [value JSValueRef], 0, &exception);
199         JSStringRelease(name);
200         if (exception)
201             continue;
202     }
203
204     return result;
205 }
206
207 static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *item, JSContext *context)
208 {
209     NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
210
211     NSDictionary *extras = [item extraAttributes];
212     for (id key in [extras keyEnumerator]) {
213         if (![key isKindOfClass:[NSString class]])
214             continue;
215         id value = [extras objectForKey:key];
216         NSString *keyString = key;
217
218         if ([key isEqualToString:@"MIMEtype"])
219             keyString = @"type";
220         else if ([key isEqualToString:@"dataTypeNamespace"] || [key isEqualToString:@"pictureType"])
221             continue;
222         else if ([key isEqualToString:@"dataType"]) {
223             id dataTypeNamespace = [extras objectForKey:@"dataTypeNamespace"];
224             if (!dataTypeNamespace || ![dataTypeNamespace isKindOfClass:[NSString class]] || ![dataTypeNamespace isEqualToString:@"org.iana.media-type"])
225                 continue;
226             keyString = @"type";
227         } else if ([value isKindOfClass:[NSString class]]) {
228             if (![value length])
229                 continue;
230             keyString = [key lowercaseString];
231         }
232
233         [dictionary setObject:value forKey:keyString];
234     }
235
236     if (item.key)
237         [dictionary setObject:item.key forKey:@"key"];
238
239     if (item.locale)
240         [dictionary setObject:item.locale forKey:@"locale"];
241
242     if (item.value)
243         [dictionary setObject:item.value forKey:@"data"];
244
245     return jsValueWithDictionaryInContext(dictionary, context);
246 }
247 #endif
248
249 } // namespace WebCore
250
251 #endif