/* * Copyright (C) 2012 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #import "config.h" #import "ObjCObjectGraph.h" #import "ArgumentCodersMac.h" #import "ArgumentDecoder.h" #import "ArgumentEncoder.h" #import #if WK_API_ENABLED #import "UserData.h" #import "WKAPICast.h" #import "WKBrowsingContextHandleInternal.h" #import "WKTypeRefWrapper.h" #endif namespace WebKit { static bool shouldTransformGraph(id object, const ObjCObjectGraph::Transformer& transformer) { if (NSArray *array = dynamic_objc_cast(object)) { for (id element in array) { if (shouldTransformGraph(element, transformer)) return true; } } if (NSDictionary *dictionary = dynamic_objc_cast(object)) { bool result = false; [dictionary enumerateKeysAndObjectsUsingBlock:[&transformer, &result](id key, id object, BOOL* stop) { if (shouldTransformGraph(object, transformer)) { result = true; *stop = YES; } }]; return result; } return transformer.shouldTransformObject(object); } static RetainPtr transformGraph(id object, const ObjCObjectGraph::Transformer& transformer) { if (NSArray *array = dynamic_objc_cast(object)) { auto result = adoptNS([[NSMutableArray alloc] initWithCapacity:array.count]); for (id element in array) [result addObject:transformGraph(element, transformer).get()]; return result; } if (NSDictionary *dictionary = dynamic_objc_cast(object)) { auto result = adoptNS([[NSMutableDictionary alloc] initWithCapacity:dictionary.count]); [dictionary enumerateKeysAndObjectsUsingBlock:[&result, &transformer](id key, id object, BOOL*) { [result setObject:transformGraph(object, transformer).get() forKey:key]; }]; return result; } return transformer.transformObject(object); } RetainPtr ObjCObjectGraph::transform(id object, const Transformer& transformer) { if (!object) return nullptr; if (!shouldTransformGraph(object, transformer)) return object; return transformGraph(object, transformer); } enum class ObjCType { Null, NSArray, NSData, NSDate, NSDictionary, NSNumber, NSString, #if WK_API_ENABLED WKBrowsingContextHandle, WKTypeRefWrapper, #endif }; static Optional typeFromObject(id object) { ASSERT(object); if (dynamic_objc_cast(object)) return ObjCType::NSArray; if (dynamic_objc_cast(object)) return ObjCType::NSData; if (dynamic_objc_cast(object)) return ObjCType::NSDate; if (dynamic_objc_cast(object)) return ObjCType::NSDictionary; if (dynamic_objc_cast(object)) return ObjCType::NSNumber; if (dynamic_objc_cast(object)) return ObjCType::NSString; #if WK_API_ENABLED if (dynamic_objc_cast(object)) return ObjCType::WKBrowsingContextHandle; if (dynamic_objc_cast(object)) return ObjCType::WKTypeRefWrapper; #endif return Nullopt; } void ObjCObjectGraph::encode(IPC::ArgumentEncoder& encoder, id object) { if (!object) { encoder << static_cast(ObjCType::Null); return; } auto type = typeFromObject(object); if (!type) [NSException raise:NSInvalidArgumentException format:@"Can not encode objects of class type '%@'", static_cast(NSStringFromClass([object class]))]; encoder << static_cast(type.value()); switch (type.value()) { case ObjCType::NSArray: { NSArray *array = object; encoder << static_cast(array.count); for (id element in array) encode(encoder, element); break; } case ObjCType::NSData: IPC::encode(encoder, static_cast(object)); break; case ObjCType::NSDate: IPC::encode(encoder, static_cast(object)); break; case ObjCType::NSDictionary: { NSDictionary *dictionary = object; encoder << static_cast(dictionary.count); [dictionary enumerateKeysAndObjectsUsingBlock:[&encoder](id key, id object, BOOL *stop) { encode(encoder, key); encode(encoder, object); }]; break; } case ObjCType::NSNumber: IPC::encode(encoder, static_cast(object)); break; case ObjCType::NSString: IPC::encode(encoder, static_cast(object)); break; #if WK_API_ENABLED case ObjCType::WKBrowsingContextHandle: encoder << static_cast(object).pageID; break; case ObjCType::WKTypeRefWrapper: UserData::encode(encoder, toImpl(static_cast(object).object)); break; #endif default: ASSERT_NOT_REACHED(); } } void ObjCObjectGraph::encode(IPC::ArgumentEncoder& encoder) const { encode(encoder, m_rootObject.get()); } bool ObjCObjectGraph::decode(IPC::ArgumentDecoder& decoder, RetainPtr& result) { uint32_t typeAsUInt32; if (!decoder.decode(typeAsUInt32)) return false; auto type = static_cast(typeAsUInt32); switch (type) { case ObjCType::Null: result = nullptr; break; case ObjCType::NSArray: { uint64_t size; if (!decoder.decode(size)) return false; auto array = adoptNS([[NSMutableArray alloc] init]); for (uint64_t i = 0; i < size; ++i) { RetainPtr element; if (!decode(decoder, element)) return false; [array addObject:element.get()]; } result = WTF::move(array); break; } case ObjCType::NSData: { RetainPtr data; if (!IPC::decode(decoder, data)) return false; result = WTF::move(data); break; } case ObjCType::NSDate: { RetainPtr date; if (!IPC::decode(decoder, date)) return false; result = WTF::move(date); break; } case ObjCType::NSDictionary: { uint64_t size; if (!decoder.decode(size)) return false; auto dictionary = adoptNS([[NSMutableDictionary alloc] init]); for (uint64_t i = 0; i < size; ++i) { RetainPtr key; if (!decode(decoder, key)) return false; RetainPtr object; if (!decode(decoder, object)) return false; @try { [dictionary setObject:object.get() forKey:key.get()]; } @catch (id) { return false; } } result = WTF::move(dictionary); break; } case ObjCType::NSNumber: { RetainPtr number; if (!IPC::decode(decoder, number)) return false; result = WTF::move(number); break; } case ObjCType::NSString: { RetainPtr string; if (!IPC::decode(decoder, string)) return false; result = WTF::move(string); break; } #if WK_API_ENABLED case ObjCType::WKBrowsingContextHandle: { uint64_t pageID; if (!decoder.decode(pageID)) return false; result = adoptNS([[WKBrowsingContextHandle alloc] _initWithPageID:pageID]); break; } case ObjCType::WKTypeRefWrapper: { RefPtr object; if (!UserData::decode(decoder, object)) return false; result = adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(object.get())]); break; } #endif default: return false; } return true; } bool ObjCObjectGraph::decode(IPC::ArgumentDecoder& decoder, RefPtr& result) { RetainPtr rootObject; if (!decode(decoder, rootObject)) return false; result = ObjCObjectGraph::create(rootObject.get()); return true; } }