Change bindings generation to make Exposed annotation aware of members/partial interfaces

This commit is contained in:
sreeise 2019-05-28 03:23:47 -04:00
parent 2b84348372
commit 871239a3e3
25 changed files with 151 additions and 78 deletions

View file

@ -1513,7 +1513,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
returnType)
def MemberCondition(pref, func):
def MemberCondition(pref, func, exposed):
"""
A string representing the condition for a member to actually be exposed.
Any of the arguments can be None. If not None, they should have the
@ -1521,14 +1521,18 @@ def MemberCondition(pref, func):
pref: The name of the preference.
func: The name of the function.
exposed: One or more names of an exposed global.
"""
assert pref is None or isinstance(pref, str)
assert func is None or isinstance(func, str)
assert func is None or pref is None
assert exposed is None or isinstance(exposed, set)
assert func is None or pref is None or exposed is None
if pref:
return 'Condition::Pref("%s")' % pref
if func:
return 'Condition::Func(%s)' % func
if exposed:
return ["Condition::Exposed(InterfaceObjectMap::Globals::%s)" % camel_to_upper_snake(i) for i in exposed]
return "Condition::Satisfied"
@ -1571,7 +1575,8 @@ class PropertyDefiner:
PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
"Func"))
"Func"),
interfaceMember.exposedSet())
def generateGuardedArray(self, array, name, specTemplate, specTerminator,
specType, getCondition, getDataTuple):
@ -1609,8 +1614,13 @@ class PropertyDefiner:
if specTerminator:
currentSpecs.append(specTerminator)
specs.append("&[\n" + ",\n".join(currentSpecs) + "]\n")
prefableSpecs.append(
prefableTemplate % (cond, name + "_specs", len(specs) - 1))
if isinstance(cond, list):
for i in cond:
prefableSpecs.append(
prefableTemplate % (i, name + "_specs", len(specs) - 1))
else:
prefableSpecs.append(
prefableTemplate % (cond, name + "_specs", len(specs) - 1))
specsArray = ("const %s_specs: &'static [&'static[%s]] = &[\n" +
",\n".join(specs) + "\n" +
@ -2640,8 +2650,8 @@ def InitUnforgeablePropertiesOnHolder(descriptor, properties):
"""
unforgeables = []
defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s);"
defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s);"
defineUnforgeableAttrs = "define_guarded_properties(cx, unforgeable_holder.handle(), %s, global);"
defineUnforgeableMethods = "define_guarded_methods(cx, unforgeable_holder.handle(), %s, global);"
unforgeableMembers = [
(defineUnforgeableAttrs, properties.unforgeable_attrs),
@ -2751,7 +2761,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
("define_guarded_methods", self.properties.methods),
("define_guarded_constants", self.properties.consts)
]
members = ["%s(cx, obj.handle(), %s);" % (function, array.variableName())
members = ["%s(cx, obj.handle(), %s, obj.handle());" % (function, array.variableName())
for (function, array) in pairs if array.length() > 0]
values["members"] = "\n".join(members)
@ -2966,6 +2976,7 @@ assert!(!prototype_proto.is_null());""" % getPrototypeProto)]
code.append(CGGeneric("""
rooted!(in(cx) let mut prototype = ptr::null_mut::<JSObject>());
create_interface_prototype_object(cx,
global.into(),
prototype_proto.handle().into(),
&PrototypeClass,
%(methods)s,
@ -7543,6 +7554,7 @@ impl %(base)s {
for m in descriptor.interface.members:
if PropertyDefiner.getStringAttr(m, 'Pref') or \
PropertyDefiner.getStringAttr(m, 'Func') or \
PropertyDefiner.getStringAttr(m, 'Exposed') or \
(m.isMethod() and m.isIdentifierLess()):
continue
display = m.identifier.name + ('()' if m.isMethod() else '')

View file

@ -3723,6 +3723,7 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location)
self.tag = tag
self.exposed = set()
if extendedAttrDict is None:
self._extendedAttrDict = {}
else:
@ -3756,12 +3757,16 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
def getExtendedAttribute(self, name):
return self._extendedAttrDict.get(name, None)
def exposedSet(self):
return self.exposed
def finish(self, scope):
# We better be exposed _somewhere_.
if (len(self._exposureGlobalNames) == 0):
print(self.identifier.name)
assert len(self._exposureGlobalNames) != 0
IDLExposureMixins.finish(self, scope)
globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposed)
def validate(self):
if self.isAttr() or self.isMethod():

View file

@ -0,0 +1,27 @@
--- WebIDL.py
+++ WebIDL.py
@@ -3653,6 +3653,7 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location)
self.tag = tag
+ self.exposed = set()
if extendedAttrDict is None:
self._extendedAttrDict = {}
else:
@@ -3686,12 +3687,16 @@ class IDLInterfaceMember(IDLObjectWithIdentifier, IDLExposureMixins):
def getExtendedAttribute(self, name):
return self._extendedAttrDict.get(name, None)
+ def exposedSet(self):
+ return self.exposed
+
def finish(self, scope):
# We better be exposed _somewhere_.
if (len(self._exposureGlobalNames) == 0):
print self.identifier.name
assert len(self._exposureGlobalNames) != 0
IDLExposureMixins.finish(self, scope)
+ globalNameSetToExposureSet(scope, self._exposureGlobalNames, self.exposed)
def validate(self):
if self.isAttr() or self.isMethod():

View file

@ -4,6 +4,7 @@ patch < debug.patch
patch < callback-location.patch
patch < union-typedef.patch
patch < inline.patch
patch < exposed-globals.patch
wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
rm -r tests

View file

@ -4,6 +4,8 @@
//! Machinery to conditionally expose things.
use crate::dom::bindings::codegen::InterfaceObjectMap;
use crate::dom::bindings::interface::is_exposed_in;
use js::jsapi::JSContext;
use js::rust::HandleObject;
use servo_config::prefs;
@ -26,8 +28,13 @@ impl<T: Clone + Copy> Guard<T> {
/// Expose the value if the condition is satisfied.
///
/// The passed handle is the object on which the value may be exposed.
pub unsafe fn expose(&self, cx: *mut JSContext, obj: HandleObject) -> Option<T> {
if self.condition.is_satisfied(cx, obj) {
pub unsafe fn expose(
&self,
cx: *mut JSContext,
obj: HandleObject,
global: HandleObject,
) -> Option<T> {
if self.condition.is_satisfied(cx, obj, global) {
Some(self.value)
} else {
None
@ -41,15 +48,23 @@ pub enum Condition {
Func(unsafe fn(*mut JSContext, HandleObject) -> bool),
/// The condition is satisfied if the preference is set.
Pref(&'static str),
// The condition is satisfied if the interface is exposed in the global.
Exposed(InterfaceObjectMap::Globals),
/// The condition is always satisfied.
Satisfied,
}
impl Condition {
unsafe fn is_satisfied(&self, cx: *mut JSContext, obj: HandleObject) -> bool {
unsafe fn is_satisfied(
&self,
cx: *mut JSContext,
obj: HandleObject,
global: HandleObject,
) -> bool {
match *self {
Condition::Pref(name) => prefs::pref_map().get(name).as_bool().unwrap_or(false),
Condition::Func(f) => f(cx, obj),
Condition::Exposed(globals) => is_exposed_in(global, globals),
Condition::Satisfied => true,
}
}

View file

@ -173,7 +173,7 @@ pub unsafe fn create_callback_interface_object(
assert!(!constants.is_empty());
rval.set(JS_NewObject(cx, ptr::null()));
assert!(!rval.is_null());
define_guarded_constants(cx, rval.handle(), constants);
define_guarded_constants(cx, rval.handle(), constants, global);
define_name(cx, rval.handle(), name);
define_on_global_object(cx, global, name, rval.handle());
}
@ -181,6 +181,7 @@ pub unsafe fn create_callback_interface_object(
/// Create the interface prototype object of a non-callback interface.
pub unsafe fn create_interface_prototype_object(
cx: *mut JSContext,
global: HandleObject,
proto: HandleObject,
class: &'static JSClass,
regular_methods: &[Guard<&'static [JSFunctionSpec]>],
@ -191,6 +192,7 @@ pub unsafe fn create_interface_prototype_object(
) {
create_object(
cx,
global,
proto,
class,
regular_methods,
@ -233,6 +235,7 @@ pub unsafe fn create_noncallback_interface_object(
) {
create_object(
cx,
global,
proto,
class.as_jsclass(),
static_methods,
@ -288,6 +291,7 @@ pub unsafe fn create_named_constructors(
/// Create a new object with a unique type.
pub unsafe fn create_object(
cx: *mut JSContext,
global: HandleObject,
proto: HandleObject,
class: &'static JSClass,
methods: &[Guard<&'static [JSFunctionSpec]>],
@ -297,9 +301,9 @@ pub unsafe fn create_object(
) {
rval.set(JS_NewObjectWithUniqueType(cx, class, proto));
assert!(!rval.is_null());
define_guarded_methods(cx, rval.handle(), methods);
define_guarded_properties(cx, rval.handle(), properties);
define_guarded_constants(cx, rval.handle(), constants);
define_guarded_methods(cx, rval.handle(), methods, global);
define_guarded_properties(cx, rval.handle(), properties, global);
define_guarded_constants(cx, rval.handle(), constants, global);
}
/// Conditionally define constants on an object.
@ -307,9 +311,10 @@ pub unsafe fn define_guarded_constants(
cx: *mut JSContext,
obj: HandleObject,
constants: &[Guard<&[ConstantSpec]>],
global: HandleObject,
) {
for guard in constants {
if let Some(specs) = guard.expose(cx, obj) {
if let Some(specs) = guard.expose(cx, obj, global) {
define_constants(cx, obj, specs);
}
}
@ -320,9 +325,10 @@ pub unsafe fn define_guarded_methods(
cx: *mut JSContext,
obj: HandleObject,
methods: &[Guard<&'static [JSFunctionSpec]>],
global: HandleObject,
) {
for guard in methods {
if let Some(specs) = guard.expose(cx, obj) {
if let Some(specs) = guard.expose(cx, obj, global) {
define_methods(cx, obj, specs).unwrap();
}
}
@ -333,9 +339,10 @@ pub unsafe fn define_guarded_properties(
cx: *mut JSContext,
obj: HandleObject,
properties: &[Guard<&'static [JSPropertySpec]>],
global: HandleObject,
) {
for guard in properties {
if let Some(specs) = guard.expose(cx, obj) {
if let Some(specs) = guard.expose(cx, obj, global) {
define_properties(cx, obj, specs).unwrap();
}
}

View file

@ -37,6 +37,6 @@ pub unsafe fn create_namespace_object(
name: &[u8],
rval: MutableHandleObject,
) {
create_object(cx, proto, &class.0, methods, &[], &[], rval);
create_object(cx, global, proto, &class.0, methods, &[], &[], rval);
define_on_global_object(cx, global, name, rval.handle());
}

View file

@ -1087,6 +1087,18 @@ impl TestBindingMethods for TestBinding {
fn IncumbentGlobal(&self) -> DomRoot<GlobalScope> {
GlobalScope::incumbent().unwrap()
}
fn SemiExposedBoolFromInterface(&self) -> bool {
true
}
fn BoolFromSemiExposedPartialInterface(&self) -> bool {
true
}
fn SemiExposedBoolFromPartialInterface(&self) -> bool {
true
}
}
impl TestBinding {

View file

@ -9,7 +9,7 @@
[
ExceptionClass,
Exposed=(Window,Worker),
Exposed=(Window,Worker,Worklet,DissimilarOriginWindow),
Constructor(optional DOMString message="", optional DOMString name="Error")
]
interface DOMException {

View file

@ -14,7 +14,8 @@
// way to enforce security policy.
// https://html.spec.whatwg.org/multipage/#location
[Unforgeable, NoInterfaceObject] interface DissimilarOriginLocation {
[Exposed=(Window,DissimilarOriginWindow), Unforgeable, NoInterfaceObject]
interface DissimilarOriginLocation {
[Throws] attribute USVString href;
[Throws] void assign(USVString url);
[Throws] void replace(USVString url);

View file

@ -13,7 +13,7 @@
// way to enforce security policy.
// https://html.spec.whatwg.org/multipage/#window
[Global, NoInterfaceObject]
[Global, Exposed=(Window,DissimilarOriginWindow), NoInterfaceObject]
interface DissimilarOriginWindow : GlobalScope {
[Unforgeable] readonly attribute WindowProxy window;
[BinaryName="Self_", Replaceable] readonly attribute WindowProxy self;

View file

@ -5,7 +5,7 @@
* https://dom.spec.whatwg.org/#interface-eventtarget
*/
[Constructor, Exposed=(Window,Worker,Worklet)]
[Constructor, Exposed=(Window,Worker,Worklet,DissimilarOriginWindow)]
interface EventTarget {
void addEventListener(
DOMString type,

View file

@ -5,6 +5,6 @@
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
[Exposed=(Window,Worker,Worklet),
[Exposed=(Window,Worker,Worklet,DissimilarOriginWindow),
Inline]
interface GlobalScope : EventTarget {};

View file

@ -556,6 +556,19 @@ interface TestBinding {
GlobalScope entryGlobal();
GlobalScope incumbentGlobal();
[Exposed=(Window)]
readonly attribute boolean semiExposedBoolFromInterface;
};
[Exposed=(Window)]
partial interface TestBinding {
readonly attribute boolean boolFromSemiExposedPartialInterface;
};
partial interface TestBinding {
[Exposed=(Window)]
readonly attribute boolean semiExposedBoolFromPartialInterface;
};
callback SimpleCallback = void(any value);

View file

@ -3,5 +3,5 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// https://html.spec.whatwg.org/multipage/#the-windowproxy-exotic-object
[NoInterfaceObject]
[Exposed=(Window,DissimilarOriginWindow), NoInterfaceObject]
interface WindowProxy {};

View file

@ -689238,7 +689238,7 @@
"testharness"
],
"xhr/abort-after-send.any.js": [
"41922c915a653ee96e949a3c8ce2aeeb4fd0630c",
"0ffd8877f87f9255668409c1fc9e973d006e6ae9",
"testharness"
],
"xhr/abort-after-stop.any.js": [

View file

@ -630,24 +630,6 @@
[WorkerNavigator interface: self.navigator must not have property "taintEnabled"]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "vendor"]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "vendorSub"]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "productSub"]
expected: FAIL
[WorkerNavigator interface: member vendor]
expected: FAIL
[WorkerNavigator interface: member vendorSub]
expected: FAIL
[WorkerNavigator interface: member productSub]
expected: FAIL
[OffscreenCanvasRenderingContext2D interface: operation arc(unrestricted double, unrestricted double, unrestricted double, unrestricted double, unrestricted double, boolean)]
expected: FAIL

View file

@ -30,24 +30,6 @@
[WorkerNavigator interface: attribute onLine]
expected: FAIL
[WorkerNavigator interface: member productSub]
expected: FAIL
[WorkerNavigator interface: member vendor]
expected: FAIL
[WorkerNavigator interface: member vendorSub]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "productSub"]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "vendor"]
expected: FAIL
[WorkerNavigator interface: self.navigator must not have property "vendorSub"]
expected: FAIL
[WorkerNavigator interface: self.navigator must inherit property "languages" with the proper type (7)]
expected: FAIL

View file

@ -12,9 +12,6 @@
[Resource timing seems to work in workers]
expected: FAIL
[performance.timing is not available in workers]
expected: FAIL
[performance.toJSON is available in workers]
expected: FAIL

View file

@ -18,12 +18,6 @@
[idlharness]
expected: FAIL
[XMLHttpRequest interface: new XMLHttpRequest() must not have property "responseXML"]
expected: FAIL
[XMLHttpRequest interface: member responseXML]
expected: FAIL
[Testing Symbol.iterator property of iterable interface FormData]
expected: FAIL

View file

@ -1,5 +0,0 @@
[responseXML-unavailable-in-worker.html]
type: testharness
[XMLHttpRequest's responseXML property should not be exposed in workers.]
expected: FAIL

View file

@ -9735,6 +9735,9 @@
"mozilla/webgl/tex_image_2d_simple_ref.html": [
[]
],
"mozilla/worker_member_test.js": [
[]
],
"mozilla/worklets/syntax_error.js": [
[]
],
@ -18895,7 +18898,7 @@
"testharness"
],
"mozilla/interface_member_exposed.html": [
"dd637cf92a894e4569e8fb0baf11eea6968033af",
"f408f9c3dae4b78b49bf77b5ad32c0d5ee406f7e",
"testharness"
],
"mozilla/interfaces.html": [
@ -19594,6 +19597,10 @@
"d5c75899eb546d7243d65b6f55e876c5008c6292",
"testharness"
],
"mozilla/worker_member_test.js": [
"abca5cd280ac07914cb21ee4968ac4d27e7feb68",
"support"
],
"mozilla/worklets/syntax_error.js": [
"4adade8939ce62eb5e83d73d4faf2261b264d809",
"support"

View file

@ -43,4 +43,22 @@ for (var i = 0; i < staticMembers.length; i++) {
test_member(name + 'Enabled', true, function(o) { return o; });
test_member(name + 'Disabled', false, function(o) { return o; });
}
members = [
'semiExposedBoolFromInterface',
'boolFromSemiExposedPartialInterface',
'semiExposedBoolFromPartialInterface',
];
for (const member of members) {
var interface = new TestBinding();
async_test(function(t) {
assert_true(member in interface);
assert_true(interface[member]);
let w = new Worker("worker_member_test.js?" + member);
w.onmessage = t.step_func(function(e) {
assert_equals(e.data, undefined);
t.done();
});
}, member);
}
</script>

View file

@ -0,0 +1,3 @@
let member = location.search.slice(1);
var binding = new TestBinding();
postMessage(binding[member]);

View file

@ -9,7 +9,9 @@
client.addEventListener("readystatechange", test.step_func(function() {
if(client.readyState == 4) {
control_flag = true
assert_equals(client.responseXML, null)
if (self.GLOBAL.isWindow()) {
assert_equals(client.responseXML, null)
}
assert_equals(client.responseText, "")
assert_equals(client.status, 0)
assert_equals(client.statusText, "")