From ada83552066cbc075647591ecee2dfef91bfaf2c Mon Sep 17 00:00:00 2001 From: Yu-Teh Shen Date: Fri, 17 Apr 2015 22:19:30 +0800 Subject: [PATCH] Add JSON parsing support (issue #1188). --- cef.gyp | 1 + include/capi/cef_parser_capi.h | 20 ++++ include/cef_parser.h | 24 +++++ include/internal/cef_types.h | 64 +++++++++++ libcef/common/json_impl.cc | 78 ++++++++++++++ libcef/common/value_base.h | 9 ++ libcef/common/values_impl.cc | 70 ++++++++++++ libcef/common/values_impl.h | 46 +++++++- libcef_dll/libcef_dll.cc | 75 +++++++++++++ libcef_dll/wrapper/libcef_dll_wrapper.cc | 59 ++++++++++ tests/unittests/parser_unittest.cc | 131 +++++++++++++++++++++++ tools/cef_parser.py | 1 + 12 files changed, 577 insertions(+), 1 deletion(-) create mode 100644 libcef/common/json_impl.cc diff --git a/cef.gyp b/cef.gyp index 2c4958963..3a2dae871 100644 --- a/cef.gyp +++ b/cef.gyp @@ -1070,6 +1070,7 @@ 'libcef/common/drag_data_impl.h', 'libcef/common/http_header_utils.cc', 'libcef/common/http_header_utils.h', + 'libcef/common/json_impl.cc', 'libcef/common/main_delegate.cc', 'libcef/common/main_delegate.h', 'libcef/common/net_resource_provider.cc', diff --git a/include/capi/cef_parser_capi.h b/include/capi/cef_parser_capi.h index e6af4e949..4a529bec8 100644 --- a/include/capi/cef_parser_capi.h +++ b/include/capi/cef_parser_capi.h @@ -122,6 +122,26 @@ CEF_EXPORT cef_string_userfree_t cef_uridecode(const cef_string_t* text, CEF_EXPORT int cef_parse_csscolor(const cef_string_t* string, int strict, cef_color_t* color); +// Parses the specified |json_string| and returns a dictionary or list +// representation. If JSON parsing fails this function returns NULL. +CEF_EXPORT struct _cef_value_t* cef_parse_json(const cef_string_t* json_string, + cef_json_parser_options_t options); + +// Parses the specified |json_string| and returns a dictionary or list +// representation. If JSON parsing fails this function returns NULL and +// populates |error_code_out| and |error_msg_out| with an error code and a +// formatted error message respectively. +CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error( + const cef_string_t* json_string, cef_json_parser_options_t options, + cef_json_parser_error_t* error_code_out, cef_string_t* error_msg_out); + +// Generates a JSON string from the specified root |node| which should be a +// dictionary or list value. Returns an NULL string on failure. This function +// requires exclusive access to |node| including any underlying data. +// The resulting string must be freed by calling cef_string_userfree_free(). +CEF_EXPORT cef_string_userfree_t cef_write_json(struct _cef_value_t* node, + cef_json_writer_options_t options); + #ifdef __cplusplus } #endif diff --git a/include/cef_parser.h b/include/cef_parser.h index 92f46c6ab..f6812d1e8 100644 --- a/include/cef_parser.h +++ b/include/cef_parser.h @@ -123,4 +123,28 @@ bool CefParseCSSColor(const CefString& string, bool strict, cef_color_t& color); +// Parses the specified |json_string| and returns a dictionary or list +// representation. If JSON parsing fails this method returns NULL. +/*--cef()--*/ +CefRefPtr CefParseJSON(const CefString& json_string, + cef_json_parser_options_t options); + +// Parses the specified |json_string| and returns a dictionary or list +// representation. If JSON parsing fails this method returns NULL and populates +// |error_code_out| and |error_msg_out| with an error code and a formatted error +// message respectively. +/*--cef()--*/ +CefRefPtr CefParseJSONAndReturnError( + const CefString& json_string, + cef_json_parser_options_t options, + cef_json_parser_error_t& error_code_out, + CefString& error_msg_out); + +// Generates a JSON string from the specified root |node| which should be a +// dictionary or list value. Returns an empty string on failure. This method +// requires exclusive access to |node| including any underlying data. +/*--cef()--*/ +CefString CefWriteJSON(CefRefPtr node, + cef_json_writer_options_t options); + #endif // CEF_INCLUDE_CEF_PARSER_H_ diff --git a/include/internal/cef_types.h b/include/internal/cef_types.h index c326c924c..d5ee105d8 100644 --- a/include/internal/cef_types.h +++ b/include/internal/cef_types.h @@ -2128,6 +2128,70 @@ typedef enum { UU_REPLACE_PLUS_WITH_SPACE = 16, } cef_uri_unescape_rule_t; +/// +// Options that can be passed to CefParseJSON. +/// +typedef enum { + /// + // Parses the input strictly according to RFC 4627. See comments in Chromium's + // base/json/json_reader.h file for known limitations/deviations from the RFC. + /// + JSON_PARSER_RFC = 0, + + /// + // Allows commas to exist after the last element in structures. + /// + JSON_PARSER_ALLOW_TRAILING_COMMAS = 1 << 0, +} cef_json_parser_options_t; + +/// +// Error codes that can be returned from CefParseJSONAndReturnError. +/// +typedef enum { + JSON_NO_ERROR = 0, + JSON_INVALID_ESCAPE, + JSON_SYNTAX_ERROR, + JSON_UNEXPECTED_TOKEN, + JSON_TRAILING_COMMA, + JSON_TOO_MUCH_NESTING, + JSON_UNEXPECTED_DATA_AFTER_ROOT, + JSON_UNSUPPORTED_ENCODING, + JSON_UNQUOTED_DICTIONARY_KEY, + JSON_PARSE_ERROR_COUNT +} cef_json_parser_error_t; + +/// +// Options that can be passed to CefWriteJSON. +/// +typedef enum { + /// + // Default behavior. + /// + JSON_WRITER_DEFAULT = 0, + + /// + // This option instructs the writer that if a Binary value is encountered, + // the value (and key if within a dictionary) will be omitted from the + // output, and success will be returned. Otherwise, if a binary value is + // encountered, failure will be returned. + /// + JSON_WRITER_OMIT_BINARY_VALUES = 1 << 0, + + /// + // This option instructs the writer to write doubles that have no fractional + // part as a normal integer (i.e., without using exponential notation + // or appending a '.0') as long as the value is within the range of a + // 64-bit int. + /// + JSON_WRITER_OMIT_DOUBLE_TYPE_PRESERVATION = 1 << 1, + + /// + // Return a slightly nicer formatted json string (pads with whitespace to + // help with readability). + /// + JSON_WRITER_PRETTY_PRINT = 1 << 2, +} cef_json_writer_options_t; + #ifdef __cplusplus } #endif diff --git a/libcef/common/json_impl.cc b/libcef/common/json_impl.cc new file mode 100644 index 000000000..297c8bb96 --- /dev/null +++ b/libcef/common/json_impl.cc @@ -0,0 +1,78 @@ +// Copyright (c) 2015 The Chromium Embedded Framework Authors. All rights +// reserved. Use of this source code is governed by a BSD-style license that can +// be found in the LICENSE file. + +#include "include/cef_parser.h" +#include "libcef/common/values_impl.h" + +#include "base/values.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" + +namespace { + +int GetJSONReaderOptions(cef_json_parser_options_t options) { + int op = base::JSON_PARSE_RFC; + if (options & JSON_PARSER_ALLOW_TRAILING_COMMAS) + op |= base::JSON_ALLOW_TRAILING_COMMAS; + return op; +} + +int GetJSONWriterOptions(cef_json_writer_options_t options) { + int op = 0; + if (op & JSON_WRITER_OMIT_BINARY_VALUES) + op |= base::JSONWriter::OPTIONS_OMIT_BINARY_VALUES; + if (op & JSON_WRITER_OMIT_DOUBLE_TYPE_PRESERVATION) + op |= base::JSONWriter::OPTIONS_OMIT_DOUBLE_TYPE_PRESERVATION; + if (op & JSON_WRITER_PRETTY_PRINT) + op |= base::JSONWriter::OPTIONS_PRETTY_PRINT; + return op; +} + +} // namespace + +CefRefPtr CefParseJSON(const CefString& json_string, + cef_json_parser_options_t options) { + const std::string& json = json_string.ToString(); + base::Value* parse_result = + base::JSONReader::Read(json, GetJSONReaderOptions(options)); + if (parse_result) + return new CefValueImpl(parse_result); + return NULL; +} + +CefRefPtr CefParseJSONAndReturnError( + const CefString& json_string, + cef_json_parser_options_t options, + cef_json_parser_error_t& error_code_out, + CefString& error_msg_out) { + const std::string& json = json_string.ToString(); + + int error_code; + std::string error_msg; + base::Value* parse_result = base::JSONReader::ReadAndReturnError( + json, GetJSONReaderOptions(options), &error_code, &error_msg); + if (parse_result) + return new CefValueImpl(parse_result); + + error_code_out = static_cast(error_code); + error_msg_out = error_msg; + return NULL; +} + +CefString CefWriteJSON(CefRefPtr node, + cef_json_writer_options_t options) { + if (!node.get() || !node->IsValid()) + return CefString(); + + CefValueImpl* impl = static_cast(node.get()); + CefValueImpl::ScopedLockedValue scoped_value(impl); + + std::string json_string; + if (base::JSONWriter::WriteWithOptions(scoped_value.value(), + GetJSONWriterOptions(options), + &json_string)) { + return json_string; + } + return CefString(); +} diff --git a/libcef/common/value_base.h b/libcef/common/value_base.h index d9483ee21..5eaf2b93d 100644 --- a/libcef/common/value_base.h +++ b/libcef/common/value_base.h @@ -71,6 +71,9 @@ class CefValueController // Returns true if the controller is locked on the current thread. virtual bool locked() =0; + // Assert that the lock has been acquired. + virtual void AssertLockAcquired() =0; + // Verify that the current thread is correct for accessing the controller. inline bool VerifyThread() { if (!thread_safe() && !on_correct_thread()) { @@ -149,6 +152,9 @@ class CefValueControllerThreadSafe : public CefValueController { bool locked() override { return (locked_thread_id_ == base::PlatformThread::CurrentId()); } + void AssertLockAcquired() override { + lock_.AssertAcquired(); + } private: base::Lock lock_; @@ -171,6 +177,9 @@ class CefValueControllerNonThreadSafe : public CefValueController { void lock() override {} void unlock() override {} bool locked() override { return on_correct_thread(); } + void AssertLockAcquired() override { + DCHECK(locked()); + } private: base::PlatformThreadId thread_id_; diff --git a/libcef/common/values_impl.cc b/libcef/common/values_impl.cc index 6964c0bd9..c871c5c30 100644 --- a/libcef/common/values_impl.cc +++ b/libcef/common/values_impl.cc @@ -363,6 +363,55 @@ void CefValueImpl::SetValueInternal(base::Value* value) { } } +CefValueController* CefValueImpl::GetValueController() const { + lock_.AssertAcquired(); + + if (binary_value_) { + return static_cast(binary_value_.get())->controller(); + } else if (dictionary_value_) { + return static_cast(dictionary_value_.get())-> + controller(); + } else if (list_value_) { + return static_cast(list_value_.get())->controller(); + } + + return NULL; +} + +void CefValueImpl::AcquireLock() { + lock_.Acquire(); + + CefValueController* controller = GetValueController(); + if (controller) + controller->lock(); +} + +void CefValueImpl::ReleaseLock() { + CefValueController* controller = GetValueController(); + if (controller) { + controller->AssertLockAcquired(); + controller->unlock(); + } + + lock_.Release(); +} + +base::Value* CefValueImpl::GetValueUnsafe() const { + lock_.AssertAcquired(); + + if (binary_value_) { + return static_cast(binary_value_.get())-> + GetValueUnsafe(); + } else if (dictionary_value_) { + return static_cast(dictionary_value_.get())-> + GetValueUnsafe(); + } else if (list_value_) { + return static_cast(list_value_.get())->GetValueUnsafe(); + } + + return value_.get(); +} + // CefBinaryValueImpl implementation. @@ -441,6 +490,13 @@ bool CefBinaryValueImpl::IsEqualValue(const base::BinaryValue* that) { return const_value().Equals(that); } +base::BinaryValue* CefBinaryValueImpl::GetValueUnsafe() { + if (!VerifyAttached()) + return NULL; + controller()->AssertLockAcquired(); + return const_cast(&const_value()); +} + bool CefBinaryValueImpl::IsValid() { return !detached(); } @@ -572,6 +628,13 @@ bool CefDictionaryValueImpl::IsEqualValue(const base::DictionaryValue* that) { return const_value().Equals(that); } +base::DictionaryValue* CefDictionaryValueImpl::GetValueUnsafe() { + if (!VerifyAttached()) + return NULL; + controller()->AssertLockAcquired(); + return const_cast(&const_value()); +} + bool CefDictionaryValueImpl::IsValid() { return !detached(); } @@ -979,6 +1042,13 @@ bool CefListValueImpl::IsEqualValue(const base::ListValue* that) { return const_value().Equals(that); } +base::ListValue* CefListValueImpl::GetValueUnsafe() { + if (!VerifyAttached()) + return NULL; + controller()->AssertLockAcquired(); + return const_cast(&const_value()); +} + bool CefListValueImpl::IsValid() { return !detached(); } diff --git a/libcef/common/values_impl.h b/libcef/common/values_impl.h index db600b33b..3380ba8cb 100644 --- a/libcef/common/values_impl.h +++ b/libcef/common/values_impl.h @@ -73,9 +73,41 @@ class CefValueImpl : public CefValue { bool SetDictionary(CefRefPtr value) override; bool SetList(CefRefPtr value) override; + // Ensures exclusive access to the underlying data for the life of this scoped + // object. + class ScopedLockedValue { + public: + explicit ScopedLockedValue(CefRefPtr impl) + : impl_(impl) { + impl_->AcquireLock(); + } + ~ScopedLockedValue() { + impl_->ReleaseLock(); + } + + base::Value* value() const { + return impl_->GetValueUnsafe(); + } + + private: + CefRefPtr impl_; + DISALLOW_COPY_AND_ASSIGN(ScopedLockedValue); + }; + private: void SetValueInternal(base::Value* value); + // Returns the controller for the current value, if any. + CefValueController* GetValueController() const; + + // Explicitly lock/unlock this object and the underlying data. + void AcquireLock(); + void ReleaseLock(); + + // Returns a reference to the underlying data. Access must be protected by + // calling AcquireLock/ReleaseLock. + base::Value* GetValueUnsafe() const; + // Access to all members must be protected by |lock_|. base::Lock lock_; @@ -127,6 +159,10 @@ class CefBinaryValueImpl bool IsSameValue(const base::BinaryValue* that); bool IsEqualValue(const base::BinaryValue* that); + // Returns the underlying value. Access must be protected by calling + // lock/unlock on the controller. + base::BinaryValue* GetValueUnsafe(); + // CefBinaryValue methods. bool IsValid() override; bool IsOwned() override; @@ -181,6 +217,10 @@ class CefDictionaryValueImpl bool IsSameValue(const base::DictionaryValue* that); bool IsEqualValue(const base::DictionaryValue* that); + // Returns the underlying value. Access must be protected by calling + // lock/unlock on the controller. + base::DictionaryValue* GetValueUnsafe(); + // CefDictionaryValue methods. bool IsValid() override; bool IsOwned() override; @@ -264,7 +304,11 @@ class CefListValueImpl bool IsSameValue(const base::ListValue* that); bool IsEqualValue(const base::ListValue* that); - /// CefListValue methods. + // Returns the underlying value. Access must be protected by calling + // lock/unlock on the controller. + base::ListValue* GetValueUnsafe(); + + // CefListValue methods. bool IsValid() override; bool IsOwned() override; bool IsReadOnly() override; diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index ae208ae64..c0eab67b2 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -594,6 +594,81 @@ CEF_EXPORT int cef_parse_csscolor(const cef_string_t* string, int strict, return _retval; } +CEF_EXPORT struct _cef_value_t* cef_parse_json(const cef_string_t* json_string, + cef_json_parser_options_t options) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: json_string; type: string_byref_const + DCHECK(json_string); + if (!json_string) + return NULL; + + // Execute + CefRefPtr _retval = CefParseJSON( + CefString(json_string), + options); + + // Return type: refptr_same + return CefValueCppToC::Wrap(_retval); +} + +CEF_EXPORT struct _cef_value_t* cef_parse_jsonand_return_error( + const cef_string_t* json_string, cef_json_parser_options_t options, + cef_json_parser_error_t* error_code_out, cef_string_t* error_msg_out) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: json_string; type: string_byref_const + DCHECK(json_string); + if (!json_string) + return NULL; + // Verify param: error_code_out; type: simple_byref + DCHECK(error_code_out); + if (!error_code_out) + return NULL; + // Verify param: error_msg_out; type: string_byref + DCHECK(error_msg_out); + if (!error_msg_out) + return NULL; + + // Translate param: error_code_out; type: simple_byref + cef_json_parser_error_t error_code_outVal = + error_code_out?*error_code_out:JSON_NO_ERROR; + // Translate param: error_msg_out; type: string_byref + CefString error_msg_outStr(error_msg_out); + + // Execute + CefRefPtr _retval = CefParseJSONAndReturnError( + CefString(json_string), + options, + error_code_outVal, + error_msg_outStr); + + // Restore param: error_code_out; type: simple_byref + if (error_code_out) + *error_code_out = error_code_outVal; + + // Return type: refptr_same + return CefValueCppToC::Wrap(_retval); +} + +CEF_EXPORT cef_string_userfree_t cef_write_json(struct _cef_value_t* node, + cef_json_writer_options_t options) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: node; type: refptr_same + DCHECK(node); + if (!node) + return NULL; + + // Execute + CefString _retval = CefWriteJSON( + CefValueCppToC::Unwrap(node), + options); + + // Return type: string + return _retval.DetachToUserFree(); +} + CEF_EXPORT int cef_get_path(cef_path_key_t key, cef_string_t* path) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index e88031b1c..3d7006d53 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -549,6 +549,65 @@ CEF_GLOBAL bool CefParseCSSColor(const CefString& string, bool strict, return _retval?true:false; } +CEF_GLOBAL CefRefPtr CefParseJSON(const CefString& json_string, + cef_json_parser_options_t options) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: json_string; type: string_byref_const + DCHECK(!json_string.empty()); + if (json_string.empty()) + return NULL; + + // Execute + cef_value_t* _retval = cef_parse_json( + json_string.GetStruct(), + options); + + // Return type: refptr_same + return CefValueCToCpp::Wrap(_retval); +} + +CEF_GLOBAL CefRefPtr CefParseJSONAndReturnError( + const CefString& json_string, cef_json_parser_options_t options, + cef_json_parser_error_t& error_code_out, CefString& error_msg_out) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: json_string; type: string_byref_const + DCHECK(!json_string.empty()); + if (json_string.empty()) + return NULL; + + // Execute + cef_value_t* _retval = cef_parse_jsonand_return_error( + json_string.GetStruct(), + options, + &error_code_out, + error_msg_out.GetWritableStruct()); + + // Return type: refptr_same + return CefValueCToCpp::Wrap(_retval); +} + +CEF_GLOBAL CefString CefWriteJSON(CefRefPtr node, + cef_json_writer_options_t options) { + // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING + + // Verify param: node; type: refptr_same + DCHECK(node.get()); + if (!node.get()) + return CefString(); + + // Execute + cef_string_userfree_t _retval = cef_write_json( + CefValueCToCpp::Unwrap(node), + options); + + // Return type: string + CefString _retvalStr; + _retvalStr.AttachToUserFree(_retval); + return _retvalStr; +} + CEF_GLOBAL bool CefGetPath(PathKey key, CefString& path) { // AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING diff --git a/tests/unittests/parser_unittest.cc b/tests/unittests/parser_unittest.cc index 951c6bf59..ef7e1c3ea 100644 --- a/tests/unittests/parser_unittest.cc +++ b/tests/unittests/parser_unittest.cc @@ -309,3 +309,134 @@ TEST(ParserTest, ParseCSSColor) { EXPECT_FALSE(CefParseCSSColor(value, false, color)); EXPECT_EQ(0U, color); } + + +TEST(ParserTest, ParseJSONInvalid) { + const char data[] = "This is my test data"; + CefRefPtr value = CefParseJSON(data, JSON_PARSER_RFC); + EXPECT_FALSE(value.get()); +} + +TEST(ParserTest, ParseJSONNull) { + const char data[] = "{\"key1\":null}"; + CefRefPtr value = CefParseJSON(data, JSON_PARSER_RFC); + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->IsValid()); + EXPECT_TRUE(value->GetType() == VTYPE_DICTIONARY); + EXPECT_FALSE(value->IsOwned()); + CefRefPtr dict = value->GetDictionary(); + CefDictionaryValue::KeyList key_list; + EXPECT_TRUE(dict->GetKeys(key_list)); + EXPECT_EQ((size_t)1, key_list.size()); + EXPECT_EQ("key1", key_list[0].ToString()); + EXPECT_EQ(VTYPE_NULL, dict->GetType("key1")); + + // generate string from parsed result + CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT); + CefString expected_result = data; + EXPECT_EQ(expected_result, result); +} + +TEST(ParserTest, WriteJSONBinary) { + const char data[] = "\00\01\02"; + CefRefPtr dict = CefDictionaryValue::Create(); + CefRefPtr binary = CefBinaryValue::Create(data, sizeof(data)); + dict->SetBinary("key1", binary); + CefRefPtr node = CefValue::Create(); + node->SetDictionary(dict); + CefString result = CefWriteJSON(node, JSON_WRITER_DEFAULT); + CefString expect_result = ""; + // binary data will be omitted. + EXPECT_EQ(expect_result, result); +} + +TEST(ParserTest, ParseJSONDictionary) { + const char data[] = "{\"key1\":\"value1\",\"key2\":123,\"key3\":[1,2,3]}"; + CefRefPtr value = CefParseJSON(data, JSON_PARSER_RFC); + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->IsValid()); + EXPECT_FALSE(value->IsOwned()); + EXPECT_TRUE(value->GetType() == VTYPE_DICTIONARY); + CefRefPtr dict = value->GetDictionary(); + CefDictionaryValue::KeyList key_list; + EXPECT_TRUE(dict->GetKeys(key_list)); + EXPECT_EQ((size_t)3, key_list.size()); + EXPECT_EQ("key1", key_list[0].ToString()); + EXPECT_EQ("key2", key_list[1].ToString()); + EXPECT_EQ("key3", key_list[2].ToString()); + EXPECT_EQ(VTYPE_STRING, dict->GetType("key1")); + EXPECT_EQ(dict->GetString("key1"), "value1"); + EXPECT_EQ(VTYPE_INT, dict->GetType("key2")); + EXPECT_EQ(123, dict->GetInt("key2")); + EXPECT_EQ(VTYPE_LIST, dict->GetType("key3")); + CefRefPtr key3 = dict->GetList("key3"); + EXPECT_TRUE(NULL != key3); + EXPECT_TRUE(key3->IsValid()); + EXPECT_EQ((size_t)3, key3->GetSize()); + EXPECT_EQ(1, key3->GetInt(0)); + EXPECT_EQ(2, key3->GetInt(1)); + EXPECT_EQ(3, key3->GetInt(2)); + + // generate string from parsed result + CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT); + CefString expected_result = data; + EXPECT_EQ(expected_result, result); +} + +TEST(ParserTest, ParseJSONList) { + const char data[] = "[\"value1\", 123, {\"key3\": [1, 2, 3]}]"; + CefRefPtr value = CefParseJSON(data, JSON_PARSER_RFC); + EXPECT_TRUE(value.get()); + EXPECT_TRUE(value->IsValid()); + EXPECT_TRUE(value->GetType() == VTYPE_LIST); + EXPECT_FALSE(value->IsOwned()); + CefRefPtr list = value->GetList(); + EXPECT_TRUE(NULL != list); + EXPECT_TRUE(list->IsValid()); + EXPECT_EQ((size_t)3, list->GetSize()); + + EXPECT_EQ(VTYPE_STRING, list->GetType(0)); + EXPECT_EQ(list->GetString(0), "value1"); + EXPECT_EQ(VTYPE_INT, list->GetType(1)); + EXPECT_EQ(123, list->GetInt(1)); + EXPECT_EQ(VTYPE_DICTIONARY, list->GetType(2)); + CefRefPtr dict = list->GetDictionary(2); + CefDictionaryValue::KeyList key_list2; + EXPECT_TRUE(dict->GetKeys(key_list2)); + EXPECT_EQ((size_t)1, key_list2.size()); + CefRefPtr list2 = dict->GetList("key3"); + EXPECT_EQ((size_t)3, list2->GetSize()); + EXPECT_EQ(1, list2->GetInt(0)); + EXPECT_EQ(2, list2->GetInt(1)); + EXPECT_EQ(3, list2->GetInt(2)); + + // generate string from parsed result + CefString result = CefWriteJSON(value, JSON_WRITER_DEFAULT); + CefString expected_result = "[\"value1\",123,{\"key3\":[1,2,3]}]"; + EXPECT_EQ(expected_result.ToString(), result.ToString()); +} + +TEST(ParserTest, ParseJSONAndReturnErrorInvalid) { + const char data[] = "This is my test data"; + cef_json_parser_error_t error_code; + CefString error_msg; + CefRefPtr value = CefParseJSONAndReturnError(data, + JSON_PARSER_RFC, error_code, error_msg); + CefString expect_error_msg = "Line: 1, column: 1, Unexpected token."; + EXPECT_FALSE(value.get()); + EXPECT_EQ(JSON_UNEXPECTED_TOKEN, error_code); + EXPECT_EQ(expect_error_msg, error_msg); +} + +TEST(ParserTest, ParseJSONAndReturnErrorTrailingComma) { + const char data[] = "{\"key1\":123,}"; + cef_json_parser_error_t error_code; + CefString error_msg; + CefRefPtr value = CefParseJSONAndReturnError(data, + JSON_PARSER_RFC, error_code, error_msg); + CefString expect_error_msg = + "Line: 1, column: 13, Trailing comma not allowed."; + EXPECT_FALSE(value.get()); + EXPECT_EQ(JSON_TRAILING_COMMA, error_code); + EXPECT_EQ(expect_error_msg, error_msg); +} diff --git a/tools/cef_parser.py b/tools/cef_parser.py index 6eb0f6f7b..b91467a3a 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -378,6 +378,7 @@ _simpletypes = { 'char': ['char', '0'], 'char* const': ['char* const', 'NULL'], 'cef_color_t': ['cef_color_t', '0'], + 'cef_json_parser_error_t': ['cef_json_parser_error_t', 'JSON_NO_ERROR'], 'CefCursorHandle' : ['cef_cursor_handle_t', 'kNullCursorHandle'], 'CefEventHandle' : ['cef_event_handle_t', 'kNullEventHandle'], 'CefWindowHandle' : ['cef_window_handle_t', 'kNullWindowHandle'],