Commit 65d50ad9 authored by jsbell@chromium.org's avatar jsbell@chromium.org

Add ByteString support to IDL bindings

ByteString is like DOMString, but should throw TypeError 
during the V8->C++ hop if script tries to set a string with a 
code unit greater than 0x00FF. No enforcement is done on the 
C++->V8 side.

R=nbarth@chromium.org
BUG=347426

Review URL: https://codereview.chromium.org/309553002

git-svn-id: svn://svn.chromium.org/blink/trunk@175995 bbb929c8-8fbe-4397-9dbb-9b2b20218538
parent b54a99d9
......@@ -1018,6 +1018,26 @@ PASS converter.testEnforceRangeUnsignedShort is 0
PASS converter.testEnforceRangeUnsignedShort = undefined threw exception TypeError: Failed to set the 'testEnforceRangeUnsignedShort' property on 'TypeConversions': Value is not of type 'unsigned short'..
PASS converter.testEnforceRangeUnsignedShort = {valueOf:function(){throw new Error('custom');}} threw exception Error: custom.
converter.testByteString = '!@#123ABCabc\x00\x80\xFF\r\n\t'
PASS converter.testByteString is "!@#123ABCabc\u0000€ÿ\r\n\t"
PASS converter.testByteString = '\u0100' threw exception TypeError: Failed to set the 'testByteString' property on 'TypeConversions': Value is not a valid ByteString..
PASS converter.testByteString = {toString: function() { throw Error(); }} threw exception Error.
PASS converter.testByteString is "!@#123ABCabc\u0000€ÿ\r\n\t"
converter.testByteString = true
PASS converter.testByteString is "true"
converter.testByteString = 123
PASS converter.testByteString is "123"
converter.testByteString = null
PASS converter.testByteString is "null"
converter.testByteString = undefined
PASS converter.testByteString is "undefined"
PASS converter.setTestByteString('abc') did not throw exception.
PASS converter.setTestByteStringDefaultNull('abc') did not throw exception.
PASS converter.setTestByteString('\u0100') threw exception TypeError: Failed to execute 'setTestByteString' on 'TypeConversions': Value is not a valid ByteString..
PASS converter.setTestByteStringDefaultNull('\u0100') threw exception TypeError: Failed to execute 'setTestByteStringDefaultNull' on 'TypeConversions': Value is not a valid ByteString..
PASS converter.setTestByteString() threw exception TypeError: Failed to execute 'setTestByteString' on 'TypeConversions': 1 argument required, but only 0 present..
PASS converter.setTestByteStringDefaultNull() did not throw exception.
PASS converter.testByteString is ""
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -581,4 +581,21 @@ testNonNumericToNumericEnforceRange(type);
convertThrows(type, "{valueOf:function(){throw new Error('custom');}}");
debug("");
evalAndLog("converter.testByteString = '!@#123ABCabc\\x00\\x80\\xFF\\r\\n\\t'");
shouldBeEqualToString("converter.testByteString", "!@#123ABCabc\x00\x80\xFF\r\n\t");
shouldThrow("converter.testByteString = '\\u0100'");
shouldThrow("converter.testByteString = {toString: function() { throw Error(); }}");
shouldBeEqualToString("converter.testByteString", "!@#123ABCabc\x00\x80\xFF\r\n\t");
["true", "123", "null", "undefined"].forEach(function(value) {
evalAndLog("converter.testByteString = " + value);
shouldBeEqualToString("converter.testByteString", value);
});
shouldNotThrow("converter.setTestByteString('abc')");
shouldNotThrow("converter.setTestByteStringDefaultNull('abc')");
shouldThrow("converter.setTestByteString('\\u0100')");
shouldThrow("converter.setTestByteStringDefaultNull('\\u0100')");
shouldThrow("converter.setTestByteString()");
shouldNotThrow("converter.setTestByteStringDefaultNull()");
shouldBeEqualToString("converter.testByteString", "");
</script>
......@@ -38,10 +38,11 @@ NUMERIC_TYPES = (INTEGER_TYPES | frozenset([
PRIMITIVE_TYPES = (frozenset(['boolean']) | NUMERIC_TYPES)
BASIC_TYPES = (PRIMITIVE_TYPES | frozenset([
# Built-in, non-composite, non-object data types
# http://www.w3.org/TR/WebIDL/#idl-types
# http://heycam.github.io/webidl/#idl-types
'DOMString',
'ByteString',
'Date',
# http://www.w3.org/TR/WebIDL/#es-type-mapping
# http://heycam.github.io/webidl/#es-type-mapping
'void',
]))
TYPE_NAMES = {
......@@ -63,6 +64,7 @@ TYPE_NAMES = {
'DOMString': 'String',
'ByteString': 'ByteString',
'object': 'Object',
'Date': 'Date',
}
......
......@@ -108,7 +108,7 @@ def generate_attribute(interface, attribute):
'has_setter_exception_state':
is_setter_raises_exception or has_type_checking_interface or
has_type_checking_nullable or has_type_checking_unrestricted or
idl_type.is_integer_type,
idl_type.is_integer_type or idl_type.name == 'ByteString',
'has_type_checking_interface': has_type_checking_interface,
'has_type_checking_nullable': has_type_checking_nullable,
'has_type_checking_unrestricted': has_type_checking_unrestricted,
......
......@@ -119,7 +119,7 @@ def generate_method(interface, method):
is_raises_exception or
is_check_security_for_frame or
any(argument for argument in arguments
if argument.idl_type.name == 'SerializedScriptValue' or
if argument.idl_type.name in ('ByteString', 'SerializedScriptValue') or
argument.idl_type.is_integer_type),
'is_call_with_execution_context': has_extended_attribute_value(method, 'CallWith', 'ExecutionContext'),
'is_call_with_script_arguments': is_call_with_script_arguments,
......@@ -286,7 +286,7 @@ def v8_value_to_local_cpp_value(argument, index):
if argument.is_variadic:
return v8_value_to_local_cpp_variadic_value(argument, index)
# [Default=NullString]
if (argument.is_optional and idl_type.name == 'String' and
if (argument.is_optional and idl_type.name in ('String', 'ByteString') and
extended_attributes.get('Default') == 'NullString'):
v8_value = 'argumentOrNull(info, %s)' % index
else:
......
......@@ -167,7 +167,7 @@ def cpp_type(idl_type, extended_attributes=None, used_as_argument=False, used_as
if base_idl_type in NON_WRAPPER_TYPES:
return 'RefPtr<%s>' % base_idl_type
if base_idl_type == 'DOMString':
if base_idl_type in ('DOMString', 'ByteString'):
if not used_as_argument:
return 'String'
return 'V8StringResource<%s>' % string_mode()
......@@ -364,6 +364,7 @@ V8_VALUE_TO_CPP_VALUE = {
# Basic
'Date': 'toCoreDate({v8_value})',
'DOMString': '{v8_value}',
'ByteString': 'toByteString({arguments})',
'boolean': '{v8_value}->BooleanValue()',
'float': 'static_cast<float>({v8_value}->NumberValue())',
'unrestricted float': 'static_cast<float>({v8_value}->NumberValue())',
......@@ -404,7 +405,7 @@ def v8_value_to_cpp_value(idl_type, extended_attributes, v8_value, index):
if 'EnforceRange' in extended_attributes:
arguments = ', '.join([v8_value, 'EnforceRange', 'exceptionState'])
elif idl_type.is_integer_type: # NormalConversion
elif idl_type.is_integer_type or idl_type.name == 'ByteString': # NormalConversion
arguments = ', '.join([v8_value, 'exceptionState'])
else:
arguments = v8_value
......@@ -452,7 +453,7 @@ def v8_value_to_local_cpp_value(idl_type, extended_attributes, v8_value, variabl
args = [variable_name, cpp_value]
if idl_type.base_type == 'DOMString' and not idl_type.array_or_sequence_type:
macro = 'TOSTRING_VOID'
elif idl_type.is_integer_type:
elif idl_type.is_integer_type or idl_type.name == 'ByteString':
macro = 'TONATIVE_VOID_EXCEPTIONSTATE'
args.append('exceptionState')
else:
......@@ -563,6 +564,7 @@ V8_SET_RETURN_VALUE = {
'int': 'v8SetReturnValueInt(info, {cpp_value})',
'unsigned': 'v8SetReturnValueUnsigned(info, {cpp_value})',
'DOMString': 'v8SetReturnValueString(info, {cpp_value}, info.GetIsolate())',
'ByteString': 'v8SetReturnValueString(info, {cpp_value}, info.GetIsolate())',
# [TreatNullReturnValueAs]
'StringOrNull': 'v8SetReturnValueStringOrNull(info, {cpp_value}, info.GetIsolate())',
'StringOrUndefined': 'v8SetReturnValueStringOrUndefined(info, {cpp_value}, info.GetIsolate())',
......@@ -642,6 +644,7 @@ CPP_VALUE_TO_V8_VALUE = {
# Built-in types
'Date': 'v8DateOrNaN({cpp_value}, {isolate})',
'DOMString': 'v8String({isolate}, {cpp_value})',
'ByteString': 'v8String({isolate}, {cpp_value})',
'boolean': 'v8Boolean({cpp_value}, {isolate})',
'int': 'v8::Integer::New({isolate}, {cpp_value})',
'unsigned': 'v8::Integer::NewFromUnsigned({isolate}, {cpp_value})',
......
......@@ -87,6 +87,7 @@ interface TestObject {
// Basic types
attribute Date dateAttribute;
attribute DOMString stringAttribute;
attribute ByteString byteStringAttribute;
attribute DOMTimeStamp domTimeStampAttribute;
attribute boolean booleanAttribute;
attribute byte byteAttribute;
......@@ -261,6 +262,7 @@ interface TestObject {
// Basic types
Date dateMethod();
DOMString stringMethod();
ByteString byteStringMethod();
DOMTimeStamp readonlyDOMTimeStampMethod();
boolean booleanMethod();
byte byteMethod();
......@@ -276,6 +278,7 @@ interface TestObject {
void voidMethodDateArg(Date dateArg);
void voidMethodStringArg(DOMString stringArg);
void voidMethodByteStringArg(ByteString stringArg);
void voidMethodDOMTimeStampArg(DOMTimeStamp domTimeStampArg);
void voidMethodBooleanArg(boolean booleanArg);
void voidMethodByteArg(byte byteArg);
......
......@@ -190,6 +190,36 @@ static void stringAttributeAttributeSetterCallback(v8::Local<v8::String>, v8::Lo
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void byteStringAttributeAttributeGetter(const v8::PropertyCallbackInfo<v8::Value>& info)
{
v8::Handle<v8::Object> holder = info.Holder();
TestObject* impl = V8TestObject::toNative(holder);
v8SetReturnValueString(info, impl->byteStringAttribute(), info.GetIsolate());
}
static void byteStringAttributeAttributeGetterCallback(v8::Local<v8::String>, const v8::PropertyCallbackInfo<v8::Value>& info)
{
TRACE_EVENT_SET_SAMPLING_STATE("Blink", "DOMGetter");
TestObjectV8Internal::byteStringAttributeAttributeGetter(info);
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void byteStringAttributeAttributeSetter(v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<void>& info)
{
v8::Handle<v8::Object> holder = info.Holder();
ExceptionState exceptionState(ExceptionState::SetterContext, "byteStringAttribute", "TestObject", holder, info.GetIsolate());
TestObject* impl = V8TestObject::toNative(holder);
TONATIVE_VOID_EXCEPTIONSTATE(V8StringResource<>, cppValue, toByteString(v8Value, exceptionState), exceptionState);
impl->setByteStringAttribute(cppValue);
}
static void byteStringAttributeAttributeSetterCallback(v8::Local<v8::String>, v8::Local<v8::Value> v8Value, const v8::PropertyCallbackInfo<void>& info)
{
TRACE_EVENT_SET_SAMPLING_STATE("Blink", "DOMSetter");
TestObjectV8Internal::byteStringAttributeAttributeSetter(v8Value, info);
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void domTimeStampAttributeAttributeGetter(const v8::PropertyCallbackInfo<v8::Value>& info)
{
v8::Handle<v8::Object> holder = info.Holder();
......@@ -4636,6 +4666,19 @@ static void stringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void byteStringMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info)
{
TestObject* impl = V8TestObject::toNative(info.Holder());
v8SetReturnValueString(info, impl->byteStringMethod(), info.GetIsolate());
}
static void byteStringMethodMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
TRACE_EVENT_SET_SAMPLING_STATE("Blink", "DOMMethod");
TestObjectV8Internal::byteStringMethodMethod(info);
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void readonlyDOMTimeStampMethodMethod(const v8::FunctionCallbackInfo<v8::Value>& info)
{
TestObject* impl = V8TestObject::toNative(info.Holder());
......@@ -4836,6 +4879,30 @@ static void voidMethodStringArgMethodCallback(const v8::FunctionCallbackInfo<v8:
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void voidMethodByteStringArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info)
{
ExceptionState exceptionState(ExceptionState::ExecutionContext, "voidMethodByteStringArg", "TestObject", info.Holder(), info.GetIsolate());
if (UNLIKELY(info.Length() < 1)) {
throwMinimumArityTypeError(exceptionState, 1, info.Length());
return;
}
TestObject* impl = V8TestObject::toNative(info.Holder());
V8StringResource<> stringArg;
{
v8::TryCatch block;
V8RethrowTryCatchScope rethrow(block);
TONATIVE_VOID_EXCEPTIONSTATE_INTERNAL(stringArg, toByteString(info[0], exceptionState), exceptionState);
}
impl->voidMethodByteStringArg(stringArg);
}
static void voidMethodByteStringArgMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
{
TRACE_EVENT_SET_SAMPLING_STATE("Blink", "DOMMethod");
TestObjectV8Internal::voidMethodByteStringArgMethod(info);
TRACE_EVENT_SET_SAMPLING_STATE("V8", "V8Execution");
}
static void voidMethodDOMTimeStampArgMethod(const v8::FunctionCallbackInfo<v8::Value>& info)
{
ExceptionState exceptionState(ExceptionState::ExecutionContext, "voidMethodDOMTimeStampArg", "TestObject", info.Holder(), info.GetIsolate());
......@@ -8964,6 +9031,7 @@ static const V8DOMConfiguration::AttributeConfiguration V8TestObjectAttributes[]
{"readonlyLongAttribute", TestObjectV8Internal::readonlyLongAttributeAttributeGetterCallback, 0, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"dateAttribute", TestObjectV8Internal::dateAttributeAttributeGetterCallback, TestObjectV8Internal::dateAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"stringAttribute", TestObjectV8Internal::stringAttributeAttributeGetterCallback, TestObjectV8Internal::stringAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"byteStringAttribute", TestObjectV8Internal::byteStringAttributeAttributeGetterCallback, TestObjectV8Internal::byteStringAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"domTimeStampAttribute", TestObjectV8Internal::domTimeStampAttributeAttributeGetterCallback, TestObjectV8Internal::domTimeStampAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"booleanAttribute", TestObjectV8Internal::booleanAttributeAttributeGetterCallback, TestObjectV8Internal::booleanAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
{"byteAttribute", TestObjectV8Internal::byteAttributeAttributeGetterCallback, TestObjectV8Internal::byteAttributeAttributeSetterCallback, 0, 0, 0, static_cast<v8::AccessControl>(v8::DEFAULT), static_cast<v8::PropertyAttribute>(v8::None), 0 /* on instance */},
......@@ -9117,6 +9185,7 @@ static const V8DOMConfiguration::MethodConfiguration V8TestObjectMethods[] = {
{"voidMethod", TestObjectV8Internal::voidMethodMethodCallback, 0, 0},
{"dateMethod", TestObjectV8Internal::dateMethodMethodCallback, 0, 0},
{"stringMethod", TestObjectV8Internal::stringMethodMethodCallback, 0, 0},
{"byteStringMethod", TestObjectV8Internal::byteStringMethodMethodCallback, 0, 0},
{"readonlyDOMTimeStampMethod", TestObjectV8Internal::readonlyDOMTimeStampMethodMethodCallback, 0, 0},
{"booleanMethod", TestObjectV8Internal::booleanMethodMethodCallback, 0, 0},
{"byteMethod", TestObjectV8Internal::byteMethodMethodCallback, 0, 0},
......@@ -9131,6 +9200,7 @@ static const V8DOMConfiguration::MethodConfiguration V8TestObjectMethods[] = {
{"unsignedShortMethod", TestObjectV8Internal::unsignedShortMethodMethodCallback, 0, 0},
{"voidMethodDateArg", TestObjectV8Internal::voidMethodDateArgMethodCallback, 0, 1},
{"voidMethodStringArg", TestObjectV8Internal::voidMethodStringArgMethodCallback, 0, 1},
{"voidMethodByteStringArg", TestObjectV8Internal::voidMethodByteStringArgMethodCallback, 0, 1},
{"voidMethodDOMTimeStampArg", TestObjectV8Internal::voidMethodDOMTimeStampArgMethodCallback, 0, 1},
{"voidMethodBooleanArg", TestObjectV8Internal::voidMethodBooleanArgMethodCallback, 0, 1},
{"voidMethodByteArg", TestObjectV8Internal::voidMethodByteArgMethodCallback, 0, 1},
......
......@@ -492,6 +492,31 @@ float toFloat(v8::Handle<v8::Value> value, ExceptionState& exceptionState)
return numberObject->NumberValue();
}
String toByteString(v8::Handle<v8::Value> value, ExceptionState& exceptionState)
{
// Handle [Default=NullString]
if (value.IsEmpty())
return String();
// From the Web IDL spec: http://heycam.github.io/webidl/#es-ByteString
// 1. Let x be ToString(v)
TONATIVE_DEFAULT_EXCEPTIONSTATE(v8::Local<v8::String>, stringObject, value->ToString(), exceptionState, String());
String x = toCoreString(stringObject);
// 2. If the value of any element of x is greater than 255, then throw a TypeError.
if (!x.containsOnlyLatin1()) {
exceptionState.throwTypeError("Value is not a valid ByteString.");
return String();
}
// 3. Return an IDL ByteString value whose length is the length of x, and where the
// value of each element is the value of the corresponding element of x.
// Blink: A ByteString is simply a String with a range constrained per the above, so
// this is the identity operation.
return x;
}
PassRefPtrWillBeRawPtr<XPathNSResolver> toXPathNSResolver(v8::Handle<v8::Value> value, v8::Isolate* isolate)
{
RefPtrWillBeRawPtr<XPathNSResolver> resolver = nullptr;
......
......@@ -525,6 +525,9 @@ inline float toFloat(v8::Local<v8::Value> value)
return static_cast<float>(value->NumberValue());
}
// Converts a value to a String, throwing if any code unit is outside 0-255.
String toByteString(v8::Handle<v8::Value>, ExceptionState&);
inline v8::Handle<v8::Boolean> v8Boolean(bool value, v8::Isolate* isolate)
{
return value ? v8::True(isolate) : v8::False(isolate);
......
......@@ -185,6 +185,11 @@ public:
m_v8Object = object;
}
void operator=(const String& string)
{
setString(string);
}
bool prepare()
{
if (m_v8Object.IsEmpty())
......
......@@ -59,6 +59,9 @@ public:
uint16_t testUnsignedShort() { return m_unsignedShort; }
void setTestUnsignedShort(uint16_t value) { m_unsignedShort = value; }
const String& testByteString() const { return m_byteString; }
void setTestByteString(const String& value) { m_byteString = value; }
void trace(Visitor*) { }
private:
......@@ -74,6 +77,7 @@ private:
uint8_t m_octet;
int16_t m_short;
uint16_t m_unsignedShort;
String m_byteString;
};
} // namespace WebCore
......
......@@ -45,4 +45,8 @@
[EnforceRange, ImplementedAs=testShort] attribute short testEnforceRangeShort;
attribute unsigned short testUnsignedShort;
[EnforceRange, ImplementedAs=testUnsignedShort] attribute unsigned short testEnforceRangeUnsignedShort;
attribute ByteString testByteString;
void setTestByteString(ByteString byteString);
[ImplementedAs=setTestByteString] void setTestByteStringDefaultNull([Default=NullString] optional ByteString byteString);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment