diff options
13 files changed, 237 insertions, 21 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 4e8b3d40101..cd3ee9baacb 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -2473,7 +2473,7 @@ let traps = ProxyTraps { enter: None, getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), defineProperty: Some(%s), - ownPropertyKeys: Some(proxyhandler::own_property_keys), + ownPropertyKeys: Some(own_property_keys), delete_: Some(%s), enumerate: None, preventExtensions: Some(proxyhandler::prevent_extensions), @@ -4178,6 +4178,59 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): return CGGeneric(self.getBody()) +class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): + def __init__(self, descriptor): + args = [Argument('*mut JSContext', 'cx'), + Argument('HandleObject', 'proxy'), + Argument('*mut AutoIdVector', 'props')] + CGAbstractExternMethod.__init__(self, descriptor, "own_property_keys", "u8", args) + self.descriptor = descriptor + + def getBody(self): + body = dedent( + """ + let unwrapped_proxy = UnwrapProxy(proxy); + """) + + if self.descriptor.operations['IndexedGetter']: + body += dedent( + """ + for i in 0..(*unwrapped_proxy).Length() { + let rooted_jsid = RootedId::new(cx, int_to_jsid(i as i32)); + AppendToAutoIdVector(props, rooted_jsid.handle().get()); + } + """) + + if self.descriptor.operations['NamedGetter']: + body += dedent( + """ + for name in (*unwrapped_proxy).SupportedPropertyNames() { + let cstring = CString::new(name).unwrap(); + let jsstring = JS_InternString(cx, cstring.as_ptr()); + let mut rooted = RootedString::new(cx, jsstring); + let jsid = INTERNED_STRING_TO_JSID(cx, rooted.handle().get()); + let rooted_jsid = RootedId::new(cx, jsid); + AppendToAutoIdVector(props, rooted_jsid.handle().get()); + } + """) + + body += dedent( + """ + let expando = get_expando_object(proxy); + if !expando.is_null() { + let rooted_expando = RootedObject::new(cx, expando); + GetPropertyKeys(cx, rooted_expando.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, props); + } + + return JSTrue; + """) + + return body + + def definition_body(self): + return CGGeneric(self.getBody()) + + class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): def __init__(self, descriptor): args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), @@ -4496,6 +4549,14 @@ class CGInterfaceTrait(CGThing): infallible = 'infallible' in descriptor.getExtendedAttributes(operation) if operation.isGetter(): arguments = method_arguments(descriptor, rettype, arguments, trailing=("found", "&mut bool")) + + # If this interface 'supports named properties', then we + # should be able to access 'supported property names' + # + # WebIDL, Second Draft, section 3.2.4.5 + # https://heycam.github.io/webidl/#idl-named-properties + if operation.isNamed(): + yield "SupportedPropertyNames", [], "Vec<DOMString>" else: arguments = method_arguments(descriptor, rettype, arguments) rettype = return_type(descriptor, rettype, infallible) @@ -4600,6 +4661,7 @@ class CGDescriptor(CGThing): # cgThings.append(CGProxyIsProxy(descriptor)) cgThings.append(CGProxyUnwrap(descriptor)) cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor)) + cgThings.append(CGDOMJSProxyHandler_ownPropertyKeys(descriptor)) cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)) cgThings.append(CGDOMJSProxyHandler_className(descriptor)) cgThings.append(CGDOMJSProxyHandler_get(descriptor)) @@ -4958,6 +5020,7 @@ class CGBindingRoot(CGThing): 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}', 'js::{JSCLASS_RESERVED_SLOTS_MASK}', 'js::{JSPROP_ENUMERATE, JSPROP_SHARED}', + 'js::{JSITER_OWNONLY, JSITER_HIDDEN, JSITER_SYMBOLS}', 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}', 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}', 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}', @@ -4967,20 +5030,22 @@ class CGBindingRoot(CGThing): 'js::jsapi::{JSClass, FreeOp, JSFreeOp, JSFunctionSpec, jsid}', 'js::jsapi::{MutableHandleValue, MutableHandleObject, HandleObject, HandleValue, RootedObject}', 'js::jsapi::{RootedValue, JSNativeWrapper, JSNative, JSObject, JSPropertyDescriptor}', + 'js::jsapi::{RootedId, JS_InternString, RootedString, INTERNED_STRING_TO_JSID}', 'js::jsapi::{JSPropertySpec}', 'js::jsapi::{JSString, JSTracer, JSJitInfo, JSJitInfo_OpType, JSJitInfo_AliasSet}', 'js::jsapi::{MutableHandle, Handle, HandleId, JSType, JSValueType}', 'js::jsapi::{SymbolCode, ObjectOpResult, HandleValueArray}', 'js::jsapi::{JSJitGetterCallArgs, JSJitSetterCallArgs, JSJitMethodCallArgs, CallArgs}', 'js::jsapi::{JSAutoCompartment, JSAutoRequest, JS_ComputeThis}', - 'js::jsapi::GetGlobalForObjectCrossCompartment', + 'js::jsapi::{GetGlobalForObjectCrossCompartment, AutoIdVector, GetPropertyKeys}', 'js::jsval::JSVal', 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}', 'js::jsval::{NullValue, UndefinedValue}', 'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}', 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}', 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}', - 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}', + 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING, int_to_jsid}', + 'js::glue::AppendToAutoIdVector', 'js::rust::GCMethods', 'js::{JSTrue, JSFalse}', 'dom::bindings', diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 69671a71145..2dde7370442 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -11,7 +11,6 @@ use dom::bindings::utils::delete_property_by_id; use js::glue::GetProxyExtra; use js::glue::InvokeGetOwnPropertyDescriptor; use js::glue::{SetProxyExtra, GetProxyHandler}; -use js::jsapi::AutoIdVector; use js::jsapi::GetObjectProto; use js::jsapi::{Handle, HandleObject, HandleId, MutableHandle, RootedObject, ObjectOpResult}; use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject}; @@ -83,15 +82,6 @@ pub unsafe extern fn delete(cx: *mut JSContext, proxy: HandleObject, id: HandleI delete_property_by_id(cx, expando.handle(), id, bp) } -/// Stub for ownPropertyKeys -pub unsafe extern fn own_property_keys(_cx: *mut JSContext, - _proxy: HandleObject, - _props: *mut AutoIdVector) -> u8 { - // FIXME: implement this - // https://github.com/servo/servo/issues/6390 - JSTrue -} - /// Controls whether the Extensible bit can be changed pub unsafe extern fn prevent_extensions(_cx: *mut JSContext, _proxy: HandleObject, diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 78399ac1c2b..7e5adec0353 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1912,6 +1912,12 @@ impl<'a> DocumentMethods for &'a Document { collection.r().reflector().get_jsobject().get() } + // https://html.spec.whatwg.org/#document + fn SupportedPropertyNames(self) -> Vec<DOMString> { + // FIXME: unimplemented (https://github.com/servo/servo/issues/7273) + vec![] + } + global_event_handlers!(); event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange); } diff --git a/components/script/dom/domstringmap.rs b/components/script/dom/domstringmap.rs index fdaf9d7f65c..7d3683f74db 100644 --- a/components/script/dom/domstringmap.rs +++ b/components/script/dom/domstringmap.rs @@ -67,5 +67,10 @@ impl<'a> DOMStringMapMethods for &'a DOMStringMap { } } } -} + // https://html.spec.whatwg.org/multipage/#domstringmap + fn SupportedPropertyNames(self) -> Vec<DOMString> { + // FIXME: unimplemented (https://github.com/servo/servo/issues/7273) + vec![] + } +} diff --git a/components/script/dom/htmlcollection.rs b/components/script/dom/htmlcollection.rs index bbac05d5b97..71da0d9c04c 100644 --- a/components/script/dom/htmlcollection.rs +++ b/components/script/dom/htmlcollection.rs @@ -227,5 +227,30 @@ impl<'a> HTMLCollectionMethods for &'a HTMLCollection { *found = maybe_elem.is_some(); maybe_elem } -} + // https://dom.spec.whatwg.org/#interface-htmlcollection + fn SupportedPropertyNames(self) -> Vec<DOMString> { + // Step 1 + let mut result = vec![]; + + // Step 2 + let ref filter = self.collection.1; + let root = self.collection.0.root(); + let elems = HTMLCollection::traverse(root.r()).filter(|element| filter.filter(element.r(), root.r())); + for elem in elems { + // Step 2.1 + let id_attr = elem.get_string_attribute(&atom!("id")); + if !id_attr.is_empty() && !result.contains(&id_attr) { + result.push(id_attr) + } + // Step 2.2 + let name_attr = elem.get_string_attribute(&atom!("name")); + if !name_attr.is_empty() && !result.contains(&name_attr) && *elem.namespace() == ns!(HTML) { + result.push(name_attr) + } + } + + // Step 3 + result + } +} diff --git a/components/script/dom/namednodemap.rs b/components/script/dom/namednodemap.rs index 8894764e073..829e97519a2 100644 --- a/components/script/dom/namednodemap.rs +++ b/components/script/dom/namednodemap.rs @@ -105,5 +105,9 @@ impl<'a> NamedNodeMapMethods for &'a NamedNodeMap { *found = item.is_some(); item } -} + fn SupportedPropertyNames(self) -> Vec<DOMString> { + // FIXME: unimplemented (https://github.com/servo/servo/issues/7273) + vec![] + } +} diff --git a/components/script/dom/storage.rs b/components/script/dom/storage.rs index ca47bb76c03..6ee0d3c9ccd 100644 --- a/components/script/dom/storage.rs +++ b/components/script/dom/storage.rs @@ -134,6 +134,11 @@ impl<'a> StorageMethods for &'a Storage { fn NamedDeleter(self, name: DOMString) { self.RemoveItem(name); } + + fn SupportedPropertyNames(self) -> Vec<DOMString> { + // FIXME: unimplemented (https://github.com/servo/servo/issues/7273) + vec![] + } } trait PrivateStorageHelpers { diff --git a/components/script/dom/testbindingproxy.rs b/components/script/dom/testbindingproxy.rs index 2d2434e5e38..57b925e8d55 100644 --- a/components/script/dom/testbindingproxy.rs +++ b/components/script/dom/testbindingproxy.rs @@ -16,7 +16,8 @@ pub struct TestBindingProxy { } impl<'a> TestBindingProxyMethods for &'a TestBindingProxy { - + fn Length(self) -> u32 {0} + fn SupportedPropertyNames(self) -> Vec<DOMString> {vec![]} fn GetNamedItem(self, _: DOMString) -> DOMString {"".to_owned()} fn SetNamedItem(self, _: DOMString, _: DOMString) -> () {} fn GetItem(self, _: u32) -> DOMString {"".to_owned()} diff --git a/components/script/dom/webidls/TestBindingProxy.webidl b/components/script/dom/webidls/TestBindingProxy.webidl index 3a478eb8acf..64a15b35658 100644 --- a/components/script/dom/webidls/TestBindingProxy.webidl +++ b/components/script/dom/webidls/TestBindingProxy.webidl @@ -13,6 +13,7 @@ // web pages. interface TestBindingProxy : TestBinding { + readonly attribute unsigned long length; getter DOMString getNamedItem(DOMString name); diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index 3e618095b54..10050f88235 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -13072,6 +13072,10 @@ "url": "/dom/collections/HTMLCollection-empty-name.html" }, { + "path": "dom/collections/HTMLCollection-supported-property-names.html", + "url": "/dom/collections/HTMLCollection-supported-property-names.html" + }, + { "path": "dom/events/Event-constants.html", "url": "/dom/events/Event-constants.html" }, @@ -18072,6 +18076,10 @@ "url": "/js/builtins/Object.prototype.freeze.html" }, { + "path": "js/builtins/Object.prototype.getOwnPropertyNames.html", + "url": "/js/builtins/Object.prototype.getOwnPropertyNames.html" + }, + { "path": "js/builtins/Object.prototype.hasOwnProperty-order.html", "url": "/js/builtins/Object.prototype.hasOwnProperty-order.html" }, diff --git a/tests/wpt/metadata/html/dom/documents/dom-tree-accessors/document.forms.html.ini b/tests/wpt/metadata/html/dom/documents/dom-tree-accessors/document.forms.html.ini index e91f95fe3df..3d14180ee0f 100644 --- a/tests/wpt/metadata/html/dom/documents/dom-tree-accessors/document.forms.html.ini +++ b/tests/wpt/metadata/html/dom/documents/dom-tree-accessors/document.forms.html.ini @@ -5,7 +5,3 @@ [document.forms iteration] expected: FAIL - - [document.forms getOwnPropertyNames] - expected: FAIL - diff --git a/tests/wpt/web-platform-tests/dom/collections/HTMLCollection-supported-property-names.html b/tests/wpt/web-platform-tests/dom/collections/HTMLCollection-supported-property-names.html new file mode 100644 index 00000000000..a37163b7d18 --- /dev/null +++ b/tests/wpt/web-platform-tests/dom/collections/HTMLCollection-supported-property-names.html @@ -0,0 +1,54 @@ +<!doctype html> +<meta charset=utf-8> +<link rel=help href=https://dom.spec.whatwg.org/#interface-htmlcollection> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<div id=log></div> + +<!-- with no attribute --> +<span></span> + +<!-- with `id` attribute --> +<span id=''></span> +<span id='some-id'></span> +<span id='some-id'></span><!-- to ensure no duplicates --> + +<!-- with `name` attribute --> +<span name=''></span> +<span name='some-name'></span> +<span name='some-name'></span><!-- to ensure no duplicates --> + +<!-- with `name` and `id` attribute --> +<span id='another-id' name='another-name'></span> + +<script> +test(function () { + var elements = document.getElementsByTagName("span"); + assert_array_equals( + Object.getOwnPropertyNames(elements), + ['0', '1', '2', '3', '4', '5', '6', '7', 'some-id', 'some-name', 'another-id', 'another-name'] + ); +}, 'Object.getOwnPropertyNames on HTMLCollection'); + +test(function () { + var elem = document.createElementNS('some-random-namespace', 'foo'); + this.add_cleanup(function () {elem.remove();}); + elem.setAttribute("name", "some-name"); + document.body.appendChild(elem); + + var elements = document.getElementsByTagName("foo"); + assert_array_equals(Object.getOwnPropertyNames(elements), ['0']); +}, 'Object.getOwnPropertyNames on HTMLCollection with non-HTML namespace'); + +test(function () { + var elem = document.createElement('foo'); + this.add_cleanup(function () {elem.remove();}); + document.body.appendChild(elem); + + var elements = document.getElementsByTagName("foo"); + elements.someProperty = "some value"; + + assert_array_equals(Object.getOwnPropertyNames(elements), ['0', 'someProperty']); +}, 'Object.getOwnPropertyNames on HTMLCollection with expando object'); +</script> diff --git a/tests/wpt/web-platform-tests/js/builtins/Object.prototype.getOwnPropertyNames.html b/tests/wpt/web-platform-tests/js/builtins/Object.prototype.getOwnPropertyNames.html new file mode 100644 index 00000000000..582f41ba105 --- /dev/null +++ b/tests/wpt/web-platform-tests/js/builtins/Object.prototype.getOwnPropertyNames.html @@ -0,0 +1,56 @@ +<!doctype html> +<title>Object.prototype.getOwnPropertyNames</title> +<link rel=help href=http://es5.github.io/#x15.2.3.4> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> + +<div id=log></div> +<script> +test(function () { + var obj = {0: 'a', 1: 'b', 2: 'c'}; + assert_array_equals( + Object.getOwnPropertyNames(obj).sort(), + ['0', '1', '2'] + ); +}, "object"); + +test(function () { + var arr = ['a', 'b', 'c']; + assert_array_equals( + Object.getOwnPropertyNames(arr).sort(), + ['0', '1', '2', 'length'] + ); +}, "array-like"); + +test(function () { + var obj = Object.create({}, { + getFoo: { + value: function() { return this.foo; }, + enumerable: false + } + }); + obj.foo = 1; + assert_array_equals( + Object.getOwnPropertyNames(obj).sort(), + ['foo', 'getFoo'] + ); +}, "non-enumerable property"); + +test(function() { + function ParentClass() {} + ParentClass.prototype.inheritedMethod = function() {}; + + function ChildClass() { + this.prop = 5; + this.method = function() {}; + } + ChildClass.prototype = new ParentClass; + ChildClass.prototype.prototypeMethod = function() {}; + + var obj = new ChildClass; + assert_array_equals( + Object.getOwnPropertyNames(obj).sort(), + ['method', 'prop'] + ); +}, 'items on the prototype chain are not listed'); +</script> |