blob: f9a9059192fed8acbace46fc2431e1d87d14854f [file] [log] [blame]
/*
* Copyright (C) 2012-2020 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 "ArgumentCodersCocoa.h"
#import "Decoder.h"
#import "Encoder.h"
#import "UserData.h"
#import "WKAPICast.h"
#import "WKBrowsingContextHandleInternal.h"
#import "WKTypeRefWrapper.h"
#import <wtf/EnumTraits.h>
#import <wtf/cocoa/TypeCastsCocoa.h>
namespace WebKit {
static bool shouldTransformGraph(id object, const ObjCObjectGraph::Transformer& transformer)
{
if (NSArray *array = dynamic_objc_cast<NSArray>(object)) {
for (id element in array) {
if (shouldTransformGraph(element, transformer))
return true;
}
}
if (NSDictionary *dictionary = dynamic_objc_cast<NSDictionary>(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<id> transformGraph(id object, const ObjCObjectGraph::Transformer& transformer)
{
if (NSArray *array = dynamic_objc_cast<NSArray>(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<NSDictionary>(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<id> ObjCObjectGraph::transform(id object, const Transformer& transformer)
{
if (!object)
return nullptr;
if (!shouldTransformGraph(object, transformer))
return object;
return transformGraph(object, transformer);
}
enum class ObjCType : uint8_t {
Null,
NSArray,
NSData,
NSDate,
NSDictionary,
NSNumber,
NSString,
WKBrowsingContextHandle,
WKTypeRefWrapper,
};
static std::optional<ObjCType> typeFromObject(id object)
{
ASSERT(object);
if (dynamic_objc_cast<NSArray>(object))
return ObjCType::NSArray;
if (dynamic_objc_cast<NSData>(object))
return ObjCType::NSData;
if (dynamic_objc_cast<NSDate>(object))
return ObjCType::NSDate;
if (dynamic_objc_cast<NSDictionary>(object))
return ObjCType::NSDictionary;
if (dynamic_objc_cast<NSNumber>(object))
return ObjCType::NSNumber;
if (dynamic_objc_cast<NSString>(object))
return ObjCType::NSString;
if (dynamic_objc_cast<WKBrowsingContextHandle>(object))
return ObjCType::WKBrowsingContextHandle;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (dynamic_objc_cast<WKTypeRefWrapper>(object))
return ObjCType::WKTypeRefWrapper;
ALLOW_DEPRECATED_DECLARATIONS_END
return std::nullopt;
}
void ObjCObjectGraph::encode(IPC::Encoder& encoder, id object)
{
if (!object) {
encoder << static_cast<uint32_t>(ObjCType::Null);
return;
}
auto type = typeFromObject(object);
if (!type)
[NSException raise:NSInvalidArgumentException format:@"Can not encode objects of class type '%@'", static_cast<NSString *>(NSStringFromClass([object class]))];
encoder << *type;
switch (type.value()) {
case ObjCType::Null:
return;
case ObjCType::NSArray: {
NSArray *array = object;
encoder << static_cast<uint64_t>(array.count);
for (id element in array)
encode(encoder, element);
return;
}
case ObjCType::NSData: {
IPC::encode(encoder, static_cast<NSData *>(object));
return;
}
case ObjCType::NSDate: {
IPC::encode(encoder, static_cast<NSDate *>(object));
return;
}
case ObjCType::NSDictionary: {
NSDictionary *dictionary = object;
encoder << static_cast<uint64_t>(dictionary.count);
[dictionary enumerateKeysAndObjectsUsingBlock:[&encoder](id key, id object, BOOL *stop) {
encode(encoder, key);
encode(encoder, object);
}];
return;
}
case ObjCType::NSNumber: {
IPC::encode(encoder, static_cast<NSNumber *>(object));
return;
}
case ObjCType::NSString: {
IPC::encode(encoder, static_cast<NSString *>(object));
return;
}
case ObjCType::WKBrowsingContextHandle: {
encoder << static_cast<WKBrowsingContextHandle *>(object).pageProxyID;
encoder << static_cast<WKBrowsingContextHandle *>(object).webPageID;
return;
}
case ObjCType::WKTypeRefWrapper: {
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
UserData::encode(encoder, toImpl(static_cast<WKTypeRefWrapper *>(object).object));
ALLOW_DEPRECATED_DECLARATIONS_END
return;
}
}
ASSERT_NOT_REACHED();
}
void ObjCObjectGraph::encode(IPC::Encoder& encoder) const
{
encode(encoder, m_rootObject.get());
}
bool ObjCObjectGraph::decode(IPC::Decoder& decoder, RetainPtr<id>& result)
{
ObjCType type;
if (!decoder.decode(type))
return false;
switch (type) {
case ObjCType::Null: {
result = nil;
return true;
}
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<id> element;
if (!decode(decoder, element))
return false;
[array addObject:element.get()];
}
result = WTFMove(array);
return true;
}
case ObjCType::NSData: {
RetainPtr<NSData> data;
if (!IPC::decode(decoder, data))
return false;
result = WTFMove(data);
return true;
}
case ObjCType::NSDate: {
RetainPtr<NSDate> date;
if (!IPC::decode(decoder, date))
return false;
result = WTFMove(date);
return true;
}
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<id> key;
if (!decode(decoder, key))
return false;
RetainPtr<id> object;
if (!decode(decoder, object))
return false;
@try {
[dictionary setObject:object.get() forKey:key.get()];
} @catch (id) {
return false;
}
}
result = WTFMove(dictionary);
return true;
}
case ObjCType::NSNumber: {
RetainPtr<NSNumber> number;
if (!IPC::decode(decoder, number))
return false;
result = WTFMove(number);
return true;
}
case ObjCType::NSString: {
RetainPtr<NSString> string;
if (!IPC::decode(decoder, string))
return false;
result = WTFMove(string);
return true;
}
case ObjCType::WKBrowsingContextHandle: {
std::optional<WebPageProxyIdentifier> pageProxyID;
decoder >> pageProxyID;
if (!pageProxyID)
return false;
std::optional<WebCore::PageIdentifier> webPageID;
decoder >> webPageID;
if (!webPageID)
return false;
result = adoptNS([[WKBrowsingContextHandle alloc] _initWithPageProxyID:*pageProxyID andWebPageID:*webPageID]);
return true;
}
case ObjCType::WKTypeRefWrapper: {
RefPtr<API::Object> object;
if (!UserData::decode(decoder, object))
return false;
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
result = adoptNS([[WKTypeRefWrapper alloc] initWithObject:toAPI(object.get())]);
ALLOW_DEPRECATED_DECLARATIONS_END
return true;
}
}
ASSERT_NOT_REACHED();
return false;
}
bool ObjCObjectGraph::decode(IPC::Decoder& decoder, RefPtr<API::Object>& result)
{
RetainPtr<id> rootObject;
if (!decode(decoder, rootObject))
return false;
result = ObjCObjectGraph::create(rootObject.get());
return true;
}
} // namespace WebKit
namespace WTF {
template<> struct EnumTraits<WebKit::ObjCType> {
using values = EnumValues<
WebKit::ObjCType,
WebKit::ObjCType::Null,
WebKit::ObjCType::NSArray,
WebKit::ObjCType::NSData,
WebKit::ObjCType::NSDate,
WebKit::ObjCType::NSDictionary,
WebKit::ObjCType::NSNumber,
WebKit::ObjCType::NSString,
WebKit::ObjCType::WKBrowsingContextHandle,
WebKit::ObjCType::WKTypeRefWrapper
>;
};
} // namespace WTF