// Engine/Source/Runtime/WebBrowser/Private/CEF/CEFJSStructDeserializerBackend.cpp #include "CEF/CEFInterfaceJSStructDeserializerBackend.h" #if WITH_CEF3 #include "UObject/EnumProperty.h" #include "UObject/TextProperty.h" #include "WebInterfaceJSFunction.h" /* Internal helpers *****************************************************************************/ namespace { template ValueType GetNumeric(CefRefPtr Container, KeyType Key) { switch(Container->GetType(Key)) { case VTYPE_BOOL: return static_cast(Container->GetBool(Key)); case VTYPE_INT: return static_cast(Container->GetInt(Key)); case VTYPE_DOUBLE: return static_cast(Container->GetDouble(Key)); case VTYPE_STRING: case VTYPE_DICTIONARY: case VTYPE_LIST: case VTYPE_NULL: case VTYPE_BINARY: default: return static_cast(0); } } template void AssignTokenFromContainer(ContainerType Container, KeyType Key, EStructDeserializerBackendTokens& OutToken, FString& PropertyName, TSharedPtr& Retval) { switch (Container->GetType(Key)) { case VTYPE_NULL: case VTYPE_BOOL: case VTYPE_INT: case VTYPE_DOUBLE: case VTYPE_STRING: OutToken = EStructDeserializerBackendTokens::Property; break; case VTYPE_DICTIONARY: { CefRefPtr Dictionary = Container->GetDictionary(Key); if (Dictionary->GetType("$type") == VTYPE_STRING ) { OutToken = EStructDeserializerBackendTokens::Property; } else { TSharedPtr NewWalker(new FCefInterfaceDictionaryValueWalker(Retval, Dictionary)); Retval = NewWalker->GetNextToken(OutToken, PropertyName); } break; } case VTYPE_LIST: { TSharedPtr NewWalker(new FCefInterfaceListValueWalker(Retval, Container->GetList(Key))); Retval = NewWalker->GetNextToken(OutToken, PropertyName); break; } case VTYPE_BINARY: case VTYPE_INVALID: default: OutToken = EStructDeserializerBackendTokens::Error; break; } } /** * Gets a pointer to object of the given property. * * @param Property The property to get. * @param Outer The property that contains the property to be get, if any. * @param Data A pointer to the memory holding the property's data. * @param ArrayIndex The index of the element to set (if the property is an array). * @return A pointer to the object represented by the property, null otherwise.. * @see ClearPropertyValue */ void* GetPropertyValuePtr( FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex ) { check(Property); if (FArrayProperty* ArrayProperty = CastField(Outer)) { if (ArrayProperty->Inner != Property) { return nullptr; } FScriptArrayHelper ArrayHelper(ArrayProperty, ArrayProperty->template ContainerPtrToValuePtr(Data)); int32 Index = ArrayHelper.AddValue(); return ArrayHelper.GetRawPtr(Index); } if (ArrayIndex >= Property->ArrayDim) { return nullptr; } return Property->template ContainerPtrToValuePtr(Data, ArrayIndex); } /** * Sets the value of the given property. * * @param Property The property to set. * @param Outer The property that contains the property to be set, if any. * @param Data A pointer to the memory holding the property's data. * @param ArrayIndex The index of the element to set (if the property is an array). * @return true on success, false otherwise. * @see ClearPropertyValue */ template bool SetPropertyValue( PropertyType* Property, FProperty* Outer, void* Data, int32 ArrayIndex, const ValueType& Value ) { if (void* Ptr = GetPropertyValuePtr(Property, Outer, Data, ArrayIndex)) { *(ValueType*)Ptr = Value; return true; } return false; } template bool ReadNumericProperty(FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex, CefRefPtr Container, KeyType Key ) { typedef typename PropertyType::TCppType TCppType; if (PropertyType* TypedProperty = CastField(Property)) { return SetPropertyValue(TypedProperty, Outer, Data, ArrayIndex, GetNumeric(Container, Key)); } else { return false; } } template bool ReadBoolProperty(FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex, CefRefPtr Container, KeyType Key ) { if (FBoolProperty* BoolProperty = CastField(Property)) { return SetPropertyValue(BoolProperty, Outer, Data, ArrayIndex, GetNumeric(Container, Key)!=0); } return false; } template bool ReadJSFunctionProperty(TSharedPtr Scripting, FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex, CefRefPtr Container, KeyType Key ) { if (Container->GetType(Key) != VTYPE_DICTIONARY || !Property->IsA()) { return false; } CefRefPtr Dictionary = Container->GetDictionary(Key); FStructProperty* StructProperty = CastField(Property); if ( !StructProperty || StructProperty->Struct != FWebInterfaceJSFunction::StaticStruct()) { return false; } FGuid CallbackID; if (!FGuid::Parse(FString(WCHAR_TO_TCHAR(Dictionary->GetString("$id").ToWString().c_str())), CallbackID)) { // Invalid GUID return false; } FWebInterfaceJSFunction CallbackObject(Scripting, CallbackID); return SetPropertyValue(StructProperty, Outer, Data, ArrayIndex, CallbackObject); } template bool ReadStringProperty(FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex, CefRefPtr Container, KeyType Key ) { if (Container->GetType(Key) == VTYPE_STRING) { FString StringValue = WCHAR_TO_TCHAR(Container->GetString(Key).ToWString().c_str()); if (FStrProperty* StrProperty = CastField(Property)) { return SetPropertyValue(StrProperty, Outer, Data, ArrayIndex, StringValue); } if (FNameProperty* NameProperty = CastField(Property)) { return SetPropertyValue(NameProperty, Outer, Data, ArrayIndex, FName(*StringValue)); } if (FTextProperty* TextProperty = CastField(Property)) { return SetPropertyValue(TextProperty, Outer, Data, ArrayIndex, FText::FromString(StringValue)); } if (FByteProperty* ByteProperty = CastField(Property)) { if (!ByteProperty->Enum) { return false; } int32 Index = ByteProperty->Enum->GetIndexByNameString(StringValue); if (Index == INDEX_NONE) { return false; } return SetPropertyValue(ByteProperty, Outer, Data, ArrayIndex, (uint8)ByteProperty->Enum->GetValueByIndex(Index)); } if (FEnumProperty* EnumProperty = CastField(Property)) { int32 Index = EnumProperty->GetEnum()->GetIndexByNameString(StringValue); if (Index == INDEX_NONE) { return false; } if (void* ElementPtr = GetPropertyValuePtr(EnumProperty, Outer, Data, ArrayIndex)) { EnumProperty->GetUnderlyingProperty()->SetIntPropertyValue(ElementPtr, EnumProperty->GetEnum()->GetValueByIndex(Index)); return true; } return false; } } return false; } template bool ReadProperty(TSharedPtr Scripting, FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex, CefRefPtr Container, KeyType Key ) { return ReadBoolProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadStringProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadNumericProperty(Property, Outer, Data, ArrayIndex, Container, Key) || ReadJSFunctionProperty(Scripting, Property, Outer, Data, ArrayIndex, Container, Key); } } TSharedPtr FCefInterfaceListValueWalker::GetNextToken(EStructDeserializerBackendTokens& OutToken, FString& PropertyName) { TSharedPtr Retval = SharedThis(this); Index++; if (Index == -1) { OutToken = EStructDeserializerBackendTokens::ArrayStart; } else if ( Index < List->GetSize() ) { AssignTokenFromContainer(List, Index, OutToken, PropertyName, Retval); PropertyName = FString(); } else { OutToken = EStructDeserializerBackendTokens::ArrayEnd; Retval = Parent; } return Retval; } bool FCefInterfaceListValueWalker::ReadProperty(TSharedPtr Scripting, FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex) { return ::ReadProperty(Scripting, Property, Outer, Data, ArrayIndex, List, Index); } TSharedPtr FCefInterfaceDictionaryValueWalker::GetNextToken(EStructDeserializerBackendTokens& OutToken, FString& PropertyName) { TSharedPtr Retval = SharedThis(this); Index++; if (Index == -1) { OutToken = EStructDeserializerBackendTokens::StructureStart; } else if ( Index < Keys.size() ) { AssignTokenFromContainer(Dictionary, Keys[Index], OutToken, PropertyName, Retval); PropertyName = WCHAR_TO_TCHAR(Keys[Index].ToWString().c_str()); } else { OutToken = EStructDeserializerBackendTokens::StructureEnd; Retval = Parent; } return Retval; } bool FCefInterfaceDictionaryValueWalker::ReadProperty(TSharedPtr Scripting, FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex) { return ::ReadProperty(Scripting, Property, Outer, Data, ArrayIndex, Dictionary, Keys[Index]); } /* IStructDeserializerBackend interface *****************************************************************************/ const FString& FCEFInterfaceJSStructDeserializerBackend::GetCurrentPropertyName() const { return CurrentPropertyName; } FString FCEFInterfaceJSStructDeserializerBackend::GetDebugString() const { return CurrentPropertyName; } const FString& FCEFInterfaceJSStructDeserializerBackend::GetLastErrorMessage() const { return CurrentPropertyName; } bool FCEFInterfaceJSStructDeserializerBackend::GetNextToken( EStructDeserializerBackendTokens& OutToken ) { if (Walker.IsValid()) { Walker = Walker->GetNextToken(OutToken, CurrentPropertyName); return true; } else { return false; } } bool FCEFInterfaceJSStructDeserializerBackend::ReadProperty( FProperty* Property, FProperty* Outer, void* Data, int32 ArrayIndex ) { return Walker->ReadProperty(Scripting, Property, Outer, Data, ArrayIndex); } void FCEFInterfaceJSStructDeserializerBackend::SkipArray() { EStructDeserializerBackendTokens Token; int32 depth = 1; while (GetNextToken(Token) && depth > 0) { switch (Token) { case EStructDeserializerBackendTokens::ArrayEnd: depth --; break; case EStructDeserializerBackendTokens::ArrayStart: depth ++; break; default: break; } } } void FCEFInterfaceJSStructDeserializerBackend::SkipStructure() { EStructDeserializerBackendTokens Token; int32 depth = 1; while (GetNextToken(Token) && depth > 0) { switch (Token) { case EStructDeserializerBackendTokens::StructureEnd: depth --; break; case EStructDeserializerBackendTokens::StructureStart: depth ++; break; default: break; } } } #endif