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