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 45SOFT_LINK_FRAMEWORK_OPTIONAL(CoreMedia) 46SOFT_LINK(CoreMedia, CMTimeCopyAsDictionary, CFDictionaryRef, (CMTime time, CFAllocatorRef allocator), (time, allocator)) 47 48typedef AVMetadataItem AVMetadataItemType; 49SOFT_LINK_FRAMEWORK_OPTIONAL(AVFoundation) 50SOFT_LINK_CLASS(AVFoundation, AVMetadataItem) 51#define AVMetadataItem getAVMetadataItemClass() 52 53 54namespace WebCore { 55 56#if JSC_OBJC_API_ENABLED 57static JSValue *jsValueWithDataInContext(NSData *, JSContext *); 58static JSValue *jsValueWithArrayInContext(NSArray *, JSContext *); 59static JSValue *jsValueWithDictionaryInContext(NSDictionary *, JSContext *); 60static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *, JSContext *); 61static JSValue *jsValueWithValueInContext(id, JSContext *); 62#endif 63 64SerializedPlatformRepresentationMac::SerializedPlatformRepresentationMac(id nativeValue) 65 : SerializedPlatformRepresentation() 66 , m_nativeValue(nativeValue) 67{ 68} 69 70SerializedPlatformRepresentationMac::~SerializedPlatformRepresentationMac() 71{ 72} 73 74PassRef<SerializedPlatformRepresentation> SerializedPlatformRepresentationMac::create(id nativeValue) 75{ 76 return adoptRef(*new SerializedPlatformRepresentationMac(nativeValue)); 77} 78 79PassRefPtr<ArrayBuffer> SerializedPlatformRepresentationMac::data() const 80{ 81 return nullptr; 82} 83 84JSC::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 101bool 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 114SerializedPlatformRepresentationMac* toSerializedPlatformRepresentationMac(SerializedPlatformRepresentation* rep) 115{ 116 return const_cast<SerializedPlatformRepresentationMac*>(toSerializedPlatformRepresentationMac(const_cast<const SerializedPlatformRepresentation*>(rep))); 117} 118 119const 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 126static 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 149static 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 159static 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 181static 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 if (exception) 200 continue; 201 } 202 203 return result; 204} 205 206static JSValue *jsValueWithAVMetadataItemInContext(AVMetadataItemType *item, JSContext *context) 207{ 208 NSMutableDictionary *dictionary = [NSMutableDictionary dictionary]; 209 210 NSDictionary *extras = [item extraAttributes]; 211 for (id key in [extras keyEnumerator]) { 212 if (![key isKindOfClass:[NSString class]]) 213 continue; 214 id value = [extras objectForKey:key]; 215 NSString *keyString = key; 216 217 if ([key isEqualToString:@"MIMEtype"]) 218 keyString = @"type"; 219 else if ([key isEqualToString:@"dataTypeNamespace"] || [key isEqualToString:@"pictureType"]) 220 continue; 221 else if ([key isEqualToString:@"dataType"]) { 222 id dataTypeNamespace = [extras objectForKey:@"dataTypeNamespace"]; 223 if (!dataTypeNamespace || ![dataTypeNamespace isKindOfClass:[NSString class]] || ![dataTypeNamespace isEqualToString:@"org.iana.media-type"]) 224 continue; 225 keyString = @"type"; 226 } else if ([value isKindOfClass:[NSString class]]) { 227 if (![value length]) 228 continue; 229 keyString = [key lowercaseString]; 230 } 231 232 [dictionary setObject:value forKey:keyString]; 233 } 234 235 if (item.key) 236 [dictionary setObject:item.key forKey:@"key"]; 237 238 if (item.locale) 239 [dictionary setObject:item.locale forKey:@"locale"]; 240 241 if (item.value) 242 [dictionary setObject:item.value forKey:@"data"]; 243 244 return jsValueWithDictionaryInContext(dictionary, context); 245} 246#endif 247 248} // namespace WebCore 249 250#endif 251