Skip to content

Commit

Permalink
Change bindings generation to make Exposed annotation aware of member…
Browse files Browse the repository at this point in the history
…s/partial interfaces
  • Loading branch information
sreeise committed Jul 14, 2019
1 parent 2b84348 commit 871239a
Show file tree
Hide file tree
Showing 25 changed files with 151 additions and 78 deletions.
28 changes: 20 additions & 8 deletions components/script/dom/bindings/codegen/CodegenRust.py
Expand Up @@ -1513,22 +1513,26 @@ 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
following types:
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"


Expand Down Expand Up @@ -1571,7 +1575,8 @@ def getControllingCondition(interfaceMember, descriptor):
PropertyDefiner.getStringAttr(interfaceMember,
"Pref"),
PropertyDefiner.getStringAttr(interfaceMember,
"Func"))
"Func"),
interfaceMember.exposedSet())

def generateGuardedArray(self, array, name, specTemplate, specTerminator,
specType, getCondition, getDataTuple):
Expand Down Expand Up @@ -1609,8 +1614,13 @@ def generateGuardedArray(self, array, name, specTemplate, specTerminator,
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" +
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -2751,7 +2761,7 @@ def definition_body(self):
("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)

Expand Down Expand Up @@ -2966,6 +2976,7 @@ def definition_body(self):
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,
Expand Down Expand Up @@ -7543,6 +7554,7 @@ def SupportedDomApis(config):
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 '')
Expand Down
5 changes: 5 additions & 0 deletions components/script/dom/bindings/codegen/parser/WebIDL.py
Expand Up @@ -3723,6 +3723,7 @@ def __init__(self, location, identifier, tag, extendedAttrDict=None):
IDLObjectWithIdentifier.__init__(self, location, None, identifier)
IDLExposureMixins.__init__(self, location)
self.tag = tag
self.exposed = set()
if extendedAttrDict is None:
self._extendedAttrDict = {}
else:
Expand Down Expand Up @@ -3756,12 +3757,16 @@ def handleExtendedAttribute(self, attr):
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():
Expand Down
@@ -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():
1 change: 1 addition & 0 deletions components/script/dom/bindings/codegen/parser/update.sh
Expand Up @@ -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
Expand Down
21 changes: 18 additions & 3 deletions components/script/dom/bindings/guard.rs
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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,
}
}
Expand Down
21 changes: 14 additions & 7 deletions components/script/dom/bindings/interface.rs
Expand Up @@ -173,14 +173,15 @@ 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());
}

/// 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]>],
Expand All @@ -191,6 +192,7 @@ pub unsafe fn create_interface_prototype_object(
) {
create_object(
cx,
global,
proto,
class,
regular_methods,
Expand Down Expand Up @@ -233,6 +235,7 @@ pub unsafe fn create_noncallback_interface_object(
) {
create_object(
cx,
global,
proto,
class.as_jsclass(),
static_methods,
Expand Down Expand Up @@ -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]>],
Expand All @@ -297,19 +301,20 @@ 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.
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);
}
}
Expand All @@ -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();
}
}
Expand All @@ -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();
}
}
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/bindings/namespace.rs
Expand Up @@ -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());
}
12 changes: 12 additions & 0 deletions components/script/dom/testbinding.rs
Expand Up @@ -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 {
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/webidls/DOMException.webidl
Expand Up @@ -9,7 +9,7 @@

[
ExceptionClass,
Exposed=(Window,Worker),
Exposed=(Window,Worker,Worklet,DissimilarOriginWindow),
Constructor(optional DOMString message="", optional DOMString name="Error")
]
interface DOMException {
Expand Down
Expand Up @@ -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);
Expand Down
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/webidls/EventTarget.webidl
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/webidls/GlobalScope.webidl
Expand Up @@ -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 {};
13 changes: 13 additions & 0 deletions components/script/dom/webidls/TestBinding.webidl
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion components/script/dom/webidls/WindowProxy.webidl
Expand Up @@ -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 {};
2 changes: 1 addition & 1 deletion tests/wpt/metadata/MANIFEST.json
Expand Up @@ -689238,7 +689238,7 @@
"testharness"
],
"xhr/abort-after-send.any.js": [
"41922c915a653ee96e949a3c8ce2aeeb4fd0630c",
"0ffd8877f87f9255668409c1fc9e973d006e6ae9",
"testharness"
],
"xhr/abort-after-stop.any.js": [
Expand Down

0 comments on commit 871239a

Please sign in to comment.