From 6023560863b4d08d68e1f637ba110c7023a9f18c Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 20 Sep 2016 10:18:05 +0200 Subject: [PATCH 1/7] Improve jsid_to_str's name and documentation. --- components/script/dom/bindings/codegen/CodegenRust.py | 4 ++-- components/script/dom/bindings/conversions.rs | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 486758182d2..9644f589072 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4658,7 +4658,7 @@ class CGProxyNamedOperation(CGProxySpecialOperation): def define(self): # Our first argument is the id we're getting. argName = self.arguments[0].identifier.name - return ("let %s = jsid_to_str(cx, id);\n" + return ("let %s = string_jsid_to_string(cx, id);\n" "let this = UnwrapProxy(proxy);\n" "let this = &*this;\n" % argName + CGProxySpecialOperation.define(self)) @@ -5528,13 +5528,13 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'dom::bindings::conversions::StringificationBehavior', 'dom::bindings::conversions::ToJSValConvertible', 'dom::bindings::conversions::is_array_like', - 'dom::bindings::conversions::jsid_to_str', 'dom::bindings::conversions::native_from_handlevalue', 'dom::bindings::conversions::native_from_object', 'dom::bindings::conversions::private_from_object', 'dom::bindings::conversions::root_from_handleobject', 'dom::bindings::conversions::root_from_handlevalue', 'dom::bindings::conversions::root_from_object', + 'dom::bindings::conversions::string_jsid_to_string', 'dom::bindings::codegen::PrototypeList', 'dom::bindings::codegen::RegisterBindings', 'dom::bindings::codegen::UnionTypes', diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 113a9d279d5..bb29b9904a1 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -115,9 +115,14 @@ impl FromJSValConvertible for Root { } } -/// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a -/// string, or if the string does not contain valid UTF-16. -pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { +/// Convert `id` to a `DOMString`, assuming it is string-valued. +/// +/// Handling of invalid UTF-16 in strings depends on the relevant option. +/// +/// # Panics +/// +/// Panics if `id` is not string-valued. +pub fn string_jsid_to_string(cx: *mut JSContext, id: HandleId) -> DOMString { unsafe { assert!(RUST_JSID_IS_STRING(id)); jsstring_to_str(cx, RUST_JSID_TO_STRING(id)) From d1d2074d43a0f8a4979198119242ba91e5ca1594 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Mon, 19 Sep 2016 16:54:25 +0200 Subject: [PATCH 2/7] Improve handling of ConversionResult::Failure in unions. --- .../dom/bindings/codegen/CodegenRust.py | 40 +++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 9644f589072..6fb376aba1a 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -649,6 +649,11 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if exceptionCode is None: exceptionCode = "return false;" + if failureCode is None: + failOrPropagate = "throw_type_error(cx, &error);\n%s" % exceptionCode + else: + failOrPropagate = failureCode + needsRooting = typeNeedsRooting(type, descriptorProvider) def handleOptional(template, declType, default): @@ -732,11 +737,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" " Ok(ConversionResult::Success(value)) => value,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (config, exceptionCode, exceptionCode)) + "}" % (config, indent(failOrPropagate, 8), exceptionCode)) return handleOptional(templateBody, declType, handleDefaultNull("None")) @@ -748,11 +752,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" " Ok(ConversionResult::Success(value)) => value,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (exceptionCode, exceptionCode)) + "}" % (indent(failOrPropagate, 8), exceptionCode)) return handleOptional(templateBody, declType, handleDefaultNull("None")) @@ -823,11 +826,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" " Ok(ConversionResult::Success(strval)) => strval,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (nullBehavior, exceptionCode, exceptionCode)) + "}" % (nullBehavior, indent(failOrPropagate, 8), exceptionCode)) if defaultValue is None: default = None @@ -853,11 +855,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" " Ok(ConversionResult::Success(strval)) => strval,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (exceptionCode, exceptionCode)) + "}" % (indent(failOrPropagate, 8), exceptionCode)) if defaultValue is None: default = None @@ -883,11 +884,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, "match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" " Ok(ConversionResult::Success(strval)) => strval,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (exceptionCode, exceptionCode)) + "}" % (indent(failOrPropagate, 8), exceptionCode)) if defaultValue is None: default = None @@ -1042,11 +1042,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, template = ("match %s::new(cx, ${val}) {\n" " Ok(ConversionResult::Success(dictionary)) => dictionary,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s },\n" - "}" % (typeName, exceptionCode, exceptionCode)) + "}" % (typeName, indent(failOrPropagate, 8), exceptionCode)) return handleOptional(template, declType, handleDefaultNull("%s::empty(cx)" % typeName)) @@ -1071,11 +1070,10 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, "match FromJSValConvertible::from_jsval(cx, ${val}, %s) {\n" " Ok(ConversionResult::Success(v)) => v,\n" " Ok(ConversionResult::Failure(error)) => {\n" - " throw_type_error(cx, &error);\n" - " %s\n" + "%s\n" " }\n" " _ => { %s }\n" - "}" % (conversionBehavior, exceptionCode, exceptionCode)) + "}" % (conversionBehavior, indent(failOrPropagate, 8), exceptionCode)) if defaultValue is not None: if isinstance(defaultValue, IDLNullValue): From e942f50e1b4bf854c8326ac3260b72c149652ce1 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 20 Sep 2016 10:22:33 +0200 Subject: [PATCH 3/7] Handle unsupported types better in getUnionTypeTemplateVars. --- .../script/dom/bindings/codegen/CodegenRust.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 6fb376aba1a..00c5e83320a 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -3989,13 +3989,6 @@ class CGConstant(CGThing): def getUnionTypeTemplateVars(type, descriptorProvider): - # For dictionaries and sequences we need to pass None as the failureCode - # for getJSToNativeConversionInfo. - # Also, for dictionaries we would need to handle conversion of - # null/undefined to the dictionary correctly. - if type.isDictionary(): - raise TypeError("Can't handle dictionaries in unions") - if type.isGeckoInterface(): name = type.inner.identifier.name typeName = descriptorProvider.getDescriptor(name).returnType @@ -4006,10 +3999,6 @@ def getUnionTypeTemplateVars(type, descriptorProvider): name = type.name inner = getUnionTypeTemplateVars(innerSequenceType(type), descriptorProvider) typeName = "Vec<" + inner["typeName"] + ">" - elif type.isArray(): - name = str(type) - # XXXjdm dunno about typeName here - typeName = "/*" + type.name + "*/" elif type.isByteString(): name = type.name typeName = "ByteString" @@ -4023,8 +4012,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): name = type.name typeName = builtinNames[type.tag()] else: - name = type.name - typeName = "/*" + type.name + "*/" + raise TypeError("Can't handle %s in unions yet" % type) info = getJSToNativeConversionInfo( type, descriptorProvider, failureCode="return Ok(None);", From b2fc80a8479549fe26abbfcd06edeadd325461e3 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 21 Sep 2016 13:42:35 +0200 Subject: [PATCH 4/7] Rename innerSequenceType to innerContainerType. --- components/script/dom/bindings/codegen/CodegenRust.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 00c5e83320a..59df88339d4 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -92,7 +92,7 @@ def stripTrailingWhitespace(text): return '\n'.join(lines) + tail -def innerSequenceType(type): +def innerContainerType(type): assert type.isSequence() return type.inner.inner if type.nullable() else type.inner @@ -725,7 +725,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, raise TypeError("Can't handle array arguments yet") if type.isSequence(): - innerInfo = getJSToNativeConversionInfo(innerSequenceType(type), + innerInfo = getJSToNativeConversionInfo(innerContainerType(type), descriptorProvider, isMember=isMember) declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">") @@ -1351,7 +1351,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): result = CGWrapper(result, pre="Option<", post=">") return result if returnType.isSequence(): - result = getRetvalDeclarationForType(innerSequenceType(returnType), descriptorProvider) + result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider) result = CGWrapper(result, pre="Vec<", post=">") if returnType.nullable(): result = CGWrapper(result, pre="Option<", post=">") @@ -3997,7 +3997,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): typeName = name elif type.isSequence(): name = type.name - inner = getUnionTypeTemplateVars(innerSequenceType(type), descriptorProvider) + inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider) typeName = "Vec<" + inner["typeName"] + ">" elif type.isByteString(): name = type.name From 9371889a03f9d181405fa2aa68e7cc57c1640aa0 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 21 Sep 2016 13:44:31 +0200 Subject: [PATCH 5/7] Use innerContainerType in getConversionConfigForType. --- components/script/dom/bindings/codegen/CodegenRust.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 59df88339d4..5efdf40b60a 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -1266,7 +1266,7 @@ def typeNeedsCx(type, retVal=False): # Returns a conversion behavior suitable for a type def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs): if type.isSequence(): - return getConversionConfigForType(type.unroll(), isEnforceRange, isClamp, treatNullAs) + return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs) if type.isDOMString(): assert not isEnforceRange and not isClamp From 9a9ca450846457613a96c4364d16d0630320d3d5 Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Wed, 21 Sep 2016 13:53:16 +0200 Subject: [PATCH 6/7] Introduce wrapInNativeContainerType. --- .../script/dom/bindings/codegen/CodegenRust.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 5efdf40b60a..654714ec204 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -97,6 +97,15 @@ def innerContainerType(type): return type.inner.inner if type.nullable() else type.inner +def wrapInNativeContainerType(type, inner): + if type.isSequence(): + containerType = "Vec" + else: + raise TypeError("Unexpected container type %s", type) + + return CGWrapper(inner, pre=containerType + "<", post=">") + + builtinNames = { IDLType.Tags.bool: 'bool', IDLType.Tags.int8: 'i8', @@ -728,7 +737,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, innerInfo = getJSToNativeConversionInfo(innerContainerType(type), descriptorProvider, isMember=isMember) - declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">") + declType = wrapInNativeContainerType(type, innerInfo.declType) config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) if type.nullable(): @@ -1352,7 +1361,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): return result if returnType.isSequence(): result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider) - result = CGWrapper(result, pre="Vec<", post=">") + result = wrapInNativeContainerType(returnType, result) if returnType.nullable(): result = CGWrapper(result, pre="Option<", post=">") return result @@ -3998,7 +4007,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): elif type.isSequence(): name = type.name inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider) - typeName = "Vec<" + inner["typeName"] + ">" + typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define() elif type.isByteString(): name = type.name typeName = "ByteString" From 2d83e5a78880724713366a38f599a1dac65403dc Mon Sep 17 00:00:00 2001 From: Ms2ger Date: Tue, 20 Sep 2016 10:22:39 +0200 Subject: [PATCH 7/7] Implement the MozMap type. Fixes #13144. --- .../dom/bindings/codegen/CodegenRust.py | 31 +++-- components/script/dom/bindings/conversions.rs | 17 +++ components/script/dom/bindings/mod.rs | 1 + components/script/dom/bindings/mozmap.rs | 109 ++++++++++++++++++ components/script/dom/testbinding.rs | 28 +++++ .../script/dom/webidls/TestBinding.webidl | 28 +++++ 6 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 components/script/dom/bindings/mozmap.rs diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 654714ec204..ca7901a56e3 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -93,13 +93,15 @@ def stripTrailingWhitespace(text): def innerContainerType(type): - assert type.isSequence() + assert type.isSequence() or type.isMozMap() return type.inner.inner if type.nullable() else type.inner def wrapInNativeContainerType(type, inner): if type.isSequence(): containerType = "Vec" + elif type.isMozMap(): + containerType = "MozMap" else: raise TypeError("Unexpected container type %s", type) @@ -733,7 +735,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.isArray(): raise TypeError("Can't handle array arguments yet") - if type.isSequence(): + if type.isSequence() or type.isMozMap(): innerInfo = getJSToNativeConversionInfo(innerContainerType(type), descriptorProvider, isMember=isMember) @@ -1274,7 +1276,7 @@ def typeNeedsCx(type, retVal=False): # Returns a conversion behavior suitable for a type def getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs): - if type.isSequence(): + if type.isSequence() or type.isMozMap(): return getConversionConfigForType(innerContainerType(type), isEnforceRange, isClamp, treatNullAs) if type.isDOMString(): assert not isEnforceRange and not isClamp @@ -1359,7 +1361,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): if returnType.nullable(): result = CGWrapper(result, pre="Option<", post=">") return result - if returnType.isSequence(): + if returnType.isSequence() or returnType.isMozMap(): result = getRetvalDeclarationForType(innerContainerType(returnType), descriptorProvider) result = wrapInNativeContainerType(returnType, result) if returnType.nullable(): @@ -1894,6 +1896,8 @@ class CGImports(CGWrapper): parentName = getIdentifier(descriptor.interface.parent).name descriptor = descriptorProvider.getDescriptor(parentName) extras += [descriptor.path, descriptor.bindingPath] + elif t.isType() and t.isMozMap(): + extras += ['dom::bindings::mozmap::MozMap'] else: if t.isEnum(): extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values'] @@ -2186,6 +2190,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): 'dom::bindings::conversions::root_from_handlevalue', 'dom::bindings::error::throw_not_in_union', 'dom::bindings::js::Root', + 'dom::bindings::mozmap::MozMap', 'dom::bindings::str::ByteString', 'dom::bindings::str::DOMString', 'dom::bindings::str::USVString', @@ -4004,7 +4009,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider): elif type.isEnum(): name = type.inner.identifier.name typeName = name - elif type.isSequence(): + elif type.isSequence() or type.isMozMap(): name = type.name inner = getUnionTypeTemplateVars(innerContainerType(type), descriptorProvider) typeName = wrapInNativeContainerType(type, CGGeneric(inner["typeName"])).define() @@ -4153,14 +4158,25 @@ class CGUnionConversionStruct(CGThing): else: object = None - hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object + mozMapMemberTypes = filter(lambda t: t.isMozMap(), memberTypes) + if len(mozMapMemberTypes) > 0: + assert len(mozMapMemberTypes) == 1 + typeName = mozMapMemberTypes[0].name + mozMapObject = CGGeneric(get_match(typeName)) + names.append(typeName) + else: + mozMapObject = None + + hasObjectTypes = interfaceObject or arrayObject or dateObject or nonPlatformObject or object or mozMapObject if hasObjectTypes: - assert interfaceObject or arrayObject + assert interfaceObject or arrayObject or mozMapObject templateBody = CGList([], "\n") if interfaceObject: templateBody.append(interfaceObject) if arrayObject: templateBody.append(arrayObject) + if mozMapObject: + templateBody.append(mozMapObject) conversions.append(CGIfWrapper("value.get().is_object()", templateBody)) stringTypes = [t for t in memberTypes if t.isString() or t.isEnum()] numericTypes = [t for t in memberTypes if t.isNumeric()] @@ -5545,6 +5561,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'dom::bindings::proxyhandler::fill_property_descriptor', 'dom::bindings::proxyhandler::get_expando_object', 'dom::bindings::proxyhandler::get_property_descriptor', + 'dom::bindings::mozmap::MozMap', 'dom::bindings::num::Finite', 'dom::bindings::str::ByteString', 'dom::bindings::str::DOMString', diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index bb29b9904a1..55c3c41f460 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -44,6 +44,7 @@ pub use js::conversions::ConversionBehavior; use js::conversions::latin1_to_string; use js::error::throw_type_error; use js::glue::{GetProxyPrivate, IsWrapper}; +use js::glue::{RUST_JSID_IS_INT, RUST_JSID_TO_INT}; use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_STRING, UnwrapObject}; use js::jsapi::{HandleId, HandleObject, HandleValue, JSClass, JSContext}; use js::jsapi::{JSObject, JSString, JS_GetArrayBufferViewType, JS_GetClass}; @@ -129,6 +130,22 @@ pub fn string_jsid_to_string(cx: *mut JSContext, id: HandleId) -> DOMString { } } +/// Convert `id` to a `DOMString`. Returns `None` if `id` is not a string or +/// integer. +/// +/// Handling of invalid UTF-16 in strings depends on the relevant option. +pub unsafe fn jsid_to_string(cx: *mut JSContext, id: HandleId) -> Option { + if RUST_JSID_IS_STRING(id) { + return Some(jsstring_to_str(cx, RUST_JSID_TO_STRING(id))); + } + + if RUST_JSID_IS_INT(id) { + return Some(RUST_JSID_TO_INT(id).to_string().into()); + } + + None +} + // http://heycam.github.io/webidl/#es-USVString impl ToJSValConvertible for USVString { unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 16607c9045f..4cb10add852 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -140,6 +140,7 @@ pub mod inheritance; pub mod interface; pub mod iterable; pub mod js; +pub mod mozmap; pub mod namespace; pub mod num; pub mod proxyhandler; diff --git a/components/script/dom/bindings/mozmap.rs b/components/script/dom/bindings/mozmap.rs new file mode 100644 index 00000000000..557ba0662a0 --- /dev/null +++ b/components/script/dom/bindings/mozmap.rs @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! The `MozMap` (open-ended dictionary) type. + +use dom::bindings::conversions::jsid_to_string; +use dom::bindings::str::DOMString; +use js::conversions::{FromJSValConvertible, ToJSValConvertible, ConversionResult}; +use js::jsapi::GetPropertyKeys; +use js::jsapi::HandleValue; +use js::jsapi::JSContext; +use js::jsapi::JSITER_OWNONLY; +use js::jsapi::JSPROP_ENUMERATE; +use js::jsapi::JS_DefineUCProperty2; +use js::jsapi::JS_GetPropertyById; +use js::jsapi::JS_NewPlainObject; +use js::jsapi::MutableHandleValue; +use js::jsval::ObjectValue; +use js::jsval::UndefinedValue; +use js::rust::IdVector; +use std::collections::HashMap; +use std::ops::Deref; + +/// The `MozMap` (open-ended dictionary) type. +pub struct MozMap { + map: HashMap, +} + +impl MozMap { + /// Create an empty `MozMap`. + pub fn new() -> Self { + MozMap { + map: HashMap::new(), + } + } +} + +impl Deref for MozMap { + type Target = HashMap; + + fn deref(&self) -> &HashMap { + &self.map + } +} + +impl FromJSValConvertible for MozMap + where T: FromJSValConvertible, + C: Clone, +{ + type Config = C; + unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, config: C) + -> Result, ()> { + if !value.is_object() { + return Ok(ConversionResult::Failure("MozMap value was not an object".into())); + } + + rooted!(in(cx) let object = value.to_object()); + let ids = IdVector::new(cx); + assert!(GetPropertyKeys(cx, object.handle(), JSITER_OWNONLY, ids.get())); + + let mut map = HashMap::new(); + for id in &*ids { + rooted!(in(cx) let id = *id); + + rooted!(in(cx) let mut property = UndefinedValue()); + if !JS_GetPropertyById(cx, object.handle(), id.handle(), property.handle_mut()) { + return Err(()); + } + + let property = match try!(T::from_jsval(cx, property.handle(), config.clone())) { + ConversionResult::Success(property) => property, + ConversionResult::Failure(message) => return Ok(ConversionResult::Failure(message)), + }; + + let key = jsid_to_string(cx, id.handle()).unwrap(); + map.insert(key, property); + } + + Ok(ConversionResult::Success(MozMap { + map: map, + })) + } +} + +impl ToJSValConvertible for MozMap { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + rooted!(in(cx) let js_object = JS_NewPlainObject(cx)); + assert!(!js_object.handle().is_null()); + + rooted!(in(cx) let mut js_value = UndefinedValue()); + for (key, value) in &self.map { + let key = key.encode_utf16().collect::>(); + value.to_jsval(cx, js_value.handle_mut()); + + assert!(JS_DefineUCProperty2(cx, + js_object.handle(), + key.as_ptr(), + key.len(), + js_value.handle(), + JSPROP_ENUMERATE, + None, + None)); + } + + rval.set(ObjectValue(&*js_object.handle().get())); + } +} diff --git a/components/script/dom/testbinding.rs b/components/script/dom/testbinding.rs index b9e34ebe0a7..566ff2d9375 100644 --- a/components/script/dom/testbinding.rs +++ b/components/script/dom/testbinding.rs @@ -10,6 +10,7 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::TestBindingBinding; use dom::bindings::codegen::Bindings::TestBindingBinding::{TestBindingMethods, TestDictionary}; use dom::bindings::codegen::Bindings::TestBindingBinding::{TestDictionaryDefaults, TestEnum}; +use dom::bindings::codegen::UnionTypes; use dom::bindings::codegen::UnionTypes::{BlobOrBoolean, BlobOrBlobSequence, LongOrLongSequenceSequence}; use dom::bindings::codegen::UnionTypes::{BlobOrString, BlobOrUnsignedLong, EventOrString}; use dom::bindings::codegen::UnionTypes::{ByteStringOrLong, ByteStringSequenceOrLongOrString}; @@ -21,6 +22,7 @@ use dom::bindings::codegen::UnionTypes::{StringOrUnsignedLong, StringOrBoolean, use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::js::Root; +use dom::bindings::mozmap::MozMap; use dom::bindings::num::Finite; use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::str::{ByteString, DOMString, USVString}; @@ -619,6 +621,32 @@ impl TestBindingMethods for TestBinding { fn FuncControlledMethodDisabled(&self) {} fn FuncControlledMethodEnabled(&self) {} + fn PassMozMap(&self, _: MozMap) {} + fn PassNullableMozMap(&self, _: Option >) {} + fn PassMozMapOfNullableInts(&self, _: MozMap>) {} + fn PassOptionalMozMapOfNullableInts(&self, _: Option>>) {} + fn PassOptionalNullableMozMapOfNullableInts(&self, _: Option> >>) {} + fn PassCastableObjectMozMap(&self, _: MozMap>) {} + fn PassNullableCastableObjectMozMap(&self, _: MozMap>>) {} + fn PassCastableObjectNullableMozMap(&self, _: Option>>) {} + fn PassNullableCastableObjectNullableMozMap(&self, _: Option>>>) {} + fn PassOptionalMozMap(&self, _: Option>) {} + fn PassOptionalNullableMozMap(&self, _: Option>>) {} + fn PassOptionalNullableMozMapWithDefaultValue(&self, _: Option>) {} + fn PassOptionalObjectMozMap(&self, _: Option>>) {} + fn PassStringMozMap(&self, _: MozMap) {} + fn PassByteStringMozMap(&self, _: MozMap) {} + fn PassMozMapOfMozMaps(&self, _: MozMap>) {} + fn PassMozMapUnion(&self, _: UnionTypes::LongOrByteStringMozMap) {} + fn PassMozMapUnion2(&self, _: UnionTypes::TestBindingOrByteStringMozMap) {} + fn PassMozMapUnion3(&self, _: UnionTypes::TestBindingOrByteStringSequenceSequenceOrByteStringMozMap) {} + fn ReceiveMozMap(&self) -> MozMap { MozMap::new() } + fn ReceiveNullableMozMap(&self) -> Option> { Some(MozMap::new()) } + fn ReceiveMozMapOfNullableInts(&self) -> MozMap> { MozMap::new() } + fn ReceiveNullableMozMapOfNullableInts(&self) -> Option>> { Some(MozMap::new()) } + fn ReceiveMozMapOfMozMaps(&self) -> MozMap> { MozMap::new() } + fn ReceiveAnyMozMap(&self) -> MozMap { MozMap::new() } + fn PassSequenceSequence(&self, _seq: Vec>) {} fn ReturnSequenceSequence(&self) -> Vec> { vec![] } fn PassUnionSequenceSequence(&self, seq: LongOrLongSequenceSequence) { diff --git a/components/script/dom/webidls/TestBinding.webidl b/components/script/dom/webidls/TestBinding.webidl index d98b9428031..e35b914d08e 100644 --- a/components/script/dom/webidls/TestBinding.webidl +++ b/components/script/dom/webidls/TestBinding.webidl @@ -429,6 +429,34 @@ interface TestBinding { sequence> returnSequenceSequence(); void passUnionSequenceSequence((long or sequence>) seq); + void passMozMap(MozMap arg); + void passNullableMozMap(MozMap? arg); + void passMozMapOfNullableInts(MozMap arg); + void passOptionalMozMapOfNullableInts(optional MozMap arg); + void passOptionalNullableMozMapOfNullableInts(optional MozMap? arg); + void passCastableObjectMozMap(MozMap arg); + void passNullableCastableObjectMozMap(MozMap arg); + void passCastableObjectNullableMozMap(MozMap? arg); + void passNullableCastableObjectNullableMozMap(MozMap? arg); + void passOptionalMozMap(optional MozMap arg); + void passOptionalNullableMozMap(optional MozMap? arg); + void passOptionalNullableMozMapWithDefaultValue(optional MozMap? arg = null); + void passOptionalObjectMozMap(optional MozMap arg); + void passStringMozMap(MozMap arg); + void passByteStringMozMap(MozMap arg); + void passMozMapOfMozMaps(MozMap> arg); + + void passMozMapUnion((long or MozMap) init); + void passMozMapUnion2((TestBinding or MozMap) init); + void passMozMapUnion3((TestBinding or sequence> or MozMap) init); + + MozMap receiveMozMap(); + MozMap? receiveNullableMozMap(); + MozMap receiveMozMapOfNullableInts(); + MozMap? receiveNullableMozMapOfNullableInts(); + MozMap> receiveMozMapOfMozMaps(); + MozMap receiveAnyMozMap(); + static attribute boolean booleanAttributeStatic; static void receiveVoidStatic(); boolean BooleanMozPreference(DOMString pref_name);