Only include those parts of AVFoundation.framework which are strictly needed.
[WebKit-https.git] / Source / WebCore / platform / mac / SerializedPlatformRepresentationMac.mm
1 /*
2  * Copyright (C) 2014, 2015 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 <AVFoundation/AVMetadataItem.h>
34 #import <Foundation/NSString.h>
35 #import <JavaScriptCore/APICast.h>
36 #import <JavaScriptCore/JSContextRef.h>
37 #import <JavaScriptCore/JSObjectRef.h>
38 #import <JavaScriptCore/JavaScriptCore.h>
39 #import <objc/runtime.h>
40 #import <runtime/ArrayBuffer.h>
41 #import <runtime/JSArrayBuffer.h>
42 #import <wtf/text/Base64.h>
43
44 typedef AVMetadataItem AVMetadataItemType;
45 SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation)
46 SOFT_LINK_CLASS(AVFoundation, AVMetadataItem)
47 #define AVMetadataItem getAVMetadataItemClass()
48
49
50 namespace WebCore {
51
52 #if JSC_OBJC_API_ENABLED
53 static JSValue *jsValueWithDataInContext(NSData *, JSContext *);
54 static JSValue *jsValueWithArrayInContext(NSArray *, JSContext *);
55 static JSValue *jsValueWithDictionaryInContext(NSDictionary *, JSContext *);
56 static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *, JSContext *);
57 static JSValue *jsValueWithValueInContext(id, JSContext *);
58 #endif
59
60 SerializedPlatformRepresentationMac::SerializedPlatformRepresentationMac(id nativeValue)
61     : SerializedPlatformRepresentation()
62     , m_nativeValue(nativeValue)
63 {
64 }
65
66 SerializedPlatformRepresentationMac::~SerializedPlatformRepresentationMac()
67 {
68 }
69
70 Ref<SerializedPlatformRepresentation> SerializedPlatformRepresentationMac::create(id nativeValue)
71 {
72     return adoptRef(*new SerializedPlatformRepresentationMac(nativeValue));
73 }
74
75 RefPtr<ArrayBuffer> SerializedPlatformRepresentationMac::data() const
76 {
77     return nullptr;
78 }
79
80 JSC::JSValue SerializedPlatformRepresentationMac::deserialize(JSC::ExecState* exec) const
81 {
82 #if JSC_OBJC_API_ENABLED
83     if (!m_nativeValue)
84         return JSC::jsNull();
85
86     JSGlobalContextRef jsGlobalContextRef = toGlobalRef(exec->lexicalGlobalObject()->globalExec());
87     JSContext *jsContext = [JSContext contextWithJSGlobalContextRef:jsGlobalContextRef];
88     JSValue *serializedValue = jsValueWithValueInContext(m_nativeValue.get(), jsContext);
89
90     return toJS(exec, [serializedValue JSValueRef]);
91 #else
92     UNUSED_PARAM(exec);
93     return JSC::jsNull();
94 #endif
95 }
96
97 bool SerializedPlatformRepresentationMac::isEqual(const SerializedPlatformRepresentation& other) const
98 {
99     if (other.platformType() != SerializedPlatformRepresentation::ObjC)
100         return false;
101
102     const SerializedPlatformRepresentationMac* otherObjC = toSerializedPlatformRepresentationMac(&other);
103
104     if (!m_nativeValue || !otherObjC->nativeValue())
105         return false;
106
107     return [m_nativeValue.get() isEqual:otherObjC->nativeValue()];
108 }
109
110 SerializedPlatformRepresentationMac* toSerializedPlatformRepresentationMac(SerializedPlatformRepresentation* rep)
111 {
112     return const_cast<SerializedPlatformRepresentationMac*>(toSerializedPlatformRepresentationMac(const_cast<const SerializedPlatformRepresentation*>(rep)));
113 }
114
115 const SerializedPlatformRepresentationMac* toSerializedPlatformRepresentationMac(const SerializedPlatformRepresentation* rep)
116 {
117     ASSERT_WITH_SECURITY_IMPLICATION(rep->platformType() == SerializedPlatformRepresentation::ObjC);
118     return static_cast<const SerializedPlatformRepresentationMac*>(rep);
119 }
120
121 #if JSC_OBJC_API_ENABLED
122 static JSValue *jsValueWithValueInContext(id value, JSContext *context)
123 {
124     if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]])
125         return [JSValue valueWithObject:value inContext:context];
126
127     if ([value isKindOfClass:[NSLocale class]])
128         return [JSValue valueWithObject:[value localeIdentifier] inContext:context];
129
130     if ([value isKindOfClass:[NSDictionary class]])
131         return jsValueWithDictionaryInContext(value, context);
132
133     if ([value isKindOfClass:[NSArray class]])
134         return jsValueWithArrayInContext(value, context);
135
136     if ([value isKindOfClass:[NSData class]])
137         return jsValueWithDataInContext(value, context);
138
139     if ([value isKindOfClass:[AVMetadataItem class]])
140         return jsValueWithAVMetadataItemInContext(value, context);
141
142     return nil;
143 }
144
145 static JSValue *jsValueWithDataInContext(NSData *data, JSContext *context)
146 {
147     auto dataArray = ArrayBuffer::tryCreate([data bytes], [data length]);
148
149     JSC::ExecState* exec = toJS([context JSGlobalContextRef]);
150     JSC::JSValue array = toJS(exec, JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject()), dataArray.get());
151
152     return [JSValue valueWithJSValueRef:toRef(exec, array) inContext:context];
153 }
154
155 static JSValue *jsValueWithArrayInContext(NSArray *array, JSContext *context)
156 {
157     JSValueRef exception = 0;
158     JSValue *result = [JSValue valueWithNewArrayInContext:context];
159     JSObjectRef resultObject = JSValueToObject([context JSGlobalContextRef], [result JSValueRef], &exception);
160     if (exception)
161         return [JSValue valueWithUndefinedInContext:context];
162
163     NSUInteger count = [array count];
164     for (NSUInteger i = 0; i < count; ++i) {
165         JSValue *value = jsValueWithValueInContext([array objectAtIndex:i], context);
166         if (!value)
167             continue;
168
169         JSObjectSetPropertyAtIndex([context JSGlobalContextRef], resultObject, (unsigned)i, [value JSValueRef], &exception);
170         if (exception)
171             continue;
172     }
173
174     return result;
175 }
176
177 static JSValue *jsValueWithDictionaryInContext(NSDictionary *dictionary, JSContext *context)
178 {
179     JSValueRef exception = 0;
180     JSValue *result = [JSValue valueWithNewObjectInContext:context];
181     JSObjectRef resultObject = JSValueToObject([context JSGlobalContextRef], [result JSValueRef], &exception);
182     if (exception)
183         return [JSValue valueWithUndefinedInContext:context];
184
185     for (id key in [dictionary keyEnumerator]) {
186         if (![key isKindOfClass:[NSString class]])
187             continue;
188
189         JSValue *value = jsValueWithValueInContext([dictionary objectForKey:key], context);
190         if (!value)
191             continue;
192
193         JSStringRef name = JSStringCreateWithCFString((CFStringRef)key);
194         JSObjectSetProperty([context JSGlobalContextRef], resultObject, name, [value JSValueRef], 0, &exception);
195         JSStringRelease(name);
196         if (exception)
197             continue;
198     }
199
200     return result;
201 }
202
203 static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *item, JSContext *context)
204 {
205     NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
206
207     NSDictionary *extras = [item extraAttributes];
208     for (id key in [extras keyEnumerator]) {
209         if (![key isKindOfClass:[NSString class]])
210             continue;
211         id value = [extras objectForKey:key];
212         NSString *keyString = key;
213
214         if ([key isEqualToString:@"MIMEtype"])
215             keyString = @"type";
216         else if ([key isEqualToString:@"dataTypeNamespace"] || [key isEqualToString:@"pictureType"])
217             continue;
218         else if ([key isEqualToString:@"dataType"]) {
219             id dataTypeNamespace = [extras objectForKey:@"dataTypeNamespace"];
220             if (!dataTypeNamespace || ![dataTypeNamespace isKindOfClass:[NSString class]] || ![dataTypeNamespace isEqualToString:@"org.iana.media-type"])
221                 continue;
222             keyString = @"type";
223         } else if ([value isKindOfClass:[NSString class]]) {
224             if (![value length])
225                 continue;
226             keyString = [key lowercaseString];
227         }
228
229         [dictionary setObject:value forKey:keyString];
230     }
231
232     if (item.key)
233         [dictionary setObject:item.key forKey:@"key"];
234
235     if (item.locale)
236         [dictionary setObject:item.locale forKey:@"locale"];
237
238     if (item.value)
239         [dictionary setObject:item.value forKey:@"data"];
240
241     return jsValueWithDictionaryInContext(dictionary, context);
242 }
243 #endif
244
245 } // namespace WebCore
246
247 #endif