Implement [Unforgeable]

This is mostly stolen from Gecko. As there, we define the unforgeable members
on an object stored in the slots of the prototype object. They are then copied
onto instance objects when they are instantiated. It should be noted that
proxy objects see their unforgeable memebers defined on their expando object.

Unforgeable attributes aren't properly inherited in codegen (in a similar
fashion as getters and setters as filed in #5875) and require to be redefined
in derived interfaces. Fortunately, there are currently no such interfaces.

No unforgeable members can be included into the TestBinding interfaces for good
measure because they are not compatible with setters.

Given the unforgeable holder object has the same prototype as actual instances
of the interface, the finalize hook needs to check its slot pointer for nullity
before dropping it.

The new failing test isn't related to Unforgeable attributes, but to the fact
that all Document instances currently have a Location, even if their window
isn't in a browsing context.
This commit is contained in:
Anthony Ramine 2015-10-12 14:50:07 +02:00
parent 29c42a9f78
commit 60976406cc
11 changed files with 267 additions and 125 deletions

View file

@ -21,7 +21,12 @@ from WebIDL import (
IDLUndefinedValue, IDLUndefinedValue,
) )
from Configuration import getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback from Configuration import (
MemberIsUnforgeable,
getTypesFromCallback,
getTypesFromDescriptor,
getTypesFromDictionary,
)
AUTOGENERATED_WARNING_COMMENT = \ AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
@ -1380,7 +1385,8 @@ class MethodDefiner(PropertyDefiner):
""" """
A class for defining methods on a prototype object. A class for defining methods on a prototype object.
""" """
def __init__(self, descriptor, name, static): def __init__(self, descriptor, name, static, unforgeable):
assert not (static and unforgeable)
PropertyDefiner.__init__(self, descriptor, name) PropertyDefiner.__init__(self, descriptor, name)
# FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822 # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
@ -1391,36 +1397,40 @@ class MethodDefiner(PropertyDefiner):
if not descriptor.interface.isCallback() or static: if not descriptor.interface.isCallback() or static:
methods = [m for m in descriptor.interface.members if methods = [m for m in descriptor.interface.members if
m.isMethod() and m.isStatic() == static and m.isMethod() and m.isStatic() == static and
not m.isIdentifierLess()] not m.isIdentifierLess() and
MemberIsUnforgeable(m, descriptor) == unforgeable]
else: else:
methods = [] methods = []
self.regular = [{"name": m.identifier.name, self.regular = [{"name": m.identifier.name,
"methodInfo": not m.isStatic(), "methodInfo": not m.isStatic(),
"length": methodLength(m), "length": methodLength(m)} for m in methods]
"flags": "JSPROP_ENUMERATE"} for m in methods]
# FIXME Check for an existing iterator on the interface first. # FIXME Check for an existing iterator on the interface first.
if any(m.isGetter() and m.isIndexed() for m in methods): if any(m.isGetter() and m.isIndexed() for m in methods):
self.regular.append({"name": '@@iterator', self.regular.append({"name": '@@iterator',
"methodInfo": False, "methodInfo": False,
"selfHostedName": "ArrayValues", "selfHostedName": "ArrayValues",
"length": 0, "length": 0})
"flags": "JSPROP_ENUMERATE"})
if not static: isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable"))
if not static and unforgeable == isUnforgeableInterface:
stringifier = descriptor.operations['Stringifier'] stringifier = descriptor.operations['Stringifier']
if stringifier: if stringifier:
self.regular.append({ self.regular.append({
"name": "toString", "name": "toString",
"nativeName": stringifier.identifier.name, "nativeName": stringifier.identifier.name,
"length": 0, "length": 0,
"flags": "JSPROP_ENUMERATE"
}) })
self.unforgeable = unforgeable
def generateArray(self, array, name): def generateArray(self, array, name):
if len(array) == 0: if len(array) == 0:
return "" return ""
flags = "JSPROP_ENUMERATE"
if self.unforgeable:
flags += " | JSPROP_PERMANENT | JSPROP_READONLY"
def specData(m): def specData(m):
# TODO: Use something like JS_FNSPEC # TODO: Use something like JS_FNSPEC
# https://github.com/servo/servo/issues/6391 # https://github.com/servo/servo/issues/6391
@ -1444,8 +1454,8 @@ class MethodDefiner(PropertyDefiner):
accessor = 'Some(%s)' % m.get("nativeName", m["name"]) accessor = 'Some(%s)' % m.get("nativeName", m["name"])
if m["name"].startswith("@@"): if m["name"].startswith("@@"):
return ('(SymbolCode::%s as i32 + 1)' return ('(SymbolCode::%s as i32 + 1)'
% m["name"][2:], accessor, jitinfo, m["length"], m["flags"], selfHostedName) % m["name"][2:], accessor, jitinfo, m["length"], flags, selfHostedName)
return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], m["flags"], selfHostedName) return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], flags, selfHostedName)
return self.generatePrefableArray( return self.generatePrefableArray(
array, name, array, name,
@ -1453,7 +1463,7 @@ class MethodDefiner(PropertyDefiner):
' name: %s as *const u8 as *const libc::c_char,\n' ' name: %s as *const u8 as *const libc::c_char,\n'
' call: JSNativeWrapper { op: %s, info: %s },\n' ' call: JSNativeWrapper { op: %s, info: %s },\n'
' nargs: %s,\n' ' nargs: %s,\n'
' flags: %s as u16,\n' ' flags: (%s) as u16,\n'
' selfHostedName: %s\n' ' selfHostedName: %s\n'
' }', ' }',
' JSFunctionSpec {\n' ' JSFunctionSpec {\n'
@ -1468,23 +1478,27 @@ class MethodDefiner(PropertyDefiner):
class AttrDefiner(PropertyDefiner): class AttrDefiner(PropertyDefiner):
def __init__(self, descriptor, name, static): def __init__(self, descriptor, name, static, unforgeable):
assert not (static and unforgeable)
PropertyDefiner.__init__(self, descriptor, name) PropertyDefiner.__init__(self, descriptor, name)
self.name = name self.name = name
self.descriptor = descriptor self.descriptor = descriptor
self.regular = [ self.regular = [
m m
for m in descriptor.interface.members for m in descriptor.interface.members if
if m.isAttr() and m.isStatic() == static m.isAttr() and m.isStatic() == static and
MemberIsUnforgeable(m, descriptor) == unforgeable
] ]
self.static = static self.static = static
self.unforgeable = unforgeable
def generateArray(self, array, name): def generateArray(self, array, name):
if len(array) == 0: if len(array) == 0:
return "" return ""
def flags(attr): flags = "JSPROP_ENUMERATE | JSPROP_SHARED"
return "JSPROP_SHARED | JSPROP_ENUMERATE" if self.unforgeable:
flags += " | JSPROP_READONLY | JSPROP_PERMANENT"
def getter(attr): def getter(attr):
if self.static: if self.static:
@ -1520,7 +1534,7 @@ class AttrDefiner(PropertyDefiner):
"native": accessor}) "native": accessor})
def specData(attr): def specData(attr):
return (str_to_const_array(attr.identifier.name), flags(attr), getter(attr), return (str_to_const_array(attr.identifier.name), flags, getter(attr),
setter(attr)) setter(attr))
return self.generatePrefableArray( return self.generatePrefableArray(
@ -1844,10 +1858,16 @@ class CGPrototypeJSClass(CGThing):
self.descriptor = descriptor self.descriptor = descriptor
def define(self): def define(self):
name = str_to_const_array(self.descriptor.interface.identifier.name + "Prototype")
slotCount = 0
if self.descriptor.hasUnforgeableMembers:
slotCount += 1
return """\ return """\
static PrototypeClass: JSClass = JSClass { static PrototypeClass: JSClass = JSClass {
name: %s as *const u8 as *const libc::c_char, name: %(name)s as *const u8 as *const libc::c_char,
flags: 0, flags:
// JSCLASS_HAS_RESERVED_SLOTS(%(slotCount)s)
(%(slotCount)s & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT,
addProperty: None, addProperty: None,
delProperty: None, delProperty: None,
getProperty: None, getProperty: None,
@ -1862,7 +1882,7 @@ static PrototypeClass: JSClass = JSClass {
trace: None, trace: None,
reserved: [0 as *mut libc::c_void; 25] reserved: [0 as *mut libc::c_void; 25]
}; };
""" % str_to_const_array(self.descriptor.interface.identifier.name + "Prototype") """ % {'name': name, 'slotCount': slotCount}
class CGInterfaceObjectJSClass(CGThing): class CGInterfaceObjectJSClass(CGThing):
@ -2181,6 +2201,68 @@ JS_SetReservedSlot(obj.ptr, DOM_WEAK_SLOT, PrivateValue(ptr::null()));"""
return create return create
def InitUnforgeablePropertiesOnHolder(descriptor, properties):
"""
Define the unforgeable properties on the unforgeable holder for
the interface represented by descriptor.
properties is a PropertyArrays instance.
"""
unforgeables = []
defineUnforgeableAttrs = "define_properties(cx, unforgeable_holder.handle(), %s).unwrap();"
defineUnforgeableMethods = "define_methods(cx, unforgeable_holder.handle(), %s).unwrap();"
unforgeableMembers = [
(defineUnforgeableAttrs, properties.unforgeable_attrs),
(defineUnforgeableMethods, properties.unforgeable_methods),
]
for template, array in unforgeableMembers:
if array.length() > 0:
unforgeables.append(CGGeneric(template % array.variableName()))
return CGList(unforgeables, "\n")
def CopyUnforgeablePropertiesToInstance(descriptor):
"""
Copy the unforgeable properties from the unforgeable holder for
this interface to the instance object we have.
"""
if not descriptor.hasUnforgeableMembers:
return ""
copyCode = ""
# For proxies, we want to define on the expando object, not directly on the
# reflector, so we can make sure we don't get confused by named getters.
if descriptor.proxy:
copyCode += """\
let mut expando = RootedObject::new(cx, ptr::null_mut());
{
let _ac = JSAutoCompartment::new(cx, scope.get());
expando.handle_mut().set(ensure_expando_object(cx, obj.handle()));
}
"""
obj = "expando"
else:
obj = "obj"
# We can't do the fast copy for globals, because we can't allocate the
# unforgeable holder for those with the right JSClass. Luckily, there
# aren't too many globals being created.
if descriptor.isGlobal():
copyFunc = "JS_CopyPropertiesFrom"
else:
copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
copyCode += """\
let mut unforgeable_holder = RootedObject::new(cx, ptr::null_mut());
unforgeable_holder.handle_mut().set(
JS_GetReservedSlot(proto.ptr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT).to_object());
assert!(%(copyFunc)s(cx, %(obj)s.handle(), unforgeable_holder.handle()));
""" % {'copyFunc': copyFunc, 'obj': obj}
return copyCode
class CGWrapMethod(CGAbstractMethod): class CGWrapMethod(CGAbstractMethod):
""" """
Class that generates the FooBinding::Wrap function for non-callback Class that generates the FooBinding::Wrap function for non-callback
@ -2199,7 +2281,9 @@ class CGWrapMethod(CGAbstractMethod):
pub=True, unsafe=True) pub=True, unsafe=True)
def definition_body(self): def definition_body(self):
unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor)
if not self.descriptor.isGlobal(): if not self.descriptor.isGlobal():
create = CreateBindingJSObject(self.descriptor, "scope")
return CGGeneric("""\ return CGGeneric("""\
let _ar = JSAutoRequest::new(cx); let _ar = JSAutoRequest::new(cx);
let scope = scope.reflector().get_jsobject(); let scope = scope.reflector().get_jsobject();
@ -2213,28 +2297,31 @@ let mut proto = RootedObject::new(cx, ptr::null_mut());
} }
assert!(!proto.ptr.is_null()); assert!(!proto.ptr.is_null());
%s %(createObject)s
%(copyUnforgeable)s
(*raw).init_reflector(obj.ptr); (*raw).init_reflector(obj.ptr);
Root::from_ref(&*raw)""" % CreateBindingJSObject(self.descriptor, "scope")) Root::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create})
else: else:
create = CreateBindingJSObject(self.descriptor)
return CGGeneric("""\ return CGGeneric("""\
let _ar = JSAutoRequest::new(cx); let _ar = JSAutoRequest::new(cx);
%s %(createObject)s
let _ac = JSAutoCompartment::new(cx, obj.ptr); let _ac = JSAutoCompartment::new(cx, obj.ptr);
let mut proto = RootedObject::new(cx, ptr::null_mut()); let mut proto = RootedObject::new(cx, ptr::null_mut());
GetProtoObject(cx, obj.handle(), obj.handle(), proto.handle_mut()); GetProtoObject(cx, obj.handle(), obj.handle(), proto.handle_mut());
JS_SetPrototype(cx, obj.handle(), proto.handle()); JS_SetPrototype(cx, obj.handle(), proto.handle());
%(copyUnforgeable)s
(*raw).init_reflector(obj.ptr); (*raw).init_reflector(obj.ptr);
let ret = Root::from_ref(&*raw); let ret = Root::from_ref(&*raw);
RegisterBindings::Register(cx, obj.handle()); RegisterBindings::Register(cx, obj.handle());
ret""" % CreateBindingJSObject(self.descriptor)) ret""" % {'copyUnforgeable': unforgeable, 'createObject': create})
class CGIDLInterface(CGThing): class CGIDLInterface(CGThing):
@ -2279,17 +2366,29 @@ class CGAbstractExternMethod(CGAbstractMethod):
class PropertyArrays(): class PropertyArrays():
def __init__(self, descriptor): def __init__(self, descriptor):
self.static_methods = MethodDefiner(descriptor, "StaticMethods", self.static_methods = MethodDefiner(descriptor, "StaticMethods",
static=True) static=True, unforgeable=False)
self.static_attrs = AttrDefiner(descriptor, "StaticAttributes", self.static_attrs = AttrDefiner(descriptor, "StaticAttributes",
static=True) static=True, unforgeable=False)
self.methods = MethodDefiner(descriptor, "Methods", static=False) self.methods = MethodDefiner(descriptor, "Methods", static=False, unforgeable=False)
self.attrs = AttrDefiner(descriptor, "Attributes", static=False) self.unforgeable_methods = MethodDefiner(descriptor, "UnforgeableMethods",
static=False, unforgeable=True)
self.attrs = AttrDefiner(descriptor, "Attributes", static=False, unforgeable=False)
self.unforgeable_attrs = AttrDefiner(descriptor, "UnforgeableAttributes",
static=False, unforgeable=True)
self.consts = ConstDefiner(descriptor, "Constants") self.consts = ConstDefiner(descriptor, "Constants")
pass pass
@staticmethod @staticmethod
def arrayNames(): def arrayNames():
return ["static_methods", "static_attrs", "methods", "attrs", "consts"] return [
"static_methods",
"static_attrs",
"methods",
"unforgeable_methods",
"attrs",
"unforgeable_attrs",
"consts",
]
def variableNames(self): def variableNames(self):
names = {} names = {}
@ -2392,10 +2491,54 @@ let named_constructors: [(NonNullJSNative, &'static str, u32); %d] = [
createArray += "," createArray += ","
createArray += "];" createArray += "];"
if self.descriptor.hasUnforgeableMembers:
# We want to use the same JSClass and prototype as the object we'll
# end up defining the unforgeable properties on in the end, so that
# we can use JS_InitializePropertiesFromCompatibleNativeObject to do
# a fast copy. In the case of proxies that's null, because the
# expando object is a vanilla object, but in the case of other DOM
# objects it's whatever our class is.
#
# Also, for a global we can't use the global's class; just use
# nullpr and when we do the copy off the holder we'll take a slower
# path. This also means that we don't need to worry about matching
# the prototype.
if self.descriptor.proxy or self.descriptor.isGlobal():
holderClass = "ptr::null()"
holderProto = "ptr::null_mut()"
else:
holderClass = "&Class.base as *const js::jsapi::Class as *const JSClass"
holderProto = "rval.get()"
# JS_NewObjectWithoutMetadata() is unsafe.
self.unsafe = True
createUnforgeableHolder = CGGeneric("""
let mut unforgeable_holder = RootedObject::new(cx, ptr::null_mut());
{
let holder_class = %(holderClass)s;
let holder_proto = RootedObject::new(cx, %(holderProto)s);
unforgeable_holder.handle_mut().set(
JS_NewObjectWithoutMetadata(cx, holder_class, holder_proto.handle()));
assert!(!unforgeable_holder.ptr.is_null());
}""" % {'holderClass': holderClass, 'holderProto': holderProto})
defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
self.properties)
createUnforgeableHolder = CGList(
[createUnforgeableHolder, defineUnforgeables], "\n")
installUnforgeableHolder = CGGeneric("""\
JS_SetReservedSlot(rval.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT,
ObjectValue(&*unforgeable_holder.ptr))""")
unforgeableHolderSetup = CGList(
[createUnforgeableHolder, installUnforgeableHolder], "\n")
else:
unforgeableHolderSetup = None
return CGList([ return CGList([
CGGeneric(getParentProto), CGGeneric(getParentProto),
CGGeneric(createArray), CGGeneric(createArray),
CGGeneric(call % self.properties.variableNames()) CGGeneric(call % self.properties.variableNames()),
unforgeableHolderSetup,
], "\n") ], "\n")
@ -4269,6 +4412,9 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
namedSetter = self.descriptor.operations['NamedSetter'] namedSetter = self.descriptor.operations['NamedSetter']
if namedSetter: if namedSetter:
if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a named setter on an interface that has "
"unforgeables. Figure out how that should work!")
set += ("if RUST_JSID_IS_STRING(id) {\n" + set += ("if RUST_JSID_IS_STRING(id) {\n" +
CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + CGIndenter(CGProxyNamedSetter(self.descriptor)).define() +
" (*opresult).code_ = 0; /* SpecialCodes::OkCode */\n" + " (*opresult).code_ = 0; /* SpecialCodes::OkCode */\n" +
@ -4308,6 +4454,9 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod):
def getBody(self): def getBody(self):
set = "" set = ""
if self.descriptor.operations['NamedDeleter']: if self.descriptor.operations['NamedDeleter']:
if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a deleter on an interface that has "
"unforgeables. Figure out how that should work!")
set += CGProxyNamedDeleter(self.descriptor).define() set += CGProxyNamedDeleter(self.descriptor).define()
set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args) set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args)
return set return set
@ -4422,7 +4571,7 @@ return true;"""
class CGDOMJSProxyHandler_get(CGAbstractExternMethod): class CGDOMJSProxyHandler_get(CGAbstractExternMethod):
def __init__(self, descriptor): def __init__(self, descriptor):
args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'),
Argument('HandleObject', '_receiver'), Argument('HandleId', 'id'), Argument('HandleObject', 'receiver'), Argument('HandleId', 'id'),
Argument('MutableHandleValue', 'vp')] Argument('MutableHandleValue', 'vp')]
CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args) CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args)
self.descriptor = descriptor self.descriptor = descriptor
@ -4437,7 +4586,7 @@ if !expando.ptr.is_null() {
} }
if hasProp { if hasProp {
return JS_GetPropertyById(cx, expando.handle(), id, vp); return JS_ForwardGetPropertyTo(cx, expando.handle(), id, receiver, vp);
} }
}""" }"""
@ -4554,7 +4703,10 @@ if !weak_box_ptr.is_null() {
} }
""" % descriptor.concreteType """ % descriptor.concreteType
release += """\ release += """\
if !this.is_null() {
// The pointer can be null if the object is the unforgeable holder of that interface.
let _ = Box::from_raw(this as *mut %s); let _ = Box::from_raw(this as *mut %s);
}
debug!("%s finalize: {:p}", this);\ debug!("%s finalize: {:p}", this);\
""" % (descriptor.concreteType, descriptor.concreteType) """ % (descriptor.concreteType, descriptor.concreteType)
return release return release
@ -5177,30 +5329,31 @@ class CGBindingRoot(CGThing):
# Add imports # Add imports
curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [ curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [
'js', 'js',
'js::JS_CALLEE', 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IMPLEMENTS_BARRIERS}',
'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IMPLEMENTS_BARRIERS}', 'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}',
'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}', 'js::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}',
'js::{JSCLASS_RESERVED_SLOTS_MASK}', 'js::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY}',
'js::{JSPROP_ENUMERATE, JSPROP_SHARED}', 'js::{JSPROP_SHARED, JS_CALLEE}',
'js::{JSITER_OWNONLY, JSITER_HIDDEN, JSITER_SYMBOLS}',
'js::error::throw_type_error', 'js::error::throw_type_error',
'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}', 'js::jsapi::{AliasSet, ArgType, AutoIdVector, CallArgs, FreeOp}',
'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}', 'js::jsapi::{GetGlobalForObjectCrossCompartment , GetPropertyKeys, Handle}',
'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}', 'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}',
'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}', 'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}',
'js::jsapi::{JS_NewObjectWithGivenProto, JS_NewObject, IsCallable, JS_SetProperty, JS_SetPrototype}', 'js::jsapi::{JS_ComputeThis, JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}',
'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSContext}', 'js::jsapi::{JS_GetClass, JS_GetGlobalForObject, JS_GetObjectPrototype}',
'js::jsapi::{JSClass, FreeOp, JSFreeOp, JSFunctionSpec, jsid}', 'js::jsapi::{JS_GetProperty, JS_GetPropertyById, JS_GetPropertyDescriptorById}',
'js::jsapi::{MutableHandleValue, MutableHandleObject, HandleObject, HandleValue, RootedObject}', 'js::jsapi::{JS_GetReservedSlot, JS_HasProperty, JS_HasPropertyById}',
'js::jsapi::{RootedValue, JSNativeWrapper, JSNative, JSObject, JSPropertyDescriptor}', 'js::jsapi::{JS_InitializePropertiesFromCompatibleNativeObject, JS_InternString}',
'js::jsapi::{RootedId, JS_InternString, RootedString, INTERNED_STRING_TO_JSID}', 'js::jsapi::{JS_IsExceptionPending, JS_NewObject, JS_NewObjectWithGivenProto}',
'js::jsapi::{JSPropertySpec}', 'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty, JS_SetPrototype}',
'js::jsapi::{JSString, JSTracer, JSJitInfo, JSTypedMethodJitInfo, OpType, AliasSet, ArgType}', 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSAutoCompartment, JSAutoRequest}',
'js::jsapi::{MutableHandle, Handle, HandleId, JSType, JSValueType}', 'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec, JSJitGetterCallArgs}',
'js::jsapi::{SymbolCode, ObjectOpResult, HandleValueArray}', 'js::jsapi::{JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs, JSNative}',
'js::jsapi::{JSJitGetterCallArgs, JSJitSetterCallArgs, JSJitMethodCallArgs, CallArgs}', 'js::jsapi::{JSObject, JSNativeWrapper, JSPropertyDescriptor, JSPropertySpec}',
'js::jsapi::{JSAutoCompartment, JSAutoRequest, JS_ComputeThis}', 'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
'js::jsapi::{GetGlobalForObjectCrossCompartment, AutoIdVector, GetPropertyKeys}', 'js::jsapi::{ObjectOpResult, OpType, MutableHandle, MutableHandleObject}',
'js::jsapi::{MutableHandleValue, RootedId, RootedObject, RootedString}',
'js::jsapi::{RootedValue, SymbolCode, jsid}',
'js::jsval::JSVal', 'js::jsval::JSVal',
'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}', 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
'js::jsval::{NullValue, UndefinedValue}', 'js::jsval::{NullValue, UndefinedValue}',
@ -5209,30 +5362,24 @@ class CGBindingRoot(CGThing):
'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}', 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING, int_to_jsid}', 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING, int_to_jsid}',
'js::glue::AppendToAutoIdVector', 'js::glue::AppendToAutoIdVector',
'js::rust::GCMethods', 'js::rust::{GCMethods, define_methods, define_properties}',
'dom::bindings', 'dom::bindings',
'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}', 'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}',
'dom::bindings::js::{JS, Root, RootedReference}', 'dom::bindings::js::{JS, Root, RootedReference}',
'dom::bindings::js::{OptionalRootedReference}', 'dom::bindings::js::{OptionalRootedReference}',
'dom::bindings::reflector::{Reflectable}', 'dom::bindings::reflector::{Reflectable}',
'dom::bindings::utils::{create_dom_global, do_create_interface_objects}', 'dom::bindings::utils::{ConstantSpec, DOMClass, DOMJSClass}',
'dom::bindings::utils::ConstantSpec', 'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
'dom::bindings::utils::{DOMClass}', 'dom::bindings::utils::{NativeProperties, NativePropertyHooks, NonNullJSNative}',
'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}', 'dom::bindings::utils::{create_dom_global, do_create_interface_objects, finalize_global}',
'dom::bindings::utils::{find_enum_string_index, get_array_index_from_id}', 'dom::bindings::utils::{find_enum_string_index, generic_getter}',
'dom::bindings::utils::{get_property_on_prototype, get_proto_or_iface_array}', 'dom::bindings::utils::{generic_lenient_getter, generic_lenient_setter}',
'dom::bindings::utils::{finalize_global, trace_global}', 'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}',
'dom::bindings::utils::has_property_on_prototype', 'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}',
'dom::bindings::utils::is_platform_object', 'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}',
'dom::bindings::utils::throwing_constructor', 'dom::bindings::utils::{is_platform_object, set_dictionary_property}',
'dom::bindings::utils::get_dictionary_property', 'dom::bindings::utils::{throwing_constructor, trace_global}',
'dom::bindings::utils::set_dictionary_property',
'dom::bindings::utils::{NativeProperties, NativePropertyHooks}',
'dom::bindings::utils::ConstantVal::{IntVal, UintVal}', 'dom::bindings::utils::ConstantVal::{IntVal, UintVal}',
'dom::bindings::utils::NonNullJSNative',
'dom::bindings::utils::{generic_getter, generic_lenient_getter}',
'dom::bindings::utils::{generic_lenient_setter, generic_method}',
'dom::bindings::utils::generic_setter',
'dom::bindings::trace::{JSTraceable, RootedTraceable}', 'dom::bindings::trace::{JSTraceable, RootedTraceable}',
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}', 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
'dom::bindings::callback::{CallSetup,ExceptionHandling}', 'dom::bindings::callback::{CallSetup,ExceptionHandling}',
@ -5248,8 +5395,8 @@ class CGBindingRoot(CGThing):
'dom::bindings::error::Error::JSFailed', 'dom::bindings::error::Error::JSFailed',
'dom::bindings::error::throw_dom_exception', 'dom::bindings::error::throw_dom_exception',
'dom::bindings::proxyhandler', 'dom::bindings::proxyhandler',
'dom::bindings::proxyhandler::{fill_property_descriptor, get_expando_object}', 'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
'dom::bindings::proxyhandler::{get_property_descriptor}', 'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
'dom::bindings::num::Finite', 'dom::bindings::num::Finite',
'dom::bindings::str::ByteString', 'dom::bindings::str::ByteString',
'dom::bindings::str::USVString', 'dom::bindings::str::USVString',

View file

@ -139,6 +139,13 @@ class DescriptorProvider:
return self.config.getDescriptor(interfaceName) return self.config.getDescriptor(interfaceName)
def MemberIsUnforgeable(member, descriptor):
return ((member.isAttr() or member.isMethod()) and
not member.isStatic() and
(member.isUnforgeable() or
bool(descriptor.interface.getExtendedAttribute("Unforgeable"))))
class Descriptor(DescriptorProvider): class Descriptor(DescriptorProvider):
""" """
Represents a single descriptor for an interface. See Bindings.conf. Represents a single descriptor for an interface. See Bindings.conf.
@ -174,6 +181,9 @@ class Descriptor(DescriptorProvider):
# them as having a concrete descendant. # them as having a concrete descendant.
self.concrete = (not self.interface.isCallback() and self.concrete = (not self.interface.isCallback() and
desc.get('concrete', True)) desc.get('concrete', True))
self.hasUnforgeableMembers = (self.concrete and
any(MemberIsUnforgeable(m, self) for m in
self.interface.members))
self.operations = { self.operations = {
'IndexedGetter': None, 'IndexedGetter': None,

View file

@ -78,6 +78,10 @@ impl GlobalStaticData {
} }
} }
/// The index of the slot where the object holder of that interface's
/// unforgeable members are defined.
pub const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0;
/// The index of the slot that contains a reference to the ProtoOrIfaceArray. /// The index of the slot that contains a reference to the ProtoOrIfaceArray.
// All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. // All DOM globals must have a slot at DOM_PROTOTYPE_SLOT.
pub const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT; pub const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT;
@ -181,8 +185,12 @@ pub fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray
pub struct NativeProperties { pub struct NativeProperties {
/// Instance methods for the interface. /// Instance methods for the interface.
pub methods: Option<&'static [JSFunctionSpec]>, pub methods: Option<&'static [JSFunctionSpec]>,
/// Unforgeable instance methods for the interface.
pub unforgeable_methods: Option<&'static [JSFunctionSpec]>,
/// Instance attributes for the interface. /// Instance attributes for the interface.
pub attrs: Option<&'static [JSPropertySpec]>, pub attrs: Option<&'static [JSPropertySpec]>,
/// Unforgeable instance attributes for the interface.
pub unforgeable_attrs: Option<&'static [JSPropertySpec]>,
/// Constants for the interface. /// Constants for the interface.
pub consts: Option<&'static [ConstantSpec]>, pub consts: Option<&'static [ConstantSpec]>,
/// Static methods for the interface. /// Static methods for the interface.

View file

@ -79,7 +79,7 @@ enum DocumentReadyState { "loading", "interactive", "complete" };
// [OverrideBuiltins] // [OverrideBuiltins]
partial /*sealed*/ interface Document { partial /*sealed*/ interface Document {
// resource metadata management // resource metadata management
// [PutForwards=href, Unforgeable] [/*PutForwards=href, */Unforgeable]
readonly attribute Location/*?*/ location; readonly attribute Location/*?*/ location;
readonly attribute DOMString domain; readonly attribute DOMString domain;
// readonly attribute DOMString referrer; // readonly attribute DOMString referrer;

View file

@ -4,7 +4,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#location // https://html.spec.whatwg.org/multipage/#location
/*[Unforgeable]*/ interface Location { [Unforgeable] interface Location {
/*stringifier*/ attribute USVString href; /*stringifier*/ attribute USVString href;
// attribute USVString origin; // attribute USVString origin;
attribute USVString protocol; attribute USVString protocol;

View file

@ -11,9 +11,9 @@
//[Replaceable] readonly attribute WindowProxy self; //[Replaceable] readonly attribute WindowProxy self;
readonly attribute Window window; readonly attribute Window window;
[BinaryName="Self_"] readonly attribute Window self; [BinaryName="Self_"] readonly attribute Window self;
/*[Unforgeable]*/ readonly attribute Document document; [Unforgeable] readonly attribute Document document;
// attribute DOMString name; // attribute DOMString name;
/*[PutForwards=href, Unforgeable]*/ readonly attribute Location location; [/*PutForwards=href, */Unforgeable] readonly attribute Location location;
//readonly attribute History history; //readonly attribute History history;
//[Replaceable] readonly attribute BarProp locationbar; //[Replaceable] readonly attribute BarProp locationbar;
//[Replaceable] readonly attribute BarProp menubar; //[Replaceable] readonly attribute BarProp menubar;

View file

@ -12,6 +12,7 @@ use dom::bindings::inheritance::Castable;
use dom::bindings::js::{Root, RootedReference}; use dom::bindings::js::{Root, RootedReference};
use dom::bindings::reflector::{Reflectable, reflect_dom_object}; use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::document::{Document, DocumentSource, IsHTMLDocument}; use dom::document::{Document, DocumentSource, IsHTMLDocument};
use dom::location::Location;
use dom::node::Node; use dom::node::Node;
use dom::window::Window; use dom::window::Window;
use js::jsapi::{JSContext, JSObject}; use js::jsapi::{JSContext, JSObject};
@ -85,6 +86,11 @@ impl XMLDocument {
} }
impl XMLDocumentMethods for XMLDocument { impl XMLDocumentMethods for XMLDocument {
// https://html.spec.whatwg.org/multipage/#dom-document-location
fn Location(&self) -> Root<Location> {
self.document.Location()
}
// https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
fn SupportedPropertyNames(&self) -> Vec<DOMString> { fn SupportedPropertyNames(&self) -> Vec<DOMString> {
self.document.SupportedPropertyNames() self.document.SupportedPropertyNames()

View file

@ -300,9 +300,6 @@
[Window unforgeable attribute: window] [Window unforgeable attribute: window]
expected: FAIL expected: FAIL
[Window unforgeable attribute: document]
expected: FAIL
[Window unforgeable attribute: location] [Window unforgeable attribute: location]
expected: FAIL expected: FAIL

View file

@ -6855,9 +6855,6 @@
[Window interface: window must inherit property "self" with the proper type (1)] [Window interface: window must inherit property "self" with the proper type (1)]
expected: FAIL expected: FAIL
[Window interface: window must have own property "document"]
expected: FAIL
[Window interface: window must inherit property "name" with the proper type (3)] [Window interface: window must inherit property "name" with the proper type (3)]
expected: FAIL expected: FAIL
@ -8490,18 +8487,9 @@
[Window interface: window must inherit property "localStorage" with the proper type (124)] [Window interface: window must inherit property "localStorage" with the proper type (124)]
expected: FAIL expected: FAIL
[Location interface: window.location must have own property "assign"]
expected: FAIL
[Location interface: calling assign(DOMString) on window.location with too few arguments must throw TypeError]
expected: FAIL
[Location interface: window.location must have own property "replace"] [Location interface: window.location must have own property "replace"]
expected: FAIL expected: FAIL
[Location interface: window.location must have own property "reload"]
expected: FAIL
[HTMLOptionElement must be primary interface of new Option()] [HTMLOptionElement must be primary interface of new Option()]
expected: FAIL expected: FAIL
@ -9363,36 +9351,9 @@
[HTMLAreaElement interface: document.createElement("area") must inherit property "hash" with the proper type (19)] [HTMLAreaElement interface: document.createElement("area") must inherit property "hash" with the proper type (19)]
expected: FAIL expected: FAIL
[Location interface: window.location must have own property "href"]
expected: FAIL
[Location interface: window.location must have own property "origin"] [Location interface: window.location must have own property "origin"]
expected: FAIL expected: FAIL
[Location interface: window.location must have own property "protocol"]
expected: FAIL
[Location interface: window.location must have own property "host"]
expected: FAIL
[Location interface: window.location must have own property "hostname"]
expected: FAIL
[Location interface: window.location must have own property "port"]
expected: FAIL
[Location interface: window.location must have own property "pathname"]
expected: FAIL
[Location interface: window.location must have own property "search"]
expected: FAIL
[Location interface: window.location must have own property "hash"]
expected: FAIL
[Location interface: calling assign(USVString) on window.location with too few arguments must throw TypeError]
expected: FAIL
[Location interface: calling replace(USVString) on window.location with too few arguments must throw TypeError] [Location interface: calling replace(USVString) on window.location with too few arguments must throw TypeError]
expected: FAIL expected: FAIL

View file

@ -1,5 +1,5 @@
[windowproxy.html] [windowproxy.html]
type: testharness type: testharness
[Unforgeable location] [document.location is the right thing on non-rendered document]
expected: FAIL expected: FAIL

View file

@ -5,6 +5,7 @@
</head> </head>
<body> <body>
<div></div> <div></div>
<img name="location">
<script> <script>
test(function() { test(function() {
window.abcd = 15; window.abcd = 15;
@ -23,6 +24,18 @@ test(function() {
test(function() { test(function() {
assert_own_property(window, 'location') assert_own_property(window, 'location')
}, "Unforgeable location"); }, "Unforgeable location");
test(function() {
document.foo
assert_equals(document.location, window.location,
'The <img name="location"> should not override the location getter');
}, "document.location is the right thing");
test(function() {
var doc = new DOMParser().parseFromString("<img name='location'>", "text/html");
assert_equals(doc.location, null,
'The <img name="location"> should not override the location getter on a data document');
}, "document.location is the right thing on non-rendered document");
</script> </script>
</body> </body>
</html> </html>