diff options
Diffstat (limited to 'components/script/dom')
81 files changed, 3841 insertions, 1195 deletions
diff --git a/components/script/dom/audiocontext.rs b/components/script/dom/audiocontext.rs index d5b1c3bdb56..b81caed793b 100644 --- a/components/script/dom/audiocontext.rs +++ b/components/script/dom/audiocontext.rs @@ -75,9 +75,7 @@ impl AudioContext { #[allow(unrooted_must_root)] pub fn new(window: &Window, options: &AudioContextOptions) -> DomRoot<AudioContext> { - let pipeline_id = window - .pipeline_id() - .expect("Cannot create AudioContext outside of a pipeline"); + let pipeline_id = window.pipeline_id(); let context = AudioContext::new_inherited(options, pipeline_id); let context = reflect_dom_object(Box::new(context), window, AudioContextBinding::Wrap); context.resume(); diff --git a/components/script/dom/baseaudiocontext.rs b/components/script/dom/baseaudiocontext.rs index c84d62f6dc3..14c6bc94e1c 100644 --- a/components/script/dom/baseaudiocontext.rs +++ b/components/script/dom/baseaudiocontext.rs @@ -331,7 +331,7 @@ impl BaseAudioContextMethods for BaseAudioContext { self.listener.or_init(|| AudioListener::new(&window, self)) } - /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-onstatechange + // https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-onstatechange event_handler!(statechange, GetOnstatechange, SetOnstatechange); /// https://webaudio.github.io/web-audio-api/#dom-baseaudiocontext-createoscillator diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index 5f978f5d3c4..7aa8ffa4c8b 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -150,6 +150,10 @@ DOMInterfaces = { 'GPUAdapter': { 'inRealms': ['RequestDevice'], +}, + +'GPUBuffer': { + 'inRealms': ['MapReadAsync'], } } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index ce1487d5fa6..fab5e1e713a 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -427,17 +427,6 @@ class CGMethodCall(CGThing): (s[1][distinguishingIndex].type.isSequence() or s[1][distinguishingIndex].type.isObject())) - # Check for Date objects - # XXXbz Do we need to worry about security wrappers around the Date? - pickFirstSignature("%s.get().is_object() && " - "{ rooted!(in(*cx) let obj = %s.get().to_object()); " - "let mut is_date = false; " - "assert!(ObjectIsDate(*cx, obj.handle(), &mut is_date)); " - "is_date }" % - (distinguishingArg, distinguishingArg), - lambda s: (s[1][distinguishingIndex].type.isDate() or - s[1][distinguishingIndex].type.isObject())) - # Check for vanilla JS objects # XXXbz Do we need to worry about security wrappers? pickFirstSignature("%s.get().is_object() && !is_platform_object(%s.get().to_object(), *cx)" % @@ -596,8 +585,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # We should not have a defaultValue if we know we're an object assert not isDefinitelyObject or defaultValue is None - isEnforceRange = type.enforceRange - isClamp = type.clamp + isEnforceRange = type.hasEnforceRange() + isClamp = type.hasClamp() if type.treatNullAsEmpty: treatNullAs = "EmptyString" else: @@ -1654,7 +1643,7 @@ class MethodDefiner(PropertyDefiner): if any(m.isGetter() and m.isIndexed() for m in methods): self.regular.append({"name": '@@iterator', "methodInfo": False, - "selfHostedName": "ArrayValues", + "selfHostedName": "$ArrayValues", "length": 0, "flags": "0", # Not enumerable, per spec. "condition": "Condition::Satisfied"}) @@ -1665,6 +1654,8 @@ class MethodDefiner(PropertyDefiner): (maplikeOrSetlikeOrIterable and maplikeOrSetlikeOrIterable.isIterable() and maplikeOrSetlikeOrIterable.isValueIterator())): + m = maplikeOrSetlikeOrIterable + # Add our keys/values/entries/forEach self.regular.append({ "name": "keys", @@ -1678,7 +1669,7 @@ class MethodDefiner(PropertyDefiner): self.regular.append({ "name": "values", "methodInfo": False, - "selfHostedName": "ArrayValues", + "selfHostedName": "$ArrayValues", "length": 0, "flags": "JSPROP_ENUMERATE", "condition": PropertyDefiner.getControllingCondition(m, @@ -1731,7 +1722,7 @@ class MethodDefiner(PropertyDefiner): selfHostedName = '%s as *const u8 as *const libc::c_char' % str_to_const_array(m["selfHostedName"]) assert not m.get("methodInfo", True) accessor = "None" - jitinfo = "0 as *const JSJitInfo" + jitinfo = "ptr::null()" else: selfHostedName = "0 as *const libc::c_char" if m.get("methodInfo", True): @@ -1743,28 +1734,30 @@ class MethodDefiner(PropertyDefiner): jitinfo = "&%s_methodinfo as *const _ as *const JSJitInfo" % identifier accessor = "Some(generic_method)" else: - jitinfo = "0 as *const JSJitInfo" + jitinfo = "ptr::null()" accessor = 'Some(%s)' % m.get("nativeName", m["name"]) if m["name"].startswith("@@"): - return ('(SymbolCode::%s as i32 + 1)' - % m["name"][2:], accessor, jitinfo, m["length"], flags, selfHostedName) - return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], flags, selfHostedName) + name = 'JSPropertySpec_Name { symbol_: SymbolCode::%s as usize + 1 }' % m["name"][2:] + else: + name = ('JSPropertySpec_Name { string_: %s as *const u8 as *const libc::c_char }' + % str_to_const_array(m["name"])) + return (name, accessor, jitinfo, m["length"], flags, selfHostedName) return self.generateGuardedArray( array, name, ' JSFunctionSpec {\n' - ' name: %s as *const u8 as *const libc::c_char,\n' + ' name: %s,\n' ' call: JSNativeWrapper { op: %s, info: %s },\n' ' nargs: %s,\n' ' flags: (%s) as u16,\n' ' selfHostedName: %s\n' ' }', ' JSFunctionSpec {\n' - ' name: 0 as *const libc::c_char,\n' - ' call: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo },\n' + ' name: JSPropertySpec_Name { string_: ptr::null() },\n' + ' call: JSNativeWrapper { op: None, info: ptr::null() },\n' ' nargs: 0,\n' ' flags: 0,\n' - ' selfHostedName: 0 as *const libc::c_char\n' + ' selfHostedName: ptr::null()\n' ' }', 'JSFunctionSpec', condition, specData) @@ -1834,14 +1827,14 @@ class AttrDefiner(PropertyDefiner): return self.generateGuardedArray( array, name, ' JSPropertySpec {\n' - ' name: %s as *const u8 as *const libc::c_char,\n' + ' name: JSPropertySpec_Name { string_: %s as *const u8 as *const libc::c_char },\n' ' flags: (%s) as u8,\n' - ' __bindgen_anon_1: JSPropertySpec__bindgen_ty_1 {\n' - ' accessors: JSPropertySpec__bindgen_ty_1__bindgen_ty_1 {\n' - ' getter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 {\n' + ' u: JSPropertySpec_AccessorsOrValue {\n' + ' accessors: JSPropertySpec_AccessorsOrValue_Accessors {\n' + ' getter: JSPropertySpec_Accessor {\n' ' native: %s,\n' ' },\n' - ' setter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 {\n' + ' setter: JSPropertySpec_Accessor {\n' ' native: %s,\n' ' }\n' ' }\n' @@ -2203,7 +2196,9 @@ static Class: DOMJSClass = DOMJSClass { (((%(slots)s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT) /* JSCLASS_HAS_RESERVED_SLOTS(%(slots)s) */, cOps: &CLASS_OPS, - reserved: [0 as *mut _; 3], + spec: ptr::null(), + ext: ptr::null(), + oOps: ptr::null(), }, dom_class: %(domClass)s }; @@ -2274,7 +2269,9 @@ static PrototypeClass: JSClass = JSClass { // JSCLASS_HAS_RESERVED_SLOTS(%(slotCount)s) (%(slotCount)s & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT, cOps: 0 as *const _, - reserved: [0 as *mut os::raw::c_void; 3] + spec: ptr::null(), + ext: ptr::null(), + oOps: ptr::null(), }; """ % {'name': name, 'slotCount': slotCount} @@ -2632,35 +2629,6 @@ class CGConstructorEnabled(CGAbstractMethod): return CGList((CGGeneric(cond) for cond in conditions), " &&\n") -def CreateBindingJSObject(descriptor): - assert not descriptor.isGlobal() - create = "let raw = Box::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" - if descriptor.proxy: - create += """ -let handler = RegisterBindings::PROXY_HANDLERS[PrototypeList::Proxies::%s as usize]; -rooted!(in(*cx) let private = PrivateValue(raw as *const libc::c_void)); -let obj = NewProxyObject(*cx, handler, - Handle::from_raw(UndefinedHandleValue), - proto.get()); -assert!(!obj.is_null()); -SetProxyReservedSlot(obj, 0, &private.get()); -rooted!(in(*cx) let obj = obj);\ -""" % (descriptor.name) - else: - create += ("rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto(\n" - " *cx, &Class.base as *const JSClass, proto.handle()));\n" - "assert!(!obj.is_null());\n" - "\n" - "let val = PrivateValue(raw as *const libc::c_void);\n" - "\n" - "JS_SetReservedSlot(obj.get(), DOM_OBJECT_SLOT, &val);") - if descriptor.weakReferenceable: - create += """ -let val = PrivateValue(ptr::null()); -JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, &val);""" - return create - - def InitUnforgeablePropertiesOnHolder(descriptor, properties): """ Define the unforgeable properties on the unforgeable holder for @@ -2738,23 +2706,62 @@ class CGWrapMethod(CGAbstractMethod): def definition_body(self): unforgeable = CopyUnforgeablePropertiesToInstance(self.descriptor) - create = CreateBindingJSObject(self.descriptor) + if self.descriptor.proxy: + create = """ +let handler = RegisterBindings::PROXY_HANDLERS[PrototypeList::Proxies::%(concreteType)s as usize]; +rooted!(in(*cx) let obj = NewProxyObject( + *cx, + handler, + Handle::from_raw(UndefinedHandleValue), + proto.get(), +)); +assert!(!obj.is_null()); +SetProxyReservedSlot( + obj.get(), + 0, + &PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void), +); +""" + else: + create = """ +rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto( + *cx, + &Class.base, + proto.handle(), +)); +assert!(!obj.is_null()); +JS_SetReservedSlot( + obj.get(), + DOM_OBJECT_SLOT, + &PrivateValue(raw.as_ptr() as *const %(concreteType)s as *const libc::c_void), +); +""" + create = create % {"concreteType": self.descriptor.concreteType} + if self.descriptor.weakReferenceable: + create += """ +let val = PrivateValue(ptr::null()); +JS_SetReservedSlot(obj.get(), DOM_WEAK_SLOT, &val); +""" + return CGGeneric("""\ +let raw = Root::new(MaybeUnreflectedDom::from_box(object)); + let scope = scope.reflector().get_jsobject(); assert!(!scope.get().is_null()); assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); +let _ac = JSAutoRealm::new(*cx, scope.get()); rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>()); -let _ac = JSAutoRealm::new(*cx, scope.get()); GetProtoObject(cx, scope, proto.handle_mut()); assert!(!proto.is_null()); %(createObject)s +let root = raw.reflect_with(obj.get()); %(copyUnforgeable)s -(*raw).init_reflector(obj.get()); -DomRoot::from_ref(&*raw)""" % {'copyUnforgeable': unforgeable, 'createObject': create}) +DomRoot::from_ref(&*root)\ +""" % {'copyUnforgeable': unforgeable, 'createObject': create}) class CGWrapGlobalMethod(CGAbstractMethod): @@ -2773,6 +2780,7 @@ class CGWrapGlobalMethod(CGAbstractMethod): def definition_body(self): values = { + "concreteType": self.descriptor.concreteType, "unforgeable": CopyUnforgeablePropertiesToInstance(self.descriptor) } @@ -2786,19 +2794,18 @@ class CGWrapGlobalMethod(CGAbstractMethod): values["members"] = "\n".join(members) return CGGeneric("""\ -let raw = Box::into_raw(object); -let _rt = RootedTraceable::new(&*raw); +let raw = Root::new(MaybeUnreflectedDom::from_box(object)); rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>()); create_global_object( cx, &Class.base, - raw as *const libc::c_void, + raw.as_ptr() as *const %(concreteType)s as *const libc::c_void, _trace, obj.handle_mut()); assert!(!obj.is_null()); -(*raw).init_reflector(obj.get()); +let root = raw.reflect_with(obj.get()); let _ac = JSAutoRealm::new(*cx, obj.get()); rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>()); @@ -2812,7 +2819,7 @@ assert!(immutable); %(unforgeable)s -DomRoot::from_ref(&*raw)\ +DomRoot::from_ref(&*root)\ """ % values) @@ -2906,9 +2913,9 @@ class CGCollectJSONAttributesMethod(CGAbstractMethod): Generate the CollectJSONAttributes method for an interface descriptor """ def __init__(self, descriptor, toJSONMethod): - args = [Argument('SafeJSContext', 'cx'), - Argument('HandleObject', 'obj'), - Argument('*const %s' % descriptor.concreteType, 'this'), + args = [Argument('*mut JSContext', 'cx'), + Argument('RawHandleObject', 'obj'), + Argument('*mut libc::c_void', 'this'), Argument('&RootedGuard<*mut JSObject>', 'result')] CGAbstractMethod.__init__(self, descriptor, 'CollectJSONAttributes', 'bool', args, pub=True, unsafe=True) @@ -2922,11 +2929,11 @@ class CGCollectJSONAttributesMethod(CGAbstractMethod): name = m.identifier.name ret += fill( """ - rooted!(in(*cx) let mut temp = UndefinedValue()); + rooted!(in(cx) let mut temp = UndefinedValue()); if !get_${name}(cx, obj, this, JSJitGetterCallArgs { _base: temp.handle_mut().into() }) { return false; } - if !JS_DefineProperty(*cx, result.handle().into(), + if !JS_DefineProperty(cx, result.handle().into(), ${nameAsArray} as *const u8 as *const libc::c_char, temp.handle(), JSPROP_ENUMERATE as u32) { return false; @@ -3658,8 +3665,9 @@ class CGSpecializedMethod(CGAbstractExternMethod): def __init__(self, descriptor, method): self.method = method name = method.identifier.name - args = [Argument('SafeJSContext', 'cx'), Argument('HandleObject', '_obj'), - Argument('*const %s' % descriptor.concreteType, 'this'), + args = [Argument('*mut JSContext', 'cx'), + Argument('RawHandleObject', '_obj'), + Argument('*mut libc::c_void', 'this'), Argument('*const JSJitMethodCallArgs', 'args')] CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args) @@ -3668,7 +3676,8 @@ class CGSpecializedMethod(CGAbstractExternMethod): self.method) return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), self.descriptor, self.method), - pre="let this = &*this;\n" + pre="let cx = SafeJSContext::from_ptr(cx);\n" + + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType) + "let args = &*args;\n" "let argc = args.argc_;\n") @@ -3691,7 +3700,7 @@ class CGDefaultToJSONMethod(CGSpecializedMethod): def definition_body(self): ret = dedent(""" use crate::dom::bindings::inheritance::HasParent; - rooted!(in(*cx) let result = JS_NewPlainObject(*cx)); + rooted!(in(cx) let result = JS_NewPlainObject(cx)); if result.is_null() { return false; } @@ -3707,17 +3716,16 @@ class CGDefaultToJSONMethod(CGSpecializedMethod): parents = len(jsonDescriptors) - 1 form = """ - if !${parentclass}CollectJSONAttributes(cx, _obj, this${asparent}, &result) { + if !${parentclass}CollectJSONAttributes(cx, _obj, this, &result) { return false; } """ # Iterate the array in reverse: oldest ancestor first for descriptor in jsonDescriptors[:0:-1]: - ret += fill(form, parentclass=toBindingNamespace(descriptor.name) + "::", - asparent=".as_ref().unwrap()" + ".as_parent()" * parents) + ret += fill(form, parentclass=toBindingNamespace(descriptor.name) + "::") parents -= 1 - ret += fill(form, parentclass="", asparent="") + ret += fill(form, parentclass="") ret += ('(*args).rval().set(ObjectValue(*result));\n' 'return true;\n') return CGGeneric(ret) @@ -3749,9 +3757,9 @@ class CGSpecializedGetter(CGAbstractExternMethod): def __init__(self, descriptor, attr): self.attr = attr name = 'get_' + descriptor.internalNameFor(attr.identifier.name) - args = [Argument('SafeJSContext', 'cx'), - Argument('HandleObject', '_obj'), - Argument('*const %s' % descriptor.concreteType, 'this'), + args = [Argument('*mut JSContext', 'cx'), + Argument('RawHandleObject', '_obj'), + Argument('*mut libc::c_void', 'this'), Argument('JSJitGetterCallArgs', 'args')] CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) @@ -3761,7 +3769,8 @@ class CGSpecializedGetter(CGAbstractExternMethod): return CGWrapper(CGGetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), - pre="let this = &*this;\n") + pre="let cx = SafeJSContext::from_ptr(cx);\n" + + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType)) @staticmethod def makeNativeName(descriptor, attr): @@ -3805,9 +3814,9 @@ class CGSpecializedSetter(CGAbstractExternMethod): def __init__(self, descriptor, attr): self.attr = attr name = 'set_' + descriptor.internalNameFor(attr.identifier.name) - args = [Argument('SafeJSContext', 'cx'), - Argument('HandleObject', 'obj'), - Argument('*const %s' % descriptor.concreteType, 'this'), + args = [Argument('*mut JSContext', 'cx'), + Argument('RawHandleObject', 'obj'), + Argument('*mut libc::c_void', 'this'), Argument('JSJitSetterCallArgs', 'args')] CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) @@ -3816,7 +3825,8 @@ class CGSpecializedSetter(CGAbstractExternMethod): self.attr) return CGWrapper(CGSetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), - pre="let this = &*this;\n") + pre="let cx = SafeJSContext::from_ptr(cx);\n" + + ("let this = &*(this as *const %s);\n" % self.descriptor.concreteType)) @staticmethod def makeNativeName(descriptor, attr): @@ -3865,8 +3875,9 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter): assert all(ord(c) < 128 for c in attrName) assert all(ord(c) < 128 for c in forwardToAttrName) return CGGeneric("""\ +let cx = SafeJSContext::from_ptr(cx); rooted!(in(*cx) let mut v = UndefinedValue()); -if !JS_GetProperty(*cx, obj, %s as *const u8 as *const libc::c_char, v.handle_mut()) { +if !JS_GetProperty(*cx, HandleObject::from_raw(obj), %s as *const u8 as *const libc::c_char, v.handle_mut()) { return false; } if !v.is_object() { @@ -3891,7 +3902,7 @@ class CGSpecializedReplaceableSetter(CGSpecializedSetter): # JS_DefineProperty can only deal with ASCII. assert all(ord(c) < 128 for c in name) return CGGeneric("""\ -JS_DefineProperty(*cx, obj, %s as *const u8 as *const libc::c_char, +JS_DefineProperty(cx, HandleObject::from_raw(obj), %s as *const u8 as *const libc::c_char, HandleValue::from_raw(args.get(0)), JSPROP_ENUMERATE as u32)""" % name) @@ -3921,27 +3932,34 @@ class CGMemberJITInfo(CGThing): initializer = fill( """ JSJitInfo { - call: ${opName} as *const os::raw::c_void, - protoID: PrototypeList::ID::${name} as u16, - depth: ${depth}, - _bitfield_1: new_jsjitinfo_bitfield_1!( - JSJitInfo_OpType::${opType} as u8, - JSJitInfo_AliasSet::${aliasSet} as u8, - JSValueType::${returnType} as u8, - ${isInfallible}, - ${isMovable}, - ${isEliminatable}, - ${isAlwaysInSlot}, - ${isLazilyCachedInSlot}, - ${isTypedMethod}, - ${slotIndex}, - ), + __bindgen_anon_1: JSJitInfo__bindgen_ty_1 { + ${opKind}: Some(${opName}) + }, + __bindgen_anon_2: JSJitInfo__bindgen_ty_2 { + protoID: PrototypeList::ID::${name} as u16, + }, + __bindgen_anon_3: JSJitInfo__bindgen_ty_3 { depth: ${depth} }, + _bitfield_1: unsafe { + mem::transmute(new_jsjitinfo_bitfield_1!( + JSJitInfo_OpType::${opType} as u8, + JSJitInfo_AliasSet::${aliasSet} as u8, + JSValueType::${returnType} as u8, + ${isInfallible}, + ${isMovable}, + ${isEliminatable}, + ${isAlwaysInSlot}, + ${isLazilyCachedInSlot}, + ${isTypedMethod}, + ${slotIndex}, + )) + }, } """, opName=opName, name=self.descriptor.name, depth=self.descriptor.interface.inheritanceDepth(), opType=opType, + opKind=opType.lower(), aliasSet=aliasSet, returnType=functools.reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, ""), @@ -4135,8 +4153,6 @@ class CGMemberJITInfo(CGThing): u.flatMemberTypes, "") if t.isDictionary(): return "JSVAL_TYPE_OBJECT" - if t.isDate(): - return "JSVAL_TYPE_OBJECT" if not t.isPrimitive(): raise TypeError("No idea what type " + str(t) + " is.") tag = t.tag() @@ -4201,13 +4217,11 @@ class CGMemberJITInfo(CGThing): return "JSJitInfo_ArgType::Object as i32" if t.isUnion(): u = t.unroll() - type = "JSJitInfo::Null as i32" if u.hasNullableType else "" + type = "JSJitInfo_ArgType::Null as i32" if u.hasNullableType else "" return functools.reduce(CGMemberJITInfo.getSingleArgType, u.flatMemberTypes, type) if t.isDictionary(): return "JSJitInfo_ArgType::Object as i32" - if t.isDate(): - return "JSJitInfo_ArgType::Object as i32" if not t.isPrimitive(): raise TypeError("No idea what type " + str(t) + " is.") tag = t.tag() @@ -4513,13 +4527,6 @@ class CGUnionConversionStruct(CGThing): else: arrayObject = None - dateObjectMemberTypes = filter(lambda t: t.isDate(), memberTypes) - if len(dateObjectMemberTypes) > 0: - assert len(dateObjectMemberTypes) == 1 - raise TypeError("Can't handle dates in unions.") - else: - dateObject = None - callbackMemberTypes = filter(lambda t: t.isCallback() or t.isCallbackInterface(), memberTypes) if len(callbackMemberTypes) > 0: assert len(callbackMemberTypes) == 1 @@ -4555,10 +4562,10 @@ class CGUnionConversionStruct(CGThing): else: mozMapObject = None - hasObjectTypes = object or interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject + hasObjectTypes = object or interfaceObject or arrayObject or callbackObject or mozMapObject if hasObjectTypes: # "object" is not distinguishable from other types - assert not object or not (interfaceObject or arrayObject or dateObject or callbackObject or mozMapObject) + assert not object or not (interfaceObject or arrayObject or callbackObject or mozMapObject) templateBody = CGList([], "\n") if object: templateBody.append(object) @@ -5301,7 +5308,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): def __init__(self, descriptor): args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'), - Argument('*mut AutoIdVector', 'props')] + Argument('RawMutableHandleIdVector', 'props')] CGAbstractExternMethod.__init__(self, descriptor, "own_property_keys", "bool", args) self.descriptor = descriptor @@ -5318,7 +5325,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): for i in 0..(*unwrapped_proxy).Length() { rooted!(in(*cx) let mut rooted_jsid: jsid); int_to_jsid(i as i32, rooted_jsid.handle_mut()); - AppendToAutoIdVector(props, rooted_jsid.handle()); + AppendToIdVector(props, rooted_jsid.handle()); } """) @@ -5331,7 +5338,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): rooted!(in(*cx) let rooted = jsstring); rooted!(in(*cx) let mut rooted_jsid: jsid); RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut()); - AppendToAutoIdVector(props, rooted_jsid.handle()); + AppendToIdVector(props, rooted_jsid.handle()); } """) @@ -5359,7 +5366,7 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod): descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties")) args = [Argument('*mut JSContext', 'cx'), Argument('RawHandleObject', 'proxy'), - Argument('*mut AutoIdVector', 'props')] + Argument('RawMutableHandleIdVector', 'props')] CGAbstractExternMethod.__init__(self, descriptor, "getOwnEnumerablePropertyKeys", "bool", args) self.descriptor = descriptor @@ -5377,7 +5384,7 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod): for i in 0..(*unwrapped_proxy).Length() { rooted!(in(*cx) let mut rooted_jsid: jsid); int_to_jsid(i as i32, rooted_jsid.handle_mut()); - AppendToAutoIdVector(props, rooted_jsid.handle()); + AppendToIdVector(props, rooted_jsid.handle()); } """) @@ -5907,11 +5914,9 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::JS_CALLEE', 'js::error::throw_type_error', 'js::error::throw_internal_error', - 'js::jsapi::AutoIdVector', 'js::rust::wrappers::Call', 'js::jsapi::CallArgs', 'js::jsapi::CurrentGlobalOrNull', - 'js::jsapi::FreeOp', 'js::rust::wrappers::GetPropertyKeys', 'js::jsapi::GetWellKnownSymbol', 'js::rust::Handle', @@ -5938,6 +5943,9 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::jsapi::JSITER_SYMBOLS', 'js::jsapi::JSJitGetterCallArgs', 'js::jsapi::JSJitInfo', + 'js::jsapi::JSJitInfo__bindgen_ty_1', + 'js::jsapi::JSJitInfo__bindgen_ty_2', + 'js::jsapi::JSJitInfo__bindgen_ty_3', 'js::jsapi::JSJitInfo_AliasSet', 'js::jsapi::JSJitInfo_ArgType', 'js::jsapi::JSJitInfo_OpType', @@ -5950,10 +5958,10 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::jsapi::JSPROP_PERMANENT', 'js::jsapi::JSPROP_READONLY', 'js::jsapi::JSPropertySpec', - 'js::jsapi::JSPropertySpec__bindgen_ty_1', - 'js::jsapi::JSPropertySpec__bindgen_ty_1__bindgen_ty_1', - 'js::jsapi::JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1', - 'js::jsapi::JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2', + 'js::jsapi::JSPropertySpec_Accessor', + 'js::jsapi::JSPropertySpec_AccessorsOrValue', + 'js::jsapi::JSPropertySpec_AccessorsOrValue_Accessors', + 'js::jsapi::JSPropertySpec_Name', 'js::jsapi::JSString', 'js::jsapi::JSTracer', 'js::jsapi::JSType', @@ -5995,6 +6003,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::jsapi::MutableHandleObject as RawMutableHandleObject', 'js::rust::MutableHandleValue', 'js::jsapi::MutableHandleValue as RawMutableHandleValue', + 'js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector', 'js::jsapi::ObjectOpResult', 'js::jsapi::PropertyDescriptor', 'js::jsapi::Rooted', @@ -6010,7 +6019,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'js::jsval::PrivateValue', 'js::jsval::UndefinedValue', 'js::jsapi::UndefinedHandleValue', - 'js::rust::wrappers::AppendToAutoIdVector', + 'js::rust::wrappers::AppendToIdVector', 'js::glue::CallJitGetterOp', 'js::glue::CallJitMethodOp', 'js::glue::CallJitSetterOp', @@ -6060,8 +6069,10 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'crate::dom::bindings::reflector::DomObject', 'crate::dom::bindings::root::Dom', 'crate::dom::bindings::root::DomRoot', - 'crate::dom::bindings::root::OptionalHeapSetter', 'crate::dom::bindings::root::DomSlice', + 'crate::dom::bindings::root::MaybeUnreflectedDom', + 'crate::dom::bindings::root::OptionalHeapSetter', + 'crate::dom::bindings::root::Root', 'crate::dom::bindings::utils::AsVoidPtr', 'crate::dom::bindings::utils::DOMClass', 'crate::dom::bindings::utils::DOMJSClass', @@ -6086,7 +6097,6 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries 'crate::dom::bindings::utils::set_dictionary_property', 'crate::dom::bindings::utils::trace_global', 'crate::dom::bindings::trace::JSTraceable', - 'crate::dom::bindings::trace::RootedTraceable', 'crate::dom::bindings::trace::RootedTraceableBox', 'crate::dom::bindings::callback::CallSetup', 'crate::dom::bindings::callback::CallbackContainer', @@ -6818,7 +6828,7 @@ def type_needs_tracing(t): def is_typed_array(t): assert isinstance(t, IDLObject), (t, type(t)) - return t.isTypedArray() or t.isArrayBuffer() or t.isArrayBufferView() or t.isSharedArrayBuffer() + return t.isTypedArray() or t.isArrayBuffer() or t.isArrayBufferView() def type_needs_auto_root(t): diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py index b2e56c9deaf..223fd7efbb4 100644 --- a/components/script/dom/bindings/codegen/parser/WebIDL.py +++ b/components/script/dom/bindings/codegen/parser/WebIDL.py @@ -481,9 +481,6 @@ class IDLExposureMixins(): def isExposedInWindow(self): return 'Window' in self.exposureSet - def isExposedOnMainThread(self): - return self.isExposedInWindow() - def isExposedInAnyWorker(self): return len(self.getWorkerExposureSet()) > 0 @@ -2090,9 +2087,9 @@ class IDLType(IDLObject): 'domstring', 'bytestring', 'usvstring', + 'utf8string', 'jsstring', 'object', - 'date', 'void', # Funny stuff 'interface', @@ -2109,15 +2106,17 @@ class IDLType(IDLObject): IDLObject.__init__(self, location) self.name = name self.builtin = False - self.clamp = False self.treatNullAsEmpty = False - self.enforceRange = False + self._clamp = False + self._enforceRange = False + self._allowShared = False self._extendedAttrDict = {} def __eq__(self, other): return (other and self.builtin == other.builtin and self.name == other.name and - self.clamp == other.clamp and self.enforceRange == other.enforceRange and - self.treatNullAsEmpty == other.treatNullAsEmpty) + self._clamp == other.hasClamp() and self._enforceRange == other.hasEnforceRange() and + self.treatNullAsEmpty == other.treatNullAsEmpty and + self._allowShared == other.hasAllowShared()) def __ne__(self, other): return not self == other @@ -2125,6 +2124,14 @@ class IDLType(IDLObject): def __str__(self): return str(self.name) + def prettyName(self): + """ + A name that looks like what this type is named in the IDL spec. By default + this is just our .name, but types that have more interesting spec + representations should override this. + """ + return str(self.name) + def isType(self): return True @@ -2152,6 +2159,9 @@ class IDLType(IDLObject): def isUSVString(self): return False + def isUTF8String(self): + return False + def isJSString(self): return False @@ -2173,12 +2183,12 @@ class IDLType(IDLObject): def isArrayBufferView(self): return False - def isSharedArrayBuffer(self): - return False - def isTypedArray(self): return False + def isBufferSource(self): + return self.isArrayBuffer() or self.isArrayBufferView() or self.isTypedArray() + def isCallbackInterface(self): return False @@ -2195,10 +2205,7 @@ class IDLType(IDLObject): def isSpiderMonkeyInterface(self): """ Returns a boolean indicating whether this type is an 'interface' type that is implemented in SpiderMonkey. """ - return self.isInterface() and (self.isArrayBuffer() or - self.isArrayBufferView() or - self.isSharedArrayBuffer() or - self.isTypedArray() or + return self.isInterface() and (self.isBufferSource() or self.isReadableStream()) def isDictionary(self): @@ -2210,9 +2217,6 @@ class IDLType(IDLObject): def isAny(self): return self.tag() == IDLType.Tags.any - def isDate(self): - return self.tag() == IDLType.Tags.date - def isObject(self): return self.tag() == IDLType.Tags.object @@ -2235,6 +2239,15 @@ class IDLType(IDLObject): def isJSONType(self): return False + def hasClamp(self): + return self._clamp + + def hasEnforceRange(self): + return self._enforceRange + + def hasAllowShared(self): + return self._allowShared + def tag(self): assert False # Override me! @@ -2342,10 +2355,7 @@ class IDLNullableType(IDLParametrizedType): assert not innerType.isVoid() assert not innerType == BuiltinTypes[IDLBuiltinType.Types.any] - name = innerType.name - if innerType.isComplete(): - name += "OrNull" - IDLParametrizedType.__init__(self, location, name, innerType) + IDLParametrizedType.__init__(self, location, None, innerType) def __eq__(self, other): return isinstance(other, IDLNullableType) and self.inner == other.inner @@ -2353,6 +2363,9 @@ class IDLNullableType(IDLParametrizedType): def __str__(self): return self.inner.__str__() + "OrNull" + def prettyName(self): + return self.inner.prettyName() + "?" + def nullable(self): return True @@ -2380,6 +2393,9 @@ class IDLNullableType(IDLParametrizedType): def isUSVString(self): return self.inner.isUSVString() + def isUTF8String(self): + return self.inner.isUTF8String() + def isJSString(self): return self.inner.isJSString() @@ -2410,9 +2426,6 @@ class IDLNullableType(IDLParametrizedType): def isArrayBufferView(self): return self.inner.isArrayBufferView() - def isSharedArrayBuffer(self): - return self.inner.isSharedArrayBuffer() - def isTypedArray(self): return self.inner.isTypedArray() @@ -2442,11 +2455,26 @@ class IDLNullableType(IDLParametrizedType): def isJSONType(self): return self.inner.isJSONType() + def hasClamp(self): + return self.inner.hasClamp() + + def hasEnforceRange(self): + return self.inner.hasEnforceRange() + + def hasAllowShared(self): + return self.inner.hasAllowShared() + + def isComplete(self): + return self.name is not None + def tag(self): return self.inner.tag() def complete(self, scope): - self.inner = self.inner.complete(scope) + if not self.inner.isComplete(): + self.inner = self.inner.complete(scope) + assert self.inner.isComplete() + if self.inner.nullable(): raise WebIDLError("The inner type of a nullable type must not be " "a nullable type", @@ -2456,6 +2484,10 @@ class IDLNullableType(IDLParametrizedType): raise WebIDLError("The inner type of a nullable type must not " "be a union type that itself has a nullable " "type as a member type", [self.location]) + if self.inner.isDOMString(): + if self.inner.treatNullAsEmpty: + raise WebIDLError("[TreatNullAs] not allowed on a nullable DOMString", + [self.location, self.inner.location]) self.name = self.inner.name + "OrNull" return self @@ -2469,6 +2501,13 @@ class IDLNullableType(IDLParametrizedType): return False return self.inner.isDistinguishableFrom(other) + def withExtendedAttributes(self, attrs): + # See https://github.com/heycam/webidl/issues/827#issuecomment-565131350 + # Allowing extended attributes to apply to a nullable type is an intermediate solution. + # A potential longer term solution is to introduce a null type and get rid of nullables. + # For example, we could do `([Clamp] long or null) foo` in the future. + return IDLNullableType(self.location, self.inner.withExtendedAttributes(attrs)) + class IDLSequenceType(IDLParametrizedType): def __init__(self, location, parameterType): @@ -2486,6 +2525,9 @@ class IDLSequenceType(IDLParametrizedType): def __str__(self): return self.inner.__str__() + "Sequence" + def prettyName(self): + return "sequence<%s>" % self.inner.prettyName() + def nullable(self): return False @@ -2504,6 +2546,9 @@ class IDLSequenceType(IDLParametrizedType): def isUSVString(self): return False + def isUTF8String(self): + return False + def isJSString(self): return False @@ -2540,8 +2585,7 @@ class IDLSequenceType(IDLParametrizedType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isInterface() or - other.isDictionary() or + other.isInterface() or other.isDictionary() or other.isCallback() or other.isRecord()) @@ -2565,6 +2609,9 @@ class IDLRecordType(IDLParametrizedType): def __str__(self): return self.keyType.__str__() + self.inner.__str__() + "Record" + def prettyName(self): + return "record<%s, %s>" % (self.keyType.prettyName(), self.inner.prettyName()) + def isRecord(self): return True @@ -2592,7 +2639,7 @@ class IDLRecordType(IDLParametrizedType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isNonCallbackInterface() or other.isSequence()) + other.isNonCallbackInterface() or other.isSequence()) def isExposedInAllOf(self, exposureSet): return self.inner.unroll().isExposedInAllOf(exposureSet) @@ -2614,6 +2661,9 @@ class IDLUnionType(IDLType): assert self.isComplete() return self.name.__hash__() + def prettyName(self): + return "(" + " or ".join(m.prettyName() for m in self.memberTypes) + ")" + def isVoid(self): return False @@ -2645,6 +2695,9 @@ class IDLUnionType(IDLType): return typeName(type._identifier.object()) if isinstance(type, IDLObjectWithIdentifier): return typeName(type.identifier) + if isinstance(type, IDLBuiltinType) and type.hasAllowShared(): + assert type.isBufferSource() + return "MaybeShared" + type.name return type.name for (i, type) in enumerate(self.memberTypes): @@ -2768,6 +2821,9 @@ class IDLTypedefType(IDLType): def isUSVString(self): return self.inner.isUSVString() + def isUTF8String(self): + return self.inner.isUTF8String() + def isJSString(self): return self.inner.isJSString() @@ -2795,9 +2851,6 @@ class IDLTypedefType(IDLType): def isArrayBufferView(self): return self.inner.isArrayBufferView() - def isSharedArrayBuffer(self): - return self.inner.isSharedArrayBuffer() - def isTypedArray(self): return self.inner.isTypedArray() @@ -2901,6 +2954,9 @@ class IDLWrapperType(IDLType): def isUSVString(self): return False + def isUTF8String(self): + return False + def isJSString(self): return False @@ -2976,11 +3032,11 @@ class IDLWrapperType(IDLType): if self.isEnum(): return (other.isPrimitive() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isRecord() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isDictionary() and other.nullable(): return False if (other.isPrimitive() or other.isString() or other.isEnum() or - other.isDate() or other.isSequence()): + other.isSequence()): return True if self.isDictionary(): return other.isNonCallbackInterface() @@ -3052,6 +3108,9 @@ class IDLPromiseType(IDLParametrizedType): def __str__(self): return self.inner.__str__() + "Promise" + def prettyName(self): + return "Promise<%s>" % self.inner.prettyName() + def isPromise(self): return True @@ -3104,14 +3163,13 @@ class IDLBuiltinType(IDLType): 'domstring', 'bytestring', 'usvstring', + 'utf8string', 'jsstring', 'object', - 'date', 'void', # Funny stuff 'ArrayBuffer', 'ArrayBufferView', - 'SharedArrayBuffer', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray', @@ -3142,13 +3200,12 @@ class IDLBuiltinType(IDLType): Types.domstring: IDLType.Tags.domstring, Types.bytestring: IDLType.Tags.bytestring, Types.usvstring: IDLType.Tags.usvstring, + Types.utf8string: IDLType.Tags.utf8string, Types.jsstring: IDLType.Tags.jsstring, Types.object: IDLType.Tags.object, - Types.date: IDLType.Tags.date, Types.void: IDLType.Tags.void, Types.ArrayBuffer: IDLType.Tags.interface, Types.ArrayBufferView: IDLType.Tags.interface, - Types.SharedArrayBuffer: IDLType.Tags.interface, Types.Int8Array: IDLType.Tags.interface, Types.Uint8Array: IDLType.Tags.interface, Types.Uint8ClampedArray: IDLType.Tags.interface, @@ -3161,11 +3218,48 @@ class IDLBuiltinType(IDLType): Types.ReadableStream: IDLType.Tags.interface, } + PrettyNames = { + Types.byte: "byte", + Types.octet: "octet", + Types.short: "short", + Types.unsigned_short: "unsigned short", + Types.long: "long", + Types.unsigned_long: "unsigned long", + Types.long_long: "long long", + Types.unsigned_long_long: "unsigned long long", + Types.boolean: "boolean", + Types.unrestricted_float: "unrestricted float", + Types.float: "float", + Types.unrestricted_double: "unrestricted double", + Types.double: "double", + Types.any: "any", + Types.domstring: "DOMString", + Types.bytestring: "ByteString", + Types.usvstring: "USVString", + Types.utf8string: "USVString", # That's what it is in spec terms + Types.jsstring: "USVString", # Again, that's what it is in spec terms + Types.object: "object", + Types.void: "void", + Types.ArrayBuffer: "ArrayBuffer", + Types.ArrayBufferView: "ArrayBufferView", + Types.Int8Array: "Int8Array", + Types.Uint8Array: "Uint8Array", + Types.Uint8ClampedArray: "Uint8ClampedArray", + Types.Int16Array: "Int16Array", + Types.Uint16Array: "Uint16Array", + Types.Int32Array: "Int32Array", + Types.Uint32Array: "Uint32Array", + Types.Float32Array: "Float32Array", + Types.Float64Array: "Float64Array", + Types.ReadableStream: "ReadableStream", + } + def __init__(self, location, name, type, clamp=False, enforceRange=False, treatNullAsEmpty=False, - attrLocation=[]): + allowShared=False, attrLocation=[]): """ - The mutually exclusive clamp/enforceRange/treatNullAsEmpty arguments are used to create instances - of this type with the appropriate attributes attached. Use .clamped(), .rangeEnforced(), and .treatNullAs(). + The mutually exclusive clamp/enforceRange/treatNullAsEmpty/allowShared arguments are used + to create instances of this type with the appropriate attributes attached. Use .clamped(), + .rangeEnforced(), .withTreatNullAs() and .withAllowShared(). attrLocation is an array of source locations of these attributes for error reporting. """ @@ -3175,24 +3269,40 @@ class IDLBuiltinType(IDLType): self._clamped = None self._rangeEnforced = None self._withTreatNullAs = None - if self.isNumeric(): + self._withAllowShared = None; + if self.isInteger(): if clamp: - self.clamp = True + self._clamp = True self.name = "Clamped" + self.name self._extendedAttrDict["Clamp"] = True elif enforceRange: - self.enforceRange = True + self._enforceRange = True self.name = "RangeEnforced" + self.name self._extendedAttrDict["EnforceRange"] = True elif clamp or enforceRange: - raise WebIDLError("Non-numeric types cannot be [Clamp] or [EnforceRange]", attrLocation) - if self.isDOMString(): + raise WebIDLError("Non-integer types cannot be [Clamp] or [EnforceRange]", attrLocation) + if self.isDOMString() or self.isUTF8String(): if treatNullAsEmpty: self.treatNullAsEmpty = True self.name = "NullIsEmpty" + self.name self._extendedAttrDict["TreatNullAs"] = ["EmptyString"] elif treatNullAsEmpty: raise WebIDLError("Non-string types cannot be [TreatNullAs]", attrLocation) + if self.isBufferSource(): + if allowShared: + self._allowShared = True + self._extendedAttrDict["AllowShared"] = True + elif allowShared: + raise WebIDLError("Types that are not buffer source types cannot be [AllowShared]", attrLocation) + + def __str__(self): + if self._allowShared: + assert self.isBufferSource() + return "MaybeShared" + str(self.name) + return str(self.name) + + def prettyName(self): + return IDLBuiltinType.PrettyNames[self._typeTag] def clamped(self, attrLocation): if not self._clamped: @@ -3215,6 +3325,13 @@ class IDLBuiltinType(IDLType): attrLocation=attrLocation) return self._withTreatNullAs + def withAllowShared(self, attrLocation): + if not self._withAllowShared: + self._withAllowShared = IDLBuiltinType(self.location, self.name, + self._typeTag, allowShared=True, + attrLocation=attrLocation) + return self._withAllowShared + def isPrimitive(self): return self._typeTag <= IDLBuiltinType.Types.double @@ -3228,6 +3345,7 @@ class IDLBuiltinType(IDLType): return (self._typeTag == IDLBuiltinType.Types.domstring or self._typeTag == IDLBuiltinType.Types.bytestring or self._typeTag == IDLBuiltinType.Types.usvstring or + self._typeTag == IDLBuiltinType.Types.utf8string or self._typeTag == IDLBuiltinType.Types.jsstring) def isByteString(self): @@ -3239,6 +3357,9 @@ class IDLBuiltinType(IDLType): def isUSVString(self): return self._typeTag == IDLBuiltinType.Types.usvstring + def isUTF8String(self): + return self._typeTag == IDLBuiltinType.Types.utf8string + def isJSString(self): return self._typeTag == IDLBuiltinType.Types.jsstring @@ -3251,9 +3372,6 @@ class IDLBuiltinType(IDLType): def isArrayBufferView(self): return self._typeTag == IDLBuiltinType.Types.ArrayBufferView - def isSharedArrayBuffer(self): - return self._typeTag == IDLBuiltinType.Types.SharedArrayBuffer - def isTypedArray(self): return (self._typeTag >= IDLBuiltinType.Types.Int8Array and self._typeTag <= IDLBuiltinType.Types.Float64Array) @@ -3267,7 +3385,6 @@ class IDLBuiltinType(IDLType): # all of it internally. return (self.isArrayBuffer() or self.isArrayBufferView() or - self.isSharedArrayBuffer() or self.isTypedArray() or self.isReadableStream()) @@ -3305,27 +3422,22 @@ class IDLBuiltinType(IDLType): return (other.isNumeric() or other.isString() or other.isEnum() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isRecord() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isNumeric(): return (other.isBoolean() or other.isString() or other.isEnum() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isRecord() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isString(): return (other.isPrimitive() or other.isInterface() or other.isObject() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isRecord() or other.isDate()) + other.isSequence() or other.isRecord()) if self.isAny(): # Can't tell "any" apart from anything return False if self.isObject(): return other.isPrimitive() or other.isString() or other.isEnum() - if self.isDate(): - return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isInterface() or other.isCallback() or - other.isDictionary() or other.isSequence() or - other.isRecord()) if self.isVoid(): return not other.isVoid() # Not much else we could be! @@ -3333,12 +3445,11 @@ class IDLBuiltinType(IDLType): # Like interfaces, but we know we're not a callback return (other.isPrimitive() or other.isString() or other.isEnum() or other.isCallback() or other.isDictionary() or - other.isSequence() or other.isRecord() or other.isDate() or + other.isSequence() or other.isRecord() or (other.isInterface() and ( # ArrayBuffer is distinguishable from everything # that's not an ArrayBuffer or a callback interface (self.isArrayBuffer() and not other.isArrayBuffer()) or - (self.isSharedArrayBuffer() and not other.isSharedArrayBuffer()) or (self.isReadableStream() and not other.isReadableStream()) or # ArrayBufferView is distinguishable from everything # that's not an ArrayBufferView or typed array. @@ -3361,7 +3472,7 @@ class IDLBuiltinType(IDLType): if not attribute.noArguments(): raise WebIDLError("[Clamp] must take no arguments", [attribute.location]) - if ret.enforceRange or self.enforceRange: + if ret.hasEnforceRange() or self._enforceRange: raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", [self.location, attribute.location]) ret = self.clamped([self.location, attribute.location]) @@ -3369,17 +3480,17 @@ class IDLBuiltinType(IDLType): if not attribute.noArguments(): raise WebIDLError("[EnforceRange] must take no arguments", [attribute.location]) - if ret.clamp or self.clamp: + if ret.hasClamp() or self._clamp: raise WebIDLError("[EnforceRange] and [Clamp] are mutually exclusive", [self.location, attribute.location]) ret = self.rangeEnforced([self.location, attribute.location]) elif identifier == "TreatNullAs": - if not self.isDOMString(): - raise WebIDLError("[TreatNullAs] only allowed on DOMStrings", + if not (self.isDOMString() or self.isUTF8String()): + raise WebIDLError("[TreatNullAs] only allowed on DOMStrings and UTF8Strings", [self.location, attribute.location]) assert not self.nullable() if not attribute.hasValue(): - raise WebIDLError("[TreatNullAs] must take an identifier argument" + raise WebIDLError("[TreatNullAs] must take an identifier argument", [attribute.location]) value = attribute.value() if value != 'EmptyString': @@ -3387,6 +3498,15 @@ class IDLBuiltinType(IDLType): "'EmptyString', not '%s'" % value, [attribute.location]) ret = self.withTreatNullAs([self.location, attribute.location]) + elif identifier == "AllowShared": + if not attribute.noArguments(): + raise WebIDLError("[AllowShared] must take no arguments", + [attribute.location]) + if not self.isBufferSource(): + raise WebIDLError("[AllowShared] only allowed on buffer source types", + [self.location, attribute.location]) + ret = self.withAllowShared([self.location, attribute.location]) + else: raise WebIDLError("Unhandled extended attribute on type", [self.location, attribute.location]) @@ -3444,15 +3564,15 @@ BuiltinTypes = { IDLBuiltinType.Types.usvstring: IDLBuiltinType(BuiltinLocation("<builtin type>"), "USVString", IDLBuiltinType.Types.usvstring), + IDLBuiltinType.Types.utf8string: + IDLBuiltinType(BuiltinLocation("<builtin type>"), "UTF8String", + IDLBuiltinType.Types.utf8string), IDLBuiltinType.Types.jsstring: IDLBuiltinType(BuiltinLocation("<builtin type>"), "JSString", IDLBuiltinType.Types.jsstring), IDLBuiltinType.Types.object: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Object", IDLBuiltinType.Types.object), - IDLBuiltinType.Types.date: - IDLBuiltinType(BuiltinLocation("<builtin type>"), "Date", - IDLBuiltinType.Types.date), IDLBuiltinType.Types.void: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Void", IDLBuiltinType.Types.void), @@ -3462,9 +3582,6 @@ BuiltinTypes = { IDLBuiltinType.Types.ArrayBufferView: IDLBuiltinType(BuiltinLocation("<builtin type>"), "ArrayBufferView", IDLBuiltinType.Types.ArrayBufferView), - IDLBuiltinType.Types.SharedArrayBuffer: - IDLBuiltinType(BuiltinLocation("<builtin type>"), "SharedArrayBuffer", - IDLBuiltinType.Types.SharedArrayBuffer), IDLBuiltinType.Types.Int8Array: IDLBuiltinType(BuiltinLocation("<builtin type>"), "Int8Array", IDLBuiltinType.Types.Int8Array), @@ -3613,8 +3730,9 @@ class IDLValue(IDLObject): # TreatNullAsEmpty is a different type for resolution reasons, # however once you have a value it doesn't matter return self - elif self.type.isString() and (type.isByteString() or type.isJSString()): - # Allow ByteStrings and JSStrings to use a default value like DOMString. + elif self.type.isString() and (type.isByteString() or type.isJSString() or type.isUTF8String()): + # Allow ByteStrings, UTF8String, and JSStrings to use a default + # value like DOMString. # No coercion is required as Codegen.py will handle the # extra steps. We want to make sure that our string contains # only valid characters, so we check that here. @@ -4307,8 +4425,9 @@ class IDLAttribute(IDLInterfaceMember): assert not isinstance(t.name, IDLUnresolvedIdentifier) self.type = t - if self.readonly and (self.type.clamp or self.type.enforceRange or self.type.treatNullAsEmpty): - raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange]", + if self.readonly and (self.type.hasClamp() or self.type.hasEnforceRange() or + self.type.hasAllowShared() or self.type.treatNullAsEmpty): + raise WebIDLError("A readonly attribute cannot be [Clamp] or [EnforceRange] or [AllowShared]", [self.location]) if self.type.isDictionary() and not self.getExtendedAttribute("Cached"): raise WebIDLError("An attribute cannot be of a dictionary type", @@ -4709,7 +4828,7 @@ class IDLArgument(IDLObjectWithIdentifier): for attribute in attrs: identifier = attribute.identifier() if self.allowTypeAttributes and (identifier == "EnforceRange" or identifier == "Clamp" or - identifier == "TreatNullAs"): + identifier == "TreatNullAs" or identifier == "AllowShared"): self.type = self.type.withExtendedAttributes([attribute]) elif identifier == "TreatNonCallableAsNull": self._allowTreatNonCallableAsNull = True @@ -4879,8 +4998,7 @@ class IDLCallbackType(IDLType): # Just forward to the union; it'll deal return other.isDistinguishableFrom(self) return (other.isPrimitive() or other.isString() or other.isEnum() or - other.isNonCallbackInterface() or other.isDate() or - other.isSequence()) + other.isNonCallbackInterface() or other.isSequence()) def _getDependentObjects(self): return self.callback._getDependentObjects() @@ -5475,7 +5593,9 @@ class IDLConstructor(IDLMethod): identifier == "ChromeOnly" or identifier == "NewObject" or identifier == "SecureContext" or - identifier == "Throws"): + identifier == "Throws" or + identifier == "Func" or + identifier == "Pref"): IDLMethod.handleExtendedAttribute(self, attr) elif identifier == "HTMLConstructor": if not attr.noArguments(): @@ -5675,11 +5795,11 @@ class Tokenizer(object): "optional": "OPTIONAL", "...": "ELLIPSIS", "::": "SCOPE", - "Date": "DATE", "DOMString": "DOMSTRING", "ByteString": "BYTESTRING", "USVString": "USVSTRING", "JSString": "JSSTRING", + "UTF8String": "UTF8STRING", "any": "ANY", "boolean": "BOOLEAN", "byte": "BYTE", @@ -5709,7 +5829,6 @@ class Tokenizer(object): "<": "LT", ">": "GT", "ArrayBuffer": "ARRAYBUFFER", - "SharedArrayBuffer": "SHAREDARRAYBUFFER", "or": "OR", "maplike": "MAPLIKE", "setlike": "SETLIKE", @@ -6939,10 +7058,10 @@ class Parser(Tokenizer): | EQUALS | GT | QUESTIONMARK - | DATE | DOMSTRING | BYTESTRING | USVSTRING + | UTF8STRING | JSSTRING | PROMISE | ANY @@ -7050,7 +7169,6 @@ class Parser(Tokenizer): """ DistinguishableType : PrimitiveType Null | ARRAYBUFFER Null - | SHAREDARRAYBUFFER Null | READABLESTREAM Null | OBJECT Null """ @@ -7058,8 +7176,6 @@ class Parser(Tokenizer): type = BuiltinTypes[IDLBuiltinType.Types.object] elif p[1] == "ArrayBuffer": type = BuiltinTypes[IDLBuiltinType.Types.ArrayBuffer] - elif p[1] == "SharedArrayBuffer": - type = BuiltinTypes[IDLBuiltinType.Types.SharedArrayBuffer] elif p[1] == "ReadableStream": type = BuiltinTypes[IDLBuiltinType.Types.ReadableStream] else: @@ -7122,13 +7238,6 @@ class Parser(Tokenizer): type = IDLUnresolvedType(self.getLocation(p, 1), p[1]) p[0] = self.handleNullable(type, p[2]) - def p_DistinguishableTypeDate(self, p): - """ - DistinguishableType : DATE Null - """ - p[0] = self.handleNullable(BuiltinTypes[IDLBuiltinType.Types.date], - p[2]) - def p_ConstType(self, p): """ ConstType : PrimitiveType @@ -7215,6 +7324,12 @@ class Parser(Tokenizer): """ p[0] = IDLBuiltinType.Types.usvstring + def p_BuiltinStringTypeUTF8String(self, p): + """ + BuiltinStringType : UTF8STRING + """ + p[0] = IDLBuiltinType.Types.utf8string + def p_BuiltinStringTypeJSString(self, p): """ BuiltinStringType : JSSTRING @@ -7354,7 +7469,13 @@ class Parser(Tokenizer): IdentifierList : IDENTIFIER Identifiers """ idents = list(p[2]) - idents.insert(0, p[1]) + # This is only used for identifier-list-valued extended attributes, and if + # we're going to restrict to IDENTIFIER here we should at least allow + # escaping with leading '_' as usual for identifiers. + ident = p[1] + if ident[0] == '_': + ident = ident[1:] + idents.insert(0, ident) p[0] = idents def p_IdentifiersList(self, p): @@ -7362,7 +7483,13 @@ class Parser(Tokenizer): Identifiers : COMMA IDENTIFIER Identifiers """ idents = list(p[3]) - idents.insert(0, p[2]) + # This is only used for identifier-list-valued extended attributes, and if + # we're going to restrict to IDENTIFIER here we should at least allow + # escaping with leading '_' as usual for identifiers. + ident = p[2] + if ident[0] == '_': + ident = ident[1:] + idents.insert(0, ident) p[0] = idents def p_IdentifiersEmpty(self, p): diff --git a/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch new file mode 100644 index 00000000000..210134d8ca6 --- /dev/null +++ b/components/script/dom/bindings/codegen/parser/ext-attribute-no-value-error.patch @@ -0,0 +1,11 @@ +--- WebIDL.py ++++ WebIDL.py +@@ -3490,7 +3490,7 @@ class IDLBuiltinType(IDLType): + [self.location, attribute.location]) + assert not self.nullable() + if not attribute.hasValue(): +- raise WebIDLError("[TreatNullAs] must take an identifier argument" ++ raise WebIDLError("[TreatNullAs] must take an identifier argument", + [attribute.location]) + value = attribute.value() + if value != 'EmptyString': diff --git a/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py index 43daca3c453..ff08791d16f 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_attributes_on_types.py @@ -25,6 +25,12 @@ def WebIDLTest(parser, harness): void method2(optional [EnforceRange] long foo, optional [Clamp] long bar, optional [TreatNullAs=EmptyString] DOMString baz); }; + interface C { + attribute [EnforceRange] long? foo; + attribute [Clamp] long? bar; + void method([EnforceRange] long? foo, [Clamp] long? bar); + void method2(optional [EnforceRange] long? foo, optional [Clamp] long? bar); + }; interface Setlike { setlike<[Clamp] long>; }; @@ -41,29 +47,105 @@ def WebIDLTest(parser, harness): harness.ok(not threw, "Should not have thrown on parsing normal") if not threw: - harness.check(results[0].innerType.enforceRange, True, "Foo is [EnforceRange]") - harness.check(results[1].innerType.clamp, True, "Bar is [Clamp]") + harness.check(results[0].innerType.hasEnforceRange(), True, "Foo is [EnforceRange]") + harness.check(results[1].innerType.hasClamp(), True, "Bar is [Clamp]") harness.check(results[2].innerType.treatNullAsEmpty, True, "Baz is [TreatNullAs=EmptyString]") A = results[3] - harness.check(A.members[0].type.enforceRange, True, "A.a is [EnforceRange]") - harness.check(A.members[1].type.clamp, True, "A.b is [Clamp]") - harness.check(A.members[2].type.enforceRange, True, "A.c is [EnforceRange]") - harness.check(A.members[3].type.enforceRange, True, "A.d is [EnforceRange]") + harness.check(A.members[0].type.hasEnforceRange(), True, "A.a is [EnforceRange]") + harness.check(A.members[1].type.hasClamp(), True, "A.b is [Clamp]") + harness.check(A.members[2].type.hasEnforceRange(), True, "A.c is [EnforceRange]") + harness.check(A.members[3].type.hasEnforceRange(), True, "A.d is [EnforceRange]") B = results[4] - harness.check(B.members[0].type.enforceRange, True, "B.typedefFoo is [EnforceRange]") - harness.check(B.members[1].type.enforceRange, True, "B.foo is [EnforceRange]") - harness.check(B.members[2].type.clamp, True, "B.bar is [Clamp]") + harness.check(B.members[0].type.hasEnforceRange(), True, "B.typedefFoo is [EnforceRange]") + harness.check(B.members[1].type.hasEnforceRange(), True, "B.foo is [EnforceRange]") + harness.check(B.members[2].type.hasClamp(), True, "B.bar is [Clamp]") harness.check(B.members[3].type.treatNullAsEmpty, True, "B.baz is [TreatNullAs=EmptyString]") method = B.members[4].signatures()[0][1] - harness.check(method[0].type.enforceRange, True, "foo argument of method is [EnforceRange]") - harness.check(method[1].type.clamp, True, "bar argument of method is [Clamp]") + harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method is [EnforceRange]") + harness.check(method[1].type.hasClamp(), True, "bar argument of method is [Clamp]") harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method is [TreatNullAs=EmptyString]") method2 = B.members[5].signatures()[0][1] - harness.check(method[0].type.enforceRange, True, "foo argument of method2 is [EnforceRange]") - harness.check(method[1].type.clamp, True, "bar argument of method2 is [Clamp]") + harness.check(method[0].type.hasEnforceRange(), True, "foo argument of method2 is [EnforceRange]") + harness.check(method[1].type.hasClamp(), True, "bar argument of method2 is [Clamp]") harness.check(method[2].type.treatNullAsEmpty, True, "baz argument of method2 is [TreatNullAs=EmptyString]") + C = results[5] + harness.ok(C.members[0].type.nullable(), "C.foo is nullable") + harness.ok(C.members[0].type.hasEnforceRange(), "C.foo has [EnforceRange]") + harness.ok(C.members[1].type.nullable(), "C.bar is nullable") + harness.ok(C.members[1].type.hasClamp(), "C.bar has [Clamp]") + method = C.members[2].signatures()[0][1] + harness.ok(method[0].type.nullable(), "foo argument of method is nullable") + harness.ok(method[0].type.hasEnforceRange(), "foo argument of method has [EnforceRange]") + harness.ok(method[1].type.nullable(), "bar argument of method is nullable") + harness.ok(method[1].type.hasClamp(), "bar argument of method has [Clamp]") + method2 = C.members[3].signatures()[0][1] + harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable") + harness.ok(method2[0].type.hasEnforceRange(), "foo argument of method2 has [EnforceRange]") + harness.ok(method2[1].type.nullable(), "bar argument of method2 is nullable") + harness.ok(method2[1].type.hasClamp(), "bar argument of method2 has [Clamp]") + + # Test [AllowShared] + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [AllowShared] ArrayBufferView Foo; + dictionary A { + required [AllowShared] ArrayBufferView a; + [ChromeOnly, AllowShared] ArrayBufferView b; + Foo c; + }; + interface B { + attribute Foo typedefFoo; + attribute [AllowShared] ArrayBufferView foo; + void method([AllowShared] ArrayBufferView foo); + void method2(optional [AllowShared] ArrayBufferView foo); + }; + interface C { + attribute [AllowShared] ArrayBufferView? foo; + void method([AllowShared] ArrayBufferView? foo); + void method2(optional [AllowShared] ArrayBufferView? foo); + }; + interface Setlike { + setlike<[AllowShared] ArrayBufferView>; + }; + interface Maplike { + maplike<[Clamp] long, [AllowShared] ArrayBufferView>; + }; + interface Iterable { + iterable<[Clamp] long, [AllowShared] ArrayBufferView>; + }; + """) + results = parser.finish() + except: + threw = True + + harness.ok(not threw, "Should not have thrown on parsing normal") + if not threw: + harness.ok(results[0].innerType.hasAllowShared(), "Foo is [AllowShared]") + A = results[1] + harness.ok(A.members[0].type.hasAllowShared(), "A.a is [AllowShared]") + harness.ok(A.members[1].type.hasAllowShared(), "A.b is [AllowShared]") + harness.ok(A.members[2].type.hasAllowShared(), "A.c is [AllowShared]") + B = results[2] + harness.ok(B.members[0].type.hasAllowShared(), "B.typedefFoo is [AllowShared]") + harness.ok(B.members[1].type.hasAllowShared(), "B.foo is [AllowShared]") + method = B.members[2].signatures()[0][1] + harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]") + method2 = B.members[3].signatures()[0][1] + harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]") + C = results[3] + harness.ok(C.members[0].type.nullable(), "C.foo is nullable") + harness.ok(C.members[0].type.hasAllowShared(), "C.foo is [AllowShared]") + method = C.members[1].signatures()[0][1] + harness.ok(method[0].type.nullable(), "foo argument of method is nullable") + harness.ok(method[0].type.hasAllowShared(), "foo argument of method is [AllowShared]") + method2 = C.members[2].signatures()[0][1] + harness.ok(method2[0].type.nullable(), "foo argument of method2 is nullable") + harness.ok(method2[0].type.hasAllowShared(), "foo argument of method2 is [AllowShared]") - ATTRIBUTES = [("[Clamp]", "long"), ("[EnforceRange]", "long"), ("[TreatNullAs=EmptyString]", "DOMString")] + ATTRIBUTES = [("[Clamp]", "long"), ("[EnforceRange]", "long"), + ("[TreatNullAs=EmptyString]", "DOMString"), ("[AllowShared]", "ArrayBufferView")] TEMPLATES = [ ("required dictionary members", """ dictionary Foo { @@ -93,7 +175,54 @@ def WebIDLTest(parser, harness): readonly attribute Bar baz; }; typedef %s %s Bar; - """) + """), + ("method", """ + interface Foo { + %s %s foo(); + }; + """), + ("interface",""" + %s + interface Foo { + attribute %s foo; + }; + """), + ("partial interface",""" + interface Foo { + void foo(); + }; + %s + partial interface Foo { + attribute %s bar; + }; + """), + ("interface mixin",""" + %s + interface mixin Foo { + attribute %s foo; + }; + """), + ("namespace",""" + %s + namespace Foo { + attribute %s foo; + }; + """), + ("partial namespace",""" + namespace Foo { + void foo(); + }; + %s + partial namespace Foo { + attribute %s bar; + }; + """), + ("dictionary",""" + %s + dictionary Foo { + %s foo; + }; + """) ]; for (name, template) in TEMPLATES: @@ -167,55 +296,91 @@ def WebIDLTest(parser, harness): harness.ok(threw, "Should not allow mixing [Clamp] and [EnforceRange] via typedefs") + TYPES = ["DOMString", "unrestricted float", "float", "unrestricted double", "double"] + + for type in TYPES: + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [Clamp] %s Foo; + """ % type) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [Clamp] on %s" % type) + + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [EnforceRange] %s Foo; + """ % type) + parser.finish() + except: + threw = True + + harness.ok(threw, "Should not allow [EnforceRange] on %s" % type) + + parser = parser.reset() threw = False try: parser.parse(""" - typedef [Clamp] DOMString Foo; + typedef [TreatNullAs=EmptyString] long Foo; """) parser.finish() except: threw = True - harness.ok(threw, "Should not allow [Clamp] on DOMString") - + harness.ok(threw, "Should not allow [TreatNullAs] on long") parser = parser.reset() threw = False try: parser.parse(""" - typedef [EnforceRange] DOMString Foo; + typedef [TreatNullAs=EmptyString] JSString Foo; """) parser.finish() except: threw = True - harness.ok(threw, "Should not allow [EnforceRange] on DOMString") - + harness.ok(threw, "Should not allow [TreatNullAs] on JSString") parser = parser.reset() threw = False try: parser.parse(""" - typedef [TreatNullAs=EmptyString] long Foo; + typedef [TreatNullAs=EmptyString] DOMString? Foo; """) parser.finish() except: threw = True - harness.ok(threw, "Should not allow [TreatNullAs] on long") + harness.ok(threw, "Should not allow [TreatNullAs] on nullable DOMString") parser = parser.reset() threw = False try: parser.parse(""" - typedef [TreatNullAs=EmptyString] JSString Foo; + typedef [AllowShared] DOMString Foo; """) - parser.finish() + results = parser.finish() except: threw = True + harness.ok(threw, "[AllowShared] only allowed on buffer source types") - harness.ok(threw, "Should not allow [TreatNullAs] on JSString") + parser = parser.reset() + threw = False + try: + parser.parse(""" + typedef [AllowShared=something] ArrayBufferView Foo; + """) + results = parser.finish() + except: + threw = True + harness.ok(threw, "[AllowShared] must take no arguments") parser = parser.reset() threw = False @@ -230,7 +395,7 @@ def WebIDLTest(parser, harness): except: threw = True harness.ok(not threw, "Should allow type attributes on unresolved types") - harness.check(results[0].members[0].signatures()[0][1][0].type.clamp, True, + harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True, "Unresolved types with type attributes should correctly resolve with attributes") parser = parser.reset() @@ -246,5 +411,5 @@ def WebIDLTest(parser, harness): except: threw = True harness.ok(not threw, "Should allow type attributes on typedefs") - harness.check(results[0].members[0].signatures()[0][1][0].type.clamp, True, + harness.check(results[0].members[0].signatures()[0][1][0].type.hasClamp(), True, "Unresolved types that resolve to typedefs with attributes should correctly resolve with attributes") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py index 721f9c2089e..83e1f4fc34f 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_constructor.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_constructor.py @@ -11,9 +11,9 @@ def WebIDLTest(parser, harness): harness.check(argument.variadic, variadic, "Argument has the right variadic value") def checkMethod(method, QName, name, signatures, - static=True, getter=False, setter=False, - deleter=False, legacycaller=False, stringifier=False, - chromeOnly=False, htmlConstructor=False): + static=True, getter=False, setter=False, deleter=False, + legacycaller=False, stringifier=False, chromeOnly=False, + htmlConstructor=False, secureContext=False, pref=None, func=None): harness.ok(isinstance(method, WebIDL.IDLMethod), "Should be an IDLMethod") harness.ok(method.isMethod(), "Method is a method") @@ -30,6 +30,9 @@ def WebIDLTest(parser, harness): harness.check(method.getExtendedAttribute("ChromeOnly") is not None, chromeOnly, "Method has the correct value for ChromeOnly") harness.check(method.isHTMLConstructor(), htmlConstructor, "Method has the correct htmlConstructor value") harness.check(len(method.signatures()), len(signatures), "Method has the correct number of signatures") + harness.check(method.getExtendedAttribute("Pref"), pref, "Method has the correct pref value") + harness.check(method.getExtendedAttribute("Func"), func, "Method has the correct func value") + harness.check(method.getExtendedAttribute("SecureContext") is not None, secureContext, "Method has the correct SecureContext value") sigpairs = zip(method.signatures(), signatures) for (gotSignature, expectedSignature) in sigpairs: @@ -90,6 +93,21 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" + interface TestPrefConstructor { + [Pref="dom.webidl.test1"] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestPrefConstructor::constructor", + "constructor", [("TestPrefConstructor (Wrapper)", [])], + pref=["dom.webidl.test1"]) + + parser = parser.reset() + parser.parse(""" interface TestChromeOnlyConstructor { [ChromeOnly] constructor(); }; @@ -105,6 +123,53 @@ def WebIDLTest(parser, harness): parser = parser.reset() parser.parse(""" + interface TestSCConstructor { + [SecureContext] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestSCConstructor::constructor", + "constructor", [("TestSCConstructor (Wrapper)", [])], + secureContext=True) + + parser = parser.reset() + parser.parse(""" + interface TestFuncConstructor { + [Func="Document::IsWebAnimationsEnabled"] constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestFuncConstructor::constructor", + "constructor", [("TestFuncConstructor (Wrapper)", [])], + func=["Document::IsWebAnimationsEnabled"]) + + parser = parser.reset() + parser.parse(""" + interface TestPrefChromeOnlySCFuncConstructor { + [ChromeOnly, Pref="dom.webidl.test1", SecureContext, Func="Document::IsWebAnimationsEnabled"] + constructor(); + }; + """) + results = parser.finish() + harness.check(len(results), 1, "Should be one production") + harness.ok(isinstance(results[0], WebIDL.IDLInterface), + "Should be an IDLInterface") + + checkMethod(results[0].ctor(), "::TestPrefChromeOnlySCFuncConstructor::constructor", + "constructor", [("TestPrefChromeOnlySCFuncConstructor (Wrapper)", [])], + func=["Document::IsWebAnimationsEnabled"], pref=["dom.webidl.test1"], + chromeOnly=True, secureContext=True) + + parser = parser.reset() + parser.parse(""" interface TestHTMLConstructor { [HTMLConstructor] constructor(); }; diff --git a/components/script/dom/bindings/codegen/parser/tests/test_date.py b/components/script/dom/bindings/codegen/parser/tests/test_date.py deleted file mode 100644 index 2bdfc95e14f..00000000000 --- a/components/script/dom/bindings/codegen/parser/tests/test_date.py +++ /dev/null @@ -1,15 +0,0 @@ -def WebIDLTest(parser, harness): - parser.parse(""" - interface WithDates { - attribute Date foo; - void bar(Date arg); - void baz(sequence<Date> arg); - }; - """) - - results = parser.finish() - harness.ok(results[0].members[0].type.isDate(), "Should have Date") - harness.ok(results[0].members[1].signatures()[0][1][0].type.isDate(), - "Should have Date argument") - harness.ok(not results[0].members[2].signatures()[0][1][0].type.isDate(), - "Should have non-Date argument") diff --git a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py index bd9996e34c9..505b36468d6 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_distinguishability.py @@ -149,7 +149,7 @@ def WebIDLTest(parser, harness): # Now let's test our whole distinguishability table argTypes = [ "long", "short", "long?", "short?", "boolean", - "boolean?", "DOMString", "ByteString", "Enum", "Enum2", + "boolean?", "DOMString", "ByteString", "UTF8String", "Enum", "Enum2", "Interface", "Interface?", "AncestorInterface", "UnrelatedInterface", "CallbackInterface", "CallbackInterface?", "CallbackInterface2", @@ -158,14 +158,12 @@ def WebIDLTest(parser, harness): "record<DOMString, object>", "record<USVString, Dict>", "record<ByteString, long>", - "Date", "Date?", "any", - "Promise<any>", "Promise<any>?", - "USVString", "JSString", "ArrayBuffer", "ArrayBufferView", "SharedArrayBuffer", + "record<UTF8String, long>", + "any", "Promise<any>", "Promise<any>?", + "USVString", "JSString", "ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array", "(long or Callback)", "(long or Dict)", ] - # When we can parse Date, we need to add it here. - # XXXbz we can, and should really do that... # Try to categorize things a bit to keep list lengths down def allBut(list1, list2): @@ -177,26 +175,24 @@ def WebIDLTest(parser, harness): primitives = numerics + booleans nonNumerics = allBut(argTypes, numerics + unions) nonBooleans = allBut(argTypes, booleans) - strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString" ] + strings = [ "DOMString", "ByteString", "Enum", "Enum2", "USVString", "JSString", "UTF8String" ] nonStrings = allBut(argTypes, strings) nonObjects = primitives + strings objects = allBut(argTypes, nonObjects ) bufferSourceTypes = ["ArrayBuffer", "ArrayBufferView", "Uint8Array", "Uint16Array"] - sharedBufferSourceTypes = ["SharedArrayBuffer"] interfaces = [ "Interface", "Interface?", "AncestorInterface", - "UnrelatedInterface" ] + bufferSourceTypes + sharedBufferSourceTypes + "UnrelatedInterface" ] + bufferSourceTypes nullables = (["long?", "short?", "boolean?", "Interface?", "CallbackInterface?", "Dict", "Dict2", "Date?", "any", "Promise<any>?"] + allBut(unions, [ "(long or Callback)" ])) - dates = [ "Date", "Date?" ] sequences = [ "sequence<long>", "sequence<short>" ] - nonUserObjects = nonObjects + interfaces + dates + sequences + nonUserObjects = nonObjects + interfaces + sequences otherObjects = allBut(argTypes, nonUserObjects + ["object"]) notRelatedInterfaces = (nonObjects + ["UnrelatedInterface"] + - otherObjects + dates + sequences + bufferSourceTypes + sharedBufferSourceTypes) + otherObjects + sequences + bufferSourceTypes) records = [ "record<DOMString, object>", "record<USVString, Dict>", - "record<ByteString, long>" ] # JSString not supported in records + "record<ByteString, long>", "record<UTF8String, long>" ] # JSString not supported in records # Build a representation of the distinguishability table as a dict # of dicts, holding True values where needed, holes elsewhere. @@ -215,6 +211,7 @@ def WebIDLTest(parser, harness): setDistinguishable("boolean?", allBut(nonBooleans, nullables)) setDistinguishable("DOMString", nonStrings) setDistinguishable("ByteString", nonStrings) + setDistinguishable("UTF8String", nonStrings) setDistinguishable("USVString", nonStrings) setDistinguishable("JSString", nonStrings) setDistinguishable("Enum", nonStrings) @@ -240,8 +237,7 @@ def WebIDLTest(parser, harness): setDistinguishable("record<USVString, Dict>", nonUserObjects) # JSString not supported in records setDistinguishable("record<ByteString, long>", nonUserObjects) - setDistinguishable("Date", allBut(argTypes, dates + ["object"])) - setDistinguishable("Date?", allBut(argTypes, dates + nullables + ["object"])) + setDistinguishable("record<UTF8String, long>", nonUserObjects) setDistinguishable("any", []) setDistinguishable("Promise<any>", []) setDistinguishable("Promise<any>?", []) @@ -249,7 +245,6 @@ def WebIDLTest(parser, harness): setDistinguishable("ArrayBufferView", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "Uint16Array", "object"])) setDistinguishable("Uint8Array", allBut(argTypes, ["ArrayBufferView", "Uint8Array", "object"])) setDistinguishable("Uint16Array", allBut(argTypes, ["ArrayBufferView", "Uint16Array", "object"])) - setDistinguishable("SharedArrayBuffer", allBut(argTypes, ["SharedArrayBuffer", "object"])) setDistinguishable("(long or Callback)", allBut(nonUserObjects, numerics)) setDistinguishable("(long or Dict)", diff --git a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py index 97184ec2478..144c945bc10 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_extended_attributes.py @@ -56,9 +56,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].type.clamp, + harness.ok(results[0].members[0].signatures()[0][1][0].type.hasClamp(), "Should be clamped") - harness.ok(not results[0].members[1].signatures()[0][1][0].type.clamp, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasClamp(), "Should not be clamped") parser = parser.reset() @@ -86,9 +86,9 @@ def WebIDLTest(parser, harness): results = parser.finish() # Pull out the first argument out of the arglist of the first (and # only) signature. - harness.ok(results[0].members[0].signatures()[0][1][0].type.enforceRange, + harness.ok(results[0].members[0].signatures()[0][1][0].type.hasEnforceRange(), "Should be enforceRange") - harness.ok(not results[0].members[1].signatures()[0][1][0].type.enforceRange, + harness.ok(not results[0].members[1].signatures()[0][1][0].type.hasEnforceRange(), "Should not be enforceRange") parser = parser.reset() diff --git a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py index 2b48b615dd4..8ba6771677a 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_nullable_equivalency.py @@ -80,7 +80,7 @@ def checkEquivalent(iface, harness): for attr in dir(type1): if attr.startswith('_') or \ attr in ['nullable', 'builtin', 'filename', 'location', - 'inner', 'QName', 'getDeps', 'name'] or \ + 'inner', 'QName', 'getDeps', 'name', 'prettyName'] or \ (hasattr(type(type1), attr) and not callable(getattr(type1, attr))): continue diff --git a/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py index b8b4f796ccb..ad01330e65a 100644 --- a/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py +++ b/components/script/dom/bindings/codegen/parser/tests/test_toJSON.py @@ -85,7 +85,7 @@ def WebIDLTest(parser, harness): JsonTypes = [ "byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long", "float", "unrestricted float", "double", "unrestricted double", "boolean", - "DOMString", "ByteString", "USVString", "Enum", "InterfaceWithToJSON", "object" ] + "DOMString", "ByteString", "UTF8String", "USVString", "Enum", "InterfaceWithToJSON", "object" ] nonJsonTypes = [ "InterfaceWithoutToJSON", "any", "Int8Array", "Int16Array", "Int32Array","Uint8Array", "Uint16Array", "Uint32Array", "Uint8ClampedArray", "Float32Array", "Float64Array", "ArrayBuffer" ] @@ -129,9 +129,12 @@ def WebIDLTest(parser, harness): doTest("interface Test { record<DOMString, %s> toJSON(); };" % type, False, "record<DOMString, %s> should be a JSON type" % type) - doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, False, + doTest("interface Test { record<ByteString, %s> toJSON(); };" % type, False, "record<ByteString, %s> should be a JSON type" % type) + doTest("interface Test { record<UTF8String, %s> toJSON(); };" % type, False, + "record<UTF8String, %s> should be a JSON type" % type) + doTest("interface Test { record<USVString, %s> toJSON(); };" % type, False, "record<USVString, %s> should be a JSON type" % type) @@ -174,12 +177,12 @@ def WebIDLTest(parser, harness): doTest("interface Test { record<USVString, %s> toJSON(); };" % type, True, "record<USVString, %s> should not be a JSON type" % type) - + if type != "any": doTest("interface Foo { object toJSON(); }; " "interface Test { (Foo or %s) toJSON(); };" % type, True, "union containing a non-JSON type (%s) should not be a JSON type" % type) - + doTest("interface test { %s? toJSON(); };" % type, True, "Nullable type (%s) should not be a JSON type" % type) diff --git a/components/script/dom/bindings/codegen/parser/update.sh b/components/script/dom/bindings/codegen/parser/update.sh index fee9720ab2d..dd7803c940c 100755 --- a/components/script/dom/bindings/codegen/parser/update.sh +++ b/components/script/dom/bindings/codegen/parser/update.sh @@ -5,8 +5,8 @@ patch < callback-location.patch patch < union-typedef.patch patch < inline.patch -wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz +wget https://hg.mozilla.org/mozilla-central/archive/tip.zip/dom/bindings/parser/tests/ -O tests.zip rm -r tests mkdir tests -tar xvpf tests.tar.gz -C tests --strip-components=5 -rm tests.tar.gz WebIDL.py.orig +unzip -d tests -j tests.zip +rm tests.zip WebIDL.py.orig diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index b88a6cabed3..f678611423b 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -19,6 +19,7 @@ use crate::script_runtime::JSContext as SafeJSContext; #[cfg(feature = "js_backtrace")] use backtrace::Backtrace; use js::error::{throw_range_error, throw_type_error}; +use js::jsapi::ExceptionStackBehavior; use js::jsapi::JSContext; use js::jsapi::JS_ClearPendingException; use js::jsapi::JS_IsExceptionPending; @@ -161,7 +162,7 @@ pub fn throw_dom_exception(cx: SafeJSContext, global: &GlobalScope, result: Erro let exception = DOMException::new(global, code); rooted!(in(*cx) let mut thrown = UndefinedValue()); exception.to_jsval(*cx, thrown.handle_mut()); - JS_SetPendingException(*cx, thrown.handle()); + JS_SetPendingException(*cx, thrown.handle(), ExceptionStackBehavior::Capture); } } diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index 2097769cb31..d0b3771890e 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -15,8 +15,8 @@ use js::error::throw_type_error; use js::glue::UncheckedUnwrapObject; use js::jsapi::GetWellKnownSymbol; use js::jsapi::HandleObject as RawHandleObject; -use js::jsapi::{jsid, Class, ClassOps}; -use js::jsapi::{JSAutoRealm, JSClass, JSContext, JSFunctionSpec, JSObject, JSFUN_CONSTRUCTOR}; +use js::jsapi::{jsid, JSClass, JSClassOps}; +use js::jsapi::{JSAutoRealm, JSContext, JSFunctionSpec, JSObject, JSFUN_CONSTRUCTOR}; use js::jsapi::{JSPropertySpec, JSString, JSTracer, JS_AtomizeAndPinString}; use js::jsapi::{JS_GetFunctionObject, JS_NewFunction, JS_NewGlobalObject}; use js::jsapi::{JS_NewObject, JS_NewPlainObject}; @@ -38,8 +38,8 @@ use std::ptr; /// The class of a non-callback interface object. #[derive(Clone, Copy)] pub struct NonCallbackInterfaceObjectClass { - /// The SpiderMonkey Class structure. - pub class: Class, + /// The SpiderMonkey class structure. + pub class: JSClass, /// The prototype id of that interface, used in the hasInstance hook. pub proto_id: PrototypeList::ID, /// The prototype depth of that interface, used in the hasInstance hook. @@ -59,7 +59,7 @@ impl NonCallbackInterfaceObjectClass { proto_depth: u16, ) -> NonCallbackInterfaceObjectClass { NonCallbackInterfaceObjectClass { - class: Class { + class: JSClass { name: b"Function\0" as *const _ as *const libc::c_char, flags: 0, cOps: &constructor_behavior.0, @@ -84,12 +84,12 @@ pub type ConstructorClassHook = unsafe extern "C" fn(cx: *mut JSContext, argc: u32, vp: *mut Value) -> bool; /// The constructor behavior of a non-callback interface object. -pub struct InterfaceConstructorBehavior(ClassOps); +pub struct InterfaceConstructorBehavior(JSClassOps); impl InterfaceConstructorBehavior { /// An interface constructor that unconditionally throws a type error. pub const fn throw() -> Self { - InterfaceConstructorBehavior(ClassOps { + InterfaceConstructorBehavior(JSClassOps { addProperty: None, delProperty: None, enumerate: None, @@ -106,7 +106,7 @@ impl InterfaceConstructorBehavior { /// An interface constructor that calls a native Rust function. pub const fn call(hook: ConstructorClassHook) -> Self { - InterfaceConstructorBehavior(ClassOps { + InterfaceConstructorBehavior(JSClassOps { addProperty: None, delProperty: None, enumerate: None, diff --git a/components/script/dom/bindings/namespace.rs b/components/script/dom/bindings/namespace.rs index 82b7bd8834e..64b7b0b2626 100644 --- a/components/script/dom/bindings/namespace.rs +++ b/components/script/dom/bindings/namespace.rs @@ -9,6 +9,7 @@ use crate::dom::bindings::interface::{create_object, define_on_global_object}; use crate::script_runtime::JSContext; use js::jsapi::{JSClass, JSFunctionSpec}; use js::rust::{HandleObject, MutableHandleObject}; +use std::ptr; /// The class of a namespace object. #[derive(Clone, Copy)] @@ -23,7 +24,9 @@ impl NamespaceObjectClass { name: name as *const _ as *const libc::c_char, flags: 0, cOps: 0 as *mut _, - reserved: [0 as *mut _; 3], + spec: ptr::null(), + ext: ptr::null(), + oOps: ptr::null(), }) } } diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index a4b5f418cd2..11913c03642 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -55,7 +55,11 @@ pub unsafe extern "C" fn shadow_check_callback( /// Initialize the infrastructure for DOM proxy objects. pub unsafe fn init() { - SetDOMProxyInformation(GetProxyHandlerFamily(), Some(shadow_check_callback)); + SetDOMProxyInformation( + GetProxyHandlerFamily(), + Some(shadow_check_callback), + ptr::null(), + ); } /// Defines an expando on the given `proxy`. diff --git a/components/script/dom/bindings/record.rs b/components/script/dom/bindings/record.rs index 86453b59fc3..c54a2403d70 100644 --- a/components/script/dom/bindings/record.rs +++ b/components/script/dom/bindings/record.rs @@ -120,12 +120,12 @@ where } rooted!(in(cx) let object = value.to_object()); - let ids = IdVector::new(cx); + let mut ids = IdVector::new(cx); if !GetPropertyKeys( cx, object.handle(), JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS, - ids.get(), + ids.handle_mut(), ) { return Err(()); } diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index 2b5a4987eb5..78635b2be31 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -6,6 +6,7 @@ use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::trace::JSTraceable; use crate::dom::globalscope::GlobalScope; use crate::script_runtime::JSContext; use js::jsapi::{Heap, JSObject}; @@ -53,7 +54,7 @@ impl Reflector { } /// Initialize the reflector. (May be called only once.) - pub fn set_jsobject(&mut self, object: *mut JSObject) { + pub unsafe fn set_jsobject(&self, object: *mut JSObject) { assert!(self.object.get().is_null()); assert!(!object.is_null()); self.object.set(object); @@ -75,7 +76,7 @@ impl Reflector { } /// A trait to provide access to the `Reflector` for a DOM object. -pub trait DomObject: 'static { +pub trait DomObject: JSTraceable + 'static { /// Returns the receiver's reflector. fn reflector(&self) -> &Reflector; @@ -97,11 +98,11 @@ impl DomObject for Reflector { /// A trait to initialize the `Reflector` for a DOM object. pub trait MutDomObject: DomObject { /// Initializes the Reflector - fn init_reflector(&mut self, obj: *mut JSObject); + unsafe fn init_reflector(&self, obj: *mut JSObject); } impl MutDomObject for Reflector { - fn init_reflector(&mut self, obj: *mut JSObject) { + unsafe fn init_reflector(&self, obj: *mut JSObject) { self.set_jsobject(obj) } } diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 19df89df53f..0ba69687dba 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -26,7 +26,7 @@ use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::reflector::{DomObject, Reflector}; +use crate::dom::bindings::reflector::{DomObject, MutDomObject, Reflector}; use crate::dom::bindings::trace::trace_reflector; use crate::dom::bindings::trace::JSTraceable; use crate::dom::node::Node; @@ -62,7 +62,7 @@ where /// It cannot outlive its associated `RootCollection`, and it gives /// out references which cannot outlive this new `Root`. #[allow(unrooted_must_root)] - unsafe fn new(value: T) -> Self { + pub unsafe fn new(value: T) -> Self { debug_assert!(thread_state::get().is_script()); STACK_ROOTS.with(|ref root_list| { let root_list = &*root_list.get().unwrap(); @@ -99,6 +99,32 @@ where } } +unsafe impl<T> StableTraceObject for MaybeUnreflectedDom<T> +where + T: DomObject, +{ + fn stable_trace_object<'a>(&'a self) -> *const dyn JSTraceable { + // The JSTraceable impl for Reflector doesn't actually do anything, + // so we need this shenanigan to actually trace the reflector of the + // T pointer in Dom<T>. + #[allow(unrooted_must_root)] + struct MaybeUnreflectedStackRoot<T>(T); + unsafe impl<T> JSTraceable for MaybeUnreflectedStackRoot<T> + where + T: DomObject, + { + unsafe fn trace(&self, tracer: *mut JSTracer) { + if self.0.reflector().get_jsobject().is_null() { + self.0.trace(tracer); + } else { + trace_reflector(tracer, "on stack", &self.0.reflector()); + } + } + } + unsafe { &*(self.ptr.as_ptr() as *const T as *const MaybeUnreflectedStackRoot<T>) } + } +} + impl<T> Deref for Root<T> where T: Deref + StableTraceObject, @@ -236,7 +262,10 @@ impl RootCollection { unsafe fn unroot(&self, object: *const dyn JSTraceable) { debug_assert!(thread_state::get().is_script()); let roots = &mut *self.roots.get(); - match roots.iter().rposition(|r| *r == object) { + match roots + .iter() + .rposition(|r| *r as *const () == object as *const ()) + { Some(idx) => { roots.remove(idx); }, @@ -341,6 +370,46 @@ unsafe impl<T: DomObject> JSTraceable for Dom<T> { } } +/// A traced reference to a DOM object that may not be reflected yet. +#[unrooted_must_root_lint::must_root] +pub struct MaybeUnreflectedDom<T> { + ptr: ptr::NonNull<T>, +} + +impl<T> MaybeUnreflectedDom<T> +where + T: DomObject, +{ + #[allow(unrooted_must_root)] + pub unsafe fn from_box(value: Box<T>) -> Self { + Self { + ptr: Box::into_raw_non_null(value), + } + } +} + +impl<T> Root<MaybeUnreflectedDom<T>> +where + T: DomObject, +{ + pub fn as_ptr(&self) -> *const T { + self.value.ptr.as_ptr() + } +} + +impl<T> Root<MaybeUnreflectedDom<T>> +where + T: MutDomObject, +{ + pub unsafe fn reflect_with(self, obj: *mut JSObject) -> DomRoot<T> { + let ptr = self.as_ptr(); + drop(self); + let root = DomRoot::from_ref(&*ptr); + root.init_reflector(obj); + root + } +} + /// An unrooted reference to a DOM object for use in layout. `Layout*Helpers` /// traits must be implemented on this. #[unrooted_must_root_lint::allow_unrooted_interior] diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index dbf852907a8..511b8d1650e 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -361,6 +361,9 @@ pub fn read( JS_STRUCTURED_CLONE_VERSION, StructuredCloneScope::DifferentProcess, rval, + CloneDataPolicy { + sharedArrayBuffer_: false, + }, &STRUCTURED_CLONE_CALLBACKS, sc_holder_ptr as *mut raw::c_void, ); diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 3fb93b98dfa..2d6b6c04379 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -83,8 +83,8 @@ use media::WindowGLContext; use metrics::{InteractiveMetrics, InteractiveWindow}; use mime::Mime; use msg::constellation_msg::{ - BlobId, BrowsingContextId, HistoryStateId, MessagePortId, MessagePortRouterId, PipelineId, - TopLevelBrowsingContextId, + BlobId, BroadcastChannelRouterId, BrowsingContextId, HistoryStateId, MessagePortId, + MessagePortRouterId, PipelineId, TopLevelBrowsingContextId, }; use net_traits::filemanager_thread::RelativePos; use net_traits::image::base::{Image, ImageMetadata}; @@ -175,6 +175,8 @@ unsafe_no_jsmanaged_fields!(MessagePortId); unsafe_no_jsmanaged_fields!(RefCell<Option<MessagePortId>>); unsafe_no_jsmanaged_fields!(MessagePortRouterId); +unsafe_no_jsmanaged_fields!(BroadcastChannelRouterId); + unsafe_no_jsmanaged_fields!(BlobId); unsafe_no_jsmanaged_fields!(BlobImpl); @@ -889,7 +891,11 @@ impl RootedTraceableSet { unsafe fn remove(traceable: *const dyn JSTraceable) { ROOTED_TRACEABLES.with(|ref traceables| { let mut traceables = traceables.borrow_mut(); - let idx = match traceables.set.iter().rposition(|x| *x == traceable) { + let idx = match traceables + .set + .iter() + .rposition(|x| *x as *const () == traceable as *const ()) + { Some(idx) => idx, None => unreachable!(), }; @@ -916,35 +922,6 @@ impl RootedTraceableSet { /// If you have GC things like *mut JSObject or JSVal, use rooted!. /// If you have an arbitrary number of DomObjects to root, use rooted_vec!. /// If you know what you're doing, use this. -#[derive(JSTraceable)] -pub struct RootedTraceable<'a, T: 'static + JSTraceable> { - ptr: &'a T, -} - -impl<'a, T: JSTraceable + 'static> RootedTraceable<'a, T> { - /// DomRoot a JSTraceable thing for the life of this RootedTraceable - pub fn new(traceable: &'a T) -> RootedTraceable<'a, T> { - unsafe { - RootedTraceableSet::add(traceable); - } - RootedTraceable { ptr: traceable } - } -} - -impl<'a, T: JSTraceable + 'static> Drop for RootedTraceable<'a, T> { - fn drop(&mut self) { - unsafe { - RootedTraceableSet::remove(self.ptr); - } - } -} - -/// Roots any JSTraceable thing -/// -/// If you have a valid DomObject, use DomRoot. -/// If you have GC things like *mut JSObject or JSVal, use rooted!. -/// If you have an arbitrary number of DomObjects to root, use rooted_vec!. -/// If you know what you're doing, use this. #[unrooted_must_root_lint::allow_unrooted_interior] pub struct RootedTraceableBox<T: 'static + JSTraceable> { ptr: *mut T, @@ -957,12 +934,12 @@ unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> { } impl<T: JSTraceable + 'static> RootedTraceableBox<T> { - /// DomRoot a JSTraceable thing for the life of this RootedTraceable + /// DomRoot a JSTraceable thing for the life of this RootedTraceableBox pub fn new(traceable: T) -> RootedTraceableBox<T> { Self::from_box(Box::new(traceable)) } - /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceable. + /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox. pub fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> { let traceable = Box::into_raw(boxed_traceable); unsafe { diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 56b2112e498..21f82be9cee 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -23,8 +23,9 @@ use js::glue::{ }; use js::jsapi::HandleId as RawHandleId; use js::jsapi::HandleObject as RawHandleObject; +use js::jsapi::MutableHandleIdVector as RawMutableHandleIdVector; use js::jsapi::MutableHandleObject as RawMutableHandleObject; -use js::jsapi::{AutoIdVector, CallArgs, DOMCallbacks, GetNonCCWObjectGlobal}; +use js::jsapi::{CallArgs, DOMCallbacks, GetNonCCWObjectGlobal}; use js::jsapi::{Heap, JSAutoRealm, JSContext, JS_FreezeObject}; use js::jsapi::{JSJitInfo, JSObject, JSTracer, JSWrapObjectCallbacks}; use js::jsapi::{JS_EnumerateStandardClasses, JS_GetLatin1StringCharsAndLength}; @@ -375,7 +376,7 @@ pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) { pub unsafe extern "C" fn enumerate_global( cx: *mut JSContext, obj: RawHandleObject, - _props: *mut AutoIdVector, + _props: RawMutableHandleIdVector, _enumerable_only: bool, ) -> bool { assert!(JS_IsGlobalObject(obj.get())); @@ -439,6 +440,7 @@ unsafe extern "C" fn wrap( unsafe extern "C" fn pre_wrap( cx: *mut JSContext, _scope: RawHandleObject, + _orig_obj: RawHandleObject, obj: RawHandleObject, _object_passed_to_wrap: RawHandleObject, rval: RawMutableHandleObject, @@ -482,7 +484,7 @@ unsafe fn generic_call( let args = CallArgs::from_vp(vp, argc); let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp)); - let proto_id = (*info).protoID; + let proto_id = (*info).__bindgen_anon_2.protoID; let thisobj = args.thisv(); if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() { @@ -495,7 +497,7 @@ unsafe fn generic_call( } else { GetNonCCWObjectGlobal(JS_CALLEE(cx, vp).to_object_or_null()) }); - let depth = (*info).depth; + let depth = (*info).__bindgen_anon_3.depth; let proto_check = |class: &'static DOMClass| class.interface_chain[depth as usize] as u16 == proto_id; let this = match private_from_proto_check(obj.get(), cx, proto_check) { @@ -582,7 +584,7 @@ pub unsafe extern "C" fn generic_lenient_setter( } unsafe extern "C" fn instance_class_has_proto_at_depth( - clasp: *const js::jsapi::Class, + clasp: *const js::jsapi::JSClass, proto_id: u32, depth: u32, ) -> bool { diff --git a/components/script/dom/broadcastchannel.rs b/components/script/dom/broadcastchannel.rs new file mode 100644 index 00000000000..f7d75d8c101 --- /dev/null +++ b/components/script/dom/broadcastchannel.rs @@ -0,0 +1,106 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::{ + BroadcastChannelMethods, Wrap, +}; +use crate::dom::bindings::error::{Error, ErrorResult}; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::structuredclone; +use crate::dom::eventtarget::EventTarget; +use crate::dom::globalscope::GlobalScope; +use crate::script_runtime::JSContext as SafeJSContext; +use dom_struct::dom_struct; +use js::rust::HandleValue; +use script_traits::BroadcastMsg; +use std::cell::Cell; +use uuid::Uuid; + +#[dom_struct] +pub struct BroadcastChannel { + eventtarget: EventTarget, + name: DOMString, + closed: Cell<bool>, + id: Uuid, +} + +impl BroadcastChannel { + /// <https://html.spec.whatwg.org/multipage/#broadcastchannel> + #[allow(non_snake_case)] + pub fn Constructor(global: &GlobalScope, name: DOMString) -> DomRoot<BroadcastChannel> { + BroadcastChannel::new(global, name) + } + + pub fn new(global: &GlobalScope, name: DOMString) -> DomRoot<BroadcastChannel> { + let channel = reflect_dom_object( + Box::new(BroadcastChannel::new_inherited(name)), + global, + Wrap, + ); + global.track_broadcast_channel(&*channel); + channel + } + + pub fn new_inherited(name: DOMString) -> BroadcastChannel { + BroadcastChannel { + eventtarget: EventTarget::new_inherited(), + name, + closed: Default::default(), + id: Uuid::new_v4(), + } + } + + /// The unique Id of this channel. + /// Used for filtering out the sender from the local broadcast. + pub fn id(&self) -> &Uuid { + &self.id + } + + /// Is this channel closed? + pub fn closed(&self) -> bool { + self.closed.get() + } +} + +impl BroadcastChannelMethods for BroadcastChannel { + /// <https://html.spec.whatwg.org/multipage/#dom-messageport-postmessage> + fn PostMessage(&self, cx: SafeJSContext, message: HandleValue) -> ErrorResult { + // Step 3, if closed. + if self.closed.get() { + return Err(Error::InvalidState); + } + + // Step 6, StructuredSerialize(message). + let data = structuredclone::write(cx, message, None)?; + + let global = self.global(); + + let msg = BroadcastMsg { + origin: global.origin().immutable().clone(), + channel_name: self.Name().to_string(), + data, + }; + + global.schedule_broadcast(msg, &self.id); + Ok(()) + } + + /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-name> + fn Name(&self) -> DOMString { + self.name.clone() + } + + /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-close> + fn Close(&self) { + self.closed.set(true); + } + + // <https://html.spec.whatwg.org/multipage/#handler-broadcastchannel-onmessageerror> + event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror); + + // <https://html.spec.whatwg.org/multipage/#handler-broadcastchannel-onmessage> + event_handler!(message, GetOnmessage, SetOnmessage); +} diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6ee46bea95b..303c51660f0 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -519,7 +519,7 @@ impl Document { // Set the document's activity level, reflow if necessary, and suspend or resume timers. self.activity.set(activity); let media = ServoMedia::get().unwrap(); - let pipeline_id = self.window().pipeline_id().expect("doc with no pipeline"); + let pipeline_id = self.window().pipeline_id(); let client_context_id = ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get()); @@ -1636,11 +1636,10 @@ impl Document { .borrow_mut() .push((ident, Some(callback))); - // TODO: Should tick animation only when document is visible - // If we are running 'fake' animation frames, we unconditionally // set up a one-shot timer for script to execute the rAF callbacks. - if self.is_faking_animation_frames() { + if self.is_faking_animation_frames() && self.window().visible() { + warn!("Scheduling fake animation frame. Animation frames tick too fast."); let callback = FakeRequestAnimationFrameCallback { document: Trusted::new(self), }; @@ -3062,14 +3061,56 @@ impl Document { self.redirect_count.set(count) } - fn create_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> DomRoot<NodeList> { + pub fn elements_by_name_count(&self, name: &DOMString) -> u32 { + if name.is_empty() { + return 0; + } + self.count_node_list(|n| Document::is_element_in_get_by_name(n, name)) + } + + pub fn nth_element_by_name(&self, index: u32, name: &DOMString) -> Option<DomRoot<Node>> { + if name.is_empty() { + return None; + } + self.nth_in_node_list(index, |n| Document::is_element_in_get_by_name(n, name)) + } + + // Note that document.getByName does not match on the same conditions + // as the document named getter. + fn is_element_in_get_by_name(node: &Node, name: &DOMString) -> bool { + let element = match node.downcast::<Element>() { + Some(element) => element, + None => return false, + }; + if element.namespace() != &ns!(html) { + return false; + } + element.get_name().map_or(false, |n| *n == **name) + } + + fn count_node_list<F: Fn(&Node) -> bool>(&self, callback: F) -> u32 { + let doc = self.GetDocumentElement(); + let maybe_node = doc.as_deref().map(Castable::upcast::<Node>); + maybe_node + .iter() + .flat_map(|node| node.traverse_preorder(ShadowIncluding::No)) + .filter(|node| callback(&node)) + .count() as u32 + } + + fn nth_in_node_list<F: Fn(&Node) -> bool>( + &self, + index: u32, + callback: F, + ) -> Option<DomRoot<Node>> { let doc = self.GetDocumentElement(); let maybe_node = doc.as_deref().map(Castable::upcast::<Node>); - let iter = maybe_node + maybe_node .iter() .flat_map(|node| node.traverse_preorder(ShadowIncluding::No)) - .filter(|node| callback(&node)); - NodeList::new_simple_list(&self.window, iter) + .filter(|node| callback(&node)) + .nth(index as usize) + .map(|n| DomRoot::from_ref(&*n)) } fn get_html_element(&self) -> Option<DomRoot<HTMLHtmlElement>> { @@ -3318,7 +3359,7 @@ impl Document { let script_msg = CommonScriptMsg::Task( ScriptThreadEventCategory::EnterFullscreen, handler, - pipeline_id, + Some(pipeline_id), TaskSourceName::DOMManipulation, ); let msg = MainThreadScriptMsg::Common(script_msg); @@ -4250,16 +4291,7 @@ impl DocumentMethods for Document { // https://html.spec.whatwg.org/multipage/#dom-document-getelementsbyname fn GetElementsByName(&self, name: DOMString) -> DomRoot<NodeList> { - self.create_node_list(|node| { - let element = match node.downcast::<Element>() { - Some(element) => element, - None => return false, - }; - if element.namespace() != &ns!(html) { - return false; - } - element.get_name().map_or(false, |atom| *atom == *name) - }) + NodeList::new_elements_by_name_list(self.window(), self, name) } // https://html.spec.whatwg.org/multipage/#dom-document-images diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index eecff289cdb..268b263fc41 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -540,6 +540,16 @@ impl Element { } true // whatwg/html#5239 } + + // https://html.spec.whatwg.org/multipage/#the-directionality + pub fn directionality(&self) -> String { + self.downcast::<HTMLElement>() + .and_then(|html_element| html_element.directionality()) + .unwrap_or_else(|| { + let node = self.upcast::<Node>(); + node.parent_directionality() + }) + } } #[allow(unsafe_code)] diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 4020f938b24..90e533c8cc3 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -37,9 +37,9 @@ use crate::dom::workerglobalscope::WorkerGlobalScope; use crate::realms::{enter_realm, InRealm}; use dom_struct::dom_struct; use fnv::FnvHasher; -use js::jsapi::{JSFunction, JS_GetFunctionObject, SourceText}; +use js::jsapi::{JS_GetFunctionObject, SourceText}; use js::rust::wrappers::CompileFunction; -use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper}; +use js::rust::{CompileOptionsWrapper, RootedObjectVectorWrapper}; use libc::c_char; use servo_atoms::Atom; use servo_url::ServoUrl; @@ -51,7 +51,6 @@ use std::hash::BuildHasherDefault; use std::marker::PhantomData; use std::mem; use std::ops::{Deref, DerefMut}; -use std::ptr; use std::rc::Rc; #[derive(Clone, JSTraceable, MallocSizeOf, PartialEq)] @@ -491,9 +490,10 @@ impl EventTarget { // source text, we handle parse errors later // Step 3.8 TODO: settings objects not implemented + let window = document.window(); + let _ac = enter_realm(&*window); // Step 3.9 - let window = document.window(); let url_serialized = CString::new(handler.url.to_string()).unwrap(); let name = CString::new(&**ty).unwrap(); @@ -517,10 +517,12 @@ impl EventTarget { }; let cx = window.get_cx(); - let options = CompileOptionsWrapper::new(*cx, url_serialized.as_ptr(), handler.line as u32); + let options = unsafe { + CompileOptionsWrapper::new(*cx, url_serialized.as_ptr(), handler.line as u32) + }; // Step 3.9, subsection Scope steps 1-6 - let scopechain = AutoObjectVectorWrapper::new(*cx); + let scopechain = RootedObjectVectorWrapper::new(*cx); if let Some(element) = element { scopechain.append(document.reflector().get_jsobject().get()); @@ -530,12 +532,10 @@ impl EventTarget { scopechain.append(element.reflector().get_jsobject().get()); } - let _ac = enter_realm(&*window); // TODO 3.8 should replace this - rooted!(in(*cx) let mut handler = ptr::null_mut::<JSFunction>()); - let rv = unsafe { + rooted!(in(*cx) let mut handler = unsafe { CompileFunction( *cx, - scopechain.ptr, + scopechain.handle(), options.ptr, name.as_ptr(), args.len() as u32, @@ -546,10 +546,9 @@ impl EventTarget { ownsUnits_: false, _phantom_0: PhantomData, }, - handler.handle_mut().into(), ) - }; - if !rv || handler.get().is_null() { + }); + if handler.get().is_null() { // Step 3.7 unsafe { let ar = enter_realm(&*self); diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index de3e83101e1..9e55f593f59 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3,7 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::BroadcastChannelBinding::BroadcastChannelMethods; use crate::dom::bindings::codegen::Bindings::EventSourceBinding::EventSourceBinding::EventSourceMethods; +use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState; use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::WorkerGlobalScopeBinding::WorkerGlobalScopeMethods; @@ -19,6 +21,7 @@ use crate::dom::bindings::structuredclone; use crate::dom::bindings::utils::to_frozen_array; use crate::dom::bindings::weakref::{DOMTracker, WeakRef}; use crate::dom::blob::Blob; +use crate::dom::broadcastchannel::BroadcastChannel; use crate::dom::crypto::Crypto; use crate::dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use crate::dom::errorevent::ErrorEvent; @@ -58,20 +61,22 @@ use crate::timers::{OneshotTimers, TimerCallback}; use content_security_policy::CspList; use devtools_traits::{PageError, ScriptToDevtoolsControlMsg}; use dom_struct::dom_struct; +use embedder_traits::EmbedderMsg; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use js::glue::{IsWrapper, UnwrapObjectDynamic}; -use js::jsapi::JSContext; -use js::jsapi::JSObject; use js::jsapi::{CurrentGlobalOrNull, GetNonCCWObjectGlobal}; use js::jsapi::{HandleObject, Heap}; +use js::jsapi::{JSContext, JSObject, SourceText}; use js::jsval::{JSVal, UndefinedValue}; use js::panic::maybe_resume_unwind; -use js::rust::wrappers::EvaluateUtf8; +use js::rust::wrappers::Evaluate2; use js::rust::{get_object_class, CompileOptionsWrapper, ParentRuntime, Runtime}; use js::rust::{HandleValue, MutableHandleValue}; use js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL}; -use msg::constellation_msg::{BlobId, MessagePortId, MessagePortRouterId, PipelineId}; +use msg::constellation_msg::{ + BlobId, BroadcastChannelRouterId, MessagePortId, MessagePortRouterId, PipelineId, +}; use net_traits::blob_url_store::{get_blob_origin, BlobBuf}; use net_traits::filemanager_thread::{ FileManagerResult, FileManagerThreadMsg, ReadFileProgress, RelativePos, @@ -82,16 +87,17 @@ use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_tim use script_traits::serializable::{BlobData, BlobImpl, FileBlob}; use script_traits::transferable::MessagePortImpl; use script_traits::{ - MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, ScriptToConstellationChan, TimerEvent, + BroadcastMsg, MessagePortMsg, MsDuration, PortMessageTask, ScriptMsg, + ScriptToConstellationChan, TimerEvent, }; use script_traits::{TimerEventId, TimerSchedulerMsg, TimerSource}; use servo_url::{MutableOrigin, ServoUrl}; -use smallvec::SmallVec; use std::borrow::Cow; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, RefCell, RefMut}; use std::collections::hash_map::Entry; use std::collections::{HashMap, VecDeque}; use std::ffi::CString; +use std::marker::PhantomData; use std::mem; use std::ops::Index; use std::rc::Rc; @@ -99,13 +105,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use time::{get_time, Timespec}; use uuid::Uuid; -use webgpu::wgpu::{ - id::{ - AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandEncoderId, ComputePipelineId, - DeviceId, PipelineLayoutId, ShaderModuleId, - }, - Backend, -}; #[derive(JSTraceable)] pub struct AutoCloseWorker(Arc<AtomicBool>); @@ -124,6 +123,9 @@ pub struct GlobalScope { /// The message-port router id for this global, if it is managing ports. message_port_state: DomRefCell<MessagePortState>, + /// The broadcast channels state this global, if it is managing any. + broadcast_channel_state: DomRefCell<BroadcastChannelState>, + /// The blobs managed by this global, if any. blob_state: DomRefCell<BlobState>, @@ -181,6 +183,9 @@ pub struct GlobalScope { /// The origin of the globalscope origin: MutableOrigin, + /// A map for storing the previous permission state read results. + permission_state_invocation_results: DomRefCell<HashMap<String, PermissionState>>, + /// The microtask queue associated with this global. /// /// It is refcounted because windows in the same script thread share the @@ -237,6 +242,13 @@ struct MessageListener { context: Trusted<GlobalScope>, } +/// A wrapper for broadcasts coming in over IPC, and the event-loop. +struct BroadcastListener { + canceller: TaskCanceller, + task_source: DOMManipulationTaskSource, + context: Trusted<GlobalScope>, +} + /// A wrapper between timer events coming in over IPC, and the event-loop. struct TimerListener { canceller: TaskCanceller, @@ -310,6 +322,23 @@ pub struct ManagedMessagePort { closed: bool, } +/// State representing whether this global is currently managing broadcast channels. +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub enum BroadcastChannelState { + /// The broadcast-channel router id for this global, and a queue of managed channels. + /// Step 9, "sort destinations" + /// of https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage + /// requires keeping track of creation order, hence the queue. + Managed( + BroadcastChannelRouterId, + /// The map of channel-name to queue of channels, in order of creation. + HashMap<DOMString, VecDeque<Dom<BroadcastChannel>>>, + ), + /// This global is not managing any broadcast channels at this time. + UnManaged, +} + /// State representing whether this global is currently managing messageports. #[derive(JSTraceable, MallocSizeOf)] #[unrooted_must_root_lint::must_root] @@ -323,6 +352,29 @@ pub enum MessagePortState { UnManaged, } +impl BroadcastListener { + /// Handle a broadcast coming in over IPC, + /// by queueing the appropriate task on the relevant event-loop. + fn handle(&self, event: BroadcastMsg) { + let context = self.context.clone(); + + // Note: strictly speaking we should just queue the message event tasks, + // not queue a task that then queues more tasks. + // This however seems to be hard to avoid in the light of the IPC. + // One can imagine queueing tasks directly, + // for channels that would be in the same script-thread. + let _ = self.task_source.queue_with_canceller( + task!(broadcast_message_event: move || { + let global = context.root(); + // Step 10 of https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage, + // For each BroadcastChannel object destination in destinations, queue a task. + global.broadcast_message_event(event, None); + }), + &self.canceller, + ); + } +} + impl TimerListener { /// Handle a timer-event coming-in over IPC, /// by queuing the appropriate task on the relevant event-loop. @@ -501,6 +553,7 @@ impl GlobalScope { ) -> Self { Self { message_port_state: DomRefCell::new(MessagePortState::UnManaged), + broadcast_channel_state: DomRefCell::new(BroadcastChannelState::UnManaged), blob_state: DomRefCell::new(BlobState::UnManaged), eventtarget: EventTarget::new_inherited(), crypto: Default::default(), @@ -519,6 +572,7 @@ impl GlobalScope { timers: OneshotTimers::new(scheduler_chan), init_timers: Default::default(), origin, + permission_state_invocation_results: Default::default(), microtask_queue, list_auto_close_worker: Default::default(), event_source_tracker: DOMTracker::new(), @@ -613,11 +667,18 @@ impl GlobalScope { pub fn perform_a_dom_garbage_collection_checkpoint(&self) { self.perform_a_message_port_garbage_collection_checkpoint(); self.perform_a_blob_garbage_collection_checkpoint(); + self.perform_a_broadcast_channel_garbage_collection_checkpoint(); + } + + /// Remove the routers for ports and broadcast-channels. + pub fn remove_web_messaging_infra(&self) { + self.remove_message_ports_router(); + self.remove_broadcast_channel_router(); } /// Update our state to un-managed, /// and tell the constellation to drop the sender to our message-port router. - pub fn remove_message_ports_router(&self) { + fn remove_message_ports_router(&self) { if let MessagePortState::Managed(router_id, _message_ports) = &*self.message_port_state.borrow() { @@ -628,6 +689,22 @@ impl GlobalScope { *self.message_port_state.borrow_mut() = MessagePortState::UnManaged; } + /// Update our state to un-managed, + /// and tell the constellation to drop the sender to our broadcast router. + fn remove_broadcast_channel_router(&self) { + if let BroadcastChannelState::Managed(router_id, _channels) = + &*self.broadcast_channel_state.borrow() + { + let _ = + self.script_to_constellation_chan() + .send(ScriptMsg::RemoveBroadcastChannelRouter( + router_id.clone(), + self.origin().immutable().clone(), + )); + } + *self.broadcast_channel_state.borrow_mut() = BroadcastChannelState::UnManaged; + } + /// <https://html.spec.whatwg.org/multipage/#entangle> pub fn entangle_ports(&self, port1: MessagePortId, port2: MessagePortId) { if let MessagePortState::Managed(_id, message_ports) = @@ -789,6 +866,115 @@ impl GlobalScope { .send(ScriptMsg::RerouteMessagePort(port_id, task)); } + /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage> + /// Step 7 and following steps. + pub fn schedule_broadcast(&self, msg: BroadcastMsg, channel_id: &Uuid) { + // First, broadcast locally. + self.broadcast_message_event(msg.clone(), Some(channel_id)); + + if let BroadcastChannelState::Managed(router_id, _) = + &*self.broadcast_channel_state.borrow() + { + // Second, broadcast to other globals via the constellation. + // + // Note: for globals in the same script-thread, + // we could skip the hop to the constellation. + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::ScheduleBroadcast(router_id.clone(), msg)); + } else { + panic!("Attemps to broadcast a message via global not managing any channels."); + } + } + + /// <https://html.spec.whatwg.org/multipage/#dom-broadcastchannel-postmessage> + /// Step 7 and following steps. + pub fn broadcast_message_event(&self, event: BroadcastMsg, channel_id: Option<&Uuid>) { + if let BroadcastChannelState::Managed(_, channels) = &*self.broadcast_channel_state.borrow() + { + let BroadcastMsg { + data, + origin, + channel_name, + } = event; + + // Step 7, a few preliminary steps. + + // - Check the worker is not closing. + if let Some(worker) = self.downcast::<WorkerGlobalScope>() { + if worker.is_closing() { + return; + } + } + + // - Check the associated document is fully-active. + if let Some(window) = self.downcast::<Window>() { + if !window.Document().is_fully_active() { + return; + } + } + + // - Check for a case-sensitive match for the name of the channel. + let channel_name = DOMString::from_string(channel_name); + + if let Some(channels) = channels.get(&channel_name) { + channels + .iter() + .filter(|ref channel| { + // Step 8. + // Filter out the sender. + if let Some(id) = channel_id { + channel.id() != id + } else { + true + } + }) + .map(|channel| DomRoot::from_ref(&**channel)) + // Step 9, sort by creation order, + // done by using a queue to store channels in creation order. + .for_each(|channel| { + let data = data.clone_for_broadcast(); + let origin = origin.clone(); + + // Step 10: Queue a task on the DOM manipulation task-source, + // to fire the message event + let channel = Trusted::new(&*channel); + let global = Trusted::new(&*self); + let _ = self.dom_manipulation_task_source().queue( + task!(process_pending_port_messages: move || { + let destination = channel.root(); + let global = global.root(); + + // 10.1 Check for closed flag. + if destination.closed() { + return; + } + + rooted!(in(*global.get_cx()) let mut message = UndefinedValue()); + + // Step 10.3 StructuredDeserialize(serialized, targetRealm). + if let Ok(ports) = structuredclone::read(&global, data, message.handle_mut()) { + // Step 10.4, Fire an event named message at destination. + MessageEvent::dispatch_jsval( + &*destination.upcast(), + &global, + message.handle(), + Some(&origin.ascii_serialization()), + None, + ports, + ); + } else { + // Step 10.3, fire an event named messageerror at destination. + MessageEvent::dispatch_error(&*destination.upcast(), &global); + } + }), + &self, + ); + }); + } + } + } + /// Route the task to be handled by the relevant port. pub fn route_task_to_port(&self, port_id: MessagePortId, task: PortMessageTask) { let should_dispatch = if let MessagePortState::Managed(_id, message_ports) = @@ -905,6 +1091,93 @@ impl GlobalScope { } } + /// Remove broadcast-channels that are closed. + /// TODO: Also remove them if they do not have an event-listener. + /// see https://github.com/servo/servo/issues/25772 + pub fn perform_a_broadcast_channel_garbage_collection_checkpoint(&self) { + let is_empty = if let BroadcastChannelState::Managed(router_id, ref mut channels) = + &mut *self.broadcast_channel_state.borrow_mut() + { + channels.retain(|name, ref mut channels| { + channels.retain(|ref chan| !chan.closed()); + if channels.is_empty() { + let _ = self.script_to_constellation_chan().send( + ScriptMsg::RemoveBroadcastChannelNameInRouter( + router_id.clone(), + name.to_string(), + self.origin().immutable().clone(), + ), + ); + false + } else { + true + } + }); + channels.is_empty() + } else { + false + }; + if is_empty { + self.remove_broadcast_channel_router(); + } + } + + /// Start tracking a broadcast-channel. + pub fn track_broadcast_channel(&self, dom_channel: &BroadcastChannel) { + let mut current_state = self.broadcast_channel_state.borrow_mut(); + + if let BroadcastChannelState::UnManaged = &*current_state { + // Setup a route for IPC, for broadcasts from the constellation to our channels. + let (broadcast_control_sender, broadcast_control_receiver) = + ipc::channel().expect("ipc channel failure"); + let context = Trusted::new(self); + let (task_source, canceller) = ( + self.dom_manipulation_task_source(), + self.task_canceller(TaskSourceName::DOMManipulation), + ); + let listener = BroadcastListener { + canceller, + task_source, + context, + }; + ROUTER.add_route( + broadcast_control_receiver.to_opaque(), + Box::new(move |message| { + let msg = message.to(); + match msg { + Ok(msg) => listener.handle(msg), + Err(err) => warn!("Error receiving a BroadcastMsg: {:?}", err), + } + }), + ); + let router_id = BroadcastChannelRouterId::new(); + *current_state = BroadcastChannelState::Managed(router_id.clone(), HashMap::new()); + let _ = self + .script_to_constellation_chan() + .send(ScriptMsg::NewBroadcastChannelRouter( + router_id, + broadcast_control_sender, + self.origin().immutable().clone(), + )); + } + + if let BroadcastChannelState::Managed(router_id, channels) = &mut *current_state { + let entry = channels.entry(dom_channel.Name()).or_insert_with(|| { + let _ = self.script_to_constellation_chan().send( + ScriptMsg::NewBroadcastChannelNameInRouter( + router_id.clone(), + dom_channel.Name().to_string(), + self.origin().immutable().clone(), + ), + ); + VecDeque::new() + }); + entry.push_back(Dom::from_ref(dom_channel)); + } else { + panic!("track_broadcast_channel should have first switched the state to managed."); + } + } + /// Start tracking a message-port pub fn track_message_port(&self, dom_port: &MessagePort, port_impl: Option<MessagePortImpl>) { let mut current_state = self.message_port_state.borrow_mut(); @@ -1400,8 +1673,7 @@ impl GlobalScope { let resource_threads = self.resource_threads(); let (chan, recv) = profile_ipc::channel(self.time_profiler_chan().clone()).unwrap(); let origin = get_blob_origin(&self.get_url()); - let check_url_validity = false; - let msg = FileManagerThreadMsg::ReadFile(chan, id, check_url_validity, origin); + let msg = FileManagerThreadMsg::ReadFile(chan, id, origin); let _ = resource_threads.send(CoreResourceMsg::ToFileManager(msg)); recv } @@ -1427,6 +1699,12 @@ impl GlobalScope { } } + pub fn permission_state_invocation_results( + &self, + ) -> &DomRefCell<HashMap<String, PermissionState>> { + &self.permission_state_invocation_results + } + pub fn track_worker(&self, closing_worker: Arc<AtomicBool>) { self.list_auto_close_worker .borrow_mut() @@ -1632,6 +1910,14 @@ impl GlobalScope { &self.script_to_constellation_chan } + pub fn send_to_embedder(&self, msg: EmbedderMsg) { + self.send_to_constellation(ScriptMsg::ForwardToEmbedder(msg)); + } + + pub fn send_to_constellation(&self, msg: ScriptMsg) { + self.script_to_constellation_chan().send(msg).unwrap(); + } + pub fn scheduler_chan(&self) -> &IpcSender<TimerSchedulerMsg> { &self.scheduler_chan } @@ -1872,15 +2158,20 @@ impl GlobalScope { let ar = enter_realm(&*self); let _aes = AutoEntryScript::new(self); - let options = CompileOptionsWrapper::new(*cx, filename.as_ptr(), line_number); + let options = + unsafe { CompileOptionsWrapper::new(*cx, filename.as_ptr(), line_number) }; debug!("evaluating Dom string"); let result = unsafe { - EvaluateUtf8( + Evaluate2( *cx, options.ptr, - code.as_ptr() as *const _, - code.len() as libc::size_t, + &mut SourceText { + units_: code.as_ptr() as *const _, + length_: code.len() as u32, + ownsUnits_: false, + _phantom_0: PhantomData, + }, rval, ) }; @@ -2155,50 +2446,8 @@ impl GlobalScope { None } - pub fn wgpu_create_adapter_ids(&self) -> SmallVec<[AdapterId; 4]> { - self.gpu_id_hub.borrow_mut().create_adapter_ids() - } - - pub fn wgpu_create_bind_group_id(&self, backend: Backend) -> BindGroupId { - self.gpu_id_hub.borrow_mut().create_bind_group_id(backend) - } - - pub fn wgpu_create_bind_group_layout_id(&self, backend: Backend) -> BindGroupLayoutId { - self.gpu_id_hub - .borrow_mut() - .create_bind_group_layout_id(backend) - } - - pub fn wgpu_create_buffer_id(&self, backend: Backend) -> BufferId { - self.gpu_id_hub.borrow_mut().create_buffer_id(backend) - } - - pub fn wgpu_create_device_id(&self, backend: Backend) -> DeviceId { - self.gpu_id_hub.borrow_mut().create_device_id(backend) - } - - pub fn wgpu_create_pipeline_layout_id(&self, backend: Backend) -> PipelineLayoutId { - self.gpu_id_hub - .borrow_mut() - .create_pipeline_layout_id(backend) - } - - pub fn wgpu_create_shader_module_id(&self, backend: Backend) -> ShaderModuleId { - self.gpu_id_hub - .borrow_mut() - .create_shader_module_id(backend) - } - - pub fn wgpu_create_compute_pipeline_id(&self, backend: Backend) -> ComputePipelineId { - self.gpu_id_hub - .borrow_mut() - .create_compute_pipeline_id(backend) - } - - pub fn wgpu_create_command_encoder_id(&self, backend: Backend) -> CommandEncoderId { - self.gpu_id_hub - .borrow_mut() - .create_command_encoder_id(backend) + pub fn wgpu_id_hub(&self) -> RefMut<Identities> { + self.gpu_id_hub.borrow_mut() } } diff --git a/components/script/dom/gpu.rs b/components/script/dom/gpu.rs index 8f129e14c30..e2f7e3e5a82 100644 --- a/components/script/dom/gpu.rs +++ b/components/script/dom/gpu.rs @@ -115,7 +115,7 @@ impl GPUMethods for GPU { }, None => wgpu::instance::PowerPreference::Default, }; - let ids = global.wgpu_create_adapter_ids(); + let ids = global.wgpu_id_hub().create_adapter_ids(); let script_to_constellation_chan = global.script_to_constellation_chan(); if script_to_constellation_chan @@ -135,13 +135,17 @@ impl GPUMethods for GPU { impl AsyncWGPUListener for GPU { fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { match response { - WebGPUResponse::RequestAdapter(name, adapter, channel) => { + WebGPUResponse::RequestAdapter { + adapter_name, + adapter_id, + channel, + } => { let adapter = GPUAdapter::new( &self.global(), channel, - DOMString::from(format!("{} ({:?})", name, adapter.0.backend())), + DOMString::from(format!("{} ({:?})", adapter_name, adapter_id.0.backend())), Heap::default(), - adapter, + adapter_id, ); promise.resolve_native(&adapter); }, diff --git a/components/script/dom/gpuadapter.rs b/components/script/dom/gpuadapter.rs index abf8844522d..34f040405c1 100644 --- a/components/script/dom/gpuadapter.rs +++ b/components/script/dom/gpuadapter.rs @@ -91,11 +91,17 @@ impl GPUAdapterMethods for GPUAdapter { }; let id = self .global() - .wgpu_create_device_id(self.adapter.0.backend()); + .wgpu_id_hub() + .create_device_id(self.adapter.0.backend()); if self .channel .0 - .send(WebGPURequest::RequestDevice(sender, self.adapter, desc, id)) + .send(WebGPURequest::RequestDevice { + sender, + adapter_id: self.adapter, + descriptor: desc, + device_id: id, + }) .is_err() { promise.reject_error(Error::Operation); @@ -107,7 +113,11 @@ impl GPUAdapterMethods for GPUAdapter { impl AsyncWGPUListener for GPUAdapter { fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { match response { - WebGPUResponse::RequestDevice(device_id, queue_id, _descriptor) => { + WebGPUResponse::RequestDevice { + device_id, + queue_id, + _descriptor, + } => { let device = GPUDevice::new( &self.global(), self.channel.clone(), diff --git a/components/script/dom/gpubuffer.rs b/components/script/dom/gpubuffer.rs index e0694e4d726..fc2d17a689e 100644 --- a/components/script/dom/gpubuffer.rs +++ b/components/script/dom/gpubuffer.rs @@ -6,17 +6,36 @@ use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::GPUBufferBinding::{ self, GPUBufferMethods, GPUBufferSize, }; +use crate::dom::bindings::error::Error; +use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::str::DOMString; +use crate::dom::bindings::trace::RootedTraceableBox; use crate::dom::globalscope::GlobalScope; +use crate::dom::gpu::{response_async, AsyncWGPUListener}; +use crate::dom::promise::Promise; +use crate::realms::InRealm; use dom_struct::dom_struct; +use js::jsapi::{Heap, JSObject}; +use js::jsval::UndefinedValue; +use js::rust::jsapi_wrapped::{DetachArrayBuffer, IsPromiseObject, RejectPromise}; +use js::rust::MutableHandle; +use js::typedarray::{ArrayBuffer, CreateWith}; use std::cell::Cell; -use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest}; +use std::ptr; +use std::rc::Rc; +use webgpu::{ + wgpu::resource::BufferUsage, WebGPU, WebGPUBuffer, WebGPUDevice, WebGPURequest, WebGPUResponse, +}; -#[derive(MallocSizeOf)] +// https://gpuweb.github.io/gpuweb/#buffer-state +#[derive(Clone, MallocSizeOf)] pub enum GPUBufferState { - Mapped, + MappedForReading, + MappedForWriting, + MappedPendingForReading, + MappedPendingForWriting, Unmapped, Destroyed, } @@ -24,7 +43,7 @@ pub enum GPUBufferState { #[dom_struct] pub struct GPUBuffer { reflector_: Reflector, - #[ignore_malloc_size_of = "channels are hard"] + #[ignore_malloc_size_of = "defined in webgpu"] channel: WebGPU, label: DomRefCell<Option<DOMString>>, size: GPUBufferSize, @@ -33,6 +52,8 @@ pub struct GPUBuffer { buffer: WebGPUBuffer, device: WebGPUDevice, valid: Cell<bool>, + #[ignore_malloc_size_of = "defined in mozjs"] + mapping: RootedTraceableBox<Heap<*mut JSObject>>, } impl GPUBuffer { @@ -44,6 +65,7 @@ impl GPUBuffer { size: GPUBufferSize, usage: u32, valid: bool, + mapping: RootedTraceableBox<Heap<*mut JSObject>>, ) -> GPUBuffer { Self { reflector_: Reflector::new(), @@ -55,6 +77,7 @@ impl GPUBuffer { valid: Cell::new(valid), device, buffer, + mapping, } } @@ -68,10 +91,11 @@ impl GPUBuffer { size: GPUBufferSize, usage: u32, valid: bool, + mapping: RootedTraceableBox<Heap<*mut JSObject>>, ) -> DomRoot<GPUBuffer> { reflect_dom_object( Box::new(GPUBuffer::new_inherited( - channel, buffer, device, state, size, usage, valid, + channel, buffer, device, state, size, usage, valid, mapping, )), global, GPUBufferBinding::Wrap, @@ -104,30 +128,136 @@ impl Drop for GPUBuffer { } impl GPUBufferMethods for GPUBuffer { + #[allow(unsafe_code)] /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-unmap fn Unmap(&self) { - self.channel - .0 - .send(WebGPURequest::UnmapBuffer(self.buffer)) - .unwrap(); + let cx = self.global().get_cx(); + // Step 1 + match *self.state.borrow() { + GPUBufferState::Unmapped | GPUBufferState::Destroyed => { + // TODO: Record validation error on the current scope + return; + }, + GPUBufferState::MappedForWriting => { + // Step 3.1 + match ArrayBuffer::from(self.mapping.get()) { + Ok(array_buffer) => { + self.channel + .0 + .send(WebGPURequest::UnmapBuffer { + device_id: self.device.0, + buffer_id: self.id().0, + array_buffer: array_buffer.to_vec(), + }) + .unwrap(); + // Step 3.2 + unsafe { + DetachArrayBuffer(*cx, self.mapping.handle()); + } + }, + _ => { + // Step 2 + unsafe { + if IsPromiseObject(self.mapping.handle()) { + let err = Error::Abort; + rooted!(in(*cx) let mut undef = UndefinedValue()); + err.to_jsval(*cx, &self.global(), undef.handle_mut()); + RejectPromise(*cx, self.mapping.handle(), undef.handle()); + }; + } + }, + }; + }, + _ => {}, + }; + // Step 3.3 + self.mapping.set(ptr::null_mut()); + // Step 4 *self.state.borrow_mut() = GPUBufferState::Unmapped; } /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-destroy fn Destroy(&self) { - match *self.state.borrow() { - GPUBufferState::Mapped => { + let state = self.state.borrow().clone(); + match state { + GPUBufferState::MappedForReading | GPUBufferState::MappedForWriting => { self.Unmap(); }, _ => {}, }; self.channel .0 - .send(WebGPURequest::DestroyBuffer(self.buffer)) + .send(WebGPURequest::DestroyBuffer(self.buffer.0)) .unwrap(); *self.state.borrow_mut() = GPUBufferState::Destroyed; } + #[allow(unsafe_code)] + /// https://gpuweb.github.io/gpuweb/#dom-gpubuffer-mapreadasync + fn MapReadAsync(&self, comp: InRealm) -> Rc<Promise> { + // Step 1 & 2 + let promise = Promise::new_in_current_realm(&self.global(), comp); + match *self.state.borrow() { + GPUBufferState::Unmapped => { + match BufferUsage::from_bits(self.usage) { + Some(usage) => { + if !usage.contains(BufferUsage::MAP_READ) { + // TODO: Record validation error on the current scope + promise.reject_error(Error::Abort); + return promise; + }; + }, + None => { + promise.reject_error(Error::Abort); + return promise; + }, + } + }, + _ => { + promise.reject_error(Error::Abort); + return promise; + }, + } + // Step 3 + self.mapping.set(*promise.promise_obj()); + // Step 4 + *self.state.borrow_mut() = GPUBufferState::MappedPendingForReading; + + // Step 5.1 + if unsafe { + ArrayBuffer::create( + *self.global().get_cx(), + CreateWith::Length(self.size as u32), + MutableHandle::from_raw(self.mapping.handle_mut()), + ) + } + .is_err() + { + promise.reject_error(Error::Operation); + return promise; + } + + let sender = response_async(&promise, self); + if self + .channel + .0 + .send(WebGPURequest::MapReadAsync { + sender, + buffer_id: self.buffer.0, + device_id: self.device.0, + usage: self.usage, + size: self.size, + }) + .is_err() + { + promise.reject_error(Error::Operation); + return promise; + } + + // Step 6 + promise + } + /// https://gpuweb.github.io/gpuweb/#dom-gpuobjectbase-label fn GetLabel(&self) -> Option<DOMString> { self.label.borrow().clone() @@ -138,3 +268,25 @@ impl GPUBufferMethods for GPUBuffer { *self.label.borrow_mut() = value; } } + +impl AsyncWGPUListener for GPUBuffer { + #[allow(unsafe_code)] + fn handle_response(&self, response: WebGPUResponse, promise: &Rc<Promise>) { + match response { + WebGPUResponse::MapReadAsync(bytes) => unsafe { + match ArrayBuffer::from(self.mapping.get()) { + Ok(mut array_buffer) => { + // Step 5.2 + array_buffer.update(&bytes); + // Step 5.3 + *self.state.borrow_mut() = GPUBufferState::MappedForReading; + // Step 5.4 + promise.resolve_native(&array_buffer); + }, + _ => promise.reject_error(Error::Operation), + }; + }, + _ => promise.reject_error(Error::Operation), + } + } +} diff --git a/components/script/dom/gpucommandencoder.rs b/components/script/dom/gpucommandencoder.rs index 03b9373f3ab..348e77828f1 100644 --- a/components/script/dom/gpucommandencoder.rs +++ b/components/script/dom/gpucommandencoder.rs @@ -87,14 +87,14 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { .insert(DomRoot::from_ref(destination)); self.channel .0 - .send(WebGPURequest::CopyBufferToBuffer( - self.encoder.0, - source.id().0, + .send(WebGPURequest::CopyBufferToBuffer { + command_encoder_id: self.encoder.0, + source_id: source.id().0, source_offset, - destination.id().0, + destination_id: destination.id().0, destination_offset, size, - )) + }) .expect("Failed to send CopyBufferToBuffer"); } @@ -103,12 +103,12 @@ impl GPUCommandEncoderMethods for GPUCommandEncoder { let (sender, receiver) = ipc::channel().unwrap(); self.channel .0 - .send(WebGPURequest::CommandEncoderFinish( + .send(WebGPURequest::CommandEncoderFinish { sender, - self.encoder.0, + command_encoder_id: self.encoder.0, // TODO(zakorgy): We should use `_descriptor` here after it's not empty // and the underlying wgpu-core struct is serializable - )) + }) .expect("Failed to send Finish"); let buffer = receiver.recv().unwrap(); diff --git a/components/script/dom/gpucomputepassencoder.rs b/components/script/dom/gpucomputepassencoder.rs index f6116c88f9d..a25f47e1330 100644 --- a/components/script/dom/gpucomputepassencoder.rs +++ b/components/script/dom/gpucomputepassencoder.rs @@ -81,11 +81,14 @@ impl GPUComputePassEncoderMethods for GPUComputePassEncoder { /// https://gpuweb.github.io/gpuweb/#dom-gpurenderpassencoder-endpass fn EndPass(&self) { if let Some(raw_pass) = self.raw_pass.borrow_mut().take() { - let (pass_data, id) = unsafe { raw_pass.finish_compute() }; + let (pass_data, command_encoder_id) = unsafe { raw_pass.finish_compute() }; self.channel .0 - .send(WebGPURequest::RunComputePass(id, pass_data)) + .send(WebGPURequest::RunComputePass { + command_encoder_id, + pass_data, + }) .unwrap(); } } diff --git a/components/script/dom/gpudevice.rs b/components/script/dom/gpudevice.rs index 1d17961f87c..4a1e4747715 100644 --- a/components/script/dom/gpudevice.rs +++ b/components/script/dom/gpudevice.rs @@ -46,7 +46,7 @@ use webgpu::wgpu::binding_model::{ ShaderStage, }; use webgpu::wgpu::resource::{BufferDescriptor, BufferUsage}; -use webgpu::{WebGPU, WebGPUBuffer, WebGPUDevice, WebGPUQueue, WebGPURequest}; +use webgpu::{WebGPU, WebGPUDevice, WebGPUQueue, WebGPURequest}; #[dom_struct] pub struct GPUDevice { @@ -106,38 +106,6 @@ impl GPUDevice { } impl GPUDevice { - unsafe fn resolve_create_buffer_mapped( - &self, - cx: SafeJSContext, - gpu_buffer: WebGPUBuffer, - array_buffer: Vec<u8>, - descriptor: BufferDescriptor, - valid: bool, - ) -> Vec<JSVal> { - rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>()); - let mut out = Vec::new(); - assert!(ArrayBuffer::create( - *cx, - CreateWith::Slice(array_buffer.as_slice()), - js_array_buffer.handle_mut(), - ) - .is_ok()); - - let buff = GPUBuffer::new( - &self.global(), - self.channel.clone(), - gpu_buffer, - self.device, - GPUBufferState::Mapped, - descriptor.size, - descriptor.usage.bits(), - valid, - ); - out.push(ObjectValue(buff.reflector().get_jsobject().get())); - out.push(ObjectValue(js_array_buffer.get())); - out - } - fn validate_buffer_descriptor( &self, descriptor: &GPUBufferDescriptor, @@ -201,15 +169,18 @@ impl GPUDeviceMethods for GPUDevice { fn CreateBuffer(&self, descriptor: &GPUBufferDescriptor) -> DomRoot<GPUBuffer> { let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor); let (sender, receiver) = ipc::channel().unwrap(); - let id = self.global().wgpu_create_buffer_id(self.device.0.backend()); + let id = self + .global() + .wgpu_id_hub() + .create_buffer_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateBuffer( + .send(WebGPURequest::CreateBuffer { sender, - self.device, - id, - wgpu_descriptor, - )) + device_id: self.device.0, + buffer_id: id, + descriptor: wgpu_descriptor, + }) .expect("Failed to create WebGPU buffer"); let buffer = receiver.recv().unwrap(); @@ -223,6 +194,7 @@ impl GPUDeviceMethods for GPUDevice { descriptor.size, descriptor.usage, valid, + RootedTraceableBox::new(Heap::default()), ) } @@ -234,22 +206,47 @@ impl GPUDeviceMethods for GPUDevice { ) -> Vec<JSVal> { let (valid, wgpu_descriptor) = self.validate_buffer_descriptor(descriptor); let (sender, receiver) = ipc::channel().unwrap(); - let id = self.global().wgpu_create_buffer_id(self.device.0.backend()); + let buffer_id = self + .global() + .wgpu_id_hub() + .create_buffer_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateBufferMapped( + .send(WebGPURequest::CreateBufferMapped { sender, - self.device, - id, - wgpu_descriptor.clone(), - )) + device_id: self.device.0, + buffer_id, + descriptor: wgpu_descriptor.clone(), + }) .expect("Failed to create WebGPU buffer"); - let (buffer, array_buffer) = receiver.recv().unwrap(); - + rooted!(in(*cx) let mut js_array_buffer = ptr::null_mut::<JSObject>()); unsafe { - self.resolve_create_buffer_mapped(cx, buffer, array_buffer, wgpu_descriptor, valid) + assert!(ArrayBuffer::create( + *cx, + CreateWith::Length(descriptor.size as u32), + js_array_buffer.handle_mut(), + ) + .is_ok()); } + + let buffer = receiver.recv().unwrap(); + let buff = GPUBuffer::new( + &self.global(), + self.channel.clone(), + buffer, + self.device, + GPUBufferState::MappedForWriting, + wgpu_descriptor.size, + wgpu_descriptor.usage.bits(), + valid, + RootedTraceableBox::from_box(Heap::boxed(js_array_buffer.get())), + ); + + vec![ + ObjectValue(buff.reflector().get_jsobject().get()), + ObjectValue(js_array_buffer.get()), + ] } /// https://gpuweb.github.io/gpuweb/#GPUDevice-createBindGroupLayout @@ -394,17 +391,18 @@ impl GPUDeviceMethods for GPUDevice { max_dynamic_storage_buffers_per_pipeline_layout >= 0; let (sender, receiver) = ipc::channel().unwrap(); - let id = self + let bind_group_layout_id = self .global() - .wgpu_create_bind_group_layout_id(self.device.0.backend()); + .wgpu_id_hub() + .create_bind_group_layout_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateBindGroupLayout( + .send(WebGPURequest::CreateBindGroupLayout { sender, - self.device, - id, - bindings.clone(), - )) + device_id: self.device.0, + bind_group_layout_id, + bindings: bindings.clone(), + }) .expect("Failed to create WebGPU BindGroupLayout"); let bgl = receiver.recv().unwrap(); @@ -472,17 +470,18 @@ impl GPUDeviceMethods for GPUDevice { max_dynamic_storage_buffers_per_pipeline_layout >= 0; let (sender, receiver) = ipc::channel().unwrap(); - let id = self + let pipeline_layout_id = self .global() - .wgpu_create_pipeline_layout_id(self.device.0.backend()); + .wgpu_id_hub() + .create_pipeline_layout_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreatePipelineLayout( + .send(WebGPURequest::CreatePipelineLayout { sender, - self.device, - id, - bgl_ids, - )) + device_id: self.device.0, + pipeline_layout_id, + bind_group_layouts: bgl_ids, + }) .expect("Failed to create WebGPU PipelineLayout"); let pipeline_layout = receiver.recv().unwrap(); @@ -531,18 +530,19 @@ impl GPUDeviceMethods for GPUDevice { }) .collect::<Vec<_>>(); let (sender, receiver) = ipc::channel().unwrap(); - let id = self + let bind_group_id = self .global() - .wgpu_create_bind_group_id(self.device.0.backend()); + .wgpu_id_hub() + .create_bind_group_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateBindGroup( + .send(WebGPURequest::CreateBindGroup { sender, - self.device, - id, - descriptor.layout.id(), + device_id: self.device.0, + bind_group_id, + bind_group_layout_id: descriptor.layout.id().0, bindings, - )) + }) .expect("Failed to create WebGPU BindGroup"); let bind_group = receiver.recv().unwrap(); @@ -559,17 +559,18 @@ impl GPUDeviceMethods for GPUDevice { Uint32Array(program) => program.to_vec(), String(program) => program.chars().map(|c| c as u32).collect::<Vec<u32>>(), }; - let id = self + let program_id = self .global() - .wgpu_create_shader_module_id(self.device.0.backend()); + .wgpu_id_hub() + .create_shader_module_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateShaderModule( + .send(WebGPURequest::CreateShaderModule { sender, - self.device, - id, + device_id: self.device.0, + program_id, program, - )) + }) .expect("Failed to create WebGPU ShaderModule"); let shader_module = receiver.recv().unwrap(); @@ -584,20 +585,21 @@ impl GPUDeviceMethods for GPUDevice { let pipeline = descriptor.parent.layout.id(); let program = descriptor.computeStage.module.id(); let entry_point = descriptor.computeStage.entryPoint.to_string(); - let id = self + let compute_pipeline_id = self .global() - .wgpu_create_compute_pipeline_id(self.device.0.backend()); + .wgpu_id_hub() + .create_compute_pipeline_id(self.device.0.backend()); let (sender, receiver) = ipc::channel().unwrap(); self.channel .0 - .send(WebGPURequest::CreateComputePipeline( + .send(WebGPURequest::CreateComputePipeline { sender, - self.device, - id, - pipeline.0, - program.0, + device_id: self.device.0, + compute_pipeline_id, + pipeline_layout_id: pipeline.0, + program_id: program.0, entry_point, - )) + }) .expect("Failed to create WebGPU ComputePipeline"); let compute_pipeline = receiver.recv().unwrap(); @@ -609,12 +611,17 @@ impl GPUDeviceMethods for GPUDevice { _descriptor: &GPUCommandEncoderDescriptor, ) -> DomRoot<GPUCommandEncoder> { let (sender, receiver) = ipc::channel().unwrap(); - let id = self + let command_encoder_id = self .global() - .wgpu_create_command_encoder_id(self.device.0.backend()); + .wgpu_id_hub() + .create_command_encoder_id(self.device.0.backend()); self.channel .0 - .send(WebGPURequest::CreateCommandEncoder(sender, self.device, id)) + .send(WebGPURequest::CreateCommandEncoder { + sender, + device_id: self.device.0, + command_encoder_id, + }) .expect("Failed to create WebGPU command encoder"); let encoder = receiver.recv().unwrap(); diff --git a/components/script/dom/gpuqueue.rs b/components/script/dom/gpuqueue.rs index dfa97402253..c257ec5ef09 100644 --- a/components/script/dom/gpuqueue.rs +++ b/components/script/dom/gpuqueue.rs @@ -64,10 +64,13 @@ impl GPUQueueMethods for GPUQueue { // TODO: Generate error to the ErrorScope return; } - let buffer_ids = command_buffers.iter().map(|cb| cb.id().0).collect(); + let command_buffers = command_buffers.iter().map(|cb| cb.id().0).collect(); self.channel .0 - .send(WebGPURequest::Submit(self.queue.0, buffer_ids)) + .send(WebGPURequest::Submit { + queue_id: self.queue.0, + command_buffers, + }) .unwrap(); } } diff --git a/components/script/dom/htmlcanvaselement.rs b/components/script/dom/htmlcanvaselement.rs index e800a89bdf1..e56ca4a8266 100644 --- a/components/script/dom/htmlcanvaselement.rs +++ b/components/script/dom/htmlcanvaselement.rs @@ -392,7 +392,7 @@ impl HTMLCanvasElementMethods for HTMLCanvasElement { // FIXME(nox): https://github.com/PistonDevelopers/image-png/issues/86 // FIXME(nox): https://github.com/PistonDevelopers/image-png/issues/87 PNGEncoder::new(&mut png) - .encode(&file, self.Width(), self.Height(), ColorType::RGBA(8)) + .encode(&file, self.Width(), self.Height(), ColorType::Rgba8) .unwrap(); let mut url = "data:image/png;base64,".to_owned(); // FIXME(nox): Should this use base64::URL_SAFE? diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index ac0f99416c2..00d7a64ed32 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -27,6 +27,7 @@ use crate::dom::htmlframesetelement::HTMLFrameSetElement; use crate::dom::htmlhtmlelement::HTMLHtmlElement; use crate::dom::htmlinputelement::{HTMLInputElement, InputType}; use crate::dom::htmllabelelement::HTMLLabelElement; +use crate::dom::htmltextareaelement::HTMLTextAreaElement; use crate::dom::node::{document_from_node, window_from_node}; use crate::dom::node::{BindContext, Node, NodeFlags, ShadowIncluding}; use crate::dom::text::Text; @@ -170,6 +171,11 @@ impl HTMLElementMethods for HTMLElement { // https://html.spec.whatwg.org/multipage/#dom-hidden make_bool_setter!(SetHidden, "hidden"); + // https://html.spec.whatwg.org/multipage/#the-dir-attribute + make_getter!(Dir, "dir"); + // https://html.spec.whatwg.org/multipage/#the-dir-attribute + make_setter!(SetDir, "dir"); + // https://html.spec.whatwg.org/multipage/#globaleventhandlers global_event_handlers!(NoOnload); @@ -767,6 +773,48 @@ impl HTMLElement { }) .count() as u32 } + + // https://html.spec.whatwg.org/multipage/#the-directionality. + // returns Some if can infer direction by itself or from child nodes + // returns None if requires to go up to parent + pub fn directionality(&self) -> Option<String> { + let element_direction: &str = &self.Dir(); + + if element_direction == "ltr" { + return Some("ltr".to_owned()); + } + + if element_direction == "rtl" { + return Some("rtl".to_owned()); + } + + if let Some(input) = self.downcast::<HTMLInputElement>() { + if input.input_type() == InputType::Tel { + return Some("ltr".to_owned()); + } + } + + if element_direction == "auto" { + if let Some(directionality) = self + .downcast::<HTMLInputElement>() + .and_then(|input| input.auto_directionality()) + { + return Some(directionality); + } + + if let Some(area) = self.downcast::<HTMLTextAreaElement>() { + return Some(area.auto_directionality()); + } + } + + // TODO(NeverHappened): Implement condition + // If the element's dir attribute is in the auto state OR + // If the element is a bdi element and the dir attribute is not in a defined state + // (i.e. it is not present or has an invalid value) + // Requires bdi element implementation (https://html.spec.whatwg.org/multipage/#the-bdi-element) + + None + } } impl VirtualMethods for HTMLElement { diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index adbb790a181..30d1857de9d 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -950,7 +950,6 @@ impl HTMLFormElement { match element { HTMLElementTypeId::HTMLInputElement => { let input = child.downcast::<HTMLInputElement>().unwrap(); - data_set.append(&mut input.form_datums(submitter, encoding)); }, HTMLElementTypeId::HTMLButtonElement => { @@ -981,10 +980,30 @@ impl HTMLFormElement { _ => (), } } + + // Step: 5.13. Add an entry if element has dirname attribute + // An element can only have a dirname attribute if it is a textarea element + // or an input element whose type attribute is in either the Text state or the Search state + let child_element = child.downcast::<Element>().unwrap(); + let input_matches = + child_element + .downcast::<HTMLInputElement>() + .map_or(false, |input| { + input.input_type() == InputType::Text || + input.input_type() == InputType::Search + }); + let textarea_matches = child_element.is::<HTMLTextAreaElement>(); + let dirname = child_element.get_string_attribute(&local_name!("dirname")); + if (input_matches || textarea_matches) && !dirname.is_empty() { + let dir = DOMString::from(child_element.directionality()); + data_set.push(FormDatum { + ty: DOMString::from("string"), + name: dirname, + value: FormDatumValue::String(dir), + }); + } } data_set - // TODO: Handle `dirnames` (needs directionality support) - // https://html.spec.whatwg.org/multipage/#the-directionality } /// <https://html.spec.whatwg.org/multipage/#constructing-the-form-data-set> diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index a18eedebfb8..95e5c43af66 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -167,13 +167,10 @@ impl HTMLImageElement { // https://html.spec.whatwg.org/multipage/#check-the-usability-of-the-image-argument pub fn is_usable(&self) -> Fallible<bool> { // If image has an intrinsic width or intrinsic height (or both) equal to zero, then return bad. - match &self.current_request.borrow().image { - Some(image) => { - if image.width == 0 || image.height == 0 { - return Ok(false); - } - }, - None => return Ok(false), + if let Some(image) = &self.current_request.borrow().image { + if image.width == 0 || image.height == 0 { + return Ok(false); + } } match self.current_request.borrow().state { diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs index 441206c9dad..c27d5b2f9b5 100755 --- a/components/script/dom/htmlinputelement.rs +++ b/components/script/dom/htmlinputelement.rs @@ -75,6 +75,7 @@ use std::ptr::NonNull; use style::attr::AttrValue; use style::element_state::ElementState; use style::str::{split_commas, str_join}; +use unicode_bidi::{bidi_class, BidiClass}; const DEFAULT_SUBMIT_VALUE: &'static str = "Submit"; const DEFAULT_RESET_VALUE: &'static str = "Reset"; @@ -327,6 +328,36 @@ impl HTMLInputElement { ) } + pub fn auto_directionality(&self) -> Option<String> { + match self.input_type() { + InputType::Text | InputType::Search | InputType::Url | InputType::Email => { + let value: String = self.Value().to_string(); + Some(HTMLInputElement::directionality_from_value(&value)) + }, + _ => None, + } + } + + pub fn directionality_from_value(value: &str) -> String { + if HTMLInputElement::is_first_strong_character_rtl(value) { + "rtl".to_owned() + } else { + "ltr".to_owned() + } + } + + fn is_first_strong_character_rtl(value: &str) -> bool { + for ch in value.chars() { + return match bidi_class(ch) { + BidiClass::L => false, + BidiClass::AL => true, + BidiClass::R => true, + _ => continue, + }; + } + false + } + // https://html.spec.whatwg.org/multipage/#dom-input-value // https://html.spec.whatwg.org/multipage/#concept-input-apply fn value_mode(&self) -> ValueMode { @@ -2457,6 +2488,9 @@ impl Activatable for HTMLInputElement { // https://html.spec.whatwg.org/multipage/#checkbox-state-(type=checkbox):activation-behavior // https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):activation-behavior // Check if document owner is fully active + if !self.upcast::<Node>().is_connected() { + return (); + } let target = self.upcast::<EventTarget>(); target.fire_bubbling_event(atom!("input")); target.fire_bubbling_event(atom!("change")); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index d9b0f11beef..f8c686d34f2 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -74,7 +74,7 @@ use html5ever::{LocalName, Prefix}; use http::header::{self, HeaderMap, HeaderValue}; use ipc_channel::ipc; use ipc_channel::router::ROUTER; -use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward}; +use media::{glplayer_channel, GLPlayerMsg, GLPlayerMsgForward, WindowGLContext}; use net_traits::image::base::Image; use net_traits::image_cache::ImageResponse; use net_traits::request::{Destination, Referrer}; @@ -96,7 +96,8 @@ use std::rc::Rc; use std::sync::{Arc, Mutex}; use time::{self, Duration, Timespec}; use webrender_api::{ExternalImageData, ExternalImageId, ExternalImageType, TextureTarget}; -use webrender_api::{ImageData, ImageDescriptor, ImageFormat, ImageKey, Transaction}; +use webrender_api::{ImageData, ImageDescriptor, ImageDescriptorFlags, ImageFormat}; +use webrender_api::{ImageKey, Transaction}; #[derive(PartialEq)] enum FrameStatus { @@ -186,8 +187,7 @@ impl VideoFrameRenderer for MediaFrameRenderer { frame.get_width(), frame.get_height(), ImageFormat::BGRA8, - false, - false, + ImageDescriptorFlags::empty(), ); match self.current_frame { @@ -373,6 +373,8 @@ pub struct HTMLMediaElement { /// the access to the "privileged" document.servoGetMediaControls(id) API by /// keeping a whitelist of media controls identifiers. media_controls_id: DomRefCell<Option<String>>, + #[ignore_malloc_size_of = "Defined in other crates"] + player_context: WindowGLContext, } /// <https://html.spec.whatwg.org/multipage/#dom-media-networkstate> @@ -437,6 +439,7 @@ impl HTMLMediaElement { current_fetch_context: DomRefCell::new(None), id: Cell::new(0), media_controls_id: DomRefCell::new(None), + player_context: document.window().get_player_context(), } } @@ -1340,9 +1343,7 @@ impl HTMLMediaElement { let audio_renderer = self.audio_renderer.borrow().as_ref().map(|r| r.clone()); - let pipeline_id = window - .pipeline_id() - .expect("Cannot create player outside of a pipeline"); + let pipeline_id = window.pipeline_id(); let client_context_id = ClientContextId::build(pipeline_id.namespace_id.0, pipeline_id.index.0.get()); let player = ServoMedia::get().unwrap().create_player( @@ -1969,15 +1970,14 @@ impl HTMLMediaElement { impl Drop for HTMLMediaElement { fn drop(&mut self) { - let window = window_from_node(self); - window.get_player_context().glplayer_chan.map(|pipeline| { + if let Some(ref pipeline) = self.player_context.glplayer_chan { if let Err(err) = pipeline .channel() .send(GLPlayerMsg::UnregisterPlayer(self.id.get())) { warn!("GLPlayer disappeared!: {:?}", err); } - }); + } self.remove_controls(); } diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs index 9fd316e3a18..d077d9eec8f 100755 --- a/components/script/dom/htmltextareaelement.rs +++ b/components/script/dom/htmltextareaelement.rs @@ -22,6 +22,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlfieldsetelement::HTMLFieldSetElement; use crate::dom::htmlformelement::{FormControl, HTMLFormElement}; +use crate::dom::htmlinputelement::HTMLInputElement; use crate::dom::keyboardevent::KeyboardEvent; use crate::dom::node::{document_from_node, window_from_node}; use crate::dom::node::{ @@ -173,6 +174,11 @@ impl HTMLTextAreaElement { ) } + pub fn auto_directionality(&self) -> String { + let value: String = self.Value().to_string(); + return HTMLInputElement::directionality_from_value(&value); + } + fn update_placeholder_shown_state(&self) { let has_placeholder = !self.placeholder.borrow().is_empty(); let has_value = !self.textinput.borrow().is_empty(); @@ -205,6 +211,12 @@ impl HTMLTextAreaElementMethods for HTMLTextAreaElement { // https://html.spec.whatwg.org/multipage/#dom-textarea-cols make_limited_uint_setter!(SetCols, "cols", DEFAULT_COLS); + // https://html.spec.whatwg.org/multipage/#dom-input-dirName + make_getter!(DirName, "dirname"); + + // https://html.spec.whatwg.org/multipage/#dom-input-dirName + make_setter!(SetDirName, "dirname"); + // https://html.spec.whatwg.org/multipage/#dom-fe-disabled make_bool_getter!(Disabled, "disabled"); diff --git a/components/script/dom/mediasession.rs b/components/script/dom/mediasession.rs index c8df282f364..54a6d2d3660 100644 --- a/components/script/dom/mediasession.rs +++ b/components/script/dom/mediasession.rs @@ -105,9 +105,7 @@ impl MediaSession { pub fn send_event(&self, event: MediaSessionEvent) { let global = self.global(); let window = global.as_window(); - let pipeline_id = window - .pipeline_id() - .expect("Cannot send media session event outside of a pipeline"); + let pipeline_id = window.pipeline_id(); window.send_to_constellation(ScriptMsg::MediaSessionEvent(pipeline_id, event)); } diff --git a/components/script/dom/messageport.rs b/components/script/dom/messageport.rs index 9cbf7152f01..ee2f54323a6 100644 --- a/components/script/dom/messageport.rs +++ b/components/script/dom/messageport.rs @@ -338,6 +338,6 @@ impl MessagePortMethods for MessagePort { self.global().start_message_port(self.message_port_id()); } - /// <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror> + // <https://html.spec.whatwg.org/multipage/#handler-messageport-onmessageerror> event_handler!(messageerror, GetOnmessageerror, SetOnmessageerror); } diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index e25a88b9da2..3691ba5a299 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -239,6 +239,7 @@ pub mod bluetoothremotegattdescriptor; pub mod bluetoothremotegattserver; pub mod bluetoothremotegattservice; pub mod bluetoothuuid; +pub mod broadcastchannel; pub mod canvasgradient; pub mod canvaspattern; pub mod canvasrenderingcontext2d; @@ -526,6 +527,7 @@ pub mod userscripts; pub mod validation; pub mod validitystate; pub mod values; +pub mod vertexarrayobject; pub mod videotrack; pub mod videotracklist; pub mod virtualmethods; @@ -537,6 +539,8 @@ pub mod vrfieldofview; pub mod vrframedata; pub mod vrpose; pub mod vrstageparameters; +pub mod vttcue; +pub mod vttregion; pub mod webgl_extensions; pub use self::webgl_extensions::ext::*; pub mod webgl2renderingcontext; @@ -557,6 +561,7 @@ pub mod webglsync; pub mod webgltexture; pub mod webgltransformfeedback; pub mod webgluniformlocation; +pub mod webglvertexarrayobject; pub mod webglvertexarrayobjectoes; pub mod websocket; pub mod wheelevent; diff --git a/components/script/dom/navigator.rs b/components/script/dom/navigator.rs index 690eb72abd3..b297b02bc69 100644 --- a/components/script/dom/navigator.rs +++ b/components/script/dom/navigator.rs @@ -184,7 +184,8 @@ impl NavigatorMethods for Navigator { /// https://immersive-web.github.io/webxr/#dom-navigator-xr fn Xr(&self) -> DomRoot<XRSystem> { - self.xr.or_init(|| XRSystem::new(&self.global())) + self.xr + .or_init(|| XRSystem::new(&self.global().as_window())) } /// https://w3c.github.io/mediacapture-main/#dom-navigator-mediadevices diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 457c1f0d55c..05b81a84d5d 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -67,6 +67,7 @@ use crate::dom::window::Window; use crate::script_runtime::JSContext; use crate::script_thread::ScriptThread; use app_units::Au; +use crossbeam_channel::Sender; use devtools_traits::NodeInfo; use dom_struct::dom_struct; use euclid::default::{Point2D, Rect, Size2D, Vector2D}; @@ -209,7 +210,9 @@ impl NodeFlags { impl Drop for Node { #[allow(unsafe_code)] fn drop(&mut self) { - self.style_and_layout_data.get().map(|d| self.dispose(d)); + if let Some(data) = self.style_and_layout_data.get() { + self.dispose(data, ScriptThread::get_any_layout_chan().as_ref()); + } } } @@ -224,15 +227,16 @@ enum SuppressObserver { impl Node { /// Sends the style and layout data, if any, back to the layout thread to be destroyed. - pub fn dispose(&self, data: OpaqueStyleAndLayoutData) { + pub(crate) fn dispose( + &self, + data: OpaqueStyleAndLayoutData, + layout_chan: Option<&Sender<Msg>>, + ) { debug_assert!(thread_state::get().is_script()); - let win = window_from_node(self); self.style_and_layout_data.set(None); - if win - .layout_chan() - .send(Msg::ReapStyleAndLayoutData(data)) - .is_err() - { + if layout_chan.map_or(false, |chan| { + chan.send(Msg::ReapStyleAndLayoutData(data)).is_err() + }) { warn!("layout thread unreachable - leaking layout data"); } } @@ -315,12 +319,16 @@ impl Node { false, ); } + let window = window_from_node(root); + let layout_chan = window.layout_chan(); for node in root.traverse_preorder(ShadowIncluding::Yes) { // This needs to be in its own loop, because unbind_from_tree may // rely on the state of IS_IN_DOC of the context node's descendants, // e.g. when removing a <form>. vtable_for(&&*node).unbind_from_tree(&context); - node.style_and_layout_data.get().map(|d| node.dispose(d)); + if let Some(data) = node.style_and_layout_data.get() { + node.dispose(data, Some(layout_chan)); + } // https://dom.spec.whatwg.org/#concept-node-remove step 14 if let Some(element) = node.as_custom_element() { ScriptThread::enqueue_callback_reaction( @@ -432,6 +440,26 @@ impl Node { .upcast::<Event>() .dispatch(self.upcast::<EventTarget>(), false); } + + pub fn parent_directionality(&self) -> String { + let mut current = self.GetParentNode(); + + loop { + match current { + Some(node) => { + if let Some(directionality) = node + .downcast::<HTMLElement>() + .and_then(|html_element| html_element.directionality()) + { + return directionality; + } else { + current = node.GetParentNode(); + } + }, + None => return "ltr".to_owned(), + } + } + } } pub struct QuerySelectorIterator { @@ -481,10 +509,12 @@ impl<'a> Iterator for QuerySelectorIterator { impl Node { impl_rare_data!(NodeRareData); - pub fn teardown(&self) { - self.style_and_layout_data.get().map(|d| self.dispose(d)); + pub(crate) fn teardown(&self, layout_chan: &Sender<Msg>) { + if let Some(data) = self.style_and_layout_data.get() { + self.dispose(data, Some(layout_chan)); + } for kid in self.children() { - kid.teardown(); + kid.teardown(layout_chan); } } diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs index 8c4f6d39358..5218d1b3026 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -7,6 +7,8 @@ use crate::dom::bindings::codegen::Bindings::NodeListBinding; use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::bindings::str::DOMString; +use crate::dom::document::Document; use crate::dom::htmlelement::HTMLElement; use crate::dom::htmlformelement::HTMLFormElement; use crate::dom::node::{ChildrenMutation, Node}; @@ -22,6 +24,7 @@ pub enum NodeListType { Children(ChildrenList), Labels(LabelsList), Radio(RadioList), + ElementsByName(ElementsByNameList), } // https://dom.spec.whatwg.org/#interface-nodelist @@ -74,6 +77,17 @@ impl NodeList { NodeList::new(window, NodeListType::Labels(LabelsList::new(element))) } + pub fn new_elements_by_name_list( + window: &Window, + document: &Document, + name: DOMString, + ) -> DomRoot<NodeList> { + NodeList::new( + window, + NodeListType::ElementsByName(ElementsByNameList::new(document, name)), + ) + } + pub fn empty(window: &Window) -> DomRoot<NodeList> { NodeList::new(window, NodeListType::Simple(vec![])) } @@ -87,6 +101,7 @@ impl NodeListMethods for NodeList { NodeListType::Children(ref list) => list.len(), NodeListType::Labels(ref list) => list.len(), NodeListType::Radio(ref list) => list.len(), + NodeListType::ElementsByName(ref list) => list.len(), } } @@ -99,6 +114,7 @@ impl NodeListMethods for NodeList { NodeListType::Children(ref list) => list.item(index), NodeListType::Labels(ref list) => list.item(index), NodeListType::Radio(ref list) => list.item(index), + NodeListType::ElementsByName(ref list) => list.item(index), } } @@ -401,3 +417,29 @@ impl RadioList { self.form.nth_for_radio_list(index, self.mode, &self.name) } } + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct ElementsByNameList { + document: Dom<Document>, + name: DOMString, +} + +impl ElementsByNameList { + pub fn new(document: &Document, name: DOMString) -> ElementsByNameList { + ElementsByNameList { + document: Dom::from_ref(document), + name: name, + } + } + + pub fn len(&self) -> u32 { + self.document.elements_by_name_count(&self.name) + } + + pub fn item(&self, index: u32) -> Option<DomRoot<Node>> { + self.document + .nth_element_by_name(index, &self.name) + .and_then(|n| Some(DomRoot::from_ref(&*n))) + } +} diff --git a/components/script/dom/offlineaudiocontext.rs b/components/script/dom/offlineaudiocontext.rs index a524595d6ea..7b8d3f52111 100644 --- a/components/script/dom/offlineaudiocontext.rs +++ b/components/script/dom/offlineaudiocontext.rs @@ -83,9 +83,7 @@ impl OfflineAudioContext { { return Err(Error::NotSupported); } - let pipeline_id = window - .pipeline_id() - .expect("Cannot create audio context outside of a pipeline"); + let pipeline_id = window.pipeline_id(); let context = OfflineAudioContext::new_inherited(channel_count, length, sample_rate, pipeline_id); Ok(reflect_dom_object( diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs index cb91fb692f9..37887f60773 100644 --- a/components/script/dom/permissions.rs +++ b/components/script/dom/permissions.rs @@ -19,19 +19,13 @@ use crate::dom::promise::Promise; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_runtime::JSContext; use dom_struct::dom_struct; +use embedder_traits::{self, EmbedderMsg, PermissionPrompt, PermissionRequest}; +use ipc_channel::ipc; use js::conversions::ConversionResult; use js::jsapi::JSObject; use js::jsval::{ObjectValue, UndefinedValue}; use servo_config::pref; use std::rc::Rc; -#[cfg(target_os = "linux")] -use tinyfiledialogs::{self, MessageBoxIcon, YesNo}; - -#[cfg(target_os = "linux")] -const DIALOG_TITLE: &'static str = "Permission request dialog"; -const NONSECURE_DIALOG_MESSAGE: &'static str = "feature is only safe to use in secure context,\ - but servo can't guarantee\n that the current context is secure. Do you want to proceed and grant permission?"; -const REQUEST_DIALOG_MESSAGE: &'static str = "Do you want to grant permission for"; pub trait PermissionAlgorithm { type Descriptor; @@ -143,7 +137,6 @@ impl Permissions { // (Revoke) Step 3. let globalscope = self.global(); globalscope - .as_window() .permission_state_invocation_results() .borrow_mut() .remove(&root_desc.name.to_string()); @@ -176,7 +169,6 @@ impl Permissions { // (Revoke) Step 3. let globalscope = self.global(); globalscope - .as_window() .permission_state_invocation_results() .borrow_mut() .remove(&root_desc.name.to_string()); @@ -259,17 +251,13 @@ impl PermissionAlgorithm for Permissions { // Step 3. PermissionState::Prompt => { let perm_name = status.get_query(); - - let globalscope = GlobalScope::current().expect("No current global object"); + let prompt = + PermissionPrompt::Request(embedder_traits::PermissionName::from(perm_name)); // https://w3c.github.io/permissions/#request-permission-to-use (Step 3 - 4) - let state = prompt_user( - &format!("{} {} ?", REQUEST_DIALOG_MESSAGE, perm_name.clone()), - globalscope.is_headless(), - ); - + let globalscope = GlobalScope::current().expect("No current global object"); + let state = prompt_user_from_embedder(prompt, &globalscope); globalscope - .as_window() .permission_state_invocation_results() .borrow_mut() .insert(perm_name.to_string(), state); @@ -292,7 +280,7 @@ pub fn get_descriptor_permission_state( env_settings_obj: Option<&GlobalScope>, ) -> PermissionState { // Step 1. - let settings = match env_settings_obj { + let globalscope = match env_settings_obj { Some(env_settings_obj) => DomRoot::from_ref(env_settings_obj), None => GlobalScope::current().expect("No current global object"), }; @@ -308,22 +296,20 @@ pub fn get_descriptor_permission_state( if pref!(dom.permissions.testing.allowed_in_nonsecure_contexts) { PermissionState::Granted } else { - settings - .as_window() + globalscope .permission_state_invocation_results() .borrow_mut() .remove(&permission_name.to_string()); - prompt_user( - &format!("The {} {}", permission_name, NONSECURE_DIALOG_MESSAGE), - settings.is_headless(), + prompt_user_from_embedder( + PermissionPrompt::Insecure(embedder_traits::PermissionName::from(permission_name)), + &globalscope, ) } }; // Step 3. - if let Some(prev_result) = settings - .as_window() + if let Some(prev_result) = globalscope .permission_state_invocation_results() .borrow() .get(&permission_name.to_string()) @@ -332,8 +318,7 @@ pub fn get_descriptor_permission_state( } // Store the invocation result - settings - .as_window() + globalscope .permission_state_invocation_results() .borrow_mut() .insert(permission_name.to_string(), state); @@ -342,28 +327,6 @@ pub fn get_descriptor_permission_state( state } -#[cfg(target_os = "linux")] -fn prompt_user(message: &str, headless: bool) -> PermissionState { - if headless { - return PermissionState::Denied; - } - match tinyfiledialogs::message_box_yes_no( - DIALOG_TITLE, - message, - MessageBoxIcon::Question, - YesNo::No, - ) { - YesNo::Yes => PermissionState::Granted, - YesNo::No => PermissionState::Denied, - } -} - -#[cfg(not(target_os = "linux"))] -fn prompt_user(_message: &str, _headless: bool) -> PermissionState { - // TODO popup only supported on linux - PermissionState::Denied -} - // https://w3c.github.io/permissions/#allowed-in-non-secure-contexts fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool { match *permission_name { @@ -391,3 +354,40 @@ fn allowed_in_nonsecure_contexts(permission_name: &PermissionName) -> bool { PermissionName::Persistent_storage => false, } } + +fn prompt_user_from_embedder(prompt: PermissionPrompt, gs: &GlobalScope) -> PermissionState { + let (sender, receiver) = ipc::channel().expect("Failed to create IPC channel!"); + gs.send_to_embedder(EmbedderMsg::PromptPermission(prompt, sender)); + + match receiver.recv() { + Ok(PermissionRequest::Granted) => PermissionState::Granted, + Ok(PermissionRequest::Denied) => PermissionState::Denied, + Err(e) => { + warn!( + "Failed to receive permission state from embedder ({:?}).", + e + ); + PermissionState::Denied + }, + } +} + +impl From<PermissionName> for embedder_traits::PermissionName { + fn from(permission_name: PermissionName) -> Self { + match permission_name { + PermissionName::Geolocation => embedder_traits::PermissionName::Geolocation, + PermissionName::Notifications => embedder_traits::PermissionName::Notifications, + PermissionName::Push => embedder_traits::PermissionName::Push, + PermissionName::Midi => embedder_traits::PermissionName::Midi, + PermissionName::Camera => embedder_traits::PermissionName::Camera, + PermissionName::Microphone => embedder_traits::PermissionName::Microphone, + PermissionName::Speaker => embedder_traits::PermissionName::Speaker, + PermissionName::Device_info => embedder_traits::PermissionName::DeviceInfo, + PermissionName::Background_sync => embedder_traits::PermissionName::BackgroundSync, + PermissionName::Bluetooth => embedder_traits::PermissionName::Bluetooth, + PermissionName::Persistent_storage => { + embedder_traits::PermissionName::PersistentStorage + }, + } + } +} diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index f3671a5057f..5e60507f8fc 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -91,7 +91,7 @@ impl Promise { pub fn new_in_current_realm(global: &GlobalScope, _comp: InRealm) -> Rc<Promise> { let cx = global.get_cx(); rooted!(in(*cx) let mut obj = ptr::null_mut::<JSObject>()); - Promise::create_js_promise(cx, HandleObject::null(), obj.handle_mut()); + Promise::create_js_promise(cx, obj.handle_mut()); Promise::new_with_js_promise(obj.handle(), cx) } @@ -109,15 +109,15 @@ impl Promise { reflector: Reflector::new(), permanent_js_root: Heap::default(), }; - let mut promise = Rc::new(promise); - Rc::get_mut(&mut promise).unwrap().init_reflector(obj.get()); + let promise = Rc::new(promise); + promise.init_reflector(obj.get()); promise.initialize(cx); promise } } #[allow(unsafe_code)] - fn create_js_promise(cx: SafeJSContext, proto: HandleObject, mut obj: MutableHandleObject) { + fn create_js_promise(cx: SafeJSContext, mut obj: MutableHandleObject) { unsafe { let do_nothing_func = JS_NewFunction( *cx, @@ -129,7 +129,7 @@ impl Promise { assert!(!do_nothing_func.is_null()); rooted!(in(*cx) let do_nothing_obj = JS_GetFunctionObject(do_nothing_func)); assert!(!do_nothing_obj.is_null()); - obj.set(NewPromiseObject(*cx, do_nothing_obj.handle(), proto)); + obj.set(NewPromiseObject(*cx, do_nothing_obj.handle())); assert!(!obj.is_null()); } } diff --git a/components/script/dom/rtcpeerconnection.rs b/components/script/dom/rtcpeerconnection.rs index ae246cae5b4..e6b45d93676 100644 --- a/components/script/dom/rtcpeerconnection.rs +++ b/components/script/dom/rtcpeerconnection.rs @@ -55,8 +55,8 @@ pub struct RTCPeerConnection { #[ignore_malloc_size_of = "defined in servo-media"] controller: DomRefCell<Option<WebRtcController>>, closed: Cell<bool>, - /// Helps track state changes between the time createOffer/createAnswer - /// is called and resolved + // Helps track state changes between the time createOffer/createAnswer + // is called and resolved offer_answer_generation: Cell<u32>, #[ignore_malloc_size_of = "promises are hard"] offer_promises: DomRefCell<Vec<Rc<Promise>>>, @@ -419,34 +419,34 @@ impl RTCPeerConnection { } impl RTCPeerConnectionMethods for RTCPeerConnection { - /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icecandidate + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icecandidate event_handler!(icecandidate, GetOnicecandidate, SetOnicecandidate); - /// https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack + // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnection-ontrack event_handler!(track, GetOntrack, SetOntrack); - /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-iceconnectionstatechange + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-iceconnectionstatechange event_handler!( iceconnectionstatechange, GetOniceconnectionstatechange, SetOniceconnectionstatechange ); - /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icegatheringstatechange + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-icegatheringstatechange event_handler!( icegatheringstatechange, GetOnicegatheringstatechange, SetOnicegatheringstatechange ); - /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-onnegotiationneeded + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-onnegotiationneeded event_handler!( negotiationneeded, GetOnnegotiationneeded, SetOnnegotiationneeded ); - /// https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-signalingstatechange + // https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-signalingstatechange event_handler!( signalingstatechange, GetOnsignalingstatechange, diff --git a/components/script/dom/texttrackcue.rs b/components/script/dom/texttrackcue.rs index ce55cdf3259..61a07d2cb3c 100644 --- a/components/script/dom/texttrackcue.rs +++ b/components/script/dom/texttrackcue.rs @@ -25,24 +25,32 @@ pub struct TextTrackCue { } impl TextTrackCue { - // FIXME(#22314, dlrobertson) implement VTTCue. - #[allow(dead_code)] - pub fn new_inherited(id: DOMString, track: Option<&TextTrack>) -> TextTrackCue { + pub fn new_inherited( + id: DOMString, + start_time: f64, + end_time: f64, + track: Option<&TextTrack>, + ) -> TextTrackCue { TextTrackCue { eventtarget: EventTarget::new_inherited(), id: DomRefCell::new(id), track: track.map(Dom::from_ref), - start_time: Cell::new(0.), - end_time: Cell::new(0.), + start_time: Cell::new(start_time), + end_time: Cell::new(end_time), pause_on_exit: Cell::new(false), } } - // FIXME(#22314, dlrobertson) implement VTTCue. #[allow(dead_code)] - pub fn new(window: &Window, id: DOMString, track: Option<&TextTrack>) -> DomRoot<TextTrackCue> { + pub fn new( + window: &Window, + id: DOMString, + start_time: f64, + end_time: f64, + track: Option<&TextTrack>, + ) -> DomRoot<TextTrackCue> { reflect_dom_object( - Box::new(TextTrackCue::new_inherited(id, track)), + Box::new(TextTrackCue::new_inherited(id, start_time, end_time, track)), window, TextTrackCueBinding::Wrap, ) diff --git a/components/script/dom/vertexarrayobject.rs b/components/script/dom/vertexarrayobject.rs new file mode 100644 index 00000000000..025cff9194a --- /dev/null +++ b/components/script/dom/vertexarrayobject.rs @@ -0,0 +1,296 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref}; +use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use crate::dom::bindings::root::{Dom, MutNullableDom}; +use crate::dom::webglbuffer::WebGLBuffer; +use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use canvas_traits::webgl::{ + ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId, +}; +use std::cell::Cell; + +#[derive(JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct VertexArrayObject { + context: Dom<WebGLRenderingContext>, + id: Option<WebGLVertexArrayId>, + ever_bound: Cell<bool>, + is_deleted: Cell<bool>, + vertex_attribs: DomRefCell<Box<[VertexAttribData]>>, + element_array_buffer: MutNullableDom<WebGLBuffer>, +} + +impl VertexArrayObject { + pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { + let max_vertex_attribs = context.limits().max_vertex_attribs as usize; + Self { + context: Dom::from_ref(context), + id, + ever_bound: Default::default(), + is_deleted: Default::default(), + vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()), + element_array_buffer: Default::default(), + } + } + + pub fn id(&self) -> Option<WebGLVertexArrayId> { + self.id + } + + pub fn is_deleted(&self) -> bool { + self.is_deleted.get() + } + + pub fn delete(&self, fallible: bool) { + assert!(self.id.is_some()); + if self.is_deleted.get() { + return; + } + self.is_deleted.set(true); + let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap()); + if fallible { + self.context.send_command_ignored(cmd); + } else { + self.context.send_command(cmd); + } + + for attrib_data in &**self.vertex_attribs.borrow() { + if let Some(buffer) = attrib_data.buffer() { + buffer.decrement_attached_counter(); + } + } + if let Some(buffer) = self.element_array_buffer.get() { + buffer.decrement_attached_counter(); + } + } + + pub fn ever_bound(&self) -> bool { + return self.ever_bound.get(); + } + + pub fn set_ever_bound(&self) { + self.ever_bound.set(true); + } + + pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { + &self.element_array_buffer + } + + pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { + ref_filter_map(self.vertex_attribs.borrow(), |attribs| { + attribs.get(index as usize) + }) + } + + pub fn vertex_attrib_pointer( + &self, + index: u32, + size: i32, + type_: u32, + normalized: bool, + stride: i32, + offset: i64, + ) -> WebGLResult<()> { + let mut attribs = self.vertex_attribs.borrow_mut(); + let data = attribs + .get_mut(index as usize) + .ok_or(WebGLError::InvalidValue)?; + + if size < 1 || size > 4 { + return Err(WebGLError::InvalidValue); + } + + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE + if stride < 0 || stride > 255 || offset < 0 { + return Err(WebGLError::InvalidValue); + } + let bytes_per_component: i32 = match type_ { + constants::BYTE | constants::UNSIGNED_BYTE => 1, + constants::SHORT | constants::UNSIGNED_SHORT => 2, + constants::FLOAT => 4, + _ => return Err(WebGLError::InvalidEnum), + }; + if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 { + return Err(WebGLError::InvalidOperation); + } + + let buffer = self.context.array_buffer(); + match buffer { + Some(ref buffer) => buffer.increment_attached_counter(), + None if offset != 0 => { + // https://github.com/KhronosGroup/WebGL/pull/2228 + return Err(WebGLError::InvalidOperation); + }, + _ => {}, + } + self.context.send_command(WebGLCommand::VertexAttribPointer( + index, + size, + type_, + normalized, + stride, + offset as u32, + )); + if let Some(old) = data.buffer() { + old.decrement_attached_counter(); + } + + *data = VertexAttribData { + enabled_as_array: data.enabled_as_array, + size: size as u8, + type_, + bytes_per_vertex: size as u8 * bytes_per_component as u8, + normalized, + stride: stride as u8, + offset: offset as u32, + buffer: buffer.map(|b| Dom::from_ref(&*b)), + divisor: data.divisor, + }; + + Ok(()) + } + + pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { + self.vertex_attribs.borrow_mut()[index as usize].divisor = value; + } + + pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { + self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value; + } + + pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { + for attrib in &mut **self.vertex_attribs.borrow_mut() { + if let Some(b) = attrib.buffer() { + if b.id() != buffer.id() { + continue; + } + b.decrement_attached_counter(); + } + attrib.buffer = None; + } + if self + .element_array_buffer + .get() + .map_or(false, |b| buffer == &*b) + { + buffer.decrement_attached_counter(); + self.element_array_buffer.set(None); + } + } + + pub fn validate_for_draw( + &self, + required_len: u32, + instance_count: u32, + active_attribs: &[ActiveAttribInfo], + ) -> WebGLResult<()> { + // TODO(nox): Cache limits per VAO. + let attribs = self.vertex_attribs.borrow(); + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2 + if attribs + .iter() + .any(|data| data.enabled_as_array && data.buffer.is_none()) + { + return Err(WebGLError::InvalidOperation); + } + let mut has_active_attrib = false; + let mut has_divisor_0 = false; + for active_info in active_attribs { + if active_info.location < 0 { + continue; + } + has_active_attrib = true; + let attrib = &attribs[active_info.location as usize]; + if attrib.divisor == 0 { + has_divisor_0 = true; + } + if !attrib.enabled_as_array { + continue; + } + // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6 + if required_len > 0 && instance_count > 0 { + let max_vertices = attrib.max_vertices(); + if attrib.divisor == 0 { + if max_vertices < required_len { + return Err(WebGLError::InvalidOperation); + } + } else if max_vertices + .checked_mul(attrib.divisor) + .map_or(false, |v| v < instance_count) + { + return Err(WebGLError::InvalidOperation); + } + } + } + if has_active_attrib && !has_divisor_0 { + return Err(WebGLError::InvalidOperation); + } + Ok(()) + } +} + +impl Drop for VertexArrayObject { + fn drop(&mut self) { + if self.id.is_some() { + self.delete(true); + } + } +} + +#[derive(Clone, JSTraceable, MallocSizeOf)] +#[unrooted_must_root_lint::must_root] +pub struct VertexAttribData { + pub enabled_as_array: bool, + pub size: u8, + pub type_: u32, + bytes_per_vertex: u8, + pub normalized: bool, + pub stride: u8, + pub offset: u32, + pub buffer: Option<Dom<WebGLBuffer>>, + pub divisor: u32, +} + +impl Default for VertexAttribData { + #[allow(unrooted_must_root)] + fn default() -> Self { + Self { + enabled_as_array: false, + size: 4, + type_: constants::FLOAT, + bytes_per_vertex: 16, + normalized: false, + stride: 0, + offset: 0, + buffer: None, + divisor: 0, + } + } +} + +impl VertexAttribData { + pub fn buffer(&self) -> Option<&WebGLBuffer> { + self.buffer.as_ref().map(|b| &**b) + } + + pub fn max_vertices(&self) -> u32 { + let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset); + if capacity < self.bytes_per_vertex as u32 { + 0 + } else if self.stride == 0 { + capacity / self.bytes_per_vertex as u32 + } else if self.stride < self.bytes_per_vertex { + (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32 + } else { + let mut max = capacity / self.stride as u32; + if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 { + max += 1; + } + max + } + } +} diff --git a/components/script/dom/vrdisplay.rs b/components/script/dom/vrdisplay.rs index f595952b07a..4b3f7426dda 100644 --- a/components/script/dom/vrdisplay.rs +++ b/components/script/dom/vrdisplay.rs @@ -23,7 +23,6 @@ use crate::dom::bindings::root::{DomRoot, MutDom, MutNullableDom}; use crate::dom::bindings::str::DOMString; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; -use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::vrdisplaycapabilities::VRDisplayCapabilities; use crate::dom::vrdisplayevent::VRDisplayEvent; @@ -32,6 +31,7 @@ use crate::dom::vrframedata::VRFrameData; use crate::dom::vrpose::VRPose; use crate::dom::vrstageparameters::VRStageParameters; use crate::dom::webglrenderingcontext::{WebGLMessageSender, WebGLRenderingContext}; +use crate::dom::window::Window; use crate::realms::InRealm; use crate::script_runtime::CommonScriptMsg; use crate::script_runtime::ScriptThreadEventCategory::WebVREvent; @@ -40,6 +40,7 @@ use canvas_traits::webgl::{webgl_channel, WebGLReceiver, WebVRCommand}; use crossbeam_channel::{unbounded, Sender}; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSender; +use msg::constellation_msg::PipelineId; use profile_traits::ipc; use std::cell::Cell; use std::mem; @@ -82,6 +83,9 @@ pub struct VRDisplay { running_display_raf: Cell<bool>, paused: Cell<bool>, stopped_on_pause: Cell<bool>, + #[ignore_malloc_size_of = "channels are hard"] + webvr_thread: IpcSender<WebVRMsg>, + pipeline: PipelineId, } unsafe_no_jsmanaged_fields!(WebVRDisplayData); @@ -110,7 +114,7 @@ struct VRRAFUpdate { type VRRAFUpdateSender = Sender<Result<VRRAFUpdate, ()>>; impl VRDisplay { - fn new_inherited(global: &GlobalScope, display: WebVRDisplayData) -> VRDisplay { + fn new_inherited(global: &Window, display: WebVRDisplayData) -> VRDisplay { let stage = match display.stage_parameters { Some(ref params) => Some(VRStageParameters::new(params.clone(), &global)), None => None, @@ -152,10 +156,12 @@ impl VRDisplay { // This flag is set when the Display was presenting when it received a VR Pause event. // When the VR Resume event is received and the flag is set, VR presentation automatically restarts. stopped_on_pause: Cell::new(false), + webvr_thread: global.webvr_thread().expect("webvr is disabled"), + pipeline: global.pipeline_id(), } } - pub fn new(global: &GlobalScope, display: WebVRDisplayData) -> DomRoot<VRDisplay> { + pub fn new(global: &Window, display: WebVRDisplayData) -> DomRoot<VRDisplay> { reflect_dom_object( Box::new(VRDisplay::new_inherited(&global, display)), global, @@ -229,7 +235,7 @@ impl VRDisplayMethods for VRDisplay { // If not presenting we fetch inmediante VRFrameData let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread() + self.webvr_thread .send(WebVRMsg::GetFrameData( self.global().pipeline_id(), self.DisplayId(), @@ -258,7 +264,7 @@ impl VRDisplayMethods for VRDisplay { // https://w3c.github.io/webvr/#dom-vrdisplay-resetpose fn ResetPose(&self) { let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread() + self.webvr_thread .send(WebVRMsg::ResetPose( self.global().pipeline_id(), self.DisplayId(), @@ -398,7 +404,7 @@ impl VRDisplayMethods for VRDisplay { // Exit present let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread() + self.webvr_thread .send(WebVRMsg::ExitPresent( self.global().pipeline_id(), self.display.borrow().display_id, @@ -452,18 +458,14 @@ impl VRDisplayMethods for VRDisplay { } impl VRDisplay { - fn webvr_thread(&self) -> IpcSender<WebVRMsg> { - self.global() - .as_window() - .webvr_thread() - .expect("Shouldn't arrive here with WebVR disabled") - } - pub fn update_display(&self, display: &WebVRDisplayData) { *self.display.borrow_mut() = display.clone(); if let Some(ref stage) = display.stage_parameters { if self.stage_params.get().is_none() { - let params = Some(VRStageParameters::new(stage.clone(), &self.global())); + let params = Some(VRStageParameters::new( + stage.clone(), + &self.global().as_window(), + )); self.stage_params.set(params.as_deref()); } else { self.stage_params.get().unwrap().update(&stage); @@ -484,7 +486,7 @@ impl VRDisplay { { // Request Present let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); - self.webvr_thread() + self.webvr_thread .send(WebVRMsg::RequestPresent( self.global().pipeline_id(), self.display.borrow().display_id, @@ -730,7 +732,7 @@ impl VRDisplay { // Only called when the JSContext is destroyed while presenting. // In this case we don't want to wait for WebVR Thread response. fn force_stop_present(&self) { - self.webvr_thread() + self.webvr_thread .send(WebVRMsg::ExitPresent( self.global().pipeline_id(), self.display.borrow().display_id, diff --git a/components/script/dom/vrdisplaycapabilities.rs b/components/script/dom/vrdisplaycapabilities.rs index 858c40978a7..214ee617e1c 100644 --- a/components/script/dom/vrdisplaycapabilities.rs +++ b/components/script/dom/vrdisplaycapabilities.rs @@ -7,7 +7,7 @@ use crate::dom::bindings::codegen::Bindings::VRDisplayCapabilitiesBinding; use crate::dom::bindings::codegen::Bindings::VRDisplayCapabilitiesBinding::VRDisplayCapabilitiesMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use dom_struct::dom_struct; use webvr_traits::WebVRDisplayCapabilities; @@ -30,7 +30,7 @@ impl VRDisplayCapabilities { pub fn new( capabilities: WebVRDisplayCapabilities, - global: &GlobalScope, + global: &Window, ) -> DomRoot<VRDisplayCapabilities> { reflect_dom_object( Box::new(VRDisplayCapabilities::new_inherited(capabilities)), diff --git a/components/script/dom/vreyeparameters.rs b/components/script/dom/vreyeparameters.rs index a7463c34a28..c908b23540d 100644 --- a/components/script/dom/vreyeparameters.rs +++ b/components/script/dom/vreyeparameters.rs @@ -7,8 +7,8 @@ use crate::dom::bindings::codegen::Bindings::VREyeParametersBinding; use crate::dom::bindings::codegen::Bindings::VREyeParametersBinding::VREyeParametersMethods; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::{Dom, DomRoot}; -use crate::dom::globalscope::GlobalScope; use crate::dom::vrfieldofview::VRFieldOfView; +use crate::dom::window::Window; use crate::script_runtime::JSContext; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; @@ -41,7 +41,7 @@ impl VREyeParameters { } #[allow(unsafe_code)] - pub fn new(parameters: WebVREyeParameters, global: &GlobalScope) -> DomRoot<VREyeParameters> { + pub fn new(parameters: WebVREyeParameters, global: &Window) -> DomRoot<VREyeParameters> { let fov = VRFieldOfView::new(&global, parameters.field_of_view.clone()); let cx = global.get_cx(); diff --git a/components/script/dom/vrfieldofview.rs b/components/script/dom/vrfieldofview.rs index 25a01daaf16..8009f6a2461 100644 --- a/components/script/dom/vrfieldofview.rs +++ b/components/script/dom/vrfieldofview.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::codegen::Bindings::VRFieldOfViewBinding::VRFieldOfView use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, Reflector}; use crate::dom::bindings::root::DomRoot; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use dom_struct::dom_struct; use webvr_traits::WebVRFieldOfView; @@ -29,7 +29,7 @@ impl VRFieldOfView { } } - pub fn new(global: &GlobalScope, fov: WebVRFieldOfView) -> DomRoot<VRFieldOfView> { + pub fn new(global: &Window, fov: WebVRFieldOfView) -> DomRoot<VRFieldOfView> { reflect_dom_object( Box::new(VRFieldOfView::new_inherited(fov)), global, diff --git a/components/script/dom/vrstageparameters.rs b/components/script/dom/vrstageparameters.rs index 747608763e9..e9df89364c4 100644 --- a/components/script/dom/vrstageparameters.rs +++ b/components/script/dom/vrstageparameters.rs @@ -8,7 +8,7 @@ use crate::dom::bindings::codegen::Bindings::VRStageParametersBinding::VRStagePa use crate::dom::bindings::num::Finite; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; use crate::dom::bindings::root::DomRoot; -use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; use crate::script_runtime::JSContext; use dom_struct::dom_struct; use js::jsapi::{Heap, JSObject}; @@ -38,10 +38,7 @@ impl VRStageParameters { } #[allow(unsafe_code)] - pub fn new( - parameters: WebVRStageParameters, - global: &GlobalScope, - ) -> DomRoot<VRStageParameters> { + pub fn new(parameters: WebVRStageParameters, global: &Window) -> DomRoot<VRStageParameters> { let cx = global.get_cx(); rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>()); unsafe { diff --git a/components/script/dom/vttcue.rs b/components/script/dom/vttcue.rs new file mode 100644 index 00000000000..82ffb36c473 --- /dev/null +++ b/components/script/dom/vttcue.rs @@ -0,0 +1,232 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::VTTCueBinding::{ + self, AlignSetting, AutoKeyword, DirectionSetting, LineAlignSetting, PositionAlignSetting, + VTTCueMethods, +}; +use crate::dom::bindings::error::{Error, ErrorResult}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{Dom, DomRoot}; +use crate::dom::bindings::str::DOMString; +use crate::dom::documentfragment::DocumentFragment; +use crate::dom::globalscope::GlobalScope; +use crate::dom::texttrackcue::TextTrackCue; +use crate::dom::vttregion::VTTRegion; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct VTTCue { + texttrackcue: TextTrackCue, + region: DomRefCell<Option<Dom<VTTRegion>>>, + vertical: Cell<DirectionSetting>, + snap_to_lines: Cell<bool>, + line: DomRefCell<LineAndPositionSetting>, + line_align: Cell<LineAlignSetting>, + position: DomRefCell<LineAndPositionSetting>, + position_align: Cell<PositionAlignSetting>, + size: Cell<f64>, + align: Cell<AlignSetting>, + text: DomRefCell<DOMString>, +} + +impl VTTCue { + pub fn new_inherited(start_time: f64, end_time: f64, text: DOMString) -> Self { + VTTCue { + texttrackcue: TextTrackCue::new_inherited( + DOMString::default(), + start_time, + end_time, + None, + ), + region: DomRefCell::new(None), + vertical: Cell::new(DirectionSetting::default()), + snap_to_lines: Cell::new(true), + line: DomRefCell::new(LineAndPositionSetting::Auto), + line_align: Cell::new(LineAlignSetting::Start), + position: DomRefCell::new(LineAndPositionSetting::Auto), + position_align: Cell::new(PositionAlignSetting::Auto), + size: Cell::new(100_f64), + align: Cell::new(AlignSetting::Center), + text: DomRefCell::new(text), + } + } + + pub fn new( + global: &GlobalScope, + start_time: f64, + end_time: f64, + text: DOMString, + ) -> DomRoot<Self> { + reflect_dom_object( + Box::new(Self::new_inherited(start_time, end_time, text)), + global, + VTTCueBinding::Wrap, + ) + } + + #[allow(non_snake_case)] + pub fn Constructor( + window: &Window, + start_time: Finite<f64>, + end_time: Finite<f64>, + text: DOMString, + ) -> DomRoot<Self> { + VTTCue::new(&window.global(), *start_time, *end_time, text) + } +} + +impl VTTCueMethods for VTTCue { + // https://w3c.github.io/webvtt/#dom-vttcue-region + fn GetRegion(&self) -> Option<DomRoot<VTTRegion>> { + self.region + .borrow() + .as_ref() + .map(|r| DomRoot::from_ref(&**r)) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-region + fn SetRegion(&self, value: Option<&VTTRegion>) { + *self.region.borrow_mut() = value.map(|r| Dom::from_ref(r)) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-vertical + fn Vertical(&self) -> DirectionSetting { + self.vertical.get() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-vertical + fn SetVertical(&self, value: DirectionSetting) { + self.vertical.set(value); + } + + // https://w3c.github.io/webvtt/#dom-vttcue-snaptolines + fn SnapToLines(&self) -> bool { + self.snap_to_lines.get() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-snaptolines + fn SetSnapToLines(&self, value: bool) { + self.snap_to_lines.set(value) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-line + fn Line(&self) -> VTTCueBinding::LineAndPositionSetting { + VTTCueBinding::LineAndPositionSetting::from(self.line.borrow().clone()) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-line + fn SetLine(&self, value: VTTCueBinding::LineAndPositionSetting) { + *self.line.borrow_mut() = value.into(); + } + + // https://w3c.github.io/webvtt/#dom-vttcue-linealign + fn LineAlign(&self) -> LineAlignSetting { + self.line_align.get() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-linealign + fn SetLineAlign(&self, value: LineAlignSetting) { + self.line_align.set(value); + } + + // https://w3c.github.io/webvtt/#dom-vttcue-position + fn Position(&self) -> VTTCueBinding::LineAndPositionSetting { + VTTCueBinding::LineAndPositionSetting::from(self.position.borrow().clone()) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-position + fn SetPosition(&self, value: VTTCueBinding::LineAndPositionSetting) -> ErrorResult { + if let VTTCueBinding::LineAndPositionSetting::Double(x) = value { + if *x < 0_f64 || *x > 100_f64 { + return Err(Error::IndexSize); + } + } + + *self.position.borrow_mut() = value.into(); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-positionalign + fn PositionAlign(&self) -> PositionAlignSetting { + self.position_align.get() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-positionalign + fn SetPositionAlign(&self, value: PositionAlignSetting) { + self.position_align.set(value); + } + + // https://w3c.github.io/webvtt/#dom-vttcue-size + fn Size(&self) -> Finite<f64> { + Finite::wrap(self.size.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-size + fn SetSize(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.size.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttcue-align + fn Align(&self) -> AlignSetting { + self.align.get() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-align + fn SetAlign(&self, value: AlignSetting) { + self.align.set(value); + } + + // https://w3c.github.io/webvtt/#dom-vttcue-text + fn Text(&self) -> DOMString { + self.text.borrow().clone() + } + + // https://w3c.github.io/webvtt/#dom-vttcue-text + fn SetText(&self, value: DOMString) { + *self.text.borrow_mut() = value; + } + + // https://w3c.github.io/webvtt/#dom-vttcue-getcueashtml + fn GetCueAsHTML(&self) -> DomRoot<DocumentFragment> { + todo!() + } +} + +#[derive(Clone, JSTraceable, MallocSizeOf)] +enum LineAndPositionSetting { + Double(f64), + Auto, +} + +impl From<VTTCueBinding::LineAndPositionSetting> for LineAndPositionSetting { + fn from(value: VTTCueBinding::LineAndPositionSetting) -> Self { + match value { + VTTCueBinding::LineAndPositionSetting::Double(x) => LineAndPositionSetting::Double(*x), + VTTCueBinding::LineAndPositionSetting::AutoKeyword(_) => LineAndPositionSetting::Auto, + } + } +} + +impl From<LineAndPositionSetting> for VTTCueBinding::LineAndPositionSetting { + fn from(value: LineAndPositionSetting) -> Self { + match value { + LineAndPositionSetting::Double(x) => { + VTTCueBinding::LineAndPositionSetting::Double(Finite::wrap(x)) + }, + LineAndPositionSetting::Auto => { + VTTCueBinding::LineAndPositionSetting::AutoKeyword(AutoKeyword::Auto) + }, + } + } +} diff --git a/components/script/dom/vttregion.rs b/components/script/dom/vttregion.rs new file mode 100644 index 00000000000..167e9fa37bf --- /dev/null +++ b/components/script/dom/vttregion.rs @@ -0,0 +1,167 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::DomRefCell; +use crate::dom::bindings::codegen::Bindings::VTTRegionBinding::{ + self, ScrollSetting, VTTRegionMethods, +}; +use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; +use crate::dom::bindings::num::Finite; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::globalscope::GlobalScope; +use crate::dom::window::Window; +use dom_struct::dom_struct; +use std::cell::Cell; + +#[dom_struct] +pub struct VTTRegion { + reflector_: Reflector, + id: DomRefCell<DOMString>, + width: Cell<f64>, + lines: Cell<u32>, + region_anchor_x: Cell<f64>, + region_anchor_y: Cell<f64>, + viewport_anchor_x: Cell<f64>, + viewport_anchor_y: Cell<f64>, + scroll: Cell<ScrollSetting>, +} + +impl VTTRegion { + pub fn new_inherited() -> Self { + VTTRegion { + reflector_: Reflector::new(), + id: DomRefCell::new(DOMString::default()), + width: Cell::new(100_f64), + lines: Cell::new(3), + region_anchor_x: Cell::new(0_f64), + region_anchor_y: Cell::new(100_f64), + viewport_anchor_x: Cell::new(0_f64), + viewport_anchor_y: Cell::new(100_f64), + scroll: Cell::new(Default::default()), + } + } + + pub fn new(global: &GlobalScope) -> DomRoot<Self> { + reflect_dom_object( + Box::new(Self::new_inherited()), + global, + VTTRegionBinding::Wrap, + ) + } + + #[allow(non_snake_case)] + pub fn Constructor(window: &Window) -> Fallible<DomRoot<Self>> { + Ok(VTTRegion::new(&window.global())) + } +} + +impl VTTRegionMethods for VTTRegion { + // https://w3c.github.io/webvtt/#dom-vttregion-id + fn Id(&self) -> DOMString { + self.id.borrow().clone() + } + + // https://w3c.github.io/webvtt/#dom-vttregion-id + fn SetId(&self, value: DOMString) { + *self.id.borrow_mut() = value; + } + + // https://w3c.github.io/webvtt/#dom-vttregion-width + fn Width(&self) -> Finite<f64> { + Finite::wrap(self.width.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-width + fn SetWidth(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.width.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-lines + fn Lines(&self) -> u32 { + self.lines.get() + } + + // https://w3c.github.io/webvtt/#dom-vttregion-lines + fn SetLines(&self, value: u32) -> ErrorResult { + self.lines.set(value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-regionanchorx + fn RegionAnchorX(&self) -> Finite<f64> { + Finite::wrap(self.region_anchor_x.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-regionanchorx + fn SetRegionAnchorX(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.region_anchor_x.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-regionanchory + fn RegionAnchorY(&self) -> Finite<f64> { + Finite::wrap(self.region_anchor_y.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-regionanchory + fn SetRegionAnchorY(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.region_anchor_y.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-viewportanchorx + fn ViewportAnchorX(&self) -> Finite<f64> { + Finite::wrap(self.viewport_anchor_x.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-viewportanchorx + fn SetViewportAnchorX(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.viewport_anchor_x.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-viewportanchory + fn ViewportAnchorY(&self) -> Finite<f64> { + Finite::wrap(self.viewport_anchor_y.get()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-viewportanchory + fn SetViewportAnchorY(&self, value: Finite<f64>) -> ErrorResult { + if *value < 0_f64 || *value > 100_f64 { + return Err(Error::IndexSize); + } + + self.viewport_anchor_y.set(*value); + Ok(()) + } + + // https://w3c.github.io/webvtt/#dom-vttregion-scroll + fn Scroll(&self) -> ScrollSetting { + self.scroll.get() + } + + // https://w3c.github.io/webvtt/#dom-vttregion-scroll + fn SetScroll(&self, value: ScrollSetting) { + self.scroll.set(value); + } +} diff --git a/components/script/dom/webgl2renderingcontext.rs b/components/script/dom/webgl2renderingcontext.rs index cc735f36e41..7b1a68b475a 100644 --- a/components/script/dom/webgl2renderingcontext.rs +++ b/components/script/dom/webgl2renderingcontext.rs @@ -36,12 +36,14 @@ use crate::dom::webglsync::WebGLSync; use crate::dom::webgltexture::WebGLTexture; use crate::dom::webgltransformfeedback::WebGLTransformFeedback; use crate::dom::webgluniformlocation::WebGLUniformLocation; +use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject; use crate::dom::window::Window; use crate::js::conversions::ToJSValConvertible; use crate::script_runtime::JSContext; use canvas_traits::webgl::WebGLError::*; use canvas_traits::webgl::{ - webgl_channel, GLContextAttributes, WebGLCommand, WebGLResult, WebGLVersion, + webgl_channel, GLContextAttributes, InternalFormatParameter, WebGLCommand, WebGLResult, + WebGLVersion, }; use dom_struct::dom_struct; use euclid::default::{Point2D, Rect, Size2D}; @@ -50,12 +52,30 @@ use js::jsapi::{JSObject, Type}; use js::jsval::{BooleanValue, DoubleValue, Int32Value, UInt32Value}; use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; use js::rust::CustomAutoRooterGuard; -use js::typedarray::{ArrayBufferView, CreateWith, Float32, Uint32, Uint32Array}; +use js::typedarray::{ArrayBufferView, CreateWith, Float32, Int32Array, Uint32, Uint32Array}; use script_layout_interface::HTMLCanvasDataSource; use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; +#[unrooted_must_root_lint::must_root] +#[derive(JSTraceable, MallocSizeOf)] +struct IndexedBinding { + buffer: MutNullableDom<WebGLBuffer>, + start: Cell<i64>, + size: Cell<i64>, +} + +impl IndexedBinding { + fn new() -> IndexedBinding { + IndexedBinding { + buffer: MutNullableDom::new(None), + start: Cell::new(0), + size: Cell::new(0), + } + } +} + #[dom_struct] pub struct WebGL2RenderingContext { reflector_: Reflector, @@ -69,11 +89,15 @@ pub struct WebGL2RenderingContext { bound_pixel_unpack_buffer: MutNullableDom<WebGLBuffer>, bound_transform_feedback_buffer: MutNullableDom<WebGLBuffer>, bound_uniform_buffer: MutNullableDom<WebGLBuffer>, + indexed_uniform_buffer_bindings: Box<[IndexedBinding]>, + indexed_transform_feedback_buffer_bindings: Box<[IndexedBinding]>, current_transform_feedback: MutNullableDom<WebGLTransformFeedback>, texture_pack_row_length: Cell<usize>, texture_pack_skip_pixels: Cell<usize>, texture_pack_skip_rows: Cell<usize>, enable_rasterizer_discard: Cell<bool>, + default_fb_readbuffer: Cell<u32>, + default_fb_drawbuffer: Cell<u32>, } fn typedarray_elem_size(typeid: Type) -> usize { @@ -82,6 +106,7 @@ fn typedarray_elem_size(typeid: Type) -> usize { Type::Int16 | Type::Uint16 => 2, Type::Int32 | Type::Uint32 | Type::Float32 => 4, Type::Int64 | Type::Float64 => 8, + Type::BigInt64 | Type::BigUint64 => 8, Type::MaxTypedArrayViewType => unreachable!(), } } @@ -110,6 +135,15 @@ impl WebGL2RenderingContext { .map(|_| Default::default()) .collect::<Vec<_>>() .into(); + let indexed_uniform_buffer_bindings = (0..base.limits().max_uniform_buffer_bindings) + .map(|_| IndexedBinding::new()) + .collect::<Vec<_>>() + .into(); + let indexed_transform_feedback_buffer_bindings = + (0..base.limits().max_transform_feedback_separate_attribs) + .map(|_| IndexedBinding::new()) + .collect::<Vec<_>>() + .into(); Some(WebGL2RenderingContext { reflector_: Reflector::new(), @@ -123,11 +157,15 @@ impl WebGL2RenderingContext { bound_pixel_unpack_buffer: MutNullableDom::new(None), bound_transform_feedback_buffer: MutNullableDom::new(None), bound_uniform_buffer: MutNullableDom::new(None), + indexed_uniform_buffer_bindings, + indexed_transform_feedback_buffer_bindings, current_transform_feedback: MutNullableDom::new(None), texture_pack_row_length: Cell::new(0), texture_pack_skip_pixels: Cell::new(0), texture_pack_skip_rows: Cell::new(0), enable_rasterizer_discard: Cell::new(false), + default_fb_readbuffer: Cell::new(constants::BACK), + default_fb_drawbuffer: Cell::new(constants::BACK), }) } @@ -149,6 +187,10 @@ impl WebGL2RenderingContext { self.base.recreate(size) } + pub fn current_vao(&self) -> DomRoot<WebGLVertexArrayObject> { + self.base.current_vao_webgl2() + } + pub fn base_context(&self) -> DomRoot<WebGLRenderingContext> { DomRoot::from_ref(&*self.base) } @@ -161,6 +203,7 @@ impl WebGL2RenderingContext { constants::PIXEL_UNPACK_BUFFER => Ok(self.bound_pixel_unpack_buffer.get()), constants::TRANSFORM_FEEDBACK_BUFFER => Ok(self.bound_transform_feedback_buffer.get()), constants::UNIFORM_BUFFER => Ok(self.bound_uniform_buffer.get()), + constants::ELEMENT_ARRAY_BUFFER => Ok(self.current_vao().element_array_buffer().get()), _ => self.base.bound_buffer(target), } } @@ -305,6 +348,15 @@ impl WebGL2RenderingContext { return self.base.webgl_error(InvalidOperation); } + let fb_slot = self.base.get_draw_framebuffer_slot(); + let fb_readbuffer_valid = match fb_slot.get() { + Some(fb) => fb.attachment(fb.read_buffer()).is_some(), + None => self.default_fb_readbuffer.get() != constants::NONE, + }; + if !fb_readbuffer_valid { + return self.base.webgl_error(InvalidOperation); + } + let dst_byte_offset = { let dst_elem_size = typedarray_elem_size(dst.get_array_type()); dst_elem_offset as usize * dst_elem_size @@ -603,6 +655,51 @@ impl WebGL2RenderingContext { self.base.send_command(msg(buffer, draw_buffer, array)); } + + fn valid_fb_attachment_values(&self, target: u32, attachments: &[u32]) -> bool { + let fb_slot = match target { + constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => { + self.base.get_draw_framebuffer_slot() + }, + constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(), + _ => { + self.base.webgl_error(InvalidEnum); + return false; + }, + }; + + if let Some(fb) = fb_slot.get() { + if fb.check_status() != constants::FRAMEBUFFER_COMPLETE { + return false; + } + + for &attachment in attachments { + match attachment { + constants::DEPTH_ATTACHMENT | + constants::STENCIL_ATTACHMENT | + constants::DEPTH_STENCIL_ATTACHMENT => {}, + constants::COLOR_ATTACHMENT0..=constants::COLOR_ATTACHMENT15 => { + let last_slot = constants::COLOR_ATTACHMENT0 + + self.base.limits().max_color_attachments - + 1; + if last_slot < attachment { + return false; + } + }, + _ => return false, + } + } + } else { + for &attachment in attachments { + match attachment { + constants::COLOR | constants::DEPTH | constants::STENCIL => {}, + _ => return false, + } + } + } + + true + } } impl WebGL2RenderingContextMethods for WebGL2RenderingContext { @@ -700,6 +797,15 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.current_transform_feedback.get() ); }, + constants::ELEMENT_ARRAY_BUFFER_BINDING => unsafe { + let buffer = self.current_vao().element_array_buffer().get(); + return optional_root_object_to_js_or_null!(*cx, buffer); + }, + constants::VERTEX_ARRAY_BINDING => unsafe { + let vao = self.current_vao(); + let vao = vao.id().map(|_| &*vao); + return optional_root_object_to_js_or_null!(*cx, vao); + }, // NOTE: DRAW_FRAMEBUFFER_BINDING is the same as FRAMEBUFFER_BINDING, handled on the WebGL1 side constants::READ_FRAMEBUFFER_BINDING => unsafe { return optional_root_object_to_js_or_null!( @@ -707,6 +813,26 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { &self.base.get_read_framebuffer_slot().get() ); }, + constants::READ_BUFFER => { + let buffer = match self.base.get_read_framebuffer_slot().get() { + Some(fb) => fb.read_buffer(), + None => self.default_fb_readbuffer.get(), + }; + return UInt32Value(buffer); + }, + constants::DRAW_BUFFER0..=constants::DRAW_BUFFER15 => { + let buffer = match self.base.get_read_framebuffer_slot().get() { + Some(fb) => { + let idx = parameter - constants::DRAW_BUFFER0; + fb.draw_buffer_i(idx as usize) + }, + None if parameter == constants::DRAW_BUFFER0 => { + self.default_fb_readbuffer.get() + }, + None => constants::NONE, + }; + return UInt32Value(buffer); + }, _ => {}, } @@ -865,6 +991,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 fn BindBuffer(&self, target: u32, buffer: Option<&WebGLBuffer>) { + let current_vao; let slot = match target { constants::COPY_READ_BUFFER => &self.bound_copy_read_buffer, constants::COPY_WRITE_BUFFER => &self.bound_copy_write_buffer, @@ -872,6 +999,10 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { constants::PIXEL_UNPACK_BUFFER => &self.bound_pixel_unpack_buffer, constants::TRANSFORM_FEEDBACK_BUFFER => &self.bound_transform_feedback_buffer, constants::UNIFORM_BUFFER => &self.bound_uniform_buffer, + constants::ELEMENT_ARRAY_BUFFER => { + current_vao = self.current_vao(); + current_vao.element_array_buffer() + }, _ => return self.base.BindBuffer(target, buffer), }; self.base.bind_buffer_maybe(&slot, target, buffer); @@ -1332,6 +1463,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.CreateShader(shader_type) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn CreateVertexArray(&self) -> Option<DomRoot<WebGLVertexArrayObject>> { + self.base.create_vertex_array_webgl2() + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5 fn DeleteBuffer(&self, buffer: Option<&WebGLBuffer>) { let buffer = match buffer { @@ -1342,7 +1478,7 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { if buffer.is_marked_for_deletion() { return; } - self.base.current_vao().unbind_buffer(buffer); + self.current_vao().unbind_buffer(buffer); self.unbind_from(&self.base.array_buffer_slot(), &buffer); self.unbind_from(&self.bound_copy_read_buffer, &buffer); self.unbind_from(&self.bound_copy_write_buffer, &buffer); @@ -1350,6 +1486,14 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.unbind_from(&self.bound_pixel_unpack_buffer, &buffer); self.unbind_from(&self.bound_transform_feedback_buffer, &buffer); self.unbind_from(&self.bound_uniform_buffer, &buffer); + + for binding in self.indexed_uniform_buffer_bindings.iter() { + self.unbind_from(&binding.buffer, &buffer); + } + for binding in self.indexed_transform_feedback_buffer_bindings.iter() { + self.unbind_from(&binding.buffer, &buffer); + } + buffer.mark_for_deletion(false); } @@ -1378,6 +1522,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.DeleteShader(shader) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn DeleteVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) { + self.base.delete_vertex_array_webgl2(vertex_array); + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11 fn DrawArrays(&self, mode: u32, first: i32, count: i32) { self.base.DrawArrays(mode, first, count) @@ -1421,6 +1570,12 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.GetAttribLocation(program, name) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.7 + fn GetFragDataLocation(&self, program: &WebGLProgram, name: DOMString) -> i32 { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(program), return -1); + handle_potential_webgl_error!(self.base, program.get_frag_data_location(name), -1) + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn GetProgramInfoLog(&self, program: &WebGLProgram) -> Option<DOMString> { self.base.GetProgramInfoLog(program) @@ -1468,6 +1623,46 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { .GetShaderPrecisionFormat(shader_type, precision_type) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.2 + #[allow(unsafe_code)] + fn GetIndexedParameter(&self, cx: JSContext, target: u32, index: u32) -> JSVal { + let bindings = match target { + constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | + constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | + constants::TRANSFORM_FEEDBACK_BUFFER_START => { + &self.indexed_transform_feedback_buffer_bindings + }, + constants::UNIFORM_BUFFER_BINDING | + constants::UNIFORM_BUFFER_SIZE | + constants::UNIFORM_BUFFER_START => &self.indexed_uniform_buffer_bindings, + _ => { + self.base.webgl_error(InvalidEnum); + return NullValue(); + }, + }; + + let binding = match bindings.get(index as usize) { + Some(binding) => binding, + None => { + self.base.webgl_error(InvalidValue); + return NullValue(); + }, + }; + + match target { + constants::TRANSFORM_FEEDBACK_BUFFER_BINDING | constants::UNIFORM_BUFFER_BINDING => unsafe { + optional_root_object_to_js_or_null!(*cx, binding.buffer.get()) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_START | constants::UNIFORM_BUFFER_START => { + Int32Value(binding.start.get() as _) + }, + constants::TRANSFORM_FEEDBACK_BUFFER_SIZE | constants::UNIFORM_BUFFER_SIZE => { + Int32Value(binding.size.get() as _) + }, + _ => unreachable!(), + } + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10 fn GetUniformLocation( &self, @@ -1531,6 +1726,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { self.base.IsTexture(texture) } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn IsVertexArray(&self, vertex_array: Option<&WebGLVertexArrayObject>) -> bool { + self.base.is_vertex_array_webgl2(vertex_array) + } + /// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 fn LineWidth(&self, width: f32) { self.base.LineWidth(width) @@ -2293,6 +2493,9 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { &uniform_get(triple, WebGLCommand::GetUniformFloat4x3), ) }, + constants::SAMPLER_3D | constants::SAMPLER_2D_ARRAY => { + Int32Value(uniform_get(triple, WebGLCommand::GetUniformInt)) + }, _ => self.base.GetUniform(cx, program, location), } } @@ -2911,6 +3114,11 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { } } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 + fn BindVertexArray(&self, array: Option<&WebGLVertexArrayObject>) { + self.base.bind_vertex_array_webgl2(array); + } + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.13 fn SamplerParameteri(&self, sampler: &WebGLSampler, pname: u32, param: i32) { handle_potential_webgl_error!(self.base, self.base.validate_ownership(sampler), return); @@ -3160,20 +3368,21 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 fn BindBufferBase(&self, target: u32, index: u32, buffer: Option<&WebGLBuffer>) { - let (bind_limit, slot) = match target { + let (generic_slot, indexed_bindings) = match target { constants::TRANSFORM_FEEDBACK_BUFFER => ( - self.base.limits().max_transform_feedback_separate_attribs, &self.bound_transform_feedback_buffer, + &self.indexed_transform_feedback_buffer_bindings, ), constants::UNIFORM_BUFFER => ( - self.base.limits().max_uniform_buffer_bindings, &self.bound_uniform_buffer, + &self.indexed_uniform_buffer_bindings, ), _ => return self.base.webgl_error(InvalidEnum), }; - if index >= bind_limit { - return self.base.webgl_error(InvalidValue); - } + let indexed_binding = match indexed_bindings.get(index as usize) { + Some(slot) => slot, + None => return self.base.webgl_error(InvalidValue), + }; if let Some(buffer) = buffer { handle_potential_webgl_error!(self.base, self.base.validate_ownership(buffer), return); @@ -3182,6 +3391,9 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { return self.base.webgl_error(InvalidOperation); } handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return); + + // for both the generic and the indexed bindings + buffer.increment_attached_counter(); buffer.increment_attached_counter(); } @@ -3190,11 +3402,15 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { index, buffer.map(|b| b.id()), )); - if let Some(old) = slot.get() { - old.decrement_attached_counter(); - } - slot.set(buffer); + for slot in &[&generic_slot, &indexed_binding.buffer] { + if let Some(old) = slot.get() { + old.decrement_attached_counter(); + } + slot.set(buffer); + } + indexed_binding.start.set(0); + indexed_binding.size.set(0); } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 @@ -3206,20 +3422,21 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { offset: i64, size: i64, ) { - let (bind_limit, slot) = match target { + let (generic_slot, indexed_bindings) = match target { constants::TRANSFORM_FEEDBACK_BUFFER => ( - self.base.limits().max_transform_feedback_separate_attribs, &self.bound_transform_feedback_buffer, + &self.indexed_transform_feedback_buffer_bindings, ), constants::UNIFORM_BUFFER => ( - self.base.limits().max_uniform_buffer_bindings, &self.bound_uniform_buffer, + &self.indexed_uniform_buffer_bindings, ), _ => return self.base.webgl_error(InvalidEnum), }; - if index >= bind_limit { - return self.base.webgl_error(InvalidValue); - } + let indexed_binding = match indexed_bindings.get(index as usize) { + Some(slot) => slot, + None => return self.base.webgl_error(InvalidValue), + }; if offset < 0 || size < 0 { return self.base.webgl_error(InvalidValue); @@ -3250,6 +3467,9 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { return self.base.webgl_error(InvalidOperation); } handle_potential_webgl_error!(self.base, buffer.set_target_maybe(target), return); + + // for both the generic and the indexed bindings + buffer.increment_attached_counter(); buffer.increment_attached_counter(); } @@ -3260,11 +3480,15 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { offset, size, )); - if let Some(old) = slot.get() { - old.decrement_attached_counter(); - } - slot.set(buffer); + for slot in &[&generic_slot, &indexed_binding.buffer] { + if let Some(old) = slot.get() { + old.decrement_attached_counter(); + } + slot.set(buffer); + } + indexed_binding.start.set(offset); + indexed_binding.size.set(size); } /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.16 @@ -3497,6 +3721,169 @@ impl WebGL2RenderingContextMethods for WebGL2RenderingContext { stencil, )); } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + fn InvalidateFramebuffer(&self, target: u32, attachments: Vec<u32>) { + if !self.valid_fb_attachment_values(target, &attachments) { + return; + } + + self.base + .send_command(WebGLCommand::InvalidateFramebuffer(target, attachments)) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + fn InvalidateSubFramebuffer( + &self, + target: u32, + attachments: Vec<u32>, + x: i32, + y: i32, + width: i32, + height: i32, + ) { + if !self.valid_fb_attachment_values(target, &attachments) { + return; + } + + if width < 0 || height < 0 { + return; + } + + self.base + .send_command(WebGLCommand::InvalidateSubFramebuffer( + target, + attachments, + x, + y, + width, + height, + )) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + fn FramebufferTextureLayer( + &self, + target: u32, + attachment: u32, + texture: Option<&WebGLTexture>, + level: i32, + layer: i32, + ) { + if let Some(tex) = texture { + handle_potential_webgl_error!(self.base, self.base.validate_ownership(tex), return); + } + + let fb_slot = match target { + constants::FRAMEBUFFER | constants::DRAW_FRAMEBUFFER => { + self.base.get_draw_framebuffer_slot() + }, + constants::READ_FRAMEBUFFER => self.base.get_read_framebuffer_slot(), + _ => return self.base.webgl_error(InvalidEnum), + }; + + match fb_slot.get() { + Some(fb) => handle_potential_webgl_error!( + self.base, + fb.texture_layer(attachment, texture, level, layer) + ), + None => self.base.webgl_error(InvalidOperation), + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5 + #[allow(unsafe_code)] + fn GetInternalformatParameter( + &self, + cx: JSContext, + target: u32, + internal_format: u32, + pname: u32, + ) -> JSVal { + if target != constants::RENDERBUFFER { + self.base.webgl_error(InvalidEnum); + return NullValue(); + } + + match handle_potential_webgl_error!( + self.base, + InternalFormatParameter::from_u32(pname), + return NullValue() + ) { + InternalFormatParameter::IntVec(param) => unsafe { + let (sender, receiver) = webgl_channel().unwrap(); + self.base + .send_command(WebGLCommand::GetInternalFormatIntVec( + target, + internal_format, + param, + sender, + )); + + rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>()); + let _ = Int32Array::create( + *cx, + CreateWith::Slice(&receiver.recv().unwrap()), + rval.handle_mut(), + ) + .unwrap(); + ObjectValue(rval.get()) + }, + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.5 + fn RenderbufferStorageMultisample( + &self, + target: u32, + samples: i32, + internal_format: u32, + width: i32, + height: i32, + ) { + self.base + .renderbuffer_storage(target, samples, internal_format, width, height) + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.4 + fn ReadBuffer(&self, src: u32) { + match src { + constants::BACK | constants::NONE => {}, + _ if self.base.valid_color_attachment_enum(src) => {}, + _ => return self.base.webgl_error(InvalidEnum), + } + + if let Some(fb) = self.base.get_read_framebuffer_slot().get() { + handle_potential_webgl_error!(self.base, fb.set_read_buffer(src), return) + } else { + match src { + constants::NONE | constants::BACK => {}, + _ => return self.base.webgl_error(InvalidOperation), + } + + self.default_fb_readbuffer.set(src); + self.base.send_command(WebGLCommand::ReadBuffer(src)); + } + } + + /// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.11 + fn DrawBuffers(&self, buffers: Vec<u32>) { + if let Some(fb) = self.base.get_draw_framebuffer_slot().get() { + handle_potential_webgl_error!(self.base, fb.set_draw_buffers(buffers), return) + } else { + if buffers.len() != 1 { + return self.base.webgl_error(InvalidOperation); + } + + match buffers[0] { + constants::NONE | constants::BACK => {}, + _ => return self.base.webgl_error(InvalidOperation), + } + + self.default_fb_drawbuffer.set(buffers[0]); + self.base.send_command(WebGLCommand::DrawBuffers(buffers)); + } + } } impl LayoutCanvasWebGLRenderingContextHelpers for LayoutDom<WebGL2RenderingContext> { diff --git a/components/script/dom/webglframebuffer.rs b/components/script/dom/webglframebuffer.rs index c2eb10fcd60..f3f6e939f4f 100644 --- a/components/script/dom/webglframebuffer.rs +++ b/components/script/dom/webglframebuffer.rs @@ -101,6 +101,8 @@ pub struct WebGLFramebuffer { depth: DomRefCell<Option<WebGLFramebufferAttachment>>, stencil: DomRefCell<Option<WebGLFramebufferAttachment>>, depthstencil: DomRefCell<Option<WebGLFramebufferAttachment>>, + color_read_buffer: DomRefCell<u32>, + color_draw_buffers: DomRefCell<Vec<u32>>, is_initialized: Cell<bool>, // Framebuffers for XR keep a reference to the XR session. // https://github.com/immersive-web/webxr/issues/856 @@ -121,6 +123,8 @@ impl WebGLFramebuffer { depth: DomRefCell::new(None), stencil: DomRefCell::new(None), depthstencil: DomRefCell::new(None), + color_read_buffer: DomRefCell::new(constants::COLOR_ATTACHMENT0), + color_draw_buffers: DomRefCell::new(vec![constants::COLOR_ATTACHMENT0]), is_initialized: Cell::new(false), xr_session: Default::default(), } @@ -142,9 +146,11 @@ impl WebGLFramebuffer { size: Size2D<i32, Viewport>, ) -> Option<(WebXRSwapChainId, DomRoot<Self>)> { let (sender, receiver) = webgl_channel().unwrap(); - let _ = context - .webgl_sender() - .send_create_webxr_swap_chain(size.to_untyped(), sender); + let _ = context.webgl_sender().send_create_webxr_swap_chain( + size.to_untyped(), + sender, + session.session_id(), + ); let swap_chain_id = receiver.recv().unwrap()?; let framebuffer_id = WebGLFramebufferId::Opaque(WebGLOpaqueFramebufferId::WebXR(swap_chain_id)); @@ -219,10 +225,10 @@ impl WebGLFramebuffer { self.size.get() } - fn check_attachment_constraints( + fn check_attachment_constraints<'a>( &self, attachment: &Option<WebGLFramebufferAttachment>, - constraints: &[u32], + mut constraints: impl Iterator<Item = &'a u32>, fb_size: &mut Option<(i32, i32)>, ) -> Result<(), u32> { // Get the size of this attachment. @@ -254,7 +260,7 @@ impl WebGLFramebuffer { } if let Some(format) = format { - if constraints.iter().all(|c| *c != format) { + if constraints.all(|c| *c != format) { return Err(constants::FRAMEBUFFER_INCOMPLETE_ATTACHMENT); } } @@ -269,22 +275,6 @@ impl WebGLFramebuffer { let has_z = z.is_some(); let has_s = s.is_some(); let has_zs = zs.is_some(); - let attachments = [&*z, &*s, &*zs]; - let attachment_constraints = [ - &[ - constants::DEPTH_COMPONENT16, - constants::DEPTH_COMPONENT24, - constants::DEPTH_COMPONENT32F, - constants::DEPTH24_STENCIL8, - constants::DEPTH32F_STENCIL8, - ][..], - &[ - constants::STENCIL_INDEX8, - constants::DEPTH24_STENCIL8, - constants::DEPTH32F_STENCIL8, - ][..], - &[constants::DEPTH_STENCIL][..], - ]; let is_supported = match self.webgl_version { // From the WebGL 1.0 spec, 6.6 ("Framebuffer Object Attachments"): @@ -326,7 +316,38 @@ impl WebGLFramebuffer { let mut fb_size = None; - for (attachment, constraints) in attachments.iter().zip(&attachment_constraints) { + let attachments = [&*z, &*s, &*zs]; + let webgl1_attachment_constraints = &[ + &[ + constants::DEPTH_COMPONENT16, + constants::DEPTH_COMPONENT24, + constants::DEPTH_COMPONENT32F, + constants::DEPTH24_STENCIL8, + constants::DEPTH32F_STENCIL8, + ][..], + &[ + constants::STENCIL_INDEX8, + constants::DEPTH24_STENCIL8, + constants::DEPTH32F_STENCIL8, + ][..], + &[constants::DEPTH_STENCIL][..], + ]; + let webgl2_attachment_constraints = &[ + &[constants::DEPTH_STENCIL][..], + &[constants::DEPTH_STENCIL][..], + &[][..], + ]; + let empty_attachment_constrains = &[&[][..], &[][..], &[][..]]; + let extra_attachment_constraints = match self.webgl_version { + WebGLVersion::WebGL1 => empty_attachment_constrains, + WebGLVersion::WebGL2 => webgl2_attachment_constraints, + }; + let attachment_constraints = webgl1_attachment_constraints + .iter() + .zip(extra_attachment_constraints.iter()) + .map(|(a, b)| a.iter().chain(b.iter())); + + for (attachment, constraints) in attachments.iter().zip(attachment_constraints) { if let Err(errnum) = self.check_attachment_constraints(attachment, constraints, &mut fb_size) { @@ -334,18 +355,79 @@ impl WebGLFramebuffer { } } - let color_constraints = &[ - constants::RGBA4, - constants::RGB5_A1, + let webgl1_color_constraints = &[ + constants::RGB, constants::RGB565, + constants::RGB5_A1, constants::RGBA, - constants::RGB, + constants::RGBA4, ][..]; + let webgl2_color_constraints = &[ + constants::ALPHA, + constants::LUMINANCE, + constants::LUMINANCE_ALPHA, + constants::R11F_G11F_B10F, + constants::R16F, + constants::R16I, + constants::R16UI, + constants::R32F, + constants::R32I, + constants::R32UI, + constants::R8, + constants::R8_SNORM, + constants::R8I, + constants::R8UI, + constants::RG16F, + constants::RG16I, + constants::RG16UI, + constants::RG32F, + constants::RG32I, + constants::RG32UI, + constants::RG8, + constants::RG8_SNORM, + constants::RG8I, + constants::RG8UI, + constants::RGB10_A2, + constants::RGB10_A2UI, + constants::RGB16F, + constants::RGB16I, + constants::RGB16UI, + constants::RGB32F, + constants::RGB32I, + constants::RGB32UI, + constants::RGB8, + constants::RGB8_SNORM, + constants::RGB8I, + constants::RGB8UI, + constants::RGB9_E5, + constants::RGBA16F, + constants::RGBA16I, + constants::RGBA16UI, + constants::RGBA32F, + constants::RGBA32I, + constants::RGBA32UI, + constants::RGBA8, + constants::RGBA8_SNORM, + constants::RGBA8I, + constants::RGBA8UI, + constants::SRGB8, + constants::SRGB8_ALPHA8, + ][..]; + let empty_color_constrains = &[][..]; + let extra_color_constraints = match self.webgl_version { + WebGLVersion::WebGL1 => empty_color_constrains, + WebGLVersion::WebGL2 => webgl2_color_constraints, + }; + let color_constraints = webgl1_color_constraints + .iter() + .chain(extra_color_constraints.iter()); + let has_c = self.colors.iter().any(|att| att.borrow().is_some()); for attachment in self.colors.iter() { let attachment = attachment.borrow(); + let constraints = color_constraints.clone(); if let Err(errnum) = - self.check_attachment_constraints(&*attachment, color_constraints, &mut fb_size) + self.check_attachment_constraints(&*attachment, constraints, &mut fb_size) { return self.status.set(errnum); } @@ -392,7 +474,7 @@ impl WebGLFramebuffer { return CompleteForRendering::Complete; } - if self.colors.iter().any(|att| att.borrow().is_none()) { + if self.colors.iter().all(|att| att.borrow().is_none()) { return CompleteForRendering::MissingColorAttachment; } @@ -651,6 +733,61 @@ impl WebGLFramebuffer { Ok(()) } + pub fn texture_layer( + &self, + attachment: u32, + texture: Option<&WebGLTexture>, + level: i32, + layer: i32, + ) -> WebGLResult<()> { + let binding = self + .attachment_binding(attachment) + .ok_or(WebGLError::InvalidEnum)?; + + let context = self.upcast::<WebGLObject>().context(); + + let tex_id = match texture { + Some(texture) => { + let (max_level, max_layer) = match texture.target() { + Some(constants::TEXTURE_3D) => ( + log2(context.limits().max_3d_texture_size), + context.limits().max_3d_texture_size - 1, + ), + Some(constants::TEXTURE_2D) => ( + log2(context.limits().max_tex_size), + context.limits().max_array_texture_layers - 1, + ), + _ => return Err(WebGLError::InvalidOperation), + }; + + if level < 0 || level as u32 >= max_level { + return Err(WebGLError::InvalidValue); + } + if layer < 0 || layer as u32 >= max_layer { + return Err(WebGLError::InvalidValue); + } + + *binding.borrow_mut() = Some(WebGLFramebufferAttachment::Texture { + texture: Dom::from_ref(texture), + level: level, + }); + texture.attach_to_framebuffer(self); + + Some(texture.id()) + }, + _ => None, + }; + + context.send_command(WebGLCommand::FramebufferTextureLayer( + self.target.get().unwrap(), + attachment, + tex_id, + level, + layer, + )); + Ok(()) + } + fn with_matching_renderbuffers<F>(&self, rb: &WebGLRenderbuffer, mut closure: F) where F: FnMut(&DomRefCell<Option<WebGLFramebufferAttachment>>, u32), @@ -781,6 +918,55 @@ impl WebGLFramebuffer { }); } + pub fn set_read_buffer(&self, buffer: u32) -> WebGLResult<()> { + let context = self.upcast::<WebGLObject>().context(); + + match buffer { + constants::NONE => {}, + _ if context.valid_color_attachment_enum(buffer) => {}, + _ => return Err(WebGLError::InvalidOperation), + }; + + *self.color_read_buffer.borrow_mut() = buffer; + context.send_command(WebGLCommand::ReadBuffer(buffer)); + Ok(()) + } + + pub fn set_draw_buffers(&self, buffers: Vec<u32>) -> WebGLResult<()> { + let context = self.upcast::<WebGLObject>().context(); + + if buffers.len() > context.limits().max_draw_buffers as usize { + return Err(WebGLError::InvalidValue); + } + + let enums_valid = buffers + .iter() + .all(|&val| val == constants::NONE || context.valid_color_attachment_enum(val)); + if !enums_valid { + return Err(WebGLError::InvalidEnum); + } + + let values_valid = buffers.iter().enumerate().all(|(i, &val)| { + val == constants::NONE || val == (constants::COLOR_ATTACHMENT0 + i as u32) + }); + if !values_valid { + return Err(WebGLError::InvalidOperation); + } + + *self.color_draw_buffers.borrow_mut() = buffers.clone(); + context.send_command(WebGLCommand::DrawBuffers(buffers)); + Ok(()) + } + + pub fn read_buffer(&self) -> u32 { + *self.color_read_buffer.borrow() + } + + pub fn draw_buffer_i(&self, index: usize) -> u32 { + let buffers = &*self.color_draw_buffers.borrow(); + *buffers.get(index).unwrap_or(&constants::NONE) + } + pub fn target(&self) -> Option<u32> { self.target.get() } diff --git a/components/script/dom/webglprogram.rs b/components/script/dom/webglprogram.rs index 556573aae2e..4bb59fb1428 100644 --- a/components/script/dom/webglprogram.rs +++ b/components/script/dom/webglprogram.rs @@ -365,6 +365,30 @@ impl WebGLProgram { Ok(location) } + /// glGetFragDataLocation + pub fn get_frag_data_location(&self, name: DOMString) -> WebGLResult<i32> { + if !self.is_linked() || self.is_deleted() { + return Err(WebGLError::InvalidOperation); + } + + if !validate_glsl_name(&name)? { + return Ok(-1); + } + if name.starts_with("gl_") { + return Ok(-1); + } + + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>() + .context() + .send_command(WebGLCommand::GetFragDataLocation( + self.id, + name.into(), + sender, + )); + Ok(receiver.recv().unwrap()) + } + /// glGetUniformLocation pub fn get_uniform_location( &self, diff --git a/components/script/dom/webglrenderbuffer.rs b/components/script/dom/webglrenderbuffer.rs index 9c173d20241..71e56bac1d3 100644 --- a/components/script/dom/webglrenderbuffer.rs +++ b/components/script/dom/webglrenderbuffer.rs @@ -14,7 +14,8 @@ use crate::dom::webglframebuffer::WebGLFramebuffer; use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderingcontext::WebGLRenderingContext; use canvas_traits::webgl::{ - webgl_channel, GlType, WebGLCommand, WebGLError, WebGLRenderbufferId, WebGLResult, WebGLVersion, + webgl_channel, GlType, InternalFormatIntVec, WebGLCommand, WebGLError, WebGLRenderbufferId, + WebGLResult, WebGLVersion, }; use dom_struct::dom_struct; use std::cell::Cell; @@ -133,11 +134,13 @@ impl WebGLRenderbuffer { pub fn storage( &self, api_type: GlType, + sample_count: i32, internal_format: u32, width: i32, height: i32, ) -> WebGLResult<()> { let is_gles = api_type == GlType::Gles; + let webgl_version = self.upcast().context().webgl_version(); // Validate the internal_format, and save it for completeness // validation. @@ -145,10 +148,35 @@ impl WebGLRenderbuffer { constants::RGBA4 | constants::DEPTH_COMPONENT16 | constants::STENCIL_INDEX8 => { internal_format }, + constants::R8 | + constants::R8UI | + constants::R8I | + constants::R16UI | + constants::R16I | + constants::R32UI | + constants::R32I | + constants::RG8 | + constants::RG8UI | + constants::RG8I | + constants::RG16UI | + constants::RG16I | + constants::RG32UI | + constants::RG32I | + constants::RGB8 | + constants::RGBA8 | + constants::SRGB8_ALPHA8 | + constants::RGB10_A2 | + constants::RGBA8UI | + constants::RGBA8I | + constants::RGB10_A2UI | + constants::RGBA16UI | + constants::RGBA16I | + constants::RGBA32I | + constants::RGBA32UI | constants::DEPTH_COMPONENT24 | constants::DEPTH_COMPONENT32F | constants::DEPTH24_STENCIL8 | - constants::DEPTH32F_STENCIL8 => match self.upcast().context().webgl_version() { + constants::DEPTH32F_STENCIL8 => match webgl_version { WebGLVersion::WebGL1 => return Err(WebGLError::InvalidEnum), _ => internal_format, }, @@ -196,6 +224,22 @@ impl WebGLRenderbuffer { _ => return Err(WebGLError::InvalidEnum), }; + if webgl_version != WebGLVersion::WebGL1 { + let (sender, receiver) = webgl_channel().unwrap(); + self.upcast::<WebGLObject>().context().send_command( + WebGLCommand::GetInternalFormatIntVec( + constants::RENDERBUFFER, + internal_format, + InternalFormatIntVec::Samples, + sender, + ), + ); + let samples = receiver.recv().unwrap(); + if sample_count < 0 || sample_count as usize > samples.len() { + return Err(WebGLError::InvalidOperation); + } + } + self.internal_format.set(Some(internal_format)); self.is_initialized.set(false); @@ -203,17 +247,24 @@ impl WebGLRenderbuffer { fb.update_status(); } - self.upcast::<WebGLObject>() - .context() - .send_command(WebGLCommand::RenderbufferStorage( + let command = match sample_count { + 0 => WebGLCommand::RenderbufferStorage( constants::RENDERBUFFER, actual_format, width, height, - )); + ), + _ => WebGLCommand::RenderbufferStorageMultisample( + constants::RENDERBUFFER, + sample_count, + actual_format, + width, + height, + ), + }; + self.upcast::<WebGLObject>().context().send_command(command); self.size.set(Some((width, height))); - Ok(()) } diff --git a/components/script/dom/webglrenderingcontext.rs b/components/script/dom/webglrenderingcontext.rs index ca5c08b25e3..25e5bf4baa1 100644 --- a/components/script/dom/webglrenderingcontext.rs +++ b/components/script/dom/webglrenderingcontext.rs @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use crate::dom::bindings::cell::Ref; use crate::dom::bindings::codegen::Bindings::ANGLEInstancedArraysBinding::ANGLEInstancedArraysConstants; use crate::dom::bindings::codegen::Bindings::EXTBlendMinmaxBinding::EXTBlendMinmaxConstants; use crate::dom::bindings::codegen::Bindings::OESVertexArrayObjectBinding::OESVertexArrayObjectConstants; @@ -26,6 +27,7 @@ use crate::dom::htmlcanvaselement::HTMLCanvasElement; use crate::dom::htmliframeelement::HTMLIFrameElement; use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage}; use crate::dom::promise::Promise; +use crate::dom::vertexarrayobject::VertexAttribData; use crate::dom::webgl_extensions::WebGLExtensions; use crate::dom::webgl_validations::tex_image_2d::{ CommonCompressedTexImage2DValidatorResult, CommonTexImage2DValidator, @@ -47,6 +49,7 @@ use crate::dom::webglshader::WebGLShader; use crate::dom::webglshaderprecisionformat::WebGLShaderPrecisionFormat; use crate::dom::webgltexture::{TexParameterValue, WebGLTexture}; use crate::dom::webgluniformlocation::WebGLUniformLocation; +use crate::dom::webglvertexarrayobject::WebGLVertexArrayObject; use crate::dom::webglvertexarrayobjectoes::WebGLVertexArrayObjectOES; use crate::dom::window::Window; use crate::script_runtime::JSContext as SafeJSContext; @@ -81,6 +84,7 @@ use std::cell::Cell; use std::cmp; use std::ptr::{self, NonNull}; use std::rc::Rc; +use webxr_api::SessionId; use webxr_api::SwapChainId as WebXRSwapChainId; // From the GLES 2.0.25 spec, page 85: @@ -182,6 +186,8 @@ pub struct WebGLRenderingContext { capabilities: Capabilities, default_vao: DomOnceCell<WebGLVertexArrayObjectOES>, current_vao: MutNullableDom<WebGLVertexArrayObjectOES>, + default_vao_webgl2: DomOnceCell<WebGLVertexArrayObject>, + current_vao_webgl2: MutNullableDom<WebGLVertexArrayObject>, textures: Textures, api_type: GlType, } @@ -241,6 +247,8 @@ impl WebGLRenderingContext { capabilities: Default::default(), default_vao: Default::default(), current_vao: Default::default(), + default_vao_webgl2: Default::default(), + current_vao_webgl2: Default::default(), textures: Textures::new(max_combined_texture_image_units), api_type: ctx_data.api_type, } @@ -293,6 +301,15 @@ impl WebGLRenderingContext { }) } + pub fn current_vao_webgl2(&self) -> DomRoot<WebGLVertexArrayObject> { + self.current_vao_webgl2.or_init(|| { + DomRoot::from_ref( + self.default_vao_webgl2 + .init_once(|| WebGLVertexArrayObject::new(self, None)), + ) + }) + } + pub fn recreate(&self, size: Size2D<u32>) { let (sender, receiver) = webgl_channel().unwrap(); self.webgl_sender.send_resize(size, sender).unwrap(); @@ -888,11 +905,18 @@ impl WebGLRenderingContext { 0 }; - self.current_vao().validate_for_draw( - required_len, - primcount as u32, - ¤t_program.active_attribs(), - )?; + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().validate_for_draw( + required_len, + primcount as u32, + ¤t_program.active_attribs(), + )?, + WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw( + required_len, + primcount as u32, + ¤t_program.active_attribs(), + )?, + }; self.validate_framebuffer()?; @@ -949,11 +973,11 @@ impl WebGLRenderingContext { } let current_program = self.current_program.get().ok_or(InvalidOperation)?; - let array_buffer = self - .current_vao() - .element_array_buffer() - .get() - .ok_or(InvalidOperation)?; + let array_buffer = match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().element_array_buffer().get(), + WebGLVersion::WebGL2 => self.current_vao_webgl2().element_array_buffer().get(), + } + .ok_or(InvalidOperation)?; if count > 0 && primcount > 0 { // This operation cannot overflow in u64 and we know all those values are nonnegative. @@ -964,11 +988,18 @@ impl WebGLRenderingContext { } // TODO(nox): Pass the correct number of vertices required. - self.current_vao().validate_for_draw( - 0, - primcount as u32, - ¤t_program.active_attribs(), - )?; + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().validate_for_draw( + 0, + primcount as u32, + ¤t_program.active_attribs(), + )?, + WebGLVersion::WebGL2 => self.current_vao_webgl2().validate_for_draw( + 0, + primcount as u32, + ¤t_program.active_attribs(), + )?, + }; self.validate_framebuffer()?; @@ -1002,7 +1033,12 @@ impl WebGLRenderingContext { return self.webgl_error(InvalidValue); } - self.current_vao().vertex_attrib_divisor(index, divisor); + match self.webgl_version() { + WebGLVersion::WebGL1 => self.current_vao().vertex_attrib_divisor(index, divisor), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .vertex_attrib_divisor(index, divisor), + }; self.send_command(WebGLCommand::VertexAttribDivisor { index, divisor }); } @@ -1065,6 +1101,15 @@ impl WebGLRenderingContext { .map(|id| WebGLVertexArrayObjectOES::new(self, Some(id))) } + pub fn create_vertex_array_webgl2(&self) -> Option<DomRoot<WebGLVertexArrayObject>> { + let (sender, receiver) = webgl_channel().unwrap(); + self.send_command(WebGLCommand::CreateVertexArray(sender)); + receiver + .recv() + .unwrap() + .map(|id| WebGLVertexArrayObject::new(self, Some(id))) + } + pub fn delete_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) { if let Some(vao) = vao { handle_potential_webgl_error!(self, self.validate_ownership(vao), return); @@ -1083,6 +1128,24 @@ impl WebGLRenderingContext { } } + pub fn delete_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) { + if let Some(vao) = vao { + handle_potential_webgl_error!(self, self.validate_ownership(vao), return); + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + if vao.is_deleted() { + return; + } + if vao == &*self.current_vao_webgl2() { + // Setting it to None will make self.current_vao() reset it to the default one + // next time it is called. + self.current_vao_webgl2.set(None); + self.send_command(WebGLCommand::BindVertexArray(None)); + } + vao.delete(false); + } + } + pub fn is_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) -> bool { vao.map_or(false, |vao| { // The default vertex array has no id and should never be passed around. @@ -1091,6 +1154,14 @@ impl WebGLRenderingContext { }) } + pub fn is_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) -> bool { + vao.map_or(false, |vao| { + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + self.validate_ownership(vao).is_ok() && vao.ever_bound() && !vao.is_deleted() + }) + } + pub fn bind_vertex_array(&self, vao: Option<&WebGLVertexArrayObjectOES>) { if let Some(vao) = vao { // The default vertex array has no id and should never be passed around. @@ -1107,6 +1178,22 @@ impl WebGLRenderingContext { self.current_vao.set(vao); } + pub fn bind_vertex_array_webgl2(&self, vao: Option<&WebGLVertexArrayObject>) { + if let Some(vao) = vao { + // The default vertex array has no id and should never be passed around. + assert!(vao.id().is_some()); + handle_potential_webgl_error!(self, self.validate_ownership(vao), return); + if vao.is_deleted() { + return self.webgl_error(InvalidOperation); + } + vao.set_ever_bound(); + } + self.send_command(WebGLCommand::BindVertexArray(vao.and_then(|vao| vao.id()))); + // Setting it to None will make self.current_vao() reset it to the default one + // next time it is called. + self.current_vao_webgl2.set(vao); + } + fn validate_blend_mode(&self, mode: u32) -> WebGLResult<()> { match mode { constants::FUNC_ADD | constants::FUNC_SUBTRACT | constants::FUNC_REVERSE_SUBTRACT => { @@ -1389,6 +1476,45 @@ impl WebGLRenderingContext { } slot.set(framebuffer); } + + pub fn renderbuffer_storage( + &self, + target: u32, + samples: i32, + internal_format: u32, + width: i32, + height: i32, + ) { + if target != constants::RENDERBUFFER { + return self.webgl_error(InvalidEnum); + } + + let max = self.limits.max_renderbuffer_size; + + if samples < 0 || width < 0 || width as u32 > max || height < 0 || height as u32 > max { + return self.webgl_error(InvalidValue); + } + + let rb = handle_potential_webgl_error!( + self, + self.bound_renderbuffer.get().ok_or(InvalidOperation), + return + ); + handle_potential_webgl_error!( + self, + rb.storage(self.api_type, samples, internal_format, width, height) + ); + if let Some(fb) = self.bound_draw_framebuffer.get() { + fb.invalidate_renderbuffer(&*rb); + } + + // FIXME: https://github.com/servo/servo/issues/13710 + } + + pub fn valid_color_attachment_enum(&self, attachment: u32) -> bool { + let last_slot = constants::COLOR_ATTACHMENT0 + self.limits().max_color_attachments - 1; + constants::COLOR_ATTACHMENT0 <= attachment && attachment <= last_slot + } } #[cfg(not(feature = "webgl_backtrace"))] @@ -2551,9 +2677,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { if attrib_id >= self.limits.max_vertex_attribs { return self.webgl_error(InvalidValue); } - - self.current_vao() - .enabled_vertex_attrib_array(attrib_id, true); + match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .enabled_vertex_attrib_array(attrib_id, true), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .enabled_vertex_attrib_array(attrib_id, true), + }; self.send_command(WebGLCommand::EnableVertexAttribArray(attrib_id)); } @@ -2562,9 +2693,14 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { if attrib_id >= self.limits.max_vertex_attribs { return self.webgl_error(InvalidValue); } - - self.current_vao() - .enabled_vertex_attrib_array(attrib_id, false); + match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .enabled_vertex_attrib_array(attrib_id, false), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .enabled_vertex_attrib_array(attrib_id, false), + }; self.send_command(WebGLCommand::DisableVertexAttribArray(attrib_id)); } @@ -2889,56 +3025,73 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { #[allow(unsafe_code)] // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9 fn GetVertexAttrib(&self, cx: SafeJSContext, index: u32, param: u32) -> JSVal { - let current_vao = self.current_vao(); - let data = handle_potential_webgl_error!( - self, - current_vao.get_vertex_attrib(index).ok_or(InvalidValue), - return NullValue() - ); - if param == constants::CURRENT_VERTEX_ATTRIB { - let value = if index == 0 { - let (x, y, z, w) = self.current_vertex_attrib_0.get(); - [x, y, z, w] - } else { - let (sender, receiver) = webgl_channel().unwrap(); - self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender)); - receiver.recv().unwrap() - }; - unsafe { - rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>()); - let _ = Float32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut()) - .unwrap(); - return ObjectValue(result.get()); + let get_attrib = |data: Ref<VertexAttribData>| -> JSVal { + if param == constants::CURRENT_VERTEX_ATTRIB { + let value = if index == 0 { + let (x, y, z, w) = self.current_vertex_attrib_0.get(); + [x, y, z, w] + } else { + let (sender, receiver) = webgl_channel().unwrap(); + self.send_command(WebGLCommand::GetCurrentVertexAttrib(index, sender)); + receiver.recv().unwrap() + }; + unsafe { + rooted!(in(*cx) let mut result = ptr::null_mut::<JSObject>()); + let _ = + Float32Array::create(*cx, CreateWith::Slice(&value), result.handle_mut()) + .unwrap(); + return ObjectValue(result.get()); + } + } + if !self + .extension_manager + .is_get_vertex_attrib_name_enabled(param) + { + self.webgl_error(WebGLError::InvalidEnum); + return NullValue(); } - } - if !self - .extension_manager - .is_get_vertex_attrib_name_enabled(param) - { - self.webgl_error(WebGLError::InvalidEnum); - return NullValue(); - } + match param { + constants::VERTEX_ATTRIB_ARRAY_ENABLED => BooleanValue(data.enabled_as_array), + constants::VERTEX_ATTRIB_ARRAY_SIZE => Int32Value(data.size as i32), + constants::VERTEX_ATTRIB_ARRAY_TYPE => Int32Value(data.type_ as i32), + constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => BooleanValue(data.normalized), + constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32), + constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => unsafe { + rooted!(in(*cx) let mut jsval = NullValue()); + if let Some(buffer) = data.buffer() { + buffer.to_jsval(*cx, jsval.handle_mut()); + } + jsval.get() + }, + ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => { + UInt32Value(data.divisor) + }, + _ => { + self.webgl_error(InvalidEnum); + NullValue() + }, + } + }; - match param { - constants::VERTEX_ATTRIB_ARRAY_ENABLED => BooleanValue(data.enabled_as_array), - constants::VERTEX_ATTRIB_ARRAY_SIZE => Int32Value(data.size as i32), - constants::VERTEX_ATTRIB_ARRAY_TYPE => Int32Value(data.type_ as i32), - constants::VERTEX_ATTRIB_ARRAY_NORMALIZED => BooleanValue(data.normalized), - constants::VERTEX_ATTRIB_ARRAY_STRIDE => Int32Value(data.stride as i32), - constants::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING => unsafe { - rooted!(in(*cx) let mut jsval = NullValue()); - if let Some(buffer) = data.buffer() { - buffer.to_jsval(*cx, jsval.handle_mut()); - } - jsval.get() - }, - ANGLEInstancedArraysConstants::VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE => { - UInt32Value(data.divisor) + match self.webgl_version() { + WebGLVersion::WebGL1 => { + let current_vao = self.current_vao(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return NullValue() + ); + get_attrib(data) }, - _ => { - self.webgl_error(InvalidEnum); - NullValue() + WebGLVersion::WebGL2 => { + let current_vao = self.current_vao_webgl2(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return NullValue() + ); + get_attrib(data) }, } } @@ -2949,13 +3102,26 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { self.webgl_error(InvalidEnum); return 0; } - let vao = self.current_vao(); - let data = handle_potential_webgl_error!( - self, - vao.get_vertex_attrib(index).ok_or(InvalidValue), - return 0 - ); - data.offset as i64 + match self.webgl_version() { + WebGLVersion::WebGL1 => { + let current_vao = self.current_vao(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return 0 + ); + data.offset as i64 + }, + WebGLVersion::WebGL2 => { + let current_vao = self.current_vao_webgl2(); + let data = handle_potential_webgl_error!( + self, + current_vao.get_vertex_attrib(index).ok_or(InvalidValue), + return 0 + ); + data.offset as i64 + }, + } } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.3 @@ -3818,11 +3984,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { stride: i32, offset: i64, ) { - handle_potential_webgl_error!( - self, - self.current_vao() - .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) - ); + let res = match self.webgl_version() { + WebGLVersion::WebGL1 => self + .current_vao() + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset), + WebGLVersion::WebGL2 => self + .current_vao_webgl2() + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset), + }; + handle_potential_webgl_error!(self, res); } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.4 @@ -4205,30 +4375,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext { // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.7 fn RenderbufferStorage(&self, target: u32, internal_format: u32, width: i32, height: i32) { - if target != constants::RENDERBUFFER { - return self.webgl_error(InvalidEnum); - } - - let max = self.limits.max_renderbuffer_size; - - if width < 0 || width as u32 > max || height < 0 || height as u32 > max { - return self.webgl_error(InvalidValue); - } - - let rb = handle_potential_webgl_error!( - self, - self.bound_renderbuffer.get().ok_or(InvalidOperation), - return - ); - handle_potential_webgl_error!( - self, - rb.storage(self.api_type, internal_format, width, height) - ); - if let Some(fb) = self.bound_draw_framebuffer.get() { - fb.invalidate_renderbuffer(&*rb); - } - - // FIXME: https://github.com/servo/servo/issues/13710 + self.renderbuffer_storage(target, 0, internal_format, width, height) } // https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.6 @@ -4576,8 +4723,9 @@ impl WebGLMessageSender { &self, size: Size2D<i32>, sender: WebGLSender<Option<WebXRSwapChainId>>, + id: SessionId, ) -> WebGLSendResult { - self.wake_after_send(|| self.sender.send_create_webxr_swap_chain(size, sender)) + self.wake_after_send(|| self.sender.send_create_webxr_swap_chain(size, sender, id)) } pub fn send_resize( diff --git a/components/script/dom/webglvertexarrayobject.rs b/components/script/dom/webglvertexarrayobject.rs new file mode 100644 index 00000000000..48355d05557 --- /dev/null +++ b/components/script/dom/webglvertexarrayobject.rs @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use crate::dom::bindings::cell::Ref; +use crate::dom::bindings::codegen::Bindings::WebGLVertexArrayObjectBinding; +use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; +use crate::dom::webglbuffer::WebGLBuffer; +use crate::dom::webglobject::WebGLObject; +use crate::dom::webglrenderingcontext::WebGLRenderingContext; +use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; +use dom_struct::dom_struct; + +#[dom_struct] +pub struct WebGLVertexArrayObject { + webgl_object_: WebGLObject, + array_object: VertexArrayObject, +} + +impl WebGLVertexArrayObject { + fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { + Self { + webgl_object_: WebGLObject::new_inherited(context), + array_object: VertexArrayObject::new(context, id), + } + } + + pub fn new(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> DomRoot<Self> { + reflect_dom_object( + Box::new(WebGLVertexArrayObject::new_inherited(context, id)), + &*context.global(), + WebGLVertexArrayObjectBinding::Wrap, + ) + } + + pub fn id(&self) -> Option<WebGLVertexArrayId> { + self.array_object.id() + } + + pub fn is_deleted(&self) -> bool { + self.array_object.is_deleted() + } + + pub fn delete(&self, fallible: bool) { + self.array_object.delete(fallible); + } + + pub fn ever_bound(&self) -> bool { + self.array_object.ever_bound() + } + + pub fn set_ever_bound(&self) { + self.array_object.set_ever_bound(); + } + + pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { + self.array_object.element_array_buffer() + } + + pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { + self.array_object.get_vertex_attrib(index) + } + + pub fn vertex_attrib_pointer( + &self, + index: u32, + size: i32, + type_: u32, + normalized: bool, + stride: i32, + offset: i64, + ) -> WebGLResult<()> { + self.array_object + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) + } + + pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { + self.array_object.vertex_attrib_divisor(index, value); + } + + pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { + self.array_object.enabled_vertex_attrib_array(index, value); + } + + pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { + self.array_object.unbind_buffer(buffer); + } + + pub fn validate_for_draw( + &self, + required_len: u32, + instance_count: u32, + active_attribs: &[ActiveAttribInfo], + ) -> WebGLResult<()> { + self.array_object + .validate_for_draw(required_len, instance_count, active_attribs) + } +} diff --git a/components/script/dom/webglvertexarrayobjectoes.rs b/components/script/dom/webglvertexarrayobjectoes.rs index 1d802968ae1..2c03f084031 100644 --- a/components/script/dom/webglvertexarrayobjectoes.rs +++ b/components/script/dom/webglvertexarrayobjectoes.rs @@ -2,41 +2,28 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -use crate::dom::bindings::cell::{ref_filter_map, DomRefCell, Ref}; -use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants; +use crate::dom::bindings::cell::Ref; use crate::dom::bindings::codegen::Bindings::WebGLVertexArrayObjectOESBinding; -use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::{reflect_dom_object, DomObject}; -use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; +use crate::dom::bindings::root::{DomRoot, MutNullableDom}; +use crate::dom::vertexarrayobject::{VertexArrayObject, VertexAttribData}; use crate::dom::webglbuffer::WebGLBuffer; use crate::dom::webglobject::WebGLObject; use crate::dom::webglrenderingcontext::WebGLRenderingContext; -use canvas_traits::webgl::{ - ActiveAttribInfo, WebGLCommand, WebGLError, WebGLResult, WebGLVertexArrayId, -}; +use canvas_traits::webgl::{ActiveAttribInfo, WebGLResult, WebGLVertexArrayId}; use dom_struct::dom_struct; -use std::cell::Cell; #[dom_struct] pub struct WebGLVertexArrayObjectOES { webgl_object_: WebGLObject, - id: Option<WebGLVertexArrayId>, - ever_bound: Cell<bool>, - is_deleted: Cell<bool>, - vertex_attribs: DomRefCell<Box<[VertexAttribData]>>, - element_array_buffer: MutNullableDom<WebGLBuffer>, + array_object: VertexArrayObject, } impl WebGLVertexArrayObjectOES { fn new_inherited(context: &WebGLRenderingContext, id: Option<WebGLVertexArrayId>) -> Self { - let max_vertex_attribs = context.limits().max_vertex_attribs as usize; Self { webgl_object_: WebGLObject::new_inherited(context), - id, - ever_bound: Default::default(), - is_deleted: Default::default(), - vertex_attribs: DomRefCell::new(vec![Default::default(); max_vertex_attribs].into()), - element_array_buffer: Default::default(), + array_object: VertexArrayObject::new(context, id), } } @@ -49,54 +36,31 @@ impl WebGLVertexArrayObjectOES { } pub fn id(&self) -> Option<WebGLVertexArrayId> { - self.id + self.array_object.id() } pub fn is_deleted(&self) -> bool { - self.is_deleted.get() + self.array_object.is_deleted() } pub fn delete(&self, fallible: bool) { - assert!(self.id.is_some()); - if self.is_deleted.get() { - return; - } - self.is_deleted.set(true); - - let context = self.upcast::<WebGLObject>().context(); - let cmd = WebGLCommand::DeleteVertexArray(self.id.unwrap()); - if fallible { - context.send_command_ignored(cmd); - } else { - context.send_command(cmd); - } - - for attrib_data in &**self.vertex_attribs.borrow() { - if let Some(buffer) = attrib_data.buffer() { - buffer.decrement_attached_counter(); - } - } - if let Some(buffer) = self.element_array_buffer.get() { - buffer.decrement_attached_counter(); - } + self.array_object.delete(fallible); } pub fn ever_bound(&self) -> bool { - return self.ever_bound.get(); + self.array_object.ever_bound() } pub fn set_ever_bound(&self) { - self.ever_bound.set(true); + self.array_object.set_ever_bound(); } pub fn element_array_buffer(&self) -> &MutNullableDom<WebGLBuffer> { - &self.element_array_buffer + self.array_object.element_array_buffer() } pub fn get_vertex_attrib(&self, index: u32) -> Option<Ref<VertexAttribData>> { - ref_filter_map(self.vertex_attribs.borrow(), |attribs| { - attribs.get(index as usize) - }) + self.array_object.get_vertex_attrib(index) } pub fn vertex_attrib_pointer( @@ -108,93 +72,20 @@ impl WebGLVertexArrayObjectOES { stride: i32, offset: i64, ) -> WebGLResult<()> { - let mut attribs = self.vertex_attribs.borrow_mut(); - let data = attribs - .get_mut(index as usize) - .ok_or(WebGLError::InvalidValue)?; - - if size < 1 || size > 4 { - return Err(WebGLError::InvalidValue); - } - - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#BUFFER_OFFSET_AND_STRIDE - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#VERTEX_STRIDE - if stride < 0 || stride > 255 || offset < 0 { - return Err(WebGLError::InvalidValue); - } - let bytes_per_component: i32 = match type_ { - constants::BYTE | constants::UNSIGNED_BYTE => 1, - constants::SHORT | constants::UNSIGNED_SHORT => 2, - constants::FLOAT => 4, - _ => return Err(WebGLError::InvalidEnum), - }; - if offset % bytes_per_component as i64 > 0 || stride % bytes_per_component > 0 { - return Err(WebGLError::InvalidOperation); - } - - let context = self.upcast::<WebGLObject>().context(); - let buffer = context.array_buffer(); - match buffer { - Some(ref buffer) => buffer.increment_attached_counter(), - None if offset != 0 => { - // https://github.com/KhronosGroup/WebGL/pull/2228 - return Err(WebGLError::InvalidOperation); - }, - _ => {}, - } - context.send_command(WebGLCommand::VertexAttribPointer( - index, - size, - type_, - normalized, - stride, - offset as u32, - )); - if let Some(old) = data.buffer() { - old.decrement_attached_counter(); - } - - *data = VertexAttribData { - enabled_as_array: data.enabled_as_array, - size: size as u8, - type_, - bytes_per_vertex: size as u8 * bytes_per_component as u8, - normalized, - stride: stride as u8, - offset: offset as u32, - buffer: buffer.map(|b| Dom::from_ref(&*b)), - divisor: data.divisor, - }; - - Ok(()) + self.array_object + .vertex_attrib_pointer(index, size, type_, normalized, stride, offset) } pub fn vertex_attrib_divisor(&self, index: u32, value: u32) { - self.vertex_attribs.borrow_mut()[index as usize].divisor = value; + self.array_object.vertex_attrib_divisor(index, value); } pub fn enabled_vertex_attrib_array(&self, index: u32, value: bool) { - self.vertex_attribs.borrow_mut()[index as usize].enabled_as_array = value; + self.array_object.enabled_vertex_attrib_array(index, value); } pub fn unbind_buffer(&self, buffer: &WebGLBuffer) { - for attrib in &mut **self.vertex_attribs.borrow_mut() { - if let Some(b) = attrib.buffer() { - if b.id() != buffer.id() { - continue; - } - b.decrement_attached_counter(); - } - attrib.buffer = None; - } - if self - .element_array_buffer - .get() - .map_or(false, |b| buffer == &*b) - { - buffer.decrement_attached_counter(); - self.element_array_buffer.set(None); - } + self.array_object.unbind_buffer(buffer); } pub fn validate_for_draw( @@ -203,109 +94,7 @@ impl WebGLVertexArrayObjectOES { instance_count: u32, active_attribs: &[ActiveAttribInfo], ) -> WebGLResult<()> { - // TODO(nox): Cache limits per VAO. - let attribs = self.vertex_attribs.borrow(); - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.2 - if attribs - .iter() - .any(|data| data.enabled_as_array && data.buffer.is_none()) - { - return Err(WebGLError::InvalidOperation); - } - let mut has_active_attrib = false; - let mut has_divisor_0 = false; - for active_info in active_attribs { - if active_info.location < 0 { - continue; - } - has_active_attrib = true; - let attrib = &attribs[active_info.location as usize]; - if attrib.divisor == 0 { - has_divisor_0 = true; - } - if !attrib.enabled_as_array { - continue; - } - // https://www.khronos.org/registry/webgl/specs/latest/1.0/#6.6 - if required_len > 0 && instance_count > 0 { - let max_vertices = attrib.max_vertices(); - if attrib.divisor == 0 { - if max_vertices < required_len { - return Err(WebGLError::InvalidOperation); - } - } else if max_vertices - .checked_mul(attrib.divisor) - .map_or(false, |v| v < instance_count) - { - return Err(WebGLError::InvalidOperation); - } - } - } - if has_active_attrib && !has_divisor_0 { - return Err(WebGLError::InvalidOperation); - } - Ok(()) - } -} - -impl Drop for WebGLVertexArrayObjectOES { - fn drop(&mut self) { - if self.id.is_some() { - self.delete(true); - } - } -} - -#[derive(Clone, JSTraceable, MallocSizeOf)] -#[unrooted_must_root_lint::must_root] -pub struct VertexAttribData { - pub enabled_as_array: bool, - pub size: u8, - pub type_: u32, - bytes_per_vertex: u8, - pub normalized: bool, - pub stride: u8, - pub offset: u32, - pub buffer: Option<Dom<WebGLBuffer>>, - pub divisor: u32, -} - -impl Default for VertexAttribData { - #[allow(unrooted_must_root)] - fn default() -> Self { - Self { - enabled_as_array: false, - size: 4, - type_: constants::FLOAT, - bytes_per_vertex: 16, - normalized: false, - stride: 0, - offset: 0, - buffer: None, - divisor: 0, - } - } -} - -impl VertexAttribData { - pub fn buffer(&self) -> Option<&WebGLBuffer> { - self.buffer.as_ref().map(|b| &**b) - } - - fn max_vertices(&self) -> u32 { - let capacity = (self.buffer().unwrap().capacity() as u32).saturating_sub(self.offset); - if capacity < self.bytes_per_vertex as u32 { - 0 - } else if self.stride == 0 { - capacity / self.bytes_per_vertex as u32 - } else if self.stride < self.bytes_per_vertex { - (capacity - (self.bytes_per_vertex - self.stride) as u32) / self.stride as u32 - } else { - let mut max = capacity / self.stride as u32; - if capacity % self.stride as u32 >= self.bytes_per_vertex as u32 { - max += 1; - } - max - } + self.array_object + .validate_for_draw(required_len, instance_count, active_attribs) } } diff --git a/components/script/dom/webidls/BroadcastChannel.webidl b/components/script/dom/webidls/BroadcastChannel.webidl new file mode 100644 index 00000000000..6d72f3997cf --- /dev/null +++ b/components/script/dom/webidls/BroadcastChannel.webidl @@ -0,0 +1,18 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* + * The origin of this IDL file is: + * https://html.spec.whatwg.org/multipage/#broadcastchannel + */ + +[Exposed=(Window,Worker)] +interface BroadcastChannel : EventTarget { + constructor(DOMString name); + + readonly attribute DOMString name; + [Throws] void postMessage(any message); + void close(); + attribute EventHandler onmessage; + attribute EventHandler onmessageerror; +}; diff --git a/components/script/dom/webidls/GPUBuffer.webidl b/components/script/dom/webidls/GPUBuffer.webidl index 0b327bdcbd2..1688060b514 100644 --- a/components/script/dom/webidls/GPUBuffer.webidl +++ b/components/script/dom/webidls/GPUBuffer.webidl @@ -5,7 +5,7 @@ // https://gpuweb.github.io/gpuweb/#gpubuffer [Exposed=(Window, DedicatedWorker), Serializable, Pref="dom.webgpu.enabled"] interface GPUBuffer { - // Promise<ArrayBuffer> mapReadAsync(); + Promise<ArrayBuffer> mapReadAsync(); // Promise<ArrayBuffer> mapWriteAsync(); void unmap(); diff --git a/components/script/dom/webidls/HTMLElement.webidl b/components/script/dom/webidls/HTMLElement.webidl index ef529dc80af..c743c0a7129 100644 --- a/components/script/dom/webidls/HTMLElement.webidl +++ b/components/script/dom/webidls/HTMLElement.webidl @@ -14,8 +14,8 @@ interface HTMLElement : Element { attribute DOMString lang; [CEReactions] attribute boolean translate; - // [CEReactions] - // attribute DOMString dir; + [CEReactions] + attribute DOMString dir; readonly attribute DOMStringMap dataset; // microdata diff --git a/components/script/dom/webidls/HTMLTextAreaElement.webidl b/components/script/dom/webidls/HTMLTextAreaElement.webidl index 99cf18e0c10..b09200f1dc5 100644 --- a/components/script/dom/webidls/HTMLTextAreaElement.webidl +++ b/components/script/dom/webidls/HTMLTextAreaElement.webidl @@ -13,8 +13,8 @@ interface HTMLTextAreaElement : HTMLElement { // attribute boolean autofocus; [CEReactions, SetterThrows] attribute unsigned long cols; - // [CEReactions] - // attribute DOMString dirName; + [CEReactions] + attribute DOMString dirName; [CEReactions] attribute boolean disabled; readonly attribute HTMLFormElement? form; diff --git a/components/script/dom/webidls/VTTCue.webidl b/components/script/dom/webidls/VTTCue.webidl new file mode 100644 index 00000000000..073aa12f79b --- /dev/null +++ b/components/script/dom/webidls/VTTCue.webidl @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/webvtt/#the-vttcue-interface + +enum AutoKeyword { "auto"}; +typedef (double or AutoKeyword) LineAndPositionSetting; +enum DirectionSetting { "" /* horizontal */, "rl", "lr" }; +enum LineAlignSetting { "start", "center", "end" }; +enum PositionAlignSetting { "line-left", "center", "line-right", "auto" }; +enum AlignSetting { "start", "center", "end", "left", "right" }; + +[Pref="dom.webvtt.enabled", Exposed=Window] +interface VTTCue : TextTrackCue { + constructor(double startTime, double endTime, DOMString text); + attribute VTTRegion? region; + attribute DirectionSetting vertical; + attribute boolean snapToLines; + attribute LineAndPositionSetting line; + attribute LineAlignSetting lineAlign; + [SetterThrows] + attribute LineAndPositionSetting position; + attribute PositionAlignSetting positionAlign; + [SetterThrows] + attribute double size; + attribute AlignSetting align; + attribute DOMString text; + DocumentFragment getCueAsHTML(); +}; diff --git a/components/script/dom/webidls/VTTRegion.webidl b/components/script/dom/webidls/VTTRegion.webidl new file mode 100644 index 00000000000..12fbe16170b --- /dev/null +++ b/components/script/dom/webidls/VTTRegion.webidl @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +// https://w3c.github.io/webvtt/#the-vttregion-interface + +enum ScrollSetting { "" /* none */, "up"}; + +[Pref="dom.webvtt.enabled", Exposed=Window] +interface VTTRegion { + [Throws] constructor(); + attribute DOMString id; + [SetterThrows] + attribute double width; + [SetterThrows] + attribute unsigned long lines; + [SetterThrows] + attribute double regionAnchorX; + [SetterThrows] + attribute double regionAnchorY; + [SetterThrows] + attribute double viewportAnchorX; + [SetterThrows] + attribute double viewportAnchorY; + attribute ScrollSetting scroll; +}; diff --git a/components/script/dom/webidls/WebGL2RenderingContext.webidl b/components/script/dom/webidls/WebGL2RenderingContext.webidl index 23686adb211..dca86da39fe 100644 --- a/components/script/dom/webidls/WebGL2RenderingContext.webidl +++ b/components/script/dom/webidls/WebGL2RenderingContext.webidl @@ -11,10 +11,6 @@ typedef long long GLint64; typedef unsigned long long GLuint64; - -// interface WebGLVertexArrayObject : WebGLObject { -// }; - typedef (/*[AllowShared]*/ Uint32Array or sequence<GLuint>) Uint32List; interface mixin WebGL2RenderingContextBase @@ -312,17 +308,17 @@ interface mixin WebGL2RenderingContextBase /* Framebuffer objects */ // void blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, // GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); - // void framebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level, - // GLint layer); - // void invalidateFramebuffer(GLenum target, sequence<GLenum> attachments); - // void invalidateSubFramebuffer(GLenum target, sequence<GLenum> attachments, - // GLint x, GLint y, GLsizei width, GLsizei height); - // void readBuffer(GLenum src); + void framebufferTextureLayer(GLenum target, GLenum attachment, WebGLTexture? texture, GLint level, + GLint layer); + void invalidateFramebuffer(GLenum target, sequence<GLenum> attachments); + void invalidateSubFramebuffer(GLenum target, sequence<GLenum> attachments, + GLint x, GLint y, GLsizei width, GLsizei height); + void readBuffer(GLenum src); /* Renderbuffer objects */ - // any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname); - // void renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, - // GLsizei width, GLsizei height); + any getInternalformatParameter(GLenum target, GLenum internalformat, GLenum pname); + void renderbufferStorageMultisample(GLenum target, GLsizei samples, GLenum internalformat, + GLsizei width, GLsizei height); /* Texture objects */ // void texStorage2D(GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, @@ -422,7 +418,7 @@ interface mixin WebGL2RenderingContextBase // optional GLuint srcLengthOverride = 0); /* Programs and shaders */ - // [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram program, DOMString name); + [WebGLHandlesContextLoss] GLint getFragDataLocation(WebGLProgram program, DOMString name); /* Uniforms */ void uniform1ui(WebGLUniformLocation? location, GLuint v0); @@ -479,7 +475,7 @@ interface mixin WebGL2RenderingContextBase /*[AllowShared]*/ ArrayBufferView dstData, GLuint dstOffset); /* Multiple Render Targets */ - // void drawBuffers(sequence<GLenum> buffers); + void drawBuffers(sequence<GLenum> buffers); void clearBufferfv(GLenum buffer, GLint drawbuffer, Float32List values, optional GLuint srcOffset = 0); @@ -531,7 +527,7 @@ interface mixin WebGL2RenderingContextBase /* Uniform Buffer Objects and Transform Feedback Buffers */ void bindBufferBase(GLenum target, GLuint index, WebGLBuffer? buffer); void bindBufferRange(GLenum target, GLuint index, WebGLBuffer? buffer, GLintptr offset, GLsizeiptr size); - // any getIndexedParameter(GLenum target, GLuint index); + any getIndexedParameter(GLenum target, GLuint index); sequence<GLuint>? getUniformIndices(WebGLProgram program, sequence<DOMString> uniformNames); any getActiveUniforms(WebGLProgram program, sequence<GLuint> uniformIndices, GLenum pname); GLuint getUniformBlockIndex(WebGLProgram program, DOMString uniformBlockName); @@ -540,10 +536,10 @@ interface mixin WebGL2RenderingContextBase void uniformBlockBinding(WebGLProgram program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); /* Vertex Array Objects */ - /*WebGLVertexArrayObject? createVertexArray(); + WebGLVertexArrayObject? createVertexArray(); void deleteVertexArray(WebGLVertexArrayObject? vertexArray); [WebGLHandlesContextLoss] GLboolean isVertexArray(WebGLVertexArrayObject? vertexArray); - void bindVertexArray(WebGLVertexArrayObject? array);*/ + void bindVertexArray(WebGLVertexArrayObject? array); }; [Exposed=Window, Pref="dom.webgl2.enabled"] diff --git a/components/script/dom/webidls/WebGLVertexArrayObject.webidl b/components/script/dom/webidls/WebGLVertexArrayObject.webidl new file mode 100644 index 00000000000..a42d8cbe051 --- /dev/null +++ b/components/script/dom/webidls/WebGLVertexArrayObject.webidl @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +// +// WebGL IDL definitions scraped from the Khronos specification: +// https://www.khronos.org/registry/webgl/specs/latest/2.0/#3.7.17 +// + +[Exposed=(Window), Pref="dom.webgl2.enabled"] +interface WebGLVertexArrayObject : WebGLObject { +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index ce290caabf7..6b3b51f9a79 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,7 +8,6 @@ use crate::dom::bindings::codegen::Bindings::DocumentBinding::{ }; use crate::dom::bindings::codegen::Bindings::HistoryBinding::HistoryBinding::HistoryMethods; use crate::dom::bindings::codegen::Bindings::MediaQueryListBinding::MediaQueryListBinding::MediaQueryListMethods; -use crate::dom::bindings::codegen::Bindings::PermissionStatusBinding::PermissionState; use crate::dom::bindings::codegen::Bindings::RequestBinding::RequestInit; use crate::dom::bindings::codegen::Bindings::VoidFunctionBinding::VoidFunction; use crate::dom::bindings::codegen::Bindings::WindowBinding::{ @@ -275,9 +274,6 @@ pub struct Window { #[ignore_malloc_size_of = "defined in webxr"] webxr_registry: webxr_api::Registry, - /// A map for storing the previous permission state read results. - permission_state_invocation_results: DomRefCell<HashMap<String, PermissionState>>, - /// All of the elements that have an outstanding image request that was /// initiated by layout during a reflow. They are stored in the script thread /// to ensure that the element can be marked dirty when the image data becomes @@ -336,6 +332,8 @@ pub struct Window { /// A mechanism to force the compositor to process events. #[ignore_malloc_size_of = "traits are cumbersome"] event_loop_waker: Option<Box<dyn EventLoopWaker>>, + + visible: Cell<bool>, } impl Window { @@ -476,12 +474,6 @@ impl Window { Worklet::new(self, WorkletGlobalScopeType::Paint) } - pub fn permission_state_invocation_results( - &self, - ) -> &DomRefCell<HashMap<String, PermissionState>> { - &self.permission_state_invocation_results - } - pub fn pending_image_notification(&self, response: PendingImageResponse) { //XXXjdm could be more efficient to send the responses to the layout thread, // rather than making the layout thread talk to the image cache to @@ -1403,10 +1395,12 @@ impl Window { // We tear down the active document, which causes all the attached // nodes to dispose of their layout data. This messages the layout // thread, informing it that it can safely free the memory. - self.Document().upcast::<Node>().teardown(); + self.Document() + .upcast::<Node>() + .teardown(self.layout_chan()); - // Tell the constellation to drop the sender to our message-port router, if there is any. - self.upcast::<GlobalScope>().remove_message_ports_router(); + // Remove the infra for managing messageports and broadcast channels. + self.upcast::<GlobalScope>().remove_web_messaging_infra(); // Clean up any active promises // https://github.com/servo/servo/issues/15318 @@ -1987,7 +1981,7 @@ impl Window { .task_canceller(TaskSourceName::DOMManipulation) .wrap_task(task), ), - self.pipeline_id(), + Some(self.pipeline_id()), TaskSourceName::DOMManipulation, )); doc.set_url(load_data.url.clone()); @@ -2193,6 +2187,7 @@ impl Window { /// Slow down/speed up timers based on visibility. pub fn alter_resource_utilization(&self, visible: bool) { + self.visible.set(visible); if visible { self.upcast::<GlobalScope>().speed_up_timers(); } else { @@ -2200,6 +2195,10 @@ impl Window { } } + pub fn visible(&self) -> bool { + self.visible.get() + } + pub fn unminified_js_dir(&self) -> Option<String> { self.unminified_js_dir.borrow().clone() } @@ -2331,7 +2330,6 @@ impl Window { webgl_chan, webvr_chan, webxr_registry, - permission_state_invocation_results: Default::default(), pending_layout_images: Default::default(), unminified_js_dir: Default::default(), test_worklet: Default::default(), @@ -2348,13 +2346,14 @@ impl Window { replace_surrogates, player_context, event_loop_waker, + visible: Cell::new(true), }); unsafe { WindowBinding::Wrap(JSContext::from_ptr(runtime.cx()), win) } } - pub fn pipeline_id(&self) -> Option<PipelineId> { - Some(self.upcast::<GlobalScope>().pipeline_id()) + pub fn pipeline_id(&self) -> PipelineId { + self.upcast::<GlobalScope>().pipeline_id() } } @@ -2485,7 +2484,7 @@ impl Window { .task_canceller(TaskSourceName::DOMManipulation) .wrap_task(task), ), - self.pipeline_id(), + Some(self.pipeline_id()), TaskSourceName::DOMManipulation, )); } diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs index 8abbbb480d6..4155d3626a2 100644 --- a/components/script/dom/windowproxy.rs +++ b/components/script/dom/windowproxy.rs @@ -166,7 +166,7 @@ impl WindowProxy { // Create a new browsing context. let current = Some(window.global().pipeline_id()); - let mut window_proxy = Box::new(WindowProxy::new_inherited( + let window_proxy = Box::new(WindowProxy::new_inherited( browsing_context_id, top_level_browsing_context_id, current, @@ -212,7 +212,7 @@ impl WindowProxy { let cx = global_to_clone_from.get_cx(); // Create a new browsing context. - let mut window_proxy = Box::new(WindowProxy::new_inherited( + let window_proxy = Box::new(WindowProxy::new_inherited( browsing_context_id, top_level_browsing_context_id, None, diff --git a/components/script/dom/xrsession.rs b/components/script/dom/xrsession.rs index ec51c910a17..ed111fb3c02 100644 --- a/components/script/dom/xrsession.rs +++ b/components/script/dom/xrsession.rs @@ -5,7 +5,6 @@ use crate::dom::bindings::callback::ExceptionHandling; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::NavigatorBinding::NavigatorBinding::NavigatorMethods; -use crate::dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::XRReferenceSpaceBinding::XRReferenceSpaceType; use crate::dom::bindings::codegen::Bindings::XRRenderStateBinding::XRRenderStateInit; @@ -16,7 +15,6 @@ use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRFrameRequestCal use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRSessionMethods; use crate::dom::bindings::codegen::Bindings::XRSessionBinding::XRVisibilityState; use crate::dom::bindings::codegen::Bindings::XRSystemBinding::XRSessionMode; -use crate::dom::bindings::codegen::Bindings::XRWebGLLayerBinding::XRWebGLLayerMethods; use crate::dom::bindings::error::{Error, ErrorResult}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; @@ -25,8 +23,6 @@ use crate::dom::bindings::root::{Dom, DomRoot, MutDom, MutNullableDom}; use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; -use crate::dom::node::Node; -use crate::dom::node::NodeDamage; use crate::dom::performance::reduce_timing_resolution; use crate::dom::promise::Promise; use crate::dom::xrframe::XRFrame; @@ -51,7 +47,7 @@ use std::mem; use std::rc::Rc; use webxr_api::{ self, util, Display, EnvironmentBlendMode, Event as XREvent, Frame, SelectEvent, SelectKind, - Session, View, Viewer, Visibility, + Session, SessionId, View, Viewer, Visibility, }; #[dom_struct] @@ -412,14 +408,6 @@ impl XRSession { "WEBXR PROFILING [raf execute]:\t{}ms", (time::precise_time_ns() - raf_start) as f64 / 1_000_000. ); - - // If the canvas element is attached to the DOM, it is now dirty, - // and we need to trigger a reflow. - base_layer - .Context() - .Canvas() - .upcast::<Node>() - .dirty(NodeDamage::OtherNodeDamage); } fn update_inline_projection_matrix(&self) { @@ -462,38 +450,42 @@ impl XRSession { viewport: Rect::from_size(size.to_i32()), } } + + pub fn session_id(&self) -> SessionId { + self.session.borrow().id() + } } impl XRSessionMethods for XRSession { - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-end + // https://immersive-web.github.io/webxr/#eventdef-xrsession-end event_handler!(end, GetOnend, SetOnend); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-select + // https://immersive-web.github.io/webxr/#eventdef-xrsession-select event_handler!(select, GetOnselect, SetOnselect); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-selectstart + // https://immersive-web.github.io/webxr/#eventdef-xrsession-selectstart event_handler!(selectstart, GetOnselectstart, SetOnselectstart); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-selectend + // https://immersive-web.github.io/webxr/#eventdef-xrsession-selectend event_handler!(selectend, GetOnselectend, SetOnselectend); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeeze + // https://immersive-web.github.io/webxr/#eventdef-xrsession-squeeze event_handler!(squeeze, GetOnsqueeze, SetOnsqueeze); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezestart + // https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezestart event_handler!(squeezestart, GetOnsqueezestart, SetOnsqueezestart); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezeend + // https://immersive-web.github.io/webxr/#eventdef-xrsession-squeezeend event_handler!(squeezeend, GetOnsqueezeend, SetOnsqueezeend); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-visibilitychange + // https://immersive-web.github.io/webxr/#eventdef-xrsession-visibilitychange event_handler!( visibilitychange, GetOnvisibilitychange, SetOnvisibilitychange ); - /// https://immersive-web.github.io/webxr/#eventdef-xrsession-inputsourceschange + // https://immersive-web.github.io/webxr/#eventdef-xrsession-inputsourceschange event_handler!( inputsourceschange, GetOninputsourceschange, diff --git a/components/script/dom/xrsystem.rs b/components/script/dom/xrsystem.rs index e397a4fc0b2..8a8f109722b 100644 --- a/components/script/dom/xrsystem.rs +++ b/components/script/dom/xrsystem.rs @@ -18,10 +18,10 @@ use crate::dom::event::Event; use crate::dom::eventtarget::EventTarget; use crate::dom::gamepad::Gamepad; use crate::dom::gamepadevent::GamepadEventType; -use crate::dom::globalscope::GlobalScope; use crate::dom::promise::Promise; use crate::dom::vrdisplay::VRDisplay; use crate::dom::vrdisplayevent::VRDisplayEvent; +use crate::dom::window::Window; use crate::dom::xrsession::XRSession; use crate::dom::xrtest::XRTest; use crate::realms::InRealm; @@ -30,6 +30,7 @@ use crate::task_source::TaskSource; use dom_struct::dom_struct; use ipc_channel::ipc::{self as ipc_crate, IpcReceiver, IpcSender}; use ipc_channel::router::ROUTER; +use msg::constellation_msg::PipelineId; use profile_traits::ipc; use std::cell::Cell; use std::rc::Rc; @@ -46,10 +47,13 @@ pub struct XRSystem { active_immersive_session: MutNullableDom<XRSession>, active_inline_sessions: DomRefCell<Vec<Dom<XRSession>>>, test: MutNullableDom<XRTest>, + pipeline: PipelineId, + #[ignore_malloc_size_of = "channels are hard"] + webvr_thread: Option<IpcSender<WebVRMsg>>, } impl XRSystem { - fn new_inherited() -> XRSystem { + fn new_inherited(pipeline: PipelineId, webvr_thread: Option<IpcSender<WebVRMsg>>) -> XRSystem { XRSystem { eventtarget: EventTarget::new_inherited(), displays: DomRefCell::new(Vec::new()), @@ -58,13 +62,18 @@ impl XRSystem { active_immersive_session: Default::default(), active_inline_sessions: DomRefCell::new(Vec::new()), test: Default::default(), + pipeline, + webvr_thread, } } - pub fn new(global: &GlobalScope) -> DomRoot<XRSystem> { + pub fn new(window: &Window) -> DomRoot<XRSystem> { let root = reflect_dom_object( - Box::new(XRSystem::new_inherited()), - global, + Box::new(XRSystem::new_inherited( + window.pipeline_id(), + window.webvr_thread(), + )), + window, XRSystemBinding::Wrap, ); root.register(); @@ -304,7 +313,7 @@ impl XRSystem { } pub fn get_displays(&self) -> Result<Vec<DomRoot<VRDisplay>>, ()> { - if let Some(webvr_thread) = self.webvr_thread() { + if let Some(ref webvr_thread) = self.webvr_thread { let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); webvr_thread.send(WebVRMsg::GetDisplays(sender)).unwrap(); @@ -333,10 +342,6 @@ impl XRSystem { .collect()) } - fn webvr_thread(&self) -> Option<IpcSender<WebVRMsg>> { - self.global().as_window().webvr_thread() - } - fn find_display(&self, display_id: u32) -> Option<DomRoot<VRDisplay>> { self.displays .borrow() @@ -346,15 +351,15 @@ impl XRSystem { } fn register(&self) { - if let Some(webvr_thread) = self.webvr_thread() { + if let Some(ref webvr_thread) = self.webvr_thread { let msg = WebVRMsg::RegisterContext(self.global().pipeline_id()); webvr_thread.send(msg).unwrap(); } } fn unregister(&self) { - if let Some(webvr_thread) = self.webvr_thread() { - let msg = WebVRMsg::UnregisterContext(self.global().pipeline_id()); + if let Some(ref webvr_thread) = self.webvr_thread { + let msg = WebVRMsg::UnregisterContext(self.pipeline); webvr_thread.send(msg).unwrap(); } } @@ -364,7 +369,7 @@ impl XRSystem { existing.update_display(&display); existing } else { - let root = VRDisplay::new(&self.global(), display.clone()); + let root = VRDisplay::new(&self.global().as_window(), display.clone()); self.displays.borrow_mut().push(Dom::from_ref(&*root)); root } @@ -474,7 +479,7 @@ impl XRSystem { // guarantees that the gamepads always have a valid state and can be very useful for // motion capture or drawing applications. pub fn get_gamepads(&self) -> Vec<DomRoot<Gamepad>> { - if let Some(wevbr_sender) = self.webvr_thread() { + if let Some(ref wevbr_sender) = self.webvr_thread { let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); let synced_ids = self |