mirror of
https://github.com/servo/servo.git
synced 2025-06-06 00:25:37 +00:00
Generate stub implementations for unimplemented dom interfaces
Signed-off-by: Simon Wülker <simon.wuelker@arcor.de>
This commit is contained in:
parent
d42fe20403
commit
0db032a4c9
5 changed files with 89 additions and 11 deletions
|
@ -99,6 +99,15 @@ pub struct Preferences {
|
|||
pub dom_serviceworker_timeout_seconds: i64,
|
||||
pub dom_servo_helpers_enabled: bool,
|
||||
pub dom_servoparser_async_html_tokenizer_enabled: bool,
|
||||
/// When enabled, Servo will create stub implementations for all DOM methods and attributes
|
||||
/// that are known but not implemented. When a page tries to use one of these then a warning
|
||||
/// with the method name will be logged and some appropriate default behaviour will be chosen:
|
||||
/// * Getting the value of a stub attribute will return `undefined`
|
||||
/// * Setting the value of a stub attribute will do nothing
|
||||
/// * Calling a stub method will return `undefined`
|
||||
///
|
||||
/// The warning is dispatched with target `dom-stubs`. Set `RUST_LOG=error,dom-stubs=warn` to see it.
|
||||
pub dom_stub_unimplemented_features: bool,
|
||||
pub dom_svg_enabled: bool,
|
||||
pub dom_testable_crash_enabled: bool,
|
||||
pub dom_testbinding_enabled: bool,
|
||||
|
@ -272,6 +281,7 @@ impl Preferences {
|
|||
dom_serviceworker_timeout_seconds: 60,
|
||||
dom_servo_helpers_enabled: false,
|
||||
dom_servoparser_async_html_tokenizer_enabled: false,
|
||||
dom_stub_unimplemented_features: false,
|
||||
dom_svg_enabled: false,
|
||||
dom_testable_crash_enabled: false,
|
||||
dom_testbinding_enabled: false,
|
||||
|
|
|
@ -417,10 +417,11 @@ class CGMethodCall(CGThing):
|
|||
|
||||
def getPerSignatureCall(signature, argConversionStartsAt=0):
|
||||
signatureIndex = signatures.index(signature)
|
||||
isUnimplemented = method.getExtendedAttribute("Unimplemented") is not None
|
||||
return CGPerSignatureCall(signature[0], argsPre, signature[1],
|
||||
f"{nativeMethodName}{'_' * signatureIndex}",
|
||||
static, descriptor,
|
||||
method, argConversionStartsAt)
|
||||
method, isUnimplemented, argConversionStartsAt)
|
||||
|
||||
if len(signatures) == 1:
|
||||
# Special case: we can just do a per-signature method call
|
||||
|
@ -1614,11 +1615,23 @@ class PropertyDefiner:
|
|||
assert attr[0] is not None
|
||||
return attr[0]
|
||||
|
||||
@staticmethod
|
||||
def hasAttr(member, name) -> bool:
|
||||
attr = member.getExtendedAttribute(name)
|
||||
return attr is not None
|
||||
|
||||
@staticmethod
|
||||
def getControllingCondition(interfaceMember, descriptor):
|
||||
prefCondition = PropertyDefiner.getStringAttr(interfaceMember, "Pref")
|
||||
isUnimplemented = PropertyDefiner.hasAttr(interfaceMember, "Unimplemented")
|
||||
|
||||
if isUnimplemented:
|
||||
if prefCondition is not None:
|
||||
raise TypeError("Cannot preference-gate unimplemented features")
|
||||
prefCondition = "dom_stub_unimplemented_features"
|
||||
|
||||
return MemberCondition(
|
||||
PropertyDefiner.getStringAttr(interfaceMember,
|
||||
"Pref"),
|
||||
prefCondition,
|
||||
PropertyDefiner.getStringAttr(interfaceMember,
|
||||
"Func"),
|
||||
interfaceMember.exposureSet,
|
||||
|
@ -2801,7 +2814,10 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs,
|
|||
traits += ["crate::reflector::DomObjectWrap<Self>"]
|
||||
|
||||
if not descriptor.interface.isCallback() and not descriptor.interface.isIteratorInterface():
|
||||
nonConstMembers = [m for m in descriptor.interface.members if not m.isConst()]
|
||||
nonConstMembers = [
|
||||
m for m in descriptor.interface.members
|
||||
if not m.isConst() and not m.getExtendedAttribute("Unimplemented")
|
||||
]
|
||||
ctor = descriptor.interface.ctor()
|
||||
if (
|
||||
nonConstMembers
|
||||
|
@ -3928,7 +3944,7 @@ def needCx(returnType, arguments, considerTypes):
|
|||
|
||||
class CGCallGenerator(CGThing):
|
||||
"""
|
||||
A class to generate an actual call to a C++ object. Assumes that the C++
|
||||
A class to generate an actual call to a Rust object. Assumes that the Rust
|
||||
object is stored in a variable whose name is given by the |object| argument.
|
||||
|
||||
errorResult should be a string for the value to return in case of an
|
||||
|
@ -3936,7 +3952,7 @@ class CGCallGenerator(CGThing):
|
|||
"""
|
||||
def __init__(self, errorResult, arguments, argsPre, returnType,
|
||||
extendedAttributes, descriptor, nativeMethodName,
|
||||
static, object="this", hasCEReactions=False):
|
||||
static, isUnimplemented: bool, object="this", hasCEReactions=False):
|
||||
CGThing.__init__(self)
|
||||
|
||||
assert errorResult is None or isinstance(errorResult, str)
|
||||
|
@ -3974,6 +3990,17 @@ class CGCallGenerator(CGThing):
|
|||
# Build up our actual call
|
||||
self.cgRoot = CGList([], "\n")
|
||||
|
||||
if isUnimplemented:
|
||||
# Generate a stub implementation that logs a warning and does nothing else
|
||||
logMessage = (
|
||||
f"log::warn!(target: \"dom-stubs\", \""
|
||||
f"Attempt to use unimplemented method or attribute "
|
||||
f"{descriptor.interface.identifier.name}::{nativeMethodName}"
|
||||
f"\");"
|
||||
)
|
||||
self.cgRoot.append(CGGeneric(logMessage))
|
||||
return
|
||||
|
||||
if rootType:
|
||||
self.cgRoot.append(CGList([
|
||||
CGGeneric("rooted!(in(*cx) let mut retval: "),
|
||||
|
@ -4045,7 +4072,7 @@ class CGPerSignatureCall(CGThing):
|
|||
# there.
|
||||
|
||||
def __init__(self, returnType, argsPre, arguments, nativeMethodName, static,
|
||||
descriptor, idlNode, argConversionStartsAt=0,
|
||||
descriptor, idlNode, isUnimplemented: bool, argConversionStartsAt=0,
|
||||
getter=False, setter=False):
|
||||
CGThing.__init__(self)
|
||||
self.returnType = returnType
|
||||
|
@ -4057,6 +4084,8 @@ class CGPerSignatureCall(CGThing):
|
|||
self.argsPre = argsPre
|
||||
self.arguments = arguments
|
||||
self.argCount = len(arguments)
|
||||
self.isUnimplemented = isUnimplemented
|
||||
|
||||
cgThings = []
|
||||
cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgs(),
|
||||
self.getArgc(), self.descriptor,
|
||||
|
@ -4083,7 +4112,7 @@ class CGPerSignatureCall(CGThing):
|
|||
errorResult,
|
||||
self.getArguments(), self.argsPre, returnType,
|
||||
self.extendedAttributes, descriptor, nativeMethodName,
|
||||
static, hasCEReactions=hasCEReactions))
|
||||
static, isUnimplemented, hasCEReactions=hasCEReactions))
|
||||
|
||||
self.cgRoot = CGList(cgThings, "\n")
|
||||
|
||||
|
@ -4100,6 +4129,14 @@ class CGPerSignatureCall(CGThing):
|
|||
return 'infallible' not in self.extendedAttributes
|
||||
|
||||
def wrap_return_value(self):
|
||||
if self.isUnimplemented:
|
||||
# Unimplemented methods don't actually compute a result type, so we stub
|
||||
# it out here
|
||||
return (
|
||||
"args.rval().set(UndefinedValue());\n"
|
||||
"return true;"
|
||||
)
|
||||
|
||||
resultName = "result"
|
||||
# Maplike methods have `any` return values in WebIDL, but our internal bindings
|
||||
# use stronger types so we need to exclude them from being handled like other
|
||||
|
@ -4170,9 +4207,10 @@ class CGGetterCall(CGPerSignatureCall):
|
|||
getter.
|
||||
"""
|
||||
def __init__(self, argsPre, returnType, nativeMethodName, descriptor, attr):
|
||||
isUnimplemented = attr.getExtendedAttribute("Unimplemented") is not None
|
||||
CGPerSignatureCall.__init__(self, returnType, argsPre, [],
|
||||
nativeMethodName, attr.isStatic(), descriptor,
|
||||
attr, getter=True)
|
||||
attr, isUnimplemented, getter=True)
|
||||
|
||||
|
||||
class FakeArgument():
|
||||
|
@ -4197,9 +4235,10 @@ class CGSetterCall(CGPerSignatureCall):
|
|||
setter.
|
||||
"""
|
||||
def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr):
|
||||
isUnimplemented = attr.getExtendedAttribute("Unimplemented") is not None
|
||||
CGPerSignatureCall.__init__(self, None, argsPre,
|
||||
[FakeArgument(argType, attr, allowTreatNonObjectAsNull=True)],
|
||||
nativeMethodName, attr.isStatic(), descriptor, attr,
|
||||
nativeMethodName, attr.isStatic(), descriptor, attr, isUnimplemented,
|
||||
setter=True)
|
||||
|
||||
def wrap_return_value(self):
|
||||
|
@ -5826,8 +5865,9 @@ class CGProxySpecialOperation(CGPerSignatureCall):
|
|||
|
||||
# We pass len(arguments) as the final argument so that the
|
||||
# CGPerSignatureCall won't do any argument conversion of its own.
|
||||
isUnimplemented = False
|
||||
CGPerSignatureCall.__init__(self, returnType, "", arguments, nativeName,
|
||||
False, descriptor, operation,
|
||||
False, descriptor, operation, isUnimplemented,
|
||||
len(arguments))
|
||||
|
||||
if operation.isSetter():
|
||||
|
@ -6687,6 +6727,11 @@ class CGInterfaceTrait(CGThing):
|
|||
|
||||
def members():
|
||||
for m in descriptor.interface.members:
|
||||
if m.getExtendedAttribute("Unimplemented"):
|
||||
# Unimplemented methods or attributes generate a stub implementation and should
|
||||
# therefore not be part of the trait
|
||||
continue
|
||||
|
||||
if (m.isMethod()
|
||||
and not m.isMaplikeOrSetlikeOrIterableMethod()
|
||||
and (not m.isIdentifierLess() or (m.isStringifier() and not m.underlyingAttr))
|
||||
|
|
2
third_party/WebIDL/WebIDL.py
vendored
2
third_party/WebIDL/WebIDL.py
vendored
|
@ -5939,6 +5939,7 @@ class IDLAttribute(IDLInterfaceMember):
|
|||
or identifier == "BinaryName"
|
||||
or identifier == "NonEnumerable"
|
||||
or identifier == "BindingTemplate"
|
||||
or identifier == "Unimplemented"
|
||||
):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
|
@ -7018,6 +7019,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
|||
or identifier == "NonEnumerable"
|
||||
or identifier == "Unexposed"
|
||||
or identifier == "WebExtensionStub"
|
||||
or identifier == "Unimplemented"
|
||||
):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
|
|
20
third_party/WebIDL/dom-stubs.patch
vendored
Normal file
20
third_party/WebIDL/dom-stubs.patch
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
diff --git a/third_party/WebIDL/WebIDL.py b/third_party/WebIDL/WebIDL.py
|
||||
index 40e118e378..437e3640a6 100644
|
||||
--- a/third_party/WebIDL/WebIDL.py
|
||||
+++ b/third_party/WebIDL/WebIDL.py
|
||||
@@ -5938,6 +5938,7 @@ class IDLAttribute(IDLInterfaceMember):
|
||||
or identifier == "BinaryName"
|
||||
or identifier == "NonEnumerable"
|
||||
or identifier == "BindingTemplate"
|
||||
+ or identifier == "Unimplemented"
|
||||
):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
||||
@@ -7017,6 +7018,7 @@ class IDLMethod(IDLInterfaceMember, IDLScope):
|
||||
or identifier == "NonEnumerable"
|
||||
or identifier == "Unexposed"
|
||||
or identifier == "WebExtensionStub"
|
||||
+ or identifier == "Unimplemented"
|
||||
):
|
||||
# Known attributes that we don't need to do anything with here
|
||||
pass
|
1
third_party/WebIDL/update.sh
vendored
1
third_party/WebIDL/update.sh
vendored
|
@ -8,6 +8,7 @@ patch < like-as-iterable.patch
|
|||
patch < builtin-array.patch
|
||||
patch < array-type.patch
|
||||
patch < transferable.patch
|
||||
patch < dom-stubs.patch
|
||||
|
||||
wget https://hg.mozilla.org/mozilla-central/archive/tip.zip/dom/bindings/parser/tests/ -O tests.zip
|
||||
rm -r tests
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue