This commit is contained in:
Simon Wülker 2025-06-02 14:13:50 -05:00 committed by GitHub
commit 327c71076f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 142 additions and 55 deletions

View file

@ -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,

View file

@ -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))

View file

@ -16,13 +16,28 @@ typedef (HTMLOrSVGImageElement or
/*VideoFrame or*/
/*CSSImageValue*/ CSSStyleValue) CanvasImageSource;
enum PredefinedColorSpace { "srgb", "display-p3" };
enum CanvasColorType { "unorm8", "float16" };
enum CanvasFillRule { "nonzero", "evenodd" };
dictionary CanvasRenderingContext2DSettings {
boolean alpha = true;
boolean desynchronized = false;
PredefinedColorSpace colorSpace = "srgb";
CanvasColorType colorType = "unorm8";
boolean willReadFrequently = false;
};
enum ImageSmoothingQuality { "low", "medium", "high" };
[Exposed=Window]
interface CanvasRenderingContext2D {
// back-reference to the canvas
readonly attribute HTMLCanvasElement canvas;
};
CanvasRenderingContext2D includes CanvasSettings;
CanvasRenderingContext2D includes CanvasState;
CanvasRenderingContext2D includes CanvasTransform;
CanvasRenderingContext2D includes CanvasCompositing;
@ -40,11 +55,17 @@ CanvasRenderingContext2D includes CanvasPathDrawingStyles;
CanvasRenderingContext2D includes CanvasTextDrawingStyles;
CanvasRenderingContext2D includes CanvasPath;
interface mixin CanvasSettings {
// settings
[Unimplemented] CanvasRenderingContext2DSettings getContextAttributes();
};
interface mixin CanvasState {
// state
undefined save(); // push state on state stack
undefined restore(); // pop state stack and restore state
undefined reset();
undefined reset(); // reset the rendering context to its default state
[Unimplemented] boolean isContextLost(); // return whether context is lost
};
interface mixin CanvasTransform {
@ -66,7 +87,7 @@ interface mixin CanvasTransform {
unrestricted double d,
unrestricted double e,
unrestricted double f);
// void setTransform(optional DOMMatrixInit matrix);
// [Unimplemented] undefined setTransform(optional DOMMatrixInit matrix);
undefined resetTransform();
};
@ -79,7 +100,7 @@ interface mixin CanvasCompositing {
interface mixin CanvasImageSmoothing {
// image smoothing
attribute boolean imageSmoothingEnabled; // (default true)
// attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
[Unimplemented] attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
};
interface mixin CanvasFillStrokeStyles {
@ -89,6 +110,7 @@ interface mixin CanvasFillStrokeStyles {
CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
[Throws]
CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
[Unimplemented] CanvasGradient createConicGradient(double startAngle, double x, double y);
[Throws]
CanvasPattern? createPattern(CanvasImageSource image, [LegacyNullToEmptyString] DOMString repetition);
};
@ -103,7 +125,7 @@ interface mixin CanvasShadowStyles {
interface mixin CanvasFilters {
// filters
//attribute DOMString filter; // (default "none")
[Unimplemented] attribute DOMString filter; // (default "none")
};
interface mixin CanvasRect {
@ -126,15 +148,15 @@ interface mixin CanvasDrawPath {
optional CanvasFillRule fillRule = "nonzero");
boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y,
optional CanvasFillRule fillRule = "nonzero");
//boolean isPointInStroke(unrestricted double x, unrestricted double y);
//boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
[Unimplemented] boolean isPointInStroke(unrestricted double x, unrestricted double y);
[Unimplemented] boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
};
interface mixin CanvasUserInterface {
//void drawFocusIfNeeded(Element element);
//void drawFocusIfNeeded(Path2D path, Element element);
//void scrollPathIntoView();
//void scrollPathIntoView(Path2D path);
[Unimplemented] undefined drawFocusIfNeeded(Element element);
[Unimplemented] undefined drawFocusIfNeeded(Path2D path, Element element);
[Unimplemented] undefined scrollPathIntoView();
[Unimplemented] undefined scrollPathIntoView(Path2D path);
};
interface mixin CanvasText {
@ -142,8 +164,7 @@ interface mixin CanvasText {
[Pref="dom_canvas_text_enabled"]
undefined fillText(DOMString text, unrestricted double x, unrestricted double y,
optional unrestricted double maxWidth);
//void strokeText(DOMString text, unrestricted double x, unrestricted double y,
// optional unrestricted double maxWidth);
[Unimplemented] undefined strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
[Pref="dom_canvas_text_enabled"]
TextMetrics measureText(DOMString text);
};

View file

@ -44,9 +44,8 @@ interface HTMLElement : Element {
// attribute boolean draggable;
// [SameObject, PutForwards=value] readonly attribute DOMTokenList dropzone;
// attribute HTMLMenuElement? contextMenu;
// [CEReactions]
// attribute boolean spellcheck;
// void forceSpellCheck();
[Unimplemented, CEReactions] attribute boolean spellcheck;
[Unimplemented] undefined forceSpellCheck();
[CEReactions] attribute [LegacyNullToEmptyString] DOMString innerText;
[CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString outerText;

View file

@ -7,30 +7,22 @@
interface HTMLLinkElement : HTMLElement {
[HTMLConstructor] constructor();
[CEReactions]
attribute USVString href;
[CEReactions]
attribute DOMString? crossOrigin;
[CEReactions]
attribute DOMString rel;
[CEReactions] attribute USVString href;
[CEReactions] attribute DOMString? crossOrigin;
[CEReactions] attribute DOMString rel;
[CEReactions] attribute DOMString as;
[SameObject, PutForwards=value] readonly attribute DOMTokenList relList;
[CEReactions]
attribute DOMString media;
[CEReactions]
attribute DOMString integrity;
[CEReactions]
attribute DOMString hreflang;
[CEReactions]
attribute DOMString type;
// [SameObject, PutForwards=value] readonly attribute DOMTokenList sizes;
// [CEReactions] attribute USVString imageSrcset;
// [CEReactions] attribute DOMString imageSizes;
[CEReactions]
attribute DOMString referrerPolicy;
// [SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
[CEReactions] attribute DOMString media;
[CEReactions] attribute DOMString integrity;
[CEReactions] attribute DOMString hreflang;
[CEReactions] attribute DOMString type;
[Unimplemented, SameObject, PutForwards=value] readonly attribute DOMTokenList sizes;
[Unimplemented, CEReactions] attribute USVString imageSrcset;
[Unimplemented, CEReactions] attribute DOMString imageSizes;
[CEReactions] attribute DOMString referrerPolicy;
[Unimplemented, SameObject, PutForwards=value] readonly attribute DOMTokenList blocking;
[CEReactions] attribute boolean disabled;
// [CEReactions] attribute DOMString fetchPriority;
[Unimplemented, CEReactions] attribute DOMString fetchPriority;
// also has obsolete members
};
@ -38,10 +30,7 @@ HTMLLinkElement includes LinkStyle;
// https://html.spec.whatwg.org/multipage/#HTMLLinkElement-partial
partial interface HTMLLinkElement {
[CEReactions]
attribute DOMString charset;
[CEReactions]
attribute DOMString rev;
[CEReactions]
attribute DOMString target;
[CEReactions] attribute DOMString charset;
[CEReactions] attribute DOMString rev;
[CEReactions] attribute DOMString target;
};

View file

@ -20,5 +20,5 @@
undefined replace(USVString url);
[Throws] undefined reload();
//[SameObject] readonly attribute USVString[] ancestorOrigins;
[Unimplemented, SameObject] readonly attribute DOMStringList ancestorOrigins;
};

View file

@ -14,7 +14,7 @@ interface TextTrack : EventTarget {
readonly attribute DOMString language;
readonly attribute DOMString id;
// readonly attribute DOMString inBandMetadataTrackDispatchType;
[Unimplemented] readonly attribute DOMString inBandMetadataTrackDispatchType;
attribute TextTrackMode mode;

View file

@ -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
View 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

View file

@ -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