From 4622df74e0d4081f4ab14a70426a1643f9844b0c Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 29 Mar 2016 15:50:38 -0400 Subject: [PATCH] Bug 1104955 part 3. Pass our unscopable names to CreateInterfaceObjects and have it define the right thing on the prototype. r=khuey --- dom/bindings/BindingUtils.cpp | 31 +++++++++++++++++-- dom/bindings/BindingUtils.h | 5 ++- dom/bindings/Codegen.py | 12 ++++--- testing/web-platform/meta/MANIFEST.json | 6 ++++ .../tests/dom/nodes/remove-unscopable.html | 18 +++++++++++ 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 testing/web-platform/tests/dom/nodes/remove-unscopable.html diff --git a/dom/bindings/BindingUtils.cpp b/dom/bindings/BindingUtils.cpp index b96ddb2be701..b9d082fdb68e 100644 --- a/dom/bindings/BindingUtils.cpp +++ b/dom/bindings/BindingUtils.cpp @@ -766,7 +766,8 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle global, JS::Handle parentProto, const js::Class* protoClass, const NativeProperties* properties, - const NativeProperties* chromeOnlyProperties) + const NativeProperties* chromeOnlyProperties, + const char* const* unscopableNames) { JS::Rooted ourProto(cx, JS_NewObjectWithUniqueType(cx, Jsvalify(protoClass), parentProto)); @@ -775,6 +776,28 @@ CreateInterfacePrototypeObject(JSContext* cx, JS::Handle global, return nullptr; } + if (unscopableNames) { + JS::Rooted unscopableObj(cx, JS_NewPlainObject(cx)); + if (!unscopableObj) { + return nullptr; + } + + for (; *unscopableNames; ++unscopableNames) { + if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames, + JS::TrueHandleValue, JSPROP_ENUMERATE)) { + return nullptr; + } + } + + JS::Rooted unscopableId(cx, + SYMBOL_TO_JSID(JS::GetWellKnownSymbol(cx, JS::SymbolCode::unscopables))); + // Readonly and non-enumerable to match Array.prototype. + if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj, + JSPROP_READONLY)) { + return nullptr; + } + } + return ourProto; } @@ -830,7 +853,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, JS::Heap* constructorCache, const NativeProperties* properties, const NativeProperties* chromeOnlyProperties, - const char* name, bool defineOnGlobal) + const char* name, bool defineOnGlobal, + const char* const* unscopableNames) { MOZ_ASSERT(protoClass || constructorClass || constructor, "Need at least one class or a constructor!"); @@ -861,7 +885,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, if (protoClass) { proto = CreateInterfacePrototypeObject(cx, global, protoProto, protoClass, - properties, chromeOnlyProperties); + properties, chromeOnlyProperties, + unscopableNames); if (!proto) { return; } diff --git a/dom/bindings/BindingUtils.h b/dom/bindings/BindingUtils.h index 15cc688fb6e7..0b600f28562d 100644 --- a/dom/bindings/BindingUtils.h +++ b/dom/bindings/BindingUtils.h @@ -594,6 +594,8 @@ struct NamedConstructor * false in situations where we want the properties to only * appear on privileged Xrays but not on the unprivileged * underlying global. + * unscopableNames if not null it points to a null-terminated list of const + * char* names of the unscopable properties for this interface. * * At least one of protoClass, constructorClass or constructor should be * non-null. If constructorClass or constructor are non-null, the resulting @@ -610,7 +612,8 @@ CreateInterfaceObjects(JSContext* cx, JS::Handle global, JS::Heap* constructorCache, const NativeProperties* regularProperties, const NativeProperties* chromeOnlyProperties, - const char* name, bool defineOnGlobal); + const char* name, bool defineOnGlobal, + const char* const* unscopableNames); /** * Define the properties (regular and chrome-only) on obj. diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index 1436b3d38b7a..79904aedf793 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -2745,13 +2745,14 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): properties should be a PropertyArrays instance. """ - def __init__(self, descriptor, properties): + def __init__(self, descriptor, properties, haveUnscopables): args = [Argument('JSContext*', 'aCx'), Argument('JS::Handle', 'aGlobal'), Argument('ProtoAndIfaceCache&', 'aProtoAndIfaceCache'), Argument('bool', 'aDefineOnGlobal')] CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) self.properties = properties + self.haveUnscopables = haveUnscopables def definition_body(self): (protoGetter, protoHandleGetter) = InterfacePrototypeObjectProtoGetter(self.descriptor) @@ -2891,7 +2892,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): interfaceCache, ${properties}, ${chromeProperties}, - ${name}, aDefineOnGlobal); + ${name}, aDefineOnGlobal, + ${unscopableNames}); """, protoClass=protoClass, parentProto=parentProto, @@ -2903,7 +2905,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): interfaceCache=interfaceCache, properties=properties, chromeProperties=chromeProperties, - name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr") + name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr", + unscopableNames="unscopableNames" if self.haveUnscopables else "nullptr") # If we fail after here, we must clear interface and prototype caches # using this code: intermediate failure must not expose the interface in @@ -12078,7 +12081,8 @@ class CGDescriptor(CGThing): # CGCreateInterfaceObjectsMethod needs to come after our # CGDOMJSClass and unscopables, if any. - cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties)) + cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, + haveUnscopables)) # CGGetProtoObjectMethod and CGGetConstructorObjectMethod need # to come after CGCreateInterfaceObjectsMethod. diff --git a/testing/web-platform/meta/MANIFEST.json b/testing/web-platform/meta/MANIFEST.json index 085a50d4200b..94b50428025c 100644 --- a/testing/web-platform/meta/MANIFEST.json +++ b/testing/web-platform/meta/MANIFEST.json @@ -34873,6 +34873,12 @@ "url": "/dom/collections/HTMLCollection-as-proto-length-get-throws.html" } ], + "dom/nodes/remove-unscopable.html": [ + { + "path": "dom/nodes/remove-unscopable.html", + "url": "/dom/nodes/remove-unscopable.html" + } + ], "js/builtins/Promise-incumbent-global.sub.html": [ { "path": "js/builtins/Promise-incumbent-global.sub.html", diff --git a/testing/web-platform/tests/dom/nodes/remove-unscopable.html b/testing/web-platform/tests/dom/nodes/remove-unscopable.html new file mode 100644 index 000000000000..120ae8132053 --- /dev/null +++ b/testing/web-platform/tests/dom/nodes/remove-unscopable.html @@ -0,0 +1,18 @@ + + + + + +
+